rk30fb :fix a bug in rk30_lcdc.c
[firefly-linux-kernel-4.4.55.git] / drivers / video / rockchip / rk_fb.c
index f1abbee15593027dddca97ef7e4e01a90d51b001..480115c1976140917574619bc3cdfff591b8088e 100644 (file)
 #include <linux/device.h>
 #include <linux/fb.h>
 #include <linux/init.h>
-#include <linux/interrupt.h>
 #include <linux/platform_device.h>
 #include <linux/earlysuspend.h>
 #include <asm/div64.h>
 #include <asm/uaccess.h>
-#include <mach/board.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
@@ -71,7 +68,7 @@ defautl:we alloc three buffer,one for fb0 and fb2 display ui,one for ipp rotate
         pass the phy addr to fix.smem_start by ioctl
 ****************************************************************************/
 
-static int get_fb_layer_id(struct fb_fix_screeninfo *fix)
+int get_fb_layer_id(struct fb_fix_screeninfo *fix)
 {
        int layer_id;
        if(!strcmp(fix->id,"fb1")||!strcmp(fix->id,"fb3"))
@@ -90,34 +87,44 @@ static int get_fb_layer_id(struct fb_fix_screeninfo *fix)
 
        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_fb_inf *inf = dev_get_drvdata(info->device);
-    struct rk_lcdc_device_driver *dev_drv = NULL;
-    struct fb_fix_screeninfo *fix = &info->fix;
+    struct rk_lcdc_device_driver * dev_drv = (struct rk_lcdc_device_driver * )info->par;
     int layer_id;
-    if(!strcmp(fix->id,"fb1")){
-        dev_drv = inf->lcdc_dev_drv[0];
-        layer_id = 0;
-        dev_drv->blank(dev_drv,1,FB_BLANK_NORMAL);  //when open fb1,defautl close fb0 layer win1
-        dev_drv->blank(dev_drv,layer_id,FB_BLANK_UNBLANK); //open fb1 layer win0
-        inf->video_mode = 1;
-    }
+    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;
-    }
-
-    return 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;
 }
 
 static int rk_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
@@ -131,13 +138,14 @@ static int rk_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
        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];
+                par = dev_drv->layer_par[layer_id];
        }
        switch (par->format)
        {
@@ -179,8 +187,8 @@ static int rk_fb_ioctl(struct fb_info *info, unsigned int cmd,unsigned long arg)
        u32 yuv_phy[2];
        void __user *argp = (void __user *)arg;
        fbprintk(">>>>>> %s : cmd:0x%x \n",__FUNCTION__,cmd);
-       CHK_SUSPEND(inf);
        
+       CHK_SUSPEND(dev_drv);
        switch(cmd)
        {
                case FBIOPUT_FBPHYADD:
@@ -221,6 +229,7 @@ static int rk_fb_blank(int blank_mode, struct fb_info *info)
        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)
        {
@@ -234,7 +243,8 @@ static int rk_fb_blank(int blank_mode, struct fb_info *info)
 
 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 ||
@@ -328,7 +338,8 @@ static int rk_fb_set_par(struct fb_info *info)
     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)
     {
@@ -336,7 +347,7 @@ static int rk_fb_set_par(struct fb_info *info)
     }
     else
     {
-       par = &dev_drv->layer_par[layer_id];
+       par = dev_drv->layer_par[layer_id];
     }
     if((!strcmp(fix->id,"fb0"))||(!strcmp(fix->id,"fb2")))  //four ui
     {
@@ -348,7 +359,7 @@ 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)
@@ -462,7 +473,7 @@ 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,
@@ -516,67 +527,147 @@ 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;
-    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\n",fb_id,fbi->fix.smem_start,fbi->screen_base);
-        #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;
-            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)
+       if(NULL == dev_drv)
        {
                printk("null lcdc device driver?");
                return -ENOENT;
@@ -597,8 +688,12 @@ int rk_fb_register(struct rk_lcdc_device_driver *dev_drv)
                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);
@@ -628,14 +723,15 @@ int rk_fb_register(struct rk_lcdc_device_driver *dev_drv)
         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->lcdc_dev_drv[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++;      
@@ -646,7 +742,6 @@ 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
@@ -654,86 +749,121 @@ int rk_fb_register(struct rk_lcdc_device_driver *dev_drv)
        
        
 }
-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->lcdc_dev_drv[i]->id == i ){
-               fb_inf->lcdc_dev_drv[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;
 }
 
-int init_lcdc_device_driver(struct rk_lcdc_device_driver *def_drv,
-       struct rk_lcdc_device_driver *dev_drv,int id)
+
+
+#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)
 {
-       if(!def_drv)
+       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++)
        {
-               printk(KERN_ERR "default lcdc device driver is null!\n");
-               return -EINVAL;
+               atomic_set(&inf->lcdc_dev_drv[i]->in_suspend,1);
+               inf->lcdc_dev_drv[i]->suspend(inf->lcdc_dev_drv[i]);
        }
-       if(!dev_drv)
+}
+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++)
        {
-               printk(KERN_ERR "lcdc device driver is null!\n");
-               return -EINVAL; 
+               inf->lcdc_dev_drv[i]->resume(inf->lcdc_dev_drv[i]);
+               atomic_set(&inf->lcdc_dev_drv[i]->in_suspend,0);
        }
-       sprintf(dev_drv->name, "lcdc%d",id);
-       dev_drv->layer_par = def_drv->layer_par;
-       dev_drv->num_layer = def_drv->num_layer;
-       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;
-       
-       return 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 rk29lcd_info *lcd_info = NULL;
+       struct rk_fb_inf *fb_inf = NULL;
+       struct rk29fb_info * mach_info = NULL;
        int ret = 0;
        g_fb_pdev=pdev;
-       lcd_info =  pdev->dev.platform_data;
-    /* Malloc rk_fb_inf and set it to pdev for drvdata */
-       fb_inf = kmalloc(sizeof(struct rk_fb_inf), GFP_KERNEL);
+       /* 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;
        }
-       memset(fb_inf, 0, sizeof(struct rk_fb_inf));
-       platform_set_drvdata(pdev, fb_inf);
-       if(lcd_info->io_init)
-               lcd_info->io_init();
+       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 = {
@@ -756,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);