2005-04-17 02:20:36 +04:00
/*
* linux / drivers / video / hitfb . c - - Hitachi LCD frame buffer device
*
* ( C ) 1999 Mihai Spatar
* ( C ) 2000 YAEGASHI Takeshi
* ( C ) 2003 , 2004 Paul Mundt
2006-09-27 08:47:22 +04:00
* ( C ) 2003 , 2004 , 2006 Andriy Skulysh
2005-04-17 02:20:36 +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 .
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/string.h>
# include <linux/mm.h>
# include <linux/delay.h>
# include <linux/init.h>
2006-09-27 08:47:22 +04:00
# include <linux/platform_device.h>
2005-04-17 02:20:36 +04:00
# include <linux/fb.h>
# include <asm/machvec.h>
# include <asm/uaccess.h>
# include <asm/pgtable.h>
# include <asm/io.h>
2006-09-27 08:47:22 +04:00
# include <asm/hd64461.h>
2008-07-29 21:16:12 +04:00
# include <cpu/dac.h>
2005-04-17 02:20:36 +04:00
# define WIDTH 640
2012-12-22 01:07:39 +04:00
static struct fb_var_screeninfo hitfb_var = {
2005-04-17 02:20:36 +04:00
. activate = FB_ACTIVATE_NOW ,
. height = - 1 ,
. width = - 1 ,
. vmode = FB_VMODE_NONINTERLACED ,
} ;
2012-12-22 01:07:39 +04:00
static struct fb_fix_screeninfo hitfb_fix = {
2005-04-17 02:20:36 +04:00
. id = " Hitachi HD64461 " ,
. type = FB_TYPE_PACKED_PIXELS ,
. accel = FB_ACCEL_NONE ,
} ;
static inline void hitfb_accel_wait ( void )
{
while ( fb_readw ( HD64461_GRCFGR ) & HD64461_GRCFGR_ACCSTATUS ) ;
}
static inline void hitfb_accel_start ( int truecolor )
{
if ( truecolor ) {
fb_writew ( 6 , HD64461_GRCFGR ) ;
} else {
fb_writew ( 7 , HD64461_GRCFGR ) ;
}
}
static inline void hitfb_accel_set_dest ( int truecolor , u16 dx , u16 dy ,
u16 width , u16 height )
{
u32 saddr = WIDTH * dy + dx ;
if ( truecolor )
saddr < < = 1 ;
2006-09-27 08:47:22 +04:00
fb_writew ( width - 1 , HD64461_BBTDWR ) ;
fb_writew ( height - 1 , HD64461_BBTDHR ) ;
2005-04-17 02:20:36 +04:00
fb_writew ( saddr & 0xffff , HD64461_BBTDSARL ) ;
fb_writew ( saddr > > 16 , HD64461_BBTDSARH ) ;
}
static inline void hitfb_accel_bitblt ( int truecolor , u16 sx , u16 sy , u16 dx ,
u16 dy , u16 width , u16 height , u16 rop ,
u32 mask_addr )
{
u32 saddr , daddr ;
u32 maddr = 0 ;
2006-09-27 08:47:22 +04:00
height - - ;
width - - ;
2005-04-17 02:20:36 +04:00
fb_writew ( rop , HD64461_BBTROPR ) ;
if ( ( sy < dy ) | | ( ( sy = = dy ) & & ( sx < = dx ) ) ) {
saddr = WIDTH * ( sy + height ) + sx + width ;
daddr = WIDTH * ( dy + height ) + dx + width ;
if ( mask_addr ) {
if ( truecolor )
maddr = ( ( width > > 3 ) + 1 ) * ( height + 1 ) - 1 ;
else
maddr =
( ( ( width > > 4 ) + 1 ) * ( height + 1 ) - 1 ) * 2 ;
fb_writew ( ( 1 < < 5 ) | 1 , HD64461_BBTMDR ) ;
} else
fb_writew ( 1 , HD64461_BBTMDR ) ;
} else {
saddr = WIDTH * sy + sx ;
daddr = WIDTH * dy + dx ;
if ( mask_addr ) {
fb_writew ( ( 1 < < 5 ) , HD64461_BBTMDR ) ;
} else {
fb_writew ( 0 , HD64461_BBTMDR ) ;
}
}
if ( truecolor ) {
saddr < < = 1 ;
daddr < < = 1 ;
}
fb_writew ( width , HD64461_BBTDWR ) ;
fb_writew ( height , HD64461_BBTDHR ) ;
fb_writew ( saddr & 0xffff , HD64461_BBTSSARL ) ;
fb_writew ( saddr > > 16 , HD64461_BBTSSARH ) ;
fb_writew ( daddr & 0xffff , HD64461_BBTDSARL ) ;
fb_writew ( daddr > > 16 , HD64461_BBTDSARH ) ;
if ( mask_addr ) {
maddr + = mask_addr ;
fb_writew ( maddr & 0xffff , HD64461_BBTMARL ) ;
fb_writew ( maddr > > 16 , HD64461_BBTMARH ) ;
}
hitfb_accel_start ( truecolor ) ;
}
static void hitfb_fillrect ( struct fb_info * p , const struct fb_fillrect * rect )
{
if ( rect - > rop ! = ROP_COPY )
cfb_fillrect ( p , rect ) ;
else {
2006-09-27 08:47:22 +04:00
hitfb_accel_wait ( ) ;
2005-04-17 02:20:36 +04:00
fb_writew ( 0x00f0 , HD64461_BBTROPR ) ;
fb_writew ( 16 , HD64461_BBTMDR ) ;
if ( p - > var . bits_per_pixel = = 16 ) {
fb_writew ( ( ( u32 * ) ( p - > pseudo_palette ) ) [ rect - > color ] ,
HD64461_GRSCR ) ;
hitfb_accel_set_dest ( 1 , rect - > dx , rect - > dy , rect - > width ,
rect - > height ) ;
hitfb_accel_start ( 1 ) ;
} else {
fb_writew ( rect - > color , HD64461_GRSCR ) ;
hitfb_accel_set_dest ( 0 , rect - > dx , rect - > dy , rect - > width ,
rect - > height ) ;
hitfb_accel_start ( 0 ) ;
}
}
}
static void hitfb_copyarea ( struct fb_info * p , const struct fb_copyarea * area )
{
2006-09-27 08:47:22 +04:00
hitfb_accel_wait ( ) ;
2005-04-17 02:20:36 +04:00
hitfb_accel_bitblt ( p - > var . bits_per_pixel = = 16 , area - > sx , area - > sy ,
area - > dx , area - > dy , area - > width , area - > height ,
0x00cc , 0 ) ;
}
static int hitfb_pan_display ( struct fb_var_screeninfo * var ,
struct fb_info * info )
{
int xoffset = var - > xoffset ;
int yoffset = var - > yoffset ;
if ( xoffset ! = 0 )
return - EINVAL ;
2006-09-27 08:47:22 +04:00
fb_writew ( ( yoffset * info - > fix . line_length ) > > 10 , HD64461_LCDCBAR ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
int hitfb_blank ( int blank_mode , struct fb_info * info )
{
unsigned short v ;
if ( blank_mode ) {
v = fb_readw ( HD64461_LDR1 ) ;
v & = ~ HD64461_LDR1_DON ;
fb_writew ( v , HD64461_LDR1 ) ;
v = fb_readw ( HD64461_LCDCCR ) ;
v | = HD64461_LCDCCR_MOFF ;
fb_writew ( v , HD64461_LCDCCR ) ;
v = fb_readw ( HD64461_STBCR ) ;
v | = HD64461_STBCR_SLCDST ;
fb_writew ( v , HD64461_STBCR ) ;
} else {
v = fb_readw ( HD64461_STBCR ) ;
v & = ~ HD64461_STBCR_SLCDST ;
fb_writew ( v , HD64461_STBCR ) ;
2006-09-27 08:07:38 +04:00
2006-09-27 08:47:22 +04:00
v = fb_readw ( HD64461_LCDCCR ) ;
v & = ~ ( HD64461_LCDCCR_MOFF | HD64461_LCDCCR_STREQ ) ;
fb_writew ( v , HD64461_LCDCCR ) ;
do {
v = fb_readw ( HD64461_LCDCCR ) ;
} while ( v & HD64461_LCDCCR_STBACK ) ;
2005-04-17 02:20:36 +04:00
v = fb_readw ( HD64461_LDR1 ) ;
v | = HD64461_LDR1_DON ;
fb_writew ( v , HD64461_LDR1 ) ;
}
return 0 ;
}
static int hitfb_setcolreg ( unsigned regno , unsigned red , unsigned green ,
unsigned blue , unsigned transp , struct fb_info * info )
{
2006-09-27 08:47:22 +04:00
if ( regno > = 256 )
2005-04-17 02:20:36 +04:00
return 1 ;
switch ( info - > var . bits_per_pixel ) {
case 8 :
fb_writew ( regno < < 8 , HD64461_CPTWAR ) ;
fb_writew ( red > > 10 , HD64461_CPTWDR ) ;
fb_writew ( green > > 10 , HD64461_CPTWDR ) ;
fb_writew ( blue > > 10 , HD64461_CPTWDR ) ;
break ;
case 16 :
2006-09-27 08:47:22 +04:00
if ( regno > = 16 )
return 1 ;
2005-04-17 02:20:36 +04:00
( ( u32 * ) ( info - > pseudo_palette ) ) [ regno ] =
( ( red & 0xf800 ) ) |
( ( green & 0xfc00 ) > > 5 ) | ( ( blue & 0xf800 ) > > 11 ) ;
break ;
}
return 0 ;
}
2006-09-27 08:47:22 +04:00
static int hitfb_sync ( struct fb_info * info )
{
hitfb_accel_wait ( ) ;
return 0 ;
}
static int hitfb_check_var ( struct fb_var_screeninfo * var , struct fb_info * info )
{
int maxy ;
var - > xres = info - > var . xres ;
var - > xres_virtual = info - > var . xres ;
var - > yres = info - > var . yres ;
if ( ( var - > bits_per_pixel ! = 8 ) & & ( var - > bits_per_pixel ! = 16 ) )
var - > bits_per_pixel = info - > var . bits_per_pixel ;
if ( var - > yres_virtual < var - > yres )
var - > yres_virtual = var - > yres ;
maxy = info - > fix . smem_len / var - > xres ;
if ( var - > bits_per_pixel = = 16 )
maxy / = 2 ;
if ( var - > yres_virtual > maxy )
var - > yres_virtual = maxy ;
var - > xoffset = 0 ;
var - > yoffset = 0 ;
switch ( var - > bits_per_pixel ) {
case 8 :
var - > red . offset = 0 ;
var - > red . length = 8 ;
var - > green . offset = 0 ;
var - > green . length = 8 ;
var - > blue . offset = 0 ;
var - > blue . length = 8 ;
var - > transp . offset = 0 ;
var - > transp . length = 0 ;
break ;
case 16 : /* RGB 565 */
var - > red . offset = 11 ;
var - > red . length = 5 ;
var - > green . offset = 5 ;
var - > green . length = 6 ;
var - > blue . offset = 0 ;
var - > blue . length = 5 ;
var - > transp . offset = 0 ;
var - > transp . length = 0 ;
break ;
}
return 0 ;
}
static int hitfb_set_par ( struct fb_info * info )
{
unsigned short ldr3 ;
switch ( info - > var . bits_per_pixel ) {
case 8 :
info - > fix . line_length = info - > var . xres ;
info - > fix . visual = FB_VISUAL_PSEUDOCOLOR ;
info - > fix . ypanstep = 16 ;
break ;
case 16 :
info - > fix . line_length = info - > var . xres * 2 ;
info - > fix . visual = FB_VISUAL_TRUECOLOR ;
info - > fix . ypanstep = 8 ;
break ;
}
fb_writew ( info - > fix . line_length , HD64461_LCDCLOR ) ;
ldr3 = fb_readw ( HD64461_LDR3 ) ;
ldr3 & = ~ 15 ;
ldr3 | = ( info - > var . bits_per_pixel = = 8 ) ? 4 : 8 ;
fb_writew ( ldr3 , HD64461_LDR3 ) ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
static struct fb_ops hitfb_ops = {
. owner = THIS_MODULE ,
2006-09-27 08:47:22 +04:00
. fb_check_var = hitfb_check_var ,
. fb_set_par = hitfb_set_par ,
2005-04-17 02:20:36 +04:00
. fb_setcolreg = hitfb_setcolreg ,
. fb_blank = hitfb_blank ,
2006-09-27 08:47:22 +04:00
. fb_sync = hitfb_sync ,
2005-04-17 02:20:36 +04:00
. fb_pan_display = hitfb_pan_display ,
. fb_fillrect = hitfb_fillrect ,
. fb_copyarea = hitfb_copyarea ,
. fb_imageblit = cfb_imageblit ,
} ;
2012-12-22 01:07:39 +04:00
static int hitfb_probe ( struct platform_device * dev )
2005-04-17 02:20:36 +04:00
{
unsigned short lcdclor , ldr3 , ldvndr ;
2009-07-07 06:44:56 +04:00
struct fb_info * info ;
int ret ;
2005-04-17 02:20:36 +04:00
if ( fb_get_options ( " hitfb " , NULL ) )
return - ENODEV ;
2009-05-20 06:27:13 +04:00
hitfb_fix . mmio_start = HD64461_IO_OFFSET ( 0x1000 ) ;
2006-09-27 08:47:22 +04:00
hitfb_fix . mmio_len = 0x1000 ;
2009-05-20 06:27:13 +04:00
hitfb_fix . smem_start = HD64461_IO_OFFSET ( 0x02000000 ) ;
2006-09-27 08:47:22 +04:00
hitfb_fix . smem_len = 512 * 1024 ;
2005-04-17 02:20:36 +04:00
lcdclor = fb_readw ( HD64461_LCDCLOR ) ;
ldvndr = fb_readw ( HD64461_LDVNDR ) ;
ldr3 = fb_readw ( HD64461_LDR3 ) ;
switch ( ldr3 & 15 ) {
default :
case 4 :
hitfb_var . bits_per_pixel = 8 ;
hitfb_var . xres = lcdclor ;
break ;
case 8 :
hitfb_var . bits_per_pixel = 16 ;
hitfb_var . xres = lcdclor / 2 ;
break ;
}
hitfb_fix . line_length = lcdclor ;
hitfb_fix . visual = ( hitfb_var . bits_per_pixel = = 8 ) ?
FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR ;
hitfb_var . yres = ldvndr + 1 ;
hitfb_var . xres_virtual = hitfb_var . xres ;
hitfb_var . yres_virtual = hitfb_fix . smem_len / lcdclor ;
switch ( hitfb_var . bits_per_pixel ) {
case 8 :
hitfb_var . red . offset = 0 ;
hitfb_var . red . length = 8 ;
hitfb_var . green . offset = 0 ;
hitfb_var . green . length = 8 ;
hitfb_var . blue . offset = 0 ;
hitfb_var . blue . length = 8 ;
hitfb_var . transp . offset = 0 ;
hitfb_var . transp . length = 0 ;
break ;
case 16 : /* RGB 565 */
hitfb_var . red . offset = 11 ;
hitfb_var . red . length = 5 ;
hitfb_var . green . offset = 5 ;
hitfb_var . green . length = 6 ;
hitfb_var . blue . offset = 0 ;
hitfb_var . blue . length = 5 ;
hitfb_var . transp . offset = 0 ;
hitfb_var . transp . length = 0 ;
break ;
}
2009-07-07 06:44:56 +04:00
info = framebuffer_alloc ( sizeof ( u32 ) * 16 , & dev - > dev ) ;
if ( unlikely ( ! info ) )
return - ENOMEM ;
info - > fbops = & hitfb_ops ;
info - > var = hitfb_var ;
info - > fix = hitfb_fix ;
info - > pseudo_palette = info - > par ;
info - > flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN |
2006-09-27 08:47:22 +04:00
FBINFO_HWACCEL_FILLRECT | FBINFO_HWACCEL_COPYAREA ;
2005-04-17 02:20:36 +04:00
2009-07-07 06:44:56 +04:00
info - > screen_base = ( void * ) hitfb_fix . smem_start ;
2005-04-17 02:20:36 +04:00
2009-07-07 06:44:56 +04:00
ret = fb_alloc_cmap ( & info - > cmap , 256 , 0 ) ;
if ( unlikely ( ret < 0 ) )
goto err_fb ;
2005-04-17 02:20:36 +04:00
2009-07-07 06:44:56 +04:00
ret = register_framebuffer ( info ) ;
if ( unlikely ( ret < 0 ) )
goto err ;
platform_set_drvdata ( dev , info ) ;
2005-04-17 02:20:36 +04:00
2013-09-20 05:35:55 +04:00
fb_info ( info , " %s frame buffer device \n " , info - > fix . id ) ;
2009-07-07 06:44:56 +04:00
2005-04-17 02:20:36 +04:00
return 0 ;
2009-07-07 06:44:56 +04:00
err :
fb_dealloc_cmap ( & info - > cmap ) ;
err_fb :
framebuffer_release ( info ) ;
return ret ;
2005-04-17 02:20:36 +04:00
}
2012-12-22 01:07:39 +04:00
static int hitfb_remove ( struct platform_device * dev )
2006-09-27 08:47:22 +04:00
{
2009-07-07 06:44:56 +04:00
struct fb_info * info = platform_get_drvdata ( dev ) ;
unregister_framebuffer ( info ) ;
fb_dealloc_cmap ( & info - > cmap ) ;
framebuffer_release ( info ) ;
return 0 ;
2006-09-27 08:47:22 +04:00
}
2009-07-07 06:50:57 +04:00
static int hitfb_suspend ( struct device * dev )
2006-09-27 08:47:22 +04:00
{
u16 v ;
hitfb_blank ( 1 , 0 ) ;
v = fb_readw ( HD64461_STBCR ) ;
v | = HD64461_STBCR_SLCKE_IST ;
fb_writew ( v , HD64461_STBCR ) ;
return 0 ;
}
2009-07-07 06:50:57 +04:00
static int hitfb_resume ( struct device * dev )
2006-09-27 08:47:22 +04:00
{
u16 v ;
v = fb_readw ( HD64461_STBCR ) ;
v & = ~ HD64461_STBCR_SLCKE_OST ;
msleep ( 100 ) ;
v = fb_readw ( HD64461_STBCR ) ;
v & = ~ HD64461_STBCR_SLCKE_IST ;
fb_writew ( v , HD64461_STBCR ) ;
hitfb_blank ( 0 , 0 ) ;
return 0 ;
}
2009-07-07 06:50:57 +04:00
2009-12-15 05:00:08 +03:00
static const struct dev_pm_ops hitfb_dev_pm_ops = {
2009-07-07 06:50:57 +04:00
. suspend = hitfb_suspend ,
. resume = hitfb_resume ,
} ;
2006-09-27 08:47:22 +04:00
static struct platform_driver hitfb_driver = {
. probe = hitfb_probe ,
2012-12-22 01:07:39 +04:00
. remove = hitfb_remove ,
2006-09-27 08:47:22 +04:00
. driver = {
. name = " hitfb " ,
2009-07-07 06:50:57 +04:00
. owner = THIS_MODULE ,
. pm = & hitfb_dev_pm_ops ,
2006-09-27 08:47:22 +04:00
} ,
} ;
static struct platform_device hitfb_device = {
. name = " hitfb " ,
. id = - 1 ,
} ;
static int __init hitfb_init ( void )
{
int ret ;
ret = platform_driver_register ( & hitfb_driver ) ;
if ( ! ret ) {
ret = platform_device_register ( & hitfb_device ) ;
if ( ret )
platform_driver_unregister ( & hitfb_driver ) ;
}
return ret ;
}
2005-04-17 02:20:36 +04:00
static void __exit hitfb_exit ( void )
{
2006-09-27 08:47:22 +04:00
platform_device_unregister ( & hitfb_device ) ;
platform_driver_unregister ( & hitfb_driver ) ;
2005-04-17 02:20:36 +04:00
}
module_init ( hitfb_init ) ;
module_exit ( hitfb_exit ) ;
MODULE_LICENSE ( " GPL " ) ;