summaryrefslogtreecommitdiff
path: root/drivers/video/sprdfb/sprdfb_main.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video/sprdfb/sprdfb_main.c')
-rw-r--r--drivers/video/sprdfb/sprdfb_main.c888
1 files changed, 888 insertions, 0 deletions
diff --git a/drivers/video/sprdfb/sprdfb_main.c b/drivers/video/sprdfb/sprdfb_main.c
new file mode 100644
index 00000000..176715a9
--- /dev/null
+++ b/drivers/video/sprdfb/sprdfb_main.c
@@ -0,0 +1,888 @@
+/*
+ * Copyright (C) 2012 Spreadtrum Communications Inc.
+ *
+ * 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.
+ *
+ * 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.
+ */
+
+#define pr_fmt(fmt) "sprdfb: " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+#include <linux/platform_device.h>
+#include <linux/fb.h>
+#include <linux/dma-mapping.h>
+#include <linux/uaccess.h>
+#include <asm/uaccess.h>
+#include <linux/compat.h>
+
+#ifdef CONFIG_OF
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+#include <linux/of_address.h>
+#endif
+
+#include <soc/sprd/arch_misc.h>
+#include "sprdfb_chip_common.h"
+#include "sprdfb.h"
+#include "sprdfb_panel.h"
+
+#include <linux/proc_fs.h>
+
+#ifdef CONFIG_FB_MMAP_CACHED
+#include <asm/pgtable.h>
+#include <linux/mm.h>
+#endif
+
+enum{
+ SPRD_IN_DATA_TYPE_ABGR888 = 0,
+ SPRD_IN_DATA_TYPE_BGR565,
+/*
+ SPRD_IN_DATA_TYPE_RGB666,
+ SPRD_IN_DATA_TYPE_RGB555,
+ SPRD_IN_DATA_TYPE_PACKET,
+*/ /*not support*/
+ SPRD_IN_DATA_TYPE_LIMIT
+};
+
+#define SPRDFB_IN_DATA_TYPE SPRD_IN_DATA_TYPE_ABGR888
+
+#ifdef CONFIG_FB_TRIPLE_FRAMEBUFFER
+#define FRAMEBUFFER_NR (3)
+#else
+#define FRAMEBUFFER_NR (2)
+#endif
+
+#define SPRDFB_FRAMES_TO_SKIP (1)
+
+#define SPRDFB_DEFAULT_FPS (60)
+
+#define SPRDFB_ESD_TIME_OUT_CMD (2000)
+
+#define SPRDFB_ESD_TIME_OUT_VIDEO (1000)
+
+extern bool sprdfb_panel_get(struct sprdfb_device *dev);
+extern int sprdfb_panel_probe(struct sprdfb_device *dev);
+extern void sprdfb_panel_remove(struct sprdfb_device *dev);
+
+extern struct display_ctrl sprdfb_dispc_ctrl ;
+
+#ifdef CONFIG_OF
+extern unsigned long g_dispc_base_addr;
+#endif
+
+static unsigned PP[16];
+static int frame_count = 0;
+
+static int sprdfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fb);
+static int sprdfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *fb);
+static int sprdfb_ioctl(struct fb_info *info, unsigned int cmd,
+ unsigned long arg);
+#ifdef CONFIG_COMPAT
+static long sprdfb_compat_ioctl(struct fb_info *info, unsigned int cmd,
+ unsigned long arg);
+#endif
+#ifdef CONFIG_FB_MMAP_CACHED
+static int sprdfb_mmap(struct fb_info *info, struct vm_area_struct *vma);
+#endif
+
+static struct fb_ops sprdfb_ops = {
+ .owner = THIS_MODULE,
+ .fb_check_var = sprdfb_check_var,
+ .fb_pan_display = sprdfb_pan_display,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+ .fb_ioctl = sprdfb_ioctl,
+#ifdef CONFIG_COMPAT
+ .fb_compat_ioctl = sprdfb_compat_ioctl,
+#endif
+#ifdef CONFIG_FB_MMAP_CACHED
+ .fb_mmap = sprdfb_mmap,
+#endif
+};
+
+#ifdef CONFIG_FB_MMAP_CACHED
+static int sprdfb_mmap(struct fb_info *info,struct vm_area_struct *vma)
+{
+ struct sprdfb_device *dev = NULL;
+ if(NULL == info){
+ printk(KERN_ERR "sprdfb: sprdfb_ioctl error. (Invalid Parameter)");
+ return -1;
+ }
+
+ dev = info->par;
+ printk("sprdfb: sprdfb_mmap,vma=0x%x\n",vma);
+ vma->vm_page_prot = pgprot_cached(vma->vm_page_prot);
+ dev->ctrl->set_vma(vma);
+
+ return vm_iomap_memory(vma, info->fix.smem_start, info->fix.smem_len);
+}
+#endif
+
+extern uint32_t lcd_id_from_uboot;
+
+static struct proc_dir_entry *lcd_user = NULL;
+
+ssize_t DrvLcdNameRead(struct file *pFile, char __user *pBuffer, size_t nCount, loff_t *pPos)
+{
+ u32 nLength;
+
+ if (*pPos != 0)
+ {
+ return 0;
+ }
+
+ nLength = sprintf(pBuffer, "%x\n", lcd_id_from_uboot);
+
+ *pPos += nLength;
+
+ return nLength;
+}
+
+ssize_t DrvLcdNameWrite(struct file *pFile, const char __user *pBuffer, size_t nCount, loff_t *pPos)
+{
+ return nCount;
+}
+
+static const struct file_operations _gProclcdName = {
+ .read = DrvLcdNameRead,
+ .write = DrvLcdNameWrite,
+};
+
+
+
+static int setup_fb_mem(struct sprdfb_device *dev, struct platform_device *pdev)
+{
+ uint32_t len;
+ void *addr;
+ bool use_reserve_mem;
+ uint32_t reserve_mem[2];
+ int ret;
+
+#ifdef CONFIG_FB_LOW_RES_SIMU
+ if ((0!= dev->display_width) && (0 != dev->display_height))
+ len = dev->display_width * dev->display_height * (dev->bpp / 8) * FRAMEBUFFER_NR;
+ else
+#endif
+ len = dev->panel->width * dev->panel->height * (dev->bpp / 8) * FRAMEBUFFER_NR;
+
+#ifdef CONFIG_OF
+ use_reserve_mem = of_property_read_bool(pdev->dev.of_node, "sprd,fb_use_reservemem");
+ if(use_reserve_mem) {
+ ret = of_property_read_u32_array(pdev->dev.of_node, "sprd,fb_mem",
+ reserve_mem, 2);
+ if(0 != ret)
+ printk(KERN_ERR "sprdfb: Failed to got framebuffer memory from dt file\n");
+ }
+#else
+#ifdef CONFIG_FB_LCD_RESERVE_MEM
+ use_reserve_mem = true;
+ reserve_mem[0] = SPRD_FB_MEM_BASE;
+ reserve_mem[1] = SPRD_FB_MEM_SIZE;
+#else
+ use_reserve_mem = false;
+#endif
+#endif
+
+ if(!use_reserve_mem) {
+ addr = (void*)__get_free_pages(GFP_ATOMIC | __GFP_ZERO, get_order(len));
+ if (NULL == addr) {
+ printk(KERN_ERR "sprdfb: Failed to allocate framebuffer memory\n");
+ return -ENOMEM;
+ }
+ printk(KERN_INFO "sprdfb: got %d bytes mem at 0x%p\n", len, addr);
+
+ dev->fb->fix.smem_start = __pa(addr);
+ dev->fb->fix.smem_len = len;
+ dev->fb->screen_base = (char*)addr;
+ }else{
+ dev->fb->fix.smem_start = reserve_mem[0];
+ printk("sprdfb: setup_fb_mem--smem_start:%lx,len:%d,reserved len:%d\n",
+ dev->fb->fix.smem_start, len, reserve_mem[1]);
+ addr = ioremap(dev->fb->fix.smem_start, len);
+ if (NULL == addr) {
+ printk(KERN_ERR "sprdfb: Unable to map framebuffer base: 0x%p\n", addr);
+ return -ENOMEM;
+ }
+ dev->fb->fix.smem_len = len;
+ dev->fb->screen_base = (char*)addr;
+ }
+ return 0;
+}
+
+static void setup_fb_info(struct sprdfb_device *dev)
+{
+ struct fb_info *fb = dev->fb;
+ struct panel_spec *panel = dev->panel;
+ int r;
+
+ fb->fbops = &sprdfb_ops;
+ fb->flags = FBINFO_DEFAULT;
+
+ /* finish setting up the fb_info struct */
+ strncpy(fb->fix.id, "sprdfb", 16);
+ fb->fix.ypanstep = 1;
+ fb->fix.type = FB_TYPE_PACKED_PIXELS;
+ fb->fix.visual = FB_VISUAL_TRUECOLOR;
+#ifdef CONFIG_FB_LOW_RES_SIMU
+ if((0 != dev->display_width) && (0 != dev->display_height)){
+ fb->fix.line_length = dev->display_width * dev->bpp / 8;
+
+ fb->var.xres = dev->display_width;
+ fb->var.yres = dev->display_height;
+ fb->var.width = dev->display_width;
+ fb->var.height = dev->display_height;
+
+ fb->var.xres_virtual = dev->display_width;
+ fb->var.yres_virtual = dev->display_height * FRAMEBUFFER_NR;
+ }else
+#endif
+ {
+ fb->fix.line_length = panel->width * dev->bpp / 8;
+
+ fb->var.xres = panel->width;
+ fb->var.yres = panel->height;
+ fb->var.width = panel->width;
+ fb->var.height = panel->height;
+
+ fb->var.xres_virtual = panel->width;
+ fb->var.yres_virtual = panel->height * FRAMEBUFFER_NR;
+ }
+ fb->var.bits_per_pixel = dev->bpp;
+#ifdef CONFIG_FB_LOW_RES_SIMU
+ if((0 != dev->display_width) && (0 != dev->display_height)){
+ if(0 != dev->panel->fps){
+ fb->var.pixclock = ((1000000000 /dev->display_width) * 1000) / (dev->panel->fps * dev->display_height);
+ }else{
+ fb->var.pixclock = ((1000000000 /dev->display_width) * 1000) / (SPRDFB_DEFAULT_FPS * dev->display_height);
+ }
+ }else
+#endif
+ {
+ if(0 != dev->panel->fps){
+ fb->var.pixclock = ((1000000000 /panel->width) * 1000) / (dev->panel->fps * panel->height);
+ }else{
+ fb->var.pixclock = ((1000000000 /panel->width) * 1000) / (SPRDFB_DEFAULT_FPS * panel->height);
+ }
+ }
+
+ fb->var.accel_flags = 0;
+ fb->var.yoffset = 0;
+
+ /* only support two pixel format */
+ if (dev->bpp == 32) { /* ABGR */
+ fb->var.red.offset = 16;
+ fb->var.red.length = 8;
+ fb->var.red.msb_right = 0;
+ fb->var.green.offset = 8;
+ fb->var.green.length = 8;
+ fb->var.green.msb_right = 0;
+ fb->var.blue.offset = 0;
+ fb->var.blue.length = 8;
+ fb->var.blue.msb_right = 0;
+ } else { /*BGR*/
+ fb->var.red.offset = 11;
+ fb->var.red.length = 5;
+ fb->var.red.msb_right = 0;
+ fb->var.green.offset = 5;
+ fb->var.green.length = 6;
+ fb->var.green.msb_right = 0;
+ fb->var.blue.offset = 0;
+ fb->var.blue.length = 5;
+ fb->var.blue.msb_right = 0;
+ }
+ r = fb_alloc_cmap(&fb->cmap, 16, 0);
+ fb->pseudo_palette = PP;
+
+ PP[0] = 0;
+ for (r = 1; r < 16; r++){
+ PP[r] = 0xffffffff;
+ }
+}
+
+static void fb_free_resources(struct sprdfb_device *dev)
+{
+ if (dev == NULL)
+ return;
+
+ if (&dev->fb->cmap != NULL) {
+ fb_dealloc_cmap(&dev->fb->cmap);
+ }
+ if (dev->fb->screen_base) {
+ free_pages((unsigned long)dev->fb->screen_base,
+ get_order(dev->fb->fix.smem_len));
+ }
+ unregister_framebuffer(dev->fb);
+ framebuffer_release(dev->fb);
+}
+
+static int sprdfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *fb)
+{
+ int32_t ret;
+ struct sprdfb_device *dev = fb->par;
+
+ pr_debug("sprdfb: [%s]\n", __FUNCTION__);
+
+ if(frame_count < SPRDFB_FRAMES_TO_SKIP) {
+ frame_count++;
+ return 0;
+ }
+
+ if(0 == dev->enable){
+ printk(KERN_ERR "sprdfb: [%s]: Invalid Device status %d", __FUNCTION__, dev->enable);
+ return -1;
+ }
+
+ ret = dev->ctrl->refresh(dev);
+ if (ret) {
+ printk(KERN_ERR "sprdfb: failed to refresh !!!!\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+#include <video/sprd_fb.h>
+static int sprdfb_ioctl(struct fb_info *info, unsigned int cmd,
+ unsigned long arg)
+{
+ int result = -1;
+ struct sprdfb_device *dev = NULL;
+#ifdef CONFIG_FB_LCD_OVERLAY_SUPPORT
+ overlay_info local_overlay_info;
+ overlay_display local_overlay_display;
+#endif
+ int power_mode;
+ void __user *argp = (void __user *)arg;
+
+ if(NULL == info){
+ printk(KERN_ERR "sprdfb: sprdfb_ioctl error. (Invalid Parameter)");
+ return -1;
+ }
+
+ dev = info->par;
+
+ switch(cmd){
+#ifdef CONFIG_FB_LCD_OVERLAY_SUPPORT
+ case SPRD_FB_SET_OVERLAY:
+ pr_debug(KERN_INFO "sprdfb: [%s]: SPRD_FB_SET_OVERLAY\n", __FUNCTION__);
+ memset(&local_overlay_info,0,sizeof(local_overlay_info));
+ if (copy_from_user(&local_overlay_info, argp, sizeof(local_overlay_info))){
+ printk("sprdfb: SET_OVERLAY copy failed!\n");
+ return -EFAULT;
+ }
+ if(NULL != dev->ctrl->enable_overlay){
+ result = dev->ctrl->enable_overlay(dev, &local_overlay_info, 1);
+ }
+ break;
+ case SPRD_FB_DISPLAY_OVERLAY:
+ pr_debug(KERN_INFO "sprdfb: [%s]: SPRD_FB_DISPLAY_OVERLAY\n", __FUNCTION__);
+ memset(&local_overlay_display,0,sizeof(local_overlay_display));
+ if (copy_from_user(&local_overlay_display, argp, sizeof(local_overlay_display))){
+ printk("sprdfb: DISPLAY_OVERLAY copy failed!\n");
+ return -EFAULT;
+ }
+ if(NULL != dev->ctrl->display_overlay){
+ result = dev->ctrl->display_overlay(dev, &local_overlay_display);
+ }
+ break;
+#endif
+#ifdef CONFIG_FB_VSYNC_SUPPORT
+ case FBIO_WAITFORVSYNC:
+ pr_debug(KERN_INFO "sprdfb: [%s]: FBIO_WAITFORVSYNC\n", __FUNCTION__);
+ if(NULL != dev->ctrl->wait_for_vsync){
+ result = dev->ctrl->wait_for_vsync(dev);
+ }
+ break;
+#endif
+
+#ifdef CONFIG_FB_DYNAMIC_FREQ_SCALING
+ case SPRD_FB_CHANGE_FPS:
+ {
+ int fps;
+ result = copy_from_user(&fps, argp, sizeof(fps));
+ if (result) {
+ pr_err("%s: copy_from_user failed", __func__);
+ return result;
+ }
+ pr_info("%s: fps will be changed to %d via ioctl\n",
+ __func__, fps);
+ result = sprdfb_chg_clk_intf(dev,
+ SPRDFB_DYNAMIC_FPS, fps);
+ if (result) {
+ pr_err("%s: fps is set fail. fps=%d, ret=%d\n",
+ __func__, fps, result);
+ return result;
+ }
+ break;
+ }
+#endif
+
+ case SPRD_FB_IS_REFRESH_DONE:
+ pr_debug(KERN_INFO "sprdfb: [%s]: SPRD_FB_IS_REFRESH_DONE\n", __FUNCTION__);
+ if(NULL != dev->ctrl->is_refresh_done){
+ result = dev->ctrl->is_refresh_done(dev);
+ }
+ break;
+
+ case SPRD_FB_SET_POWER_MODE:
+ result = copy_from_user(&power_mode, argp, sizeof(power_mode));
+ printk("sprdfb: [%s] : SPRD_FB_SET_POWER_MODE (%d)\n", __FUNCTION__, power_mode);
+ break;
+
+ default:
+ printk(KERN_INFO "sprdfb: [%s]: unknown cmd(%d)\n", __FUNCTION__, cmd);
+ break;
+ }
+
+ pr_debug(KERN_INFO "sprdfb: [%s]: return %d\n",__FUNCTION__, result);
+ return result;
+}
+
+
+#ifdef CONFIG_COMPAT
+#ifdef CONFIG_FB_LCD_OVERLAY_SUPPORT
+
+static int sprdfb_set_overlayinfo(struct fb_info *info, unsigned int cmd,
+ unsigned long arg)
+{
+ overlay_info __user *local_overlay_info;
+ overlay_info32 __user *local_overlay_info32;
+ __u32 buffer;
+ int err;
+
+ local_overlay_info = compat_alloc_user_space(sizeof(overlay_info));
+ local_overlay_info32 = compat_ptr(arg);
+
+ if (copy_in_user(&(local_overlay_info->layer_index), &(local_overlay_info32->layer_index),
+ 4*sizeof(int)))
+ return -EFAULT;
+
+ if (copy_in_user(&(local_overlay_info->rb_switch), &(local_overlay_info32->rb_switch),
+ sizeof(bool)))
+ return -EFAULT;
+
+ if (copy_in_user(&(local_overlay_info->rect), &(local_overlay_info32->rect),
+ sizeof(overlay_rect)))
+ return -EFAULT;
+
+ if (get_user(buffer, &local_overlay_info32->buffer)||
+ put_user(compat_ptr(buffer), &local_overlay_info->buffer))
+ return -EFAULT;
+
+ err = sprdfb_ioctl(info, cmd, (unsigned long) local_overlay_info);
+
+ return err;
+}
+#endif
+static long sprdfb_compat_ioctl(struct fb_info *info, unsigned int cmd,
+ unsigned long arg)
+{
+ int result = -1;
+
+ pr_debug(KERN_INFO "sprdfb: [%s]: ++\n", __FUNCTION__);
+
+ if(NULL == info){
+ printk(KERN_ERR "sprdfb: sprdfb_ioctl error. (Invalid Parameter)");
+ return -1;
+ }
+
+ switch(cmd){
+#ifdef CONFIG_FB_LCD_OVERLAY_SUPPORT
+ case SPRD_FB_SET_OVERLAY:
+ pr_debug(KERN_INFO "sprdfb: [%s]: SPRD_FB_SET_OVERLAY\n", __FUNCTION__);
+ result = sprdfb_set_overlayinfo(info, cmd, arg);
+ break;
+ case SPRD_FB_DISPLAY_OVERLAY:
+ pr_debug(KERN_INFO "sprdfb: [%s]: SPRD_FB_DISPLAY_OVERLAY\n", __FUNCTION__);
+ arg = (unsigned long) compat_ptr(arg);
+ result = sprdfb_ioctl(info, cmd, arg);
+ break;
+#endif
+#ifdef CONFIG_FB_VSYNC_SUPPORT
+ case FBIO_WAITFORVSYNC:
+ pr_debug(KERN_INFO "sprdfb: [%s]: FBIO_WAITFORVSYNC\n", __FUNCTION__);
+ result = sprdfb_ioctl(info, cmd, arg);
+ break;
+#endif
+#ifdef CONFIG_FB_DYNAMIC_FREQ_SCALING
+ case SPRD_FB_CHANGE_FPS:
+#endif
+ case SPRD_FB_SET_POWER_MODE:
+ pr_debug(KERN_INFO "sprdfb: [%s]: (%d)\n", __FUNCTION__,cmd);
+ arg = (unsigned long) compat_ptr(arg);
+ result = sprdfb_ioctl(info, cmd, arg);
+ break;
+ case SPRD_FB_IS_REFRESH_DONE:
+ default:
+ pr_debug(KERN_INFO "sprdfb: [%s]: (%d)\n", __FUNCTION__,cmd);
+ result = sprdfb_ioctl(info, cmd, arg);
+ break;
+ }
+ pr_debug(KERN_INFO "sprdfb: [%s]: return %d\n",__FUNCTION__, result);
+ return result;
+}
+#endif
+
+static int sprdfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fb)
+{
+ if ((var->xres != fb->var.xres) ||
+ (var->yres != fb->var.yres) ||
+ (var->xres_virtual != fb->var.xres_virtual) ||
+ (var->yres_virtual != fb->var.yres_virtual) ||
+ (var->xoffset != fb->var.xoffset) ||
+#ifndef BIT_PER_PIXEL_SURPPORT
+ (var->bits_per_pixel != fb->var.bits_per_pixel) ||
+#endif
+ (var->grayscale != fb->var.grayscale))
+ return -EINVAL;
+ return 0;
+}
+
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void sprdfb_early_suspend (struct early_suspend* es)
+{
+ struct sprdfb_device *dev = container_of(es, struct sprdfb_device, early_suspend);
+ struct fb_info *fb = dev->fb;
+ printk("sprdfb: [%s]\n",__FUNCTION__);
+
+ fb_set_suspend(fb, FBINFO_STATE_SUSPENDED);
+
+ if (!lock_fb_info(fb)) {
+ return ;
+ }
+ dev->ctrl->suspend(dev);
+ unlock_fb_info(fb);
+}
+
+static void sprdfb_late_resume (struct early_suspend* es)
+{
+ struct sprdfb_device *dev = container_of(es, struct sprdfb_device, early_suspend);
+ struct fb_info *fb = dev->fb;
+ pr_debug("sprdfb: [%s]\n",__FUNCTION__);
+
+ if (!lock_fb_info(fb)) {
+ return ;
+ }
+ dev->ctrl->resume(dev);
+ unlock_fb_info(fb);
+
+ fb_set_suspend(fb, FBINFO_STATE_RUNNING);
+}
+
+#else
+
+static int sprdfb_suspend(struct platform_device *pdev,pm_message_t state)
+{
+ struct sprdfb_device *dev = platform_get_drvdata(pdev);
+ printk("sprdfb: [%s]\n",__FUNCTION__);
+
+ dev->ctrl->suspend(dev);
+
+ return 0;
+}
+
+static int sprdfb_resume(struct platform_device *pdev)
+{
+ struct sprdfb_device *dev = platform_get_drvdata(pdev);
+ printk("sprdfb: [%s]\n",__FUNCTION__);
+
+ dev->ctrl->resume(dev);
+
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_FB_ESD_SUPPORT
+static void ESD_work_func(struct work_struct *work)
+{
+ struct sprdfb_device *dev = container_of(work, struct sprdfb_device, ESD_work.work);
+
+ pr_debug("sprdfb: [%s] enter!\n", __FUNCTION__);
+
+ //do real ESD check
+ //mdelay(1000);
+ if(NULL != dev->ctrl->ESD_check){
+ dev->ctrl->ESD_check(dev);
+ }
+
+ if(0 != dev->enable){
+ pr_debug("sprdfb: reschedule ESD workqueue!\n");
+ schedule_delayed_work(&dev->ESD_work, msecs_to_jiffies(dev->ESD_timeout_val));
+ dev->ESD_work_start = true;
+ }else{
+ printk("sprdfb: DON't reschedule ESD workqueue since device not avialbe!!\n");
+ }
+
+ pr_debug("sprdfb: [%s] leave!\n", __FUNCTION__);
+}
+#endif
+
+uint32_t sprdfb_get_chip_id(void)
+{
+ uint32_t adie_chip_id = sci_get_chip_id();
+ if((adie_chip_id & 0xffff0000) < 0x50000000) {
+ printk("sprdfb: [%s], chip id 0x%08x is invalidate, try to get it by reading reg directly!\n", __FUNCTION__, adie_chip_id);
+
+ adie_chip_id = dispc_glb_read(SPRD_AONAPB_BASE+0xFC);
+ if((adie_chip_id & 0xffff0000) < 0x50000000) {
+ printk("sprdfb: [%s], chip id 0x%08x from reg is invalidate too!\n", __FUNCTION__, adie_chip_id);
+ }
+ }
+
+ printk("sprdfb: return chip id 0x%08x \n", adie_chip_id);
+ return adie_chip_id;
+}
+
+static fb_capability sprdfb_config_capability(void)
+{
+ uint32_t adie_chip_id = 0;
+ static fb_capability s_fb_capability = {0};
+
+ memset((void*)&s_fb_capability, 0, sizeof(s_fb_capability));
+
+ adie_chip_id = sprdfb_get_chip_id();
+ adie_chip_id &= 0xffff0000;
+ switch(adie_chip_id) {
+ case 0x88600000: /*Pike*/
+ case 0x98200000: /*PikeL*/
+ s_fb_capability.need_light_sleep = 0;
+ s_fb_capability.mipi_pll_type = 1;
+ break;
+
+ case 0x96310000: /*Sharkl64*/
+ s_fb_capability.need_light_sleep = 1;
+ s_fb_capability.mipi_pll_type = 2;
+ break;
+
+ default:
+ s_fb_capability.need_light_sleep = 1;
+ s_fb_capability.mipi_pll_type = 0;
+ break;
+ }
+ return s_fb_capability;
+}
+
+static int sprdfb_probe(struct platform_device *pdev)
+{
+ struct fb_info *fb = NULL;
+ struct sprdfb_device *dev = NULL;
+ int ret = 0;
+#ifdef CONFIG_OF
+ struct resource r;
+
+#ifdef CONFIG_FB_LOW_RES_SIMU
+ uint32_t display_size_array[2];
+#endif
+
+ lcd_user = proc_mkdir("lcd_user", NULL);
+
+ if (NULL == proc_create("lcd_name", 0777, lcd_user, &_gProclcdName))
+ {
+ printk("Failed to create _gProclcdName!\n");
+ }
+ else
+ {
+ printk("create _gProclcdName OK!\n");
+ }
+
+ pr_debug(KERN_INFO "sprdfb: [%s]\n", __FUNCTION__);
+#else
+ pr_debug(KERN_INFO "sprdfb: [%s], id = %d\n", __FUNCTION__, pdev->id);
+#endif
+
+ fb = framebuffer_alloc(sizeof(struct sprdfb_device), &pdev->dev);
+ if (!fb) {
+ printk(KERN_ERR "sprdfb: sprdfb_probe allocate buffer fail.\n");
+ ret = -ENOMEM;
+ goto err0;
+ }
+
+ dev = fb->par;
+ dev->fb = fb;
+#ifdef CONFIG_OF
+ dev->of_dev = &(pdev->dev);
+
+ dev->dev_id = of_alias_get_id(pdev->dev.of_node, "lcd");
+ printk("sprdfb: [%s] id = %d\n", __FUNCTION__, dev->dev_id);
+#else
+ dev->dev_id = pdev->id;
+#endif
+ if((SPRDFB_MAINLCD_ID != dev->dev_id) &&(SPRDFB_SUBLCD_ID != dev->dev_id)){
+ printk(KERN_ERR "sprdfb: sprdfb_probe fail. (unsupported device id)\n");
+ goto err0;
+ }
+
+ switch(SPRDFB_IN_DATA_TYPE){
+ case SPRD_IN_DATA_TYPE_ABGR888:
+ dev->bpp = 32;
+ break;
+ case SPRD_IN_DATA_TYPE_BGR565:
+ dev->bpp = 16;
+ break;
+ default:
+ dev->bpp = 32;
+ break;
+ }
+
+ if(SPRDFB_MAINLCD_ID == dev->dev_id){
+ dev->ctrl = &sprdfb_dispc_ctrl;
+#ifdef CONFIG_OF
+ if(0 != of_address_to_resource(pdev->dev.of_node, 0, &r)){
+ printk(KERN_ERR "sprdfb: sprdfb_probe fail. (can't get register base address)\n");
+ goto err0;
+ }
+ g_dispc_base_addr = (unsigned long)ioremap_nocache(r.start,
+ resource_size(&r));
+ if(!g_dispc_base_addr)
+ BUG();
+ printk("sprdfb: set g_dispc_base_addr = %ld\n", g_dispc_base_addr);
+#endif
+ }
+
+ dev->frame_count = 0;
+ dev->logo_buffer_addr_v = 0;
+ dev->capability = sprdfb_config_capability();
+
+ if(sprdfb_panel_get(dev)){
+ dev->panel_ready = true;
+
+#ifdef CONFIG_OF
+#ifdef CONFIG_FB_LOW_RES_SIMU
+ ret = of_property_read_u32_array(pdev->dev.of_node, "sprd,fb_display_size", display_size_array, 2);
+ if (0 != ret) {
+ printk("sprdfb: read display_size from dts fail (%d)\n", ret);
+ dev->display_width = dev->panel->width;
+ dev->display_height = dev->panel->height;
+ } else {
+ dev->display_width = display_size_array[0];
+ dev->display_height = display_size_array[1];
+ }
+#endif
+#endif
+
+ dev->ctrl->logo_proc(dev);
+ }else{
+ dev->panel_ready = false;
+ }
+
+ dev->ctrl->early_init(dev);
+
+ if(!dev->panel_ready){
+ if (!sprdfb_panel_probe(dev)) {
+ ret = -EIO;
+ goto cleanup;
+ }
+ }
+
+ ret = setup_fb_mem(dev, pdev);
+ if (ret) {
+ goto cleanup;
+ }
+
+ setup_fb_info(dev);
+ /* register framebuffer device */
+ ret = register_framebuffer(fb);
+ if (ret) {
+ printk(KERN_ERR "sprdfb: sprdfb_probe register framebuffer fail.\n");
+ goto cleanup;
+ }
+ platform_set_drvdata(pdev, dev);
+ sprdfb_create_sysfs(dev);
+ dev->ctrl->init(dev);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ dev->early_suspend.suspend = sprdfb_early_suspend;
+ dev->early_suspend.resume = sprdfb_late_resume;
+ dev->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
+ register_early_suspend(&dev->early_suspend);
+#endif
+
+#ifdef CONFIG_FB_ESD_SUPPORT
+ pr_debug("sprdfb: Init ESD work queue!\n");
+ INIT_DELAYED_WORK(&dev->ESD_work, ESD_work_func);
+// sema_init(&dev->ESD_lock, 1);
+
+ if(SPRDFB_PANEL_IF_DPI == dev->panel_if_type){
+ dev->ESD_timeout_val = SPRDFB_ESD_TIME_OUT_VIDEO;
+ }else{
+ dev->ESD_timeout_val = SPRDFB_ESD_TIME_OUT_CMD;
+ }
+
+ dev->ESD_work_start = false;
+ dev->check_esd_time = 0;
+ dev->reset_dsi_time = 0;
+ dev->panel_reset_time = 0;
+#endif
+
+ return 0;
+
+cleanup:
+ sprdfb_panel_remove(dev);
+ dev->ctrl->uninit(dev);
+ fb_free_resources(dev);
+err0:
+ dev_err(&pdev->dev, "failed to probe sprdfb\n");
+ return ret;
+}
+
+static int sprdfb_remove(struct platform_device *pdev)
+{
+ struct sprdfb_device *dev = platform_get_drvdata(pdev);
+
+ printk("sprdfb: [%s]\n",__FUNCTION__);
+ sprdfb_remove_sysfs(dev);
+ sprdfb_panel_remove(dev);
+ dev->ctrl->uninit(dev);
+ fb_free_resources(dev);
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id sprdfb_dt_ids[] = {
+ { .compatible = "sprd,sprdfb", },
+ {}
+};
+#endif
+
+static struct platform_driver sprdfb_driver = {
+ .probe = sprdfb_probe,
+
+#ifndef CONFIG_HAS_EARLYSUSPEND
+ .suspend = sprdfb_suspend,
+ .resume = sprdfb_resume,
+#endif
+ .remove = sprdfb_remove,
+ .driver = {
+ .name = "sprd_fb",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_OF
+ .of_match_table = of_match_ptr(sprdfb_dt_ids),
+#endif
+ },
+};
+
+
+static int __init sprdfb_init(void)
+{
+ return platform_driver_register(&sprdfb_driver);
+}
+
+static void __exit sprdfb_exit(void)
+{
+ return platform_driver_unregister(&sprdfb_driver);
+}
+
+module_init(sprdfb_init);
+module_exit(sprdfb_exit);