2005-04-17 02:20:36 +04:00
/*
* linux / drivers / video / offb . c - - Open Firmware based frame buffer device
*
* Copyright ( C ) 1997 Geert Uytterhoeven
*
* This driver is partly based on the PowerMac console driver :
*
* Copyright ( C ) 1996 Paul Mackerras
*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file COPYING in the main directory of this archive for
* more details .
*/
# include <linux/config.h>
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/string.h>
# include <linux/mm.h>
# include <linux/tty.h>
# include <linux/slab.h>
# include <linux/vmalloc.h>
# include <linux/delay.h>
# include <linux/interrupt.h>
# include <linux/fb.h>
# include <linux/init.h>
# include <linux/ioport.h>
2005-11-18 08:41:49 +03:00
# include <linux/pci.h>
2005-04-17 02:20:36 +04:00
# include <asm/io.h>
# include <asm/prom.h>
# ifdef CONFIG_PPC64
# include <asm/pci-bridge.h>
# endif
# ifdef CONFIG_PPC32
# include <asm/bootx.h>
# endif
# include "macmodes.h"
/* Supported palette hacks */
enum {
cmap_unknown ,
cmap_m64 , /* ATI Mach64 */
cmap_r128 , /* ATI Rage128 */
cmap_M3A , /* ATI Rage Mobility M3 Head A */
cmap_M3B , /* ATI Rage Mobility M3 Head B */
cmap_radeon , /* ATI Radeon */
cmap_gxt2000 , /* IBM GXT2000 */
} ;
struct offb_par {
volatile void __iomem * cmap_adr ;
volatile void __iomem * cmap_data ;
int cmap_type ;
int blanked ;
} ;
struct offb_par default_par ;
/*
* Interface used by the world
*/
int offb_init ( void ) ;
static int offb_setcolreg ( u_int regno , u_int red , u_int green , u_int blue ,
u_int transp , struct fb_info * info ) ;
static int offb_blank ( int blank , struct fb_info * info ) ;
# ifdef CONFIG_PPC32
extern boot_infos_t * boot_infos ;
# endif
static void offb_init_nodriver ( struct device_node * ) ;
static void offb_init_fb ( const char * name , const char * full_name ,
int width , int height , int depth , int pitch ,
unsigned long address , struct device_node * dp ) ;
static struct fb_ops offb_ops = {
. owner = THIS_MODULE ,
. fb_setcolreg = offb_setcolreg ,
. fb_blank = offb_blank ,
. fb_fillrect = cfb_fillrect ,
. fb_copyarea = cfb_copyarea ,
. fb_imageblit = cfb_imageblit ,
} ;
/*
* Set a single color register . The values supplied are already
* rounded down to the hardware ' s capabilities ( according to the
* entries in the var structure ) . Return ! = 0 for invalid regno .
*/
static int offb_setcolreg ( u_int regno , u_int red , u_int green , u_int blue ,
u_int transp , struct fb_info * info )
{
struct offb_par * par = ( struct offb_par * ) info - > par ;
if ( ! par - > cmap_adr | | regno > 255 )
return 1 ;
red > > = 8 ;
green > > = 8 ;
blue > > = 8 ;
switch ( par - > cmap_type ) {
case cmap_m64 :
writeb ( regno , par - > cmap_adr ) ;
writeb ( red , par - > cmap_data ) ;
writeb ( green , par - > cmap_data ) ;
writeb ( blue , par - > cmap_data ) ;
break ;
case cmap_M3A :
/* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */
out_le32 ( par - > cmap_adr + 0x58 ,
in_le32 ( par - > cmap_adr + 0x58 ) & ~ 0x20 ) ;
case cmap_r128 :
/* Set palette index & data */
out_8 ( par - > cmap_adr + 0xb0 , regno ) ;
out_le32 ( par - > cmap_adr + 0xb4 ,
( red < < 16 | green < < 8 | blue ) ) ;
break ;
case cmap_M3B :
/* Set PALETTE_ACCESS_CNTL in DAC_CNTL */
out_le32 ( par - > cmap_adr + 0x58 ,
in_le32 ( par - > cmap_adr + 0x58 ) | 0x20 ) ;
/* Set palette index & data */
out_8 ( par - > cmap_adr + 0xb0 , regno ) ;
out_le32 ( par - > cmap_adr + 0xb4 , ( red < < 16 | green < < 8 | blue ) ) ;
break ;
case cmap_radeon :
/* Set palette index & data (could be smarter) */
out_8 ( par - > cmap_adr + 0xb0 , regno ) ;
out_le32 ( par - > cmap_adr + 0xb4 , ( red < < 16 | green < < 8 | blue ) ) ;
break ;
case cmap_gxt2000 :
out_le32 ( ( unsigned __iomem * ) par - > cmap_adr + regno ,
( red < < 16 | green < < 8 | blue ) ) ;
break ;
}
if ( regno < 16 )
switch ( info - > var . bits_per_pixel ) {
case 16 :
( ( u16 * ) ( info - > pseudo_palette ) ) [ regno ] =
( regno < < 10 ) | ( regno < < 5 ) | regno ;
break ;
case 32 :
{
int i = ( regno < < 8 ) | regno ;
( ( u32 * ) ( info - > pseudo_palette ) ) [ regno ] =
( i < < 16 ) | i ;
break ;
}
}
return 0 ;
}
/*
* Blank the display .
*/
static int offb_blank ( int blank , struct fb_info * info )
{
struct offb_par * par = ( struct offb_par * ) info - > par ;
int i , j ;
if ( ! par - > cmap_adr )
return 0 ;
if ( ! par - > blanked )
if ( ! blank )
return 0 ;
par - > blanked = blank ;
if ( blank )
for ( i = 0 ; i < 256 ; i + + ) {
switch ( par - > cmap_type ) {
case cmap_m64 :
writeb ( i , par - > cmap_adr ) ;
for ( j = 0 ; j < 3 ; j + + )
writeb ( 0 , par - > cmap_data ) ;
break ;
case cmap_M3A :
/* Clear PALETTE_ACCESS_CNTL in DAC_CNTL */
out_le32 ( par - > cmap_adr + 0x58 ,
in_le32 ( par - > cmap_adr + 0x58 ) & ~ 0x20 ) ;
case cmap_r128 :
/* Set palette index & data */
out_8 ( par - > cmap_adr + 0xb0 , i ) ;
out_le32 ( par - > cmap_adr + 0xb4 , 0 ) ;
break ;
case cmap_M3B :
/* Set PALETTE_ACCESS_CNTL in DAC_CNTL */
out_le32 ( par - > cmap_adr + 0x58 ,
in_le32 ( par - > cmap_adr + 0x58 ) | 0x20 ) ;
/* Set palette index & data */
out_8 ( par - > cmap_adr + 0xb0 , i ) ;
out_le32 ( par - > cmap_adr + 0xb4 , 0 ) ;
break ;
case cmap_radeon :
out_8 ( par - > cmap_adr + 0xb0 , i ) ;
out_le32 ( par - > cmap_adr + 0xb4 , 0 ) ;
break ;
case cmap_gxt2000 :
out_le32 ( ( unsigned __iomem * ) par - > cmap_adr + i ,
0 ) ;
break ;
}
} else
fb_set_cmap ( & info - > cmap , info ) ;
return 0 ;
}
/*
* Initialisation
*/
int __init offb_init ( void )
{
struct device_node * dp = NULL , * boot_disp = NULL ;
2005-12-13 10:01:21 +03:00
2005-04-17 02:20:36 +04:00
# if defined(CONFIG_BOOTX_TEXT) && defined(CONFIG_PPC32)
struct device_node * macos_display = NULL ;
# endif
if ( fb_get_options ( " offb " , NULL ) )
return - ENODEV ;
# if defined(CONFIG_BOOTX_TEXT) && defined(CONFIG_PPC32)
/* If we're booted from BootX... */
if ( boot_infos ! = 0 ) {
unsigned long addr =
( unsigned long ) boot_infos - > dispDeviceBase ;
2005-12-13 10:01:21 +03:00
u32 * addrp ;
u64 daddr , dsize ;
unsigned int flags ;
2005-04-17 02:20:36 +04:00
/* find the device node corresponding to the macos display */
while ( ( dp = of_find_node_by_type ( dp , " display " ) ) ) {
int i ;
/*
2005-12-13 10:01:21 +03:00
* Look for an AAPL , address property first .
2005-04-17 02:20:36 +04:00
*/
2005-12-13 10:01:21 +03:00
unsigned int na ;
unsigned int * ap =
( unsigned int * ) get_property ( dp , " AAPL,address " ,
& na ) ;
if ( ap ! = 0 ) {
for ( na / = sizeof ( unsigned int ) ; na > 0 ;
- - na , + + ap )
if ( * ap < = addr & &
addr < * ap + 0x1000000 ) {
macos_display = dp ;
goto foundit ;
}
2005-04-17 02:20:36 +04:00
}
/*
* See if the display address is in one of the address
* ranges for this display .
*/
2005-12-13 10:01:21 +03:00
i = 0 ;
for ( ; ; ) {
addrp = of_get_address ( dp , i + + , & dsize , & flags ) ;
if ( addrp = = NULL )
2005-04-17 02:20:36 +04:00
break ;
2005-12-13 10:01:21 +03:00
if ( ! ( flags & IORESOURCE_MEM ) )
continue ;
daddr = of_translate_address ( dp , addrp ) ;
if ( daddr = = OF_BAD_ADDR )
continue ;
if ( daddr < = addr & & addr < ( daddr + dsize ) ) {
macos_display = dp ;
goto foundit ;
}
2005-04-17 02:20:36 +04:00
}
2005-12-13 10:01:21 +03:00
foundit :
if ( macos_display ) {
2005-04-17 02:20:36 +04:00
printk ( KERN_INFO " MacOS display is %s \n " ,
dp - > full_name ) ;
break ;
}
}
/* initialize it */
offb_init_fb ( macos_display ? macos_display - >
name : " MacOS display " ,
macos_display ? macos_display - >
full_name : " MacOS display " ,
boot_infos - > dispDeviceRect [ 2 ] ,
boot_infos - > dispDeviceRect [ 3 ] ,
boot_infos - > dispDeviceDepth ,
boot_infos - > dispDeviceRowBytes , addr , NULL ) ;
}
# endif /* defined(CONFIG_BOOTX_TEXT) && defined(CONFIG_PPC32) */
for ( dp = NULL ; ( dp = of_find_node_by_type ( dp , " display " ) ) ; ) {
if ( get_property ( dp , " linux,opened " , NULL ) & &
get_property ( dp , " linux,boot-display " , NULL ) ) {
boot_disp = dp ;
offb_init_nodriver ( dp ) ;
}
}
for ( dp = NULL ; ( dp = of_find_node_by_type ( dp , " display " ) ) ; ) {
if ( get_property ( dp , " linux,opened " , NULL ) & &
dp ! = boot_disp )
offb_init_nodriver ( dp ) ;
}
return 0 ;
}
static void __init offb_init_nodriver ( struct device_node * dp )
{
int * pp , i ;
unsigned int len ;
int width = 640 , height = 480 , depth = 8 , pitch ;
2005-12-13 10:01:21 +03:00
unsigned int flags , rsize , * up ;
u64 address = OF_BAD_ADDR ;
u32 * addrp ;
u64 asize ;
2005-04-17 02:20:36 +04:00
if ( ( pp = ( int * ) get_property ( dp , " depth " , & len ) ) ! = NULL
& & len = = sizeof ( int ) )
depth = * pp ;
if ( ( pp = ( int * ) get_property ( dp , " width " , & len ) ) ! = NULL
& & len = = sizeof ( int ) )
width = * pp ;
if ( ( pp = ( int * ) get_property ( dp , " height " , & len ) ) ! = NULL
& & len = = sizeof ( int ) )
height = * pp ;
if ( ( pp = ( int * ) get_property ( dp , " linebytes " , & len ) ) ! = NULL
& & len = = sizeof ( int ) ) {
pitch = * pp ;
if ( pitch = = 1 )
pitch = 0x1000 ;
} else
pitch = width ;
2005-11-17 05:34:57 +03:00
rsize = ( unsigned long ) pitch * ( unsigned long ) height *
( unsigned long ) ( depth / 8 ) ;
/* Try to match device to a PCI device in order to get a properly
* translated address rather then trying to decode the open firmware
* stuff in various incorrect ways
*/
# ifdef CONFIG_PCI
/* First try to locate the PCI device if any */
{
struct pci_dev * pdev = NULL ;
for_each_pci_dev ( pdev ) {
if ( dp = = pci_device_to_OF_node ( pdev ) )
break ;
}
if ( pdev ) {
2005-12-13 10:01:21 +03:00
for ( i = 0 ; i < 6 & & address = = OF_BAD_ADDR ; i + + ) {
2005-11-17 05:34:57 +03:00
if ( ( pci_resource_flags ( pdev , i ) &
IORESOURCE_MEM ) & &
( pci_resource_len ( pdev , i ) > = rsize ) )
address = pci_resource_start ( pdev , i ) ;
}
pci_dev_put ( pdev ) ;
}
}
# endif /* CONFIG_PCI */
2005-12-13 10:01:21 +03:00
/* This one is dodgy, we may drop it ... */
if ( address = = OF_BAD_ADDR & &
( up = ( unsigned * ) get_property ( dp , " address " , & len ) ) ! = NULL & &
len = = sizeof ( unsigned int ) )
address = ( u64 ) * up ;
if ( address = = OF_BAD_ADDR ) {
for ( i = 0 ; ( addrp = of_get_address ( dp , i , & asize , & flags ) )
! = NULL ; i + + ) {
if ( ! ( flags & IORESOURCE_MEM ) )
continue ;
if ( asize > = pitch * height * depth / 8 )
break ;
}
if ( addrp = = NULL ) {
2005-04-17 02:20:36 +04:00
printk ( KERN_ERR
" no framebuffer address found for %s \n " ,
dp - > full_name ) ;
return ;
}
2005-12-13 10:01:21 +03:00
address = of_translate_address ( dp , addrp ) ;
if ( address = = OF_BAD_ADDR ) {
printk ( KERN_ERR
" can't translate framebuffer address for %s \n " ,
dp - > full_name ) ;
return ;
}
2005-04-17 02:20:36 +04:00
/* kludge for valkyrie */
if ( strcmp ( dp - > name , " valkyrie " ) = = 0 )
address + = 0x1000 ;
}
offb_init_fb ( dp - > name , dp - > full_name , width , height , depth ,
pitch , address , dp ) ;
}
static void __init offb_init_fb ( const char * name , const char * full_name ,
int width , int height , int depth ,
int pitch , unsigned long address ,
struct device_node * dp )
{
unsigned long res_size = pitch * height * depth / 8 ;
struct offb_par * par = & default_par ;
unsigned long res_start = address ;
struct fb_fix_screeninfo * fix ;
struct fb_var_screeninfo * var ;
struct fb_info * info ;
int size ;
if ( ! request_mem_region ( res_start , res_size , " offb " ) )
return ;
printk ( KERN_INFO
" Using unsupported %dx%d %s at %lx, depth=%d, pitch=%d \n " ,
width , height , name , address , depth , pitch ) ;
if ( depth ! = 8 & & depth ! = 16 & & depth ! = 32 ) {
printk ( KERN_ERR " %s: can't use depth = %d \n " , full_name ,
depth ) ;
release_mem_region ( res_start , res_size ) ;
return ;
}
size = sizeof ( struct fb_info ) + sizeof ( u32 ) * 17 ;
info = kmalloc ( size , GFP_ATOMIC ) ;
if ( info = = 0 ) {
release_mem_region ( res_start , res_size ) ;
return ;
}
memset ( info , 0 , size ) ;
fix = & info - > fix ;
var = & info - > var ;
strcpy ( fix - > id , " OFfb " ) ;
strncat ( fix - > id , name , sizeof ( fix - > id ) - sizeof ( " OFfb " ) ) ;
fix - > id [ sizeof ( fix - > id ) - 1 ] = ' \0 ' ;
var - > xres = var - > xres_virtual = width ;
var - > yres = var - > yres_virtual = height ;
fix - > line_length = pitch ;
fix - > smem_start = address ;
fix - > smem_len = pitch * height ;
fix - > type = FB_TYPE_PACKED_PIXELS ;
fix - > type_aux = 0 ;
par - > cmap_type = cmap_unknown ;
if ( depth = = 8 ) {
2005-12-13 10:01:21 +03:00
/* Palette hacks disabled for now */
#if 0
2005-04-17 02:20:36 +04:00
if ( dp & & ! strncmp ( name , " ATY,Rage128 " , 11 ) ) {
unsigned long regbase = dp - > addrs [ 2 ] . address ;
par - > cmap_adr = ioremap ( regbase , 0x1FFF ) ;
par - > cmap_type = cmap_r128 ;
} else if ( dp & & ( ! strncmp ( name , " ATY,RageM3pA " , 12 )
| | ! strncmp ( name , " ATY,RageM3p12A " , 14 ) ) ) {
unsigned long regbase =
dp - > parent - > addrs [ 2 ] . address ;
par - > cmap_adr = ioremap ( regbase , 0x1FFF ) ;
par - > cmap_type = cmap_M3A ;
} else if ( dp & & ! strncmp ( name , " ATY,RageM3pB " , 12 ) ) {
unsigned long regbase =
dp - > parent - > addrs [ 2 ] . address ;
par - > cmap_adr = ioremap ( regbase , 0x1FFF ) ;
par - > cmap_type = cmap_M3B ;
} else if ( dp & & ! strncmp ( name , " ATY,Rage6 " , 9 ) ) {
unsigned long regbase = dp - > addrs [ 1 ] . address ;
par - > cmap_adr = ioremap ( regbase , 0x1FFF ) ;
par - > cmap_type = cmap_radeon ;
} else if ( ! strncmp ( name , " ATY, " , 4 ) ) {
unsigned long base = address & 0xff000000UL ;
par - > cmap_adr =
ioremap ( base + 0x7ff000 , 0x1000 ) + 0xcc0 ;
par - > cmap_data = par - > cmap_adr + 1 ;
par - > cmap_type = cmap_m64 ;
} else if ( device_is_compatible ( dp , " pci1014,b7 " ) ) {
unsigned long regbase = dp - > addrs [ 0 ] . address ;
par - > cmap_adr = ioremap ( regbase + 0x6000 , 0x1000 ) ;
par - > cmap_type = cmap_gxt2000 ;
}
2005-12-13 10:01:21 +03:00
# endif
2005-04-17 02:20:36 +04:00
fix - > visual = par - > cmap_adr ? FB_VISUAL_PSEUDOCOLOR
: FB_VISUAL_STATIC_PSEUDOCOLOR ;
} else
fix - > visual = /* par->cmap_adr ? FB_VISUAL_DIRECTCOLOR
: */ FB_VISUAL_TRUECOLOR ;
var - > xoffset = var - > yoffset = 0 ;
var - > bits_per_pixel = depth ;
switch ( depth ) {
case 8 :
var - > bits_per_pixel = 8 ;
var - > red . offset = 0 ;
var - > red . length = 8 ;
var - > green . offset = 0 ;
var - > green . length = 8 ;
var - > blue . offset = 0 ;
var - > blue . length = 8 ;
var - > transp . offset = 0 ;
var - > transp . length = 0 ;
break ;
case 16 : /* RGB 555 */
var - > bits_per_pixel = 16 ;
var - > red . offset = 10 ;
var - > red . length = 5 ;
var - > green . offset = 5 ;
var - > green . length = 5 ;
var - > blue . offset = 0 ;
var - > blue . length = 5 ;
var - > transp . offset = 0 ;
var - > transp . length = 0 ;
break ;
case 32 : /* RGB 888 */
var - > bits_per_pixel = 32 ;
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 ;
}
var - > red . msb_right = var - > green . msb_right = var - > blue . msb_right =
var - > transp . msb_right = 0 ;
var - > grayscale = 0 ;
var - > nonstd = 0 ;
var - > activate = 0 ;
var - > height = var - > width = - 1 ;
var - > pixclock = 10000 ;
var - > left_margin = var - > right_margin = 16 ;
var - > upper_margin = var - > lower_margin = 16 ;
var - > hsync_len = var - > vsync_len = 8 ;
var - > sync = 0 ;
var - > vmode = FB_VMODE_NONINTERLACED ;
info - > fbops = & offb_ops ;
info - > screen_base = ioremap ( address , fix - > smem_len ) ;
info - > par = par ;
info - > pseudo_palette = ( void * ) ( info + 1 ) ;
info - > flags = FBINFO_DEFAULT ;
fb_alloc_cmap ( & info - > cmap , 256 , 0 ) ;
if ( register_framebuffer ( info ) < 0 ) {
kfree ( info ) ;
release_mem_region ( res_start , res_size ) ;
return ;
}
printk ( KERN_INFO " fb%d: Open Firmware frame buffer device on %s \n " ,
info - > node , full_name ) ;
}
module_init ( offb_init ) ;
MODULE_LICENSE ( " GPL " ) ;