2007-02-21 00:58:21 +03:00
/* linux/drivers/video/sm501fb.c
*
* Copyright ( c ) 2006 Simtec Electronics
* Vincent Sanders < vince @ simtec . co . uk >
* Ben Dooks < ben @ simtec . co . uk >
*
* 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 .
*
* Framebuffer driver for the Silicon Motion SM501
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/string.h>
# include <linux/mm.h>
# include <linux/tty.h>
# include <linux/slab.h>
# include <linux/delay.h>
# include <linux/fb.h>
# include <linux/init.h>
# include <linux/vmalloc.h>
# include <linux/dma-mapping.h>
# include <linux/interrupt.h>
# include <linux/workqueue.h>
# include <linux/wait.h>
# include <linux/platform_device.h>
# include <linux/clk.h>
2007-10-16 12:28:38 +04:00
# include <linux/console.h>
2009-12-16 03:46:34 +03:00
# include <linux/io.h>
2007-02-21 00:58:21 +03:00
# include <asm/uaccess.h>
# include <asm/div64.h>
# ifdef CONFIG_PM
# include <linux/pm.h>
# endif
# include <linux/sm501.h>
# include <linux/sm501-regs.h>
2011-01-26 10:21:22 +03:00
# include "edid.h"
static char * fb_mode = " 640x480-16@60 " ;
static unsigned long default_bpp = 16 ;
2012-12-22 01:07:39 +04:00
static struct fb_videomode sm501_default_mode = {
2011-01-26 10:21:22 +03:00
. refresh = 60 ,
. xres = 640 ,
. yres = 480 ,
. pixclock = 20833 ,
. left_margin = 142 ,
. right_margin = 13 ,
. upper_margin = 21 ,
. lower_margin = 1 ,
. hsync_len = 69 ,
. vsync_len = 3 ,
. sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT ,
. vmode = FB_VMODE_NONINTERLACED
} ;
2007-02-21 00:58:21 +03:00
# define NR_PALETTE 256
enum sm501_controller {
HEAD_CRT = 0 ,
HEAD_PANEL = 1 ,
} ;
2008-07-24 08:31:37 +04:00
/* SM501 memory address.
*
* This structure is used to track memory usage within the SM501 framebuffer
* allocation . The sm_addr field is stored as an offset as it is often used
* against both the physical and mapped addresses .
*/
2007-02-21 00:58:21 +03:00
struct sm501_mem {
unsigned long size ;
2008-07-24 08:31:37 +04:00
unsigned long sm_addr ; /* offset from base of sm501 fb. */
2007-02-21 00:58:21 +03:00
void __iomem * k_addr ;
} ;
/* private data that is shared between all frambuffers* */
struct sm501fb_info {
struct device * dev ;
struct fb_info * fb [ 2 ] ; /* fb info for both heads */
struct resource * fbmem_res ; /* framebuffer resource */
struct resource * regs_res ; /* registers resource */
2009-12-16 03:46:35 +03:00
struct resource * regs2d_res ; /* 2d registers resource */
2007-02-21 00:58:21 +03:00
struct sm501_platdata_fb * pdata ; /* our platform data */
2007-10-16 12:28:37 +04:00
unsigned long pm_crt_ctrl ; /* pm: crt ctrl save */
2007-02-21 00:58:21 +03:00
int irq ;
int swap_endian ; /* set to swap rgb=>bgr */
void __iomem * regs ; /* remapped registers */
2009-12-16 03:46:35 +03:00
void __iomem * regs2d ; /* 2d remapped registers */
2007-02-21 00:58:21 +03:00
void __iomem * fbmem ; /* remapped framebuffer */
size_t fbmem_len ; /* length of remapped region */
2011-01-26 10:21:22 +03:00
u8 * edid_data ;
2007-02-21 00:58:21 +03:00
} ;
/* per-framebuffer private data */
struct sm501fb_par {
u32 pseudo_palette [ 16 ] ;
enum sm501_controller head ;
struct sm501_mem cursor ;
struct sm501_mem screen ;
struct fb_ops ops ;
void * store_fb ;
void * store_cursor ;
void __iomem * cursor_regs ;
struct sm501fb_info * info ;
} ;
/* Helper functions */
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 ;
}
/* sm501fb_sync_regs()
*
* This call is mainly for PCI bus systems where we need to
* ensure that any writes to the bus are completed before the
* next phase , or after completing a function .
*/
static inline void sm501fb_sync_regs ( struct sm501fb_info * info )
{
2011-01-24 12:57:20 +03:00
smc501_readl ( info - > regs ) ;
2007-02-21 00:58:21 +03:00
}
/* sm501_alloc_mem
*
* This is an attempt to lay out memory for the two framebuffers and
* everything else
*
2009-12-16 03:46:35 +03:00
* | fbmem_res - > start fbmem_res - > end |
* | |
* | fb [ 0 ] . fix . smem_start | | fb [ 1 ] . fix . smem_start | 2 K |
2007-02-21 00:58:21 +03:00
* | - > fb [ 0 ] . fix . smem_len < - | spare | - > fb [ 1 ] . fix . smem_len < - | - > cursors < - |
*
* The " spare " space is for the 2 d engine data
* the fixed is space for the cursors ( 2 x1Kbyte )
*
* we need to allocate memory for the 2 D acceleration engine
* command list and the data for the engine to deal with .
*
* - all allocations must be 128 bit aligned
* - cursors are 64 x64x2 bits ( 1 Kbyte )
*
*/
# define SM501_MEMF_CURSOR (1)
# define SM501_MEMF_PANEL (2)
# define SM501_MEMF_CRT (4)
# define SM501_MEMF_ACCEL (8)
2007-03-01 07:11:06 +03:00
static int sm501_alloc_mem ( struct sm501fb_info * inf , struct sm501_mem * mem ,
2009-06-30 22:41:29 +04:00
unsigned int why , size_t size , u32 smem_len )
2007-02-21 00:58:21 +03:00
{
2008-07-24 08:31:37 +04:00
struct sm501fb_par * par ;
2008-07-24 08:31:36 +04:00
struct fb_info * fbi ;
2008-07-24 08:31:37 +04:00
unsigned int ptr ;
unsigned int end ;
2007-02-21 00:58:21 +03:00
switch ( why ) {
case SM501_MEMF_CURSOR :
ptr = inf - > fbmem_len - size ;
2008-07-24 08:31:37 +04:00
inf - > fbmem_len = ptr ; /* adjust available memory. */
2007-02-21 00:58:21 +03:00
break ;
case SM501_MEMF_PANEL :
2009-01-07 01:42:36 +03:00
if ( size > inf - > fbmem_len )
return - ENOMEM ;
2007-02-21 00:58:21 +03:00
ptr = inf - > fbmem_len - size ;
2008-07-24 08:31:37 +04:00
fbi = inf - > fb [ HEAD_CRT ] ;
/* round down, some programs such as directfb do not draw
* 0 , 0 correctly unless the start is aligned to a page start .
*/
if ( ptr > 0 )
ptr & = ~ ( PAGE_SIZE - 1 ) ;
2008-07-24 08:31:36 +04:00
2009-06-30 22:41:29 +04:00
if ( fbi & & ptr < smem_len )
2007-02-21 00:58:21 +03:00
return - ENOMEM ;
break ;
case SM501_MEMF_CRT :
ptr = 0 ;
2008-07-24 08:31:37 +04:00
/* check to see if we have panel memory allocated
* which would put an limit on available memory . */
fbi = inf - > fb [ HEAD_PANEL ] ;
if ( fbi ) {
par = fbi - > par ;
end = par - > screen . k_addr ? par - > screen . sm_addr : inf - > fbmem_len ;
} else
end = inf - > fbmem_len ;
if ( ( ptr + size ) > end )
return - ENOMEM ;
2007-02-21 00:58:21 +03:00
break ;
case SM501_MEMF_ACCEL :
2008-07-24 08:31:37 +04:00
fbi = inf - > fb [ HEAD_CRT ] ;
2009-06-30 22:41:29 +04:00
ptr = fbi ? smem_len : 0 ;
2008-07-24 08:31:36 +04:00
2008-07-24 08:31:37 +04:00
fbi = inf - > fb [ HEAD_PANEL ] ;
if ( fbi ) {
par = fbi - > par ;
end = par - > screen . sm_addr ;
} else
2008-07-24 08:31:36 +04:00
end = inf - > fbmem_len ;
2007-02-21 00:58:21 +03:00
2008-07-24 08:31:36 +04:00
if ( ( ptr + size ) > end )
2007-02-21 00:58:21 +03:00
return - ENOMEM ;
2008-07-24 08:31:36 +04:00
2007-02-21 00:58:21 +03:00
break ;
default :
return - EINVAL ;
}
mem - > size = size ;
mem - > sm_addr = ptr ;
mem - > k_addr = inf - > fbmem + ptr ;
dev_dbg ( inf - > dev , " %s: result %08lx, %p - %u, %zd \n " ,
__func__ , mem - > sm_addr , mem - > k_addr , why , size ) ;
return 0 ;
}
/* sm501fb_ps_to_hz
*
* Converts a period in picoseconds to Hz .
*
* Note , we try to keep this in Hz to minimise rounding with
* the limited PLL settings on the SM501 .
*/
static unsigned long sm501fb_ps_to_hz ( unsigned long psvalue )
{
unsigned long long numerator = 1000000000000ULL ;
/* 10^12 / picosecond period gives frequency in Hz */
do_div ( numerator , psvalue ) ;
return ( unsigned long ) numerator ;
}
2011-03-31 05:57:33 +04:00
/* sm501fb_hz_to_ps is identical to the opposite transform */
2007-02-21 00:58:21 +03:00
# define sm501fb_hz_to_ps(x) sm501fb_ps_to_hz(x)
/* sm501fb_setup_gamma
*
* Programs a linear 1.0 gamma ramp in case the gamma
* correction is enabled without programming anything else .
*/
static void sm501fb_setup_gamma ( struct sm501fb_info * fbi ,
unsigned long palette )
{
unsigned long value = 0 ;
int offset ;
/* set gamma values */
for ( offset = 0 ; offset < 256 * 4 ; offset + = 4 ) {
2011-01-24 12:57:20 +03:00
smc501_writel ( value , fbi - > regs + palette + offset ) ;
2007-02-21 00:58:21 +03:00
value + = 0x010101 ; /* Advance RGB by 1,1,1.*/
}
}
/* sm501fb_check_var
*
* check common variables for both panel and crt
*/
static int sm501fb_check_var ( struct fb_var_screeninfo * var ,
struct fb_info * info )
{
struct sm501fb_par * par = info - > par ;
struct sm501fb_info * sm = par - > info ;
unsigned long tmp ;
/* check we can fit these values into the registers */
2008-03-05 01:28:49 +03:00
if ( var - > hsync_len > 255 | | var - > vsync_len > 63 )
2007-02-21 00:58:21 +03:00
return - EINVAL ;
2008-03-05 01:28:49 +03:00
/* hdisplay end and hsync start */
if ( ( var - > xres + var - > right_margin ) > 4096 )
2007-02-21 00:58:21 +03:00
return - EINVAL ;
2008-03-05 01:28:49 +03:00
/* vdisplay end and vsync start */
2007-02-21 00:58:21 +03:00
if ( ( var - > yres + var - > lower_margin ) > 2048 )
return - EINVAL ;
/* hard limits of device */
if ( h_total ( var ) > 4096 | | v_total ( var ) > 2048 )
return - EINVAL ;
/* check our line length is going to be 128 bit aligned */
tmp = ( var - > xres * var - > bits_per_pixel ) / 8 ;
if ( ( tmp & 15 ) ! = 0 )
return - EINVAL ;
/* check the virtual size */
if ( var - > xres_virtual > 4096 | | var - > yres_virtual > 2048 )
return - EINVAL ;
/* can cope with 8,16 or 32bpp */
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 = = 24 )
var - > bits_per_pixel = 32 ;
/* set r/g/b positions and validate bpp */
switch ( var - > bits_per_pixel ) {
case 8 :
var - > red . length = var - > bits_per_pixel ;
var - > red . offset = 0 ;
var - > green . length = var - > bits_per_pixel ;
var - > green . offset = 0 ;
var - > blue . length = var - > bits_per_pixel ;
var - > blue . offset = 0 ;
var - > transp . length = 0 ;
2008-03-05 01:28:48 +03:00
var - > transp . offset = 0 ;
2007-02-21 00:58:21 +03:00
break ;
case 16 :
if ( sm - > pdata - > flags & SM501_FBPD_SWAP_FB_ENDIAN ) {
var - > blue . offset = 11 ;
var - > green . offset = 5 ;
var - > red . offset = 0 ;
2008-03-05 01:28:47 +03:00
} else {
var - > red . offset = 11 ;
var - > green . offset = 5 ;
var - > blue . offset = 0 ;
2007-02-21 00:58:21 +03:00
}
2008-03-05 01:28:48 +03:00
var - > transp . offset = 0 ;
2007-02-21 00:58:21 +03:00
var - > red . length = 5 ;
var - > green . length = 6 ;
var - > blue . length = 5 ;
var - > transp . length = 0 ;
break ;
case 32 :
if ( sm - > pdata - > flags & SM501_FBPD_SWAP_FB_ENDIAN ) {
var - > transp . offset = 0 ;
var - > red . offset = 8 ;
var - > green . offset = 16 ;
var - > blue . offset = 24 ;
} else {
var - > transp . offset = 24 ;
var - > red . offset = 16 ;
var - > green . offset = 8 ;
var - > blue . offset = 0 ;
}
var - > red . length = 8 ;
var - > green . length = 8 ;
var - > blue . length = 8 ;
var - > transp . length = 0 ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
/*
* sm501fb_check_var_crt ( ) :
*
* check the parameters for the CRT head , and either bring them
* back into range , or return - EINVAL .
*/
static int sm501fb_check_var_crt ( struct fb_var_screeninfo * var ,
struct fb_info * info )
{
return sm501fb_check_var ( var , info ) ;
}
/* sm501fb_check_var_pnl():
*
* check the parameters for the CRT head , and either bring them
* back into range , or return - EINVAL .
*/
static int sm501fb_check_var_pnl ( struct fb_var_screeninfo * var ,
struct fb_info * info )
{
return sm501fb_check_var ( var , info ) ;
}
/* sm501fb_set_par_common
*
* set common registers for framebuffers
*/
static int sm501fb_set_par_common ( struct fb_info * info ,
struct fb_var_screeninfo * var )
{
struct sm501fb_par * par = info - > par ;
struct sm501fb_info * fbi = par - > info ;
unsigned long pixclock ; /* pixelclock in Hz */
tree-wide: Assorted spelling fixes
In particular, several occurances of funny versions of 'success',
'unknown', 'therefore', 'acknowledge', 'argument', 'achieve', 'address',
'beginning', 'desirable', 'separate' and 'necessary' are fixed.
Signed-off-by: Daniel Mack <daniel@caiaq.de>
Cc: Joe Perches <joe@perches.com>
Cc: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2010-02-03 03:01:28 +03:00
unsigned long sm501pixclock ; /* pixelclock the 501 can achieve in Hz */
2007-02-21 00:58:21 +03:00
unsigned int mem_type ;
unsigned int clock_type ;
unsigned int head_addr ;
2009-06-30 22:41:29 +04:00
unsigned int smem_len ;
2007-02-21 00:58:21 +03:00
dev_dbg ( fbi - > dev , " %s: %dx%d, bpp = %d, virtual %dx%d \n " ,
__func__ , var - > xres , var - > yres , var - > bits_per_pixel ,
var - > xres_virtual , var - > yres_virtual ) ;
switch ( par - > head ) {
case HEAD_CRT :
mem_type = SM501_MEMF_CRT ;
clock_type = SM501_CLOCK_V2XCLK ;
head_addr = SM501_DC_CRT_FB_ADDR ;
break ;
case HEAD_PANEL :
mem_type = SM501_MEMF_PANEL ;
clock_type = SM501_CLOCK_P2XCLK ;
head_addr = SM501_DC_PANEL_FB_ADDR ;
break ;
default :
mem_type = 0 ; /* stop compiler warnings */
head_addr = 0 ;
clock_type = 0 ;
}
switch ( var - > bits_per_pixel ) {
case 8 :
info - > fix . visual = FB_VISUAL_PSEUDOCOLOR ;
break ;
case 16 :
2008-03-05 01:28:46 +03:00
info - > fix . visual = FB_VISUAL_TRUECOLOR ;
2007-02-21 00:58:21 +03:00
break ;
case 32 :
info - > fix . visual = FB_VISUAL_TRUECOLOR ;
break ;
}
/* allocate fb memory within 501 */
info - > fix . line_length = ( var - > xres_virtual * var - > bits_per_pixel ) / 8 ;
2009-06-30 22:41:29 +04:00
smem_len = info - > fix . line_length * var - > yres_virtual ;
2007-02-21 00:58:21 +03:00
dev_dbg ( fbi - > dev , " %s: line length = %u \n " , __func__ ,
info - > fix . line_length ) ;
2009-06-30 22:41:29 +04:00
if ( sm501_alloc_mem ( fbi , & par - > screen , mem_type , smem_len , smem_len ) ) {
2007-02-21 00:58:21 +03:00
dev_err ( fbi - > dev , " no memory available \n " ) ;
return - ENOMEM ;
}
2009-06-30 22:41:29 +04:00
mutex_lock ( & info - > mm_lock ) ;
2007-02-21 00:58:21 +03:00
info - > fix . smem_start = fbi - > fbmem_res - > start + par - > screen . sm_addr ;
2009-06-30 22:41:29 +04:00
info - > fix . smem_len = smem_len ;
mutex_unlock ( & info - > mm_lock ) ;
2007-02-21 00:58:21 +03:00
info - > screen_base = fbi - > fbmem + par - > screen . sm_addr ;
info - > screen_size = info - > fix . smem_len ;
/* set start of framebuffer to the screen */
2011-01-24 12:57:20 +03:00
smc501_writel ( par - > screen . sm_addr | SM501_ADDR_FLIP ,
fbi - > regs + head_addr ) ;
2007-02-21 00:58:21 +03:00
/* program CRT clock */
pixclock = sm501fb_ps_to_hz ( var - > pixclock ) ;
sm501pixclock = sm501_set_clock ( fbi - > dev - > parent , clock_type ,
pixclock ) ;
/* update fb layer with actual clock used */
var - > pixclock = sm501fb_hz_to_ps ( sm501pixclock ) ;
dev_dbg ( fbi - > dev , " %s: pixclock(ps) = %u, pixclock(Hz) = %lu, "
" sm501pixclock = %lu, error = %ld%% \n " ,
__func__ , var - > pixclock , pixclock , sm501pixclock ,
( ( pixclock - sm501pixclock ) * 100 ) / pixclock ) ;
return 0 ;
}
/* sm501fb_set_par_geometry
*
* set the geometry registers for specified framebuffer .
*/
static void sm501fb_set_par_geometry ( struct fb_info * info ,
struct fb_var_screeninfo * var )
{
struct sm501fb_par * par = info - > par ;
struct sm501fb_info * fbi = par - > info ;
void __iomem * base = fbi - > regs ;
unsigned long reg ;
if ( par - > head = = HEAD_CRT )
base + = SM501_DC_CRT_H_TOT ;
else
base + = SM501_DC_PANEL_H_TOT ;
/* set framebuffer width and display width */
reg = info - > fix . line_length ;
reg | = ( ( var - > xres * var - > bits_per_pixel ) / 8 ) < < 16 ;
2011-01-24 12:57:20 +03:00
smc501_writel ( reg , fbi - > regs + ( par - > head = = HEAD_CRT ?
2007-02-21 00:58:21 +03:00
SM501_DC_CRT_FB_OFFSET : SM501_DC_PANEL_FB_OFFSET ) ) ;
/* program horizontal total */
reg = ( h_total ( var ) - 1 ) < < 16 ;
reg | = ( var - > xres - 1 ) ;
2011-01-24 12:57:20 +03:00
smc501_writel ( reg , base + SM501_OFF_DC_H_TOT ) ;
2007-02-21 00:58:21 +03:00
/* program horizontal sync */
reg = var - > hsync_len < < 16 ;
reg | = var - > xres + var - > right_margin - 1 ;
2011-01-24 12:57:20 +03:00
smc501_writel ( reg , base + SM501_OFF_DC_H_SYNC ) ;
2007-02-21 00:58:21 +03:00
/* program vertical total */
reg = ( v_total ( var ) - 1 ) < < 16 ;
reg | = ( var - > yres - 1 ) ;
2011-01-24 12:57:20 +03:00
smc501_writel ( reg , base + SM501_OFF_DC_V_TOT ) ;
2007-02-21 00:58:21 +03:00
/* program vertical sync */
reg = var - > vsync_len < < 16 ;
reg | = var - > yres + var - > lower_margin - 1 ;
2011-01-24 12:57:20 +03:00
smc501_writel ( reg , base + SM501_OFF_DC_V_SYNC ) ;
2007-02-21 00:58:21 +03:00
}
/* sm501fb_pan_crt
*
* pan the CRT display output within an virtual framebuffer
*/
static int sm501fb_pan_crt ( struct fb_var_screeninfo * var ,
struct fb_info * info )
{
struct sm501fb_par * par = info - > par ;
struct sm501fb_info * fbi = par - > info ;
2011-05-25 13:34:52 +04:00
unsigned int bytes_pixel = info - > var . bits_per_pixel / 8 ;
2007-02-21 00:58:21 +03:00
unsigned long reg ;
unsigned long xoffs ;
xoffs = var - > xoffset * bytes_pixel ;
2011-01-24 12:57:20 +03:00
reg = smc501_readl ( fbi - > regs + SM501_DC_CRT_CONTROL ) ;
2007-02-21 00:58:21 +03:00
reg & = ~ SM501_DC_CRT_CONTROL_PIXEL_MASK ;
reg | = ( ( xoffs & 15 ) / bytes_pixel ) < < 4 ;
2011-01-24 12:57:20 +03:00
smc501_writel ( reg , fbi - > regs + SM501_DC_CRT_CONTROL ) ;
2007-02-21 00:58:21 +03:00
reg = ( par - > screen . sm_addr + xoffs +
var - > yoffset * info - > fix . line_length ) ;
2011-01-24 12:57:20 +03:00
smc501_writel ( reg | SM501_ADDR_FLIP , fbi - > regs + SM501_DC_CRT_FB_ADDR ) ;
2007-02-21 00:58:21 +03:00
sm501fb_sync_regs ( fbi ) ;
return 0 ;
}
/* sm501fb_pan_pnl
*
* pan the panel display output within an virtual framebuffer
*/
static int sm501fb_pan_pnl ( struct fb_var_screeninfo * var ,
struct fb_info * info )
{
struct sm501fb_par * par = info - > par ;
struct sm501fb_info * fbi = par - > info ;
unsigned long reg ;
2011-05-25 13:34:52 +04:00
reg = var - > xoffset | ( info - > var . xres_virtual < < 16 ) ;
2011-01-24 12:57:20 +03:00
smc501_writel ( reg , fbi - > regs + SM501_DC_PANEL_FB_WIDTH ) ;
2007-02-21 00:58:21 +03:00
2011-05-25 13:34:52 +04:00
reg = var - > yoffset | ( info - > var . yres_virtual < < 16 ) ;
2011-01-24 12:57:20 +03:00
smc501_writel ( reg , fbi - > regs + SM501_DC_PANEL_FB_HEIGHT ) ;
2007-02-21 00:58:21 +03:00
sm501fb_sync_regs ( fbi ) ;
return 0 ;
}
/* sm501fb_set_par_crt
*
* Set the CRT video mode from the fb_info structure
*/
static int sm501fb_set_par_crt ( struct fb_info * info )
{
struct sm501fb_par * par = info - > par ;
struct sm501fb_info * fbi = par - > info ;
struct fb_var_screeninfo * var = & info - > var ;
unsigned long control ; /* control register */
int ret ;
/* activate new configuration */
dev_dbg ( fbi - > dev , " %s(%p) \n " , __func__ , info ) ;
/* enable CRT DAC - note 0 is on!*/
sm501_misc_control ( fbi - > dev - > parent , 0 , SM501_MISC_DAC_POWER ) ;
2011-01-24 12:57:20 +03:00
control = smc501_readl ( fbi - > regs + SM501_DC_CRT_CONTROL ) ;
2007-02-21 00:58:21 +03:00
control & = ( SM501_DC_CRT_CONTROL_PIXEL_MASK |
SM501_DC_CRT_CONTROL_GAMMA |
SM501_DC_CRT_CONTROL_BLANK |
SM501_DC_CRT_CONTROL_SEL |
SM501_DC_CRT_CONTROL_CP |
SM501_DC_CRT_CONTROL_TVP ) ;
/* set the sync polarities before we check data source */
if ( ( var - > sync & FB_SYNC_HOR_HIGH_ACT ) = = 0 )
control | = SM501_DC_CRT_CONTROL_HSP ;
if ( ( var - > sync & FB_SYNC_VERT_HIGH_ACT ) = = 0 )
control | = SM501_DC_CRT_CONTROL_VSP ;
if ( ( control & SM501_DC_CRT_CONTROL_SEL ) = = 0 ) {
/* the head is displaying panel data... */
2009-06-30 22:41:29 +04:00
sm501_alloc_mem ( fbi , & par - > screen , SM501_MEMF_CRT , 0 ,
info - > fix . smem_len ) ;
2007-02-21 00:58:21 +03:00
goto out_update ;
}
ret = sm501fb_set_par_common ( info , var ) ;
if ( ret ) {
dev_err ( fbi - > dev , " failed to set common parameters \n " ) ;
return ret ;
}
sm501fb_pan_crt ( var , info ) ;
sm501fb_set_par_geometry ( info , var ) ;
control | = SM501_FIFO_3 ; /* fill if >3 free slots */
switch ( var - > bits_per_pixel ) {
case 8 :
control | = SM501_DC_CRT_CONTROL_8BPP ;
break ;
case 16 :
control | = SM501_DC_CRT_CONTROL_16BPP ;
2008-03-05 01:28:46 +03:00
sm501fb_setup_gamma ( fbi , SM501_DC_CRT_PALETTE ) ;
2007-02-21 00:58:21 +03:00
break ;
case 32 :
control | = SM501_DC_CRT_CONTROL_32BPP ;
sm501fb_setup_gamma ( fbi , SM501_DC_CRT_PALETTE ) ;
break ;
default :
BUG ( ) ;
}
control | = SM501_DC_CRT_CONTROL_SEL ; /* CRT displays CRT data */
control | = SM501_DC_CRT_CONTROL_TE ; /* enable CRT timing */
control | = SM501_DC_CRT_CONTROL_ENABLE ; /* enable CRT plane */
out_update :
dev_dbg ( fbi - > dev , " new control is %08lx \n " , control ) ;
2011-01-24 12:57:20 +03:00
smc501_writel ( control , fbi - > regs + SM501_DC_CRT_CONTROL ) ;
2007-02-21 00:58:21 +03:00
sm501fb_sync_regs ( fbi ) ;
return 0 ;
}
static void sm501fb_panel_power ( struct sm501fb_info * fbi , int to )
{
unsigned long control ;
void __iomem * ctrl_reg = fbi - > regs + SM501_DC_PANEL_CONTROL ;
2008-02-06 12:39:24 +03:00
struct sm501_platdata_fbsub * pd = fbi - > pdata - > fb_pnl ;
2007-02-21 00:58:21 +03:00
2011-01-24 12:57:20 +03:00
control = smc501_readl ( ctrl_reg ) ;
2007-02-21 00:58:21 +03:00
if ( to & & ( control & SM501_DC_PANEL_CONTROL_VDD ) = = 0 ) {
/* enable panel power */
control | = SM501_DC_PANEL_CONTROL_VDD ; /* FPVDDEN */
2011-01-24 12:57:20 +03:00
smc501_writel ( control , ctrl_reg ) ;
2007-02-21 00:58:21 +03:00
sm501fb_sync_regs ( fbi ) ;
mdelay ( 10 ) ;
control | = SM501_DC_PANEL_CONTROL_DATA ; /* DATA */
2011-01-24 12:57:20 +03:00
smc501_writel ( control , ctrl_reg ) ;
2007-02-21 00:58:21 +03:00
sm501fb_sync_regs ( fbi ) ;
mdelay ( 10 ) ;
2008-07-24 08:31:35 +04:00
/* VBIASEN */
2008-05-24 00:04:53 +04:00
if ( ! ( pd - > flags & SM501FB_FLAG_PANEL_NO_VBIASEN ) ) {
2008-07-24 08:31:35 +04:00
if ( pd - > flags & SM501FB_FLAG_PANEL_INV_VBIASEN )
control & = ~ SM501_DC_PANEL_CONTROL_BIAS ;
else
control | = SM501_DC_PANEL_CONTROL_BIAS ;
2011-01-24 12:57:20 +03:00
smc501_writel ( control , ctrl_reg ) ;
2008-02-06 12:39:24 +03:00
sm501fb_sync_regs ( fbi ) ;
mdelay ( 10 ) ;
}
2007-02-21 00:58:21 +03:00
2008-05-24 00:04:53 +04:00
if ( ! ( pd - > flags & SM501FB_FLAG_PANEL_NO_FPEN ) ) {
2008-07-24 08:31:35 +04:00
if ( pd - > flags & SM501FB_FLAG_PANEL_INV_FPEN )
control & = ~ SM501_DC_PANEL_CONTROL_FPEN ;
else
control | = SM501_DC_PANEL_CONTROL_FPEN ;
2011-01-24 12:57:20 +03:00
smc501_writel ( control , ctrl_reg ) ;
2008-02-06 12:39:24 +03:00
sm501fb_sync_regs ( fbi ) ;
mdelay ( 10 ) ;
}
2007-02-21 00:58:21 +03:00
} else if ( ! to & & ( control & SM501_DC_PANEL_CONTROL_VDD ) ! = 0 ) {
/* disable panel power */
2008-05-24 00:04:53 +04:00
if ( ! ( pd - > flags & SM501FB_FLAG_PANEL_NO_FPEN ) ) {
2008-07-24 08:31:35 +04:00
if ( pd - > flags & SM501FB_FLAG_PANEL_INV_FPEN )
control | = SM501_DC_PANEL_CONTROL_FPEN ;
else
control & = ~ SM501_DC_PANEL_CONTROL_FPEN ;
2011-01-24 12:57:20 +03:00
smc501_writel ( control , ctrl_reg ) ;
2008-02-06 12:39:24 +03:00
sm501fb_sync_regs ( fbi ) ;
mdelay ( 10 ) ;
}
2007-02-21 00:58:21 +03:00
2008-05-24 00:04:53 +04:00
if ( ! ( pd - > flags & SM501FB_FLAG_PANEL_NO_VBIASEN ) ) {
2008-07-24 08:31:35 +04:00
if ( pd - > flags & SM501FB_FLAG_PANEL_INV_VBIASEN )
control | = SM501_DC_PANEL_CONTROL_BIAS ;
else
control & = ~ SM501_DC_PANEL_CONTROL_BIAS ;
2011-01-24 12:57:20 +03:00
smc501_writel ( control , ctrl_reg ) ;
2008-02-06 12:39:24 +03:00
sm501fb_sync_regs ( fbi ) ;
mdelay ( 10 ) ;
}
2007-02-21 00:58:21 +03:00
control & = ~ SM501_DC_PANEL_CONTROL_DATA ;
2011-01-24 12:57:20 +03:00
smc501_writel ( control , ctrl_reg ) ;
2007-02-21 00:58:21 +03:00
sm501fb_sync_regs ( fbi ) ;
mdelay ( 10 ) ;
control & = ~ SM501_DC_PANEL_CONTROL_VDD ;
2011-01-24 12:57:20 +03:00
smc501_writel ( control , ctrl_reg ) ;
2007-02-21 00:58:21 +03:00
sm501fb_sync_regs ( fbi ) ;
mdelay ( 10 ) ;
}
sm501fb_sync_regs ( fbi ) ;
}
/* sm501fb_set_par_pnl
*
* Set the panel video mode from the fb_info structure
*/
static int sm501fb_set_par_pnl ( struct fb_info * info )
{
struct sm501fb_par * par = info - > par ;
struct sm501fb_info * fbi = par - > info ;
struct fb_var_screeninfo * var = & info - > var ;
unsigned long control ;
unsigned long reg ;
int ret ;
dev_dbg ( fbi - > dev , " %s(%p) \n " , __func__ , info ) ;
/* activate this new configuration */
ret = sm501fb_set_par_common ( info , var ) ;
if ( ret )
return ret ;
sm501fb_pan_pnl ( var , info ) ;
sm501fb_set_par_geometry ( info , var ) ;
/* update control register */
2011-01-24 12:57:20 +03:00
control = smc501_readl ( fbi - > regs + SM501_DC_PANEL_CONTROL ) ;
2007-02-21 00:58:21 +03:00
control & = ( SM501_DC_PANEL_CONTROL_GAMMA |
SM501_DC_PANEL_CONTROL_VDD |
SM501_DC_PANEL_CONTROL_DATA |
SM501_DC_PANEL_CONTROL_BIAS |
SM501_DC_PANEL_CONTROL_FPEN |
SM501_DC_PANEL_CONTROL_CP |
SM501_DC_PANEL_CONTROL_CK |
SM501_DC_PANEL_CONTROL_HP |
SM501_DC_PANEL_CONTROL_VP |
SM501_DC_PANEL_CONTROL_HPD |
SM501_DC_PANEL_CONTROL_VPD ) ;
control | = SM501_FIFO_3 ; /* fill if >3 free slots */
switch ( var - > bits_per_pixel ) {
case 8 :
control | = SM501_DC_PANEL_CONTROL_8BPP ;
break ;
case 16 :
control | = SM501_DC_PANEL_CONTROL_16BPP ;
2008-03-05 01:28:46 +03:00
sm501fb_setup_gamma ( fbi , SM501_DC_PANEL_PALETTE ) ;
2007-02-21 00:58:21 +03:00
break ;
case 32 :
control | = SM501_DC_PANEL_CONTROL_32BPP ;
sm501fb_setup_gamma ( fbi , SM501_DC_PANEL_PALETTE ) ;
break ;
default :
BUG ( ) ;
}
2011-01-24 12:57:20 +03:00
smc501_writel ( 0x0 , fbi - > regs + SM501_DC_PANEL_PANNING_CONTROL ) ;
2007-02-21 00:58:21 +03:00
/* panel plane top left and bottom right location */
2011-01-24 12:57:20 +03:00
smc501_writel ( 0x00 , fbi - > regs + SM501_DC_PANEL_TL_LOC ) ;
2007-02-21 00:58:21 +03:00
reg = var - > xres - 1 ;
reg | = ( var - > yres - 1 ) < < 16 ;
2011-01-24 12:57:20 +03:00
smc501_writel ( reg , fbi - > regs + SM501_DC_PANEL_BR_LOC ) ;
2007-02-21 00:58:21 +03:00
/* program panel control register */
control | = SM501_DC_PANEL_CONTROL_TE ; /* enable PANEL timing */
control | = SM501_DC_PANEL_CONTROL_EN ; /* enable PANEL gfx plane */
if ( ( var - > sync & FB_SYNC_HOR_HIGH_ACT ) = = 0 )
control | = SM501_DC_PANEL_CONTROL_HSP ;
if ( ( var - > sync & FB_SYNC_VERT_HIGH_ACT ) = = 0 )
control | = SM501_DC_PANEL_CONTROL_VSP ;
2011-01-24 12:57:20 +03:00
smc501_writel ( control , fbi - > regs + SM501_DC_PANEL_CONTROL ) ;
2007-02-21 00:58:21 +03:00
sm501fb_sync_regs ( fbi ) ;
2007-10-16 12:28:39 +04:00
/* ensure the panel interface is not tristated at this point */
sm501_modify_reg ( fbi - > dev - > parent , SM501_SYSTEM_CONTROL ,
0 , SM501_SYSCTRL_PANEL_TRISTATE ) ;
2007-02-21 00:58:21 +03:00
/* power the panel up */
sm501fb_panel_power ( fbi , 1 ) ;
return 0 ;
}
/* chan_to_field
*
* convert a colour value into a field position
*
* from pxafb . c
*/
static inline unsigned int chan_to_field ( unsigned int chan ,
struct fb_bitfield * bf )
{
chan & = 0xffff ;
chan > > = 16 - bf - > length ;
return chan < < bf - > offset ;
}
/* sm501fb_setcolreg
*
* set the colour mapping for modes that support palettised data
*/
static int sm501fb_setcolreg ( unsigned regno ,
unsigned red , unsigned green , unsigned blue ,
unsigned transp , struct fb_info * info )
{
struct sm501fb_par * par = info - > par ;
struct sm501fb_info * fbi = par - > info ;
void __iomem * base = fbi - > regs ;
unsigned int val ;
if ( par - > head = = HEAD_CRT )
base + = SM501_DC_CRT_PALETTE ;
else
base + = SM501_DC_PANEL_PALETTE ;
switch ( info - > fix . visual ) {
case FB_VISUAL_TRUECOLOR :
/* true-colour, use pseuo-palette */
if ( regno < 16 ) {
u32 * pal = par - > pseudo_palette ;
val = chan_to_field ( red , & info - > var . red ) ;
val | = chan_to_field ( green , & info - > var . green ) ;
val | = chan_to_field ( blue , & info - > var . blue ) ;
pal [ regno ] = val ;
}
break ;
case FB_VISUAL_PSEUDOCOLOR :
if ( regno < 256 ) {
val = ( red > > 8 ) < < 16 ;
val | = ( green > > 8 ) < < 8 ;
val | = blue > > 8 ;
2011-01-24 12:57:20 +03:00
smc501_writel ( val , base + ( regno * 4 ) ) ;
2007-02-21 00:58:21 +03:00
}
break ;
default :
return 1 ; /* unknown type */
}
return 0 ;
}
/* sm501fb_blank_pnl
*
* Blank or un - blank the panel interface
*/
static int sm501fb_blank_pnl ( int blank_mode , struct fb_info * info )
{
struct sm501fb_par * par = info - > par ;
struct sm501fb_info * fbi = par - > info ;
dev_dbg ( fbi - > dev , " %s(mode=%d, %p) \n " , __func__ , blank_mode , info ) ;
switch ( blank_mode ) {
case FB_BLANK_POWERDOWN :
sm501fb_panel_power ( fbi , 0 ) ;
break ;
case FB_BLANK_UNBLANK :
sm501fb_panel_power ( fbi , 1 ) ;
break ;
case FB_BLANK_NORMAL :
case FB_BLANK_VSYNC_SUSPEND :
case FB_BLANK_HSYNC_SUSPEND :
default :
return 1 ;
}
return 0 ;
}
/* sm501fb_blank_crt
*
* Blank or un - blank the crt interface
*/
static int sm501fb_blank_crt ( int blank_mode , struct fb_info * info )
{
struct sm501fb_par * par = info - > par ;
struct sm501fb_info * fbi = par - > info ;
unsigned long ctrl ;
dev_dbg ( fbi - > dev , " %s(mode=%d, %p) \n " , __func__ , blank_mode , info ) ;
2011-01-24 12:57:20 +03:00
ctrl = smc501_readl ( fbi - > regs + SM501_DC_CRT_CONTROL ) ;
2007-02-21 00:58:21 +03:00
switch ( blank_mode ) {
case FB_BLANK_POWERDOWN :
ctrl & = ~ SM501_DC_CRT_CONTROL_ENABLE ;
sm501_misc_control ( fbi - > dev - > parent , SM501_MISC_DAC_POWER , 0 ) ;
case FB_BLANK_NORMAL :
ctrl | = SM501_DC_CRT_CONTROL_BLANK ;
break ;
case FB_BLANK_UNBLANK :
ctrl & = ~ SM501_DC_CRT_CONTROL_BLANK ;
ctrl | = SM501_DC_CRT_CONTROL_ENABLE ;
sm501_misc_control ( fbi - > dev - > parent , 0 , SM501_MISC_DAC_POWER ) ;
break ;
case FB_BLANK_VSYNC_SUSPEND :
case FB_BLANK_HSYNC_SUSPEND :
default :
return 1 ;
}
2011-01-24 12:57:20 +03:00
smc501_writel ( ctrl , fbi - > regs + SM501_DC_CRT_CONTROL ) ;
2007-02-21 00:58:21 +03:00
sm501fb_sync_regs ( fbi ) ;
return 0 ;
}
/* sm501fb_cursor
*
* set or change the hardware cursor parameters
*/
2007-03-01 07:11:06 +03:00
static int sm501fb_cursor ( struct fb_info * info , struct fb_cursor * cursor )
2007-02-21 00:58:21 +03:00
{
struct sm501fb_par * par = info - > par ;
struct sm501fb_info * fbi = par - > info ;
void __iomem * base = fbi - > regs ;
unsigned long hwc_addr ;
unsigned long fg , bg ;
dev_dbg ( fbi - > dev , " %s(%p,%p) \n " , __func__ , info , cursor ) ;
if ( par - > head = = HEAD_CRT )
base + = SM501_DC_CRT_HWC_BASE ;
else
base + = SM501_DC_PANEL_HWC_BASE ;
/* check not being asked to exceed capabilities */
if ( cursor - > image . width > 64 )
return - EINVAL ;
if ( cursor - > image . height > 64 )
return - EINVAL ;
if ( cursor - > image . depth > 1 )
return - EINVAL ;
2011-01-24 12:57:20 +03:00
hwc_addr = smc501_readl ( base + SM501_OFF_HWC_ADDR ) ;
2007-02-21 00:58:21 +03:00
if ( cursor - > enable )
2011-01-24 12:57:20 +03:00
smc501_writel ( hwc_addr | SM501_HWC_EN ,
base + SM501_OFF_HWC_ADDR ) ;
2007-02-21 00:58:21 +03:00
else
2011-01-24 12:57:20 +03:00
smc501_writel ( hwc_addr & ~ SM501_HWC_EN ,
base + SM501_OFF_HWC_ADDR ) ;
2007-02-21 00:58:21 +03:00
/* set data */
if ( cursor - > set & FB_CUR_SETPOS ) {
unsigned int x = cursor - > image . dx ;
unsigned int y = cursor - > image . dy ;
if ( x > = 2048 | | y > = 2048 )
return - EINVAL ;
dev_dbg ( fbi - > dev , " set position %d,%d \n " , x , y ) ;
//y += cursor->image.height;
2011-01-24 12:57:20 +03:00
smc501_writel ( x | ( y < < 16 ) , base + SM501_OFF_HWC_LOC ) ;
2007-02-21 00:58:21 +03:00
}
if ( cursor - > set & FB_CUR_SETCMAP ) {
unsigned int bg_col = cursor - > image . bg_color ;
unsigned int fg_col = cursor - > image . fg_color ;
dev_dbg ( fbi - > dev , " %s: update cmap (%08x,%08x) \n " ,
__func__ , bg_col , fg_col ) ;
bg = ( ( info - > cmap . red [ bg_col ] & 0xF8 ) < < 8 ) |
( ( info - > cmap . green [ bg_col ] & 0xFC ) < < 3 ) |
( ( info - > cmap . blue [ bg_col ] & 0xF8 ) > > 3 ) ;
fg = ( ( info - > cmap . red [ fg_col ] & 0xF8 ) < < 8 ) |
( ( info - > cmap . green [ fg_col ] & 0xFC ) < < 3 ) |
( ( info - > cmap . blue [ fg_col ] & 0xF8 ) > > 3 ) ;
2007-05-08 11:40:22 +04:00
dev_dbg ( fbi - > dev , " fgcol %08lx, bgcol %08lx \n " , fg , bg ) ;
2007-02-21 00:58:21 +03:00
2011-01-24 12:57:20 +03:00
smc501_writel ( bg , base + SM501_OFF_HWC_COLOR_1_2 ) ;
smc501_writel ( fg , base + SM501_OFF_HWC_COLOR_3 ) ;
2007-02-21 00:58:21 +03:00
}
if ( cursor - > set & FB_CUR_SETSIZE | |
cursor - > set & ( FB_CUR_SETIMAGE | FB_CUR_SETSHAPE ) ) {
/* SM501 cursor is a two bpp 64x64 bitmap this routine
* clears it to transparent then combines the cursor
* shape plane with the colour plane to set the
* cursor */
int x , y ;
const unsigned char * pcol = cursor - > image . data ;
const unsigned char * pmsk = cursor - > mask ;
void __iomem * dst = par - > cursor . k_addr ;
unsigned char dcol = 0 ;
unsigned char dmsk = 0 ;
unsigned int op ;
dev_dbg ( fbi - > dev , " %s: setting shape (%d,%d) \n " ,
__func__ , cursor - > image . width , cursor - > image . height ) ;
for ( op = 0 ; op < ( 64 * 64 * 2 ) / 8 ; op + = 4 )
2011-01-24 12:57:20 +03:00
smc501_writel ( 0x0 , dst + op ) ;
2007-02-21 00:58:21 +03:00
for ( y = 0 ; y < cursor - > image . height ; y + + ) {
for ( x = 0 ; x < cursor - > image . width ; x + + ) {
if ( ( x % 8 ) = = 0 ) {
dcol = * pcol + + ;
dmsk = * pmsk + + ;
} else {
dcol > > = 1 ;
dmsk > > = 1 ;
}
if ( dmsk & 1 ) {
op = ( dcol & 1 ) ? 1 : 3 ;
op < < = ( ( x % 4 ) * 2 ) ;
op | = readb ( dst + ( x / 4 ) ) ;
writeb ( op , dst + ( x / 4 ) ) ;
}
}
dst + = ( 64 * 2 ) / 8 ;
}
}
sm501fb_sync_regs ( fbi ) ; /* ensure cursor data flushed */
return 0 ;
}
/* sm501fb_crtsrc_show
*
* device attribute code to show where the crt output is sourced from
*/
static ssize_t sm501fb_crtsrc_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct sm501fb_info * info = dev_get_drvdata ( dev ) ;
unsigned long ctrl ;
2011-01-24 12:57:20 +03:00
ctrl = smc501_readl ( info - > regs + SM501_DC_CRT_CONTROL ) ;
2007-02-21 00:58:21 +03:00
ctrl & = SM501_DC_CRT_CONTROL_SEL ;
return snprintf ( buf , PAGE_SIZE , " %s \n " , ctrl ? " crt " : " panel " ) ;
}
/* sm501fb_crtsrc_show
*
* device attribute code to set where the crt output is sourced from
*/
static ssize_t sm501fb_crtsrc_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t len )
{
struct sm501fb_info * info = dev_get_drvdata ( dev ) ;
enum sm501_controller head ;
unsigned long ctrl ;
if ( len < 1 )
return - EINVAL ;
2007-03-05 11:30:31 +03:00
if ( strnicmp ( buf , " crt " , 3 ) = = 0 )
2007-02-21 00:58:21 +03:00
head = HEAD_CRT ;
2007-03-05 11:30:31 +03:00
else if ( strnicmp ( buf , " panel " , 5 ) = = 0 )
2007-02-21 00:58:21 +03:00
head = HEAD_PANEL ;
else
return - EINVAL ;
dev_info ( dev , " setting crt source to head %d \n " , head ) ;
2011-01-24 12:57:20 +03:00
ctrl = smc501_readl ( info - > regs + SM501_DC_CRT_CONTROL ) ;
2007-02-21 00:58:21 +03:00
if ( head = = HEAD_CRT ) {
ctrl | = SM501_DC_CRT_CONTROL_SEL ;
ctrl | = SM501_DC_CRT_CONTROL_ENABLE ;
ctrl | = SM501_DC_CRT_CONTROL_TE ;
} else {
ctrl & = ~ SM501_DC_CRT_CONTROL_SEL ;
ctrl & = ~ SM501_DC_CRT_CONTROL_ENABLE ;
ctrl & = ~ SM501_DC_CRT_CONTROL_TE ;
}
2011-01-24 12:57:20 +03:00
smc501_writel ( ctrl , info - > regs + SM501_DC_CRT_CONTROL ) ;
2007-02-21 00:58:21 +03:00
sm501fb_sync_regs ( info ) ;
2007-03-05 11:30:31 +03:00
return len ;
2007-02-21 00:58:21 +03:00
}
/* Prepare the device_attr for registration with sysfs later */
static DEVICE_ATTR ( crt_src , 0666 , sm501fb_crtsrc_show , sm501fb_crtsrc_store ) ;
/* sm501fb_show_regs
*
* show the primary sm501 registers
*/
static int sm501fb_show_regs ( struct sm501fb_info * info , char * ptr ,
unsigned int start , unsigned int len )
{
void __iomem * mem = info - > regs ;
char * buf = ptr ;
unsigned int reg ;
for ( reg = start ; reg < ( len + start ) ; reg + = 4 )
2011-01-24 12:57:20 +03:00
ptr + = sprintf ( ptr , " %08x = %08x \n " , reg ,
smc501_readl ( mem + reg ) ) ;
2007-02-21 00:58:21 +03:00
return ptr - buf ;
}
/* sm501fb_debug_show_crt
*
* show the crt control and cursor registers
*/
static ssize_t sm501fb_debug_show_crt ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct sm501fb_info * info = dev_get_drvdata ( dev ) ;
char * ptr = buf ;
ptr + = sm501fb_show_regs ( info , ptr , SM501_DC_CRT_CONTROL , 0x40 ) ;
ptr + = sm501fb_show_regs ( info , ptr , SM501_DC_CRT_HWC_BASE , 0x10 ) ;
return ptr - buf ;
}
static DEVICE_ATTR ( fbregs_crt , 0444 , sm501fb_debug_show_crt , NULL ) ;
/* sm501fb_debug_show_pnl
*
* show the panel control and cursor registers
*/
static ssize_t sm501fb_debug_show_pnl ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct sm501fb_info * info = dev_get_drvdata ( dev ) ;
char * ptr = buf ;
ptr + = sm501fb_show_regs ( info , ptr , 0x0 , 0x40 ) ;
ptr + = sm501fb_show_regs ( info , ptr , SM501_DC_PANEL_HWC_BASE , 0x10 ) ;
return ptr - buf ;
}
static DEVICE_ATTR ( fbregs_pnl , 0444 , sm501fb_debug_show_pnl , NULL ) ;
2009-12-16 03:46:35 +03:00
/* acceleration operations */
static int sm501fb_sync ( struct fb_info * info )
{
int count = 1000000 ;
struct sm501fb_par * par = info - > par ;
struct sm501fb_info * fbi = par - > info ;
/* wait for the 2d engine to be ready */
while ( ( count > 0 ) & &
2011-01-24 12:57:20 +03:00
( smc501_readl ( fbi - > regs + SM501_SYSTEM_CONTROL ) &
2009-12-16 03:46:35 +03:00
SM501_SYSCTRL_2D_ENGINE_STATUS ) ! = 0 )
count - - ;
if ( count < = 0 ) {
dev_err ( info - > dev , " Timeout waiting for 2d engine sync \n " ) ;
return 1 ;
}
return 0 ;
}
static void sm501fb_copyarea ( struct fb_info * info , const struct fb_copyarea * area )
{
struct sm501fb_par * par = info - > par ;
struct sm501fb_info * fbi = par - > info ;
int width = area - > width ;
int height = area - > height ;
int sx = area - > sx ;
int sy = area - > sy ;
int dx = area - > dx ;
int dy = area - > dy ;
unsigned long rtl = 0 ;
/* source clip */
if ( ( sx > = info - > var . xres_virtual ) | |
( sy > = info - > var . yres_virtual ) )
/* source Area not within virtual screen, skipping */
return ;
if ( ( sx + width ) > = info - > var . xres_virtual )
width = info - > var . xres_virtual - sx - 1 ;
if ( ( sy + height ) > = info - > var . yres_virtual )
height = info - > var . yres_virtual - sy - 1 ;
/* dest clip */
if ( ( dx > = info - > var . xres_virtual ) | |
( dy > = info - > var . yres_virtual ) )
/* Destination Area not within virtual screen, skipping */
return ;
if ( ( dx + width ) > = info - > var . xres_virtual )
width = info - > var . xres_virtual - dx - 1 ;
if ( ( dy + height ) > = info - > var . yres_virtual )
height = info - > var . yres_virtual - dy - 1 ;
if ( ( sx < dx ) | | ( sy < dy ) ) {
rtl = 1 < < 27 ;
sx + = width - 1 ;
dx + = width - 1 ;
sy + = height - 1 ;
dy + = height - 1 ;
}
if ( sm501fb_sync ( info ) )
return ;
/* set the base addresses */
2011-01-24 12:57:20 +03:00
smc501_writel ( par - > screen . sm_addr , fbi - > regs2d + SM501_2D_SOURCE_BASE ) ;
smc501_writel ( par - > screen . sm_addr ,
fbi - > regs2d + SM501_2D_DESTINATION_BASE ) ;
2009-12-16 03:46:35 +03:00
/* set the window width */
2011-01-24 12:57:20 +03:00
smc501_writel ( ( info - > var . xres < < 16 ) | info - > var . xres ,
2009-12-16 03:46:35 +03:00
fbi - > regs2d + SM501_2D_WINDOW_WIDTH ) ;
/* set window stride */
2011-01-24 12:57:20 +03:00
smc501_writel ( ( info - > var . xres_virtual < < 16 ) | info - > var . xres_virtual ,
2009-12-16 03:46:35 +03:00
fbi - > regs2d + SM501_2D_PITCH ) ;
/* set data format */
switch ( info - > var . bits_per_pixel ) {
case 8 :
2011-01-24 12:57:20 +03:00
smc501_writel ( 0 , fbi - > regs2d + SM501_2D_STRETCH ) ;
2009-12-16 03:46:35 +03:00
break ;
case 16 :
2011-01-24 12:57:20 +03:00
smc501_writel ( 0x00100000 , fbi - > regs2d + SM501_2D_STRETCH ) ;
2009-12-16 03:46:35 +03:00
break ;
case 32 :
2011-01-24 12:57:20 +03:00
smc501_writel ( 0x00200000 , fbi - > regs2d + SM501_2D_STRETCH ) ;
2009-12-16 03:46:35 +03:00
break ;
}
/* 2d compare mask */
2011-01-24 12:57:20 +03:00
smc501_writel ( 0xffffffff , fbi - > regs2d + SM501_2D_COLOR_COMPARE_MASK ) ;
2009-12-16 03:46:35 +03:00
/* 2d mask */
2011-01-24 12:57:20 +03:00
smc501_writel ( 0xffffffff , fbi - > regs2d + SM501_2D_MASK ) ;
2009-12-16 03:46:35 +03:00
/* source and destination x y */
2011-01-24 12:57:20 +03:00
smc501_writel ( ( sx < < 16 ) | sy , fbi - > regs2d + SM501_2D_SOURCE ) ;
smc501_writel ( ( dx < < 16 ) | dy , fbi - > regs2d + SM501_2D_DESTINATION ) ;
2009-12-16 03:46:35 +03:00
/* w/h */
2011-01-24 12:57:20 +03:00
smc501_writel ( ( width < < 16 ) | height , fbi - > regs2d + SM501_2D_DIMENSION ) ;
2009-12-16 03:46:35 +03:00
/* do area move */
2011-01-24 12:57:20 +03:00
smc501_writel ( 0x800000cc | rtl , fbi - > regs2d + SM501_2D_CONTROL ) ;
2009-12-16 03:46:35 +03:00
}
static void sm501fb_fillrect ( struct fb_info * info , const struct fb_fillrect * rect )
{
struct sm501fb_par * par = info - > par ;
struct sm501fb_info * fbi = par - > info ;
int width = rect - > width , height = rect - > height ;
if ( ( rect - > dx > = info - > var . xres_virtual ) | |
( rect - > dy > = info - > var . yres_virtual ) )
/* Rectangle not within virtual screen, skipping */
return ;
if ( ( rect - > dx + width ) > = info - > var . xres_virtual )
width = info - > var . xres_virtual - rect - > dx - 1 ;
if ( ( rect - > dy + height ) > = info - > var . yres_virtual )
height = info - > var . yres_virtual - rect - > dy - 1 ;
if ( sm501fb_sync ( info ) )
return ;
/* set the base addresses */
2011-01-24 12:57:20 +03:00
smc501_writel ( par - > screen . sm_addr , fbi - > regs2d + SM501_2D_SOURCE_BASE ) ;
smc501_writel ( par - > screen . sm_addr ,
fbi - > regs2d + SM501_2D_DESTINATION_BASE ) ;
2009-12-16 03:46:35 +03:00
/* set the window width */
2011-01-24 12:57:20 +03:00
smc501_writel ( ( info - > var . xres < < 16 ) | info - > var . xres ,
2009-12-16 03:46:35 +03:00
fbi - > regs2d + SM501_2D_WINDOW_WIDTH ) ;
/* set window stride */
2011-01-24 12:57:20 +03:00
smc501_writel ( ( info - > var . xres_virtual < < 16 ) | info - > var . xres_virtual ,
2009-12-16 03:46:35 +03:00
fbi - > regs2d + SM501_2D_PITCH ) ;
/* set data format */
switch ( info - > var . bits_per_pixel ) {
case 8 :
2011-01-24 12:57:20 +03:00
smc501_writel ( 0 , fbi - > regs2d + SM501_2D_STRETCH ) ;
2009-12-16 03:46:35 +03:00
break ;
case 16 :
2011-01-24 12:57:20 +03:00
smc501_writel ( 0x00100000 , fbi - > regs2d + SM501_2D_STRETCH ) ;
2009-12-16 03:46:35 +03:00
break ;
case 32 :
2011-01-24 12:57:20 +03:00
smc501_writel ( 0x00200000 , fbi - > regs2d + SM501_2D_STRETCH ) ;
2009-12-16 03:46:35 +03:00
break ;
}
/* 2d compare mask */
2011-01-24 12:57:20 +03:00
smc501_writel ( 0xffffffff , fbi - > regs2d + SM501_2D_COLOR_COMPARE_MASK ) ;
2009-12-16 03:46:35 +03:00
/* 2d mask */
2011-01-24 12:57:20 +03:00
smc501_writel ( 0xffffffff , fbi - > regs2d + SM501_2D_MASK ) ;
2009-12-16 03:46:35 +03:00
/* colour */
2011-01-24 12:57:20 +03:00
smc501_writel ( rect - > color , fbi - > regs2d + SM501_2D_FOREGROUND ) ;
2009-12-16 03:46:35 +03:00
/* x y */
2011-01-24 12:57:20 +03:00
smc501_writel ( ( rect - > dx < < 16 ) | rect - > dy ,
fbi - > regs2d + SM501_2D_DESTINATION ) ;
2009-12-16 03:46:35 +03:00
/* w/h */
2011-01-24 12:57:20 +03:00
smc501_writel ( ( width < < 16 ) | height , fbi - > regs2d + SM501_2D_DIMENSION ) ;
2009-12-16 03:46:35 +03:00
/* do rectangle fill */
2011-01-24 12:57:20 +03:00
smc501_writel ( 0x800100cc , fbi - > regs2d + SM501_2D_CONTROL ) ;
2009-12-16 03:46:35 +03:00
}
2007-02-21 00:58:21 +03:00
static struct fb_ops sm501fb_ops_crt = {
. owner = THIS_MODULE ,
. fb_check_var = sm501fb_check_var_crt ,
. fb_set_par = sm501fb_set_par_crt ,
. fb_blank = sm501fb_blank_crt ,
. fb_setcolreg = sm501fb_setcolreg ,
. fb_pan_display = sm501fb_pan_crt ,
. fb_cursor = sm501fb_cursor ,
2009-12-16 03:46:35 +03:00
. fb_fillrect = sm501fb_fillrect ,
. fb_copyarea = sm501fb_copyarea ,
2007-02-21 00:58:21 +03:00
. fb_imageblit = cfb_imageblit ,
2009-12-16 03:46:35 +03:00
. fb_sync = sm501fb_sync ,
2007-02-21 00:58:21 +03:00
} ;
static struct fb_ops sm501fb_ops_pnl = {
. owner = THIS_MODULE ,
. fb_check_var = sm501fb_check_var_pnl ,
. fb_set_par = sm501fb_set_par_pnl ,
. fb_pan_display = sm501fb_pan_pnl ,
. fb_blank = sm501fb_blank_pnl ,
. fb_setcolreg = sm501fb_setcolreg ,
. fb_cursor = sm501fb_cursor ,
2009-12-16 03:46:35 +03:00
. fb_fillrect = sm501fb_fillrect ,
. fb_copyarea = sm501fb_copyarea ,
2007-02-21 00:58:21 +03:00
. fb_imageblit = cfb_imageblit ,
2009-12-16 03:46:35 +03:00
. fb_sync = sm501fb_sync ,
2007-02-21 00:58:21 +03:00
} ;
/* sm501_init_cursor
*
* initialise hw cursor parameters
*/
2007-03-01 07:11:06 +03:00
static int sm501_init_cursor ( struct fb_info * fbi , unsigned int reg_base )
2007-02-21 00:58:21 +03:00
{
2008-07-24 08:31:36 +04:00
struct sm501fb_par * par ;
struct sm501fb_info * info ;
2007-02-21 00:58:21 +03:00
int ret ;
2008-07-24 08:31:36 +04:00
if ( fbi = = NULL )
return 0 ;
par = fbi - > par ;
info = par - > info ;
2007-02-21 00:58:21 +03:00
par - > cursor_regs = info - > regs + reg_base ;
2009-06-30 22:41:29 +04:00
ret = sm501_alloc_mem ( info , & par - > cursor , SM501_MEMF_CURSOR , 1024 ,
fbi - > fix . smem_len ) ;
2007-02-21 00:58:21 +03:00
if ( ret < 0 )
return ret ;
/* initialise the colour registers */
2011-01-24 12:57:20 +03:00
smc501_writel ( par - > cursor . sm_addr ,
par - > cursor_regs + SM501_OFF_HWC_ADDR ) ;
2007-02-21 00:58:21 +03:00
2011-01-24 12:57:20 +03:00
smc501_writel ( 0x00 , par - > cursor_regs + SM501_OFF_HWC_LOC ) ;
smc501_writel ( 0x00 , par - > cursor_regs + SM501_OFF_HWC_COLOR_1_2 ) ;
smc501_writel ( 0x00 , par - > cursor_regs + SM501_OFF_HWC_COLOR_3 ) ;
2007-02-21 00:58:21 +03:00
sm501fb_sync_regs ( info ) ;
return 0 ;
}
/* sm501fb_info_start
*
* fills the par structure claiming resources and remapping etc .
*/
static int sm501fb_start ( struct sm501fb_info * info ,
struct platform_device * pdev )
{
struct resource * res ;
2008-07-24 08:31:36 +04:00
struct device * dev = & pdev - > dev ;
2008-02-06 12:39:25 +03:00
int k ;
2007-02-21 00:58:21 +03:00
int ret ;
info - > irq = ret = platform_get_irq ( pdev , 0 ) ;
if ( ret < 0 ) {
/* we currently do not use the IRQ */
dev_warn ( dev , " no irq for device \n " ) ;
}
2009-12-16 03:46:35 +03:00
/* allocate, reserve and remap resources for display
* controller registers */
2007-02-21 00:58:21 +03:00
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( res = = NULL ) {
dev_err ( dev , " no resource definition for registers \n " ) ;
ret = - ENOENT ;
goto err_release ;
}
info - > regs_res = request_mem_region ( res - > start ,
2009-12-16 03:46:33 +03:00
resource_size ( res ) ,
2007-02-21 00:58:21 +03:00
pdev - > name ) ;
if ( info - > regs_res = = NULL ) {
dev_err ( dev , " cannot claim registers \n " ) ;
ret = - ENXIO ;
goto err_release ;
}
2009-12-16 03:46:33 +03:00
info - > regs = ioremap ( res - > start , resource_size ( res ) ) ;
2007-02-21 00:58:21 +03:00
if ( info - > regs = = NULL ) {
dev_err ( dev , " cannot remap registers \n " ) ;
ret = - ENXIO ;
goto err_regs_res ;
}
2009-12-16 03:46:35 +03:00
/* allocate, reserve and remap resources for 2d
* controller registers */
res = platform_get_resource ( pdev , IORESOURCE_MEM , 1 ) ;
if ( res = = NULL ) {
dev_err ( dev , " no resource definition for 2d registers \n " ) ;
ret = - ENOENT ;
goto err_regs_map ;
}
info - > regs2d_res = request_mem_region ( res - > start ,
resource_size ( res ) ,
pdev - > name ) ;
if ( info - > regs2d_res = = NULL ) {
dev_err ( dev , " cannot claim registers \n " ) ;
ret = - ENXIO ;
goto err_regs_map ;
}
info - > regs2d = ioremap ( res - > start , resource_size ( res ) ) ;
if ( info - > regs2d = = NULL ) {
dev_err ( dev , " cannot remap registers \n " ) ;
ret = - ENXIO ;
goto err_regs2d_res ;
}
2007-02-21 00:58:21 +03:00
/* allocate, reserve resources for framebuffer */
res = platform_get_resource ( pdev , IORESOURCE_MEM , 2 ) ;
if ( res = = NULL ) {
dev_err ( dev , " no memory resource defined \n " ) ;
ret = - ENXIO ;
2009-12-16 03:46:35 +03:00
goto err_regs2d_map ;
2007-02-21 00:58:21 +03:00
}
info - > fbmem_res = request_mem_region ( res - > start ,
2009-12-16 03:46:33 +03:00
resource_size ( res ) ,
2007-02-21 00:58:21 +03:00
pdev - > name ) ;
if ( info - > fbmem_res = = NULL ) {
dev_err ( dev , " cannot claim framebuffer \n " ) ;
ret = - ENXIO ;
2009-12-16 03:46:35 +03:00
goto err_regs2d_map ;
2007-02-21 00:58:21 +03:00
}
2009-12-16 03:46:33 +03:00
info - > fbmem = ioremap ( res - > start , resource_size ( res ) ) ;
2007-02-21 00:58:21 +03:00
if ( info - > fbmem = = NULL ) {
dev_err ( dev , " cannot remap framebuffer \n " ) ;
goto err_mem_res ;
}
2009-12-16 03:46:33 +03:00
info - > fbmem_len = resource_size ( res ) ;
2007-02-21 00:58:21 +03:00
2008-02-06 12:39:25 +03:00
/* clear framebuffer memory - avoids garbage data on unused fb */
memset ( info - > fbmem , 0 , info - > fbmem_len ) ;
/* clear palette ram - undefined at power on */
for ( k = 0 ; k < ( 256 * 3 ) ; k + + )
2011-01-24 12:57:20 +03:00
smc501_writel ( 0 , info - > regs + SM501_DC_PANEL_PALETTE + ( k * 4 ) ) ;
2008-02-06 12:39:25 +03:00
2007-02-21 00:58:21 +03:00
/* enable display controller */
sm501_unit_power ( dev - > parent , SM501_GATE_DISPLAY , 1 ) ;
2009-12-16 03:46:35 +03:00
/* enable 2d controller */
sm501_unit_power ( dev - > parent , SM501_GATE_2D_ENGINE , 1 ) ;
2007-02-21 00:58:21 +03:00
2009-12-16 03:46:35 +03:00
/* setup cursors */
2007-02-21 00:58:21 +03:00
sm501_init_cursor ( info - > fb [ HEAD_CRT ] , SM501_DC_CRT_HWC_ADDR ) ;
sm501_init_cursor ( info - > fb [ HEAD_PANEL ] , SM501_DC_PANEL_HWC_ADDR ) ;
return 0 ; /* everything is setup */
err_mem_res :
2011-04-23 00:11:22 +04:00
release_mem_region ( info - > fbmem_res - > start ,
resource_size ( info - > fbmem_res ) ) ;
2007-02-21 00:58:21 +03:00
2009-12-16 03:46:35 +03:00
err_regs2d_map :
iounmap ( info - > regs2d ) ;
err_regs2d_res :
2011-04-23 00:11:22 +04:00
release_mem_region ( info - > regs2d_res - > start ,
resource_size ( info - > regs2d_res ) ) ;
2009-12-16 03:46:35 +03:00
2007-02-21 00:58:21 +03:00
err_regs_map :
iounmap ( info - > regs ) ;
err_regs_res :
2011-04-23 00:11:22 +04:00
release_mem_region ( info - > regs_res - > start ,
resource_size ( info - > regs_res ) ) ;
2007-02-21 00:58:21 +03:00
err_release :
return ret ;
}
static void sm501fb_stop ( struct sm501fb_info * info )
{
/* disable display controller */
sm501_unit_power ( info - > dev - > parent , SM501_GATE_DISPLAY , 0 ) ;
iounmap ( info - > fbmem ) ;
2011-04-23 00:11:22 +04:00
release_mem_region ( info - > fbmem_res - > start ,
resource_size ( info - > fbmem_res ) ) ;
2007-02-21 00:58:21 +03:00
2009-12-16 03:46:35 +03:00
iounmap ( info - > regs2d ) ;
2011-04-23 00:11:22 +04:00
release_mem_region ( info - > regs2d_res - > start ,
resource_size ( info - > regs2d_res ) ) ;
2009-12-16 03:46:35 +03:00
2007-02-21 00:58:21 +03:00
iounmap ( info - > regs ) ;
2011-04-23 00:11:22 +04:00
release_mem_region ( info - > regs_res - > start ,
resource_size ( info - > regs_res ) ) ;
2007-02-21 00:58:21 +03:00
}
2012-12-22 01:07:39 +04:00
static int sm501fb_init_fb ( struct fb_info * fb , enum sm501_controller head ,
2007-02-21 00:58:21 +03:00
const char * fbname )
{
struct sm501_platdata_fbsub * pd ;
struct sm501fb_par * par = fb - > par ;
struct sm501fb_info * info = par - > info ;
unsigned long ctrl ;
unsigned int enable ;
int ret ;
switch ( head ) {
case HEAD_CRT :
pd = info - > pdata - > fb_crt ;
2011-01-24 12:57:20 +03:00
ctrl = smc501_readl ( info - > regs + SM501_DC_CRT_CONTROL ) ;
2007-02-21 00:58:21 +03:00
enable = ( ctrl & SM501_DC_CRT_CONTROL_ENABLE ) ? 1 : 0 ;
/* ensure we set the correct source register */
if ( info - > pdata - > fb_route ! = SM501_FB_CRT_PANEL ) {
ctrl | = SM501_DC_CRT_CONTROL_SEL ;
2011-01-24 12:57:20 +03:00
smc501_writel ( ctrl , info - > regs + SM501_DC_CRT_CONTROL ) ;
2007-02-21 00:58:21 +03:00
}
break ;
case HEAD_PANEL :
pd = info - > pdata - > fb_pnl ;
2011-01-24 12:57:20 +03:00
ctrl = smc501_readl ( info - > regs + SM501_DC_PANEL_CONTROL ) ;
2007-02-21 00:58:21 +03:00
enable = ( ctrl & SM501_DC_PANEL_CONTROL_EN ) ? 1 : 0 ;
break ;
default :
pd = NULL ; /* stop compiler warnings */
ctrl = 0 ;
enable = 0 ;
BUG ( ) ;
}
dev_info ( info - > dev , " fb %s %sabled at start \n " ,
fbname , enable ? " en " : " dis " ) ;
/* check to see if our routing allows this */
if ( head = = HEAD_CRT & & info - > pdata - > fb_route = = SM501_FB_CRT_PANEL ) {
ctrl & = ~ SM501_DC_CRT_CONTROL_SEL ;
2011-01-24 12:57:20 +03:00
smc501_writel ( ctrl , info - > regs + SM501_DC_CRT_CONTROL ) ;
2007-02-21 00:58:21 +03:00
enable = 0 ;
}
strlcpy ( fb - > fix . id , fbname , sizeof ( fb - > fix . id ) ) ;
memcpy ( & par - > ops ,
( head = = HEAD_CRT ) ? & sm501fb_ops_crt : & sm501fb_ops_pnl ,
sizeof ( struct fb_ops ) ) ;
2011-03-31 05:57:33 +04:00
/* update ops dependent on what we've been passed */
2007-02-21 00:58:21 +03:00
if ( ( pd - > flags & SM501FB_FLAG_USE_HWCURSOR ) = = 0 )
par - > ops . fb_cursor = NULL ;
fb - > fbops = & par - > ops ;
2009-12-16 03:46:35 +03:00
fb - > flags = FBINFO_FLAG_DEFAULT | FBINFO_READS_FAST |
FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT |
2007-02-21 00:58:21 +03:00
FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN ;
2011-01-26 10:21:30 +03:00
# if defined(CONFIG_OF)
# ifdef __BIG_ENDIAN
if ( of_get_property ( info - > dev - > parent - > of_node , " little-endian " , NULL ) )
fb - > flags | = FBINFO_FOREIGN_ENDIAN ;
# else
if ( of_get_property ( info - > dev - > parent - > of_node , " big-endian " , NULL ) )
fb - > flags | = FBINFO_FOREIGN_ENDIAN ;
# endif
# endif
2007-02-21 00:58:21 +03:00
/* fixed data */
fb - > fix . type = FB_TYPE_PACKED_PIXELS ;
fb - > fix . type_aux = 0 ;
fb - > fix . xpanstep = 1 ;
fb - > fix . ypanstep = 1 ;
fb - > fix . ywrapstep = 0 ;
fb - > fix . accel = FB_ACCEL_NONE ;
/* screenmode */
fb - > var . nonstd = 0 ;
fb - > var . activate = FB_ACTIVATE_NOW ;
fb - > var . accel_flags = 0 ;
fb - > var . vmode = FB_VMODE_NONINTERLACED ;
fb - > var . bits_per_pixel = 16 ;
2011-01-26 10:21:22 +03:00
if ( info - > edid_data ) {
/* Now build modedb from EDID */
fb_edid_to_monspecs ( info - > edid_data , & fb - > monspecs ) ;
fb_videomode_to_modelist ( fb - > monspecs . modedb ,
fb - > monspecs . modedb_len ,
& fb - > modelist ) ;
}
2007-02-21 00:58:21 +03:00
if ( enable & & ( pd - > flags & SM501FB_FLAG_USE_INIT_MODE ) & & 0 ) {
/* TODO read the mode from the current display */
} else {
if ( pd - > def_mode ) {
dev_info ( info - > dev , " using supplied mode \n " ) ;
fb_videomode_to_var ( & fb - > var , pd - > def_mode ) ;
fb - > var . bits_per_pixel = pd - > def_bpp ? pd - > def_bpp : 8 ;
fb - > var . xres_virtual = fb - > var . xres ;
fb - > var . yres_virtual = fb - > var . yres ;
} else {
2011-01-26 10:21:30 +03:00
if ( info - > edid_data ) {
2011-01-26 10:21:22 +03:00
ret = fb_find_mode ( & fb - > var , fb , fb_mode ,
fb - > monspecs . modedb ,
fb - > monspecs . modedb_len ,
& sm501_default_mode , default_bpp ) ;
2011-01-26 10:21:30 +03:00
/* edid_data is no longer needed, free it */
kfree ( info - > edid_data ) ;
} else {
2011-01-26 10:21:22 +03:00
ret = fb_find_mode ( & fb - > var , fb ,
2007-02-21 00:58:21 +03:00
NULL , NULL , 0 , NULL , 8 ) ;
2011-01-26 10:21:30 +03:00
}
2007-02-21 00:58:21 +03:00
2011-01-26 10:21:22 +03:00
switch ( ret ) {
case 1 :
dev_info ( info - > dev , " using mode specified in "
" @mode \n " ) ;
break ;
case 2 :
dev_info ( info - > dev , " using mode specified in "
" @mode with ignored refresh rate \n " ) ;
break ;
case 3 :
dev_info ( info - > dev , " using mode default "
" mode \n " ) ;
break ;
case 4 :
dev_info ( info - > dev , " using mode from list \n " ) ;
break ;
default :
dev_info ( info - > dev , " ret = %d \n " , ret ) ;
dev_info ( info - > dev , " failed to find mode \n " ) ;
2007-02-21 00:58:21 +03:00
return - EINVAL ;
}
}
}
/* initialise and set the palette */
2009-04-01 02:25:24 +04:00
if ( fb_alloc_cmap ( & fb - > cmap , NR_PALETTE , 0 ) ) {
dev_err ( info - > dev , " failed to allocate cmap memory \n " ) ;
return - ENOMEM ;
}
2007-02-21 00:58:21 +03:00
fb_set_cmap ( & fb - > cmap , fb ) ;
ret = ( fb - > fbops - > fb_check_var ) ( & fb - > var , fb ) ;
if ( ret )
dev_err ( info - > dev , " check_var() failed on initial setup? \n " ) ;
return 0 ;
}
/* default platform data if none is supplied (ie, PCI device) */
static struct sm501_platdata_fbsub sm501fb_pdata_crt = {
. flags = ( SM501FB_FLAG_USE_INIT_MODE |
SM501FB_FLAG_USE_HWCURSOR |
SM501FB_FLAG_USE_HWACCEL |
SM501FB_FLAG_DISABLE_AT_EXIT ) ,
} ;
static struct sm501_platdata_fbsub sm501fb_pdata_pnl = {
. flags = ( SM501FB_FLAG_USE_INIT_MODE |
SM501FB_FLAG_USE_HWCURSOR |
SM501FB_FLAG_USE_HWACCEL |
SM501FB_FLAG_DISABLE_AT_EXIT ) ,
} ;
static struct sm501_platdata_fb sm501fb_def_pdata = {
. fb_route = SM501_FB_OWN ,
. fb_crt = & sm501fb_pdata_crt ,
. fb_pnl = & sm501fb_pdata_pnl ,
} ;
static char driver_name_crt [ ] = " sm501fb-crt " ;
static char driver_name_pnl [ ] = " sm501fb-panel " ;
2012-12-22 01:07:39 +04:00
static int sm501fb_probe_one ( struct sm501fb_info * info ,
enum sm501_controller head )
2007-02-21 00:58:21 +03:00
{
2008-07-24 08:31:36 +04:00
unsigned char * name = ( head = = HEAD_CRT ) ? " crt " : " panel " ;
struct sm501_platdata_fbsub * pd ;
struct sm501fb_par * par ;
struct fb_info * fbi ;
2007-02-21 00:58:21 +03:00
2008-07-24 08:31:36 +04:00
pd = ( head = = HEAD_CRT ) ? info - > pdata - > fb_crt : info - > pdata - > fb_pnl ;
2007-02-21 00:58:21 +03:00
2008-07-24 08:31:36 +04:00
/* Do not initialise if we've not been given any platform data */
if ( pd = = NULL ) {
dev_info ( info - > dev , " no data for fb %s (disabled) \n " , name ) ;
return 0 ;
}
fbi = framebuffer_alloc ( sizeof ( struct sm501fb_par ) , info - > dev ) ;
if ( fbi = = NULL ) {
dev_err ( info - > dev , " cannot allocate %s framebuffer \n " , name ) ;
2007-02-21 00:58:21 +03:00
return - ENOMEM ;
}
2008-07-24 08:31:36 +04:00
par = fbi - > par ;
par - > info = info ;
par - > head = head ;
fbi - > pseudo_palette = & par - > pseudo_palette ;
info - > fb [ head ] = fbi ;
return 0 ;
}
/* Free up anything allocated by sm501fb_init_fb */
static void sm501_free_init_fb ( struct sm501fb_info * info ,
enum sm501_controller head )
{
struct fb_info * fbi = info - > fb [ head ] ;
fb_dealloc_cmap ( & fbi - > cmap ) ;
}
2012-12-22 01:07:39 +04:00
static int sm501fb_start_one ( struct sm501fb_info * info ,
enum sm501_controller head , const char * drvname )
2008-07-24 08:31:36 +04:00
{
struct fb_info * fbi = info - > fb [ head ] ;
int ret ;
if ( ! fbi )
return 0 ;
2009-07-08 20:20:11 +04:00
mutex_init ( & info - > fb [ head ] - > mm_lock ) ;
2008-07-24 08:31:36 +04:00
ret = sm501fb_init_fb ( info - > fb [ head ] , head , drvname ) ;
if ( ret ) {
dev_err ( info - > dev , " cannot initialise fb %s \n " , drvname ) ;
return ret ;
}
ret = register_framebuffer ( info - > fb [ head ] ) ;
if ( ret ) {
dev_err ( info - > dev , " failed to register fb %s \n " , drvname ) ;
sm501_free_init_fb ( info , head ) ;
return ret ;
2007-02-21 00:58:21 +03:00
}
2008-07-24 08:31:36 +04:00
dev_info ( info - > dev , " fb%d: %s frame buffer \n " , fbi - > node , fbi - > fix . id ) ;
return 0 ;
}
2012-12-22 01:07:39 +04:00
static int sm501fb_probe ( struct platform_device * pdev )
2008-07-24 08:31:36 +04:00
{
struct sm501fb_info * info ;
struct device * dev = & pdev - > dev ;
int ret ;
/* allocate our framebuffers */
info = kzalloc ( sizeof ( struct sm501fb_info ) , GFP_KERNEL ) ;
if ( ! info ) {
dev_err ( dev , " failed to allocate state \n " ) ;
return - ENOMEM ;
2007-02-21 00:58:21 +03:00
}
2008-07-24 08:31:36 +04:00
info - > dev = dev = & pdev - > dev ;
platform_set_drvdata ( pdev , info ) ;
2007-02-21 00:58:21 +03:00
if ( dev - > parent - > platform_data ) {
struct sm501_platdata * pd = dev - > parent - > platform_data ;
info - > pdata = pd - > fb ;
}
if ( info - > pdata = = NULL ) {
2011-01-26 10:21:30 +03:00
int found = 0 ;
# if defined(CONFIG_OF)
struct device_node * np = pdev - > dev . parent - > of_node ;
const u8 * prop ;
const char * cp ;
int len ;
2007-02-21 00:58:21 +03:00
info - > pdata = & sm501fb_def_pdata ;
2011-01-26 10:21:30 +03:00
if ( np ) {
/* Get EDID */
cp = of_get_property ( np , " mode " , & len ) ;
if ( cp )
strcpy ( fb_mode , cp ) ;
prop = of_get_property ( np , " edid " , & len ) ;
if ( prop & & len = = EDID_LENGTH ) {
info - > edid_data = kmemdup ( prop , EDID_LENGTH ,
GFP_KERNEL ) ;
if ( info - > edid_data )
found = 1 ;
}
}
# endif
if ( ! found ) {
dev_info ( dev , " using default configuration data \n " ) ;
info - > pdata = & sm501fb_def_pdata ;
}
2007-02-21 00:58:21 +03:00
}
2008-07-24 08:31:36 +04:00
/* probe for the presence of each panel */
2007-02-21 00:58:21 +03:00
2008-07-24 08:31:36 +04:00
ret = sm501fb_probe_one ( info , HEAD_CRT ) ;
if ( ret < 0 ) {
dev_err ( dev , " failed to probe CRT \n " ) ;
goto err_alloc ;
2007-02-21 00:58:21 +03:00
}
2008-07-24 08:31:36 +04:00
ret = sm501fb_probe_one ( info , HEAD_PANEL ) ;
if ( ret < 0 ) {
dev_err ( dev , " failed to probe PANEL \n " ) ;
goto err_probed_crt ;
}
2007-02-21 00:58:21 +03:00
2008-07-24 08:31:36 +04:00
if ( info - > fb [ HEAD_PANEL ] = = NULL & &
info - > fb [ HEAD_CRT ] = = NULL ) {
dev_err ( dev , " no framebuffers found \n " ) ;
goto err_alloc ;
2007-02-21 00:58:21 +03:00
}
2008-07-24 08:31:36 +04:00
/* get the resources for both of the framebuffers */
2007-02-21 00:58:21 +03:00
2008-07-24 08:31:36 +04:00
ret = sm501fb_start ( info , pdev ) ;
2007-02-21 00:58:21 +03:00
if ( ret ) {
2008-07-24 08:31:36 +04:00
dev_err ( dev , " cannot initialise SM501 \n " ) ;
goto err_probed_panel ;
2007-02-21 00:58:21 +03:00
}
2008-07-24 08:31:36 +04:00
ret = sm501fb_start_one ( info , HEAD_CRT , driver_name_crt ) ;
if ( ret ) {
dev_err ( dev , " failed to start CRT \n " ) ;
goto err_started ;
2007-02-21 00:58:21 +03:00
}
2008-07-24 08:31:36 +04:00
ret = sm501fb_start_one ( info , HEAD_PANEL , driver_name_pnl ) ;
if ( ret ) {
dev_err ( dev , " failed to start Panel \n " ) ;
goto err_started_crt ;
2007-02-21 00:58:21 +03:00
}
/* create device files */
ret = device_create_file ( dev , & dev_attr_crt_src ) ;
if ( ret )
2008-07-24 08:31:36 +04:00
goto err_started_panel ;
2007-02-21 00:58:21 +03:00
ret = device_create_file ( dev , & dev_attr_fbregs_pnl ) ;
if ( ret )
2008-07-24 08:31:36 +04:00
goto err_attached_crtsrc_file ;
2007-02-21 00:58:21 +03:00
ret = device_create_file ( dev , & dev_attr_fbregs_crt ) ;
if ( ret )
2008-07-24 08:31:36 +04:00
goto err_attached_pnlregs_file ;
2007-02-21 00:58:21 +03:00
/* we registered, return ok */
return 0 ;
2008-07-24 08:31:36 +04:00
err_attached_pnlregs_file :
2007-02-21 00:58:21 +03:00
device_remove_file ( dev , & dev_attr_fbregs_pnl ) ;
2008-07-24 08:31:36 +04:00
err_attached_crtsrc_file :
2007-02-21 00:58:21 +03:00
device_remove_file ( dev , & dev_attr_crt_src ) ;
2008-07-24 08:31:36 +04:00
err_started_panel :
unregister_framebuffer ( info - > fb [ HEAD_PANEL ] ) ;
sm501_free_init_fb ( info , HEAD_PANEL ) ;
2007-02-21 00:58:21 +03:00
2008-07-24 08:31:36 +04:00
err_started_crt :
unregister_framebuffer ( info - > fb [ HEAD_CRT ] ) ;
sm501_free_init_fb ( info , HEAD_CRT ) ;
2007-02-21 00:58:21 +03:00
2008-07-24 08:31:36 +04:00
err_started :
2007-02-21 00:58:21 +03:00
sm501fb_stop ( info ) ;
2008-07-24 08:31:36 +04:00
err_probed_panel :
framebuffer_release ( info - > fb [ HEAD_PANEL ] ) ;
2007-02-21 00:58:21 +03:00
2008-07-24 08:31:36 +04:00
err_probed_crt :
framebuffer_release ( info - > fb [ HEAD_CRT ] ) ;
2007-02-21 00:58:21 +03:00
2008-07-24 08:31:36 +04:00
err_alloc :
kfree ( info ) ;
2007-02-21 00:58:21 +03:00
return ret ;
}
/*
* Cleanup
*/
static int sm501fb_remove ( struct platform_device * pdev )
{
struct sm501fb_info * info = platform_get_drvdata ( pdev ) ;
struct fb_info * fbinfo_crt = info - > fb [ 0 ] ;
struct fb_info * fbinfo_pnl = info - > fb [ 1 ] ;
device_remove_file ( & pdev - > dev , & dev_attr_fbregs_crt ) ;
device_remove_file ( & pdev - > dev , & dev_attr_fbregs_pnl ) ;
device_remove_file ( & pdev - > dev , & dev_attr_crt_src ) ;
2008-07-24 08:31:36 +04:00
sm501_free_init_fb ( info , HEAD_CRT ) ;
sm501_free_init_fb ( info , HEAD_PANEL ) ;
2007-02-21 00:58:21 +03:00
unregister_framebuffer ( fbinfo_crt ) ;
unregister_framebuffer ( fbinfo_pnl ) ;
sm501fb_stop ( info ) ;
2008-07-24 08:31:36 +04:00
kfree ( info ) ;
2007-02-21 00:58:21 +03:00
framebuffer_release ( fbinfo_pnl ) ;
framebuffer_release ( fbinfo_crt ) ;
return 0 ;
}
# ifdef CONFIG_PM
static int sm501fb_suspend_fb ( struct sm501fb_info * info ,
enum sm501_controller head )
{
struct fb_info * fbi = info - > fb [ head ] ;
struct sm501fb_par * par = fbi - > par ;
if ( par - > screen . size = = 0 )
return 0 ;
2008-02-06 12:39:38 +03:00
/* blank the relevant interface to ensure unit power minimised */
( par - > ops . fb_blank ) ( FB_BLANK_POWERDOWN , fbi ) ;
/* tell console/fb driver we are suspending */
2011-01-26 02:07:35 +03:00
console_lock ( ) ;
2008-02-06 12:39:38 +03:00
fb_set_suspend ( fbi , 1 ) ;
2011-01-26 02:07:35 +03:00
console_unlock ( ) ;
2008-02-06 12:39:38 +03:00
2007-02-21 00:58:21 +03:00
/* backup copies in case chip is powered down over suspend */
par - > store_fb = vmalloc ( par - > screen . size ) ;
if ( par - > store_fb = = NULL ) {
dev_err ( info - > dev , " no memory to store screen \n " ) ;
return - ENOMEM ;
}
par - > store_cursor = vmalloc ( par - > cursor . size ) ;
if ( par - > store_cursor = = NULL ) {
dev_err ( info - > dev , " no memory to store cursor \n " ) ;
goto err_nocursor ;
}
2007-10-16 12:28:37 +04:00
dev_dbg ( info - > dev , " suspending screen to %p \n " , par - > store_fb ) ;
dev_dbg ( info - > dev , " suspending cursor to %p \n " , par - > store_cursor ) ;
2007-02-21 00:58:21 +03:00
memcpy_fromio ( par - > store_fb , par - > screen . k_addr , par - > screen . size ) ;
memcpy_fromio ( par - > store_cursor , par - > cursor . k_addr , par - > cursor . size ) ;
2007-10-16 12:28:38 +04:00
2007-02-21 00:58:21 +03:00
return 0 ;
err_nocursor :
vfree ( par - > store_fb ) ;
2007-10-16 12:28:37 +04:00
par - > store_fb = NULL ;
2007-02-21 00:58:21 +03:00
return - ENOMEM ;
}
static void sm501fb_resume_fb ( struct sm501fb_info * info ,
enum sm501_controller head )
{
struct fb_info * fbi = info - > fb [ head ] ;
struct sm501fb_par * par = fbi - > par ;
if ( par - > screen . size = = 0 )
return ;
/* re-activate the configuration */
( par - > ops . fb_set_par ) ( fbi ) ;
/* restore the data */
2007-10-16 12:28:37 +04:00
dev_dbg ( info - > dev , " restoring screen from %p \n " , par - > store_fb ) ;
dev_dbg ( info - > dev , " restoring cursor from %p \n " , par - > store_cursor ) ;
if ( par - > store_fb )
memcpy_toio ( par - > screen . k_addr , par - > store_fb ,
par - > screen . size ) ;
if ( par - > store_cursor )
memcpy_toio ( par - > cursor . k_addr , par - > store_cursor ,
par - > cursor . size ) ;
2007-02-21 00:58:21 +03:00
2011-01-26 02:07:35 +03:00
console_lock ( ) ;
2007-10-16 12:28:38 +04:00
fb_set_suspend ( fbi , 0 ) ;
2011-01-26 02:07:35 +03:00
console_unlock ( ) ;
2007-10-16 12:28:38 +04:00
2007-02-21 00:58:21 +03:00
vfree ( par - > store_fb ) ;
vfree ( par - > store_cursor ) ;
}
/* suspend and resume support */
static int sm501fb_suspend ( struct platform_device * pdev , pm_message_t state )
{
struct sm501fb_info * info = platform_get_drvdata ( pdev ) ;
2007-10-16 12:28:37 +04:00
/* store crt control to resume with */
2011-01-24 12:57:20 +03:00
info - > pm_crt_ctrl = smc501_readl ( info - > regs + SM501_DC_CRT_CONTROL ) ;
2007-10-16 12:28:37 +04:00
2007-02-21 00:58:21 +03:00
sm501fb_suspend_fb ( info , HEAD_CRT ) ;
sm501fb_suspend_fb ( info , HEAD_PANEL ) ;
/* turn off the clocks, in case the device is not powered down */
sm501_unit_power ( info - > dev - > parent , SM501_GATE_DISPLAY , 0 ) ;
return 0 ;
}
2007-10-16 12:28:37 +04:00
# define SM501_CRT_CTRL_SAVE (SM501_DC_CRT_CONTROL_TVP | \
SM501_DC_CRT_CONTROL_SEL )
2007-02-21 00:58:21 +03:00
static int sm501fb_resume ( struct platform_device * pdev )
{
struct sm501fb_info * info = platform_get_drvdata ( pdev ) ;
2007-10-16 12:28:37 +04:00
unsigned long crt_ctrl ;
2007-02-21 00:58:21 +03:00
sm501_unit_power ( info - > dev - > parent , SM501_GATE_DISPLAY , 1 ) ;
2007-10-16 12:28:37 +04:00
/* restore the items we want to be saved for crt control */
2011-01-24 12:57:20 +03:00
crt_ctrl = smc501_readl ( info - > regs + SM501_DC_CRT_CONTROL ) ;
2007-10-16 12:28:37 +04:00
crt_ctrl & = ~ SM501_CRT_CTRL_SAVE ;
crt_ctrl | = info - > pm_crt_ctrl & SM501_CRT_CTRL_SAVE ;
2011-01-24 12:57:20 +03:00
smc501_writel ( crt_ctrl , info - > regs + SM501_DC_CRT_CONTROL ) ;
2007-10-16 12:28:37 +04:00
2007-02-21 00:58:21 +03:00
sm501fb_resume_fb ( info , HEAD_CRT ) ;
sm501fb_resume_fb ( info , HEAD_PANEL ) ;
return 0 ;
}
# else
# define sm501fb_suspend NULL
# define sm501fb_resume NULL
# endif
static struct platform_driver sm501fb_driver = {
. probe = sm501fb_probe ,
. remove = sm501fb_remove ,
. suspend = sm501fb_suspend ,
. resume = sm501fb_resume ,
. driver = {
. name = " sm501-fb " ,
. owner = THIS_MODULE ,
} ,
} ;
2011-11-26 06:25:54 +04:00
module_platform_driver ( sm501fb_driver ) ;
2007-02-21 00:58:21 +03:00
2011-01-26 10:21:22 +03:00
module_param_named ( mode , fb_mode , charp , 0 ) ;
MODULE_PARM_DESC ( mode ,
" Specify resolution as \" <xres>x<yres>[-<bpp>][@<refresh>] \" " ) ;
module_param_named ( bpp , default_bpp , ulong , 0 ) ;
MODULE_PARM_DESC ( bpp , " Specify bit-per-pixel if not specified mode " ) ;
2007-02-21 00:58:21 +03:00
MODULE_AUTHOR ( " Ben Dooks, Vincent Sanders " ) ;
MODULE_DESCRIPTION ( " SM501 Framebuffer driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;