18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * drivers/video/pvr2fb.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Frame buffer and fbcon support for the NEC PowerVR2 found within the Sega 68c2ecf20Sopenharmony_ci * Dreamcast. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Copyright (c) 2001 M. R. Brown <mrbrown@0xd6.org> 98c2ecf20Sopenharmony_ci * Copyright (c) 2001 - 2008 Paul Mundt <lethal@linux-sh.org> 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * This driver is mostly based on the excellent amifb and vfb sources. It uses 128c2ecf20Sopenharmony_ci * an odd scheme for converting hardware values to/from framebuffer values, 138c2ecf20Sopenharmony_ci * here are some hacked-up formulas: 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * The Dreamcast has screen offsets from each side of its four borders and 168c2ecf20Sopenharmony_ci * the start offsets of the display window. I used these values to calculate 178c2ecf20Sopenharmony_ci * 'pseudo' values (think of them as placeholders) for the fb video mode, so 188c2ecf20Sopenharmony_ci * that when it came time to convert these values back into their hardware 198c2ecf20Sopenharmony_ci * values, I could just add mode- specific offsets to get the correct mode 208c2ecf20Sopenharmony_ci * settings: 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * left_margin = diwstart_h - borderstart_h; 238c2ecf20Sopenharmony_ci * right_margin = borderstop_h - (diwstart_h + xres); 248c2ecf20Sopenharmony_ci * upper_margin = diwstart_v - borderstart_v; 258c2ecf20Sopenharmony_ci * lower_margin = borderstop_v - (diwstart_h + yres); 268c2ecf20Sopenharmony_ci * 278c2ecf20Sopenharmony_ci * hsync_len = borderstart_h + (hsync_total - borderstop_h); 288c2ecf20Sopenharmony_ci * vsync_len = borderstart_v + (vsync_total - borderstop_v); 298c2ecf20Sopenharmony_ci * 308c2ecf20Sopenharmony_ci * Then, when it's time to convert back to hardware settings, the only 318c2ecf20Sopenharmony_ci * constants are the borderstart_* offsets, all other values are derived from 328c2ecf20Sopenharmony_ci * the fb video mode: 338c2ecf20Sopenharmony_ci * 348c2ecf20Sopenharmony_ci * // PAL 358c2ecf20Sopenharmony_ci * borderstart_h = 116; 368c2ecf20Sopenharmony_ci * borderstart_v = 44; 378c2ecf20Sopenharmony_ci * ... 388c2ecf20Sopenharmony_ci * borderstop_h = borderstart_h + hsync_total - hsync_len; 398c2ecf20Sopenharmony_ci * ... 408c2ecf20Sopenharmony_ci * diwstart_v = borderstart_v - upper_margin; 418c2ecf20Sopenharmony_ci * 428c2ecf20Sopenharmony_ci * However, in the current implementation, the borderstart values haven't had 438c2ecf20Sopenharmony_ci * the benefit of being fully researched, so some modes may be broken. 448c2ecf20Sopenharmony_ci */ 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#undef DEBUG 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#include <linux/module.h> 498c2ecf20Sopenharmony_ci#include <linux/kernel.h> 508c2ecf20Sopenharmony_ci#include <linux/errno.h> 518c2ecf20Sopenharmony_ci#include <linux/string.h> 528c2ecf20Sopenharmony_ci#include <linux/mm.h> 538c2ecf20Sopenharmony_ci#include <linux/slab.h> 548c2ecf20Sopenharmony_ci#include <linux/delay.h> 558c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 568c2ecf20Sopenharmony_ci#include <linux/fb.h> 578c2ecf20Sopenharmony_ci#include <linux/init.h> 588c2ecf20Sopenharmony_ci#include <linux/pci.h> 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#ifdef CONFIG_SH_DREAMCAST 618c2ecf20Sopenharmony_ci#include <asm/machvec.h> 628c2ecf20Sopenharmony_ci#include <mach-dreamcast/mach/sysasic.h> 638c2ecf20Sopenharmony_ci#endif 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci#ifdef CONFIG_PVR2_DMA 668c2ecf20Sopenharmony_ci#include <linux/pagemap.h> 678c2ecf20Sopenharmony_ci#include <mach/dma.h> 688c2ecf20Sopenharmony_ci#include <asm/dma.h> 698c2ecf20Sopenharmony_ci#endif 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci#ifdef CONFIG_SH_STORE_QUEUES 728c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 738c2ecf20Sopenharmony_ci#include <cpu/sq.h> 748c2ecf20Sopenharmony_ci#endif 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci#ifndef PCI_DEVICE_ID_NEC_NEON250 778c2ecf20Sopenharmony_ci# define PCI_DEVICE_ID_NEC_NEON250 0x0067 788c2ecf20Sopenharmony_ci#endif 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci/* 2D video registers */ 818c2ecf20Sopenharmony_ci#define DISP_BASE par->mmio_base 828c2ecf20Sopenharmony_ci#define DISP_BRDRCOLR (DISP_BASE + 0x40) 838c2ecf20Sopenharmony_ci#define DISP_DIWMODE (DISP_BASE + 0x44) 848c2ecf20Sopenharmony_ci#define DISP_DIWADDRL (DISP_BASE + 0x50) 858c2ecf20Sopenharmony_ci#define DISP_DIWADDRS (DISP_BASE + 0x54) 868c2ecf20Sopenharmony_ci#define DISP_DIWSIZE (DISP_BASE + 0x5c) 878c2ecf20Sopenharmony_ci#define DISP_SYNCCONF (DISP_BASE + 0xd0) 888c2ecf20Sopenharmony_ci#define DISP_BRDRHORZ (DISP_BASE + 0xd4) 898c2ecf20Sopenharmony_ci#define DISP_SYNCSIZE (DISP_BASE + 0xd8) 908c2ecf20Sopenharmony_ci#define DISP_BRDRVERT (DISP_BASE + 0xdc) 918c2ecf20Sopenharmony_ci#define DISP_DIWCONF (DISP_BASE + 0xe8) 928c2ecf20Sopenharmony_ci#define DISP_DIWHSTRT (DISP_BASE + 0xec) 938c2ecf20Sopenharmony_ci#define DISP_DIWVSTRT (DISP_BASE + 0xf0) 948c2ecf20Sopenharmony_ci#define DISP_PIXDEPTH (DISP_BASE + 0x108) 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci/* Pixel clocks, one for TV output, doubled for VGA output */ 978c2ecf20Sopenharmony_ci#define TV_CLK 74239 988c2ecf20Sopenharmony_ci#define VGA_CLK 37119 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci/* This is for 60Hz - the VTOTAL is doubled for interlaced modes */ 1018c2ecf20Sopenharmony_ci#define PAL_HTOTAL 863 1028c2ecf20Sopenharmony_ci#define PAL_VTOTAL 312 1038c2ecf20Sopenharmony_ci#define NTSC_HTOTAL 857 1048c2ecf20Sopenharmony_ci#define NTSC_VTOTAL 262 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci/* Supported cable types */ 1078c2ecf20Sopenharmony_cienum { CT_VGA, CT_NONE, CT_RGB, CT_COMPOSITE }; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci/* Supported video output types */ 1108c2ecf20Sopenharmony_cienum { VO_PAL, VO_NTSC, VO_VGA }; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci/* Supported palette types */ 1138c2ecf20Sopenharmony_cienum { PAL_ARGB1555, PAL_RGB565, PAL_ARGB4444, PAL_ARGB8888 }; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistruct pvr2_params { unsigned int val; char *name; }; 1168c2ecf20Sopenharmony_cistatic struct pvr2_params cables[] = { 1178c2ecf20Sopenharmony_ci { CT_VGA, "VGA" }, { CT_RGB, "RGB" }, { CT_COMPOSITE, "COMPOSITE" }, 1188c2ecf20Sopenharmony_ci}; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic struct pvr2_params outputs[] = { 1218c2ecf20Sopenharmony_ci { VO_PAL, "PAL" }, { VO_NTSC, "NTSC" }, { VO_VGA, "VGA" }, 1228c2ecf20Sopenharmony_ci}; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci/* 1258c2ecf20Sopenharmony_ci * This describes the current video mode 1268c2ecf20Sopenharmony_ci */ 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic struct pvr2fb_par { 1298c2ecf20Sopenharmony_ci unsigned int hsync_total; /* Clocks/line */ 1308c2ecf20Sopenharmony_ci unsigned int vsync_total; /* Lines/field */ 1318c2ecf20Sopenharmony_ci unsigned int borderstart_h; 1328c2ecf20Sopenharmony_ci unsigned int borderstop_h; 1338c2ecf20Sopenharmony_ci unsigned int borderstart_v; 1348c2ecf20Sopenharmony_ci unsigned int borderstop_v; 1358c2ecf20Sopenharmony_ci unsigned int diwstart_h; /* Horizontal offset of the display field */ 1368c2ecf20Sopenharmony_ci unsigned int diwstart_v; /* Vertical offset of the display field, for 1378c2ecf20Sopenharmony_ci interlaced modes, this is the long field */ 1388c2ecf20Sopenharmony_ci unsigned long disp_start; /* Address of image within VRAM */ 1398c2ecf20Sopenharmony_ci unsigned char is_interlaced; /* Is the display interlaced? */ 1408c2ecf20Sopenharmony_ci unsigned char is_doublescan; /* Are scanlines output twice? (doublescan) */ 1418c2ecf20Sopenharmony_ci unsigned char is_lowres; /* Is horizontal pixel-doubling enabled? */ 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci void __iomem *mmio_base; /* MMIO base */ 1448c2ecf20Sopenharmony_ci u32 palette[16]; 1458c2ecf20Sopenharmony_ci} *currentpar; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic struct fb_info *fb_info; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic struct fb_fix_screeninfo pvr2_fix = { 1508c2ecf20Sopenharmony_ci .id = "NEC PowerVR2", 1518c2ecf20Sopenharmony_ci .type = FB_TYPE_PACKED_PIXELS, 1528c2ecf20Sopenharmony_ci .visual = FB_VISUAL_TRUECOLOR, 1538c2ecf20Sopenharmony_ci .ypanstep = 1, 1548c2ecf20Sopenharmony_ci .ywrapstep = 1, 1558c2ecf20Sopenharmony_ci .accel = FB_ACCEL_NONE, 1568c2ecf20Sopenharmony_ci}; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic const struct fb_var_screeninfo pvr2_var = { 1598c2ecf20Sopenharmony_ci .xres = 640, 1608c2ecf20Sopenharmony_ci .yres = 480, 1618c2ecf20Sopenharmony_ci .xres_virtual = 640, 1628c2ecf20Sopenharmony_ci .yres_virtual = 480, 1638c2ecf20Sopenharmony_ci .bits_per_pixel =16, 1648c2ecf20Sopenharmony_ci .red = { 11, 5, 0 }, 1658c2ecf20Sopenharmony_ci .green = { 5, 6, 0 }, 1668c2ecf20Sopenharmony_ci .blue = { 0, 5, 0 }, 1678c2ecf20Sopenharmony_ci .activate = FB_ACTIVATE_NOW, 1688c2ecf20Sopenharmony_ci .height = -1, 1698c2ecf20Sopenharmony_ci .width = -1, 1708c2ecf20Sopenharmony_ci .vmode = FB_VMODE_NONINTERLACED, 1718c2ecf20Sopenharmony_ci}; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic int cable_type = CT_VGA; 1748c2ecf20Sopenharmony_cistatic int video_output = VO_VGA; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic int nopan = 0; 1778c2ecf20Sopenharmony_cistatic int nowrap = 1; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci/* 1808c2ecf20Sopenharmony_ci * We do all updating, blanking, etc. during the vertical retrace period 1818c2ecf20Sopenharmony_ci */ 1828c2ecf20Sopenharmony_cistatic unsigned int do_vmode_full = 0; /* Change the video mode */ 1838c2ecf20Sopenharmony_cistatic unsigned int do_vmode_pan = 0; /* Update the video mode */ 1848c2ecf20Sopenharmony_cistatic short do_blank = 0; /* (Un)Blank the screen */ 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic unsigned int is_blanked = 0; /* Is the screen blanked? */ 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci#ifdef CONFIG_SH_STORE_QUEUES 1898c2ecf20Sopenharmony_cistatic unsigned long pvr2fb_map; 1908c2ecf20Sopenharmony_ci#endif 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci#ifdef CONFIG_PVR2_DMA 1938c2ecf20Sopenharmony_cistatic unsigned int shdma = PVR2_CASCADE_CHAN; 1948c2ecf20Sopenharmony_cistatic unsigned int pvr2dma = ONCHIP_NR_DMA_CHANNELS; 1958c2ecf20Sopenharmony_ci#endif 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic struct fb_videomode pvr2_modedb[] = { 1988c2ecf20Sopenharmony_ci /* 1998c2ecf20Sopenharmony_ci * Broadcast video modes (PAL and NTSC). I'm unfamiliar with 2008c2ecf20Sopenharmony_ci * PAL-M and PAL-N, but from what I've read both modes parallel PAL and 2018c2ecf20Sopenharmony_ci * NTSC, so it shouldn't be a problem (I hope). 2028c2ecf20Sopenharmony_ci */ 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci { 2058c2ecf20Sopenharmony_ci /* 640x480 @ 60Hz interlaced (NTSC) */ 2068c2ecf20Sopenharmony_ci "ntsc_640x480i", 60, 640, 480, TV_CLK, 38, 33, 0, 18, 146, 26, 2078c2ecf20Sopenharmony_ci FB_SYNC_BROADCAST, FB_VMODE_INTERLACED | FB_VMODE_YWRAP 2088c2ecf20Sopenharmony_ci }, { 2098c2ecf20Sopenharmony_ci /* 640x240 @ 60Hz (NTSC) */ 2108c2ecf20Sopenharmony_ci /* XXX: Broken! Don't use... */ 2118c2ecf20Sopenharmony_ci "ntsc_640x240", 60, 640, 240, TV_CLK, 38, 33, 0, 0, 146, 22, 2128c2ecf20Sopenharmony_ci FB_SYNC_BROADCAST, FB_VMODE_YWRAP 2138c2ecf20Sopenharmony_ci }, { 2148c2ecf20Sopenharmony_ci /* 640x480 @ 60hz (VGA) */ 2158c2ecf20Sopenharmony_ci "vga_640x480", 60, 640, 480, VGA_CLK, 38, 33, 0, 18, 146, 26, 2168c2ecf20Sopenharmony_ci 0, FB_VMODE_YWRAP 2178c2ecf20Sopenharmony_ci }, 2188c2ecf20Sopenharmony_ci}; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci#define NUM_TOTAL_MODES ARRAY_SIZE(pvr2_modedb) 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci#define DEFMODE_NTSC 0 2238c2ecf20Sopenharmony_ci#define DEFMODE_PAL 0 2248c2ecf20Sopenharmony_ci#define DEFMODE_VGA 2 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic int defmode = DEFMODE_NTSC; 2278c2ecf20Sopenharmony_cistatic char *mode_option = NULL; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic inline void pvr2fb_set_pal_type(unsigned int type) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci struct pvr2fb_par *par = (struct pvr2fb_par *)fb_info->par; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci fb_writel(type, par->mmio_base + 0x108); 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic inline void pvr2fb_set_pal_entry(struct pvr2fb_par *par, 2378c2ecf20Sopenharmony_ci unsigned int regno, 2388c2ecf20Sopenharmony_ci unsigned int val) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci fb_writel(val, par->mmio_base + 0x1000 + (4 * regno)); 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistatic int pvr2fb_blank(int blank, struct fb_info *info) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci do_blank = blank ? blank : -1; 2468c2ecf20Sopenharmony_ci return 0; 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic inline unsigned long get_line_length(int xres_virtual, int bpp) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci return (unsigned long)((((xres_virtual*bpp)+31)&~31) >> 3); 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic void set_color_bitfields(struct fb_var_screeninfo *var) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci switch (var->bits_per_pixel) { 2578c2ecf20Sopenharmony_ci case 16: /* RGB 565 */ 2588c2ecf20Sopenharmony_ci pvr2fb_set_pal_type(PAL_RGB565); 2598c2ecf20Sopenharmony_ci var->red.offset = 11; var->red.length = 5; 2608c2ecf20Sopenharmony_ci var->green.offset = 5; var->green.length = 6; 2618c2ecf20Sopenharmony_ci var->blue.offset = 0; var->blue.length = 5; 2628c2ecf20Sopenharmony_ci var->transp.offset = 0; var->transp.length = 0; 2638c2ecf20Sopenharmony_ci break; 2648c2ecf20Sopenharmony_ci case 24: /* RGB 888 */ 2658c2ecf20Sopenharmony_ci var->red.offset = 16; var->red.length = 8; 2668c2ecf20Sopenharmony_ci var->green.offset = 8; var->green.length = 8; 2678c2ecf20Sopenharmony_ci var->blue.offset = 0; var->blue.length = 8; 2688c2ecf20Sopenharmony_ci var->transp.offset = 0; var->transp.length = 0; 2698c2ecf20Sopenharmony_ci break; 2708c2ecf20Sopenharmony_ci case 32: /* ARGB 8888 */ 2718c2ecf20Sopenharmony_ci pvr2fb_set_pal_type(PAL_ARGB8888); 2728c2ecf20Sopenharmony_ci var->red.offset = 16; var->red.length = 8; 2738c2ecf20Sopenharmony_ci var->green.offset = 8; var->green.length = 8; 2748c2ecf20Sopenharmony_ci var->blue.offset = 0; var->blue.length = 8; 2758c2ecf20Sopenharmony_ci var->transp.offset = 24; var->transp.length = 8; 2768c2ecf20Sopenharmony_ci break; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic int pvr2fb_setcolreg(unsigned int regno, unsigned int red, 2818c2ecf20Sopenharmony_ci unsigned int green, unsigned int blue, 2828c2ecf20Sopenharmony_ci unsigned int transp, struct fb_info *info) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci struct pvr2fb_par *par = (struct pvr2fb_par *)info->par; 2858c2ecf20Sopenharmony_ci unsigned int tmp; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci if (regno > info->cmap.len) 2888c2ecf20Sopenharmony_ci return 1; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci /* 2918c2ecf20Sopenharmony_ci * We only support the hardware palette for 16 and 32bpp. It's also 2928c2ecf20Sopenharmony_ci * expected that the palette format has been set by the time we get 2938c2ecf20Sopenharmony_ci * here, so we don't waste time setting it again. 2948c2ecf20Sopenharmony_ci */ 2958c2ecf20Sopenharmony_ci switch (info->var.bits_per_pixel) { 2968c2ecf20Sopenharmony_ci case 16: /* RGB 565 */ 2978c2ecf20Sopenharmony_ci tmp = (red & 0xf800) | 2988c2ecf20Sopenharmony_ci ((green & 0xfc00) >> 5) | 2998c2ecf20Sopenharmony_ci ((blue & 0xf800) >> 11); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci pvr2fb_set_pal_entry(par, regno, tmp); 3028c2ecf20Sopenharmony_ci break; 3038c2ecf20Sopenharmony_ci case 24: /* RGB 888 */ 3048c2ecf20Sopenharmony_ci red >>= 8; green >>= 8; blue >>= 8; 3058c2ecf20Sopenharmony_ci tmp = (red << 16) | (green << 8) | blue; 3068c2ecf20Sopenharmony_ci break; 3078c2ecf20Sopenharmony_ci case 32: /* ARGB 8888 */ 3088c2ecf20Sopenharmony_ci red >>= 8; green >>= 8; blue >>= 8; 3098c2ecf20Sopenharmony_ci tmp = (transp << 24) | (red << 16) | (green << 8) | blue; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci pvr2fb_set_pal_entry(par, regno, tmp); 3128c2ecf20Sopenharmony_ci break; 3138c2ecf20Sopenharmony_ci default: 3148c2ecf20Sopenharmony_ci pr_debug("Invalid bit depth %d?!?\n", info->var.bits_per_pixel); 3158c2ecf20Sopenharmony_ci return 1; 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci if (regno < 16) 3198c2ecf20Sopenharmony_ci ((u32*)(info->pseudo_palette))[regno] = tmp; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci return 0; 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci/* 3258c2ecf20Sopenharmony_ci * Determine the cable type and initialize the cable output format. Don't do 3268c2ecf20Sopenharmony_ci * anything if the cable type has been overidden (via "cable:XX"). 3278c2ecf20Sopenharmony_ci */ 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci#define PCTRA ((void __iomem *)0xff80002c) 3308c2ecf20Sopenharmony_ci#define PDTRA ((void __iomem *)0xff800030) 3318c2ecf20Sopenharmony_ci#define VOUTC ((void __iomem *)0xa0702c00) 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_cistatic int pvr2_init_cable(void) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci if (cable_type < 0) { 3368c2ecf20Sopenharmony_ci fb_writel((fb_readl(PCTRA) & 0xfff0ffff) | 0x000a0000, 3378c2ecf20Sopenharmony_ci PCTRA); 3388c2ecf20Sopenharmony_ci cable_type = (fb_readw(PDTRA) >> 8) & 3; 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci /* Now select the output format (either composite or other) */ 3428c2ecf20Sopenharmony_ci /* XXX: Save the previous val first, as this reg is also AICA 3438c2ecf20Sopenharmony_ci related */ 3448c2ecf20Sopenharmony_ci if (cable_type == CT_COMPOSITE) 3458c2ecf20Sopenharmony_ci fb_writel(3 << 8, VOUTC); 3468c2ecf20Sopenharmony_ci else if (cable_type == CT_RGB) 3478c2ecf20Sopenharmony_ci fb_writel(1 << 9, VOUTC); 3488c2ecf20Sopenharmony_ci else 3498c2ecf20Sopenharmony_ci fb_writel(0, VOUTC); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci return cable_type; 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic int pvr2fb_set_par(struct fb_info *info) 3558c2ecf20Sopenharmony_ci{ 3568c2ecf20Sopenharmony_ci struct pvr2fb_par *par = (struct pvr2fb_par *)info->par; 3578c2ecf20Sopenharmony_ci struct fb_var_screeninfo *var = &info->var; 3588c2ecf20Sopenharmony_ci unsigned long line_length; 3598c2ecf20Sopenharmony_ci unsigned int vtotal; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci /* 3628c2ecf20Sopenharmony_ci * XXX: It's possible that a user could use a VGA box, change the cable 3638c2ecf20Sopenharmony_ci * type in hardware (i.e. switch from VGA<->composite), then change 3648c2ecf20Sopenharmony_ci * modes (i.e. switching to another VT). If that happens we should 3658c2ecf20Sopenharmony_ci * automagically change the output format to cope, but currently I 3668c2ecf20Sopenharmony_ci * don't have a VGA box to make sure this works properly. 3678c2ecf20Sopenharmony_ci */ 3688c2ecf20Sopenharmony_ci cable_type = pvr2_init_cable(); 3698c2ecf20Sopenharmony_ci if (cable_type == CT_VGA && video_output != VO_VGA) 3708c2ecf20Sopenharmony_ci video_output = VO_VGA; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci var->vmode &= FB_VMODE_MASK; 3738c2ecf20Sopenharmony_ci if (var->vmode & FB_VMODE_INTERLACED && video_output != VO_VGA) 3748c2ecf20Sopenharmony_ci par->is_interlaced = 1; 3758c2ecf20Sopenharmony_ci /* 3768c2ecf20Sopenharmony_ci * XXX: Need to be more creative with this (i.e. allow doublecan for 3778c2ecf20Sopenharmony_ci * PAL/NTSC output). 3788c2ecf20Sopenharmony_ci */ 3798c2ecf20Sopenharmony_ci if (var->vmode & FB_VMODE_DOUBLE && video_output == VO_VGA) 3808c2ecf20Sopenharmony_ci par->is_doublescan = 1; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci par->hsync_total = var->left_margin + var->xres + var->right_margin + 3838c2ecf20Sopenharmony_ci var->hsync_len; 3848c2ecf20Sopenharmony_ci par->vsync_total = var->upper_margin + var->yres + var->lower_margin + 3858c2ecf20Sopenharmony_ci var->vsync_len; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci if (var->sync & FB_SYNC_BROADCAST) { 3888c2ecf20Sopenharmony_ci vtotal = par->vsync_total; 3898c2ecf20Sopenharmony_ci if (par->is_interlaced) 3908c2ecf20Sopenharmony_ci vtotal /= 2; 3918c2ecf20Sopenharmony_ci if (vtotal > (PAL_VTOTAL + NTSC_VTOTAL)/2) { 3928c2ecf20Sopenharmony_ci /* XXX: Check for start values here... */ 3938c2ecf20Sopenharmony_ci /* XXX: Check hardware for PAL-compatibility */ 3948c2ecf20Sopenharmony_ci par->borderstart_h = 116; 3958c2ecf20Sopenharmony_ci par->borderstart_v = 44; 3968c2ecf20Sopenharmony_ci } else { 3978c2ecf20Sopenharmony_ci /* NTSC video output */ 3988c2ecf20Sopenharmony_ci par->borderstart_h = 126; 3998c2ecf20Sopenharmony_ci par->borderstart_v = 18; 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci } else { 4028c2ecf20Sopenharmony_ci /* VGA mode */ 4038c2ecf20Sopenharmony_ci /* XXX: What else needs to be checked? */ 4048c2ecf20Sopenharmony_ci /* 4058c2ecf20Sopenharmony_ci * XXX: We have a little freedom in VGA modes, what ranges 4068c2ecf20Sopenharmony_ci * should be here (i.e. hsync/vsync totals, etc.)? 4078c2ecf20Sopenharmony_ci */ 4088c2ecf20Sopenharmony_ci par->borderstart_h = 126; 4098c2ecf20Sopenharmony_ci par->borderstart_v = 40; 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci /* Calculate the remainding offsets */ 4138c2ecf20Sopenharmony_ci par->diwstart_h = par->borderstart_h + var->left_margin; 4148c2ecf20Sopenharmony_ci par->diwstart_v = par->borderstart_v + var->upper_margin; 4158c2ecf20Sopenharmony_ci par->borderstop_h = par->diwstart_h + var->xres + 4168c2ecf20Sopenharmony_ci var->right_margin; 4178c2ecf20Sopenharmony_ci par->borderstop_v = par->diwstart_v + var->yres + 4188c2ecf20Sopenharmony_ci var->lower_margin; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci if (!par->is_interlaced) 4218c2ecf20Sopenharmony_ci par->borderstop_v /= 2; 4228c2ecf20Sopenharmony_ci if (info->var.xres < 640) 4238c2ecf20Sopenharmony_ci par->is_lowres = 1; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci line_length = get_line_length(var->xres_virtual, var->bits_per_pixel); 4268c2ecf20Sopenharmony_ci par->disp_start = info->fix.smem_start + (line_length * var->yoffset) * line_length; 4278c2ecf20Sopenharmony_ci info->fix.line_length = line_length; 4288c2ecf20Sopenharmony_ci return 0; 4298c2ecf20Sopenharmony_ci} 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_cistatic int pvr2fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) 4328c2ecf20Sopenharmony_ci{ 4338c2ecf20Sopenharmony_ci struct pvr2fb_par *par = (struct pvr2fb_par *)info->par; 4348c2ecf20Sopenharmony_ci unsigned int vtotal, hsync_total; 4358c2ecf20Sopenharmony_ci unsigned long line_length; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci if (var->pixclock != TV_CLK && var->pixclock != VGA_CLK) { 4388c2ecf20Sopenharmony_ci pr_debug("Invalid pixclock value %d\n", var->pixclock); 4398c2ecf20Sopenharmony_ci return -EINVAL; 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci if (var->xres < 320) 4438c2ecf20Sopenharmony_ci var->xres = 320; 4448c2ecf20Sopenharmony_ci if (var->yres < 240) 4458c2ecf20Sopenharmony_ci var->yres = 240; 4468c2ecf20Sopenharmony_ci if (var->xres_virtual < var->xres) 4478c2ecf20Sopenharmony_ci var->xres_virtual = var->xres; 4488c2ecf20Sopenharmony_ci if (var->yres_virtual < var->yres) 4498c2ecf20Sopenharmony_ci var->yres_virtual = var->yres; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci if (var->bits_per_pixel <= 16) 4528c2ecf20Sopenharmony_ci var->bits_per_pixel = 16; 4538c2ecf20Sopenharmony_ci else if (var->bits_per_pixel <= 24) 4548c2ecf20Sopenharmony_ci var->bits_per_pixel = 24; 4558c2ecf20Sopenharmony_ci else if (var->bits_per_pixel <= 32) 4568c2ecf20Sopenharmony_ci var->bits_per_pixel = 32; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci set_color_bitfields(var); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci if (var->vmode & FB_VMODE_YWRAP) { 4618c2ecf20Sopenharmony_ci if (var->xoffset || var->yoffset >= var->yres_virtual) { 4628c2ecf20Sopenharmony_ci var->xoffset = var->yoffset = 0; 4638c2ecf20Sopenharmony_ci } else { 4648c2ecf20Sopenharmony_ci if (var->xoffset > var->xres_virtual - var->xres || 4658c2ecf20Sopenharmony_ci var->yoffset > var->yres_virtual - var->yres) 4668c2ecf20Sopenharmony_ci var->xoffset = var->yoffset = 0; 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci } else { 4698c2ecf20Sopenharmony_ci var->xoffset = var->yoffset = 0; 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci /* 4738c2ecf20Sopenharmony_ci * XXX: Need to be more creative with this (i.e. allow doublecan for 4748c2ecf20Sopenharmony_ci * PAL/NTSC output). 4758c2ecf20Sopenharmony_ci */ 4768c2ecf20Sopenharmony_ci if (var->yres < 480 && video_output == VO_VGA) 4778c2ecf20Sopenharmony_ci var->vmode |= FB_VMODE_DOUBLE; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci if (video_output != VO_VGA) { 4808c2ecf20Sopenharmony_ci var->sync |= FB_SYNC_BROADCAST; 4818c2ecf20Sopenharmony_ci var->vmode |= FB_VMODE_INTERLACED; 4828c2ecf20Sopenharmony_ci } else { 4838c2ecf20Sopenharmony_ci var->sync &= ~FB_SYNC_BROADCAST; 4848c2ecf20Sopenharmony_ci var->vmode &= ~FB_VMODE_INTERLACED; 4858c2ecf20Sopenharmony_ci var->vmode |= FB_VMODE_NONINTERLACED; 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci if ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_TEST) { 4898c2ecf20Sopenharmony_ci var->right_margin = par->borderstop_h - 4908c2ecf20Sopenharmony_ci (par->diwstart_h + var->xres); 4918c2ecf20Sopenharmony_ci var->left_margin = par->diwstart_h - par->borderstart_h; 4928c2ecf20Sopenharmony_ci var->hsync_len = par->borderstart_h + 4938c2ecf20Sopenharmony_ci (par->hsync_total - par->borderstop_h); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci var->upper_margin = par->diwstart_v - par->borderstart_v; 4968c2ecf20Sopenharmony_ci var->lower_margin = par->borderstop_v - 4978c2ecf20Sopenharmony_ci (par->diwstart_v + var->yres); 4988c2ecf20Sopenharmony_ci var->vsync_len = par->borderstop_v + 4998c2ecf20Sopenharmony_ci (par->vsync_total - par->borderstop_v); 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci hsync_total = var->left_margin + var->xres + var->right_margin + 5038c2ecf20Sopenharmony_ci var->hsync_len; 5048c2ecf20Sopenharmony_ci vtotal = var->upper_margin + var->yres + var->lower_margin + 5058c2ecf20Sopenharmony_ci var->vsync_len; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci if (var->sync & FB_SYNC_BROADCAST) { 5088c2ecf20Sopenharmony_ci if (var->vmode & FB_VMODE_INTERLACED) 5098c2ecf20Sopenharmony_ci vtotal /= 2; 5108c2ecf20Sopenharmony_ci if (vtotal > (PAL_VTOTAL + NTSC_VTOTAL)/2) { 5118c2ecf20Sopenharmony_ci /* PAL video output */ 5128c2ecf20Sopenharmony_ci /* XXX: Should be using a range here ... ? */ 5138c2ecf20Sopenharmony_ci if (hsync_total != PAL_HTOTAL) { 5148c2ecf20Sopenharmony_ci pr_debug("invalid hsync total for PAL\n"); 5158c2ecf20Sopenharmony_ci return -EINVAL; 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci } else { 5188c2ecf20Sopenharmony_ci /* NTSC video output */ 5198c2ecf20Sopenharmony_ci if (hsync_total != NTSC_HTOTAL) { 5208c2ecf20Sopenharmony_ci pr_debug("invalid hsync total for NTSC\n"); 5218c2ecf20Sopenharmony_ci return -EINVAL; 5228c2ecf20Sopenharmony_ci } 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci /* Check memory sizes */ 5278c2ecf20Sopenharmony_ci line_length = get_line_length(var->xres_virtual, var->bits_per_pixel); 5288c2ecf20Sopenharmony_ci if (line_length * var->yres_virtual > info->fix.smem_len) 5298c2ecf20Sopenharmony_ci return -ENOMEM; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci return 0; 5328c2ecf20Sopenharmony_ci} 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_cistatic void pvr2_update_display(struct fb_info *info) 5358c2ecf20Sopenharmony_ci{ 5368c2ecf20Sopenharmony_ci struct pvr2fb_par *par = (struct pvr2fb_par *) info->par; 5378c2ecf20Sopenharmony_ci struct fb_var_screeninfo *var = &info->var; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci /* Update the start address of the display image */ 5408c2ecf20Sopenharmony_ci fb_writel(par->disp_start, DISP_DIWADDRL); 5418c2ecf20Sopenharmony_ci fb_writel(par->disp_start + 5428c2ecf20Sopenharmony_ci get_line_length(var->xoffset+var->xres, var->bits_per_pixel), 5438c2ecf20Sopenharmony_ci DISP_DIWADDRS); 5448c2ecf20Sopenharmony_ci} 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci/* 5478c2ecf20Sopenharmony_ci * Initialize the video mode. Currently, the 16bpp and 24bpp modes aren't 5488c2ecf20Sopenharmony_ci * very stable. It's probably due to the fact that a lot of the 2D video 5498c2ecf20Sopenharmony_ci * registers are still undocumented. 5508c2ecf20Sopenharmony_ci */ 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_cistatic void pvr2_init_display(struct fb_info *info) 5538c2ecf20Sopenharmony_ci{ 5548c2ecf20Sopenharmony_ci struct pvr2fb_par *par = (struct pvr2fb_par *) info->par; 5558c2ecf20Sopenharmony_ci struct fb_var_screeninfo *var = &info->var; 5568c2ecf20Sopenharmony_ci unsigned int diw_height, diw_width, diw_modulo = 1; 5578c2ecf20Sopenharmony_ci unsigned int bytesperpixel = var->bits_per_pixel >> 3; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci /* hsync and vsync totals */ 5608c2ecf20Sopenharmony_ci fb_writel((par->vsync_total << 16) | par->hsync_total, DISP_SYNCSIZE); 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci /* column height, modulo, row width */ 5638c2ecf20Sopenharmony_ci /* since we're "panning" within vram, we need to offset things based 5648c2ecf20Sopenharmony_ci * on the offset from the virtual x start to our real gfx. */ 5658c2ecf20Sopenharmony_ci if (video_output != VO_VGA && par->is_interlaced) 5668c2ecf20Sopenharmony_ci diw_modulo += info->fix.line_length / 4; 5678c2ecf20Sopenharmony_ci diw_height = (par->is_interlaced ? var->yres / 2 : var->yres); 5688c2ecf20Sopenharmony_ci diw_width = get_line_length(var->xres, var->bits_per_pixel) / 4; 5698c2ecf20Sopenharmony_ci fb_writel((diw_modulo << 20) | (--diw_height << 10) | --diw_width, 5708c2ecf20Sopenharmony_ci DISP_DIWSIZE); 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci /* display address, long and short fields */ 5738c2ecf20Sopenharmony_ci fb_writel(par->disp_start, DISP_DIWADDRL); 5748c2ecf20Sopenharmony_ci fb_writel(par->disp_start + 5758c2ecf20Sopenharmony_ci get_line_length(var->xoffset+var->xres, var->bits_per_pixel), 5768c2ecf20Sopenharmony_ci DISP_DIWADDRS); 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci /* border horizontal, border vertical, border color */ 5798c2ecf20Sopenharmony_ci fb_writel((par->borderstart_h << 16) | par->borderstop_h, DISP_BRDRHORZ); 5808c2ecf20Sopenharmony_ci fb_writel((par->borderstart_v << 16) | par->borderstop_v, DISP_BRDRVERT); 5818c2ecf20Sopenharmony_ci fb_writel(0, DISP_BRDRCOLR); 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci /* display window start position */ 5848c2ecf20Sopenharmony_ci fb_writel(par->diwstart_h, DISP_DIWHSTRT); 5858c2ecf20Sopenharmony_ci fb_writel((par->diwstart_v << 16) | par->diwstart_v, DISP_DIWVSTRT); 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci /* misc. settings */ 5888c2ecf20Sopenharmony_ci fb_writel((0x16 << 16) | par->is_lowres, DISP_DIWCONF); 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci /* clock doubler (for VGA), scan doubler, display enable */ 5918c2ecf20Sopenharmony_ci fb_writel(((video_output == VO_VGA) << 23) | 5928c2ecf20Sopenharmony_ci (par->is_doublescan << 1) | 1, DISP_DIWMODE); 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci /* bits per pixel */ 5958c2ecf20Sopenharmony_ci fb_writel(fb_readl(DISP_DIWMODE) | (--bytesperpixel << 2), DISP_DIWMODE); 5968c2ecf20Sopenharmony_ci fb_writel(bytesperpixel << 2, DISP_PIXDEPTH); 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci /* video enable, color sync, interlace, 5998c2ecf20Sopenharmony_ci * hsync and vsync polarity (currently unused) */ 6008c2ecf20Sopenharmony_ci fb_writel(0x100 | ((par->is_interlaced /*|4*/) << 4), DISP_SYNCCONF); 6018c2ecf20Sopenharmony_ci} 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci/* Simulate blanking by making the border cover the entire screen */ 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci#define BLANK_BIT (1<<3) 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_cistatic void pvr2_do_blank(void) 6088c2ecf20Sopenharmony_ci{ 6098c2ecf20Sopenharmony_ci struct pvr2fb_par *par = currentpar; 6108c2ecf20Sopenharmony_ci unsigned long diwconf; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci diwconf = fb_readl(DISP_DIWCONF); 6138c2ecf20Sopenharmony_ci if (do_blank > 0) 6148c2ecf20Sopenharmony_ci fb_writel(diwconf | BLANK_BIT, DISP_DIWCONF); 6158c2ecf20Sopenharmony_ci else 6168c2ecf20Sopenharmony_ci fb_writel(diwconf & ~BLANK_BIT, DISP_DIWCONF); 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci is_blanked = do_blank > 0 ? do_blank : 0; 6198c2ecf20Sopenharmony_ci} 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_cistatic irqreturn_t __maybe_unused pvr2fb_interrupt(int irq, void *dev_id) 6228c2ecf20Sopenharmony_ci{ 6238c2ecf20Sopenharmony_ci struct fb_info *info = dev_id; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci if (do_vmode_pan || do_vmode_full) 6268c2ecf20Sopenharmony_ci pvr2_update_display(info); 6278c2ecf20Sopenharmony_ci if (do_vmode_full) 6288c2ecf20Sopenharmony_ci pvr2_init_display(info); 6298c2ecf20Sopenharmony_ci if (do_vmode_pan) 6308c2ecf20Sopenharmony_ci do_vmode_pan = 0; 6318c2ecf20Sopenharmony_ci if (do_vmode_full) 6328c2ecf20Sopenharmony_ci do_vmode_full = 0; 6338c2ecf20Sopenharmony_ci if (do_blank) { 6348c2ecf20Sopenharmony_ci pvr2_do_blank(); 6358c2ecf20Sopenharmony_ci do_blank = 0; 6368c2ecf20Sopenharmony_ci } 6378c2ecf20Sopenharmony_ci return IRQ_HANDLED; 6388c2ecf20Sopenharmony_ci} 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci#ifdef CONFIG_PVR2_DMA 6418c2ecf20Sopenharmony_cistatic ssize_t pvr2fb_write(struct fb_info *info, const char *buf, 6428c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 6438c2ecf20Sopenharmony_ci{ 6448c2ecf20Sopenharmony_ci unsigned long dst, start, end, len; 6458c2ecf20Sopenharmony_ci unsigned int nr_pages; 6468c2ecf20Sopenharmony_ci struct page **pages; 6478c2ecf20Sopenharmony_ci int ret, i; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci nr_pages = (count + PAGE_SIZE - 1) >> PAGE_SHIFT; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci pages = kmalloc_array(nr_pages, sizeof(struct page *), GFP_KERNEL); 6528c2ecf20Sopenharmony_ci if (!pages) 6538c2ecf20Sopenharmony_ci return -ENOMEM; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci ret = pin_user_pages_fast((unsigned long)buf, nr_pages, FOLL_WRITE, pages); 6568c2ecf20Sopenharmony_ci if (ret < nr_pages) { 6578c2ecf20Sopenharmony_ci if (ret < 0) { 6588c2ecf20Sopenharmony_ci /* 6598c2ecf20Sopenharmony_ci * Clamp the unsigned nr_pages to zero so that the 6608c2ecf20Sopenharmony_ci * error handling works. And leave ret at whatever 6618c2ecf20Sopenharmony_ci * -errno value was returned from GUP. 6628c2ecf20Sopenharmony_ci */ 6638c2ecf20Sopenharmony_ci nr_pages = 0; 6648c2ecf20Sopenharmony_ci } else { 6658c2ecf20Sopenharmony_ci nr_pages = ret; 6668c2ecf20Sopenharmony_ci /* 6678c2ecf20Sopenharmony_ci * Use -EINVAL to represent a mildly desperate guess at 6688c2ecf20Sopenharmony_ci * why we got fewer pages (maybe even zero pages) than 6698c2ecf20Sopenharmony_ci * requested. 6708c2ecf20Sopenharmony_ci */ 6718c2ecf20Sopenharmony_ci ret = -EINVAL; 6728c2ecf20Sopenharmony_ci } 6738c2ecf20Sopenharmony_ci goto out_unmap; 6748c2ecf20Sopenharmony_ci } 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci dma_configure_channel(shdma, 0x12c1); 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci dst = (unsigned long)fb_info->screen_base + *ppos; 6798c2ecf20Sopenharmony_ci start = (unsigned long)page_address(pages[0]); 6808c2ecf20Sopenharmony_ci end = (unsigned long)page_address(pages[nr_pages]); 6818c2ecf20Sopenharmony_ci len = nr_pages << PAGE_SHIFT; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci /* Half-assed contig check */ 6848c2ecf20Sopenharmony_ci if (start + len == end) { 6858c2ecf20Sopenharmony_ci /* As we do this in one shot, it's either all or nothing.. */ 6868c2ecf20Sopenharmony_ci if ((*ppos + len) > fb_info->fix.smem_len) { 6878c2ecf20Sopenharmony_ci ret = -ENOSPC; 6888c2ecf20Sopenharmony_ci goto out_unmap; 6898c2ecf20Sopenharmony_ci } 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci dma_write(shdma, start, 0, len); 6928c2ecf20Sopenharmony_ci dma_write(pvr2dma, 0, dst, len); 6938c2ecf20Sopenharmony_ci dma_wait_for_completion(pvr2dma); 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci goto out; 6968c2ecf20Sopenharmony_ci } 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci /* Not contiguous, writeout per-page instead.. */ 6998c2ecf20Sopenharmony_ci for (i = 0; i < nr_pages; i++, dst += PAGE_SIZE) { 7008c2ecf20Sopenharmony_ci if ((*ppos + (i << PAGE_SHIFT)) > fb_info->fix.smem_len) { 7018c2ecf20Sopenharmony_ci ret = -ENOSPC; 7028c2ecf20Sopenharmony_ci goto out_unmap; 7038c2ecf20Sopenharmony_ci } 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci dma_write_page(shdma, (unsigned long)page_address(pages[i]), 0); 7068c2ecf20Sopenharmony_ci dma_write_page(pvr2dma, 0, dst); 7078c2ecf20Sopenharmony_ci dma_wait_for_completion(pvr2dma); 7088c2ecf20Sopenharmony_ci } 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ciout: 7118c2ecf20Sopenharmony_ci *ppos += count; 7128c2ecf20Sopenharmony_ci ret = count; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ciout_unmap: 7158c2ecf20Sopenharmony_ci unpin_user_pages(pages, nr_pages); 7168c2ecf20Sopenharmony_ci kfree(pages); 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci return ret; 7198c2ecf20Sopenharmony_ci} 7208c2ecf20Sopenharmony_ci#endif /* CONFIG_PVR2_DMA */ 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_cistatic const struct fb_ops pvr2fb_ops = { 7238c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 7248c2ecf20Sopenharmony_ci .fb_setcolreg = pvr2fb_setcolreg, 7258c2ecf20Sopenharmony_ci .fb_blank = pvr2fb_blank, 7268c2ecf20Sopenharmony_ci .fb_check_var = pvr2fb_check_var, 7278c2ecf20Sopenharmony_ci .fb_set_par = pvr2fb_set_par, 7288c2ecf20Sopenharmony_ci#ifdef CONFIG_PVR2_DMA 7298c2ecf20Sopenharmony_ci .fb_write = pvr2fb_write, 7308c2ecf20Sopenharmony_ci#endif 7318c2ecf20Sopenharmony_ci .fb_fillrect = cfb_fillrect, 7328c2ecf20Sopenharmony_ci .fb_copyarea = cfb_copyarea, 7338c2ecf20Sopenharmony_ci .fb_imageblit = cfb_imageblit, 7348c2ecf20Sopenharmony_ci}; 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci#ifndef MODULE 7378c2ecf20Sopenharmony_cistatic int pvr2_get_param_val(const struct pvr2_params *p, const char *s, 7388c2ecf20Sopenharmony_ci int size) 7398c2ecf20Sopenharmony_ci{ 7408c2ecf20Sopenharmony_ci int i; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci for (i = 0; i < size; i++) { 7438c2ecf20Sopenharmony_ci if (!strncasecmp(p[i].name, s, strlen(s))) 7448c2ecf20Sopenharmony_ci return p[i].val; 7458c2ecf20Sopenharmony_ci } 7468c2ecf20Sopenharmony_ci return -1; 7478c2ecf20Sopenharmony_ci} 7488c2ecf20Sopenharmony_ci#endif 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_cistatic char *pvr2_get_param_name(const struct pvr2_params *p, int val, 7518c2ecf20Sopenharmony_ci int size) 7528c2ecf20Sopenharmony_ci{ 7538c2ecf20Sopenharmony_ci int i; 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci for (i = 0; i < size; i++) { 7568c2ecf20Sopenharmony_ci if (p[i].val == val) 7578c2ecf20Sopenharmony_ci return p[i].name; 7588c2ecf20Sopenharmony_ci } 7598c2ecf20Sopenharmony_ci return NULL; 7608c2ecf20Sopenharmony_ci} 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci/** 7638c2ecf20Sopenharmony_ci * pvr2fb_common_init 7648c2ecf20Sopenharmony_ci * 7658c2ecf20Sopenharmony_ci * Common init code for the PVR2 chips. 7668c2ecf20Sopenharmony_ci * 7678c2ecf20Sopenharmony_ci * This mostly takes care of the common aspects of the fb setup and 7688c2ecf20Sopenharmony_ci * registration. It's expected that the board-specific init code has 7698c2ecf20Sopenharmony_ci * already setup pvr2_fix with something meaningful at this point. 7708c2ecf20Sopenharmony_ci * 7718c2ecf20Sopenharmony_ci * Device info reporting is also done here, as well as picking a sane 7728c2ecf20Sopenharmony_ci * default from the modedb. For board-specific modelines, simply define 7738c2ecf20Sopenharmony_ci * a per-board modedb. 7748c2ecf20Sopenharmony_ci * 7758c2ecf20Sopenharmony_ci * Also worth noting is that the cable and video output types are likely 7768c2ecf20Sopenharmony_ci * always going to be VGA for the PCI-based PVR2 boards, but we leave this 7778c2ecf20Sopenharmony_ci * in for flexibility anyways. Who knows, maybe someone has tv-out on a 7788c2ecf20Sopenharmony_ci * PCI-based version of these things ;-) 7798c2ecf20Sopenharmony_ci */ 7808c2ecf20Sopenharmony_cistatic int __maybe_unused pvr2fb_common_init(void) 7818c2ecf20Sopenharmony_ci{ 7828c2ecf20Sopenharmony_ci struct pvr2fb_par *par = currentpar; 7838c2ecf20Sopenharmony_ci unsigned long modememused, rev; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci fb_info->screen_base = ioremap(pvr2_fix.smem_start, 7868c2ecf20Sopenharmony_ci pvr2_fix.smem_len); 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci if (!fb_info->screen_base) { 7898c2ecf20Sopenharmony_ci printk(KERN_ERR "pvr2fb: Failed to remap smem space\n"); 7908c2ecf20Sopenharmony_ci goto out_err; 7918c2ecf20Sopenharmony_ci } 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci par->mmio_base = ioremap(pvr2_fix.mmio_start, 7948c2ecf20Sopenharmony_ci pvr2_fix.mmio_len); 7958c2ecf20Sopenharmony_ci if (!par->mmio_base) { 7968c2ecf20Sopenharmony_ci printk(KERN_ERR "pvr2fb: Failed to remap mmio space\n"); 7978c2ecf20Sopenharmony_ci goto out_err; 7988c2ecf20Sopenharmony_ci } 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci fb_memset(fb_info->screen_base, 0, pvr2_fix.smem_len); 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci pvr2_fix.ypanstep = nopan ? 0 : 1; 8038c2ecf20Sopenharmony_ci pvr2_fix.ywrapstep = nowrap ? 0 : 1; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci fb_info->fbops = &pvr2fb_ops; 8068c2ecf20Sopenharmony_ci fb_info->fix = pvr2_fix; 8078c2ecf20Sopenharmony_ci fb_info->par = currentpar; 8088c2ecf20Sopenharmony_ci fb_info->pseudo_palette = currentpar->palette; 8098c2ecf20Sopenharmony_ci fb_info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN; 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci if (video_output == VO_VGA) 8128c2ecf20Sopenharmony_ci defmode = DEFMODE_VGA; 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci if (!mode_option) 8158c2ecf20Sopenharmony_ci mode_option = "640x480@60"; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci if (!fb_find_mode(&fb_info->var, fb_info, mode_option, pvr2_modedb, 8188c2ecf20Sopenharmony_ci NUM_TOTAL_MODES, &pvr2_modedb[defmode], 16)) 8198c2ecf20Sopenharmony_ci fb_info->var = pvr2_var; 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci fb_alloc_cmap(&fb_info->cmap, 256, 0); 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci if (register_framebuffer(fb_info) < 0) 8248c2ecf20Sopenharmony_ci goto out_err; 8258c2ecf20Sopenharmony_ci /*Must write PIXDEPTH to register before anything is displayed - so force init */ 8268c2ecf20Sopenharmony_ci pvr2_init_display(fb_info); 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci modememused = get_line_length(fb_info->var.xres_virtual, 8298c2ecf20Sopenharmony_ci fb_info->var.bits_per_pixel); 8308c2ecf20Sopenharmony_ci modememused *= fb_info->var.yres_virtual; 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci rev = fb_readl(par->mmio_base + 0x04); 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci fb_info(fb_info, "%s (rev %ld.%ld) frame buffer device, using %ldk/%ldk of video memory\n", 8358c2ecf20Sopenharmony_ci fb_info->fix.id, (rev >> 4) & 0x0f, rev & 0x0f, 8368c2ecf20Sopenharmony_ci modememused >> 10, 8378c2ecf20Sopenharmony_ci (unsigned long)(fb_info->fix.smem_len >> 10)); 8388c2ecf20Sopenharmony_ci fb_info(fb_info, "Mode %dx%d-%d pitch = %ld cable: %s video output: %s\n", 8398c2ecf20Sopenharmony_ci fb_info->var.xres, fb_info->var.yres, 8408c2ecf20Sopenharmony_ci fb_info->var.bits_per_pixel, 8418c2ecf20Sopenharmony_ci get_line_length(fb_info->var.xres, fb_info->var.bits_per_pixel), 8428c2ecf20Sopenharmony_ci pvr2_get_param_name(cables, cable_type, 3), 8438c2ecf20Sopenharmony_ci pvr2_get_param_name(outputs, video_output, 3)); 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci#ifdef CONFIG_SH_STORE_QUEUES 8468c2ecf20Sopenharmony_ci fb_notice(fb_info, "registering with SQ API\n"); 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci pvr2fb_map = sq_remap(fb_info->fix.smem_start, fb_info->fix.smem_len, 8498c2ecf20Sopenharmony_ci fb_info->fix.id, PAGE_SHARED); 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci fb_notice(fb_info, "Mapped video memory to SQ addr 0x%lx\n", 8528c2ecf20Sopenharmony_ci pvr2fb_map); 8538c2ecf20Sopenharmony_ci#endif 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci return 0; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ciout_err: 8588c2ecf20Sopenharmony_ci if (fb_info->screen_base) 8598c2ecf20Sopenharmony_ci iounmap(fb_info->screen_base); 8608c2ecf20Sopenharmony_ci if (par->mmio_base) 8618c2ecf20Sopenharmony_ci iounmap(par->mmio_base); 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci return -ENXIO; 8648c2ecf20Sopenharmony_ci} 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci#ifdef CONFIG_SH_DREAMCAST 8678c2ecf20Sopenharmony_cistatic int __init pvr2fb_dc_init(void) 8688c2ecf20Sopenharmony_ci{ 8698c2ecf20Sopenharmony_ci if (!mach_is_dreamcast()) 8708c2ecf20Sopenharmony_ci return -ENXIO; 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci /* Make a guess at the monitor based on the attached cable */ 8738c2ecf20Sopenharmony_ci if (pvr2_init_cable() == CT_VGA) { 8748c2ecf20Sopenharmony_ci fb_info->monspecs.hfmin = 30000; 8758c2ecf20Sopenharmony_ci fb_info->monspecs.hfmax = 70000; 8768c2ecf20Sopenharmony_ci fb_info->monspecs.vfmin = 60; 8778c2ecf20Sopenharmony_ci fb_info->monspecs.vfmax = 60; 8788c2ecf20Sopenharmony_ci } else { 8798c2ecf20Sopenharmony_ci /* Not VGA, using a TV (taken from acornfb) */ 8808c2ecf20Sopenharmony_ci fb_info->monspecs.hfmin = 15469; 8818c2ecf20Sopenharmony_ci fb_info->monspecs.hfmax = 15781; 8828c2ecf20Sopenharmony_ci fb_info->monspecs.vfmin = 49; 8838c2ecf20Sopenharmony_ci fb_info->monspecs.vfmax = 51; 8848c2ecf20Sopenharmony_ci } 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci /* 8878c2ecf20Sopenharmony_ci * XXX: This needs to pull default video output via BIOS or other means 8888c2ecf20Sopenharmony_ci */ 8898c2ecf20Sopenharmony_ci if (video_output < 0) { 8908c2ecf20Sopenharmony_ci if (cable_type == CT_VGA) { 8918c2ecf20Sopenharmony_ci video_output = VO_VGA; 8928c2ecf20Sopenharmony_ci } else { 8938c2ecf20Sopenharmony_ci video_output = VO_NTSC; 8948c2ecf20Sopenharmony_ci } 8958c2ecf20Sopenharmony_ci } 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci /* 8988c2ecf20Sopenharmony_ci * Nothing exciting about the DC PVR2 .. only a measly 8MiB. 8998c2ecf20Sopenharmony_ci */ 9008c2ecf20Sopenharmony_ci pvr2_fix.smem_start = 0xa5000000; /* RAM starts here */ 9018c2ecf20Sopenharmony_ci pvr2_fix.smem_len = 8 << 20; 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci pvr2_fix.mmio_start = 0xa05f8000; /* registers start here */ 9048c2ecf20Sopenharmony_ci pvr2_fix.mmio_len = 0x2000; 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci if (request_irq(HW_EVENT_VSYNC, pvr2fb_interrupt, IRQF_SHARED, 9078c2ecf20Sopenharmony_ci "pvr2 VBL handler", fb_info)) { 9088c2ecf20Sopenharmony_ci return -EBUSY; 9098c2ecf20Sopenharmony_ci } 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci#ifdef CONFIG_PVR2_DMA 9128c2ecf20Sopenharmony_ci if (request_dma(pvr2dma, "pvr2") != 0) { 9138c2ecf20Sopenharmony_ci free_irq(HW_EVENT_VSYNC, fb_info); 9148c2ecf20Sopenharmony_ci return -EBUSY; 9158c2ecf20Sopenharmony_ci } 9168c2ecf20Sopenharmony_ci#endif 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci return pvr2fb_common_init(); 9198c2ecf20Sopenharmony_ci} 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_cistatic void pvr2fb_dc_exit(void) 9228c2ecf20Sopenharmony_ci{ 9238c2ecf20Sopenharmony_ci if (fb_info->screen_base) { 9248c2ecf20Sopenharmony_ci iounmap(fb_info->screen_base); 9258c2ecf20Sopenharmony_ci fb_info->screen_base = NULL; 9268c2ecf20Sopenharmony_ci } 9278c2ecf20Sopenharmony_ci if (currentpar->mmio_base) { 9288c2ecf20Sopenharmony_ci iounmap(currentpar->mmio_base); 9298c2ecf20Sopenharmony_ci currentpar->mmio_base = NULL; 9308c2ecf20Sopenharmony_ci } 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci free_irq(HW_EVENT_VSYNC, fb_info); 9338c2ecf20Sopenharmony_ci#ifdef CONFIG_PVR2_DMA 9348c2ecf20Sopenharmony_ci free_dma(pvr2dma); 9358c2ecf20Sopenharmony_ci#endif 9368c2ecf20Sopenharmony_ci} 9378c2ecf20Sopenharmony_ci#endif /* CONFIG_SH_DREAMCAST */ 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI 9408c2ecf20Sopenharmony_cistatic int pvr2fb_pci_probe(struct pci_dev *pdev, 9418c2ecf20Sopenharmony_ci const struct pci_device_id *ent) 9428c2ecf20Sopenharmony_ci{ 9438c2ecf20Sopenharmony_ci int ret; 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci ret = pci_enable_device(pdev); 9468c2ecf20Sopenharmony_ci if (ret) { 9478c2ecf20Sopenharmony_ci printk(KERN_ERR "pvr2fb: PCI enable failed\n"); 9488c2ecf20Sopenharmony_ci return ret; 9498c2ecf20Sopenharmony_ci } 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci ret = pci_request_regions(pdev, "pvr2fb"); 9528c2ecf20Sopenharmony_ci if (ret) { 9538c2ecf20Sopenharmony_ci printk(KERN_ERR "pvr2fb: PCI request regions failed\n"); 9548c2ecf20Sopenharmony_ci return ret; 9558c2ecf20Sopenharmony_ci } 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci /* 9588c2ecf20Sopenharmony_ci * Slightly more exciting than the DC PVR2 .. 16MiB! 9598c2ecf20Sopenharmony_ci */ 9608c2ecf20Sopenharmony_ci pvr2_fix.smem_start = pci_resource_start(pdev, 0); 9618c2ecf20Sopenharmony_ci pvr2_fix.smem_len = pci_resource_len(pdev, 0); 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci pvr2_fix.mmio_start = pci_resource_start(pdev, 1); 9648c2ecf20Sopenharmony_ci pvr2_fix.mmio_len = pci_resource_len(pdev, 1); 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci fb_info->device = &pdev->dev; 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci return pvr2fb_common_init(); 9698c2ecf20Sopenharmony_ci} 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_cistatic void pvr2fb_pci_remove(struct pci_dev *pdev) 9728c2ecf20Sopenharmony_ci{ 9738c2ecf20Sopenharmony_ci if (fb_info->screen_base) { 9748c2ecf20Sopenharmony_ci iounmap(fb_info->screen_base); 9758c2ecf20Sopenharmony_ci fb_info->screen_base = NULL; 9768c2ecf20Sopenharmony_ci } 9778c2ecf20Sopenharmony_ci if (currentpar->mmio_base) { 9788c2ecf20Sopenharmony_ci iounmap(currentpar->mmio_base); 9798c2ecf20Sopenharmony_ci currentpar->mmio_base = NULL; 9808c2ecf20Sopenharmony_ci } 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci pci_release_regions(pdev); 9838c2ecf20Sopenharmony_ci} 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_cistatic const struct pci_device_id pvr2fb_pci_tbl[] = { 9868c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_NEC, PCI_DEVICE_ID_NEC_NEON250, 9878c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, 9888c2ecf20Sopenharmony_ci { 0, }, 9898c2ecf20Sopenharmony_ci}; 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, pvr2fb_pci_tbl); 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_cistatic struct pci_driver pvr2fb_pci_driver = { 9948c2ecf20Sopenharmony_ci .name = "pvr2fb", 9958c2ecf20Sopenharmony_ci .id_table = pvr2fb_pci_tbl, 9968c2ecf20Sopenharmony_ci .probe = pvr2fb_pci_probe, 9978c2ecf20Sopenharmony_ci .remove = pvr2fb_pci_remove, 9988c2ecf20Sopenharmony_ci}; 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_cistatic int __init pvr2fb_pci_init(void) 10018c2ecf20Sopenharmony_ci{ 10028c2ecf20Sopenharmony_ci return pci_register_driver(&pvr2fb_pci_driver); 10038c2ecf20Sopenharmony_ci} 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_cistatic void pvr2fb_pci_exit(void) 10068c2ecf20Sopenharmony_ci{ 10078c2ecf20Sopenharmony_ci pci_unregister_driver(&pvr2fb_pci_driver); 10088c2ecf20Sopenharmony_ci} 10098c2ecf20Sopenharmony_ci#endif /* CONFIG_PCI */ 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci/* 10128c2ecf20Sopenharmony_ci * Parse command arguments. Supported arguments are: 10138c2ecf20Sopenharmony_ci * inverse Use inverse color maps 10148c2ecf20Sopenharmony_ci * cable:composite|rgb|vga Override the video cable type 10158c2ecf20Sopenharmony_ci * output:NTSC|PAL|VGA Override the video output format 10168c2ecf20Sopenharmony_ci * 10178c2ecf20Sopenharmony_ci * <xres>x<yres>[-<bpp>][@<refresh>] or, 10188c2ecf20Sopenharmony_ci * <name>[-<bpp>][@<refresh>] Startup using this video mode 10198c2ecf20Sopenharmony_ci */ 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci#ifndef MODULE 10228c2ecf20Sopenharmony_cistatic int __init pvr2fb_setup(char *options) 10238c2ecf20Sopenharmony_ci{ 10248c2ecf20Sopenharmony_ci char *this_opt; 10258c2ecf20Sopenharmony_ci char cable_arg[80]; 10268c2ecf20Sopenharmony_ci char output_arg[80]; 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci if (!options || !*options) 10298c2ecf20Sopenharmony_ci return 0; 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci cable_arg[0] = output_arg[0] = 0; 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci while ((this_opt = strsep(&options, ","))) { 10348c2ecf20Sopenharmony_ci if (!*this_opt) 10358c2ecf20Sopenharmony_ci continue; 10368c2ecf20Sopenharmony_ci if (!strcmp(this_opt, "inverse")) { 10378c2ecf20Sopenharmony_ci fb_invert_cmaps(); 10388c2ecf20Sopenharmony_ci } else if (!strncmp(this_opt, "cable:", 6)) { 10398c2ecf20Sopenharmony_ci strcpy(cable_arg, this_opt + 6); 10408c2ecf20Sopenharmony_ci } else if (!strncmp(this_opt, "output:", 7)) { 10418c2ecf20Sopenharmony_ci strcpy(output_arg, this_opt + 7); 10428c2ecf20Sopenharmony_ci } else if (!strncmp(this_opt, "nopan", 5)) { 10438c2ecf20Sopenharmony_ci nopan = 1; 10448c2ecf20Sopenharmony_ci } else if (!strncmp(this_opt, "nowrap", 6)) { 10458c2ecf20Sopenharmony_ci nowrap = 1; 10468c2ecf20Sopenharmony_ci } else { 10478c2ecf20Sopenharmony_ci mode_option = this_opt; 10488c2ecf20Sopenharmony_ci } 10498c2ecf20Sopenharmony_ci } 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci if (*cable_arg) 10528c2ecf20Sopenharmony_ci cable_type = pvr2_get_param_val(cables, cable_arg, 3); 10538c2ecf20Sopenharmony_ci if (*output_arg) 10548c2ecf20Sopenharmony_ci video_output = pvr2_get_param_val(outputs, output_arg, 3); 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci return 0; 10578c2ecf20Sopenharmony_ci} 10588c2ecf20Sopenharmony_ci#endif 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_cistatic struct pvr2_board { 10618c2ecf20Sopenharmony_ci int (*init)(void); 10628c2ecf20Sopenharmony_ci void (*exit)(void); 10638c2ecf20Sopenharmony_ci char name[16]; 10648c2ecf20Sopenharmony_ci} board_driver[] __refdata = { 10658c2ecf20Sopenharmony_ci#ifdef CONFIG_SH_DREAMCAST 10668c2ecf20Sopenharmony_ci { pvr2fb_dc_init, pvr2fb_dc_exit, "Sega DC PVR2" }, 10678c2ecf20Sopenharmony_ci#endif 10688c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI 10698c2ecf20Sopenharmony_ci { pvr2fb_pci_init, pvr2fb_pci_exit, "PCI PVR2" }, 10708c2ecf20Sopenharmony_ci#endif 10718c2ecf20Sopenharmony_ci { 0, }, 10728c2ecf20Sopenharmony_ci}; 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_cistatic int __init pvr2fb_init(void) 10758c2ecf20Sopenharmony_ci{ 10768c2ecf20Sopenharmony_ci int i, ret = -ENODEV; 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci#ifndef MODULE 10798c2ecf20Sopenharmony_ci char *option = NULL; 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci if (fb_get_options("pvr2fb", &option)) 10828c2ecf20Sopenharmony_ci return -ENODEV; 10838c2ecf20Sopenharmony_ci pvr2fb_setup(option); 10848c2ecf20Sopenharmony_ci#endif 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci fb_info = framebuffer_alloc(sizeof(struct pvr2fb_par), NULL); 10878c2ecf20Sopenharmony_ci if (!fb_info) 10888c2ecf20Sopenharmony_ci return -ENOMEM; 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci currentpar = fb_info->par; 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(board_driver); i++) { 10938c2ecf20Sopenharmony_ci struct pvr2_board *pvr_board = board_driver + i; 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci if (!pvr_board->init) 10968c2ecf20Sopenharmony_ci continue; 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci ret = pvr_board->init(); 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci if (ret != 0) { 11018c2ecf20Sopenharmony_ci printk(KERN_ERR "pvr2fb: Failed init of %s device\n", 11028c2ecf20Sopenharmony_ci pvr_board->name); 11038c2ecf20Sopenharmony_ci framebuffer_release(fb_info); 11048c2ecf20Sopenharmony_ci break; 11058c2ecf20Sopenharmony_ci } 11068c2ecf20Sopenharmony_ci } 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci return ret; 11098c2ecf20Sopenharmony_ci} 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_cistatic void __exit pvr2fb_exit(void) 11128c2ecf20Sopenharmony_ci{ 11138c2ecf20Sopenharmony_ci int i; 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(board_driver); i++) { 11168c2ecf20Sopenharmony_ci struct pvr2_board *pvr_board = board_driver + i; 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci if (pvr_board->exit) 11198c2ecf20Sopenharmony_ci pvr_board->exit(); 11208c2ecf20Sopenharmony_ci } 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci#ifdef CONFIG_SH_STORE_QUEUES 11238c2ecf20Sopenharmony_ci sq_unmap(pvr2fb_map); 11248c2ecf20Sopenharmony_ci#endif 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci unregister_framebuffer(fb_info); 11278c2ecf20Sopenharmony_ci framebuffer_release(fb_info); 11288c2ecf20Sopenharmony_ci} 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_cimodule_init(pvr2fb_init); 11318c2ecf20Sopenharmony_cimodule_exit(pvr2fb_exit); 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ciMODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>, M. R. Brown <mrbrown@0xd6.org>"); 11348c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Framebuffer driver for NEC PowerVR 2 based graphics boards"); 11358c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1136