2011-05-18 15:10:07 +04:00
/*
* SuperH Mobile MERAM Driver for SuperH Mobile LCDC Driver
*
* Copyright ( c ) 2011 Damian Hobson - Garcia < dhobsong @ igel . co . jp >
* Takanari Hayama < taki @ igel . co . jp >
*
* 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 .
*/
2011-09-19 13:40:31 +04:00
# include <linux/device.h>
2011-12-12 19:36:13 +04:00
# include <linux/err.h>
2012-03-15 15:40:47 +04:00
# include <linux/export.h>
2011-09-19 13:40:31 +04:00
# include <linux/genalloc.h>
2011-09-19 13:40:31 +04:00
# include <linux/io.h>
2011-05-18 15:10:07 +04:00
# include <linux/kernel.h>
# include <linux/module.h>
2011-09-19 13:40:31 +04:00
# include <linux/platform_device.h>
2011-07-04 10:06:11 +04:00
# include <linux/pm_runtime.h>
2011-05-18 15:10:07 +04:00
# include <linux/slab.h>
2011-09-19 13:40:31 +04:00
2011-07-13 14:13:47 +04:00
# include <video/sh_mobile_meram.h>
2011-05-18 15:10:07 +04:00
2011-09-19 13:40:31 +04:00
/* -----------------------------------------------------------------------------
* MERAM registers
*/
2011-07-13 14:13:47 +04:00
# define MEVCR1 0x4
# define MEVCR1_RST (1 << 31)
# define MEVCR1_WD (1 << 30)
# define MEVCR1_AMD1 (1 << 29)
# define MEVCR1_AMD0 (1 << 28)
# define MEQSEL1 0x40
# define MEQSEL2 0x44
# define MExxCTL 0x400
# define MExxCTL_BV (1 << 31)
# define MExxCTL_BSZ_SHIFT 28
# define MExxCTL_MSAR_MASK (0x7ff << MExxCTL_MSAR_SHIFT)
# define MExxCTL_MSAR_SHIFT 16
# define MExxCTL_NXT_MASK (0x1f << MExxCTL_NXT_SHIFT)
# define MExxCTL_NXT_SHIFT 11
# define MExxCTL_WD1 (1 << 10)
# define MExxCTL_WD0 (1 << 9)
# define MExxCTL_WS (1 << 8)
# define MExxCTL_CB (1 << 7)
# define MExxCTL_WBF (1 << 6)
# define MExxCTL_WF (1 << 5)
# define MExxCTL_RF (1 << 4)
# define MExxCTL_CM (1 << 3)
# define MExxCTL_MD_READ (1 << 0)
# define MExxCTL_MD_WRITE (2 << 0)
# define MExxCTL_MD_ICB_WB (3 << 0)
# define MExxCTL_MD_ICB (4 << 0)
# define MExxCTL_MD_FB (7 << 0)
# define MExxCTL_MD_MASK (7 << 0)
# define MExxBSIZE 0x404
# define MExxBSIZE_RCNT_SHIFT 28
# define MExxBSIZE_YSZM1_SHIFT 16
# define MExxBSIZE_XSZM1_SHIFT 0
# define MExxMNCF 0x408
# define MExxMNCF_KWBNM_SHIFT 28
# define MExxMNCF_KRBNM_SHIFT 24
# define MExxMNCF_BNM_SHIFT 16
# define MExxMNCF_XBV (1 << 15)
# define MExxMNCF_CPL_YCBCR444 (1 << 12)
# define MExxMNCF_CPL_YCBCR420 (2 << 12)
# define MExxMNCF_CPL_YCBCR422 (3 << 12)
# define MExxMNCF_CPL_MSK (3 << 12)
# define MExxMNCF_BL (1 << 2)
# define MExxMNCF_LNM_SHIFT 0
# define MExxSARA 0x410
# define MExxSARB 0x414
# define MExxSBSIZE 0x418
# define MExxSBSIZE_HDV (1 << 31)
# define MExxSBSIZE_HSZ16 (0 << 28)
# define MExxSBSIZE_HSZ32 (1 << 28)
# define MExxSBSIZE_HSZ64 (2 << 28)
# define MExxSBSIZE_HSZ128 (3 << 28)
# define MExxSBSIZE_SBSIZZ_SHIFT 0
# define MERAM_MExxCTL_VAL(next, addr) \
( ( ( ( next ) < < MExxCTL_NXT_SHIFT ) & MExxCTL_NXT_MASK ) | \
( ( ( addr ) < < MExxCTL_MSAR_SHIFT ) & MExxCTL_MSAR_MASK ) )
# define MERAM_MExxBSIZE_VAL(rcnt, yszm1, xszm1) \
( ( ( rcnt ) < < MExxBSIZE_RCNT_SHIFT ) | \
( ( yszm1 ) < < MExxBSIZE_YSZM1_SHIFT ) | \
( ( xszm1 ) < < MExxBSIZE_XSZM1_SHIFT ) )
2011-05-18 15:10:07 +04:00
2011-09-19 13:40:31 +04:00
static const unsigned long common_regs [ ] = {
2011-06-22 09:49:51 +04:00
MEVCR1 ,
MEQSEL1 ,
MEQSEL2 ,
} ;
2011-09-19 13:40:31 +04:00
# define MERAM_REGS_SIZE ARRAY_SIZE(common_regs)
2011-06-22 09:49:51 +04:00
2011-09-19 13:40:31 +04:00
static const unsigned long icb_regs [ ] = {
2011-06-22 09:49:51 +04:00
MExxCTL ,
MExxBSIZE ,
MExxMNCF ,
MExxSARA ,
MExxSARB ,
MExxSBSIZE ,
} ;
# define ICB_REGS_SIZE ARRAY_SIZE(icb_regs)
2011-09-19 13:40:31 +04:00
/*
* sh_mobile_meram_icb - MERAM ICB information
* @ regs : Registers cache
2011-12-12 19:36:13 +04:00
* @ index : ICB index
2011-09-19 13:40:31 +04:00
* @ offset : MERAM block offset
2011-12-12 19:36:13 +04:00
* @ size : MERAM block size in KiB
2011-09-19 13:40:31 +04:00
* @ cache_unit : Bytes to cache per ICB
* @ pixelformat : Video pixel format of the data stored in the ICB
* @ current_reg : Which of Start Address Register A ( 0 ) or B ( 1 ) is in use
*/
struct sh_mobile_meram_icb {
unsigned long regs [ ICB_REGS_SIZE ] ;
2011-12-12 19:36:13 +04:00
unsigned int index ;
2011-09-19 13:40:31 +04:00
unsigned long offset ;
unsigned int size ;
2011-09-19 13:40:31 +04:00
unsigned int cache_unit ;
unsigned int pixelformat ;
unsigned int current_reg ;
} ;
2011-09-19 13:40:31 +04:00
# define MERAM_ICB_NUM 32
2011-12-12 19:36:13 +04:00
struct sh_mobile_meram_fb_plane {
struct sh_mobile_meram_icb * marker ;
struct sh_mobile_meram_icb * cache ;
} ;
struct sh_mobile_meram_fb_cache {
unsigned int nplanes ;
struct sh_mobile_meram_fb_plane planes [ 2 ] ;
} ;
2011-09-19 13:40:31 +04:00
/*
* sh_mobile_meram_priv - MERAM device
* @ base : Registers base address
2011-09-19 13:40:31 +04:00
* @ meram : MERAM physical address
2011-09-19 13:40:31 +04:00
* @ regs : Registers cache
* @ lock : Protects used_icb and icbs
* @ used_icb : Bitmask of used ICBs
* @ icbs : ICBs
2011-09-19 13:40:31 +04:00
* @ pool : Allocation pool to manage the MERAM
2011-09-19 13:40:31 +04:00
*/
2011-06-22 09:49:50 +04:00
struct sh_mobile_meram_priv {
2011-09-19 13:40:31 +04:00
void __iomem * base ;
2011-09-19 13:40:31 +04:00
unsigned long meram ;
2011-09-19 13:40:31 +04:00
unsigned long regs [ MERAM_REGS_SIZE ] ;
2011-09-19 13:40:31 +04:00
struct mutex lock ;
unsigned long used_icb ;
2011-09-19 13:40:31 +04:00
struct sh_mobile_meram_icb icbs [ MERAM_ICB_NUM ] ;
2011-09-19 13:40:31 +04:00
struct gen_pool * pool ;
2011-06-22 09:49:50 +04:00
} ;
2011-05-18 15:10:07 +04:00
/* settings */
2011-09-19 13:40:31 +04:00
# define MERAM_GRANULARITY 1024
2011-09-19 13:40:31 +04:00
# define MERAM_SEC_LINE 15
# define MERAM_LINE_WIDTH 2048
2011-05-18 15:10:07 +04:00
2011-09-19 13:40:31 +04:00
/* -----------------------------------------------------------------------------
* Registers access
2011-05-18 15:10:07 +04:00
*/
2011-07-13 14:13:47 +04:00
# define MERAM_ICB_OFFSET(base, idx, off) ((base) + (off) + (idx) * 0x20)
2011-05-18 15:10:07 +04:00
2011-09-19 13:40:31 +04:00
static inline void meram_write_icb ( void __iomem * base , unsigned int idx ,
unsigned int off , unsigned long val )
2011-05-18 15:10:07 +04:00
{
iowrite32 ( val , MERAM_ICB_OFFSET ( base , idx , off ) ) ;
}
2011-09-19 13:40:31 +04:00
static inline unsigned long meram_read_icb ( void __iomem * base , unsigned int idx ,
unsigned int off )
2011-05-18 15:10:07 +04:00
{
return ioread32 ( MERAM_ICB_OFFSET ( base , idx , off ) ) ;
}
2011-09-19 13:40:31 +04:00
static inline void meram_write_reg ( void __iomem * base , unsigned int off ,
unsigned long val )
2011-05-18 15:10:07 +04:00
{
iowrite32 ( val , base + off ) ;
}
2011-09-19 13:40:31 +04:00
static inline unsigned long meram_read_reg ( void __iomem * base , unsigned int off )
2011-05-18 15:10:07 +04:00
{
return ioread32 ( base + off ) ;
}
2012-03-15 16:18:17 +04:00
/* -----------------------------------------------------------------------------
* MERAM allocation and free
*/
static unsigned long meram_alloc ( struct sh_mobile_meram_priv * priv , size_t size )
{
return gen_pool_alloc ( priv - > pool , size ) ;
}
static void meram_free ( struct sh_mobile_meram_priv * priv , unsigned long mem ,
size_t size )
{
gen_pool_free ( priv - > pool , mem , size ) ;
}
2011-09-19 13:40:31 +04:00
/* -----------------------------------------------------------------------------
2012-03-15 15:40:47 +04:00
* LCDC cache planes allocation , init , cleanup and free
2011-05-18 15:10:07 +04:00
*/
2011-12-12 19:36:13 +04:00
/* Allocate ICBs and MERAM for a plane. */
2012-03-15 15:40:47 +04:00
static int meram_plane_alloc ( struct sh_mobile_meram_priv * priv ,
struct sh_mobile_meram_fb_plane * plane ,
size_t size )
2011-05-18 15:10:07 +04:00
{
2011-12-12 19:36:13 +04:00
unsigned long mem ;
unsigned long idx ;
2011-05-18 15:10:07 +04:00
2011-12-12 19:36:13 +04:00
idx = find_first_zero_bit ( & priv - > used_icb , 28 ) ;
if ( idx = = 28 )
return - ENOMEM ;
plane - > cache = & priv - > icbs [ idx ] ;
2011-05-18 15:10:07 +04:00
2011-12-12 19:36:13 +04:00
idx = find_next_zero_bit ( & priv - > used_icb , 32 , 28 ) ;
if ( idx = = 32 )
return - ENOMEM ;
plane - > marker = & priv - > icbs [ idx ] ;
2011-09-19 13:40:31 +04:00
2012-03-15 16:18:17 +04:00
mem = meram_alloc ( priv , size * 1024 ) ;
2011-09-19 13:40:31 +04:00
if ( mem = = 0 )
return - ENOMEM ;
2011-12-12 19:36:13 +04:00
__set_bit ( plane - > marker - > index , & priv - > used_icb ) ;
__set_bit ( plane - > cache - > index , & priv - > used_icb ) ;
2011-05-18 15:10:07 +04:00
2011-12-12 19:36:13 +04:00
plane - > marker - > offset = mem - priv - > meram ;
plane - > marker - > size = size ;
2011-09-19 13:40:31 +04:00
return 0 ;
2011-05-18 15:10:07 +04:00
}
2011-12-12 19:36:13 +04:00
/* Free ICBs and MERAM for a plane. */
2012-03-15 15:40:47 +04:00
static void meram_plane_free ( struct sh_mobile_meram_priv * priv ,
struct sh_mobile_meram_fb_plane * plane )
2011-05-18 15:10:07 +04:00
{
2012-03-15 16:18:17 +04:00
meram_free ( priv , priv - > meram + plane - > marker - > offset ,
plane - > marker - > size * 1024 ) ;
2011-09-19 13:40:31 +04:00
2011-12-12 19:36:13 +04:00
__clear_bit ( plane - > marker - > index , & priv - > used_icb ) ;
__clear_bit ( plane - > cache - > index , & priv - > used_icb ) ;
2011-05-18 15:10:07 +04:00
}
2011-09-19 13:40:31 +04:00
/* Is this a YCbCr(NV12, NV16 or NV24) colorspace? */
2011-09-19 13:40:31 +04:00
static int is_nvcolor ( int cspace )
2011-05-18 15:10:08 +04:00
{
if ( cspace = = SH_MOBILE_MERAM_PF_NV | |
2011-09-19 13:40:31 +04:00
cspace = = SH_MOBILE_MERAM_PF_NV24 )
2011-05-18 15:10:08 +04:00
return 1 ;
return 0 ;
}
2011-05-18 15:10:07 +04:00
2011-09-19 13:40:31 +04:00
/* Set the next address to fetch. */
2011-09-19 13:40:31 +04:00
static void meram_set_next_addr ( struct sh_mobile_meram_priv * priv ,
2011-12-12 19:36:13 +04:00
struct sh_mobile_meram_fb_cache * cache ,
2011-09-19 13:40:31 +04:00
unsigned long base_addr_y ,
unsigned long base_addr_c )
2011-05-18 15:10:07 +04:00
{
2011-12-12 19:36:13 +04:00
struct sh_mobile_meram_icb * icb = cache - > planes [ 0 ] . marker ;
2011-05-18 15:10:07 +04:00
unsigned long target ;
2011-09-19 13:40:31 +04:00
icb - > current_reg ^ = 1 ;
target = icb - > current_reg ? MExxSARB : MExxSARA ;
2011-05-18 15:10:07 +04:00
/* set the next address to fetch */
2011-12-12 19:36:13 +04:00
meram_write_icb ( priv - > base , cache - > planes [ 0 ] . cache - > index , target ,
2011-05-18 15:10:07 +04:00
base_addr_y ) ;
2011-12-12 19:36:13 +04:00
meram_write_icb ( priv - > base , cache - > planes [ 0 ] . marker - > index , target ,
base_addr_y + cache - > planes [ 0 ] . marker - > cache_unit ) ;
if ( cache - > nplanes = = 2 ) {
meram_write_icb ( priv - > base , cache - > planes [ 1 ] . cache - > index ,
target , base_addr_c ) ;
meram_write_icb ( priv - > base , cache - > planes [ 1 ] . marker - > index ,
target , base_addr_c +
cache - > planes [ 1 ] . marker - > cache_unit ) ;
2011-05-18 15:10:07 +04:00
}
}
2011-09-19 13:40:31 +04:00
/* Get the next ICB address. */
2011-09-19 13:40:31 +04:00
static void
2011-09-19 13:40:31 +04:00
meram_get_next_icb_addr ( struct sh_mobile_meram_info * pdata ,
2011-12-12 19:36:13 +04:00
struct sh_mobile_meram_fb_cache * cache ,
2011-09-19 13:40:31 +04:00
unsigned long * icb_addr_y , unsigned long * icb_addr_c )
2011-05-18 15:10:07 +04:00
{
2011-12-12 19:36:13 +04:00
struct sh_mobile_meram_icb * icb = cache - > planes [ 0 ] . marker ;
2011-05-18 15:10:07 +04:00
unsigned long icb_offset ;
if ( pdata - > addr_mode = = SH_MOBILE_MERAM_MODE0 )
2011-09-19 13:40:31 +04:00
icb_offset = 0x80000000 | ( icb - > current_reg < < 29 ) ;
2011-05-18 15:10:07 +04:00
else
2011-09-19 13:40:31 +04:00
icb_offset = 0xc0000000 | ( icb - > current_reg < < 23 ) ;
2011-05-18 15:10:07 +04:00
2011-12-12 19:36:13 +04:00
* icb_addr_y = icb_offset | ( cache - > planes [ 0 ] . marker - > index < < 24 ) ;
if ( cache - > nplanes = = 2 )
* icb_addr_c = icb_offset
| ( cache - > planes [ 1 ] . marker - > index < < 24 ) ;
2011-05-18 15:10:07 +04:00
}
# define MERAM_CALC_BYTECOUNT(x, y) \
( ( ( x ) * ( y ) + ( MERAM_LINE_WIDTH - 1 ) ) & ~ ( MERAM_LINE_WIDTH - 1 ) )
2011-09-19 13:40:31 +04:00
/* Initialize MERAM. */
2012-03-15 15:40:47 +04:00
static int meram_plane_init ( struct sh_mobile_meram_priv * priv ,
struct sh_mobile_meram_fb_plane * plane ,
unsigned int xres , unsigned int yres ,
unsigned int * out_pitch )
2011-05-18 15:10:07 +04:00
{
2011-12-12 19:36:13 +04:00
struct sh_mobile_meram_icb * marker = plane - > marker ;
2011-05-18 15:10:07 +04:00
unsigned long total_byte_count = MERAM_CALC_BYTECOUNT ( xres , yres ) ;
unsigned long bnm ;
2011-09-19 13:40:31 +04:00
unsigned int lcdc_pitch ;
unsigned int xpitch ;
unsigned int line_cnt ;
unsigned int save_lines ;
2011-05-18 15:10:07 +04:00
/* adjust pitch to 1024, 2048, 4096 or 8192 */
lcdc_pitch = ( xres - 1 ) | 1023 ;
lcdc_pitch = lcdc_pitch | ( lcdc_pitch > > 1 ) ;
lcdc_pitch = lcdc_pitch | ( lcdc_pitch > > 2 ) ;
lcdc_pitch + = 1 ;
/* derive settings */
if ( lcdc_pitch = = 8192 & & yres > = 1024 ) {
lcdc_pitch = xpitch = MERAM_LINE_WIDTH ;
line_cnt = total_byte_count > > 11 ;
* out_pitch = xres ;
2011-12-12 19:36:13 +04:00
save_lines = plane - > marker - > size / 16 / MERAM_SEC_LINE ;
2011-05-18 15:10:07 +04:00
save_lines * = MERAM_SEC_LINE ;
} else {
xpitch = xres ;
line_cnt = yres ;
* out_pitch = lcdc_pitch ;
2011-12-12 19:36:13 +04:00
save_lines = plane - > marker - > size / ( lcdc_pitch > > 10 ) / 2 ;
2011-05-18 15:10:07 +04:00
save_lines & = 0xff ;
}
bnm = ( save_lines - 1 ) < < 16 ;
/* TODO: we better to check if we have enough MERAM buffer size */
/* set up ICB */
2011-12-12 19:36:13 +04:00
meram_write_icb ( priv - > base , plane - > cache - > index , MExxBSIZE ,
2011-05-18 15:10:07 +04:00
MERAM_MExxBSIZE_VAL ( 0x0 , line_cnt - 1 , xpitch - 1 ) ) ;
2011-12-12 19:36:13 +04:00
meram_write_icb ( priv - > base , plane - > marker - > index , MExxBSIZE ,
2011-05-18 15:10:07 +04:00
MERAM_MExxBSIZE_VAL ( 0xf , line_cnt - 1 , xpitch - 1 ) ) ;
2011-12-12 19:36:13 +04:00
meram_write_icb ( priv - > base , plane - > cache - > index , MExxMNCF , bnm ) ;
meram_write_icb ( priv - > base , plane - > marker - > index , MExxMNCF , bnm ) ;
2011-05-18 15:10:07 +04:00
2011-12-12 19:36:13 +04:00
meram_write_icb ( priv - > base , plane - > cache - > index , MExxSBSIZE , xpitch ) ;
meram_write_icb ( priv - > base , plane - > marker - > index , MExxSBSIZE , xpitch ) ;
2011-05-18 15:10:07 +04:00
/* save a cache unit size */
2011-12-12 19:36:13 +04:00
plane - > cache - > cache_unit = xres * save_lines ;
plane - > marker - > cache_unit = xres * save_lines ;
2011-05-18 15:10:07 +04:00
/*
* Set MERAM for framebuffer
*
* we also chain the cache_icb and the marker_icb .
* we also split the allocated MERAM buffer between two ICBs .
*/
2011-12-12 19:36:13 +04:00
meram_write_icb ( priv - > base , plane - > cache - > index , MExxCTL ,
MERAM_MExxCTL_VAL ( plane - > marker - > index , marker - > offset )
| MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM |
2011-07-13 14:13:47 +04:00
MExxCTL_MD_FB ) ;
2011-12-12 19:36:13 +04:00
meram_write_icb ( priv - > base , plane - > marker - > index , MExxCTL ,
MERAM_MExxCTL_VAL ( plane - > cache - > index , marker - > offset +
plane - > marker - > size / 2 ) |
2011-07-13 14:13:47 +04:00
MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM |
MExxCTL_MD_FB ) ;
2011-05-18 15:10:07 +04:00
return 0 ;
}
2012-03-15 15:40:47 +04:00
static void meram_plane_cleanup ( struct sh_mobile_meram_priv * priv ,
struct sh_mobile_meram_fb_plane * plane )
2011-05-18 15:10:07 +04:00
{
/* disable ICB */
2011-12-12 19:36:13 +04:00
meram_write_icb ( priv - > base , plane - > cache - > index , MExxCTL ,
2011-08-31 15:00:52 +04:00
MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF ) ;
2011-12-12 19:36:13 +04:00
meram_write_icb ( priv - > base , plane - > marker - > index , MExxCTL ,
2011-08-31 15:00:52 +04:00
MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF ) ;
2011-09-19 13:40:31 +04:00
2011-12-12 19:36:13 +04:00
plane - > cache - > cache_unit = 0 ;
plane - > marker - > cache_unit = 0 ;
2011-05-18 15:10:07 +04:00
}
2011-09-19 13:40:31 +04:00
/* -----------------------------------------------------------------------------
2012-03-15 16:18:17 +04:00
* MERAM operations
2011-05-18 15:10:07 +04:00
*/
2012-03-15 16:18:17 +04:00
unsigned long sh_mobile_meram_alloc ( struct sh_mobile_meram_info * pdata ,
size_t size )
{
struct sh_mobile_meram_priv * priv = pdata - > priv ;
return meram_alloc ( priv , size ) ;
}
EXPORT_SYMBOL_GPL ( sh_mobile_meram_alloc ) ;
void sh_mobile_meram_free ( struct sh_mobile_meram_info * pdata , unsigned long mem ,
size_t size )
{
struct sh_mobile_meram_priv * priv = pdata - > priv ;
meram_free ( priv , mem , size ) ;
}
EXPORT_SYMBOL_GPL ( sh_mobile_meram_free ) ;
2012-03-15 15:40:47 +04:00
/* Allocate memory for the ICBs and mark them as used. */
static struct sh_mobile_meram_fb_cache *
meram_cache_alloc ( struct sh_mobile_meram_priv * priv ,
const struct sh_mobile_meram_cfg * cfg ,
int pixelformat )
{
unsigned int nplanes = is_nvcolor ( pixelformat ) ? 2 : 1 ;
struct sh_mobile_meram_fb_cache * cache ;
int ret ;
cache = kzalloc ( sizeof ( * cache ) , GFP_KERNEL ) ;
if ( cache = = NULL )
return ERR_PTR ( - ENOMEM ) ;
cache - > nplanes = nplanes ;
ret = meram_plane_alloc ( priv , & cache - > planes [ 0 ] ,
cfg - > icb [ 0 ] . meram_size ) ;
if ( ret < 0 )
goto error ;
cache - > planes [ 0 ] . marker - > current_reg = 1 ;
cache - > planes [ 0 ] . marker - > pixelformat = pixelformat ;
if ( cache - > nplanes = = 1 )
return cache ;
ret = meram_plane_alloc ( priv , & cache - > planes [ 1 ] ,
cfg - > icb [ 1 ] . meram_size ) ;
if ( ret < 0 ) {
meram_plane_free ( priv , & cache - > planes [ 0 ] ) ;
goto error ;
}
return cache ;
error :
kfree ( cache ) ;
return ERR_PTR ( - ENOMEM ) ;
}
2012-03-15 15:40:47 +04:00
void * sh_mobile_meram_cache_alloc ( struct sh_mobile_meram_info * pdata ,
const struct sh_mobile_meram_cfg * cfg ,
unsigned int xres , unsigned int yres ,
unsigned int pixelformat , unsigned int * pitch )
2011-05-18 15:10:07 +04:00
{
2011-12-12 19:36:13 +04:00
struct sh_mobile_meram_fb_cache * cache ;
2011-11-22 03:56:58 +04:00
struct sh_mobile_meram_priv * priv = pdata - > priv ;
struct platform_device * pdev = pdata - > pdev ;
2012-03-15 15:40:47 +04:00
unsigned int nplanes = is_nvcolor ( pixelformat ) ? 2 : 1 ;
2011-09-19 13:40:31 +04:00
unsigned int out_pitch ;
2011-05-18 15:10:07 +04:00
2012-03-15 15:40:47 +04:00
if ( priv = = NULL )
return ERR_PTR ( - ENODEV ) ;
2011-05-18 15:10:07 +04:00
if ( pixelformat ! = SH_MOBILE_MERAM_PF_NV & &
2011-05-18 15:10:08 +04:00
pixelformat ! = SH_MOBILE_MERAM_PF_NV24 & &
2011-05-18 15:10:07 +04:00
pixelformat ! = SH_MOBILE_MERAM_PF_RGB )
2011-12-12 19:36:13 +04:00
return ERR_PTR ( - EINVAL ) ;
2011-05-18 15:10:07 +04:00
2011-11-22 03:56:58 +04:00
dev_dbg ( & pdev - > dev , " registering %dx%d (%s) " , xres , yres ,
! pixelformat ? " yuv " : " rgb " ) ;
2011-05-18 15:10:07 +04:00
/* we can't handle wider than 8192px */
if ( xres > 8192 ) {
dev_err ( & pdev - > dev , " width exceeding the limit (> 8192). " ) ;
2011-12-12 19:36:13 +04:00
return ERR_PTR ( - EINVAL ) ;
2011-07-13 14:13:47 +04:00
}
2012-03-15 15:40:47 +04:00
if ( cfg - > icb [ 0 ] . meram_size = = 0 )
return ERR_PTR ( - EINVAL ) ;
if ( nplanes = = 2 & & cfg - > icb [ 1 ] . meram_size = = 0 )
return ERR_PTR ( - EINVAL ) ;
2011-07-13 14:13:47 +04:00
mutex_lock ( & priv - > lock ) ;
2011-09-19 13:40:31 +04:00
/* We now register the ICBs and allocate the MERAM regions. */
2012-03-15 15:40:47 +04:00
cache = meram_cache_alloc ( priv , cfg , pixelformat ) ;
2011-12-12 19:36:13 +04:00
if ( IS_ERR ( cache ) ) {
dev_err ( & pdev - > dev , " MERAM allocation failed (%ld). " ,
PTR_ERR ( cache ) ) ;
2011-09-19 13:40:31 +04:00
goto err ;
}
2011-05-18 15:10:07 +04:00
/* initialize MERAM */
2012-03-15 15:40:47 +04:00
meram_plane_init ( priv , & cache - > planes [ 0 ] , xres , yres , & out_pitch ) ;
2011-05-18 15:10:07 +04:00
* pitch = out_pitch ;
if ( pixelformat = = SH_MOBILE_MERAM_PF_NV )
2012-03-15 15:40:47 +04:00
meram_plane_init ( priv , & cache - > planes [ 1 ] ,
xres , ( yres + 1 ) / 2 , & out_pitch ) ;
2011-05-18 15:10:08 +04:00
else if ( pixelformat = = SH_MOBILE_MERAM_PF_NV24 )
2012-03-15 15:40:47 +04:00
meram_plane_init ( priv , & cache - > planes [ 1 ] ,
2 * xres , ( yres + 1 ) / 2 , & out_pitch ) ;
2011-05-18 15:10:07 +04:00
err :
mutex_unlock ( & priv - > lock ) ;
2011-12-12 19:36:13 +04:00
return cache ;
2011-05-18 15:10:07 +04:00
}
2012-03-15 15:40:47 +04:00
EXPORT_SYMBOL_GPL ( sh_mobile_meram_cache_alloc ) ;
2011-05-18 15:10:07 +04:00
2012-03-15 15:40:47 +04:00
void
sh_mobile_meram_cache_free ( struct sh_mobile_meram_info * pdata , void * data )
2011-05-18 15:10:07 +04:00
{
2011-12-12 19:36:13 +04:00
struct sh_mobile_meram_fb_cache * cache = data ;
2011-11-22 03:56:58 +04:00
struct sh_mobile_meram_priv * priv = pdata - > priv ;
2011-05-18 15:10:07 +04:00
mutex_lock ( & priv - > lock ) ;
2012-03-15 15:40:47 +04:00
/* Cleanup and free. */
meram_plane_cleanup ( priv , & cache - > planes [ 0 ] ) ;
meram_plane_free ( priv , & cache - > planes [ 0 ] ) ;
2011-12-12 19:36:13 +04:00
2012-03-15 15:40:47 +04:00
if ( cache - > nplanes = = 2 ) {
meram_plane_cleanup ( priv , & cache - > planes [ 1 ] ) ;
meram_plane_free ( priv , & cache - > planes [ 1 ] ) ;
}
kfree ( cache ) ;
2011-05-18 15:10:07 +04:00
mutex_unlock ( & priv - > lock ) ;
}
2012-03-15 15:40:47 +04:00
EXPORT_SYMBOL_GPL ( sh_mobile_meram_cache_free ) ;
void
sh_mobile_meram_cache_update ( struct sh_mobile_meram_info * pdata , void * data ,
unsigned long base_addr_y ,
unsigned long base_addr_c ,
unsigned long * icb_addr_y ,
unsigned long * icb_addr_c )
2011-05-18 15:10:07 +04:00
{
2011-12-12 19:36:13 +04:00
struct sh_mobile_meram_fb_cache * cache = data ;
2011-11-22 03:56:58 +04:00
struct sh_mobile_meram_priv * priv = pdata - > priv ;
2011-05-18 15:10:07 +04:00
mutex_lock ( & priv - > lock ) ;
2011-12-12 19:36:13 +04:00
meram_set_next_addr ( priv , cache , base_addr_y , base_addr_c ) ;
meram_get_next_icb_addr ( pdata , cache , icb_addr_y , icb_addr_c ) ;
2011-05-18 15:10:07 +04:00
mutex_unlock ( & priv - > lock ) ;
}
2012-03-15 15:40:47 +04:00
EXPORT_SYMBOL_GPL ( sh_mobile_meram_cache_update ) ;
2011-09-19 13:40:31 +04:00
/* -----------------------------------------------------------------------------
* Power management
*/
2011-11-22 03:56:58 +04:00
static int sh_mobile_meram_suspend ( struct device * dev )
2011-06-22 09:49:51 +04:00
{
struct platform_device * pdev = to_platform_device ( dev ) ;
struct sh_mobile_meram_priv * priv = platform_get_drvdata ( pdev ) ;
2011-09-19 13:40:31 +04:00
unsigned int i , j ;
2011-06-22 09:49:51 +04:00
2011-09-19 13:40:31 +04:00
for ( i = 0 ; i < MERAM_REGS_SIZE ; i + + )
2011-09-19 13:40:31 +04:00
priv - > regs [ i ] = meram_read_reg ( priv - > base , common_regs [ i ] ) ;
2011-06-22 09:49:51 +04:00
2011-09-19 13:40:31 +04:00
for ( i = 0 ; i < 32 ; i + + ) {
if ( ! test_bit ( i , & priv - > used_icb ) )
2011-06-22 09:49:51 +04:00
continue ;
2011-09-19 13:40:31 +04:00
for ( j = 0 ; j < ICB_REGS_SIZE ; j + + ) {
2011-09-19 13:40:31 +04:00
priv - > icbs [ i ] . regs [ j ] =
2011-09-19 13:40:31 +04:00
meram_read_icb ( priv - > base , i , icb_regs [ j ] ) ;
2011-06-22 09:49:51 +04:00
/* Reset ICB on resume */
2011-09-19 13:40:31 +04:00
if ( icb_regs [ j ] = = MExxCTL )
2011-09-19 13:40:31 +04:00
priv - > icbs [ i ] . regs [ j ] | =
2011-07-13 14:13:47 +04:00
MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF ;
2011-06-22 09:49:51 +04:00
}
}
return 0 ;
}
2011-11-22 03:56:58 +04:00
static int sh_mobile_meram_resume ( struct device * dev )
2011-06-22 09:49:51 +04:00
{
struct platform_device * pdev = to_platform_device ( dev ) ;
struct sh_mobile_meram_priv * priv = platform_get_drvdata ( pdev ) ;
2011-09-19 13:40:31 +04:00
unsigned int i , j ;
2011-06-22 09:49:51 +04:00
2011-09-19 13:40:31 +04:00
for ( i = 0 ; i < 32 ; i + + ) {
if ( ! test_bit ( i , & priv - > used_icb ) )
2011-06-22 09:49:51 +04:00
continue ;
2011-09-19 13:40:31 +04:00
for ( j = 0 ; j < ICB_REGS_SIZE ; j + + )
2011-09-19 13:40:31 +04:00
meram_write_icb ( priv - > base , i , icb_regs [ j ] ,
2011-09-19 13:40:31 +04:00
priv - > icbs [ i ] . regs [ j ] ) ;
2011-06-22 09:49:51 +04:00
}
2011-09-19 13:40:31 +04:00
for ( i = 0 ; i < MERAM_REGS_SIZE ; i + + )
2011-09-19 13:40:31 +04:00
meram_write_reg ( priv - > base , common_regs [ i ] , priv - > regs [ i ] ) ;
2011-06-22 09:49:51 +04:00
return 0 ;
}
2011-11-22 03:56:58 +04:00
static UNIVERSAL_DEV_PM_OPS ( sh_mobile_meram_dev_pm_ops ,
sh_mobile_meram_suspend ,
sh_mobile_meram_resume , NULL ) ;
2011-06-22 09:49:51 +04:00
2011-09-19 13:40:31 +04:00
/* -----------------------------------------------------------------------------
* Probe / remove and driver init / exit
2011-05-18 15:10:07 +04:00
*/
2012-12-22 01:07:39 +04:00
static int sh_mobile_meram_probe ( struct platform_device * pdev )
2011-05-18 15:10:07 +04:00
{
struct sh_mobile_meram_priv * priv ;
struct sh_mobile_meram_info * pdata = pdev - > dev . platform_data ;
2011-09-19 13:40:31 +04:00
struct resource * regs ;
struct resource * meram ;
2011-12-12 19:36:13 +04:00
unsigned int i ;
2011-05-18 15:10:07 +04:00
int error ;
if ( ! pdata ) {
dev_err ( & pdev - > dev , " no platform data defined \n " ) ;
return - EINVAL ;
}
2011-09-19 13:40:31 +04:00
regs = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
meram = platform_get_resource ( pdev , IORESOURCE_MEM , 1 ) ;
if ( regs = = NULL | | meram = = NULL ) {
2011-05-18 15:10:07 +04:00
dev_err ( & pdev - > dev , " cannot get platform resources \n " ) ;
return - ENOENT ;
}
priv = kzalloc ( sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv ) {
dev_err ( & pdev - > dev , " cannot allocate device data \n " ) ;
return - ENOMEM ;
}
2011-12-12 19:36:13 +04:00
/* Initialize private data. */
2011-05-18 15:10:07 +04:00
mutex_init ( & priv - > lock ) ;
2011-12-12 19:36:13 +04:00
priv - > used_icb = pdata - > reserved_icbs ;
for ( i = 0 ; i < MERAM_ICB_NUM ; + + i )
priv - > icbs [ i ] . index = i ;
2011-09-19 13:40:31 +04:00
pdata - > priv = priv ;
pdata - > pdev = pdev ;
2011-09-19 13:40:31 +04:00
/* Request memory regions and remap the registers. */
2011-09-19 13:40:31 +04:00
if ( ! request_mem_region ( regs - > start , resource_size ( regs ) , pdev - > name ) ) {
dev_err ( & pdev - > dev , " MERAM registers region already claimed \n " ) ;
error = - EBUSY ;
goto err_req_regs ;
}
if ( ! request_mem_region ( meram - > start , resource_size ( meram ) ,
pdev - > name ) ) {
dev_err ( & pdev - > dev , " MERAM memory region already claimed \n " ) ;
error = - EBUSY ;
goto err_req_meram ;
}
priv - > base = ioremap_nocache ( regs - > start , resource_size ( regs ) ) ;
2011-05-18 15:10:07 +04:00
if ( ! priv - > base ) {
dev_err ( & pdev - > dev , " ioremap failed \n " ) ;
error = - EFAULT ;
2011-09-19 13:40:31 +04:00
goto err_ioremap ;
2011-05-18 15:10:07 +04:00
}
2011-09-19 13:40:31 +04:00
priv - > meram = meram - > start ;
/* Create and initialize the MERAM memory pool. */
priv - > pool = gen_pool_create ( ilog2 ( MERAM_GRANULARITY ) , - 1 ) ;
if ( priv - > pool = = NULL ) {
error = - ENOMEM ;
goto err_genpool ;
}
error = gen_pool_add ( priv - > pool , meram - > start , resource_size ( meram ) ,
- 1 ) ;
if ( error < 0 )
goto err_genpool ;
2011-05-18 15:10:07 +04:00
/* initialize ICB addressing mode */
if ( pdata - > addr_mode = = SH_MOBILE_MERAM_MODE1 )
2011-07-13 14:13:47 +04:00
meram_write_reg ( priv - > base , MEVCR1 , MEVCR1_AMD1 ) ;
2011-05-18 15:10:07 +04:00
2011-09-19 13:40:31 +04:00
platform_set_drvdata ( pdev , priv ) ;
2011-07-04 10:06:11 +04:00
pm_runtime_enable ( & pdev - > dev ) ;
2011-05-18 15:10:07 +04:00
dev_info ( & pdev - > dev , " sh_mobile_meram initialized. " ) ;
return 0 ;
2011-09-19 13:40:31 +04:00
err_genpool :
if ( priv - > pool )
gen_pool_destroy ( priv - > pool ) ;
iounmap ( priv - > base ) ;
2011-09-19 13:40:31 +04:00
err_ioremap :
release_mem_region ( meram - > start , resource_size ( meram ) ) ;
err_req_meram :
release_mem_region ( regs - > start , resource_size ( regs ) ) ;
err_req_regs :
mutex_destroy ( & priv - > lock ) ;
kfree ( priv ) ;
2011-05-18 15:10:07 +04:00
return error ;
}
static int sh_mobile_meram_remove ( struct platform_device * pdev )
{
struct sh_mobile_meram_priv * priv = platform_get_drvdata ( pdev ) ;
2011-09-19 13:40:31 +04:00
struct resource * regs = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
struct resource * meram = platform_get_resource ( pdev , IORESOURCE_MEM , 1 ) ;
2011-05-18 15:10:07 +04:00
2011-07-04 10:06:11 +04:00
pm_runtime_disable ( & pdev - > dev ) ;
2011-09-19 13:40:31 +04:00
gen_pool_destroy ( priv - > pool ) ;
2011-09-19 13:40:31 +04:00
iounmap ( priv - > base ) ;
release_mem_region ( meram - > start , resource_size ( meram ) ) ;
release_mem_region ( regs - > start , resource_size ( regs ) ) ;
2011-05-18 15:10:07 +04:00
mutex_destroy ( & priv - > lock ) ;
kfree ( priv ) ;
return 0 ;
}
static struct platform_driver sh_mobile_meram_driver = {
. driver = {
. name = " sh_mobile_meram " ,
. owner = THIS_MODULE ,
2011-06-22 09:49:51 +04:00
. pm = & sh_mobile_meram_dev_pm_ops ,
2011-05-18 15:10:07 +04:00
} ,
. probe = sh_mobile_meram_probe ,
. remove = sh_mobile_meram_remove ,
} ;
2011-11-26 06:25:54 +04:00
module_platform_driver ( sh_mobile_meram_driver ) ;
2011-05-18 15:10:07 +04:00
MODULE_DESCRIPTION ( " SuperH Mobile MERAM driver " ) ;
MODULE_AUTHOR ( " Damian Hobson-Garcia / Takanari Hayama " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;