2005-05-01 19:59:24 +04:00
/*
* Freescale i . MX Frame Buffer device driver
*
* Copyright ( C ) 2004 Sascha Hauer , Pengutronix
* Based on acornfb . c Copyright ( C ) Russell King .
*
* 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 .
*
* Please direct your questions and comments on this driver to the following
* email address :
*
* linux - arm - kernel @ lists . arm . linux . org . uk
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/string.h>
# include <linux/interrupt.h>
# include <linux/slab.h>
2008-07-24 08:28:13 +04:00
# include <linux/mm.h>
2005-05-01 19:59:24 +04:00
# include <linux/fb.h>
# include <linux/delay.h>
# include <linux/init.h>
# include <linux/ioport.h>
# include <linux/cpufreq.h>
2009-01-15 17:21:00 +03:00
# include <linux/clk.h>
2005-10-29 22:07:23 +04:00
# include <linux/platform_device.h>
2005-05-01 19:59:24 +04:00
# include <linux/dma-mapping.h>
2008-12-16 13:44:07 +03:00
# include <linux/io.h>
2009-01-15 17:21:00 +03:00
# include <linux/math64.h>
2005-05-01 19:59:24 +04:00
2008-08-05 19:14:15 +04:00
# include <mach/imxfb.h>
2009-03-18 13:29:31 +03:00
# include <mach/hardware.h>
2005-05-01 19:59:24 +04:00
/*
* Complain if VAR is out of range .
*/
# define DEBUG_VAR 1
2010-08-02 11:39:55 +04:00
# if defined(CONFIG_BACKLIGHT_CLASS_DEVICE) || \
( defined ( CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE ) & & \
defined ( CONFIG_FB_IMX_MODULE ) )
# define PWMR_BACKLIGHT_AVAILABLE
# endif
2008-12-16 13:44:07 +03:00
# define DRIVER_NAME "imx-fb"
# define LCDC_SSA 0x00
# define LCDC_SIZE 0x04
# define SIZE_XMAX(x) ((((x) >> 4) & 0x3f) << 20)
2009-01-26 19:29:10 +03:00
2010-12-24 15:28:24 +03:00
# define YMAX_MASK (cpu_is_mx1() ? 0x1ff : 0x3ff)
# define SIZE_YMAX(y) ((y) & YMAX_MASK)
2008-12-16 13:44:07 +03:00
# define LCDC_VPW 0x08
# define VPW_VPW(x) ((x) & 0x3ff)
# define LCDC_CPOS 0x0C
# define CPOS_CC1 (1<<31)
# define CPOS_CC0 (1<<30)
# define CPOS_OP (1<<28)
# define CPOS_CXP(x) (((x) & 3ff) << 16)
2009-01-26 19:29:10 +03:00
# ifdef CONFIG_ARCH_MX1
2008-12-16 13:44:07 +03:00
# define CPOS_CYP(y) ((y) & 0x1ff)
2009-01-26 19:29:10 +03:00
# else
# define CPOS_CYP(y) ((y) & 0x3ff)
# endif
2008-12-16 13:44:07 +03:00
# define LCDC_LCWHB 0x10
# define LCWHB_BK_EN (1<<31)
# define LCWHB_CW(w) (((w) & 0x1f) << 24)
# define LCWHB_CH(h) (((h) & 0x1f) << 16)
# define LCWHB_BD(x) ((x) & 0xff)
# define LCDC_LCHCC 0x14
2009-01-26 19:29:10 +03:00
# ifdef CONFIG_ARCH_MX1
2008-12-16 13:44:07 +03:00
# define LCHCC_CUR_COL_R(r) (((r) & 0x1f) << 11)
# define LCHCC_CUR_COL_G(g) (((g) & 0x3f) << 5)
# define LCHCC_CUR_COL_B(b) ((b) & 0x1f)
2009-01-26 19:29:10 +03:00
# else
# define LCHCC_CUR_COL_R(r) (((r) & 0x3f) << 12)
# define LCHCC_CUR_COL_G(g) (((g) & 0x3f) << 6)
# define LCHCC_CUR_COL_B(b) ((b) & 0x3f)
# endif
2008-12-16 13:44:07 +03:00
# define LCDC_PCR 0x18
# define LCDC_HCR 0x1C
# define HCR_H_WIDTH(x) (((x) & 0x3f) << 26)
# define HCR_H_WAIT_1(x) (((x) & 0xff) << 8)
# define HCR_H_WAIT_2(x) ((x) & 0xff)
# define LCDC_VCR 0x20
# define VCR_V_WIDTH(x) (((x) & 0x3f) << 26)
# define VCR_V_WAIT_1(x) (((x) & 0xff) << 8)
# define VCR_V_WAIT_2(x) ((x) & 0xff)
# define LCDC_POS 0x24
# define POS_POS(x) ((x) & 1f)
# define LCDC_LSCR1 0x28
/* bit fields in imxfb.h */
# define LCDC_PWMR 0x2C
/* bit fields in imxfb.h */
# define LCDC_DMACR 0x30
/* bit fields in imxfb.h */
# define LCDC_RMCR 0x34
2009-01-26 19:29:10 +03:00
# ifdef CONFIG_ARCH_MX1
2008-12-16 13:44:07 +03:00
# define RMCR_LCDC_EN (1<<1)
2009-01-26 19:29:10 +03:00
# else
# define RMCR_LCDC_EN 0
# endif
2008-12-16 13:44:07 +03:00
# define RMCR_SELF_REF (1<<0)
# define LCDC_LCDICR 0x38
# define LCDICR_INT_SYN (1<<2)
# define LCDICR_INT_CON (1)
# define LCDC_LCDISR 0x40
# define LCDISR_UDR_ERR (1<<3)
# define LCDISR_ERR_RES (1<<2)
# define LCDISR_EOF (1<<1)
# define LCDISR_BOF (1<<0)
2009-03-19 10:25:41 +03:00
/* Used fb-mode. Can be set on kernel command line, therefore file-static. */
static const char * fb_mode ;
2008-12-16 13:44:08 +03:00
/*
* These are the bitfields for each
* display depth that we support .
*/
struct imxfb_rgb {
struct fb_bitfield red ;
struct fb_bitfield green ;
struct fb_bitfield blue ;
struct fb_bitfield transp ;
} ;
struct imxfb_info {
struct platform_device * pdev ;
void __iomem * regs ;
2009-01-15 17:21:00 +03:00
struct clk * clk ;
2008-12-16 13:44:08 +03:00
/*
* These are the addresses we mapped
* the framebuffer memory region to .
*/
dma_addr_t map_dma ;
u_char * map_cpu ;
u_int map_size ;
u_char * screen_cpu ;
dma_addr_t screen_dma ;
u_int palette_size ;
dma_addr_t dbar1 ;
dma_addr_t dbar2 ;
u_int pcr ;
u_int pwmr ;
u_int lscr1 ;
u_int dmacr ;
u_int cmap_inverse : 1 ,
cmap_static : 1 ,
unused : 30 ;
2009-03-19 10:25:41 +03:00
struct imx_fb_videomode * mode ;
int num_modes ;
2010-08-02 11:39:55 +04:00
# ifdef PWMR_BACKLIGHT_AVAILABLE
2010-07-16 17:09:07 +04:00
struct backlight_device * bl ;
2010-08-02 11:39:55 +04:00
# endif
2009-03-19 10:25:41 +03:00
2008-12-16 13:44:08 +03:00
void ( * lcd_power ) ( int ) ;
void ( * backlight_power ) ( int ) ;
} ;
# define IMX_NAME "IMX"
/*
* Minimum X and Y resolutions
*/
# define MIN_XRES 64
# define MIN_YRES 64
2009-01-26 19:31:02 +03:00
/* Actually this really is 18bit support, the lowest 2 bits of each colour
* are unused in hardware . We claim to have 24 bit support to make software
* like X work , which does not support 18 bit .
*/
static struct imxfb_rgb def_rgb_18 = {
. red = { . offset = 16 , . length = 8 , } ,
. green = { . offset = 8 , . length = 8 , } ,
. blue = { . offset = 0 , . length = 8 , } ,
. transp = { . offset = 0 , . length = 0 , } ,
} ;
2008-12-16 13:44:09 +03:00
static struct imxfb_rgb def_rgb_16_tft = {
. red = { . offset = 11 , . length = 5 , } ,
. green = { . offset = 5 , . length = 6 , } ,
. blue = { . offset = 0 , . length = 5 , } ,
. transp = { . offset = 0 , . length = 0 , } ,
} ;
static struct imxfb_rgb def_rgb_16_stn = {
2008-12-16 13:44:08 +03:00
. red = { . offset = 8 , . length = 4 , } ,
. green = { . offset = 4 , . length = 4 , } ,
. blue = { . offset = 0 , . length = 4 , } ,
. transp = { . offset = 0 , . length = 0 , } ,
2005-05-01 19:59:24 +04:00
} ;
static struct imxfb_rgb def_rgb_8 = {
2008-12-16 13:44:08 +03:00
. red = { . offset = 0 , . length = 8 , } ,
. green = { . offset = 0 , . length = 8 , } ,
. blue = { . offset = 0 , . length = 8 , } ,
. transp = { . offset = 0 , . length = 0 , } ,
2005-05-01 19:59:24 +04:00
} ;
2008-12-16 13:44:08 +03:00
static int imxfb_activate_var ( struct fb_var_screeninfo * var ,
struct fb_info * info ) ;
2005-05-01 19:59:24 +04:00
static inline u_int chan_to_field ( u_int chan , struct fb_bitfield * bf )
{
chan & = 0xffff ;
chan > > = 16 - bf - > length ;
return chan < < bf - > offset ;
}
2008-12-16 13:44:08 +03:00
static int imxfb_setpalettereg ( u_int regno , u_int red , u_int green , u_int blue ,
u_int trans , struct fb_info * info )
2005-05-01 19:59:24 +04:00
{
struct imxfb_info * fbi = info - > par ;
u_int val , ret = 1 ;
# define CNVT_TOHW(val,width) ((((val)<<(width))+0x7FFF-(val))>>16)
if ( regno < fbi - > palette_size ) {
val = ( CNVT_TOHW ( red , 4 ) < < 8 ) |
( CNVT_TOHW ( green , 4 ) < < 4 ) |
CNVT_TOHW ( blue , 4 ) ;
2008-12-16 13:44:07 +03:00
writel ( val , fbi - > regs + 0x800 + ( regno < < 2 ) ) ;
2005-05-01 19:59:24 +04:00
ret = 0 ;
}
return ret ;
}
2008-12-16 13:44:08 +03:00
static int imxfb_setcolreg ( u_int regno , u_int red , u_int green , u_int blue ,
2005-05-01 19:59:24 +04:00
u_int trans , struct fb_info * info )
{
struct imxfb_info * fbi = info - > par ;
unsigned int val ;
int ret = 1 ;
/*
* If inverse mode was selected , invert all the colours
* rather than the register number . The register number
* is what you poke into the framebuffer to produce the
* colour you requested .
*/
if ( fbi - > cmap_inverse ) {
red = 0xffff - red ;
green = 0xffff - green ;
blue = 0xffff - blue ;
}
/*
* If greyscale is true , then we convert the RGB value
* to greyscale no mater what visual we are using .
*/
if ( info - > var . grayscale )
red = green = blue = ( 19595 * red + 38470 * green +
7471 * blue ) > > 16 ;
switch ( info - > fix . visual ) {
case FB_VISUAL_TRUECOLOR :
/*
* 12 or 16 - bit True Colour . We encode the RGB value
* according to the RGB bitfield information .
*/
if ( regno < 16 ) {
u32 * pal = info - > 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 ;
ret = 0 ;
}
break ;
case FB_VISUAL_STATIC_PSEUDOCOLOR :
case FB_VISUAL_PSEUDOCOLOR :
ret = imxfb_setpalettereg ( regno , red , green , blue , trans , info ) ;
break ;
}
return ret ;
}
2009-03-19 10:25:41 +03:00
static const struct imx_fb_videomode * imxfb_find_mode ( struct imxfb_info * fbi )
{
struct imx_fb_videomode * m ;
int i ;
for ( i = 0 , m = & fbi - > mode [ 0 ] ; i < fbi - > num_modes ; i + + , m + + ) {
if ( ! strcmp ( m - > mode . name , fb_mode ) )
return m ;
}
return NULL ;
}
2005-05-01 19:59:24 +04:00
/*
* imxfb_check_var ( ) :
* Round up in the following order : bits_per_pixel , xres ,
* yres , xres_virtual , yres_virtual , xoffset , yoffset , grayscale ,
* bitfields , horizontal timing , vertical timing .
*/
2008-12-16 13:44:08 +03:00
static int imxfb_check_var ( struct fb_var_screeninfo * var , struct fb_info * info )
2005-05-01 19:59:24 +04:00
{
struct imxfb_info * fbi = info - > par ;
2008-12-16 13:44:09 +03:00
struct imxfb_rgb * rgb ;
2009-03-19 10:25:41 +03:00
const struct imx_fb_videomode * imxfb_mode ;
unsigned long lcd_clk ;
unsigned long long tmp ;
u32 pcr = 0 ;
2005-05-01 19:59:24 +04:00
if ( var - > xres < MIN_XRES )
var - > xres = MIN_XRES ;
if ( var - > yres < MIN_YRES )
var - > yres = MIN_YRES ;
2009-03-19 10:25:41 +03:00
imxfb_mode = imxfb_find_mode ( fbi ) ;
if ( ! imxfb_mode )
return - EINVAL ;
var - > xres = imxfb_mode - > mode . xres ;
var - > yres = imxfb_mode - > mode . yres ;
var - > bits_per_pixel = imxfb_mode - > bpp ;
var - > pixclock = imxfb_mode - > mode . pixclock ;
var - > hsync_len = imxfb_mode - > mode . hsync_len ;
var - > left_margin = imxfb_mode - > mode . left_margin ;
var - > right_margin = imxfb_mode - > mode . right_margin ;
var - > vsync_len = imxfb_mode - > mode . vsync_len ;
var - > upper_margin = imxfb_mode - > mode . upper_margin ;
var - > lower_margin = imxfb_mode - > mode . lower_margin ;
var - > sync = imxfb_mode - > mode . sync ;
var - > xres_virtual = max ( var - > xres_virtual , var - > xres ) ;
var - > yres_virtual = max ( var - > yres_virtual , var - > yres ) ;
2005-05-01 19:59:24 +04:00
pr_debug ( " var->bits_per_pixel=%d \n " , var - > bits_per_pixel ) ;
2009-03-19 10:25:41 +03:00
lcd_clk = clk_get_rate ( fbi - > clk ) ;
tmp = var - > pixclock * ( unsigned long long ) lcd_clk ;
do_div ( tmp , 1000000 ) ;
if ( do_div ( tmp , 1000000 ) > 500000 )
tmp + + ;
pcr = ( unsigned int ) tmp ;
if ( - - pcr > 0x3F ) {
pcr = 0x3F ;
printk ( KERN_WARNING " Must limit pixel clock to %luHz \n " ,
lcd_clk / pcr ) ;
}
2005-05-01 19:59:24 +04:00
switch ( var - > bits_per_pixel ) {
2009-01-26 19:31:02 +03:00
case 32 :
2009-03-19 10:25:41 +03:00
pcr | = PCR_BPIX_18 ;
2009-01-26 19:31:02 +03:00
rgb = & def_rgb_18 ;
break ;
2005-05-01 19:59:24 +04:00
case 16 :
2008-12-16 13:44:09 +03:00
default :
2009-03-19 10:25:41 +03:00
if ( cpu_is_mx1 ( ) )
pcr | = PCR_BPIX_12 ;
else
pcr | = PCR_BPIX_16 ;
if ( imxfb_mode - > pcr & PCR_TFT )
2008-12-16 13:44:09 +03:00
rgb = & def_rgb_16_tft ;
else
rgb = & def_rgb_16_stn ;
2005-05-01 19:59:24 +04:00
break ;
case 8 :
2009-03-19 10:25:41 +03:00
pcr | = PCR_BPIX_8 ;
2008-12-16 13:44:09 +03:00
rgb = & def_rgb_8 ;
2005-05-01 19:59:24 +04:00
break ;
}
2009-03-19 10:25:41 +03:00
/* add sync polarities */
pcr | = imxfb_mode - > pcr & ~ ( 0x3f | ( 7 < < 25 ) ) ;
fbi - > pcr = pcr ;
2005-05-01 19:59:24 +04:00
/*
* Copy the RGB parameters for this display
* from the machine specific parameters .
*/
2008-12-16 13:44:09 +03:00
var - > red = rgb - > red ;
var - > green = rgb - > green ;
var - > blue = rgb - > blue ;
var - > transp = rgb - > transp ;
2005-05-01 19:59:24 +04:00
pr_debug ( " RGBT length = %d:%d:%d:%d \n " ,
var - > red . length , var - > green . length , var - > blue . length ,
var - > transp . length ) ;
pr_debug ( " RGBT offset = %d:%d:%d:%d \n " ,
var - > red . offset , var - > green . offset , var - > blue . offset ,
var - > transp . offset ) ;
return 0 ;
}
/*
* imxfb_set_par ( ) :
* Set the user defined part of the display for the specified console
*/
static int imxfb_set_par ( struct fb_info * info )
{
struct imxfb_info * fbi = info - > par ;
struct fb_var_screeninfo * var = & info - > var ;
2009-01-26 19:31:02 +03:00
if ( var - > bits_per_pixel = = 16 | | var - > bits_per_pixel = = 32 )
2005-05-01 19:59:24 +04:00
info - > fix . visual = FB_VISUAL_TRUECOLOR ;
else if ( ! fbi - > cmap_static )
info - > fix . visual = FB_VISUAL_PSEUDOCOLOR ;
else {
/*
* Some people have weird ideas about wanting static
* pseudocolor maps . I suspect their user space
* applications are broken .
*/
info - > fix . visual = FB_VISUAL_STATIC_PSEUDOCOLOR ;
}
2008-12-16 13:44:08 +03:00
info - > fix . line_length = var - > xres_virtual * var - > bits_per_pixel / 8 ;
2005-05-01 19:59:24 +04:00
fbi - > palette_size = var - > bits_per_pixel = = 8 ? 256 : 16 ;
imxfb_activate_var ( var , info ) ;
return 0 ;
}
2010-08-02 11:39:55 +04:00
# ifdef PWMR_BACKLIGHT_AVAILABLE
2010-07-16 17:09:07 +04:00
static int imxfb_bl_get_brightness ( struct backlight_device * bl )
{
struct imxfb_info * fbi = bl_get_data ( bl ) ;
return readl ( fbi - > regs + LCDC_PWMR ) & 0xFF ;
}
static int imxfb_bl_update_status ( struct backlight_device * bl )
{
struct imxfb_info * fbi = bl_get_data ( bl ) ;
int brightness = bl - > props . brightness ;
if ( bl - > props . power ! = FB_BLANK_UNBLANK )
brightness = 0 ;
if ( bl - > props . fb_blank ! = FB_BLANK_UNBLANK )
brightness = 0 ;
fbi - > pwmr = ( fbi - > pwmr & ~ 0xFF ) | brightness ;
if ( bl - > props . fb_blank ! = FB_BLANK_UNBLANK )
clk_enable ( fbi - > clk ) ;
writel ( fbi - > pwmr , fbi - > regs + LCDC_PWMR ) ;
if ( bl - > props . fb_blank ! = FB_BLANK_UNBLANK )
clk_disable ( fbi - > clk ) ;
return 0 ;
}
static const struct backlight_ops imxfb_lcdc_bl_ops = {
. update_status = imxfb_bl_update_status ,
. get_brightness = imxfb_bl_get_brightness ,
} ;
static void imxfb_init_backlight ( struct imxfb_info * fbi )
{
struct backlight_properties props ;
struct backlight_device * bl ;
if ( fbi - > bl )
return ;
memset ( & props , 0 , sizeof ( struct backlight_properties ) ) ;
props . max_brightness = 0xff ;
writel ( fbi - > pwmr , fbi - > regs + LCDC_PWMR ) ;
bl = backlight_device_register ( " imxfb-bl " , & fbi - > pdev - > dev , fbi ,
& imxfb_lcdc_bl_ops , & props ) ;
if ( IS_ERR ( bl ) ) {
dev_err ( & fbi - > pdev - > dev , " error %ld on backlight register \n " ,
PTR_ERR ( bl ) ) ;
return ;
}
fbi - > bl = bl ;
bl - > props . power = FB_BLANK_UNBLANK ;
bl - > props . fb_blank = FB_BLANK_UNBLANK ;
bl - > props . brightness = imxfb_bl_get_brightness ( bl ) ;
}
static void imxfb_exit_backlight ( struct imxfb_info * fbi )
{
if ( fbi - > bl )
backlight_device_unregister ( fbi - > bl ) ;
}
2010-08-02 11:39:55 +04:00
# endif
2010-07-16 17:09:07 +04:00
2005-05-01 19:59:24 +04:00
static void imxfb_enable_controller ( struct imxfb_info * fbi )
{
pr_debug ( " Enabling LCD controller \n " ) ;
2008-12-16 13:44:07 +03:00
writel ( fbi - > screen_dma , fbi - > regs + LCDC_SSA ) ;
2005-05-01 19:59:24 +04:00
2008-12-16 13:44:07 +03:00
/* panning offset 0 (0 pixel offset) */
writel ( 0x00000000 , fbi - > regs + LCDC_POS ) ;
2005-05-01 19:59:24 +04:00
/* disable hardware cursor */
2008-12-16 13:44:07 +03:00
writel ( readl ( fbi - > regs + LCDC_CPOS ) & ~ ( CPOS_CC0 | CPOS_CC1 ) ,
fbi - > regs + LCDC_CPOS ) ;
2005-05-01 19:59:24 +04:00
2008-12-16 13:44:07 +03:00
writel ( RMCR_LCDC_EN , fbi - > regs + LCDC_RMCR ) ;
2005-05-01 19:59:24 +04:00
2009-01-15 17:21:00 +03:00
clk_enable ( fbi - > clk ) ;
2008-12-16 13:44:08 +03:00
if ( fbi - > backlight_power )
2005-05-01 19:59:24 +04:00
fbi - > backlight_power ( 1 ) ;
2008-12-16 13:44:08 +03:00
if ( fbi - > lcd_power )
2005-05-01 19:59:24 +04:00
fbi - > lcd_power ( 1 ) ;
}
static void imxfb_disable_controller ( struct imxfb_info * fbi )
{
pr_debug ( " Disabling LCD controller \n " ) ;
2008-12-16 13:44:08 +03:00
if ( fbi - > backlight_power )
2005-05-01 19:59:24 +04:00
fbi - > backlight_power ( 0 ) ;
2008-12-16 13:44:08 +03:00
if ( fbi - > lcd_power )
2005-05-01 19:59:24 +04:00
fbi - > lcd_power ( 0 ) ;
2009-01-15 17:21:00 +03:00
clk_disable ( fbi - > clk ) ;
2008-12-16 13:44:07 +03:00
writel ( 0 , fbi - > regs + LCDC_RMCR ) ;
2005-05-01 19:59:24 +04:00
}
static int imxfb_blank ( int blank , struct fb_info * info )
{
struct imxfb_info * fbi = info - > par ;
pr_debug ( " imxfb_blank: blank=%d \n " , blank ) ;
switch ( blank ) {
case FB_BLANK_POWERDOWN :
case FB_BLANK_VSYNC_SUSPEND :
case FB_BLANK_HSYNC_SUSPEND :
case FB_BLANK_NORMAL :
imxfb_disable_controller ( fbi ) ;
break ;
case FB_BLANK_UNBLANK :
imxfb_enable_controller ( fbi ) ;
break ;
}
return 0 ;
}
static struct fb_ops imxfb_ops = {
. owner = THIS_MODULE ,
. fb_check_var = imxfb_check_var ,
. fb_set_par = imxfb_set_par ,
. fb_setcolreg = imxfb_setcolreg ,
. fb_fillrect = cfb_fillrect ,
. fb_copyarea = cfb_copyarea ,
. fb_imageblit = cfb_imageblit ,
. fb_blank = imxfb_blank ,
} ;
/*
* imxfb_activate_var ( ) :
* Configures LCD Controller based on entries in var parameter . Settings are
* only written to the controller if changes were made .
*/
static int imxfb_activate_var ( struct fb_var_screeninfo * var , struct fb_info * info )
{
struct imxfb_info * fbi = info - > par ;
2009-01-15 17:21:00 +03:00
2005-05-01 19:59:24 +04:00
pr_debug ( " var: xres=%d hslen=%d lm=%d rm=%d \n " ,
var - > xres , var - > hsync_len ,
var - > left_margin , var - > right_margin ) ;
pr_debug ( " var: yres=%d vslen=%d um=%d bm=%d \n " ,
var - > yres , var - > vsync_len ,
var - > upper_margin , var - > lower_margin ) ;
# if DEBUG_VAR
if ( var - > xres < 16 | | var - > xres > 1024 )
printk ( KERN_ERR " %s: invalid xres %d \n " ,
info - > fix . id , var - > xres ) ;
if ( var - > hsync_len < 1 | | var - > hsync_len > 64 )
printk ( KERN_ERR " %s: invalid hsync_len %d \n " ,
info - > fix . id , var - > hsync_len ) ;
if ( var - > left_margin > 255 )
printk ( KERN_ERR " %s: invalid left_margin %d \n " ,
info - > fix . id , var - > left_margin ) ;
if ( var - > right_margin > 255 )
printk ( KERN_ERR " %s: invalid right_margin %d \n " ,
info - > fix . id , var - > right_margin ) ;
2010-12-24 15:28:24 +03:00
if ( var - > yres < 1 | | var - > yres > YMAX_MASK )
2005-05-01 19:59:24 +04:00
printk ( KERN_ERR " %s: invalid yres %d \n " ,
info - > fix . id , var - > yres ) ;
if ( var - > vsync_len > 100 )
printk ( KERN_ERR " %s: invalid vsync_len %d \n " ,
info - > fix . id , var - > vsync_len ) ;
if ( var - > upper_margin > 63 )
printk ( KERN_ERR " %s: invalid upper_margin %d \n " ,
info - > fix . id , var - > upper_margin ) ;
if ( var - > lower_margin > 255 )
printk ( KERN_ERR " %s: invalid lower_margin %d \n " ,
info - > fix . id , var - > lower_margin ) ;
# endif
2009-03-19 10:25:41 +03:00
/* physical screen start address */
writel ( VPW_VPW ( var - > xres * var - > bits_per_pixel / 8 / 4 ) ,
fbi - > regs + LCDC_VPW ) ;
2009-02-14 18:29:38 +03:00
writel ( HCR_H_WIDTH ( var - > hsync_len - 1 ) |
HCR_H_WAIT_1 ( var - > right_margin - 1 ) |
HCR_H_WAIT_2 ( var - > left_margin - 3 ) ,
2008-12-16 13:44:07 +03:00
fbi - > regs + LCDC_HCR ) ;
2005-05-01 19:59:24 +04:00
2008-12-16 13:44:07 +03:00
writel ( VCR_V_WIDTH ( var - > vsync_len ) |
2008-12-16 13:44:08 +03:00
VCR_V_WAIT_1 ( var - > lower_margin ) |
VCR_V_WAIT_2 ( var - > upper_margin ) ,
2008-12-16 13:44:07 +03:00
fbi - > regs + LCDC_VCR ) ;
2005-05-01 19:59:24 +04:00
2008-12-16 13:44:07 +03:00
writel ( SIZE_XMAX ( var - > xres ) | SIZE_YMAX ( var - > yres ) ,
fbi - > regs + LCDC_SIZE ) ;
2009-01-15 17:21:00 +03:00
2009-03-19 10:25:41 +03:00
writel ( fbi - > pcr , fbi - > regs + LCDC_PCR ) ;
2010-08-02 11:39:55 +04:00
# ifndef PWMR_BACKLIGHT_AVAILABLE
writel ( fbi - > pwmr , fbi - > regs + LCDC_PWMR ) ;
# endif
2008-12-16 13:44:07 +03:00
writel ( fbi - > lscr1 , fbi - > regs + LCDC_LSCR1 ) ;
writel ( fbi - > dmacr , fbi - > regs + LCDC_DMACR ) ;
2005-05-01 19:59:24 +04:00
return 0 ;
}
# 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 .
*/
2005-11-10 01:32:44 +03:00
static int imxfb_suspend ( struct platform_device * dev , pm_message_t state )
2005-05-01 19:59:24 +04:00
{
2010-02-03 00:44:10 +03:00
struct fb_info * info = platform_get_drvdata ( dev ) ;
struct imxfb_info * fbi = info - > par ;
2008-12-16 13:44:08 +03:00
pr_debug ( " %s \n " , __func__ ) ;
2005-05-01 19:59:24 +04:00
2005-10-28 20:52:56 +04:00
imxfb_disable_controller ( fbi ) ;
2005-05-01 19:59:24 +04:00
return 0 ;
}
2005-11-10 01:32:44 +03:00
static int imxfb_resume ( struct platform_device * dev )
2005-05-01 19:59:24 +04:00
{
2010-02-03 00:44:10 +03:00
struct fb_info * info = platform_get_drvdata ( dev ) ;
struct imxfb_info * fbi = info - > par ;
2008-12-16 13:44:08 +03:00
pr_debug ( " %s \n " , __func__ ) ;
2005-05-01 19:59:24 +04:00
2005-10-28 20:52:56 +04:00
imxfb_enable_controller ( fbi ) ;
2005-05-01 19:59:24 +04:00
return 0 ;
}
# else
# define imxfb_suspend NULL
# define imxfb_resume NULL
# endif
2008-12-16 13:44:07 +03:00
static int __init imxfb_init_fbinfo ( struct platform_device * pdev )
2005-05-01 19:59:24 +04:00
{
2008-12-16 13:44:09 +03:00
struct imx_fb_platform_data * pdata = pdev - > dev . platform_data ;
2008-12-16 13:44:07 +03:00
struct fb_info * info = dev_get_drvdata ( & pdev - > dev ) ;
2005-05-01 19:59:24 +04:00
struct imxfb_info * fbi = info - > par ;
2009-03-19 10:25:41 +03:00
struct imx_fb_videomode * m ;
int i ;
2005-05-01 19:59:24 +04:00
2008-04-28 13:15:47 +04:00
pr_debug ( " %s \n " , __func__ ) ;
2005-05-01 19:59:24 +04:00
2008-12-16 13:44:08 +03:00
info - > pseudo_palette = kmalloc ( sizeof ( u32 ) * 16 , GFP_KERNEL ) ;
2005-05-01 19:59:24 +04:00
if ( ! info - > pseudo_palette )
return - ENOMEM ;
memset ( fbi , 0 , sizeof ( struct imxfb_info ) ) ;
strlcpy ( info - > fix . id , IMX_NAME , sizeof ( info - > fix . id ) ) ;
2008-12-16 13:44:08 +03:00
info - > fix . type = FB_TYPE_PACKED_PIXELS ;
2005-05-01 19:59:24 +04:00
info - > fix . type_aux = 0 ;
info - > fix . xpanstep = 0 ;
info - > fix . ypanstep = 0 ;
info - > fix . ywrapstep = 0 ;
2008-12-16 13:44:08 +03:00
info - > fix . accel = FB_ACCEL_NONE ;
2005-05-01 19:59:24 +04:00
info - > var . nonstd = 0 ;
info - > var . activate = FB_ACTIVATE_NOW ;
info - > var . height = - 1 ;
info - > var . width = - 1 ;
info - > var . accel_flags = 0 ;
2008-12-16 13:44:08 +03:00
info - > var . vmode = FB_VMODE_NONINTERLACED ;
2005-05-01 19:59:24 +04:00
info - > fbops = & imxfb_ops ;
2008-12-16 13:44:08 +03:00
info - > flags = FBINFO_FLAG_DEFAULT |
FBINFO_READS_FAST ;
2008-12-16 13:44:09 +03:00
info - > var . grayscale = pdata - > cmap_greyscale ;
fbi - > cmap_inverse = pdata - > cmap_inverse ;
fbi - > cmap_static = pdata - > cmap_static ;
fbi - > lscr1 = pdata - > lscr1 ;
fbi - > dmacr = pdata - > dmacr ;
fbi - > pwmr = pdata - > pwmr ;
fbi - > lcd_power = pdata - > lcd_power ;
fbi - > backlight_power = pdata - > backlight_power ;
2009-03-19 10:25:41 +03:00
for ( i = 0 , m = & pdata - > mode [ 0 ] ; i < pdata - > num_modes ; i + + , m + + )
info - > fix . smem_len = max_t ( size_t , info - > fix . smem_len ,
m - > mode . xres * m - > mode . yres * m - > bpp / 8 ) ;
2005-05-01 19:59:24 +04:00
return 0 ;
}
2005-11-10 01:32:44 +03:00
static int __init imxfb_probe ( struct platform_device * pdev )
2005-05-01 19:59:24 +04:00
{
struct imxfb_info * fbi ;
struct fb_info * info ;
2008-12-16 13:44:09 +03:00
struct imx_fb_platform_data * pdata ;
2005-05-01 19:59:24 +04:00
struct resource * res ;
2009-03-19 10:25:41 +03:00
int ret , i ;
2005-05-01 19:59:24 +04:00
2009-06-29 13:41:09 +04:00
dev_info ( & pdev - > dev , " i.MX Framebuffer driver \n " ) ;
2005-05-01 19:59:24 +04:00
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2008-12-16 13:44:08 +03:00
if ( ! res )
2005-05-01 19:59:24 +04:00
return - ENODEV ;
2008-12-16 13:44:09 +03:00
pdata = pdev - > dev . platform_data ;
if ( ! pdata ) {
2006-01-07 13:44:32 +03:00
dev_err ( & pdev - > dev , " No platform_data available \n " ) ;
2005-05-01 19:59:24 +04:00
return - ENOMEM ;
}
2005-11-10 01:32:44 +03:00
info = framebuffer_alloc ( sizeof ( struct imxfb_info ) , & pdev - > dev ) ;
2008-12-16 13:44:08 +03:00
if ( ! info )
2005-05-01 19:59:24 +04:00
return - ENOMEM ;
fbi = info - > par ;
2009-03-19 10:25:41 +03:00
if ( ! fb_mode )
fb_mode = pdata - > mode [ 0 ] . mode . name ;
2005-11-10 01:32:44 +03:00
platform_set_drvdata ( pdev , info ) ;
2005-05-01 19:59:24 +04:00
2008-12-16 13:44:07 +03:00
ret = imxfb_init_fbinfo ( pdev ) ;
2008-12-16 13:44:08 +03:00
if ( ret < 0 )
2005-05-01 19:59:24 +04:00
goto failed_init ;
2008-12-16 13:44:07 +03:00
res = request_mem_region ( res - > start , resource_size ( res ) ,
DRIVER_NAME ) ;
2005-05-01 19:59:24 +04:00
if ( ! res ) {
ret = - EBUSY ;
2008-12-16 13:44:07 +03:00
goto failed_req ;
}
2009-01-15 17:21:00 +03:00
fbi - > clk = clk_get ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( fbi - > clk ) ) {
2009-08-18 22:18:35 +04:00
ret = PTR_ERR ( fbi - > clk ) ;
2009-01-15 17:21:00 +03:00
dev_err ( & pdev - > dev , " unable to get clock: %d \n " , ret ) ;
goto failed_getclock ;
}
2008-12-16 13:44:07 +03:00
fbi - > regs = ioremap ( res - > start , resource_size ( res ) ) ;
if ( fbi - > regs = = NULL ) {
2009-06-29 13:41:09 +04:00
dev_err ( & pdev - > dev , " Cannot map frame buffer registers \n " ) ;
2008-12-16 13:44:07 +03:00
goto failed_ioremap ;
2005-05-01 19:59:24 +04:00
}
2008-12-16 13:44:09 +03:00
if ( ! pdata - > fixed_screen_cpu ) {
2008-12-16 13:44:07 +03:00
fbi - > map_size = PAGE_ALIGN ( info - > fix . smem_len ) ;
fbi - > map_cpu = dma_alloc_writecombine ( & pdev - > dev ,
fbi - > map_size , & fbi - > map_dma , GFP_KERNEL ) ;
if ( ! fbi - > map_cpu ) {
2006-01-07 13:44:32 +03:00
dev_err ( & pdev - > dev , " Failed to allocate video RAM: %d \n " , ret ) ;
2005-05-01 19:59:24 +04:00
ret = - ENOMEM ;
goto failed_map ;
}
2008-12-16 13:44:07 +03:00
info - > screen_base = fbi - > map_cpu ;
fbi - > screen_cpu = fbi - > map_cpu ;
fbi - > screen_dma = fbi - > map_dma ;
info - > fix . smem_start = fbi - > screen_dma ;
2005-05-01 19:59:24 +04:00
} else {
/* Fixed framebuffer mapping enables location of the screen in eSRAM */
2008-12-16 13:44:09 +03:00
fbi - > map_cpu = pdata - > fixed_screen_cpu ;
fbi - > map_dma = pdata - > fixed_screen_dma ;
2005-05-01 19:59:24 +04:00
info - > screen_base = fbi - > map_cpu ;
fbi - > screen_cpu = fbi - > map_cpu ;
fbi - > screen_dma = fbi - > map_dma ;
info - > fix . smem_start = fbi - > screen_dma ;
}
2009-01-15 17:37:22 +03:00
if ( pdata - > init ) {
ret = pdata - > init ( fbi - > pdev ) ;
if ( ret )
goto failed_platform_init ;
}
2009-03-19 10:25:41 +03:00
fbi - > mode = pdata - > mode ;
fbi - > num_modes = pdata - > num_modes ;
INIT_LIST_HEAD ( & info - > modelist ) ;
for ( i = 0 ; i < pdata - > num_modes ; i + + )
fb_add_videomode ( & pdata - > mode [ i ] . mode , & info - > modelist ) ;
2005-05-01 19:59:24 +04:00
/*
* This makes sure that our colour bitfield
* descriptors are correctly initialised .
*/
imxfb_check_var ( & info - > var , info ) ;
2008-12-16 13:44:08 +03:00
ret = fb_alloc_cmap ( & info - > cmap , 1 < < info - > var . bits_per_pixel , 0 ) ;
2005-05-01 19:59:24 +04:00
if ( ret < 0 )
goto failed_cmap ;
imxfb_set_par ( info ) ;
ret = register_framebuffer ( info ) ;
if ( ret < 0 ) {
2006-01-07 13:44:32 +03:00
dev_err ( & pdev - > dev , " failed to register framebuffer \n " ) ;
2005-05-01 19:59:24 +04:00
goto failed_register ;
}
imxfb_enable_controller ( fbi ) ;
2010-07-16 17:09:07 +04:00
fbi - > pdev = pdev ;
2010-08-02 11:39:55 +04:00
# ifdef PWMR_BACKLIGHT_AVAILABLE
2010-07-16 17:09:07 +04:00
imxfb_init_backlight ( fbi ) ;
2010-08-02 11:39:55 +04:00
# endif
2005-05-01 19:59:24 +04:00
return 0 ;
failed_register :
fb_dealloc_cmap ( & info - > cmap ) ;
failed_cmap :
2009-01-15 17:37:22 +03:00
if ( pdata - > exit )
pdata - > exit ( fbi - > pdev ) ;
failed_platform_init :
2008-12-16 13:44:09 +03:00
if ( ! pdata - > fixed_screen_cpu )
2005-11-10 01:32:44 +03:00
dma_free_writecombine ( & pdev - > dev , fbi - > map_size , fbi - > map_cpu ,
2008-12-16 13:44:07 +03:00
fbi - > map_dma ) ;
2005-05-01 19:59:24 +04:00
failed_map :
2009-01-15 17:21:00 +03:00
clk_put ( fbi - > clk ) ;
failed_getclock :
2008-12-16 13:44:07 +03:00
iounmap ( fbi - > regs ) ;
failed_ioremap :
2009-06-29 13:41:09 +04:00
release_mem_region ( res - > start , resource_size ( res ) ) ;
2008-12-16 13:44:07 +03:00
failed_req :
kfree ( info - > pseudo_palette ) ;
2005-05-01 19:59:24 +04:00
failed_init :
2005-11-10 01:32:44 +03:00
platform_set_drvdata ( pdev , NULL ) ;
2005-05-01 19:59:24 +04:00
framebuffer_release ( info ) ;
return ret ;
}
2008-12-16 13:44:07 +03:00
static int __devexit imxfb_remove ( struct platform_device * pdev )
2005-05-01 19:59:24 +04:00
{
2009-01-15 17:37:22 +03:00
struct imx_fb_platform_data * pdata ;
2005-11-10 01:32:44 +03:00
struct fb_info * info = platform_get_drvdata ( pdev ) ;
2005-07-17 23:15:36 +04:00
struct imxfb_info * fbi = info - > par ;
2005-05-01 19:59:24 +04:00
struct resource * res ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2005-07-17 23:15:36 +04:00
imxfb_disable_controller ( fbi ) ;
2005-05-01 19:59:24 +04:00
2010-08-02 11:39:55 +04:00
# ifdef PWMR_BACKLIGHT_AVAILABLE
2010-07-16 17:09:07 +04:00
imxfb_exit_backlight ( fbi ) ;
2010-08-02 11:39:55 +04:00
# endif
2005-05-01 19:59:24 +04:00
unregister_framebuffer ( info ) ;
2009-01-15 17:37:22 +03:00
pdata = pdev - > dev . platform_data ;
if ( pdata - > exit )
pdata - > exit ( fbi - > pdev ) ;
2005-05-01 19:59:24 +04:00
fb_dealloc_cmap ( & info - > cmap ) ;
kfree ( info - > pseudo_palette ) ;
framebuffer_release ( info ) ;
2008-12-16 13:44:07 +03:00
iounmap ( fbi - > regs ) ;
2009-06-29 13:41:09 +04:00
release_mem_region ( res - > start , resource_size ( res ) ) ;
2009-01-15 17:21:00 +03:00
clk_disable ( fbi - > clk ) ;
clk_put ( fbi - > clk ) ;
2005-11-10 01:32:44 +03:00
platform_set_drvdata ( pdev , NULL ) ;
2005-05-01 19:59:24 +04:00
return 0 ;
}
2005-11-10 01:32:44 +03:00
void imxfb_shutdown ( struct platform_device * dev )
2005-05-01 19:59:24 +04:00
{
2005-11-10 01:32:44 +03:00
struct fb_info * info = platform_get_drvdata ( dev ) ;
2005-07-17 23:15:36 +04:00
struct imxfb_info * fbi = info - > par ;
imxfb_disable_controller ( fbi ) ;
2005-05-01 19:59:24 +04:00
}
2005-11-10 01:32:44 +03:00
static struct platform_driver imxfb_driver = {
2005-05-01 19:59:24 +04:00
. suspend = imxfb_suspend ,
. resume = imxfb_resume ,
2008-12-16 13:44:07 +03:00
. remove = __devexit_p ( imxfb_remove ) ,
2005-05-01 19:59:24 +04:00
. shutdown = imxfb_shutdown ,
2005-11-10 01:32:44 +03:00
. driver = {
2008-12-16 13:44:07 +03:00
. name = DRIVER_NAME ,
2005-11-10 01:32:44 +03:00
} ,
2005-05-01 19:59:24 +04:00
} ;
2009-03-19 10:25:41 +03:00
static int imxfb_setup ( void )
{
# ifndef MODULE
char * opt , * options = NULL ;
if ( fb_get_options ( " imxfb " , & options ) )
return - ENODEV ;
if ( ! options | | ! * options )
return 0 ;
while ( ( opt = strsep ( & options , " , " ) ) ! = NULL ) {
if ( ! * opt )
continue ;
else
fb_mode = opt ;
}
# endif
return 0 ;
}
2005-05-01 19:59:24 +04:00
int __init imxfb_init ( void )
{
2009-03-19 10:25:41 +03:00
int ret = imxfb_setup ( ) ;
if ( ret < 0 )
return ret ;
2008-12-16 13:44:07 +03:00
return platform_driver_probe ( & imxfb_driver , imxfb_probe ) ;
2005-05-01 19:59:24 +04:00
}
static void __exit imxfb_cleanup ( void )
{
2005-11-10 01:32:44 +03:00
platform_driver_unregister ( & imxfb_driver ) ;
2005-05-01 19:59:24 +04:00
}
module_init ( imxfb_init ) ;
module_exit ( imxfb_cleanup ) ;
2011-01-10 14:47:41 +03:00
MODULE_DESCRIPTION ( " Freescale i.MX framebuffer driver " ) ;
2005-05-01 19:59:24 +04:00
MODULE_AUTHOR ( " Sascha Hauer, Pengutronix " ) ;
MODULE_LICENSE ( " GPL " ) ;