2006-12-08 00:58:10 +03:00
/*
* File : arch / arm / plat - omap / fb . c
*
* Framebuffer device registration for TI OMAP platforms
*
* Copyright ( C ) 2006 Nokia Corporation
* Author : 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 .
*/
2006-04-02 20:46:26 +04:00
# include <linux/module.h>
# include <linux/kernel.h>
2008-07-24 08:28:13 +04:00
# include <linux/mm.h>
2006-04-02 20:46:26 +04:00
# include <linux/init.h>
# include <linux/platform_device.h>
# include <linux/bootmem.h>
2008-09-06 15:10:45 +04:00
# include <linux/io.h>
2009-08-03 16:06:36 +04:00
# include <linux/omapfb.h>
2006-04-02 20:46:26 +04:00
2008-08-05 19:14:15 +04:00
# include <mach/hardware.h>
2006-04-02 20:46:26 +04:00
# include <asm/mach/map.h>
2009-10-20 20:40:47 +04:00
# include <plat/board.h>
# include <plat/sram.h>
2006-04-02 20:46:26 +04:00
# if defined(CONFIG_FB_OMAP) || defined(CONFIG_FB_OMAP_MODULE)
static struct omapfb_platform_data omapfb_config ;
2007-03-06 14:16:36 +03:00
static int config_invalid ;
static int configured_regions ;
2006-04-02 20:46:26 +04:00
static u64 omap_fb_dma_mask = ~ ( u32 ) 0 ;
static struct platform_device omap_fb_device = {
. name = " omapfb " ,
. id = - 1 ,
. dev = {
. dma_mask = & omap_fb_dma_mask ,
. coherent_dma_mask = ~ ( u32 ) 0 ,
. platform_data = & omapfb_config ,
} ,
. num_resources = 0 ,
} ;
2009-08-04 17:12:50 +04:00
void omapfb_set_platform_data ( struct omapfb_platform_data * data )
{
}
2007-03-06 14:16:36 +03:00
static inline int ranges_overlap ( unsigned long start1 , unsigned long size1 ,
unsigned long start2 , unsigned long size2 )
2006-04-02 20:46:26 +04:00
{
2007-03-06 14:16:36 +03:00
return ( start1 > = start2 & & start1 < start2 + size2 ) | |
( start2 > = start1 & & start2 < start1 + size1 ) ;
}
static inline int range_included ( unsigned long start1 , unsigned long size1 ,
unsigned long start2 , unsigned long size2 )
{
return start1 > = start2 & & start1 + size1 < = start2 + size2 ;
}
/* Check if there is an overlapping region. */
static int fbmem_region_reserved ( unsigned long start , size_t size )
{
struct omapfb_mem_region * rg ;
2006-12-08 00:58:10 +03:00
int i ;
2007-03-06 14:16:36 +03:00
rg = & omapfb_config . mem_desc . region [ 0 ] ;
for ( i = 0 ; i < OMAPFB_PLANE_NUM ; i + + , rg + + ) {
if ( ! rg - > paddr )
/* Empty slot. */
continue ;
if ( ranges_overlap ( start , size , rg - > paddr , rg - > size ) )
return 1 ;
2006-12-08 00:58:10 +03:00
}
2007-03-06 14:16:36 +03:00
return 0 ;
}
2006-12-08 00:58:10 +03:00
2007-03-06 14:16:36 +03:00
/*
* Get the region_idx ` th region from board config / ATAG and convert it to
* our internal format .
*/
static int get_fbmem_region ( int region_idx , struct omapfb_mem_region * rg )
{
const struct omap_fbmem_config * conf ;
u32 paddr ;
2006-04-02 20:46:26 +04:00
2007-03-06 14:16:36 +03:00
conf = omap_get_nr_config ( OMAP_TAG_FBMEM ,
struct omap_fbmem_config , region_idx ) ;
if ( conf = = NULL )
return - ENOENT ;
paddr = conf - > start ;
/*
* Low bits encode the page allocation mode , if high bits
* are zero . Otherwise we need a page aligned fixed
* address .
*/
memset ( rg , 0 , sizeof ( * rg ) ) ;
rg - > type = paddr & ~ PAGE_MASK ;
rg - > paddr = paddr & PAGE_MASK ;
rg - > size = PAGE_ALIGN ( conf - > size ) ;
return 0 ;
}
static int set_fbmem_region_type ( struct omapfb_mem_region * rg , int mem_type ,
unsigned long mem_start ,
unsigned long mem_size )
{
/*
* Check if the configuration specifies the type explicitly .
* type = 0 & & paddr = 0 , a default don ' t care case maps to
* the SDRAM type .
*/
if ( rg - > type | | ( ! rg - > type & & ! rg - > paddr ) )
return 0 ;
if ( ranges_overlap ( rg - > paddr , rg - > size , mem_start , mem_size ) ) {
rg - > type = mem_type ;
return 0 ;
}
/* Can't determine it. */
return - 1 ;
}
static int check_fbmem_region ( int region_idx , struct omapfb_mem_region * rg ,
unsigned long start_avail , unsigned size_avail )
{
unsigned long paddr = rg - > paddr ;
size_t size = rg - > size ;
if ( rg - > type > OMAPFB_MEMTYPE_MAX ) {
printk ( KERN_ERR
" Invalid start address for FB region %d \n " , region_idx ) ;
return - EINVAL ;
}
if ( ! rg - > size ) {
printk ( KERN_ERR " Zero size for FB region %d \n " , region_idx ) ;
return - EINVAL ;
}
if ( ! paddr )
/* Allocate this dynamically, leave paddr 0 for now. */
return 0 ;
/*
* Fixed region for the given RAM range . Check if it ' s already
* reserved by the FB code or someone else .
*/
if ( fbmem_region_reserved ( paddr , size ) | |
! range_included ( paddr , size , start_avail , size_avail ) ) {
printk ( KERN_ERR " Trying to use reserved memory "
" for FB region %d \n " , region_idx ) ;
return - EINVAL ;
}
return 0 ;
}
/*
* Called from map_io . We need to call to this early enough so that we
* can reserve the fixed SDRAM regions before VM could get hold of them .
*/
2007-10-27 00:48:37 +04:00
void __init omapfb_reserve_sdram ( void )
2007-03-06 14:16:36 +03:00
{
struct bootmem_data * bdata ;
unsigned long sdram_start , sdram_size ;
unsigned long reserved ;
int i ;
if ( config_invalid )
return ;
bdata = NODE_DATA ( 0 ) - > bdata ;
2008-07-24 08:28:09 +04:00
sdram_start = bdata - > node_min_pfn < < PAGE_SHIFT ;
2007-03-06 14:16:36 +03:00
sdram_size = ( bdata - > node_low_pfn < < PAGE_SHIFT ) - sdram_start ;
reserved = 0 ;
for ( i = 0 ; ; i + + ) {
struct omapfb_mem_region rg ;
if ( get_fbmem_region ( i , & rg ) < 0 )
2006-12-08 00:58:10 +03:00
break ;
2007-03-06 14:16:36 +03:00
if ( i = = OMAPFB_PLANE_NUM ) {
printk ( KERN_ERR
" Extraneous FB mem configuration entries \n " ) ;
config_invalid = 1 ;
return ;
2006-04-02 20:46:26 +04:00
}
2007-03-06 14:16:36 +03:00
/* Check if it's our memory type. */
if ( set_fbmem_region_type ( & rg , OMAPFB_MEMTYPE_SDRAM ,
sdram_start , sdram_size ) < 0 | |
( rg . type ! = OMAPFB_MEMTYPE_SDRAM ) )
continue ;
BUG_ON ( omapfb_config . mem_desc . region [ i ] . size ) ;
if ( check_fbmem_region ( i , & rg , sdram_start , sdram_size ) < 0 ) {
config_invalid = 1 ;
return ;
2006-12-08 00:58:10 +03:00
}
2009-05-11 20:58:19 +04:00
if ( rg . paddr ) {
2008-02-07 11:15:17 +03:00
reserve_bootmem ( rg . paddr , rg . size , BOOTMEM_DEFAULT ) ;
2009-05-11 20:58:19 +04:00
reserved + = rg . size ;
}
2007-03-06 14:16:36 +03:00
omapfb_config . mem_desc . region [ i ] = rg ;
configured_regions + + ;
2006-04-02 20:46:26 +04:00
}
2006-12-08 00:58:10 +03:00
omapfb_config . mem_desc . region_cnt = i ;
2007-03-06 14:16:36 +03:00
if ( reserved )
2006-12-08 00:58:10 +03:00
pr_info ( " Reserving %lu bytes SDRAM for frame buffer \n " ,
2007-03-06 14:16:36 +03:00
reserved ) ;
}
/*
* Called at sram init time , before anything is pushed to the SRAM stack .
* Because of the stack scheme , we will allocate everything from the
* start of the lowest address region to the end of SRAM . This will also
* include padding for page alignment and possible holes between regions .
*
* As opposed to the SDRAM case , we ' ll also do any dynamic allocations at
* this point , since the driver built as a module would have problem with
* freeing / reallocating the regions .
*/
unsigned long omapfb_reserve_sram ( unsigned long sram_pstart ,
unsigned long sram_vstart ,
unsigned long sram_size ,
unsigned long pstart_avail ,
unsigned long size_avail )
{
struct omapfb_mem_region rg ;
unsigned long pend_avail ;
unsigned long reserved ;
int i ;
if ( config_invalid )
return 0 ;
reserved = 0 ;
pend_avail = pstart_avail + size_avail ;
for ( i = 0 ; ; i + + ) {
if ( get_fbmem_region ( i , & rg ) < 0 )
break ;
if ( i = = OMAPFB_PLANE_NUM ) {
printk ( KERN_ERR
" Extraneous FB mem configuration entries \n " ) ;
config_invalid = 1 ;
return 0 ;
}
2006-12-08 00:58:10 +03:00
2007-03-06 14:16:36 +03:00
/* Check if it's our memory type. */
if ( set_fbmem_region_type ( & rg , OMAPFB_MEMTYPE_SRAM ,
sram_pstart , sram_size ) < 0 | |
( rg . type ! = OMAPFB_MEMTYPE_SRAM ) )
continue ;
BUG_ON ( omapfb_config . mem_desc . region [ i ] . size ) ;
if ( check_fbmem_region ( i , & rg , pstart_avail , size_avail ) < 0 ) {
config_invalid = 1 ;
return 0 ;
}
if ( ! rg . paddr ) {
/* Dynamic allocation */
if ( ( size_avail & PAGE_MASK ) < rg . size ) {
printk ( " Not enough SRAM for FB region %d \n " ,
i ) ;
config_invalid = 1 ;
return 0 ;
}
size_avail = ( size_avail - rg . size ) & PAGE_MASK ;
rg . paddr = pstart_avail + size_avail ;
}
/* Reserve everything above the start of the region. */
if ( pend_avail - rg . paddr > reserved )
reserved = pend_avail - rg . paddr ;
size_avail = pend_avail - reserved - pstart_avail ;
/*
* We have a kernel mapping for this already , so the
* driver won ' t have to make one .
*/
rg . vaddr = ( void * ) ( sram_vstart + rg . paddr - sram_pstart ) ;
omapfb_config . mem_desc . region [ i ] = rg ;
configured_regions + + ;
}
omapfb_config . mem_desc . region_cnt = i ;
if ( reserved )
pr_info ( " Reserving %lu bytes SRAM for frame buffer \n " ,
reserved ) ;
return reserved ;
2006-04-02 20:46:26 +04:00
}
2006-12-07 04:13:50 +03:00
void omapfb_set_ctrl_platform_data ( void * data )
{
omapfb_config . ctrl_platform_data = data ;
}
2006-04-02 20:46:26 +04:00
static inline int omap_init_fb ( void )
{
const struct omap_lcd_config * conf ;
2007-03-06 14:16:36 +03:00
if ( config_invalid )
return 0 ;
if ( configured_regions ! = omapfb_config . mem_desc . region_cnt ) {
printk ( KERN_ERR " Invalid FB mem configuration entries \n " ) ;
return 0 ;
}
2006-04-02 20:46:26 +04:00
conf = omap_get_config ( OMAP_TAG_LCD , struct omap_lcd_config ) ;
2007-03-06 14:16:36 +03:00
if ( conf = = NULL ) {
if ( configured_regions )
/* FB mem config, but no LCD config? */
printk ( KERN_ERR " Missing LCD configuration \n " ) ;
2006-04-02 20:46:26 +04:00
return 0 ;
2007-03-06 14:16:36 +03:00
}
2006-04-02 20:46:26 +04:00
omapfb_config . lcd = * conf ;
return platform_device_register ( & omap_fb_device ) ;
}
arch_initcall ( omap_init_fb ) ;
2009-08-04 17:12:50 +04:00
# elif defined(CONFIG_FB_OMAP2) || defined(CONFIG_FB_OMAP2_MODULE)
static u64 omap_fb_dma_mask = ~ ( u32 ) 0 ;
static struct omapfb_platform_data omapfb_config ;
static struct platform_device omap_fb_device = {
. name = " omapfb " ,
. id = - 1 ,
. dev = {
. dma_mask = & omap_fb_dma_mask ,
. coherent_dma_mask = ~ ( u32 ) 0 ,
. platform_data = & omapfb_config ,
} ,
. num_resources = 0 ,
} ;
void omapfb_set_platform_data ( struct omapfb_platform_data * data )
{
omapfb_config = * data ;
}
static inline int omap_init_fb ( void )
{
return platform_device_register ( & omap_fb_device ) ;
}
arch_initcall ( omap_init_fb ) ;
2006-04-02 20:46:26 +04:00
2007-03-06 14:16:36 +03:00
void omapfb_reserve_sdram ( void ) { }
unsigned long omapfb_reserve_sram ( unsigned long sram_pstart ,
unsigned long sram_vstart ,
unsigned long sram_size ,
unsigned long start_avail ,
2007-03-29 03:38:14 +04:00
unsigned long size_avail )
{
return 0 ;
}
2007-03-06 14:16:36 +03:00
2009-08-04 17:12:50 +04:00
# else
void omapfb_set_platform_data ( struct omapfb_platform_data * data )
{
}
void omapfb_reserve_sdram ( void ) { }
unsigned long omapfb_reserve_sram ( unsigned long sram_pstart ,
unsigned long sram_vstart ,
unsigned long sram_size ,
unsigned long start_avail ,
unsigned long size_avail )
{
return 0 ;
}
2006-04-02 20:46:26 +04:00
# endif