diff options
| author | Yuval Adam <_@yuv.al> | 2017-08-30 08:25:59 +0000 |
|---|---|---|
| committer | Yuval Adam <_@yuv.al> | 2017-08-30 08:25:59 +0000 |
| commit | 8e4bff4cab0ddac6060645b0715210484d02ff40 (patch) | |
| tree | 3a5c3024371a04692a3a6d9974d001cdff8ebf84 /drivers/video/sprdfb/sprdfb_dispc.c | |
Diffstat (limited to 'drivers/video/sprdfb/sprdfb_dispc.c')
| -rw-r--r-- | drivers/video/sprdfb/sprdfb_dispc.c | 2446 |
1 files changed, 2446 insertions, 0 deletions
diff --git a/drivers/video/sprdfb/sprdfb_dispc.c b/drivers/video/sprdfb/sprdfb_dispc.c new file mode 100644 index 00000000..b50dfd89 --- /dev/null +++ b/drivers/video/sprdfb/sprdfb_dispc.c @@ -0,0 +1,2446 @@ +/* + * 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/workqueue.h> +#include <linux/spinlock.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/fb.h> +#include <linux/delay.h> +#if (defined(CONFIG_SPRD_SCXX30_DMC_FREQ) || defined(CONFIG_SPRD_SCX35_DMC_FREQ)) +#include <linux/devfreq.h> +#endif +#include <linux/irqreturn.h> +#include <linux/interrupt.h> +#ifdef CONFIG_OF +#include <linux/of_irq.h> +#endif +#include <asm/cacheflush.h> +#include "sprdfb_dispc_reg.h" +#include "sprdfb_panel.h" +#include "sprdfb.h" +#include "sprdfb_chip_common.h" +#ifdef CONFIG_CPU_IDLE +#include <soc/sprd/cpuidle.h> +#endif +#include <linux/dma-mapping.h> + +//#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, + +}; + + |
