2008-11-06 23:53:29 +03:00
/*
* drivers / mb862xx / mb862xxfb . c
*
* Fujitsu Carmine / Coral - P ( A ) / Lime framebuffer driver
*
* ( C ) 2008 Anatolij Gustschin < agust @ denx . de >
* DENX Software Engineering
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
*/
# undef DEBUG
# include <linux/fb.h>
# include <linux/delay.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/pci.h>
2009-06-17 02:34:31 +04:00
# if defined(CONFIG_OF)
2008-11-06 23:53:29 +03:00
# include <linux/of_platform.h>
# endif
# include "mb862xxfb.h"
# include "mb862xx_reg.h"
# define NR_PALETTE 256
# define MB862XX_MEM_SIZE 0x1000000
# define CORALP_MEM_SIZE 0x4000000
# define CARMINE_MEM_SIZE 0x8000000
# define DRV_NAME "mb862xxfb"
# if defined(CONFIG_LWMON5)
static struct mb862xx_gc_mode lwmon5_gc_mode = {
/* Mode for Sharp LQ104V1DG61 TFT LCD Panel */
{ " 640x480 " , 60 , 640 , 480 , 40000 , 48 , 16 , 32 , 11 , 96 , 2 , 0 , 0 , 0 } ,
/* 16 bits/pixel, 32MB, 100MHz, SDRAM memory mode value */
16 , 0x2000000 , GC_CCF_COT_100 , 0x414fb7f2
} ;
# endif
# if defined(CONFIG_SOCRATES)
static struct mb862xx_gc_mode socrates_gc_mode = {
/* Mode for Prime View PM070WL4 TFT LCD Panel */
{ " 800x480 " , 45 , 800 , 480 , 40000 , 86 , 42 , 33 , 10 , 128 , 2 , 0 , 0 , 0 } ,
/* 16 bits/pixel, 16MB, 133MHz, SDRAM memory mode value */
16 , 0x1000000 , GC_CCF_COT_133 , 0x4157ba63
} ;
# endif
/* Helpers */
static inline int h_total ( struct fb_var_screeninfo * var )
{
return var - > xres + var - > left_margin +
var - > right_margin + var - > hsync_len ;
}
static inline int v_total ( struct fb_var_screeninfo * var )
{
return var - > yres + var - > upper_margin +
var - > lower_margin + var - > vsync_len ;
}
static inline int hsp ( struct fb_var_screeninfo * var )
{
return var - > xres + var - > right_margin - 1 ;
}
static inline int vsp ( struct fb_var_screeninfo * var )
{
return var - > yres + var - > lower_margin - 1 ;
}
static inline int d_pitch ( struct fb_var_screeninfo * var )
{
return var - > xres * var - > bits_per_pixel / 8 ;
}
static inline unsigned int chan_to_field ( unsigned int chan ,
struct fb_bitfield * bf )
{
chan & = 0xffff ;
chan > > = 16 - bf - > length ;
return chan < < bf - > offset ;
}
static int mb862xxfb_setcolreg ( unsigned regno ,
unsigned red , unsigned green , unsigned blue ,
unsigned transp , struct fb_info * info )
{
struct mb862xxfb_par * par = info - > par ;
unsigned int val ;
switch ( info - > fix . visual ) {
case FB_VISUAL_TRUECOLOR :
if ( regno < 16 ) {
val = chan_to_field ( red , & info - > var . red ) ;
val | = chan_to_field ( green , & info - > var . green ) ;
val | = chan_to_field ( blue , & info - > var . blue ) ;
par - > pseudo_palette [ regno ] = val ;
}
break ;
case FB_VISUAL_PSEUDOCOLOR :
if ( regno < 256 ) {
val = ( red > > 8 ) < < 16 ;
val | = ( green > > 8 ) < < 8 ;
val | = blue > > 8 ;
outreg ( disp , GC_L0PAL0 + ( regno * 4 ) , val ) ;
}
break ;
default :
return 1 ; /* unsupported type */
}
return 0 ;
}
static int mb862xxfb_check_var ( struct fb_var_screeninfo * var ,
struct fb_info * fbi )
{
unsigned long tmp ;
if ( fbi - > dev )
dev_dbg ( fbi - > dev , " %s \n " , __func__ ) ;
/* check if these values fit into the registers */
if ( var - > hsync_len > 255 | | var - > vsync_len > 255 )
return - EINVAL ;
if ( ( var - > xres + var - > right_margin ) > = 4096 )
return - EINVAL ;
if ( ( var - > yres + var - > lower_margin ) > 4096 )
return - EINVAL ;
if ( h_total ( var ) > 4096 | | v_total ( var ) > 4096 )
return - EINVAL ;
if ( var - > xres_virtual > 4096 | | var - > yres_virtual > 4096 )
return - EINVAL ;
if ( var - > bits_per_pixel < = 8 )
var - > bits_per_pixel = 8 ;
else if ( var - > bits_per_pixel < = 16 )
var - > bits_per_pixel = 16 ;
else if ( var - > bits_per_pixel < = 32 )
var - > bits_per_pixel = 32 ;
/*
* can cope with 8 , 16 or 24 / 32 bpp if resulting
* pitch is divisible by 64 without remainder
*/
if ( d_pitch ( & fbi - > var ) % GC_L0M_L0W_UNIT ) {
int r ;
var - > bits_per_pixel = 0 ;
do {
var - > bits_per_pixel + = 8 ;
r = d_pitch ( & fbi - > var ) % GC_L0M_L0W_UNIT ;
} while ( r & & var - > bits_per_pixel < = 32 ) ;
if ( d_pitch ( & fbi - > var ) % GC_L0M_L0W_UNIT )
return - EINVAL ;
}
/* line length is going to be 128 bit aligned */
tmp = ( var - > xres * var - > bits_per_pixel ) / 8 ;
if ( ( tmp & 15 ) ! = 0 )
return - EINVAL ;
/* set r/g/b positions and validate bpp */
switch ( var - > bits_per_pixel ) {
case 8 :
var - > red . length = var - > bits_per_pixel ;
var - > green . length = var - > bits_per_pixel ;
var - > blue . length = var - > bits_per_pixel ;
var - > red . offset = 0 ;
var - > green . offset = 0 ;
var - > blue . offset = 0 ;
var - > transp . length = 0 ;
break ;
case 16 :
var - > red . length = 5 ;
var - > green . length = 5 ;
var - > blue . length = 5 ;
var - > red . offset = 10 ;
var - > green . offset = 5 ;
var - > blue . offset = 0 ;
var - > transp . length = 0 ;
break ;
case 24 :
case 32 :
var - > transp . length = 8 ;
var - > red . length = 8 ;
var - > green . length = 8 ;
var - > blue . length = 8 ;
var - > transp . offset = 24 ;
var - > red . offset = 16 ;
var - > green . offset = 8 ;
var - > blue . offset = 0 ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
/*
* set display parameters
*/
static int mb862xxfb_set_par ( struct fb_info * fbi )
{
struct mb862xxfb_par * par = fbi - > par ;
unsigned long reg , sc ;
dev_dbg ( par - > dev , " %s \n " , __func__ ) ;
2009-12-16 03:46:28 +03:00
if ( par - > type = = BT_CORALP )
mb862xxfb_init_accel ( fbi , fbi - > var . xres ) ;
2008-11-06 23:53:29 +03:00
if ( par - > pre_init )
return 0 ;
/* disp off */
reg = inreg ( disp , GC_DCM1 ) ;
reg & = ~ GC_DCM01_DEN ;
outreg ( disp , GC_DCM1 , reg ) ;
/* set display reference clock div. */
sc = par - > refclk / ( 1000000 / fbi - > var . pixclock ) - 1 ;
reg = inreg ( disp , GC_DCM1 ) ;
reg & = ~ ( GC_DCM01_CKS | GC_DCM01_RESV | GC_DCM01_SC ) ;
reg | = sc < < 8 ;
outreg ( disp , GC_DCM1 , reg ) ;
dev_dbg ( par - > dev , " SC 0x%lx \n " , sc ) ;
/* disp dimension, format */
reg = pack ( d_pitch ( & fbi - > var ) / GC_L0M_L0W_UNIT ,
( fbi - > var . yres - 1 ) ) ;
if ( fbi - > var . bits_per_pixel = = 16 )
reg | = GC_L0M_L0C_16 ;
outreg ( disp , GC_L0M , reg ) ;
if ( fbi - > var . bits_per_pixel = = 32 ) {
reg = inreg ( disp , GC_L0EM ) ;
outreg ( disp , GC_L0EM , reg | GC_L0EM_L0EC_24 ) ;
}
outreg ( disp , GC_WY_WX , 0 ) ;
reg = pack ( fbi - > var . yres - 1 , fbi - > var . xres ) ;
outreg ( disp , GC_WH_WW , reg ) ;
outreg ( disp , GC_L0OA0 , 0 ) ;
outreg ( disp , GC_L0DA0 , 0 ) ;
outreg ( disp , GC_L0DY_L0DX , 0 ) ;
outreg ( disp , GC_L0WY_L0WX , 0 ) ;
outreg ( disp , GC_L0WH_L0WW , reg ) ;
/* both HW-cursors off */
reg = inreg ( disp , GC_CPM_CUTC ) ;
reg & = ~ ( GC_CPM_CEN0 | GC_CPM_CEN1 ) ;
outreg ( disp , GC_CPM_CUTC , reg ) ;
/* timings */
reg = pack ( fbi - > var . xres - 1 , fbi - > var . xres - 1 ) ;
outreg ( disp , GC_HDB_HDP , reg ) ;
reg = pack ( ( fbi - > var . yres - 1 ) , vsp ( & fbi - > var ) ) ;
outreg ( disp , GC_VDP_VSP , reg ) ;
reg = ( ( fbi - > var . vsync_len - 1 ) < < 24 ) |
pack ( ( fbi - > var . hsync_len - 1 ) , hsp ( & fbi - > var ) ) ;
outreg ( disp , GC_VSW_HSW_HSP , reg ) ;
outreg ( disp , GC_HTP , pack ( h_total ( & fbi - > var ) - 1 , 0 ) ) ;
outreg ( disp , GC_VTR , pack ( v_total ( & fbi - > var ) - 1 , 0 ) ) ;
/* display on */
reg = inreg ( disp , GC_DCM1 ) ;
reg | = GC_DCM01_DEN | GC_DCM01_L0E ;
reg & = ~ GC_DCM01_ESY ;
outreg ( disp , GC_DCM1 , reg ) ;
return 0 ;
}
static int mb862xxfb_pan ( struct fb_var_screeninfo * var ,
struct fb_info * info )
{
struct mb862xxfb_par * par = info - > par ;
unsigned long reg ;
reg = pack ( var - > yoffset , var - > xoffset ) ;
outreg ( disp , GC_L0WY_L0WX , reg ) ;
reg = pack ( var - > yres_virtual , var - > xres_virtual ) ;
outreg ( disp , GC_L0WH_L0WW , reg ) ;
return 0 ;
}
static int mb862xxfb_blank ( int mode , struct fb_info * fbi )
{
struct mb862xxfb_par * par = fbi - > par ;
unsigned long reg ;
dev_dbg ( fbi - > dev , " blank mode=%d \n " , mode ) ;
switch ( mode ) {
case FB_BLANK_POWERDOWN :
reg = inreg ( disp , GC_DCM1 ) ;
reg & = ~ GC_DCM01_DEN ;
outreg ( disp , GC_DCM1 , reg ) ;
break ;
case FB_BLANK_UNBLANK :
reg = inreg ( disp , GC_DCM1 ) ;
reg | = GC_DCM01_DEN ;
outreg ( disp , GC_DCM1 , reg ) ;
break ;
case FB_BLANK_NORMAL :
case FB_BLANK_VSYNC_SUSPEND :
case FB_BLANK_HSYNC_SUSPEND :
default :
return 1 ;
}
return 0 ;
}
/* framebuffer ops */
static struct fb_ops mb862xxfb_ops = {
. owner = THIS_MODULE ,
. fb_check_var = mb862xxfb_check_var ,
. fb_set_par = mb862xxfb_set_par ,
. fb_setcolreg = mb862xxfb_setcolreg ,
. fb_blank = mb862xxfb_blank ,
. fb_pan_display = mb862xxfb_pan ,
. fb_fillrect = cfb_fillrect ,
. fb_copyarea = cfb_copyarea ,
. fb_imageblit = cfb_imageblit ,
} ;
/* initialize fb_info data */
static int mb862xxfb_init_fbinfo ( struct fb_info * fbi )
{
struct mb862xxfb_par * par = fbi - > par ;
struct mb862xx_gc_mode * mode = par - > gc_mode ;
unsigned long reg ;
fbi - > fbops = & mb862xxfb_ops ;
fbi - > pseudo_palette = par - > pseudo_palette ;
fbi - > screen_base = par - > fb_base ;
fbi - > screen_size = par - > mapped_vram ;
strcpy ( fbi - > fix . id , DRV_NAME ) ;
fbi - > fix . smem_start = ( unsigned long ) par - > fb_base_phys ;
fbi - > fix . smem_len = par - > mapped_vram ;
fbi - > fix . mmio_start = ( unsigned long ) par - > mmio_base_phys ;
fbi - > fix . mmio_len = par - > mmio_len ;
fbi - > fix . accel = FB_ACCEL_NONE ;
fbi - > fix . type = FB_TYPE_PACKED_PIXELS ;
fbi - > fix . type_aux = 0 ;
fbi - > fix . xpanstep = 1 ;
fbi - > fix . ypanstep = 1 ;
fbi - > fix . ywrapstep = 0 ;
reg = inreg ( disp , GC_DCM1 ) ;
if ( reg & GC_DCM01_DEN & & reg & GC_DCM01_L0E ) {
/* get the disp mode from active display cfg */
unsigned long sc = ( ( reg & GC_DCM01_SC ) > > 8 ) + 1 ;
unsigned long hsp , vsp , ht , vt ;
dev_dbg ( par - > dev , " using bootloader's disp. mode \n " ) ;
fbi - > var . pixclock = ( sc * 1000000 ) / par - > refclk ;
fbi - > var . xres = ( inreg ( disp , GC_HDB_HDP ) & 0x0fff ) + 1 ;
reg = inreg ( disp , GC_VDP_VSP ) ;
fbi - > var . yres = ( ( reg > > 16 ) & 0x0fff ) + 1 ;
vsp = ( reg & 0x0fff ) + 1 ;
fbi - > var . xres_virtual = fbi - > var . xres ;
fbi - > var . yres_virtual = fbi - > var . yres ;
reg = inreg ( disp , GC_L0EM ) ;
if ( reg & GC_L0EM_L0EC_24 ) {
fbi - > var . bits_per_pixel = 32 ;
} else {
reg = inreg ( disp , GC_L0M ) ;
if ( reg & GC_L0M_L0C_16 )
fbi - > var . bits_per_pixel = 16 ;
else
fbi - > var . bits_per_pixel = 8 ;
}
reg = inreg ( disp , GC_VSW_HSW_HSP ) ;
fbi - > var . hsync_len = ( ( reg & 0xff0000 ) > > 16 ) + 1 ;
fbi - > var . vsync_len = ( ( reg & 0x3f000000 ) > > 24 ) + 1 ;
hsp = ( reg & 0xffff ) + 1 ;
ht = ( ( inreg ( disp , GC_HTP ) & 0xfff0000 ) > > 16 ) + 1 ;
fbi - > var . right_margin = hsp - fbi - > var . xres ;
fbi - > var . left_margin = ht - hsp - fbi - > var . hsync_len ;
vt = ( ( inreg ( disp , GC_VTR ) & 0xfff0000 ) > > 16 ) + 1 ;
fbi - > var . lower_margin = vsp - fbi - > var . yres ;
fbi - > var . upper_margin = vt - vsp - fbi - > var . vsync_len ;
} else if ( mode ) {
dev_dbg ( par - > dev , " using supplied mode \n " ) ;
fb_videomode_to_var ( & fbi - > var , ( struct fb_videomode * ) mode ) ;
fbi - > var . bits_per_pixel = mode - > def_bpp ? mode - > def_bpp : 8 ;
} else {
int ret ;
ret = fb_find_mode ( & fbi - > var , fbi , " 640x480-16@60 " ,
NULL , 0 , NULL , 16 ) ;
if ( ret = = 0 | | ret = = 4 ) {
dev_err ( par - > dev ,
" failed to get initial mode \n " ) ;
return - EINVAL ;
}
}
fbi - > var . xoffset = 0 ;
fbi - > var . yoffset = 0 ;
fbi - > var . grayscale = 0 ;
fbi - > var . nonstd = 0 ;
fbi - > var . height = - 1 ;
fbi - > var . width = - 1 ;
fbi - > var . accel_flags = 0 ;
fbi - > var . vmode = FB_VMODE_NONINTERLACED ;
fbi - > var . activate = FB_ACTIVATE_NOW ;
fbi - > flags = FBINFO_DEFAULT |
# ifdef __BIG_ENDIAN
FBINFO_FOREIGN_ENDIAN |
# endif
FBINFO_HWACCEL_XPAN |
FBINFO_HWACCEL_YPAN ;
/* check and possibly fix bpp */
if ( ( fbi - > fbops - > fb_check_var ) ( & fbi - > var , fbi ) )
dev_err ( par - > dev , " check_var() failed on initial setup? \n " ) ;
fbi - > fix . visual = fbi - > var . bits_per_pixel = = 8 ?
FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR ;
fbi - > fix . line_length = ( fbi - > var . xres_virtual *
fbi - > var . bits_per_pixel ) / 8 ;
return 0 ;
}
/*
* show some display controller and cursor registers
*/
static ssize_t mb862xxfb_show_dispregs ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct fb_info * fbi = dev_get_drvdata ( dev ) ;
struct mb862xxfb_par * par = fbi - > par ;
char * ptr = buf ;
unsigned int reg ;
for ( reg = GC_DCM0 ; reg < = GC_L0DY_L0DX ; reg + = 4 )
ptr + = sprintf ( ptr , " %08x = %08x \n " ,
reg , inreg ( disp , reg ) ) ;
for ( reg = GC_CPM_CUTC ; reg < = GC_CUY1_CUX1 ; reg + = 4 )
ptr + = sprintf ( ptr , " %08x = %08x \n " ,
reg , inreg ( disp , reg ) ) ;
for ( reg = GC_DCM1 ; reg < = GC_L0WH_L0WW ; reg + = 4 )
ptr + = sprintf ( ptr , " %08x = %08x \n " ,
reg , inreg ( disp , reg ) ) ;
2009-12-16 03:46:28 +03:00
for ( reg = 0x400 ; reg < = 0x410 ; reg + = 4 )
ptr + = sprintf ( ptr , " geo %08x = %08x \n " ,
reg , inreg ( geo , reg ) ) ;
for ( reg = 0x400 ; reg < = 0x410 ; reg + = 4 )
ptr + = sprintf ( ptr , " draw %08x = %08x \n " ,
reg , inreg ( draw , reg ) ) ;
for ( reg = 0x440 ; reg < = 0x450 ; reg + = 4 )
ptr + = sprintf ( ptr , " draw %08x = %08x \n " ,
reg , inreg ( draw , reg ) ) ;
2008-11-06 23:53:29 +03:00
return ptr - buf ;
}
static DEVICE_ATTR ( dispregs , 0444 , mb862xxfb_show_dispregs , NULL ) ;
irqreturn_t mb862xx_intr ( int irq , void * dev_id )
{
struct mb862xxfb_par * par = ( struct mb862xxfb_par * ) dev_id ;
unsigned long reg_ist , mask ;
if ( ! par )
return IRQ_NONE ;
if ( par - > type = = BT_CARMINE ) {
/* Get Interrupt Status */
reg_ist = inreg ( ctrl , GC_CTRL_STATUS ) ;
mask = inreg ( ctrl , GC_CTRL_INT_MASK ) ;
if ( reg_ist = = 0 )
return IRQ_HANDLED ;
reg_ist & = mask ;
if ( reg_ist = = 0 )
return IRQ_HANDLED ;
/* Clear interrupt status */
outreg ( ctrl , 0x0 , reg_ist ) ;
} else {
/* Get status */
reg_ist = inreg ( host , GC_IST ) ;
mask = inreg ( host , GC_IMASK ) ;
reg_ist & = mask ;
if ( reg_ist = = 0 )
return IRQ_HANDLED ;
/* Clear status */
outreg ( host , GC_IST , ~ reg_ist ) ;
}
return IRQ_HANDLED ;
}
# if defined(CONFIG_FB_MB862XX_LIME)
/*
* GDC ( Lime , Coral ( B / Q ) , Mint , . . . ) on host bus
*/
static int mb862xx_gdc_init ( struct mb862xxfb_par * par )
{
unsigned long ccf , mmr ;
unsigned long ver , rev ;
if ( ! par )
return - ENODEV ;
# if defined(CONFIG_FB_PRE_INIT_FB)
par - > pre_init = 1 ;
# endif
par - > host = par - > mmio_base ;
par - > i2c = par - > mmio_base + MB862XX_I2C_BASE ;
par - > disp = par - > mmio_base + MB862XX_DISP_BASE ;
par - > cap = par - > mmio_base + MB862XX_CAP_BASE ;
par - > draw = par - > mmio_base + MB862XX_DRAW_BASE ;
par - > geo = par - > mmio_base + MB862XX_GEO_BASE ;
par - > pio = par - > mmio_base + MB862XX_PIO_BASE ;
par - > refclk = GC_DISP_REFCLK_400 ;
ver = inreg ( host , GC_CID ) ;
rev = inreg ( pio , GC_REVISION ) ;
if ( ( ver = = 0x303 ) & & ( rev & 0xffffff00 ) = = 0x20050100 ) {
dev_info ( par - > dev , " Fujitsu Lime v1.%d found \n " ,
( int ) rev & 0xff ) ;
par - > type = BT_LIME ;
ccf = par - > gc_mode ? par - > gc_mode - > ccf : GC_CCF_COT_100 ;
mmr = par - > gc_mode ? par - > gc_mode - > mmr : 0x414fb7f2 ;
} else {
dev_info ( par - > dev , " ? GDC, CID/Rev.: 0x%lx/0x%lx \n " , ver , rev ) ;
return - ENODEV ;
}
if ( ! par - > pre_init ) {
outreg ( host , GC_CCF , ccf ) ;
udelay ( 200 ) ;
outreg ( host , GC_MMR , mmr ) ;
udelay ( 10 ) ;
}
/* interrupt status */
outreg ( host , GC_IST , 0 ) ;
outreg ( host , GC_IMASK , GC_INT_EN ) ;
return 0 ;
}
static int __devinit of_platform_mb862xx_probe ( struct of_device * ofdev ,
const struct of_device_id * id )
{
struct device_node * np = ofdev - > node ;
struct device * dev = & ofdev - > dev ;
struct mb862xxfb_par * par ;
struct fb_info * info ;
struct resource res ;
resource_size_t res_size ;
unsigned long ret = - ENODEV ;
if ( of_address_to_resource ( np , 0 , & res ) ) {
dev_err ( dev , " Invalid address \n " ) ;
return - ENXIO ;
}
info = framebuffer_alloc ( sizeof ( struct mb862xxfb_par ) , dev ) ;
if ( info = = NULL ) {
dev_err ( dev , " cannot allocate framebuffer \n " ) ;
return - ENOMEM ;
}
par = info - > par ;
par - > info = info ;
par - > dev = dev ;
par - > irq = irq_of_parse_and_map ( np , 0 ) ;
if ( par - > irq = = NO_IRQ ) {
dev_err ( dev , " failed to map irq \n " ) ;
ret = - ENODEV ;
goto fbrel ;
}
res_size = 1 + res . end - res . start ;
par - > res = request_mem_region ( res . start , res_size , DRV_NAME ) ;
if ( par - > res = = NULL ) {
dev_err ( dev , " Cannot claim framebuffer/mmio \n " ) ;
ret = - ENXIO ;
goto irqdisp ;
}
# if defined(CONFIG_LWMON5)
par - > gc_mode = & lwmon5_gc_mode ;
# endif
# if defined(CONFIG_SOCRATES)
par - > gc_mode = & socrates_gc_mode ;
# endif
par - > fb_base_phys = res . start ;
par - > mmio_base_phys = res . start + MB862XX_MMIO_BASE ;
par - > mmio_len = MB862XX_MMIO_SIZE ;
if ( par - > gc_mode )
par - > mapped_vram = par - > gc_mode - > max_vram ;
else
par - > mapped_vram = MB862XX_MEM_SIZE ;
par - > fb_base = ioremap ( par - > fb_base_phys , par - > mapped_vram ) ;
if ( par - > fb_base = = NULL ) {
dev_err ( dev , " Cannot map framebuffer \n " ) ;
goto rel_reg ;
}
par - > mmio_base = ioremap ( par - > mmio_base_phys , par - > mmio_len ) ;
if ( par - > mmio_base = = NULL ) {
dev_err ( dev , " Cannot map registers \n " ) ;
goto fb_unmap ;
}
dev_dbg ( dev , " fb phys 0x%llx 0x%lx \n " ,
( u64 ) par - > fb_base_phys , ( ulong ) par - > mapped_vram ) ;
dev_dbg ( dev , " mmio phys 0x%llx 0x%lx, (irq = %d) \n " ,
( u64 ) par - > mmio_base_phys , ( ulong ) par - > mmio_len , par - > irq ) ;
if ( mb862xx_gdc_init ( par ) )
goto io_unmap ;
if ( request_irq ( par - > irq , mb862xx_intr , IRQF_DISABLED ,
DRV_NAME , ( void * ) par ) ) {
dev_err ( dev , " Cannot request irq \n " ) ;
goto io_unmap ;
}
mb862xxfb_init_fbinfo ( info ) ;
if ( fb_alloc_cmap ( & info - > cmap , NR_PALETTE , 0 ) < 0 ) {
dev_err ( dev , " Could not allocate cmap for fb_info. \n " ) ;
goto free_irq ;
}
if ( ( info - > fbops - > fb_set_par ) ( info ) )
dev_err ( dev , " set_var() failed on initial setup? \n " ) ;
if ( register_framebuffer ( info ) ) {
dev_err ( dev , " failed to register framebuffer \n " ) ;
goto rel_cmap ;
}
dev_set_drvdata ( dev , info ) ;
if ( device_create_file ( dev , & dev_attr_dispregs ) )
dev_err ( dev , " Can't create sysfs regdump file \n " ) ;
return 0 ;
rel_cmap :
fb_dealloc_cmap ( & info - > cmap ) ;
free_irq :
outreg ( host , GC_IMASK , 0 ) ;
free_irq ( par - > irq , ( void * ) par ) ;
io_unmap :
iounmap ( par - > mmio_base ) ;
fb_unmap :
iounmap ( par - > fb_base ) ;
rel_reg :
release_mem_region ( res . start , res_size ) ;
irqdisp :
irq_dispose_mapping ( par - > irq ) ;
fbrel :
dev_set_drvdata ( dev , NULL ) ;
framebuffer_release ( info ) ;
return ret ;
}
static int __devexit of_platform_mb862xx_remove ( struct of_device * ofdev )
{
struct fb_info * fbi = dev_get_drvdata ( & ofdev - > dev ) ;
struct mb862xxfb_par * par = fbi - > par ;
resource_size_t res_size = 1 + par - > res - > end - par - > res - > start ;
unsigned long reg ;
dev_dbg ( fbi - > dev , " %s release \n " , fbi - > fix . id ) ;
/* display off */
reg = inreg ( disp , GC_DCM1 ) ;
reg & = ~ ( GC_DCM01_DEN | GC_DCM01_L0E ) ;
outreg ( disp , GC_DCM1 , reg ) ;
/* disable interrupts */
outreg ( host , GC_IMASK , 0 ) ;
free_irq ( par - > irq , ( void * ) par ) ;
irq_dispose_mapping ( par - > irq ) ;
device_remove_file ( & ofdev - > dev , & dev_attr_dispregs ) ;
unregister_framebuffer ( fbi ) ;
fb_dealloc_cmap ( & fbi - > cmap ) ;
iounmap ( par - > mmio_base ) ;
iounmap ( par - > fb_base ) ;
dev_set_drvdata ( & ofdev - > dev , NULL ) ;
release_mem_region ( par - > res - > start , res_size ) ;
framebuffer_release ( fbi ) ;
return 0 ;
}
/*
* common types
*/
static struct of_device_id __devinitdata of_platform_mb862xx_tbl [ ] = {
{ . compatible = " fujitsu,MB86276 " , } ,
{ . compatible = " fujitsu,lime " , } ,
{ . compatible = " fujitsu,MB86277 " , } ,
{ . compatible = " fujitsu,mint " , } ,
{ . compatible = " fujitsu,MB86293 " , } ,
{ . compatible = " fujitsu,MB86294 " , } ,
{ . compatible = " fujitsu,coral " , } ,
{ /* end */ }
} ;
static struct of_platform_driver of_platform_mb862xxfb_driver = {
. owner = THIS_MODULE ,
. name = DRV_NAME ,
. match_table = of_platform_mb862xx_tbl ,
. probe = of_platform_mb862xx_probe ,
. remove = __devexit_p ( of_platform_mb862xx_remove ) ,
} ;
# endif
# if defined(CONFIG_FB_MB862XX_PCI_GDC)
static int coralp_init ( struct mb862xxfb_par * par )
{
int cn , ver ;
par - > host = par - > mmio_base ;
par - > i2c = par - > mmio_base + MB862XX_I2C_BASE ;
par - > disp = par - > mmio_base + MB862XX_DISP_BASE ;
par - > cap = par - > mmio_base + MB862XX_CAP_BASE ;
par - > draw = par - > mmio_base + MB862XX_DRAW_BASE ;
par - > geo = par - > mmio_base + MB862XX_GEO_BASE ;
par - > pio = par - > mmio_base + MB862XX_PIO_BASE ;
par - > refclk = GC_DISP_REFCLK_400 ;
ver = inreg ( host , GC_CID ) ;
cn = ( ver & GC_CID_CNAME_MSK ) > > 8 ;
ver = ver & GC_CID_VERSION_MSK ;
if ( cn = = 3 ) {
dev_info ( par - > dev , " Fujitsu Coral-%s GDC Rev.%d found \n " , \
( ver = = 6 ) ? " P " : ( ver = = 8 ) ? " PA " : " ? " ,
par - > pdev - > revision ) ;
outreg ( host , GC_CCF , GC_CCF_CGE_166 | GC_CCF_COT_133 ) ;
udelay ( 200 ) ;
outreg ( host , GC_MMR , GC_MMR_CORALP_EVB_VAL ) ;
udelay ( 10 ) ;
/* Clear interrupt status */
outreg ( host , GC_IST , 0 ) ;
} else {
return - ENODEV ;
}
return 0 ;
}
static int init_dram_ctrl ( struct mb862xxfb_par * par )
{
unsigned long i = 0 ;
/*
* Set io mode first ! Spec . says IC may be destroyed
* if not set to SSTL2 / LVCMOS before init .
*/
outreg ( dram_ctrl , GC_DCTL_IOCONT1_IOCONT0 , GC_EVB_DCTL_IOCONT1_IOCONT0 ) ;
/* DRAM init */
outreg ( dram_ctrl , GC_DCTL_MODE_ADD , GC_EVB_DCTL_MODE_ADD ) ;
outreg ( dram_ctrl , GC_DCTL_SETTIME1_EMODE , GC_EVB_DCTL_SETTIME1_EMODE ) ;
outreg ( dram_ctrl , GC_DCTL_REFRESH_SETTIME2 ,
GC_EVB_DCTL_REFRESH_SETTIME2 ) ;
outreg ( dram_ctrl , GC_DCTL_RSV2_RSV1 , GC_EVB_DCTL_RSV2_RSV1 ) ;
outreg ( dram_ctrl , GC_DCTL_DDRIF2_DDRIF1 , GC_EVB_DCTL_DDRIF2_DDRIF1 ) ;
outreg ( dram_ctrl , GC_DCTL_RSV0_STATES , GC_EVB_DCTL_RSV0_STATES ) ;
/* DLL reset done? */
while ( ( inreg ( dram_ctrl , GC_DCTL_RSV0_STATES ) & GC_DCTL_STATES_MSK ) ) {
udelay ( GC_DCTL_INIT_WAIT_INTERVAL ) ;
if ( i + + > GC_DCTL_INIT_WAIT_CNT ) {
dev_err ( par - > dev , " VRAM init failed. \n " ) ;
return - EINVAL ;
}
}
outreg ( dram_ctrl , GC_DCTL_MODE_ADD , GC_EVB_DCTL_MODE_ADD_AFT_RST ) ;
outreg ( dram_ctrl , GC_DCTL_RSV0_STATES , GC_EVB_DCTL_RSV0_STATES_AFT_RST ) ;
return 0 ;
}
static int carmine_init ( struct mb862xxfb_par * par )
{
unsigned long reg ;
par - > ctrl = par - > mmio_base + MB86297_CTRL_BASE ;
par - > i2c = par - > mmio_base + MB86297_I2C_BASE ;
par - > disp = par - > mmio_base + MB86297_DISP0_BASE ;
par - > disp1 = par - > mmio_base + MB86297_DISP1_BASE ;
par - > cap = par - > mmio_base + MB86297_CAP0_BASE ;
par - > cap1 = par - > mmio_base + MB86297_CAP1_BASE ;
par - > draw = par - > mmio_base + MB86297_DRAW_BASE ;
par - > dram_ctrl = par - > mmio_base + MB86297_DRAMCTRL_BASE ;
par - > wrback = par - > mmio_base + MB86297_WRBACK_BASE ;
par - > refclk = GC_DISP_REFCLK_533 ;
/* warm up */
reg = GC_CTRL_CLK_EN_DRAM | GC_CTRL_CLK_EN_2D3D | GC_CTRL_CLK_EN_DISP0 ;
outreg ( ctrl , GC_CTRL_CLK_ENABLE , reg ) ;
/* check for engine module revision */
if ( inreg ( draw , GC_2D3D_REV ) = = GC_RE_REVISION )
dev_info ( par - > dev , " Fujitsu Carmine GDC Rev.%d found \n " ,
par - > pdev - > revision ) ;
else
goto err_init ;
reg & = ~ GC_CTRL_CLK_EN_2D3D ;
outreg ( ctrl , GC_CTRL_CLK_ENABLE , reg ) ;
/* set up vram */
if ( init_dram_ctrl ( par ) < 0 )
goto err_init ;
outreg ( ctrl , GC_CTRL_INT_MASK , 0 ) ;
return 0 ;
err_init :
outreg ( ctrl , GC_CTRL_CLK_ENABLE , 0 ) ;
return - EINVAL ;
}
static inline int mb862xx_pci_gdc_init ( struct mb862xxfb_par * par )
{
switch ( par - > type ) {
case BT_CORALP :
return coralp_init ( par ) ;
case BT_CARMINE :
return carmine_init ( par ) ;
default :
return - ENODEV ;
}
}
# define CHIP_ID(id) \
{ PCI_DEVICE ( PCI_VENDOR_ID_FUJITSU_LIMITED , id ) }
static struct pci_device_id mb862xx_pci_tbl [ ] __devinitdata = {
/* MB86295/MB86296 */
CHIP_ID ( PCI_DEVICE_ID_FUJITSU_CORALP ) ,
CHIP_ID ( PCI_DEVICE_ID_FUJITSU_CORALPA ) ,
/* MB86297 */
CHIP_ID ( PCI_DEVICE_ID_FUJITSU_CARMINE ) ,
{ 0 , }
} ;
MODULE_DEVICE_TABLE ( pci , mb862xx_pci_tbl ) ;
static int __devinit mb862xx_pci_probe ( struct pci_dev * pdev ,
const struct pci_device_id * ent )
{
struct mb862xxfb_par * par ;
struct fb_info * info ;
struct device * dev = & pdev - > dev ;
int ret ;
ret = pci_enable_device ( pdev ) ;
if ( ret < 0 ) {
dev_err ( dev , " Cannot enable PCI device \n " ) ;
goto out ;
}
info = framebuffer_alloc ( sizeof ( struct mb862xxfb_par ) , dev ) ;
if ( ! info ) {
dev_err ( dev , " framebuffer alloc failed \n " ) ;
ret = - ENOMEM ;
goto dis_dev ;
}
par = info - > par ;
par - > info = info ;
par - > dev = dev ;
par - > pdev = pdev ;
par - > irq = pdev - > irq ;
ret = pci_request_regions ( pdev , DRV_NAME ) ;
if ( ret < 0 ) {
dev_err ( dev , " Cannot reserve region(s) for PCI device \n " ) ;
goto rel_fb ;
}
switch ( pdev - > device ) {
case PCI_DEVICE_ID_FUJITSU_CORALP :
case PCI_DEVICE_ID_FUJITSU_CORALPA :
par - > fb_base_phys = pci_resource_start ( par - > pdev , 0 ) ;
par - > mapped_vram = CORALP_MEM_SIZE ;
par - > mmio_base_phys = par - > fb_base_phys + MB862XX_MMIO_BASE ;
par - > mmio_len = MB862XX_MMIO_SIZE ;
par - > type = BT_CORALP ;
break ;
case PCI_DEVICE_ID_FUJITSU_CARMINE :
par - > fb_base_phys = pci_resource_start ( par - > pdev , 2 ) ;
par - > mmio_base_phys = pci_resource_start ( par - > pdev , 3 ) ;
par - > mmio_len = pci_resource_len ( par - > pdev , 3 ) ;
par - > mapped_vram = CARMINE_MEM_SIZE ;
par - > type = BT_CARMINE ;
break ;
default :
/* should never occur */
goto rel_reg ;
}
par - > fb_base = ioremap ( par - > fb_base_phys , par - > mapped_vram ) ;
if ( par - > fb_base = = NULL ) {
dev_err ( dev , " Cannot map framebuffer \n " ) ;
goto rel_reg ;
}
par - > mmio_base = ioremap ( par - > mmio_base_phys , par - > mmio_len ) ;
if ( par - > mmio_base = = NULL ) {
dev_err ( dev , " Cannot map registers \n " ) ;
ret = - EIO ;
goto fb_unmap ;
}
dev_dbg ( dev , " fb phys 0x%llx 0x%lx \n " ,
2008-12-10 00:14:31 +03:00
( unsigned long long ) par - > fb_base_phys , ( ulong ) par - > mapped_vram ) ;
2008-11-06 23:53:29 +03:00
dev_dbg ( dev , " mmio phys 0x%llx 0x%lx \n " ,
2008-12-10 00:14:31 +03:00
( unsigned long long ) par - > mmio_base_phys , ( ulong ) par - > mmio_len ) ;
2008-11-06 23:53:29 +03:00
if ( mb862xx_pci_gdc_init ( par ) )
goto io_unmap ;
if ( request_irq ( par - > irq , mb862xx_intr , IRQF_DISABLED | IRQF_SHARED ,
DRV_NAME , ( void * ) par ) ) {
dev_err ( dev , " Cannot request irq \n " ) ;
goto io_unmap ;
}
mb862xxfb_init_fbinfo ( info ) ;
if ( fb_alloc_cmap ( & info - > cmap , NR_PALETTE , 0 ) < 0 ) {
dev_err ( dev , " Could not allocate cmap for fb_info. \n " ) ;
ret = - ENOMEM ;
goto free_irq ;
}
if ( ( info - > fbops - > fb_set_par ) ( info ) )
dev_err ( dev , " set_var() failed on initial setup? \n " ) ;
ret = register_framebuffer ( info ) ;
if ( ret < 0 ) {
dev_err ( dev , " failed to register framebuffer \n " ) ;
goto rel_cmap ;
}
pci_set_drvdata ( pdev , info ) ;
if ( device_create_file ( dev , & dev_attr_dispregs ) )
dev_err ( dev , " Can't create sysfs regdump file \n " ) ;
if ( par - > type = = BT_CARMINE )
outreg ( ctrl , GC_CTRL_INT_MASK , GC_CARMINE_INT_EN ) ;
else
outreg ( host , GC_IMASK , GC_INT_EN ) ;
return 0 ;
rel_cmap :
fb_dealloc_cmap ( & info - > cmap ) ;
free_irq :
free_irq ( par - > irq , ( void * ) par ) ;
io_unmap :
iounmap ( par - > mmio_base ) ;
fb_unmap :
iounmap ( par - > fb_base ) ;
rel_reg :
pci_release_regions ( pdev ) ;
rel_fb :
framebuffer_release ( info ) ;
dis_dev :
pci_disable_device ( pdev ) ;
out :
return ret ;
}
static void __devexit mb862xx_pci_remove ( struct pci_dev * pdev )
{
struct fb_info * fbi = pci_get_drvdata ( pdev ) ;
struct mb862xxfb_par * par = fbi - > par ;
unsigned long reg ;
dev_dbg ( fbi - > dev , " %s release \n " , fbi - > fix . id ) ;
/* display off */
reg = inreg ( disp , GC_DCM1 ) ;
reg & = ~ ( GC_DCM01_DEN | GC_DCM01_L0E ) ;
outreg ( disp , GC_DCM1 , reg ) ;
if ( par - > type = = BT_CARMINE ) {
outreg ( ctrl , GC_CTRL_INT_MASK , 0 ) ;
outreg ( ctrl , GC_CTRL_CLK_ENABLE , 0 ) ;
} else {
outreg ( host , GC_IMASK , 0 ) ;
}
device_remove_file ( & pdev - > dev , & dev_attr_dispregs ) ;
pci_set_drvdata ( pdev , NULL ) ;
unregister_framebuffer ( fbi ) ;
fb_dealloc_cmap ( & fbi - > cmap ) ;
free_irq ( par - > irq , ( void * ) par ) ;
iounmap ( par - > mmio_base ) ;
iounmap ( par - > fb_base ) ;
pci_release_regions ( pdev ) ;
framebuffer_release ( fbi ) ;
pci_disable_device ( pdev ) ;
}
static struct pci_driver mb862xxfb_pci_driver = {
. name = DRV_NAME ,
. id_table = mb862xx_pci_tbl ,
. probe = mb862xx_pci_probe ,
. remove = __devexit_p ( mb862xx_pci_remove ) ,
} ;
# endif
static int __devinit mb862xxfb_init ( void )
{
int ret = - ENODEV ;
# if defined(CONFIG_FB_MB862XX_LIME)
ret = of_register_platform_driver ( & of_platform_mb862xxfb_driver ) ;
# endif
# if defined(CONFIG_FB_MB862XX_PCI_GDC)
ret = pci_register_driver ( & mb862xxfb_pci_driver ) ;
# endif
return ret ;
}
static void __exit mb862xxfb_exit ( void )
{
# if defined(CONFIG_FB_MB862XX_LIME)
of_unregister_platform_driver ( & of_platform_mb862xxfb_driver ) ;
# endif
# if defined(CONFIG_FB_MB862XX_PCI_GDC)
pci_unregister_driver ( & mb862xxfb_pci_driver ) ;
# endif
}
module_init ( mb862xxfb_init ) ;
module_exit ( mb862xxfb_exit ) ;
MODULE_DESCRIPTION ( " Fujitsu MB862xx Framebuffer driver " ) ;
MODULE_AUTHOR ( " Anatolij Gustschin <agust@denx.de> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;