fbdev: dpu20: ESD solution
authorKim Manseok <manseoks.kim@samsung.com>
Tue, 3 Jul 2018 08:02:51 +0000 (17:02 +0900)
committerhskang <hs1218.kang@samsung.com>
Thu, 9 Aug 2018 05:12:28 +0000 (01:12 -0400)
Change-Id: Ic1aaf989b039a17f21098784bc2d5d9f34c580cb
Signed-off-by: ChiHun Won <chihun.won@samsung.com>
drivers/video/fbdev/exynos/dpu20/Kconfig
drivers/video/fbdev/exynos/dpu20/decon.h
drivers/video/fbdev/exynos/dpu20/decon_core.c
drivers/video/fbdev/exynos/dpu20/decon_dsi.c
drivers/video/fbdev/exynos/dpu20/dsim.h
drivers/video/fbdev/exynos/dpu20/dsim_drv.c
drivers/video/fbdev/exynos/dpu20/panels/lcd_ctrl.h
drivers/video/fbdev/exynos/dpu20/panels/s6e3fa0_lcd_ctrl.c
drivers/video/fbdev/exynos/dpu20/panels/s6e3fa0_mipi_lcd.c

index a681bfb9760de82b870781117e75888a30e94fc3..05d2d9ce10fadf75d9efac41acebcae317d0b85b 100644 (file)
@@ -60,6 +60,13 @@ config EXYNOS_VIRTUAL_DISPLAY
        help
          If this menu is enabled, you can boot up board without LCD.
 
+config EXYNOS_READ_ESD_SOLUTION
+       bool "Support ESD solution using read DDI power state"
+       depends on EXYNOS_DPU20
+       default n
+       help
+         If this menu is enabled, you can detect ESD of panel.
+
 config EXYNOS_LOW_PERSISTENCE
        bool "Support Low Persistence Mode"
         depends on EXYNOS_DPU20
index 78de8fcb81d1079b0e964af0e27f464a92fbd94a..a14945128e645cb200d864beb0655bcb49608b0d 100644 (file)
@@ -783,6 +783,15 @@ struct decon_vsync {
        struct task_struct *thread;
 };
 
