2005-04-17 02:20:36 +04:00
/*
2005-09-04 02:56:11 +04:00
* linux / drivers / video / pmagb - b - fb . c
2005-04-17 02:20:36 +04:00
*
2005-09-04 02:56:11 +04:00
* PMAGB - B TURBOchannel Smart Frame Buffer ( SFB ) card support ,
* derived from :
2005-04-17 02:20:36 +04:00
* " HP300 Topcat framebuffer support (derived from macfb of all things)
* Phil Blundell < philb @ gnu . org > 1998 " , the original code can be
2005-09-04 02:56:11 +04:00
* found in the file hpfb . c in the same directory .
2005-04-17 02:20:36 +04:00
*
2005-09-04 02:56:11 +04:00
* DECstation related code Copyright ( C ) 1999 , 2000 , 2001 by
* Michael Engel < engel @ unix - ag . org > ,
* Karsten Merker < merker @ linuxtag . org > and
2005-04-17 02:20:36 +04:00
* Harald Koerfgen .
2007-02-06 03:28:29 +03:00
* Copyright ( c ) 2005 , 2006 Maciej W . Rozycki
2005-04-17 02:20:36 +04:00
*
2005-09-04 02:56:11 +04:00
* 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 .
2005-04-17 02:20:36 +04:00
*/
2005-09-04 02:56:11 +04:00
# include <linux/compiler.h>
2005-04-17 02:20:36 +04:00
# include <linux/delay.h>
2005-09-04 02:56:11 +04:00
# include <linux/errno.h>
2005-04-17 02:20:36 +04:00
# include <linux/fb.h>
2005-09-04 02:56:11 +04:00
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/module.h>
2007-02-06 03:28:29 +03:00
# include <linux/tc.h>
2005-09-04 02:56:11 +04:00
# include <linux/types.h>
# include <asm/io.h>
# include <asm/system.h>
2005-04-17 02:20:36 +04:00
# include <video/pmagb-b-fb.h>
2005-09-04 02:56:11 +04:00
struct pmagbbfb_par {
volatile void __iomem * mmio ;
volatile void __iomem * smem ;
volatile u32 __iomem * sfb ;
volatile u32 __iomem * dac ;
unsigned int osc0 ;
unsigned int osc1 ;
int slot ;
2005-04-17 02:20:36 +04:00
} ;
2005-09-04 02:56:11 +04:00
static struct fb_var_screeninfo pmagbbfb_defined __initdata = {
2005-04-17 02:20:36 +04:00
. bits_per_pixel = 8 ,
. red . length = 8 ,
. green . length = 8 ,
. blue . length = 8 ,
. activate = FB_ACTIVATE_NOW ,
2005-09-04 02:56:11 +04:00
. height = - 1 ,
. width = - 1 ,
2005-04-17 02:20:36 +04:00
. accel_flags = FB_ACCEL_NONE ,
2005-09-04 02:56:11 +04:00
. sync = FB_SYNC_ON_GREEN ,
2005-04-17 02:20:36 +04:00
. vmode = FB_VMODE_NONINTERLACED ,
} ;
2005-09-04 02:56:11 +04:00
static struct fb_fix_screeninfo pmagbbfb_fix __initdata = {
2005-04-17 02:20:36 +04:00
. id = " PMAGB-BA " ,
2005-09-04 02:56:11 +04:00
. smem_len = ( 2048 * 1024 ) ,
2005-04-17 02:20:36 +04:00
. type = FB_TYPE_PACKED_PIXELS ,
. visual = FB_VISUAL_PSEUDOCOLOR ,
2005-09-04 02:56:11 +04:00
. mmio_len = PMAGB_B_FBMEM ,
} ;
static inline void sfb_write ( struct pmagbbfb_par * par , unsigned int reg , u32 v )
{
writel ( v , par - > sfb + reg / 4 ) ;
2005-04-17 02:20:36 +04:00
}
2005-09-04 02:56:11 +04:00
static inline u32 sfb_read ( struct pmagbbfb_par * par , unsigned int reg )
{
return readl ( par - > sfb + reg / 4 ) ;
}
static inline void dac_write ( struct pmagbbfb_par * par , unsigned int reg , u8 v )
2005-04-17 02:20:36 +04:00
{
2005-09-04 02:56:11 +04:00
writeb ( v , par - > dac + reg / 4 ) ;
2005-04-17 02:20:36 +04:00
}
2005-09-04 02:56:11 +04:00
static inline u8 dac_read ( struct pmagbbfb_par * par , unsigned int reg )
{
return readb ( par - > dac + reg / 4 ) ;
}
static inline void gp0_write ( struct pmagbbfb_par * par , u32 v )
{
writel ( v , par - > mmio + PMAGB_B_GP0 ) ;
}
2005-04-17 02:20:36 +04:00
/*
2005-09-04 02:56:11 +04:00
* Set the palette .
2005-04-17 02:20:36 +04:00
*/
2005-09-04 02:56:11 +04:00
static int pmagbbfb_setcolreg ( unsigned int regno , unsigned int red ,
unsigned int green , unsigned int blue ,
unsigned int transp , struct fb_info * info )
2005-04-17 02:20:36 +04:00
{
2005-09-04 02:56:11 +04:00
struct pmagbbfb_par * par = info - > par ;
BUG_ON ( regno > = info - > cmap . len ) ;
2005-04-17 02:20:36 +04:00
red > > = 8 ; /* The cmap fields are 16 bits */
2005-09-04 02:56:11 +04:00
green > > = 8 ; /* wide, but the hardware colormap */
2005-04-17 02:20:36 +04:00
blue > > = 8 ; /* registers are only 8 bits wide */
2005-09-04 02:56:11 +04:00
mb ( ) ;
dac_write ( par , BT459_ADDR_LO , regno ) ;
dac_write ( par , BT459_ADDR_HI , 0x00 ) ;
wmb ( ) ;
dac_write ( par , BT459_CMAP , red ) ;
wmb ( ) ;
dac_write ( par , BT459_CMAP , green ) ;
wmb ( ) ;
dac_write ( par , BT459_CMAP , blue ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static struct fb_ops pmagbbfb_ops = {
. owner = THIS_MODULE ,
. fb_setcolreg = pmagbbfb_setcolreg ,
. fb_fillrect = cfb_fillrect ,
. fb_copyarea = cfb_copyarea ,
. fb_imageblit = cfb_imageblit ,
} ;
2005-09-04 02:56:11 +04:00
/*
* Turn the hardware cursor off .
*/
static void __init pmagbbfb_erase_cursor ( struct fb_info * info )
{
struct pmagbbfb_par * par = info - > par ;
mb ( ) ;
dac_write ( par , BT459_ADDR_LO , 0x00 ) ;
dac_write ( par , BT459_ADDR_HI , 0x03 ) ;
wmb ( ) ;
dac_write ( par , BT459_DATA , 0x00 ) ;
}
/*
* Set up screen parameters .
*/
static void __init pmagbbfb_screen_setup ( struct fb_info * info )
{
struct pmagbbfb_par * par = info - > par ;
info - > var . xres = ( ( sfb_read ( par , SFB_REG_VID_HOR ) > >
SFB_VID_HOR_PIX_SHIFT ) & SFB_VID_HOR_PIX_MASK ) * 4 ;
info - > var . xres_virtual = info - > var . xres ;
info - > var . yres = ( sfb_read ( par , SFB_REG_VID_VER ) > >
SFB_VID_VER_SL_SHIFT ) & SFB_VID_VER_SL_MASK ;
info - > var . yres_virtual = info - > var . yres ;
info - > var . left_margin = ( ( sfb_read ( par , SFB_REG_VID_HOR ) > >
SFB_VID_HOR_BP_SHIFT ) &
SFB_VID_HOR_BP_MASK ) * 4 ;
info - > var . right_margin = ( ( sfb_read ( par , SFB_REG_VID_HOR ) > >
SFB_VID_HOR_FP_SHIFT ) &
SFB_VID_HOR_FP_MASK ) * 4 ;
info - > var . upper_margin = ( sfb_read ( par , SFB_REG_VID_VER ) > >
SFB_VID_VER_BP_SHIFT ) & SFB_VID_VER_BP_MASK ;
info - > var . lower_margin = ( sfb_read ( par , SFB_REG_VID_VER ) > >
SFB_VID_VER_FP_SHIFT ) & SFB_VID_VER_FP_MASK ;
info - > var . hsync_len = ( ( sfb_read ( par , SFB_REG_VID_HOR ) > >
SFB_VID_HOR_SYN_SHIFT ) &
SFB_VID_HOR_SYN_MASK ) * 4 ;
info - > var . vsync_len = ( sfb_read ( par , SFB_REG_VID_VER ) > >
SFB_VID_VER_SYN_SHIFT ) & SFB_VID_VER_SYN_MASK ;
info - > fix . line_length = info - > var . xres ;
} ;
/*
* Determine oscillator configuration .
*/
static void __init pmagbbfb_osc_setup ( struct fb_info * info )
2005-04-17 02:20:36 +04:00
{
2005-09-04 02:56:11 +04:00
static unsigned int pmagbbfb_freqs [ ] __initdata = {
2006-12-08 13:40:21 +03:00
130808 , 119843 , 104000 , 92980 , 74370 , 72800 ,
2005-09-04 02:56:11 +04:00
69197 , 66000 , 65000 , 50350 , 36000 , 32000 , 25175
} ;
struct pmagbbfb_par * par = info - > par ;
2007-02-06 03:28:29 +03:00
struct tc_bus * tbus = to_tc_dev ( info - > device ) - > bus ;
2005-09-04 02:56:11 +04:00
u32 count0 = 8 , count1 = 8 , counttc = 16 * 256 + 8 ;
2007-02-06 03:28:29 +03:00
u32 freq0 , freq1 , freqtc = tc_get_speed ( tbus ) / 250 ;
2005-09-04 02:56:11 +04:00
int i , j ;
gp0_write ( par , 0 ) ; /* select Osc0 */
for ( j = 0 ; j < 16 ; j + + ) {
mb ( ) ;
sfb_write ( par , SFB_REG_TCCLK_COUNT , 0 ) ;
mb ( ) ;
for ( i = 0 ; i < 100 ; i + + ) { /* nominally max. 20.5us */
if ( sfb_read ( par , SFB_REG_TCCLK_COUNT ) = = 0 )
break ;
udelay ( 1 ) ;
}
count0 + = sfb_read ( par , SFB_REG_VIDCLK_COUNT ) ;
}
gp0_write ( par , 1 ) ; /* select Osc1 */
for ( j = 0 ; j < 16 ; j + + ) {
mb ( ) ;
sfb_write ( par , SFB_REG_TCCLK_COUNT , 0 ) ;
for ( i = 0 ; i < 100 ; i + + ) { /* nominally max. 20.5us */
if ( sfb_read ( par , SFB_REG_TCCLK_COUNT ) = = 0 )
break ;
udelay ( 1 ) ;
}
count1 + = sfb_read ( par , SFB_REG_VIDCLK_COUNT ) ;
}
freq0 = ( freqtc * count0 + counttc / 2 ) / counttc ;
par - > osc0 = freq0 ;
if ( freq0 > = pmagbbfb_freqs [ 0 ] - ( pmagbbfb_freqs [ 0 ] + 32 ) / 64 & &
freq0 < = pmagbbfb_freqs [ 0 ] + ( pmagbbfb_freqs [ 0 ] + 32 ) / 64 )
par - > osc0 = pmagbbfb_freqs [ 0 ] ;
freq1 = ( par - > osc0 * count1 + count0 / 2 ) / count0 ;
par - > osc1 = freq1 ;
2006-03-27 13:17:39 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( pmagbbfb_freqs ) ; i + + )
2005-09-04 02:56:11 +04:00
if ( freq1 > = pmagbbfb_freqs [ i ] -
( pmagbbfb_freqs [ i ] + 128 ) / 256 & &
freq1 < = pmagbbfb_freqs [ i ] +
( pmagbbfb_freqs [ i ] + 128 ) / 256 ) {
par - > osc1 = pmagbbfb_freqs [ i ] ;
break ;
}
if ( par - > osc0 - par - > osc1 < = ( par - > osc0 + par - > osc1 + 256 ) / 512 | |
par - > osc1 - par - > osc0 < = ( par - > osc0 + par - > osc1 + 256 ) / 512 )
par - > osc1 = 0 ;
gp0_write ( par , par - > osc1 ! = 0 ) ; /* reselect OscX */
info - > var . pixclock = par - > osc1 ?
( 1000000000 + par - > osc1 / 2 ) / par - > osc1 :
( 1000000000 + par - > osc0 / 2 ) / par - > osc0 ;
} ;
2007-02-06 03:28:29 +03:00
static int __init pmagbbfb_probe ( struct device * dev )
2005-09-04 02:56:11 +04:00
{
2007-02-06 03:28:29 +03:00
struct tc_dev * tdev = to_tc_dev ( dev ) ;
resource_size_t start , len ;
2005-09-04 02:56:11 +04:00
struct fb_info * info ;
struct pmagbbfb_par * par ;
2007-02-06 03:28:29 +03:00
char freq0 [ 12 ] , freq1 [ 12 ] ;
2005-09-04 02:56:11 +04:00
u32 vid_base ;
2007-10-16 12:29:36 +04:00
int err ;
2005-09-04 02:56:11 +04:00
2007-02-06 03:28:29 +03:00
info = framebuffer_alloc ( sizeof ( struct pmagbbfb_par ) , dev ) ;
2007-10-16 12:29:36 +04:00
if ( ! info ) {
printk ( KERN_ERR " %s: Cannot allocate memory \n " , dev - > bus_id ) ;
2005-09-04 02:56:11 +04:00
return - ENOMEM ;
2007-10-16 12:29:36 +04:00
}
2005-09-04 02:56:11 +04:00
par = info - > par ;
2007-02-06 03:28:29 +03:00
dev_set_drvdata ( dev , info ) ;
2005-09-04 02:56:11 +04:00
2007-10-16 12:29:36 +04:00
if ( fb_alloc_cmap ( & info - > cmap , 256 , 0 ) < 0 ) {
printk ( KERN_ERR " %s: Cannot allocate color map \n " ,
dev - > bus_id ) ;
err = - ENOMEM ;
2005-09-04 02:56:11 +04:00
goto err_alloc ;
2007-10-16 12:29:36 +04:00
}
2005-09-04 02:56:11 +04:00
2005-04-17 02:20:36 +04:00
info - > fbops = & pmagbbfb_ops ;
info - > fix = pmagbbfb_fix ;
2005-09-04 02:56:11 +04:00
info - > var = pmagbbfb_defined ;
2005-04-17 02:20:36 +04:00
info - > flags = FBINFO_DEFAULT ;
2007-02-06 03:28:29 +03:00
/* Request the I/O MEM resource. */
start = tdev - > resource . start ;
len = tdev - > resource . end - start + 1 ;
2007-10-16 12:29:36 +04:00
if ( ! request_mem_region ( start , len , dev - > bus_id ) ) {
printk ( KERN_ERR " %s: Cannot reserve FB region \n " , dev - > bus_id ) ;
err = - EBUSY ;
2007-02-06 03:28:29 +03:00
goto err_cmap ;
2007-10-16 12:29:36 +04:00
}
2007-02-06 03:28:29 +03:00
2005-09-04 02:56:11 +04:00
/* MMIO mapping setup. */
2007-02-06 03:28:29 +03:00
info - > fix . mmio_start = start ;
2005-09-04 02:56:11 +04:00
par - > mmio = ioremap_nocache ( info - > fix . mmio_start , info - > fix . mmio_len ) ;
2007-10-16 12:29:36 +04:00
if ( ! par - > mmio ) {
printk ( KERN_ERR " %s: Cannot map MMIO \n " , dev - > bus_id ) ;
err = - ENOMEM ;
2007-02-06 03:28:29 +03:00
goto err_resource ;
2007-10-16 12:29:36 +04:00
}
2005-09-04 02:56:11 +04:00
par - > sfb = par - > mmio + PMAGB_B_SFB ;
par - > dac = par - > mmio + PMAGB_B_BT459 ;
/* Frame buffer mapping setup. */
2007-02-06 03:28:29 +03:00
info - > fix . smem_start = start + PMAGB_B_FBMEM ;
2005-09-04 02:56:11 +04:00
par - > smem = ioremap_nocache ( info - > fix . smem_start , info - > fix . smem_len ) ;
2007-10-16 12:29:36 +04:00
if ( ! par - > smem ) {
printk ( KERN_ERR " %s: Cannot map FB \n " , dev - > bus_id ) ;
err = - ENOMEM ;
2005-09-04 02:56:11 +04:00
goto err_mmio_map ;
2007-10-16 12:29:36 +04:00
}
2005-09-04 02:56:11 +04:00
vid_base = sfb_read ( par , SFB_REG_VID_BASE ) ;
info - > screen_base = ( void __iomem * ) par - > smem + vid_base * 0x1000 ;
info - > screen_size = info - > fix . smem_len - 2 * vid_base * 0x1000 ;
pmagbbfb_erase_cursor ( info ) ;
pmagbbfb_screen_setup ( info ) ;
pmagbbfb_osc_setup ( info ) ;
2005-04-17 02:20:36 +04:00
2007-10-16 12:29:36 +04:00
err = register_framebuffer ( info ) ;
if ( err < 0 ) {
printk ( KERN_ERR " %s: Cannot register framebuffer \n " ,
dev - > bus_id ) ;
2005-09-04 02:56:11 +04:00
goto err_smem_map ;
2007-10-16 12:29:36 +04:00
}
2005-09-04 02:56:11 +04:00
2007-02-06 03:28:29 +03:00
get_device ( dev ) ;
2005-09-04 02:56:11 +04:00
snprintf ( freq0 , sizeof ( freq0 ) , " %u.%03uMHz " ,
par - > osc0 / 1000 , par - > osc0 % 1000 ) ;
snprintf ( freq1 , sizeof ( freq1 ) , " %u.%03uMHz " ,
par - > osc1 / 1000 , par - > osc1 % 1000 ) ;
2007-02-06 03:28:29 +03:00
pr_info ( " fb%d: %s frame buffer device at %s \n " ,
info - > node , info - > fix . id , dev - > bus_id ) ;
2005-09-04 02:56:11 +04:00
pr_info ( " fb%d: Osc0: %s, Osc1: %s, Osc%u selected \n " ,
info - > node , freq0 , par - > osc1 ? freq1 : " disabled " ,
par - > osc1 ! = 0 ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
2005-09-04 02:56:11 +04:00
err_smem_map :
iounmap ( par - > smem ) ;
err_mmio_map :
iounmap ( par - > mmio ) ;
2007-02-06 03:28:29 +03:00
err_resource :
release_mem_region ( start , len ) ;
2005-09-04 02:56:11 +04:00
err_cmap :
fb_dealloc_cmap ( & info - > cmap ) ;
err_alloc :
framebuffer_release ( info ) ;
2007-10-16 12:29:36 +04:00
return err ;
2005-04-17 02:20:36 +04:00
}
2007-02-06 03:28:29 +03:00
static int __exit pmagbbfb_remove ( struct device * dev )
2005-09-04 02:56:11 +04:00
{
2007-02-06 03:28:29 +03:00
struct tc_dev * tdev = to_tc_dev ( dev ) ;
struct fb_info * info = dev_get_drvdata ( dev ) ;
2005-09-04 02:56:11 +04:00
struct pmagbbfb_par * par = info - > par ;
2007-02-06 03:28:29 +03:00
resource_size_t start , len ;
2005-09-04 02:56:11 +04:00
2007-02-06 03:28:29 +03:00
put_device ( dev ) ;
2005-09-04 02:56:11 +04:00
unregister_framebuffer ( info ) ;
iounmap ( par - > smem ) ;
iounmap ( par - > mmio ) ;
2007-02-06 03:28:29 +03:00
start = tdev - > resource . start ;
len = tdev - > resource . end - start + 1 ;
release_mem_region ( start , len ) ;
2005-09-04 02:56:11 +04:00
fb_dealloc_cmap ( & info - > cmap ) ;
framebuffer_release ( info ) ;
2007-02-06 03:28:29 +03:00
return 0 ;
2005-09-04 02:56:11 +04:00
}
2005-04-17 02:20:36 +04:00
2005-09-04 02:56:11 +04:00
/*
2007-02-06 03:28:29 +03:00
* Initialize the framebuffer .
2005-09-04 02:56:11 +04:00
*/
2007-02-06 03:28:29 +03:00
static const struct tc_device_id pmagbbfb_tc_table [ ] = {
{ " DEC " , " PMAGB-BA " } ,
{ }
} ;
MODULE_DEVICE_TABLE ( tc , pmagbbfb_tc_table ) ;
static struct tc_driver pmagbbfb_driver = {
. id_table = pmagbbfb_tc_table ,
. driver = {
. name = " pmagbbfb " ,
. bus = & tc_bus_type ,
. probe = pmagbbfb_probe ,
. remove = __exit_p ( pmagbbfb_remove ) ,
} ,
} ;
2005-09-04 02:56:11 +04:00
static int __init pmagbbfb_init ( void )
2005-04-17 02:20:36 +04:00
{
2007-02-06 03:28:29 +03:00
# ifndef MODULE
2005-04-17 02:20:36 +04:00
if ( fb_get_options ( " pmagbbfb " , NULL ) )
2005-09-04 02:56:11 +04:00
return - ENXIO ;
2007-02-06 03:28:29 +03:00
# endif
return tc_register_driver ( & pmagbbfb_driver ) ;
2005-09-04 02:56:11 +04:00
}
static void __exit pmagbbfb_exit ( void )
{
2007-02-06 03:28:29 +03:00
tc_unregister_driver ( & pmagbbfb_driver ) ;
2005-04-17 02:20:36 +04:00
}
2005-09-04 02:56:11 +04:00
2005-04-17 02:20:36 +04:00
module_init ( pmagbbfb_init ) ;
2005-09-04 02:56:11 +04:00
module_exit ( pmagbbfb_exit ) ;
2005-04-17 02:20:36 +04:00
MODULE_LICENSE ( " GPL " ) ;