2005-04-17 02:20:36 +04:00
/* cg3.c: CGTHREE 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 ) 1997 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 cg3_setcolreg ( unsigned , unsigned , unsigned , unsigned ,
unsigned , struct fb_info * ) ;
static int cg3_blank ( int , struct fb_info * ) ;
2006-01-15 00:21:25 +03:00
static int cg3_mmap ( struct fb_info * , struct vm_area_struct * ) ;
2006-01-15 00:21:25 +03:00
static int cg3_ioctl ( struct fb_info * , unsigned int , unsigned long ) ;
2005-04-17 02:20:36 +04:00
/*
* Frame buffer operations
*/
static struct fb_ops cg3_ops = {
. owner = THIS_MODULE ,
. fb_setcolreg = cg3_setcolreg ,
. fb_blank = cg3_blank ,
. fb_fillrect = cfb_fillrect ,
. fb_copyarea = cfb_copyarea ,
. fb_imageblit = cfb_imageblit ,
. fb_mmap = cg3_mmap ,
. fb_ioctl = cg3_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
} ;
/* Control Register Constants */
# define CG3_CR_ENABLE_INTS 0x80
# define CG3_CR_ENABLE_VIDEO 0x40
# define CG3_CR_ENABLE_TIMING 0x20
# define CG3_CR_ENABLE_CURCMP 0x10
# define CG3_CR_XTAL_MASK 0x0c
# define CG3_CR_DIVISOR_MASK 0x03
/* Status Register Constants */
# define CG3_SR_PENDING_INT 0x80
# define CG3_SR_RES_MASK 0x70
# define CG3_SR_1152_900_76_A 0x40
# define CG3_SR_1152_900_76_B 0x60
# define CG3_SR_ID_MASK 0x0f
# define CG3_SR_ID_COLOR 0x01
# define CG3_SR_ID_MONO 0x02
# define CG3_SR_ID_MONO_ECL 0x03
enum cg3_type {
CG3_AT_66HZ = 0 ,
CG3_AT_76HZ ,
CG3_RDI
} ;
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
} ;
struct cg3_regs {
struct bt_regs cmap ;
2006-06-30 01:35:52 +04:00
u8 control ;
u8 status ;
u8 cursor_start ;
u8 cursor_end ;
u8 h_blank_start ;
u8 h_blank_end ;
u8 h_sync_start ;
u8 h_sync_end ;
u8 comp_sync_end ;
u8 v_blank_start_high ;
u8 v_blank_start_low ;
u8 v_blank_end ;
u8 v_sync_start ;
u8 v_sync_end ;
u8 xfer_holdoff_start ;
u8 xfer_holdoff_end ;
2005-04-17 02:20:36 +04:00
} ;
/* Offset of interesting structures in the OBIO space */
# define CG3_REGS_OFFSET 0x400000UL
# define CG3_RAM_OFFSET 0x800000UL
struct cg3_par {
spinlock_t lock ;
struct cg3_regs __iomem * regs ;
u32 sw_cmap [ ( ( 256 * 3 ) + 3 ) / 4 ] ;
u32 flags ;
# define CG3_FLAG_BLANKED 0x00000001
# define CG3_FLAG_RDI 0x00000002
unsigned long physbase ;
2006-06-30 01:35:52 +04:00
unsigned long which_io ;
2005-04-17 02:20:36 +04:00
unsigned long fbsize ;
} ;
/**
* cg3_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
*
* The cg3 palette is loaded with 4 color values at each time
* so you end up with : ( rgb ) ( r ) , ( gb ) ( rg ) , ( b ) ( rgb ) , and so on .
* We keep a sw copy of the hw cmap to assist us in this esoteric
* loading procedure .
*/
static int cg3_setcolreg ( unsigned regno ,
unsigned red , unsigned green , unsigned blue ,
unsigned transp , struct fb_info * info )
{
struct cg3_par * par = ( struct cg3_par * ) info - > par ;
struct bt_regs __iomem * bt = & par - > regs - > cmap ;
unsigned long flags ;
u32 * p32 ;
u8 * p8 ;
int count ;
if ( regno > = 256 )
return 1 ;
red > > = 8 ;
green > > = 8 ;
blue > > = 8 ;
spin_lock_irqsave ( & par - > lock , flags ) ;
p8 = ( u8 * ) par - > sw_cmap + ( regno * 3 ) ;
p8 [ 0 ] = red ;
p8 [ 1 ] = green ;
p8 [ 2 ] = blue ;
# define D4M3(x) ((((x)>>2)<<1) + ((x)>>2)) /* (x/4)*3 */
# define D4M4(x) ((x)&~0x3) /* (x/4)*4 */
count = 3 ;
p32 = & par - > sw_cmap [ D4M3 ( regno ) ] ;
sbus_writel ( D4M4 ( regno ) , & bt - > addr ) ;
while ( count - - )
sbus_writel ( * p32 + + , & bt - > color_map ) ;
# undef D4M3
# undef D4M4
spin_unlock_irqrestore ( & par - > lock , flags ) ;
return 0 ;
}
/**
* cg3_blank - Optional function . Blanks the display .
* @ blank_mode : the blank mode we want .
* @ info : frame buffer structure that represents a single frame buffer
*/
2007-03-28 23:50:56 +04:00
static int cg3_blank ( int blank , struct fb_info * info )
2005-04-17 02:20:36 +04:00
{
struct cg3_par * par = ( struct cg3_par * ) info - > par ;
struct cg3_regs __iomem * regs = par - > regs ;
unsigned long flags ;
u8 val ;
spin_lock_irqsave ( & par - > lock , flags ) ;
switch ( blank ) {
case FB_BLANK_UNBLANK : /* Unblanking */
val = sbus_readb ( & regs - > control ) ;
val | = CG3_CR_ENABLE_VIDEO ;
sbus_writeb ( val , & regs - > control ) ;
par - > flags & = ~ CG3_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_readb ( & regs - > control ) ;
val & = ~ CG3_CR_ENABLE_VIDEO ;
sbus_writeb ( val , & regs - > control ) ;
par - > flags | = CG3_FLAG_BLANKED ;
break ;
}
spin_unlock_irqrestore ( & par - > lock , flags ) ;
return 0 ;
}
static struct sbus_mmap_map cg3_mmap_map [ ] = {
{
. voff = CG3_MMAP_OFFSET ,
. poff = CG3_RAM_OFFSET ,
. size = SBUS_MMAP_FBSIZE ( 1 )
} ,
{ . size = 0 }
} ;
2006-01-15 00:21:25 +03:00
static int cg3_mmap ( struct fb_info * info , struct vm_area_struct * vma )
2005-04-17 02:20:36 +04:00
{
struct cg3_par * par = ( struct cg3_par * ) info - > par ;
return sbusfb_mmap_helper ( cg3_mmap_map ,
par - > physbase , par - > fbsize ,
2006-06-30 01:35:52 +04:00
par - > which_io ,
2005-04-17 02:20:36 +04:00
vma ) ;
}
2006-01-15 00:21:25 +03:00
static int cg3_ioctl ( struct fb_info * info , unsigned int cmd , unsigned long arg )
2005-04-17 02:20:36 +04:00
{
struct cg3_par * par = ( struct cg3_par * ) info - > par ;
return sbusfb_ioctl_helper ( cmd , arg , info ,
FBTYPE_SUN3COLOR , 8 , par - > fbsize ) ;
}
/*
* Initialisation
*/
2007-03-28 23:50:56 +04:00
static void __devinit cg3_init_fix ( struct fb_info * info , int linebytes ,
struct device_node * dp )
2005-04-17 02:20:36 +04:00
{
2006-06-30 01:35:52 +04:00
strlcpy ( info - > fix . id , dp - > name , sizeof ( info - > fix . id ) ) ;
2005-04-17 02:20:36 +04:00
info - > fix . type = FB_TYPE_PACKED_PIXELS ;
info - > fix . visual = FB_VISUAL_PSEUDOCOLOR ;
info - > fix . line_length = linebytes ;
info - > fix . accel = FB_ACCEL_SUN_CGTHREE ;
}
2007-03-28 23:50:56 +04:00
static void __devinit cg3_rdi_maybe_fixup_var ( struct fb_var_screeninfo * var ,
struct device_node * dp )
2005-04-17 02:20:36 +04:00
{
2007-03-29 11:49:54 +04:00
const char * params ;
2005-04-17 02:20:36 +04:00
char * p ;
int ww , hh ;
2006-06-30 01:35:52 +04:00
params = of_get_property ( dp , " params " , NULL ) ;
if ( params ) {
ww = simple_strtoul ( params , & p , 10 ) ;
2005-04-17 02:20:36 +04:00
if ( ww & & * p = = ' x ' ) {
hh = simple_strtoul ( p + 1 , & p , 10 ) ;
if ( hh & & * p = = ' - ' ) {
if ( var - > xres ! = ww | |
var - > yres ! = hh ) {
var - > xres = var - > xres_virtual = ww ;
var - > yres = var - > yres_virtual = hh ;
}
}
}
}
}
2007-03-28 23:50:56 +04:00
static u8 cg3regvals_66hz [ ] __devinitdata = { /* 1152 x 900, 66 Hz */
2005-04-17 02:20:36 +04:00
0x14 , 0xbb , 0x15 , 0x2b , 0x16 , 0x04 , 0x17 , 0x14 ,
0x18 , 0xae , 0x19 , 0x03 , 0x1a , 0xa8 , 0x1b , 0x24 ,
0x1c , 0x01 , 0x1d , 0x05 , 0x1e , 0xff , 0x1f , 0x01 ,
0x10 , 0x20 , 0
} ;
2007-03-28 23:50:56 +04:00
static u8 cg3regvals_76hz [ ] __devinitdata = { /* 1152 x 900, 76 Hz */
2005-04-17 02:20:36 +04:00
0x14 , 0xb7 , 0x15 , 0x27 , 0x16 , 0x03 , 0x17 , 0x0f ,
0x18 , 0xae , 0x19 , 0x03 , 0x1a , 0xae , 0x1b , 0x2a ,
0x1c , 0x01 , 0x1d , 0x09 , 0x1e , 0xff , 0x1f , 0x01 ,
0x10 , 0x24 , 0
} ;
2007-03-28 23:50:56 +04:00
static u8 cg3regvals_rdi [ ] __devinitdata = { /* 640 x 480, cgRDI */
2005-04-17 02:20:36 +04:00
0x14 , 0x70 , 0x15 , 0x20 , 0x16 , 0x08 , 0x17 , 0x10 ,
0x18 , 0x06 , 0x19 , 0x02 , 0x1a , 0x31 , 0x1b , 0x51 ,
0x1c , 0x06 , 0x1d , 0x0c , 0x1e , 0xff , 0x1f , 0x01 ,
0x10 , 0x22 , 0
} ;
2007-03-28 23:50:56 +04:00
static u8 * cg3_regvals [ ] __devinitdata = {
2005-04-17 02:20:36 +04:00
cg3regvals_66hz , cg3regvals_76hz , cg3regvals_rdi
} ;
2007-03-28 23:50:56 +04:00
static u_char cg3_dacvals [ ] __devinitdata = {
2005-04-17 02:20:36 +04:00
4 , 0xff , 5 , 0x00 , 6 , 0x70 , 7 , 0x00 , 0
} ;
2007-08-25 09:33:15 +04:00
static int __devinit cg3_do_default_mode ( struct cg3_par * par )
2005-04-17 02:20:36 +04:00
{
enum cg3_type type ;
u8 * p ;
if ( par - > flags & CG3_FLAG_RDI )
type = CG3_RDI ;
else {
u8 status = sbus_readb ( & par - > regs - > status ) , mon ;
if ( ( status & CG3_SR_ID_MASK ) = = CG3_SR_ID_COLOR ) {
mon = status & CG3_SR_RES_MASK ;
if ( mon = = CG3_SR_1152_900_76_A | |
mon = = CG3_SR_1152_900_76_B )
type = CG3_AT_76HZ ;
else
type = CG3_AT_66HZ ;
} else {
2007-08-25 09:33:15 +04:00
printk ( KERN_ERR " cgthree: can't handle SR %02x \n " ,
status ) ;
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
}
for ( p = cg3_regvals [ type ] ; * p ; p + = 2 ) {
u8 __iomem * regp = & ( ( u8 __iomem * ) par - > regs ) [ p [ 0 ] ] ;
sbus_writeb ( p [ 1 ] , regp ) ;
}
for ( p = cg3_dacvals ; * p ; p + = 2 ) {
2006-06-30 01:35:52 +04:00
u8 __iomem * regp ;
2005-04-17 02:20:36 +04:00
2006-06-30 01:35:52 +04:00
regp = ( u8 __iomem * ) & par - > regs - > cmap . addr ;
2005-04-17 02:20:36 +04:00
sbus_writeb ( p [ 0 ] , regp ) ;
2006-06-30 01:35:52 +04:00
regp = ( u8 __iomem * ) & par - > regs - > cmap . control ;
2005-04-17 02:20:36 +04:00
sbus_writeb ( p [ 1 ] , regp ) ;
}
2007-08-25 09:33:15 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2007-07-28 09:31:46 +04:00
static int __devinit cg3_probe ( struct of_device * op ,
const struct of_device_id * match )
2005-04-17 02:20:36 +04:00
{
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 cg3_par * par ;
2006-06-30 01:35:52 +04:00
int linebytes , err ;
2005-04-17 02:20:36 +04:00
2007-07-28 09:31:46 +04:00
info = framebuffer_alloc ( sizeof ( struct cg3_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 ) ;
2005-04-17 02:20:36 +04:00
2007-07-28 09:31:46 +04:00
par - > physbase = op - > resource [ 0 ] . start ;
par - > which_io = op - > resource [ 0 ] . flags & IORESOURCE_BITS ;
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 ;
2006-06-30 01:35:52 +04:00
if ( ! strcmp ( dp - > name , " cgRDI " ) )
2007-07-28 09:31:46 +04:00
par - > flags | = CG3_FLAG_RDI ;
if ( par - > flags & CG3_FLAG_RDI )
cg3_rdi_maybe_fixup_var ( & info - > var , dp ) ;
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 ) ;
par - > fbsize = PAGE_ALIGN ( linebytes * info - > var . yres ) ;
2005-04-17 02:20:36 +04:00
2007-07-28 09:31:46 +04:00
par - > regs = of_ioremap ( & op - > resource [ 0 ] , CG3_REGS_OFFSET ,
sizeof ( struct cg3_regs ) , " cg3 regs " ) ;
if ( ! par - > regs )
goto out_release_fb ;
2005-04-17 02:20:36 +04:00
2007-07-28 09:31:46 +04:00
info - > flags = FBINFO_DEFAULT ;
info - > fbops = & cg3_ops ;
info - > screen_base = of_ioremap ( & op - > resource [ 0 ] , CG3_RAM_OFFSET ,
par - > fbsize , " cg3 ram " ) ;
if ( ! info - > screen_base )
goto out_unmap_regs ;
2005-04-17 02:20:36 +04:00
2008-05-04 08:12:00 +04:00
cg3_blank ( FB_BLANK_UNBLANK , info ) ;
2005-04-17 02:20:36 +04:00
2007-08-25 09:33:15 +04:00
if ( ! of_find_property ( dp , " width " , NULL ) ) {
err = cg3_do_default_mode ( par ) ;
if ( err )
goto out_unmap_screen ;
}
2007-07-28 09:31:46 +04:00
if ( fb_alloc_cmap ( & info - > cmap , 256 , 0 ) )
goto out_unmap_screen ;
fb_set_cmap ( & info - > cmap , info ) ;
2005-04-17 02:20:36 +04:00
2007-07-28 09:31:46 +04:00
cg3_init_fix ( info , linebytes , dp ) ;
err = register_framebuffer ( info ) ;
if ( err < 0 )
goto out_dealloc_cmap ;
dev_set_drvdata ( & op - > dev , info ) ;
2006-06-30 01:35:52 +04:00
2008-04-28 02:18:57 +04:00
printk ( KERN_INFO " %s: cg3 at %lx:%lx \n " ,
2007-07-28 09:31:46 +04:00
dp - > full_name , par - > which_io , par - > physbase ) ;
2005-04-17 02:20:36 +04:00
2006-06-30 01:35:52 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
2007-07-28 09:31:46 +04:00
out_dealloc_cmap :
fb_dealloc_cmap ( & info - > cmap ) ;
out_unmap_screen :
of_iounmap ( & op - > resource [ 0 ] , info - > screen_base , par - > fbsize ) ;
out_unmap_regs :
of_iounmap ( & op - > resource [ 0 ] , par - > regs , sizeof ( struct cg3_regs ) ) ;
out_release_fb :
framebuffer_release ( info ) ;
2005-04-17 02:20:36 +04:00
2007-07-28 09:31:46 +04:00
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 cg3_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 cg3_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
of_iounmap ( & op - > resource [ 0 ] , par - > regs , sizeof ( struct cg3_regs ) ) ;
of_iounmap ( & op - > resource [ 0 ] , info - > screen_base , par - > fbsize ) ;
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 cg3_match [ ] = {
2006-06-30 01:35:52 +04:00
{
. name = " cgthree " ,
} ,
{
. name = " cgRDI " ,
} ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , cg3_match ) ;
2005-04-17 02:20:36 +04:00
2006-06-30 01:35:52 +04:00
static struct of_platform_driver cg3_driver = {
. name = " cg3 " ,
. match_table = cg3_match ,
. probe = cg3_probe ,
. remove = __devexit_p ( cg3_remove ) ,
} ;
2005-04-17 02:20:36 +04:00
2006-06-30 01:35:52 +04:00
static int __init cg3_init ( void )
{
if ( fb_get_options ( " cg3fb " , NULL ) )
return - ENODEV ;
return of_register_driver ( & cg3_driver , & of_bus_type ) ;
2005-04-17 02:20:36 +04:00
}
2006-06-30 01:35:52 +04:00
static void __exit cg3_exit ( void )
2005-04-17 02:20:36 +04:00
{
2006-06-30 01:35:52 +04:00
of_unregister_driver ( & cg3_driver ) ;
2005-04-17 02:20:36 +04:00
}
module_init ( cg3_init ) ;
module_exit ( cg3_exit ) ;
MODULE_DESCRIPTION ( " framebuffer driver for CGthree 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 " ) ;