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
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;
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;
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)
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);
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,
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)
}
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:
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:
}
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;
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;
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);
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 =
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);
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;
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);
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:
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)) {
unlock_fb_info(fbinfo);
decon_info("%s -\n", __func__);
+#if defined(CONFIG_EXYNOS_READ_ESD_SOLUTION)
+ mutex_unlock(&decon->esd.lock);
+#endif
return;
}
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)
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 {
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);
#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__ */
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;
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
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);
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);
+
}
/*
{
int backlightlevel;
+ dsim_dbg("%s +\n", __func__);
+
backlightlevel = get_backlight_level(brightness);
set_brightness[1] = backlightlevel;
return -EINVAL;
}
+ dsim_dbg("%s -(brightness:%d)\n", __func__, brightness);
+
return 0;
}
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;
}
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
};