rk30fb :fix a bug in rk30_lcdc.c
[firefly-linux-kernel-4.4.55.git] / drivers / video / rockchip / rk_fb.c
index 6010f020d775bb8c00e07b8d3d109c7e32a9f6ee..480115c1976140917574619bc3cdfff591b8088e 100644 (file)
@@ -2,7 +2,8 @@
  * drivers/video/rockchip/rk_fb.c
  *
  * Copyright (C) 2012 ROCKCHIP, Inc.
- *
+ *Author:yzq<yzq@rock-chips.com>
+       yxj<yxj@rock-chips.com>
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
  * may be copied, distributed, and modified under those terms.
 #include <linux/device.h>
 #include <linux/fb.h>
 #include <linux/init.h>
-#include <linux/dma-mapping.h>
-#include <linux/interrupt.h>
 #include <linux/platform_device.h>
-#include <linux/clk.h>
-#include <linux/backlight.h>
-#include <linux/timer.h>
-#include <linux/time.h>
-#include <linux/wait.h>
 #include <linux/earlysuspend.h>
-#include <linux/cpufreq.h>
-#include <linux/wakelock.h>
-
-#include <asm/io.h>
 #include <asm/div64.h>
 #include <asm/uaccess.h>
-#include <asm/cacheflush.h>
-
-#include <mach/iomux.h>
-#include <mach/gpio.h>
-#include <mach/board.h>
-#include <mach/pmu.h>
+#include<linux/rk_fb.h>
 
-#include "../display/screen/screen.h"
-#include "rk_fb.h"
 
 #if 0
        #define fbprintk(msg...)        printk(msg);
        #define fbprintk(msg...)
 #endif
 
-#if 0
-#define CHK_SUSPEND(inf)       \
-       if(inf->in_suspend)     {       \
-               fbprintk(">>>>>> fb is in suspend! return! \n");        \
+#if 1
+#define CHK_SUSPEND(drv)       \
+       if(atomic_dec_and_test(&drv->in_suspend))       {       \
+               printk(">>>>>> fb is in suspend! return! \n");  \
                return -EPERM;  \
        }
 #else
@@ -84,67 +67,158 @@ defautl:we alloc three buffer,one for fb0 and fb2 display ui,one for ipp rotate
         fb1 and fb3 are used for video play,the buffer is alloc by android,and
         pass the phy addr to fix.smem_start by ioctl
 ****************************************************************************/
-static int rk_fb_open(struct fb_info *info,int user)
+
+int get_fb_layer_id(struct fb_fix_screeninfo *fix)
 {
-    struct rk_fb_inf *inf = dev_get_drvdata(info->device);
-    struct fb_fix_screeninfo *fix = &info->fix;
-    if(!strcmp(fix->id,"fb1")){
-        inf->video_mode = 1;
-    }
+       int layer_id;
+       if(!strcmp(fix->id,"fb1")||!strcmp(fix->id,"fb3"))
+       {
+               layer_id = 0;
+       }
+       else if(!strcmp(fix->id,"fb0")||!strcmp(fix->id,"fb2"))
+       {
+               layer_id = 1;
+       }
+       else
+       {
+               printk(KERN_ERR "unsupported %s",fix->id);
+               layer_id = -ENODEV;
+       }
 
+       return layer_id;
+}
+
+/**********************************************************************
+this is for hdmi
+name: lcdc device name ,lcdc0 , lcdc1
+***********************************************************************/
+struct rk_lcdc_device_driver * rk_get_lcdc_drv(char *name)
+{
+       struct rk_fb_inf *inf =  platform_get_drvdata(g_fb_pdev);
+       int i = 0;
+       for( i = 0; i < inf->num_lcdc; i++)
+       {
+               if(!strcmp(inf->lcdc_dev_drv[i]->name,name))
+                       break;
+       }
+       return inf->lcdc_dev_drv[i];
+       
+}
+static int rk_fb_open(struct fb_info *info,int user)
+{
+    struct rk_lcdc_device_driver * dev_drv = (struct rk_lcdc_device_driver * )info->par;
+    int layer_id;
+    CHK_SUSPEND(dev_drv);
+    layer_id = get_fb_layer_id(&info->fix);
+    dev_drv->open(dev_drv,layer_id,1);
+    
     return 0;
     
 }
 
-static int rk_fb_release(struct fb_info *info,int user)
+static int rk_fb_close(struct fb_info *info,int user)
 {
-    struct rk_fb_inf *inf = dev_get_drvdata(info->device);
-    struct fb_fix_screeninfo *fix = &info->fix;
-    if(!strcmp(fix->id,"fb1")){
-        inf->video_mode = 0;
-    }
+       struct rk_lcdc_device_driver * dev_drv = (struct rk_lcdc_device_driver * )info->par;
+       int layer_id;
+       CHK_SUSPEND(dev_drv);
+       layer_id = get_fb_layer_id(&info->fix);
+       dev_drv->open(dev_drv,layer_id,0);
+       
+       return 0;
+}
 
-    return 0;
+static int rk_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+       struct fb_fix_screeninfo *fix = &info->fix;
+       struct rk_lcdc_device_driver * dev_drv = (struct rk_lcdc_device_driver * )info->par;
+       struct layer_par *par = NULL;
+       int layer_id = 0;
+       u32 xoffset = var->xoffset;             // offset from virtual to visible 
+       u32 yoffset = var->yoffset;                             
+       u32 xvir = var->xres_virtual;
+       u8 data_format = var->nonstd&0xff;
+       layer_id = get_fb_layer_id(fix);
+       CHK_SUSPEND(dev_drv);
+       if(layer_id < 0)
+       {
+               return  -ENODEV;
+       }
+       else
+       {
+                par = dev_drv->layer_par[layer_id];
+       }
+       switch (par->format)
+       {
+               case ARGB888:
+                       par->y_offset = (yoffset*xvir + xoffset)*4;
+                       break;
+               case  RGB888:
+                       par->y_offset = (yoffset*xvir + xoffset)*3;
+                       break;
+               case RGB565:
+                       par->y_offset = (yoffset*xvir + xoffset)*2;
+                       break;
+               case  YUV422:
+                       par->y_offset = yoffset*xvir + xoffset;
+                       par->c_offset = par->y_offset;
+                       break;
+               case  YUV420:
+                       par->y_offset = yoffset*xvir + xoffset;
+                       par->c_offset = (yoffset>>1)*xvir + xoffset;
+                       break;
+               case  YUV444 : // yuv444
+                       par->y_offset = yoffset*xvir + xoffset;
+                       par->c_offset = yoffset*2*xvir +(xoffset<<1);
+                       break;
+               default:
+                       printk("un supported format:0x%x\n",data_format);
+                       return -EINVAL;
+       }
+       
+       dev_drv->pan_display(dev_drv,layer_id);
+       
+       return 0;
 }
