2007-07-17 15:05:59 +04:00
/*
* OMAP2 Remote Frame Buffer Interface support
*
* Copyright ( C ) 2005 Nokia Corporation
* Author : Juha Yrj <EFBFBD> l <EFBFBD> < juha . yrjola @ nokia . com >
* Imre Deak < imre . deak @ nokia . com >
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation ; either version 2 of the License , or ( at your
* option ) any later version .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*
* You should have received a copy of the GNU General Public License along
* with this program ; if not , write to the Free Software Foundation , Inc . ,
* 59 Temple Place - Suite 330 , Boston , MA 02111 - 1307 , USA .
*/
# include <linux/module.h>
# include <linux/delay.h>
# include <linux/i2c.h>
# include <linux/err.h>
# include <linux/interrupt.h>
# include <linux/clk.h>
# include <linux/io.h>
2008-08-05 19:14:15 +04:00
# include <mach/omapfb.h>
2007-07-17 15:05:59 +04:00
# include "dispc.h"
/* To work around an RFBI transfer rate limitation */
# define OMAP_RFBI_RATE_LIMIT 1
# define RFBI_BASE 0x48050800
# define RFBI_REVISION 0x0000
# define RFBI_SYSCONFIG 0x0010
# define RFBI_SYSSTATUS 0x0014
# define RFBI_CONTROL 0x0040
# define RFBI_PIXEL_CNT 0x0044
# define RFBI_LINE_NUMBER 0x0048
# define RFBI_CMD 0x004c
# define RFBI_PARAM 0x0050
# define RFBI_DATA 0x0054
# define RFBI_READ 0x0058
# define RFBI_STATUS 0x005c
# define RFBI_CONFIG0 0x0060
# define RFBI_ONOFF_TIME0 0x0064
# define RFBI_CYCLE_TIME0 0x0068
# define RFBI_DATA_CYCLE1_0 0x006c
# define RFBI_DATA_CYCLE2_0 0x0070
# define RFBI_DATA_CYCLE3_0 0x0074
# define RFBI_VSYNC_WIDTH 0x0090
# define RFBI_HSYNC_WIDTH 0x0094
# define DISPC_BASE 0x48050400
# define DISPC_CONTROL 0x0040
2009-09-23 03:46:57 +04:00
# define DISPC_IRQ_FRAMEMASK 0x0001
2007-07-17 15:05:59 +04:00
static struct {
2008-09-04 17:07:22 +04:00
void __iomem * base ;
2007-07-17 15:05:59 +04:00
void ( * lcdc_callback ) ( void * data ) ;
void * lcdc_callback_data ;
unsigned long l4_khz ;
int bits_per_cycle ;
struct omapfb_device * fbdev ;
struct clk * dss_ick ;
struct clk * dss1_fck ;
unsigned tearsync_pin_cnt ;
unsigned tearsync_mode ;
} rfbi ;
static inline void rfbi_write_reg ( int idx , u32 val )
{
__raw_writel ( val , rfbi . base + idx ) ;
}
static inline u32 rfbi_read_reg ( int idx )
{
return __raw_readl ( rfbi . base + idx ) ;
}
static int rfbi_get_clocks ( void )
{
2009-05-16 19:28:17 +04:00
rfbi . dss_ick = clk_get ( rfbi . fbdev - > dev , " ick " ) ;
if ( IS_ERR ( rfbi . dss_ick ) ) {
dev_err ( rfbi . fbdev - > dev , " can't get ick \n " ) ;
2007-07-17 15:05:59 +04:00
return PTR_ERR ( rfbi . dss_ick ) ;
}
2009-05-16 19:28:17 +04:00
rfbi . dss1_fck = clk_get ( rfbi . fbdev - > dev , " dss1_fck " ) ;
if ( IS_ERR ( rfbi . dss1_fck ) ) {
2007-10-18 14:06:30 +04:00
dev_err ( rfbi . fbdev - > dev , " can't get dss1_fck \n " ) ;
2007-07-17 15:05:59 +04:00
clk_put ( rfbi . dss_ick ) ;
return PTR_ERR ( rfbi . dss1_fck ) ;
}
return 0 ;
}
static void rfbi_put_clocks ( void )
{
clk_put ( rfbi . dss1_fck ) ;
clk_put ( rfbi . dss_ick ) ;
}
static void rfbi_enable_clocks ( int enable )
{
if ( enable ) {
clk_enable ( rfbi . dss_ick ) ;
clk_enable ( rfbi . dss1_fck ) ;
} else {
clk_disable ( rfbi . dss1_fck ) ;
clk_disable ( rfbi . dss_ick ) ;
}
}
# ifdef VERBOSE
static void rfbi_print_timings ( void )
{
u32 l ;
u32 time ;
l = rfbi_read_reg ( RFBI_CONFIG0 ) ;
time = 1000000000 / rfbi . l4_khz ;
if ( l & ( 1 < < 4 ) )
time * = 2 ;
dev_dbg ( rfbi . fbdev - > dev , " Tick time %u ps \n " , time ) ;
l = rfbi_read_reg ( RFBI_ONOFF_TIME0 ) ;
dev_dbg ( rfbi . fbdev - > dev ,
" CSONTIME %d, CSOFFTIME %d, WEONTIME %d, WEOFFTIME %d, "
" REONTIME %d, REOFFTIME %d \n " ,
l & 0x0f , ( l > > 4 ) & 0x3f , ( l > > 10 ) & 0x0f , ( l > > 14 ) & 0x3f ,
( l > > 20 ) & 0x0f , ( l > > 24 ) & 0x3f ) ;
l = rfbi_read_reg ( RFBI_CYCLE_TIME0 ) ;
dev_dbg ( rfbi . fbdev - > dev ,
" WECYCLETIME %d, RECYCLETIME %d, CSPULSEWIDTH %d, "
" ACCESSTIME %d \n " ,
( l & 0x3f ) , ( l > > 6 ) & 0x3f , ( l > > 12 ) & 0x3f ,
( l > > 22 ) & 0x3f ) ;
}
# else
static void rfbi_print_timings ( void ) { }
# endif
static void rfbi_set_timings ( const struct extif_timings * t )
{
u32 l ;
BUG_ON ( ! t - > converted ) ;
rfbi_enable_clocks ( 1 ) ;
rfbi_write_reg ( RFBI_ONOFF_TIME0 , t - > tim [ 0 ] ) ;
rfbi_write_reg ( RFBI_CYCLE_TIME0 , t - > tim [ 1 ] ) ;
l = rfbi_read_reg ( RFBI_CONFIG0 ) ;
l & = ~ ( 1 < < 4 ) ;
l | = ( t - > tim [ 2 ] ? 1 : 0 ) < < 4 ;
rfbi_write_reg ( RFBI_CONFIG0 , l ) ;
rfbi_print_timings ( ) ;
rfbi_enable_clocks ( 0 ) ;
}
static void rfbi_get_clk_info ( u32 * clk_period , u32 * max_clk_div )
{
* clk_period = 1000000000 / rfbi . l4_khz ;
* max_clk_div = 2 ;
}
static int ps_to_rfbi_ticks ( int time , int div )
{
unsigned long tick_ps ;
int ret ;
/* Calculate in picosecs to yield more exact results */
tick_ps = 1000000000 / ( rfbi . l4_khz ) * div ;
ret = ( time + tick_ps - 1 ) / tick_ps ;
return ret ;
}
# ifdef OMAP_RFBI_RATE_LIMIT
static unsigned long rfbi_get_max_tx_rate ( void )
{
unsigned long l4_rate , dss1_rate ;
int min_l4_ticks = 0 ;
int i ;
/* According to TI this can't be calculated so make the
* adjustments for a couple of known frequencies and warn for
* others .
*/
static const struct {
unsigned long l4_clk ; /* HZ */
unsigned long dss1_clk ; /* HZ */
unsigned long min_l4_ticks ;
} ftab [ ] = {
{ 55 , 132 , 7 , } , /* 7.86 MPix/s */
{ 110 , 110 , 12 , } , /* 9.16 MPix/s */
{ 110 , 132 , 10 , } , /* 11 Mpix/s */
{ 120 , 120 , 10 , } , /* 12 Mpix/s */
{ 133 , 133 , 10 , } , /* 13.3 Mpix/s */
} ;
l4_rate = rfbi . l4_khz / 1000 ;
dss1_rate = clk_get_rate ( rfbi . dss1_fck ) / 1000000 ;
for ( i = 0 ; i < ARRAY_SIZE ( ftab ) ; i + + ) {
/* Use a window instead of an exact match, to account
* for different DPLL multiplier / divider pairs .
*/
if ( abs ( ftab [ i ] . l4_clk - l4_rate ) < 3 & &
abs ( ftab [ i ] . dss1_clk - dss1_rate ) < 3 ) {
min_l4_ticks = ftab [ i ] . min_l4_ticks ;
break ;
}
}
if ( i = = ARRAY_SIZE ( ftab ) ) {
/* Can't be sure, return anyway the maximum not
* rate - limited . This might cause a problem only for the
* tearing synchronisation .
*/
dev_err ( rfbi . fbdev - > dev ,
" can't determine maximum RFBI transfer rate \n " ) ;
return rfbi . l4_khz * 1000 ;
}
return rfbi . l4_khz * 1000 / min_l4_ticks ;
}
# else
static int rfbi_get_max_tx_rate ( void )
{
return rfbi . l4_khz * 1000 ;
}
# endif
static int rfbi_convert_timings ( struct extif_timings * t )
{
u32 l ;
int reon , reoff , weon , weoff , cson , csoff , cs_pulse ;
int actim , recyc , wecyc ;
int div = t - > clk_div ;
if ( div < = 0 | | div > 2 )
return - 1 ;
/* Make sure that after conversion it still holds that:
* weoff > weon , reoff > reon , recyc > = reoff , wecyc > = weoff ,
* csoff > cson , csoff > = max ( weoff , reoff ) , actim > reon
*/
weon = ps_to_rfbi_ticks ( t - > we_on_time , div ) ;
weoff = ps_to_rfbi_ticks ( t - > we_off_time , div ) ;
if ( weoff < = weon )
weoff = weon + 1 ;
if ( weon > 0x0f )
return - 1 ;
if ( weoff > 0x3f )
return - 1 ;
reon = ps_to_rfbi_ticks ( t - > re_on_time , div ) ;
reoff = ps_to_rfbi_ticks ( t - > re_off_time , div ) ;
if ( reoff < = reon )
reoff = reon + 1 ;
if ( reon > 0x0f )
return - 1 ;
if ( reoff > 0x3f )
return - 1 ;
cson = ps_to_rfbi_ticks ( t - > cs_on_time , div ) ;
csoff = ps_to_rfbi_ticks ( t - > cs_off_time , div ) ;
if ( csoff < = cson )
csoff = cson + 1 ;
if ( csoff < max ( weoff , reoff ) )
csoff = max ( weoff , reoff ) ;
if ( cson > 0x0f )
return - 1 ;
if ( csoff > 0x3f )
return - 1 ;
l = cson ;
l | = csoff < < 4 ;
l | = weon < < 10 ;
l | = weoff < < 14 ;
l | = reon < < 20 ;
l | = reoff < < 24 ;
t - > tim [ 0 ] = l ;
actim = ps_to_rfbi_ticks ( t - > access_time , div ) ;
if ( actim < = reon )
actim = reon + 1 ;
if ( actim > 0x3f )
return - 1 ;
wecyc = ps_to_rfbi_ticks ( t - > we_cycle_time , div ) ;
if ( wecyc < weoff )
wecyc = weoff ;
if ( wecyc > 0x3f )
return - 1 ;
recyc = ps_to_rfbi_ticks ( t - > re_cycle_time , div ) ;
if ( recyc < reoff )
recyc = reoff ;
if ( recyc > 0x3f )
return - 1 ;
cs_pulse = ps_to_rfbi_ticks ( t - > cs_pulse_width , div ) ;
if ( cs_pulse > 0x3f )
return - 1 ;
l = wecyc ;
l | = recyc < < 6 ;
l | = cs_pulse < < 12 ;
l | = actim < < 22 ;
t - > tim [ 1 ] = l ;
t - > tim [ 2 ] = div - 1 ;
t - > converted = 1 ;
return 0 ;
}
static int rfbi_setup_tearsync ( unsigned pin_cnt ,
unsigned hs_pulse_time , unsigned vs_pulse_time ,
int hs_pol_inv , int vs_pol_inv , int extif_div )
{
int hs , vs ;
int min ;
u32 l ;
if ( pin_cnt ! = 1 & & pin_cnt ! = 2 )
return - EINVAL ;
hs = ps_to_rfbi_ticks ( hs_pulse_time , 1 ) ;
vs = ps_to_rfbi_ticks ( vs_pulse_time , 1 ) ;
if ( hs < 2 )
return - EDOM ;
if ( pin_cnt = = 2 )
min = 2 ;
else
min = 4 ;
if ( vs < min )
return - EDOM ;
if ( vs = = hs )
return - EINVAL ;
rfbi . tearsync_pin_cnt = pin_cnt ;
dev_dbg ( rfbi . fbdev - > dev ,
" setup_tearsync: pins %d hs %d vs %d hs_inv %d vs_inv %d \n " ,
pin_cnt , hs , vs , hs_pol_inv , vs_pol_inv ) ;
rfbi_enable_clocks ( 1 ) ;
rfbi_write_reg ( RFBI_HSYNC_WIDTH , hs ) ;
rfbi_write_reg ( RFBI_VSYNC_WIDTH , vs ) ;
l = rfbi_read_reg ( RFBI_CONFIG0 ) ;
if ( hs_pol_inv )
l & = ~ ( 1 < < 21 ) ;
else
l | = 1 < < 21 ;
if ( vs_pol_inv )
l & = ~ ( 1 < < 20 ) ;
else
l | = 1 < < 20 ;
rfbi_enable_clocks ( 0 ) ;
return 0 ;
}
static int rfbi_enable_tearsync ( int enable , unsigned line )
{
u32 l ;
dev_dbg ( rfbi . fbdev - > dev , " tearsync %d line %d mode %d \n " ,
enable , line , rfbi . tearsync_mode ) ;
if ( line > ( 1 < < 11 ) - 1 )
return - EINVAL ;
rfbi_enable_clocks ( 1 ) ;
l = rfbi_read_reg ( RFBI_CONFIG0 ) ;
l & = ~ ( 0x3 < < 2 ) ;
if ( enable ) {
rfbi . tearsync_mode = rfbi . tearsync_pin_cnt ;
l | = rfbi . tearsync_mode < < 2 ;
} else
rfbi . tearsync_mode = 0 ;
rfbi_write_reg ( RFBI_CONFIG0 , l ) ;
rfbi_write_reg ( RFBI_LINE_NUMBER , line ) ;
rfbi_enable_clocks ( 0 ) ;
return 0 ;
}
static void rfbi_write_command ( const void * buf , unsigned int len )
{
rfbi_enable_clocks ( 1 ) ;
if ( rfbi . bits_per_cycle = = 16 ) {
const u16 * w = buf ;
BUG_ON ( len & 1 ) ;
for ( ; len ; len - = 2 )
rfbi_write_reg ( RFBI_CMD , * w + + ) ;
} else {
const u8 * b = buf ;
BUG_ON ( rfbi . bits_per_cycle ! = 8 ) ;
for ( ; len ; len - - )
rfbi_write_reg ( RFBI_CMD , * b + + ) ;
}
rfbi_enable_clocks ( 0 ) ;
}
static void rfbi_read_data ( void * buf , unsigned int len )
{
rfbi_enable_clocks ( 1 ) ;
if ( rfbi . bits_per_cycle = = 16 ) {
u16 * w = buf ;
BUG_ON ( len & ~ 1 ) ;
for ( ; len ; len - = 2 ) {
rfbi_write_reg ( RFBI_READ , 0 ) ;
* w + + = rfbi_read_reg ( RFBI_READ ) ;
}
} else {
u8 * b = buf ;
BUG_ON ( rfbi . bits_per_cycle ! = 8 ) ;
for ( ; len ; len - - ) {
rfbi_write_reg ( RFBI_READ , 0 ) ;
* b + + = rfbi_read_reg ( RFBI_READ ) ;
}
}
rfbi_enable_clocks ( 0 ) ;
}
static void rfbi_write_data ( const void * buf , unsigned int len )
{
rfbi_enable_clocks ( 1 ) ;
if ( rfbi . bits_per_cycle = = 16 ) {
const u16 * w = buf ;
BUG_ON ( len & 1 ) ;
for ( ; len ; len - = 2 )
rfbi_write_reg ( RFBI_PARAM , * w + + ) ;
} else {
const u8 * b = buf ;
BUG_ON ( rfbi . bits_per_cycle ! = 8 ) ;
for ( ; len ; len - - )
rfbi_write_reg ( RFBI_PARAM , * b + + ) ;
}
rfbi_enable_clocks ( 0 ) ;
}
static void rfbi_transfer_area ( int width , int height ,
void ( callback ) ( void * data ) , void * data )
{
u32 w ;
BUG_ON ( callback = = NULL ) ;
rfbi_enable_clocks ( 1 ) ;
omap_dispc_set_lcd_size ( width , height ) ;
rfbi . lcdc_callback = callback ;
rfbi . lcdc_callback_data = data ;
rfbi_write_reg ( RFBI_PIXEL_CNT , width * height ) ;
w = rfbi_read_reg ( RFBI_CONTROL ) ;
w | = 1 ; /* enable */
if ( ! rfbi . tearsync_mode )
w | = 1 < < 4 ; /* internal trigger, reset by HW */
rfbi_write_reg ( RFBI_CONTROL , w ) ;
omap_dispc_enable_lcd_out ( 1 ) ;
}
static inline void _stop_transfer ( void )
{
u32 w ;
w = rfbi_read_reg ( RFBI_CONTROL ) ;
rfbi_write_reg ( RFBI_CONTROL , w & ~ ( 1 < < 0 ) ) ;
rfbi_enable_clocks ( 0 ) ;
}
static void rfbi_dma_callback ( void * data )
{
_stop_transfer ( ) ;
rfbi . lcdc_callback ( rfbi . lcdc_callback_data ) ;
}
static void rfbi_set_bits_per_cycle ( int bpc )
{
u32 l ;
rfbi_enable_clocks ( 1 ) ;
l = rfbi_read_reg ( RFBI_CONFIG0 ) ;
l & = ~ ( 0x03 < < 0 ) ;
switch ( bpc ) {
case 8 :
break ;
case 16 :
l | = 3 ;
break ;
default :
BUG ( ) ;
}
rfbi_write_reg ( RFBI_CONFIG0 , l ) ;
rfbi . bits_per_cycle = bpc ;
rfbi_enable_clocks ( 0 ) ;
}
static int rfbi_init ( struct omapfb_device * fbdev )
{
u32 l ;
int r ;
rfbi . fbdev = fbdev ;
2008-09-04 17:07:22 +04:00
rfbi . base = ioremap ( RFBI_BASE , SZ_1K ) ;
if ( ! rfbi . base ) {
dev_err ( fbdev - > dev , " can't ioremap RFBI \n " ) ;
return - ENOMEM ;
}
2007-07-17 15:05:59 +04:00
if ( ( r = rfbi_get_clocks ( ) ) < 0 )
return r ;
rfbi_enable_clocks ( 1 ) ;
rfbi . l4_khz = clk_get_rate ( rfbi . dss_ick ) / 1000 ;
/* Reset */
rfbi_write_reg ( RFBI_SYSCONFIG , 1 < < 1 ) ;
while ( ! ( rfbi_read_reg ( RFBI_SYSSTATUS ) & ( 1 < < 0 ) ) ) ;
l = rfbi_read_reg ( RFBI_SYSCONFIG ) ;
/* Enable autoidle and smart-idle */
l | = ( 1 < < 0 ) | ( 2 < < 3 ) ;
rfbi_write_reg ( RFBI_SYSCONFIG , l ) ;
/* 16-bit interface, ITE trigger mode, 16-bit data */
l = ( 0x03 < < 0 ) | ( 0x00 < < 2 ) | ( 0x01 < < 5 ) | ( 0x02 < < 7 ) ;
l | = ( 0 < < 9 ) | ( 1 < < 20 ) | ( 1 < < 21 ) ;
rfbi_write_reg ( RFBI_CONFIG0 , l ) ;
rfbi_write_reg ( RFBI_DATA_CYCLE1_0 , 0x00000010 ) ;
l = rfbi_read_reg ( RFBI_CONTROL ) ;
/* Select CS0, clear bypass mode */
l = ( 0x01 < < 2 ) ;
rfbi_write_reg ( RFBI_CONTROL , l ) ;
2009-09-23 03:46:57 +04:00
r = omap_dispc_request_irq ( DISPC_IRQ_FRAMEMASK , rfbi_dma_callback ,
NULL ) ;
if ( r < 0 ) {
2007-07-17 15:05:59 +04:00
dev_err ( fbdev - > dev , " can't get DISPC irq \n " ) ;
rfbi_enable_clocks ( 0 ) ;
return r ;
}
l = rfbi_read_reg ( RFBI_REVISION ) ;
pr_info ( " omapfb: RFBI version %d.%d initialized \n " ,
( l > > 4 ) & 0x0f , l & 0x0f ) ;
rfbi_enable_clocks ( 0 ) ;
return 0 ;
}
static void rfbi_cleanup ( void )
{
2009-09-23 03:46:57 +04:00
omap_dispc_free_irq ( DISPC_IRQ_FRAMEMASK , rfbi_dma_callback , NULL ) ;
2007-07-17 15:05:59 +04:00
rfbi_put_clocks ( ) ;
2008-09-04 17:07:22 +04:00
iounmap ( rfbi . base ) ;
2007-07-17 15:05:59 +04:00
}
const struct lcd_ctrl_extif omap2_ext_if = {
. init = rfbi_init ,
. cleanup = rfbi_cleanup ,
. get_clk_info = rfbi_get_clk_info ,
. get_max_tx_rate = rfbi_get_max_tx_rate ,
. set_bits_per_cycle = rfbi_set_bits_per_cycle ,
. convert_timings = rfbi_convert_timings ,
. set_timings = rfbi_set_timings ,
. write_command = rfbi_write_command ,
. read_data = rfbi_read_data ,
. write_data = rfbi_write_data ,
. transfer_area = rfbi_transfer_area ,
. setup_tearsync = rfbi_setup_tearsync ,
. enable_tearsync = rfbi_enable_tearsync ,
. max_transmit_size = ( u32 ) ~ 0 ,
} ;