2009-01-21 20:32:34 +03:00
/*
* Copyright ( C ) 2008
* Guennadi Liakhovetski , DENX Software Engineering , < lg @ denx . de >
*
* Copyright 2004 - 2007 Freescale Semiconductor , Inc . All Rights Reserved .
*
* 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 .
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/platform_device.h>
# include <linux/sched.h>
# include <linux/errno.h>
# include <linux/string.h>
# include <linux/interrupt.h>
# include <linux/slab.h>
# include <linux/fb.h>
# include <linux/delay.h>
# include <linux/init.h>
# include <linux/ioport.h>
# include <linux/dma-mapping.h>
# include <linux/dmaengine.h>
# include <linux/console.h>
# include <linux/clk.h>
# include <linux/mutex.h>
2010-08-16 10:03:04 +04:00
# include <mach/dma.h>
2009-01-21 20:32:34 +03:00
# include <mach/hardware.h>
# include <mach/ipu.h>
# include <mach/mx3fb.h>
# include <asm/io.h>
# include <asm/uaccess.h>
2009-02-26 23:34:28 +03:00
# define MX3FB_NAME "mx3_sdc_fb"
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
# define MX3FB_REG_OFFSET 0xB4
2009-01-21 20:32:34 +03:00
/* SDC Registers */
2009-02-26 23:34:28 +03:00
# define SDC_COM_CONF (0xB4 - MX3FB_REG_OFFSET)
# define SDC_GW_CTRL (0xB8 - MX3FB_REG_OFFSET)
# define SDC_FG_POS (0xBC - MX3FB_REG_OFFSET)
# define SDC_BG_POS (0xC0 - MX3FB_REG_OFFSET)
# define SDC_CUR_POS (0xC4 - MX3FB_REG_OFFSET)
# define SDC_PWM_CTRL (0xC8 - MX3FB_REG_OFFSET)
# define SDC_CUR_MAP (0xCC - MX3FB_REG_OFFSET)
# define SDC_HOR_CONF (0xD0 - MX3FB_REG_OFFSET)
# define SDC_VER_CONF (0xD4 - MX3FB_REG_OFFSET)
# define SDC_SHARP_CONF_1 (0xD8 - MX3FB_REG_OFFSET)
# define SDC_SHARP_CONF_2 (0xDC - MX3FB_REG_OFFSET)
2009-01-21 20:32:34 +03:00
/* Register bits */
2009-02-26 23:34:28 +03:00
# define SDC_COM_TFT_COLOR 0x00000001UL
# define SDC_COM_FG_EN 0x00000010UL
# define SDC_COM_GWSEL 0x00000020UL
# define SDC_COM_GLB_A 0x00000040UL
# define SDC_COM_KEY_COLOR_G 0x00000080UL
# define SDC_COM_BG_EN 0x00000200UL
# define SDC_COM_SHARP 0x00001000UL
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
# define SDC_V_SYNC_WIDTH_L 0x00000001UL
2009-01-21 20:32:34 +03:00
/* Display Interface registers */
2009-02-26 23:34:28 +03:00
# define DI_DISP_IF_CONF (0x0124 - MX3FB_REG_OFFSET)
# define DI_DISP_SIG_POL (0x0128 - MX3FB_REG_OFFSET)
# define DI_SER_DISP1_CONF (0x012C - MX3FB_REG_OFFSET)
# define DI_SER_DISP2_CONF (0x0130 - MX3FB_REG_OFFSET)
# define DI_HSP_CLK_PER (0x0134 - MX3FB_REG_OFFSET)
# define DI_DISP0_TIME_CONF_1 (0x0138 - MX3FB_REG_OFFSET)
# define DI_DISP0_TIME_CONF_2 (0x013C - MX3FB_REG_OFFSET)
# define DI_DISP0_TIME_CONF_3 (0x0140 - MX3FB_REG_OFFSET)
# define DI_DISP1_TIME_CONF_1 (0x0144 - MX3FB_REG_OFFSET)
# define DI_DISP1_TIME_CONF_2 (0x0148 - MX3FB_REG_OFFSET)
# define DI_DISP1_TIME_CONF_3 (0x014C - MX3FB_REG_OFFSET)
# define DI_DISP2_TIME_CONF_1 (0x0150 - MX3FB_REG_OFFSET)
# define DI_DISP2_TIME_CONF_2 (0x0154 - MX3FB_REG_OFFSET)
# define DI_DISP2_TIME_CONF_3 (0x0158 - MX3FB_REG_OFFSET)
# define DI_DISP3_TIME_CONF (0x015C - MX3FB_REG_OFFSET)
# define DI_DISP0_DB0_MAP (0x0160 - MX3FB_REG_OFFSET)
# define DI_DISP0_DB1_MAP (0x0164 - MX3FB_REG_OFFSET)
# define DI_DISP0_DB2_MAP (0x0168 - MX3FB_REG_OFFSET)
# define DI_DISP0_CB0_MAP (0x016C - MX3FB_REG_OFFSET)
# define DI_DISP0_CB1_MAP (0x0170 - MX3FB_REG_OFFSET)
# define DI_DISP0_CB2_MAP (0x0174 - MX3FB_REG_OFFSET)
# define DI_DISP1_DB0_MAP (0x0178 - MX3FB_REG_OFFSET)
# define DI_DISP1_DB1_MAP (0x017C - MX3FB_REG_OFFSET)
# define DI_DISP1_DB2_MAP (0x0180 - MX3FB_REG_OFFSET)
# define DI_DISP1_CB0_MAP (0x0184 - MX3FB_REG_OFFSET)
# define DI_DISP1_CB1_MAP (0x0188 - MX3FB_REG_OFFSET)
# define DI_DISP1_CB2_MAP (0x018C - MX3FB_REG_OFFSET)
# define DI_DISP2_DB0_MAP (0x0190 - MX3FB_REG_OFFSET)
# define DI_DISP2_DB1_MAP (0x0194 - MX3FB_REG_OFFSET)
# define DI_DISP2_DB2_MAP (0x0198 - MX3FB_REG_OFFSET)
# define DI_DISP2_CB0_MAP (0x019C - MX3FB_REG_OFFSET)
# define DI_DISP2_CB1_MAP (0x01A0 - MX3FB_REG_OFFSET)
# define DI_DISP2_CB2_MAP (0x01A4 - MX3FB_REG_OFFSET)
# define DI_DISP3_B0_MAP (0x01A8 - MX3FB_REG_OFFSET)
# define DI_DISP3_B1_MAP (0x01AC - MX3FB_REG_OFFSET)
# define DI_DISP3_B2_MAP (0x01B0 - MX3FB_REG_OFFSET)
# define DI_DISP_ACC_CC (0x01B4 - MX3FB_REG_OFFSET)
# define DI_DISP_LLA_CONF (0x01B8 - MX3FB_REG_OFFSET)
# define DI_DISP_LLA_DATA (0x01BC - MX3FB_REG_OFFSET)
2009-01-21 20:32:34 +03:00
/* DI_DISP_SIG_POL bits */
2009-02-26 23:34:28 +03:00
# define DI_D3_VSYNC_POL_SHIFT 28
# define DI_D3_HSYNC_POL_SHIFT 27
# define DI_D3_DRDY_SHARP_POL_SHIFT 26
# define DI_D3_CLK_POL_SHIFT 25
# define DI_D3_DATA_POL_SHIFT 24
2009-01-21 20:32:34 +03:00
/* DI_DISP_IF_CONF bits */
2009-02-26 23:34:28 +03:00
# define DI_D3_CLK_IDLE_SHIFT 26
# define DI_D3_CLK_SEL_SHIFT 25
# define DI_D3_DATAMSK_SHIFT 24
2009-01-21 20:32:34 +03:00
enum ipu_panel {
2009-02-26 23:34:28 +03:00
IPU_PANEL_SHARP_TFT ,
IPU_PANEL_TFT ,
2009-01-21 20:32:34 +03:00
} ;
struct ipu_di_signal_cfg {
2009-02-26 23:34:28 +03:00
unsigned datamask_en : 1 ;
unsigned clksel_en : 1 ;
unsigned clkidle_en : 1 ;
unsigned data_pol : 1 ; /* true = inverted */
unsigned clk_pol : 1 ; /* true = rising edge */
unsigned enable_pol : 1 ;
unsigned Hsync_pol : 1 ; /* true = active high */
unsigned Vsync_pol : 1 ;
2009-01-21 20:32:34 +03:00
} ;
static const struct fb_videomode mx3fb_modedb [ ] = {
2009-02-26 23:34:28 +03:00
{
/* 240x320 @ 60 Hz */
. name = " Sharp-QVGA " ,
. refresh = 60 ,
. xres = 240 ,
. yres = 320 ,
. pixclock = 185925 ,
. left_margin = 9 ,
. right_margin = 16 ,
. upper_margin = 7 ,
. lower_margin = 9 ,
. hsync_len = 1 ,
. vsync_len = 1 ,
. sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_SHARP_MODE |
FB_SYNC_CLK_INVERT | FB_SYNC_DATA_INVERT |
FB_SYNC_CLK_IDLE_EN ,
. vmode = FB_VMODE_NONINTERLACED ,
. flag = 0 ,
} , {
/* 240x33 @ 60 Hz */
. name = " Sharp-CLI " ,
. refresh = 60 ,
. xres = 240 ,
. yres = 33 ,
. pixclock = 185925 ,
. left_margin = 9 ,
. right_margin = 16 ,
. upper_margin = 7 ,
. lower_margin = 9 + 287 ,
. hsync_len = 1 ,
. vsync_len = 1 ,
. sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_SHARP_MODE |
FB_SYNC_CLK_INVERT | FB_SYNC_DATA_INVERT |
FB_SYNC_CLK_IDLE_EN ,
. vmode = FB_VMODE_NONINTERLACED ,
. flag = 0 ,
} , {
/* 640x480 @ 60 Hz */
. name = " NEC-VGA " ,
. refresh = 60 ,
. xres = 640 ,
. yres = 480 ,
. pixclock = 38255 ,
. left_margin = 144 ,
. right_margin = 0 ,
. upper_margin = 34 ,
. lower_margin = 40 ,
. hsync_len = 1 ,
. vsync_len = 1 ,
. sync = FB_SYNC_VERT_HIGH_ACT | FB_SYNC_OE_ACT_HIGH ,
. vmode = FB_VMODE_NONINTERLACED ,
. flag = 0 ,
} , {
/* NTSC TV output */
. name = " TV-NTSC " ,
. refresh = 60 ,
. xres = 640 ,
. yres = 480 ,
. pixclock = 37538 ,
. left_margin = 38 ,
. right_margin = 858 - 640 - 38 - 3 ,
. upper_margin = 36 ,
. lower_margin = 518 - 480 - 36 - 1 ,
. hsync_len = 3 ,
. vsync_len = 1 ,
. sync = 0 ,
. vmode = FB_VMODE_NONINTERLACED ,
. flag = 0 ,
} , {
/* PAL TV output */
. name = " TV-PAL " ,
. refresh = 50 ,
. xres = 640 ,
. yres = 480 ,
. pixclock = 37538 ,
. left_margin = 38 ,
. right_margin = 960 - 640 - 38 - 32 ,
. upper_margin = 32 ,
. lower_margin = 555 - 480 - 32 - 3 ,
. hsync_len = 32 ,
. vsync_len = 3 ,
. sync = 0 ,
. vmode = FB_VMODE_NONINTERLACED ,
. flag = 0 ,
} , {
/* TV output VGA mode, 640x480 @ 65 Hz */
. name = " TV-VGA " ,
. refresh = 60 ,
. xres = 640 ,
. yres = 480 ,
. pixclock = 40574 ,
. left_margin = 35 ,
. right_margin = 45 ,
. upper_margin = 9 ,
. lower_margin = 1 ,
. hsync_len = 46 ,
. vsync_len = 5 ,
. sync = 0 ,
. vmode = FB_VMODE_NONINTERLACED ,
. flag = 0 ,
} ,
2009-01-21 20:32:34 +03:00
} ;
struct mx3fb_data {
2009-02-26 23:34:28 +03:00
struct fb_info * fbi ;
int backlight_level ;
void __iomem * reg_base ;
spinlock_t lock ;
struct device * dev ;
uint32_t h_start_width ;
uint32_t v_start_width ;
2009-01-21 20:32:34 +03:00
} ;
struct dma_chan_request {
2009-02-26 23:34:28 +03:00
struct mx3fb_data * mx3fb ;
enum ipu_channel id ;
2009-01-21 20:32:34 +03:00
} ;
/* MX3 specific framebuffer information. */
struct mx3fb_info {
2009-02-26 23:34:28 +03:00
int blank ;
enum ipu_channel ipu_ch ;
uint32_t cur_ipu_buf ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
u32 pseudo_palette [ 16 ] ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
struct completion flip_cmpl ;
struct mutex mutex ; /* Protects fb-ops */
struct mx3fb_data * mx3fb ;
struct idmac_channel * idmac_channel ;
struct dma_async_tx_descriptor * txd ;
dma_cookie_t cookie ;
struct scatterlist sg [ 2 ] ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
u32 sync ; /* preserve var->sync flags */
2009-01-21 20:32:34 +03:00
} ;
static void mx3fb_dma_done ( void * ) ;
/* Used fb-mode and bpp. Can be set on kernel command line, therefore file-static. */
static const char * fb_mode ;
static unsigned long default_bpp = 16 ;
static u32 mx3fb_read_reg ( struct mx3fb_data * mx3fb , unsigned long reg )
{
2009-02-26 23:34:28 +03:00
return __raw_readl ( mx3fb - > reg_base + reg ) ;
2009-01-21 20:32:34 +03:00
}
static void mx3fb_write_reg ( struct mx3fb_data * mx3fb , u32 value , unsigned long reg )
{
2009-02-26 23:34:28 +03:00
__raw_writel ( value , mx3fb - > reg_base + reg ) ;
2009-01-21 20:32:34 +03:00
}
static const uint32_t di_mappings [ ] = {
2009-02-26 23:34:28 +03:00
0x1600AAAA , 0x00E05555 , 0x00070000 , 3 , /* RGB888 */
0x0005000F , 0x000B000F , 0x0011000F , 1 , /* RGB666 */
0x0011000F , 0x000B000F , 0x0005000F , 1 , /* BGR666 */
0x0004003F , 0x000A000F , 0x000F003F , 1 /* RGB565 */
2009-01-21 20:32:34 +03:00
} ;
static void sdc_fb_init ( struct mx3fb_info * fbi )
{
2009-02-26 23:34:28 +03:00
struct mx3fb_data * mx3fb = fbi - > mx3fb ;
uint32_t reg ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
reg = mx3fb_read_reg ( mx3fb , SDC_COM_CONF ) ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
mx3fb_write_reg ( mx3fb , reg | SDC_COM_BG_EN , SDC_COM_CONF ) ;
2009-01-21 20:32:34 +03:00
}
/* Returns enabled flag before uninit */
static uint32_t sdc_fb_uninit ( struct mx3fb_info * fbi )
{
2009-02-26 23:34:28 +03:00
struct mx3fb_data * mx3fb = fbi - > mx3fb ;
uint32_t reg ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
reg = mx3fb_read_reg ( mx3fb , SDC_COM_CONF ) ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
mx3fb_write_reg ( mx3fb , reg & ~ SDC_COM_BG_EN , SDC_COM_CONF ) ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
return reg & SDC_COM_BG_EN ;
2009-01-21 20:32:34 +03:00
}
static void sdc_enable_channel ( struct mx3fb_info * mx3_fbi )
{
2009-02-26 23:34:28 +03:00
struct mx3fb_data * mx3fb = mx3_fbi - > mx3fb ;
struct idmac_channel * ichan = mx3_fbi - > idmac_channel ;
struct dma_chan * dma_chan = & ichan - > dma_chan ;
unsigned long flags ;
dma_cookie_t cookie ;
2010-02-03 00:43:59 +03:00
if ( mx3_fbi - > txd )
dev_dbg ( mx3fb - > dev , " mx3fbi %p, desc %p, sg %p \n " , mx3_fbi ,
to_tx_desc ( mx3_fbi - > txd ) , to_tx_desc ( mx3_fbi - > txd ) - > sg ) ;
else
dev_dbg ( mx3fb - > dev , " mx3fbi %p, txd = NULL \n " , mx3_fbi ) ;
2009-02-26 23:34:28 +03:00
/* This enables the channel */
if ( mx3_fbi - > cookie < 0 ) {
mx3_fbi - > txd = dma_chan - > device - > device_prep_slave_sg ( dma_chan ,
& mx3_fbi - > sg [ 0 ] , 1 , DMA_TO_DEVICE , DMA_PREP_INTERRUPT ) ;
if ( ! mx3_fbi - > txd ) {
dev_err ( mx3fb - > dev , " Cannot allocate descriptor on %d \n " ,
dma_chan - > chan_id ) ;
return ;
}
mx3_fbi - > txd - > callback_param = mx3_fbi - > txd ;
mx3_fbi - > txd - > callback = mx3fb_dma_done ;
cookie = mx3_fbi - > txd - > tx_submit ( mx3_fbi - > txd ) ;
dev_dbg ( mx3fb - > dev , " %d: Submit %p #%d [%c] \n " , __LINE__ ,
mx3_fbi - > txd , cookie , list_empty ( & ichan - > queue ) ? ' - ' : ' + ' ) ;
} else {
if ( ! mx3_fbi - > txd | | ! mx3_fbi - > txd - > tx_submit ) {
dev_err ( mx3fb - > dev , " Cannot enable channel %d \n " ,
dma_chan - > chan_id ) ;
return ;
}
/* Just re-activate the same buffer */
dma_async_issue_pending ( dma_chan ) ;
cookie = mx3_fbi - > cookie ;
dev_dbg ( mx3fb - > dev , " %d: Re-submit %p #%d [%c] \n " , __LINE__ ,
mx3_fbi - > txd , cookie , list_empty ( & ichan - > queue ) ? ' - ' : ' + ' ) ;
}
if ( cookie > = 0 ) {
spin_lock_irqsave ( & mx3fb - > lock , flags ) ;
sdc_fb_init ( mx3_fbi ) ;
mx3_fbi - > cookie = cookie ;
spin_unlock_irqrestore ( & mx3fb - > lock , flags ) ;
}
/*
* Attention ! Without this msleep the channel keeps generating
* interrupts . Next sdc_set_brightness ( ) is going to be called
* from mx3fb_blank ( ) .
*/
msleep ( 2 ) ;
2009-01-21 20:32:34 +03:00
}
static void sdc_disable_channel ( struct mx3fb_info * mx3_fbi )
{
2009-02-26 23:34:28 +03:00
struct mx3fb_data * mx3fb = mx3_fbi - > mx3fb ;
uint32_t enabled ;
unsigned long flags ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
spin_lock_irqsave ( & mx3fb - > lock , flags ) ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
enabled = sdc_fb_uninit ( mx3_fbi ) ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
spin_unlock_irqrestore ( & mx3fb - > lock , flags ) ;
2009-01-21 20:32:34 +03:00
2010-03-27 02:44:01 +03:00
mx3_fbi - > txd - > chan - > device - > device_control ( mx3_fbi - > txd - > chan ,
2010-05-18 03:30:42 +04:00
DMA_TERMINATE_ALL , 0 ) ;
2009-02-26 23:34:28 +03:00
mx3_fbi - > txd = NULL ;
mx3_fbi - > cookie = - EINVAL ;
2009-01-21 20:32:34 +03:00
}
/**
* sdc_set_window_pos ( ) - set window position of the respective plane .
2009-02-26 23:34:28 +03:00
* @ mx3fb : mx3fb context .
* @ channel : IPU DMAC channel ID .
* @ x_pos : X coordinate relative to the top left corner to place window at .
* @ y_pos : Y coordinate relative to the top left corner to place window at .
* @ return : 0 on success or negative error code on failure .
2009-01-21 20:32:34 +03:00
*/
static int sdc_set_window_pos ( struct mx3fb_data * mx3fb , enum ipu_channel channel ,
2009-02-26 23:34:28 +03:00
int16_t x_pos , int16_t y_pos )
2009-01-21 20:32:34 +03:00
{
2009-02-26 23:34:28 +03:00
if ( channel ! = IDMAC_SDC_0 )
return - EINVAL ;
2009-01-21 20:32:34 +03:00
2009-04-07 06:01:05 +04:00
x_pos + = mx3fb - > h_start_width ;
y_pos + = mx3fb - > v_start_width ;
2009-02-26 23:34:28 +03:00
mx3fb_write_reg ( mx3fb , ( x_pos < < 16 ) | y_pos , SDC_BG_POS ) ;
return 0 ;
2009-01-21 20:32:34 +03:00
}
/**
* sdc_init_panel ( ) - initialize a synchronous LCD panel .
2009-02-26 23:34:28 +03:00
* @ mx3fb : mx3fb context .
* @ panel : panel type .
* @ pixel_clk : desired pixel clock frequency in Hz .
* @ width : width of panel in pixels .
* @ height : height of panel in pixels .
* @ pixel_fmt : pixel format of buffer as FOURCC ASCII code .
* @ h_start_width : number of pixel clocks between the HSYNC signal pulse
* and the start of valid data .
* @ h_sync_width : width of the HSYNC signal in units of pixel clocks .
* @ h_end_width : number of pixel clocks between the end of valid data
* and the HSYNC signal for next line .
* @ v_start_width : number of lines between the VSYNC signal pulse and the
* start of valid data .
* @ v_sync_width : width of the VSYNC signal in units of lines
* @ v_end_width : number of lines between the end of valid data and the
* VSYNC signal for next frame .
* @ sig : bitfield of signal polarities for LCD interface .
* @ return : 0 on success or negative error code on failure .
2009-01-21 20:32:34 +03:00
*/
static int sdc_init_panel ( struct mx3fb_data * mx3fb , enum ipu_panel panel ,
2009-02-26 23:34:28 +03:00
uint32_t pixel_clk ,
uint16_t width , uint16_t height ,
enum pixel_fmt pixel_fmt ,
uint16_t h_start_width , uint16_t h_sync_width ,
uint16_t h_end_width , uint16_t v_start_width ,
uint16_t v_sync_width , uint16_t v_end_width ,
struct ipu_di_signal_cfg sig )
2009-01-21 20:32:34 +03:00
{
2009-02-26 23:34:28 +03:00
unsigned long lock_flags ;
uint32_t reg ;
uint32_t old_conf ;
uint32_t div ;
struct clk * ipu_clk ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
dev_dbg ( mx3fb - > dev , " panel size = %d x %d " , width , height ) ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
if ( v_sync_width = = 0 | | h_sync_width = = 0 )
return - EINVAL ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
/* Init panel size and blanking periods */
reg = ( ( uint32_t ) ( h_sync_width - 1 ) < < 26 ) |
( ( uint32_t ) ( width + h_start_width + h_end_width - 1 ) < < 16 ) ;
mx3fb_write_reg ( mx3fb , reg , SDC_HOR_CONF ) ;
2009-01-21 20:32:34 +03:00
# ifdef DEBUG
2009-02-26 23:34:28 +03:00
printk ( KERN_CONT " hor_conf %x, " , reg ) ;
2009-01-21 20:32:34 +03:00
# endif
2009-02-26 23:34:28 +03:00
reg = ( ( uint32_t ) ( v_sync_width - 1 ) < < 26 ) | SDC_V_SYNC_WIDTH_L |
( ( uint32_t ) ( height + v_start_width + v_end_width - 1 ) < < 16 ) ;
mx3fb_write_reg ( mx3fb , reg , SDC_VER_CONF ) ;
2009-01-21 20:32:34 +03:00
# ifdef DEBUG
2009-02-26 23:34:28 +03:00
printk ( KERN_CONT " ver_conf %x \n " , reg ) ;
2009-01-21 20:32:34 +03:00
# endif
2009-02-26 23:34:28 +03:00
mx3fb - > h_start_width = h_start_width ;
mx3fb - > v_start_width = v_start_width ;
switch ( panel ) {
case IPU_PANEL_SHARP_TFT :
mx3fb_write_reg ( mx3fb , 0x00FD0102L , SDC_SHARP_CONF_1 ) ;
mx3fb_write_reg ( mx3fb , 0x00F500F4L , SDC_SHARP_CONF_2 ) ;
mx3fb_write_reg ( mx3fb , SDC_COM_SHARP | SDC_COM_TFT_COLOR , SDC_COM_CONF ) ;
break ;
case IPU_PANEL_TFT :
mx3fb_write_reg ( mx3fb , SDC_COM_TFT_COLOR , SDC_COM_CONF ) ;
break ;
default :
return - EINVAL ;
}
/* Init clocking */
/*
* Calculate divider : fractional part is 4 bits so simply multiple by
* 2 ^ 4 to get fractional part , as long as we stay under ~ 250 MHz and on
* i . MX31 it ( HSP_CLK ) is < = 178 MHz . Currently 128.267 MHz
*/
2009-03-17 01:28:04 +03:00
ipu_clk = clk_get ( mx3fb - > dev , NULL ) ;
2009-04-07 06:01:05 +04:00
if ( ! IS_ERR ( ipu_clk ) ) {
div = clk_get_rate ( ipu_clk ) * 16 / pixel_clk ;
clk_put ( ipu_clk ) ;
} else {
div = 0 ;
}
2009-02-26 23:34:28 +03:00
if ( div < 0x40 ) { /* Divider less than 4 */
dev_dbg ( mx3fb - > dev ,
" InitPanel() - Pixel clock divider less than 4 \n " ) ;
div = 0x40 ;
}
2009-04-07 06:01:05 +04:00
dev_dbg ( mx3fb - > dev , " pixel clk = %u, divider %u.%u \n " ,
pixel_clk , div > > 4 , ( div & 7 ) * 125 ) ;
2009-02-26 23:34:28 +03:00
spin_lock_irqsave ( & mx3fb - > lock , lock_flags ) ;
/*
* DISP3_IF_CLK_DOWN_WR is half the divider value and 2 fraction bits
* fewer . Subtract 1 extra from DISP3_IF_CLK_DOWN_WR based on timing
* debug . DISP3_IF_CLK_UP_WR is 0
*/
mx3fb_write_reg ( mx3fb , ( ( ( div / 8 ) - 1 ) < < 22 ) | div , DI_DISP3_TIME_CONF ) ;
/* DI settings */
old_conf = mx3fb_read_reg ( mx3fb , DI_DISP_IF_CONF ) & 0x78FFFFFF ;
old_conf | = sig . datamask_en < < DI_D3_DATAMSK_SHIFT |
2009-04-07 06:01:05 +04:00
sig . clksel_en < < DI_D3_CLK_SEL_SHIFT |
sig . clkidle_en < < DI_D3_CLK_IDLE_SHIFT ;
2009-02-26 23:34:28 +03:00
mx3fb_write_reg ( mx3fb , old_conf , DI_DISP_IF_CONF ) ;
old_conf = mx3fb_read_reg ( mx3fb , DI_DISP_SIG_POL ) & 0xE0FFFFFF ;
old_conf | = sig . data_pol < < DI_D3_DATA_POL_SHIFT |
2009-04-07 06:01:05 +04:00
sig . clk_pol < < DI_D3_CLK_POL_SHIFT |
sig . enable_pol < < DI_D3_DRDY_SHARP_POL_SHIFT |
sig . Hsync_pol < < DI_D3_HSYNC_POL_SHIFT |
sig . Vsync_pol < < DI_D3_VSYNC_POL_SHIFT ;
2009-02-26 23:34:28 +03:00
mx3fb_write_reg ( mx3fb , old_conf , DI_DISP_SIG_POL ) ;
switch ( pixel_fmt ) {
case IPU_PIX_FMT_RGB24 :
mx3fb_write_reg ( mx3fb , di_mappings [ 0 ] , DI_DISP3_B0_MAP ) ;
mx3fb_write_reg ( mx3fb , di_mappings [ 1 ] , DI_DISP3_B1_MAP ) ;
mx3fb_write_reg ( mx3fb , di_mappings [ 2 ] , DI_DISP3_B2_MAP ) ;
mx3fb_write_reg ( mx3fb , mx3fb_read_reg ( mx3fb , DI_DISP_ACC_CC ) |
( ( di_mappings [ 3 ] - 1 ) < < 12 ) , DI_DISP_ACC_CC ) ;
break ;
case IPU_PIX_FMT_RGB666 :
mx3fb_write_reg ( mx3fb , di_mappings [ 4 ] , DI_DISP3_B0_MAP ) ;
mx3fb_write_reg ( mx3fb , di_mappings [ 5 ] , DI_DISP3_B1_MAP ) ;
mx3fb_write_reg ( mx3fb , di_mappings [ 6 ] , DI_DISP3_B2_MAP ) ;
mx3fb_write_reg ( mx3fb , mx3fb_read_reg ( mx3fb , DI_DISP_ACC_CC ) |
( ( di_mappings [ 7 ] - 1 ) < < 12 ) , DI_DISP_ACC_CC ) ;
break ;
case IPU_PIX_FMT_BGR666 :
mx3fb_write_reg ( mx3fb , di_mappings [ 8 ] , DI_DISP3_B0_MAP ) ;
mx3fb_write_reg ( mx3fb , di_mappings [ 9 ] , DI_DISP3_B1_MAP ) ;
mx3fb_write_reg ( mx3fb , di_mappings [ 10 ] , DI_DISP3_B2_MAP ) ;
mx3fb_write_reg ( mx3fb , mx3fb_read_reg ( mx3fb , DI_DISP_ACC_CC ) |
( ( di_mappings [ 11 ] - 1 ) < < 12 ) , DI_DISP_ACC_CC ) ;
break ;
default :
mx3fb_write_reg ( mx3fb , di_mappings [ 12 ] , DI_DISP3_B0_MAP ) ;
mx3fb_write_reg ( mx3fb , di_mappings [ 13 ] , DI_DISP3_B1_MAP ) ;
mx3fb_write_reg ( mx3fb , di_mappings [ 14 ] , DI_DISP3_B2_MAP ) ;
mx3fb_write_reg ( mx3fb , mx3fb_read_reg ( mx3fb , DI_DISP_ACC_CC ) |
( ( di_mappings [ 15 ] - 1 ) < < 12 ) , DI_DISP_ACC_CC ) ;
break ;
}
spin_unlock_irqrestore ( & mx3fb - > lock , lock_flags ) ;
dev_dbg ( mx3fb - > dev , " DI_DISP_IF_CONF = 0x%08X \n " ,
mx3fb_read_reg ( mx3fb , DI_DISP_IF_CONF ) ) ;
dev_dbg ( mx3fb - > dev , " DI_DISP_SIG_POL = 0x%08X \n " ,
mx3fb_read_reg ( mx3fb , DI_DISP_SIG_POL ) ) ;
dev_dbg ( mx3fb - > dev , " DI_DISP3_TIME_CONF = 0x%08X \n " ,
mx3fb_read_reg ( mx3fb , DI_DISP3_TIME_CONF ) ) ;
return 0 ;
2009-01-21 20:32:34 +03:00
}
/**
* sdc_set_color_key ( ) - set the transparent color key for SDC graphic plane .
2009-02-26 23:34:28 +03:00
* @ mx3fb : mx3fb context .
* @ channel : IPU DMAC channel ID .
* @ enable : boolean to enable or disable color keyl .
* @ color_key : 24 - bit RGB color to use as transparent color key .
* @ return : 0 on success or negative error code on failure .
2009-01-21 20:32:34 +03:00
*/
static int sdc_set_color_key ( struct mx3fb_data * mx3fb , enum ipu_channel channel ,
2009-02-26 23:34:28 +03:00
bool enable , uint32_t color_key )
2009-01-21 20:32:34 +03:00
{
2009-02-26 23:34:28 +03:00
uint32_t reg , sdc_conf ;
unsigned long lock_flags ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
spin_lock_irqsave ( & mx3fb - > lock , lock_flags ) ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
sdc_conf = mx3fb_read_reg ( mx3fb , SDC_COM_CONF ) ;
if ( channel = = IDMAC_SDC_0 )
sdc_conf & = ~ SDC_COM_GWSEL ;
else
sdc_conf | = SDC_COM_GWSEL ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
if ( enable ) {
reg = mx3fb_read_reg ( mx3fb , SDC_GW_CTRL ) & 0xFF000000L ;
mx3fb_write_reg ( mx3fb , reg | ( color_key & 0x00FFFFFFL ) ,
SDC_GW_CTRL ) ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
sdc_conf | = SDC_COM_KEY_COLOR_G ;
} else {
sdc_conf & = ~ SDC_COM_KEY_COLOR_G ;
}
mx3fb_write_reg ( mx3fb , sdc_conf , SDC_COM_CONF ) ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
spin_unlock_irqrestore ( & mx3fb - > lock , lock_flags ) ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
return 0 ;
2009-01-21 20:32:34 +03:00
}
/**
* sdc_set_global_alpha ( ) - set global alpha blending modes .
2009-02-26 23:34:28 +03:00
* @ mx3fb : mx3fb context .
* @ enable : boolean to enable or disable global alpha blending . If disabled ,
* per pixel blending is used .
* @ alpha : global alpha value .
* @ return : 0 on success or negative error code on failure .
2009-01-21 20:32:34 +03:00
*/
static int sdc_set_global_alpha ( struct mx3fb_data * mx3fb , bool enable , uint8_t alpha )
{
2009-02-26 23:34:28 +03:00
uint32_t reg ;
unsigned long lock_flags ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
spin_lock_irqsave ( & mx3fb - > lock , lock_flags ) ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
if ( enable ) {
reg = mx3fb_read_reg ( mx3fb , SDC_GW_CTRL ) & 0x00FFFFFFL ;
mx3fb_write_reg ( mx3fb , reg | ( ( uint32_t ) alpha < < 24 ) , SDC_GW_CTRL ) ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
reg = mx3fb_read_reg ( mx3fb , SDC_COM_CONF ) ;
mx3fb_write_reg ( mx3fb , reg | SDC_COM_GLB_A , SDC_COM_CONF ) ;
} else {
reg = mx3fb_read_reg ( mx3fb , SDC_COM_CONF ) ;
mx3fb_write_reg ( mx3fb , reg & ~ SDC_COM_GLB_A , SDC_COM_CONF ) ;
}
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
spin_unlock_irqrestore ( & mx3fb - > lock , lock_flags ) ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
return 0 ;
2009-01-21 20:32:34 +03:00
}
static void sdc_set_brightness ( struct mx3fb_data * mx3fb , uint8_t value )
{
2010-02-03 00:43:59 +03:00
dev_dbg ( mx3fb - > dev , " %s: value = %d \n " , __func__ , value ) ;
2009-02-26 23:34:28 +03:00
/* This might be board-specific */
mx3fb_write_reg ( mx3fb , 0x03000000UL | value < < 16 , SDC_PWM_CTRL ) ;
return ;
2009-01-21 20:32:34 +03:00
}
static uint32_t bpp_to_pixfmt ( int bpp )
{
2009-02-26 23:34:28 +03:00
uint32_t pixfmt = 0 ;
switch ( bpp ) {
case 24 :
pixfmt = IPU_PIX_FMT_BGR24 ;
break ;
case 32 :
pixfmt = IPU_PIX_FMT_BGR32 ;
break ;
case 16 :
pixfmt = IPU_PIX_FMT_RGB565 ;
break ;
}
return pixfmt ;
2009-01-21 20:32:34 +03:00
}
static int mx3fb_blank ( int blank , struct fb_info * fbi ) ;
2009-08-07 02:07:40 +04:00
static int mx3fb_map_video_memory ( struct fb_info * fbi , unsigned int mem_len ,
bool lock ) ;
2009-01-21 20:32:34 +03:00
static int mx3fb_unmap_video_memory ( struct fb_info * fbi ) ;
/**
* mx3fb_set_fix ( ) - set fixed framebuffer parameters from variable settings .
2009-02-26 23:34:28 +03:00
* @ info : framebuffer information pointer
* @ return : 0 on success or negative error code on failure .
2009-01-21 20:32:34 +03:00
*/
static int mx3fb_set_fix ( struct fb_info * fbi )
{
2009-02-26 23:34:28 +03:00
struct fb_fix_screeninfo * fix = & fbi - > fix ;
struct fb_var_screeninfo * var = & fbi - > var ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
strncpy ( fix - > id , " DISP3 BG " , 8 ) ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
fix - > line_length = var - > xres_virtual * var - > bits_per_pixel / 8 ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
fix - > type = FB_TYPE_PACKED_PIXELS ;
fix - > accel = FB_ACCEL_NONE ;
fix - > visual = FB_VISUAL_TRUECOLOR ;
fix - > xpanstep = 1 ;
fix - > ypanstep = 1 ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
return 0 ;
2009-01-21 20:32:34 +03:00
}
static void mx3fb_dma_done ( void * arg )
{
2009-02-26 23:34:28 +03:00
struct idmac_tx_desc * tx_desc = to_tx_desc ( arg ) ;
struct dma_chan * chan = tx_desc - > txd . chan ;
struct idmac_channel * ichannel = to_idmac_chan ( chan ) ;
struct mx3fb_data * mx3fb = ichannel - > client ;
struct mx3fb_info * mx3_fbi = mx3fb - > fbi - > par ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
dev_dbg ( mx3fb - > dev , " irq %d callback \n " , ichannel - > eof_irq ) ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
/* We only need one interrupt, it will be re-enabled as needed */
2009-05-12 23:41:03 +04:00
disable_irq_nosync ( ichannel - > eof_irq ) ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
complete ( & mx3_fbi - > flip_cmpl ) ;
2009-01-21 20:32:34 +03:00
}
2009-08-07 02:07:40 +04:00
static int __set_par ( struct fb_info * fbi , bool lock )
2009-01-21 20:32:34 +03:00
{
2009-02-26 23:34:28 +03:00
u32 mem_len ;
struct ipu_di_signal_cfg sig_cfg ;
enum ipu_panel mode = IPU_PANEL_TFT ;
struct mx3fb_info * mx3_fbi = fbi - > par ;
struct mx3fb_data * mx3fb = mx3_fbi - > mx3fb ;
struct idmac_channel * ichan = mx3_fbi - > idmac_channel ;
struct idmac_video_param * video = & ichan - > params . video ;
struct scatterlist * sg = mx3_fbi - > sg ;
/* Total cleanup */
if ( mx3_fbi - > txd )
sdc_disable_channel ( mx3_fbi ) ;
mx3fb_set_fix ( fbi ) ;
mem_len = fbi - > var . yres_virtual * fbi - > fix . line_length ;
if ( mem_len > fbi - > fix . smem_len ) {
if ( fbi - > fix . smem_start )
mx3fb_unmap_video_memory ( fbi ) ;
2009-08-07 02:07:40 +04:00
if ( mx3fb_map_video_memory ( fbi , mem_len , lock ) < 0 )
2009-02-26 23:34:28 +03:00
return - ENOMEM ;
}
sg_init_table ( & sg [ 0 ] , 1 ) ;
sg_init_table ( & sg [ 1 ] , 1 ) ;
2009-04-07 06:01:05 +04:00
sg_dma_address ( & sg [ 0 ] ) = fbi - > fix . smem_start ;
2009-02-26 23:34:28 +03:00
sg_set_page ( & sg [ 0 ] , virt_to_page ( fbi - > screen_base ) ,
fbi - > fix . smem_len ,
offset_in_page ( fbi - > screen_base ) ) ;
if ( mx3_fbi - > ipu_ch = = IDMAC_SDC_0 ) {
memset ( & sig_cfg , 0 , sizeof ( sig_cfg ) ) ;
if ( fbi - > var . sync & FB_SYNC_HOR_HIGH_ACT )
sig_cfg . Hsync_pol = true ;
if ( fbi - > var . sync & FB_SYNC_VERT_HIGH_ACT )
sig_cfg . Vsync_pol = true ;
if ( fbi - > var . sync & FB_SYNC_CLK_INVERT )
sig_cfg . clk_pol = true ;
if ( fbi - > var . sync & FB_SYNC_DATA_INVERT )
sig_cfg . data_pol = true ;
if ( fbi - > var . sync & FB_SYNC_OE_ACT_HIGH )
sig_cfg . enable_pol = true ;
if ( fbi - > var . sync & FB_SYNC_CLK_IDLE_EN )
sig_cfg . clkidle_en = true ;
if ( fbi - > var . sync & FB_SYNC_CLK_SEL_EN )
sig_cfg . clksel_en = true ;
if ( fbi - > var . sync & FB_SYNC_SHARP_MODE )
mode = IPU_PANEL_SHARP_TFT ;
dev_dbg ( fbi - > device , " pixclock = %ul Hz \n " ,
( u32 ) ( PICOS2KHZ ( fbi - > var . pixclock ) * 1000UL ) ) ;
if ( sdc_init_panel ( mx3fb , mode ,
( PICOS2KHZ ( fbi - > var . pixclock ) ) * 1000UL ,
fbi - > var . xres , fbi - > var . yres ,
( fbi - > var . sync & FB_SYNC_SWAP_RGB ) ?
IPU_PIX_FMT_BGR666 : IPU_PIX_FMT_RGB666 ,
fbi - > var . left_margin ,
fbi - > var . hsync_len ,
fbi - > var . right_margin +
fbi - > var . hsync_len ,
fbi - > var . upper_margin ,
fbi - > var . vsync_len ,
fbi - > var . lower_margin +
fbi - > var . vsync_len , sig_cfg ) ! = 0 ) {
dev_err ( fbi - > device ,
" mx3fb: Error initializing panel. \n " ) ;
return - EINVAL ;
}
}
sdc_set_window_pos ( mx3fb , mx3_fbi - > ipu_ch , 0 , 0 ) ;
mx3_fbi - > cur_ipu_buf = 0 ;
video - > out_pixel_fmt = bpp_to_pixfmt ( fbi - > var . bits_per_pixel ) ;
video - > out_width = fbi - > var . xres ;
video - > out_height = fbi - > var . yres ;
video - > out_stride = fbi - > var . xres_virtual ;
if ( mx3_fbi - > blank = = FB_BLANK_UNBLANK )
sdc_enable_channel ( mx3_fbi ) ;
2009-08-07 02:07:40 +04:00
return 0 ;
}
/**
* mx3fb_set_par ( ) - set framebuffer parameters and change the operating mode .
* @ fbi : framebuffer information pointer .
* @ return : 0 on success or negative error code on failure .
*/
static int mx3fb_set_par ( struct fb_info * fbi )
{
struct mx3fb_info * mx3_fbi = fbi - > par ;
struct mx3fb_data * mx3fb = mx3_fbi - > mx3fb ;
struct idmac_channel * ichan = mx3_fbi - > idmac_channel ;
int ret ;
dev_dbg ( mx3fb - > dev , " %s [%c] \n " , __func__ , list_empty ( & ichan - > queue ) ? ' - ' : ' + ' ) ;
mutex_lock ( & mx3_fbi - > mutex ) ;
ret = __set_par ( fbi , true ) ;
2009-02-26 23:34:28 +03:00
mutex_unlock ( & mx3_fbi - > mutex ) ;
2009-08-07 02:07:40 +04:00
return ret ;
2009-01-21 20:32:34 +03:00
}
/**
* mx3fb_check_var ( ) - check and adjust framebuffer variable parameters .
2009-02-26 23:34:28 +03:00
* @ var : framebuffer variable parameters
* @ fbi : framebuffer information pointer
2009-01-21 20:32:34 +03:00
*/
static int mx3fb_check_var ( struct fb_var_screeninfo * var , struct fb_info * fbi )
{
2009-02-26 23:34:28 +03:00
struct mx3fb_info * mx3_fbi = fbi - > par ;
u32 vtotal ;
u32 htotal ;
dev_dbg ( fbi - > device , " %s \n " , __func__ ) ;
if ( var - > xres_virtual < var - > xres )
var - > xres_virtual = var - > xres ;
if ( var - > yres_virtual < var - > yres )
var - > yres_virtual = var - > yres ;
if ( ( var - > bits_per_pixel ! = 32 ) & & ( var - > bits_per_pixel ! = 24 ) & &
( var - > bits_per_pixel ! = 16 ) )
var - > bits_per_pixel = default_bpp ;
switch ( var - > bits_per_pixel ) {
case 16 :
var - > red . length = 5 ;
var - > red . offset = 11 ;
var - > red . msb_right = 0 ;
var - > green . length = 6 ;
var - > green . offset = 5 ;
var - > green . msb_right = 0 ;
var - > blue . length = 5 ;
var - > blue . offset = 0 ;
var - > blue . msb_right = 0 ;
var - > transp . length = 0 ;
var - > transp . offset = 0 ;
var - > transp . msb_right = 0 ;
break ;
case 24 :
var - > red . length = 8 ;
var - > red . offset = 16 ;
var - > red . msb_right = 0 ;
var - > green . length = 8 ;
var - > green . offset = 8 ;
var - > green . msb_right = 0 ;
var - > blue . length = 8 ;
var - > blue . offset = 0 ;
var - > blue . msb_right = 0 ;
var - > transp . length = 0 ;
var - > transp . offset = 0 ;
var - > transp . msb_right = 0 ;
break ;
case 32 :
var - > red . length = 8 ;
var - > red . offset = 16 ;
var - > red . msb_right = 0 ;
var - > green . length = 8 ;
var - > green . offset = 8 ;
var - > green . msb_right = 0 ;
var - > blue . length = 8 ;
var - > blue . offset = 0 ;
var - > blue . msb_right = 0 ;
var - > transp . length = 8 ;
var - > transp . offset = 24 ;
var - > transp . msb_right = 0 ;
break ;
}
if ( var - > pixclock < 1000 ) {
htotal = var - > xres + var - > right_margin + var - > hsync_len +
var - > left_margin ;
vtotal = var - > yres + var - > lower_margin + var - > vsync_len +
var - > upper_margin ;
var - > pixclock = ( vtotal * htotal * 6UL ) / 100UL ;
var - > pixclock = KHZ2PICOS ( var - > pixclock ) ;
dev_dbg ( fbi - > device , " pixclock set for 60Hz refresh = %u ps \n " ,
var - > pixclock ) ;
}
var - > height = - 1 ;
var - > width = - 1 ;
var - > grayscale = 0 ;
/* Preserve sync flags */
var - > sync | = mx3_fbi - > sync ;
mx3_fbi - > sync | = var - > sync ;
return 0 ;
2009-01-21 20:32:34 +03:00
}
static u32 chan_to_field ( unsigned int chan , struct fb_bitfield * bf )
{
2009-02-26 23:34:28 +03:00
chan & = 0xffff ;
chan > > = 16 - bf - > length ;
return chan < < bf - > offset ;
2009-01-21 20:32:34 +03:00
}
static int mx3fb_setcolreg ( unsigned int regno , unsigned int red ,
2009-02-26 23:34:28 +03:00
unsigned int green , unsigned int blue ,
unsigned int trans , struct fb_info * fbi )
2009-01-21 20:32:34 +03:00
{
2009-02-26 23:34:28 +03:00
struct mx3fb_info * mx3_fbi = fbi - > par ;
u32 val ;
int ret = 1 ;
2009-04-07 06:01:05 +04:00
dev_dbg ( fbi - > device , " %s, regno = %u \n " , __func__ , regno ) ;
2009-02-26 23:34:28 +03:00
mutex_lock ( & mx3_fbi - > mutex ) ;
/*
* If greyscale is true , then we convert the RGB value
* to greyscale no matter what visual we are using .
*/
if ( fbi - > var . grayscale )
red = green = blue = ( 19595 * red + 38470 * green +
7471 * blue ) > > 16 ;
switch ( fbi - > fix . visual ) {
case FB_VISUAL_TRUECOLOR :
/*
* 16 - bit True Colour . We encode the RGB value
* according to the RGB bitfield information .
*/
if ( regno < 16 ) {
u32 * pal = fbi - > pseudo_palette ;
val = chan_to_field ( red , & fbi - > var . red ) ;
val | = chan_to_field ( green , & fbi - > var . green ) ;
val | = chan_to_field ( blue , & fbi - > var . blue ) ;
pal [ regno ] = val ;
ret = 0 ;
}
break ;
case FB_VISUAL_STATIC_PSEUDOCOLOR :
case FB_VISUAL_PSEUDOCOLOR :
break ;
}
mutex_unlock ( & mx3_fbi - > mutex ) ;
return ret ;
2009-01-21 20:32:34 +03:00
}
2009-08-07 02:07:40 +04:00
static void __blank ( int blank , struct fb_info * fbi )
2009-01-21 20:32:34 +03:00
{
2009-02-26 23:34:28 +03:00
struct mx3fb_info * mx3_fbi = fbi - > par ;
struct mx3fb_data * mx3fb = mx3_fbi - > mx3fb ;
mx3_fbi - > blank = blank ;
switch ( blank ) {
case FB_BLANK_POWERDOWN :
case FB_BLANK_VSYNC_SUSPEND :
case FB_BLANK_HSYNC_SUSPEND :
case FB_BLANK_NORMAL :
sdc_set_brightness ( mx3fb , 0 ) ;
2009-04-07 06:01:05 +04:00
memset ( ( char * ) fbi - > screen_base , 0 , fbi - > fix . smem_len ) ;
/* Give LCD time to update - enough for 50 and 60 Hz */
msleep ( 25 ) ;
sdc_disable_channel ( mx3_fbi ) ;
2009-02-26 23:34:28 +03:00
break ;
case FB_BLANK_UNBLANK :
sdc_enable_channel ( mx3_fbi ) ;
sdc_set_brightness ( mx3fb , mx3fb - > backlight_level ) ;
break ;
}
2009-08-07 02:07:40 +04:00
}
/**
* mx3fb_blank ( ) - blank the display .
*/
static int mx3fb_blank ( int blank , struct fb_info * fbi )
{
struct mx3fb_info * mx3_fbi = fbi - > par ;
dev_dbg ( fbi - > device , " %s, blank = %d, base %p, len %u \n " , __func__ ,
blank , fbi - > screen_base , fbi - > fix . smem_len ) ;
if ( mx3_fbi - > blank = = blank )
return 0 ;
mutex_lock ( & mx3_fbi - > mutex ) ;
__blank ( blank , fbi ) ;
2009-02-26 23:34:28 +03:00
mutex_unlock ( & mx3_fbi - > mutex ) ;
return 0 ;
2009-01-21 20:32:34 +03:00
}
/**
* mx3fb_pan_display ( ) - pan or wrap the display
2009-02-26 23:34:28 +03:00
* @ var : variable screen buffer information .
* @ info : framebuffer information pointer .
2009-01-21 20:32:34 +03:00
*
* We look only at xoffset , yoffset and the FB_VMODE_YWRAP flag
*/
static int mx3fb_pan_display ( struct fb_var_screeninfo * var ,
2009-02-26 23:34:28 +03:00
struct fb_info * fbi )
2009-01-21 20:32:34 +03:00
{
2009-02-26 23:34:28 +03:00
struct mx3fb_info * mx3_fbi = fbi - > par ;
u32 y_bottom ;
unsigned long base ;
off_t offset ;
dma_cookie_t cookie ;
struct scatterlist * sg = mx3_fbi - > sg ;
struct dma_chan * dma_chan = & mx3_fbi - > idmac_channel - > dma_chan ;
struct dma_async_tx_descriptor * txd ;
int ret ;
dev_dbg ( fbi - > device , " %s [%c] \n " , __func__ ,
list_empty ( & mx3_fbi - > idmac_channel - > queue ) ? ' - ' : ' + ' ) ;
if ( var - > xoffset > 0 ) {
dev_dbg ( fbi - > device , " x panning not supported \n " ) ;
return - EINVAL ;
}
if ( fbi - > var . xoffset = = var - > xoffset & &
fbi - > var . yoffset = = var - > yoffset )
return 0 ; /* No change, do nothing */
y_bottom = var - > yoffset ;
if ( ! ( var - > vmode & FB_VMODE_YWRAP ) )
y_bottom + = var - > yres ;
if ( y_bottom > fbi - > var . yres_virtual )
return - EINVAL ;
mutex_lock ( & mx3_fbi - > mutex ) ;
offset = ( var - > yoffset * var - > xres_virtual + var - > xoffset ) *
( var - > bits_per_pixel / 8 ) ;
base = fbi - > fix . smem_start + offset ;
dev_dbg ( fbi - > device , " Updating SDC BG buf %d address=0x%08lX \n " ,
mx3_fbi - > cur_ipu_buf , base ) ;
/*
* We enable the End of Frame interrupt , which will free a tx - descriptor ,
* which we will need for the next device_prep_slave_sg ( ) . The
* IRQ - handler will disable the IRQ again .
*/
init_completion ( & mx3_fbi - > flip_cmpl ) ;
enable_irq ( mx3_fbi - > idmac_channel - > eof_irq ) ;
ret = wait_for_completion_timeout ( & mx3_fbi - > flip_cmpl , HZ / 10 ) ;
if ( ret < = 0 ) {
mutex_unlock ( & mx3_fbi - > mutex ) ;
dev_info ( fbi - > device , " Panning failed due to %s \n " , ret < 0 ?
" user interrupt " : " timeout " ) ;
2009-04-07 06:01:05 +04:00
disable_irq ( mx3_fbi - > idmac_channel - > eof_irq ) ;
2009-02-26 23:34:28 +03:00
return ret ? : - ETIMEDOUT ;
}
mx3_fbi - > cur_ipu_buf = ! mx3_fbi - > cur_ipu_buf ;
sg_dma_address ( & sg [ mx3_fbi - > cur_ipu_buf ] ) = base ;
sg_set_page ( & sg [ mx3_fbi - > cur_ipu_buf ] ,
virt_to_page ( fbi - > screen_base + offset ) , fbi - > fix . smem_len ,
offset_in_page ( fbi - > screen_base + offset ) ) ;
2009-04-07 06:01:05 +04:00
if ( mx3_fbi - > txd )
async_tx_ack ( mx3_fbi - > txd ) ;
2009-02-26 23:34:28 +03:00
txd = dma_chan - > device - > device_prep_slave_sg ( dma_chan , sg +
mx3_fbi - > cur_ipu_buf , 1 , DMA_TO_DEVICE , DMA_PREP_INTERRUPT ) ;
if ( ! txd ) {
dev_err ( fbi - > device ,
" Error preparing a DMA transaction descriptor. \n " ) ;
mutex_unlock ( & mx3_fbi - > mutex ) ;
return - EIO ;
}
txd - > callback_param = txd ;
txd - > callback = mx3fb_dma_done ;
/*
* Emulate original mx3fb behaviour : each new call to idmac_tx_submit ( )
* should switch to another buffer
*/
cookie = txd - > tx_submit ( txd ) ;
dev_dbg ( fbi - > device , " %d: Submit %p #%d \n " , __LINE__ , txd , cookie ) ;
if ( cookie < 0 ) {
dev_err ( fbi - > device ,
" Error updating SDC buf %d to address=0x%08lX \n " ,
mx3_fbi - > cur_ipu_buf , base ) ;
mutex_unlock ( & mx3_fbi - > mutex ) ;
return - EIO ;
}
mx3_fbi - > txd = txd ;
fbi - > var . xoffset = var - > xoffset ;
fbi - > var . yoffset = var - > yoffset ;
if ( var - > vmode & FB_VMODE_YWRAP )
fbi - > var . vmode | = FB_VMODE_YWRAP ;
else
fbi - > var . vmode & = ~ FB_VMODE_YWRAP ;
mutex_unlock ( & mx3_fbi - > mutex ) ;
dev_dbg ( fbi - > device , " Update complete \n " ) ;
return 0 ;
2009-01-21 20:32:34 +03:00
}
/*
* This structure contains the pointers to the control functions that are
* invoked by the core framebuffer driver to perform operations like
* blitting , rectangle filling , copy regions and cursor definition .
*/
static struct fb_ops mx3fb_ops = {
2009-02-26 23:34:28 +03:00
. owner = THIS_MODULE ,
. fb_set_par = mx3fb_set_par ,
. fb_check_var = mx3fb_check_var ,
. fb_setcolreg = mx3fb_setcolreg ,
. fb_pan_display = mx3fb_pan_display ,
. fb_fillrect = cfb_fillrect ,
. fb_copyarea = cfb_copyarea ,
. fb_imageblit = cfb_imageblit ,
. fb_blank = mx3fb_blank ,
2009-01-21 20:32:34 +03:00
} ;
# ifdef CONFIG_PM
/*
* Power management hooks . Note that we won ' t be called from IRQ context ,
* unlike the blank functions above , so we may sleep .
*/
/*
* Suspends the framebuffer and blanks the screen . Power management support
*/
static int mx3fb_suspend ( struct platform_device * pdev , pm_message_t state )
{
2009-04-08 13:45:47 +04:00
struct mx3fb_data * mx3fb = platform_get_drvdata ( pdev ) ;
struct mx3fb_info * mx3_fbi = mx3fb - > fbi - > par ;
2009-01-21 20:32:34 +03:00
2011-01-26 02:07:35 +03:00
console_lock ( ) ;
2009-04-08 13:45:47 +04:00
fb_set_suspend ( mx3fb - > fbi , 1 ) ;
2011-01-26 02:07:35 +03:00
console_unlock ( ) ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
if ( mx3_fbi - > blank = = FB_BLANK_UNBLANK ) {
sdc_disable_channel ( mx3_fbi ) ;
sdc_set_brightness ( mx3fb , 0 ) ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
}
return 0 ;
2009-01-21 20:32:34 +03:00
}
/*
* Resumes the framebuffer and unblanks the screen . Power management support
*/
static int mx3fb_resume ( struct platform_device * pdev )
{
2009-04-08 13:45:47 +04:00
struct mx3fb_data * mx3fb = platform_get_drvdata ( pdev ) ;
struct mx3fb_info * mx3_fbi = mx3fb - > fbi - > par ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
if ( mx3_fbi - > blank = = FB_BLANK_UNBLANK ) {
sdc_enable_channel ( mx3_fbi ) ;
2009-04-08 13:45:47 +04:00
sdc_set_brightness ( mx3fb , mx3fb - > backlight_level ) ;
2009-02-26 23:34:28 +03:00
}
2009-01-21 20:32:34 +03:00
2011-01-26 02:07:35 +03:00
console_lock ( ) ;
2009-04-08 13:45:47 +04:00
fb_set_suspend ( mx3fb - > fbi , 0 ) ;
2011-01-26 02:07:35 +03:00
console_unlock ( ) ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
return 0 ;
2009-01-21 20:32:34 +03:00
}
# else
# define mx3fb_suspend NULL
# define mx3fb_resume NULL
# endif
/*
* Main framebuffer functions
*/
/**
* mx3fb_map_video_memory ( ) - allocates the DRAM memory for the frame buffer .
2009-02-26 23:34:28 +03:00
* @ fbi : framebuffer information pointer
2009-06-30 22:41:29 +04:00
* @ mem_len : length of mapped memory
2009-08-07 02:07:40 +04:00
* @ lock : do not lock during initialisation
2009-02-26 23:34:28 +03:00
* @ return : Error code indicating success or failure
2009-01-21 20:32:34 +03:00
*
* This buffer is remapped into a non - cached , non - buffered , memory region to
* allow palette and pixel writes to occur without flushing the cache . Once this
* area is remapped , all virtual memory access to the video memory should occur
* at the new region .
*/
2009-08-07 02:07:40 +04:00
static int mx3fb_map_video_memory ( struct fb_info * fbi , unsigned int mem_len ,
bool lock )
2009-01-21 20:32:34 +03:00
{
2009-02-26 23:34:28 +03:00
int retval = 0 ;
dma_addr_t addr ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
fbi - > screen_base = dma_alloc_writecombine ( fbi - > device ,
2009-06-30 22:41:29 +04:00
mem_len ,
2009-02-26 23:34:28 +03:00
& addr , GFP_DMA ) ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
if ( ! fbi - > screen_base ) {
dev_err ( fbi - > device , " Cannot allocate %u bytes framebuffer memory \n " ,
2009-06-30 22:41:29 +04:00
mem_len ) ;
2009-02-26 23:34:28 +03:00
retval = - EBUSY ;
goto err0 ;
}
2009-01-21 20:32:34 +03:00
2009-08-07 02:07:40 +04:00
if ( lock )
mutex_lock ( & fbi - > mm_lock ) ;
2009-02-26 23:34:28 +03:00
fbi - > fix . smem_start = addr ;
2009-06-30 22:41:29 +04:00
fbi - > fix . smem_len = mem_len ;
2009-08-07 02:07:40 +04:00
if ( lock )
mutex_unlock ( & fbi - > mm_lock ) ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
dev_dbg ( fbi - > device , " allocated fb @ p=0x%08x, v=0x%p, size=%d. \n " ,
( uint32_t ) fbi - > fix . smem_start , fbi - > screen_base , fbi - > fix . smem_len ) ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
fbi - > screen_size = fbi - > fix . smem_len ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
/* Clear the screen */
memset ( ( char * ) fbi - > screen_base , 0 , fbi - > fix . smem_len ) ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
return 0 ;
2009-01-21 20:32:34 +03:00
err0 :
2009-02-26 23:34:28 +03:00
fbi - > fix . smem_len = 0 ;
fbi - > fix . smem_start = 0 ;
fbi - > screen_base = NULL ;
return retval ;
2009-01-21 20:32:34 +03:00
}
/**
* mx3fb_unmap_video_memory ( ) - de - allocate frame buffer memory .
2009-02-26 23:34:28 +03:00
* @ fbi : framebuffer information pointer
* @ return : error code indicating success or failure
2009-01-21 20:32:34 +03:00
*/
static int mx3fb_unmap_video_memory ( struct fb_info * fbi )
{
2009-02-26 23:34:28 +03:00
dma_free_writecombine ( fbi - > device , fbi - > fix . smem_len ,
fbi - > screen_base , fbi - > fix . smem_start ) ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
fbi - > screen_base = 0 ;
2009-06-30 22:41:29 +04:00
mutex_lock ( & fbi - > mm_lock ) ;
2009-02-26 23:34:28 +03:00
fbi - > fix . smem_start = 0 ;
fbi - > fix . smem_len = 0 ;
2009-06-30 22:41:29 +04:00
mutex_unlock ( & fbi - > mm_lock ) ;
2009-02-26 23:34:28 +03:00
return 0 ;
2009-01-21 20:32:34 +03:00
}
/**
* mx3fb_init_fbinfo ( ) - initialize framebuffer information object .
2009-02-26 23:34:28 +03:00
* @ return : initialized framebuffer structure .
2009-01-21 20:32:34 +03:00
*/
static struct fb_info * mx3fb_init_fbinfo ( struct device * dev , struct fb_ops * ops )
{
2009-02-26 23:34:28 +03:00
struct fb_info * fbi ;
struct mx3fb_info * mx3fbi ;
int ret ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
/* Allocate sufficient memory for the fb structure */
fbi = framebuffer_alloc ( sizeof ( struct mx3fb_info ) , dev ) ;
if ( ! fbi )
return NULL ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
mx3fbi = fbi - > par ;
mx3fbi - > cookie = - EINVAL ;
mx3fbi - > cur_ipu_buf = 0 ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
fbi - > var . activate = FB_ACTIVATE_NOW ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
fbi - > fbops = ops ;
fbi - > flags = FBINFO_FLAG_DEFAULT ;
fbi - > pseudo_palette = mx3fbi - > pseudo_palette ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
mutex_init ( & mx3fbi - > mutex ) ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
/* Allocate colormap */
ret = fb_alloc_cmap ( & fbi - > cmap , 16 , 0 ) ;
if ( ret < 0 ) {
framebuffer_release ( fbi ) ;
return NULL ;
}
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
return fbi ;
2009-01-21 20:32:34 +03:00
}
static int init_fb_chan ( struct mx3fb_data * mx3fb , struct idmac_channel * ichan )
{
2009-02-26 23:34:28 +03:00
struct device * dev = mx3fb - > dev ;
struct mx3fb_platform_data * mx3fb_pdata = dev - > platform_data ;
const char * name = mx3fb_pdata - > name ;
unsigned int irq ;
struct fb_info * fbi ;
struct mx3fb_info * mx3fbi ;
const struct fb_videomode * mode ;
int ret , num_modes ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
ichan - > client = mx3fb ;
irq = ichan - > eof_irq ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
if ( ichan - > dma_chan . chan_id ! = IDMAC_SDC_0 )
return - EINVAL ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
fbi = mx3fb_init_fbinfo ( dev , & mx3fb_ops ) ;
if ( ! fbi )
return - ENOMEM ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
if ( ! fb_mode )
fb_mode = name ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
if ( ! fb_mode ) {
ret = - EINVAL ;
goto emode ;
}
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
if ( mx3fb_pdata - > mode & & mx3fb_pdata - > num_modes ) {
mode = mx3fb_pdata - > mode ;
num_modes = mx3fb_pdata - > num_modes ;
} else {
mode = mx3fb_modedb ;
num_modes = ARRAY_SIZE ( mx3fb_modedb ) ;
}
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
if ( ! fb_find_mode ( & fbi - > var , fbi , fb_mode , mode ,
num_modes , NULL , default_bpp ) ) {
ret = - EBUSY ;
goto emode ;
}
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
fb_videomode_to_modelist ( mode , num_modes , & fbi - > modelist ) ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
/* Default Y virtual size is 2x panel size */
fbi - > var . yres_virtual = fbi - > var . yres * 2 ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
mx3fb - > fbi = fbi ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
/* set Display Interface clock period */
mx3fb_write_reg ( mx3fb , 0x00100010L , DI_HSP_CLK_PER ) ;
/* Might need to trigger HSP clock change - see 44.3.3.8.5 */
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
sdc_set_brightness ( mx3fb , 255 ) ;
sdc_set_global_alpha ( mx3fb , true , 0xFF ) ;
sdc_set_color_key ( mx3fb , IDMAC_SDC_0 , false , 0 ) ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
mx3fbi = fbi - > par ;
mx3fbi - > idmac_channel = ichan ;
mx3fbi - > ipu_ch = ichan - > dma_chan . chan_id ;
mx3fbi - > mx3fb = mx3fb ;
mx3fbi - > blank = FB_BLANK_NORMAL ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
init_completion ( & mx3fbi - > flip_cmpl ) ;
disable_irq ( ichan - > eof_irq ) ;
dev_dbg ( mx3fb - > dev , " disabling irq %d \n " , ichan - > eof_irq ) ;
2009-08-07 02:07:40 +04:00
ret = __set_par ( fbi , false ) ;
if ( ret < 0 )
goto esetpar ;
__blank ( FB_BLANK_UNBLANK , fbi ) ;
2009-01-21 20:32:34 +03:00
2009-03-20 22:27:37 +03:00
dev_info ( dev , " registered, using mode %s \n " , fb_mode ) ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
ret = register_framebuffer ( fbi ) ;
if ( ret < 0 )
goto erfb ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
return 0 ;
2009-01-21 20:32:34 +03:00
erfb :
esetpar :
emode :
2009-02-26 23:34:28 +03:00
fb_dealloc_cmap ( & fbi - > cmap ) ;
framebuffer_release ( fbi ) ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
return ret ;
2009-01-21 20:32:34 +03:00
}
static bool chan_filter ( struct dma_chan * chan , void * arg )
{
2009-02-26 23:34:28 +03:00
struct dma_chan_request * rq = arg ;
struct device * dev ;
struct mx3fb_platform_data * mx3fb_pdata ;
2009-01-21 20:32:34 +03:00
2010-08-16 10:03:04 +04:00
if ( ! imx_dma_is_ipu ( chan ) )
return false ;
2009-02-26 23:34:28 +03:00
if ( ! rq )
return false ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
dev = rq - > mx3fb - > dev ;
mx3fb_pdata = dev - > platform_data ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
return rq - > id = = chan - > chan_id & &
mx3fb_pdata - > dma_dev = = chan - > device - > dev ;
2009-01-21 20:32:34 +03:00
}
static void release_fbi ( struct fb_info * fbi )
{
2009-02-26 23:34:28 +03:00
mx3fb_unmap_video_memory ( fbi ) ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
fb_dealloc_cmap ( & fbi - > cmap ) ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
unregister_framebuffer ( fbi ) ;
framebuffer_release ( fbi ) ;
2009-01-21 20:32:34 +03:00
}
static int mx3fb_probe ( struct platform_device * pdev )
{
2009-02-26 23:34:28 +03:00
struct device * dev = & pdev - > dev ;
int ret ;
struct resource * sdc_reg ;
struct mx3fb_data * mx3fb ;
dma_cap_mask_t mask ;
struct dma_chan * chan ;
struct dma_chan_request rq ;
/*
* Display Interface ( DI ) and Synchronous Display Controller ( SDC )
* registers
*/
sdc_reg = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! sdc_reg )
return - EINVAL ;
mx3fb = kzalloc ( sizeof ( * mx3fb ) , GFP_KERNEL ) ;
if ( ! mx3fb )
return - ENOMEM ;
spin_lock_init ( & mx3fb - > lock ) ;
mx3fb - > reg_base = ioremap ( sdc_reg - > start , resource_size ( sdc_reg ) ) ;
if ( ! mx3fb - > reg_base ) {
ret = - ENOMEM ;
goto eremap ;
}
2010-12-14 00:42:21 +03:00
pr_debug ( " Remapped %pR at %p \n " , sdc_reg , mx3fb - > reg_base ) ;
2009-02-26 23:34:28 +03:00
/* IDMAC interface */
dmaengine_get ( ) ;
mx3fb - > dev = dev ;
platform_set_drvdata ( pdev , mx3fb ) ;
rq . mx3fb = mx3fb ;
dma_cap_zero ( mask ) ;
dma_cap_set ( DMA_SLAVE , mask ) ;
dma_cap_set ( DMA_PRIVATE , mask ) ;
rq . id = IDMAC_SDC_0 ;
chan = dma_request_channel ( mask , chan_filter , & rq ) ;
if ( ! chan ) {
ret = - EBUSY ;
goto ersdc0 ;
}
2010-02-03 00:43:59 +03:00
mx3fb - > backlight_level = 255 ;
2009-02-26 23:34:28 +03:00
ret = init_fb_chan ( mx3fb , to_idmac_chan ( chan ) ) ;
if ( ret < 0 )
goto eisdc0 ;
return 0 ;
2009-01-21 20:32:34 +03:00
eisdc0 :
2009-02-26 23:34:28 +03:00
dma_release_channel ( chan ) ;
2009-01-21 20:32:34 +03:00
ersdc0 :
2009-02-26 23:34:28 +03:00
dmaengine_put ( ) ;
iounmap ( mx3fb - > reg_base ) ;
2009-01-21 20:32:34 +03:00
eremap :
2009-02-26 23:34:28 +03:00
kfree ( mx3fb ) ;
dev_err ( dev , " mx3fb: failed to register fb \n " ) ;
return ret ;
2009-01-21 20:32:34 +03:00
}
static int mx3fb_remove ( struct platform_device * dev )
{
2009-02-26 23:34:28 +03:00
struct mx3fb_data * mx3fb = platform_get_drvdata ( dev ) ;
struct fb_info * fbi = mx3fb - > fbi ;
struct mx3fb_info * mx3_fbi = fbi - > par ;
struct dma_chan * chan ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
chan = & mx3_fbi - > idmac_channel - > dma_chan ;
release_fbi ( fbi ) ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
dma_release_channel ( chan ) ;
dmaengine_put ( ) ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
iounmap ( mx3fb - > reg_base ) ;
kfree ( mx3fb ) ;
return 0 ;
2009-01-21 20:32:34 +03:00
}
static struct platform_driver mx3fb_driver = {
2009-02-26 23:34:28 +03:00
. driver = {
. name = MX3FB_NAME ,
} ,
. probe = mx3fb_probe ,
. remove = mx3fb_remove ,
. suspend = mx3fb_suspend ,
. resume = mx3fb_resume ,
2009-01-21 20:32:34 +03:00
} ;
/*
* Parse user specified options ( ` video = mx3fb : ' )
* example :
2009-02-26 23:34:28 +03:00
* video = mx3fb : bpp = 16
2009-01-21 20:32:34 +03:00
*/
2009-04-07 06:01:05 +04:00
static int __init mx3fb_setup ( void )
2009-01-21 20:32:34 +03:00
{
# ifndef MODULE
2009-02-26 23:34:28 +03:00
char * opt , * options = NULL ;
if ( fb_get_options ( " mx3fb " , & options ) )
return - ENODEV ;
if ( ! options | | ! * options )
return 0 ;
while ( ( opt = strsep ( & options , " , " ) ) ! = NULL ) {
if ( ! * opt )
continue ;
if ( ! strncmp ( opt , " bpp= " , 4 ) )
default_bpp = simple_strtoul ( opt + 4 , NULL , 0 ) ;
else
fb_mode = opt ;
}
2009-01-21 20:32:34 +03:00
# endif
2009-02-26 23:34:28 +03:00
return 0 ;
2009-01-21 20:32:34 +03:00
}
static int __init mx3fb_init ( void )
{
2009-02-26 23:34:28 +03:00
int ret = mx3fb_setup ( ) ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
if ( ret < 0 )
return ret ;
2009-01-21 20:32:34 +03:00
2009-02-26 23:34:28 +03:00
ret = platform_driver_register ( & mx3fb_driver ) ;
return ret ;
2009-01-21 20:32:34 +03:00
}
static void __exit mx3fb_exit ( void )
{
2009-02-26 23:34:28 +03:00
platform_driver_unregister ( & mx3fb_driver ) ;
2009-01-21 20:32:34 +03:00
}
module_init ( mx3fb_init ) ;
module_exit ( mx3fb_exit ) ;
MODULE_AUTHOR ( " Freescale Semiconductor, Inc. " ) ;
MODULE_DESCRIPTION ( " MX3 framebuffer driver " ) ;
MODULE_ALIAS ( " platform: " MX3FB_NAME ) ;
MODULE_LICENSE ( " GPL v2 " ) ;