+#if defined(CONFIG_EXYNOS_READ_ESD_SOLUTION)
+struct decon_esd {
+#define PWR_STATE_RECHECK_TIME 5
+#define ESD_SLEEP_TIME         4
+       struct mutex lock;
+       struct task_struct *thread;
+};
+#endif
+
 struct decon_hiber {
        struct mutex lock;
        struct task_struct *thread;
@@ -875,6 +884,9 @@ struct decon_device {
        struct mutex lock;
        struct mutex pm_lock;
        spinlock_t slock;
+#if defined(CONFIG_EXYNOS_READ_ESD_SOLUTION)
+       struct decon_esd esd;
+#endif
 
 #if defined(CONFIG_SUPPORT_LEGACY_ION)
        struct ion_client *ion_client;
@@ -935,6 +947,10 @@ struct decon_device {
 
        bool mres_enabled;
        bool low_persistence;
+#if defined(CONFIG_EXYNOS_READ_ESD_SOLUTION)
+       atomic_t bypass;
+       struct decon_reg_data last_regs;
+#endif
 };
 
 static inline struct decon_device *get_decon_drvdata(u32 id)
@@ -1037,6 +1053,8 @@ int decon_get_pinctrl(struct decon_device *decon);
 int decon_register_ext_irq(struct decon_device *decon);
 int decon_create_vsync_thread(struct decon_device *decon);
 void decon_destroy_vsync_thread(struct decon_device *decon);
+int decon_create_esd_thread(struct decon_device *decon);
+void decon_destroy_esd_thread(struct decon_device *decon);
 int decon_create_psr_info(struct decon_device *decon);
 void decon_destroy_psr_info(struct decon_device *decon);
 
@@ -1193,6 +1211,30 @@ static inline void decon_enter_shutdown_reset(struct decon_device *decon)
        atomic_set(&decon->is_shutdown, 0);
 }
 
+#if defined(CONFIG_EXYNOS_READ_ESD_SOLUTION)
+static inline void decon_set_bypass(struct decon_device *decon, bool on)
+{
+       atomic_set(&decon->bypass, !!on);
+}
+
+static inline void decon_bypass_on(struct decon_device *decon)
+{
+       atomic_inc(&decon->bypass);
+}
+
+static inline void decon_bypass_off(struct decon_device *decon)
+{
+       atomic_dec(&decon->bypass);
+}
+
+static inline bool decon_is_bypass(struct decon_device *decon)
+{
+       return atomic_read(&decon->bypass);
+}
+#endif
+
+int decon_update_pwr_state(struct decon_device *decon, u32 mode);
+
 enum disp_pwr_mode {
        DISP_PWR_OFF = 0,
        DISP_PWR_DOZE,
@@ -1292,6 +1334,8 @@ int dpu_sysmmu_fault_handler(struct iommu_domain *domain,
 int dpu_pm_domain_check_status(struct exynos_pm_domain *pm_domain);
 #endif
 int decon_set_out_sd_state(struct decon_device *decon, enum decon_state state);
+int decon_update_last_regs(struct decon_device *decon,
+               struct decon_reg_data *regs);
 
 /* IOCTL commands */
 #define S3CFB_SET_VSYNC_INT            _IOW('F', 206, __u32)
index ebf652f20d303672f540c4dd91509cea37c72a45..6fd69691cd7e1b2b7bbfaaa79da49046706297ea 100644 (file)
@@ -930,7 +930,9 @@ static int decon_blank(int blank_mode, struct fb_info *info)
        }
 
        decon_hiber_block_exit(decon);
-
+#if defined(CONFIG_EXYNOS_READ_ESD_SOLUTION)
+       mutex_lock(&decon->esd.lock);
+#endif
        switch (blank_mode) {
        case FB_BLANK_POWERDOWN:
        case FB_BLANK_NORMAL:
@@ -948,6 +950,10 @@ static int decon_blank(int blank_mode, struct fb_info *info)
                        decon_err("failed to enable decon\n");
                        goto blank_exit;
                }
+#if defined(CONFIG_EXYNOS_READ_ESD_SOLUTION)
+               if (decon->esd.thread)
+                       wake_up_process(decon->esd.thread);
+#endif
                break;
        case FB_BLANK_VSYNC_SUSPEND:
        case FB_BLANK_HSYNC_SUSPEND:
@@ -956,6 +962,9 @@ static int decon_blank(int blank_mode, struct fb_info *info)
        }
 
 blank_exit:
+#if defined(CONFIG_EXYNOS_READ_ESD_SOLUTION)
+       mutex_unlock(&decon->esd.lock);
+#endif
        decon_hiber_unblock(decon);
        decon_info("%s -\n", __func__);
        return ret;
@@ -997,6 +1006,11 @@ int decon_wait_for_vsync(struct decon_device *decon, u32 timeout)
 
        decon_to_psr_info(decon, &psr);
 
+#if defined(CONFIG_EXYNOS_READ_ESD_SOLUTION)
+       if (decon_is_bypass(decon))
+               return 0;
+#endif
+
        if (psr.trig_mode != DECON_HW_TRIG)
                return 0;
 
@@ -2125,6 +2139,10 @@ static void decon_update_regs(struct decon_device *decon,
 
                decon_wait_for_vstatus(decon, 50);
                if (decon_reg_wait_update_done_timeout(decon->id, SHADOW_UPDATE_TIMEOUT) < 0) {
+#if defined(CONFIG_EXYNOS_READ_ESD_SOLUTION)
+                       if (decon_is_bypass(decon))
+                               goto end;
+#endif
                        decon_up_list_saved();
 #if defined(CONFIG_EXYNOS_AFBC_DEBUG)
                        decon_dump_afbc_handle(decon, old_dma_bufs);
@@ -2165,6 +2183,94 @@ end:
        decon_dpp_stop(decon, false);
 }
 
+/*
+ * this function is made for refresh last decon_reg_data that is stored
+ * in update_handler. it will be called after recovery subdev.
+ * TODO : combine decon_update_regs and decon_update_last_regs
+ */
+int decon_update_last_regs(struct decon_device *decon,
+               struct decon_reg_data *regs)
+{
+       int ret = 0;
+       struct decon_mode_info psr;
+
+       decon_info("%s +\n", __func__);
+
+       decon_exit_hiber(decon);
+
+       decon_check_used_dpp(decon, regs);
+
+#if defined(CONFIG_EXYNOS_AFBC_DEBUG)
+       decon_update_vgf_info(decon, regs, true);
+#endif
+
+       decon_update_hdr_info(decon, regs);
+
+#if defined(CONFIG_EXYNOS_BTS)
+       /* add calc and update bw : cur > prev */
+       decon->bts.ops->bts_calc_bw(decon, regs);
+       decon->bts.ops->bts_update_bw(decon, regs, 0);
+#endif
+
+       DPU_EVENT_LOG_WINCON(&decon->sd, regs);
+
+       decon_to_psr_info(decon, &psr);
+       if (regs->num_of_window) {
+               if (__decon_update_regs(decon, regs) < 0) {
+#if defined(CONFIG_EXYNOS_AFBC_DEBUG)
+                       decon_dump_afbc_handle(decon, old_dma_bufs);
+#endif
+                       decon_dump(decon);
+                       BUG();
+               }
+               if (!regs->num_of_window) {
+                       __decon_update_clear(decon, regs);
+                       decon_wait_for_vsync(decon, VSYNC_TIMEOUT_MSEC);
+                       goto end;
+               }
+       } else {
+               __decon_update_clear(decon, regs);
+               decon_wait_for_vsync(decon, VSYNC_TIMEOUT_MSEC);
+               goto end;
+       }
+
+       decon_wait_for_vsync(decon, VSYNC_TIMEOUT_MSEC);
+
+       if (decon->cursor.unmask)
+               decon_set_cursor_unmask(decon, false);
+
+       decon_wait_for_vstatus(decon, 50);
+       if (decon_reg_wait_update_done_timeout(decon->id, SHADOW_UPDATE_TIMEOUT) < 0) {
+               decon_err("%s shadow update timeout\n", __func__);
+               ret = -ETIMEDOUT;
+               goto end;
+       }
+
+       decon_info("%s ...\n", __func__);
+
+       if (!decon->low_persistence)
+               decon_reg_set_trigger(decon->id, &psr, DECON_TRIG_DISABLE);
+
+end:
+       DPU_EVENT_LOG(DPU_EVT_FENCE_RELEASE, &decon->sd, ktime_set(0, 0));
+
+#if defined(CONFIG_EXYNOS_AFBC_DEBUG)
+       decon_save_vgf_connected_win_id(decon, regs);
+       decon_update_vgf_info(decon, regs, false);
+#endif
+
+#if defined(CONFIG_EXYNOS_BTS)
+       /* add update bw : cur < prev */
+       decon->bts.ops->bts_update_bw(decon, regs, 1);
+#endif
+
+       decon_dpp_stop(decon, false);
+
+       decon_info("%s -\n", __func__);
+
+       return ret;
+}
+
 static void decon_update_regs_handler(struct kthread_work *work)
 {
        struct decon_update_regs *up =
@@ -2189,6 +2295,9 @@ static void decon_update_regs_handler(struct kthread_work *work)
 
                decon_set_cursor_reset(decon, data);
                decon_update_regs(decon, data);
+#if defined(CONFIG_EXYNOS_READ_ESD_SOLUTION)
+               memcpy(&decon->last_regs, data, sizeof(struct decon_reg_data));
+#endif
                decon_hiber_unblock(decon);
                if (!decon->up_list_saved) {
                        list_del(&data->list);
@@ -2336,8 +2445,19 @@ static int decon_set_win_config(struct decon_device *decon,
        mutex_lock(&decon->lock);
 
        if (IS_DECON_OFF_STATE(decon) ||
+#if defined(CONFIG_EXYNOS_READ_ESD_SOLUTION)
+               decon_is_bypass(decon) ||
+#endif
                decon->state == DECON_STATE_TUI ||
                IS_ENABLED(CONFIG_EXYNOS_VIRTUAL_DISPLAY)) {
+#if defined(CONFIG_EXYNOS_READ_ESD_SOLUTION)
+               decon_warn("decon-%d skip win_config(state:%s, bypass:%s)\n",
+                               decon->id, decon_state_names[decon->state],
+                               decon_is_bypass(decon) ? "on" : "off");
+#else
+               decon_warn("decon-%d skip win_config(state:%s)\n",
+                               decon->id, decon_state_names[decon->state]);
+#endif
                win_data->retire_fence = decon_create_fence(decon, &sync_file);
                if (win_data->retire_fence < 0)
                        goto err;
@@ -3670,6 +3790,9 @@ static int decon_probe(struct platform_device *pdev)
        mutex_init(&decon->pm_lock);
        mutex_init(&decon->up.lock);
        mutex_init(&decon->cursor.lock);
+#if defined(CONFIG_EXYNOS_READ_ESD_SOLUTION)
+       mutex_init(&decon->esd.lock);
+#endif
 
        decon_enter_shutdown_reset(decon);
 
@@ -3746,14 +3869,28 @@ static int decon_probe(struct platform_device *pdev)
        itmon_notifier_chain_register(&decon->itmon_nb);
 #endif
 
+#if defined(CONFIG_EXYNOS_READ_ESD_SOLUTION)
+       decon_set_bypass(decon, false);
+#endif
+
        ret = decon_initial_display(decon, false);
        if (ret)
                goto err_display;
 
+#if defined(CONFIG_EXYNOS_READ_ESD_SOLUTION)
+       ret = decon_create_esd_thread(decon);
+       if (ret)
+               goto err_esd;
+#endif
+
        decon_info("decon%d registered successfully", decon->id);
 
        return 0;
 
+#if defined(CONFIG_EXYNOS_READ_ESD_SOLUTION)
+err_esd:
+       decon_destroy_esd_thread(decon);
+#endif
 err_display:
        decon_destroy_update_thread(decon);
 err_win:
@@ -3802,6 +3939,9 @@ static void decon_shutdown(struct platform_device *pdev)
        struct decon_device *decon = platform_get_drvdata(pdev);
        struct fb_info *fbinfo = decon->win[decon->dt.dft_win]->fbinfo;
 
+#if defined(CONFIG_EXYNOS_READ_ESD_SOLUTION)
+       mutex_lock(&decon->esd.lock);
+#endif
        decon_enter_shutdown(decon);
 
        if (!lock_fb_info(fbinfo)) {
@@ -3820,6 +3960,9 @@ static void decon_shutdown(struct platform_device *pdev)
        unlock_fb_info(fbinfo);
 
        decon_info("%s -\n", __func__);
+#if defined(CONFIG_EXYNOS_READ_ESD_SOLUTION)
+       mutex_unlock(&decon->esd.lock);
+#endif
        return;
 }
 
index 3cfe3a38934e6586d2c12c541a3dc6c36c4ca797..8c3d51aeeff4cd21eb2a0bd175e2ce0ce7a9df97 100644 (file)
@@ -380,6 +380,179 @@ void decon_destroy_vsync_thread(struct decon_device *decon)
                kthread_stop(decon->vsync.thread);
 }
 
+#if defined(CONFIG_EXYNOS_READ_ESD_SOLUTION)
+#define ESD_RECOVERY_RETRY_CNT 5
+static int decon_handle_esd(struct decon_device *decon)
+{
+       struct dsim_device *dsim;
+       int ret = 0;
+       int retry = 0;
+       int status = 0;
+
+       decon_info("%s +\n", __func__);
+
+       if (decon == NULL) {
+               decon_warn("%s invalid param\n", __func__);
+               return -EINVAL;
+       }
+
+       decon_bypass_on(decon);
+       dsim = container_of(decon->out_sd[0], struct dsim_device, sd);
+       dsim->esd_recovering = true;
+
+       while (++retry <= ESD_RECOVERY_RETRY_CNT) {
+               decon_warn("%s try recovery(%d times)\n",
+                               __func__, retry);
+               ret = decon_update_pwr_state(decon, DISP_PWR_OFF);
+               if (ret < 0) {
+                       decon_err("%s decon-%d failed to set subdev OFF state\n",
+                                       __func__, decon->id);
+                       continue;
+               }
+
+               ret = decon_update_pwr_state(decon, DISP_PWR_NORMAL);
+               if (ret < 0) {
+                       decon_err("%s decon-%d failed to set subdev ON state\n",
+                                       __func__, decon->id);
+                       continue;
+               }
+
+#if defined(READ_ESD_SOLUTION_TEST)
+               status = DSIM_ESD_OK;
+#else
+               status = call_panel_ops(dsim, read_state, dsim);
+#endif
+               if (status != DSIM_ESD_OK) {
+                       decon_err("%s failed to recover subdev(status %d)\n",
+                                       __func__, status);
+                       continue;
+               }
+
+               DPU_FULL_RECT(&decon->last_regs.up_region, decon->lcd_info);
+               decon->last_regs.need_update = true;
+               ret = decon_update_last_regs(decon, &decon->last_regs);
+               if (ret < 0) {
+                       decon_err("%s failed to update last image(ret %d)\n",
+                                       __func__, ret);
+                       continue;
+               }
+               decon_info("%s recovery successfully(retry %d times)\n",
+                               __func__, retry);
+               break;
+       }
+
+       if (retry > ESD_RECOVERY_RETRY_CNT) {
+               decon_err("DECON:ERR:%s:failed to recover(retry %d times)\n",
+                               __func__, ESD_RECOVERY_RETRY_CNT);
+               decon_dump(decon);
+               if (decon->dt.out_type == DECON_OUT_DSI)
+                       v4l2_subdev_call(decon->out_sd[0], core, ioctl,
+                                       DSIM_IOC_DUMP, NULL);
+               BUG();
+       }
+
+       dsim->esd_recovering = false;
+       decon_bypass_off(decon);
+       decon_info("%s -\n", __func__);
+
+       return ret;
+}
+
+static void decon_esd_process(int esd, struct decon_device *decon)
+{
+       int ret;
+
+       switch (esd) {
+       case DSIM_ESD_CHECK_ERROR:
+               decon_err("%s, It is not ESD, \
+                       but DDI is abnormal state(%d)\n", __func__, esd);
+               break;
+       case DSIM_ESD_OK:
+               decon_info("%s, DDI has normal state(%d)\n", __func__, esd);
+               break;
+       case DSIM_ESD_ERROR:
+               decon_err("%s, ESD is detected(%d)\n", __func__, esd);
+               ret = decon_handle_esd(decon);
+               if (ret)
+                       decon_err("%s, failed to recover ESD\n", __func__);
+               break;
+       default:
+               decon_err("%s, Not supported value(%d)\n", __func__, esd);
+               break;
+       }
+}
+
+static int decon_esd_thread(void *data)
+{
+       struct decon_device *decon = data;
+       struct dsim_device *dsim = NULL;
+       int esd = 0;
+
+       while (!kthread_should_stop()) {
+               /* Loop for ESD detection */
+               if (decon->state == DECON_STATE_OFF) {
+                       /* go to sleep when decon is not ready */
+                       decon_info("%s, Sleep \n", __func__);
+                       set_current_state(TASK_INTERRUPTIBLE);
+                       schedule();
+                       set_current_state(TASK_RUNNING);
+                       continue;
+               }
+
+               decon_hiber_block_exit(decon);
+               if (decon->state == DECON_STATE_ON) {
+                       mutex_lock(&decon->esd.lock);
+
+                       dsim = container_of(decon->out_sd[0],
+                                       struct dsim_device, sd);
+
+                       decon_info("%s, Try to check ESD\n", __func__);
+
+                       esd = call_panel_ops(dsim, read_state, dsim);
+                       decon_esd_process(esd, decon);
+
+                       mutex_unlock(&decon->esd.lock);
+               }
+               decon_hiber_unblock(decon);
+               /* sleep ESD_SLEEP_TIME second when decon is not state on
+                * and after read DDI state
+                */
+               decon_info("%s, Sleep %d second\n", __func__, ESD_SLEEP_TIME);
+               set_current_state(TASK_INTERRUPTIBLE);
+               schedule_timeout(ESD_SLEEP_TIME * HZ);
+       }
+       /* when if kthread_should_stop() return true */
+       return 0;
+}
+
+int decon_create_esd_thread(struct decon_device *decon)
+{
+       int ret = 0;
+       char name[16];
+
+       if (decon->dt.out_type != DECON_OUT_DSI) {
+               decon_info("esd thread is only needed for DSI path\n");
+               return 0;
+       }
+
+       sprintf(name, "decon%d-esd", decon->id);
+       decon->esd.thread = kthread_run(decon_esd_thread, decon, name);
+       if (IS_ERR_OR_NULL(decon->esd.thread)) {
+               decon_err("failed to run esd thread\n");
+               decon->esd.thread = NULL;
+               ret = PTR_ERR(decon->esd.thread);
+       }
+
+       return ret;
+}
+
+void decon_destroy_esd_thread(struct decon_device *decon)
+{
+       if (decon->esd.thread)
+               kthread_stop(decon->esd.thread);
+}
+#endif /* CONFIG_EXYNOS_READ_ESD_SOLUTION */
+
 /*
  * Variable Descriptions
  *   dsc_en : comp_mode (0=No Comp, 1=DSC, 2=MIC, 3=LEGO)
index 481ae72add38aaa3df27419b2e23fbf5d96a7a00..6a640a7af4947bee971b2358963fc32b91c2b06d 100644 (file)
@@ -241,6 +241,11 @@ struct dsim_device {
        bool fb_reservation;
        phys_addr_t phys_addr;
        phys_addr_t phys_size;
+#if defined(CONFIG_EXYNOS_READ_ESD_SOLUTION)
+//#define READ_ESD_SOLUTION_TEST
+       int esd_test;
+       bool esd_recovering;
+#endif
 };
 
 struct dsim_lcd_driver {
@@ -252,6 +257,9 @@ struct dsim_lcd_driver {
        int (*mres)(struct dsim_device *dsim, int mres_idx);
        int (*doze)(struct dsim_device *dsim);
        int (*doze_suspend)(struct dsim_device *dsim);
+#if defined(CONFIG_EXYNOS_READ_ESD_SOLUTION)
+       int (*read_state)(struct dsim_device *dsim);
+#endif
 };
 
 int dsim_write_data(struct dsim_device *dsim, u32 id, unsigned long d0, u32 d1);
@@ -402,4 +410,10 @@ static inline bool IS_DSIM_OFF_STATE(struct dsim_device *dsim)
 #define DSIM_IOC_DOZE                  _IOW('D', 20, u32)
 #define DSIM_IOC_DOZE_SUSPEND          _IOW('D', 21, u32)
 
+#if defined(CONFIG_EXYNOS_READ_ESD_SOLUTION)
+#define DSIM_ESD_OK                    0
+#define DSIM_ESD_ERROR                 1
+#define DSIM_ESD_CHECK_ERROR           2
+#endif
+
 #endif /* __SAMSUNG_DSIM_H__ */
index 0342404fa90bd9a8cd6a5230ce866abca1957979..81d676f7cb4581da299fe27544397db1830f74ce 100644 (file)
@@ -1141,6 +1141,36 @@ static void dsim_init_subdev(struct dsim_device *dsim)
        v4l2_set_subdevdata(sd, dsim);
 }
 
+#if defined(CONFIG_EXYNOS_READ_ESD_SOLUTION) && defined(READ_ESD_SOLUTION_TEST)
+static ssize_t dsim_esd_test_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       int ret;
+       unsigned long cmd;
+       struct dsim_device *dsim = dev_get_drvdata(dev);
+
+       ret = kstrtoul(buf, 0, &cmd);
+       if (ret)
+               return ret;
+
+       dsim->esd_test = cmd;
+
+       return count;
+}
+static DEVICE_ATTR(esd_test, 0644, NULL, dsim_esd_test_store);
+
+int dsim_create_esd_test_sysfs(struct dsim_device *dsim)
+{
+       int ret = 0;
+
+       ret = device_create_file(dsim->dev, &dev_attr_esd_test);
+       if (ret)
+               dsim_err("failed to create command read & write sysfs\n");
+
+       return ret;
+}
+#endif
+
 static int dsim_cmd_sysfs_write(struct dsim_device *dsim, bool on)
 {
        int ret = 0;
@@ -1658,6 +1688,13 @@ static int dsim_probe(struct platform_device *pdev)
        dsim_clocks_info(dsim);
        dsim_create_cmd_rw_sysfs(dsim);
 
+#if defined(CONFIG_EXYNOS_READ_ESD_SOLUTION)
+       dsim->esd_recovering = false;
+#if defined(READ_ESD_SOLUTION_TEST)
+       dsim_create_esd_test_sysfs(dsim);
+#endif
+#endif
+
 #ifdef DPHY_LOOP
        dsim_reg_set_dphy_loop_back_test(dsim->id);
 #endif
index 16df59cbcb0be28378686074708515958d2ba59b..2057538ab2839852021d3fcc79ea5b0db50174e1 100644 (file)
@@ -17,6 +17,8 @@
 void lcd_init(int id, struct decon_lcd *lcd);
 void lcd_enable(int id);
 void lcd_disable(int id);
+void lcd_sleepin(int id);
+void lcd_sleepout(int id);
 int lcd_gamma_ctrl(int id, unsigned int backlightlevel);
 int lcd_gamma_update(int id);
 int lcd_dump(int id);
index f1de610c39dad93cba8317d6e8ca8a0cc5b01bd3..3a82f754d3bda3335a79fd03268181fc272c1f12 100644 (file)
@@ -176,7 +176,26 @@ void lcd_enable(int id)
 
 void lcd_disable(int id)
 {
-       /* This function needs to implement */
+       if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)SEQ_DISPLAY_OFF,
+                               ARRAY_SIZE(SEQ_DISPLAY_OFF)) < 0)
+               dsim_err("fail to send SEQ_DISPLAY_OFF command.\n");
+       mdelay(20);
+}
+
+void lcd_sleepin(int id)
+{
+       if (dsim_wr_data(id, MIPI_DSI_DCS_LONG_WRITE, (unsigned long)SEQ_SLEEP_IN,
+                               ARRAY_SIZE(SEQ_SLEEP_IN)) < 0)
+               dsim_err("fail to send SEQ_SLEEP_IN command.\n");
+       mdelay(120);
+}
+
+void lcd_sleepout(int id)
+{
+       if (dsim_wr_data(id, MIPI_DSI_DCS_SHORT_WRITE, SEQ_SLEEP_OUT[0], 0) < 0)
+               dsim_err("fail to send SEQ_SLEEP_OUT command.\n");
+       mdelay(120);
+
 }
 
 /*
index e5972294e08586be32e558b4d05047baa4e2d635..1657c4b38be8118b3487fd20fc93fe13cd9f4cab 100644 (file)
@@ -177,6 +177,8 @@ static int update_brightness(struct dsim_device *dsim, int brightness)
 {
        int backlightlevel;
 
+       dsim_dbg("%s +\n", __func__);
+
        backlightlevel = get_backlight_level(brightness);
 
        set_brightness[1] = backlightlevel;
@@ -196,6 +198,8 @@ static int update_brightness(struct dsim_device *dsim, int brightness)
                return -EINVAL;
        }
 
+       dsim_dbg("%s -(brightness:%d)\n", __func__, brightness);
+
        return 0;
 }
 
@@ -405,11 +409,18 @@ static int s6e3fa0_displayon(struct dsim_device *dsim)
                s6e3fa0_cabc_mode(dsim, panel->cabc_mode);
 #endif
 
+#if defined(CONFIG_EXYNOS_READ_ESD_SOLUTION)
+       if (dsim->esd_recovering)
+               s6e3fa0_set_brightness(dsim->bd);
+#endif
        return 1;
 }
 
 static int s6e3fa0_suspend(struct dsim_device *dsim)
 {
+       lcd_disable(dsim->id);
+       lcd_sleepin(dsim->id);
+
        return 1;
 }
 
@@ -418,9 +429,181 @@ static int s6e3fa0_resume(struct dsim_device *dsim)
        return 1;
 }
 
+#if defined(CONFIG_EXYNOS_READ_ESD_SOLUTION)
+#if defined(READ_ESD_SOLUTION_TEST)
+static int s6e3fa0_read_state(struct dsim_device *dsim)
+{
+       int ret = 0;
+       u8 buf[2] = {0x0};
+       int i = 0;
+       int RETRY = 5; /* several retries are allowed */
+       bool esd_detect = false;
+
+       dsim_info("%s, read DDI for checking ESD\n", __func__);
+
+       switch (dsim->esd_test) {
+       case 0: /* same as original operation */
+               for (i = 0; i < RETRY; i++) {
+                       ret = dsim_read_data(dsim, MIPI_DSI_DCS_READ,
+                                       MIPI_DCS_GET_POWER_MODE, 0x1, buf);
+                       if (ret < 0) {
+                               dsim_err("Failed to read panel REG 0x%02X!: 0x%02x, i(%d)\n",
+                                       MIPI_DCS_GET_POWER_MODE,
+                                       *(unsigned int *)buf & 0xFF, i);
+                               if (dsim->state != DSIM_STATE_ON)
+                                       return DSIM_ESD_OK;
+                               usleep_range(1000, 1100);
+                               continue;
+                       }
+
+                       if ((buf[0] & 0x7c) == 0x1c) {
+                               dsim_info("s6e3fa0 panel REG 0x%02X=0x%02x\n",
+                                       MIPI_DCS_GET_POWER_MODE, buf[0]);
+                               break;
+                       }
+
+                       dsim_err("s6e3fa0 panel REG 0x%02X Not match: 0x%02x, i(%d)\n",
+                                       MIPI_DCS_GET_POWER_MODE,
+                                       *(unsigned int *)buf & 0xFF, i);
+                       esd_detect = true;
+               }
+
+               if (i < RETRY)
+                       return DSIM_ESD_OK;
+               else if (esd_detect)
+                       return DSIM_ESD_ERROR;
+               else
+                       return DSIM_ESD_CHECK_ERROR;
+
+
+       case 1: /* simple check and always return esd_detection */
+               ret = dsim_read_data(dsim, MIPI_DSI_DCS_READ,
+                                       MIPI_DCS_GET_POWER_MODE, 0x1, buf);
+               if (ret < 0) {
+                       dsim_err("Failed to read panel REG 0x%02X!: 0x%02x, i(%d)\n",
+                                       MIPI_DCS_GET_POWER_MODE,
+                                       *(unsigned int *)buf & 0xFF, i);
+               } else {
+                       if ((buf[0] & 0x7c) == 0x1c)
+                               dsim_info("s6e3fa0 panel REG 0x%02X=0x%02x\n",
+                                       MIPI_DCS_GET_POWER_MODE, buf[0]);
+                       else {
+                               dsim_err("s6e3fa0 panel REG 0x%02X Not match: 0x%02x, i(%d)\n",
+                                       MIPI_DCS_GET_POWER_MODE,
+                                       *(unsigned int *)buf & 0xFF, i);
+                       }
+               }
+               dsim->esd_test = 0;
+               return DSIM_ESD_ERROR;
+       case 2: /* always return esd detection and initialize  */
+               dsim->esd_test = 0;
+               return DSIM_ESD_ERROR;
+       case 3: /* always return esd detection */
+               dsim->esd_test = 3;
+               return DSIM_ESD_ERROR;
+       case 4: /* always return esd ok */
+               dsim->esd_test = 4;
+               return DSIM_ESD_OK;
+       case 5: /* always return esd ok and display off/on by force*/
+               dsim_info("%s, lcd_disable is called for ESD test\n", __func__);
+               lcd_disable(dsim->id);
+               dsim_info("%s, 2sec sleep for ESD test\n", __func__);
+               msleep(2000);
+               dsim_info("%s, lcd_enable is called for ESD test\n", __func__);
+               lcd_enable(dsim->id);
+               dsim->esd_test = 4;
+               return DSIM_ESD_OK;
+       case 6: /* always return esd detection and display off by force*/
+               dsim_info("%s, lcd_disable is called for ESD test\n", __func__);
+               lcd_disable(dsim->id);
+               dsim_info("%s, 2sec sleep for ESD test\n", __func__);
+               msleep(2000);
+               dsim_info("%s, lcd_enable is called for ESD test\n", __func__);
+               lcd_enable(dsim->id);
+               dsim->esd_test = 4;
+               return DSIM_ESD_ERROR;
+       case 7: /* return DSIM_ESD_CHECK_ERROR by force */
+               for (i = 0; i < RETRY; i++) {
+                       ret = dsim_read_data(dsim, MIPI_DSI_DCS_READ,
+                                       MIPI_DCS_GET_POWER_MODE, 0x1, buf);
+                       ret = -ETIMEDOUT;
+                       if (ret < 0) {
+                               dsim_err("Failed to read panel REG 0x%02X!: 0x%02x, i(%d)\n",
+                                       MIPI_DCS_GET_POWER_MODE,
+                                       *(unsigned int *)buf & 0xFF, i);
+                               if (dsim->state != DSIM_STATE_ON)
+                                       return DSIM_ESD_OK;
+                               usleep_range(1000, 1100);
+                               continue;
+                       }
+               }
+
+               dsim->esd_test = 0;
+
+               if (i < RETRY)
+                       return DSIM_ESD_OK;
+               else if (esd_detect)
+                       return DSIM_ESD_ERROR;
+               else
+                       return DSIM_ESD_CHECK_ERROR;
+       default:
+               break;
+       }
+       return DSIM_ESD_OK;
+}
+
+#else
+static int s6e3fa0_read_state(struct dsim_device *dsim)
+{
+       int ret = 0;
+       u8 buf[2] = {0x0};
+       int i = 0;
+       int RETRY = 5; /* several retries are allowed */
+       bool esd_detect = false;
+
+       dsim_info("%s, read DDI for checking ESD\n", __func__);
+
+       for (i = 0; i < RETRY; i++) {
+               ret = dsim_read_data(dsim, MIPI_DSI_DCS_READ,
+                                       MIPI_DCS_GET_POWER_MODE, 0x1, buf);
+               if (ret < 0) {
+                       dsim_err("Failed to read panel REG 0x%02X!: 0x%02x, i(%d)\n",
+                                       MIPI_DCS_GET_POWER_MODE,
+                                       *(unsigned int *)buf & 0xFF, i);
+                       if (dsim->state != DSIM_STATE_ON)
+                               return DSIM_ESD_OK;
+                       usleep_range(1000, 1100);
+                       continue;
+               }
+
+               if ((buf[0] & 0x7c) == 0x1c) {
+                       dsim_info("s6e3fa0 panel REG 0x%02X=0x%02x\n",
+                                       MIPI_DCS_GET_POWER_MODE, buf[0]);
+                       break;
+               }
+
+               dsim_err("s6e3fa0 panel REG 0x%02X Not match: 0x%02x, i(%d)\n",
+                                       MIPI_DCS_GET_POWER_MODE,
+                                       *(unsigned int *)buf & 0xFF, i);
+               esd_detect = true;
+       }
+
+       if (i < RETRY)
+               return DSIM_ESD_OK;
+       else if (esd_detect)
+               return DSIM_ESD_ERROR;
+       else
+               return DSIM_ESD_CHECK_ERROR;
+}
+#endif
+#endif
+
 struct dsim_lcd_driver s6e3fa0_mipi_lcd_driver = {
        .probe          = s6e3fa0_probe,
        .displayon      = s6e3fa0_displayon,
        .suspend        = s6e3fa0_suspend,
        .resume         = s6e3fa0_resume,
+#if defined(CONFIG_EXYNOS_READ_ESD_SOLUTION)
+       .read_state     = s6e3fa0_read_state,
+#endif
 };