2007-05-08 11:40:18 +04:00
/*
* xilinxfb . c
*
* Xilinx TFT LCD frame buffer driver
*
* Author : MontaVista Software , Inc .
* source @ mvista . com
*
* 2002 - 2007 ( c ) MontaVista Software , Inc . This file is licensed under the
* terms of the GNU General Public License version 2. This program is licensed
* " as is " without any warranty of any kind , whether express or implied .
*/
/*
* This driver was based on au1100fb . c by MontaVista rewritten for 2.6
* by Embedded Alley Solutions < source @ embeddedalley . com > , which in turn
* was based on skeletonfb . c , Skeleton for a frame buffer device by
* Geert Uytterhoeven .
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/version.h>
# include <linux/errno.h>
# include <linux/string.h>
# include <linux/mm.h>
# include <linux/fb.h>
# include <linux/init.h>
# include <linux/dma-mapping.h>
# include <linux/platform_device.h>
# include <asm/io.h>
2007-10-01 01:47:00 +04:00
# include <linux/xilinxfb.h>
2007-05-08 11:40:18 +04:00
# define DRIVER_NAME "xilinxfb"
# define DRIVER_DESCRIPTION "Xilinx TFT LCD frame buffer driver"
/*
* Xilinx calls it " PLB TFT LCD Controller " though it can also be used for
* the VGA port on the Xilinx ML40x board . This is a hardware display controller
* for a 640 x480 resolution TFT or VGA screen .
*
* The interface to the framebuffer is nice and simple . There are two
* control registers . The first tells the LCD interface where in memory
* the frame buffer is ( only the 11 most significant bits are used , so
* don ' t start thinking about scrolling ) . The second allows the LCD to
* be turned on or off as well as rotated 180 degrees .
*/
# define NUM_REGS 2
# define REG_FB_ADDR 0
# define REG_CTRL 1
# define REG_CTRL_ENABLE 0x0001
# define REG_CTRL_ROTATE 0x0002
/*
* The hardware only handles a single mode : 640 x480 24 bit true
* color . Each pixel gets a word ( 32 bits ) of memory . Within each word ,
* the 8 most significant bits are ignored , the next 8 bits are the red
* level , the next 8 bits are the green level and the 8 least
* significant bits are the blue level . Each row of the LCD uses 1024
* words , but only the first 640 pixels are displayed with the other 384
* words being ignored . There are 480 rows .
*/
# define BYTES_PER_PIXEL 4
# define BITS_PER_PIXEL (BYTES_PER_PIXEL * 8)
# define XRES 640
# define YRES 480
# define XRES_VIRTUAL 1024
# define YRES_VIRTUAL YRES
# define LINE_LENGTH (XRES_VIRTUAL * BYTES_PER_PIXEL)
# define FB_SIZE (YRES_VIRTUAL * LINE_LENGTH)
# define RED_SHIFT 16
# define GREEN_SHIFT 8
# define BLUE_SHIFT 0
# define PALETTE_ENTRIES_NO 16 /* passed to fb_alloc_cmap() */
/*
* Here are the default fb_fix_screeninfo and fb_var_screeninfo structures
*/
2007-07-31 11:37:38 +04:00
static struct fb_fix_screeninfo xilinx_fb_fix = {
2007-05-08 11:40:18 +04:00
. id = " Xilinx " ,
. type = FB_TYPE_PACKED_PIXELS ,
. visual = FB_VISUAL_TRUECOLOR ,
. smem_len = FB_SIZE ,
. line_length = LINE_LENGTH ,
. accel = FB_ACCEL_NONE
} ;
2007-07-31 11:37:38 +04:00
static struct fb_var_screeninfo xilinx_fb_var = {
2007-05-08 11:40:18 +04:00
. xres = XRES ,
. yres = YRES ,
. xres_virtual = XRES_VIRTUAL ,
. yres_virtual = YRES_VIRTUAL ,
. bits_per_pixel = BITS_PER_PIXEL ,
. red = { RED_SHIFT , 8 , 0 } ,
. green = { GREEN_SHIFT , 8 , 0 } ,
. blue = { BLUE_SHIFT , 8 , 0 } ,
. transp = { 0 , 0 , 0 } ,
. activate = FB_ACTIVATE_NOW
} ;
struct xilinxfb_drvdata {
struct fb_info info ; /* FB driver info record */
u32 regs_phys ; /* phys. address of the control registers */
u32 __iomem * regs ; /* virt. address of the control registers */
unsigned char __iomem * fb_virt ; /* virt. address of the frame buffer */
dma_addr_t fb_phys ; /* phys. address of the frame buffer */
u32 reg_ctrl_default ;
u32 pseudo_palette [ PALETTE_ENTRIES_NO ] ;
/* Fake palette of 16 colors */
} ;
# define to_xilinxfb_drvdata(_info) \
container_of ( _info , struct xilinxfb_drvdata , info )
/*
* The LCD controller has DCR interface to its registers , but all
* the boards and configurations the driver has been tested with
* use opb2dcr bridge . So the registers are seen as memory mapped .
* This macro is to make it simple to add the direct DCR access
* when it ' s needed .
*/
# define xilinx_fb_out_be32(driverdata, offset, val) \
out_be32 ( driverdata - > regs + offset , val )
static int
xilinx_fb_setcolreg ( unsigned regno , unsigned red , unsigned green , unsigned blue ,
unsigned transp , struct fb_info * fbi )
{
u32 * palette = fbi - > pseudo_palette ;
if ( regno > = PALETTE_ENTRIES_NO )
return - EINVAL ;
if ( fbi - > var . grayscale ) {
/* Convert color to grayscale.
* grayscale = 0.30 * R + 0.59 * G + 0.11 * B */
red = green = blue =
( red * 77 + green * 151 + blue * 28 + 127 ) > > 8 ;
}
/* fbi->fix.visual is always FB_VISUAL_TRUECOLOR */
/* We only handle 8 bits of each color. */
red > > = 8 ;
green > > = 8 ;
blue > > = 8 ;
palette [ regno ] = ( red < < RED_SHIFT ) | ( green < < GREEN_SHIFT ) |
( blue < < BLUE_SHIFT ) ;
return 0 ;
}
static int
xilinx_fb_blank ( int blank_mode , struct fb_info * fbi )
{
struct xilinxfb_drvdata * drvdata = to_xilinxfb_drvdata ( fbi ) ;
switch ( blank_mode ) {
case FB_BLANK_UNBLANK :
/* turn on panel */
xilinx_fb_out_be32 ( drvdata , REG_CTRL , drvdata - > reg_ctrl_default ) ;
break ;
case FB_BLANK_NORMAL :
case FB_BLANK_VSYNC_SUSPEND :
case FB_BLANK_HSYNC_SUSPEND :
case FB_BLANK_POWERDOWN :
/* turn off panel */
xilinx_fb_out_be32 ( drvdata , REG_CTRL , 0 ) ;
default :
break ;
}
return 0 ; /* success */
}
static struct fb_ops xilinxfb_ops =
{
. owner = THIS_MODULE ,
. fb_setcolreg = xilinx_fb_setcolreg ,
. fb_blank = xilinx_fb_blank ,
. fb_fillrect = cfb_fillrect ,
. fb_copyarea = cfb_copyarea ,
. fb_imageblit = cfb_imageblit ,
} ;
/* === The device driver === */
static int
xilinxfb_drv_probe ( struct device * dev )
{
struct platform_device * pdev ;
struct xilinxfb_platform_data * pdata ;
struct xilinxfb_drvdata * drvdata ;
struct resource * regs_res ;
int retval ;
if ( ! dev )
return - EINVAL ;
pdev = to_platform_device ( dev ) ;
pdata = pdev - > dev . platform_data ;
drvdata = kzalloc ( sizeof ( * drvdata ) , GFP_KERNEL ) ;
if ( ! drvdata ) {
printk ( KERN_ERR " Couldn't allocate device private record \n " ) ;
return - ENOMEM ;
}
dev_set_drvdata ( dev , drvdata ) ;
/* Map the control registers in */
regs_res = platform_get_resource ( pdev , IORESOURCE_IO , 0 ) ;
if ( ! regs_res | | ( regs_res - > end - regs_res - > start + 1 < 8 ) ) {
printk ( KERN_ERR " Couldn't get registers resource \n " ) ;
retval = - EFAULT ;
goto failed1 ;
}
if ( ! request_mem_region ( regs_res - > start , 8 , DRIVER_NAME ) ) {
printk ( KERN_ERR
" Couldn't lock memory region at 0x%08X \n " ,
regs_res - > start ) ;
retval = - EBUSY ;
goto failed1 ;
}
drvdata - > regs = ( u32 __iomem * ) ioremap ( regs_res - > start , 8 ) ;
drvdata - > regs_phys = regs_res - > start ;
/* Allocate the framebuffer memory */
drvdata - > fb_virt = dma_alloc_coherent ( dev , PAGE_ALIGN ( FB_SIZE ) ,
& drvdata - > fb_phys , GFP_KERNEL ) ;
if ( ! drvdata - > fb_virt ) {
printk ( KERN_ERR " Could not allocate frame buffer memory \n " ) ;
retval = - ENOMEM ;
goto failed2 ;
}
/* Clear (turn to black) the framebuffer */
memset_io ( ( void * ) drvdata - > fb_virt , 0 , FB_SIZE ) ;
/* Tell the hardware where the frame buffer is */
xilinx_fb_out_be32 ( drvdata , REG_FB_ADDR , drvdata - > fb_phys ) ;
/* Turn on the display */
2007-07-31 11:37:39 +04:00
drvdata - > reg_ctrl_default = REG_CTRL_ENABLE ;
if ( pdata & & pdata - > rotate_screen )
drvdata - > reg_ctrl_default | = REG_CTRL_ROTATE ;
2007-05-08 11:40:18 +04:00
xilinx_fb_out_be32 ( drvdata , REG_CTRL , drvdata - > reg_ctrl_default ) ;
/* Fill struct fb_info */
drvdata - > info . device = dev ;
drvdata - > info . screen_base = drvdata - > fb_virt ;
drvdata - > info . fbops = & xilinxfb_ops ;
drvdata - > info . fix = xilinx_fb_fix ;
drvdata - > info . fix . smem_start = drvdata - > fb_phys ;
drvdata - > info . pseudo_palette = drvdata - > pseudo_palette ;
if ( fb_alloc_cmap ( & drvdata - > info . cmap , PALETTE_ENTRIES_NO , 0 ) < 0 ) {
printk ( KERN_ERR " Fail to allocate colormap (%d entries) \n " ,
PALETTE_ENTRIES_NO ) ;
retval = - EFAULT ;
goto failed3 ;
}
drvdata - > info . flags = FBINFO_DEFAULT ;
2007-07-31 11:37:39 +04:00
if ( pdata ) {
xilinx_fb_var . height = pdata - > screen_height_mm ;
xilinx_fb_var . width = pdata - > screen_width_mm ;
}
2007-05-08 11:40:18 +04:00
drvdata - > info . var = xilinx_fb_var ;
/* Register new frame buffer */
if ( register_framebuffer ( & drvdata - > info ) < 0 ) {
printk ( KERN_ERR " Could not register frame buffer \n " ) ;
retval = - EINVAL ;
goto failed4 ;
}
return 0 ; /* success */
failed4 :
fb_dealloc_cmap ( & drvdata - > info . cmap ) ;
failed3 :
dma_free_coherent ( dev , PAGE_ALIGN ( FB_SIZE ) , drvdata - > fb_virt ,
drvdata - > fb_phys ) ;
/* Turn off the display */
xilinx_fb_out_be32 ( drvdata , REG_CTRL , 0 ) ;
iounmap ( drvdata - > regs ) ;
failed2 :
release_mem_region ( regs_res - > start , 8 ) ;
failed1 :
kfree ( drvdata ) ;
dev_set_drvdata ( dev , NULL ) ;
return retval ;
}
static int
xilinxfb_drv_remove ( struct device * dev )
{
struct xilinxfb_drvdata * drvdata ;
if ( ! dev )
return - ENODEV ;
drvdata = ( struct xilinxfb_drvdata * ) dev_get_drvdata ( dev ) ;
# if !defined(CONFIG_FRAMEBUFFER_CONSOLE) && defined(CONFIG_LOGO)
xilinx_fb_blank ( VESA_POWERDOWN , & drvdata - > info ) ;
# endif
unregister_framebuffer ( & drvdata - > info ) ;
fb_dealloc_cmap ( & drvdata - > info . cmap ) ;
dma_free_coherent ( dev , PAGE_ALIGN ( FB_SIZE ) , drvdata - > fb_virt ,
drvdata - > fb_phys ) ;
/* Turn off the display */
xilinx_fb_out_be32 ( drvdata , REG_CTRL , 0 ) ;
iounmap ( drvdata - > regs ) ;
release_mem_region ( drvdata - > regs_phys , 8 ) ;
kfree ( drvdata ) ;
dev_set_drvdata ( dev , NULL ) ;
return 0 ;
}
static struct device_driver xilinxfb_driver = {
. name = DRIVER_NAME ,
. bus = & platform_bus_type ,
. probe = xilinxfb_drv_probe ,
. remove = xilinxfb_drv_remove
} ;
static int __init
xilinxfb_init ( void )
{
/*
* No kernel boot options used ,
* so we just need to register the driver
*/
return driver_register ( & xilinxfb_driver ) ;
}
static void __exit
xilinxfb_cleanup ( void )
{
driver_unregister ( & xilinxfb_driver ) ;
}
module_init ( xilinxfb_init ) ;
module_exit ( xilinxfb_cleanup ) ;
MODULE_AUTHOR ( " MontaVista Software, Inc. <source@mvista.com> " ) ;
MODULE_DESCRIPTION ( DRIVER_DESCRIPTION ) ;
MODULE_LICENSE ( " GPL " ) ;