2006-07-14 11:24:34 +04:00
/*
* linux / drivers / video / mbx / mbxfb . c
*
2007-10-16 12:28:40 +04:00
* Copyright ( C ) 2006 - 2007 8 D Technologies inc
2006-12-08 13:40:36 +03:00
* Raphael Assenat < raph @ 8 d . com >
* - Added video overlay support
* - Various improvements
*
2006-07-14 11:24:34 +04:00
* Copyright ( C ) 2006 Compulab , Ltd .
* Mike Rapoport < mike @ compulab . co . il >
2006-12-08 13:40:36 +03:00
* - Creation of driver
2006-07-14 11:24:34 +04:00
*
* Based on pxafb . c
*
* 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 .
*
* Intel 2700 G ( Marathon ) Graphics Accelerator Frame Buffer Driver
*
*/
# include <linux/delay.h>
# include <linux/fb.h>
# include <linux/init.h>
# include <linux/module.h>
# include <linux/platform_device.h>
2006-12-08 13:40:36 +03:00
# include <linux/uaccess.h>
2006-07-14 11:24:34 +04:00
# include <asm/io.h>
# include <video/mbxfb.h>
# include "regs.h"
# include "reg_bits.h"
static unsigned long virt_base_2700 ;
2006-12-08 13:40:36 +03:00
# define write_reg(val, reg) do { writel((val), (reg)); } while(0)
/* Without this delay, the graphics appears somehow scaled and
* there is a lot of jitter in scanlines . This delay is probably
* needed only after setting some specific register ( s ) somewhere ,
* not all over the place . . . */
# define write_reg_dly(val, reg) do { writel((val), reg); udelay(1000); } while(0)
2006-07-14 11:24:34 +04:00
# define MIN_XRES 16
# define MIN_YRES 16
# define MAX_XRES 2048
# define MAX_YRES 2048
# define MAX_PALETTES 16
/* FIXME: take care of different chip revisions with different sizes
of ODFB */
# define MEMORY_OFFSET 0x60000
struct mbxfb_info {
struct device * dev ;
struct resource * fb_res ;
struct resource * fb_req ;
struct resource * reg_res ;
struct resource * reg_req ;
void __iomem * fb_virt_addr ;
unsigned long fb_phys_addr ;
void __iomem * reg_virt_addr ;
unsigned long reg_phys_addr ;
int ( * platform_probe ) ( struct fb_info * fb ) ;
int ( * platform_remove ) ( struct fb_info * fb ) ;
u32 pseudo_palette [ MAX_PALETTES ] ;
# ifdef CONFIG_FB_MBX_DEBUG
void * debugfs_data ;
# endif
} ;
static struct fb_var_screeninfo mbxfb_default __devinitdata = {
. xres = 640 ,
. yres = 480 ,
. xres_virtual = 640 ,
. yres_virtual = 480 ,
. bits_per_pixel = 16 ,
. red = { 11 , 5 , 0 } ,
. green = { 5 , 6 , 0 } ,
. blue = { 0 , 5 , 0 } ,
. activate = FB_ACTIVATE_TEST ,
. height = - 1 ,
. width = - 1 ,
. pixclock = 40000 ,
. left_margin = 48 ,
. right_margin = 16 ,
. upper_margin = 33 ,
. lower_margin = 10 ,
. hsync_len = 96 ,
. vsync_len = 2 ,
. vmode = FB_VMODE_NONINTERLACED ,
. sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT ,
} ;
static struct fb_fix_screeninfo mbxfb_fix __devinitdata = {
. id = " MBX " ,
. type = FB_TYPE_PACKED_PIXELS ,
. visual = FB_VISUAL_TRUECOLOR ,
. xpanstep = 0 ,
. ypanstep = 0 ,
. ywrapstep = 0 ,
. accel = FB_ACCEL_NONE ,
} ;
struct pixclock_div {
u8 m ;
u8 n ;
u8 p ;
} ;
static unsigned int mbxfb_get_pixclock ( unsigned int pixclock_ps ,
struct pixclock_div * div )
{
u8 m , n , p ;
unsigned int err = 0 ;
unsigned int min_err = ~ 0x0 ;
unsigned int clk ;
unsigned int best_clk = 0 ;
unsigned int ref_clk = 13000 ; /* FIXME: take from platform data */
unsigned int pixclock ;
/* convert pixclock to KHz */
pixclock = PICOS2KHZ ( pixclock_ps ) ;
2006-10-03 12:15:03 +04:00
/* PLL output freq = (ref_clk * M) / (N * 2^P)
*
* M : 1 to 63
* N : 1 to 7
* P : 0 to 7
*/
/* RAPH: When N==1, the resulting pixel clock appears to
* get divided by 2. Preventing N = 1 by starting the following
* loop at 2 prevents this . Is this a bug with my chip
* revision or something I dont understand ? */
2006-07-14 11:24:34 +04:00
for ( m = 1 ; m < 64 ; m + + ) {
2006-10-03 12:15:03 +04:00
for ( n = 2 ; n < 8 ; n + + ) {
2006-07-14 11:24:34 +04:00
for ( p = 0 ; p < 8 ; p + + ) {
clk = ( ref_clk * m ) / ( n * ( 1 < < p ) ) ;
err = ( clk > pixclock ) ? ( clk - pixclock ) :
( pixclock - clk ) ;
if ( err < min_err ) {
min_err = err ;
best_clk = clk ;
div - > m = m ;
div - > n = n ;
div - > p = p ;
}
}
}
}
return KHZ2PICOS ( best_clk ) ;
}
static int mbxfb_setcolreg ( u_int regno , u_int red , u_int green , u_int blue ,
u_int trans , struct fb_info * info )
{
u32 val , ret = 1 ;
if ( regno < MAX_PALETTES ) {
u32 * pal = info - > pseudo_palette ;
val = ( red & 0xf800 ) | ( ( green & 0xfc00 ) > > 5 ) |
( ( blue & 0xf800 ) > > 11 ) ;
pal [ regno ] = val ;
ret = 0 ;
}
return ret ;
}
static int mbxfb_check_var ( struct fb_var_screeninfo * var , struct fb_info * info )
{
struct pixclock_div div ;
var - > pixclock = mbxfb_get_pixclock ( var - > pixclock , & div ) ;
if ( var - > xres < MIN_XRES )
var - > xres = MIN_XRES ;
if ( var - > yres < MIN_YRES )
var - > yres = MIN_YRES ;
if ( var - > xres > MAX_XRES )
return - EINVAL ;
if ( var - > yres > MAX_YRES )
return - EINVAL ;
var - > xres_virtual = max ( var - > xres_virtual , var - > xres ) ;
var - > yres_virtual = max ( var - > yres_virtual , var - > yres ) ;
switch ( var - > bits_per_pixel ) {
/* 8 bits-per-pixel is not supported yet */
case 8 :
return - EINVAL ;
case 16 :
var - > green . length = ( var - > green . length = = 5 ) ? 5 : 6 ;
var - > red . length = 5 ;
var - > blue . length = 5 ;
var - > transp . length = 6 - var - > green . length ;
var - > blue . offset = 0 ;
var - > green . offset = 5 ;
var - > red . offset = 5 + var - > green . length ;
var - > transp . offset = ( 5 + var - > red . offset ) & 15 ;
break ;
case 24 : /* RGB 888 */
case 32 : /* RGBA 8888 */
var - > red . offset = 16 ;
var - > red . length = 8 ;
var - > green . offset = 8 ;
var - > green . length = 8 ;
var - > blue . offset = 0 ;
var - > blue . length = 8 ;
var - > transp . length = var - > bits_per_pixel - 24 ;
var - > transp . offset = ( var - > transp . length ) ? 24 : 0 ;
break ;
}
var - > red . msb_right = 0 ;
var - > green . msb_right = 0 ;
var - > blue . msb_right = 0 ;
var - > transp . msb_right = 0 ;
return 0 ;
}
static int mbxfb_set_par ( struct fb_info * info )
{
struct fb_var_screeninfo * var = & info - > var ;
struct pixclock_div div ;
ushort hbps , ht , hfps , has ;
ushort vbps , vt , vfps , vas ;
u32 gsctrl = readl ( GSCTRL ) ;
u32 gsadr = readl ( GSADR ) ;
info - > fix . line_length = var - > xres_virtual * var - > bits_per_pixel / 8 ;
/* setup color mode */
gsctrl & = ~ ( FMsk ( GSCTRL_GPIXFMT ) ) ;
/* FIXME: add *WORKING* support for 8-bits per color */
if ( info - > var . bits_per_pixel = = 8 ) {
return - EINVAL ;
} else {
fb_dealloc_cmap ( & info - > cmap ) ;
gsctrl & = ~ GSCTRL_LUT_EN ;
info - > fix . visual = FB_VISUAL_TRUECOLOR ;
switch ( info - > var . bits_per_pixel ) {
case 16 :
if ( info - > var . green . length = = 5 )
gsctrl | = GSCTRL_GPIXFMT_ARGB1555 ;
else
gsctrl | = GSCTRL_GPIXFMT_RGB565 ;
break ;
case 24 :
gsctrl | = GSCTRL_GPIXFMT_RGB888 ;
break ;
case 32 :
gsctrl | = GSCTRL_GPIXFMT_ARGB8888 ;
break ;
}
}
/* setup resolution */
gsctrl & = ~ ( FMsk ( GSCTRL_GSWIDTH ) | FMsk ( GSCTRL_GSHEIGHT ) ) ;
2006-10-03 12:15:05 +04:00
gsctrl | = Gsctrl_Width ( info - > var . xres ) |
Gsctrl_Height ( info - > var . yres ) ;
2006-12-08 13:40:36 +03:00
write_reg_dly ( gsctrl , GSCTRL ) ;
2006-07-14 11:24:34 +04:00
gsadr & = ~ ( FMsk ( GSADR_SRCSTRIDE ) ) ;
gsadr | = Gsadr_Srcstride ( info - > var . xres * info - > var . bits_per_pixel /
( 8 * 16 ) - 1 ) ;
2006-12-08 13:40:36 +03:00
write_reg_dly ( gsadr , GSADR ) ;
2006-07-14 11:24:34 +04:00
/* setup timings */
var - > pixclock = mbxfb_get_pixclock ( info - > var . pixclock , & div ) ;
2006-12-08 13:40:36 +03:00
write_reg_dly ( ( Disp_Pll_M ( div . m ) | Disp_Pll_N ( div . n ) |
2006-07-14 11:24:34 +04:00
Disp_Pll_P ( div . p ) | DISP_PLL_EN ) , DISPPLL ) ;
hbps = var - > hsync_len ;
has = hbps + var - > left_margin ;
hfps = has + var - > xres ;
ht = hfps + var - > right_margin ;
vbps = var - > vsync_len ;
vas = vbps + var - > upper_margin ;
vfps = vas + var - > yres ;
vt = vfps + var - > lower_margin ;
2006-12-08 13:40:36 +03:00
write_reg_dly ( ( Dht01_Hbps ( hbps ) | Dht01_Ht ( ht ) ) , DHT01 ) ;
write_reg_dly ( ( Dht02_Hlbs ( has ) | Dht02_Has ( has ) ) , DHT02 ) ;
write_reg_dly ( ( Dht03_Hfps ( hfps ) | Dht03_Hrbs ( hfps ) ) , DHT03 ) ;
write_reg_dly ( ( Dhdet_Hdes ( has ) | Dhdet_Hdef ( hfps ) ) , DHDET ) ;
2006-07-14 11:24:34 +04:00
2006-12-08 13:40:36 +03:00
write_reg_dly ( ( Dvt01_Vbps ( vbps ) | Dvt01_Vt ( vt ) ) , DVT01 ) ;
write_reg_dly ( ( Dvt02_Vtbs ( vas ) | Dvt02_Vas ( vas ) ) , DVT02 ) ;
write_reg_dly ( ( Dvt03_Vfps ( vfps ) | Dvt03_Vbbs ( vfps ) ) , DVT03 ) ;
write_reg_dly ( ( Dvdet_Vdes ( vas ) | Dvdet_Vdef ( vfps ) ) , DVDET ) ;
write_reg_dly ( ( Dvectrl_Vevent ( vfps ) | Dvectrl_Vfetch ( vbps ) ) , DVECTRL ) ;
2006-07-14 11:24:34 +04:00
2006-12-08 13:40:36 +03:00
write_reg_dly ( ( readl ( DSCTRL ) | DSCTRL_SYNCGEN_EN ) , DSCTRL ) ;
write_reg_dly ( DINTRE_VEVENT0_EN , DINTRE ) ;
2006-07-14 11:24:34 +04:00
return 0 ;
}
static int mbxfb_blank ( int blank , struct fb_info * info )
{
switch ( blank ) {
case FB_BLANK_POWERDOWN :
case FB_BLANK_VSYNC_SUSPEND :
case FB_BLANK_HSYNC_SUSPEND :
case FB_BLANK_NORMAL :
2006-12-08 13:40:36 +03:00
write_reg_dly ( ( readl ( DSCTRL ) & ~ DSCTRL_SYNCGEN_EN ) , DSCTRL ) ;
write_reg_dly ( ( readl ( PIXCLK ) & ~ PIXCLK_EN ) , PIXCLK ) ;
write_reg_dly ( ( readl ( VOVRCLK ) & ~ VOVRCLK_EN ) , VOVRCLK ) ;
2006-07-14 11:24:34 +04:00
break ;
case FB_BLANK_UNBLANK :
2006-12-08 13:40:36 +03:00
write_reg_dly ( ( readl ( DSCTRL ) | DSCTRL_SYNCGEN_EN ) , DSCTRL ) ;
write_reg_dly ( ( readl ( PIXCLK ) | PIXCLK_EN ) , PIXCLK ) ;
2006-07-14 11:24:34 +04:00
break ;
}
return 0 ;
}
2006-12-08 13:40:36 +03:00
static int mbxfb_setupOverlay ( struct mbxfb_overlaySetup * set )
{
2007-10-16 12:28:40 +04:00
u32 vsctrl , vscadr , vsadr ;
u32 sssize , spoctrl , shctrl ;
2006-12-08 13:40:36 +03:00
u32 vubase , vvbase ;
u32 vovrclk ;
if ( set - > scaled_width = = 0 | | set - > scaled_height = = 0 )
return - EINVAL ;
/* read registers which have reserved bits
* so we can write them back as - is . */
vovrclk = readl ( VOVRCLK ) ;
vsctrl = readl ( VSCTRL ) ;
vscadr = readl ( VSCADR ) ;
vubase = readl ( VUBASE ) ;
vvbase = readl ( VVBASE ) ;
2007-10-16 12:28:40 +04:00
shctrl = readl ( SHCTRL ) ;
2006-12-08 13:40:36 +03:00
spoctrl = readl ( SPOCTRL ) ;
sssize = readl ( SSSIZE ) ;
vsctrl & = ~ ( FMsk ( VSCTRL_VSWIDTH ) |
FMsk ( VSCTRL_VSHEIGHT ) |
FMsk ( VSCTRL_VPIXFMT ) |
VSCTRL_GAMMA_EN | VSCTRL_CSC_EN |
VSCTRL_COSITED ) ;
vsctrl | = Vsctrl_Width ( set - > width ) | Vsctrl_Height ( set - > height ) |
VSCTRL_CSC_EN ;
2007-10-16 12:28:40 +04:00
vscadr & = ~ ( VSCADR_STR_EN | FMsk ( VSCADR_VBASE_ADR ) ) ;
2006-12-08 13:40:36 +03:00
vubase & = ~ ( VUBASE_UVHALFSTR | FMsk ( VUBASE_UBASE_ADR ) ) ;
vvbase & = ~ ( FMsk ( VVBASE_VBASE_ADR ) ) ;
2007-10-16 12:28:40 +04:00
switch ( set - > fmt ) {
case MBXFB_FMT_YUV16 :
vsctrl | = VSCTRL_VPIXFMT_YUV12 ;
2006-12-08 13:40:36 +03:00
2007-10-16 12:28:40 +04:00
set - > Y_stride = ( ( set - > width ) + 0xf ) & ~ 0xf ;
break ;
case MBXFB_FMT_YUV12 :
vsctrl | = VSCTRL_VPIXFMT_YUV12 ;
2006-12-08 13:40:36 +03:00
2007-10-16 12:28:40 +04:00
set - > Y_stride = ( ( set - > width ) + 0xf ) & ~ 0xf ;
vubase | = VUBASE_UVHALFSTR ;
break ;
case MBXFB_FMT_UY0VY1 :
vsctrl | = VSCTRL_VPIXFMT_UY0VY1 ;
set - > Y_stride = ( set - > width * 2 + 0xf ) & ~ 0xf ;
break ;
case MBXFB_FMT_VY0UY1 :
vsctrl | = VSCTRL_VPIXFMT_VY0UY1 ;
set - > Y_stride = ( set - > width * 2 + 0xf ) & ~ 0xf ;
break ;
case MBXFB_FMT_Y0UY1V :
vsctrl | = VSCTRL_VPIXFMT_Y0UY1V ;
set - > Y_stride = ( set - > width * 2 + 0xf ) & ~ 0xf ;
break ;
case MBXFB_FMT_Y0VY1U :
vsctrl | = VSCTRL_VPIXFMT_Y0VY1U ;
set - > Y_stride = ( set - > width * 2 + 0xf ) & ~ 0xf ;
2006-12-08 13:40:36 +03:00
break ;
2007-10-16 12:28:40 +04:00
default :
return - EINVAL ;
2006-12-08 13:40:36 +03:00
}
/* VSCTRL has the bits which sets the Video Pixel Format.
* When passing from a packed to planar format ,
* if we write VSCTRL first , VVBASE and VUBASE would
* be zero if we would not set them here . ( And then ,
* the chips hangs and only a reset seems to fix it ) .
*
* If course , the values calculated here have no meaning
* for packed formats .
*/
set - > UV_stride = ( ( set - > width / 2 ) + 0x7 ) & ~ 0x7 ;
set - > U_offset = set - > height * set - > Y_stride ;
set - > V_offset = set - > U_offset +
set - > height * set - > UV_stride ;
vubase | = Vubase_Ubase_Adr (
( 0x60000 + set - > mem_offset + set - > U_offset ) > > 3 ) ;
vvbase | = Vvbase_Vbase_Adr (
( 0x60000 + set - > mem_offset + set - > V_offset ) > > 3 ) ;
2007-10-16 12:28:40 +04:00
vscadr | = Vscadr_Vbase_Adr ( ( 0x60000 + set - > mem_offset ) > > 4 ) ;
2006-12-08 13:40:36 +03:00
if ( set - > enable )
vscadr | = VSCADR_STR_EN ;
vsadr = Vsadr_Srcstride ( ( set - > Y_stride ) / 16 - 1 ) |
Vsadr_Xstart ( set - > x ) | Vsadr_Ystart ( set - > y ) ;
sssize & = ~ ( FMsk ( SSSIZE_SC_WIDTH ) | FMsk ( SSSIZE_SC_HEIGHT ) ) ;
sssize = Sssize_Sc_Width ( set - > scaled_width - 1 ) |
Sssize_Sc_Height ( set - > scaled_height - 1 ) ;
spoctrl & = ~ ( SPOCTRL_H_SC_BP | SPOCTRL_V_SC_BP |
SPOCTRL_HV_SC_OR | SPOCTRL_VS_UR_C |
2007-10-16 12:28:40 +04:00
FMsk ( SPOCTRL_VPITCH ) ) ;
spoctrl | = Spoctrl_Vpitch ( ( set - > height < < 11 ) / set - > scaled_height ) ;
2006-12-08 13:40:36 +03:00
/* Bypass horiz/vert scaler when same size */
if ( set - > scaled_width = = set - > width )
spoctrl | = SPOCTRL_H_SC_BP ;
if ( set - > scaled_height = = set - > height )
spoctrl | = SPOCTRL_V_SC_BP ;
2007-10-16 12:28:40 +04:00
shctrl & = ~ ( FMsk ( SHCTRL_HPITCH ) | SHCTRL_HDECIM ) ;
shctrl | = Shctrl_Hpitch ( ( set - > width < < 11 ) / set - > scaled_width ) ;
2006-12-08 13:40:36 +03:00
/* Video plane registers */
write_reg ( vsctrl , VSCTRL ) ;
write_reg ( vscadr , VSCADR ) ;
write_reg ( vubase , VUBASE ) ;
write_reg ( vvbase , VVBASE ) ;
write_reg ( vsadr , VSADR ) ;
/* Video scaler registers */
write_reg ( sssize , SSSIZE ) ;
write_reg ( spoctrl , SPOCTRL ) ;
write_reg ( shctrl , SHCTRL ) ;
/* Clock */
if ( set - > enable )
vovrclk | = 1 ;
else
vovrclk & = ~ 1 ;
write_reg ( vovrclk , VOVRCLK ) ;
return 0 ;
}
2007-10-16 12:28:40 +04:00
static int mbxfb_ioctl_planeorder ( struct mbxfb_planeorder * porder )
{
unsigned long gscadr , vscadr ;
if ( porder - > bottom = = porder - > top )
return - EINVAL ;
gscadr = readl ( GSCADR ) ;
vscadr = readl ( VSCADR ) ;
gscadr & = ~ ( FMsk ( GSCADR_BLEND_POS ) ) ;
vscadr & = ~ ( FMsk ( VSCADR_BLEND_POS ) ) ;
switch ( porder - > bottom ) {
case MBXFB_PLANE_GRAPHICS :
gscadr | = GSCADR_BLEND_GFX ;
break ;
case MBXFB_PLANE_VIDEO :
vscadr | = VSCADR_BLEND_GFX ;
break ;
default :
return - EINVAL ;
}
switch ( porder - > top ) {
case MBXFB_PLANE_GRAPHICS :
gscadr | = GSCADR_BLEND_VID ;
break ;
case MBXFB_PLANE_VIDEO :
vscadr | = GSCADR_BLEND_VID ;
break ;
default :
return - EINVAL ;
}
write_reg_dly ( vscadr , VSCADR ) ;
write_reg_dly ( gscadr , GSCADR ) ;
return 0 ;
}
static int mbxfb_ioctl_alphactl ( struct mbxfb_alphaCtl * alpha )
{
unsigned long vscadr , vbbase , vcmsk ;
unsigned long gscadr , gbbase , gdrctrl ;
vbbase = Vbbase_Glalpha ( alpha - > overlay_global_alpha ) |
Vbbase_Colkey ( alpha - > overlay_colorkey ) ;
gbbase = Gbbase_Glalpha ( alpha - > graphics_global_alpha ) |
Gbbase_Colkey ( alpha - > graphics_colorkey ) ;
vcmsk = readl ( VCMSK ) ;
vcmsk & = ~ ( FMsk ( VCMSK_COLKEY_M ) ) ;
vcmsk | = Vcmsk_colkey_m ( alpha - > overlay_colorkey_mask ) ;
gdrctrl = readl ( GDRCTRL ) ;
gdrctrl & = ~ ( FMsk ( GDRCTRL_COLKEYM ) ) ;
gdrctrl | = Gdrctrl_Colkeym ( alpha - > graphics_colorkey_mask ) ;
vscadr = readl ( VSCADR ) ;
vscadr & = ~ ( FMsk ( VSCADR_BLEND_M ) | VSCADR_COLKEYSRC | VSCADR_COLKEY_EN ) ;
gscadr = readl ( GSCADR ) ;
gscadr & = ~ ( FMsk ( GSCADR_BLEND_M ) | GSCADR_COLKEY_EN | GSCADR_COLKEYSRC ) ;
switch ( alpha - > overlay_colorkey_mode ) {
case MBXFB_COLORKEY_DISABLED :
break ;
case MBXFB_COLORKEY_PREVIOUS :
vscadr | = VSCADR_COLKEY_EN ;
break ;
case MBXFB_COLORKEY_CURRENT :
vscadr | = VSCADR_COLKEY_EN | VSCADR_COLKEYSRC ;
break ;
default :
return - EINVAL ;
}
switch ( alpha - > overlay_blend_mode ) {
case MBXFB_ALPHABLEND_NONE :
vscadr | = VSCADR_BLEND_NONE ;
break ;
case MBXFB_ALPHABLEND_GLOBAL :
vscadr | = VSCADR_BLEND_GLOB ;
break ;
case MBXFB_ALPHABLEND_PIXEL :
vscadr | = VSCADR_BLEND_PIX ;
break ;
default :
return - EINVAL ;
}
switch ( alpha - > graphics_colorkey_mode ) {
case MBXFB_COLORKEY_DISABLED :
break ;
case MBXFB_COLORKEY_PREVIOUS :
gscadr | = GSCADR_COLKEY_EN ;
break ;
case MBXFB_COLORKEY_CURRENT :
gscadr | = GSCADR_COLKEY_EN | GSCADR_COLKEYSRC ;
break ;
default :
return - EINVAL ;
}
switch ( alpha - > graphics_blend_mode ) {
case MBXFB_ALPHABLEND_NONE :
gscadr | = GSCADR_BLEND_NONE ;
break ;
case MBXFB_ALPHABLEND_GLOBAL :
gscadr | = GSCADR_BLEND_GLOB ;
break ;
case MBXFB_ALPHABLEND_PIXEL :
gscadr | = GSCADR_BLEND_PIX ;
break ;
default :
return - EINVAL ;
}
write_reg_dly ( vbbase , VBBASE ) ;
write_reg_dly ( gbbase , GBBASE ) ;
write_reg_dly ( vcmsk , VCMSK ) ;
write_reg_dly ( gdrctrl , GDRCTRL ) ;
write_reg_dly ( gscadr , GSCADR ) ;
write_reg_dly ( vscadr , VSCADR ) ;
return 0 ;
}
2006-12-08 13:40:36 +03:00
static int mbxfb_ioctl ( struct fb_info * info , unsigned int cmd ,
unsigned long arg )
{
2007-10-16 12:28:40 +04:00
struct mbxfb_overlaySetup setup ;
struct mbxfb_planeorder porder ;
struct mbxfb_alphaCtl alpha ;
struct mbxfb_reg reg ;
2006-12-08 13:40:36 +03:00
int res ;
2007-10-16 12:28:40 +04:00
__u32 tmp ;
2006-12-08 13:40:36 +03:00
2007-10-16 12:28:40 +04:00
switch ( cmd )
2006-12-08 13:40:36 +03:00
{
2007-10-16 12:28:40 +04:00
case MBXFB_IOCX_OVERLAY :
if ( copy_from_user ( & setup , ( void __user * ) arg ,
sizeof ( struct mbxfb_overlaySetup ) ) )
return - EFAULT ;
res = mbxfb_setupOverlay ( & setup ) ;
if ( res )
return res ;
if ( copy_to_user ( ( void __user * ) arg , & setup ,
sizeof ( struct mbxfb_overlaySetup ) ) )
return - EFAULT ;
return 0 ;
case MBXFB_IOCS_PLANEORDER :
if ( copy_from_user ( & porder , ( void __user * ) arg ,
sizeof ( struct mbxfb_planeorder ) ) )
2006-12-08 13:40:36 +03:00
return - EFAULT ;
2007-10-16 12:28:40 +04:00
return mbxfb_ioctl_planeorder ( & porder ) ;
2006-12-08 13:40:36 +03:00
2007-10-16 12:28:40 +04:00
case MBXFB_IOCS_ALPHA :
if ( copy_from_user ( & alpha , ( void __user * ) arg ,
sizeof ( struct mbxfb_alphaCtl ) ) )
2006-12-08 13:40:36 +03:00
return - EFAULT ;
2007-10-16 12:28:40 +04:00
return mbxfb_ioctl_alphactl ( & alpha ) ;
case MBXFB_IOCS_REG :
if ( copy_from_user ( & reg , ( void __user * ) arg ,
sizeof ( struct mbxfb_reg ) ) )
return - EFAULT ;
if ( reg . addr > = 0x10000 ) /* regs are from 0x3fe0000 to 0x3feffff */
return - EINVAL ;
tmp = readl ( virt_base_2700 + reg . addr ) ;
tmp & = ~ reg . mask ;
tmp | = reg . val & reg . mask ;
writel ( tmp , virt_base_2700 + reg . addr ) ;
return 0 ;
case MBXFB_IOCX_REG :
if ( copy_from_user ( & reg , ( void __user * ) arg ,
sizeof ( struct mbxfb_reg ) ) )
return - EFAULT ;
if ( reg . addr > = 0x10000 ) /* regs are from 0x3fe0000 to 0x3feffff */
return - EINVAL ;
reg . val = readl ( virt_base_2700 + reg . addr ) ;
if ( copy_to_user ( ( void __user * ) arg , & reg ,
sizeof ( struct mbxfb_reg ) ) )
return - EFAULT ;
return 0 ;
2006-12-08 13:40:36 +03:00
}
return - EINVAL ;
}
2006-07-14 11:24:34 +04:00
static struct fb_ops mbxfb_ops = {
. owner = THIS_MODULE ,
. fb_check_var = mbxfb_check_var ,
. fb_set_par = mbxfb_set_par ,
. fb_setcolreg = mbxfb_setcolreg ,
. fb_fillrect = cfb_fillrect ,
. fb_copyarea = cfb_copyarea ,
. fb_imageblit = cfb_imageblit ,
. fb_blank = mbxfb_blank ,
2006-12-08 13:40:36 +03:00
. fb_ioctl = mbxfb_ioctl ,
2006-07-14 11:24:34 +04:00
} ;
/*
Enable external SDRAM controller . Assume that all clocks are active
by now .
*/
static void __devinit setup_memc ( struct fb_info * fbi )
{
unsigned long tmp ;
int i ;
2010-01-07 02:03:52 +03:00
/* FIXME: use platform specific parameters */
2006-07-14 11:24:34 +04:00
/* setup SDRAM controller */
2006-12-08 13:40:36 +03:00
write_reg_dly ( ( LMCFG_LMC_DS | LMCFG_LMC_TS | LMCFG_LMD_TS |
2006-07-14 11:24:34 +04:00
LMCFG_LMA_TS ) ,
LMCFG ) ;
2006-12-08 13:40:36 +03:00
write_reg_dly ( LMPWR_MC_PWR_ACT , LMPWR ) ;
2006-07-14 11:24:34 +04:00
/* setup SDRAM timings */
2006-12-08 13:40:36 +03:00
write_reg_dly ( ( Lmtim_Tras ( 7 ) | Lmtim_Trp ( 3 ) | Lmtim_Trcd ( 3 ) |
2006-07-14 11:24:34 +04:00
Lmtim_Trc ( 9 ) | Lmtim_Tdpl ( 2 ) ) ,
LMTIM ) ;
/* setup SDRAM refresh rate */
2006-12-08 13:40:36 +03:00
write_reg_dly ( 0xc2b , LMREFRESH ) ;
2006-07-14 11:24:34 +04:00
/* setup SDRAM type parameters */
2006-12-08 13:40:36 +03:00
write_reg_dly ( ( LMTYPE_CASLAT_3 | LMTYPE_BKSZ_2 | LMTYPE_ROWSZ_11 |
2006-07-14 11:24:34 +04:00
LMTYPE_COLSZ_8 ) ,
LMTYPE ) ;
/* enable memory controller */
2006-12-08 13:40:36 +03:00
write_reg_dly ( LMPWR_MC_PWR_ACT , LMPWR ) ;
2006-07-14 11:24:34 +04:00
/* perform dummy reads */
for ( i = 0 ; i < 16 ; i + + ) {
tmp = readl ( fbi - > screen_base ) ;
}
}
static void enable_clocks ( struct fb_info * fbi )
{
/* enable clocks */
2006-12-08 13:40:36 +03:00
write_reg_dly ( SYSCLKSRC_PLL_2 , SYSCLKSRC ) ;
write_reg_dly ( PIXCLKSRC_PLL_1 , PIXCLKSRC ) ;
write_reg_dly ( 0x00000000 , CLKSLEEP ) ;
/* PLL output = (Frefclk * M) / (N * 2^P )
*
* M : 0x17 , N : 0x3 , P : 0x0 = = 100 Mhz !
* M : 0xb , N : 0x1 , P : 0x1 = = 71 Mhz
* */
write_reg_dly ( ( Core_Pll_M ( 0xb ) | Core_Pll_N ( 0x1 ) | Core_Pll_P ( 0x1 ) |
2006-07-14 11:24:34 +04:00
CORE_PLL_EN ) ,
COREPLL ) ;
2006-12-08 13:40:36 +03:00
write_reg_dly ( ( Disp_Pll_M ( 0x1b ) | Disp_Pll_N ( 0x7 ) | Disp_Pll_P ( 0x1 ) |
2006-07-14 11:24:34 +04:00
DISP_PLL_EN ) ,
DISPPLL ) ;
2006-12-08 13:40:36 +03:00
write_reg_dly ( 0x00000000 , VOVRCLK ) ;
write_reg_dly ( PIXCLK_EN , PIXCLK ) ;
write_reg_dly ( MEMCLK_EN , MEMCLK ) ;
2007-10-16 12:28:40 +04:00
write_reg_dly ( 0x00000001 , M24CLK ) ;
write_reg_dly ( 0x00000001 , MBXCLK ) ;
2006-12-08 13:40:36 +03:00
write_reg_dly ( SDCLK_EN , SDCLK ) ;
write_reg_dly ( 0x00000001 , PIXCLKDIV ) ;
2006-07-14 11:24:34 +04:00
}
static void __devinit setup_graphics ( struct fb_info * fbi )
{
unsigned long gsctrl ;
2007-10-16 12:28:40 +04:00
unsigned long vscadr ;
2006-07-14 11:24:34 +04:00
2006-10-03 12:15:05 +04:00
gsctrl = GSCTRL_GAMMA_EN | Gsctrl_Width ( fbi - > var . xres ) |
Gsctrl_Height ( fbi - > var . yres ) ;
2006-07-14 11:24:34 +04:00
switch ( fbi - > var . bits_per_pixel ) {
case 16 :
if ( fbi - > var . green . length = = 5 )
gsctrl | = GSCTRL_GPIXFMT_ARGB1555 ;
else
gsctrl | = GSCTRL_GPIXFMT_RGB565 ;
break ;
case 24 :
gsctrl | = GSCTRL_GPIXFMT_RGB888 ;
break ;
case 32 :
gsctrl | = GSCTRL_GPIXFMT_ARGB8888 ;
break ;
}
2006-12-08 13:40:36 +03:00
write_reg_dly ( gsctrl , GSCTRL ) ;
write_reg_dly ( 0x00000000 , GBBASE ) ;
write_reg_dly ( 0x00ffffff , GDRCTRL ) ;
write_reg_dly ( ( GSCADR_STR_EN | Gscadr_Gbase_Adr ( 0x6000 ) ) , GSCADR ) ;
write_reg_dly ( 0x00000000 , GPLUT ) ;
2007-10-16 12:28:40 +04:00
vscadr = readl ( VSCADR ) ;
vscadr & = ~ ( FMsk ( VSCADR_BLEND_POS ) | FMsk ( VSCADR_BLEND_M ) ) ;
vscadr | = VSCADR_BLEND_VID | VSCADR_BLEND_NONE ;
write_reg_dly ( vscadr , VSCADR ) ;
2006-07-14 11:24:34 +04:00
}
static void __devinit setup_display ( struct fb_info * fbi )
{
unsigned long dsctrl = 0 ;
dsctrl = DSCTRL_BLNK_POL ;
if ( fbi - > var . sync & FB_SYNC_HOR_HIGH_ACT )
dsctrl | = DSCTRL_HS_POL ;
if ( fbi - > var . sync & FB_SYNC_VERT_HIGH_ACT )
dsctrl | = DSCTRL_VS_POL ;
2006-12-08 13:40:36 +03:00
write_reg_dly ( dsctrl , DSCTRL ) ;
write_reg_dly ( 0xd0303010 , DMCTRL ) ;
write_reg_dly ( ( readl ( DSCTRL ) | DSCTRL_SYNCGEN_EN ) , DSCTRL ) ;
2006-07-14 11:24:34 +04:00
}
static void __devinit enable_controller ( struct fb_info * fbi )
{
2007-10-16 12:28:40 +04:00
u32 svctrl , shctrl ;
2006-12-08 13:40:36 +03:00
write_reg_dly ( SYSRST_RST , SYSRST ) ;
2006-07-14 11:24:34 +04:00
2007-10-16 12:28:40 +04:00
/* setup a timeout, raise drive strength */
write_reg_dly ( 0xffffff0c , SYSCFG ) ;
2006-07-14 11:24:34 +04:00
enable_clocks ( fbi ) ;
setup_memc ( fbi ) ;
setup_graphics ( fbi ) ;
setup_display ( fbi ) ;
2007-10-16 12:28:40 +04:00
shctrl = readl ( SHCTRL ) ;
shctrl & = ~ ( FMsk ( SHCTRL_HINITIAL ) ) ;
shctrl | = Shctrl_Hinitial ( 4 < < 11 ) ;
writel ( shctrl , SHCTRL ) ;
svctrl = Svctrl_Initial1 ( 1 < < 10 ) | Svctrl_Initial2 ( 1 < < 10 ) ;
writel ( svctrl , SVCTRL ) ;
writel ( SPOCTRL_H_SC_BP | SPOCTRL_V_SC_BP | SPOCTRL_VORDER_4TAP
, SPOCTRL ) ;
/* Those coefficients are good for scaling up. For scaling
* down , the application has to calculate them . */
write_reg ( 0xff000100 , VSCOEFF0 ) ;
write_reg ( 0xfdfcfdfe , VSCOEFF1 ) ;
write_reg ( 0x170d0500 , VSCOEFF2 ) ;
write_reg ( 0x3d372d22 , VSCOEFF3 ) ;
write_reg ( 0x00000040 , VSCOEFF4 ) ;
write_reg ( 0xff010100 , HSCOEFF0 ) ;
write_reg ( 0x00000000 , HSCOEFF1 ) ;
write_reg ( 0x02010000 , HSCOEFF2 ) ;
write_reg ( 0x01020302 , HSCOEFF3 ) ;
write_reg ( 0xf9fbfe00 , HSCOEFF4 ) ;
write_reg ( 0xfbf7f6f7 , HSCOEFF5 ) ;
write_reg ( 0x1c110700 , HSCOEFF6 ) ;
write_reg ( 0x3e393127 , HSCOEFF7 ) ;
write_reg ( 0x00000040 , HSCOEFF8 ) ;
2006-07-14 11:24:34 +04:00
}
# ifdef CONFIG_PM
/*
* Power management hooks . Note that we won ' t be called from IRQ context ,
* unlike the blank functions above , so we may sleep .
*/
static int mbxfb_suspend ( struct platform_device * dev , pm_message_t state )
{
/* make frame buffer memory enter self-refresh mode */
2006-12-08 13:40:36 +03:00
write_reg_dly ( LMPWR_MC_PWR_SRM , LMPWR ) ;
2006-07-14 11:24:34 +04:00
while ( LMPWRSTAT ! = LMPWRSTAT_MC_PWR_SRM )
; /* empty statement */
/* reset the device, since it's initial state is 'mostly sleeping' */
2006-12-08 13:40:36 +03:00
write_reg_dly ( SYSRST_RST , SYSRST ) ;
2006-07-14 11:24:34 +04:00
return 0 ;
}
static int mbxfb_resume ( struct platform_device * dev )
{
struct fb_info * fbi = platform_get_drvdata ( dev ) ;
enable_clocks ( fbi ) ;
/* setup_graphics(fbi); */
/* setup_display(fbi); */
2006-12-08 13:40:36 +03:00
write_reg_dly ( ( readl ( DSCTRL ) | DSCTRL_SYNCGEN_EN ) , DSCTRL ) ;
2006-07-14 11:24:34 +04:00
return 0 ;
}
# else
# define mbxfb_suspend NULL
# define mbxfb_resume NULL
# endif
/* debugfs entries */
# ifndef CONFIG_FB_MBX_DEBUG
# define mbxfb_debugfs_init(x) do {} while(0)
# define mbxfb_debugfs_remove(x) do {} while(0)
# endif
# define res_size(_r) (((_r)->end - (_r)->start) + 1)
static int __devinit mbxfb_probe ( struct platform_device * dev )
{
int ret ;
struct fb_info * fbi ;
struct mbxfb_info * mfbi ;
struct mbxfb_platform_data * pdata ;
2008-03-10 21:44:00 +03:00
dev_dbg ( & dev - > dev , " mbxfb_probe \n " ) ;
2006-07-14 11:24:34 +04:00
2006-12-08 13:40:36 +03:00
pdata = dev - > dev . platform_data ;
if ( ! pdata ) {
dev_err ( & dev - > dev , " platform data is required \n " ) ;
return - EINVAL ;
}
2006-07-14 11:24:34 +04:00
fbi = framebuffer_alloc ( sizeof ( struct mbxfb_info ) , & dev - > dev ) ;
if ( fbi = = NULL ) {
dev_err ( & dev - > dev , " framebuffer_alloc failed \n " ) ;
return - ENOMEM ;
}
mfbi = fbi - > par ;
fbi - > pseudo_palette = mfbi - > pseudo_palette ;
2006-12-08 13:40:36 +03:00
2006-07-14 11:24:34 +04:00
if ( pdata - > probe )
mfbi - > platform_probe = pdata - > probe ;
if ( pdata - > remove )
mfbi - > platform_remove = pdata - > remove ;
mfbi - > fb_res = platform_get_resource ( dev , IORESOURCE_MEM , 0 ) ;
mfbi - > reg_res = platform_get_resource ( dev , IORESOURCE_MEM , 1 ) ;
if ( ! mfbi - > fb_res | | ! mfbi - > reg_res ) {
dev_err ( & dev - > dev , " no resources found \n " ) ;
ret = - ENODEV ;
goto err1 ;
}
mfbi - > fb_req = request_mem_region ( mfbi - > fb_res - > start ,
res_size ( mfbi - > fb_res ) , dev - > name ) ;
if ( mfbi - > fb_req = = NULL ) {
dev_err ( & dev - > dev , " failed to claim framebuffer memory \n " ) ;
ret = - EINVAL ;
goto err1 ;
}
mfbi - > fb_phys_addr = mfbi - > fb_res - > start ;
mfbi - > reg_req = request_mem_region ( mfbi - > reg_res - > start ,
res_size ( mfbi - > reg_res ) , dev - > name ) ;
if ( mfbi - > reg_req = = NULL ) {
dev_err ( & dev - > dev , " failed to claim Marathon registers \n " ) ;
ret = - EINVAL ;
goto err2 ;
}
mfbi - > reg_phys_addr = mfbi - > reg_res - > start ;
mfbi - > reg_virt_addr = ioremap_nocache ( mfbi - > reg_phys_addr ,
res_size ( mfbi - > reg_req ) ) ;
if ( ! mfbi - > reg_virt_addr ) {
dev_err ( & dev - > dev , " failed to ioremap Marathon registers \n " ) ;
ret = - EINVAL ;
goto err3 ;
}
virt_base_2700 = ( unsigned long ) mfbi - > reg_virt_addr ;
mfbi - > fb_virt_addr = ioremap_nocache ( mfbi - > fb_phys_addr ,
res_size ( mfbi - > fb_req ) ) ;
if ( ! mfbi - > reg_virt_addr ) {
dev_err ( & dev - > dev , " failed to ioremap frame buffer \n " ) ;
ret = - EINVAL ;
goto err4 ;
}
fbi - > screen_base = ( char __iomem * ) ( mfbi - > fb_virt_addr + 0x60000 ) ;
2006-12-08 13:40:36 +03:00
fbi - > screen_size = pdata - > memsize ;
2006-07-14 11:24:34 +04:00
fbi - > fbops = & mbxfb_ops ;
fbi - > var = mbxfb_default ;
fbi - > fix = mbxfb_fix ;
fbi - > fix . smem_start = mfbi - > fb_phys_addr + 0x60000 ;
2006-12-08 13:40:36 +03:00
fbi - > fix . smem_len = pdata - > memsize ;
fbi - > fix . line_length = mbxfb_default . xres_virtual *
mbxfb_default . bits_per_pixel / 8 ;
2006-07-14 11:24:34 +04:00
ret = fb_alloc_cmap ( & fbi - > cmap , 256 , 0 ) ;
if ( ret < 0 ) {
dev_err ( & dev - > dev , " fb_alloc_cmap failed \n " ) ;
ret = - EINVAL ;
goto err5 ;
}
platform_set_drvdata ( dev , fbi ) ;
printk ( KERN_INFO " fb%d: mbx frame buffer device \n " , fbi - > node ) ;
if ( mfbi - > platform_probe )
mfbi - > platform_probe ( fbi ) ;
enable_controller ( fbi ) ;
mbxfb_debugfs_init ( fbi ) ;
ret = register_framebuffer ( fbi ) ;
if ( ret < 0 ) {
dev_err ( & dev - > dev , " register_framebuffer failed \n " ) ;
ret = - EINVAL ;
goto err6 ;
}
return 0 ;
err6 :
fb_dealloc_cmap ( & fbi - > cmap ) ;
err5 :
iounmap ( mfbi - > fb_virt_addr ) ;
err4 :
iounmap ( mfbi - > reg_virt_addr ) ;
err3 :
release_mem_region ( mfbi - > reg_res - > start , res_size ( mfbi - > reg_res ) ) ;
err2 :
release_mem_region ( mfbi - > fb_res - > start , res_size ( mfbi - > fb_res ) ) ;
err1 :
framebuffer_release ( fbi ) ;
return ret ;
}
static int __devexit mbxfb_remove ( struct platform_device * dev )
{
struct fb_info * fbi = platform_get_drvdata ( dev ) ;
2006-12-08 13:40:36 +03:00
write_reg_dly ( SYSRST_RST , SYSRST ) ;
2006-07-14 11:24:34 +04:00
mbxfb_debugfs_remove ( fbi ) ;
if ( fbi ) {
struct mbxfb_info * mfbi = fbi - > par ;
unregister_framebuffer ( fbi ) ;
if ( mfbi ) {
if ( mfbi - > platform_remove )
mfbi - > platform_remove ( fbi ) ;
if ( mfbi - > fb_virt_addr )
iounmap ( mfbi - > fb_virt_addr ) ;
if ( mfbi - > reg_virt_addr )
iounmap ( mfbi - > reg_virt_addr ) ;
if ( mfbi - > reg_req )
release_mem_region ( mfbi - > reg_req - > start ,
res_size ( mfbi - > reg_req ) ) ;
if ( mfbi - > fb_req )
release_mem_region ( mfbi - > fb_req - > start ,
res_size ( mfbi - > fb_req ) ) ;
}
framebuffer_release ( fbi ) ;
}
return 0 ;
}
static struct platform_driver mbxfb_driver = {
. probe = mbxfb_probe ,
. remove = mbxfb_remove ,
. suspend = mbxfb_suspend ,
. resume = mbxfb_resume ,
. driver = {
. name = " mbx-fb " ,
} ,
} ;
int __devinit mbxfb_init ( void )
{
return platform_driver_register ( & mbxfb_driver ) ;
}
static void __devexit mbxfb_exit ( void )
{
platform_driver_unregister ( & mbxfb_driver ) ;
}
module_init ( mbxfb_init ) ;
module_exit ( mbxfb_exit ) ;
MODULE_DESCRIPTION ( " loadable framebuffer driver for Marathon device " ) ;
MODULE_AUTHOR ( " Mike Rapoport, Compulab " ) ;
MODULE_LICENSE ( " GPL " ) ;