2005-04-17 02:20:36 +04:00
/* tcx.c: TCX frame buffer driver
*
2006-06-30 01:35:52 +04:00
* Copyright ( C ) 2003 , 2006 David S . Miller ( davem @ davemloft . net )
2005-04-17 02:20:36 +04:00
* Copyright ( C ) 1996 , 1998 Jakub Jelinek ( jj @ ultra . linux . cz )
* Copyright ( C ) 1996 Miguel de Icaza ( miguel @ nuclecu . unam . mx )
* Copyright ( C ) 1996 Eddie C . Dost ( ecd @ skynet . be )
*
* 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>
2008-05-09 08:37:30 +04:00
# include <linux/of_device.h>
2005-04-17 02:20:36 +04:00
# include <asm/io.h>
# include <asm/fbio.h>
# include "sbuslib.h"
/*
* Local functions .
*/
static int tcx_setcolreg ( unsigned , unsigned , unsigned , unsigned ,
unsigned , struct fb_info * ) ;
static int tcx_blank ( int , struct fb_info * ) ;
2006-01-15 00:21:25 +03:00
static int tcx_mmap ( struct fb_info * , struct vm_area_struct * ) ;
2006-01-15 00:21:25 +03:00
static int tcx_ioctl ( struct fb_info * , unsigned int , unsigned long ) ;
2005-04-25 07:39:15 +04:00
static int tcx_pan_display ( struct fb_var_screeninfo * , struct fb_info * ) ;
2005-04-17 02:20:36 +04:00
/*
* Frame buffer operations
*/
static struct fb_ops tcx_ops = {
. owner = THIS_MODULE ,
. fb_setcolreg = tcx_setcolreg ,
. fb_blank = tcx_blank ,
2005-04-25 07:39:15 +04:00
. fb_pan_display = tcx_pan_display ,
2005-04-17 02:20:36 +04:00
. fb_fillrect = cfb_fillrect ,
. fb_copyarea = cfb_copyarea ,
. fb_imageblit = cfb_imageblit ,
. fb_mmap = tcx_mmap ,
. fb_ioctl = tcx_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
} ;
/* THC definitions */
# define TCX_THC_MISC_REV_SHIFT 16
# define TCX_THC_MISC_REV_MASK 15
# define TCX_THC_MISC_VSYNC_DIS (1 << 25)
# define TCX_THC_MISC_HSYNC_DIS (1 << 24)
# define TCX_THC_MISC_RESET (1 << 12)
# define TCX_THC_MISC_VIDEO (1 << 10)
# define TCX_THC_MISC_SYNC (1 << 9)
# define TCX_THC_MISC_VSYNC (1 << 8)
# define TCX_THC_MISC_SYNC_ENAB (1 << 7)
# define TCX_THC_MISC_CURS_RES (1 << 6)
# define TCX_THC_MISC_INT_ENAB (1 << 5)
# define TCX_THC_MISC_INT (1 << 4)
# define TCX_THC_MISC_INIT 0x9f
# define TCX_THC_REV_REV_SHIFT 20
# define TCX_THC_REV_REV_MASK 15
# define TCX_THC_REV_MINREV_SHIFT 28
# define TCX_THC_REV_MINREV_MASK 15
/* The contents are unknown */
struct tcx_tec {
2006-06-30 01:35:52 +04:00
u32 tec_matrix ;
u32 tec_clip ;
u32 tec_vdc ;
2005-04-17 02:20:36 +04:00
} ;
struct tcx_thc {
2006-06-30 01:35:52 +04:00
u32 thc_rev ;
2008-05-04 07:55:27 +04:00
u32 thc_pad0 [ 511 ] ;
2006-06-30 01:35:52 +04:00
u32 thc_hs ; /* hsync timing */
u32 thc_hsdvs ;
u32 thc_hd ;
u32 thc_vs ; /* vsync timing */
u32 thc_vd ;
u32 thc_refresh ;
u32 thc_misc ;
2005-04-17 02:20:36 +04:00
u32 thc_pad1 [ 56 ] ;
2006-06-30 01:35:52 +04:00
u32 thc_cursxy ; /* cursor x,y position (16 bits each) */
u32 thc_cursmask [ 32 ] ; /* cursor mask bits */
u32 thc_cursbits [ 32 ] ; /* what to show where mask enabled */
2005-04-17 02:20:36 +04:00
} ;
struct bt_regs {
2006-06-30 01:35:52 +04:00
u32 addr ;
u32 color_map ;
u32 control ;
u32 cursor ;
2005-04-17 02:20:36 +04:00
} ;
# define TCX_MMAP_ENTRIES 14
struct tcx_par {
spinlock_t lock ;
struct bt_regs __iomem * bt ;
struct tcx_thc __iomem * thc ;
struct tcx_tec __iomem * tec ;
2006-06-30 01:35:52 +04:00
u32 __iomem * cplane ;
2005-04-17 02:20:36 +04:00
u32 flags ;
# define TCX_FLAG_BLANKED 0x00000001
2006-06-30 01:35:52 +04:00
unsigned long which_io ;
2005-04-17 02:20:36 +04:00
struct sbus_mmap_map mmap_map [ TCX_MMAP_ENTRIES ] ;
int lowdepth ;
} ;
/* Reset control plane so that WID is 8-bit plane. */
2009-06-17 02:34:39 +04:00
static void __tcx_set_control_plane ( struct fb_info * info )
2005-04-17 02:20:36 +04:00
{
2009-06-17 02:34:39 +04:00
struct tcx_par * par = info - > par ;
2006-06-30 01:35:52 +04:00
u32 __iomem * p , * pend ;
2008-05-04 07:55:27 +04:00
2005-04-17 02:20:36 +04:00
if ( par - > lowdepth )
return ;
p = par - > cplane ;
if ( p = = NULL )
return ;
2009-06-17 02:34:39 +04:00
for ( pend = p + info - > fix . smem_len ; p < pend ; p + + ) {
2005-04-17 02:20:36 +04:00
u32 tmp = sbus_readl ( p ) ;
tmp & = 0xffffff ;
sbus_writel ( tmp , p ) ;
}
}
2008-05-04 07:55:27 +04:00
static void tcx_reset ( struct fb_info * info )
2005-04-17 02:20:36 +04:00
{
struct tcx_par * par = ( struct tcx_par * ) info - > par ;
unsigned long flags ;
spin_lock_irqsave ( & par - > lock , flags ) ;
2009-06-17 02:34:39 +04:00
__tcx_set_control_plane ( info ) ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & par - > lock , flags ) ;
}
2005-04-25 07:39:15 +04:00
static int tcx_pan_display ( struct fb_var_screeninfo * var , struct fb_info * info )
{
tcx_reset ( info ) ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
/**
* tcx_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 tcx_setcolreg ( unsigned regno ,
unsigned red , unsigned green , unsigned blue ,
unsigned transp , struct fb_info * info )
{
struct tcx_par * par = ( struct tcx_par * ) info - > par ;
struct bt_regs __iomem * bt = par - > bt ;
unsigned long flags ;
if ( regno > = 256 )
return 1 ;
red > > = 8 ;
green > > = 8 ;
blue > > = 8 ;
spin_lock_irqsave ( & par - > lock , flags ) ;
sbus_writel ( regno < < 24 , & bt - > addr ) ;
sbus_writel ( red < < 24 , & bt - > color_map ) ;
sbus_writel ( green < < 24 , & bt - > color_map ) ;
sbus_writel ( blue < < 24 , & bt - > color_map ) ;
spin_unlock_irqrestore ( & par - > lock , flags ) ;
return 0 ;
}
/**
* tcx_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
tcx_blank ( int blank , struct fb_info * info )
{
struct tcx_par * par = ( struct tcx_par * ) info - > par ;
struct tcx_thc __iomem * thc = par - > thc ;
unsigned long flags ;
u32 val ;
spin_lock_irqsave ( & par - > lock , flags ) ;
val = sbus_readl ( & thc - > thc_misc ) ;
switch ( blank ) {
case FB_BLANK_UNBLANK : /* Unblanking */
val & = ~ ( TCX_THC_MISC_VSYNC_DIS |
TCX_THC_MISC_HSYNC_DIS ) ;
val | = TCX_THC_MISC_VIDEO ;
par - > flags & = ~ TCX_FLAG_BLANKED ;
break ;
case FB_BLANK_NORMAL : /* Normal blanking */
val & = ~ TCX_THC_MISC_VIDEO ;
par - > flags | = TCX_FLAG_BLANKED ;
break ;
case FB_BLANK_VSYNC_SUSPEND : /* VESA blank (vsync off) */
val | = TCX_THC_MISC_VSYNC_DIS ;
break ;
case FB_BLANK_HSYNC_SUSPEND : /* VESA blank (hsync off) */
val | = TCX_THC_MISC_HSYNC_DIS ;
break ;
case FB_BLANK_POWERDOWN : /* Poweroff */
break ;
} ;
sbus_writel ( val , & thc - > thc_misc ) ;
spin_unlock_irqrestore ( & par - > lock , flags ) ;
return 0 ;
}
static struct sbus_mmap_map __tcx_mmap_map [ TCX_MMAP_ENTRIES ] = {
{
. voff = TCX_RAM8BIT ,
. size = SBUS_MMAP_FBSIZE ( 1 )
} ,
{
. voff = TCX_RAM24BIT ,
. size = SBUS_MMAP_FBSIZE ( 4 )
} ,
{
. voff = TCX_UNK3 ,
. size = SBUS_MMAP_FBSIZE ( 8 )
} ,
{
. voff = TCX_UNK4 ,
. size = SBUS_MMAP_FBSIZE ( 8 )
} ,
{
. voff = TCX_CONTROLPLANE ,
. size = SBUS_MMAP_FBSIZE ( 4 )
} ,
{
. voff = TCX_UNK6 ,
. size = SBUS_MMAP_FBSIZE ( 8 )
} ,
{
. voff = TCX_UNK7 ,
. size = SBUS_MMAP_FBSIZE ( 8 )
} ,
{
. voff = TCX_TEC ,
. size = PAGE_SIZE
} ,
{
. voff = TCX_BTREGS ,
. size = PAGE_SIZE
} ,
{
. voff = TCX_THC ,
. size = PAGE_SIZE
} ,
{
. voff = TCX_DHC ,
. size = PAGE_SIZE
} ,
{
. voff = TCX_ALT ,
. size = PAGE_SIZE
} ,
{
. voff = TCX_UNK2 ,
. size = 0x20000
} ,
{ . size = 0 }
} ;
2006-01-15 00:21:25 +03:00
static int tcx_mmap ( struct fb_info * info , struct vm_area_struct * vma )
2005-04-17 02:20:36 +04:00
{
struct tcx_par * par = ( struct tcx_par * ) info - > par ;
return sbusfb_mmap_helper ( par - > mmap_map ,
2009-06-17 02:34:39 +04:00
info - > fix . smem_start , info - > fix . smem_len ,
2006-06-30 01:35:52 +04:00
par - > which_io , vma ) ;
2005-04-17 02:20:36 +04:00
}
2006-01-15 00:21:25 +03:00
static int tcx_ioctl ( struct fb_info * info , unsigned int cmd ,
unsigned long arg )
2005-04-17 02:20:36 +04:00
{
struct tcx_par * par = ( struct tcx_par * ) info - > par ;
return sbusfb_ioctl_helper ( cmd , arg , info ,
FBTYPE_TCXCOLOR ,
( par - > lowdepth ? 8 : 24 ) ,
2009-06-17 02:34:39 +04:00
info - > fix . smem_len ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Initialisation
*/
static void
tcx_init_fix ( struct fb_info * info , int linebytes )
{
struct tcx_par * par = ( struct tcx_par * ) info - > par ;
const char * tcx_name ;
if ( par - > lowdepth )
tcx_name = " TCX8 " ;
else
tcx_name = " TCX24 " ;
strlcpy ( info - > fix . id , tcx_name , sizeof ( info - > fix . id ) ) ;
info - > fix . type = FB_TYPE_PACKED_PIXELS ;
info - > fix . visual = FB_VISUAL_PSEUDOCOLOR ;
info - > fix . line_length = linebytes ;
info - > fix . accel = FB_ACCEL_SUN_TCX ;
}
2007-07-28 09:31:46 +04:00
static void tcx_unmap_regs ( struct of_device * op , struct fb_info * info ,
struct tcx_par * par )
2005-04-17 02:20:36 +04:00
{
2007-07-28 09:31:46 +04:00
if ( par - > tec )
2006-12-29 08:01:32 +03:00
of_iounmap ( & op - > resource [ 7 ] ,
2007-07-28 09:31:46 +04:00
par - > tec , sizeof ( struct tcx_tec ) ) ;
if ( par - > thc )
2006-12-29 08:01:32 +03:00
of_iounmap ( & op - > resource [ 9 ] ,
2007-07-28 09:31:46 +04:00
par - > thc , sizeof ( struct tcx_thc ) ) ;
if ( par - > bt )
2006-12-29 08:01:32 +03:00
of_iounmap ( & op - > resource [ 8 ] ,
2007-07-28 09:31:46 +04:00
par - > bt , sizeof ( struct bt_regs ) ) ;
if ( par - > cplane )
2006-12-29 08:01:32 +03:00
of_iounmap ( & op - > resource [ 4 ] ,
2009-06-17 02:34:39 +04:00
par - > cplane , info - > fix . smem_len * sizeof ( u32 ) ) ;
2007-07-28 09:31:46 +04:00
if ( info - > screen_base )
2006-12-29 08:01:32 +03:00
of_iounmap ( & op - > resource [ 0 ] ,
2009-06-17 02:34:39 +04:00
info - > screen_base , info - > fix . smem_len ) ;
2006-06-30 01:35:52 +04:00
}
2005-04-17 02:20:36 +04:00
2008-05-04 07:55:27 +04:00
static int __devinit tcx_probe ( struct of_device * op ,
const struct of_device_id * match )
2006-06-30 01:35:52 +04:00
{
struct device_node * dp = op - > node ;
2007-07-28 09:31:46 +04:00
struct fb_info * info ;
struct tcx_par * par ;
2006-06-30 01:35:52 +04:00
int linebytes , i , err ;
2005-04-17 02:20:36 +04:00
2007-07-28 09:31:46 +04:00
info = framebuffer_alloc ( sizeof ( struct tcx_par ) , & op - > dev ) ;
2005-04-17 02:20:36 +04:00
2007-07-28 09:31:46 +04:00
err = - ENOMEM ;
if ( ! info )
goto out_err ;
par = info - > par ;
2005-04-17 02:20:36 +04:00
2007-07-28 09:31:46 +04:00
spin_lock_init ( & par - > lock ) ;
par - > lowdepth =
2006-06-30 01:35:52 +04:00
( of_find_property ( dp , " tcx-8-bit " , NULL ) ! = NULL ) ;
2005-04-17 02:20:36 +04:00
2008-05-09 08:37:30 +04:00
sbusfb_fill_var ( & info - > var , dp , 8 ) ;
2007-07-28 09:31:46 +04:00
info - > var . red . length = 8 ;
info - > var . green . length = 8 ;
info - > var . blue . length = 8 ;
2005-04-17 02:20:36 +04:00
2006-06-30 01:35:52 +04:00
linebytes = of_getintprop_default ( dp , " linebytes " ,
2007-07-28 09:31:46 +04:00
info - > var . xres ) ;
2009-06-17 02:34:39 +04:00
info - > fix . smem_len = PAGE_ALIGN ( linebytes * info - > var . yres ) ;
2005-04-17 02:20:36 +04:00
2007-07-28 09:31:46 +04:00
par - > tec = of_ioremap ( & op - > resource [ 7 ] , 0 ,
2006-06-30 01:35:52 +04:00
sizeof ( struct tcx_tec ) , " tcx tec " ) ;
2007-07-28 09:31:46 +04:00
par - > thc = of_ioremap ( & op - > resource [ 9 ] , 0 ,
2006-06-30 01:35:52 +04:00
sizeof ( struct tcx_thc ) , " tcx thc " ) ;
2007-07-28 09:31:46 +04:00
par - > bt = of_ioremap ( & op - > resource [ 8 ] , 0 ,
2006-06-30 01:35:52 +04:00
sizeof ( struct bt_regs ) , " tcx dac " ) ;
2007-07-28 09:31:46 +04:00
info - > screen_base = of_ioremap ( & op - > resource [ 0 ] , 0 ,
2009-06-17 02:34:39 +04:00
info - > fix . smem_len , " tcx ram " ) ;
2007-07-28 09:31:46 +04:00
if ( ! par - > tec | | ! par - > thc | |
! par - > bt | | ! info - > screen_base )
goto out_unmap_regs ;
memcpy ( & par - > mmap_map , & __tcx_mmap_map , sizeof ( par - > mmap_map ) ) ;
if ( ! par - > lowdepth ) {
par - > cplane = of_ioremap ( & op - > resource [ 4 ] , 0 ,
2009-06-17 02:34:39 +04:00
info - > fix . smem_len * sizeof ( u32 ) ,
2006-06-30 01:35:52 +04:00
" tcx cplane " ) ;
2007-07-28 09:31:46 +04:00
if ( ! par - > cplane )
goto out_unmap_regs ;
2005-04-17 02:20:36 +04:00
} else {
2007-07-28 09:31:46 +04:00
par - > mmap_map [ 1 ] . size = SBUS_MMAP_EMPTY ;
par - > mmap_map [ 4 ] . size = SBUS_MMAP_EMPTY ;
par - > mmap_map [ 5 ] . size = SBUS_MMAP_EMPTY ;
par - > mmap_map [ 6 ] . size = SBUS_MMAP_EMPTY ;
2005-04-17 02:20:36 +04:00
}
2009-06-17 02:34:39 +04:00
info - > fix . smem_start = op - > resource [ 0 ] . start ;
2007-07-28 09:31:46 +04:00
par - > which_io = op - > resource [ 0 ] . flags & IORESOURCE_BITS ;
2006-06-30 01:35:52 +04:00
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < TCX_MMAP_ENTRIES ; i + + ) {
int j ;
switch ( i ) {
case 10 :
j = 12 ;
break ;
case 11 : case 12 :
j = i - 1 ;
break ;
default :
j = i ;
break ;
} ;
2007-07-28 09:31:46 +04:00
par - > mmap_map [ i ] . poff = op - > resource [ j ] . start ;
2005-04-17 02:20:36 +04:00
}
2007-07-28 09:31:46 +04:00
info - > flags = FBINFO_DEFAULT ;
info - > fbops = & tcx_ops ;
2005-04-17 02:20:36 +04:00
/* Initialize brooktree DAC. */
2007-07-28 09:31:46 +04:00
sbus_writel ( 0x04 < < 24 , & par - > bt - > addr ) ; /* color planes */
sbus_writel ( 0xff < < 24 , & par - > bt - > control ) ;
sbus_writel ( 0x05 < < 24 , & par - > bt - > addr ) ;
sbus_writel ( 0x00 < < 24 , & par - > bt - > control ) ;
sbus_writel ( 0x06 < < 24 , & par - > bt - > addr ) ; /* overlay plane */
sbus_writel ( 0x73 < < 24 , & par - > bt - > control ) ;
sbus_writel ( 0x07 < < 24 , & par - > bt - > addr ) ;
sbus_writel ( 0x00 < < 24 , & par - > bt - > control ) ;
tcx_reset ( info ) ;
2005-04-17 02:20:36 +04:00
2007-07-28 09:31:46 +04:00
tcx_blank ( FB_BLANK_UNBLANK , info ) ;
2005-04-17 02:20:36 +04:00
2007-07-28 09:31:46 +04:00
if ( fb_alloc_cmap ( & info - > cmap , 256 , 0 ) )
goto out_unmap_regs ;
fb_set_cmap ( & info - > cmap , info ) ;
tcx_init_fix ( info , linebytes ) ;
err = register_framebuffer ( info ) ;
if ( err < 0 )
goto out_dealloc_cmap ;
2005-04-17 02:20:36 +04:00
2007-07-28 09:31:46 +04:00
dev_set_drvdata ( & op - > dev , info ) ;
2005-04-17 02:20:36 +04:00
2008-04-28 02:18:57 +04:00
printk ( KERN_INFO " %s: TCX at %lx:%lx, %s \n " ,
2006-06-30 01:35:52 +04:00
dp - > full_name ,
2007-07-28 09:31:46 +04:00
par - > which_io ,
2009-06-17 02:34:39 +04:00
info - > fix . smem_start ,
2007-07-28 09:31:46 +04:00
par - > lowdepth ? " 8-bit only " : " 24-bit depth " ) ;
2006-06-30 01:35:52 +04:00
return 0 ;
2007-07-28 09:31:46 +04:00
out_dealloc_cmap :
fb_dealloc_cmap ( & info - > cmap ) ;
out_unmap_regs :
tcx_unmap_regs ( op , info , par ) ;
out_err :
return err ;
2006-06-30 01:35:52 +04:00
}
2005-04-17 02:20:36 +04:00
2006-12-29 08:01:32 +03:00
static int __devexit tcx_remove ( struct of_device * op )
2006-06-30 01:35:52 +04:00
{
2007-07-28 09:31:46 +04:00
struct fb_info * info = dev_get_drvdata ( & op - > dev ) ;
struct tcx_par * par = info - > par ;
2006-06-30 01:35:52 +04:00
2007-07-28 09:31:46 +04:00
unregister_framebuffer ( info ) ;
fb_dealloc_cmap ( & info - > cmap ) ;
2006-06-30 01:35:52 +04:00
2007-07-28 09:31:46 +04:00
tcx_unmap_regs ( op , info , par ) ;
2006-06-30 01:35:52 +04:00
2007-07-28 09:31:46 +04:00
framebuffer_release ( info ) ;
2006-06-30 01:35:52 +04:00
2006-12-29 08:01:32 +03:00
dev_set_drvdata ( & op - > dev , NULL ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2008-08-31 12:23:17 +04:00
static const struct of_device_id tcx_match [ ] = {
2006-06-30 01:35:52 +04:00
{
. name = " SUNW,tcx " ,
} ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , tcx_match ) ;
2005-04-17 02:20:36 +04:00
2006-06-30 01:35:52 +04:00
static struct of_platform_driver tcx_driver = {
. name = " tcx " ,
. match_table = tcx_match ,
. probe = tcx_probe ,
. remove = __devexit_p ( tcx_remove ) ,
} ;
2005-04-17 02:20:36 +04:00
2008-04-28 02:18:12 +04:00
static int __init tcx_init ( void )
2006-06-30 01:35:52 +04:00
{
if ( fb_get_options ( " tcxfb " , NULL ) )
return - ENODEV ;
return of_register_driver ( & tcx_driver , & of_bus_type ) ;
2005-04-17 02:20:36 +04:00
}
2008-04-28 02:18:12 +04:00
static void __exit tcx_exit ( void )
2005-04-17 02:20:36 +04:00
{
2006-06-30 01:35:52 +04:00
of_unregister_driver ( & tcx_driver ) ;
2005-04-17 02:20:36 +04:00
}
module_init ( tcx_init ) ;
module_exit ( tcx_exit ) ;
MODULE_DESCRIPTION ( " framebuffer driver for TCX chipsets " ) ;
2006-06-30 01:35:52 +04:00
MODULE_AUTHOR ( " David S. Miller <davem@davemloft.net> " ) ;
MODULE_VERSION ( " 2.0 " ) ;
2005-04-17 02:20:36 +04:00
MODULE_LICENSE ( " GPL " ) ;