2019-05-19 15:08:20 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2008-03-06 07:39:18 +03:00
/*
* drivers / video / pvr2fb . c
2005-04-17 02:20:36 +04:00
*
* Frame buffer and fbcon support for the NEC PowerVR2 found within the Sega
* Dreamcast .
*
* Copyright ( c ) 2001 M . R . Brown < mrbrown @ 0xd6 . org >
2008-03-06 07:39:18 +03:00
* Copyright ( c ) 2001 - 2008 Paul Mundt < lethal @ linux - sh . org >
2005-04-17 02:20:36 +04:00
*
* This driver is mostly based on the excellent amifb and vfb sources . It uses
* an odd scheme for converting hardware values to / from framebuffer values ,
* here are some hacked - up formulas :
*
* The Dreamcast has screen offsets from each side of its four borders and
* the start offsets of the display window . I used these values to calculate
* ' pseudo ' values ( think of them as placeholders ) for the fb video mode , so
* that when it came time to convert these values back into their hardware
* values , I could just add mode - specific offsets to get the correct mode
* settings :
*
* left_margin = diwstart_h - borderstart_h ;
* right_margin = borderstop_h - ( diwstart_h + xres ) ;
* upper_margin = diwstart_v - borderstart_v ;
* lower_margin = borderstop_v - ( diwstart_h + yres ) ;
*
* hsync_len = borderstart_h + ( hsync_total - borderstop_h ) ;
* vsync_len = borderstart_v + ( vsync_total - borderstop_v ) ;
*
* Then , when it ' s time to convert back to hardware settings , the only
* constants are the borderstart_ * offsets , all other values are derived from
* the fb video mode :
*
* // PAL
* borderstart_h = 116 ;
* borderstart_v = 44 ;
* . . .
* borderstop_h = borderstart_h + hsync_total - hsync_len ;
* . . .
* diwstart_v = borderstart_v - upper_margin ;
*
* However , in the current implementation , the borderstart values haven ' t had
* the benefit of being fully researched , so some modes may be broken .
*/
# undef DEBUG
2022-07-18 10:23:17 +03:00
# include <linux/aperture.h>
2005-04-17 02:20:36 +04:00
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/string.h>
# include <linux/mm.h>
# include <linux/slab.h>
# include <linux/delay.h>
# include <linux/interrupt.h>
# include <linux/fb.h>
# include <linux/init.h>
# include <linux/pci.h>
# ifdef CONFIG_SH_DREAMCAST
# include <asm/machvec.h>
2008-07-29 17:10:01 +04:00
# include <mach-dreamcast/mach/sysasic.h>
2005-04-17 02:20:36 +04:00
# endif
2009-03-17 03:30:36 +03:00
# ifdef CONFIG_PVR2_DMA
2005-04-17 02:20:36 +04:00
# include <linux/pagemap.h>
2008-07-29 17:10:01 +04:00
# include <mach/dma.h>
2005-04-17 02:20:36 +04:00
# include <asm/dma.h>
# endif
# ifdef CONFIG_SH_STORE_QUEUES
2007-10-16 12:29:04 +04:00
# include <linux/uaccess.h>
2008-07-29 17:10:01 +04:00
# include <cpu/sq.h>
2005-04-17 02:20:36 +04:00
# endif
# ifndef PCI_DEVICE_ID_NEC_NEON250
# define PCI_DEVICE_ID_NEC_NEON250 0x0067
# endif
/* 2D video registers */
# define DISP_BASE par->mmio_base
# define DISP_BRDRCOLR (DISP_BASE + 0x40)
# define DISP_DIWMODE (DISP_BASE + 0x44)
# define DISP_DIWADDRL (DISP_BASE + 0x50)
# define DISP_DIWADDRS (DISP_BASE + 0x54)
# define DISP_DIWSIZE (DISP_BASE + 0x5c)
# define DISP_SYNCCONF (DISP_BASE + 0xd0)
# define DISP_BRDRHORZ (DISP_BASE + 0xd4)
# define DISP_SYNCSIZE (DISP_BASE + 0xd8)
# define DISP_BRDRVERT (DISP_BASE + 0xdc)
# define DISP_DIWCONF (DISP_BASE + 0xe8)
# define DISP_DIWHSTRT (DISP_BASE + 0xec)
# define DISP_DIWVSTRT (DISP_BASE + 0xf0)
2007-08-11 00:00:48 +04:00
# define DISP_PIXDEPTH (DISP_BASE + 0x108)
2005-04-17 02:20:36 +04:00
/* Pixel clocks, one for TV output, doubled for VGA output */
# define TV_CLK 74239
# define VGA_CLK 37119
/* This is for 60Hz - the VTOTAL is doubled for interlaced modes */
# define PAL_HTOTAL 863
# define PAL_VTOTAL 312
# define NTSC_HTOTAL 857
# define NTSC_VTOTAL 262
/* Supported cable types */
enum { CT_VGA , CT_NONE , CT_RGB , CT_COMPOSITE } ;
/* Supported video output types */
enum { VO_PAL , VO_NTSC , VO_VGA } ;
/* Supported palette types */
enum { PAL_ARGB1555 , PAL_RGB565 , PAL_ARGB4444 , PAL_ARGB8888 } ;
struct pvr2_params { unsigned int val ; char * name ; } ;
2012-12-22 01:07:39 +04:00
static struct pvr2_params cables [ ] = {
2005-04-17 02:20:36 +04:00
{ CT_VGA , " VGA " } , { CT_RGB , " RGB " } , { CT_COMPOSITE , " COMPOSITE " } ,
} ;
2012-12-22 01:07:39 +04:00
static struct pvr2_params outputs [ ] = {
2005-04-17 02:20:36 +04:00
{ VO_PAL , " PAL " } , { VO_NTSC , " NTSC " } , { VO_VGA , " VGA " } ,
} ;
/*
* This describes the current video mode
*/
static struct pvr2fb_par {
unsigned int hsync_total ; /* Clocks/line */
unsigned int vsync_total ; /* Lines/field */
unsigned int borderstart_h ;
unsigned int borderstop_h ;
unsigned int borderstart_v ;
unsigned int borderstop_v ;
unsigned int diwstart_h ; /* Horizontal offset of the display field */
unsigned int diwstart_v ; /* Vertical offset of the display field, for
interlaced modes , this is the long field */
unsigned long disp_start ; /* Address of image within VRAM */
unsigned char is_interlaced ; /* Is the display interlaced? */
unsigned char is_doublescan ; /* Are scanlines output twice? (doublescan) */
unsigned char is_lowres ; /* Is horizontal pixel-doubling enabled? */
2019-06-07 15:38:07 +03:00
void __iomem * mmio_base ; /* MMIO base */
2007-08-11 00:00:47 +04:00
u32 palette [ 16 ] ;
2005-04-17 02:20:36 +04:00
} * currentpar ;
static struct fb_info * fb_info ;
2012-12-22 01:07:39 +04:00
static struct fb_fix_screeninfo pvr2_fix = {
2005-04-17 02:20:36 +04:00
. id = " NEC PowerVR2 " ,
2007-07-06 22:38:51 +04:00
. type = FB_TYPE_PACKED_PIXELS ,
. visual = FB_VISUAL_TRUECOLOR ,
2005-04-17 02:20:36 +04:00
. ypanstep = 1 ,
. ywrapstep = 1 ,
2007-07-06 22:38:51 +04:00
. accel = FB_ACCEL_NONE ,
2005-04-17 02:20:36 +04:00
} ;
2017-09-04 17:00:50 +03:00
static const struct fb_var_screeninfo pvr2_var = {
2005-04-17 02:20:36 +04:00
. xres = 640 ,
. yres = 480 ,
. xres_virtual = 640 ,
. yres_virtual = 480 ,
. bits_per_pixel = 16 ,
. red = { 11 , 5 , 0 } ,
. green = { 5 , 6 , 0 } ,
. blue = { 0 , 5 , 0 } ,
. activate = FB_ACTIVATE_NOW ,
. height = - 1 ,
. width = - 1 ,
. vmode = FB_VMODE_NONINTERLACED ,
} ;
static int cable_type = CT_VGA ;
static int video_output = VO_VGA ;
static int nopan = 0 ;
static int nowrap = 1 ;
/*
* We do all updating , blanking , etc . during the vertical retrace period
*/
static unsigned int do_vmode_full = 0 ; /* Change the video mode */
static unsigned int do_vmode_pan = 0 ; /* Update the video mode */
static short do_blank = 0 ; /* (Un)Blank the screen */
static unsigned int is_blanked = 0 ; /* Is the screen blanked? */
# ifdef CONFIG_SH_STORE_QUEUES
2006-09-27 11:03:25 +04:00
static unsigned long pvr2fb_map ;
2005-04-17 02:20:36 +04:00
# endif
2009-03-17 03:30:36 +03:00
# ifdef CONFIG_PVR2_DMA
2005-04-17 02:20:36 +04:00
static unsigned int shdma = PVR2_CASCADE_CHAN ;
static unsigned int pvr2dma = ONCHIP_NR_DMA_CHANNELS ;
# endif
2012-12-22 01:07:39 +04:00
static struct fb_videomode pvr2_modedb [ ] = {
2005-04-17 02:20:36 +04:00
/*
* Broadcast video modes ( PAL and NTSC ) . I ' m unfamiliar with
* PAL - M and PAL - N , but from what I ' ve read both modes parallel PAL and
* NTSC , so it shouldn ' t be a problem ( I hope ) .
*/
{
/* 640x480 @ 60Hz interlaced (NTSC) */
" ntsc_640x480i " , 60 , 640 , 480 , TV_CLK , 38 , 33 , 0 , 18 , 146 , 26 ,
FB_SYNC_BROADCAST , FB_VMODE_INTERLACED | FB_VMODE_YWRAP
} , {
/* 640x240 @ 60Hz (NTSC) */
/* XXX: Broken! Don't use... */
" ntsc_640x240 " , 60 , 640 , 240 , TV_CLK , 38 , 33 , 0 , 0 , 146 , 22 ,
FB_SYNC_BROADCAST , FB_VMODE_YWRAP
} , {
/* 640x480 @ 60hz (VGA) */
" vga_640x480 " , 60 , 640 , 480 , VGA_CLK , 38 , 33 , 0 , 18 , 146 , 26 ,
0 , FB_VMODE_YWRAP
2007-07-06 22:38:51 +04:00
} ,
2005-04-17 02:20:36 +04:00
} ;
# define NUM_TOTAL_MODES ARRAY_SIZE(pvr2_modedb)
# define DEFMODE_NTSC 0
# define DEFMODE_PAL 0
# define DEFMODE_VGA 2
static int defmode = DEFMODE_NTSC ;
2012-12-22 01:07:39 +04:00
static char * mode_option = NULL ;
2005-04-17 02:20:36 +04:00
static inline void pvr2fb_set_pal_type ( unsigned int type )
{
struct pvr2fb_par * par = ( struct pvr2fb_par * ) fb_info - > par ;
fb_writel ( type , par - > mmio_base + 0x108 ) ;
}
static inline void pvr2fb_set_pal_entry ( struct pvr2fb_par * par ,
unsigned int regno ,
unsigned int val )
{
fb_writel ( val , par - > mmio_base + 0x1000 + ( 4 * regno ) ) ;
}
static int pvr2fb_blank ( int blank , struct fb_info * info )
{
do_blank = blank ? blank : - 1 ;
return 0 ;
}
static inline unsigned long get_line_length ( int xres_virtual , int bpp )
{
return ( unsigned long ) ( ( ( ( xres_virtual * bpp ) + 31 ) & ~ 31 ) > > 3 ) ;
}
static void set_color_bitfields ( struct fb_var_screeninfo * var )
{
switch ( var - > bits_per_pixel ) {
case 16 : /* RGB 565 */
2007-07-06 22:38:51 +04:00
pvr2fb_set_pal_type ( PAL_RGB565 ) ;
2005-04-17 02:20:36 +04:00
var - > red . offset = 11 ; var - > red . length = 5 ;
var - > green . offset = 5 ; var - > green . length = 6 ;
var - > blue . offset = 0 ; var - > blue . length = 5 ;
var - > transp . offset = 0 ; var - > transp . length = 0 ;
break ;
case 24 : /* RGB 888 */
var - > red . offset = 16 ; var - > red . length = 8 ;
var - > green . offset = 8 ; var - > green . length = 8 ;
var - > blue . offset = 0 ; var - > blue . length = 8 ;
var - > transp . offset = 0 ; var - > transp . length = 0 ;
break ;
case 32 : /* ARGB 8888 */
2007-07-06 22:38:51 +04:00
pvr2fb_set_pal_type ( PAL_ARGB8888 ) ;
2005-04-17 02:20:36 +04:00
var - > red . offset = 16 ; var - > red . length = 8 ;
var - > green . offset = 8 ; var - > green . length = 8 ;
var - > blue . offset = 0 ; var - > blue . length = 8 ;
var - > transp . offset = 24 ; var - > transp . length = 8 ;
break ;
}
}
static int pvr2fb_setcolreg ( unsigned int regno , unsigned int red ,
unsigned int green , unsigned int blue ,
unsigned int transp , struct fb_info * info )
{
struct pvr2fb_par * par = ( struct pvr2fb_par * ) info - > par ;
unsigned int tmp ;
if ( regno > info - > cmap . len )
return 1 ;
/*
* We only support the hardware palette for 16 and 32 bpp . It ' s also
* expected that the palette format has been set by the time we get
* here , so we don ' t waste time setting it again .
*/
switch ( info - > var . bits_per_pixel ) {
case 16 : /* RGB 565 */
tmp = ( red & 0xf800 ) |
( ( green & 0xfc00 ) > > 5 ) |
( ( blue & 0xf800 ) > > 11 ) ;
pvr2fb_set_pal_entry ( par , regno , tmp ) ;
break ;
case 24 : /* RGB 888 */
red > > = 8 ; green > > = 8 ; blue > > = 8 ;
2007-07-17 15:05:39 +04:00
tmp = ( red < < 16 ) | ( green < < 8 ) | blue ;
2005-04-17 02:20:36 +04:00
break ;
case 32 : /* ARGB 8888 */
red > > = 8 ; green > > = 8 ; blue > > = 8 ;
tmp = ( transp < < 24 ) | ( red < < 16 ) | ( green < < 8 ) | blue ;
pvr2fb_set_pal_entry ( par , regno , tmp ) ;
break ;
default :
pr_debug ( " Invalid bit depth %d?!? \n " , info - > var . bits_per_pixel ) ;
return 1 ;
}
2007-07-17 15:05:39 +04:00
if ( regno < 16 )
( ( u32 * ) ( info - > pseudo_palette ) ) [ regno ] = tmp ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2019-06-07 15:38:07 +03:00
/*
* Determine the cable type and initialize the cable output format . Don ' t do
* anything if the cable type has been overidden ( via " cable:XX " ) .
*/
2019-06-07 15:38:07 +03:00
# define PCTRA ((void __iomem *)0xff80002c)
# define PDTRA ((void __iomem *)0xff800030)
# define VOUTC ((void __iomem *)0xa0702c00)
2019-06-07 15:38:07 +03:00
static int pvr2_init_cable ( void )
{
if ( cable_type < 0 ) {
fb_writel ( ( fb_readl ( PCTRA ) & 0xfff0ffff ) | 0x000a0000 ,
PCTRA ) ;
cable_type = ( fb_readw ( PDTRA ) > > 8 ) & 3 ;
}
/* Now select the output format (either composite or other) */
/* XXX: Save the previous val first, as this reg is also AICA
related */
if ( cable_type = = CT_COMPOSITE )
fb_writel ( 3 < < 8 , VOUTC ) ;
else if ( cable_type = = CT_RGB )
fb_writel ( 1 < < 9 , VOUTC ) ;
else
fb_writel ( 0 , VOUTC ) ;
return cable_type ;
}
2005-04-17 02:20:36 +04:00
static int pvr2fb_set_par ( struct fb_info * info )
{
struct pvr2fb_par * par = ( struct pvr2fb_par * ) info - > par ;
struct fb_var_screeninfo * var = & info - > var ;
unsigned long line_length ;
unsigned int vtotal ;
/*
* XXX : It ' s possible that a user could use a VGA box , change the cable
* type in hardware ( i . e . switch from VGA < - > composite ) , then change
* modes ( i . e . switching to another VT ) . If that happens we should
* automagically change the output format to cope , but currently I
* don ' t have a VGA box to make sure this works properly .
*/
cable_type = pvr2_init_cable ( ) ;
if ( cable_type = = CT_VGA & & video_output ! = VO_VGA )
video_output = VO_VGA ;
var - > vmode & = FB_VMODE_MASK ;
if ( var - > vmode & FB_VMODE_INTERLACED & & video_output ! = VO_VGA )
par - > is_interlaced = 1 ;
2007-07-06 22:38:51 +04:00
/*
2005-04-17 02:20:36 +04:00
* XXX : Need to be more creative with this ( i . e . allow doublecan for
* PAL / NTSC output ) .
*/
if ( var - > vmode & FB_VMODE_DOUBLE & & video_output = = VO_VGA )
par - > is_doublescan = 1 ;
2007-07-06 22:38:51 +04:00
2005-04-17 02:20:36 +04:00
par - > hsync_total = var - > left_margin + var - > xres + var - > right_margin +
var - > hsync_len ;
par - > vsync_total = var - > upper_margin + var - > yres + var - > lower_margin +
var - > vsync_len ;
if ( var - > sync & FB_SYNC_BROADCAST ) {
vtotal = par - > vsync_total ;
if ( par - > is_interlaced )
vtotal / = 2 ;
if ( vtotal > ( PAL_VTOTAL + NTSC_VTOTAL ) / 2 ) {
/* XXX: Check for start values here... */
/* XXX: Check hardware for PAL-compatibility */
par - > borderstart_h = 116 ;
par - > borderstart_v = 44 ;
} else {
/* NTSC video output */
par - > borderstart_h = 126 ;
par - > borderstart_v = 18 ;
}
} else {
/* VGA mode */
/* XXX: What else needs to be checked? */
2007-07-06 22:38:51 +04:00
/*
2005-04-17 02:20:36 +04:00
* XXX : We have a little freedom in VGA modes , what ranges
* should be here ( i . e . hsync / vsync totals , etc . ) ?
*/
par - > borderstart_h = 126 ;
par - > borderstart_v = 40 ;
}
/* Calculate the remainding offsets */
par - > diwstart_h = par - > borderstart_h + var - > left_margin ;
par - > diwstart_v = par - > borderstart_v + var - > upper_margin ;
2007-07-06 22:38:51 +04:00
par - > borderstop_h = par - > diwstart_h + var - > xres +
var - > right_margin ;
2005-04-17 02:20:36 +04:00
par - > borderstop_v = par - > diwstart_v + var - > yres +
var - > lower_margin ;
if ( ! par - > is_interlaced )
par - > borderstop_v / = 2 ;
if ( info - > var . xres < 640 )
par - > is_lowres = 1 ;
line_length = get_line_length ( var - > xres_virtual , var - > bits_per_pixel ) ;
par - > disp_start = info - > fix . smem_start + ( line_length * var - > yoffset ) * line_length ;
info - > fix . line_length = line_length ;
return 0 ;
}
static int pvr2fb_check_var ( struct fb_var_screeninfo * var , struct fb_info * info )
{
struct pvr2fb_par * par = ( struct pvr2fb_par * ) info - > par ;
unsigned int vtotal , hsync_total ;
unsigned long line_length ;
if ( var - > pixclock ! = TV_CLK & & var - > pixclock ! = VGA_CLK ) {
pr_debug ( " Invalid pixclock value %d \n " , var - > pixclock ) ;
return - EINVAL ;
}
if ( var - > xres < 320 )
var - > xres = 320 ;
if ( var - > yres < 240 )
var - > yres = 240 ;
if ( var - > xres_virtual < var - > xres )
var - > xres_virtual = var - > xres ;
if ( var - > yres_virtual < var - > yres )
var - > yres_virtual = var - > yres ;
if ( var - > bits_per_pixel < = 16 )
var - > bits_per_pixel = 16 ;
else if ( var - > bits_per_pixel < = 24 )
var - > bits_per_pixel = 24 ;
else if ( var - > bits_per_pixel < = 32 )
var - > bits_per_pixel = 32 ;
set_color_bitfields ( var ) ;
if ( var - > vmode & FB_VMODE_YWRAP ) {
2019-07-22 23:33:58 +03:00
if ( var - > xoffset | | var - > yoffset > = var - > yres_virtual ) {
2005-04-17 02:20:36 +04:00
var - > xoffset = var - > yoffset = 0 ;
} else {
if ( var - > xoffset > var - > xres_virtual - var - > xres | |
2019-07-22 23:33:58 +03:00
var - > yoffset > var - > yres_virtual - var - > yres )
2005-04-17 02:20:36 +04:00
var - > xoffset = var - > yoffset = 0 ;
}
} else {
var - > xoffset = var - > yoffset = 0 ;
}
2007-07-06 22:38:51 +04:00
/*
2005-04-17 02:20:36 +04:00
* XXX : Need to be more creative with this ( i . e . allow doublecan for
* PAL / NTSC output ) .
*/
if ( var - > yres < 480 & & video_output = = VO_VGA )
var - > vmode | = FB_VMODE_DOUBLE ;
if ( video_output ! = VO_VGA ) {
var - > sync | = FB_SYNC_BROADCAST ;
var - > vmode | = FB_VMODE_INTERLACED ;
} else {
var - > sync & = ~ FB_SYNC_BROADCAST ;
var - > vmode & = ~ FB_VMODE_INTERLACED ;
2008-03-06 07:39:18 +03:00
var - > vmode | = FB_VMODE_NONINTERLACED ;
2005-04-17 02:20:36 +04:00
}
if ( ( var - > activate & FB_ACTIVATE_MASK ) ! = FB_ACTIVATE_TEST ) {
var - > right_margin = par - > borderstop_h -
( par - > diwstart_h + var - > xres ) ;
var - > left_margin = par - > diwstart_h - par - > borderstart_h ;
var - > hsync_len = par - > borderstart_h +
( par - > hsync_total - par - > borderstop_h ) ;
var - > upper_margin = par - > diwstart_v - par - > borderstart_v ;
var - > lower_margin = par - > borderstop_v -
( par - > diwstart_v + var - > yres ) ;
var - > vsync_len = par - > borderstop_v +
( par - > vsync_total - par - > borderstop_v ) ;
}
2007-07-06 22:38:51 +04:00
2005-04-17 02:20:36 +04:00
hsync_total = var - > left_margin + var - > xres + var - > right_margin +
var - > hsync_len ;
vtotal = var - > upper_margin + var - > yres + var - > lower_margin +
var - > vsync_len ;
if ( var - > sync & FB_SYNC_BROADCAST ) {
if ( var - > vmode & FB_VMODE_INTERLACED )
vtotal / = 2 ;
if ( vtotal > ( PAL_VTOTAL + NTSC_VTOTAL ) / 2 ) {
/* PAL video output */
/* XXX: Should be using a range here ... ? */
if ( hsync_total ! = PAL_HTOTAL ) {
pr_debug ( " invalid hsync total for PAL \n " ) ;
return - EINVAL ;
}
} else {
/* NTSC video output */
if ( hsync_total ! = NTSC_HTOTAL ) {
pr_debug ( " invalid hsync total for NTSC \n " ) ;
return - EINVAL ;
}
}
}
2007-07-06 22:38:51 +04:00
2005-04-17 02:20:36 +04:00
/* Check memory sizes */
line_length = get_line_length ( var - > xres_virtual , var - > bits_per_pixel ) ;
if ( line_length * var - > yres_virtual > info - > fix . smem_len )
return - ENOMEM ;
return 0 ;
}
static void pvr2_update_display ( struct fb_info * info )
{
struct pvr2fb_par * par = ( struct pvr2fb_par * ) info - > par ;
struct fb_var_screeninfo * var = & info - > var ;
/* Update the start address of the display image */
fb_writel ( par - > disp_start , DISP_DIWADDRL ) ;
fb_writel ( par - > disp_start +
get_line_length ( var - > xoffset + var - > xres , var - > bits_per_pixel ) ,
DISP_DIWADDRS ) ;
}
2007-07-06 22:38:51 +04:00
/*
2005-04-17 02:20:36 +04:00
* Initialize the video mode . Currently , the 16 bpp and 24 bpp modes aren ' t
* very stable . It ' s probably due to the fact that a lot of the 2 D video
* registers are still undocumented .
*/
static void pvr2_init_display ( struct fb_info * info )
{
struct pvr2fb_par * par = ( struct pvr2fb_par * ) info - > par ;
struct fb_var_screeninfo * var = & info - > var ;
unsigned int diw_height , diw_width , diw_modulo = 1 ;
unsigned int bytesperpixel = var - > bits_per_pixel > > 3 ;
/* hsync and vsync totals */
fb_writel ( ( par - > vsync_total < < 16 ) | par - > hsync_total , DISP_SYNCSIZE ) ;
/* column height, modulo, row width */
/* since we're "panning" within vram, we need to offset things based
* on the offset from the virtual x start to our real gfx . */
if ( video_output ! = VO_VGA & & par - > is_interlaced )
diw_modulo + = info - > fix . line_length / 4 ;
diw_height = ( par - > is_interlaced ? var - > yres / 2 : var - > yres ) ;
diw_width = get_line_length ( var - > xres , var - > bits_per_pixel ) / 4 ;
fb_writel ( ( diw_modulo < < 20 ) | ( - - diw_height < < 10 ) | - - diw_width ,
DISP_DIWSIZE ) ;
/* display address, long and short fields */
fb_writel ( par - > disp_start , DISP_DIWADDRL ) ;
fb_writel ( par - > disp_start +
get_line_length ( var - > xoffset + var - > xres , var - > bits_per_pixel ) ,
DISP_DIWADDRS ) ;
/* border horizontal, border vertical, border color */
fb_writel ( ( par - > borderstart_h < < 16 ) | par - > borderstop_h , DISP_BRDRHORZ ) ;
fb_writel ( ( par - > borderstart_v < < 16 ) | par - > borderstop_v , DISP_BRDRVERT ) ;
fb_writel ( 0 , DISP_BRDRCOLR ) ;
/* display window start position */
fb_writel ( par - > diwstart_h , DISP_DIWHSTRT ) ;
fb_writel ( ( par - > diwstart_v < < 16 ) | par - > diwstart_v , DISP_DIWVSTRT ) ;
2007-07-06 22:38:51 +04:00
2005-04-17 02:20:36 +04:00
/* misc. settings */
fb_writel ( ( 0x16 < < 16 ) | par - > is_lowres , DISP_DIWCONF ) ;
/* clock doubler (for VGA), scan doubler, display enable */
2007-07-06 22:38:51 +04:00
fb_writel ( ( ( video_output = = VO_VGA ) < < 23 ) |
2005-04-17 02:20:36 +04:00
( par - > is_doublescan < < 1 ) | 1 , DISP_DIWMODE ) ;
/* bits per pixel */
fb_writel ( fb_readl ( DISP_DIWMODE ) | ( - - bytesperpixel < < 2 ) , DISP_DIWMODE ) ;
2007-08-11 00:00:48 +04:00
fb_writel ( bytesperpixel < < 2 , DISP_PIXDEPTH ) ;
2005-04-17 02:20:36 +04:00
2007-07-06 22:38:51 +04:00
/* video enable, color sync, interlace,
2005-04-17 02:20:36 +04:00
* hsync and vsync polarity ( currently unused ) */
fb_writel ( 0x100 | ( ( par - > is_interlaced /*|4*/ ) < < 4 ) , DISP_SYNCCONF ) ;
}
/* Simulate blanking by making the border cover the entire screen */
# define BLANK_BIT (1<<3)
static void pvr2_do_blank ( void )
{
struct pvr2fb_par * par = currentpar ;
unsigned long diwconf ;
diwconf = fb_readl ( DISP_DIWCONF ) ;
if ( do_blank > 0 )
fb_writel ( diwconf | BLANK_BIT , DISP_DIWCONF ) ;
else
fb_writel ( diwconf & ~ BLANK_BIT , DISP_DIWCONF ) ;
is_blanked = do_blank > 0 ? do_blank : 0 ;
}
2019-06-07 15:38:07 +03:00
static irqreturn_t __maybe_unused pvr2fb_interrupt ( int irq , void * dev_id )
2005-04-17 02:20:36 +04:00
{
struct fb_info * info = dev_id ;
if ( do_vmode_pan | | do_vmode_full )
pvr2_update_display ( info ) ;
if ( do_vmode_full )
pvr2_init_display ( info ) ;
if ( do_vmode_pan )
do_vmode_pan = 0 ;
if ( do_vmode_full )
do_vmode_full = 0 ;
if ( do_blank ) {
pvr2_do_blank ( ) ;
do_blank = 0 ;
}
return IRQ_HANDLED ;
}
2009-03-17 03:30:36 +03:00
# ifdef CONFIG_PVR2_DMA
2007-05-08 11:39:02 +04:00
static ssize_t pvr2fb_write ( struct fb_info * info , const char * buf ,
2005-04-17 02:20:36 +04:00
size_t count , loff_t * ppos )
{
unsigned long dst , start , end , len ;
unsigned int nr_pages ;
struct page * * pages ;
int ret , i ;
nr_pages = ( count + PAGE_SIZE - 1 ) > > PAGE_SHIFT ;
treewide: kmalloc() -> kmalloc_array()
The kmalloc() function has a 2-factor argument form, kmalloc_array(). This
patch replaces cases of:
kmalloc(a * b, gfp)
with:
kmalloc_array(a * b, gfp)
as well as handling cases of:
kmalloc(a * b * c, gfp)
with:
kmalloc(array3_size(a, b, c), gfp)
as it's slightly less ugly than:
kmalloc_array(array_size(a, b), c, gfp)
This does, however, attempt to ignore constant size factors like:
kmalloc(4 * 1024, gfp)
though any constants defined via macros get caught up in the conversion.
Any factors with a sizeof() of "unsigned char", "char", and "u8" were
dropped, since they're redundant.
The tools/ directory was manually excluded, since it has its own
implementation of kmalloc().
The Coccinelle script used for this was:
// Fix redundant parens around sizeof().
@@
type TYPE;
expression THING, E;
@@
(
kmalloc(
- (sizeof(TYPE)) * E
+ sizeof(TYPE) * E
, ...)
|
kmalloc(
- (sizeof(THING)) * E
+ sizeof(THING) * E
, ...)
)
// Drop single-byte sizes and redundant parens.
@@
expression COUNT;
typedef u8;
typedef __u8;
@@
(
kmalloc(
- sizeof(u8) * (COUNT)
+ COUNT
, ...)
|
kmalloc(
- sizeof(__u8) * (COUNT)
+ COUNT
, ...)
|
kmalloc(
- sizeof(char) * (COUNT)
+ COUNT
, ...)
|
kmalloc(
- sizeof(unsigned char) * (COUNT)
+ COUNT
, ...)
|
kmalloc(
- sizeof(u8) * COUNT
+ COUNT
, ...)
|
kmalloc(
- sizeof(__u8) * COUNT
+ COUNT
, ...)
|
kmalloc(
- sizeof(char) * COUNT
+ COUNT
, ...)
|
kmalloc(
- sizeof(unsigned char) * COUNT
+ COUNT
, ...)
)
// 2-factor product with sizeof(type/expression) and identifier or constant.
@@
type TYPE;
expression THING;
identifier COUNT_ID;
constant COUNT_CONST;
@@
(
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * (COUNT_ID)
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * COUNT_ID
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * (COUNT_CONST)
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * COUNT_CONST
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * (COUNT_ID)
+ COUNT_ID, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * COUNT_ID
+ COUNT_ID, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * (COUNT_CONST)
+ COUNT_CONST, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * COUNT_CONST
+ COUNT_CONST, sizeof(THING)
, ...)
)
// 2-factor product, only identifiers.
@@
identifier SIZE, COUNT;
@@
- kmalloc
+ kmalloc_array
(
- SIZE * COUNT
+ COUNT, SIZE
, ...)
// 3-factor product with 1 sizeof(type) or sizeof(expression), with
// redundant parens removed.
@@
expression THING;
identifier STRIDE, COUNT;
type TYPE;
@@
(
kmalloc(
- sizeof(TYPE) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kmalloc(
- sizeof(TYPE) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kmalloc(
- sizeof(TYPE) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kmalloc(
- sizeof(TYPE) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kmalloc(
- sizeof(THING) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kmalloc(
- sizeof(THING) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kmalloc(
- sizeof(THING) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kmalloc(
- sizeof(THING) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
)
// 3-factor product with 2 sizeof(variable), with redundant parens removed.
@@
expression THING1, THING2;
identifier COUNT;
type TYPE1, TYPE2;
@@
(
kmalloc(
- sizeof(TYPE1) * sizeof(TYPE2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
kmalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
kmalloc(
- sizeof(THING1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
kmalloc(
- sizeof(THING1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
kmalloc(
- sizeof(TYPE1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
|
kmalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
)
// 3-factor product, only identifiers, with redundant parens removed.
@@
identifier STRIDE, SIZE, COUNT;
@@
(
kmalloc(
- (COUNT) * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- COUNT * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- COUNT * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- (COUNT) * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- COUNT * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- (COUNT) * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- (COUNT) * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- COUNT * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
)
// Any remaining multi-factor products, first at least 3-factor products,
// when they're not all constants...
@@
expression E1, E2, E3;
constant C1, C2, C3;
@@
(
kmalloc(C1 * C2 * C3, ...)
|
kmalloc(
- (E1) * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
|
kmalloc(
- (E1) * (E2) * E3
+ array3_size(E1, E2, E3)
, ...)
|
kmalloc(
- (E1) * (E2) * (E3)
+ array3_size(E1, E2, E3)
, ...)
|
kmalloc(
- E1 * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
)
// And then all remaining 2 factors products when they're not all constants,
// keeping sizeof() as the second factor argument.
@@
expression THING, E1, E2;
type TYPE;
constant C1, C2, C3;
@@
(
kmalloc(sizeof(THING) * C2, ...)
|
kmalloc(sizeof(TYPE) * C2, ...)
|
kmalloc(C1 * C2 * C3, ...)
|
kmalloc(C1 * C2, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * (E2)
+ E2, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * E2
+ E2, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * (E2)
+ E2, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * E2
+ E2, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- (E1) * E2
+ E1, E2
, ...)
|
- kmalloc
+ kmalloc_array
(
- (E1) * (E2)
+ E1, E2
, ...)
|
- kmalloc
+ kmalloc_array
(
- E1 * E2
+ E1, E2
, ...)
)
Signed-off-by: Kees Cook <keescook@chromium.org>
2018-06-12 23:55:00 +03:00
pages = kmalloc_array ( nr_pages , sizeof ( struct page * ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! pages )
return - ENOMEM ;
2007-07-06 22:38:51 +04:00
2020-05-22 07:15:06 +03:00
ret = pin_user_pages_fast ( ( unsigned long ) buf , nr_pages , FOLL_WRITE , pages ) ;
2005-04-17 02:20:36 +04:00
if ( ret < nr_pages ) {
video: fbdev: fix error handling for get_user_pages_fast()
Dealing with the return value of get_user_pages*() variants has a few
classic pitfalls, and this driver found one of them: the return value
might be zero, positive, or -errno. And if positive, it might be fewer
pages than were requested. And if fewer pages than requested, then
the caller should return (via put_page()) the pages that *were*
pinned.
This driver was doing that *except* that it had a problem with the
-errno case, which was being stored in an unsigned int, and which
would case an interesting mess if it ever happened: nr_pages would be
interpreted as a spectacularly huge unsigned value, rather than a
small negative value. Also, it was unnecessarily overriding a
potentially informative -errno, with -EINVAL, in some cases.
Instead: clamp the nr_pages to zero or positive, so that the error
handling works. And return the -errno value from get_user_pages*(),
unchanged, if we get one. And explain this with comments, seeing as
how it is error-prone.
Cc: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Cc: Gustavo A. R. Silva <gustavo@embeddedor.com>
Cc: Jani Nikula <jani.nikula@intel.com>
Cc: dri-devel@lists.freedesktop.org
Cc: linux-fbdev@vger.kernel.org
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
Signed-off-by: Sam Ravnborg <sam@ravnborg.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20200522041506.39638-2-jhubbard@nvidia.com
2020-05-22 07:15:05 +03:00
if ( ret < 0 ) {
/*
* Clamp the unsigned nr_pages to zero so that the
* error handling works . And leave ret at whatever
* - errno value was returned from GUP .
*/
nr_pages = 0 ;
} else {
nr_pages = ret ;
/*
* Use - EINVAL to represent a mildly desperate guess at
* why we got fewer pages ( maybe even zero pages ) than
* requested .
*/
ret = - EINVAL ;
}
2005-04-17 02:20:36 +04:00
goto out_unmap ;
}
dma_configure_channel ( shdma , 0x12c1 ) ;
2007-07-06 22:38:51 +04:00
2005-04-17 02:20:36 +04:00
dst = ( unsigned long ) fb_info - > screen_base + * ppos ;
start = ( unsigned long ) page_address ( pages [ 0 ] ) ;
end = ( unsigned long ) page_address ( pages [ nr_pages ] ) ;
len = nr_pages < < PAGE_SHIFT ;
/* Half-assed contig check */
if ( start + len = = end ) {
/* As we do this in one shot, it's either all or nothing.. */
if ( ( * ppos + len ) > fb_info - > fix . smem_len ) {
ret = - ENOSPC ;
goto out_unmap ;
}
dma_write ( shdma , start , 0 , len ) ;
dma_write ( pvr2dma , 0 , dst , len ) ;
dma_wait_for_completion ( pvr2dma ) ;
goto out ;
}
/* Not contiguous, writeout per-page instead.. */
for ( i = 0 ; i < nr_pages ; i + + , dst + = PAGE_SIZE ) {
if ( ( * ppos + ( i < < PAGE_SHIFT ) ) > fb_info - > fix . smem_len ) {
ret = - ENOSPC ;
goto out_unmap ;
}
dma_write_page ( shdma , ( unsigned long ) page_address ( pages [ i ] ) , 0 ) ;
dma_write_page ( pvr2dma , 0 , dst ) ;
dma_wait_for_completion ( pvr2dma ) ;
}
out :
* ppos + = count ;
ret = count ;
out_unmap :
2020-05-22 07:15:06 +03:00
unpin_user_pages ( pages , nr_pages ) ;
2005-04-17 02:20:36 +04:00
kfree ( pages ) ;
return ret ;
2007-07-06 22:38:51 +04:00
}
2009-03-17 03:30:36 +03:00
# endif /* CONFIG_PVR2_DMA */
2005-04-17 02:20:36 +04:00
2019-12-03 19:38:50 +03:00
static const struct fb_ops pvr2fb_ops = {
2019-06-07 15:38:07 +03:00
. owner = THIS_MODULE ,
. fb_setcolreg = pvr2fb_setcolreg ,
. fb_blank = pvr2fb_blank ,
. fb_check_var = pvr2fb_check_var ,
. fb_set_par = pvr2fb_set_par ,
# ifdef CONFIG_PVR2_DMA
. fb_write = pvr2fb_write ,
# endif
. fb_fillrect = cfb_fillrect ,
. fb_copyarea = cfb_copyarea ,
. fb_imageblit = cfb_imageblit ,
} ;
2019-06-21 14:04:38 +03:00
# ifndef MODULE
2019-06-07 15:38:07 +03:00
static int pvr2_get_param_val ( const struct pvr2_params * p , const char * s ,
int size )
2019-06-07 15:38:07 +03:00
{
int i ;
2019-06-07 15:38:07 +03:00
for ( i = 0 ; i < size ; i + + ) {
if ( ! strncasecmp ( p [ i ] . name , s , strlen ( s ) ) )
return p [ i ] . val ;
2019-06-07 15:38:07 +03:00
}
return - 1 ;
}
2019-06-21 14:04:38 +03:00
# endif
2019-06-07 15:38:07 +03:00
2019-06-07 15:38:07 +03:00
static char * pvr2_get_param_name ( const struct pvr2_params * p , int val ,
int size )
{
int i ;
for ( i = 0 ; i < size ; i + + ) {
if ( p [ i ] . val = = val )
return p [ i ] . name ;
}
return NULL ;
}
2005-04-17 02:20:36 +04:00
/**
* pvr2fb_common_init
*
* Common init code for the PVR2 chips .
*
* This mostly takes care of the common aspects of the fb setup and
* registration . It ' s expected that the board - specific init code has
* already setup pvr2_fix with something meaningful at this point .
*
* Device info reporting is also done here , as well as picking a sane
* default from the modedb . For board - specific modelines , simply define
* a per - board modedb .
*
* Also worth noting is that the cable and video output types are likely
* always going to be VGA for the PCI - based PVR2 boards , but we leave this
* in for flexibility anyways . Who knows , maybe someone has tv - out on a
* PCI - based version of these things ; - )
*/
2019-06-07 15:38:07 +03:00
static int __maybe_unused pvr2fb_common_init ( void )
2005-04-17 02:20:36 +04:00
{
struct pvr2fb_par * par = currentpar ;
unsigned long modememused , rev ;
2020-01-06 11:43:50 +03:00
fb_info - > screen_base = ioremap ( pvr2_fix . smem_start ,
2005-04-17 02:20:36 +04:00
pvr2_fix . smem_len ) ;
2007-07-06 22:38:51 +04:00
2005-04-17 02:20:36 +04:00
if ( ! fb_info - > screen_base ) {
printk ( KERN_ERR " pvr2fb: Failed to remap smem space \n " ) ;
goto out_err ;
}
2020-01-06 11:43:50 +03:00
par - > mmio_base = ioremap ( pvr2_fix . mmio_start ,
2019-06-07 15:38:07 +03:00
pvr2_fix . mmio_len ) ;
2005-04-17 02:20:36 +04:00
if ( ! par - > mmio_base ) {
printk ( KERN_ERR " pvr2fb: Failed to remap mmio space \n " ) ;
goto out_err ;
}
2006-09-27 11:03:25 +04:00
fb_memset ( fb_info - > screen_base , 0 , pvr2_fix . smem_len ) ;
2005-04-17 02:20:36 +04:00
pvr2_fix . ypanstep = nopan ? 0 : 1 ;
pvr2_fix . ywrapstep = nowrap ? 0 : 1 ;
fb_info - > fbops = & pvr2fb_ops ;
fb_info - > fix = pvr2_fix ;
fb_info - > par = currentpar ;
2007-08-11 00:00:47 +04:00
fb_info - > pseudo_palette = currentpar - > palette ;
2005-04-17 02:20:36 +04:00
fb_info - > flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN ;
if ( video_output = = VO_VGA )
defmode = DEFMODE_VGA ;
if ( ! mode_option )
mode_option = " 640x480@60 " ;
if ( ! fb_find_mode ( & fb_info - > var , fb_info , mode_option , pvr2_modedb ,
NUM_TOTAL_MODES , & pvr2_modedb [ defmode ] , 16 ) )
fb_info - > var = pvr2_var ;
fb_alloc_cmap ( & fb_info - > cmap , 256 , 0 ) ;
if ( register_framebuffer ( fb_info ) < 0 )
goto out_err ;
2007-08-11 00:00:48 +04:00
/*Must write PIXDEPTH to register before anything is displayed - so force init */
pvr2_init_display ( fb_info ) ;
2005-04-17 02:20:36 +04:00
modememused = get_line_length ( fb_info - > var . xres_virtual ,
fb_info - > var . bits_per_pixel ) ;
modememused * = fb_info - > var . yres_virtual ;
rev = fb_readl ( par - > mmio_base + 0x04 ) ;
2013-09-20 05:35:55 +04:00
fb_info ( fb_info , " %s (rev %ld.%ld) frame buffer device, using %ldk/%ldk of video memory \n " ,
fb_info - > fix . id , ( rev > > 4 ) & 0x0f , rev & 0x0f ,
modememused > > 10 ,
( unsigned long ) ( fb_info - > fix . smem_len > > 10 ) ) ;
fb_info ( fb_info , " Mode %dx%d-%d pitch = %ld cable: %s video output: %s \n " ,
fb_info - > var . xres , fb_info - > var . yres ,
fb_info - > var . bits_per_pixel ,
get_line_length ( fb_info - > var . xres , fb_info - > var . bits_per_pixel ) ,
2019-06-07 15:38:07 +03:00
pvr2_get_param_name ( cables , cable_type , 3 ) ,
pvr2_get_param_name ( outputs , video_output , 3 ) ) ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_SH_STORE_QUEUES
2013-09-20 05:35:55 +04:00
fb_notice ( fb_info , " registering with SQ API \n " ) ;
2005-04-17 02:20:36 +04:00
pvr2fb_map = sq_remap ( fb_info - > fix . smem_start , fb_info - > fix . smem_len ,
2010-02-17 07:23:00 +03:00
fb_info - > fix . id , PAGE_SHARED ) ;
2005-04-17 02:20:36 +04:00
2013-09-20 05:35:55 +04:00
fb_notice ( fb_info , " Mapped video memory to SQ addr 0x%lx \n " ,
pvr2fb_map ) ;
2005-04-17 02:20:36 +04:00
# endif
return 0 ;
out_err :
if ( fb_info - > screen_base )
iounmap ( fb_info - > screen_base ) ;
if ( par - > mmio_base )
2019-06-07 15:38:07 +03:00
iounmap ( par - > mmio_base ) ;
2005-04-17 02:20:36 +04:00
return - ENXIO ;
}
# ifdef CONFIG_SH_DREAMCAST
static int __init pvr2fb_dc_init ( void )
{
if ( ! mach_is_dreamcast ( ) )
return - ENXIO ;
/* Make a guess at the monitor based on the attached cable */
if ( pvr2_init_cable ( ) = = CT_VGA ) {
fb_info - > monspecs . hfmin = 30000 ;
fb_info - > monspecs . hfmax = 70000 ;
fb_info - > monspecs . vfmin = 60 ;
fb_info - > monspecs . vfmax = 60 ;
} else {
/* Not VGA, using a TV (taken from acornfb) */
fb_info - > monspecs . hfmin = 15469 ;
fb_info - > monspecs . hfmax = 15781 ;
fb_info - > monspecs . vfmin = 49 ;
fb_info - > monspecs . vfmax = 51 ;
}
/*
* XXX : This needs to pull default video output via BIOS or other means
*/
if ( video_output < 0 ) {
if ( cable_type = = CT_VGA ) {
video_output = VO_VGA ;
} else {
video_output = VO_NTSC ;
}
}
2007-07-06 22:38:51 +04:00
/*
2005-04-17 02:20:36 +04:00
* Nothing exciting about the DC PVR2 . . only a measly 8 MiB .
*/
pvr2_fix . smem_start = 0xa5000000 ; /* RAM starts here */
pvr2_fix . smem_len = 8 < < 20 ;
pvr2_fix . mmio_start = 0xa05f8000 ; /* registers start here */
pvr2_fix . mmio_len = 0x2000 ;
2007-09-10 07:01:42 +04:00
if ( request_irq ( HW_EVENT_VSYNC , pvr2fb_interrupt , IRQF_SHARED ,
2005-04-17 02:20:36 +04:00
" pvr2 VBL handler " , fb_info ) ) {
return - EBUSY ;
}
2009-03-17 03:30:36 +03:00
# ifdef CONFIG_PVR2_DMA
2005-04-17 02:20:36 +04:00
if ( request_dma ( pvr2dma , " pvr2 " ) ! = 0 ) {
2012-03-11 23:36:21 +04:00
free_irq ( HW_EVENT_VSYNC , fb_info ) ;
2005-04-17 02:20:36 +04:00
return - EBUSY ;
}
# endif
return pvr2fb_common_init ( ) ;
}
2019-06-21 16:13:13 +03:00
static void pvr2fb_dc_exit ( void )
2005-04-17 02:20:36 +04:00
{
2006-12-08 13:40:06 +03:00
if ( fb_info - > screen_base ) {
iounmap ( fb_info - > screen_base ) ;
fb_info - > screen_base = NULL ;
}
if ( currentpar - > mmio_base ) {
2019-06-07 15:38:07 +03:00
iounmap ( currentpar - > mmio_base ) ;
currentpar - > mmio_base = NULL ;
2006-12-08 13:40:06 +03:00
}
2012-03-11 23:36:21 +04:00
free_irq ( HW_EVENT_VSYNC , fb_info ) ;
2009-03-17 03:30:36 +03:00
# ifdef CONFIG_PVR2_DMA
2005-04-17 02:20:36 +04:00
free_dma ( pvr2dma ) ;
# endif
}
# endif /* CONFIG_SH_DREAMCAST */
# ifdef CONFIG_PCI
2012-12-22 01:07:39 +04:00
static int pvr2fb_pci_probe ( struct pci_dev * pdev ,
const struct pci_device_id * ent )
2005-04-17 02:20:36 +04:00
{
int ret ;
2022-07-18 10:23:17 +03:00
ret = aperture_remove_conflicting_pci_devices ( pdev , " pvrfb " ) ;
if ( ret )
return ret ;
2005-04-17 02:20:36 +04:00
ret = pci_enable_device ( pdev ) ;
if ( ret ) {
printk ( KERN_ERR " pvr2fb: PCI enable failed \n " ) ;
return ret ;
}
ret = pci_request_regions ( pdev , " pvr2fb " ) ;
if ( ret ) {
printk ( KERN_ERR " pvr2fb: PCI request regions failed \n " ) ;
return ret ;
}
/*
* Slightly more exciting than the DC PVR2 . . 16 MiB !
*/
pvr2_fix . smem_start = pci_resource_start ( pdev , 0 ) ;
pvr2_fix . smem_len = pci_resource_len ( pdev , 0 ) ;
pvr2_fix . mmio_start = pci_resource_start ( pdev , 1 ) ;
pvr2_fix . mmio_len = pci_resource_len ( pdev , 1 ) ;
fb_info - > device = & pdev - > dev ;
return pvr2fb_common_init ( ) ;
}
2012-12-22 01:07:39 +04:00
static void pvr2fb_pci_remove ( struct pci_dev * pdev )
2005-04-17 02:20:36 +04:00
{
2006-12-08 13:40:06 +03:00
if ( fb_info - > screen_base ) {
iounmap ( fb_info - > screen_base ) ;
fb_info - > screen_base = NULL ;
}
if ( currentpar - > mmio_base ) {
2019-06-07 15:38:07 +03:00
iounmap ( currentpar - > mmio_base ) ;
currentpar - > mmio_base = NULL ;
2006-12-08 13:40:06 +03:00
}
2005-04-17 02:20:36 +04:00
pci_release_regions ( pdev ) ;
}
2017-08-01 18:20:41 +03:00
static const struct pci_device_id pvr2fb_pci_tbl [ ] = {
2005-04-17 02:20:36 +04:00
{ PCI_VENDOR_ID_NEC , PCI_DEVICE_ID_NEC_NEON250 ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , 0 } ,
{ 0 , } ,
} ;
MODULE_DEVICE_TABLE ( pci , pvr2fb_pci_tbl ) ;
static struct pci_driver pvr2fb_pci_driver = {
. name = " pvr2fb " ,
. id_table = pvr2fb_pci_tbl ,
. probe = pvr2fb_pci_probe ,
2012-12-22 01:07:39 +04:00
. remove = pvr2fb_pci_remove ,
2005-04-17 02:20:36 +04:00
} ;
static int __init pvr2fb_pci_init ( void )
{
return pci_register_driver ( & pvr2fb_pci_driver ) ;
}
2019-06-21 16:13:13 +03:00
static void pvr2fb_pci_exit ( void )
2005-04-17 02:20:36 +04:00
{
pci_unregister_driver ( & pvr2fb_pci_driver ) ;
}
# endif /* CONFIG_PCI */
/*
* Parse command arguments . Supported arguments are :
* inverse Use inverse color maps
* cable : composite | rgb | vga Override the video cable type
* output : NTSC | PAL | VGA Override the video output format
*
* < xres > x < yres > [ - < bpp > ] [ @ < refresh > ] or ,
* < name > [ - < bpp > ] [ @ < refresh > ] Startup using this video mode
*/
# ifndef MODULE
2007-07-06 22:38:51 +04:00
static int __init pvr2fb_setup ( char * options )
2005-04-17 02:20:36 +04:00
{
char * this_opt ;
char cable_arg [ 80 ] ;
char output_arg [ 80 ] ;
if ( ! options | | ! * options )
return 0 ;
2020-07-20 22:18:45 +03:00
cable_arg [ 0 ] = output_arg [ 0 ] = 0 ;
2005-04-17 02:20:36 +04:00
while ( ( this_opt = strsep ( & options , " , " ) ) ) {
if ( ! * this_opt )
continue ;
if ( ! strcmp ( this_opt , " inverse " ) ) {
fb_invert_cmaps ( ) ;
} else if ( ! strncmp ( this_opt , " cable: " , 6 ) ) {
strcpy ( cable_arg , this_opt + 6 ) ;
} else if ( ! strncmp ( this_opt , " output: " , 7 ) ) {
strcpy ( output_arg , this_opt + 7 ) ;
} else if ( ! strncmp ( this_opt , " nopan " , 5 ) ) {
nopan = 1 ;
} else if ( ! strncmp ( this_opt , " nowrap " , 6 ) ) {
nowrap = 1 ;
} else {
mode_option = this_opt ;
}
}
if ( * cable_arg )
2019-06-07 15:38:07 +03:00
cable_type = pvr2_get_param_val ( cables , cable_arg , 3 ) ;
2005-04-17 02:20:36 +04:00
if ( * output_arg )
2019-06-07 15:38:07 +03:00
video_output = pvr2_get_param_val ( outputs , output_arg , 3 ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
# endif
static struct pvr2_board {
int ( * init ) ( void ) ;
void ( * exit ) ( void ) ;
char name [ 16 ] ;
2012-01-13 11:42:50 +04:00
} board_driver [ ] __refdata = {
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_SH_DREAMCAST
{ pvr2fb_dc_init , pvr2fb_dc_exit , " Sega DC PVR2 " } ,
# endif
# ifdef CONFIG_PCI
{ pvr2fb_pci_init , pvr2fb_pci_exit , " PCI PVR2 " } ,
# endif
{ 0 , } ,
} ;
2007-07-06 22:38:51 +04:00
static int __init pvr2fb_init ( void )
2005-04-17 02:20:36 +04:00
{
int i , ret = - ENODEV ;
# ifndef MODULE
char * option = NULL ;
if ( fb_get_options ( " pvr2fb " , & option ) )
return - ENODEV ;
pvr2fb_setup ( option ) ;
# endif
2007-08-11 00:00:47 +04:00
fb_info = framebuffer_alloc ( sizeof ( struct pvr2fb_par ) , NULL ) ;
2019-06-28 13:30:08 +03:00
if ( ! fb_info )
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
2007-08-11 00:00:47 +04:00
currentpar = fb_info - > par ;
2005-04-17 02:20:36 +04:00
2007-07-06 22:38:51 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( board_driver ) ; i + + ) {
struct pvr2_board * pvr_board = board_driver + i ;
2005-04-17 02:20:36 +04:00
if ( ! pvr_board - > init )
continue ;
ret = pvr_board - > init ( ) ;
if ( ret ! = 0 ) {
printk ( KERN_ERR " pvr2fb: Failed init of %s device \n " ,
pvr_board - > name ) ;
2007-08-11 00:00:47 +04:00
framebuffer_release ( fb_info ) ;
2005-04-17 02:20:36 +04:00
break ;
}
}
return ret ;
}
static void __exit pvr2fb_exit ( void )
{
int i ;
2007-07-06 22:38:51 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( board_driver ) ; i + + ) {
struct pvr2_board * pvr_board = board_driver + i ;
2005-04-17 02:20:36 +04:00
if ( pvr_board - > exit )
pvr_board - > exit ( ) ;
}
2007-07-06 22:38:51 +04:00
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_SH_STORE_QUEUES
sq_unmap ( pvr2fb_map ) ;
# endif
unregister_framebuffer ( fb_info ) ;
2007-08-11 00:00:47 +04:00
framebuffer_release ( fb_info ) ;
2005-04-17 02:20:36 +04:00
}
module_init ( pvr2fb_init ) ;
module_exit ( pvr2fb_exit ) ;
MODULE_AUTHOR ( " Paul Mundt <lethal@linux-sh.org>, M. R. Brown <mrbrown@0xd6.org> " ) ;
MODULE_DESCRIPTION ( " Framebuffer driver for NEC PowerVR 2 based graphics boards " ) ;
MODULE_LICENSE ( " GPL " ) ;