diff --git a/techpack/display/config/saipdisp.conf b/techpack/display/config/saipdisp.conf index dbbf3c847dbb..59c283c3455a 100644 --- a/techpack/display/config/saipdisp.conf +++ b/techpack/display/config/saipdisp.conf @@ -11,3 +11,4 @@ export CONFIG_QCOM_MDSS_PLL=y export CONFIG_MSM_SDE_ROTATOR=y export CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG=y export CONFIG_DRM_SDE_RSC=y +export CONFIG_DRM_DYNAMIC_REFRESH_RATE=y diff --git a/techpack/display/config/saipdispconf.h b/techpack/display/config/saipdispconf.h index 049024839701..09dc500defab 100644 --- a/techpack/display/config/saipdispconf.h +++ b/techpack/display/config/saipdispconf.h @@ -17,3 +17,4 @@ #define CONFIG_MSM_SDE_ROTATOR 1 #define CONFIG_MSM_SDE_ROTATOR_EVTLOG_DEBUG 1 #define CONFIG_DRM_SDE_RSC 1 +#define CONFIG_DRM_DYNAMIC_REFRESH_RATE 1 diff --git a/techpack/display/msm/Makefile b/techpack/display/msm/Makefile index 18eb12c35530..edf9d09a5b40 100644 --- a/techpack/display/msm/Makefile +++ b/techpack/display/msm/Makefile @@ -32,6 +32,7 @@ msm_drm-$(CONFIG_DRM_MSM_SDE) += sde/sde_crtc.o \ sde/sde_core_perf.o \ sde/sde_rm.o \ sde/sde_kms_utils.o \ + sde/sde_motUtil.o \ sde/sde_kms.o \ sde/sde_plane.o \ sde/sde_connector.o \ @@ -100,6 +101,7 @@ msm_drm-$(CONFIG_DRM_MSM_DSI) += dsi/dsi_phy.o \ dsi/dsi_panel.o \ dsi/dsi_clk_manager.o \ dsi/dsi_display_test.o \ + dsi/dsi_display_mot_ext.o \ msm_drm-$(CONFIG_DSI_PARSER) += dsi/dsi_parser.o \ diff --git a/techpack/display/msm/dsi/dsi_defs.h b/techpack/display/msm/dsi/dsi_defs.h index 5179edfebd02..ba4e5518ada2 100644 --- a/techpack/display/msm/dsi/dsi_defs.h +++ b/techpack/display/msm/dsi/dsi_defs.h @@ -281,6 +281,27 @@ enum dsi_dyn_clk_feature_type { * @DSI_CMD_SET_POST_TIMING_SWITCH: Post timing switch * @DSI_CMD_SET_QSYNC_ON Enable qsync mode * @DSI_CMD_SET_QSYNC_OFF Disable qsync mode + * @DSI_CMD_SET_HBM_FOD_ON: Turning HBM_FOD on + * @DSI_CMD_SET_HBM_ON: Turning HBM on + * @DSI_CMD_SET_HBM_OFF: Turning HBM off + * @DSI_CMD_SET_ACL_ON: Turning ACL on + * @DSI_CMD_SET_ACL_OFF: Turning ACL off + * @DSI_CMD_SET_CABC_UI: CABC UI mode + * @DSI_CMD_SET_CABC_MV: CABC MV mode + * @DSI_CMD_SET_CABC_DIS: CABC DIS mode + * @DSI_CMD_SET_DC_ON: DC mode on + * @DSI_CMD_SET_DC_OFF: DC mode off + * @DSI_CMD_SET_COLOR_VBT : COLOR VBT mode + * @DSI_CMD_SET_COLOR_STD: COLOR STD mode + * @DSI_CMD_SET_COLOR_GAME : COLOR GAME mode + * @DSI_CMD_SET_COLOR_NONE: COLOR NONE mode + * @DSI_CMD_SET_BRIGHTNESS_1E: Send demura codes when brightness changes to 0-1E + * @DSI_CMD_SET_BRIGHTNESS_1FD: Send demura codes when brightness changes to 1F-1FD + * @DSI_CMD_SET_BRIGHTNESS_2FF: Send demura codes when brightness changes to 1FE-2FF + * @DSI_CMD_SET_BRIGHTNESS_3FF: Send demura codes when brightness changes to 300-3FF + * @DSI_CMD_SET_BRIGHTNESS_4FF: Send demura codes when brightness changes to 400-4FF + * @DSI_CMD_SET_BRIGHTNESS_5FF: Send demura codes when brightness changes to 500-5FF + * @DSI_CMD_SET_BRIGHTNESS_FFF: Send demura codes when brightness changes to 600-FFF * @DSI_CMD_SET_MAX */ enum dsi_cmd_set_type { @@ -307,6 +328,27 @@ enum dsi_cmd_set_type { DSI_CMD_SET_POST_TIMING_SWITCH, DSI_CMD_SET_QSYNC_ON, DSI_CMD_SET_QSYNC_OFF, + DSI_CMD_SET_HBM_FOD_ON, + DSI_CMD_SET_HBM_ON, + DSI_CMD_SET_HBM_OFF, + DSI_CMD_SET_ACL_ON, + DSI_CMD_SET_ACL_OFF, + DSI_CMD_SET_CABC_UI, + DSI_CMD_SET_CABC_MV, + DSI_CMD_SET_CABC_DIS, + DSI_CMD_SET_DC_ON, + DSI_CMD_SET_DC_OFF, + DSI_CMD_SET_COLOR_VBT, + DSI_CMD_SET_COLOR_STD, + DSI_CMD_SET_COLOR_GAME, + DSI_CMD_SET_COLOR_NONE, + DSI_CMD_SET_BRIGHTNESS_1E, + DSI_CMD_SET_BRIGHTNESS_1FD, + DSI_CMD_SET_BRIGHTNESS_2FF, + DSI_CMD_SET_BRIGHTNESS_3FF, + DSI_CMD_SET_BRIGHTNESS_4FF, + DSI_CMD_SET_BRIGHTNESS_5FF, + DSI_CMD_SET_BRIGHTNESS_FFF, DSI_CMD_SET_MAX }; diff --git a/techpack/display/msm/dsi/dsi_display.c b/techpack/display/msm/dsi/dsi_display.c index acb1f7db43e1..f4ffa19cbdb4 100644 --- a/techpack/display/msm/dsi/dsi_display.c +++ b/techpack/display/msm/dsi/dsi_display.c @@ -45,6 +45,10 @@ static const struct of_device_id dsi_display_dt_match[] = { {} }; +static int dsi_display_enable_status (struct dsi_display *display, bool enable); +static void dsi_display_is_probed(struct dsi_display *display, + int probe_status); + static void dsi_display_mask_ctrl_error_interrupts(struct dsi_display *display, u32 mask, bool enable) { @@ -246,6 +250,28 @@ error: return rc; } +int dsi_display_set_param(void *display, struct msm_param_info *param_info) +{ + struct dsi_display *dsi_display = display; + struct dsi_panel *panel; + int rc = 0; + + if (dsi_display == NULL || dsi_display->panel == NULL) + return -EINVAL; + + panel = dsi_display->panel; + + DSI_DEBUG("%s+\n", __func__); + if (!dsi_panel_initialized(panel)) + return -EINVAL; + + rc = dsi_panel_set_param(panel, param_info); + if (rc) + DSI_ERR("[%s] failed to panel to set param. rc=%d\n", + dsi_display->name, rc); + return rc; +} + static int dsi_display_cmd_engine_enable(struct dsi_display *display) { int rc = 0; @@ -697,6 +723,40 @@ exit: return rc; } +int dsi_display_cmd_mipi_transfer(struct dsi_display *display, + struct mipi_dsi_msg *msg, + u32 flags) +{ + int rc = 0; + struct dsi_display_ctrl *m_ctrl; + + m_ctrl = &display->ctrl[display->cmd_master_idx]; + + if (display->tx_cmd_buf == NULL) { + rc = dsi_host_alloc_cmd_tx_buffer(display); + if (rc) { + DSI_ERR("failed to allocate cmd tx buffer memory\n"); + goto done; + } + } + + rc = dsi_display_cmd_engine_enable(display); + if (rc) { + DSI_ERR("cmd engine enable failed\n"); + return -EPERM; + } + + flags |= DSI_CTRL_CMD_FETCH_MEMORY | DSI_CTRL_CMD_CUSTOM_DMA_SCHED; + rc = dsi_ctrl_cmd_transfer(m_ctrl->ctrl, msg, &flags); + if (((flags & DSI_CTRL_CMD_READ) && rc <= 0) || + (!(flags & DSI_CTRL_CMD_READ) && rc)) + DSI_ERR("failed to transfer cmd. rc = %d\n", rc); + + dsi_display_cmd_engine_disable(display); +done: + return rc; +} + static int dsi_display_status_reg_read(struct dsi_display *display) { int rc = 0, i; @@ -777,6 +837,21 @@ static int dsi_display_status_check_te(struct dsi_display *display) return rc; } +static int dsi_display_status_check_te_video(struct dsi_display *display) +{ + int rc = 1; + struct dsi_panel *panel = display->panel; + + if((panel->video_te_count - panel->video_te_count_priv) > 1) { + panel->video_te_count_priv = panel->video_te_count; + } else { + DSI_ERR("%s: video TE check ERROR te_count %d, priv =%d\n", __func__, panel->video_te_count, panel->video_te_count_priv); + rc = 0; + } + + return rc; +} + int dsi_display_check_status(struct drm_connector *connector, void *display, bool te_check_override) { @@ -835,6 +910,8 @@ int dsi_display_check_status(struct drm_connector *connector, void *display, rc = dsi_display_status_reg_read(dsi_display); } else if (status_mode == ESD_MODE_SW_BTA) { rc = dsi_display_status_bta_request(dsi_display); + } else if (status_mode == ESD_MODE_PANEL_TE_VIDEO) { + rc = dsi_display_status_check_te_video(dsi_display); } else if (status_mode == ESD_MODE_PANEL_TE) { rc = dsi_display_status_check_te(dsi_display); } else { @@ -864,6 +941,19 @@ release_panel_lock: return rc; } +bool dsi_display_force_esd_disable(void *display) +{ + struct dsi_display *dsi_display = display; + struct dsi_panel *panel; + + if (dsi_display == NULL) + return false; + + panel = dsi_display->panel; + + return (panel->esd_utag_enable? false: true); +} + static int dsi_display_cmd_prepare(const char *cmd_buf, u32 cmd_buf_len, struct dsi_cmd_desc *cmd, u8 *payload, u32 payload_len) { @@ -894,6 +984,166 @@ static int dsi_display_cmd_prepare(const char *cmd_buf, u32 cmd_buf_len, return 0; } + +static int dsi_display_dispUtil_get_datatype (char dsi_cmd, u8 cmd_type, + size_t tx_len, u8 *tx_data_type) +{ + int rc = 0; + + *tx_data_type = 0; + + /* cmd_type = 0 : DSI DCS cmd; + * cmd_type = 1 : DSI Generic cmd */ + if (cmd_type > DISPUTIL_DSI_GENERIC) { + DSI_ERR("%s: Invalid DSI package type[%d] = 0x%x\n", + __func__, DISPUTIL_MIPI_CMD_TYPE, + cmd_type); + return -EINVAL; + } + + DSI_DEBUG("dsi_cmd =0x%x cmd_type=0x%x tx_len=0x%x\n", + dsi_cmd, cmd_type, (u32)tx_len); + + if (dsi_cmd == DISPUTIL_DSI_WRITE) { + /* This is a DSI write command */ + switch(tx_len) { + case 1: + if (cmd_type == DISPUTIL_DSI_GENERIC) + /* write, generic, no param */ + *tx_data_type = 0x3; + else if (cmd_type == DISPUTIL_DSI_DCS) + /* write, DCS, no param */ + *tx_data_type = 0x5; + break; + case 2: + if (cmd_type == DISPUTIL_DSI_GENERIC) + /* Write, generic, 1 param */ + *tx_data_type = 0x13; + else if (cmd_type == DISPUTIL_DSI_DCS) + /* write, DCS, 1 param */ + *tx_data_type = 0x15; + break; + default: + if (cmd_type == DISPUTIL_DSI_GENERIC) + /* Write, generic, long*/ + *tx_data_type = 0x29; + else if (cmd_type == DISPUTIL_DSI_DCS) + /* write, DCS, long*/ + *tx_data_type = 0x39; + break; + } + } else if (dsi_cmd == DISPUTIL_DSI_READ) { + if ((cmd_type == DISPUTIL_DSI_DCS) && (tx_len > 1)) { + /* Note: tx_len will include read command and payload */ + /* DCS read */ + DSI_ERR("DCS read supports no parameter. tx_len=%d\n", + (u32)tx_len); + rc = -EINVAL; + } else { + switch(tx_len) { + case 1: + if (cmd_type == DISPUTIL_DSI_GENERIC) + /* Read, generic, no param */ + *tx_data_type = 0x4; + else if (cmd_type == DISPUTIL_DSI_DCS) + /* Read, DCS, no param */ + *tx_data_type = 0x6; + break; + case 2: + if (cmd_type == DISPUTIL_DSI_GENERIC) + /* Read, Generic, 1 param */ + *tx_data_type = 0x14; + + break; + case 3: + if (cmd_type == DISPUTIL_DSI_GENERIC) + /* read, generic, 2 params */ + *tx_data_type = 0x24; + break; + default: + DSI_ERR("Invalid for tx_len= %d\n", (u32)tx_len); + break; + } + } + } else + rc = -EINVAL; + + if (rc) + DSI_ERR("Invalid input data: dsi_cmd=0x%x tx_data_type =0x%x for tx_len=0x%x\n", + dsi_cmd, *tx_data_type, (u32)tx_len); + + return rc; +} + +static int dsi_display_dispUtil_prepare(const char *cmd_buf, u32 cmd_buf_len, + struct dsi_cmd_desc *cmd, u8 *payload, u32 payload_len_max, + struct motUtil *motUtil_data) +{ + int rc, i; + u8 tx_data_type; + + memset(cmd, 0x00, sizeof(*cmd)); + + /* + * DISPUTIL_PAYLOAD_LEN_U and SDE_MOT_UTIL_PAYLOAD_LEN_L to + * indicate number of payload in bytes + */ + cmd->msg.tx_len = ((cmd_buf[DISPUTIL_PAYLOAD_LEN_U] << 8) | + (cmd_buf[DISPUTIL_PAYLOAD_LEN_L])); + rc = dsi_display_dispUtil_get_datatype(cmd_buf[DISPUTIL_CMD_TYPE], + cmd_buf[DISPUTIL_MIPI_CMD_TYPE], + cmd->msg.tx_len, &tx_data_type); + if (rc) + return rc; + + cmd->msg.type = tx_data_type; + cmd->msg.channel = 0; + cmd->last_command = 1; + cmd->msg.flags |= MIPI_DSI_MSG_LASTCOMMAND; + + /* DISPUTIL_CMD_TYPE = 1 for DSI write cmd + * DISPUTIL_CMD_TYPE = 0 for DSI read cmd */ + if (cmd_buf[DISPUTIL_CMD_TYPE] == DISPUTIL_DSI_READ) { + cmd->msg.flags |= MIPI_DSI_MSG_READ; /* This is a read DSI */ + cmd->msg.rx_buf = motUtil_data->rd_buf; + /* cmd_buf[3] = number bytes host wants to read */ + cmd->msg.rx_len = cmd_buf[DISPUTIL_NUM_BYTE_RD]; + + motUtil_data->read_cmd = true; + } else + motUtil_data->read_cmd = false; + + /* + * DISPUTIL_CMD_XFER_MODE = 0: DSI cmd transfers at HS mode + * DISPUTIL_CMD_XFER_MODE = 1: DSI cmd transfers at LP mode + */ + if (cmd_buf[DISPUTIL_CMD_XFER_MODE] == DISPUTIL_DSI_LP_MODE) + cmd->msg.flags |= MIPI_DSI_MSG_USE_LPM; + + cmd->msg.ctrl = 0; + cmd->post_wait_ms = 0; + + /* + * msg.tx_len is number of bytes of the payload. + * Checking msg.tx_len with the payload, DISPUTIL_PAYLOAD, + * to make sure they are matched + */ + if ((cmd->msg.tx_len > (cmd_buf_len - DISPUTIL_PAYLOAD)) || + (cmd->msg.tx_len > payload_len_max)) { + DSI_ERR("Incorrect payload length tx_len:%ld, payload_len_max=%d cmd_buf_len=%d\n", + cmd->msg.tx_len, payload_len_max, cmd_buf_len); + return -EINVAL; + } + + for (i = 0; i < cmd->msg.tx_len; i++) + payload[i] = cmd_buf[DISPUTIL_PAYLOAD + i]; + + cmd->msg.tx_buf = payload; + + return 0; +} + + static int dsi_display_ctrl_get_host_init_state(struct dsi_display *dsi_display, bool *state) { @@ -987,6 +1237,51 @@ static void _dsi_display_continuous_clk_ctrl(struct dsi_display *display, } } +int dsi_display_motUtil_transfer(void *display, const char *cmd_buf, + u32 cmd_buf_len, struct motUtil *motUtil_data) +{ + struct dsi_display *dsi_display = display; + struct dsi_cmd_desc cmd; + u8 cmd_payload[MAX_CMD_PAYLOAD_SIZE]; + int rc = 0; + bool state = false; + + if (!dsi_display || !cmd_buf) { + DSI_ERR("[DSI] invalid params\n"); + return -EINVAL; + } + + DSI_INFO("[DSI] Display dispUtil transfer\n"); + + rc = dsi_display_dispUtil_prepare(cmd_buf, cmd_buf_len, + &cmd, cmd_payload, MAX_CMD_PAYLOAD_SIZE, + motUtil_data); + if (rc) { + DSI_ERR("[DSI] command prepare failed. rc %d\n", rc); + return rc; + } + + mutex_lock(&dsi_display->display_lock); + rc = dsi_display_ctrl_get_host_init_state(dsi_display, &state); + if (rc || !state) { + DSI_ERR("[DSI] Invalid host state %d rc %d\n", + state, rc); + rc = -EPERM; + goto end; + } + + /* + * rc will be returned from ops->transfer, which will be 0 or 1 for + * DSI write command. rc will be returned for number of read bytes + * for DSI read commad + */ + rc = dsi_display->host.ops->transfer(&dsi_display->host, + &cmd.msg); +end: + mutex_unlock(&dsi_display->display_lock); + return rc; +} + int dsi_display_soft_reset(void *display) { struct dsi_display *dsi_display; @@ -2940,9 +3235,12 @@ static ssize_t dsi_host_transfer(struct mipi_dsi_host *host, msg->flags & MIPI_DSI_MSG_ASYNC_OVERRIDE) cmd_flags |= DSI_CTRL_CMD_ASYNC_WAIT; + if (msg->rx_buf) + cmd_flags |= DSI_CTRL_CMD_READ; rc = dsi_ctrl_cmd_transfer(display->ctrl[ctrl_idx].ctrl, msg, &cmd_flags); - if (rc) { + if (((cmd_flags & DSI_CTRL_CMD_READ) && rc <= 0) || + (!(cmd_flags & DSI_CTRL_CMD_READ) && rc)) { DSI_ERR("[%s] cmd transfer failed, rc=%d\n", display->name, rc); goto error_disable_cmd_engine; @@ -3762,11 +4060,88 @@ static int dsi_display_parse_dt(struct dsi_display *display) break; } + /* Parse mot extend feature supporting */ + display->is_dsi_mot_ext_enabled = of_property_read_bool(of_node, + "qcom,platform-mot-ext-feature-enable"); + if ( display->is_dsi_mot_ext_enabled ) + display->is_dsi_mot_early_power_enabled = of_property_read_bool(of_node, "qcom,platform-mot-early-power-enable"); + pr_info("is_dsi_mot_ext_enabled %d, is_dsi_mot_early_power_enabled %d\n", display->is_dsi_mot_ext_enabled, display->is_dsi_mot_early_power_enabled); + DSI_DEBUG("success\n"); error: return rc; } +static void dsi_video_te_irq_work(struct work_struct *work) +{ + struct dsi_panel *panel = NULL; + struct delayed_work *dw = to_delayed_work(work); + + panel = container_of(dw, struct dsi_panel, te_event_work); + if (!panel) { + DSI_ERR("%s: invalid panel data\n", __func__); + return; + } + + if(panel->video_te_count%60 == 0) + DSI_INFO("%s video te count = %d\n", __func__, panel->video_te_count); + + enable_irq(panel->video_te_irq); +} + +extern u32 interval_video_te; +static irqreturn_t dsi_video_te_interrupt(int irq, void *data) +{ + struct dsi_panel *panel = data; + + disable_irq_nosync(irq); + + if(panel->video_te_count++ < 3000) { + enable_irq(irq); + } else { + queue_delayed_work(panel->workq, &panel->te_event_work, msecs_to_jiffies(interval_video_te/3)); + //DSI_DEBUG("%s video_te_count=%d\n", __func__, panel->video_te_count); + } + + return IRQ_HANDLED; +} + +static int dsi_init_video_te_irq(struct dsi_panel *panel) +{ + int rc = -1; + + if (gpio_is_valid(panel->video_te_gpio)) { + rc = gpio_request(panel->video_te_gpio, "video-te-gpio"); + if (rc < 0) { + DSI_ERR("%s: video te gpio_request fail rc=%d\n", __func__,rc); + return rc ; + } + + rc = gpio_direction_input(panel->video_te_gpio); + if (rc < 0) { + DSI_ERR("%s: video te gpio_direction_input fail rc=%d\n", __func__ ,rc); + return rc ; + } + + panel->video_te_count_priv = 0; + panel->video_te_count = 3; + panel->video_te_irq = gpio_to_irq(panel->video_te_gpio); + DSI_INFO("%s: irq = %d, gpio=%d\n", __func__, panel->video_te_irq, panel->video_te_gpio); + + rc = request_threaded_irq(panel->video_te_irq, dsi_video_te_interrupt, NULL, + IRQF_TRIGGER_RISING|IRQF_ONESHOT, "video-te-irq", panel); + if (rc < 0) { + DSI_ERR("%s: video te request_irq fail rc=%d\n",__func__, rc); + return rc ; + } + } else { + DSI_ERR("%s: video te irq gpio not provided\n", __func__); + return rc ; + } + + return 0; +} + static int dsi_display_res_init(struct dsi_display *display) { int rc = 0; @@ -3814,6 +4189,9 @@ static int dsi_display_res_init(struct dsi_display *display) display->panel->host_config.phy_type; } + dsi_panel_parse_panel_cfg(display->panel, + !strcmp(display->display_type, "primary")); + rc = dsi_display_parse_lane_map(display); if (rc) { DSI_ERR("Lane map not found, rc=%d\n", rc); @@ -3826,6 +4204,17 @@ static int dsi_display_res_init(struct dsi_display *display) goto error_ctrl_put; } + if(display->panel->esd_config.status_mode == ESD_MODE_PANEL_TE_VIDEO) { + display->panel->workq = create_workqueue("mdss_dsi_video_te"); + if (!(display->panel->workq)) { + DSI_ERR("%s: Error creating video te workqueue\n", __func__); + goto error_ctrl_put; + } + + INIT_DELAYED_WORK(&(display->panel->te_event_work), dsi_video_te_irq_work); + dsi_init_video_te_irq(display->panel); + } + return 0; error_ctrl_put: for (i = i - 1; i >= 0; i--) { @@ -4642,6 +5031,20 @@ static void dsi_display_validate_dms_fps(struct dsi_display_mode *cur_mode, } } +static int dsi_phy_update_mipi_driver_strength(struct dsi_panel *panel, struct msm_dsi_phy *phy) +{ + if((panel == NULL) || (phy == NULL)) + { + return 1; + } + + phy->cfg.dsi_phy_rescode_top = panel->glbl_rescode_top_ctrl; + phy->cfg.dsi_phy_rescode_bot = panel->glbl_rescode_bot_ctrl; + phy->cfg.dsi_phy_hstx_str_ctrl_0 = panel->glbl_hstx_str_ctrl_0; + return 0; + +} + static int dsi_display_set_mode_sub(struct dsi_display *display, struct dsi_display_mode *mode, @@ -4768,6 +5171,7 @@ static int dsi_display_set_mode_sub(struct dsi_display *display, DSI_ERR("failed to add DSI PHY timing params\n"); } } + dsi_phy_update_mipi_driver_strength(display->panel, ctrl->phy); error: return rc; } @@ -5188,6 +5592,7 @@ static int dsi_display_bind(struct device *dev, } DSI_INFO("Successfully bind display panel '%s'\n", display->name); + dsi_display_is_probed(display, 0); display->drm_dev = drm; display_for_each_ctrl(i, display) { @@ -5289,6 +5694,20 @@ static void dsi_display_unbind(struct device *dev, mutex_unlock(&display->display_lock); } +static void dsi_display_dev_shutdown(struct platform_device *pdev) +{ + struct dsi_display *display = NULL; + + if (!pdev) { + DSI_ERR("Invalid device\n"); + } else { + display = platform_get_drvdata(pdev); + display->panel->reset_config.reset_force_pull_low = + display->panel->utils.read_bool(display->panel->utils.data, + "qcom,mdss-dsi-reset-force-pull-low"); + } +} + static const struct component_ops dsi_display_comp_ops = { .bind = dsi_display_bind, .unbind = dsi_display_unbind, @@ -5297,6 +5716,7 @@ static const struct component_ops dsi_display_comp_ops = { static struct platform_driver dsi_display_driver = { .probe = dsi_display_dev_probe, .remove = dsi_display_dev_remove, + .shutdown = dsi_display_dev_shutdown, .driver = { .name = "msm-dsi-display", .of_match_table = dsi_display_dt_match, @@ -5383,11 +5803,15 @@ int dsi_display_dev_probe(struct platform_device *pdev) display->display_type = of_get_property(pdev->dev.of_node, "label", NULL); - if (!display->display_type) + if (!display->display_type) { display->display_type = "primary"; + display->display_idx = 0; + } - if (!strcmp(display->display_type, "secondary")) + if (!strcmp(display->display_type, "secondary")) { index = DSI_SECONDARY; + display->display_idx = 1; + } boot_disp = &boot_displays[index]; node = pdev->dev.of_node; @@ -5442,6 +5866,13 @@ int dsi_display_dev_probe(struct platform_device *pdev) goto end; } + // Initialize mot extend code for early powern and debug tool feature + if ( display->is_dsi_mot_ext_enabled && index == DSI_PRIMARY ) { + dsi_display_ext_init(display); + } + pr_info("dsi_display_dev_probe: display(%p), name: %s, is_dsi_mot_early_power_enabled=%d, is_dsi_mot_ext_enabled=%d\n", + display, (display->name==NULL)?"Null":display->name, display->is_dsi_mot_early_power_enabled, display->is_dsi_mot_ext_enabled); + return 0; end: if (display) @@ -6054,8 +6485,12 @@ int dsi_display_get_info(struct drm_connector *connector, info->is_connected = true; - if (!strcmp(display->display_type, "primary")) + if (!strcmp(display->display_type, "primary")) { info->display_type = SDE_CONNECTOR_PRIMARY; + //Update panel info for Mot early power + if ( display->is_dsi_mot_early_power_enabled ) + display->is_dsi_mot_primary = true; + } else if (!strcmp(display->display_type, "secondary")) info->display_type = SDE_CONNECTOR_SECONDARY; @@ -6069,6 +6504,14 @@ int dsi_display_get_info(struct drm_connector *connector, (display->panel->qsync_caps.qsync_min_fps_list_len > 0) ? true : false; + info->panel_id = display->panel->panel_id; + info->panel_ver = display->panel->panel_ver; + info->panel_regDA = display->panel->panel_regDA; + strncpy(info->panel_name, display->panel->panel_name, + sizeof(display->panel->panel_name)); + strncpy(info->panel_supplier, display->panel->panel_supplier, + sizeof(display->panel->panel_supplier)); + switch (display->panel->panel_mode) { case DSI_OP_VIDEO_MODE: info->curr_panel_mode = MSM_DISPLAY_VIDEO_MODE; @@ -6783,6 +7226,14 @@ int dsi_display_set_mode(struct dsi_display *display, timing.refresh_rate); memcpy(display->panel->cur_mode, &adj_mode, sizeof(adj_mode)); + + //Update early power state + //This interface is called from dsi bridge, not moto early-power. + //It means it is system who want powr on display + //From now on early power on is forbidden + if ( display->is_dsi_mot_early_power_enabled ) + display->dsi_mot_ext.early_power_state = DSI_EARLY_POWER_FORBIDDEN; + error: mutex_unlock(&display->display_lock); return rc; @@ -7144,6 +7595,7 @@ int dsi_display_prepare(struct dsi_display *display) { int rc = 0; struct dsi_display_mode *mode; + struct dsi_display_ctrl *ctrl = &display->ctrl[0]; if (!display) { DSI_ERR("Invalid params\n"); @@ -7155,9 +7607,18 @@ int dsi_display_prepare(struct dsi_display *display) return -EINVAL; } + DSI_INFO("panel_name=%s ctrl-index=%d\n", + display->panel->name, ctrl->ctrl->cell_index); + SDE_EVT32(SDE_EVTLOG_FUNC_ENTRY); mutex_lock(&display->display_lock); + //Check if Mot early power is on going... + if ( display->is_dsi_mot_early_power_enabled && display->is_dsi_display_prepared ) { + pr_info("already prepared\n"); + goto error; + } + mode = display->panel->cur_mode; dsi_display_set_ctrl_esd_check_flag(display, false); @@ -7282,6 +7743,11 @@ int dsi_display_prepare(struct dsi_display *display) } } } + + //Update prepare statue for Mot early power + if ( display->is_dsi_mot_early_power_enabled ) + display->is_dsi_display_prepared = true; + goto error; error_ctrl_link_off: @@ -7297,7 +7763,8 @@ error_ctrl_clk_off: (void)dsi_display_clk_ctrl(display->dsi_clk_handle, DSI_CORE_CLK, DSI_CLK_OFF); error_panel_post_unprep: - (void)dsi_panel_post_unprepare(display->panel); + if (!display->is_cont_splash_enabled) + (void)dsi_panel_post_unprepare(display->panel); error: mutex_unlock(&display->display_lock); SDE_EVT32(SDE_EVTLOG_FUNC_EXIT); @@ -7566,6 +8033,94 @@ int dsi_display_pre_commit(void *display, return rc; } +struct dsi_enable_status { + struct dsi_display *display; + int probed; + bool enable; + char pname[DSI_PANEL_MAX_PANEL_LEN]; +}; + +static struct dsi_enable_status enable_status[2] = { + { + .probed = -ENODEV, + }, + { + .probed = -ENODEV, + }, +}; + +static void dsi_display_is_probed (struct dsi_display *display, + int probe_status) + +{ + int enable_idx; + + if (!display->display_type) + enable_idx = 0; + + if (!strcmp(display->display_type, "secondary")) + enable_idx = 1; + + enable_status[enable_idx].probed = probe_status; + strncpy(enable_status[enable_idx].pname, display->name, + sizeof(enable_status[enable_idx].pname)); + DSI_DEBUG("display->drm_conn[%d] set: probe =%d, name =%s\n", + enable_idx, probe_status, display->name); +} + +static int dsi_display_enable_status (struct dsi_display *display, bool enable) +{ + int ret = 0; + + if (!strncmp("DSI-1", display->drm_conn->name, 5)) { + DSI_DEBUG("display->drm_conn->name=%s enable = %d MAIN_DISPLAY\n", + display->drm_conn->name, enable); + enable_status[0].display = display; + enable_status[0].enable = enable; + } else if (!strncmp("DSI-2", display->drm_conn->name, 5)) { + DSI_DEBUG("display->drm_conn->name=%s enable = %d CLI_DISPLAY\n", + display->drm_conn->name, enable); + enable_status[1].display = display; + enable_status[1].enable = enable; + } else { + DSI_ERR("display->drm_conn: invalid name =%s\n", + display->drm_conn->name); + ret = -EINVAL; + } + + return ret; +} + +bool dsi_display_is_panel_enable (int panel_index, int *probe_status, + char **pname) +{ + struct dsi_display *display; + bool enable = false; + + if (panel_index > 1) { + pr_err("display->drm_conn: invalid panel index =%d\n", + panel_index); + if (probe_status) + *probe_status = -ENODEV; + return false; + } + + if (probe_status) + *probe_status = enable_status[panel_index].probed; + if (pname) + *pname = &enable_status[panel_index].pname[0]; + + display = enable_status[panel_index].display; + if (display) { + mutex_lock(&display->display_lock); + enable = enable_status[panel_index].enable; + mutex_unlock(&display->display_lock); + } + + return enable; +} +EXPORT_SYMBOL(dsi_display_is_panel_enable); + int dsi_display_enable(struct dsi_display *display) { int rc = 0; @@ -7596,6 +8151,7 @@ int dsi_display_enable(struct dsi_display *display) return -EINVAL; } + dsi_display_enable_status(display, 1); display->panel->panel_initialized = true; DSI_DEBUG("cont splash enabled, display enable not required\n"); return 0; @@ -7603,6 +8159,12 @@ int dsi_display_enable(struct dsi_display *display) mutex_lock(&display->display_lock); + //Check if Mot early power is on going + if ( display->is_dsi_mot_early_power_enabled && dsi_panel_initialized(display->panel)) { + pr_info("panel already enabled\n"); + goto error; + } + mode = display->panel->cur_mode; if (mode->dsi_mode_flags & DSI_MODE_FLAG_DMS) { @@ -7620,6 +8182,8 @@ int dsi_display_enable(struct dsi_display *display) display->name, rc); goto error; } + + dsi_display_enable_status(display, true); } /* Block sending pps command if modeset is due to fps difference */ @@ -7632,6 +8196,7 @@ int dsi_display_enable(struct dsi_display *display) display->name, rc); goto error; } + dsi_panel_reset_param(display->panel); } if (mode->dsi_mode_flags & DSI_MODE_FLAG_DMS) { @@ -7755,6 +8320,14 @@ int dsi_display_pre_disable(struct dsi_display *display) DSI_ERR("[%s] panel pre-disable failed, rc=%d\n", display->name, rc); } + + //Update early power state + //Here is called from dsi bridge, not moto early-power. + //It means it is system who want powr off display + //From now on early power on is permitted + if ( display->is_dsi_mot_ext_enabled ) + display->dsi_mot_ext.early_power_state = DSI_EARLY_POWER_IDLE; + goto exit; error_disable_clks: @@ -7778,13 +8351,24 @@ int dsi_display_disable(struct dsi_display *display) return -EINVAL; } + DSI_INFO("%s(%s)+\n", __func__, display->drm_conn->name); SDE_EVT32(SDE_EVTLOG_FUNC_ENTRY); mutex_lock(&display->display_lock); + //Check is Mot early power is on going + pr_info("display %p, name %s is_dsi_mot_primary(%d)\n", display, display->name, display->is_dsi_mot_primary); + if ( display->is_dsi_mot_early_power_enabled && !dsi_panel_initialized(display->panel)) { + mutex_unlock(&display->display_lock); + pr_info("panel already unprepared\n"); + return rc; + } + rc = dsi_display_wake_up(display); if (rc) DSI_ERR("[%s] display wake up failed, rc=%d\n", display->name, rc); + else + dsi_display_enable_status(display, false); if (display->config.panel_mode == DSI_OP_VIDEO_MODE) { rc = dsi_display_vid_engine_disable(display); @@ -7842,6 +8426,13 @@ int dsi_display_unprepare(struct dsi_display *display) SDE_EVT32(SDE_EVTLOG_FUNC_ENTRY); mutex_lock(&display->display_lock); + //Check if Mot early power is on going + if ( display->is_dsi_mot_early_power_enabled && !display->is_dsi_display_prepared) { + mutex_unlock(&display->display_lock); + pr_info("panel already disabled\n"); + return rc; + } + rc = dsi_display_wake_up(display); if (rc) DSI_ERR("[%s] display wake up failed, rc=%d\n", @@ -7909,6 +8500,10 @@ int dsi_display_unprepare(struct dsi_display *display) display->name, rc); } + //Update prepare state for Mot early power + if ( display->is_dsi_mot_early_power_enabled ) + display->is_dsi_display_prepared = false; + mutex_unlock(&display->display_lock); /* Free up DSI ERROR event callback */ diff --git a/techpack/display/msm/dsi/dsi_display.h b/techpack/display/msm/dsi/dsi_display.h index d254dfe739bd..9d7d02ba04d6 100644 --- a/techpack/display/msm/dsi/dsi_display.h +++ b/techpack/display/msm/dsi/dsi_display.h @@ -19,6 +19,9 @@ #include "dsi_ctrl.h" #include "dsi_phy.h" #include "dsi_panel.h" +#include "sde_connector.h" +#include "sde_motUtil.h" +#include "dsi_display_mot_ext.h" #define MAX_DSI_CTRLS_PER_DISPLAY 2 #define DSI_CLIENT_NAME_SIZE 20 @@ -141,6 +144,7 @@ struct dsi_display_ext_bridge { * @ext_conn: Pointer to external connector attached to DSI connector * @name: Name of the display. * @display_type: Display type as defined in device tree. + * @display_idx: Display index (0-primary, 1-secondary) * @list: List pointer. * @is_active: Is display active. * @is_cont_splash_enabled: Is continuous splash enabled @@ -197,6 +201,7 @@ struct dsi_display { const char *name; const char *display_type; + int display_idx; struct list_head list; bool is_cont_splash_enabled; bool sw_te_using_wd; @@ -263,6 +268,12 @@ struct dsi_display { struct work_struct fifo_underflow_work; struct work_struct fifo_overflow_work; struct work_struct lp_rx_timeout_work; + /* used for moto feature early-pane- power to speed up the power on sequence */ + bool is_dsi_mot_ext_enabled; + bool is_dsi_mot_early_power_enabled; + bool is_dsi_display_prepared; + bool is_dsi_mot_primary; + struct dsi_mot_ext_feature dsi_mot_ext; /* firmware panel data */ const struct firmware *fw; @@ -561,6 +572,8 @@ int dsi_post_clkoff_cb(void *priv, enum dsi_clk_type clk_type, enum dsi_lclk_type l_type, enum dsi_clk_state curr_state); +int dsi_display_set_param(void *display, struct msm_param_info *param_info); + /** * dsi_post_clkon_cb() - Callback after clock is turned on * @priv: private data pointer. @@ -656,6 +669,25 @@ int dsi_display_cmd_transfer(struct drm_connector *connector, void *display, const char *cmd_buffer, u32 cmd_buf_len); +/** + * dsi_display_motUtil_transfer() - Convert motUtil data and transfer command + * to the panel + * @display: Handle to display. + * @cmd_buf: Command buffer + * @cmd_buf_len: Command buffer length in bytes + * @motUtil_data: motUtil data information + */ +int dsi_display_motUtil_transfer(void *display, const char *cmd_buf, + u32 cmd_buf_len, struct motUtil *motUtil_data); + + +/** + * dsi_display_force_esd_disable() - check if ESD UTAG is forced to disable ESD + * @display: Handle to display. + */ +bool dsi_display_force_esd_disable(void *display); + + /** * dsi_display_soft_reset() - perform a soft reset on DSI controller * @display: Handle to display @@ -718,6 +750,20 @@ enum dsi_pixel_format dsi_display_get_dst_format( struct drm_connector *connector, void *display); +/* + * dsi_display_cmd_mipi_transfer() - Sending the MIPI DSI command + * @display: Handle to display + * @msg: MIPI DSI command information + * @flags: Modifier flags + * Return: if this is a MIPI DSI read then it will return number read byte + * in success case. Otherwise will be "<=0" + * if this is a MIPI DSI write then it will return "0" for success + * + */ +int dsi_display_cmd_mipi_transfer(struct dsi_display *display, + struct mipi_dsi_msg *msg, + u32 flags); + /** * dsi_display_cont_splash_config() - initialize splash resources * @display: Handle to display diff --git a/techpack/display/msm/dsi/dsi_display_mot_ext.c b/techpack/display/msm/dsi/dsi_display_mot_ext.c new file mode 100644 index 000000000000..48201c4413cd --- /dev/null +++ b/techpack/display/msm/dsi/dsi_display_mot_ext.c @@ -0,0 +1,1240 @@ +/* + * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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) "msm-dsi-display:[%s] " fmt, __func__ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "msm_drv.h" +#include "sde_connector.h" +#include "msm_mmu.h" +#include "dsi_display.h" +#include "dsi_panel.h" +#include "dsi_ctrl.h" +#include "dsi_ctrl_hw.h" +#include "dsi_drm.h" +#include "dsi_clk.h" +#include "dsi_pwr.h" +#include "sde_dbg.h" +#include "dsi_parser.h" +#include "dsi_display_mot_ext.h" + +static struct dsi_mot_ext_feature *g_dsi_mot_ext = NULL; +static int g_early_power_count = 0; +//Timer for pressure test and debug +static static struct alarm *g_wakeup_timer = NULL; +static int g_wakeup_timer_interval = 0; +static int g_early_power_test_en = 0; + +extern const char *cmd_set_prop_map[DSI_CMD_SET_MAX]; + +static void dsi_display_early_power_on_work(struct work_struct *work) +{ + struct dsi_display *display = NULL; + struct dsi_mot_ext_feature *mot_ext = NULL; + int rc = 0; + + pr_info("+++++ early_power_state: %d\n", (g_dsi_mot_ext!=NULL)?g_dsi_mot_ext->early_power_state:-1); + mot_ext = container_of(work, struct dsi_mot_ext_feature, early_on_work); + if (!mot_ext) { + pr_warning("failed to get mot_ext\n"); + return; + } + __pm_wakeup_event(mot_ext->early_wake_src, 900); + display = mot_ext->display; + if (!display || !display->panel || !display->is_dsi_mot_primary || + (display->panel->panel_mode != DSI_OP_VIDEO_MODE) || + atomic_read(&display->panel->esd_recovery_pending)) { + pr_warning("----- Invalid recovery use case, early_power_state: %d\n", mot_ext->early_power_state); + __pm_relax(mot_ext->early_wake_src); + return; + } + + mutex_lock(&display->display_lock); + if (mot_ext->early_power_state == DSI_EARLY_POWER_FORBIDDEN || display->is_dsi_display_prepared) { + pr_warning("----- already prepared or set_mode called, early_power_state: %d, is_dsi_display_prepared %d\n", + mot_ext->early_power_state, display->is_dsi_display_prepared); + mutex_unlock(&display->display_lock); + __pm_relax(mot_ext->early_wake_src); + return; + } + mot_ext->early_power_state = DSI_EARLY_POWER_BEGIN; + mutex_unlock(&display->display_lock); + + rc = dsi_display_prepare(display); + if (rc) { + pr_err("----- DSI display prepare failed, rc=%d. early_power_state %d\n", rc, mot_ext->early_power_state); + __pm_relax(mot_ext->early_wake_src); + return; + } + + mutex_lock(&display->display_lock); + if (mot_ext->early_power_state == DSI_EARLY_POWER_FORBIDDEN || display->panel->panel_initialized) { + mutex_unlock(&display->display_lock); + pr_warning("----- already enabled or set_mode called, early_power_state: %d, panel_initialized %d\n", + mot_ext->early_power_state, display->panel->panel_initialized); + __pm_relax(mot_ext->early_wake_src); + return; + } + mot_ext->early_power_state = DSI_EARLY_POWER_PREPARED; + mutex_unlock(&display->display_lock); + + rc = dsi_display_enable(display); + if (rc) { + pr_err("DSI display enable failed, rc=%d. early_power_state %d\n", rc, mot_ext->early_power_state); + (void)dsi_display_unprepare(display); + return; + } + + mutex_lock(&display->display_lock); + if (mot_ext->early_power_state != DSI_EARLY_POWER_FORBIDDEN) { + mot_ext->early_power_state = DSI_EARLY_POWER_INITIALIZED; + queue_delayed_work(mot_ext->early_power_workq, &mot_ext->early_off_work, HZ/2); + } + mutex_unlock(&display->display_lock); + + //Do not relax wake_lock here to avoid AP enter deep suspend before early_off called, which may cause kernel panic. + //__pm_relax(mot_ext->early_wake_src); + + pr_info("----- early_power_state: %d, g_early_power_count %d\n", mot_ext->early_power_state, g_early_power_count++); +} + +static void dsi_display_early_power_off_work(struct work_struct *work) +{ + struct dsi_display *display = NULL; + struct dsi_mot_ext_feature *mot_ext = NULL; + int rc = 0; + struct delayed_work *dw = to_delayed_work(work); + + pr_info("+++++ early_power_state: %d\n", (g_dsi_mot_ext!=NULL)?g_dsi_mot_ext->early_power_state:-1); + mot_ext = container_of(dw, struct dsi_mot_ext_feature, early_off_work); + if (!mot_ext) { + pr_warning("failed to get mot_ext\n"); + return; + } + __pm_wakeup_event(mot_ext->early_wake_src, 500); + display = mot_ext->display; + if (!display || !display->panel || + (display->panel->panel_mode != DSI_OP_VIDEO_MODE) || + atomic_read(&display->panel->esd_recovery_pending)) { + pr_warning("Invalid recovery use case, early_power_state: %d\n", mot_ext->early_power_state); + __pm_relax(mot_ext->early_wake_src); + return; + } + if (mot_ext->early_power_state != DSI_EARLY_POWER_INITIALIZED) { + pr_info("%s ----- no need, return. early_power_state %d\n", __func__, mot_ext->early_power_state); + //mot_ext->early_power_state = DSI_EARLY_POWER_IDLE; + __pm_relax(mot_ext->early_wake_src); + return; + } + + rc = dsi_display_disable(display); + if (rc) { + pr_err("DSI display disable failed, rc=%d. early_power_state %d\n", rc, mot_ext->early_power_state); + __pm_relax(mot_ext->early_wake_src); + return; + } + + mutex_lock(&display->display_lock); + if (mot_ext->early_power_state == DSI_EARLY_POWER_FORBIDDEN) { + pr_warning("----- set_mode called, early_power_state: %d, is_dsi_display_prepared %d\n", + mot_ext->early_power_state, display->is_dsi_display_prepared); + mutex_unlock(&display->display_lock); + __pm_relax(mot_ext->early_wake_src); + return; + } + mutex_unlock(&display->display_lock); + + rc = dsi_display_unprepare(display); + if (rc) { + pr_err("DSI display unprepare failed, rc=%d. early_power_state %d\n", rc, mot_ext->early_power_state); + __pm_relax(mot_ext->early_wake_src); + return; + } + + mutex_lock(&display->display_lock); + if (mot_ext->early_power_state != DSI_EARLY_POWER_FORBIDDEN) { + g_dsi_mot_ext->early_power_state = DSI_EARLY_POWER_IDLE; + } + mutex_unlock(&display->display_lock); + + __pm_relax(mot_ext->early_wake_src); + + pr_info("----- early_power_state: %d\n", mot_ext->early_power_state); +} + +void ext_dsi_display_early_power_on(void) +{ + // If previous work has not finished, skip + if (g_dsi_mot_ext == NULL || g_dsi_mot_ext->early_power_state == DSI_EARLY_POWER_FORBIDDEN) { + //pr_info("==== g_dsi_mot_ext %p, early_power_state: %d\n", + // g_dsi_mot_ext, g_dsi_mot_ext->early_power_state); + return; + } + if (g_dsi_mot_ext->early_power_state != DSI_EARLY_POWER_IDLE) { + //pr_info("==== early_power_state: %d is not IDLE\n", + // g_dsi_mot_ext->early_power_state); + return; + } + + //pr_info("#### display(%p), early_power_state: %d\n", g_dsi_mot_ext->display, g_dsi_mot_ext->early_power_state); + queue_work(g_dsi_mot_ext->early_power_workq, &g_dsi_mot_ext->early_on_work); +} +EXPORT_SYMBOL(ext_dsi_display_early_power_on); + +static enum alarmtimer_restart dsi_display_wakeup_timer_func(struct alarm *alarm, ktime_t now) +{ + ktime_t start, add; + int randomTime = 0; + pr_info("g_wakeup_timer_interval %d, g_early_power_test_en %d\n", g_wakeup_timer_interval, g_early_power_test_en); + if (g_early_power_test_en) + ext_dsi_display_early_power_on(); + + if (g_wakeup_timer_interval != 0) { + //srand(time(NULL)); + start = ktime_get_boottime(); + randomTime = prandom_u32_max(g_wakeup_timer_interval) + g_wakeup_timer_interval; + add = ktime_set(randomTime, 0); + alarm_start(g_wakeup_timer, ktime_add(start, add)); + pr_info("%s: randomTimer %d seconds set\n", __func__, randomTime); + } + + return ALARMTIMER_NORESTART; +} + +static int my_atoi(const char *src) +{ + int s = 0; + bool isMinus = false; + + while(*src == ' ') + { + src++; + } + + if(*src == '+' || *src == '-') + { + if(*src == '-') + { + isMinus = true; + } + src++; + } + else if(*src < '0' || *src > '9') + { + s = 2147483647; + return s; + } + + while(*src != '\0' && *src >= '0' && *src <= '9') + { + s = s * 10 + *src - '0'; + src++; + } + return s * (isMinus ? -1 : 1); +} + +static u32 atoh(const unsigned char *in) +{ + u32 sum = 0; + unsigned int mult = 1; + unsigned char c; + unsigned int len = strlen(in); + + while (len) { + int value; + + c = in[len - 1]; + value = hex_to_bin(c); + if (value >= 0) + sum += mult * value; + mult *= 16; + --len; + } + return sum; +} + +static char *strtok(char *s, const char * sep) +{ + static char *p; + if (!s && !(s = p)) return NULL; + s += strspn(s, sep); + if (!*s) return p = 0; + p = s + strcspn(s, sep); + if (*p) *p++ = 0; + else p = 0; + return s; +} + +static bool isNumStr(char *s) +{ + int i; + int len; + char* pbuf = s; + + len = strlen(pbuf); + while(i < len) { + if(!((pbuf[i]>= '0' && pbuf[i] <= '9' ) || (pbuf[i] >= 'a' && pbuf[i] <= 'f' ) || (pbuf[i] >= 'A' && pbuf[i] <= 'F' ))) + return false; + i++; + } + return true; +} + +static char* dsi_panel_line_remove_front_space(char* pinstr) +{ + char* pbuf = NULL; + int i = 0; + int len = strlen(pinstr); + pbuf = pinstr; + while(i < len) { + if( pbuf[i] != 0x20 && pbuf[i] != 0x09 ) //skip space and horizontal tab + break; + i++; + } + //pr_info("strlen %d\n", len); + return pbuf + i; +} + +static int dsi_panel_get_cmd_pkt_count(const char *data, u32 length, u32 *cnt) +{ + const u32 cmd_set_min_size = 7; + u32 count = 0; + u32 packet_length; + u32 tmp; + char* pbuf = NULL; + char tembuf[8]; + int i; + + pbuf = kmalloc(PAGE_SIZE, GFP_KERNEL); + while (length >= cmd_set_min_size) { + packet_length = cmd_set_min_size; + tmp = ((data[5] << 8) | (data[6])); + packet_length += tmp; + if (packet_length > length) { + pr_err("format error\n"); + return -EINVAL; + } +#if 1 //print data + if (pbuf) { + snprintf(pbuf, PAGE_SIZE, "packet_data: %2x %2x %2x %2x %2x %2x %2x ", + data[0], data[1], data[2], data[3], data[4], data[5], data[6]); + } + for (i=0; icount; i++) { + cmd = &set->cmds[i]; + kfree(cmd->msg.tx_buf); + } +} + +static void dsi_panel_dealloc_cmd_packets(struct dsi_panel_cmd_set *set) +{ + kfree(set->cmds); +} + +static int dsi_panel_create_cmd_packets(const char *data, + u32 length, + u32 count, + struct dsi_cmd_desc *cmd) +{ + int rc = 0; + int i, j; + u8 *payload; + + for (i = 0; i < count; i++) { + u32 size; + + cmd[i].msg.type = data[0]; + cmd[i].last_command = (data[1] == 1 ? true : false); + cmd[i].msg.channel = data[2]; + cmd[i].msg.flags |= (data[3] == 1 ? MIPI_DSI_MSG_REQ_ACK : 0); + cmd[i].msg.ctrl = 0; + cmd[i].post_wait_ms = cmd[i].msg.wait_ms = data[4]; + cmd[i].msg.tx_len = ((data[5] << 8) | (data[6])); + + size = cmd[i].msg.tx_len * sizeof(u8); + + payload = kzalloc(size, GFP_KERNEL); + if (!payload) { + rc = -ENOMEM; + goto error_free_payloads; + } + + for (j = 0; j < cmd[i].msg.tx_len; j++) + payload[j] = data[7 + j]; + + cmd[i].msg.tx_buf = payload; + data += (7 + cmd[i].msg.tx_len); + } + + return rc; +error_free_payloads: + for (i = i - 1; i >= 0; i--) { + cmd--; + kfree(cmd->msg.tx_buf); + } + + return rc; +} + +static int dsi_panel_alloc_cmd_packets(struct dsi_panel_cmd_set *cmd, + u32 packet_count) +{ + u32 size; + + size = packet_count * sizeof(*cmd->cmds); + cmd->cmds = kzalloc(size, GFP_KERNEL); + if (!cmd->cmds) + return -ENOMEM; + + cmd->count = packet_count; + return 0; +} + +static bool dsi_panel_mot_parse_commands(char* str, u32* length, struct dsi_display_mode_priv_info *priv_info, enum dsi_cmd_set_type type, char* pcur) +{ + char * pos = NULL; + char * posStart = NULL; + char * posEnd = NULL; + char *buf = NULL; //dont change this point after alloc, else kfree will error and kernel panic + char *pbuf = NULL; + char *pLine = NULL; + char *pLineEnd = NULL; + char *token = NULL; + char numstr[8]; + uint len = 0; + uint cmdlen = 0; + u8* data = NULL; + uint index = 0; + char delim[] = " \t"; //space and horizontal tab + + //pr_info("input str : %s\n", str); + *length = 0; + pos = strstr(str, "="); + if (pos) { + posStart = strstr(pos, "["); + if (posStart) { + posStart ++; + posEnd = strstr(posStart, "]"); + if (posEnd) { + pos = strstr(posEnd, "\n"); + if (pos) + posEnd = pos; + *length = posEnd - pcur + 1; + } + if (posEnd) { + pr_info("posStart address(%llx), val: posStart[0][1][2]=[%2x][%2x][%2x], posEnd address(%llx), val: posEnd[0][1][2]=[%2x][%2x][%2x]\n", + posStart, posStart[0], posStart[1], posStart[2], posEnd, posEnd[0], posEnd[1], posEnd[2]); + len = posEnd - posStart - 1; + //*length = len - 1; + buf = kmalloc(len+5, GFP_KERNEL); //data in this buffer can be modified, if there is "\n", we also copy it to buf + if (!buf) { + pr_warn("kmalloc failed for command buf\n"); + goto error_exit; + } + pbuf = buf; + memset(pbuf, 0, len+5); + //strncpy(pbuf, posStart, len); + memcpy(pbuf, posStart, len); + //copy_from_user(pbuf, posStart, len); + //pbuf[len] = '\0'; + data = (u8*)pbuf; + posEnd = pbuf + len; + memset(numstr, 0, 8); + pr_info("bufStart address(%llx), val: bufStart[0][1][2]=[%2x][%2x][%2x], bufEnd address(%llx), val: bufEnd[0][1][2]=[%2x][%2x][%2x]\n", + pbuf, pbuf[0], pbuf[1], pbuf[2], posEnd, posEnd[0], posEnd[1], posEnd[2]); + pr_info("command str : len %d\n", len); + pLine = pbuf; + pLineEnd = posEnd; + token = pLine; + + while (pbuf < posEnd) { + + pLineEnd = strstr(pLine, "\n"); + if (pLineEnd) { + pLineEnd--; //If this is a '\n' only line, ptr need -- + } else + pLineEnd = posEnd; + + pLineEnd[0] = '\0'; //add terminal flag and remove '\n' in the str + pLineEnd[1] = '\0'; + token = strstr(pLine, "]"); + if (!token) { + token = strstr(pLine, "//"); + if (!token) + token = strstr(pLine, "/*"); + } + if (token) { + pr_info("there is comment '/*' in pLine address: %llx, token[0]=%c, pLine str: %s\n", pLine, token[0], pLine); + token[0] = '\0'; + } else { + token = pLineEnd; + } + //pr_info("#pre pLine address: %llx, pLine[0]=%x, pLine str: %s\n", pLine, pLine[0], pLine); + pLine = dsi_panel_line_remove_front_space(pLine); + pr_info("=pst pLine address: %llx, pLine[0]=%x, pLine str: %s\n", pLine, pLine[0], pLine); + //pr_info("token address: %llx, token[0][1][2]=[%2x][%2x][%2x]\n", token, token[0], token[1], token[2]); + //pr_info("pLineEnd address: %llx, pLineEnd[0][1][2]=[%2x][%2x][%2x]\n", pLineEnd, pLineEnd[0], pLineEnd[1], pLineEnd[2]); + pLineEnd += 2; + if (pLine != token) { + for(token = strtok(pLine, delim); token != NULL; token = strtok(NULL, delim)) { + if (isNumStr(token)) { + data[index++] = atoh(token); + //pr_info("find num str: %s -> %2x\n", token, data[index-1]); + } + } + } else + pr_info("nothing line: %llx, pLine str: %s\n", pLine, pLine); + + pbuf = pLineEnd ; + pLine = pLineEnd; + + } + + dsi_panel_get_cmd_pkt_count(data, index, &cmdlen); + pr_info("got command packet num : %d\n", cmdlen); + if (type < DSI_CMD_SET_MAX) { + dsi_panel_destroy_cmd_packets(&priv_info->cmd_sets[type]); + dsi_panel_dealloc_cmd_packets(&priv_info->cmd_sets[type]); + dsi_panel_alloc_cmd_packets(&priv_info->cmd_sets[type], cmdlen); + dsi_panel_create_cmd_packets(data, 0, cmdlen, priv_info->cmd_sets[type].cmds); + } + + } else { + pr_err(" ] not found in this line!\n"); + goto error_exit; + } + } else { + pr_err(" [ not found in this line\n"); + goto error_exit; + } + } else { + pr_err(" = not found in this line!\n"); + goto error_exit; + } + +error_exit: + if (buf) + kfree(buf); + //pr_info("need skip length : %d\n", *length); + return true; +} + +static bool dsi_panel_mot_parse_u32(char* str, const char* keystr, u32* val) +{ + char * pos = NULL; + char * posStart = NULL; + char * posEnd = NULL; + char* pContent = NULL; + char buf[64]; + uint len = 0; + u32 keylen; + + keylen = strlen(keystr); + if ( strncmp(str, keystr, keylen) ) + return false; + + pContent = str + keylen; + posStart = strstr(pContent, "="); + if (posStart) { + posStart = strstr(posStart, "<"); + if (posStart) { + posStart ++; + posEnd = strstr(posStart, ">"); + if (posEnd) { + pos = strstr(posStart, " "); + while(pos != NULL) { + if (pos == posStart) + posStart++; + else if (pos > posStart) { + posEnd = pos; + break; + } + pos = strstr(posStart, " "); + } + len = posEnd - posStart; + strncpy(buf, posStart, len); + buf[len] = 0; + //pr_info("val str : %s\n", buf); + *val = my_atoi(buf); + } else { + pr_err(" > not found in this line!\n"); + goto error_exit; + } + } else { + pr_err(" < not found in this line, please try find [\n"); + goto error_exit; + } + } else { + pr_err(" = not found in this line!\n"); + goto error_exit; + } + return true; + +error_exit: + return false; +} + +void dsi_panel_mot_parse_timing_from_file(struct dsi_display *display, int index) +{ + int rc = 0; + u32 val = 0; + u32 keylen, llen, cmdstrlen; + struct dsi_display_mode *display_mode; + struct dsi_display_mode_priv_info *priv_info; + char keystr[128]; + char * fwbuf = NULL; + char * pbuf = NULL; + char * pline = NULL; + char * plinebuf = NULL; + char * plineEnd = NULL; + const struct firmware *fw = NULL; + struct dsi_mode_info *mode_timing = NULL ; + + if (!display) { + pr_warn("display is null\n"); + return; + } + + mode_timing = &display->modes[index].timing; + pr_info(", index: %d !\n", index); + pr_info("panel horz active:%d front_portch:%d back_porch:%d pulse_width:%d h_skew:%d\n", + mode_timing->h_active, mode_timing->h_front_porch, mode_timing->h_back_porch, mode_timing->h_sync_width, mode_timing->h_skew); + pr_info("panel vert active:%d front_portch:%d back_porch:%d pulse_width:%d\n", + mode_timing->v_active, mode_timing->v_front_porch, mode_timing->v_back_porch, mode_timing->v_sync_width); + + display_mode = &display->modes[index];//container_of(mode_timing, struct dsi_display_mode, timing); + priv_info = display->modes[index].priv_info; + pr_info("panel clk rate:%d mdp_transfer_time_us:%d refresh_rate:%d\n", + display_mode->priv_info->clk_rate_hz , display_mode->priv_info->mdp_transfer_time_us, mode_timing->refresh_rate); + + rc = request_firmware(&fw, "lcd-paras.txt", &display->pdev->dev);// + if (rc < 0) { + dev_warn_once(&display->pdev->dev, "Request firmware failed - /sdcard/lcd-paras.txt (%d)\n", rc); + return; + } + pr_info("found LCD para debug file, length: %d !\n", fw->size); + + if (fw->size < 8 || fw->size > 65536) { + dev_warn_once(&display->pdev->dev, "Invalid firmware size (%zu)\n", fw->size); + } + + fwbuf = kmalloc(fw->size+1, GFP_KERNEL); + if (!fwbuf) { + pr_warn("kmalloc failed for fwbuf\n"); + return; + } + memset(fwbuf, 0, fw->size + 1); + memcpy(fwbuf, fw->data, fw->size); + pbuf = fwbuf; + + plinebuf = kmalloc(LCD_PARA_LINE_LEN, GFP_KERNEL); + memset(plinebuf, 0, LCD_PARA_LINE_LEN); + if (!plinebuf) { + pr_warn("kmalloc failed for pline\n"); + goto timing_err_exit; + return; + } + + plineEnd = strstr(pbuf, "\n"); + if (plineEnd) { + llen = plineEnd - pbuf; + if (llen < LCD_PARA_LINE_LEN) { + //strncpy(plinebuf, pbuf, llen); + memcpy(plinebuf, pbuf, llen); + plinebuf[llen] = '\0'; + pr_info("First line pre: pbuf address(%llx), len%d, plinebuf[0]=%2x, plinebuf str: %s\n", pbuf, llen, plinebuf[0], plinebuf); + pline = dsi_panel_line_remove_front_space(plinebuf); + if (pline >= plinebuf) + llen -= (pline - plinebuf); + else { + pr_warn("error line a\n"); + goto timing_err_exit; + } + pr_info("First line pst: pbuf address(%llx), len%d, pline[0]=%2x, pline str: %s\n", pbuf, llen, pline[0], pline); + pbuf = plineEnd + 1; + } else { + pr_warn("too large line a\n"); + goto timing_err_exit; + } + } else { + pr_warn("found no line\n"); + goto timing_err_exit; + } + + while (pbuf < (fwbuf + fw->size)) { + + if ( strncmp(pline, "//", 2) == 0) { + //go relaign pbuf + goto goContinue; + } + if ( strncmp(pline, "/*", 2) == 0) { + //go relaign pbuf + goto goContinue; + } + + if (dsi_panel_mot_parse_u32(pline, "qcom,mdss-dsi-panel-clockrate", &val)) { + pr_info("got qcom,mdss-dsi-panel-clockrate: %d\n", val); + mode_timing->clk_rate_hz = val; + display_mode->priv_info->clk_rate_hz = mode_timing->clk_rate_hz; + } + + if (dsi_panel_mot_parse_u32(pline, "qcom,mdss-mdp-transfer-time-us", &val)) { + pr_info("got qcom,mdss-mdp-transfer-time-us: %d\n", val); + mode_timing->mdp_transfer_time_us = val; + display_mode->priv_info->mdp_transfer_time_us = mode_timing->mdp_transfer_time_us; + } + + if (dsi_panel_mot_parse_u32(pline, "qcom,mdss-dsi-panel-framerate", &val)) { + pr_info("got qcom,mdss-dsi-panel-framerate: %d\n", val); + mode_timing->refresh_rate = val; + } + + if (dsi_panel_mot_parse_u32(pline, "qcom,mdss-dsi-panel-width", &val)) { + pr_info("got qcom,mdss-dsi-panel-width: %d\n", val); + mode_timing->h_active = val; + } + + if (dsi_panel_mot_parse_u32(pline, "qcom,mdss-dsi-h-front-porch", &val)) { + pr_info("got qcom,mdss-dsi-h-front-porch: %d\n", val); + mode_timing->h_front_porch = val; + } + + if (dsi_panel_mot_parse_u32(pline, "qcom,mdss-dsi-h-back-porch", &val)) { + pr_info("got qcom,mdss-dsi-h-back-porch: %d\n", val); + mode_timing->h_back_porch = val; + } + + if (dsi_panel_mot_parse_u32(pline, "qcom,mdss-dsi-h-pulse-width", &val)) { + pr_info("got qcom,mdss-dsi-h-pulse-width: %d\n", val); + mode_timing->h_sync_width = val; + } + + if (dsi_panel_mot_parse_u32(pline, "qcom,mdss-dsi-h-sync-skew", &val)) { + pr_info("got qcom,mdss-dsi-h-sync-skew: %d\n", val); + mode_timing->h_skew = val; + } + + if (dsi_panel_mot_parse_u32(pline, "qcom,mdss-dsi-panel-height", &val)) { + pr_info("got qcom,mdss-dsi-panel-height: %d\n", val); + mode_timing->v_active = val; + } + + if (dsi_panel_mot_parse_u32(pline, "qcom,mdss-dsi-v-back-porch", &val)) { + pr_info("got qcom,mdss-dsi-v-back-porch: %d\n", val); + mode_timing->v_back_porch = val; + } + + if (dsi_panel_mot_parse_u32(pline, "qcom,mdss-dsi-v-front-porch", &val)) { + pr_info("got qcom,mdss-dsi-v-front-porch: %d\n", val); + mode_timing->v_front_porch = val; + } + + if (dsi_panel_mot_parse_u32(pline, "qcom,mdss-dsi-v-pulse-width", &val)) { + pr_info("got qcom,mdss-dsi-v-pulse-width: %d\n", val); + mode_timing->v_sync_width = val; + } + + if (dsi_panel_mot_parse_u32(pline, "qcom,mdss-dsi-t-clk-post", &val)) { + pr_info("got qcom,mdss-dsi-t-clk-post: %d\n", val); + display->panel->host_config.t_clk_post = val; + } + + if (dsi_panel_mot_parse_u32(pline, "qcom,mdss-dsi-t-clk-pre", &val)) { + pr_info("got qcom,mdss-dsi-t-clk-pre: %d\n", val); + display->panel->host_config.t_clk_pre = val; + } + + if (dsi_panel_mot_parse_u32(pline, "qcom,mdss-pan-physical-width-dimension", &val)) { + pr_info("got qcom,mdss-pan-physical-width-dimension: %d\n", val); + display->panel->phy_props.panel_width_mm = val; + } + + if (dsi_panel_mot_parse_u32(pline, "qcom,mdss-pan-physical-height-dimension", &val)) { + pr_info("got qcom,mdss-pan-physical-height-dimension: %d\n", val); + display->panel->phy_props.panel_height_mm = val; + } + + if (dsi_panel_mot_parse_u32(pline, "qcom,mdss-dsc-version", &val)) { + pr_info("got qcom,mdss-dsc-version: %d\n", val); + display_mode->priv_info->dsc.version = val & 0xff; + } + + if (dsi_panel_mot_parse_u32(pline, "qcom,mdss-dsc-scr-version", &val)) { + pr_info("got qcom,mdss-dsc-scr-version: %d\n", val); + display_mode->priv_info->dsc.scr_rev = val & 0xff; + } + + if (dsi_panel_mot_parse_u32(pline, "qcom,mdss-dsc-slice-height", &val)) { + pr_info("got qcom,mdss-dsc-slice-height: %d\n", val); + display_mode->priv_info->dsc.slice_height = val; + } + + if (dsi_panel_mot_parse_u32(pline, "qcom,mdss-dsc-slice-width", &val)) { + pr_info("got qcom,mdss-dsc-slice-width: %d\n", val); + display_mode->priv_info->dsc.slice_width = val; + if (display_mode->timing.h_active % priv_info->dsc.slice_width) { + pr_err("invalid slice width for the intf width:%d slice width:%d\n", + display_mode->timing.h_active, display_mode->priv_info->dsc.slice_width); + } + display_mode->priv_info->dsc.pic_width = display_mode->timing.h_active; + display_mode->priv_info->dsc.pic_height = display_mode->timing.v_active; + } + + if (dsi_panel_mot_parse_u32(pline, "qcom,mdss-dsc-slice-per-pkt", &val)) { + pr_info("got qcom,mdss-dsc-slice-per-pkt: %d\n", val); + display_mode->priv_info->dsc.slice_per_pkt = val; + } + + if (dsi_panel_mot_parse_u32(pline, "qcom,mdss-dsc-bit-per-component", &val)) { + pr_info("got qcom,mdss-dsc-bit-per-component: %d\n", val); + display_mode->priv_info->dsc.bpc = val; + } + + if (dsi_panel_mot_parse_u32(pline, "qcom,mdss-dsc-bit-per-pixel", &val)) { + pr_info("got qcom,mdss-dsc-bit-per-pixel: %d\n", val); + display_mode->priv_info->dsc.bpp = val; + } + + if (dsi_panel_mot_parse_u32(pline, "qcom,mdss-pps-delay-ms", &val)) { + pr_info("got qcom,mdss-pps-delay-ms: %d\n", val); + display_mode->priv_info->dsc.pps_delay_ms = val; + } + + // qcom,mdss-dsi-on-command + memset(keystr, 0, 128); + strcpy(keystr, "qcom,mdss-dsi-on-command"); + keylen = strlen(keystr); + if ( strncmp(pline, keystr, keylen) == 0) { + if (dsi_panel_mot_parse_commands(pbuf - llen + keylen -1, &cmdstrlen, priv_info, DSI_CMD_SET_ON, pbuf)) { + pr_info("got %s: str lenght: %d\n", keystr, cmdstrlen); + pbuf += cmdstrlen; + } + } + + // qcom,mdss-dsi-off-command + keylen = strlen("qcom,mdss-dsi-off-command"); + if ( strncmp(pline, "qcom,mdss-dsi-off-command", keylen) == 0) { + if (dsi_panel_mot_parse_commands(pbuf - llen + keylen -1, &cmdstrlen, priv_info, DSI_CMD_SET_OFF, pbuf)) { + pr_info("got qcom,mdss-dsi-off-command: lenght: %d\n", cmdstrlen); + pbuf += cmdstrlen; + } + } + + // qcom,mdss-dsi-timing-switch-command + keylen = strlen("qcom,mdss-dsi-timing-switch-command"); + if ( strncmp(pline, "qcom,mdss-dsi-timing-switch-command", keylen) == 0) { + if (dsi_panel_mot_parse_commands(pbuf - llen + keylen -1, &cmdstrlen, priv_info, DSI_CMD_SET_TIMING_SWITCH, pbuf)) { + pr_info("got qcom,mdss-dsi-timing-switch-command: lenght: %d\n", cmdstrlen); + pbuf += cmdstrlen; + } + } + +goContinue: + memset(plinebuf, 0, LCD_PARA_LINE_LEN); + plineEnd = strstr(pbuf, "\n"); + if (plineEnd) { + llen = plineEnd - pbuf; + if (llen < LCD_PARA_LINE_LEN) { + //strncpy(plinebuf, pbuf, llen); + memcpy(plinebuf, pbuf, llen); + plinebuf[llen] = '\0'; + //pr_info("New line pre: pbuf address(%llx), len%d, plinebuf[0]=%2x, plinebuf str: %s\n", pbuf, llen, plinebuf[0], plinebuf); + pline = dsi_panel_line_remove_front_space(plinebuf); + if (pline >= plinebuf) + llen -= (pline - plinebuf); + else { + pr_warn("error line b\n"); + goto timing_err_exit; + } + if (llen > 7) + pr_info("New line pst: pbuf(%llx), pbuf[0][1][2][3][4][5][6][7]=[%2x][%2x][%2x][%2x][%2x][%2x][%2x][%2x], len %d, pline[0]=%2x, pline str: %s\n", + pbuf, pbuf[0], pbuf[1], pbuf[2], pbuf[3], pbuf[4], pbuf[5], pbuf[6], pbuf[7], llen, pline[0], pline); + pbuf = plineEnd + 1; + } else { + pr_warn("too large line b\n"); + goto timing_err_exit; + } + } else { + llen = fwbuf + fw->size - pbuf; + memcpy(plinebuf, pbuf, llen); + plinebuf[llen] = '\0'; + pr_info("End line: pbuf(%llx): len%d: %s\n", pbuf, llen, plinebuf); + break; + } + + } + +timing_err_exit: + if (plinebuf) + kfree(plinebuf); + if (fwbuf) + kfree(fwbuf); + release_firmware(fw); + return; +} + +static ssize_t dsi_display_early_power_read(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int rc = 0; + struct dsi_display *display; + + display = dev_get_drvdata(dev); + if (!display) { + pr_err("Invalid display\n"); + return -EINVAL; + } + + rc = snprintf(buf, PAGE_SIZE, "%d\n", display->dsi_mot_ext.early_power_state); + pr_info("%s: early_power_state %d\n", __func__, + display->dsi_mot_ext.early_power_state); + + return rc; +} + +static ssize_t dsi_display_early_power_write(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct dsi_display *display; + + display = dev_get_drvdata(dev); + if (!display) { + pr_err("Invalid display\n"); + return count; + } + + if (display->panel->panel_initialized) { + pr_err("panel already initialized\n"); + return count; + } + pr_info("%s: early_power_state %d\n", __func__, + display->dsi_mot_ext.early_power_state); + + ext_dsi_display_early_power_on(); + + return count; + +} + +static ssize_t dsi_display_wakup_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int rc = 0; + + rc = snprintf(buf, PAGE_SIZE, "%s: timerhandle:%p\n", (g_wakeup_timer_interval==0)? "Stopped":"Started", g_wakeup_timer); + pr_info("%s: %s: timerhandle:%p\n", __func__, (g_wakeup_timer_interval==0)? "Stopped":"Started", g_wakeup_timer); + + return rc; +} + +static ssize_t dsi_display_wakup_set(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct dsi_display *display; + ktime_t now, add; + u16 val = 0; + + display = dev_get_drvdata(dev); + if (!display) { + pr_err("Invalid display\n"); + return count; + } + + if (kstrtou16(buf, 16, &val) < 0) + return count; + g_wakeup_timer_interval = val&0x7fff; + + pr_info("val: 0x%x(enable:0x%x : interval:%d seconds), early_power_state %d\n", val, (val&0x8000), g_wakeup_timer_interval, + display->dsi_mot_ext.early_power_state); + + if (g_wakeup_timer == NULL) { + g_wakeup_timer = devm_kzalloc(&display->pdev->dev, sizeof(*g_wakeup_timer), GFP_KERNEL); + if (g_wakeup_timer) { + alarm_init(g_wakeup_timer, ALARM_BOOTTIME, dsi_display_wakeup_timer_func); + } + else { + pr_err("failed to init timer\n"); + return count; + } + } + + // 0xffff : 0x8000/enable/disable 0x7fff/interval seconds + if (val&0x8000) { + now = ktime_get_boottime(); + add = ktime_set(g_wakeup_timer_interval, 0); + alarm_start(g_wakeup_timer, ktime_add(now, add)); + }else { + if (g_wakeup_timer) + alarm_cancel(g_wakeup_timer); + g_wakeup_timer_interval = 0; + } + return count; +} + +static ssize_t dsi_display_early_test_en_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int rc = 0; + + rc = snprintf(buf, PAGE_SIZE, "g_early_power_test_en: %d\n", g_early_power_test_en); + pr_info("g_early_power_test_en:%d\n", g_early_power_test_en); + + return rc; +} + +static ssize_t dsi_display_early_test_en_set(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct dsi_display *display; + u16 val = 0; + + display = dev_get_drvdata(dev); + if (!display) { + pr_err("Invalid display\n"); + return count; + } + + if (kstrtou16(buf, 10, &val) < 0) + return count; + g_early_power_test_en = val; + + pr_info("interval:%d seconds, early_power_state %d, g_early_power_test_en %d\n", + g_wakeup_timer_interval, display->dsi_mot_ext.early_power_state, g_early_power_test_en); + + return count; +} + +static void dsi_display_show_para(char* buf, char* pbuf, enum dsi_cmd_set_type type, struct dsi_display_mode_priv_info *priv_info) +{ + int i,j,rc; + u8* data; + int count = priv_info->cmd_sets[type].count; + + memset(pbuf, 0, PAGE_SIZE); + rc = snprintf(pbuf, PAGE_SIZE, "##Command for %s: count:%d ====\n", cmd_set_prop_map[type], count); + strcat(buf, pbuf); + memset(pbuf, 0, PAGE_SIZE); + for (i=0; icmd_sets[type].cmds[i].msg.type, //data0 + priv_info->cmd_sets[type].cmds[i].last_command, //data1 + priv_info->cmd_sets[type].cmds[i].msg.channel, //data2 + priv_info->cmd_sets[type].cmds[i].msg.flags&0x01, //data3 + priv_info->cmd_sets[type].cmds[i].post_wait_ms, //data4 + priv_info->cmd_sets[type].cmds[i].msg.tx_len&0xff00, //data5 + priv_info->cmd_sets[type].cmds[i].msg.tx_len&0x00ff //data6 + ); + strcat(buf, pbuf); + data = (u8*)priv_info->cmd_sets[type].cmds[i].msg.tx_buf; + for (j=0; jcmd_sets[type].cmds[i].msg.tx_len; j++) { + memset(pbuf, 0, PAGE_SIZE); + rc = snprintf(pbuf, PAGE_SIZE, " %2x ", data[j]); + strcat(buf, pbuf); + } + strcat(buf, "\n"); + } +} + +static ssize_t dsi_display_parse_para_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int rc = 0; + struct dsi_display *display; + u16 index = 0; + struct dsi_mode_info *mode_timing = NULL ; + struct dsi_display_mode *display_mode; + struct dsi_display_mode_priv_info *priv_info; + char* pbuf = NULL; + char* psubbuf = NULL; + + display = dev_get_drvdata(dev); + if (!display) { + pr_err("Invalid display\n"); + return rc; + } + display_mode = &display->modes[index]; + priv_info = display->modes[index].priv_info; + mode_timing = &display->modes[index].timing; + + pbuf = kzalloc(PAGE_SIZE*4, GFP_KERNEL); + if (pbuf) { + psubbuf = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!psubbuf) + return rc; + rc = snprintf(psubbuf, PAGE_SIZE, "panel horz active:%d front_portch:%d back_porch:%d pulse_width:%d h_skew:%d\n", + mode_timing->h_active, mode_timing->h_front_porch, mode_timing->h_back_porch, mode_timing->h_sync_width, mode_timing->h_skew); + strcat(pbuf, psubbuf); + memset(psubbuf, 0, PAGE_SIZE); + rc = snprintf(psubbuf, PAGE_SIZE, "panel vert active:%d front_portch:%d back_porch:%d pulse_width:%d\n", + mode_timing->v_active, mode_timing->v_front_porch, mode_timing->v_back_porch, mode_timing->v_sync_width); + strcat(pbuf, psubbuf); + memset(psubbuf, 0, PAGE_SIZE); + rc = snprintf(psubbuf, PAGE_SIZE, "panel clk rate:%d mdp_transfer_time_us:%d refresh_rate:%d\n", + display_mode->priv_info->clk_rate_hz , display_mode->priv_info->mdp_transfer_time_us, mode_timing->refresh_rate); + strcat(pbuf, psubbuf); + memset(psubbuf, 0, PAGE_SIZE); + dsi_display_show_para(pbuf, psubbuf, DSI_CMD_SET_ON, priv_info); + dsi_display_show_para(pbuf, psubbuf, DSI_CMD_SET_OFF, priv_info); + dsi_display_show_para(pbuf, psubbuf, DSI_CMD_SET_TIMING_SWITCH, priv_info); + rc = snprintf(buf, PAGE_SIZE, "%s\n", pbuf); + printk("%s\n", buf); + + } else { + pr_warn("kmalloc failed\n" ); + } + + if (psubbuf) + kfree(psubbuf); + if (pbuf) + kfree(pbuf); + return rc; +} + +// val 0: update the paras for the first modes +static ssize_t dsi_display_parse_para_update(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct dsi_display *display; + u16 index = 0; + + display = dev_get_drvdata(dev); + if (!display) { + pr_err("Invalid display\n"); + return count; + } + + if (kstrtou16(buf, 10, &index) < 0) + return count; + + dsi_panel_mot_parse_timing_from_file(display, index); + + return count; +} + +///sys/devices/platform/soc/soc:qcom,dsi-display/ +static DEVICE_ATTR(dsi_display_early_power, 0644, + dsi_display_early_power_read, + dsi_display_early_power_write); +static DEVICE_ATTR(dsi_display_wakeup, 0644, + dsi_display_wakup_get, + dsi_display_wakup_set); +static DEVICE_ATTR(early_test_en, 0644, + dsi_display_early_test_en_get, + dsi_display_early_test_en_set); +static DEVICE_ATTR(dsi_display_parse_para, 0644, + dsi_display_parse_para_get, + dsi_display_parse_para_update); + + +static struct attribute *dsi_display_mot_ext_fs_attrs[] = { + &dev_attr_dsi_display_early_power.attr, + &dev_attr_dsi_display_wakeup.attr, + &dev_attr_early_test_en.attr, + &dev_attr_dsi_display_parse_para.attr, + NULL, +}; +static struct attribute_group dsi_display_mot_ext_fs_attrs_group = { + .attrs = dsi_display_mot_ext_fs_attrs, +}; + +static int dsi_display_sysfs_ext_init(struct dsi_display *display) +{ + int rc = 0; + struct device *dev = &display->pdev->dev; + + rc = sysfs_create_group(&dev->kobj, + &dsi_display_mot_ext_fs_attrs_group); + + return rc; +} + +static int dsi_display_sysfs_ext_deinit(struct dsi_display *display) +{ + struct device *dev = &display->pdev->dev; + + sysfs_remove_group(&dev->kobj, + &dsi_display_mot_ext_fs_attrs_group); + + return 0; +} + +int dsi_display_ext_init(struct dsi_display *display) +{ + int rc = 0; + + g_dsi_mot_ext = &display->dsi_mot_ext; + g_dsi_mot_ext->display = display; + + dsi_display_sysfs_ext_init(display); + + //display->is_dsi_display_prepared = false; + //display->is_dsi_mot_primary = false; + g_dsi_mot_ext->early_power_state = DSI_EARLY_POWER_STATE_NUM; + g_dsi_mot_ext->early_power_workq = create_singlethread_workqueue("dsi_early_power_workq"); + if (!g_dsi_mot_ext->early_power_workq) { + pr_err("failed to create dsi early power workq!\n"); + dsi_display_sysfs_ext_deinit(display); + return 0; + } + + //wakeup_source_init(&g_dsi_mot_ext->early_wake_src, "dsi_early_wakelock"); + g_dsi_mot_ext->early_wake_src = wakeup_source_register(&display->pdev->dev, "dsi_early_wakelock"); + if(!g_dsi_mot_ext->early_wake_src){ + pr_err("failed to create dsi early power workq!\n"); + return 0; + } + + INIT_WORK(&g_dsi_mot_ext->early_on_work, + dsi_display_early_power_on_work); + INIT_DELAYED_WORK(&g_dsi_mot_ext->early_off_work, + dsi_display_early_power_off_work); + pr_info("dsi_display_ext_init success\n"); + + return rc; +} + diff --git a/techpack/display/msm/dsi/dsi_display_mot_ext.h b/techpack/display/msm/dsi/dsi_display_mot_ext.h new file mode 100644 index 000000000000..7c428749fe39 --- /dev/null +++ b/techpack/display/msm/dsi/dsi_display_mot_ext.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2015-2019, The Linux Foundation.All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + * + */ + +#ifndef _DSI_DISPLAY_MOT_EXT_H_ +#define _DSI_DISPLAY_MOT_EXT_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "msm_drv.h" +#include "dsi_defs.h" +#include "dsi_ctrl.h" +#include "dsi_phy.h" +#include "dsi_panel.h" +#include "sde_connector.h" +#include "sde_motUtil.h" +#include "dsi_display.h" +#include + +#define LCD_PARA_LINE_LEN 2*1024 + +enum dsi_display_early_power_state { + DSI_EARLY_POWER_IDLE = 0, + DSI_EARLY_POWER_BEGIN, + DSI_EARLY_POWER_PREPARED, + DSI_EARLY_POWER_INITIALIZED, + DSI_EARLY_POWER_FORBIDDEN, + DSI_EARLY_POWER_STATE_NUM +}; + +struct dsi_mot_ext_feature { + struct dsi_display *display; + struct workqueue_struct *early_power_workq; + struct work_struct early_on_work; + struct delayed_work early_off_work; + enum dsi_display_early_power_state early_power_state; + struct wakeup_source* early_wake_src; +}; + +/** + * dsi_display_ext_init() - Moto early power init + * @display: Handle to display. + * + * Prepare for early power up sequences for the primary panel. + * + * Return: error code. + */ +int dsi_display_ext_init(struct dsi_display *display); + +#endif /* _DSI_DISPLAY_MOT_EXT_H_ */ diff --git a/techpack/display/msm/dsi/dsi_panel.c b/techpack/display/msm/dsi/dsi_panel.c old mode 100644 new mode 100755 index e1f231f13c00..bb39f5a291bd --- a/techpack/display/msm/dsi/dsi_panel.c +++ b/techpack/display/msm/dsi/dsi_panel.c @@ -11,11 +11,29 @@ #include #include