/* * 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 #include #include #include #include #if (defined(CONFIG_SPRD_SCXX30_DMC_FREQ) || defined(CONFIG_SPRD_SCX35_DMC_FREQ)) #include #endif #include #include #ifdef CONFIG_OF #include #endif #include #include "sprdfb_dispc_reg.h" #include "sprdfb_panel.h" #include "sprdfb.h" #include "sprdfb_chip_common.h" #ifdef CONFIG_CPU_IDLE #include #endif #include //#define SHARK_LAYER_COLOR_SWITCH_FEATURE // bug212892 #ifndef CONFIG_OF #ifdef CONFIG_FB_SCX15 #define DISPC_CLOCK_PARENT ("clk_192m") #define DISPC_CLOCK (192*1000000) #define DISPC_DBI_CLOCK_PARENT ("clk_256m") #define DISPC_DBI_CLOCK (256*1000000) #define DISPC_DPI_CLOCK_PARENT ("clk_384m") #define SPRDFB_DPI_CLOCK_SRC (384000000) #else #define DISPC_CLOCK_PARENT ("clk_256m") #define DISPC_CLOCK (256*1000000) #define DISPC_DBI_CLOCK_PARENT ("clk_256m") #define DISPC_DBI_CLOCK (256*1000000) #define DISPC_DPI_CLOCK_PARENT ("clk_384m") #define SPRDFB_DPI_CLOCK_SRC (384000000) #endif #define DISPC_EMC_EN_PARENT ("clk_aon_apb") #else #define DISPC_CLOCK_PARENT ("dispc_clk_parent") //#define DISPC_CLOCK (192*1000000) #define DISPC_DBI_CLOCK_PARENT ("dispc_dbi_clk_parent") //#define DISPC_DBI_CLOCK (256*1000000) #define DISPC_DPI_CLOCK_PARENT ("dispc_dpi_clk_parent") //#define SPRDFB_DPI_CLOCK_SRC (192000000) #define DISPC_EMC_EN_PARENT ("dispc_emc_clk_parent") #define DISPC_PLL_CLK ("dispc_clk") #define DISPC_DBI_CLK ("dispc_dbi_clk") #define DISPC_DPI_CLK ("dispc_dpi_clk") #define DISPC_EMC_CLK ("dispc_emc_clk") #define DISPC_CLOCK_SRC_ID 0 #define DISPC_DBI_CLOCK_SRC_ID 1 #define DISPC_DPI_CLOCK_SRC_ID 2 #define DISPC_CLOCK_NUM 3 #endif #if (defined CONFIG_FB_SCX35L) #define SPRDFB_BRIGHTNESS (0x00<<16)//(0x03<<16)// 9-bits #define SPRDFB_CONTRAST (0x100<<0) //10-bits #define SPRDFB_OFFSET_U (0x81<<16)//8-bits #define SPRDFB_SATURATION_U (0x100<<0)//(0x12A<<0)//10-bits #define SPRDFB_OFFSET_V (0x82<<16)//8-bits #define SPRDFB_SATURATION_V (0x100<<0)//(0x12A<<0)//10-bits #elif ((defined CONFIG_FB_SCX15) || (defined CONFIG_FB_SCX30G)) #define SPRDFB_BRIGHTNESS (0x02<<16)//(0x03<<16)// 9-bits #define SPRDFB_CONTRAST (0x12A<<0) //10-bits #define SPRDFB_OFFSET_U (0x81<<16)//8-bits #define SPRDFB_SATURATION_U (0x123<<0)//(0x12A<<0)//10-bits #define SPRDFB_OFFSET_V (0x82<<16)//8-bits #define SPRDFB_SATURATION_V (0x123<<0)//(0x12A<<0)//10-bits #else #define SPRDFB_CONTRAST (74) #define SPRDFB_SATURATION (73) #define SPRDFB_BRIGHTNESS (2) #endif typedef enum { SPRDFB_DYNAMIC_CLK_FORCE, //force enable/disable SPRDFB_DYNAMIC_CLK_REFRESH, //enable for refresh/display_overlay SPRDFB_DYNAMIC_CLK_COUNT, //enable disable in pairs SPRDFB_DYNAMIC_CLK_MAX, } SPRDFB_DYNAMIC_CLK_SWITCH_E; struct sprdfb_dispc_context { struct clk *clk_dispc; struct clk *clk_dispc_dpi; struct clk *clk_dispc_dbi; struct clk *clk_dispc_emc; bool is_inited; bool is_first_frame; bool clk_is_open; bool clk_is_refreshing; int clk_open_count; //spinlock_t clk_spinlock; struct semaphore clk_sem; struct sprdfb_device *dev; uint32_t vsync_waiter; wait_queue_head_t vsync_queue; uint32_t vsync_done; #ifdef CONFIG_FB_LCD_OVERLAY_SUPPORT /* overlay */ uint32_t overlay_state; /*0-closed, 1-configed, 2-started*/ // struct semaphore overlay_lock; #endif #ifdef CONFIG_FB_VSYNC_SUPPORT uint32_t waitfor_vsync_waiter; wait_queue_head_t waitfor_vsync_queue; uint32_t waitfor_vsync_done; #endif #ifdef CONFIG_FB_MMAP_CACHED struct vm_area_struct *vma; #endif }; static struct sprdfb_dispc_context dispc_ctx = {0}; #ifdef CONFIG_OF unsigned long g_dispc_base_addr = 0; EXPORT_SYMBOL(g_dispc_base_addr); #endif extern void sprdfb_panel_suspend(struct sprdfb_device *dev); extern void sprdfb_panel_resume(struct sprdfb_device *dev, bool from_deep_sleep); extern void sprdfb_panel_before_refresh(struct sprdfb_device *dev); extern void sprdfb_panel_after_refresh(struct sprdfb_device *dev); extern void sprdfb_panel_invalidate(struct panel_spec *self); extern void sprdfb_panel_invalidate_rect(struct panel_spec *self, uint16_t left, uint16_t top, uint16_t right, uint16_t bottom); #ifdef CONFIG_FB_ESD_SUPPORT extern uint32_t sprdfb_panel_ESD_check(struct sprdfb_device *dev); #endif #ifdef CONFIG_FB_LCD_OVERLAY_SUPPORT static int overlay_start(struct sprdfb_device *dev, uint32_t layer_index); static int overlay_close(struct sprdfb_device *dev); #endif static int sprdfb_dispc_clk_disable(struct sprdfb_dispc_context *dispc_ctx_ptr, SPRDFB_DYNAMIC_CLK_SWITCH_E clock_switch_type); static int sprdfb_dispc_clk_enable(struct sprdfb_dispc_context *dispc_ctx_ptr, SPRDFB_DYNAMIC_CLK_SWITCH_E clock_switch_type); static int32_t sprdfb_dispc_init(struct sprdfb_device *dev); static void dispc_reset(void); static void dispc_stop(struct sprdfb_device *dev); static void dispc_module_enable(void); //#if (defined(CONFIG_FB_DYNAMIC_FREQ_SCALING) || !defined(FB_CHECK_ESD_IN_VFP)) static void dispc_stop_for_feature(struct sprdfb_device *dev); static void dispc_run_for_feature(struct sprdfb_device *dev); #if (defined(CONFIG_SPRD_SCXX30_DMC_FREQ) || defined(CONFIG_SPRD_SCX35_DMC_FREQ)) static unsigned int sprdfb_dispc_change_threshold(struct devfreq_dbs *h, unsigned int state); static unsigned int sprdfb_dispc_threshold_check(void); #endif static int dispc_update_clk(struct sprdfb_device *fb_dev, u32 new_val, int howto); /** * author: Yang.Haibing haibing.yang@spreadtrum.com * * dispc_check_new_clk - check and convert new clock to the value that can be set * @fb_dev: spreadtrum specific fb device * @new_pclk: actual settable clock via calculating new_val and fps * @pclk_src: clock source of dpi clock * @new_val: expected clock * @type: check the type is fps or pclk * * Returns 0 on success, MINUS on error. */ static int dispc_check_new_clk(struct sprdfb_device *fb_dev, u32 *new_pclk, u32 pclk_src, u32 new_val, int type) { int divider; u32 hpixels, vlines, pclk, fps; struct panel_spec* panel = fb_dev->panel; struct info_mipi * mipi; struct info_rgb* rgb; pr_debug("%s: enter\n", __func__); if(!panel){ pr_err("No panel is specified!\n"); return -ENXIO; } mipi = panel->info.mipi; rgb = panel->info.rgb; if (fb_dev->panel_if_type != SPRDFB_PANEL_IF_DPI) { pr_err("panel interface should be DPI\n"); return -EINVAL; } if (new_val <= 0 || !new_pclk) { pr_err("new parameter is invalid\n"); return -EINVAL; } if (fb_dev->panel->type == LCD_MODE_DSI) { hpixels = panel->width + mipi->timing->hsync + mipi->timing->hbp + mipi->timing->hfp; vlines = panel->height + mipi->timing->vsync + mipi->timing->vbp + mipi->timing->vfp; } else if(fb_dev->panel->type == LCD_MODE_RGB || fb_dev->panel->type == LCD_MODE_LVDS) { hpixels = panel->width + rgb->timing->hsync + rgb->timing->hbp + rgb->timing->hfp; vlines = panel->height + rgb->timing->vsync + rgb->timing->vbp + rgb->timing->vfp; } else { pr_err("[%s] unexpected panel type (%d)\n", __func__, fb_dev->panel->type); return -EINVAL; } switch (type) { /* * FIXME: TODO: SPRDFB_FORCE_FPS will be used for accurate fps someday. * But now it is the same as SPRDFB_DYNAMIC_FPS. */ case SPRDFB_FORCE_FPS: case SPRDFB_DYNAMIC_FPS: if (new_val < LCD_MIN_FPS || new_val > LCD_MAX_FPS) { pr_err("Unsupported FPS. fps range should be [%d, %d]\n", LCD_MIN_FPS, LCD_MAX_FPS); return -EINVAL; } pclk = hpixels * vlines * new_val; divider = ROUND(pclk_src, pclk); *new_pclk = pclk_src / divider; /* Save the updated fps */ panel->fps = new_val; break; case SPRDFB_DYNAMIC_PCLK: divider = ROUND(pclk_src, new_val); pclk = pclk_src / divider; fps = pclk / ( hpixels * vlines); if (fps < LCD_MIN_FPS || fps > LCD_MAX_FPS) { pr_err("Unsupported FPS. fps range should be [%d, %d]\n", LCD_MIN_FPS, LCD_MAX_FPS); return -EINVAL; } *new_pclk = pclk; /* Save the updated fps */ panel->fps = fps; break; default: pr_err("This checked type is unsupported.\n"); *new_pclk = 0; return -EINVAL; } return 0; } void dispc_irq_trick(struct sprdfb_device *dev) { if(NULL == dev){ return; } if(SPRDFB_PANEL_IF_DPI == dev->panel_if_type){ dispc_set_bits(DISPC_INT_ERR_MASK, DISPC_INT_EN); } } extern void dsi_irq_trick(void); //static uint32_t underflow_ever_happened = 0; static irqreturn_t dispc_isr(int irq, void *data) { struct sprdfb_dispc_context *dispc_ctx = (struct sprdfb_dispc_context *)data; uint32_t reg_val; struct sprdfb_device *dev = dispc_ctx->dev; bool done = false; #ifdef CONFIG_FB_VSYNC_SUPPORT bool vsync = false; #endif reg_val = dispc_read(DISPC_INT_STATUS); pr_debug("sprdfb: dispc_isr (0x%x)\n",reg_val ); //printk("%s%d: underflow_ever_happened:0x%08x \n",__func__,__LINE__,underflow_ever_happened); //dispc_irq_trick_in(reg_val); if(reg_val & DISPC_INT_ERR_MASK){ printk("sprdfb: Warning: dispc underflow (0x%x)!\n",reg_val); //underflow_ever_happened++; dispc_write(DISPC_INT_ERR_MASK, DISPC_INT_CLR); /*disable err interupt*/ dispc_clear_bits(DISPC_INT_ERR_MASK, DISPC_INT_EN); } if(NULL == dev){ return IRQ_HANDLED; } if((reg_val & 0x10) && (SPRDFB_PANEL_IF_DPI == dev->panel_if_type)){/*dispc update done isr*/ #if 0 if(dispc_ctx->is_first_frame){ /*dpi register update with SW and VSync*/ dispc_clear_bits(BIT(4), DISPC_DPI_CTRL); /* start refresh */ dispc_set_bits((1 << 4), DISPC_CTRL); dispc_ctx->is_first_frame = false; } #endif dispc_write(DISPC_INT_UPDATE_DONE_MASK, DISPC_INT_CLR); done = true; }else if ((reg_val & DISPC_INT_DONE_MASK) && (SPRDFB_PANEL_IF_DPI != dev->panel_if_type)){ /* dispc done isr */ dispc_write(DISPC_INT_DONE_MASK, DISPC_INT_CLR); dispc_ctx->is_first_frame = false; done = true; } #ifdef CONFIG_FB_ESD_SUPPORT #ifdef FB_CHECK_ESD_BY_TE_SUPPORT if((reg_val & DISPC_INT_TE_MASK) && (SPRDFB_PANEL_IF_DPI == dev->panel_if_type)){ /*dispc external TE isr*/ dispc_write(DISPC_INT_TE_MASK, DISPC_INT_CLR); if(0 != dev->esd_te_waiter){ printk("sprdfb: dispc_isr esd_te_done!"); dev->esd_te_done =1; wake_up_interruptible_all(&(dev->esd_te_queue)); dev->esd_te_waiter = 0; } } #endif #endif #ifdef CONFIG_FB_VSYNC_SUPPORT if((reg_val & DISPC_INT_HWVSYNC) && (SPRDFB_PANEL_IF_DPI == dev->panel_if_type)){/*dispc done isr*/ dispc_write(DISPC_INT_HWVSYNC, DISPC_INT_CLR); vsync = true; }else if((reg_val & DISPC_INT_TE_MASK) && (SPRDFB_PANEL_IF_EDPI == dev->panel_if_type)){ /*dispc te isr*/ dispc_write(DISPC_INT_TE_MASK, DISPC_INT_CLR); vsync = true; } if(vsync){ //printk("sprdfb: got vsync!\n"); dispc_ctx->waitfor_vsync_done = 1; if(dispc_ctx->waitfor_vsync_waiter){ //printk("sprdfb: wake!\n"); wake_up_interruptible_all(&(dispc_ctx->waitfor_vsync_queue)); } } #endif if(done){ dispc_ctx->vsync_done = 1; #ifdef CONFIG_FB_DYNAMIC_CLK_SUPPORT if(SPRDFB_PANEL_IF_DPI != dev->panel_if_type){ sprdfb_dispc_clk_disable(dispc_ctx,SPRDFB_DYNAMIC_CLK_REFRESH); } #endif if (dispc_ctx->vsync_waiter) { wake_up_interruptible_all(&(dispc_ctx->vsync_queue)); dispc_ctx->vsync_waiter = 0; } sprdfb_panel_after_refresh(dev); pr_debug(KERN_INFO "sprdfb: [%s]: Done INT, reg_val = %d !\n", __FUNCTION__, reg_val); } return IRQ_HANDLED; } /* dispc soft reset */ static void dispc_reset(void) { sci_glb_set(REG_AHB_SOFT_RST, (BIT_DISPC_SOFT_RST) ); udelay(10); sci_glb_clr(REG_AHB_SOFT_RST, (BIT_DISPC_SOFT_RST) ); } static inline void dispc_set_bg_color(uint32_t bg_color) { dispc_write(bg_color, DISPC_BG_COLOR); } static inline void dispc_set_osd_ck(uint32_t ck_color) { dispc_write(ck_color, DISPC_OSD_CK); } static inline void dispc_osd_enable(bool is_enable) { uint32_t reg_val; reg_val = dispc_read(DISPC_OSD_CTRL); if(is_enable){ reg_val = reg_val | (BIT(0)); } else{ reg_val = reg_val & (~(BIT(0))); } dispc_write(reg_val, DISPC_OSD_CTRL); } static inline void dispc_set_osd_alpha(uint8_t alpha) { dispc_write(alpha, DISPC_OSD_ALPHA); } static void dispc_dithering_enable(bool enable) { if(enable){ dispc_set_bits(BIT(6), DISPC_CTRL); }else{ dispc_clear_bits(BIT(6), DISPC_CTRL); } } static void dispc_pwr_enable(bool enable) { if(enable){ dispc_set_bits(BIT(7), DISPC_CTRL); }else{ dispc_clear_bits(BIT(7), DISPC_CTRL); } } static void dispc_set_exp_mode(uint16_t exp_mode) { uint32_t reg_val = dispc_read(DISPC_CTRL); reg_val &= ~(0x3 << 16); reg_val |= (exp_mode << 16); dispc_write(reg_val, DISPC_CTRL); } /* * thres0: module fetch data when buffer depth <= thres0 * thres1: module release busy and close AXI clock * thres2: module open AXI clock and prepare to fetch data */ static void dispc_set_threshold(uint16_t thres0, uint16_t thres1, uint16_t thres2) { dispc_write((thres0)|(thres1<<14)|(thres2<<16), DISPC_BUF_THRES); } static void dispc_module_enable(void) { /*dispc module enable */ dispc_write((1<<0), DISPC_CTRL); /*disable dispc INT*/ dispc_write(0x0, DISPC_INT_EN); /* clear dispc INT */ dispc_write(0x3F, DISPC_INT_CLR); } static inline int32_t dispc_set_disp_size(struct fb_var_screeninfo *var) { uint32_t reg_val; reg_val = (var->xres & 0xfff) | ((var->yres & 0xfff ) << 16); dispc_write(reg_val, DISPC_SIZE_XY); return 0; } static void dispc_layer_init(struct fb_var_screeninfo *var) { uint32_t reg_val = 0; // dispc_clear_bits((1<<0),DISPC_IMG_CTRL); dispc_write(0x0, DISPC_IMG_CTRL); dispc_clear_bits((1<<0),DISPC_OSD_CTRL); /******************* OSD layer setting **********************/ /*enable OSD layer*/ reg_val |= (1 << 0); /*disable color key */ /* alpha mode select - block alpha*/ reg_val |= (1 << 2); /* data format */ if (var->bits_per_pixel == 32) { /* ABGR */ reg_val |= (3 << 4); /* rb switch */ reg_val |= (1 << 15); } else { /* RGB565 */ reg_val |= (5 << 4); /* B2B3B0B1 */ reg_val |= (2 << 8); } dispc_write(reg_val, DISPC_OSD_CTRL); /* OSD layer alpha value */ dispc_set_osd_alpha(0xff); /* OSD layer size */ reg_val = ( var->xres & 0xfff) | (( var->yres & 0xfff ) << 16); dispc_write(reg_val, DISPC_OSD_SIZE_XY); /* OSD layer start position */ dispc_write(0, DISPC_OSD_DISP_XY); /* OSD layer pitch */ reg_val = ( var->xres & 0xfff) ; dispc_write(reg_val, DISPC_OSD_PITCH); /* OSD color_key value */ dispc_set_osd_ck(0x0); } static void dispc_layer_update(struct fb_var_screeninfo *var) { uint32_t reg_val = 0; /******************* OSD layer setting **********************/ /*enable OSD layer*/ reg_val |= (1 << 0); /*disable color key */ /* alpha mode select - block alpha*/ reg_val |= (1 << 2); /* data format */ if (var->bits_per_pixel == 32) { /* ABGR */ reg_val |= (3 << 4); /* rb switch */ reg_val |= (1 << 15); } else { /* RGB565 */ reg_val |= (5 << 4); /* B2B3B0B1 */ reg_val |= (2 << 8); } dispc_write(reg_val, DISPC_OSD_CTRL); } static int32_t dispc_sync(struct sprdfb_device *dev) { int ret; if (dev->enable == 0) { printk("sprdfb: dispc_sync fb suspeneded already!!\n"); return -1; } #ifdef CONFIG_SC_FPGA ret = wait_event_interruptible_timeout(dispc_ctx.vsync_queue, dispc_ctx.vsync_done, msecs_to_jiffies(500)); #else ret = wait_event_interruptible_timeout(dispc_ctx.vsync_queue, dispc_ctx.vsync_done, msecs_to_jiffies(100)); #endif if (!ret) { /* time out */ dispc_ctx.vsync_done = 1; /*error recovery */ printk(KERN_ERR "sprdfb: dispc_sync time out!!!!!\n"); {/*for debug*/ int32_t i = 0; for(i=0;i<256;i+=16){ printk("sprdfb: %x: 0x%x, 0x%x, 0x%x, 0x%x\n", i, dispc_read(i), dispc_read(i+4), dispc_read(i+8), dispc_read(i+12)); } printk("**************************************\n"); } return -1; } return 0; } static void dispc_run(struct sprdfb_device *dev) { if(0 == dev->enable){ return; } if(SPRDFB_PANEL_IF_DPI == dev->panel_if_type){ if(!dispc_ctx.is_first_frame){ dispc_ctx.vsync_done = 0; dispc_ctx.vsync_waiter ++; } /*dpi register update*/ dispc_set_bits(BIT(5), DISPC_DPI_CTRL); udelay(30); if(dispc_ctx.is_first_frame){ /*dpi register update with SW and VSync*/ dispc_clear_bits(BIT(4), DISPC_DPI_CTRL); /* start refresh */ dispc_set_bits((1 << 4), DISPC_CTRL); dispc_ctx.is_first_frame = false; } else { #ifndef CONFIG_FB_TRIPLE_FRAMEBUFFER dispc_sync(dev); #else #ifdef CONFIG_FB_LCD_OVERLAY_SUPPORT if(SPRD_OVERLAY_STATUS_STARTED == dispc_ctx.overlay_state){ dispc_sync(dev); } #endif #endif } }else{ dispc_ctx.vsync_done = 0; /* start refresh */ dispc_set_bits((1 << 4), DISPC_CTRL); } dispc_irq_trick(dev); //dispc_irq_trick_out(); #if !defined(CONFIG_FB_SCX15) && !defined(CONFIG_SC_FPGA) //dsi_irq_trick(0,0); dsi_irq_trick(); #endif } static void dispc_stop(struct sprdfb_device *dev) { if(SPRDFB_PANEL_IF_DPI == dev->panel_if_type){ /*dpi register update with SW only*/ dispc_set_bits(BIT(4), DISPC_DPI_CTRL); /* stop refresh */ dispc_clear_bits((1 << 4), DISPC_CTRL); dispc_ctx.is_first_frame = true; } } static int32_t sprdfb_dispc_uninit(struct sprdfb_device *dev) { pr_debug(KERN_INFO "sprdfb: [%s]\n",__FUNCTION__); dev->enable = 0; sprdfb_dispc_clk_disable(&dispc_ctx,SPRDFB_DYNAMIC_CLK_FORCE); return 0; } static int32_t dispc_clk_init(struct sprdfb_device *dev) { int ret = 0; u32 dpi_clk; struct clk *clk_parent1, *clk_parent2, *clk_parent3, *clk_parent4; #ifdef CONFIG_OF uint32_t clk_src[DISPC_CLOCK_NUM]; uint32_t def_dpi_clk_div = 1; #endif pr_debug(KERN_INFO "sprdfb: [%s]\n", __FUNCTION__); sci_glb_set(DISPC_CORE_EN, BIT_DISPC_CORE_EN); // sci_glb_set(DISPC_EMC_EN, BIT_DISPC_EMC_EN); #ifdef CONFIG_OF ret = of_property_read_u32_array(dev->of_dev->of_node, "clock-src", clk_src, 3); if(0 != ret){ printk("sprdfb: read clock-src fail (%d)\n", ret); return -1; } ret = of_property_read_u32(dev->of_dev->of_node, "dpi_clk_div", &def_dpi_clk_div); if(0 != ret){ printk("sprdfb: read dpi_clk_div fail (%d)\n", ret); return -1; } clk_parent1 = of_clk_get_by_name(dev->of_dev->of_node, DISPC_CLOCK_PARENT); dpi_clk = clk_src[DISPC_DPI_CLOCK_SRC_ID]/def_dpi_clk_div; #else dpi_clk = DISPC_DPI_CLOCK; clk_parent1 = clk_get(NULL, DISPC_CLOCK_PARENT); #endif if (IS_ERR(clk_parent1)) { printk(KERN_WARNING "sprdfb: get clk_parent1 fail!\n"); return -1; } else { pr_debug(KERN_INFO "sprdfb: get clk_parent1 ok!\n"); } #ifdef CONFIG_OF clk_parent2 = of_clk_get_by_name(dev->of_dev->of_node, DISPC_DBI_CLOCK_PARENT); #else clk_parent2 = clk_get(NULL, DISPC_DBI_CLOCK_PARENT); #endif if (IS_ERR(clk_parent2)) { printk(KERN_WARNING "sprdfb: get clk_parent2 fail!\n"); return -1; } else { pr_debug(KERN_INFO "sprdfb: get clk_parent2 ok!\n"); } #ifdef CONFIG_OF clk_parent3 = of_clk_get_by_name(dev->of_dev->of_node, DISPC_DPI_CLOCK_PARENT); #else clk_parent3 = clk_get(NULL, DISPC_DPI_CLOCK_PARENT); #endif if (IS_ERR(clk_parent3)) { printk(KERN_WARNING "sprdfb: get clk_parent3 fail!\n"); return -1; } else { pr_debug(KERN_INFO "sprdfb: get clk_parent3 ok!\n"); } #ifdef CONFIG_OF clk_parent4 = of_clk_get_by_name(dev->of_dev->of_node, DISPC_EMC_EN_PARENT); #else clk_parent4 = clk_get(NULL, DISPC_EMC_EN_PARENT); #endif if (IS_ERR(clk_parent3)) { printk(KERN_WARNING "sprdfb: get clk_parent4 fail!\n"); return -1; } else { pr_debug(KERN_INFO "sprdfb: get clk_parent4 ok!\n"); } #ifdef CONFIG_OF dispc_ctx.clk_dispc = of_clk_get_by_name(dev->of_dev->of_node, DISPC_PLL_CLK); #else dispc_ctx.clk_dispc = clk_get(NULL, DISPC_PLL_CLK); #endif if (IS_ERR(dispc_ctx.clk_dispc)) { printk(KERN_WARNING "sprdfb: get clk_dispc fail!\n"); return -1; } else { pr_debug(KERN_INFO "sprdfb: get clk_dispc ok!\n"); } #ifdef CONFIG_OF dispc_ctx.clk_dispc_dbi = of_clk_get_by_name(dev->of_dev->of_node, DISPC_DBI_CLK); #else dispc_ctx.clk_dispc_dbi = clk_get(NULL, DISPC_DBI_CLK); #endif if (IS_ERR(dispc_ctx.clk_dispc_dbi)) { printk(KERN_WARNING "sprdfb: get clk_dispc_dbi fail!\n"); return -1; } else { pr_debug(KERN_INFO "sprdfb: get clk_dispc_dbi ok!\n"); } #ifdef CONFIG_OF dispc_ctx.clk_dispc_dpi = of_clk_get_by_name(dev->of_dev->of_node, DISPC_DPI_CLK); #else dispc_ctx.clk_dispc_dpi = clk_get(NULL, DISPC_DPI_CLK); #endif if (IS_ERR(dispc_ctx.clk_dispc_dpi)) { printk(KERN_WARNING "sprdfb: get clk_dispc_dpi fail!\n"); return -1; } else { pr_debug(KERN_INFO "sprdfb: get clk_dispc_dpi ok!\n"); } #ifdef CONFIG_OF dispc_ctx.clk_dispc_emc = of_clk_get_by_name(dev->of_dev->of_node, DISPC_EMC_CLK); #else dispc_ctx.clk_dispc_emc = clk_get(NULL, DISPC_EMC_CLK); #endif if (IS_ERR(dispc_ctx.clk_dispc_emc)) { printk(KERN_WARNING "sprdfb: get clk_dispc_dpi fail!\n"); return -1; } else { pr_debug(KERN_INFO "sprdfb: get clk_dispc_emc ok!\n"); } ret = clk_set_parent(dispc_ctx.clk_dispc, clk_parent1); if(ret){ printk(KERN_ERR "sprdfb: dispc set clk parent fail\n"); } #ifdef CONFIG_OF ret = clk_set_rate(dispc_ctx.clk_dispc, clk_src[DISPC_CLOCK_SRC_ID]); #else ret = clk_set_rate(dispc_ctx.clk_dispc, DISPC_CLOCK); #endif if(ret){ printk(KERN_ERR "sprdfb: dispc set clk parent fail\n"); } ret = clk_set_parent(dispc_ctx.clk_dispc_dbi, clk_parent2); if(ret){ printk(KERN_ERR "sprdfb: dispc set dbi clk parent fail\n"); } #ifdef CONFIG_OF ret = clk_set_rate(dispc_ctx.clk_dispc_dbi, clk_src[DISPC_DBI_CLOCK_SRC_ID]); #else ret = clk_set_rate(dispc_ctx.clk_dispc_dbi, DISPC_DBI_CLOCK); #endif if(ret){ printk(KERN_ERR "sprdfb: dispc set dbi clk parent fail\n"); } ret = clk_set_parent(dispc_ctx.clk_dispc_dpi, clk_parent3); if(ret){ printk(KERN_ERR "sprdfb: dispc set dpi clk parent fail\n"); } ret = clk_set_parent(dispc_ctx.clk_dispc_emc, clk_parent4); if(ret){ printk(KERN_ERR "sprdfb: dispc set emc clk parent fail\n"); } if (dev->panel && dev->panel->fps) dispc_update_clk(dev, dev->panel->fps, SPRDFB_FORCE_FPS); else dispc_update_clk(dev, dpi_clk, SPRDFB_FORCE_PCLK); ret = clk_prepare_enable(dispc_ctx.clk_dispc_emc); if(ret){ printk("sprdfb: enable clk_dispc_emc error!!!\n"); } ret = sprdfb_dispc_clk_enable(&dispc_ctx,SPRDFB_DYNAMIC_CLK_FORCE); if (ret) { printk(KERN_WARNING "sprdfb: [%s] enable dispc_clk fail!\n",__FUNCTION__); return -1; } else { pr_debug(KERN_INFO "sprdfb: [%s] enable dispc_clk ok!\n",__FUNCTION__); } dispc_print_clk(); return 0; } #if (defined(CONFIG_SPRD_SCXX30_DMC_FREQ) || defined(CONFIG_SPRD_SCX35_DMC_FREQ)) struct devfreq_dbs sprd_fb_notify = { .level = 0, .data = &dispc_ctx, .devfreq_notifier = sprdfb_dispc_change_threshold, }; #endif #ifdef CONFIG_CPU_IDLE static int scxx30_dispc_cpuidle_notify(struct notifier_block *nb, unsigned long event, void *dummy) { if (event == SC_CPUIDLE_PREPARE){ if(0 == dispc_ctx.vsync_done #ifdef CONFIG_FB_VSYNC_SUPPORT || 0 == dispc_ctx.waitfor_vsync_done #endif ) { return NOTIFY_BAD; //work now, can't not sleep } else { return NOTIFY_OK; } } else{ printk("sprdfb: error in cpuidle notify type!\n"); } return 0; } static struct notifier_block scxx30_dispc_cpuidle_notifier = { .notifier_call = scxx30_dispc_cpuidle_notify, }; #endif static int32_t sprdfb_dispc_module_init(struct sprdfb_device *dev) { int ret = 0; int irq_num = 0; if(dispc_ctx.is_inited){ printk(KERN_WARNING "sprdfb: dispc_module has already initialized! warning!!"); return 0; } else{ printk(KERN_INFO "sprdfb: dispc_module_init. call only once!"); } dispc_ctx.vsync_done = 1; dispc_ctx.vsync_waiter = 0; init_waitqueue_head(&(dispc_ctx.vsync_queue)); #ifdef CONFIG_FB_ESD_SUPPORT #ifdef FB_CHECK_ESD_BY_TE_SUPPORT init_waitqueue_head(&(dev->esd_te_queue)); dev->esd_te_waiter = 0; dev->esd_te_done = 0; #endif #endif #ifdef CONFIG_FB_VSYNC_SUPPORT dispc_ctx.waitfor_vsync_done = 1; dispc_ctx.waitfor_vsync_waiter = 0; init_waitqueue_head(&(dispc_ctx.waitfor_vsync_queue)); #endif sema_init(&dev->refresh_lock, 1); #ifdef CONFIG_OF irq_num = irq_of_parse_and_map(dev->of_dev->of_node, 0); #else irq_num = IRQ_DISPC_INT; #endif printk("sprdfb: dispc irq_num = %d\n", irq_num); //ret = request_irq(IRQ_DISPC_INT, dispc_isr, IRQF_DISABLED, "DISPC", &dispc_ctx); ret = request_irq(irq_num, dispc_isr, IRQF_DISABLED, "DISPC", &dispc_ctx); if (ret) { printk(KERN_ERR "sprdfb: dispc failed to request irq!\n"); sprdfb_dispc_uninit(dev); return -1; } dispc_ctx.is_inited = true; #if (defined(CONFIG_SPRD_SCXX30_DMC_FREQ) || defined(CONFIG_SPRD_SCX35_DMC_FREQ)) ret=sprdfb_dispc_threshold_check(); if(ret) devfreq_notifier_register(&sprd_fb_notify); #endif #ifdef CONFIG_CPU_IDLE if(1 == dev->capability.need_light_sleep) { ret = register_sc_cpuidle_notifier(&scxx30_dispc_cpuidle_notifier); if (ret) { printk("sprdfb: Failed to setup light sleep notifier!\n"); } } #endif return 0; } static int32_t sprdfb_dispc_early_init(struct sprdfb_device *dev) { int ret = 0; if (!dispc_ctx.is_inited) { //spin_lock_init(&dispc_ctx.clk_spinlock); sema_init(&dispc_ctx.clk_sem, 1); } ret = dispc_clk_init(dev); if(ret){ printk(KERN_WARNING "sprdfb: dispc_clk_init fail!\n"); return -1; } if(!dispc_ctx.is_inited){ //init if(dev->panel_ready){ //panel ready printk(KERN_INFO "sprdfb: [%s]: dispc has alread initialized\n", __FUNCTION__); dispc_ctx.is_first_frame = false; }else{ //panel not ready printk(KERN_INFO "sprdfb: [%s]: dispc is not initialized\n", __FUNCTION__); dispc_reset(); dispc_module_enable(); dispc_ctx.is_first_frame = true; } ret = sprdfb_dispc_module_init(dev); }else{ //resume printk(KERN_INFO "sprdfb: [%s]: sprdfb_dispc_early_init resume\n", __FUNCTION__); dispc_reset(); dispc_module_enable(); dispc_ctx.is_first_frame = true; } return ret; } static int sprdfb_dispc_clk_disable(struct sprdfb_dispc_context *dispc_ctx_ptr, SPRDFB_DYNAMIC_CLK_SWITCH_E clock_switch_type) { bool is_need_disable=false; // unsigned long irqflags; pr_debug(KERN_INFO "sprdfb: [%s]\n",__FUNCTION__); if(!dispc_ctx_ptr){ return 0; } //spin_lock_irqsave(&dispc_ctx.clk_spinlock, irqflags); down(&dispc_ctx.clk_sem); switch(clock_switch_type){ case SPRDFB_DYNAMIC_CLK_FORCE: is_need_disable=true; break; case SPRDFB_DYNAMIC_CLK_REFRESH: dispc_ctx_ptr->clk_is_refreshing=false; if(dispc_ctx_ptr->clk_open_count<=0){ is_need_disable=true; } break; case SPRDFB_DYNAMIC_CLK_COUNT: if(dispc_ctx_ptr->clk_open_count>0){ dispc_ctx_ptr->clk_open_count--; if(dispc_ctx_ptr->clk_open_count==0){ if(!dispc_ctx_ptr->clk_is_refreshing){ is_need_disable=true; } } } break; default: break; } if(dispc_ctx_ptr->clk_is_open && is_need_disable){ pr_debug(KERN_INFO "sprdfb: sprdfb_dispc_clk_disable real\n"); clk_disable_unprepare(dispc_ctx_ptr->clk_dispc); clk_disable_unprepare(dispc_ctx_ptr->clk_dispc_dpi); clk_disable_unprepare(dispc_ctx_ptr->clk_dispc_dbi); dispc_ctx_ptr->clk_is_open=false; dispc_ctx_ptr->clk_is_refreshing=false; dispc_ctx_ptr->clk_open_count=0; } //spin_unlock_irqrestore(&dispc_ctx.clk_spinlock,irqflags); up(&dispc_ctx.clk_sem); pr_debug(KERN_INFO "sprdfb: sprdfb_dispc_clk_disable type=%d refresh=%d,count=%d\n",clock_switch_type,dispc_ctx_ptr->clk_is_refreshing,dispc_ctx_ptr->clk_open_count); return 0; } static int sprdfb_dispc_clk_enable(struct sprdfb_dispc_context *dispc_ctx_ptr, SPRDFB_DYNAMIC_CLK_SWITCH_E clock_switch_type) { int ret = 0; bool is_dispc_enable=false; bool is_dispc_dpi_enable=false; //unsigned long irqflags; pr_debug(KERN_INFO "sprdfb: [%s]\n",__FUNCTION__); if(!dispc_ctx_ptr){ return -1; } //spin_lock_irqsave(&dispc_ctx.clk_spinlock, irqflags); down(&dispc_ctx.clk_sem); if(!dispc_ctx_ptr->clk_is_open){ pr_debug(KERN_INFO "sprdfb: sprdfb_dispc_clk_enable real\n"); ret = clk_prepare_enable(dispc_ctx_ptr->clk_dispc); if(ret){ printk("sprdfb: enable clk_dispc error!!!\n"); ret=-1; goto ERROR_CLK_ENABLE; } is_dispc_enable=true; ret = clk_prepare_enable(dispc_ctx_ptr->clk_dispc_dpi); if(ret){ printk("sprdfb: enable clk_dispc_dpi error!!!\n"); ret=-1; goto ERROR_CLK_ENABLE; } is_dispc_dpi_enable=true; ret = clk_prepare_enable(dispc_ctx_ptr->clk_dispc_dbi); if(ret){ printk("sprdfb: enable clk_dispc_dbi error!!!\n"); ret=-1; goto ERROR_CLK_ENABLE; } dispc_ctx_ptr->clk_is_open=true; } switch(clock_switch_type){ case SPRDFB_DYNAMIC_CLK_FORCE: break; case SPRDFB_DYNAMIC_CLK_REFRESH: dispc_ctx_ptr->clk_is_refreshing=true; break; case SPRDFB_DYNAMIC_CLK_COUNT: dispc_ctx_ptr->clk_open_count++; break; default: break; } //spin_unlock_irqrestore(&dispc_ctx.clk_spinlock,irqflags); up(&dispc_ctx.clk_sem); pr_debug(KERN_INFO "sprdfb: sprdfb_dispc_clk_enable type=%d refresh=%d,count=%d,ret=%d\n",clock_switch_type,dispc_ctx_ptr->clk_is_refreshing,dispc_ctx_ptr->clk_open_count,ret); return ret; ERROR_CLK_ENABLE: if(is_dispc_enable){ clk_disable_unprepare(dispc_ctx_ptr->clk_dispc); } if(is_dispc_dpi_enable){ clk_disable_unprepare(dispc_ctx_ptr->clk_dispc_dpi); } //spin_unlock_irqrestore(&dispc_ctx.clk_spinlock,irqflags); up(&dispc_ctx.clk_sem); printk("sprdfb: sprdfb_dispc_clk_enable error!!!!!!\n"); return ret; } static int32_t sprdfb_dispc_init(struct sprdfb_device *dev) { uint32_t dispc_int_en_reg_val = 0x00;//ONLY for dispc interrupt en reg pr_debug(KERN_INFO "sprdfb: [%s]\n",__FUNCTION__); if(NULL == dev){ printk("sprdfb: [%s] Invalid parameter!\n", __FUNCTION__); return -1; } dispc_ctx.dev = dev; /*set bg color*/ //dispc_set_bg_color(0xFFFFFFFF); dispc_set_bg_color(0x0); /*enable dithering*/ dispc_dithering_enable(true); /*use MSBs as img exp mode*/ dispc_set_exp_mode(0x0); //enable DISPC Power Control dispc_pwr_enable(true); #if defined( CONFIG_FB_SCX30G) //set buf thres dispc_set_threshold(0x960, 0x00, 0x960);//0x1000: 4K #elif defined(CONFIG_FB_SCX35L) dispc_set_threshold(0x1388, 0x00, 0x1388);//For sharkl, linebuffer size: 5K #endif printk("sprdfb: [%s] 00000000000000001!\n", __FUNCTION__); if(dispc_ctx.is_first_frame){ dispc_layer_init(&(dev->fb->var)); }else{ dispc_layer_update(&(dev->fb->var)); } if(SPRDFB_PANEL_IF_DPI == dev->panel_if_type){ if(dispc_ctx.is_first_frame){ /*set dpi register update only with SW*/ dispc_set_bits(BIT(4), DISPC_DPI_CTRL); }else{ /*set dpi register update with SW & VSYNC*/ dispc_clear_bits(BIT(4), DISPC_DPI_CTRL); } /*enable dispc update done INT*/ dispc_int_en_reg_val |= DISPC_INT_UPDATE_DONE_MASK; /* enable hw vsync */ dispc_int_en_reg_val |= DISPC_INT_HWVSYNC; }else{ /* enable dispc DONE INT*/ dispc_int_en_reg_val |= DISPC_INT_DONE_MASK; } dispc_int_en_reg_val |= DISPC_INT_ERR_MASK; dispc_write(dispc_int_en_reg_val, DISPC_INT_EN); dev->enable = 1; return 0; } static void sprdfb_dispc_clean_lcd (struct sprdfb_device *dev) { struct fb_info *fb = NULL; uint32_t size = 0; pr_debug(KERN_INFO "sprdfb: [%s]\n",__FUNCTION__); if((NULL == dev) || (NULL == dev->fb)){ printk("sprdfb: [%s] Invalid parameter!\n",__FUNCTION__); return; } down(&dev->refresh_lock); if(!dispc_ctx.is_first_frame || NULL== dev){ printk("sprdfb: [%s] not first_frame\n",__FUNCTION__); up(&dev->refresh_lock); return; } fb = dev->fb; size = (dev->panel->width &0xffff) | ((dev->panel->height)<<16); if(SPRDFB_PANEL_IF_DPI != dev->panel_if_type){ sprdfb_panel_invalidate(dev->panel); } printk("sprdfb: [%s] clean lcd!\n",__FUNCTION__); dispc_write(size, DISPC_SIZE_XY); dispc_set_bg_color(0x00); dispc_write(dev->fb->fix.smem_start, DISPC_OSD_BASE_ADDR); //dispc_osd_enable(false); dispc_set_osd_alpha(0x00); dispc_run(dev); //dispc_osd_enable(true); up(&dev->refresh_lock); if(SPRDFB_PANEL_IF_DPI != dev->panel_if_type){ dispc_sync(dev); } #ifdef CONFIG_FB_TRIPLE_FRAMEBUFFER else{ dispc_sync(dev); } #endif mdelay(30); } static int32_t sprdfb_dispc_refresh (struct sprdfb_device *dev) { uint32_t reg_val = 0; struct fb_info *fb = dev->fb; uint32_t size = 0; uint32_t base = fb->fix.smem_start + fb->fix.line_length * fb->var.yoffset; pr_debug(KERN_INFO "sprdfb: [%s]\n",__FUNCTION__); down(&dev->refresh_lock); if(0 == dev->enable){ printk("sprdfb: [%s]: do not refresh in suspend!!!\n", __FUNCTION__); goto ERROR_REFRESH; } if(SPRDFB_PANEL_IF_DPI != dev->panel_if_type){ dispc_ctx.vsync_waiter ++; dispc_sync(dev); // dispc_ctx.vsync_done = 0; #ifdef CONFIG_FB_DYNAMIC_CLK_SUPPORT if(sprdfb_dispc_clk_enable(&dispc_ctx,SPRDFB_DYNAMIC_CLK_REFRESH)){ printk(KERN_WARNING "sprdfb: enable dispc_clk fail in refresh!\n"); goto ERROR_REFRESH; } #endif } #ifdef CONFIG_FB_TRIPLE_FRAMEBUFFER else{ if((dispc_read(DISPC_OSD_BASE_ADDR) != dispc_read(SHDW_OSD_BASE_ADDR)) &&dispc_read(SHDW_OSD_BASE_ADDR) != 0){ dispc_ctx.vsync_waiter ++; dispc_sync(dev); } } #endif pr_debug(KERN_INFO "srpdfb: [%s] got sync\n", __FUNCTION__); #ifdef CONFIG_FB_MMAP_CACHED if(NULL != dispc_ctx.vma){ pr_debug("sprdfb: sprdfb_dispc_refresh dispc_ctx.vma=0x%x\n ",dispc_ctx.vma); dma_sync_single_for_device(dev->fb->dev, dev->fb->fix.smem_start, dev->fb->fix.smem_len, DMA_TO_DEVICE); } if(fb->var.reserved[3] == 1){ dispc_dithering_enable(false); if(NULL != dev->panel->ops->panel_change_epf){ dev->panel->ops->panel_change_epf(dev->panel,false); } }else{ dispc_dithering_enable(true); if(NULL != dev->panel->ops->panel_change_epf){ dev->panel->ops->panel_change_epf(dev->panel,true); } } #endif //dispc_osd_enable(true); dispc_set_osd_alpha(0xff); // dispc_ctx.dev = dev; #ifdef CONFIG_FB_LCD_OVERLAY_SUPPORT if(SPRD_OVERLAY_STATUS_STARTED == dispc_ctx.overlay_state){ overlay_close(dev); } #endif size = (fb->var.xres & 0xffff) | ((fb->var.yres) << 16); dispc_write(base, DISPC_OSD_BASE_ADDR); dispc_write(0, DISPC_OSD_DISP_XY); dispc_write(size, DISPC_OSD_SIZE_XY); dispc_write(fb->var.xres, DISPC_OSD_PITCH); #ifdef CONFIG_FB_LOW_RES_SIMU size = (dev->panel->width &0xffff) | ((dev->panel->height)<<16); #endif dispc_write(size, DISPC_SIZE_XY); #ifdef BIT_PER_PIXEL_SURPPORT /* data format */ if (fb->var.bits_per_pixel == 32) { /* ABGR */ reg_val |= (3 << 4); /* rb switch */ reg_val |= (1 << 15); dispc_clear_bits(0x30000,DISPC_CTRL); } else { /* RGB565 */ reg_val |= (5 << 4); /* B2B3B0B1 */ reg_val |= (2 << 8); dispc_clear_bits(0x30000,DISPC_CTRL); dispc_set_bits(0x10000,DISPC_CTRL); } reg_val |= (1 << 0); /* alpha mode select - block alpha*/ reg_val |= (1 << 2); dispc_write(reg_val, DISPC_OSD_CTRL); #endif if(SPRDFB_PANEL_IF_DPI != dev->panel_if_type){ sprdfb_panel_invalidate(dev->panel); } sprdfb_panel_before_refresh(dev); #ifdef CONFIG_FB_LCD_OVERLAY_SUPPORT dispc_set_bits(BIT(0), DISPC_OSD_CTRL); if(SPRD_OVERLAY_STATUS_ON == dispc_ctx.overlay_state){ overlay_start(dev, (SPRD_LAYER_IMG)); } #endif dev->frame_count += 1; dispc_run(dev); #ifdef CONFIG_FB_ESD_SUPPORT if(!dev->ESD_work_start){ printk("sprdfb: schedule ESD work queue!\n"); schedule_delayed_work(&dev->ESD_work, msecs_to_jiffies(dev->ESD_timeout_val)); dev->ESD_work_start = true; } #endif ERROR_REFRESH: up(&dev->refresh_lock); if(NULL != dev->logo_buffer_addr_v){ printk("sprdfb: free logo proc buffer!\n"); free_pages((unsigned long)dev->logo_buffer_addr_v, get_order(dev->logo_buffer_size)); dev->logo_buffer_addr_v = 0; } pr_debug("sprdfb: DISPC_CTRL: 0x%x\n", dispc_read(DISPC_CTRL)); pr_debug("sprdfb: DISPC_SIZE_XY: 0x%x\n", dispc_read(DISPC_SIZE_XY)); pr_debug("sprdfb: DISPC_BG_COLOR: 0x%x\n", dispc_read(DISPC_BG_COLOR)); pr_debug("sprdfb: DISPC_INT_EN: 0x%x\n", dispc_read(DISPC_INT_EN)); pr_debug("sprdfb: DISPC_OSD_CTRL: 0x%x\n", dispc_read(DISPC_OSD_CTRL)); pr_debug("sprdfb: DISPC_OSD_BASE_ADDR: 0x%x\n", dispc_read(DISPC_OSD_BASE_ADDR)); pr_debug("sprdfb: DISPC_OSD_SIZE_XY: 0x%x\n", dispc_read(DISPC_OSD_SIZE_XY)); pr_debug("sprdfb: DISPC_OSD_PITCH: 0x%x\n", dispc_read(DISPC_OSD_PITCH)); pr_debug("sprdfb: DISPC_OSD_DISP_XY: 0x%x\n", dispc_read(DISPC_OSD_DISP_XY)); pr_debug("sprdfb: DISPC_OSD_ALPHA : 0x%x\n", dispc_read(DISPC_OSD_ALPHA)); return 0; } static int32_t sprdfb_dispc_suspend(struct sprdfb_device *dev) { printk(KERN_INFO "sprdfb: [%s], dev->enable = %d\n",__FUNCTION__, dev->enable); if (0 != dev->enable){ down(&dev->refresh_lock); if(SPRDFB_PANEL_IF_DPI != dev->panel_if_type){ /* must wait ,dispc_sync() */ dispc_ctx.vsync_waiter ++; dispc_sync(dev); #ifdef CONFIG_FB_DYNAMIC_CLK_SUPPORT printk("sprdfb: open clk in suspend\n"); if(sprdfb_dispc_clk_enable(&dispc_ctx,SPRDFB_DYNAMIC_CLK_COUNT)){ printk(KERN_WARNING "sprdfb: [%s] clk enable fail!!!\n",__FUNCTION__); } #endif printk(KERN_INFO "sprdfb: [%s] got sync\n",__FUNCTION__); } dev->enable = 0; up(&dev->refresh_lock); #ifdef CONFIG_FB_ESD_SUPPORT if(dev->ESD_work_start == true){ printk("sprdfb: cancel ESD work queue\n"); cancel_delayed_work_sync(&dev->ESD_work); dev->ESD_work_start = false; } #endif sprdfb_panel_suspend(dev); dispc_stop(dev); dispc_write(0, DISPC_INT_EN); msleep(50); /*fps>20*/ clk_disable_unprepare(dispc_ctx.clk_dispc_emc); sprdfb_dispc_clk_disable(&dispc_ctx,SPRDFB_DYNAMIC_CLK_FORCE); // sci_glb_clr(DISPC_EMC_EN, BIT_DISPC_EMC_EN); }else{ printk(KERN_ERR "sprdfb: [%s]: Invalid device status %d\n", __FUNCTION__, dev->enable); } return 0; } static int32_t sprdfb_dispc_resume(struct sprdfb_device *dev) { printk(KERN_INFO "sprdfb: [%s], dev->enable= %d\n",__FUNCTION__, dev->enable); if (dev->enable == 0) { if(sprdfb_dispc_clk_enable(&dispc_ctx,SPRDFB_DYNAMIC_CLK_FORCE)){ printk(KERN_WARNING "sprdfb: [%s] clk enable fail!!\n",__FUNCTION__); //return 0; } // sci_glb_set(DISPC_EMC_EN, BIT_DISPC_EMC_EN); dispc_ctx.vsync_done = 1; if (1){//(dispc_read(DISPC_SIZE_XY) == 0 ) { /* resume from deep sleep */ printk(KERN_INFO "sprdfb: [%s] from deep sleep\n",__FUNCTION__); sprdfb_dispc_early_init(dev); sprdfb_panel_resume(dev, true); sprdfb_dispc_init(dev); printk(KERN_INFO "sprdfb: [%s] from deep sleep out\n",__FUNCTION__); }else { printk(KERN_INFO "sprdfb: [%s] not from deep sleep\n",__FUNCTION__); sprdfb_panel_resume(dev, true); } dev->enable = 1; if(dev->panel->is_clean_lcd){ sprdfb_dispc_clean_lcd(dev); } } printk(KERN_INFO "sprdfb: [%s], leave dev->enable= %d\n",__FUNCTION__, dev->enable); return 0; } #ifdef CONFIG_FB_ESD_SUPPORT //for video esd check static int32_t sprdfb_dispc_check_esd_dpi(struct sprdfb_device *dev) { uint32_t ret = 0; unsigned long flags; #ifdef FB_CHECK_ESD_BY_TE_SUPPORT ret = sprdfb_panel_ESD_check(dev); if(0 !=ret){ dispc_run_for_feature(dev); } #else #ifdef FB_CHECK_ESD_IN_VFP ret = sprdfb_panel_ESD_check(dev); #else local_irq_save(flags); dispc_stop_for_feature(dev); ret = sprdfb_panel_ESD_check(dev); //make sure there is no log in this function dispc_run_for_feature(dev); local_irq_restore(flags); #endif #endif return ret; } //for cmd esd check static int32_t sprdfb_dispc_check_esd_edpi(struct sprdfb_device *dev) { uint32_t ret = 0; dispc_ctx.vsync_waiter ++; dispc_sync(dev); #ifdef CONFIG_FB_DYNAMIC_CLK_SUPPORT if(sprdfb_dispc_clk_enable(&dispc_ctx,SPRDFB_DYNAMIC_CLK_COUNT)){ printk(KERN_WARNING "sprdfb: [%s] clk enable fail!!!\n",__FUNCTION__); return -1; } #endif ret = sprdfb_panel_ESD_check(dev); if(0 !=ret){ dispc_run_for_feature(dev); } #ifdef CONFIG_FB_DYNAMIC_CLK_SUPPORT sprdfb_dispc_clk_disable(&dispc_ctx,SPRDFB_DYNAMIC_CLK_COUNT); #endif return ret; } static int32_t sprdfb_dispc_check_esd(struct sprdfb_device *dev) { uint32_t ret = 0; bool is_refresh_lock_down=false; pr_debug("sprdfb: [%s] \n", __FUNCTION__); if(SPRDFB_PANEL_IF_DBI == dev->panel_if_type){ printk("sprdfb: [%s] leave (not support dbi mode now)!\n", __FUNCTION__); ret = -1; goto ERROR_CHECK_ESD; } down(&dev->refresh_lock); is_refresh_lock_down=true; if(0 == dev->enable){ printk("sprdfb: [%s] leave (Invalid device status)!\n", __FUNCTION__); ret=-1; goto ERROR_CHECK_ESD; } if(0 == (dev->check_esd_time % 30)){ printk("sprdfb: [%s] (%d, %d, %d)\n",__FUNCTION__, dev->check_esd_time, dev->panel_reset_time, dev->reset_dsi_time); }else{ pr_debug("sprdfb: [%s] (%d, %d, %d)\n",__FUNCTION__, dev->check_esd_time, dev->panel_reset_time, dev->reset_dsi_time); } if(SPRDFB_PANEL_IF_DPI == dev->panel_if_type){ ret=sprdfb_dispc_check_esd_dpi(dev); } else{ ret=sprdfb_dispc_check_esd_edpi(dev); } ERROR_CHECK_ESD: if(is_refresh_lock_down){ up(&dev->refresh_lock); } return ret; } #endif #ifdef CONFIG_FB_LCD_OVERLAY_SUPPORT static int overlay_open(void) { pr_debug("sprdfb: [%s] : %d\n", __FUNCTION__,dispc_ctx.overlay_state); /* if(SPRD_OVERLAY_STATUS_OFF != dispc_ctx.overlay_state){ printk(KERN_ERR "sprdfb: Overlay open fail (has been opened)"); return -1; } */ dispc_ctx.overlay_state = SPRD_OVERLAY_STATUS_ON; return 0; } static int overlay_start(struct sprdfb_device *dev, uint32_t layer_index) { pr_debug("sprdfb: [%s] : %d, %d\n", __FUNCTION__,dispc_ctx.overlay_state, layer_index); if(SPRD_OVERLAY_STATUS_ON != dispc_ctx.overlay_state){ printk(KERN_ERR "sprdfb: overlay start fail. (not opened)"); return -1; } if((0 == dispc_read(DISPC_IMG_Y_BASE_ADDR)) && (0 == dispc_read(DISPC_OSD_BASE_ADDR))){ printk(KERN_ERR "sprdfb: overlay start fail. (not configged)"); return -1; } /* if(0 != dispc_sync(dev)){ printk(KERN_ERR "sprdfb: overlay start fail. (wait done fail)"); return -1; } */ dispc_set_bg_color(0x0); dispc_clear_bits(BIT(2), DISPC_OSD_CTRL); /*use pixel alpha*/ dispc_set_osd_alpha(0x80); if((layer_index & SPRD_LAYER_IMG) && (0 != dispc_read(DISPC_IMG_Y_BASE_ADDR))){ dispc_set_bits(BIT(0), DISPC_IMG_CTRL);/* enable the image layer */ } if((layer_index & SPRD_LAYER_OSD) && (0 != dispc_read(DISPC_OSD_BASE_ADDR))){ dispc_set_bits(BIT(0), DISPC_OSD_CTRL);/* enable the osd layer */ } dispc_ctx.overlay_state = SPRD_OVERLAY_STATUS_STARTED; return 0; } static int overlay_img_configure(struct sprdfb_device *dev, int type, overlay_rect *rect, unsigned char *buffer, int y_endian, int uv_endian, bool rb_switch) { uint32_t reg_value; pr_debug("sprdfb: [%s] : %d, (%d, %d,%d,%d), 0x%x\n", __FUNCTION__, type, rect->x, rect->y, rect->h, rect->w, (unsigned int)buffer); if(SPRD_OVERLAY_STATUS_ON != dispc_ctx.overlay_state){ printk(KERN_ERR "sprdfb: Overlay config fail (not opened)"); return -1; } if (type >= SPRD_DATA_TYPE_LIMIT) { printk(KERN_ERR "sprdfb: Overlay config fail (type error)"); return -1; } if((y_endian >= SPRD_IMG_DATA_ENDIAN_LIMIT) || (uv_endian >= SPRD_IMG_DATA_ENDIAN_LIMIT)){ printk(KERN_ERR "sprdfb: Overlay config fail (y, uv endian error)"); return -1; } /* lcdc_write(((type << 3) | (1 << 0)), LCDC_IMG_CTRL); */ /*lcdc_write((type << 3) , LCDC_IMG_CTRL);*/ reg_value = (y_endian << 8)|(uv_endian<< 10)|(type << 4); if(rb_switch){ reg_value |= (1 << 15); } dispc_write(reg_value, DISPC_IMG_CTRL); dispc_write((uint32_t)buffer, DISPC_IMG_Y_BASE_ADDR); if (type < SPRD_DATA_TYPE_RGB888) { uint32_t size = rect->w * rect->h; dispc_write((uint32_t)(buffer + size), DISPC_IMG_UV_BASE_ADDR); } reg_value = (rect->h << 16) | (rect->w); dispc_write(reg_value, DISPC_IMG_SIZE_XY); dispc_write(rect->w, DISPC_IMG_PITCH); reg_value = (rect->y << 16) | (rect->x); dispc_write(reg_value, DISPC_IMG_DISP_XY); if(type < SPRD_DATA_TYPE_RGB888) { dispc_write(1, DISPC_Y2R_CTRL); #ifndef CONFIG_FB_SCX35 dispc_write(SPRDFB_BRIGHTNESS|SPRDFB_CONTRAST, DISPC_Y2R_Y_PARAM); dispc_write(SPRDFB_OFFSET_U|SPRDFB_SATURATION_U, DISPC_Y2R_U_PARAM); dispc_write(SPRDFB_OFFSET_V|SPRDFB_SATURATION_V, DISPC_Y2R_V_PARAM); #else dispc_write(SPRDFB_CONTRAST, DISPC_Y2R_CONTRAST); dispc_write(SPRDFB_SATURATION, DISPC_Y2R_SATURATION); dispc_write(SPRDFB_BRIGHTNESS, DISPC_Y2R_BRIGHTNESS); #endif } pr_debug("sprdfb: DISPC_IMG_CTRL: 0x%x\n", dispc_read(DISPC_IMG_CTRL)); pr_debug("sprdfb: DISPC_IMG_Y_BASE_ADDR: 0x%x\n", dispc_read(DISPC_IMG_Y_BASE_ADDR)); pr_debug("sprdfb: DISPC_IMG_UV_BASE_ADDR: 0x%x\n", dispc_read(DISPC_IMG_UV_BASE_ADDR)); pr_debug("sprdfb: DISPC_IMG_SIZE_XY: 0x%x\n", dispc_read(DISPC_IMG_SIZE_XY)); pr_debug("sprdfb: DISPC_IMG_PITCH: 0x%x\n", dispc_read(DISPC_IMG_PITCH)); pr_debug("sprdfb: DISPC_IMG_DISP_XY: 0x%x\n", dispc_read(DISPC_IMG_DISP_XY)); pr_debug("sprdfb: DISPC_Y2R_CTRL: 0x%x\n", dispc_read(DISPC_Y2R_CTRL)); #ifndef CONFIG_FB_SCX35 pr_debug("sprdfb: DISPC_Y2R_Y_PARAM: 0x%x\n", dispc_read(DISPC_Y2R_Y_PARAM)); pr_debug("sprdfb: DISPC_Y2R_U_PARAM: 0x%x\n", dispc_read(DISPC_Y2R_U_PARAM)); pr_debug("sprdfb: DISPC_Y2R_V_PARAM: 0x%x\n", dispc_read(DISPC_Y2R_V_PARAM)); #else pr_debug("sprdfb: DISPC_Y2R_CONTRAST: 0x%x\n", dispc_read(DISPC_Y2R_CONTRAST)); pr_debug("sprdfb: DISPC_Y2R_SATURATION: 0x%x\n", dispc_read(DISPC_Y2R_SATURATION)); pr_debug("sprdfb: DISPC_Y2R_BRIGHTNESS: 0x%x\n", dispc_read(DISPC_Y2R_BRIGHTNESS)); #endif return 0; } static int overlay_osd_configure(struct sprdfb_device *dev, int type, overlay_rect *rect, unsigned char *buffer, int y_endian, int uv_endian, bool rb_switch) { uint32_t reg_value; pr_debug("sprdfb: [%s] : %d, (%d, %d,%d,%d), 0x%x\n", __FUNCTION__, type, rect->x, rect->y, rect->h, rect->w, (unsigned int)buffer); if(SPRD_OVERLAY_STATUS_ON != dispc_ctx.overlay_state){ printk(KERN_ERR "sprdfb: Overlay config fail (not opened)"); return -1; } if ((type >= SPRD_DATA_TYPE_LIMIT) || (type <= SPRD_DATA_TYPE_YUV400)) { printk(KERN_ERR "sprdfb: Overlay config fail (type error)"); return -1; } if(y_endian >= SPRD_IMG_DATA_ENDIAN_LIMIT ){ printk(KERN_ERR "sprdfb: Overlay config fail (rgb endian error)"); return -1; } /* lcdc_write(((type << 3) | (1 << 0)), LCDC_IMG_CTRL); */ /*lcdc_write((type << 3) , LCDC_IMG_CTRL);*/ /*use premultiply pixel alpha*/ reg_value = (y_endian<<8)|(type << 4|(1<<2))|(2<<16); if(rb_switch){ reg_value |= (1 << 15); } dispc_write(reg_value, DISPC_OSD_CTRL); dispc_write((uint32_t)buffer, DISPC_OSD_BASE_ADDR); reg_value = (rect->h << 16) | (rect->w); dispc_write(reg_value, DISPC_OSD_SIZE_XY); dispc_write(rect->w, DISPC_OSD_PITCH); reg_value = (rect->y << 16) | (rect->x); dispc_write(reg_value, DISPC_OSD_DISP_XY); pr_debug("sprdfb: DISPC_OSD_CTRL: 0x%x\n", dispc_read(DISPC_OSD_CTRL)); pr_debug("sprdfb: DISPC_OSD_BASE_ADDR: 0x%x\n", dispc_read(DISPC_OSD_BASE_ADDR)); pr_debug("sprdfb: DISPC_OSD_SIZE_XY: 0x%x\n", dispc_read(DISPC_OSD_SIZE_XY)); pr_debug("sprdfb: DISPC_OSD_PITCH: 0x%x\n", dispc_read(DISPC_OSD_PITCH)); pr_debug("sprdfb: DISPC_OSD_DISP_XY: 0x%x\n", dispc_read(DISPC_OSD_DISP_XY)); return 0; } static int overlay_close(struct sprdfb_device *dev) { if(SPRD_OVERLAY_STATUS_OFF == dispc_ctx.overlay_state){ printk(KERN_ERR "sprdfb: overlay close fail. (has been closed)"); return 0; } /* if (0 != sprd_lcdc_sync(dev)) { printk(KERN_ERR "sprdfb: overlay close fail. (wait done fail)\n"); return -1; } */ //dispc_set_bg_color(0xFFFFFFFF); dispc_set_bg_color(0x0); dispc_set_bits(BIT(2), DISPC_OSD_CTRL);/*use block alpha*/ dispc_set_osd_alpha(0xff); dispc_clear_bits(BIT(0), DISPC_IMG_CTRL); /* disable the image layer */ dispc_write(0, DISPC_IMG_Y_BASE_ADDR); dispc_write(0, DISPC_OSD_BASE_ADDR); dispc_layer_init(&(dev->fb->var)); dispc_ctx.overlay_state = SPRD_OVERLAY_STATUS_OFF; return 0; } /*TO DO: need mutext with suspend, resume*/ static int32_t sprdfb_dispc_enable_overlay(struct sprdfb_device *dev, struct overlay_info* info, int enable) { int result = -1; bool is_refresh_lock_down=false; #ifdef CONFIG_FB_DYNAMIC_CLK_SUPPORT bool is_clk_enable=false; #endif pr_debug("sprdfb: [%s]: %d, %d\n", __FUNCTION__, enable, dev->enable); if(enable){ /*enable*/ if(NULL == info){ printk(KERN_ERR "sprdfb: sprdfb_dispc_enable_overlay fail (Invalid parameter)\n"); goto ERROR_ENABLE_OVERLAY; } down(&dev->refresh_lock); is_refresh_lock_down=true; if(0 == dev->enable){ printk(KERN_ERR "sprdfb: sprdfb_dispc_enable_overlay fail. (dev not enable)\n"); goto ERROR_ENABLE_OVERLAY; } if(0 != dispc_sync(dev)){ printk(KERN_ERR "sprdfb: sprdfb_dispc_enable_overlay fail. (wait done fail)\n"); goto ERROR_ENABLE_OVERLAY; } #ifdef CONFIG_FB_DYNAMIC_CLK_SUPPORT if(SPRDFB_PANEL_IF_DPI != dev->panel_if_type){ if(sprdfb_dispc_clk_enable(&dispc_ctx,SPRDFB_DYNAMIC_CLK_COUNT)){ printk(KERN_WARNING "sprdfb: [%s] clk enable fail!!!\n",__FUNCTION__); goto ERROR_ENABLE_OVERLAY; } is_clk_enable=true; } #endif #ifdef CONFIG_FB_LCD_OVERLAY_SUPPORT if(SPRD_OVERLAY_STATUS_STARTED == dispc_ctx.overlay_state){ overlay_close(dev); } #endif result = overlay_open(); if(0 != result){ result=-1; goto ERROR_ENABLE_OVERLAY; } if(SPRD_LAYER_IMG == info->layer_index){ result = overlay_img_configure(dev, info->data_type, &(info->rect), info->buffer, info->y_endian, info->uv_endian, info->rb_switch); }else if(SPRD_LAYER_OSD == info->layer_index){ result = overlay_osd_configure(dev, info->data_type, &(info->rect), info->buffer, info->y_endian, info->uv_endian, info->rb_switch); }else{ printk(KERN_ERR "sprdfb: sprdfb_dispc_enable_overlay fail. (invalid layer index)\n"); } if(0 != result){ result=-1; goto ERROR_ENABLE_OVERLAY; } /*result = overlay_start(dev);*/ }else{ /*disable*/ /*result = overlay_close(dev);*/ } ERROR_ENABLE_OVERLAY: #ifdef CONFIG_FB_DYNAMIC_CLK_SUPPORT if(is_clk_enable){ sprdfb_dispc_clk_disable(&dispc_ctx,SPRDFB_DYNAMIC_CLK_COUNT); } #endif if(is_refresh_lock_down){ up(&dev->refresh_lock); } pr_debug("sprdfb: [%s] return %d\n", __FUNCTION__, result); return result; } static int32_t sprdfb_dispc_display_overlay(struct sprdfb_device *dev, struct overlay_display* setting) { #ifndef CONFIG_FB_LOW_RES_SIMU struct overlay_rect* rect = &(setting->rect); uint32_t size =( (rect->h << 16) | (rect->w & 0xffff)); #else uint32_t size = (dev->panel->width &0xffff) | ((dev->panel->height)<<16); #endif dispc_ctx.dev = dev; pr_debug("sprdfb: sprdfb_dispc_display_overlay: layer:%d, (%d, %d,%d,%d)\n", setting->layer_index, setting->rect.x, setting->rect.y, setting->rect.h, setting->rect.w); down(&dev->refresh_lock); if(0 == dev->enable){ printk("sprdfb: [%s] leave (Invalid device status)!\n", __FUNCTION__); goto ERROR_DISPLAY_OVERLAY; } if(SPRDFB_PANEL_IF_DPI != dev->panel_if_type){ dispc_ctx.vsync_waiter ++; dispc_sync(dev); //dispc_ctx.vsync_done = 0; #ifdef CONFIG_FB_DYNAMIC_CLK_SUPPORT if(sprdfb_dispc_clk_enable(&dispc_ctx,SPRDFB_DYNAMIC_CLK_REFRESH)){ printk(KERN_WARNING "sprdfb: [%s] clk enable fail!!!\n",__FUNCTION__); goto ERROR_DISPLAY_OVERLAY; } #endif } #ifdef CONFIG_FB_MMAP_CACHED dispc_dithering_enable(true); if(NULL != dev->panel->ops->panel_change_epf){ dev->panel->ops->panel_change_epf(dev->panel,true); } #endif pr_debug(KERN_INFO "srpdfb: [%s] got sync\n", __FUNCTION__); dispc_ctx.dev = dev; dispc_write(size, DISPC_SIZE_XY); if(SPRDFB_PANEL_IF_DPI != dev->panel_if_type){ sprdfb_panel_invalidate(dev->panel); } sprdfb_panel_before_refresh(dev); dispc_clear_bits(BIT(0), DISPC_OSD_CTRL); if(SPRD_OVERLAY_STATUS_ON == dispc_ctx.overlay_state){ if(overlay_start(dev, setting->layer_index) != 0){ printk("sprdfb: %s[%d] overlay_start() err, return without run dispc!\n",__func__,__LINE__); goto ERROR_DISPLAY_OVERLAY; } } dev->frame_count += 1; dispc_run(dev); if((SPRD_OVERLAY_DISPLAY_SYNC == setting->display_mode) && (SPRDFB_PANEL_IF_DPI != dev->panel_if_type)){ dispc_ctx.vsync_waiter ++; if (dispc_sync(dev) != 0) {/* time out??? disable ?? */ printk("sprdfb: sprdfb do sprd_lcdc_display_overlay time out!\n"); } //dispc_ctx.vsync_done = 0; } ERROR_DISPLAY_OVERLAY: up(&dev->refresh_lock); pr_debug("sprdfb: DISPC_CTRL: 0x%x\n", dispc_read(DISPC_CTRL)); pr_debug("sprdfb: DISPC_SIZE_XY: 0x%x\n", dispc_read(DISPC_SIZE_XY)); pr_debug("sprdfb: DISPC_BG_COLOR: 0x%x\n", dispc_read(DISPC_BG_COLOR)); pr_debug("sprdfb: DISPC_INT_EN: 0x%x\n", dispc_read(DISPC_INT_EN)); pr_debug("sprdfb: DISPC_OSD_CTRL: 0x%x\n", dispc_read(DISPC_OSD_CTRL)); pr_debug("sprdfb: DISPC_OSD_BASE_ADDR: 0x%x\n", dispc_read(DISPC_OSD_BASE_ADDR)); pr_debug("sprdfb: DISPC_OSD_SIZE_XY: 0x%x\n", dispc_read(DISPC_OSD_SIZE_XY)); pr_debug("sprdfb: DISPC_OSD_PITCH: 0x%x\n", dispc_read(DISPC_OSD_PITCH)); pr_debug("sprdfb: DISPC_OSD_DISP_XY: 0x%x\n", dispc_read(DISPC_OSD_DISP_XY)); pr_debug("sprdfb: DISPC_OSD_ALPHA : 0x%x\n", dispc_read(DISPC_OSD_ALPHA)); return 0; } #endif #ifdef CONFIG_FB_VSYNC_SUPPORT static int32_t spdfb_dispc_wait_for_vsync(struct sprdfb_device *dev) { int ret = 0; uint32_t reg_val0,reg_val1; pr_debug("sprdfb: [%s]\n", __FUNCTION__); if(SPRDFB_PANEL_IF_DPI == dev->panel_if_type){ if(!dispc_ctx.is_first_frame){ dispc_ctx.waitfor_vsync_done = 0; //dispc_set_bits(BIT(0), DISPC_INT_EN); dispc_ctx.waitfor_vsync_waiter++; #ifndef CONFIG_SC_FPGA ret = wait_event_interruptible_timeout(dispc_ctx.waitfor_vsync_queue, dispc_ctx.waitfor_vsync_done, msecs_to_jiffies(100)); #else /* fpga dpi clock is 6-7MHz, timeout should be longer than reference phone */ ret = wait_event_interruptible_timeout(dispc_ctx.waitfor_vsync_queue, dispc_ctx.waitfor_vsync_done, msecs_to_jiffies(900)); #endif if (!ret) { /* time out */ dispc_ctx.waitfor_vsync_done = 1; reg_val0 = dispc_read(DISPC_INT_RAW); reg_val1 = dispc_read(DISPC_DPI_STS1); printk(KERN_ERR "sprdfb: vsync time out!!!!!(0x%x, 0x%x)\n", reg_val0, reg_val1); {/*for debug*/ int32_t i = 0; for(i=0;i<256;i+=16){ printk("sprdfb: %x: 0x%x, 0x%x, 0x%x, 0x%x\n", i, dispc_read(i), dispc_read(i+4), dispc_read(i+8), dispc_read(i+12)); } printk("**************************************\n"); } } dispc_ctx.waitfor_vsync_waiter = 0; }else{ msleep(16); } }else{ dispc_ctx.waitfor_vsync_done = 0; dispc_set_bits(BIT(1), DISPC_INT_EN); dispc_ctx.waitfor_vsync_waiter++; ret = wait_event_interruptible_timeout(dispc_ctx.waitfor_vsync_queue, dispc_ctx.waitfor_vsync_done, msecs_to_jiffies(100)); if (!ret) { /* time out */ dispc_ctx.waitfor_vsync_done = 1; printk(KERN_ERR "sprdfb: vsync time out!!!!!\n"); {/*for debug*/ int32_t i = 0; for(i=0;i<256;i+=16){ printk("sprdfb: %x: 0x%x, 0x%x, 0x%x, 0x%x\n", i, dispc_read(i), dispc_read(i+4), dispc_read(i+8), dispc_read(i+12)); } printk("**************************************\n"); } } dispc_ctx.waitfor_vsync_waiter = 0; } pr_debug("sprdfb: [%s] (%d)\n", __FUNCTION__, ret); return 0; } #endif //#if (defined(CONFIG_FB_DYNAMIC_FREQ_SCALING) || !defined(FB_CHECK_ESD_IN_VFP)) static void dispc_stop_for_feature(struct sprdfb_device *dev) { int wait_count = 0; if(SPRDFB_PANEL_IF_DPI == dev->panel_if_type){ dispc_stop(dev); while((dispc_read(DISPC_DPI_STS1) & BIT(16)) && (wait_count < 100)){ udelay(1000); wait_count++; } if(wait_count >= 100){ printk("sprdfb:[%s] can't wait till dispc stop!!!\n", __func__); } udelay(25); } } static void dispc_run_for_feature(struct sprdfb_device *dev) { #ifdef CONFIG_FB_LCD_OVERLAY_SUPPORT if(SPRD_OVERLAY_STATUS_ON != dispc_ctx.overlay_state) #endif { dispc_run(dev); } } //#endif #ifdef CONFIG_FB_DYNAMIC_FREQ_SCALING /** * author: Yang.Haibing haibing.yang@spreadtrum.com * * sprdfb_dispc_chg_clk - interface for sysfs to change dpi or dphy clock * @fb_dev: spreadtrum specific fb device * @type: check the type is fps or pclk or mipi dphy freq * @new_val: fps or new dpi clock * * Returns 0 on success, MINUS on parsing error. */ int sprdfb_dispc_chg_clk(struct sprdfb_device *fb_dev, int type, u32 new_val) { int ret = 0; unsigned long flags = 0; struct panel_spec* panel = fb_dev->panel; pr_debug("%s --enter--", __func__); /* check if new value is valid */ if (new_val <= 0) { pr_err("new value is invalid\n"); return -EINVAL; } down(&fb_dev->refresh_lock); /* Now let's do update dpi clock */ switch (type) { case SPRDFB_DYNAMIC_PCLK: if (fb_dev->panel_if_type != SPRDFB_PANEL_IF_DPI) { pr_err("current dispc interface isn't DPI\n"); ret=-EINVAL; goto exit; } /* Note: local interrupt will be disabled */ local_irq_save(flags); dispc_stop_for_feature(fb_dev); ret = dispc_update_clk(fb_dev, new_val, SPRDFB_DYNAMIC_PCLK); break; case SPRDFB_DYNAMIC_FPS: if (fb_dev->panel_if_type != SPRDFB_PANEL_IF_DPI) { dispc_ctx.vsync_waiter ++; dispc_sync(fb_dev); sprdfb_panel_change_fps(fb_dev, new_val); ret=0; goto exit; } /* Note: local interrupt will be disabled */ local_irq_save(flags); dispc_stop_for_feature(fb_dev); ret = dispc_update_clk(fb_dev, new_val, SPRDFB_DYNAMIC_FPS); break; case SPRDFB_DYNAMIC_MIPI_CLK: /* Note: local interrupt will be disabled */ local_irq_save(flags); dispc_stop_for_feature(fb_dev); ret = dispc_update_clk(fb_dev, new_val, SPRDFB_DYNAMIC_MIPI_CLK); break; default: pr_err("invalid CMD type\n"); ret=-EINVAL; goto exit; } if (ret) { pr_err("Failed to set pixel clock, fps or dphy freq.\n"); goto DONE; } if (type != SPRDFB_DYNAMIC_MIPI_CLK && panel->type == LCD_MODE_DSI && panel->info.mipi->work_mode == SPRDFB_MIPI_MODE_VIDEO && fb_dev->enable == true) { ret = dsi_dpi_init(fb_dev); if (ret) pr_err("%s: dsi_dpi_init fail\n", __func__); } DONE: dispc_run_for_feature(fb_dev); local_irq_restore(flags); up(&fb_dev->refresh_lock); pr_debug("%s --leave--", __func__); return ret; exit: up(&fb_dev->refresh_lock); return ret; } #endif static int dispc_update_clk_intf(struct sprdfb_device *fb_dev) { return dispc_update_clk(fb_dev, fb_dev->panel->fps, SPRDFB_FORCE_FPS); } /** * author: Yang.Haibing haibing.yang@spreadtrum.com * * dispc_update_clk - update dpi clock via @howto and @new_val * @fb_dev: spreadtrum specific fb device * @new_val: fps or new dpi clock * @howto: check the type is fps or pclk or mipi dphy freq * * Returns 0 on success, MINUS on error. */ static int dispc_update_clk(struct sprdfb_device *fb_dev, u32 new_val, int howto) { int ret; u32 new_pclk; u32 old_val; u32 dpi_clk_src; struct panel_spec* panel = fb_dev->panel; #ifdef CONFIG_OF uint32_t clk_src[DISPC_CLOCK_NUM]; ret = of_property_read_u32_array(fb_dev->of_dev->of_node, "clock-src", clk_src, DISPC_CLOCK_NUM); if (ret) { pr_err("[%s] read clock-src fail (%d)\n", __func__, ret); return -EINVAL; } dpi_clk_src = clk_src[DISPC_DPI_CLOCK_SRC_ID]; #else dpi_clk_src = SPRDFB_DPI_CLOCK_SRC; #endif switch (howto) { case SPRDFB_FORCE_FPS: ret = dispc_check_new_clk(fb_dev, &new_pclk, dpi_clk_src, new_val, SPRDFB_FORCE_FPS); if (ret) { pr_err("%s: new forced fps is invalid.", __func__); return -EINVAL; } break; case SPRDFB_DYNAMIC_FPS: /* Calc dpi clock via fps */ ret = dispc_check_new_clk(fb_dev, &new_pclk, dpi_clk_src, new_val, SPRDFB_DYNAMIC_FPS); if (ret) { pr_err("%s: new dynamic fps is invalid.", __func__); return -EINVAL; } break; case SPRDFB_FORCE_PCLK: new_pclk = new_val; break; case SPRDFB_DYNAMIC_PCLK: /* Given dpi clock */ ret = dispc_check_new_clk(fb_dev, &new_pclk, dpi_clk_src, new_val, SPRDFB_DYNAMIC_PCLK); if (ret) { pr_err("%s: new dpi clock is invalid.", __func__); return -EINVAL; } break; case SPRDFB_DYNAMIC_MIPI_CLK: /* Given mipi clock */ if(!panel){ pr_err("%s: there is no panel!", __func__); return -ENXIO; } if (panel->type != LCD_MODE_DSI) { pr_err("%s: panel type isn't dsi mode", __func__); return -ENXIO; } old_val = panel->info.mipi->phy_feq; ret = sprdfb_dsi_chg_dphy_freq(fb_dev, new_val); if (ret) { pr_err("%s: new dphy freq is invalid.", __func__); return -EINVAL; } pr_info("dphy frequency is switched from %dHz to %dHz\n", old_val * 1000, new_val * 1000); return ret; default: pr_err("%s: Unsupported clock type.\n", __func__); return -EINVAL; } if (howto != SPRDFB_FORCE_PCLK && howto != SPRDFB_FORCE_FPS && !fb_dev->enable) { pr_warn("After fb_dev is resumed, dpi or mipi clk will be updated\n"); return ret; } /* Now let's do update dpi clock */ ret = clk_set_rate(dispc_ctx.clk_dispc_dpi, new_pclk); if (ret) { pr_err("Failed to set pixel clock.\n"); return ret; } pr_info("dpi clock is switched from %dHz to %dHz\n", fb_dev->dpi_clock, new_pclk); /* Save the updated clock */ fb_dev->dpi_clock = new_pclk; return ret; } #if (defined(CONFIG_SPRD_SCXX30_DMC_FREQ) || defined(CONFIG_SPRD_SCX35_DMC_FREQ)) static unsigned int sprdfb_dispc_threshold_check() { #if defined CONFIG_FB_SCX30G || defined CONFIG_FB_SCX35L //For Tshark and SharkL, not need to change threshlod return 0; #endif return 1; } /*return value: 0 -- Allow DMC change frequency 1 -- Don't allow DMC change frequency*/ static unsigned int sprdfb_dispc_change_threshold(struct devfreq_dbs *h, unsigned int state) { struct sprdfb_dispc_context *dispc_ctx = (struct sprdfb_dispc_context *)h->data; struct sprdfb_device *dev = dispc_ctx->dev; if(NULL == dev || 0 == dev->enable){ //printk(KERN_ERR "sprdfb: sprdfb_dispc_change_threshold fail.(dev not enable)\n"); return 0; } printk(KERN_ERR "sprdfb: DMC change freq(%u)\n", state); mdelay(10); if(SPRDFB_PANEL_IF_DPI == dev->panel_if_type){ if(state == DEVFREQ_PRE_CHANGE){ dispc_set_threshold(0x960, 0x00, 0x960); }else{ dispc_set_threshold(0x500, 0x00, 0x500); } } return 0; } #endif //begin bug210112 extern unsigned long lcd_base_from_uboot; static int32_t sprdfb_dispc_refresh_logo (struct sprdfb_device *dev) { uint32_t i = 0; pr_debug("%s:[%d] panel_if_type:%d\n",__func__,__LINE__,dev->panel_if_type); if(SPRDFB_PANEL_IF_DPI != dev->panel_if_type){ printk(KERN_ERR "sprdfb: sprdfb_dispc_refresh_logo if type error\n!"); return 0; } dispc_clear_bits(0x1f, DISPC_INT_EN);//disable all interrupt dispc_set_bits(0x1f, DISPC_INT_CLR);// clear all interruption dispc_set_bits(BIT(5), DISPC_DPI_CTRL);//update //wait for update- done interruption for(i=0; i<500; i++) { if(!(dispc_read(DISPC_INT_RAW) & (0x10))){ udelay(1000); } else { break; } } if(i >= 500) { printk("sprdfb: [%s] wait dispc done int time out!! (0x%x)\n", __func__, dispc_read(DISPC_INT_RAW)); } dispc_set_bits((1<<5), DISPC_INT_CLR); return 0; } void sprdfb_dispc_logo_proc(struct sprdfb_device *dev) { //inline size_t roundUpToPageSize(size_t x) { return (x + (PAGE_SIZE-1)) & ~(PAGE_SIZE-1);} unsigned long logo_dst_p = 0; void *logo_src_v = NULL; void *logo_dst_v = NULL; //use the second frame buffer ,virtual uint32_t logo_size = 0;// should be rgb565 pr_debug("sprdfb: %s[%d] enter.\n",__func__,__LINE__); if(dev == NULL) { printk("sprdfb: %s[%d]: dev == NULL, return without process logo!!\n",__func__,__LINE__); return; } if(lcd_base_from_uboot == 0L) { printk("sprdfb: %s[%d]: lcd_base_from_uboot == 0, return without process logo!!\n",__func__,__LINE__); return; } if(SPRDFB_PANEL_IF_DPI != dev->panel_if_type) return; //#define USE_OVERLAY_BUFF #ifdef CONFIG_FB_LOW_RES_SIMU if((0 != dev->display_width) && (0 != dev->display_height)){ logo_size = dev->display_width * dev->display_height * 2;// should be rgb565 }else #endif logo_size = dev->panel->width * dev->panel->height * 2;// should be rgb565 #if 0 #ifndef USE_OVERLAY_BUFF kernel_fb_size = dev->panel->width * dev->panel->height * (dev->bpp / 8); kernel_fb_size = 0; logo_dst_v = dev->fb->screen_base + kernel_fb_size;//use the second frame buffer logo_dst_p = dev->fb->fix.smem_start + kernel_fb_size;//use the second frame buffer #else logo_dst_p = SPRD_ION_OVERLAY_BASE-logo_size;//use overlay frame buffer logo_dst_v = (uint32_t)ioremap(logo_dst_p, logo_size); #endif #else dev->logo_buffer_size = logo_size; dev->logo_buffer_addr_v = (void*)__get_free_pages(GFP_ATOMIC | __GFP_ZERO , get_order(logo_size)); if (!dev->logo_buffer_addr_v) { printk(KERN_ERR "sprdfb: %s Failed to allocate logo proc buffer\n", __FUNCTION__); return; } printk(KERN_INFO "sprdfb: got %d bytes logo proc buffer at 0x%p\n", logo_size, dev->logo_buffer_addr_v); logo_dst_v = dev->logo_buffer_addr_v; logo_dst_p = __pa(dev->logo_buffer_addr_v); #endif logo_src_v = ioremap(lcd_base_from_uboot, logo_size); if (!logo_src_v || !logo_dst_v) { printk(KERN_ERR "sprdfb: %s[%d]: Unable to map boot logo memory: src-0x%p, dst-0x%p\n", __func__, __LINE__,logo_src_v, logo_dst_v); return; } printk("sprdfb: %s[%d]: lcd_base_from_uboot: 0x%lx, logo_src_v:0x%p\n",__func__,__LINE__,lcd_base_from_uboot,logo_src_v); printk("sprdfb: %s[%d]: logo_dst_p:0x%lx,logo_dst_v:0x%p\n",__func__,__LINE__,logo_dst_p,logo_dst_v); memcpy(logo_dst_v, logo_src_v, logo_size); dma_sync_single_for_device(dev->fb->dev, logo_dst_p, logo_size, DMA_TO_DEVICE); iounmap(logo_src_v); #if 0 #ifdef USE_OVERLAY_BUFF iounmap(logo_dst_v); #endif #endif //dispc_print_osd_config(__func__,__LINE__); dispc_write(logo_dst_p, DISPC_OSD_BASE_ADDR); sprdfb_dispc_refresh_logo(dev); //dispc_print_osd_config(__func__,__LINE__); } //end bug210112 #ifdef CONFIG_FB_MMAP_CACHED void sprdfb_set_vma(struct vm_area_struct *vma) { if(NULL != vma){ dispc_ctx.vma = vma; } } #endif int32_t sprdfb_is_refresh_done(struct sprdfb_device *dev) { printk("sprdfb: sprdfb_is_refresh_done vsync_done=%d",dispc_ctx.vsync_done); return (int32_t)dispc_ctx.vsync_done; } struct display_ctrl sprdfb_dispc_ctrl = { .name = "dispc", .early_init = sprdfb_dispc_early_init, .init = sprdfb_dispc_init, .uninit = sprdfb_dispc_uninit, .refresh = sprdfb_dispc_refresh, .logo_proc = sprdfb_dispc_logo_proc, .suspend = sprdfb_dispc_suspend, .resume = sprdfb_dispc_resume, .update_clk = dispc_update_clk_intf, #ifdef CONFIG_FB_ESD_SUPPORT .ESD_check = sprdfb_dispc_check_esd, #endif #ifdef CONFIG_FB_LCD_OVERLAY_SUPPORT .enable_overlay = sprdfb_dispc_enable_overlay, .display_overlay = sprdfb_dispc_display_overlay, #endif #ifdef CONFIG_FB_VSYNC_SUPPORT .wait_for_vsync = spdfb_dispc_wait_for_vsync, #endif #ifdef CONFIG_FB_MMAP_CACHED .set_vma = sprdfb_set_vma, #endif .is_refresh_done = sprdfb_is_refresh_done, };