-static int rk_fb_ioctl(struct fb_info *info, unsigned int cmd,
-                       unsigned long arg)
+static int rk_fb_ioctl(struct fb_info *info, unsigned int cmd,unsigned long arg)
 {
-    struct rk_fb_inf *inf = dev_get_drvdata(info->device);
-    u32 yuv_phy[2];
-    fbprintk(">>>>>> %s : cmd:0x%x \n",__FUNCTION__,cmd);
-
-       CHK_SUSPEND(inf);
-
-    switch(cmd)
-    {
-        case FB0_IOCTL_STOP_TIMER_FLUSH:    //stop timer flush mcu panel after android is runing
-            break;
-        case FBIOPUT_16OR32:
-               break;
-        case FBIOGET_16OR32:
-            break;
-        case FBIOGET_IDLEFBUff_16OR32:
-        case FBIOSET_COMPOSE_LAYER_COUNTS:
-            break;
-        case FBIOGET_COMPOSE_LAYER_COUNTS:
-           case FBIOPUT_FBPHYADD:
-            return info->fix.smem_start;
-        case FB1_IOCTL_SET_YUV_ADDR:
-            if (copy_from_user(yuv_phy, arg, 8))
-                           return -EFAULT;
-            info->fix.smem_start = yuv_phy[0];
-            info->fix.mmio_start = yuv_phy[1];
-            break;
-        case FBIOGET_OVERLAY_STATE:
-            return inf->video_mode;
-        case FBIOGET_SCREEN_STATE:
-        case FBIOPUT_SET_CURSOR_EN:
-        case FBIOPUT_SET_CURSOR_POS:
-        case FBIOPUT_SET_CURSOR_IMG:
-        case FBIOPUT_SET_CURSOR_CMAP:
-        case FBIOPUT_GET_CURSOR_RESOLUTION:
-        case FBIOPUT_GET_CURSOR_EN:
-        default:
+       struct rk_fb_inf *inf = dev_get_drvdata(info->device);
+       struct fb_fix_screeninfo *fix = &info->fix;
+       struct rk_lcdc_device_driver *dev_drv = (struct rk_lcdc_device_driver * )info->par;
+       u32 yuv_phy[2];
+       void __user *argp = (void __user *)arg;
+       fbprintk(">>>>>> %s : cmd:0x%x \n",__FUNCTION__,cmd);
+       
+       CHK_SUSPEND(dev_drv);
+       switch(cmd)
+       {
+               case FBIOPUT_FBPHYADD:
+                       return info->fix.smem_start;
+               case FB1_IOCTL_SET_YUV_ADDR:   //when in video mode, buff alloc by android
+                       if((!strcmp(fix->id,"fb1"))||(!strcmp(fix->id,"fb3")))
+                       {
+                               if (copy_from_user(yuv_phy, argp, 8))
+                                       return -EFAULT;
+                               info->fix.smem_start = yuv_phy[0];  //four y
+                               info->fix.mmio_start = yuv_phy[1];  //four uv
+                       }
+                       break;
+               case FBIOGET_OVERLAY_STATE:
+                       return inf->video_mode;
+               case FBIOGET_SCREEN_STATE:
+               case FBIOPUT_SET_CURSOR_EN:
+               case FBIOPUT_SET_CURSOR_POS:
+               case FBIOPUT_SET_CURSOR_IMG:
+               case FBIOPUT_SET_CURSOR_CMAP:
+               case FBIOPUT_GET_CURSOR_RESOLUTION:
+               case FBIOPUT_GET_CURSOR_EN:
+               case FB0_IOCTL_STOP_TIMER_FLUSH:    //stop timer flush mcu panel after android is runing
+               case FBIOPUT_16OR32:
+               case FBIOGET_16OR32:
+               case FBIOGET_IDLEFBUff_16OR32:
+               case FBIOSET_COMPOSE_LAYER_COUNTS:
+               case FBIOGET_COMPOSE_LAYER_COUNTS:
+               default:
+                       dev_drv->ioctl(dev_drv,cmd,arg,0);
             break;
     }
     return 0;
@@ -152,34 +226,25 @@ static int rk_fb_ioctl(struct fb_info *info, unsigned int cmd,
 
 static int rk_fb_blank(int blank_mode, struct fb_info *info)
 {
-    struct rk_fb_inf *inf = dev_get_drvdata(info->device);
-    struct rk_lcdc_device_driver *dev_drv = NULL;
-    struct fb_fix_screeninfo *fix = &info->fix;
-    int layer_id;
-    if(!strcmp(fix->id,"fb1")){
-        dev_drv = inf->rk_lcdc_device[0];
-        layer_id = 0;
-    }else if(!strcmp(fix->id,"fb0")){
-        dev_drv = inf->rk_lcdc_device[0];
-        layer_id = 1;
-    }else if(!strcmp(fix->id,"fb3")){
-        dev_drv = inf->rk_lcdc_device[1];
-        layer_id = 0;
-    }else if(!strcmp(fix->id,"fb2")){
-        dev_drv = inf->rk_lcdc_device[1];
-        layer_id = 1;
-    }else{
-        dev_drv = inf->rk_lcdc_device[0];
-        layer_id = 0;
-    }
-    dev_drv->blank(dev_drv,layer_id,blank_mode);
+       struct rk_lcdc_device_driver *dev_drv = (struct rk_lcdc_device_driver * )info->par;
+       struct fb_fix_screeninfo *fix = &info->fix;
+       int layer_id;
+       CHK_SUSPEND(dev_drv);
+       layer_id = get_fb_layer_id(fix);
+       if(layer_id < 0)
+       {
+               return  -ENODEV;
+       }
+       
+       dev_drv->blank(dev_drv,layer_id,blank_mode);
 
-    return 0;
+       return 0;
 }
 
 static int rk_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
 {
-       CHK_SUSPEND(inf);
+       struct rk_lcdc_device_driver *dev_drv = (struct rk_lcdc_device_driver * )info->par;
+       CHK_SUSPEND(dev_drv);
  
         if( 0==var->xres_virtual || 0==var->yres_virtual ||
                 0==var->xres || 0==var->yres || var->xres<16 ||
@@ -258,52 +323,35 @@ static int rk_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
 
 static int rk_fb_set_par(struct fb_info *info)
 {
-    struct rk_fb_inf *inf = dev_get_drvdata(info->device);
     struct fb_var_screeninfo *var = &info->var;
     struct fb_fix_screeninfo *fix = &info->fix;
-    struct rk_lcdc_device_driver * dev_drv = NULL;
+    struct rk_lcdc_device_driver * dev_drv = (struct rk_lcdc_device_driver * )info->par;
     struct layer_par *par = NULL;
-    rk_screen *screen = NULL;
+    rk_screen *screen =dev_drv->screen;
     int layer_id = 0;  
-    u32 smem_len=0,map_size = 0;
     u32 cblen = 0,crlen = 0;
-    u32 xvir = var->xres_virtual;              /* virtual resolution           */
-    u32 yvir = var->yres_virtual;
-    u32 xoffset = var->xoffset;                        /* offset from virtual to visible */
-    u32 yoffset = var->yoffset;                        /* resolution                   */
-
-    
-    u16 xpos = (var->nonstd>>8) & 0xfff;      //visiable pos in panel
+    u16 xsize =0,ysize = 0;              //winx display window height/width --->LCDC_WINx_DSP_INFO
+    u32 xoffset = var->xoffset;                        // offset from virtual to visible 
+    u32 yoffset = var->yoffset;                        //resolution                    
+    u16 xpos = (var->nonstd>>8) & 0xfff; //visiable pos in panel
     u16 ypos = (var->nonstd>>20) & 0xfff;
-    u16 xsize =0,ysize = 0;
-    u8 data_format = var->nonstd&0x0f;
-
-    if(!strcmp(fix->id,"fb1")){
-        dev_drv = inf->rk_lcdc_device[0];
-        par = &dev_drv->layer_par[0];
-        layer_id = 0;
-    }else if(!strcmp(fix->id,"fb0")){
-        dev_drv = inf->rk_lcdc_device[0];
-        par = &dev_drv->layer_par[1];
-        layer_id = 1;
-    }else if(!strcmp(fix->id,"fb3")){
-        dev_drv = inf->rk_lcdc_device[1];
-        par = &dev_drv->layer_par[0];
-        layer_id = 0;
-    }else if(!strcmp(fix->id,"fb2")){
-        dev_drv = inf->rk_lcdc_device[1];
-        par = &dev_drv->layer_par[1];
-        layer_id = 1;
-    }else{
-        dev_drv = inf->rk_lcdc_device[0];
-        par = &dev_drv->layer_par[1];
-        layer_id = 0;
+    u32 xvir = var->xres_virtual;
+    u32 yvir = var->yres_virtual;
+    u8 data_format = var->nonstd&0xff;
+    var->pixclock = dev_drv->pixclock;
+    CHK_SUSPEND(dev_drv);
+    layer_id = get_fb_layer_id(fix);
+    if(layer_id < 0)
+    {
+       return  -ENODEV;
+    }
+    else
+    {
+       par = dev_drv->layer_par[layer_id];
     }
-    screen = &dev_drv->screen;
-
     if((!strcmp(fix->id,"fb0"))||(!strcmp(fix->id,"fb2")))  //four ui
     {
-        xsize = screen->x_res;
+       xsize = screen->x_res;
         ysize = screen->y_res;
     }
     else if((!strcmp(fix->id,"fb1"))||(!strcmp(fix->id,"fb3")))
@@ -311,70 +359,80 @@ static int rk_fb_set_par(struct fb_info *info)
         xsize = (var->grayscale>>8) & 0xfff;  //visiable size in panel ,for vide0
         ysize = (var->grayscale>>20) & 0xfff;
     }
-       CHK_SUSPEND(inf);
+    
        /* calculate y_offset,c_offset,line_length,cblen and crlen  */
+#if 1
     switch (data_format)
     {
-        case 0:                    // rgb
-            switch(var->bits_per_pixel)
-            {
-                case 16:    // rgb565
-                    par->format =RGB565;
-                    fix->line_length = 2 * xvir;
-                    par->y_offset = (yoffset*xvir + xoffset)*2;
-                    break;
-                case 32:   // rgb888
-                    if(var->transp.length)      //we need ditinguish ARGB888 and RGB888
-                        par->format = ARGB888;  //in some register,they have different configration
-                    else
-                        par->format = RGB888;
-                    fix->line_length = 4 * xvir;
-                    par->y_offset = (yoffset*xvir + xoffset)*4;
-                    break;
-                default:
-                    return -EINVAL;
-            }
-            break;
-        case 1: // yuv422
-            par->format = YUV422;
-            fix->line_length = xvir;
-            cblen = crlen = (xvir*yvir)>>1;
-            par->y_offset = yoffset*xvir + xoffset;
-            par->c_offset = par->y_offset;
-            break;
-        case 2: // yuv4200
-            par->format = YUV420;
-            fix->line_length = xvir;
-            cblen = crlen = (xvir*yvir)>>2;
-            par->y_offset = yoffset*xvir + xoffset;
-            par->c_offset = (yoffset>>1)*xvir + xoffset;
-            break;
-        case 4: // none
-        case 5: // yuv444
-            par->format = 5;
-            fix->line_length = xvir<<2;
-            par->y_offset = yoffset*xvir + xoffset;
-            par->c_offset = yoffset*2*xvir +(xoffset<<1);
-            cblen = crlen = (xvir*yvir);
-            break;
-        default:
+       case HAL_PIXEL_FORMAT_RGBA_8888 :      // rgb
+       case HAL_PIXEL_FORMAT_RGBX_8888: 
+               par->format = ARGB888;
+               fix->line_length = 4 * xvir;
+               par->y_offset = (yoffset*xvir + xoffset)*4;
+               break;
+       case HAL_PIXEL_FORMAT_RGB_888 :
+               par->format = RGB888;
+               fix->line_length = 3 * xvir;
+               par->y_offset = (yoffset*xvir + xoffset)*3;
+               break;
+       case HAL_PIXEL_FORMAT_RGB_565:  //RGB565
+               par->format = RGB565;
+               fix->line_length = 2 * xvir;
+               par->y_offset = (yoffset*xvir + xoffset)*2;
+               break;
+       case HAL_PIXEL_FORMAT_YCbCr_422_SP : // yuv422
+               par->format = YUV422;
+               fix->line_length = xvir;
+               cblen = crlen = (xvir*yvir)>>1;
+               par->y_offset = yoffset*xvir + xoffset;
+               par->c_offset = par->y_offset;
+               break;
+       case HAL_PIXEL_FORMAT_YCrCb_NV12   : // YUV420---uvuvuv
+               par->format = YUV420;
+               fix->line_length = xvir;
+               cblen = crlen = (xvir*yvir)>>2;
+               par->y_offset = yoffset*xvir + xoffset;
+               par->c_offset = (yoffset>>1)*xvir + xoffset;
+               break;
+       case HAL_PIXEL_FORMAT_YCrCb_444 : // yuv444
+               par->format = 5;
+               fix->line_length = xvir<<2;
+               par->y_offset = yoffset*xvir + xoffset;
+               par->c_offset = yoffset*2*xvir +(xoffset<<1);
+               cblen = crlen = (xvir*yvir);
+               break;
+       default:
+               printk("un supported format:0x%x\n",data_format);
             return -EINVAL;
     }
+#else
+       switch(var->bits_per_pixel)
+       {
+               case 32:
+                       par->format = ARGB888;
+                       fix->line_length = 4 * xvir;
+                       par->y_offset = (yoffset*xvir + xoffset)*4;
+                       break;
+               case 16:
+                       par->format = RGB565;
+                       fix->line_length = 2 * xvir;
+                       par->y_offset = (yoffset*xvir + xoffset)*2;
+                       break;
+                       
+       }
+#endif
 
-    smem_len = fix->line_length * yvir + cblen + crlen;
-   // map_size = PAGE_ALIGN(smem_len);
-
-    fix->smem_len = smem_len;
     par->xpos = xpos;
     par->ypos = ypos;
     par->xsize = xsize;
     par->ysize = ysize;
     
     par->smem_start =fix->smem_start;
-    par->xact = var->xres;
+    par->cbr_start = fix->mmio_start;
+    par->xact = var->xres;              //winx active window height,is a part of vir
     par->yact = var->yres;
-    par->xres_virtual = xvir;          // virtuail resolution  
-    par->yres_virtual = yvir;
+    par->xvir =  var->xres_virtual;            // virtual resolution    stride --->LCDC_WINx_VIR
+    par->yvir =  var->yres_virtual;
     dev_drv->set_par(dev_drv,layer_id);
     
        return 0;
@@ -393,7 +451,6 @@ static int fb_setcolreg(unsigned regno,
                               unsigned transp, struct fb_info *info)
 {
        unsigned int val;
-//     fbprintk(">>>>>> %s : %s \n", __FILE__, __FUNCTION__);
 
        switch (info->fix.visual) {
        case FB_VISUAL_TRUECOLOR:
@@ -416,11 +473,12 @@ static int fb_setcolreg(unsigned regno,
 static struct fb_ops fb_ops = {
     .owner          = THIS_MODULE,
     .fb_open        = rk_fb_open,
-    .fb_release     = rk_fb_release,
+    .fb_release     = rk_fb_close,
     .fb_check_var   = rk_fb_check_var,
     .fb_set_par     = rk_fb_set_par,
     .fb_blank       = rk_fb_blank,
     .fb_ioctl       = rk_fb_ioctl,
+    .fb_pan_display = rk_pan_display,
     .fb_setcolreg   = fb_setcolreg,
     .fb_fillrect    = cfb_fillrect,
     .fb_copyarea    = cfb_copyarea,
@@ -434,8 +492,8 @@ static struct fb_var_screeninfo def_var = {
     .green  = {5,6,0},
     .blue   = {0,5,0},
     .transp = {0,0,0}, 
-    .nonstd      = 0, //win1 format & ypos & xpos (ypos<<20 + xpos<<8 + format)
-    .grayscale   = 0,  //win1 transprent mode & value(mode<<8 + value)
+    .nonstd      = HAL_PIXEL_FORMAT_RGB_565,   //(ypos<<20+xpos<<8+format) format
+    .grayscale   = 0,  //(ysize<<20+xsize<<8)
     .activate    = FB_ACTIVATE_NOW,
     .accel_flags = 0,
     .vmode       = FB_VMODE_NONINTERLACED,
@@ -469,81 +527,173 @@ EXPORT_SYMBOL(rk_get_fb);
 void rk_direct_fb_show(struct fb_info * fbi)
 {
     rk_fb_set_par(fbi);
+    rk_pan_display(&fbi->var, fbi);
 }
 EXPORT_SYMBOL(rk_direct_fb_show);
 
-static int request_fb_buffer(struct fb_info *fbi,int fb_id)
+static int rk_request_fb_buffer(struct fb_info *fbi,int fb_id)
 {
-    struct resource *res;
-    int ret = 0;
-    switch(fb_id)
-    {
-        case 0:
-            res = platform_get_resource_byname(g_fb_pdev, IORESOURCE_MEM, "fb0 buf");
-            if (res == NULL)
-            {
-                dev_err(&g_fb_pdev->dev, "failed to get win0 memory \n");
-                ret = -ENOENT;
-            }
-            fbi->fix.smem_start = res->start;
-            fbi->fix.smem_len = res->end - res->start + 1;
-            fbi->screen_base = ioremap(res->start, fbi->fix.smem_len);
-            memset(fbi->screen_base, 0, fbi->fix.smem_len);
-        #ifdef CONFIG_FB_WORK_IPP
-        /* alloc ipp buf for rotate */
-            res = platform_get_resource_byname(g_fb_pdev, IORESOURCE_MEM, "ipp buf");
-            if (res == NULL)
-            {
-                dev_err(&g_fb_pdev->dev, "failed to get win1 ipp memory \n");
-                ret = -ENOENT;
-            }
-            fbi->fix.mmio_start = res->start;
-            fbi->fix.mmio_len = res->end - res->start + 1;
-            break;
-        #endif
-        case 2:
-            res = platform_get_resource_byname(g_fb_pdev, IORESOURCE_MEM, "fb2 buf");
-            if (res == NULL)
-            {
-                dev_err(&g_fb_pdev->dev, "failed to get win0 memory \n");
-                ret = -ENOENT;
-            }
-            fbi->fix.smem_start = res->start;
-            fbi->fix.smem_len = res->end - res->start + 1;
-            fbi->screen_base = ioremap(res->start, fbi->fix.smem_len);
-            memset(fbi->screen_base, 0, fbi->fix.smem_len);
-            break;
-        default:
-            ret = -EINVAL;
-            break;             
+       struct resource *res;
+       struct resource *mem;
+       int ret = 0;
+       switch(fb_id)
+       {
+               case 0:
+                       res = platform_get_resource_byname(g_fb_pdev, IORESOURCE_MEM, "fb0 buf");
+                       if (res == NULL)
+                       {
+                               dev_err(&g_fb_pdev->dev, "failed to get win0 memory \n");
+                               ret = -ENOENT;
+                       }
+                       fbi->fix.smem_start = res->start;
+                       fbi->fix.smem_len = res->end - res->start + 1;
+                       mem = request_mem_region(res->start, resource_size(res), g_fb_pdev->name);
+                       fbi->screen_base = ioremap(res->start, fbi->fix.smem_len);
+                       memset(fbi->screen_base, 0, fbi->fix.smem_len);
+                       printk("fb%d:phy:%lx>>vir:%p>>len:0x%x\n",fb_id,
+                               fbi->fix.smem_start,fbi->screen_base,fbi->fix.smem_len);
+               #ifdef CONFIG_FB_WORK_IPP // alloc ipp buf for rotate
+                       res = platform_get_resource_byname(g_fb_pdev, IORESOURCE_MEM, "ipp buf");
+                       if (res == NULL)
+                       {
+                               dev_err(&g_fb_pdev->dev, "failed to get win1 ipp memory \n");
+                               ret = -ENOENT;
+                       }
+                       fbi->fix.mmio_start = res->start;
+                       fbi->fix.mmio_len = res->end - res->start + 1;
+               #endif
+                       break;
+               case 2:
+                       res = platform_get_resource_byname(g_fb_pdev, IORESOURCE_MEM, "fb2 buf");
+                       if (res == NULL)
+                       {
+                       dev_err(&g_fb_pdev->dev, "failed to get win0 memory \n");
+                       ret = -ENOENT;
+                       }
+                       fbi->fix.smem_start = res->start;
+                       fbi->fix.smem_len = res->end - res->start + 1;
+                       mem = request_mem_region(res->start, resource_size(res), g_fb_pdev->name);
+                       fbi->screen_base = ioremap(res->start, fbi->fix.smem_len);
+                       memset(fbi->screen_base, 0, fbi->fix.smem_len);
+                       printk("fb%d:phy:%lx>>vir:%p>>len:0x%x\n",fb_id,
+                               fbi->fix.smem_start,fbi->screen_base,fbi->fix.smem_len);
+                       break;
+               default:
+                       ret = -EINVAL;
+                       break;          
        }
     return ret;
 }
-int rk_fb_register(struct rk_lcdc_device_driver *dev_drv)
+
+static int rk_release_fb_buffer(struct fb_info *fbi)
+{
+       if(!fbi)
+       {
+               printk("no need release null fb buffer!\n");
+               return -EINVAL;
+       }
+       if(!strcmp(fbi->fix.id,"fb1")||!strcmp(fbi->fix.id,"fb3"))  //buffer for fb1 and fb3 are alloc by android
+               return 0;
+       iounmap(fbi->screen_base);
+       release_mem_region(fbi->fix.smem_start,fbi->fix.smem_len);
+       return 0;
+       
+}
+static int init_layer_par(struct rk_lcdc_device_driver *dev_drv)
+{
+       int i;
+       struct layer_par * def_par = NULL;
+       int num_par = dev_drv->num_layer;
+       for(i = 0; i < num_par; i++)
+       {
+               struct layer_par *par = NULL;
+               par =  kzalloc(sizeof(struct layer_par), GFP_KERNEL);
+               if(!par)
+               {
+                       printk(KERN_ERR "kzmalloc for layer_par fail!");
+                       return   -ENOMEM;
+                       
+               }
+              def_par = &dev_drv->def_layer_par[i];
+               strcpy(par->name,def_par->name);
+               par->id = def_par->id;
+               par->support_3d = def_par->support_3d;
+               dev_drv->layer_par[i] = par;
+       }
+               
+       return 0;
+       
+       
+}
+
+
+static int init_lcdc_device_driver(struct rk_lcdc_device_driver *dev_drv,
+       struct rk_lcdc_device_driver *def_drv,int id)
+{
+       if(!def_drv)
+       {
+               printk(KERN_ERR "default lcdc device driver is null!\n");
+               return -EINVAL;
+       }
+       if(!dev_drv)
+       {
+               printk(KERN_ERR "lcdc device driver is null!\n");
+               return -EINVAL; 
+       }
+       sprintf(dev_drv->name, "lcdc%d",id);
+       dev_drv->open           = def_drv->open;
+       dev_drv->init_lcdc      = def_drv->init_lcdc;
+       dev_drv->ioctl          = def_drv->ioctl;
+       dev_drv->blank          = def_drv->blank;
+       dev_drv->set_par        = def_drv->set_par;
+       dev_drv->pan_display    = def_drv->pan_display;
+       dev_drv->suspend        = def_drv->suspend;
+       dev_drv->resume         = def_drv->resume;
+       dev_drv->load_screen    = def_drv->load_screen;
+       dev_drv->def_layer_par  = def_drv->def_layer_par;
+       dev_drv->num_layer      = def_drv->num_layer;
+       init_layer_par(dev_drv);
+       init_completion(&dev_drv->frame_done);
+       spin_lock_init(&dev_drv->cpl_lock);
+       dev_drv->first_frame = 1;
+       
+       return 0;
+}
+
+int rk_fb_register(struct rk_lcdc_device_driver *dev_drv,
+       struct rk_lcdc_device_driver *def_drv,int id)
 {
        struct rk_fb_inf *fb_inf = platform_get_drvdata(g_fb_pdev);
        struct fb_info *fbi;
        int i=0,ret = 0;
        int lcdc_id = 0;
-       if(NULL==dev_drv){
-        printk("null lcdc device driver?");
-        return -ENOENT;
-    }
-    for(i=0;i<RK30_MAX_LCDC_SUPPORT;i++){
-        if(NULL==fb_inf->rk_lcdc_device[i]){
-            fb_inf->rk_lcdc_device[i] = dev_drv;
-            fb_inf->rk_lcdc_device[i]->id = i;
-            fb_inf->num_lcdc++;
-            break;
-        }
-    }
-    if(i==RK30_MAX_LCDC_SUPPORT){
-        printk("rk_fb_register lcdc out of support %d",i);
-        return -ENOENT;
-    }
-    lcdc_id = i;
-       
+       if(NULL == dev_drv)
+       {
+               printk("null lcdc device driver?");
+               return -ENOENT;
+       }
+       for(i=0;i<RK30_MAX_LCDC_SUPPORT;i++)
+       {
+               if(NULL==fb_inf->lcdc_dev_drv[i])
+               {
+                       fb_inf->lcdc_dev_drv[i] = dev_drv;
+                       fb_inf->lcdc_dev_drv[i]->id = i;
+                       fb_inf->num_lcdc++;
+                       break;
+               }
+       }
+       if(i==RK30_MAX_LCDC_SUPPORT)
+       {
+               printk("rk_fb_register lcdc out of support %d",i);
+               return -ENOENT;
+       }
+       lcdc_id = i;
+       init_lcdc_device_driver(dev_drv, def_drv,id);
+       set_lcd_info(dev_drv->screen, fb_inf->mach_info->lcd_info);
+       dev_drv->init_lcdc(dev_drv);
+       dev_drv->load_screen(dev_drv,1);
        /************fb set,one layer one fb ***********/
+       dev_drv->fb_index_base = fb_inf->num_fb;
     for(i=0;i<dev_drv->num_layer;i++)
     {
         fbi= framebuffer_alloc(0, &g_fb_pdev->dev);
@@ -553,33 +703,35 @@ int rk_fb_register(struct rk_lcdc_device_driver *dev_drv)
             fbi = NULL;
             ret = -ENOMEM;
         }
+       fbi->par = dev_drv;
         fbi->var = def_var;
         fbi->fix = def_fix;
         sprintf(fbi->fix.id,"fb%d",fb_inf->num_fb);
-        fbi->var.xres = fb_inf->rk_lcdc_device[lcdc_id]->screen.x_res;
-        fbi->var.yres = fb_inf->rk_lcdc_device[lcdc_id]->screen.y_res;
+        fbi->var.xres = fb_inf->lcdc_dev_drv[lcdc_id]->screen->x_res;
+        fbi->var.yres = fb_inf->lcdc_dev_drv[lcdc_id]->screen->y_res;
         fbi->var.bits_per_pixel = 16;
-        fbi->var.xres_virtual = fb_inf->rk_lcdc_device[lcdc_id]->screen.x_res;
-        fbi->var.yres_virtual = fb_inf->rk_lcdc_device[lcdc_id]->screen.y_res;
-        fbi->var.width = fb_inf->rk_lcdc_device[lcdc_id]->screen.width;
-        fbi->var.height = fb_inf->rk_lcdc_device[lcdc_id]->screen.height;
-        fbi->var.pixclock =fb_inf->rk_lcdc_device[lcdc_id]->pixclock;
-        fbi->var.left_margin = fb_inf->rk_lcdc_device[lcdc_id]->screen.left_margin;
-        fbi->var.right_margin = fb_inf->rk_lcdc_device[lcdc_id]->screen.right_margin;
-        fbi->var.upper_margin = fb_inf->rk_lcdc_device[lcdc_id]->screen.upper_margin;
-        fbi->var.lower_margin = fb_inf->rk_lcdc_device[lcdc_id]->screen.lower_margin;
-        fbi->var.vsync_len = fb_inf->rk_lcdc_device[lcdc_id]->screen.vsync_len;
-        fbi->var.hsync_len = fb_inf->rk_lcdc_device[lcdc_id]->screen.hsync_len;
+        fbi->var.xres_virtual = fb_inf->lcdc_dev_drv[lcdc_id]->screen->x_res;
+        fbi->var.yres_virtual = fb_inf->lcdc_dev_drv[lcdc_id]->screen->y_res;
+        fbi->var.width = fb_inf->lcdc_dev_drv[lcdc_id]->screen->width;
+        fbi->var.height = fb_inf->lcdc_dev_drv[lcdc_id]->screen->height;
+        fbi->var.pixclock =fb_inf->lcdc_dev_drv[lcdc_id]->pixclock;
+        fbi->var.left_margin = fb_inf->lcdc_dev_drv[lcdc_id]->screen->left_margin;
+        fbi->var.right_margin = fb_inf->lcdc_dev_drv[lcdc_id]->screen->right_margin;
+        fbi->var.upper_margin = fb_inf->lcdc_dev_drv[lcdc_id]->screen->upper_margin;
+        fbi->var.lower_margin = fb_inf->lcdc_dev_drv[lcdc_id]->screen->lower_margin;
+        fbi->var.vsync_len = fb_inf->lcdc_dev_drv[lcdc_id]->screen->vsync_len;
+        fbi->var.hsync_len = fb_inf->lcdc_dev_drv[lcdc_id]->screen->hsync_len;
         fbi->fbops                      = &fb_ops;
         fbi->flags                      = FBINFO_FLAG_DEFAULT;
-        fbi->pseudo_palette  = fb_inf->rk_lcdc_device[lcdc_id]->layer_par[i].pseudo_pal;
-        request_fb_buffer(fbi,fb_inf->num_fb);
+        fbi->pseudo_palette  = fb_inf->lcdc_dev_drv[lcdc_id]->layer_par[i]->pseudo_pal;
+        rk_request_fb_buffer(fbi,fb_inf->num_fb);
         ret = register_framebuffer(fbi);
         if(ret<0)
         {
             printk("%s>>fb%d register_framebuffer fail!\n",__func__,fb_inf->num_fb);
             ret = -EINVAL;
         }
+       rkfb_create_sysfs(fbi);
         fb_inf->fb[fb_inf->num_fb] = fbi;
         printk("%s>>>>>%s\n",__func__,fb_inf->fb[fb_inf->num_fb]->fix.id);
         fb_inf->num_fb++;      
@@ -590,64 +742,128 @@ int rk_fb_register(struct rk_lcdc_device_driver *dev_drv)
         /* Start display and show logo on boot */
         fb_set_cmap(&fb_inf->fb[fb_inf->num_fb-2]->cmap, fb_inf->fb[fb_inf->num_fb-2]);
         fb_show_logo(fb_inf->fb[fb_inf->num_fb-2], FB_ROTATE_UR);
-        fb_inf->fb[fb_inf->num_fb-2]->fbops->fb_blank(FB_BLANK_UNBLANK, fb_inf->fb[fb_inf->num_fb-2]);
+       fb_inf->fb[fb_inf->num_fb-2]->fbops->fb_pan_display(&(fb_inf->fb[fb_inf->num_fb-2]->var), fb_inf->fb[fb_inf->num_fb-2]);
     }
 #endif
        return 0;
        
        
 }
-int rk_fb_unregister(struct rk_lcdc_device_driver *fb_device_driver)
+int rk_fb_unregister(struct rk_lcdc_device_driver *dev_drv)
 {
 
        struct rk_fb_inf *fb_inf = platform_get_drvdata(g_fb_pdev);
+       struct fb_info *fbi;
+       int fb_index_base = dev_drv->fb_index_base;
+       int fb_num = dev_drv->num_layer;
        int i=0;
-       if(NULL==fb_device_driver){
-               printk("rk_fb_register lcdc register fail");
+       if(NULL == dev_drv)
+       {
+               printk(" no need to unregister null lcdc device driver!\n");
                return -ENOENT;
-               }
-       for(i=0;i<RK30_MAX_LCDC_SUPPORT;i++){
-               if(fb_inf->rk_lcdc_device[i]->id == i ){
-               fb_inf->rk_lcdc_device[i] = NULL;
-               fb_inf->num_lcdc--;
-               break;
-               }
        }
-       if(i==RK30_MAX_LCDC_SUPPORT){
-               printk("rk_fb_unregister lcdc out of support %d",i);
-               return -ENOENT;
+
+       for(i = 0; i < fb_num; i++)
+       {
+               kfree(dev_drv->layer_par[i]);
        }
-       
+
+       for(i=fb_index_base;i<(fb_index_base+fb_num);i++)
+       {
+               fbi = fb_inf->fb[i];
+               unregister_framebuffer(fbi);
+               rk_release_fb_buffer(fbi);
+               framebuffer_release(fbi);       
+       }
+       fb_inf->lcdc_dev_drv[dev_drv->id]= NULL;
 
        return 0;
 }
 
+
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+struct suspend_info {
+       struct early_suspend early_suspend;
+       struct rk_fb_inf *inf;
+};
+
+static void rkfb_early_suspend(struct early_suspend *h)
+{
+       struct suspend_info *info = container_of(h, struct suspend_info,
+                                               early_suspend);
+       struct rk_fb_inf *inf = info->inf;
+       int i;
+       inf->mach_info->io_disable();
+       for(i = 0; i < inf->num_lcdc; i++)
+       {
+               atomic_set(&inf->lcdc_dev_drv[i]->in_suspend,1);
+               inf->lcdc_dev_drv[i]->suspend(inf->lcdc_dev_drv[i]);
+       }
+}
+static void rkfb_early_resume(struct early_suspend *h)
+{
+       struct suspend_info *info = container_of(h, struct suspend_info,
+                                               early_suspend);
+       struct rk_fb_inf *inf = info->inf;
+       int i;
+       inf->mach_info->io_enable();
+       for(i = 0; i < inf->num_lcdc; i++)
+       {
+               inf->lcdc_dev_drv[i]->resume(inf->lcdc_dev_drv[i]);
+               atomic_set(&inf->lcdc_dev_drv[i]->in_suspend,0);
+       }
+
+}
+
+
+
+static struct suspend_info suspend_info = {
+       .early_suspend.suspend = rkfb_early_suspend,
+       .early_suspend.resume = rkfb_early_resume,
+       .early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB,
+};
+#endif
+
 static int __devinit rk_fb_probe (struct platform_device *pdev)
 {
-       struct rk_fb_inf *fb_inf        = NULL;
+       struct rk_fb_inf *fb_inf = NULL;
+       struct rk29fb_info * mach_info = NULL;
        int ret = 0;
        g_fb_pdev=pdev;
-    /* Malloc rk29fb_inf and set it to pdev for drvdata */
-    fb_inf = kmalloc(sizeof(struct rk_fb_inf), GFP_KERNEL);
-    if(!fb_inf)
-    {
-        dev_err(&pdev->dev, ">>fb inf kmalloc fail!");
-        ret = -ENOMEM;
-    }
-    memset(fb_inf, 0, sizeof(struct rk_fb_inf));
-       platform_set_drvdata(pdev, fb_inf);
+       /* Malloc rk_fb_inf and set it to pdev for drvdata */
+       fb_inf = kzalloc(sizeof(struct rk_fb_inf), GFP_KERNEL);
+       if(!fb_inf)
+       {
+               dev_err(&pdev->dev, ">>fb inf kmalloc fail!");
+               ret = -ENOMEM;
+       }
+       platform_set_drvdata(pdev,fb_inf);
+       mach_info =  pdev->dev.platform_data;
+       fb_inf->mach_info = mach_info;
+       if(mach_info->io_init)
+               mach_info->io_init(NULL);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+       suspend_info.inf = fb_inf;
+       register_early_suspend(&suspend_info.early_suspend);
+#endif
        printk("rk fb probe ok!\n");
     return 0;
 }
 
 static int __devexit rk_fb_remove(struct platform_device *pdev)
 {
-    return 0;
+       struct rk_fb_inf *fb_inf = platform_get_drvdata(pdev);
+       kfree(fb_inf);
+       platform_set_drvdata(pdev, NULL);
+       return 0;
 }
 
 static void rk_fb_shutdown(struct platform_device *pdev)
 {
-
+       struct rk_fb_inf *fb_inf = platform_get_drvdata(pdev);
+       kfree(fb_inf);
+       platform_set_drvdata(pdev, NULL);
 }
 
 static struct platform_driver rk_fb_driver = {
@@ -670,6 +886,6 @@ static void __exit rk_fb_exit(void)
     platform_driver_unregister(&rk_fb_driver);
 }
 
-fs_initcall(rk_fb_init);
+subsys_initcall_sync(rk_fb_init);
 module_exit(rk_fb_exit);