2005-04-17 02:20:36 +04:00
/* leo.c: LEO frame buffer driver
*
* Copyright ( C ) 2003 David S . Miller ( davem @ redhat . com )
* Copyright ( C ) 1996 - 1999 Jakub Jelinek ( jj @ ultra . linux . cz )
* Copyright ( C ) 1997 Michal Rehacek ( Michal . Rehacek @ st . mff . cuni . cz )
*
* Driver layout based loosely on tgafb . c , see that file for credits .
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/string.h>
# include <linux/slab.h>
# include <linux/delay.h>
# include <linux/init.h>
# include <linux/fb.h>
# include <linux/mm.h>
# include <asm/io.h>
# include <asm/sbus.h>
# include <asm/oplib.h>
# include <asm/fbio.h>
# include "sbuslib.h"
/*
* Local functions .
*/
static int leo_setcolreg ( unsigned , unsigned , unsigned , unsigned ,
unsigned , struct fb_info * ) ;
static int leo_blank ( int , struct fb_info * ) ;
2006-01-15 00:21:25 +03:00
static int leo_mmap ( struct fb_info * , struct vm_area_struct * ) ;
2006-01-15 00:21:25 +03:00
static int leo_ioctl ( struct fb_info * , unsigned int , unsigned long ) ;
2005-04-17 02:20:36 +04:00
static int leo_pan_display ( struct fb_var_screeninfo * , struct fb_info * ) ;
/*
* Frame buffer operations
*/
static struct fb_ops leo_ops = {
. owner = THIS_MODULE ,
. fb_setcolreg = leo_setcolreg ,
. fb_blank = leo_blank ,
. fb_pan_display = leo_pan_display ,
. fb_fillrect = cfb_fillrect ,
. fb_copyarea = cfb_copyarea ,
. fb_imageblit = cfb_imageblit ,
. fb_mmap = leo_mmap ,
. fb_ioctl = leo_ioctl ,
2005-11-12 23:11:12 +03:00
# ifdef CONFIG_COMPAT
. fb_compat_ioctl = sbusfb_compat_ioctl ,
# endif
2005-04-17 02:20:36 +04:00
} ;
# define LEO_OFF_LC_SS0_KRN 0x00200000UL
# define LEO_OFF_LC_SS0_USR 0x00201000UL
# define LEO_OFF_LC_SS1_KRN 0x01200000UL
# define LEO_OFF_LC_SS1_USR 0x01201000UL
# define LEO_OFF_LD_SS0 0x00400000UL
# define LEO_OFF_LD_SS1 0x01400000UL
# define LEO_OFF_LD_GBL 0x00401000UL
# define LEO_OFF_LX_KRN 0x00600000UL
# define LEO_OFF_LX_CURSOR 0x00601000UL
# define LEO_OFF_SS0 0x00800000UL
# define LEO_OFF_SS1 0x01800000UL
# define LEO_OFF_UNK 0x00602000UL
# define LEO_OFF_UNK2 0x00000000UL
# define LEO_CUR_ENABLE 0x00000080
# define LEO_CUR_UPDATE 0x00000030
# define LEO_CUR_PROGRESS 0x00000006
# define LEO_CUR_UPDATECMAP 0x00000003
# define LEO_CUR_TYPE_MASK 0x00000000
# define LEO_CUR_TYPE_IMAGE 0x00000020
# define LEO_CUR_TYPE_CMAP 0x00000050
struct leo_cursor {
u8 xxx0 [ 16 ] ;
volatile u32 cur_type ;
volatile u32 cur_misc ;
volatile u32 cur_cursxy ;
volatile u32 cur_data ;
} ;
# define LEO_KRN_TYPE_CLUT0 0x00001000
# define LEO_KRN_TYPE_CLUT1 0x00001001
# define LEO_KRN_TYPE_CLUT2 0x00001002
# define LEO_KRN_TYPE_WID 0x00001003
# define LEO_KRN_TYPE_UNK 0x00001006
# define LEO_KRN_TYPE_VIDEO 0x00002003
# define LEO_KRN_TYPE_CLUTDATA 0x00004000
# define LEO_KRN_CSR_ENABLE 0x00000008
# define LEO_KRN_CSR_PROGRESS 0x00000004
# define LEO_KRN_CSR_UNK 0x00000002
# define LEO_KRN_CSR_UNK2 0x00000001
struct leo_lx_krn {
volatile u32 krn_type ;
volatile u32 krn_csr ;
volatile u32 krn_value ;
} ;
struct leo_lc_ss0_krn {
volatile u32 misc ;
u8 xxx0 [ 0x800 - 4 ] ;
volatile u32 rev ;
} ;
struct leo_lc_ss0_usr {
volatile u32 csr ;
volatile u32 addrspace ;
volatile u32 fontmsk ;
volatile u32 fontt ;
volatile u32 extent ;
volatile u32 src ;
u32 dst ;
volatile u32 copy ;
volatile u32 fill ;
} ;
struct leo_lc_ss1_krn {
u8 unknown ;
} ;
struct leo_lc_ss1_usr {
u8 unknown ;
} ;
struct leo_ld {
u8 xxx0 [ 0xe00 ] ;
volatile u32 csr ;
volatile u32 wid ;
volatile u32 wmask ;
volatile u32 widclip ;
volatile u32 vclipmin ;
volatile u32 vclipmax ;
volatile u32 pickmin ; /* SS1 only */
volatile u32 pickmax ; /* SS1 only */
volatile u32 fg ;
volatile u32 bg ;
volatile u32 src ; /* Copy/Scroll (SS0 only) */
volatile u32 dst ; /* Copy/Scroll/Fill (SS0 only) */
volatile u32 extent ; /* Copy/Scroll/Fill size (SS0 only) */
u32 xxx1 [ 3 ] ;
volatile u32 setsem ; /* SS1 only */
volatile u32 clrsem ; /* SS1 only */
volatile u32 clrpick ; /* SS1 only */
volatile u32 clrdat ; /* SS1 only */
volatile u32 alpha ; /* SS1 only */
u8 xxx2 [ 0x2c ] ;
volatile u32 winbg ;
volatile u32 planemask ;
volatile u32 rop ;
volatile u32 z ;
volatile u32 dczf ; /* SS1 only */
volatile u32 dczb ; /* SS1 only */
volatile u32 dcs ; /* SS1 only */
volatile u32 dczs ; /* SS1 only */
volatile u32 pickfb ; /* SS1 only */
volatile u32 pickbb ; /* SS1 only */
volatile u32 dcfc ; /* SS1 only */
volatile u32 forcecol ; /* SS1 only */
volatile u32 door [ 8 ] ; /* SS1 only */
volatile u32 pick [ 5 ] ; /* SS1 only */
} ;
# define LEO_SS1_MISC_ENABLE 0x00000001
# define LEO_SS1_MISC_STEREO 0x00000002
struct leo_ld_ss1 {
u8 xxx0 [ 0xef4 ] ;
volatile u32 ss1_misc ;
} ;
struct leo_ld_gbl {
u8 unknown ;
} ;
struct leo_par {
spinlock_t lock ;
struct leo_lx_krn __iomem * lx_krn ;
struct leo_lc_ss0_usr __iomem * lc_ss0_usr ;
struct leo_ld_ss0 __iomem * ld_ss0 ;
struct leo_ld_ss1 __iomem * ld_ss1 ;
struct leo_cursor __iomem * cursor ;
u32 extent ;
u32 clut_data [ 256 ] ;
u32 flags ;
# define LEO_FLAG_BLANKED 0x00000001
unsigned long physbase ;
unsigned long fbsize ;
struct sbus_dev * sdev ;
} ;
static void leo_wait ( struct leo_lx_krn __iomem * lx_krn )
{
int i ;
for ( i = 0 ;
( sbus_readl ( & lx_krn - > krn_csr ) & LEO_KRN_CSR_PROGRESS ) & & i < 300000 ;
i + + )
udelay ( 1 ) ; /* Busy wait at most 0.3 sec */
return ;
}
/**
* leo_setcolreg - Optional function . Sets a color register .
* @ regno : boolean , 0 copy local , 1 get_user ( ) function
* @ red : frame buffer colormap structure
* @ green : The green value which can be up to 16 bits wide
* @ blue : The blue value which can be up to 16 bits wide .
* @ transp : If supported the alpha value which can be up to 16 bits wide .
* @ info : frame buffer info structure
*/
static int leo_setcolreg ( unsigned regno ,
unsigned red , unsigned green , unsigned blue ,
unsigned transp , struct fb_info * info )
{
struct leo_par * par = ( struct leo_par * ) info - > par ;
struct leo_lx_krn __iomem * lx_krn = par - > lx_krn ;
unsigned long flags ;
u32 val ;
int i ;
if ( regno > = 256 )
return 1 ;
red > > = 8 ;
green > > = 8 ;
blue > > = 8 ;
par - > clut_data [ regno ] = red | ( green < < 8 ) | ( blue < < 16 ) ;
spin_lock_irqsave ( & par - > lock , flags ) ;
leo_wait ( lx_krn ) ;
sbus_writel ( LEO_KRN_TYPE_CLUTDATA , & lx_krn - > krn_type ) ;
for ( i = 0 ; i < 256 ; i + + )
sbus_writel ( par - > clut_data [ i ] , & lx_krn - > krn_value ) ;
sbus_writel ( LEO_KRN_TYPE_CLUT0 , & lx_krn - > krn_type ) ;
val = sbus_readl ( & lx_krn - > krn_csr ) ;
val | = ( LEO_KRN_CSR_UNK | LEO_KRN_CSR_UNK2 ) ;
sbus_writel ( val , & lx_krn - > krn_csr ) ;
spin_unlock_irqrestore ( & par - > lock , flags ) ;
return 0 ;
}
/**
* leo_blank - Optional function . Blanks the display .
* @ blank_mode : the blank mode we want .
* @ info : frame buffer structure that represents a single frame buffer
*/
static int leo_blank ( int blank , struct fb_info * info )
{
struct leo_par * par = ( struct leo_par * ) info - > par ;
struct leo_lx_krn __iomem * lx_krn = par - > lx_krn ;
unsigned long flags ;
u32 val ;
spin_lock_irqsave ( & par - > lock , flags ) ;
switch ( blank ) {
case FB_BLANK_UNBLANK : /* Unblanking */
val = sbus_readl ( & lx_krn - > krn_csr ) ;
val | = LEO_KRN_CSR_ENABLE ;
sbus_writel ( val , & lx_krn - > krn_csr ) ;
par - > flags & = ~ LEO_FLAG_BLANKED ;
break ;
case FB_BLANK_NORMAL : /* Normal blanking */
case FB_BLANK_VSYNC_SUSPEND : /* VESA blank (vsync off) */
case FB_BLANK_HSYNC_SUSPEND : /* VESA blank (hsync off) */
case FB_BLANK_POWERDOWN : /* Poweroff */
val = sbus_readl ( & lx_krn - > krn_csr ) ;
val & = ~ LEO_KRN_CSR_ENABLE ;
sbus_writel ( val , & lx_krn - > krn_csr ) ;
par - > flags | = LEO_FLAG_BLANKED ;
break ;
}
spin_unlock_irqrestore ( & par - > lock , flags ) ;
return 0 ;
}
static struct sbus_mmap_map leo_mmap_map [ ] = {
{
. voff = LEO_SS0_MAP ,
. poff = LEO_OFF_SS0 ,
. size = 0x800000
} ,
{
. voff = LEO_LC_SS0_USR_MAP ,
. poff = LEO_OFF_LC_SS0_USR ,
. size = 0x1000
} ,
{
. voff = LEO_LD_SS0_MAP ,
. poff = LEO_OFF_LD_SS0 ,
. size = 0x1000
} ,
{
. voff = LEO_LX_CURSOR_MAP ,
. poff = LEO_OFF_LX_CURSOR ,
. size = 0x1000
} ,
{
. voff = LEO_SS1_MAP ,
. poff = LEO_OFF_SS1 ,
. size = 0x800000
} ,
{
. voff = LEO_LC_SS1_USR_MAP ,
. poff = LEO_OFF_LC_SS1_USR ,
. size = 0x1000
} ,
{
. voff = LEO_LD_SS1_MAP ,
. poff = LEO_OFF_LD_SS1 ,
. size = 0x1000
} ,
{
. voff = LEO_UNK_MAP ,
. poff = LEO_OFF_UNK ,
. size = 0x1000
} ,
{
. voff = LEO_LX_KRN_MAP ,
. poff = LEO_OFF_LX_KRN ,
. size = 0x1000
} ,
{
. voff = LEO_LC_SS0_KRN_MAP ,
. poff = LEO_OFF_LC_SS0_KRN ,
. size = 0x1000
} ,
{
. voff = LEO_LC_SS1_KRN_MAP ,
. poff = LEO_OFF_LC_SS1_KRN ,
. size = 0x1000
} ,
{
. voff = LEO_LD_GBL_MAP ,
. poff = LEO_OFF_LD_GBL ,
. size = 0x1000
} ,
{
. voff = LEO_UNK2_MAP ,
. poff = LEO_OFF_UNK2 ,
. size = 0x100000
} ,
{ . size = 0 }
} ;
2006-01-15 00:21:25 +03:00
static int leo_mmap ( struct fb_info * info , struct vm_area_struct * vma )
2005-04-17 02:20:36 +04:00
{
struct leo_par * par = ( struct leo_par * ) info - > par ;
return sbusfb_mmap_helper ( leo_mmap_map ,
par - > physbase , par - > fbsize ,
par - > sdev - > reg_addrs [ 0 ] . which_io ,
vma ) ;
}
2006-01-15 00:21:25 +03:00
static int leo_ioctl ( struct fb_info * info , unsigned int cmd , unsigned long arg )
2005-04-17 02:20:36 +04:00
{
struct leo_par * par = ( struct leo_par * ) info - > par ;
return sbusfb_ioctl_helper ( cmd , arg , info ,
FBTYPE_SUNLEO , 32 , par - > fbsize ) ;
}
/*
* Initialisation
*/
static void
leo_init_fix ( struct fb_info * info )
{
struct leo_par * par = ( struct leo_par * ) info - > par ;
strlcpy ( info - > fix . id , par - > sdev - > prom_name , sizeof ( info - > fix . id ) ) ;
info - > fix . type = FB_TYPE_PACKED_PIXELS ;
info - > fix . visual = FB_VISUAL_TRUECOLOR ;
info - > fix . line_length = 8192 ;
info - > fix . accel = FB_ACCEL_SUN_LEO ;
}
static void leo_wid_put ( struct fb_info * info , struct fb_wid_list * wl )
{
struct leo_par * par = ( struct leo_par * ) info - > par ;
struct leo_lx_krn __iomem * lx_krn = par - > lx_krn ;
struct fb_wid_item * wi ;
unsigned long flags ;
u32 val ;
int i , j ;
spin_lock_irqsave ( & par - > lock , flags ) ;
leo_wait ( lx_krn ) ;
for ( i = 0 , wi = wl - > wl_list ; i < wl - > wl_count ; i + + , wi + + ) {
switch ( wi - > wi_type ) {
case FB_WID_DBL_8 :
j = ( wi - > wi_index & 0xf ) + 0x40 ;
break ;
case FB_WID_DBL_24 :
j = wi - > wi_index & 0x3f ;
break ;
default :
continue ;
} ;
sbus_writel ( 0x5800 + j , & lx_krn - > krn_type ) ;
sbus_writel ( wi - > wi_values [ 0 ] , & lx_krn - > krn_value ) ;
}
sbus_writel ( LEO_KRN_TYPE_WID , & lx_krn - > krn_type ) ;
val = sbus_readl ( & lx_krn - > krn_csr ) ;
val | = ( LEO_KRN_CSR_UNK | LEO_KRN_CSR_UNK2 ) ;
sbus_writel ( val , & lx_krn - > krn_csr ) ;
spin_unlock_irqrestore ( & par - > lock , flags ) ;
}
static void leo_init_wids ( struct fb_info * info )
{
struct fb_wid_item wi ;
struct fb_wid_list wl ;
wl . wl_count = 1 ;
wl . wl_list = & wi ;
wi . wi_type = FB_WID_DBL_8 ;
wi . wi_index = 0 ;
wi . wi_values [ 0 ] = 0x2c0 ;
leo_wid_put ( info , & wl ) ;
wi . wi_index = 1 ;
wi . wi_values [ 0 ] = 0x30 ;
leo_wid_put ( info , & wl ) ;
wi . wi_index = 2 ;
wi . wi_values [ 0 ] = 0x20 ;
leo_wid_put ( info , & wl ) ;
wi . wi_type = FB_WID_DBL_24 ;
wi . wi_index = 1 ;
wi . wi_values [ 0 ] = 0x30 ;
leo_wid_put ( info , & wl ) ;
}
static void leo_switch_from_graph ( struct fb_info * info )
{
struct leo_par * par = ( struct leo_par * ) info - > par ;
struct leo_ld __iomem * ss = ( struct leo_ld __iomem * ) par - > ld_ss0 ;
unsigned long flags ;
u32 val ;
spin_lock_irqsave ( & par - > lock , flags ) ;
par - > extent = ( ( info - > var . xres - 1 ) |
( ( info - > var . yres - 1 ) < < 16 ) ) ;
sbus_writel ( 0xffffffff , & ss - > wid ) ;
sbus_writel ( 0xffff , & ss - > wmask ) ;
sbus_writel ( 0 , & ss - > vclipmin ) ;
sbus_writel ( par - > extent , & ss - > vclipmax ) ;
sbus_writel ( 0 , & ss - > fg ) ;
sbus_writel ( 0xff000000 , & ss - > planemask ) ;
sbus_writel ( 0x310850 , & ss - > rop ) ;
sbus_writel ( 0 , & ss - > widclip ) ;
sbus_writel ( ( info - > var . xres - 1 ) | ( ( info - > var . yres - 1 ) < < 11 ) ,
& par - > lc_ss0_usr - > extent ) ;
sbus_writel ( 4 , & par - > lc_ss0_usr - > addrspace ) ;
sbus_writel ( 0x80000000 , & par - > lc_ss0_usr - > fill ) ;
sbus_writel ( 0 , & par - > lc_ss0_usr - > fontt ) ;
do {
val = sbus_readl ( & par - > lc_ss0_usr - > csr ) ;
} while ( val & 0x20000000 ) ;
spin_unlock_irqrestore ( & par - > lock , flags ) ;
}
static int leo_pan_display ( struct fb_var_screeninfo * var , struct fb_info * info )
{
/* We just use this to catch switches out of
* graphics mode .
*/
leo_switch_from_graph ( info ) ;
if ( var - > xoffset | | var - > yoffset | | var - > vmode )
return - EINVAL ;
return 0 ;
}
static void leo_init_hw ( struct fb_info * info )
{
struct leo_par * par = ( struct leo_par * ) info - > par ;
u32 val ;
val = sbus_readl ( & par - > ld_ss1 - > ss1_misc ) ;
val | = LEO_SS1_MISC_ENABLE ;
sbus_writel ( val , & par - > ld_ss1 - > ss1_misc ) ;
leo_switch_from_graph ( info ) ;
}
static void leo_fixup_var_rgb ( struct fb_var_screeninfo * var )
{
var - > red . offset = 0 ;
var - > red . length = 8 ;
var - > green . offset = 8 ;
var - > green . length = 8 ;
var - > blue . offset = 16 ;
var - > blue . length = 8 ;
var - > transp . offset = 0 ;
var - > transp . length = 0 ;
}
struct all_info {
struct fb_info info ;
struct leo_par par ;
struct list_head list ;
} ;
static LIST_HEAD ( leo_list ) ;
static void leo_init_one ( struct sbus_dev * sdev )
{
struct all_info * all ;
int linebytes ;
all = kmalloc ( sizeof ( * all ) , GFP_KERNEL ) ;
if ( ! all ) {
printk ( KERN_ERR " leo: Cannot allocate memory. \n " ) ;
return ;
}
memset ( all , 0 , sizeof ( * all ) ) ;
INIT_LIST_HEAD ( & all - > list ) ;
spin_lock_init ( & all - > par . lock ) ;
all - > par . sdev = sdev ;
all - > par . physbase = sdev - > reg_addrs [ 0 ] . phys_addr ;
sbusfb_fill_var ( & all - > info . var , sdev - > prom_node , 32 ) ;
leo_fixup_var_rgb ( & all - > info . var ) ;
linebytes = prom_getintdefault ( sdev - > prom_node , " linebytes " ,
all - > info . var . xres ) ;
all - > par . fbsize = PAGE_ALIGN ( linebytes * all - > info . var . yres ) ;
# ifdef CONFIG_SPARC32
all - > info . screen_base = ( char __iomem * )
prom_getintdefault ( sdev - > prom_node , " address " , 0 ) ;
# endif
if ( ! all - > info . screen_base )
all - > info . screen_base =
sbus_ioremap ( & sdev - > resource [ 0 ] , LEO_OFF_SS0 ,
0x800000 , " leo ram " ) ;
all - > par . lc_ss0_usr =
sbus_ioremap ( & sdev - > resource [ 0 ] , LEO_OFF_LC_SS0_USR ,
0x1000 , " leolc ss0usr " ) ;
all - > par . ld_ss0 =
sbus_ioremap ( & sdev - > resource [ 0 ] , LEO_OFF_LD_SS0 ,
0x1000 , " leold ss0 " ) ;
all - > par . ld_ss1 =
sbus_ioremap ( & sdev - > resource [ 0 ] , LEO_OFF_LD_SS1 ,
0x1000 , " leold ss1 " ) ;
all - > par . lx_krn =
sbus_ioremap ( & sdev - > resource [ 0 ] , LEO_OFF_LX_KRN ,
0x1000 , " leolx krn " ) ;
all - > par . cursor =
sbus_ioremap ( & sdev - > resource [ 0 ] , LEO_OFF_LX_CURSOR ,
sizeof ( struct leo_cursor ) , " leolx cursor " ) ;
all - > info . flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN ;
all - > info . fbops = & leo_ops ;
all - > info . par = & all - > par ;
leo_init_wids ( & all - > info ) ;
leo_init_hw ( & all - > info ) ;
leo_blank ( 0 , & all - > info ) ;
if ( fb_alloc_cmap ( & all - > info . cmap , 256 , 0 ) ) {
printk ( KERN_ERR " leo: Could not allocate color map. \n " ) ;
kfree ( all ) ;
return ;
}
leo_init_fix ( & all - > info ) ;
if ( register_framebuffer ( & all - > info ) < 0 ) {
printk ( KERN_ERR " leo: Could not register framebuffer. \n " ) ;
fb_dealloc_cmap ( & all - > info . cmap ) ;
kfree ( all ) ;
return ;
}
list_add ( & all - > list , & leo_list ) ;
printk ( " leo: %s at %lx:%lx \n " ,
sdev - > prom_name ,
( long ) sdev - > reg_addrs [ 0 ] . which_io ,
( long ) sdev - > reg_addrs [ 0 ] . phys_addr ) ;
}
int __init leo_init ( void )
{
struct sbus_bus * sbus ;
struct sbus_dev * sdev ;
if ( fb_get_options ( " leofb " , NULL ) )
return - ENODEV ;
for_all_sbusdev ( sdev , sbus ) {
if ( ! strcmp ( sdev - > prom_name , " leo " ) )
leo_init_one ( sdev ) ;
}
return 0 ;
}
void __exit leo_exit ( void )
{
struct list_head * pos , * tmp ;
list_for_each_safe ( pos , tmp , & leo_list ) {
struct all_info * all = list_entry ( pos , typeof ( * all ) , list ) ;
unregister_framebuffer ( & all - > info ) ;
fb_dealloc_cmap ( & all - > info . cmap ) ;
kfree ( all ) ;
}
}
int __init
leo_setup ( char * arg )
{
/* No cmdline options yet... */
return 0 ;
}
module_init ( leo_init ) ;
# ifdef MODULE
module_exit ( leo_exit ) ;
# endif
MODULE_DESCRIPTION ( " framebuffer driver for LEO chipsets " ) ;
MODULE_AUTHOR ( " David S. Miller <davem@redhat.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;