/* * 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 #include #ifdef CONFIG_HAS_EARLYSUSPEND #include #endif #include #include #include #include #include #include #ifdef CONFIG_OF #include #include #include #endif #include #include "sprdfb_chip_common.h" #include "sprdfb.h" #include "sprdfb_panel.h" #include #ifdef CONFIG_FB_MMAP_CACHED #include #include #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