2007-05-08 11:37:43 +04:00
/*
2008-04-28 13:15:40 +04:00
* linux / drivers / video / hecubafb . c - - FB driver for Hecuba / Apollo controller
2007-05-08 11:37:43 +04:00
*
* Copyright ( C ) 2006 , Jaya Kumar
* This work was sponsored by CIS ( M ) Sdn Bhd
*
* 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 .
*
* Layout is based on skeletonfb . c by James Simmons and Geert Uytterhoeven .
* This work was possible because of apollo display code from E - Ink ' s website
* http : //support.eink.com/community
* All information used to write this code is from public material made
* available by E - Ink on its support site . Some commands such as 0xA4
* were found by looping through cmd = 0x00 thru 0xFF and supplying random
* values . There are other commands that the display is capable of ,
* beyond the 5 used here but they are more complex .
*
2008-04-28 13:15:40 +04:00
* This driver is written to be used with the Hecuba display architecture .
* The actual display chip is called Apollo and the interface electronics
* it needs is called Hecuba .
2007-05-08 11:37:43 +04:00
*
2008-04-28 13:15:40 +04:00
* It is intended to be architecture independent . A board specific driver
* must be used to perform all the physical IO interactions . An example
* is provided as n411 . c
2007-05-08 11:37:43 +04:00
*
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/string.h>
# include <linux/mm.h>
# include <linux/vmalloc.h>
# include <linux/delay.h>
# include <linux/interrupt.h>
# include <linux/fb.h>
# include <linux/init.h>
# include <linux/platform_device.h>
# include <linux/list.h>
2007-10-16 12:29:04 +04:00
# include <linux/uaccess.h>
2007-05-08 11:37:43 +04:00
2008-04-28 13:15:40 +04:00
# include <video/hecubafb.h>
2007-05-08 11:37:43 +04:00
/* Display specific information */
# define DPY_W 600
# define DPY_H 800
static struct fb_fix_screeninfo hecubafb_fix __devinitdata = {
. id = " hecubafb " ,
. type = FB_TYPE_PACKED_PIXELS ,
. visual = FB_VISUAL_MONO01 ,
. xpanstep = 0 ,
. ypanstep = 0 ,
. ywrapstep = 0 ,
2008-04-28 13:15:40 +04:00
. line_length = DPY_W ,
2007-05-08 11:37:43 +04:00
. accel = FB_ACCEL_NONE ,
} ;
static struct fb_var_screeninfo hecubafb_var __devinitdata = {
. xres = DPY_W ,
. yres = DPY_H ,
. xres_virtual = DPY_W ,
. yres_virtual = DPY_H ,
. bits_per_pixel = 1 ,
. nonstd = 1 ,
} ;
2008-04-28 13:15:40 +04:00
/* main hecubafb functions */
2007-05-08 11:37:43 +04:00
2007-05-08 11:37:45 +04:00
static void apollo_send_data ( struct hecubafb_par * par , unsigned char data )
2007-05-08 11:37:43 +04:00
{
/* set data */
2008-04-28 13:15:40 +04:00
par - > board - > set_data ( par , data ) ;
2007-05-08 11:37:43 +04:00
/* set DS low */
2008-04-28 13:15:40 +04:00
par - > board - > set_ctl ( par , HCB_DS_BIT , 0 ) ;
2007-05-08 11:37:43 +04:00
2008-04-28 13:15:40 +04:00
/* wait for ack */
par - > board - > wait_for_ack ( par , 0 ) ;
2007-05-08 11:37:43 +04:00
/* set DS hi */
2008-04-28 13:15:40 +04:00
par - > board - > set_ctl ( par , HCB_DS_BIT , 1 ) ;
2007-05-08 11:37:43 +04:00
2008-04-28 13:15:40 +04:00
/* wait for ack to clear */
par - > board - > wait_for_ack ( par , 1 ) ;
2007-05-08 11:37:43 +04:00
}
2007-05-08 11:37:45 +04:00
static void apollo_send_command ( struct hecubafb_par * par , unsigned char data )
2007-05-08 11:37:43 +04:00
{
/* command so set CD to high */
2008-04-28 13:15:40 +04:00
par - > board - > set_ctl ( par , HCB_CD_BIT , 1 ) ;
2007-05-08 11:37:43 +04:00
/* actually strobe with command */
apollo_send_data ( par , data ) ;
/* clear CD back to low */
2008-04-28 13:15:40 +04:00
par - > board - > set_ctl ( par , HCB_CD_BIT , 0 ) ;
2007-05-08 11:37:43 +04:00
}
static void hecubafb_dpy_update ( struct hecubafb_par * par )
{
int i ;
2007-05-08 11:38:50 +04:00
unsigned char * buf = ( unsigned char __force * ) par - > info - > screen_base ;
2007-05-08 11:37:43 +04:00
2008-04-28 13:15:40 +04:00
apollo_send_command ( par , APOLLO_START_NEW_IMG ) ;
2007-05-08 11:37:43 +04:00
for ( i = 0 ; i < ( DPY_W * DPY_H / 8 ) ; i + + ) {
apollo_send_data ( par , * ( buf + + ) ) ;
}
2008-04-28 13:15:40 +04:00
apollo_send_command ( par , APOLLO_STOP_IMG_DATA ) ;
apollo_send_command ( par , APOLLO_DISPLAY_IMG ) ;
2007-05-08 11:37:43 +04:00
}
/* this is called back from the deferred io workqueue */
static void hecubafb_dpy_deferred_io ( struct fb_info * info ,
struct list_head * pagelist )
{
hecubafb_dpy_update ( info - > par ) ;
}
static void hecubafb_fillrect ( struct fb_info * info ,
const struct fb_fillrect * rect )
{
struct hecubafb_par * par = info - > par ;
2007-05-08 11:39:00 +04:00
sys_fillrect ( info , rect ) ;
2007-05-08 11:37:43 +04:00
hecubafb_dpy_update ( par ) ;
}
static void hecubafb_copyarea ( struct fb_info * info ,
const struct fb_copyarea * area )
{
struct hecubafb_par * par = info - > par ;
2007-05-08 11:39:00 +04:00
sys_copyarea ( info , area ) ;
2007-05-08 11:37:43 +04:00
hecubafb_dpy_update ( par ) ;
}
static void hecubafb_imageblit ( struct fb_info * info ,
const struct fb_image * image )
{
struct hecubafb_par * par = info - > par ;
2007-05-08 11:39:00 +04:00
sys_imageblit ( info , image ) ;
2007-05-08 11:37:43 +04:00
hecubafb_dpy_update ( par ) ;
}
/*
* this is the slow path from userspace . they can seek and write to
* the fb . it ' s inefficient to do anything less than a full screen draw
*/
2007-05-08 11:39:02 +04:00
static ssize_t hecubafb_write ( struct fb_info * info , const char __user * buf ,
2007-05-08 11:37:43 +04:00
size_t count , loff_t * ppos )
{
2008-04-28 13:15:37 +04:00
struct hecubafb_par * par = info - > par ;
unsigned long p = * ppos ;
void * dst ;
int err = 0 ;
unsigned long total_size ;
if ( info - > state ! = FBINFO_STATE_RUNNING )
return - EPERM ;
2007-05-08 11:37:43 +04:00
2008-04-28 13:15:37 +04:00
total_size = info - > fix . smem_len ;
2007-05-08 11:37:43 +04:00
2008-04-28 13:15:37 +04:00
if ( p > total_size )
return - EFBIG ;
2007-05-08 11:37:43 +04:00
2008-04-28 13:15:37 +04:00
if ( count > total_size ) {
err = - EFBIG ;
count = total_size ;
2007-05-08 11:37:43 +04:00
}
2008-04-28 13:15:37 +04:00
if ( count + p > total_size ) {
if ( ! err )
err = - ENOSPC ;
2007-05-08 11:37:43 +04:00
2008-04-28 13:15:37 +04:00
count = total_size - p ;
2007-05-08 11:37:43 +04:00
}
2008-04-28 13:15:37 +04:00
dst = ( void __force * ) ( info - > screen_base + p ) ;
2007-05-08 11:37:43 +04:00
2008-04-28 13:15:37 +04:00
if ( copy_from_user ( dst , buf , count ) )
err = - EFAULT ;
if ( ! err )
* ppos + = count ;
hecubafb_dpy_update ( par ) ;
2007-05-08 11:37:43 +04:00
2008-04-28 13:15:37 +04:00
return ( err ) ? err : count ;
2007-05-08 11:37:43 +04:00
}
static struct fb_ops hecubafb_ops = {
. owner = THIS_MODULE ,
2007-05-08 11:39:06 +04:00
. fb_read = fb_sys_read ,
2007-05-08 11:37:43 +04:00
. fb_write = hecubafb_write ,
. fb_fillrect = hecubafb_fillrect ,
. fb_copyarea = hecubafb_copyarea ,
. fb_imageblit = hecubafb_imageblit ,
} ;
static struct fb_deferred_io hecubafb_defio = {
. delay = HZ ,
. deferred_io = hecubafb_dpy_deferred_io ,
} ;
static int __devinit hecubafb_probe ( struct platform_device * dev )
{
struct fb_info * info ;
2008-04-28 13:15:40 +04:00
struct hecuba_board * board ;
2007-05-08 11:37:43 +04:00
int retval = - ENOMEM ;
int videomemorysize ;
unsigned char * videomemory ;
struct hecubafb_par * par ;
2008-04-28 13:15:40 +04:00
/* pick up board specific routines */
board = dev - > dev . platform_data ;
if ( ! board )
return - EINVAL ;
/* try to count device specific driver, if can't, platform recalls */
if ( ! try_module_get ( board - > owner ) )
return - ENODEV ;
2007-05-08 11:37:43 +04:00
videomemorysize = ( DPY_W * DPY_H ) / 8 ;
2011-05-28 22:13:33 +04:00
videomemory = vzalloc ( videomemorysize ) ;
if ( ! videomemory )
2011-06-17 20:25:12 +04:00
goto err_videomem_alloc ;
2007-05-08 11:37:43 +04:00
info = framebuffer_alloc ( sizeof ( struct hecubafb_par ) , & dev - > dev ) ;
if ( ! info )
2008-04-28 13:15:40 +04:00
goto err_fballoc ;
2007-05-08 11:37:43 +04:00
2008-04-28 13:15:40 +04:00
info - > screen_base = ( char __force __iomem * ) videomemory ;
2007-05-08 11:37:43 +04:00
info - > fbops = & hecubafb_ops ;
info - > var = hecubafb_var ;
info - > fix = hecubafb_fix ;
info - > fix . smem_len = videomemorysize ;
par = info - > par ;
par - > info = info ;
2008-04-28 13:15:40 +04:00
par - > board = board ;
par - > send_command = apollo_send_command ;
par - > send_data = apollo_send_data ;
2007-05-08 11:37:43 +04:00
2009-12-03 18:31:58 +03:00
info - > flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB ;
2007-05-08 11:37:43 +04:00
info - > fbdefio = & hecubafb_defio ;
fb_deferred_io_init ( info ) ;
retval = register_framebuffer ( info ) ;
if ( retval < 0 )
2008-04-28 13:15:40 +04:00
goto err_fbreg ;
2007-05-08 11:37:43 +04:00
platform_set_drvdata ( dev , info ) ;
printk ( KERN_INFO
" fb%d: Hecuba frame buffer device, using %dK of video memory \n " ,
info - > node , videomemorysize > > 10 ) ;
/* this inits the dpy */
2008-04-28 13:15:40 +04:00
retval = par - > board - > init ( par ) ;
if ( retval < 0 )
goto err_fbreg ;
2007-05-08 11:37:43 +04:00
return 0 ;
2008-04-28 13:15:40 +04:00
err_fbreg :
2007-05-08 11:37:43 +04:00
framebuffer_release ( info ) ;
2008-04-28 13:15:40 +04:00
err_fballoc :
2007-05-08 11:37:43 +04:00
vfree ( videomemory ) ;
2011-06-17 20:25:12 +04:00
err_videomem_alloc :
2008-04-28 13:15:40 +04:00
module_put ( board - > owner ) ;
2007-05-08 11:37:43 +04:00
return retval ;
}
static int __devexit hecubafb_remove ( struct platform_device * dev )
{
struct fb_info * info = platform_get_drvdata ( dev ) ;
if ( info ) {
2008-04-28 13:15:40 +04:00
struct hecubafb_par * par = info - > par ;
2007-05-08 11:37:43 +04:00
fb_deferred_io_cleanup ( info ) ;
unregister_framebuffer ( info ) ;
2007-05-08 11:38:50 +04:00
vfree ( ( void __force * ) info - > screen_base ) ;
2008-04-28 13:15:40 +04:00
if ( par - > board - > remove )
par - > board - > remove ( par ) ;
module_put ( par - > board - > owner ) ;
2007-05-08 11:37:43 +04:00
framebuffer_release ( info ) ;
}
return 0 ;
}
static struct platform_driver hecubafb_driver = {
. probe = hecubafb_probe ,
2011-02-13 16:06:23 +03:00
. remove = __devexit_p ( hecubafb_remove ) ,
2007-05-08 11:37:43 +04:00
. driver = {
2008-04-28 13:15:40 +04:00
. owner = THIS_MODULE ,
2007-05-08 11:37:43 +04:00
. name = " hecubafb " ,
} ,
} ;
static int __init hecubafb_init ( void )
{
2008-04-28 13:15:40 +04:00
return platform_driver_register ( & hecubafb_driver ) ;
2007-05-08 11:37:43 +04:00
}
static void __exit hecubafb_exit ( void )
{
platform_driver_unregister ( & hecubafb_driver ) ;
}
module_init ( hecubafb_init ) ;
module_exit ( hecubafb_exit ) ;
2008-04-28 13:15:40 +04:00
MODULE_DESCRIPTION ( " fbdev driver for Hecuba/Apollo controller " ) ;
2007-05-08 11:37:43 +04:00
MODULE_AUTHOR ( " Jaya Kumar " ) ;
MODULE_LICENSE ( " GPL " ) ;