From 22e10c082a241bfb9662bc41b5303ed312bd2de7 Mon Sep 17 00:00:00 2001 From: Padmanabhan Komanduru Date: Tue, 25 Sep 2018 07:47:48 +0530 Subject: [PATCH] msm: mdss: add support to switch the DSI clock dynamically Add change to support changing the DSI bit clock dynamically for video mode panels. This helps to avoid interference of DSI clock with other subsystems runtime. Change-Id: I05790a6dd9d8a2fc3cf31727d032e5220d6164e5 Signed-off-by: Padmanabhan Komanduru --- .../devicetree/bindings/fb/mdss-dsi-panel.txt | 6 + drivers/video/fbdev/msm/mdss_dsi.c | 331 +- drivers/video/fbdev/msm/mdss_dsi.h | 6 +- drivers/video/fbdev/msm/mdss_dsi_panel.c | 6066 +++++++++-------- drivers/video/fbdev/msm/mdss_dsi_phy.c | 31 +- drivers/video/fbdev/msm/mdss_dsi_phy.h | 4 +- drivers/video/fbdev/msm/mdss_fb.c | 6 +- drivers/video/fbdev/msm/mdss_panel.h | 3 + drivers/video/fbdev/msm/msm_mdss_io_8974.c | 103 +- 9 files changed, 3396 insertions(+), 3160 deletions(-) diff --git a/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt b/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt index cc55f6e2bfa0..4d0e1d5e12a2 100644 --- a/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt +++ b/Documentation/devicetree/bindings/fb/mdss-dsi-panel.txt @@ -165,6 +165,9 @@ Optional properties: - qcom,mdss-dsi-border-color: Defines the border color value if border is present. 0 = default value. - qcom,mdss-dsi-pan-enable-dynamic-fps: Boolean used to enable change in frame rate dynamically. +- qcom,mdss-dsi-pan-enable-dynamic-bitclk: Boolean used to enable change in DSI clock dynamically. +- qcom,mdss-dsi-dynamic-bitclk_freq: An array of integers that specifies the DSI bit clock + frequencies supported as part of dynamic bit clock feature. - qcom,mdss-dsi-pan-fps-update: A string that specifies when to change the frame rate. "dfps_suspend_resume_mode"= FPS change request is implemented during suspend/resume. @@ -696,6 +699,9 @@ Example: qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled"; qcom,mdss-dsi-pan-enable-dynamic-fps; qcom,mdss-dsi-pan-fps-update = "dfps_suspend_resume_mode"; + qcom,mdss-dsi-pan-enable-dynamic-bitclk; + qcom,mdss-dsi-dynamic-bitclk_freq = <711037824 724453632 737869440 + 751285248 764701056 778116864 791532672 804948480>; qcom,min-refresh-rate = <30>; qcom,max-refresh-rate = <60>; qcom,mdss-dsi-bl-pmic-bank-select = <0>; diff --git a/drivers/video/fbdev/msm/mdss_dsi.c b/drivers/video/fbdev/msm/mdss_dsi.c index cf34f3a9b7e1..637272a61211 100644 --- a/drivers/video/fbdev/msm/mdss_dsi.c +++ b/drivers/video/fbdev/msm/mdss_dsi.c @@ -2075,10 +2075,9 @@ static void __mdss_dsi_calc_dfps_delay(struct mdss_panel_data *pdata) } static int __mdss_dsi_dfps_calc_clks(struct mdss_panel_data *pdata, - int new_fps) + u64 new_clk_rate) { int rc = 0; - u64 clk_rate; struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; struct mdss_panel_info *pinfo; u32 phy_rev; @@ -2098,14 +2097,9 @@ static int __mdss_dsi_dfps_calc_clks(struct mdss_panel_data *pdata, pinfo = &pdata->panel_info; phy_rev = ctrl_pdata->shared_data->phy_rev; - rc = mdss_dsi_clk_div_config - (&ctrl_pdata->panel_data.panel_info, new_fps); - if (rc) { - pr_err("%s: unable to initialize the clk dividers\n", - __func__); - return rc; - } - + pinfo->clk_rate = new_clk_rate; + pinfo->mipi.dsi_pclk_rate = mdss_dsi_get_pclk_rate(pinfo, + new_clk_rate); __mdss_dsi_dyn_refresh_config(ctrl_pdata); if (phy_rev == DSI_PHY_REV_20) @@ -2118,9 +2112,8 @@ static int __mdss_dsi_dfps_calc_clks(struct mdss_panel_data *pdata, ctrl_pdata->byte_clk_rate_bkp = ctrl_pdata->byte_clk_rate; ctrl_pdata->pclk_rate = pinfo->mipi.dsi_pclk_rate; - clk_rate = pinfo->clk_rate; - do_div(clk_rate, 8U); - ctrl_pdata->byte_clk_rate = (u32) clk_rate; + do_div(new_clk_rate, 8U); + ctrl_pdata->byte_clk_rate = (u32) new_clk_rate; pr_debug("byte_rate=%i\n", ctrl_pdata->byte_clk_rate); pr_debug("pclk_rate=%i\n", ctrl_pdata->pclk_rate); @@ -2128,8 +2121,7 @@ static int __mdss_dsi_dfps_calc_clks(struct mdss_panel_data *pdata, return rc; } -static int __mdss_dsi_dfps_update_clks(struct mdss_panel_data *pdata, - int new_fps) +static int __mdss_dsi_dfps_update_clks(struct mdss_panel_data *pdata) { struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; struct mdss_dsi_ctrl_pdata *sctrl_pdata = NULL; @@ -2280,12 +2272,6 @@ static int __mdss_dsi_dfps_update_clks(struct mdss_panel_data *pdata, clk_disable_unprepare(ctrl_pdata->pll_byte_clk); clk_disable_unprepare(ctrl_pdata->pll_pixel_clk); - /* update new fps that at this point is already updated in hw */ - pinfo->current_fps = new_fps; - if (sctrl_pdata) { - spinfo->current_fps = new_fps; - } - return rc; dfps_timeout: @@ -2362,13 +2348,65 @@ static void mdss_dsi_avr_config(struct mdss_dsi_ctrl_pdata *ctrl_pdata, MDSS_XLOG(ctrl_pdata->ndx, enabled, data); } -static int mdss_dsi_dfps_config(struct mdss_panel_data *pdata, int new_fps) +static int __mdss_dsi_dynamic_clock_switch(struct mdss_panel_data *pdata, + u64 new_clk_rate) { int rc = 0; struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; struct mdss_panel_info *pinfo; u32 phy_rev; - u32 frame_rate_bkp; + u64 clk_rate_bkp; + + pr_debug("%s+:\n", __func__); + + ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + + phy_rev = ctrl_pdata->shared_data->phy_rev; + pinfo = &pdata->panel_info; + + /* get the fps configured in HW */ + clk_rate_bkp = pinfo->clk_rate; + + __mdss_dsi_mask_dfps_errors(ctrl_pdata, true); + + if (phy_rev == DSI_PHY_REV_20) { + rc = mdss_dsi_phy_calc_timing_param(pinfo, phy_rev, + new_clk_rate); + if (rc) { + pr_err("PHY calculations failed-%lld\n", new_clk_rate); + goto end_update; + } + } + + rc = __mdss_dsi_dfps_calc_clks(pdata, new_clk_rate); + if (rc) { + pr_err("error calculating clocks for %lld\n", new_clk_rate); + goto error_clks; + } + + rc = __mdss_dsi_dfps_update_clks(pdata); + if (rc) { + pr_err("Dynamic refresh failed-%lld\n", new_clk_rate); + goto error_dfps; + } + return rc; +error_dfps: + if (__mdss_dsi_dfps_calc_clks(pdata, clk_rate_bkp)) + pr_err("error reverting clock calculations for %lld\n", + clk_rate_bkp); +error_clks: + if (mdss_dsi_phy_calc_timing_param(pinfo, phy_rev, clk_rate_bkp)) + pr_err("Unable to revert phy timing-%lld\n", clk_rate_bkp); +end_update: + return rc; +} + +static int mdss_dsi_dfps_config(struct mdss_panel_data *pdata, int new_fps) +{ + int rc = 0; + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + struct mdss_panel_info *pinfo; pr_debug("%s+:\n", __func__); @@ -2385,12 +2423,8 @@ static int mdss_dsi_dfps_config(struct mdss_panel_data *pdata, int new_fps) return -EINVAL; } - phy_rev = ctrl_pdata->shared_data->phy_rev; pinfo = &pdata->panel_info; - /* get the fps configured in HW */ - frame_rate_bkp = pinfo->current_fps; - if (new_fps == pinfo->current_fps) { /* * This is unlikely as mdss driver checks for previously @@ -2406,39 +2440,45 @@ static int mdss_dsi_dfps_config(struct mdss_panel_data *pdata, int new_fps) __mdss_dsi_update_video_mode_total(pdata, new_fps); } else if (pinfo->dfps_update == DFPS_IMMEDIATE_CLK_UPDATE_MODE) { /* Clock update method */ + u64 new_clk_rate = mdss_dsi_calc_bitclk + (&ctrl_pdata->panel_data.panel_info, new_fps); + if (!new_clk_rate) { + pr_err("%s: unable to get the new bit clock rate\n", + __func__); + rc = -EINVAL; + goto end_update; + } - __mdss_dsi_mask_dfps_errors(ctrl_pdata, true); + rc = __mdss_dsi_dynamic_clock_switch(pdata, new_clk_rate); + if (!rc) { + struct mdss_dsi_ctrl_pdata *mctrl_pdata = NULL; + struct mdss_panel_info *mpinfo = NULL; - if (phy_rev == DSI_PHY_REV_20) { - rc = mdss_dsi_phy_calc_timing_param(pinfo, phy_rev, - new_fps); - if (rc) { - pr_err("PHY calculations failed-%d\n", new_fps); + if (mdss_dsi_is_hw_config_split + (ctrl_pdata->shared_data) && + mdss_dsi_is_ctrl_clk_master(ctrl_pdata)) goto end_update; + + if (mdss_dsi_is_hw_config_split + (ctrl_pdata->shared_data) && + mdss_dsi_is_ctrl_clk_slave(ctrl_pdata)) { + mctrl_pdata = mdss_dsi_get_ctrl_clk_master(); + if (IS_ERR_OR_NULL(mctrl_pdata)) { + pr_err("Invalid mctrl_pdata\n"); + goto end_update; + } + + mpinfo = &mctrl_pdata->panel_data.panel_info; } - } - - rc = __mdss_dsi_dfps_calc_clks(pdata, new_fps); - if (rc) { - pr_err("error calculating clocks for %d\n", new_fps); - goto error_clks; - } - - rc = __mdss_dsi_dfps_update_clks(pdata, new_fps); - if (rc) { - pr_err("Dynamic refresh failed-%d\n", new_fps); - goto error_dfps; + /* + * update new fps that at this point is already + * updated in hw + */ + pinfo->current_fps = new_fps; + if (mctrl_pdata && mpinfo) + mpinfo->current_fps = new_fps; } } - - return rc; -error_dfps: - if (__mdss_dsi_dfps_calc_clks(pdata, frame_rate_bkp)) - pr_err("error reverting clock calculations for %d\n", - frame_rate_bkp); -error_clks: - if (mdss_dsi_phy_calc_timing_param(pinfo, phy_rev, frame_rate_bkp)) - pr_err("Unable to revert phy timing-%d\n", frame_rate_bkp); end_update: return rc; } @@ -2712,6 +2752,163 @@ static void mdss_dsi_timing_db_ctrl(struct mdss_dsi_ctrl_pdata *ctrl, MDSS_DSI_CORE_CLK, MDSS_DSI_CLK_OFF); } +static struct mdss_dsi_ctrl_pdata *mdss_dsi_get_drvdata(struct device *dev) +{ + struct msm_fb_data_type *mfd; + struct mdss_panel_data *pdata; + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + struct fb_info *fbi = dev_get_drvdata(dev); + + if (fbi) { + mfd = (struct msm_fb_data_type *)fbi->par; + pdata = dev_get_platdata(&mfd->pdev->dev); + + ctrl_pdata = container_of(pdata, + struct mdss_dsi_ctrl_pdata, panel_data); + } + + return ctrl_pdata; +} + +static ssize_t supp_bitclk_list_sysfs_rda(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = 0; + int i = 0; + struct mdss_dsi_ctrl_pdata *ctrl_pdata = mdss_dsi_get_drvdata(dev); + struct mdss_panel_info *pinfo = NULL; + + if (!ctrl_pdata) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + pinfo = &ctrl_pdata->panel_data.panel_info; + if (!pinfo) { + pr_err("no panel connected\n"); + return -ENODEV; + } + + if (!pinfo->dynamic_bitclk) { + pr_err_once("%s: Dynamic bitclk not enabled for this panel\n", + __func__); + return -EINVAL; + } + + buf[0] = 0; + for (i = 0; i < pinfo->supp_bitclk_len; i++) { + if (ret > 0) + ret += scnprintf(buf + ret, PAGE_SIZE - ret, + ",%d", pinfo->supp_bitclks[i]); + else + ret += scnprintf(buf + ret, PAGE_SIZE - ret, + "%d", pinfo->supp_bitclks[i]); + } + + ret += scnprintf(buf + ret, PAGE_SIZE - ret, "\n"); + + return ret; +} + +static ssize_t dynamic_bitclk_sysfs_wta(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int rc = 0, i = 0; + struct mdss_dsi_ctrl_pdata *ctrl_pdata = mdss_dsi_get_drvdata(dev); + struct mdss_panel_info *pinfo = NULL; + int clk_rate = 0; + + if (!ctrl_pdata) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + pinfo = &ctrl_pdata->panel_data.panel_info; + if (!pinfo) { + pr_err("no panel connected\n"); + return -ENODEV; + } + + if (!pinfo->dynamic_bitclk) { + pr_err_once("%s: Dynamic bitclk not enabled for this panel\n", + __func__); + return -EINVAL; + } + + if (mdss_panel_is_power_off(pinfo->panel_power_state)) { + pr_err_once("%s: Panel powered off!\n", __func__); + return -EINVAL; + } + + rc = kstrtoint(buf, 10, &clk_rate); + if (rc) { + pr_err("%s: kstrtoint failed. rc=%d\n", __func__, rc); + return rc; + } + + for (i = 0; i < pinfo->supp_bitclk_len; i++) { + if (pinfo->supp_bitclks[i] == clk_rate) + break; + } + if (i == pinfo->supp_bitclk_len) { + pr_err("Requested bitclk: %d not supported\n", clk_rate); + return -EINVAL; + } + + rc = __mdss_dsi_dynamic_clock_switch(&ctrl_pdata->panel_data, + clk_rate); + if (!rc && mdss_dsi_is_hw_config_split(ctrl_pdata->shared_data)) { + struct mdss_dsi_ctrl_pdata *octrl = + mdss_dsi_get_other_ctrl(ctrl_pdata); + rc = __mdss_dsi_dynamic_clock_switch(&octrl->panel_data, + clk_rate); + if (rc) + pr_err("failed to switch DSI bitclk for sctrl\n"); + } else if (rc) { + pr_err("failed to switch DSI bitclk\n"); + } + + return count; +} /* dynamic_bitclk_sysfs_wta */ + +static ssize_t dynamic_bitclk_sysfs_rda(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret; + struct mdss_dsi_ctrl_pdata *ctrl_pdata = mdss_dsi_get_drvdata(dev); + struct mdss_panel_info *pinfo = NULL; + + if (!ctrl_pdata) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + pinfo = &ctrl_pdata->panel_data.panel_info; + if (!pinfo) { + pr_err("no panel connected\n"); + return -ENODEV; + } + + ret = snprintf(buf, PAGE_SIZE, "%llu\n", pinfo->clk_rate); + pr_debug("%s: '%llu'\n", __func__, pinfo->clk_rate); + + return ret; +} /* dynamic_bitclk_sysfs_rda */ + +static DEVICE_ATTR(dynamic_bitclk, S_IRUGO | S_IWUSR | S_IWGRP, + dynamic_bitclk_sysfs_rda, dynamic_bitclk_sysfs_wta); +static DEVICE_ATTR(supported_bitclk, S_IRUGO, supp_bitclk_list_sysfs_rda, NULL); + +static struct attribute *dynamic_bitclk_fs_attrs[] = { + &dev_attr_dynamic_bitclk.attr, + &dev_attr_supported_bitclk.attr, + NULL, +}; + +static struct attribute_group mdss_dsi_fs_attrs_group = { + .attrs = dynamic_bitclk_fs_attrs, +}; + static int mdss_dsi_event_handler(struct mdss_panel_data *pdata, int event, void *arg) { @@ -2878,6 +3075,14 @@ static int mdss_dsi_event_handler(struct mdss_panel_data *pdata, ctrl_pdata->kobj = &fbi->dev->kobj; ctrl_pdata->fb_node = fbi->node; + if (!mdss_dsi_is_hw_config_split(ctrl_pdata->shared_data) || + (mdss_dsi_is_hw_config_split(ctrl_pdata->shared_data) && + mdss_dsi_is_ctrl_clk_master(ctrl_pdata))) { + if (sysfs_create_group(&fbi->dev->kobj, + &mdss_dsi_fs_attrs_group)) + pr_err("failed to create DSI sysfs group\n"); + } + if (IS_ENABLED(CONFIG_MSM_DBA) && pdata->panel_info.is_dba_panel) { queue_delayed_work(ctrl_pdata->workq, @@ -3512,7 +3717,7 @@ static int mdss_dsi_ctrl_probe(struct platform_device *pdev) pinfo = &(ctrl_pdata->panel_data.panel_info); if (!(mdss_dsi_is_hw_config_split(ctrl_pdata->shared_data) && mdss_dsi_is_ctrl_clk_slave(ctrl_pdata)) && - pinfo->dynamic_fps) { + (pinfo->dynamic_fps || pinfo->dynamic_bitclk)) { rc = mdss_dsi_shadow_clk_init(pdev, ctrl_pdata); if (rc) { @@ -4536,11 +4741,19 @@ int dsi_panel_device_register(struct platform_device *ctrl_pdev, ((mipi->mode == DSI_VIDEO_MODE) ? MIPI_VIDEO_PANEL : MIPI_CMD_PANEL); - rc = mdss_dsi_clk_div_config(pinfo, mipi->frame_rate); - if (rc) { - pr_err("%s: unable to initialize the clk dividers\n", __func__); - return rc; + pinfo->clk_rate = mdss_dsi_calc_bitclk(pinfo, mipi->frame_rate); + if (!pinfo->clk_rate) { + pr_err("%s: unable to calculate the DSI bit clock\n", __func__); + return -EINVAL; } + + pinfo->mipi.dsi_pclk_rate = mdss_dsi_get_pclk_rate(pinfo, + pinfo->clk_rate); + if (!pinfo->mipi.dsi_pclk_rate) { + pr_err("%s: unable to calculate the DSI pclk\n", __func__); + return -EINVAL; + } + ctrl_pdata->pclk_rate = mipi->dsi_pclk_rate; clk_rate = pinfo->clk_rate; do_div(clk_rate, 8U); diff --git a/drivers/video/fbdev/msm/mdss_dsi.h b/drivers/video/fbdev/msm/mdss_dsi.h index 8bd8a140773e..00895308119b 100644 --- a/drivers/video/fbdev/msm/mdss_dsi.h +++ b/drivers/video/fbdev/msm/mdss_dsi.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, 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 @@ -720,8 +720,8 @@ void disable_esd_thread(void); void mdss_dsi_irq_handler_config(struct mdss_dsi_ctrl_pdata *ctrl_pdata); void mdss_dsi_set_tx_power_mode(int mode, struct mdss_panel_data *pdata); -int mdss_dsi_clk_div_config(struct mdss_panel_info *panel_info, - int frame_rate); +u64 mdss_dsi_calc_bitclk(struct mdss_panel_info *panel_info, int frame_rate); +u32 mdss_dsi_get_pclk_rate(struct mdss_panel_info *panel_info, u64 clk_rate); int mdss_dsi_clk_refresh(struct mdss_panel_data *pdata, bool update_phy); int mdss_dsi_link_clk_init(struct platform_device *pdev, struct mdss_dsi_ctrl_pdata *ctrl_pdata); diff --git a/drivers/video/fbdev/msm/mdss_dsi_panel.c b/drivers/video/fbdev/msm/mdss_dsi_panel.c index d66da235ea80..e683c7887f50 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_panel.c +++ b/drivers/video/fbdev/msm/mdss_dsi_panel.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, 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 @@ -10,9 +10,6 @@ * GNU General Public License for more details. */ - - - #include #include #include @@ -25,11 +22,11 @@ #include #include #include - + #include "mdss_dsi.h" #include "mdss_dba_utils.h" #include "mdss_debug.h" - + #include #include #if defined(CONFIG_IRIS2P_FULL_SUPPORT) @@ -38,993 +35,988 @@ #define DT_CMD_HDR 6 #define DEFAULT_MDP_TRANSFER_TIME 14000 - + #define VSYNC_DELAY msecs_to_jiffies(17) + +DEFINE_LED_TRIGGER(bl_led_trigger); + +void mdss_dsi_panel_pwm_cfg(struct mdss_dsi_ctrl_pdata *ctrl) +{ + if (ctrl->pwm_pmi) + return; + + ctrl->pwm_bl = pwm_request(ctrl->pwm_lpg_chan, "lcd-bklt"); + if (ctrl->pwm_bl == NULL || IS_ERR(ctrl->pwm_bl)) { + pr_err("%s: Error: lpg_chan=%d pwm request failed", + __func__, ctrl->pwm_lpg_chan); + } + ctrl->pwm_enabled = 0; +} + +bool mdss_dsi_panel_pwm_enable(struct mdss_dsi_ctrl_pdata *ctrl) +{ + bool status = true; + if (!ctrl->pwm_enabled) + goto end; + + if (pwm_enable(ctrl->pwm_bl)) { + pr_err("%s: pwm_enable() failed\n", __func__); + status = false; + } + + ctrl->pwm_enabled = 1; + +end: + return status; +} + +static void mdss_dsi_panel_bklt_pwm(struct mdss_dsi_ctrl_pdata *ctrl, int level) +{ + int ret; + u32 duty; + u32 period_ns; + + if (ctrl->pwm_bl == NULL) { + pr_err("%s: no PWM\n", __func__); + return; + } + + if (level == 0) { + if (ctrl->pwm_enabled) { + ret = pwm_config_us(ctrl->pwm_bl, level, + ctrl->pwm_period); + if (ret) + pr_err("%s: pwm_config_us() failed err=%d.\n", + __func__, ret); + pwm_disable(ctrl->pwm_bl); + } + ctrl->pwm_enabled = 0; + return; + } + + duty = level * ctrl->pwm_period; + duty /= ctrl->bklt_max; + + pr_debug("%s: bklt_ctrl=%d pwm_period=%d pwm_gpio=%d pwm_lpg_chan=%d\n", + __func__, ctrl->bklt_ctrl, ctrl->pwm_period, + ctrl->pwm_pmic_gpio, ctrl->pwm_lpg_chan); + + pr_debug("%s: ndx=%d level=%d duty=%d\n", __func__, + ctrl->ndx, level, duty); + + if (ctrl->pwm_period >= USEC_PER_SEC) { + ret = pwm_config_us(ctrl->pwm_bl, duty, ctrl->pwm_period); + if (ret) { + pr_err("%s: pwm_config_us() failed err=%d.\n", + __func__, ret); + return; + } + } else { + period_ns = ctrl->pwm_period * NSEC_PER_USEC; + ret = pwm_config(ctrl->pwm_bl, + level * period_ns / ctrl->bklt_max, + period_ns); + if (ret) { + pr_err("%s: pwm_config() failed err=%d.\n", + __func__, ret); + return; + } + } + + if (!ctrl->pwm_enabled) { + ret = pwm_enable(ctrl->pwm_bl); + if (ret) + pr_err("%s: pwm_enable() failed err=%d\n", __func__, + ret); + ctrl->pwm_enabled = 1; + } +} + +static char dcs_cmd[2] = {0x54, 0x00}; /* DTYPE_DCS_READ */ +static struct dsi_cmd_desc dcs_read_cmd = { + {DTYPE_DCS_READ, 1, 0, 1, 5, sizeof(dcs_cmd)}, + dcs_cmd +}; + +int mdss_dsi_panel_cmd_read(struct mdss_dsi_ctrl_pdata *ctrl, char cmd0, + char cmd1, void (*fxn)(int), char *rbuf, int len) +{ + struct dcs_cmd_req cmdreq; + struct mdss_panel_info *pinfo; + + pinfo = &(ctrl->panel_data.panel_info); + if (pinfo->dcs_cmd_by_left) { + if (ctrl->ndx != DSI_CTRL_LEFT) + return -EINVAL; + } + + dcs_cmd[0] = cmd0; + dcs_cmd[1] = cmd1; + memset(&cmdreq, 0, sizeof(cmdreq)); + cmdreq.cmds = &dcs_read_cmd; + cmdreq.cmds_cnt = 1; + cmdreq.flags = CMD_REQ_RX | CMD_REQ_COMMIT; + cmdreq.rlen = len; + cmdreq.rbuf = rbuf; + cmdreq.cb = fxn; /* call back */ + /* + * blocked here, until call back called + */ + + return mdss_dsi_cmdlist_put(ctrl, &cmdreq); +} + +static void mdss_dsi_panel_apply_settings(struct mdss_dsi_ctrl_pdata *ctrl, + struct dsi_panel_cmds *pcmds) +{ + struct dcs_cmd_req cmdreq; + struct mdss_panel_info *pinfo; + + pinfo = &(ctrl->panel_data.panel_info); + if ((pinfo->dcs_cmd_by_left) && (ctrl->ndx != DSI_CTRL_LEFT)) + return; + + memset(&cmdreq, 0, sizeof(cmdreq)); + cmdreq.cmds = pcmds->cmds; + cmdreq.cmds_cnt = pcmds->cmd_cnt; + cmdreq.flags = CMD_REQ_COMMIT; + cmdreq.rlen = 0; + cmdreq.cb = NULL; + + mdss_dsi_cmdlist_put(ctrl, &cmdreq); +} + + +static void mdss_dsi_panel_cmds_send(struct mdss_dsi_ctrl_pdata *ctrl, + struct dsi_panel_cmds *pcmds, u32 flags) +{ + struct dcs_cmd_req cmdreq; + struct mdss_panel_info *pinfo; + + pinfo = &(ctrl->panel_data.panel_info); + if (pinfo->dcs_cmd_by_left) { + if (ctrl->ndx != DSI_CTRL_LEFT) + return; + } + + memset(&cmdreq, 0, sizeof(cmdreq)); + cmdreq.cmds = pcmds->cmds; + cmdreq.cmds_cnt = pcmds->cmd_cnt; + cmdreq.flags = flags; + + /*Panel ON/Off commands should be sent in DSI Low Power Mode*/ + if (pcmds->link_state == DSI_LP_MODE) + cmdreq.flags |= CMD_REQ_LP_MODE; + else if (pcmds->link_state == DSI_HS_MODE) + cmdreq.flags |= CMD_REQ_HS_MODE; + + cmdreq.rlen = 0; + cmdreq.cb = NULL; + + mdss_dsi_cmdlist_put(ctrl, &cmdreq); +} + +static char led_pwm1[2] = {0x51, 0x0}; /* DTYPE_DCS_WRITE1 */ +static struct dsi_cmd_desc backlight_cmd = { + {DTYPE_DCS_WRITE1, 1, 0, 0, 1, sizeof(led_pwm1)}, + led_pwm1 +}; + +//samsung s6e3fa6 panel backlight +static char led_pwm2[3] ={0x51, 0x00, 0x00}; /* DTYPE_DCS_LWRITE */ +static struct dsi_cmd_desc backlight_cmd2 ={ + {DTYPE_DCS_LWRITE, 1, 0, 0, 1, sizeof(led_pwm2)}, + led_pwm2 +}; + +static void mdss_dsi_panel_bklt_dcs(struct mdss_dsi_ctrl_pdata *ctrl, int level) +{ + struct dcs_cmd_req cmdreq; + struct mdss_panel_info *pinfo; + + pinfo = &(ctrl->panel_data.panel_info); + if (pinfo->dcs_cmd_by_left) { + if (ctrl->ndx != DSI_CTRL_LEFT) + return; + } + + pr_debug("%s: level=%d\n", __func__, level); + + if (ctrl->bklt_max > 255){ + u8 ldata = 0; + u8 hdata = 0; - DEFINE_LED_TRIGGER(bl_led_trigger); - - void mdss_dsi_panel_pwm_cfg(struct mdss_dsi_ctrl_pdata *ctrl) - { - if (ctrl->pwm_pmi) - return; - - ctrl->pwm_bl = pwm_request(ctrl->pwm_lpg_chan, "lcd-bklt"); - if (ctrl->pwm_bl == NULL || IS_ERR(ctrl->pwm_bl)) { - pr_err("%s: Error: lpg_chan=%d pwm request failed", - __func__, ctrl->pwm_lpg_chan); - } - ctrl->pwm_enabled = 0; - } - - bool mdss_dsi_panel_pwm_enable(struct mdss_dsi_ctrl_pdata *ctrl) - { - bool status = true; - if (!ctrl->pwm_enabled) - goto end; - - if (pwm_enable(ctrl->pwm_bl)) { - pr_err("%s: pwm_enable() failed\n", __func__); - status = false; - } - - ctrl->pwm_enabled = 1; - - end: - return status; - } - - static void mdss_dsi_panel_bklt_pwm(struct mdss_dsi_ctrl_pdata *ctrl, int level) - { - int ret; - u32 duty; - u32 period_ns; - - if (ctrl->pwm_bl == NULL) { - pr_err("%s: no PWM\n", __func__); - return; - } - - if (level == 0) { - if (ctrl->pwm_enabled) { - ret = pwm_config_us(ctrl->pwm_bl, level, - ctrl->pwm_period); - if (ret) - pr_err("%s: pwm_config_us() failed err=%d.\n", - __func__, ret); - pwm_disable(ctrl->pwm_bl); - } - ctrl->pwm_enabled = 0; - return; - } - - duty = level * ctrl->pwm_period; - duty /= ctrl->bklt_max; - - pr_debug("%s: bklt_ctrl=%d pwm_period=%d pwm_gpio=%d pwm_lpg_chan=%d\n", - __func__, ctrl->bklt_ctrl, ctrl->pwm_period, - ctrl->pwm_pmic_gpio, ctrl->pwm_lpg_chan); - - pr_debug("%s: ndx=%d level=%d duty=%d\n", __func__, - ctrl->ndx, level, duty); - - if (ctrl->pwm_period >= USEC_PER_SEC) { - ret = pwm_config_us(ctrl->pwm_bl, duty, ctrl->pwm_period); - if (ret) { - pr_err("%s: pwm_config_us() failed err=%d.\n", - __func__, ret); - return; - } - } else { - period_ns = ctrl->pwm_period * NSEC_PER_USEC; - ret = pwm_config(ctrl->pwm_bl, - level * period_ns / ctrl->bklt_max, - period_ns); - if (ret) { - pr_err("%s: pwm_config() failed err=%d.\n", - __func__, ret); - return; - } - } - - if (!ctrl->pwm_enabled) { - ret = pwm_enable(ctrl->pwm_bl); - if (ret) - pr_err("%s: pwm_enable() failed err=%d\n", __func__, - ret); - ctrl->pwm_enabled = 1; - } - } - - static char dcs_cmd[2] = {0x54, 0x00}; /* DTYPE_DCS_READ */ - static struct dsi_cmd_desc dcs_read_cmd = { - {DTYPE_DCS_READ, 1, 0, 1, 5, sizeof(dcs_cmd)}, - dcs_cmd - }; - - int mdss_dsi_panel_cmd_read(struct mdss_dsi_ctrl_pdata *ctrl, char cmd0, - char cmd1, void (*fxn)(int), char *rbuf, int len) - { - struct dcs_cmd_req cmdreq; - struct mdss_panel_info *pinfo; - - pinfo = &(ctrl->panel_data.panel_info); - if (pinfo->dcs_cmd_by_left) { - if (ctrl->ndx != DSI_CTRL_LEFT) - return -EINVAL; - } - - dcs_cmd[0] = cmd0; - dcs_cmd[1] = cmd1; - memset(&cmdreq, 0, sizeof(cmdreq)); - cmdreq.cmds = &dcs_read_cmd; - cmdreq.cmds_cnt = 1; - cmdreq.flags = CMD_REQ_RX | CMD_REQ_COMMIT; - cmdreq.rlen = len; - cmdreq.rbuf = rbuf; - cmdreq.cb = fxn; /* call back */ - /* - * blocked here, until call back called - */ - - return mdss_dsi_cmdlist_put(ctrl, &cmdreq); - } - - static void mdss_dsi_panel_apply_settings(struct mdss_dsi_ctrl_pdata *ctrl, - struct dsi_panel_cmds *pcmds) - { - struct dcs_cmd_req cmdreq; - struct mdss_panel_info *pinfo; - - pinfo = &(ctrl->panel_data.panel_info); - if ((pinfo->dcs_cmd_by_left) && (ctrl->ndx != DSI_CTRL_LEFT)) - return; - - memset(&cmdreq, 0, sizeof(cmdreq)); - cmdreq.cmds = pcmds->cmds; - cmdreq.cmds_cnt = pcmds->cmd_cnt; - cmdreq.flags = CMD_REQ_COMMIT; - cmdreq.rlen = 0; - cmdreq.cb = NULL; - - mdss_dsi_cmdlist_put(ctrl, &cmdreq); - } - - - static void mdss_dsi_panel_cmds_send(struct mdss_dsi_ctrl_pdata *ctrl, - struct dsi_panel_cmds *pcmds, u32 flags) - { - struct dcs_cmd_req cmdreq; - struct mdss_panel_info *pinfo; - - pinfo = &(ctrl->panel_data.panel_info); - if (pinfo->dcs_cmd_by_left) { - if (ctrl->ndx != DSI_CTRL_LEFT) - return; - } - - memset(&cmdreq, 0, sizeof(cmdreq)); - cmdreq.cmds = pcmds->cmds; - cmdreq.cmds_cnt = pcmds->cmd_cnt; - cmdreq.flags = flags; - - /*Panel ON/Off commands should be sent in DSI Low Power Mode*/ - if (pcmds->link_state == DSI_LP_MODE) - cmdreq.flags |= CMD_REQ_LP_MODE; - else if (pcmds->link_state == DSI_HS_MODE) - cmdreq.flags |= CMD_REQ_HS_MODE; - - cmdreq.rlen = 0; - cmdreq.cb = NULL; - - mdss_dsi_cmdlist_put(ctrl, &cmdreq); - } - - - - - - static char led_pwm1[2] = {0x51, 0x0}; /* DTYPE_DCS_WRITE1 */ - static struct dsi_cmd_desc backlight_cmd = { - {DTYPE_DCS_WRITE1, 1, 0, 0, 1, sizeof(led_pwm1)}, - led_pwm1 - }; - //samsung s6e3fa6 panel backlight - static char led_pwm2[3] = {0x51, 0x00, 0x00}; /* DTYPE_DCS_LWRITE */ - static struct dsi_cmd_desc backlight_cmd2 = { - {DTYPE_DCS_LWRITE, 1, 0, 0, 1, sizeof(led_pwm2)}, - led_pwm2 - }; - - static void mdss_dsi_panel_bklt_dcs(struct mdss_dsi_ctrl_pdata *ctrl, int level) - { - struct dcs_cmd_req cmdreq; - struct mdss_panel_info *pinfo; - - pinfo = &(ctrl->panel_data.panel_info); - if (pinfo->dcs_cmd_by_left) { - if (ctrl->ndx != DSI_CTRL_LEFT) - return; - } - - pr_debug("%s: level=%d\n", __func__, level); - - - if (ctrl->bklt_max > 255){ - u8 ldata = 0; - u8 hdata = 0; - - if (ctrl->bl_high2bit){ - ldata = level & 0x0ff; - hdata = (level >> 8) & 0x03; - led_pwm2[2] = ldata; - led_pwm2[1] = hdata; - } else{ - ldata = level & 0x03; - hdata = (level >> 2) & 0x0ff; - led_pwm2[2] = ldata; - led_pwm2[1] = hdata; - } + if (ctrl->bl_high2bit){ + ldata = level & 0x0ff; + hdata = (level >> 8) & 0x03; + led_pwm2[2] = ldata; + led_pwm2[1] = hdata; + } else{ + ldata = level & 0x03; + hdata = (level >> 2) & 0x0ff; + led_pwm2[2] = ldata; + led_pwm2[1] = hdata; + } }else - led_pwm1[1] = (unsigned char)level; + led_pwm1[1] = (unsigned char)level; - memset(&cmdreq, 0, sizeof(cmdreq)); - if (ctrl->bklt_max > 255){ - cmdreq.cmds = &backlight_cmd2; - }else - cmdreq.cmds = &backlight_cmd; - cmdreq.cmds_cnt = 1; - cmdreq.flags = CMD_REQ_COMMIT | CMD_CLK_CTRL; - cmdreq.rlen = 0; - cmdreq.cb = NULL; - mdss_dsi_cmdlist_put(ctrl, &cmdreq); - } - - - static int mdss_dsi_request_gpios(struct mdss_dsi_ctrl_pdata *ctrl_pdata) - { - int rc = 0; - - if (gpio_is_valid(ctrl_pdata->disp_en_gpio)) { - rc = gpio_request(ctrl_pdata->disp_en_gpio, - "disp_enable"); - if (rc) { - pr_err("request disp_en gpio failed, rc=%d\n", - rc); - goto disp_en_gpio_err; - } - } - rc = gpio_request(ctrl_pdata->rst_gpio, "disp_rst_n"); - if (rc) { - pr_err("request reset gpio failed, rc=%d\n", - rc); - goto rst_gpio_err; - } - if (gpio_is_valid(ctrl_pdata->avdd_en_gpio)) { - rc = gpio_request(ctrl_pdata->avdd_en_gpio, - "avdd_enable"); - if (rc) { - pr_err("request avdd_en gpio failed, rc=%d\n", - rc); - goto avdd_en_gpio_err; - } - } - if (gpio_is_valid(ctrl_pdata->lcd_mode_sel_gpio)) { - rc = gpio_request(ctrl_pdata->lcd_mode_sel_gpio, "mode_sel"); - if (rc) { - pr_err("request dsc/dual mode gpio failed,rc=%d\n", - rc); - goto lcd_mode_sel_gpio_err; - } - } - - return rc; - - lcd_mode_sel_gpio_err: - if (gpio_is_valid(ctrl_pdata->avdd_en_gpio)) - gpio_free(ctrl_pdata->avdd_en_gpio); - avdd_en_gpio_err: - gpio_free(ctrl_pdata->rst_gpio); - rst_gpio_err: - if (gpio_is_valid(ctrl_pdata->disp_en_gpio)) - gpio_free(ctrl_pdata->disp_en_gpio); - disp_en_gpio_err: - return rc; - } - - int mdss_dsi_bl_gpio_ctrl(struct mdss_panel_data *pdata, int enable) - { - struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; - int rc = 0, val = 0; - - if (pdata == NULL) { - pr_err("%s: Invalid input data\n", __func__); - return -EINVAL; - } - - ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, - panel_data); - if (ctrl_pdata == NULL) { - pr_err("%s: Invalid ctrl data\n", __func__); - return -EINVAL; - } - - /* if gpio is not valid */ - if (!gpio_is_valid(ctrl_pdata->bklt_en_gpio)) - return rc; - - pr_debug("%s: enable = %d\n", __func__, enable); - - /* - * if gpio state is false and enable (bl level) is - * non zero then toggle the gpio - */ - if (!ctrl_pdata->bklt_en_gpio_state && enable) { - rc = gpio_request(ctrl_pdata->bklt_en_gpio, "bklt_enable"); - if (rc) { - pr_err("request bklt gpio failed, rc=%d\n", rc); - goto free; - } - - if (ctrl_pdata->bklt_en_gpio_invert) - val = 0; - else - val = 1; - - rc = gpio_direction_output(ctrl_pdata->bklt_en_gpio, val); - if (rc) { - pr_err("%s: unable to set dir for bklt gpio val %d\n", - __func__, val); - goto free; - } - ctrl_pdata->bklt_en_gpio_state = true; - goto ret; - } else if (ctrl_pdata->bklt_en_gpio_state && !enable) { - /* - * if gpio state is true and enable (bl level) is - * zero then toggle the gpio - */ - if (ctrl_pdata->bklt_en_gpio_invert) - val = 1; - else - val = 0; - - rc = gpio_direction_output(ctrl_pdata->bklt_en_gpio, val); - if (rc) - pr_err("%s: unable to set dir for bklt gpio val %d\n", - __func__, val); - goto free; - } - - /* gpio state is true and bl level is non zero */ - goto ret; - - free: - pr_debug("%s: free bklt gpio\n", __func__); - ctrl_pdata->bklt_en_gpio_state = false; - gpio_free(ctrl_pdata->bklt_en_gpio); - ret: - return rc; - } - - int mdss_dsi_panel_reset(struct mdss_panel_data *pdata, int enable) - { - struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; - struct mdss_panel_info *pinfo = NULL; - int i, rc = 0; - - if (pdata == NULL) { - pr_err("%s: Invalid input data\n", __func__); - return -EINVAL; - } - - ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, - panel_data); - - pinfo = &(ctrl_pdata->panel_data.panel_info); - if ((mdss_dsi_is_right_ctrl(ctrl_pdata) && - mdss_dsi_is_hw_config_split(ctrl_pdata->shared_data)) || - pinfo->is_dba_panel) { - pr_debug("%s:%d, right ctrl gpio configuration not needed\n", - __func__, __LINE__); - return rc; - } - - if (!gpio_is_valid(ctrl_pdata->disp_en_gpio)) { - pr_debug("%s:%d, reset line not configured\n", - __func__, __LINE__); - } - - if (!gpio_is_valid(ctrl_pdata->rst_gpio)) { - pr_debug("%s:%d, reset line not configured\n", - __func__, __LINE__); - return rc; - } - - pr_debug("%s: enable = %d\n", __func__, enable); - - if (enable) { - rc = mdss_dsi_request_gpios(ctrl_pdata); - if (rc) { - pr_err("gpio request failed\n"); - return rc; - } - if (!pinfo->cont_splash_enabled) { - if (gpio_is_valid(ctrl_pdata->disp_en_gpio)) { - rc = gpio_direction_output( - ctrl_pdata->disp_en_gpio, 1); - if (rc) { - pr_err("%s: unable to set dir for en gpio\n", - __func__); - goto exit; - } - } - - if (pdata->panel_info.rst_seq_len) { - rc = gpio_direction_output(ctrl_pdata->rst_gpio, - pdata->panel_info.rst_seq[0]); - if (rc) { - pr_err("%s: unable to set dir for rst gpio\n", - __func__); - goto exit; - } - } - - for (i = 0; i < pdata->panel_info.rst_seq_len; ++i) { - gpio_set_value((ctrl_pdata->rst_gpio), - pdata->panel_info.rst_seq[i]); - if (pdata->panel_info.rst_seq[++i]) - usleep_range(pinfo->rst_seq[i] * 1000, pinfo->rst_seq[i] * 1000); - } - - if (gpio_is_valid(ctrl_pdata->avdd_en_gpio)) { - if (ctrl_pdata->avdd_en_gpio_invert) { - rc = gpio_direction_output( - ctrl_pdata->avdd_en_gpio, 0); - } else { - rc = gpio_direction_output( - ctrl_pdata->avdd_en_gpio, 1); - } - if (rc) { - pr_err("%s: unable to set dir for avdd_en gpio\n", - __func__); - goto exit; - } - } - } - - if (gpio_is_valid(ctrl_pdata->lcd_mode_sel_gpio)) { - bool out = false; - - if ((pinfo->mode_sel_state == MODE_SEL_SINGLE_PORT) || - (pinfo->mode_sel_state == MODE_GPIO_HIGH)) - out = true; - else if ((pinfo->mode_sel_state == MODE_SEL_DUAL_PORT) - || (pinfo->mode_sel_state == MODE_GPIO_LOW)) - out = false; - - rc = gpio_direction_output( - ctrl_pdata->lcd_mode_sel_gpio, out); - if (rc) { - pr_err("%s: unable to set dir for mode gpio\n", - __func__); - goto exit; - } - } - - if (ctrl_pdata->ctrl_state & CTRL_STATE_PANEL_INIT) { - pr_debug("%s: Panel Not properly turned OFF\n", - __func__); - ctrl_pdata->ctrl_state &= ~CTRL_STATE_PANEL_INIT; - pr_debug("%s: Reset panel done\n", __func__); - } - } else { - if (gpio_is_valid(ctrl_pdata->avdd_en_gpio)) { - if (ctrl_pdata->avdd_en_gpio_invert) - gpio_set_value((ctrl_pdata->avdd_en_gpio), 1); - else - gpio_set_value((ctrl_pdata->avdd_en_gpio), 0); - - gpio_free(ctrl_pdata->avdd_en_gpio); - } - if (gpio_is_valid(ctrl_pdata->disp_en_gpio)) { - gpio_set_value((ctrl_pdata->disp_en_gpio), 0); - gpio_free(ctrl_pdata->disp_en_gpio); - } - gpio_set_value((ctrl_pdata->rst_gpio), 0); - gpio_free(ctrl_pdata->rst_gpio); - if (gpio_is_valid(ctrl_pdata->lcd_mode_sel_gpio)) { - gpio_set_value(ctrl_pdata->lcd_mode_sel_gpio, 0); - gpio_free(ctrl_pdata->lcd_mode_sel_gpio); - } - } - - exit: - return rc; - } - - int mdss_dsi_px_clk_req(struct mdss_panel_data *pdata, int enable) - { - struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; - int rc = 0; - - if (pdata == NULL) { - pr_err("%s: Invalid input data\n", __func__); - return -EINVAL; - } - - ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, - panel_data); - if (enable) { - if(!IS_ERR(ctrl_pdata->px_clk_src) && (!ctrl_pdata->px_clk_enabled)){ - clk_set_rate(ctrl_pdata->px_clk_src, 19200000); - rc = clk_prepare_enable(ctrl_pdata->px_clk_src); - if (rc){ - pr_err("px clk_prepare_enable failed, rc=%d\n", rc); - } - ctrl_pdata->px_clk_enabled = 1; - } - } else{ - if(!IS_ERR(ctrl_pdata->px_clk_src) && ctrl_pdata->px_clk_enabled){ - clk_disable_unprepare(ctrl_pdata->px_clk_src); - ctrl_pdata->px_clk_enabled = 0; - } - } - return rc; - } - int mdss_dsi_disp_vci_en(struct mdss_panel_data *pdata, int enable) - { - struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; - struct mdss_panel_info *pinfo = NULL; - int rc = 0; - - if (pdata == NULL) { - pr_err("%s: Invalid input data\n", __func__); - return -EINVAL; - } - - ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, - panel_data); - - if (!gpio_is_valid(ctrl_pdata->disp_vci_en_gpio)) { - pr_debug("%s:%d, vci_en_gpio line not configured\n", - __func__, __LINE__); - return rc; - } - pr_debug("%s: vci_en_gpio enable = %d\n", __func__, enable); - pinfo = &(ctrl_pdata->panel_data.panel_info); - - if (enable) { - rc = gpio_request(ctrl_pdata->disp_vci_en_gpio, - "disp_vci_en"); - if (rc) { - pr_err("request vci_enable gpio failed, rc=%d\n", - rc); - return rc; - } - rc = gpio_direction_output(ctrl_pdata->disp_vci_en_gpio, 1); - } else { - gpio_set_value(ctrl_pdata->disp_vci_en_gpio, 0); - gpio_free(ctrl_pdata->disp_vci_en_gpio); - } - return rc; - } - int mdss_dsi_isp_1v1_en(struct mdss_panel_data *pdata, int enable) - { - struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; - struct mdss_panel_info *pinfo = NULL; - int rc = 0; - - - if (pdata == NULL) { - pr_err("%s: Invalid input data\n", __func__); - return -EINVAL; - } - - ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, - panel_data); - - if (!gpio_is_valid(ctrl_pdata->isp_1v1_en_gpio)) { - pr_debug("%s:%d, isp_1v1_en line not configured\n", - __func__, __LINE__); - return rc; - } - pr_debug("%s: isp_1v1_en enable = %d\n", __func__, enable); - pinfo = &(ctrl_pdata->panel_data.panel_info); - - if (enable) { - rc = gpio_request(ctrl_pdata->isp_1v1_en_gpio, - "isp_1v1_en_gpio"); - if (rc) { - pr_err("request isp_1v1 gpio failed, rc=%d\n", - rc); - return rc; - } - rc = gpio_direction_output(ctrl_pdata->isp_1v1_en_gpio, 1); - } else { - gpio_set_value(ctrl_pdata->isp_1v1_en_gpio, 0); - gpio_free(ctrl_pdata->isp_1v1_en_gpio); - } - return rc; - } - - int mdss_dsi_disp_poc_en(struct mdss_panel_data *pdata, int enable) - { - struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; - struct mdss_panel_info *pinfo = NULL; - int rc = 0; - - if (pdata == NULL) { - pr_err("%s: Invalid input data\n", __func__); - return -EINVAL; - } - - ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, - panel_data); - - if (!gpio_is_valid(ctrl_pdata->disp_poc_en_gpio)) { - pr_debug("%s:%d, poc_en_gpio line not configured\n", - __func__, __LINE__); - return rc; - } - pr_debug("%s: poc_en_gpio enable = %d\n", __func__, enable); - pinfo = &(ctrl_pdata->panel_data.panel_info); - - if (enable) { - rc = gpio_request(ctrl_pdata->disp_poc_en_gpio, - "disp_poc_en"); - if (rc) { - pr_err("request poc_enable gpio failed, rc=%d\n", - rc); - return rc; - } - rc = gpio_direction_output(ctrl_pdata->disp_poc_en_gpio, 1); - } else { - gpio_set_value(ctrl_pdata->disp_poc_en_gpio, 0); - gpio_free(ctrl_pdata->disp_poc_en_gpio); - } - return rc; - } - - - - /** - * mdss_dsi_roi_merge() - merge two roi into single roi - * - * Function used by partial update with only one dsi intf take 2A/2B - * (column/page) dcs commands. - */ - static int mdss_dsi_roi_merge(struct mdss_dsi_ctrl_pdata *ctrl, - struct mdss_rect *roi) - { - struct mdss_panel_info *l_pinfo; - struct mdss_rect *l_roi; - struct mdss_rect *r_roi; - struct mdss_dsi_ctrl_pdata *other = NULL; - int ans = 0; - - if (ctrl->ndx == DSI_CTRL_LEFT) { - other = mdss_dsi_get_ctrl_by_index(DSI_CTRL_RIGHT); - if (!other) - return ans; - l_pinfo = &(ctrl->panel_data.panel_info); - l_roi = &(ctrl->panel_data.panel_info.roi); - r_roi = &(other->panel_data.panel_info.roi); - } else { - other = mdss_dsi_get_ctrl_by_index(DSI_CTRL_LEFT); - if (!other) - return ans; - l_pinfo = &(other->panel_data.panel_info); - l_roi = &(other->panel_data.panel_info.roi); - r_roi = &(ctrl->panel_data.panel_info.roi); - } - - if (l_roi->w == 0 && l_roi->h == 0) { - /* right only */ - *roi = *r_roi; - roi->x += l_pinfo->xres;/* add left full width to x-offset */ - } else { - /* left only and left+righ */ - *roi = *l_roi; - roi->w += r_roi->w; /* add right width */ - ans = 1; - } - - return ans; - } - - static char caset[] = {0x2a, 0x00, 0x00, 0x03, 0x00}; /* DTYPE_DCS_LWRITE */ - static char paset[] = {0x2b, 0x00, 0x00, 0x05, 0x00}; /* DTYPE_DCS_LWRITE */ - - /* - * Some panels can support multiple ROIs as part of the below commands - */ - static char caset_dual[] = {0x2a, 0x00, 0x00, 0x03, 0x00, 0x03, - 0x00, 0x00, 0x00, 0x00};/* DTYPE_DCS_LWRITE */ - static char paset_dual[] = {0x2b, 0x00, 0x00, 0x05, 0x00, 0x03, - 0x00, 0x00, 0x00, 0x00};/* DTYPE_DCS_LWRITE */ - - /* pack into one frame before sent */ - static struct dsi_cmd_desc set_col_page_addr_cmd[] = { - {{DTYPE_DCS_LWRITE, 0, 0, 0, 1, sizeof(caset)}, caset}, /* packed */ - {{DTYPE_DCS_LWRITE, 1, 0, 0, 1, sizeof(paset)}, paset}, - }; - - /* pack into one frame before sent */ - static struct dsi_cmd_desc set_dual_col_page_addr_cmd[] = { /*packed*/ - {{DTYPE_DCS_LWRITE, 0, 0, 0, 1, sizeof(caset_dual)}, caset_dual}, - {{DTYPE_DCS_LWRITE, 1, 0, 0, 1, sizeof(paset_dual)}, paset_dual}, - }; - - - static void __mdss_dsi_send_col_page_addr(struct mdss_dsi_ctrl_pdata *ctrl, - struct mdss_rect *roi, bool dual_roi) - { - if (dual_roi) { - struct mdss_rect *first, *second; - - first = &ctrl->panel_data.panel_info.dual_roi.first_roi; - second = &ctrl->panel_data.panel_info.dual_roi.second_roi; - - caset_dual[1] = (((first->x) & 0xFF00) >> 8); - caset_dual[2] = (((first->x) & 0xFF)); - caset_dual[3] = (((first->x - 1 + first->w) & 0xFF00) >> 8); - caset_dual[4] = (((first->x - 1 + first->w) & 0xFF)); - /* skip the MPU setting byte*/ - caset_dual[6] = (((second->x) & 0xFF00) >> 8); - caset_dual[7] = (((second->x) & 0xFF)); - caset_dual[8] = (((second->x - 1 + second->w) & 0xFF00) >> 8); - caset_dual[9] = (((second->x - 1 + second->w) & 0xFF)); - set_dual_col_page_addr_cmd[0].payload = caset_dual; - - paset_dual[1] = (((first->y) & 0xFF00) >> 8); - paset_dual[2] = (((first->y) & 0xFF)); - paset_dual[3] = (((first->y - 1 + first->h) & 0xFF00) >> 8); - paset_dual[4] = (((first->y - 1 + first->h) & 0xFF)); - /* skip the MPU setting byte */ - paset_dual[6] = (((second->y) & 0xFF00) >> 8); - paset_dual[7] = (((second->y) & 0xFF)); - paset_dual[8] = (((second->y - 1 + second->h) & 0xFF00) >> 8); - paset_dual[9] = (((second->y - 1 + second->h) & 0xFF)); - set_dual_col_page_addr_cmd[1].payload = paset_dual; - } else { - caset[1] = (((roi->x) & 0xFF00) >> 8); - caset[2] = (((roi->x) & 0xFF)); - caset[3] = (((roi->x - 1 + roi->w) & 0xFF00) >> 8); - caset[4] = (((roi->x - 1 + roi->w) & 0xFF)); - set_col_page_addr_cmd[0].payload = caset; - - paset[1] = (((roi->y) & 0xFF00) >> 8); - paset[2] = (((roi->y) & 0xFF)); - paset[3] = (((roi->y - 1 + roi->h) & 0xFF00) >> 8); - paset[4] = (((roi->y - 1 + roi->h) & 0xFF)); - set_col_page_addr_cmd[1].payload = paset; - } - pr_debug("%s Sending 2A 2B cmnd with dual_roi=%d\n", __func__, - dual_roi); - - } - static void mdss_dsi_send_col_page_addr(struct mdss_dsi_ctrl_pdata *ctrl, - struct mdss_rect *roi, int unicast) - { - struct dcs_cmd_req cmdreq; - struct mdss_panel_info *pinfo = &ctrl->panel_data.panel_info; - bool dual_roi = pinfo->dual_roi.enabled; - - __mdss_dsi_send_col_page_addr(ctrl, roi, dual_roi); - - memset(&cmdreq, 0, sizeof(cmdreq)); - cmdreq.cmds_cnt = 2; - cmdreq.flags = CMD_REQ_COMMIT | CMD_CLK_CTRL; - if (unicast) - cmdreq.flags |= CMD_REQ_UNICAST; - cmdreq.rlen = 0; - cmdreq.cb = NULL; - - /* Send default or dual roi 2A/2B cmd */ - cmdreq.cmds = dual_roi ? set_dual_col_page_addr_cmd : - set_col_page_addr_cmd; - mdss_dsi_cmdlist_put(ctrl, &cmdreq); - } - - static int mdss_dsi_set_col_page_addr(struct mdss_panel_data *pdata, - bool force_send) - { - struct mdss_panel_info *pinfo; - struct mdss_rect roi = {0}; - struct mdss_rect *p_roi; - struct mdss_rect *c_roi; - struct mdss_dsi_ctrl_pdata *ctrl = NULL; - struct mdss_dsi_ctrl_pdata *other = NULL; - int left_or_both = 0; - - if (pdata == NULL) { - pr_err("%s: Invalid input data\n", __func__); - return -EINVAL; - } - - ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata, - panel_data); - - pinfo = &pdata->panel_info; - p_roi = &pinfo->roi; - - /* - * to avoid keep sending same col_page info to panel, - * if roi_merge enabled, the roi of left ctrl is used - * to compare against new merged roi and saved new - * merged roi to it after comparing. - * if roi_merge disabled, then the calling ctrl's roi - * and pinfo's roi are used to compare. - */ - if (pinfo->partial_update_roi_merge) { - left_or_both = mdss_dsi_roi_merge(ctrl, &roi); - other = mdss_dsi_get_ctrl_by_index(DSI_CTRL_LEFT); - c_roi = &other->roi; - } else { - c_roi = &ctrl->roi; - roi = *p_roi; - } - - /* roi had changed, do col_page update */ - if (force_send || !mdss_rect_cmp(c_roi, &roi)) { - pr_debug("%s: ndx=%d x=%d y=%d w=%d h=%d\n", - __func__, ctrl->ndx, p_roi->x, - p_roi->y, p_roi->w, p_roi->h); - - *c_roi = roi; /* keep to ctrl */ - if (c_roi->w == 0 || c_roi->h == 0) { - /* no new frame update */ - pr_debug("%s: ctrl=%d, no partial roi set\n", - __func__, ctrl->ndx); - return 0; - } - - if (pinfo->dcs_cmd_by_left) { - if (left_or_both && ctrl->ndx == DSI_CTRL_RIGHT) { - /* 2A/2B sent by left already */ - return 0; - } - } - - if (!mdss_dsi_sync_wait_enable(ctrl)) { - if (pinfo->dcs_cmd_by_left) - ctrl = mdss_dsi_get_ctrl_by_index( - DSI_CTRL_LEFT); - mdss_dsi_send_col_page_addr(ctrl, &roi, 0); - } else { - /* - * when sync_wait_broadcast enabled, - * need trigger at right ctrl to - * start both dcs cmd transmission - */ - other = mdss_dsi_get_other_ctrl(ctrl); - if (!other) - goto end; - - if (mdss_dsi_is_left_ctrl(ctrl)) { - if (pinfo->partial_update_roi_merge) { - /* - * roi is the one after merged - * to dsi-1 only - */ - mdss_dsi_send_col_page_addr(other, - &roi, 0); - } else { - mdss_dsi_send_col_page_addr(ctrl, - &ctrl->roi, 1); - mdss_dsi_send_col_page_addr(other, - &other->roi, 1); - } - } else { - if (pinfo->partial_update_roi_merge) { - /* - * roi is the one after merged - * to dsi-1 only - */ - mdss_dsi_send_col_page_addr(ctrl, - &roi, 0); - } else { - mdss_dsi_send_col_page_addr(other, - &other->roi, 1); - mdss_dsi_send_col_page_addr(ctrl, - &ctrl->roi, 1); - } - } - } - } - - end: - return 0; - } - - static int mdss_dsi_panel_apply_display_setting(struct mdss_panel_data *pdata, - u32 mode) - { - struct mdss_dsi_ctrl_pdata *ctrl = NULL; - struct dsi_panel_cmds *lp_on_cmds; - struct dsi_panel_cmds *lp_off_cmds; - - if (pdata == NULL) { - pr_err("%s: Invalid input data\n", __func__); - return -EINVAL; - } - - ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata, - panel_data); - - lp_on_cmds = &ctrl->lp_on_cmds; - lp_off_cmds = &ctrl->lp_off_cmds; - - /* Apply display settings for low-persistence mode */ - if ((mode == MDSS_PANEL_LOW_PERSIST_MODE_ON) && - (lp_on_cmds->cmd_cnt)) - mdss_dsi_panel_apply_settings(ctrl, lp_on_cmds); - else if ((mode == MDSS_PANEL_LOW_PERSIST_MODE_OFF) && - (lp_on_cmds->cmd_cnt)) - mdss_dsi_panel_apply_settings(ctrl, lp_off_cmds); - else - return -EINVAL; - - pr_debug("%s: Persistence mode %d applied\n", __func__, mode); - return 0; - } - - static void mdss_dsi_panel_switch_mode(struct mdss_panel_data *pdata, - int mode) - { - struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; - struct mipi_panel_info *mipi; - struct dsi_panel_cmds *pcmds; - u32 flags = 0; - - if (pdata == NULL) { - pr_err("%s: Invalid input data\n", __func__); - return; - } - - mipi = &pdata->panel_info.mipi; - - if (!mipi->dms_mode) - return; - - ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, - panel_data); - - if (mipi->dms_mode != DYNAMIC_MODE_RESOLUTION_SWITCH_IMMEDIATE) { - flags |= CMD_REQ_COMMIT; - if (mode == SWITCH_TO_CMD_MODE) - pcmds = &ctrl_pdata->video2cmd; + memset(&cmdreq, 0, sizeof(cmdreq)); + if (ctrl->bklt_max > 255){ + cmdreq.cmds = &backlight_cmd2; + }else + cmdreq.cmds = &backlight_cmd; + cmdreq.cmds_cnt = 1; + cmdreq.flags = CMD_REQ_COMMIT | CMD_CLK_CTRL; + cmdreq.rlen = 0; + cmdreq.cb = NULL; + mdss_dsi_cmdlist_put(ctrl, &cmdreq); +} + +static int mdss_dsi_request_gpios(struct mdss_dsi_ctrl_pdata *ctrl_pdata) +{ + int rc = 0; + + if (gpio_is_valid(ctrl_pdata->disp_en_gpio)) { + rc = gpio_request(ctrl_pdata->disp_en_gpio, + "disp_enable"); + if (rc) { + pr_err("request disp_en gpio failed, rc=%d\n", + rc); + goto disp_en_gpio_err; + } + } + rc = gpio_request(ctrl_pdata->rst_gpio, "disp_rst_n"); + if (rc) { + pr_err("request reset gpio failed, rc=%d\n", + rc); + goto rst_gpio_err; + } + if (gpio_is_valid(ctrl_pdata->avdd_en_gpio)) { + rc = gpio_request(ctrl_pdata->avdd_en_gpio, + "avdd_enable"); + if (rc) { + pr_err("request avdd_en gpio failed, rc=%d\n", + rc); + goto avdd_en_gpio_err; + } + } + if (gpio_is_valid(ctrl_pdata->lcd_mode_sel_gpio)) { + rc = gpio_request(ctrl_pdata->lcd_mode_sel_gpio, "mode_sel"); + if (rc) { + pr_err("request dsc/dual mode gpio failed,rc=%d\n", + rc); + goto lcd_mode_sel_gpio_err; + } + } + + return rc; + +lcd_mode_sel_gpio_err: + if (gpio_is_valid(ctrl_pdata->avdd_en_gpio)) + gpio_free(ctrl_pdata->avdd_en_gpio); +avdd_en_gpio_err: + gpio_free(ctrl_pdata->rst_gpio); +rst_gpio_err: + if (gpio_is_valid(ctrl_pdata->disp_en_gpio)) + gpio_free(ctrl_pdata->disp_en_gpio); +disp_en_gpio_err: + return rc; +} + +int mdss_dsi_bl_gpio_ctrl(struct mdss_panel_data *pdata, int enable) +{ + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + int rc = 0, val = 0; + + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; + } + + ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + if (ctrl_pdata == NULL) { + pr_err("%s: Invalid ctrl data\n", __func__); + return -EINVAL; + } + + /* if gpio is not valid */ + if (!gpio_is_valid(ctrl_pdata->bklt_en_gpio)) + return rc; + + pr_debug("%s: enable = %d\n", __func__, enable); + + /* + * if gpio state is false and enable (bl level) is + * non zero then toggle the gpio + */ + if (!ctrl_pdata->bklt_en_gpio_state && enable) { + rc = gpio_request(ctrl_pdata->bklt_en_gpio, "bklt_enable"); + if (rc) { + pr_err("request bklt gpio failed, rc=%d\n", rc); + goto free; + } + + if (ctrl_pdata->bklt_en_gpio_invert) + val = 0; else - pcmds = &ctrl_pdata->cmd2video; - } else if ((mipi->dms_mode == - DYNAMIC_MODE_RESOLUTION_SWITCH_IMMEDIATE) - && pdata->current_timing - && !list_empty(&pdata->timings_list)) { - struct dsi_panel_timing *pt; + val = 1; + + rc = gpio_direction_output(ctrl_pdata->bklt_en_gpio, val); + if (rc) { + pr_err("%s: unable to set dir for bklt gpio val %d\n", + __func__, val); + goto free; + } + ctrl_pdata->bklt_en_gpio_state = true; + goto ret; + } else if (ctrl_pdata->bklt_en_gpio_state && !enable) { + /* + * if gpio state is true and enable (bl level) is + * zero then toggle the gpio + */ + if (ctrl_pdata->bklt_en_gpio_invert) + val = 1; + else + val = 0; + + rc = gpio_direction_output(ctrl_pdata->bklt_en_gpio, val); + if (rc) + pr_err("%s: unable to set dir for bklt gpio val %d\n", + __func__, val); + goto free; + } + + /* gpio state is true and bl level is non zero */ + goto ret; + +free: + pr_debug("%s: free bklt gpio\n", __func__); + ctrl_pdata->bklt_en_gpio_state = false; + gpio_free(ctrl_pdata->bklt_en_gpio); +ret: + return rc; +} + +int mdss_dsi_panel_reset(struct mdss_panel_data *pdata, int enable) +{ + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + struct mdss_panel_info *pinfo = NULL; + int i, rc = 0; + + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; + } + + ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + + pinfo = &(ctrl_pdata->panel_data.panel_info); + if ((mdss_dsi_is_right_ctrl(ctrl_pdata) && + mdss_dsi_is_hw_config_split(ctrl_pdata->shared_data)) || + pinfo->is_dba_panel) { + pr_debug("%s:%d, right ctrl gpio configuration not needed\n", + __func__, __LINE__); + return rc; + } + + if (!gpio_is_valid(ctrl_pdata->disp_en_gpio)) { + pr_debug("%s:%d, reset line not configured\n", + __func__, __LINE__); + } + + if (!gpio_is_valid(ctrl_pdata->rst_gpio)) { + pr_debug("%s:%d, reset line not configured\n", + __func__, __LINE__); + return rc; + } + + pr_debug("%s: enable = %d\n", __func__, enable); + + if (enable) { + rc = mdss_dsi_request_gpios(ctrl_pdata); + if (rc) { + pr_err("gpio request failed\n"); + return rc; + } + if (!pinfo->cont_splash_enabled) { + if (gpio_is_valid(ctrl_pdata->disp_en_gpio)) { + rc = gpio_direction_output( + ctrl_pdata->disp_en_gpio, 1); + if (rc) { + pr_err("%s: unable to set dir for en gpio\n", + __func__); + goto exit; + } + } + + if (pdata->panel_info.rst_seq_len) { + rc = gpio_direction_output(ctrl_pdata->rst_gpio, + pdata->panel_info.rst_seq[0]); + if (rc) { + pr_err("%s: unable to set dir for rst gpio\n", + __func__); + goto exit; + } + } + + for (i = 0; i < pdata->panel_info.rst_seq_len; ++i) { + gpio_set_value((ctrl_pdata->rst_gpio), + pdata->panel_info.rst_seq[i]); + if (pdata->panel_info.rst_seq[++i]) + usleep_range(pinfo->rst_seq[i] * 1000, pinfo->rst_seq[i] * 1000); + } + + if (gpio_is_valid(ctrl_pdata->avdd_en_gpio)) { + if (ctrl_pdata->avdd_en_gpio_invert) { + rc = gpio_direction_output( + ctrl_pdata->avdd_en_gpio, 0); + } else { + rc = gpio_direction_output( + ctrl_pdata->avdd_en_gpio, 1); + } + if (rc) { + pr_err("%s: unable to set dir for avdd_en gpio\n", + __func__); + goto exit; + } + } + } + + if (gpio_is_valid(ctrl_pdata->lcd_mode_sel_gpio)) { + bool out = false; + + if ((pinfo->mode_sel_state == MODE_SEL_SINGLE_PORT) || + (pinfo->mode_sel_state == MODE_GPIO_HIGH)) + out = true; + else if ((pinfo->mode_sel_state == MODE_SEL_DUAL_PORT) + || (pinfo->mode_sel_state == MODE_GPIO_LOW)) + out = false; + + rc = gpio_direction_output( + ctrl_pdata->lcd_mode_sel_gpio, out); + if (rc) { + pr_err("%s: unable to set dir for mode gpio\n", + __func__); + goto exit; + } + } + + if (ctrl_pdata->ctrl_state & CTRL_STATE_PANEL_INIT) { + pr_debug("%s: Panel Not properly turned OFF\n", + __func__); + ctrl_pdata->ctrl_state &= ~CTRL_STATE_PANEL_INIT; + pr_debug("%s: Reset panel done\n", __func__); + } + } else { + if (gpio_is_valid(ctrl_pdata->avdd_en_gpio)) { + if (ctrl_pdata->avdd_en_gpio_invert) + gpio_set_value((ctrl_pdata->avdd_en_gpio), 1); + else + gpio_set_value((ctrl_pdata->avdd_en_gpio), 0); + + gpio_free(ctrl_pdata->avdd_en_gpio); + } + if (gpio_is_valid(ctrl_pdata->disp_en_gpio)) { + gpio_set_value((ctrl_pdata->disp_en_gpio), 0); + gpio_free(ctrl_pdata->disp_en_gpio); + } + gpio_set_value((ctrl_pdata->rst_gpio), 0); + gpio_free(ctrl_pdata->rst_gpio); + if (gpio_is_valid(ctrl_pdata->lcd_mode_sel_gpio)) { + gpio_set_value(ctrl_pdata->lcd_mode_sel_gpio, 0); + gpio_free(ctrl_pdata->lcd_mode_sel_gpio); + } + } + +exit: + return rc; +} + +int mdss_dsi_px_clk_req(struct mdss_panel_data *pdata, int enable) +{ + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + int rc = 0; - pt = container_of(pdata->current_timing, - struct dsi_panel_timing, timing); + if (pdata == NULL){ + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; + } - pr_debug("%s: sending switch commands\n", __func__); - pcmds = &pt->switch_cmds; - flags |= CMD_REQ_DMA_TPG; - flags |= CMD_REQ_COMMIT; - } else { - pr_warn("%s: Invalid mode switch attempted\n", __func__); - return; - } + ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + if (enable){ + if(!IS_ERR(ctrl_pdata->px_clk_src) && (!ctrl_pdata->px_clk_enabled)){ + clk_set_rate(ctrl_pdata->px_clk_src, 19200000); + rc = clk_prepare_enable(ctrl_pdata->px_clk_src); + if (rc){ + pr_err("px clk_prepare_enable failed, rc=%d\n", rc); + } + ctrl_pdata->px_clk_enabled = 1; + } + } else{ + if(!IS_ERR(ctrl_pdata->px_clk_src) && ctrl_pdata->px_clk_enabled){ + clk_disable_unprepare(ctrl_pdata->px_clk_src); + ctrl_pdata->px_clk_enabled = 0; + } + } + return rc; +} + +int mdss_dsi_disp_vci_en(struct mdss_panel_data *pdata, int enable) +{ + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + struct mdss_panel_info *pinfo = NULL; + int rc = 0; - if ((pdata->panel_info.compression_mode == COMPRESSION_DSC) && - (pdata->panel_info.send_pps_before_switch)) - mdss_dsi_panel_dsc_pps_send(ctrl_pdata, &pdata->panel_info); + if (pdata == NULL){ + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; + } - mdss_dsi_panel_cmds_send(ctrl_pdata, pcmds, flags); + ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); - if ((pdata->panel_info.compression_mode == COMPRESSION_DSC) && - (!pdata->panel_info.send_pps_before_switch)) - mdss_dsi_panel_dsc_pps_send(ctrl_pdata, &pdata->panel_info); - } + if (!gpio_is_valid(ctrl_pdata->disp_vci_en_gpio)){ + pr_debug("%s:%d, vci_en_gpio line not configured\n", + __func__, __LINE__); + return rc; + } + pr_debug("%s: vci_en_gpio enable = %d\n", __func__, enable); + pinfo = &(ctrl_pdata->panel_data.panel_info); + + if (enable){ + rc = gpio_request(ctrl_pdata->disp_vci_en_gpio, + "disp_vci_en"); + if (rc){ + pr_err("request vci_enable gpio failed, rc=%d\n", + rc); + return rc; + } + rc = gpio_direction_output(ctrl_pdata->disp_vci_en_gpio, 1); + } else{ + gpio_set_value(ctrl_pdata->disp_vci_en_gpio, 0); + gpio_free(ctrl_pdata->disp_vci_en_gpio); + } + return rc; +} + +int mdss_dsi_isp_1v1_en(struct mdss_panel_data *pdata, int enable) +{ + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + struct mdss_panel_info *pinfo = NULL; + int rc = 0; + + + if (pdata == NULL){ + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; + } + + ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + + if (!gpio_is_valid(ctrl_pdata->isp_1v1_en_gpio)){ + pr_debug("%s:%d, isp_1v1_en line not configured\n", + __func__, __LINE__); + return rc; + } + pr_debug("%s: isp_1v1_en enable = %d\n", __func__, enable); + pinfo = &(ctrl_pdata->panel_data.panel_info); + + if (enable){ + rc = gpio_request(ctrl_pdata->isp_1v1_en_gpio, + "isp_1v1_en_gpio"); + if (rc){ + pr_err("request isp_1v1 gpio failed, rc=%d\n", + rc); + return rc; + } + rc = gpio_direction_output(ctrl_pdata->isp_1v1_en_gpio, 1); + } else{ + gpio_set_value(ctrl_pdata->isp_1v1_en_gpio, 0); + gpio_free(ctrl_pdata->isp_1v1_en_gpio); + } + return rc; +} + +int mdss_dsi_disp_poc_en(struct mdss_panel_data *pdata, int enable) +{ + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + struct mdss_panel_info *pinfo = NULL; + int rc = 0; + + if (pdata == NULL){ + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; + } + + ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + + if (!gpio_is_valid(ctrl_pdata->disp_poc_en_gpio)){ + pr_debug("%s:%d, poc_en_gpio line not configured\n", + __func__, __LINE__); + return rc; + } + pr_debug("%s: poc_en_gpio enable = %d\n", __func__, enable); + pinfo = &(ctrl_pdata->panel_data.panel_info); + + if (enable){ + rc = gpio_request(ctrl_pdata->disp_poc_en_gpio, + "disp_poc_en"); + if (rc){ + pr_err("request poc_enable gpio failed, rc=%d\n", + rc); + return rc; + } + rc = gpio_direction_output(ctrl_pdata->disp_poc_en_gpio, 1); + } else{ + gpio_set_value(ctrl_pdata->disp_poc_en_gpio, 0); + gpio_free(ctrl_pdata->disp_poc_en_gpio); + } + return rc; +} + +/** + * mdss_dsi_roi_merge() - merge two roi into single roi + * + * Function used by partial update with only one dsi intf take 2A/2B + * (column/page) dcs commands. + */ +static int mdss_dsi_roi_merge(struct mdss_dsi_ctrl_pdata *ctrl, + struct mdss_rect *roi) +{ + struct mdss_panel_info *l_pinfo; + struct mdss_rect *l_roi; + struct mdss_rect *r_roi; + struct mdss_dsi_ctrl_pdata *other = NULL; + int ans = 0; + + if (ctrl->ndx == DSI_CTRL_LEFT) { + other = mdss_dsi_get_ctrl_by_index(DSI_CTRL_RIGHT); + if (!other) + return ans; + l_pinfo = &(ctrl->panel_data.panel_info); + l_roi = &(ctrl->panel_data.panel_info.roi); + r_roi = &(other->panel_data.panel_info.roi); + } else { + other = mdss_dsi_get_ctrl_by_index(DSI_CTRL_LEFT); + if (!other) + return ans; + l_pinfo = &(other->panel_data.panel_info); + l_roi = &(other->panel_data.panel_info.roi); + r_roi = &(ctrl->panel_data.panel_info.roi); + } + + if (l_roi->w == 0 && l_roi->h == 0) { + /* right only */ + *roi = *r_roi; + roi->x += l_pinfo->xres;/* add left full width to x-offset */ + } else { + /* left only and left+righ */ + *roi = *l_roi; + roi->w += r_roi->w; /* add right width */ + ans = 1; + } + + return ans; +} + +static char caset[] = {0x2a, 0x00, 0x00, 0x03, 0x00}; /* DTYPE_DCS_LWRITE */ +static char paset[] = {0x2b, 0x00, 0x00, 0x05, 0x00}; /* DTYPE_DCS_LWRITE */ + +/* + * Some panels can support multiple ROIs as part of the below commands + */ +static char caset_dual[] = {0x2a, 0x00, 0x00, 0x03, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x00};/* DTYPE_DCS_LWRITE */ +static char paset_dual[] = {0x2b, 0x00, 0x00, 0x05, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x00};/* DTYPE_DCS_LWRITE */ + +/* pack into one frame before sent */ +static struct dsi_cmd_desc set_col_page_addr_cmd[] = { + {{DTYPE_DCS_LWRITE, 0, 0, 0, 1, sizeof(caset)}, caset}, /* packed */ + {{DTYPE_DCS_LWRITE, 1, 0, 0, 1, sizeof(paset)}, paset}, +}; + +/* pack into one frame before sent */ +static struct dsi_cmd_desc set_dual_col_page_addr_cmd[] = { /*packed*/ + {{DTYPE_DCS_LWRITE, 0, 0, 0, 1, sizeof(caset_dual)}, caset_dual}, + {{DTYPE_DCS_LWRITE, 1, 0, 0, 1, sizeof(paset_dual)}, paset_dual}, +}; + + +static void __mdss_dsi_send_col_page_addr(struct mdss_dsi_ctrl_pdata *ctrl, + struct mdss_rect *roi, bool dual_roi) +{ + if (dual_roi) { + struct mdss_rect *first, *second; + + first = &ctrl->panel_data.panel_info.dual_roi.first_roi; + second = &ctrl->panel_data.panel_info.dual_roi.second_roi; + + caset_dual[1] = (((first->x) & 0xFF00) >> 8); + caset_dual[2] = (((first->x) & 0xFF)); + caset_dual[3] = (((first->x - 1 + first->w) & 0xFF00) >> 8); + caset_dual[4] = (((first->x - 1 + first->w) & 0xFF)); + /* skip the MPU setting byte*/ + caset_dual[6] = (((second->x) & 0xFF00) >> 8); + caset_dual[7] = (((second->x) & 0xFF)); + caset_dual[8] = (((second->x - 1 + second->w) & 0xFF00) >> 8); + caset_dual[9] = (((second->x - 1 + second->w) & 0xFF)); + set_dual_col_page_addr_cmd[0].payload = caset_dual; + + paset_dual[1] = (((first->y) & 0xFF00) >> 8); + paset_dual[2] = (((first->y) & 0xFF)); + paset_dual[3] = (((first->y - 1 + first->h) & 0xFF00) >> 8); + paset_dual[4] = (((first->y - 1 + first->h) & 0xFF)); + /* skip the MPU setting byte */ + paset_dual[6] = (((second->y) & 0xFF00) >> 8); + paset_dual[7] = (((second->y) & 0xFF)); + paset_dual[8] = (((second->y - 1 + second->h) & 0xFF00) >> 8); + paset_dual[9] = (((second->y - 1 + second->h) & 0xFF)); + set_dual_col_page_addr_cmd[1].payload = paset_dual; + } else { + caset[1] = (((roi->x) & 0xFF00) >> 8); + caset[2] = (((roi->x) & 0xFF)); + caset[3] = (((roi->x - 1 + roi->w) & 0xFF00) >> 8); + caset[4] = (((roi->x - 1 + roi->w) & 0xFF)); + set_col_page_addr_cmd[0].payload = caset; + + paset[1] = (((roi->y) & 0xFF00) >> 8); + paset[2] = (((roi->y) & 0xFF)); + paset[3] = (((roi->y - 1 + roi->h) & 0xFF00) >> 8); + paset[4] = (((roi->y - 1 + roi->h) & 0xFF)); + set_col_page_addr_cmd[1].payload = paset; + } + pr_debug("%s Sending 2A 2B cmnd with dual_roi=%d\n", __func__, + dual_roi); + +} +static void mdss_dsi_send_col_page_addr(struct mdss_dsi_ctrl_pdata *ctrl, + struct mdss_rect *roi, int unicast) +{ + struct dcs_cmd_req cmdreq; + struct mdss_panel_info *pinfo = &ctrl->panel_data.panel_info; + bool dual_roi = pinfo->dual_roi.enabled; + + __mdss_dsi_send_col_page_addr(ctrl, roi, dual_roi); + + memset(&cmdreq, 0, sizeof(cmdreq)); + cmdreq.cmds_cnt = 2; + cmdreq.flags = CMD_REQ_COMMIT | CMD_CLK_CTRL; + if (unicast) + cmdreq.flags |= CMD_REQ_UNICAST; + cmdreq.rlen = 0; + cmdreq.cb = NULL; + + /* Send default or dual roi 2A/2B cmd */ + cmdreq.cmds = dual_roi ? set_dual_col_page_addr_cmd : + set_col_page_addr_cmd; + mdss_dsi_cmdlist_put(ctrl, &cmdreq); +} + +static int mdss_dsi_set_col_page_addr(struct mdss_panel_data *pdata, + bool force_send) +{ + struct mdss_panel_info *pinfo; + struct mdss_rect roi = {0}; + struct mdss_rect *p_roi; + struct mdss_rect *c_roi; + struct mdss_dsi_ctrl_pdata *ctrl = NULL; + struct mdss_dsi_ctrl_pdata *other = NULL; + int left_or_both = 0; + + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; + } + + ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + + pinfo = &pdata->panel_info; + p_roi = &pinfo->roi; + + /* + * to avoid keep sending same col_page info to panel, + * if roi_merge enabled, the roi of left ctrl is used + * to compare against new merged roi and saved new + * merged roi to it after comparing. + * if roi_merge disabled, then the calling ctrl's roi + * and pinfo's roi are used to compare. + */ + if (pinfo->partial_update_roi_merge) { + left_or_both = mdss_dsi_roi_merge(ctrl, &roi); + other = mdss_dsi_get_ctrl_by_index(DSI_CTRL_LEFT); + c_roi = &other->roi; + } else { + c_roi = &ctrl->roi; + roi = *p_roi; + } + + /* roi had changed, do col_page update */ + if (force_send || !mdss_rect_cmp(c_roi, &roi)) { + pr_debug("%s: ndx=%d x=%d y=%d w=%d h=%d\n", + __func__, ctrl->ndx, p_roi->x, + p_roi->y, p_roi->w, p_roi->h); + + *c_roi = roi; /* keep to ctrl */ + if (c_roi->w == 0 || c_roi->h == 0) { + /* no new frame update */ + pr_debug("%s: ctrl=%d, no partial roi set\n", + __func__, ctrl->ndx); + return 0; + } + + if (pinfo->dcs_cmd_by_left) { + if (left_or_both && ctrl->ndx == DSI_CTRL_RIGHT) { + /* 2A/2B sent by left already */ + return 0; + } + } + + if (!mdss_dsi_sync_wait_enable(ctrl)) { + if (pinfo->dcs_cmd_by_left) + ctrl = mdss_dsi_get_ctrl_by_index( + DSI_CTRL_LEFT); + mdss_dsi_send_col_page_addr(ctrl, &roi, 0); + } else { + /* + * when sync_wait_broadcast enabled, + * need trigger at right ctrl to + * start both dcs cmd transmission + */ + other = mdss_dsi_get_other_ctrl(ctrl); + if (!other) + goto end; + + if (mdss_dsi_is_left_ctrl(ctrl)) { + if (pinfo->partial_update_roi_merge) { + /* + * roi is the one after merged + * to dsi-1 only + */ + mdss_dsi_send_col_page_addr(other, + &roi, 0); + } else { + mdss_dsi_send_col_page_addr(ctrl, + &ctrl->roi, 1); + mdss_dsi_send_col_page_addr(other, + &other->roi, 1); + } + } else { + if (pinfo->partial_update_roi_merge) { + /* + * roi is the one after merged + * to dsi-1 only + */ + mdss_dsi_send_col_page_addr(ctrl, + &roi, 0); + } else { + mdss_dsi_send_col_page_addr(other, + &other->roi, 1); + mdss_dsi_send_col_page_addr(ctrl, + &ctrl->roi, 1); + } + } + } + } + +end: + return 0; +} + +static int mdss_dsi_panel_apply_display_setting(struct mdss_panel_data *pdata, + u32 mode) +{ + struct mdss_dsi_ctrl_pdata *ctrl = NULL; + struct dsi_panel_cmds *lp_on_cmds; + struct dsi_panel_cmds *lp_off_cmds; + + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; + } + + ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + + lp_on_cmds = &ctrl->lp_on_cmds; + lp_off_cmds = &ctrl->lp_off_cmds; + + /* Apply display settings for low-persistence mode */ + if ((mode == MDSS_PANEL_LOW_PERSIST_MODE_ON) && + (lp_on_cmds->cmd_cnt)) + mdss_dsi_panel_apply_settings(ctrl, lp_on_cmds); + else if ((mode == MDSS_PANEL_LOW_PERSIST_MODE_OFF) && + (lp_on_cmds->cmd_cnt)) + mdss_dsi_panel_apply_settings(ctrl, lp_off_cmds); + else + return -EINVAL; + + pr_debug("%s: Persistence mode %d applied\n", __func__, mode); + return 0; +} + +static void mdss_dsi_panel_switch_mode(struct mdss_panel_data *pdata, + int mode) +{ + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + struct mipi_panel_info *mipi; + struct dsi_panel_cmds *pcmds; + u32 flags = 0; + + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return; + } + + mipi = &pdata->panel_info.mipi; + + if (!mipi->dms_mode) + return; + + ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + + if (mipi->dms_mode != DYNAMIC_MODE_RESOLUTION_SWITCH_IMMEDIATE) { + flags |= CMD_REQ_COMMIT; + if (mode == SWITCH_TO_CMD_MODE) + pcmds = &ctrl_pdata->video2cmd; + else + pcmds = &ctrl_pdata->cmd2video; + } else if ((mipi->dms_mode == + DYNAMIC_MODE_RESOLUTION_SWITCH_IMMEDIATE) + && pdata->current_timing + && !list_empty(&pdata->timings_list)) { + struct dsi_panel_timing *pt; + + pt = container_of(pdata->current_timing, + struct dsi_panel_timing, timing); + + pr_debug("%s: sending switch commands\n", __func__); + pcmds = &pt->switch_cmds; + flags |= CMD_REQ_DMA_TPG; + flags |= CMD_REQ_COMMIT; + } else { + pr_warn("%s: Invalid mode switch attempted\n", __func__); + return; + } + + if ((pdata->panel_info.compression_mode == COMPRESSION_DSC) && + (pdata->panel_info.send_pps_before_switch)) + mdss_dsi_panel_dsc_pps_send(ctrl_pdata, &pdata->panel_info); + + mdss_dsi_panel_cmds_send(ctrl_pdata, pcmds, flags); + + if ((pdata->panel_info.compression_mode == COMPRESSION_DSC) && + (!pdata->panel_info.send_pps_before_switch)) + mdss_dsi_panel_dsc_pps_send(ctrl_pdata, &pdata->panel_info); +} /************************************ backlight level 0-->55 ==> 0-->55 backlight level 55-->230 ==> 55-->200 @@ -1037,94 +1029,94 @@ static u32 backlight_level_remap(struct mdss_dsi_ctrl_pdata *ctrl, u32 level) if (ctrl->bklt_max == 255){ if (level < 55){ remap_level = level; - } else if ((level >= 55) && (level <= 230)){ + } else if ((level >= 55) && (level <= 230)){ remap_level = (level*29+330)/35; - }else{ + }else{ remap_level = level*11/5-306; - } - } else{ + } + } else{ remap_level = level; - } + } return remap_level; } - static void mdss_dsi_panel_bl_ctrl(struct mdss_panel_data *pdata, - u32 bl_level) - { - struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; - struct mdss_dsi_ctrl_pdata *sctrl = NULL; + +static void mdss_dsi_panel_bl_ctrl(struct mdss_panel_data *pdata, + u32 bl_level) +{ + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + struct mdss_dsi_ctrl_pdata *sctrl = NULL; static bool first_bl_level = true; if (first_bl_level || (bl_level == 0)){ printk("---backlight level = %d---\n", bl_level); first_bl_level = (bl_level == 0)? true : false; } - if (pdata == NULL) { - pr_err("%s: Invalid input data\n", __func__); - return; - } - - ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, - panel_data); - + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return; + } + + ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + if (ctrl_pdata->high_brightness_panel){ pr_debug("%s goto backlight level remap\n", __func__); bl_level = backlight_level_remap(ctrl_pdata, bl_level); - } - /* - * Some backlight controllers specify a minimum duty cycle - * for the backlight brightness. If the brightness is less - * than it, the controller can malfunction. - */ - pr_debug("%s: bl_level:%d\n", __func__, bl_level); + } + /* + * Some backlight controllers specify a minimum duty cycle + * for the backlight brightness. If the brightness is less + * than it, the controller can malfunction. + */ + pr_debug("%s: bl_level:%d\n", __func__, bl_level); - /* do not allow backlight to change when panel in disable mode */ - if (pdata->panel_disable_mode && (bl_level != 0)) - return; - - if ((bl_level < pdata->panel_info.bl_min) && (bl_level != 0)) - bl_level = pdata->panel_info.bl_min; - - /* enable the backlight gpio if present */ - mdss_dsi_bl_gpio_ctrl(pdata, bl_level); - - switch (ctrl_pdata->bklt_ctrl) { - case BL_WLED: - led_trigger_event(bl_led_trigger, bl_level); - break; - case BL_PWM: - mdss_dsi_panel_bklt_pwm(ctrl_pdata, bl_level); - break; - case BL_DCS_CMD: - if (!mdss_dsi_sync_wait_enable(ctrl_pdata)) { - mdss_dsi_panel_bklt_dcs(ctrl_pdata, bl_level); - break; - } - /* - * DCS commands to update backlight are usually sent at - * the same time to both the controllers. However, if - * sync_wait is enabled, we need to ensure that the - * dcs commands are first sent to the non-trigger - * controller so that when the commands are triggered, - * both controllers receive it at the same time. - */ - sctrl = mdss_dsi_get_other_ctrl(ctrl_pdata); - if (mdss_dsi_sync_wait_trigger(ctrl_pdata)) { - if (sctrl) - mdss_dsi_panel_bklt_dcs(sctrl, bl_level); - mdss_dsi_panel_bklt_dcs(ctrl_pdata, bl_level); - } else { - mdss_dsi_panel_bklt_dcs(ctrl_pdata, bl_level); - if (sctrl) - mdss_dsi_panel_bklt_dcs(sctrl, bl_level); - } - break; - default: - pr_err("%s: Unknown bl_ctrl configuration\n", - __func__); - break; - } - } + /* do not allow backlight to change when panel in disable mode */ + if (pdata->panel_disable_mode && (bl_level != 0)) + return; + if ((bl_level < pdata->panel_info.bl_min) && (bl_level != 0)) + bl_level = pdata->panel_info.bl_min; + + /* enable the backlight gpio if present */ + mdss_dsi_bl_gpio_ctrl(pdata, bl_level); + + switch (ctrl_pdata->bklt_ctrl) { + case BL_WLED: + led_trigger_event(bl_led_trigger, bl_level); + break; + case BL_PWM: + mdss_dsi_panel_bklt_pwm(ctrl_pdata, bl_level); + break; + case BL_DCS_CMD: + if (!mdss_dsi_sync_wait_enable(ctrl_pdata)) { + mdss_dsi_panel_bklt_dcs(ctrl_pdata, bl_level); + break; + } + /* + * DCS commands to update backlight are usually sent at + * the same time to both the controllers. However, if + * sync_wait is enabled, we need to ensure that the + * dcs commands are first sent to the non-trigger + * controller so that when the commands are triggered, + * both controllers receive it at the same time. + */ + sctrl = mdss_dsi_get_other_ctrl(ctrl_pdata); + if (mdss_dsi_sync_wait_trigger(ctrl_pdata)) { + if (sctrl) + mdss_dsi_panel_bklt_dcs(sctrl, bl_level); + mdss_dsi_panel_bklt_dcs(ctrl_pdata, bl_level); + } else { + mdss_dsi_panel_bklt_dcs(ctrl_pdata, bl_level); + if (sctrl) + mdss_dsi_panel_bklt_dcs(sctrl, bl_level); + } + break; + default: + pr_err("%s: Unknown bl_ctrl configuration\n", + __func__); + break; + } +} int mdss_dsi_panel_set_srgb_mode(struct mdss_dsi_ctrl_pdata *ctrl, int level) { @@ -1132,32 +1124,33 @@ int mdss_dsi_panel_set_srgb_mode(struct mdss_dsi_ctrl_pdata *ctrl, int level) struct dsi_panel_cmds *srgb_off_cmds; mutex_lock(&ctrl->panel_mode_lock); - if (!ctrl->is_panel_on) { + if (!ctrl->is_panel_on){ mutex_unlock(&ctrl->panel_mode_lock); return 0; } srgb_on_cmds = &ctrl->srgb_on_cmds; srgb_off_cmds = &ctrl->srgb_off_cmds; - if (level) { - if (srgb_on_cmds->cmd_cnt) { + if (level){ + if (srgb_on_cmds->cmd_cnt){ mdss_dsi_panel_cmds_send(ctrl, srgb_on_cmds, CMD_REQ_COMMIT); pr_err("sRGB Mode On.\n"); - } else { + } else{ pr_err("This panel not support sRGB mode on.\n"); } - } else { - if (srgb_off_cmds->cmd_cnt) { + } else{ + if (srgb_off_cmds->cmd_cnt){ mdss_dsi_panel_cmds_send(ctrl, srgb_off_cmds, CMD_REQ_COMMIT); pr_err("sRGB Mode off.\n"); - } else { + } else{ pr_err("This panel not support sRGB mode off.\n"); } - } + } mutex_unlock(&ctrl->panel_mode_lock); return 0; } + int mdss_dsi_panel_get_srgb_mode(struct mdss_dsi_ctrl_pdata *ctrl) { return ctrl->SRGB_mode; @@ -1170,26 +1163,26 @@ int mdss_dsi_panel_set_adobe_rgb_mode(struct mdss_dsi_ctrl_pdata *ctrl, struct dsi_panel_cmds *adobe_rgb_off_cmds; mutex_lock(&ctrl->panel_mode_lock); - if (!ctrl->is_panel_on) { + if (!ctrl->is_panel_on){ mutex_unlock(&ctrl->panel_mode_lock); return 0; } adobe_rgb_on_cmds = &ctrl->Adobe_RGB_on_cmds; adobe_rgb_off_cmds = &ctrl->Adobe_RGB_off_cmds; - if (level) { - if (adobe_rgb_on_cmds->cmd_cnt) { + if (level){ + if (adobe_rgb_on_cmds->cmd_cnt){ mdss_dsi_panel_cmds_send(ctrl, adobe_rgb_on_cmds, CMD_REQ_COMMIT); pr_err("Adobe RGB Mode On.\n"); - } else { + } else{ pr_err("This Panel not support Adobe RGB mode On.\n"); } - } else { - if (adobe_rgb_off_cmds->cmd_cnt) { + } else{ + if (adobe_rgb_off_cmds->cmd_cnt){ mdss_dsi_panel_cmds_send(ctrl, adobe_rgb_off_cmds, CMD_REQ_COMMIT); pr_err("Adobe RGB Mode Off.\n"); - } else { + } else{ pr_err("This Panel not support Adobe RGB mode Off.\n"); } } @@ -1207,26 +1200,26 @@ int mdss_dsi_panel_set_dci_p3_mode(struct mdss_dsi_ctrl_pdata *ctrl, int level) struct dsi_panel_cmds *dci_p3_off_cmds; mutex_lock(&ctrl->panel_mode_lock); - if (!ctrl->is_panel_on) { + if (!ctrl->is_panel_on){ mutex_unlock(&ctrl->panel_mode_lock); return 0; - } + } dci_p3_on_cmds = &ctrl->dci_p3_on_cmds; dci_p3_off_cmds = &ctrl->dci_p3_off_cmds; - if (level) { - if (dci_p3_on_cmds->cmd_cnt) { + if (level){ + if (dci_p3_on_cmds->cmd_cnt){ mdss_dsi_panel_cmds_send(ctrl, dci_p3_on_cmds, CMD_REQ_COMMIT); pr_err("DCI-P3 Mode On.\n"); - } else { + } else{ pr_err("This Panel not support DCI-P3 mode On.\n"); } - } else { - if (dci_p3_off_cmds->cmd_cnt) { + } else{ + if (dci_p3_off_cmds->cmd_cnt){ mdss_dsi_panel_cmds_send(ctrl, dci_p3_off_cmds, CMD_REQ_COMMIT); pr_err("DCI-P3 Mode Off.\n"); - } else { + } else{ pr_err("This Panel not support DCI-P3 mode Off.\n"); } } @@ -1245,7 +1238,7 @@ int mdss_dsi_panel_set_night_mode(struct mdss_dsi_ctrl_pdata *ctrl, int level) struct dsi_panel_cmds *night_mode_off_cmds; mutex_lock(&ctrl->panel_mode_lock); - if (!ctrl->is_panel_on) { + if (!ctrl->is_panel_on){ mutex_unlock(&ctrl->panel_mode_lock); return 0; } @@ -1253,20 +1246,20 @@ int mdss_dsi_panel_set_night_mode(struct mdss_dsi_ctrl_pdata *ctrl, int level) /* night mode same as sRGB mode */ night_mode_off_cmds = &ctrl->night_mode_off_cmds; /* night mode same as sRGB mode */ - if (level) { - if (night_mode_on_cmds->cmd_cnt) { + if (level){ + if (night_mode_on_cmds->cmd_cnt){ mdss_dsi_panel_cmds_send(ctrl, night_mode_on_cmds, CMD_REQ_COMMIT); pr_err("Night Mode On (night mode).\n"); - } else { + } else{ pr_err("This panel not support Night mode on (night mode).\n"); } - } else { - if (night_mode_off_cmds->cmd_cnt) { + } else{ + if (night_mode_off_cmds->cmd_cnt){ mdss_dsi_panel_cmds_send(ctrl, night_mode_off_cmds, CMD_REQ_COMMIT); pr_err("Night Mode off (night mode).\n"); - } else { + } else{ pr_err("This panel not support night mode off (night mode).\n"); } } @@ -1284,7 +1277,7 @@ int mdss_dsi_panel_set_oneplus_mode(struct mdss_dsi_ctrl_pdata *ctrl, int level) struct dsi_panel_cmds *oneplus_mode_off_cmds; mutex_lock(&ctrl->panel_mode_lock); - if (!ctrl->is_panel_on) { + if (!ctrl->is_panel_on){ mutex_unlock(&ctrl->panel_mode_lock); return 0; } @@ -1293,20 +1286,20 @@ int mdss_dsi_panel_set_oneplus_mode(struct mdss_dsi_ctrl_pdata *ctrl, int level) oneplus_mode_off_cmds = &ctrl->oneplus_mode_off_cmds; /* night mode same as sRGB mode */ - if (level) { - if (oneplus_mode_on_cmds->cmd_cnt) { + if (level){ + if (oneplus_mode_on_cmds->cmd_cnt){ mdss_dsi_panel_cmds_send(ctrl, oneplus_mode_on_cmds, CMD_REQ_COMMIT); pr_err("oneplus Mode On (oneplus mode).\n"); - } else { + } else{ pr_err("This panel not support oneplus mode on (oneplus mode).\n"); } - } else { - if (oneplus_mode_off_cmds->cmd_cnt) { + } else{ + if (oneplus_mode_off_cmds->cmd_cnt){ mdss_dsi_panel_cmds_send(ctrl, oneplus_mode_off_cmds, CMD_REQ_COMMIT); pr_err("oneplus Mode off (oneplus mode).\n"); - } else { + } else{ pr_err("This panel not support oneplus mode off (oneplus mode).\n"); } } @@ -1326,7 +1319,7 @@ int mdss_dsi_panel_set_adaption_mode(struct mdss_dsi_ctrl_pdata *ctrl, struct dsi_panel_cmds *adaption_mode_off_cmds; mutex_lock(&ctrl->panel_mode_lock); - if (!ctrl->is_panel_on) { + if (!ctrl->is_panel_on){ mutex_unlock(&ctrl->panel_mode_lock); return 0; } @@ -1334,32 +1327,32 @@ int mdss_dsi_panel_set_adaption_mode(struct mdss_dsi_ctrl_pdata *ctrl, /* night mode same as sRGB mode */ adaption_mode_off_cmds = &ctrl->adaption_mode_off_cmds; /* night mode same as sRGB mode */ - if (level) { - if (adaption_mode_on_cmds->cmd_cnt) { + if (level){ + if (adaption_mode_on_cmds->cmd_cnt){ mdss_dsi_panel_cmds_send(ctrl, adaption_mode_on_cmds, CMD_REQ_COMMIT); pr_err("Adaption Mode On (adaption mode).\n"); - } else { + } else{ pr_err("This panel not support Adaption mode on (adaption mode).\n"); } - } else { - if (adaption_mode_off_cmds->cmd_cnt) { + } else{ + if (adaption_mode_off_cmds->cmd_cnt){ mdss_dsi_panel_cmds_send(ctrl, adaption_mode_off_cmds, CMD_REQ_COMMIT); pr_err("Adaption Mode off (adaption mode).\n"); - } else { + } else{ pr_err("This panel not support adaption mode off (adaption mode).\n"); } } mutex_unlock(&ctrl->panel_mode_lock); return 0; } + int mdss_dsi_panel_get_adaption_mode(struct mdss_dsi_ctrl_pdata *ctrl) { return ctrl->adaption_mode; } - static int mdss_dsi_panel_on(struct mdss_panel_data *pdata) { struct mdss_dsi_ctrl_pdata *ctrl = NULL; @@ -1368,13 +1361,13 @@ static int mdss_dsi_panel_on(struct mdss_panel_data *pdata) int ret = 0; if (pdata == NULL) { - pr_err("%s: Invalid input data\n", __func__); - return -EINVAL; + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; } - pr_err("%s start\n", __func__); + pinfo = &pdata->panel_info; ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata, - panel_data); + panel_data); pr_debug("%s: ndx=%d\n", __func__, ctrl->ndx); @@ -1386,50 +1379,51 @@ static int mdss_dsi_panel_on(struct mdss_panel_data *pdata) on_cmds = &ctrl->on_cmds; if ((pinfo->mipi.dms_mode == DYNAMIC_MODE_SWITCH_IMMEDIATE) && - (pinfo->mipi.boot_mode != pinfo->mipi.mode)) + (pinfo->mipi.boot_mode != pinfo->mipi.mode)) on_cmds = &ctrl->post_dms_on_cmds; pr_debug("%s: ndx=%d cmd_cnt=%d\n", __func__, - ctrl->ndx, on_cmds->cmd_cnt); + ctrl->ndx, on_cmds->cmd_cnt); mutex_lock(&ctrl->panel_mode_lock); ctrl->is_panel_on = true; mutex_unlock(&ctrl->panel_mode_lock); - if (mdss_dsi_panel_get_srgb_mode(ctrl)) { + if (mdss_dsi_panel_get_srgb_mode(ctrl)){ mdss_dsi_panel_set_srgb_mode(ctrl, mdss_dsi_panel_get_srgb_mode(ctrl)); } - if (mdss_dsi_panel_get_adobe_rgb_mode(ctrl)) { + if (mdss_dsi_panel_get_adobe_rgb_mode(ctrl)){ mdss_dsi_panel_set_adobe_rgb_mode(ctrl, mdss_dsi_panel_get_adobe_rgb_mode(ctrl)); } - if (mdss_dsi_panel_get_dci_p3_mode(ctrl)) { + if (mdss_dsi_panel_get_dci_p3_mode(ctrl)){ mdss_dsi_panel_set_dci_p3_mode(ctrl, mdss_dsi_panel_get_dci_p3_mode(ctrl)); } - if (mdss_dsi_panel_get_night_mode(ctrl)) { + if (mdss_dsi_panel_get_night_mode(ctrl)){ mdss_dsi_panel_set_night_mode(ctrl, mdss_dsi_panel_get_night_mode(ctrl)); } - if (mdss_dsi_panel_get_oneplus_mode(ctrl)) { + if (mdss_dsi_panel_get_oneplus_mode(ctrl)){ mdss_dsi_panel_set_oneplus_mode(ctrl, mdss_dsi_panel_get_oneplus_mode(ctrl)); } - if (mdss_dsi_panel_get_adaption_mode(ctrl)) { + if (mdss_dsi_panel_get_adaption_mode(ctrl)){ mdss_dsi_panel_set_adaption_mode(ctrl, - mdss_dsi_panel_get_adaption_mode(ctrl)); + mdss_dsi_panel_get_adaption_mode(ctrl)); } + #if defined(CONFIG_IRIS2P_FULL_SUPPORT) #if !defined(WITHOUT_IRIS) iris_init(ctrl); #endif - if (on_cmds->cmd_cnt) - mdss_dsi_panel_cmds_send(ctrl, on_cmds, CMD_REQ_COMMIT); + if (on_cmds->cmd_cnt) + mdss_dsi_panel_cmds_send(ctrl, on_cmds, CMD_REQ_COMMIT); #if !defined(WITHOUT_IRIS) iris_lightup(ctrl); #endif @@ -1437,176 +1431,172 @@ static int mdss_dsi_panel_on(struct mdss_panel_data *pdata) if (on_cmds->cmd_cnt) mdss_dsi_panel_cmds_send(ctrl, on_cmds, CMD_REQ_COMMIT); #endif - - - if (pinfo->compression_mode == COMPRESSION_DSC) - mdss_dsi_panel_dsc_pps_send(ctrl, pinfo); - - if (ctrl->ds_registered) - mdss_dba_utils_video_on(pinfo->dba_data, pinfo); - - /* Ensure low persistence mode is set as before */ - mdss_dsi_panel_apply_display_setting(pdata, pinfo->persist_mode); - - end: - pr_debug("%s:-\n", __func__); - pr_err("%s end\n", __func__); - return ret; + + if (pinfo->compression_mode == COMPRESSION_DSC) + mdss_dsi_panel_dsc_pps_send(ctrl, pinfo); + + if (ctrl->ds_registered) + mdss_dba_utils_video_on(pinfo->dba_data, pinfo); + + /* Ensure low persistence mode is set as before */ + mdss_dsi_panel_apply_display_setting(pdata, pinfo->persist_mode); + +end: + pr_debug("%s:-\n", __func__); + return ret; } - + static int mdss_dsi_post_panel_on(struct mdss_panel_data *pdata) { - struct mdss_dsi_ctrl_pdata *ctrl = NULL; - struct mdss_panel_info *pinfo; - struct dsi_panel_cmds *cmds; - u32 vsync_period = 0; - - if (pdata == NULL) { - pr_err("%s: Invalid input data\n", __func__); - return -EINVAL; - } - pr_err("%s start\n", __func__); - ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata, - panel_data); - - pr_debug("%s: ctrl=%pK ndx=%d\n", __func__, ctrl, ctrl->ndx); - - pinfo = &pdata->panel_info; - if (pinfo->dcs_cmd_by_left && ctrl->ndx != DSI_CTRL_LEFT) - goto end; - - cmds = &ctrl->post_panel_on_cmds; - if (cmds->cmd_cnt) { - msleep(VSYNC_DELAY); /* wait for a vsync passed */ - mdss_dsi_panel_cmds_send(ctrl, cmds, CMD_REQ_COMMIT); - } - - if (pinfo->is_dba_panel && pinfo->is_pluggable) { - /* ensure at least 1 frame transfers to down stream device */ - vsync_period = (MSEC_PER_SEC / pinfo->mipi.frame_rate) + 1; - msleep(vsync_period); - mdss_dba_utils_hdcp_enable(pinfo->dba_data, true); - } - - end: - pr_debug("%s:-\n", __func__); - pr_err("%s end\n", __func__); - return 0; + struct mdss_dsi_ctrl_pdata *ctrl = NULL; + struct mdss_panel_info *pinfo; + struct dsi_panel_cmds *cmds; + u32 vsync_period = 0; + + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; + } + + ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + + pr_debug("%s: ctrl=%pK ndx=%d\n", __func__, ctrl, ctrl->ndx); + + pinfo = &pdata->panel_info; + if (pinfo->dcs_cmd_by_left && ctrl->ndx != DSI_CTRL_LEFT) + goto end; + + cmds = &ctrl->post_panel_on_cmds; + if (cmds->cmd_cnt) { + msleep(VSYNC_DELAY); /* wait for a vsync passed */ + mdss_dsi_panel_cmds_send(ctrl, cmds, CMD_REQ_COMMIT); + } + + if (pinfo->is_dba_panel && pinfo->is_pluggable) { + /* ensure at least 1 frame transfers to down stream device */ + vsync_period = (MSEC_PER_SEC / pinfo->mipi.frame_rate) + 1; + msleep(vsync_period); + mdss_dba_utils_hdcp_enable(pinfo->dba_data, true); + } + +end: + pr_debug("%s:-\n", __func__); + return 0; } - + static int mdss_dsi_panel_off(struct mdss_panel_data *pdata) { - struct mdss_dsi_ctrl_pdata *ctrl = NULL; - struct mdss_panel_info *pinfo; - - - if (pdata == NULL) { - pr_err("%s: Invalid input data\n", __func__); - return -EINVAL; - } - pr_err("%s start\n", __func__); - pinfo = &pdata->panel_info; - ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata, - panel_data); - - pr_debug("%s: ctrl=%pK ndx=%d\n", __func__, ctrl, ctrl->ndx); - - if (pinfo->dcs_cmd_by_left) { - if (ctrl->ndx != DSI_CTRL_LEFT) - goto end; - } - mutex_lock(&ctrl->panel_mode_lock); - ctrl->is_panel_on = false; - mutex_unlock(&ctrl->panel_mode_lock); + struct mdss_dsi_ctrl_pdata *ctrl = NULL; + struct mdss_panel_info *pinfo; + + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; + } + + pinfo = &pdata->panel_info; + ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + + pr_debug("%s: ctrl=%pK ndx=%d\n", __func__, ctrl, ctrl->ndx); + + if (pinfo->dcs_cmd_by_left) { + if (ctrl->ndx != DSI_CTRL_LEFT) + goto end; + } + + mutex_lock(&ctrl->panel_mode_lock); + ctrl->is_panel_on = false; + mutex_unlock(&ctrl->panel_mode_lock); #if defined(CONFIG_IRIS2P_FULL_SUPPORT) #if !defined(WITHOUT_IRIS) - iris_lightoff(ctrl); + iris_lightoff(ctrl); #endif - if (ctrl->off_cmds.cmd_cnt) - mdss_dsi_panel_cmds_send(ctrl, &ctrl->off_cmds, CMD_REQ_COMMIT); + if (ctrl->off_cmds.cmd_cnt) + mdss_dsi_panel_cmds_send(ctrl, &ctrl->off_cmds, CMD_REQ_COMMIT); #else - if (ctrl->off_cmds.cmd_cnt) - mdss_dsi_panel_cmds_send(ctrl, &ctrl->off_cmds, CMD_REQ_COMMIT); + if (ctrl->off_cmds.cmd_cnt) + mdss_dsi_panel_cmds_send(ctrl, &ctrl->off_cmds, CMD_REQ_COMMIT); #endif - if (ctrl->ds_registered && pinfo->is_pluggable) { - mdss_dba_utils_video_off(pinfo->dba_data); - mdss_dba_utils_hdcp_enable(pinfo->dba_data, false); - } - - end: - pr_debug("%s:-\n", __func__); - pr_err("%s end\n", __func__); - return 0; + if (ctrl->ds_registered && pinfo->is_pluggable) { + mdss_dba_utils_video_off(pinfo->dba_data); + mdss_dba_utils_hdcp_enable(pinfo->dba_data, false); + } + +end: + pr_debug("%s:-\n", __func__); + return 0; } - + static int mdss_dsi_panel_low_power_config(struct mdss_panel_data *pdata, - int enable) + int enable) { - struct mdss_dsi_ctrl_pdata *ctrl = NULL; - struct mdss_panel_info *pinfo; - - if (pdata == NULL) { - pr_err("%s: Invalid input data\n", __func__); - return -EINVAL; - } - - pinfo = &pdata->panel_info; - ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata, - panel_data); - - pr_debug("%s: ctrl=%pK ndx=%d enable=%d\n", __func__, ctrl, ctrl->ndx, - enable); - - /* Any panel specific low power commands/config */ - - pr_debug("%s:-\n", __func__); - return 0; + struct mdss_dsi_ctrl_pdata *ctrl = NULL; + struct mdss_panel_info *pinfo; + + if (pdata == NULL) { + pr_err("%s: Invalid input data\n", __func__); + return -EINVAL; + } + + pinfo = &pdata->panel_info; + ctrl = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + + pr_debug("%s: ctrl=%pK ndx=%d enable=%d\n", __func__, ctrl, ctrl->ndx, + enable); + + /* Any panel specific low power commands/config */ + + pr_debug("%s:-\n", __func__); + return 0; } - + static void mdss_dsi_parse_mdp_kickoff_threshold(struct device_node *np, - struct mdss_panel_info *pinfo) + struct mdss_panel_info *pinfo) { - int len, rc; - const u32 *src; - u32 tmp; - u32 max_delay_us; - - pinfo->mdp_koff_thshold = false; - src = of_get_property(np, "qcom,mdss-mdp-kickoff-threshold", &len); - if (!src || (len == 0)) - return; - - rc = of_property_read_u32(np, "qcom,mdss-mdp-kickoff-delay", &tmp); - if (!rc) - pinfo->mdp_koff_delay = tmp; - else - return; - - if (pinfo->mipi.frame_rate == 0) { - pr_err("cannot enable guard window, unexpected panel fps\n"); - return; - } - - pinfo->mdp_koff_thshold_low = be32_to_cpu(src[0]); - pinfo->mdp_koff_thshold_high = be32_to_cpu(src[1]); - max_delay_us = 1000000 / pinfo->mipi.frame_rate; - - /* enable the feature if threshold is valid */ - if ((pinfo->mdp_koff_thshold_low < pinfo->mdp_koff_thshold_high) && - ((pinfo->mdp_koff_delay > 0) || - (pinfo->mdp_koff_delay < max_delay_us))) - pinfo->mdp_koff_thshold = true; - - pr_debug("panel kickoff thshold:[%d, %d] delay:%d (max:%d) enable:%d\n", - pinfo->mdp_koff_thshold_low, - pinfo->mdp_koff_thshold_high, - pinfo->mdp_koff_delay, - max_delay_us, - pinfo->mdp_koff_thshold); + int len, rc; + const u32 *src; + u32 tmp; + u32 max_delay_us; + + pinfo->mdp_koff_thshold = false; + src = of_get_property(np, "qcom,mdss-mdp-kickoff-threshold", &len); + if (!src || (len == 0)) + return; + + rc = of_property_read_u32(np, "qcom,mdss-mdp-kickoff-delay", &tmp); + if (!rc) + pinfo->mdp_koff_delay = tmp; + else + return; + + if (pinfo->mipi.frame_rate == 0) { + pr_err("cannot enable guard window, unexpected panel fps\n"); + return; + } + + pinfo->mdp_koff_thshold_low = be32_to_cpu(src[0]); + pinfo->mdp_koff_thshold_high = be32_to_cpu(src[1]); + max_delay_us = 1000000 / pinfo->mipi.frame_rate; + + /* enable the feature if threshold is valid */ + if ((pinfo->mdp_koff_thshold_low < pinfo->mdp_koff_thshold_high) && + ((pinfo->mdp_koff_delay > 0) || + (pinfo->mdp_koff_delay < max_delay_us))) + pinfo->mdp_koff_thshold = true; + + pr_debug("panel kickoff thshold:[%d, %d] delay:%d (max:%d) enable:%d\n", + pinfo->mdp_koff_thshold_low, + pinfo->mdp_koff_thshold_high, + pinfo->mdp_koff_delay, + max_delay_us, + pinfo->mdp_koff_thshold); } - + static void mdss_dsi_parse_trigger(struct device_node *np, char *trigger, - char *trigger_key) + char *trigger_key) { const char *data; @@ -1623,10 +1613,10 @@ static void mdss_dsi_parse_trigger(struct device_node *np, char *trigger, *trigger = DSI_CMD_TRIGGER_SW_TE; } } - + static int mdss_dsi_parse_dcs_cmds(struct device_node *np, - struct dsi_panel_cmds *pcmds, char *cmd_key, char *link_key) + struct dsi_panel_cmds *pcmds, char *cmd_key, char *link_key) { const char *data; int blen = 0, len; @@ -1664,13 +1654,13 @@ static int mdss_dsi_parse_dcs_cmds(struct device_node *np, len -= dchdr->dlen; cnt++; } - + if (len != 0) { pr_err("%s: dcs_cmd=%x len=%d error!", __func__, buf[0], blen); goto exit_free; } - + pcmds->cmds = kzalloc(cnt * sizeof(struct dsi_cmd_desc), GFP_KERNEL); if (!pcmds->cmds) @@ -1691,7 +1681,7 @@ static int mdss_dsi_parse_dcs_cmds(struct device_node *np, bp += dchdr->dlen; len -= dchdr->dlen; } - + /*Set default link state to LP Mode*/ pcmds->link_state = DSI_LP_MODE; @@ -1712,13 +1702,12 @@ exit_free: kfree(buf); return -ENOMEM; } - - + + int mdss_panel_get_dst_fmt(u32 bpp, char mipi_mode, u32 pixel_packing, - char *dst_format) + char *dst_format) { int rc = 0; - switch (bpp) { case 3: *dst_format = DSI_CMD_DST_FORMAT_RGB111; @@ -1743,7 +1732,6 @@ int mdss_panel_get_dst_fmt(u32 bpp, char mipi_mode, u32 pixel_packing, } break; case 18: - switch (mipi_mode) { case DSI_VIDEO_MODE: if (pixel_packing == 0) @@ -1781,1668 +1769,1690 @@ int mdss_panel_get_dst_fmt(u32 bpp, char mipi_mode, u32 pixel_packing, } return rc; } - - static int mdss_dsi_parse_fbc_params(struct device_node *np, - struct mdss_panel_timing *timing) - { - int rc, fbc_enabled = 0; - u32 tmp; - struct fbc_panel_info *fbc = &timing->fbc; - - fbc_enabled = of_property_read_bool(np, "qcom,mdss-dsi-fbc-enable"); - if (fbc_enabled) { - pr_debug("%s:%d FBC panel enabled.\n", __func__, __LINE__); - fbc->enabled = 1; - rc = of_property_read_u32(np, "qcom,mdss-dsi-fbc-bpp", &tmp); - fbc->target_bpp = (!rc ? tmp : 24); - rc = of_property_read_u32(np, "qcom,mdss-dsi-fbc-packing", - &tmp); - fbc->comp_mode = (!rc ? tmp : 0); - fbc->qerr_enable = of_property_read_bool(np, - "qcom,mdss-dsi-fbc-quant-error"); - rc = of_property_read_u32(np, "qcom,mdss-dsi-fbc-bias", &tmp); - fbc->cd_bias = (!rc ? tmp : 0); - fbc->pat_enable = of_property_read_bool(np, - "qcom,mdss-dsi-fbc-pat-mode"); - fbc->vlc_enable = of_property_read_bool(np, - "qcom,mdss-dsi-fbc-vlc-mode"); - fbc->bflc_enable = of_property_read_bool(np, - "qcom,mdss-dsi-fbc-bflc-mode"); - rc = of_property_read_u32(np, "qcom,mdss-dsi-fbc-h-line-budget", - &tmp); - fbc->line_x_budget = (!rc ? tmp : 0); - rc = of_property_read_u32(np, "qcom,mdss-dsi-fbc-budget-ctrl", - &tmp); - fbc->block_x_budget = (!rc ? tmp : 0); - rc = of_property_read_u32(np, "qcom,mdss-dsi-fbc-block-budget", - &tmp); - fbc->block_budget = (!rc ? tmp : 0); - rc = of_property_read_u32(np, - "qcom,mdss-dsi-fbc-lossless-threshold", &tmp); - fbc->lossless_mode_thd = (!rc ? tmp : 0); - rc = of_property_read_u32(np, - "qcom,mdss-dsi-fbc-lossy-threshold", &tmp); - fbc->lossy_mode_thd = (!rc ? tmp : 0); - rc = of_property_read_u32(np, "qcom,mdss-dsi-fbc-rgb-threshold", - &tmp); - fbc->lossy_rgb_thd = (!rc ? tmp : 0); - rc = of_property_read_u32(np, - "qcom,mdss-dsi-fbc-lossy-mode-idx", &tmp); - fbc->lossy_mode_idx = (!rc ? tmp : 0); - rc = of_property_read_u32(np, - "qcom,mdss-dsi-fbc-slice-height", &tmp); - fbc->slice_height = (!rc ? tmp : 0); - fbc->pred_mode = of_property_read_bool(np, - "qcom,mdss-dsi-fbc-2d-pred-mode"); - fbc->enc_mode = of_property_read_bool(np, - "qcom,mdss-dsi-fbc-ver2-mode"); - rc = of_property_read_u32(np, - "qcom,mdss-dsi-fbc-max-pred-err", &tmp); - fbc->max_pred_err = (!rc ? tmp : 0); - - timing->compression_mode = COMPRESSION_FBC; - } else { - pr_debug("%s:%d Panel does not support FBC.\n", - __func__, __LINE__); - fbc->enabled = 0; - fbc->target_bpp = 24; - } - return 0; - } - - void mdss_dsi_panel_dsc_pps_send(struct mdss_dsi_ctrl_pdata *ctrl, - struct mdss_panel_info *pinfo) - { - struct dsi_panel_cmds pcmds; - struct dsi_cmd_desc cmd; - - if (!pinfo || (pinfo->compression_mode != COMPRESSION_DSC)) - return; - - memset(&pcmds, 0, sizeof(pcmds)); - memset(&cmd, 0, sizeof(cmd)); - - cmd.dchdr.dlen = mdss_panel_dsc_prepare_pps_buf(&pinfo->dsc, - ctrl->pps_buf, 0); - cmd.dchdr.dtype = DTYPE_PPS; - cmd.dchdr.last = 1; - cmd.dchdr.wait = 10; - cmd.dchdr.vc = 0; - cmd.dchdr.ack = 0; - cmd.payload = ctrl->pps_buf; - - pcmds.cmd_cnt = 1; - pcmds.cmds = &cmd; - pcmds.link_state = DSI_LP_MODE; - - mdss_dsi_panel_cmds_send(ctrl, &pcmds, CMD_REQ_COMMIT); - } - - static int mdss_dsi_parse_hdr_settings(struct device_node *np, - struct mdss_panel_info *pinfo) - { - int rc = 0; - struct mdss_panel_hdr_properties *hdr_prop; - - if (!np) { - pr_err("%s: device node pointer is NULL\n", __func__); - return -EINVAL; - } - - if (!pinfo) { - pr_err("%s: panel info is NULL\n", __func__); - return -EINVAL; - } - - hdr_prop = &pinfo->hdr_properties; - hdr_prop->hdr_enabled = of_property_read_bool(np, - "qcom,mdss-dsi-panel-hdr-enabled"); - - if (hdr_prop->hdr_enabled) { - rc = of_property_read_u32_array(np, - "qcom,mdss-dsi-panel-hdr-color-primaries", - hdr_prop->display_primaries, - DISPLAY_PRIMARIES_COUNT); - if (rc) { - pr_info("%s:%d, Unable to read color primaries,rc:%u", - __func__, __LINE__, - hdr_prop->hdr_enabled = false); - } - - rc = of_property_read_u32(np, - "qcom,mdss-dsi-panel-peak-brightness", - &(hdr_prop->peak_brightness)); - if (rc) { - pr_info("%s:%d, Unable to read hdr brightness, rc:%u", - __func__, __LINE__, rc); - hdr_prop->hdr_enabled = false; - } - - rc = of_property_read_u32(np, - "qcom,mdss-dsi-panel-blackness-level", - &(hdr_prop->blackness_level)); - if (rc) { - pr_info("%s:%d, Unable to read hdr brightness, rc:%u", - __func__, __LINE__, rc); - hdr_prop->hdr_enabled = false; - } - } - return 0; - } - - static int mdss_dsi_parse_split_link_settings(struct device_node *np, - struct mdss_panel_info *pinfo) - { - u32 tmp; - int rc = 0; - - if (!np) { - pr_err("%s: device node pointer is NULL\n", __func__); - return -EINVAL; - } - - if (!pinfo) { - pr_err("%s: panel info is NULL\n", __func__); - return -EINVAL; - } - - pinfo->split_link_enabled = of_property_read_bool(np, - "qcom,split-link-enabled"); - - if (pinfo->split_link_enabled) { - rc = of_property_read_u32(np, - "qcom,sublinks-count", &tmp); - /* default num of sublink is 1*/ - pinfo->mipi.num_of_sublinks = (!rc ? tmp : 1); - - rc = of_property_read_u32(np, - "qcom,lanes-per-sublink", &tmp); - /* default num of lanes per sublink is 1 */ - pinfo->mipi.lanes_per_sublink = (!rc ? tmp : 1); - } - - pr_info("%s: enable %d sublinks-count %d lanes per sublink %d\n", - __func__, pinfo->split_link_enabled, - pinfo->mipi.num_of_sublinks, - pinfo->mipi.lanes_per_sublink); - return 0; - } - - static int mdss_dsi_parse_dsc_version(struct device_node *np, - struct mdss_panel_timing *timing) - { - u32 data; - int rc = 0; - struct dsc_desc *dsc = &timing->dsc; - - rc = of_property_read_u32(np, "qcom,mdss-dsc-version", &data); - if (rc) { - dsc->version = 0x11; - rc = 0; - } else { - dsc->version = data & 0xff; - /* only support DSC 1.1 rev */ - if (dsc->version != 0x11) { - pr_err("%s: DSC version:%d not supported\n", __func__, - dsc->version); - rc = -EINVAL; - goto end; - } - } - - rc = of_property_read_u32(np, "qcom,mdss-dsc-scr-version", &data); - if (rc) { - dsc->scr_rev = 0x0; - rc = 0; - } else { - dsc->scr_rev = data & 0xff; - /* only one scr rev supported */ - if (dsc->scr_rev > 0x1) { - pr_err("%s: DSC scr version:%d not supported\n", - __func__, dsc->scr_rev); - rc = -EINVAL; - goto end; - } - } - - end: - return rc; - } - - static int mdss_dsi_parse_dsc_params(struct device_node *np, - struct mdss_panel_timing *timing, bool is_split_display) - { - u32 data, intf_width; - int rc = 0; - struct dsc_desc *dsc = &timing->dsc; - - if (!np) { - pr_err("%s: device node pointer is NULL\n", __func__); - return -EINVAL; - } - - rc = of_property_read_u32(np, "qcom,mdss-dsc-encoders", &data); - if (rc) { - if (!of_find_property(np, "qcom,mdss-dsc-encoders", NULL)) { - /* property is not defined, default to 1 */ - data = 1; - } else { - pr_err("%s: Error parsing qcom,mdss-dsc-encoders\n", - __func__); - goto end; - } - } - - timing->dsc_enc_total = data; - - if (is_split_display && (timing->dsc_enc_total > 1)) { - pr_err("%s: Error: for split displays, more than 1 dsc encoder per panel is not allowed.\n", - __func__); - goto end; - } - - rc = of_property_read_u32(np, "qcom,mdss-dsc-slice-height", &data); - if (rc) - goto end; - dsc->slice_height = data; - - rc = of_property_read_u32(np, "qcom,mdss-dsc-slice-width", &data); - if (rc) - goto end; - dsc->slice_width = data; - intf_width = timing->xres; - - if (intf_width % dsc->slice_width) { - pr_err("%s: Error: multiple of slice-width:%d should match panel-width:%d\n", - __func__, dsc->slice_width, intf_width); - goto end; - } - - data = intf_width / dsc->slice_width; - if (((timing->dsc_enc_total > 1) && ((data != 2) && (data != 4))) || - ((timing->dsc_enc_total == 1) && (data > 2))) { - pr_err("%s: Error: max 2 slice per encoder. slice-width:%d should match panel-width:%d dsc_enc_total:%d\n", - __func__, dsc->slice_width, - intf_width, timing->dsc_enc_total); - goto end; - } - - rc = of_property_read_u32(np, "qcom,mdss-dsc-slice-per-pkt", &data); - if (rc) - goto end; - dsc->slice_per_pkt = data; - - /* - * slice_per_pkt can be either 1 or all slices_per_intf - */ - if ((dsc->slice_per_pkt > 1) && (dsc->slice_per_pkt != - DIV_ROUND_UP(intf_width, dsc->slice_width))) { - pr_err("Error: slice_per_pkt can be either 1 or all slices_per_intf\n"); - pr_err("%s: slice_per_pkt=%d, slice_width=%d intf_width=%d\n", - __func__, - dsc->slice_per_pkt, dsc->slice_width, intf_width); - rc = -EINVAL; - goto end; - } - - pr_debug("%s: num_enc:%d :slice h=%d w=%d s_pkt=%d\n", __func__, - timing->dsc_enc_total, dsc->slice_height, - dsc->slice_width, dsc->slice_per_pkt); - - rc = of_property_read_u32(np, "qcom,mdss-dsc-bit-per-component", &data); - if (rc) - goto end; - dsc->bpc = data; - - rc = of_property_read_u32(np, "qcom,mdss-dsc-bit-per-pixel", &data); - if (rc) - goto end; - dsc->bpp = data; - - pr_debug("%s: bpc=%d bpp=%d\n", __func__, - dsc->bpc, dsc->bpp); - - dsc->block_pred_enable = of_property_read_bool(np, - "qcom,mdss-dsc-block-prediction-enable"); - - dsc->enable_422 = 0; - dsc->convert_rgb = 1; - dsc->vbr_enable = 0; - - dsc->config_by_manufacture_cmd = of_property_read_bool(np, - "qcom,mdss-dsc-config-by-manufacture-cmd"); - - mdss_panel_dsc_parameters_calc(&timing->dsc); - mdss_panel_dsc_pclk_param_calc(&timing->dsc, intf_width); - - timing->dsc.full_frame_slices = - DIV_ROUND_UP(intf_width, timing->dsc.slice_width); - - timing->compression_mode = COMPRESSION_DSC; - - end: - return rc; - } - - static struct device_node *mdss_dsi_panel_get_dsc_cfg_np( - struct device_node *np, struct mdss_panel_data *panel_data, - bool default_timing) - { - struct device_node *dsc_cfg_np = NULL; - - - /* Read the dsc config node specified by command line */ - if (default_timing) { - dsc_cfg_np = of_get_child_by_name(np, - panel_data->dsc_cfg_np_name); - if (!dsc_cfg_np) - pr_warn_once("%s: cannot find dsc config node:%s\n", - __func__, panel_data->dsc_cfg_np_name); - } - - /* - * Fall back to default from DT as nothing is specified - * in command line. - */ - if (!dsc_cfg_np && of_find_property(np, "qcom,config-select", NULL)) { - dsc_cfg_np = of_parse_phandle(np, "qcom,config-select", 0); - if (!dsc_cfg_np) - pr_warn_once("%s:err parsing qcom,config-select\n", - __func__); - } - - return dsc_cfg_np; - } - - static int mdss_dsi_parse_topology_config(struct device_node *np, - struct dsi_panel_timing *pt, struct mdss_panel_data *panel_data, - bool default_timing) - { - int rc = 0; - bool is_split_display = panel_data->panel_info.is_split_display; - const char *data; - struct mdss_panel_timing *timing = &pt->timing; - struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; - struct mdss_panel_info *pinfo; - struct device_node *cfg_np = NULL; - - ctrl_pdata = container_of(panel_data, struct mdss_dsi_ctrl_pdata, - panel_data); - pinfo = &ctrl_pdata->panel_data.panel_info; - - cfg_np = mdss_dsi_panel_get_dsc_cfg_np(np, - &ctrl_pdata->panel_data, default_timing); - - if (cfg_np) { - if (!of_property_read_u32_array(cfg_np, "qcom,lm-split", - timing->lm_widths, 2)) { - if (mdss_dsi_is_hw_config_split(ctrl_pdata->shared_data) - && (timing->lm_widths[1] != 0)) { - pr_err("%s: lm-split not allowed with split display\n", - __func__); - rc = -EINVAL; - goto end; - } - } - - if (!of_property_read_string(cfg_np, "qcom,split-mode", - &data) && !strcmp(data, "pingpong-split")) - pinfo->use_pingpong_split = true; - - if (((timing->lm_widths[0]) || (timing->lm_widths[1])) && - pinfo->use_pingpong_split) { - pr_err("%s: pingpong_split cannot be used when lm-split[%d,%d] is specified\n", - __func__, - timing->lm_widths[0], timing->lm_widths[1]); - return -EINVAL; - } - - pr_info("%s: cfg_node name %s lm_split:%dx%d pp_split:%s\n", - __func__, cfg_np->name, - timing->lm_widths[0], timing->lm_widths[1], - pinfo->use_pingpong_split ? "yes" : "no"); - } - - if (!pinfo->use_pingpong_split && - (timing->lm_widths[0] == 0) && (timing->lm_widths[1] == 0)) - timing->lm_widths[0] = pt->timing.xres; - - data = of_get_property(np, "qcom,compression-mode", NULL); - if (data) { - if (cfg_np && !strcmp(data, "dsc")) { - rc = mdss_dsi_parse_dsc_version(np, &pt->timing); - if (rc) - goto end; - - pinfo->send_pps_before_switch = - of_property_read_bool(np, - "qcom,mdss-dsi-send-pps-before-switch"); - - rc = mdss_dsi_parse_dsc_params(cfg_np, &pt->timing, - is_split_display); - } else if (!strcmp(data, "fbc")) { - rc = mdss_dsi_parse_fbc_params(np, &pt->timing); - } - } - - end: - of_node_put(cfg_np); - return rc; - } - - static void mdss_panel_parse_te_params(struct device_node *np, - struct mdss_panel_timing *timing) - { - struct mdss_mdp_pp_tear_check *te = &timing->te; - u32 tmp; - int rc = 0; - /* - * TE default: dsi byte clock calculated base on 70 fps; - * around 14 ms to complete a kickoff cycle if te disabled; - * vclk_line base on 60 fps; write is faster than read; - * init == start == rdptr; - */ - te->tear_check_en = - !of_property_read_bool(np, "qcom,mdss-tear-check-disable"); - rc = of_property_read_u32 - (np, "qcom,mdss-tear-check-sync-cfg-height", &tmp); - te->sync_cfg_height = (!rc ? tmp : 0xfff0); - rc = of_property_read_u32 - (np, "qcom,mdss-tear-check-sync-init-val", &tmp); - te->vsync_init_val = (!rc ? tmp : timing->yres); - rc = of_property_read_u32 - (np, "qcom,mdss-tear-check-sync-threshold-start", &tmp); - te->sync_threshold_start = (!rc ? tmp : 4); - rc = of_property_read_u32 - (np, "qcom,mdss-tear-check-sync-threshold-continue", &tmp); - te->sync_threshold_continue = (!rc ? tmp : 4); - rc = of_property_read_u32(np, "qcom,mdss-tear-check-frame-rate", &tmp); - te->refx100 = (!rc ? tmp : 6000); - rc = of_property_read_u32 - (np, "qcom,mdss-tear-check-start-pos", &tmp); - te->start_pos = (!rc ? tmp : timing->yres); - rc = of_property_read_u32 - (np, "qcom,mdss-tear-check-rd-ptr-trigger-intr", &tmp); - te->rd_ptr_irq = (!rc ? tmp : timing->yres + 1); - te->wr_ptr_irq = 0; - } - - - static int mdss_dsi_parse_reset_seq(struct device_node *np, - u32 rst_seq[MDSS_DSI_RST_SEQ_LEN], u32 *rst_len, - const char *name) - { - int num = 0, i; - int rc; - struct property *data; - u32 tmp[MDSS_DSI_RST_SEQ_LEN]; - *rst_len = 0; - data = of_find_property(np, name, &num); - num /= sizeof(u32); - if (!data || !num || num > MDSS_DSI_RST_SEQ_LEN || num % 2) { - pr_debug("%s:%d, error reading %s, length found = %d\n", - __func__, __LINE__, name, num); - } else { - rc = of_property_read_u32_array(np, name, tmp, num); - if (rc) - pr_debug("%s:%d, error reading %s, rc = %d\n", - __func__, __LINE__, name, rc); - else { - for (i = 0; i < num; ++i) - rst_seq[i] = tmp[i]; - *rst_len = num; - } - } - return 0; - } - - static bool mdss_dsi_cmp_panel_reg_v2(struct mdss_dsi_ctrl_pdata *ctrl) - { - int i, j = 0; - int len = 0, *lenp; - int group = 0; - - lenp = ctrl->status_valid_params ?: ctrl->status_cmds_rlen; - - for (i = 0; i < ctrl->status_cmds.cmd_cnt; i++) - len += lenp[i]; - - for (i = 0; i < len; i++) { - pr_debug("[%i] return:0x%x status:0x%x\n", - i, (unsigned int)ctrl->return_buf[i], - (unsigned int)ctrl->status_value[j + i]); - MDSS_XLOG(ctrl->ndx, ctrl->return_buf[i], - ctrl->status_value[j + i]); - j += len; - } - - for (j = 0; j < ctrl->groups; ++j) { - for (i = 0; i < len; ++i) { - if (ctrl->return_buf[i] != - ctrl->status_value[group + i]) - break; - } - - if (i == len) - return true; - group += len; - } - - return false; - } - - static int mdss_dsi_gen_read_status(struct mdss_dsi_ctrl_pdata *ctrl_pdata) - { - if (!mdss_dsi_cmp_panel_reg_v2(ctrl_pdata)) { - pr_err("%s: Read back value from panel is incorrect\n", - __func__); - return -EINVAL; - } else { - return 1; - } - } - - static int mdss_dsi_nt35596_read_status(struct mdss_dsi_ctrl_pdata *ctrl_pdata) - { - if (!mdss_dsi_cmp_panel_reg(ctrl_pdata->status_buf, - ctrl_pdata->status_value, 0)) { - ctrl_pdata->status_error_count = 0; - pr_err("%s: Read back value from panel is incorrect\n", - __func__); - return -EINVAL; - } else { - if (!mdss_dsi_cmp_panel_reg(ctrl_pdata->status_buf, - ctrl_pdata->status_value, 3)) { - ctrl_pdata->status_error_count = 0; - } else { - if (mdss_dsi_cmp_panel_reg(ctrl_pdata->status_buf, - ctrl_pdata->status_value, 4) || - mdss_dsi_cmp_panel_reg(ctrl_pdata->status_buf, - ctrl_pdata->status_value, 5)) - ctrl_pdata->status_error_count = 0; - else - ctrl_pdata->status_error_count++; - if (ctrl_pdata->status_error_count >= - ctrl_pdata->max_status_error_count) { - ctrl_pdata->status_error_count = 0; - pr_err("%s: Read value bad. Error_cnt = %i\n", - __func__, - ctrl_pdata->status_error_count); - return -EINVAL; - } - } - return 1; - } - } - - static void mdss_dsi_parse_roi_alignment(struct device_node *np, - struct dsi_panel_timing *pt) - { - int len = 0; - u32 value[6]; - struct property *data; - struct mdss_panel_timing *timing = &pt->timing; - - data = of_find_property(np, "qcom,panel-roi-alignment", &len); - len /= sizeof(u32); - if (!data || (len != 6)) { - pr_debug("%s: Panel roi alignment not found", __func__); - } else { - int rc = of_property_read_u32_array(np, - "qcom,panel-roi-alignment", value, len); - if (rc) - pr_debug("%s: Error reading panel roi alignment values", - __func__); - else { - timing->roi_alignment.xstart_pix_align = value[0]; - timing->roi_alignment.ystart_pix_align = value[1]; - timing->roi_alignment.width_pix_align = value[2]; - timing->roi_alignment.height_pix_align = value[3]; - timing->roi_alignment.min_width = value[4]; - timing->roi_alignment.min_height = value[5]; - } - - pr_debug("%s: ROI alignment: [%d, %d, %d, %d, %d, %d]", - __func__, timing->roi_alignment.xstart_pix_align, - timing->roi_alignment.width_pix_align, - timing->roi_alignment.ystart_pix_align, - timing->roi_alignment.height_pix_align, - timing->roi_alignment.min_width, - timing->roi_alignment.min_height); - } - } - - static void mdss_dsi_parse_dms_config(struct device_node *np, - struct mdss_dsi_ctrl_pdata *ctrl) - { - struct mdss_panel_info *pinfo = &ctrl->panel_data.panel_info; - const char *data; - bool dms_enabled; - - dms_enabled = of_property_read_bool(np, - "qcom,dynamic-mode-switch-enabled"); - - if (!dms_enabled) { - pinfo->mipi.dms_mode = DYNAMIC_MODE_SWITCH_DISABLED; - goto exit; - } - - /* default mode is suspend_resume */ - pinfo->mipi.dms_mode = DYNAMIC_MODE_SWITCH_SUSPEND_RESUME; - data = of_get_property(np, "qcom,dynamic-mode-switch-type", NULL); - if (data && !strcmp(data, "dynamic-resolution-switch-immediate")) { - if (!list_empty(&ctrl->panel_data.timings_list)) - pinfo->mipi.dms_mode = - DYNAMIC_MODE_RESOLUTION_SWITCH_IMMEDIATE; - else - pinfo->mipi.dms_mode = - DYNAMIC_MODE_SWITCH_DISABLED; - goto exit; - } - - if (data && !strcmp(data, "dynamic-switch-immediate")) - pinfo->mipi.dms_mode = DYNAMIC_MODE_SWITCH_IMMEDIATE; - else - pr_debug("%s: default dms suspend/resume\n", __func__); - - mdss_dsi_parse_dcs_cmds(np, &ctrl->video2cmd, - "qcom,video-to-cmd-mode-switch-commands", - "qcom,mode-switch-commands-state"); - - mdss_dsi_parse_dcs_cmds(np, &ctrl->cmd2video, - "qcom,cmd-to-video-mode-switch-commands", - "qcom,mode-switch-commands-state"); - - mdss_dsi_parse_dcs_cmds(np, &ctrl->post_dms_on_cmds, - "qcom,mdss-dsi-post-mode-switch-on-command", - "qcom,mdss-dsi-post-mode-switch-on-command-state"); - - if (pinfo->mipi.dms_mode == DYNAMIC_MODE_SWITCH_IMMEDIATE && - !ctrl->post_dms_on_cmds.cmd_cnt) { - pr_warn("%s: No post dms on cmd specified\n", __func__); - pinfo->mipi.dms_mode = DYNAMIC_MODE_SWITCH_DISABLED; - } - - if (!ctrl->video2cmd.cmd_cnt || !ctrl->cmd2video.cmd_cnt) { - pr_warn("%s: No commands specified for dynamic switch\n", - __func__); - pinfo->mipi.dms_mode = DYNAMIC_MODE_SWITCH_DISABLED; - } - exit: - pr_info("%s: dynamic switch feature enabled: %d\n", __func__, - pinfo->mipi.dms_mode); - return; - } - - /* the length of all the valid values to be checked should not be great - * than the length of returned data from read command. - */ - static bool - mdss_dsi_parse_esd_check_valid_params(struct mdss_dsi_ctrl_pdata *ctrl) - { - int i; - - for (i = 0; i < ctrl->status_cmds.cmd_cnt; ++i) { - if (ctrl->status_valid_params[i] > ctrl->status_cmds_rlen[i]) { - pr_debug("%s: ignore valid params!\n", __func__); - return false; - } - } - - return true; - } - - static bool mdss_dsi_parse_esd_status_len(struct device_node *np, - char *prop_key, u32 **target, u32 cmd_cnt) - { - int tmp; - - if (!of_find_property(np, prop_key, &tmp)) - return false; - - tmp /= sizeof(u32); - if (tmp != cmd_cnt) { - pr_err("%s: request property number(%d) not match command count(%d)\n", - __func__, tmp, cmd_cnt); - return false; - } - - *target = kcalloc(tmp, sizeof(u32), GFP_KERNEL); - if (IS_ERR_OR_NULL(*target)) { - pr_err("%s: Error allocating memory for property\n", - __func__); - return false; - } - - if (of_property_read_u32_array(np, prop_key, *target, tmp)) { - pr_err("%s: cannot get values from dts\n", __func__); - kfree(*target); - *target = NULL; - return false; - } - - return true; - } - - static void mdss_dsi_parse_esd_params(struct device_node *np, - struct mdss_dsi_ctrl_pdata *ctrl) - { - u32 tmp; - u32 i, status_len, *lenp; - int rc; - struct property *data; - const char *string; - struct mdss_panel_info *pinfo = &ctrl->panel_data.panel_info; - - pinfo->esd_check_enabled = of_property_read_bool(np, - "qcom,esd-check-enabled"); - - if (!pinfo->esd_check_enabled) - return; - - ctrl->status_mode = ESD_MAX; - rc = of_property_read_string(np, - "qcom,mdss-dsi-panel-status-check-mode", &string); - if (!rc) { - if (!strcmp(string, "bta_check")) { - ctrl->status_mode = ESD_BTA; - } else if (!strcmp(string, "reg_read")) { - ctrl->status_mode = ESD_REG; - ctrl->check_read_status = - mdss_dsi_gen_read_status; - } else if (!strcmp(string, "reg_read_nt35596")) { - ctrl->status_mode = ESD_REG_NT35596; - ctrl->status_error_count = 0; - ctrl->check_read_status = - mdss_dsi_nt35596_read_status; - } else if (!strcmp(string, "te_signal_check")) { - if (pinfo->mipi.mode == DSI_CMD_MODE) { - ctrl->status_mode = ESD_TE; - } else { - pr_err("TE-ESD not valid for video mode\n"); - goto error; - } - } else { - pr_err("No valid panel-status-check-mode string\n"); - goto error; - } - } - - if ((ctrl->status_mode == ESD_BTA) || (ctrl->status_mode == ESD_TE) || - (ctrl->status_mode == ESD_MAX)) - return; - - mdss_dsi_parse_dcs_cmds(np, &ctrl->status_cmds, - "qcom,mdss-dsi-panel-status-command", - "qcom,mdss-dsi-panel-status-command-state"); - - rc = of_property_read_u32(np, "qcom,mdss-dsi-panel-max-error-count", - &tmp); - ctrl->max_status_error_count = (!rc ? tmp : 0); - - if (!mdss_dsi_parse_esd_status_len(np, - "qcom,mdss-dsi-panel-status-read-length", - &ctrl->status_cmds_rlen, ctrl->status_cmds.cmd_cnt)) { - pinfo->esd_check_enabled = false; - return; - } - - if (mdss_dsi_parse_esd_status_len(np, - "qcom,mdss-dsi-panel-status-valid-params", - &ctrl->status_valid_params, ctrl->status_cmds.cmd_cnt)) { - if (!mdss_dsi_parse_esd_check_valid_params(ctrl)) - goto error1; - } - - status_len = 0; - lenp = ctrl->status_valid_params ?: ctrl->status_cmds_rlen; - for (i = 0; i < ctrl->status_cmds.cmd_cnt; ++i) - status_len += lenp[i]; - - data = of_find_property(np, "qcom,mdss-dsi-panel-status-value", &tmp); - tmp /= sizeof(u32); - if (!IS_ERR_OR_NULL(data) && tmp != 0 && (tmp % status_len) == 0) { - ctrl->groups = tmp / status_len; - } else { - pr_err("%s: Error parse panel-status-value\n", __func__); - goto error1; - } - - ctrl->status_value = kzalloc(sizeof(u32) * status_len * ctrl->groups, - GFP_KERNEL); - if (!ctrl->status_value) - goto error1; - - ctrl->return_buf = kcalloc(status_len * ctrl->groups, - sizeof(unsigned char), GFP_KERNEL); - if (!ctrl->return_buf) - goto error2; - - rc = of_property_read_u32_array(np, - "qcom,mdss-dsi-panel-status-value", - ctrl->status_value, ctrl->groups * status_len); - if (rc) { - pr_debug("%s: Error reading panel status values\n", - __func__); - memset(ctrl->status_value, 0, ctrl->groups * status_len); - } - - return; - - error2: - kfree(ctrl->status_value); - error1: - kfree(ctrl->status_valid_params); - kfree(ctrl->status_cmds_rlen); - error: - pinfo->esd_check_enabled = false; - } - - static void mdss_dsi_parse_partial_update_caps(struct device_node *np, - struct mdss_dsi_ctrl_pdata *ctrl) - { - struct mdss_panel_info *pinfo; - const char *data; - - pinfo = &ctrl->panel_data.panel_info; - - data = of_get_property(np, "qcom,partial-update-enabled", NULL); - if (data && !strcmp(data, "single_roi")) - pinfo->partial_update_supported = - PU_SINGLE_ROI; - else if (data && !strcmp(data, "dual_roi")) - pinfo->partial_update_supported = - PU_DUAL_ROI; - else if (data && !strcmp(data, "none")) - pinfo->partial_update_supported = - PU_NOT_SUPPORTED; - else - pinfo->partial_update_supported = - PU_NOT_SUPPORTED; - - if (pinfo->mipi.mode == DSI_CMD_MODE) { - pinfo->partial_update_enabled = pinfo->partial_update_supported; - pr_info("%s: partial_update_enabled=%d\n", __func__, - pinfo->partial_update_enabled); - ctrl->set_col_page_addr = mdss_dsi_set_col_page_addr; - if (pinfo->partial_update_enabled) { - pinfo->partial_update_roi_merge = - of_property_read_bool(np, - "qcom,partial-update-roi-merge"); - } - } - } - - static int mdss_dsi_parse_panel_features(struct device_node *np, - struct mdss_dsi_ctrl_pdata *ctrl) - { - struct mdss_panel_info *pinfo; - - if (!np || !ctrl) { - pr_err("%s: Invalid arguments\n", __func__); - return -ENODEV; - } - - pinfo = &ctrl->panel_data.panel_info; - - mdss_dsi_parse_partial_update_caps(np, ctrl); - - pinfo->dcs_cmd_by_left = of_property_read_bool(np, - "qcom,dcs-cmd-by-left"); - - pinfo->ulps_feature_enabled = of_property_read_bool(np, - "qcom,ulps-enabled"); - pr_info("%s: ulps feature %s\n", __func__, - (pinfo->ulps_feature_enabled ? "enabled" : "disabled")); - - pinfo->ulps_suspend_enabled = of_property_read_bool(np, - "qcom,suspend-ulps-enabled"); - pr_info("%s: ulps during suspend feature %s", __func__, - (pinfo->ulps_suspend_enabled ? "enabled" : "disabled")); - - mdss_dsi_parse_dms_config(np, ctrl); - - pinfo->panel_ack_disabled = pinfo->sim_panel_mode ? - 1 : of_property_read_bool(np, "qcom,panel-ack-disabled"); - - pinfo->allow_phy_power_off = of_property_read_bool(np, - "qcom,panel-allow-phy-poweroff"); - - mdss_dsi_parse_esd_params(np, ctrl); - - if (pinfo->panel_ack_disabled && pinfo->esd_check_enabled) { - pr_warn("ESD should not be enabled if panel ACK is disabled\n"); - pinfo->esd_check_enabled = false; - } - - if (ctrl->disp_en_gpio <= 0) { - ctrl->disp_en_gpio = of_get_named_gpio( - np, - "qcom,5v-boost-gpio", 0); - - if (!gpio_is_valid(ctrl->disp_en_gpio)) - pr_debug("%s:%d, Disp_en gpio not specified\n", - __func__, __LINE__); - } - - mdss_dsi_parse_dcs_cmds(np, &ctrl->lp_on_cmds, - "qcom,mdss-dsi-lp-mode-on", NULL); - - mdss_dsi_parse_dcs_cmds(np, &ctrl->lp_off_cmds, - "qcom,mdss-dsi-lp-mode-off", NULL); - - return 0; - } - - static void mdss_dsi_parse_panel_horizintal_line_idle(struct device_node *np, - struct mdss_dsi_ctrl_pdata *ctrl) - { - const u32 *src; - int i, len, cnt; - struct panel_horizontal_idle *kp; - - if (!np || !ctrl) { - pr_err("%s: Invalid arguments\n", __func__); - return; - } - - src = of_get_property(np, "qcom,mdss-dsi-hor-line-idle", &len); - if (!src || len == 0) - return; - - cnt = len % 3; /* 3 fields per entry */ - if (cnt) { - pr_err("%s: invalid horizontal idle len=%d\n", __func__, len); - return; - } - - cnt = len / sizeof(u32); - - kp = kzalloc(sizeof(*kp) * (cnt / 3), GFP_KERNEL); - if (kp == NULL) { - pr_err("%s: No memory\n", __func__); - return; - } - - ctrl->line_idle = kp; - for (i = 0; i < cnt; i += 3) { - kp->min = be32_to_cpu(src[i]); - kp->max = be32_to_cpu(src[i+1]); - kp->idle = be32_to_cpu(src[i+2]); - kp++; - ctrl->horizontal_idle_cnt++; - } - - /* - * idle is enabled for this controller, this will be used to - * enable/disable burst mode since both features are mutually - * exclusive. - */ - ctrl->idle_enabled = true; - - pr_debug("%s: horizontal_idle_cnt=%d\n", __func__, - ctrl->horizontal_idle_cnt); - } - - static int mdss_dsi_set_refresh_rate_range(struct device_node *pan_node, - struct mdss_panel_info *pinfo) - { - int rc = 0; - rc = of_property_read_u32(pan_node, - "qcom,mdss-dsi-min-refresh-rate", - &pinfo->min_fps); - if (rc) { - pr_warn("%s:%d, Unable to read min refresh rate\n", - __func__, __LINE__); - - /* - * If min refresh rate is not specified, set it to the - * default panel refresh rate. - */ - pinfo->min_fps = pinfo->mipi.frame_rate; - rc = 0; - } - - rc = of_property_read_u32(pan_node, - "qcom,mdss-dsi-max-refresh-rate", - &pinfo->max_fps); - if (rc) { - pr_warn("%s:%d, Unable to read max refresh rate\n", - __func__, __LINE__); - - /* - * Since max refresh rate was not specified when dynamic - * fps is enabled, using the default panel refresh rate - * as max refresh rate supported. - */ - pinfo->max_fps = pinfo->mipi.frame_rate; - rc = 0; - } - - pr_info("dyn_fps: min = %d, max = %d\n", - pinfo->min_fps, pinfo->max_fps); - return rc; - } - - static void mdss_dsi_parse_dfps_config(struct device_node *pan_node, - struct mdss_dsi_ctrl_pdata *ctrl_pdata) - { - const char *data; - bool dynamic_fps; - struct mdss_panel_info *pinfo = &(ctrl_pdata->panel_data.panel_info); - - dynamic_fps = of_property_read_bool(pan_node, - "qcom,mdss-dsi-pan-enable-dynamic-fps"); - - if (!dynamic_fps) - return; - - pinfo->dynamic_fps = true; - data = of_get_property(pan_node, "qcom,mdss-dsi-pan-fps-update", NULL); - if (data) { - if (!strcmp(data, "dfps_suspend_resume_mode")) { - pinfo->dfps_update = DFPS_SUSPEND_RESUME_MODE; - pr_debug("dfps mode: suspend/resume\n"); - } else if (!strcmp(data, "dfps_immediate_clk_mode")) { - pinfo->dfps_update = DFPS_IMMEDIATE_CLK_UPDATE_MODE; - pr_debug("dfps mode: Immediate clk\n"); - } else if (!strcmp(data, "dfps_immediate_porch_mode_hfp")) { - pinfo->dfps_update = - DFPS_IMMEDIATE_PORCH_UPDATE_MODE_HFP; - pr_debug("dfps mode: Immediate porch HFP\n"); - } else if (!strcmp(data, "dfps_immediate_porch_mode_vfp")) { - pinfo->dfps_update = - DFPS_IMMEDIATE_PORCH_UPDATE_MODE_VFP; - pr_debug("dfps mode: Immediate porch VFP\n"); - } else { - pinfo->dfps_update = DFPS_SUSPEND_RESUME_MODE; - pr_debug("default dfps mode: suspend/resume\n"); - } - } else { - pinfo->dynamic_fps = false; - pr_debug("dfps update mode not configured: disable\n"); - } - pinfo->new_fps = pinfo->mipi.frame_rate; - pinfo->current_fps = pinfo->mipi.frame_rate; - - return; - } - - int mdss_panel_parse_bl_settings(struct device_node *np, - struct mdss_dsi_ctrl_pdata *ctrl_pdata) - { - const char *data; - int rc = 0; - u32 tmp; - - ctrl_pdata->bklt_ctrl = UNKNOWN_CTRL; - data = of_get_property(np, "qcom,mdss-dsi-bl-pmic-control-type", NULL); - if (data) { - if (!strcmp(data, "bl_ctrl_wled")) { - led_trigger_register_simple("bkl-trigger", - &bl_led_trigger); - pr_debug("%s: SUCCESS-> WLED TRIGGER register\n", - __func__); - ctrl_pdata->bklt_ctrl = BL_WLED; - } else if (!strcmp(data, "bl_ctrl_pwm")) { - ctrl_pdata->bklt_ctrl = BL_PWM; - ctrl_pdata->pwm_pmi = of_property_read_bool(np, - "qcom,mdss-dsi-bl-pwm-pmi"); - rc = of_property_read_u32(np, - "qcom,mdss-dsi-bl-pmic-pwm-frequency", &tmp); - if (rc) { - pr_err("%s:%d, Error, panel pwm_period\n", - __func__, __LINE__); - return -EINVAL; - } - ctrl_pdata->pwm_period = tmp; - if (ctrl_pdata->pwm_pmi) { - ctrl_pdata->pwm_bl = of_pwm_get(np, NULL); - if (IS_ERR(ctrl_pdata->pwm_bl)) { - pr_err("%s: Error, pwm device\n", - __func__); - ctrl_pdata->pwm_bl = NULL; - return -EINVAL; - } - } else { - rc = of_property_read_u32(np, - "qcom,mdss-dsi-bl-pmic-bank-select", - &tmp); - if (rc) { - pr_err("%s:%d, Error, lpg channel\n", - __func__, __LINE__); - return -EINVAL; - } - ctrl_pdata->pwm_lpg_chan = tmp; - tmp = of_get_named_gpio(np, - "qcom,mdss-dsi-pwm-gpio", 0); - ctrl_pdata->pwm_pmic_gpio = tmp; - pr_debug("%s: Configured PWM bklt ctrl\n", - __func__); - } - } else if (!strcmp(data, "bl_ctrl_dcs")) { - ctrl_pdata->bklt_ctrl = BL_DCS_CMD; - data = of_get_property(np, - "qcom,mdss-dsi-bl-dcs-command-state", NULL); - if (data && !strcmp(data, "dsi_hs_mode")) - ctrl_pdata->bklt_dcs_op_mode = DSI_HS_MODE; - else - ctrl_pdata->bklt_dcs_op_mode = DSI_LP_MODE; - - pr_debug("%s: Configured DCS_CMD bklt ctrl\n", - __func__); - } - } - return 0; - } - - int mdss_dsi_panel_timing_switch(struct mdss_dsi_ctrl_pdata *ctrl, - struct mdss_panel_timing *timing) - { - struct dsi_panel_timing *pt; - struct mdss_panel_info *pinfo = &ctrl->panel_data.panel_info; - int i; - - if (!timing) - return -EINVAL; - - if (timing == ctrl->panel_data.current_timing) { - pr_warn("%s: panel timing \"%s\" already set\n", __func__, - timing->name); - return 0; /* nothing to do */ - } - - pr_debug("%s: ndx=%d switching to panel timing \"%s\"\n", __func__, - ctrl->ndx, timing->name); - - mdss_panel_info_from_timing(timing, pinfo); - - pt = container_of(timing, struct dsi_panel_timing, timing); - pinfo->mipi.t_clk_pre = pt->t_clk_pre; - pinfo->mipi.t_clk_post = pt->t_clk_post; - - for (i = 0; i < ARRAY_SIZE(pt->phy_timing); i++) - pinfo->mipi.dsi_phy_db.timing[i] = pt->phy_timing[i]; - - for (i = 0; i < ARRAY_SIZE(pt->phy_timing_8996); i++) - pinfo->mipi.dsi_phy_db.timing_8996[i] = pt->phy_timing_8996[i]; - - ctrl->on_cmds = pt->on_cmds; - ctrl->post_panel_on_cmds = pt->post_panel_on_cmds; - - ctrl->panel_data.current_timing = timing; - if (!timing->clk_rate) - ctrl->refresh_clk_rate = true; - mdss_dsi_clk_refresh(&ctrl->panel_data, ctrl->update_phy_timing); - - return 0; - } - - void mdss_dsi_unregister_bl_settings(struct mdss_dsi_ctrl_pdata *ctrl_pdata) - { - if (ctrl_pdata->bklt_ctrl == BL_WLED) - led_trigger_unregister_simple(bl_led_trigger); - } - - static int mdss_dsi_panel_timing_from_dt(struct device_node *np, - struct dsi_panel_timing *pt, - struct mdss_panel_data *panel_data) - { - u32 tmp; - u64 tmp64; - int rc, i, len; - const char *data; - struct mdss_dsi_ctrl_pdata *ctrl_pdata; - struct mdss_panel_info *pinfo; - bool phy_timings_present = false; - - pinfo = &panel_data->panel_info; - - ctrl_pdata = container_of(panel_data, struct mdss_dsi_ctrl_pdata, - panel_data); - - rc = of_property_read_u32(np, "qcom,mdss-dsi-panel-width", &tmp); - if (rc) { - pr_err("%s:%d, panel width not specified\n", - __func__, __LINE__); - return -EINVAL; - } - pt->timing.xres = tmp; - - rc = of_property_read_u32(np, "qcom,mdss-dsi-panel-height", &tmp); - if (rc) { - pr_err("%s:%d, panel height not specified\n", - __func__, __LINE__); - return -EINVAL; - } - pt->timing.yres = tmp; - - rc = of_property_read_u32(np, "qcom,mdss-dsi-h-front-porch", &tmp); - pt->timing.h_front_porch = (!rc ? tmp : 6); - rc = of_property_read_u32(np, "qcom,mdss-dsi-h-back-porch", &tmp); - pt->timing.h_back_porch = (!rc ? tmp : 6); - rc = of_property_read_u32(np, "qcom,mdss-dsi-h-pulse-width", &tmp); - pt->timing.h_pulse_width = (!rc ? tmp : 2); - rc = of_property_read_u32(np, "qcom,mdss-dsi-h-sync-skew", &tmp); - pt->timing.hsync_skew = (!rc ? tmp : 0); - rc = of_property_read_u32(np, "qcom,mdss-dsi-v-back-porch", &tmp); - pt->timing.v_back_porch = (!rc ? tmp : 6); - rc = of_property_read_u32(np, "qcom,mdss-dsi-v-front-porch", &tmp); - pt->timing.v_front_porch = (!rc ? tmp : 6); - rc = of_property_read_u32(np, "qcom,mdss-dsi-v-pulse-width", &tmp); - pt->timing.v_pulse_width = (!rc ? tmp : 2); - - rc = of_property_read_u32(np, "qcom,mdss-dsi-h-left-border", &tmp); - pt->timing.border_left = !rc ? tmp : 0; - rc = of_property_read_u32(np, "qcom,mdss-dsi-h-right-border", &tmp); - pt->timing.border_right = !rc ? tmp : 0; - - /* overriding left/right borders for split display cases */ - if (mdss_dsi_is_hw_config_split(ctrl_pdata->shared_data)) { - if (panel_data->next) - pt->timing.border_right = 0; - else - pt->timing.border_left = 0; - } - - rc = of_property_read_u32(np, "qcom,mdss-dsi-v-top-border", &tmp); - pt->timing.border_top = !rc ? tmp : 0; - rc = of_property_read_u32(np, "qcom,mdss-dsi-v-bottom-border", &tmp); - pt->timing.border_bottom = !rc ? tmp : 0; - - rc = of_property_read_u32(np, "qcom,mdss-dsi-panel-framerate", &tmp); - pt->timing.frame_rate = !rc ? tmp : DEFAULT_FRAME_RATE; - rc = of_property_read_u64(np, "qcom,mdss-dsi-panel-clockrate", &tmp64); - if (rc == -EOVERFLOW) { - tmp64 = 0; - rc = of_property_read_u32(np, - "qcom,mdss-dsi-panel-clockrate", (u32 *)&tmp64); - } - pt->timing.clk_rate = !rc ? tmp64 : 0; - - data = of_get_property(np, "qcom,mdss-dsi-panel-timings", &len); - if ((!data) || (len != 12)) { - pr_debug("%s:%d, Unable to read Phy timing settings", + +static int mdss_dsi_parse_fbc_params(struct device_node *np, + struct mdss_panel_timing *timing) +{ + int rc, fbc_enabled = 0; + u32 tmp; + struct fbc_panel_info *fbc = &timing->fbc; + + fbc_enabled = of_property_read_bool(np, "qcom,mdss-dsi-fbc-enable"); + if (fbc_enabled) { + pr_debug("%s:%d FBC panel enabled.\n", __func__, __LINE__); + fbc->enabled = 1; + rc = of_property_read_u32(np, "qcom,mdss-dsi-fbc-bpp", &tmp); + fbc->target_bpp = (!rc ? tmp : 24); + rc = of_property_read_u32(np, "qcom,mdss-dsi-fbc-packing", + &tmp); + fbc->comp_mode = (!rc ? tmp : 0); + fbc->qerr_enable = of_property_read_bool(np, + "qcom,mdss-dsi-fbc-quant-error"); + rc = of_property_read_u32(np, "qcom,mdss-dsi-fbc-bias", &tmp); + fbc->cd_bias = (!rc ? tmp : 0); + fbc->pat_enable = of_property_read_bool(np, + "qcom,mdss-dsi-fbc-pat-mode"); + fbc->vlc_enable = of_property_read_bool(np, + "qcom,mdss-dsi-fbc-vlc-mode"); + fbc->bflc_enable = of_property_read_bool(np, + "qcom,mdss-dsi-fbc-bflc-mode"); + rc = of_property_read_u32(np, "qcom,mdss-dsi-fbc-h-line-budget", + &tmp); + fbc->line_x_budget = (!rc ? tmp : 0); + rc = of_property_read_u32(np, "qcom,mdss-dsi-fbc-budget-ctrl", + &tmp); + fbc->block_x_budget = (!rc ? tmp : 0); + rc = of_property_read_u32(np, "qcom,mdss-dsi-fbc-block-budget", + &tmp); + fbc->block_budget = (!rc ? tmp : 0); + rc = of_property_read_u32(np, + "qcom,mdss-dsi-fbc-lossless-threshold", &tmp); + fbc->lossless_mode_thd = (!rc ? tmp : 0); + rc = of_property_read_u32(np, + "qcom,mdss-dsi-fbc-lossy-threshold", &tmp); + fbc->lossy_mode_thd = (!rc ? tmp : 0); + rc = of_property_read_u32(np, "qcom,mdss-dsi-fbc-rgb-threshold", + &tmp); + fbc->lossy_rgb_thd = (!rc ? tmp : 0); + rc = of_property_read_u32(np, + "qcom,mdss-dsi-fbc-lossy-mode-idx", &tmp); + fbc->lossy_mode_idx = (!rc ? tmp : 0); + rc = of_property_read_u32(np, + "qcom,mdss-dsi-fbc-slice-height", &tmp); + fbc->slice_height = (!rc ? tmp : 0); + fbc->pred_mode = of_property_read_bool(np, + "qcom,mdss-dsi-fbc-2d-pred-mode"); + fbc->enc_mode = of_property_read_bool(np, + "qcom,mdss-dsi-fbc-ver2-mode"); + rc = of_property_read_u32(np, + "qcom,mdss-dsi-fbc-max-pred-err", &tmp); + fbc->max_pred_err = (!rc ? tmp : 0); + + timing->compression_mode = COMPRESSION_FBC; + } else { + pr_debug("%s:%d Panel does not support FBC.\n", __func__, __LINE__); - } else { - for (i = 0; i < len; i++) - pt->phy_timing[i] = data[i]; - phy_timings_present = true; - } - - data = of_get_property(np, "qcom,mdss-dsi-panel-timings-phy-v2", &len); - if ((!data) || (len != 40)) { - pr_debug("%s:%d, Unable to read phy-v2 lane timing settings", + fbc->enabled = 0; + fbc->target_bpp = 24; + } + return 0; +} + +void mdss_dsi_panel_dsc_pps_send(struct mdss_dsi_ctrl_pdata *ctrl, + struct mdss_panel_info *pinfo) +{ + struct dsi_panel_cmds pcmds; + struct dsi_cmd_desc cmd; + + if (!pinfo || (pinfo->compression_mode != COMPRESSION_DSC)) + return; + + memset(&pcmds, 0, sizeof(pcmds)); + memset(&cmd, 0, sizeof(cmd)); + + cmd.dchdr.dlen = mdss_panel_dsc_prepare_pps_buf(&pinfo->dsc, + ctrl->pps_buf, 0); + cmd.dchdr.dtype = DTYPE_PPS; + cmd.dchdr.last = 1; + cmd.dchdr.wait = 10; + cmd.dchdr.vc = 0; + cmd.dchdr.ack = 0; + cmd.payload = ctrl->pps_buf; + + pcmds.cmd_cnt = 1; + pcmds.cmds = &cmd; + pcmds.link_state = DSI_LP_MODE; + + mdss_dsi_panel_cmds_send(ctrl, &pcmds, CMD_REQ_COMMIT); +} + +static int mdss_dsi_parse_hdr_settings(struct device_node *np, + struct mdss_panel_info *pinfo) +{ + int rc = 0; + struct mdss_panel_hdr_properties *hdr_prop; + + if (!np) { + pr_err("%s: device node pointer is NULL\n", __func__); + return -EINVAL; + } + + if (!pinfo) { + pr_err("%s: panel info is NULL\n", __func__); + return -EINVAL; + } + + hdr_prop = &pinfo->hdr_properties; + hdr_prop->hdr_enabled = of_property_read_bool(np, + "qcom,mdss-dsi-panel-hdr-enabled"); + + if (hdr_prop->hdr_enabled) { + rc = of_property_read_u32_array(np, + "qcom,mdss-dsi-panel-hdr-color-primaries", + hdr_prop->display_primaries, + DISPLAY_PRIMARIES_COUNT); + if (rc) { + pr_info("%s:%d, Unable to read color primaries,rc:%u", + __func__, __LINE__, + hdr_prop->hdr_enabled = false); + } + + rc = of_property_read_u32(np, + "qcom,mdss-dsi-panel-peak-brightness", + &(hdr_prop->peak_brightness)); + if (rc) { + pr_info("%s:%d, Unable to read hdr brightness, rc:%u", + __func__, __LINE__, rc); + hdr_prop->hdr_enabled = false; + } + + rc = of_property_read_u32(np, + "qcom,mdss-dsi-panel-blackness-level", + &(hdr_prop->blackness_level)); + if (rc) { + pr_info("%s:%d, Unable to read hdr brightness, rc:%u", + __func__, __LINE__, rc); + hdr_prop->hdr_enabled = false; + } + } + return 0; +} + +static int mdss_dsi_parse_split_link_settings(struct device_node *np, + struct mdss_panel_info *pinfo) +{ + u32 tmp; + int rc = 0; + + if (!np) { + pr_err("%s: device node pointer is NULL\n", __func__); + return -EINVAL; + } + + if (!pinfo) { + pr_err("%s: panel info is NULL\n", __func__); + return -EINVAL; + } + + pinfo->split_link_enabled = of_property_read_bool(np, + "qcom,split-link-enabled"); + + if (pinfo->split_link_enabled) { + rc = of_property_read_u32(np, + "qcom,sublinks-count", &tmp); + /* default num of sublink is 1*/ + pinfo->mipi.num_of_sublinks = (!rc ? tmp : 1); + + rc = of_property_read_u32(np, + "qcom,lanes-per-sublink", &tmp); + /* default num of lanes per sublink is 1 */ + pinfo->mipi.lanes_per_sublink = (!rc ? tmp : 1); + } + + pr_info("%s: enable %d sublinks-count %d lanes per sublink %d\n", + __func__, pinfo->split_link_enabled, + pinfo->mipi.num_of_sublinks, + pinfo->mipi.lanes_per_sublink); + return 0; +} + +static int mdss_dsi_parse_dsc_version(struct device_node *np, + struct mdss_panel_timing *timing) +{ + u32 data; + int rc = 0; + struct dsc_desc *dsc = &timing->dsc; + + rc = of_property_read_u32(np, "qcom,mdss-dsc-version", &data); + if (rc) { + dsc->version = 0x11; + rc = 0; + } else { + dsc->version = data & 0xff; + /* only support DSC 1.1 rev */ + if (dsc->version != 0x11) { + pr_err("%s: DSC version:%d not supported\n", __func__, + dsc->version); + rc = -EINVAL; + goto end; + } + } + + rc = of_property_read_u32(np, "qcom,mdss-dsc-scr-version", &data); + if (rc) { + dsc->scr_rev = 0x0; + rc = 0; + } else { + dsc->scr_rev = data & 0xff; + /* only one scr rev supported */ + if (dsc->scr_rev > 0x1) { + pr_err("%s: DSC scr version:%d not supported\n", + __func__, dsc->scr_rev); + rc = -EINVAL; + goto end; + } + } + +end: + return rc; +} + +static int mdss_dsi_parse_dsc_params(struct device_node *np, + struct mdss_panel_timing *timing, bool is_split_display) +{ + u32 data, intf_width; + int rc = 0; + struct dsc_desc *dsc = &timing->dsc; + + if (!np) { + pr_err("%s: device node pointer is NULL\n", __func__); + return -EINVAL; + } + + rc = of_property_read_u32(np, "qcom,mdss-dsc-encoders", &data); + if (rc) { + if (!of_find_property(np, "qcom,mdss-dsc-encoders", NULL)) { + /* property is not defined, default to 1 */ + data = 1; + } else { + pr_err("%s: Error parsing qcom,mdss-dsc-encoders\n", + __func__); + goto end; + } + } + + timing->dsc_enc_total = data; + + if (is_split_display && (timing->dsc_enc_total > 1)) { + pr_err("%s: Error: for split displays, more than 1 dsc encoder per panel is not allowed.\n", + __func__); + goto end; + } + + rc = of_property_read_u32(np, "qcom,mdss-dsc-slice-height", &data); + if (rc) + goto end; + dsc->slice_height = data; + + rc = of_property_read_u32(np, "qcom,mdss-dsc-slice-width", &data); + if (rc) + goto end; + dsc->slice_width = data; + intf_width = timing->xres; + + if (intf_width % dsc->slice_width) { + pr_err("%s: Error: multiple of slice-width:%d should match panel-width:%d\n", + __func__, dsc->slice_width, intf_width); + goto end; + } + + data = intf_width / dsc->slice_width; + if (((timing->dsc_enc_total > 1) && ((data != 2) && (data != 4))) || + ((timing->dsc_enc_total == 1) && (data > 2))) { + pr_err("%s: Error: max 2 slice per encoder. slice-width:%d should match panel-width:%d dsc_enc_total:%d\n", + __func__, dsc->slice_width, + intf_width, timing->dsc_enc_total); + goto end; + } + + rc = of_property_read_u32(np, "qcom,mdss-dsc-slice-per-pkt", &data); + if (rc) + goto end; + dsc->slice_per_pkt = data; + + /* + * slice_per_pkt can be either 1 or all slices_per_intf + */ + if ((dsc->slice_per_pkt > 1) && (dsc->slice_per_pkt != + DIV_ROUND_UP(intf_width, dsc->slice_width))) { + pr_err("Error: slice_per_pkt can be either 1 or all slices_per_intf\n"); + pr_err("%s: slice_per_pkt=%d, slice_width=%d intf_width=%d\n", + __func__, + dsc->slice_per_pkt, dsc->slice_width, intf_width); + rc = -EINVAL; + goto end; + } + + pr_debug("%s: num_enc:%d :slice h=%d w=%d s_pkt=%d\n", __func__, + timing->dsc_enc_total, dsc->slice_height, + dsc->slice_width, dsc->slice_per_pkt); + + rc = of_property_read_u32(np, "qcom,mdss-dsc-bit-per-component", &data); + if (rc) + goto end; + dsc->bpc = data; + + rc = of_property_read_u32(np, "qcom,mdss-dsc-bit-per-pixel", &data); + if (rc) + goto end; + dsc->bpp = data; + + pr_debug("%s: bpc=%d bpp=%d\n", __func__, + dsc->bpc, dsc->bpp); + + dsc->block_pred_enable = of_property_read_bool(np, + "qcom,mdss-dsc-block-prediction-enable"); + + dsc->enable_422 = 0; + dsc->convert_rgb = 1; + dsc->vbr_enable = 0; + + dsc->config_by_manufacture_cmd = of_property_read_bool(np, + "qcom,mdss-dsc-config-by-manufacture-cmd"); + + mdss_panel_dsc_parameters_calc(&timing->dsc); + mdss_panel_dsc_pclk_param_calc(&timing->dsc, intf_width); + + timing->dsc.full_frame_slices = + DIV_ROUND_UP(intf_width, timing->dsc.slice_width); + + timing->compression_mode = COMPRESSION_DSC; + +end: + return rc; +} + +static struct device_node *mdss_dsi_panel_get_dsc_cfg_np( + struct device_node *np, struct mdss_panel_data *panel_data, + bool default_timing) +{ + struct device_node *dsc_cfg_np = NULL; + + + /* Read the dsc config node specified by command line */ + if (default_timing) { + dsc_cfg_np = of_get_child_by_name(np, + panel_data->dsc_cfg_np_name); + if (!dsc_cfg_np) + pr_warn_once("%s: cannot find dsc config node:%s\n", + __func__, panel_data->dsc_cfg_np_name); + } + + /* + * Fall back to default from DT as nothing is specified + * in command line. + */ + if (!dsc_cfg_np && of_find_property(np, "qcom,config-select", NULL)) { + dsc_cfg_np = of_parse_phandle(np, "qcom,config-select", 0); + if (!dsc_cfg_np) + pr_warn_once("%s:err parsing qcom,config-select\n", + __func__); + } + + return dsc_cfg_np; +} + +static int mdss_dsi_parse_topology_config(struct device_node *np, + struct dsi_panel_timing *pt, struct mdss_panel_data *panel_data, + bool default_timing) +{ + int rc = 0; + bool is_split_display = panel_data->panel_info.is_split_display; + const char *data; + struct mdss_panel_timing *timing = &pt->timing; + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + struct mdss_panel_info *pinfo; + struct device_node *cfg_np = NULL; + + ctrl_pdata = container_of(panel_data, struct mdss_dsi_ctrl_pdata, + panel_data); + pinfo = &ctrl_pdata->panel_data.panel_info; + + cfg_np = mdss_dsi_panel_get_dsc_cfg_np(np, + &ctrl_pdata->panel_data, default_timing); + + if (cfg_np) { + if (!of_property_read_u32_array(cfg_np, "qcom,lm-split", + timing->lm_widths, 2)) { + if (mdss_dsi_is_hw_config_split(ctrl_pdata->shared_data) + && (timing->lm_widths[1] != 0)) { + pr_err("%s: lm-split not allowed with split display\n", + __func__); + rc = -EINVAL; + goto end; + } + } + + if (!of_property_read_string(cfg_np, "qcom,split-mode", + &data) && !strcmp(data, "pingpong-split")) + pinfo->use_pingpong_split = true; + + if (((timing->lm_widths[0]) || (timing->lm_widths[1])) && + pinfo->use_pingpong_split) { + pr_err("%s: pingpong_split cannot be used when lm-split[%d,%d] is specified\n", + __func__, + timing->lm_widths[0], timing->lm_widths[1]); + return -EINVAL; + } + + pr_info("%s: cfg_node name %s lm_split:%dx%d pp_split:%s\n", + __func__, cfg_np->name, + timing->lm_widths[0], timing->lm_widths[1], + pinfo->use_pingpong_split ? "yes" : "no"); + } + + if (!pinfo->use_pingpong_split && + (timing->lm_widths[0] == 0) && (timing->lm_widths[1] == 0)) + timing->lm_widths[0] = pt->timing.xres; + + data = of_get_property(np, "qcom,compression-mode", NULL); + if (data) { + if (cfg_np && !strcmp(data, "dsc")) { + rc = mdss_dsi_parse_dsc_version(np, &pt->timing); + if (rc) + goto end; + + pinfo->send_pps_before_switch = + of_property_read_bool(np, + "qcom,mdss-dsi-send-pps-before-switch"); + + rc = mdss_dsi_parse_dsc_params(cfg_np, &pt->timing, + is_split_display); + } else if (!strcmp(data, "fbc")) { + rc = mdss_dsi_parse_fbc_params(np, &pt->timing); + } + } + +end: + of_node_put(cfg_np); + return rc; +} + +static void mdss_panel_parse_te_params(struct device_node *np, + struct mdss_panel_timing *timing) +{ + struct mdss_mdp_pp_tear_check *te = &timing->te; + u32 tmp; + int rc = 0; + /* + * TE default: dsi byte clock calculated base on 70 fps; + * around 14 ms to complete a kickoff cycle if te disabled; + * vclk_line base on 60 fps; write is faster than read; + * init == start == rdptr; + */ + te->tear_check_en = + !of_property_read_bool(np, "qcom,mdss-tear-check-disable"); + rc = of_property_read_u32 + (np, "qcom,mdss-tear-check-sync-cfg-height", &tmp); + te->sync_cfg_height = (!rc ? tmp : 0xfff0); + rc = of_property_read_u32 + (np, "qcom,mdss-tear-check-sync-init-val", &tmp); + te->vsync_init_val = (!rc ? tmp : timing->yres); + rc = of_property_read_u32 + (np, "qcom,mdss-tear-check-sync-threshold-start", &tmp); + te->sync_threshold_start = (!rc ? tmp : 4); + rc = of_property_read_u32 + (np, "qcom,mdss-tear-check-sync-threshold-continue", &tmp); + te->sync_threshold_continue = (!rc ? tmp : 4); + rc = of_property_read_u32(np, "qcom,mdss-tear-check-frame-rate", &tmp); + te->refx100 = (!rc ? tmp : 6000); + rc = of_property_read_u32 + (np, "qcom,mdss-tear-check-start-pos", &tmp); + te->start_pos = (!rc ? tmp : timing->yres); + rc = of_property_read_u32 + (np, "qcom,mdss-tear-check-rd-ptr-trigger-intr", &tmp); + te->rd_ptr_irq = (!rc ? tmp : timing->yres + 1); + te->wr_ptr_irq = 0; +} + + +static int mdss_dsi_parse_reset_seq(struct device_node *np, + u32 rst_seq[MDSS_DSI_RST_SEQ_LEN], u32 *rst_len, + const char *name) +{ + int num = 0, i; + int rc; + struct property *data; + u32 tmp[MDSS_DSI_RST_SEQ_LEN]; + *rst_len = 0; + data = of_find_property(np, name, &num); + num /= sizeof(u32); + if (!data || !num || num > MDSS_DSI_RST_SEQ_LEN || num % 2) { + pr_debug("%s:%d, error reading %s, length found = %d\n", + __func__, __LINE__, name, num); + } else { + rc = of_property_read_u32_array(np, name, tmp, num); + if (rc) + pr_debug("%s:%d, error reading %s, rc = %d\n", + __func__, __LINE__, name, rc); + else { + for (i = 0; i < num; ++i) + rst_seq[i] = tmp[i]; + *rst_len = num; + } + } + return 0; +} + +static bool mdss_dsi_cmp_panel_reg_v2(struct mdss_dsi_ctrl_pdata *ctrl) +{ + int i, j = 0; + int len = 0, *lenp; + int group = 0; + + lenp = ctrl->status_valid_params ?: ctrl->status_cmds_rlen; + + for (i = 0; i < ctrl->status_cmds.cmd_cnt; i++) + len += lenp[i]; + + for (j = 0; j < ctrl->groups; ++j) { + for (i = 0; i < len; ++i) { + pr_debug("[%i] return:0x%x status:0x%x\n", + i, ctrl->return_buf[i], + (unsigned int)ctrl->status_value[group + i]); + MDSS_XLOG(ctrl->ndx, ctrl->return_buf[i], + ctrl->status_value[group + i]); + if (ctrl->return_buf[i] != + ctrl->status_value[group + i]) + break; + } + + if (i == len) + return true; + group += len; + } + + return false; +} + +static int mdss_dsi_gen_read_status(struct mdss_dsi_ctrl_pdata *ctrl_pdata) +{ + if (!mdss_dsi_cmp_panel_reg_v2(ctrl_pdata)) { + pr_err("%s: Read back value from panel is incorrect\n", + __func__); + return -EINVAL; + } else { + return 1; + } +} + +static int mdss_dsi_nt35596_read_status(struct mdss_dsi_ctrl_pdata *ctrl_pdata) +{ + if (!mdss_dsi_cmp_panel_reg(ctrl_pdata->status_buf, + ctrl_pdata->status_value, 0)) { + ctrl_pdata->status_error_count = 0; + pr_err("%s: Read back value from panel is incorrect\n", + __func__); + return -EINVAL; + } else { + if (!mdss_dsi_cmp_panel_reg(ctrl_pdata->status_buf, + ctrl_pdata->status_value, 3)) { + ctrl_pdata->status_error_count = 0; + } else { + if (mdss_dsi_cmp_panel_reg(ctrl_pdata->status_buf, + ctrl_pdata->status_value, 4) || + mdss_dsi_cmp_panel_reg(ctrl_pdata->status_buf, + ctrl_pdata->status_value, 5)) + ctrl_pdata->status_error_count = 0; + else + ctrl_pdata->status_error_count++; + if (ctrl_pdata->status_error_count >= + ctrl_pdata->max_status_error_count) { + ctrl_pdata->status_error_count = 0; + pr_err("%s: Read value bad. Error_cnt = %i\n", + __func__, + ctrl_pdata->status_error_count); + return -EINVAL; + } + } + return 1; + } +} + +static void mdss_dsi_parse_roi_alignment(struct device_node *np, + struct dsi_panel_timing *pt) +{ + int len = 0; + u32 value[6]; + struct property *data; + struct mdss_panel_timing *timing = &pt->timing; + + data = of_find_property(np, "qcom,panel-roi-alignment", &len); + len /= sizeof(u32); + if (!data || (len != 6)) { + pr_debug("%s: Panel roi alignment not found", __func__); + } else { + int rc = of_property_read_u32_array(np, + "qcom,panel-roi-alignment", value, len); + if (rc) + pr_debug("%s: Error reading panel roi alignment values", + __func__); + else { + timing->roi_alignment.xstart_pix_align = value[0]; + timing->roi_alignment.ystart_pix_align = value[1]; + timing->roi_alignment.width_pix_align = value[2]; + timing->roi_alignment.height_pix_align = value[3]; + timing->roi_alignment.min_width = value[4]; + timing->roi_alignment.min_height = value[5]; + } + + pr_debug("%s: ROI alignment: [%d, %d, %d, %d, %d, %d]", + __func__, timing->roi_alignment.xstart_pix_align, + timing->roi_alignment.width_pix_align, + timing->roi_alignment.ystart_pix_align, + timing->roi_alignment.height_pix_align, + timing->roi_alignment.min_width, + timing->roi_alignment.min_height); + } +} + +static void mdss_dsi_parse_dms_config(struct device_node *np, + struct mdss_dsi_ctrl_pdata *ctrl) +{ + struct mdss_panel_info *pinfo = &ctrl->panel_data.panel_info; + const char *data; + bool dms_enabled; + + dms_enabled = of_property_read_bool(np, + "qcom,dynamic-mode-switch-enabled"); + + if (!dms_enabled) { + pinfo->mipi.dms_mode = DYNAMIC_MODE_SWITCH_DISABLED; + goto exit; + } + + /* default mode is suspend_resume */ + pinfo->mipi.dms_mode = DYNAMIC_MODE_SWITCH_SUSPEND_RESUME; + data = of_get_property(np, "qcom,dynamic-mode-switch-type", NULL); + if (data && !strcmp(data, "dynamic-resolution-switch-immediate")) { + if (!list_empty(&ctrl->panel_data.timings_list)) + pinfo->mipi.dms_mode = + DYNAMIC_MODE_RESOLUTION_SWITCH_IMMEDIATE; + else + pinfo->mipi.dms_mode = + DYNAMIC_MODE_SWITCH_DISABLED; + goto exit; + } + + if (data && !strcmp(data, "dynamic-switch-immediate")) + pinfo->mipi.dms_mode = DYNAMIC_MODE_SWITCH_IMMEDIATE; + else + pr_debug("%s: default dms suspend/resume\n", __func__); + + mdss_dsi_parse_dcs_cmds(np, &ctrl->video2cmd, + "qcom,video-to-cmd-mode-switch-commands", + "qcom,mode-switch-commands-state"); + + mdss_dsi_parse_dcs_cmds(np, &ctrl->cmd2video, + "qcom,cmd-to-video-mode-switch-commands", + "qcom,mode-switch-commands-state"); + + mdss_dsi_parse_dcs_cmds(np, &ctrl->post_dms_on_cmds, + "qcom,mdss-dsi-post-mode-switch-on-command", + "qcom,mdss-dsi-post-mode-switch-on-command-state"); + + if (pinfo->mipi.dms_mode == DYNAMIC_MODE_SWITCH_IMMEDIATE && + !ctrl->post_dms_on_cmds.cmd_cnt) { + pr_warn("%s: No post dms on cmd specified\n", __func__); + pinfo->mipi.dms_mode = DYNAMIC_MODE_SWITCH_DISABLED; + } + + if (!ctrl->video2cmd.cmd_cnt || !ctrl->cmd2video.cmd_cnt) { + pr_warn("%s: No commands specified for dynamic switch\n", + __func__); + pinfo->mipi.dms_mode = DYNAMIC_MODE_SWITCH_DISABLED; + } +exit: + pr_info("%s: dynamic switch feature enabled: %d\n", __func__, + pinfo->mipi.dms_mode); + return; +} + +/* the length of all the valid values to be checked should not be great + * than the length of returned data from read command. + */ +static bool +mdss_dsi_parse_esd_check_valid_params(struct mdss_dsi_ctrl_pdata *ctrl) +{ + int i; + + for (i = 0; i < ctrl->status_cmds.cmd_cnt; ++i) { + if (ctrl->status_valid_params[i] > ctrl->status_cmds_rlen[i]) { + pr_debug("%s: ignore valid params!\n", __func__); + return false; + } + } + + return true; +} + +static bool mdss_dsi_parse_esd_status_len(struct device_node *np, + char *prop_key, u32 **target, u32 cmd_cnt) +{ + int tmp; + + if (!of_find_property(np, prop_key, &tmp)) + return false; + + tmp /= sizeof(u32); + if (tmp != cmd_cnt) { + pr_err("%s: request property number(%d) not match command count(%d)\n", + __func__, tmp, cmd_cnt); + return false; + } + + *target = kcalloc(tmp, sizeof(u32), GFP_KERNEL); + if (IS_ERR_OR_NULL(*target)) { + pr_err("%s: Error allocating memory for property\n", + __func__); + return false; + } + + if (of_property_read_u32_array(np, prop_key, *target, tmp)) { + pr_err("%s: cannot get values from dts\n", __func__); + kfree(*target); + *target = NULL; + return false; + } + + return true; +} + +static void mdss_dsi_parse_esd_params(struct device_node *np, + struct mdss_dsi_ctrl_pdata *ctrl) +{ + u32 tmp; + u32 i, status_len, *lenp; + int rc; + struct property *data; + const char *string; + struct mdss_panel_info *pinfo = &ctrl->panel_data.panel_info; + + pinfo->esd_check_enabled = of_property_read_bool(np, + "qcom,esd-check-enabled"); + + if (!pinfo->esd_check_enabled) + return; + + ctrl->status_mode = ESD_MAX; + rc = of_property_read_string(np, + "qcom,mdss-dsi-panel-status-check-mode", &string); + if (!rc) { + if (!strcmp(string, "bta_check")) { + ctrl->status_mode = ESD_BTA; + } else if (!strcmp(string, "reg_read")) { + ctrl->status_mode = ESD_REG; + ctrl->check_read_status = + mdss_dsi_gen_read_status; + } else if (!strcmp(string, "reg_read_nt35596")) { + ctrl->status_mode = ESD_REG_NT35596; + ctrl->status_error_count = 0; + ctrl->check_read_status = + mdss_dsi_nt35596_read_status; + } else if (!strcmp(string, "te_signal_check")) { + if (pinfo->mipi.mode == DSI_CMD_MODE) { + ctrl->status_mode = ESD_TE; + } else { + pr_err("TE-ESD not valid for video mode\n"); + goto error; + } + } else { + pr_err("No valid panel-status-check-mode string\n"); + goto error; + } + } + + if ((ctrl->status_mode == ESD_BTA) || (ctrl->status_mode == ESD_TE) || + (ctrl->status_mode == ESD_MAX)) + return; + + mdss_dsi_parse_dcs_cmds(np, &ctrl->status_cmds, + "qcom,mdss-dsi-panel-status-command", + "qcom,mdss-dsi-panel-status-command-state"); + + rc = of_property_read_u32(np, "qcom,mdss-dsi-panel-max-error-count", + &tmp); + ctrl->max_status_error_count = (!rc ? tmp : 0); + + if (!mdss_dsi_parse_esd_status_len(np, + "qcom,mdss-dsi-panel-status-read-length", + &ctrl->status_cmds_rlen, ctrl->status_cmds.cmd_cnt)) { + pinfo->esd_check_enabled = false; + return; + } + + if (mdss_dsi_parse_esd_status_len(np, + "qcom,mdss-dsi-panel-status-valid-params", + &ctrl->status_valid_params, ctrl->status_cmds.cmd_cnt)) { + if (!mdss_dsi_parse_esd_check_valid_params(ctrl)) + goto error1; + } + + status_len = 0; + lenp = ctrl->status_valid_params ?: ctrl->status_cmds_rlen; + for (i = 0; i < ctrl->status_cmds.cmd_cnt; ++i) + status_len += lenp[i]; + + data = of_find_property(np, "qcom,mdss-dsi-panel-status-value", &tmp); + tmp /= sizeof(u32); + if (!IS_ERR_OR_NULL(data) && tmp != 0 && (tmp % status_len) == 0) { + ctrl->groups = tmp / status_len; + } else { + pr_err("%s: Error parse panel-status-value\n", __func__); + goto error1; + } + + ctrl->status_value = kzalloc(sizeof(u32) * status_len * ctrl->groups, + GFP_KERNEL); + if (!ctrl->status_value) + goto error1; + + ctrl->return_buf = kcalloc(status_len * ctrl->groups, + sizeof(unsigned char), GFP_KERNEL); + if (!ctrl->return_buf) + goto error2; + + rc = of_property_read_u32_array(np, + "qcom,mdss-dsi-panel-status-value", + ctrl->status_value, ctrl->groups * status_len); + if (rc) { + pr_debug("%s: Error reading panel status values\n", + __func__); + memset(ctrl->status_value, 0, ctrl->groups * status_len); + } + + return; + +error2: + kfree(ctrl->status_value); +error1: + kfree(ctrl->status_valid_params); + kfree(ctrl->status_cmds_rlen); +error: + pinfo->esd_check_enabled = false; +} + +static void mdss_dsi_parse_partial_update_caps(struct device_node *np, + struct mdss_dsi_ctrl_pdata *ctrl) +{ + struct mdss_panel_info *pinfo; + const char *data; + + pinfo = &ctrl->panel_data.panel_info; + + data = of_get_property(np, "qcom,partial-update-enabled", NULL); + if (data && !strcmp(data, "single_roi")) + pinfo->partial_update_supported = + PU_SINGLE_ROI; + else if (data && !strcmp(data, "dual_roi")) + pinfo->partial_update_supported = + PU_DUAL_ROI; + else if (data && !strcmp(data, "none")) + pinfo->partial_update_supported = + PU_NOT_SUPPORTED; + else + pinfo->partial_update_supported = + PU_NOT_SUPPORTED; + + if (pinfo->mipi.mode == DSI_CMD_MODE) { + pinfo->partial_update_enabled = pinfo->partial_update_supported; + pr_info("%s: partial_update_enabled=%d\n", __func__, + pinfo->partial_update_enabled); + ctrl->set_col_page_addr = mdss_dsi_set_col_page_addr; + if (pinfo->partial_update_enabled) { + pinfo->partial_update_roi_merge = + of_property_read_bool(np, + "qcom,partial-update-roi-merge"); + } + } +} + +static int mdss_dsi_parse_panel_features(struct device_node *np, + struct mdss_dsi_ctrl_pdata *ctrl) +{ + struct mdss_panel_info *pinfo; + + if (!np || !ctrl) { + pr_err("%s: Invalid arguments\n", __func__); + return -ENODEV; + } + + pinfo = &ctrl->panel_data.panel_info; + + mdss_dsi_parse_partial_update_caps(np, ctrl); + + pinfo->dcs_cmd_by_left = of_property_read_bool(np, + "qcom,dcs-cmd-by-left"); + + pinfo->ulps_feature_enabled = of_property_read_bool(np, + "qcom,ulps-enabled"); + pr_info("%s: ulps feature %s\n", __func__, + (pinfo->ulps_feature_enabled ? "enabled" : "disabled")); + + pinfo->ulps_suspend_enabled = of_property_read_bool(np, + "qcom,suspend-ulps-enabled"); + pr_info("%s: ulps during suspend feature %s", __func__, + (pinfo->ulps_suspend_enabled ? "enabled" : "disabled")); + + mdss_dsi_parse_dms_config(np, ctrl); + + pinfo->panel_ack_disabled = pinfo->sim_panel_mode ? + 1 : of_property_read_bool(np, "qcom,panel-ack-disabled"); + + pinfo->allow_phy_power_off = of_property_read_bool(np, + "qcom,panel-allow-phy-poweroff"); + + mdss_dsi_parse_esd_params(np, ctrl); + + if (pinfo->panel_ack_disabled && pinfo->esd_check_enabled) { + pr_warn("ESD should not be enabled if panel ACK is disabled\n"); + pinfo->esd_check_enabled = false; + } + + if (ctrl->disp_en_gpio <= 0) { + ctrl->disp_en_gpio = of_get_named_gpio( + np, + "qcom,5v-boost-gpio", 0); + + if (!gpio_is_valid(ctrl->disp_en_gpio)) + pr_debug("%s:%d, Disp_en gpio not specified\n", + __func__, __LINE__); + } + + mdss_dsi_parse_dcs_cmds(np, &ctrl->lp_on_cmds, + "qcom,mdss-dsi-lp-mode-on", NULL); + + mdss_dsi_parse_dcs_cmds(np, &ctrl->lp_off_cmds, + "qcom,mdss-dsi-lp-mode-off", NULL); + + return 0; +} + +static void mdss_dsi_parse_panel_horizintal_line_idle(struct device_node *np, + struct mdss_dsi_ctrl_pdata *ctrl) +{ + const u32 *src; + int i, len, cnt; + struct panel_horizontal_idle *kp; + + if (!np || !ctrl) { + pr_err("%s: Invalid arguments\n", __func__); + return; + } + + src = of_get_property(np, "qcom,mdss-dsi-hor-line-idle", &len); + if (!src || len == 0) + return; + + cnt = len % 3; /* 3 fields per entry */ + if (cnt) { + pr_err("%s: invalid horizontal idle len=%d\n", __func__, len); + return; + } + + cnt = len / sizeof(u32); + + kp = kzalloc(sizeof(*kp) * (cnt / 3), GFP_KERNEL); + if (kp == NULL) { + pr_err("%s: No memory\n", __func__); + return; + } + + ctrl->line_idle = kp; + for (i = 0; i < cnt; i += 3) { + kp->min = be32_to_cpu(src[i]); + kp->max = be32_to_cpu(src[i+1]); + kp->idle = be32_to_cpu(src[i+2]); + kp++; + ctrl->horizontal_idle_cnt++; + } + + /* + * idle is enabled for this controller, this will be used to + * enable/disable burst mode since both features are mutually + * exclusive. + */ + ctrl->idle_enabled = true; + + pr_debug("%s: horizontal_idle_cnt=%d\n", __func__, + ctrl->horizontal_idle_cnt); +} + +static int mdss_dsi_set_refresh_rate_range(struct device_node *pan_node, + struct mdss_panel_info *pinfo) +{ + int rc = 0; + rc = of_property_read_u32(pan_node, + "qcom,mdss-dsi-min-refresh-rate", + &pinfo->min_fps); + if (rc) { + pr_warn("%s:%d, Unable to read min refresh rate\n", __func__, __LINE__); - } else { - for (i = 0; i < len; i++) - pt->phy_timing_8996[i] = data[i]; - phy_timings_present = true; - } - if (!phy_timings_present) { - pr_err("%s: phy timing settings not present\n", __func__); - return -EINVAL; - } - - rc = of_property_read_u32(np, "qcom,mdss-dsi-t-clk-pre", &tmp); - pt->t_clk_pre = (!rc ? tmp : 0x24); - rc = of_property_read_u32(np, "qcom,mdss-dsi-t-clk-post", &tmp); - pt->t_clk_post = (!rc ? tmp : 0x03); - - if (np->name) { - pt->timing.name = kstrdup(np->name, GFP_KERNEL); - pr_info("%s: found new timing \"%s\" (%pK)\n", __func__, - np->name, &pt->timing); - } - - return 0; - } - - static int mdss_dsi_panel_config_res_properties(struct device_node *np, - struct dsi_panel_timing *pt, - struct mdss_panel_data *panel_data, - bool default_timing) - { - int rc = 0; - - mdss_dsi_parse_roi_alignment(np, pt); - - mdss_dsi_parse_dcs_cmds(np, &pt->on_cmds, - "qcom,mdss-dsi-on-command", - "qcom,mdss-dsi-on-command-state"); - - mdss_dsi_parse_dcs_cmds(np, &pt->post_panel_on_cmds, - "qcom,mdss-dsi-post-panel-on-command", NULL); - - mdss_dsi_parse_dcs_cmds(np, &pt->switch_cmds, - "qcom,mdss-dsi-timing-switch-command", - "qcom,mdss-dsi-timing-switch-command-state"); - - rc = mdss_dsi_parse_topology_config(np, pt, panel_data, default_timing); - if (rc) { - pr_err("%s: parsing compression params failed. rc:%d\n", - __func__, rc); - return rc; - } - - mdss_panel_parse_te_params(np, &pt->timing); - return rc; - } - - static int mdss_panel_parse_display_timings(struct device_node *np, - struct mdss_panel_data *panel_data) - { - struct mdss_dsi_ctrl_pdata *ctrl; - struct dsi_panel_timing *modedb; - struct device_node *timings_np; - struct device_node *entry; - int num_timings, rc; - int i = 0, active_ndx = 0; - bool default_timing = false; - - ctrl = container_of(panel_data, struct mdss_dsi_ctrl_pdata, panel_data); - - INIT_LIST_HEAD(&panel_data->timings_list); - - timings_np = of_get_child_by_name(np, "qcom,mdss-dsi-display-timings"); - if (!timings_np) { - struct dsi_panel_timing pt; - memset(&pt, 0, sizeof(struct dsi_panel_timing)); - - /* - * display timings node is not available, fallback to reading - * timings directly from root node instead - */ - pr_debug("reading display-timings from panel node\n"); - rc = mdss_dsi_panel_timing_from_dt(np, &pt, panel_data); - if (!rc) { - mdss_dsi_panel_config_res_properties(np, &pt, - panel_data, true); - rc = mdss_dsi_panel_timing_switch(ctrl, &pt.timing); - } - return rc; - } - - num_timings = of_get_child_count(timings_np); - if (num_timings == 0) { - pr_err("no timings found within display-timings\n"); - rc = -EINVAL; - goto exit; - } - - modedb = kcalloc(num_timings, sizeof(*modedb), GFP_KERNEL); - if (!modedb) { - rc = -ENOMEM; - goto exit; - } - - for_each_child_of_node(timings_np, entry) { - rc = mdss_dsi_panel_timing_from_dt(entry, (modedb + i), - panel_data); - if (rc) { - kfree(modedb); - goto exit; - } - - default_timing = of_property_read_bool(entry, - "qcom,mdss-dsi-timing-default"); - if (default_timing) - active_ndx = i; - - mdss_dsi_panel_config_res_properties(entry, (modedb + i), - panel_data, default_timing); - - list_add(&modedb[i].timing.list, - &panel_data->timings_list); - i++; - } - - /* Configure default timing settings */ - rc = mdss_dsi_panel_timing_switch(ctrl, &modedb[active_ndx].timing); - if (rc) - pr_err("unable to configure default timing settings\n"); - - exit: - of_node_put(timings_np); - - return rc; - } - - static int mdss_panel_parse_dt(struct device_node *np, - struct mdss_dsi_ctrl_pdata *ctrl_pdata) - { - u32 tmp; - int rc, len = 0; - const char *data; - static const char *pdest; - const char *bridge_chip_name; - struct mdss_panel_info *pinfo = &(ctrl_pdata->panel_data.panel_info); - - if (mdss_dsi_is_hw_config_split(ctrl_pdata->shared_data)) - pinfo->is_split_display = true; - - rc = of_property_read_u32(np, - "qcom,mdss-pan-physical-width-dimension", &tmp); - pinfo->physical_width = (!rc ? tmp : 0); - rc = of_property_read_u32(np, - "qcom,mdss-pan-physical-height-dimension", &tmp); - pinfo->physical_height = (!rc ? tmp : 0); - - rc = of_property_read_u32(np, "qcom,mdss-dsi-bpp", &tmp); - if (rc) { - pr_err("%s:%d, bpp not specified\n", __func__, __LINE__); - return -EINVAL; - } - pinfo->bpp = (!rc ? tmp : 24); - pinfo->mipi.mode = DSI_VIDEO_MODE; - data = of_get_property(np, "qcom,mdss-dsi-panel-type", NULL); - if (data && !strncmp(data, "dsi_cmd_mode", 12)) - pinfo->mipi.mode = DSI_CMD_MODE; - pinfo->mipi.boot_mode = pinfo->mipi.mode; - tmp = 0; - data = of_get_property(np, "qcom,mdss-dsi-pixel-packing", NULL); - if (data && !strcmp(data, "loose")) - pinfo->mipi.pixel_packing = 1; - else - pinfo->mipi.pixel_packing = 0; - rc = mdss_panel_get_dst_fmt(pinfo->bpp, - pinfo->mipi.mode, pinfo->mipi.pixel_packing, - &(pinfo->mipi.dst_format)); - if (rc) { - pr_debug("%s: problem determining dst format. Set Default\n", - __func__); - pinfo->mipi.dst_format = - DSI_VIDEO_DST_FORMAT_RGB888; - } - pdest = of_get_property(np, - "qcom,mdss-dsi-panel-destination", NULL); - - rc = of_property_read_u32(np, - "qcom,mdss-dsi-underflow-color", &tmp); - pinfo->lcdc.underflow_clr = (!rc ? tmp : 0xff); - rc = of_property_read_u32(np, - "qcom,mdss-dsi-border-color", &tmp); - pinfo->lcdc.border_clr = (!rc ? tmp : 0); - data = of_get_property(np, "qcom,mdss-dsi-panel-orientation", NULL); - if (data) { - pr_debug("panel orientation is %s\n", data); - if (!strcmp(data, "180")) - pinfo->panel_orientation = MDP_ROT_180; - else if (!strcmp(data, "hflip")) - pinfo->panel_orientation = MDP_FLIP_LR; - else if (!strcmp(data, "vflip")) - pinfo->panel_orientation = MDP_FLIP_UD; - } - - rc = of_property_read_u32(np, "qcom,mdss-brightness-max-level", &tmp); - pinfo->brightness_max = (!rc ? tmp : MDSS_MAX_BL_BRIGHTNESS); - rc = of_property_read_u32(np, "qcom,mdss-dsi-bl-min-level", &tmp); - pinfo->bl_min = (!rc ? tmp : 0); - rc = of_property_read_u32(np, "qcom,mdss-dsi-bl-max-level", &tmp); - pinfo->bl_max = (!rc ? tmp : 255); - ctrl_pdata->bklt_max = pinfo->bl_max; - - rc = of_property_read_u32(np, "qcom,mdss-dsi-interleave-mode", &tmp); - pinfo->mipi.interleave_mode = (!rc ? tmp : 0); - - pinfo->mipi.vsync_enable = of_property_read_bool(np, - "qcom,mdss-dsi-te-check-enable"); - - if (pinfo->sim_panel_mode == SIM_SW_TE_MODE) - pinfo->mipi.hw_vsync_mode = false; - else - pinfo->mipi.hw_vsync_mode = of_property_read_bool(np, - "qcom,mdss-dsi-te-using-te-pin"); - - rc = of_property_read_u32(np, - "qcom,mdss-dsi-h-sync-pulse", &tmp); - pinfo->mipi.pulse_mode_hsa_he = (!rc ? tmp : false); - - pinfo->mipi.hfp_power_stop = of_property_read_bool(np, - "qcom,mdss-dsi-hfp-power-mode"); - pinfo->mipi.hsa_power_stop = of_property_read_bool(np, - "qcom,mdss-dsi-hsa-power-mode"); - pinfo->mipi.hbp_power_stop = of_property_read_bool(np, - "qcom,mdss-dsi-hbp-power-mode"); - pinfo->mipi.last_line_interleave_en = of_property_read_bool(np, - "qcom,mdss-dsi-last-line-interleave"); - pinfo->mipi.bllp_power_stop = of_property_read_bool(np, - "qcom,mdss-dsi-bllp-power-mode"); - pinfo->mipi.eof_bllp_power_stop = of_property_read_bool( - np, "qcom,mdss-dsi-bllp-eof-power-mode"); - pinfo->mipi.traffic_mode = DSI_NON_BURST_SYNCH_PULSE; - data = of_get_property(np, "qcom,mdss-dsi-traffic-mode", NULL); - if (data) { - if (!strcmp(data, "non_burst_sync_event")) - pinfo->mipi.traffic_mode = DSI_NON_BURST_SYNCH_EVENT; - else if (!strcmp(data, "burst_mode")) - pinfo->mipi.traffic_mode = DSI_BURST_MODE; - } - rc = of_property_read_u32(np, - "qcom,mdss-dsi-te-dcs-command", &tmp); - pinfo->mipi.insert_dcs_cmd = - (!rc ? tmp : 1); - rc = of_property_read_u32(np, - "qcom,mdss-dsi-wr-mem-continue", &tmp); - pinfo->mipi.wr_mem_continue = - (!rc ? tmp : 0x3c); - rc = of_property_read_u32(np, - "qcom,mdss-dsi-wr-mem-start", &tmp); - pinfo->mipi.wr_mem_start = - (!rc ? tmp : 0x2c); - rc = of_property_read_u32(np, - "qcom,mdss-dsi-te-pin-select", &tmp); - pinfo->mipi.te_sel = - (!rc ? tmp : 1); - rc = of_property_read_u32(np, "qcom,mdss-dsi-virtual-channel-id", &tmp); - pinfo->mipi.vc = (!rc ? tmp : 0); - pinfo->mipi.rgb_swap = DSI_RGB_SWAP_RGB; - data = of_get_property(np, "qcom,mdss-dsi-color-order", NULL); - if (data) { - if (!strcmp(data, "rgb_swap_rbg")) - pinfo->mipi.rgb_swap = DSI_RGB_SWAP_RBG; - else if (!strcmp(data, "rgb_swap_bgr")) - pinfo->mipi.rgb_swap = DSI_RGB_SWAP_BGR; - else if (!strcmp(data, "rgb_swap_brg")) - pinfo->mipi.rgb_swap = DSI_RGB_SWAP_BRG; - else if (!strcmp(data, "rgb_swap_grb")) - pinfo->mipi.rgb_swap = DSI_RGB_SWAP_GRB; - else if (!strcmp(data, "rgb_swap_gbr")) - pinfo->mipi.rgb_swap = DSI_RGB_SWAP_GBR; - } - pinfo->mipi.data_lane0 = of_property_read_bool(np, - "qcom,mdss-dsi-lane-0-state"); - pinfo->mipi.data_lane1 = of_property_read_bool(np, - "qcom,mdss-dsi-lane-1-state"); - pinfo->mipi.data_lane2 = of_property_read_bool(np, - "qcom,mdss-dsi-lane-2-state"); - pinfo->mipi.data_lane3 = of_property_read_bool(np, - "qcom,mdss-dsi-lane-3-state"); - - /* parse split link properties */ - rc = mdss_dsi_parse_split_link_settings(np, pinfo); - if (rc) - return rc; - - rc = mdss_panel_parse_display_timings(np, &ctrl_pdata->panel_data); - if (rc) - return rc; - - rc = mdss_dsi_parse_hdr_settings(np, pinfo); - if (rc) - return rc; - - pinfo->mipi.rx_eot_ignore = of_property_read_bool(np, - "qcom,mdss-dsi-rx-eot-ignore"); - pinfo->mipi.tx_eot_append = of_property_read_bool(np, - "qcom,mdss-dsi-tx-eot-append"); - - rc = of_property_read_u32(np, "qcom,mdss-dsi-stream", &tmp); - pinfo->mipi.stream = (!rc ? tmp : 0); - - data = of_get_property(np, "qcom,mdss-dsi-mode-sel-gpio-state", NULL); - if (data) { - if (!strcmp(data, "single_port")) - pinfo->mode_sel_state = MODE_SEL_SINGLE_PORT; - else if (!strcmp(data, "dual_port")) - pinfo->mode_sel_state = MODE_SEL_DUAL_PORT; - else if (!strcmp(data, "high")) - pinfo->mode_sel_state = MODE_GPIO_HIGH; - else if (!strcmp(data, "low")) - pinfo->mode_sel_state = MODE_GPIO_LOW; - } else { - /* Set default mode as SPLIT mode */ - pinfo->mode_sel_state = MODE_SEL_DUAL_PORT; - } - - rc = of_property_read_u32(np, "qcom,mdss-mdp-transfer-time-us", &tmp); - pinfo->mdp_transfer_time_us = (!rc ? tmp : DEFAULT_MDP_TRANSFER_TIME); - - mdss_dsi_parse_mdp_kickoff_threshold(np, pinfo); - - pinfo->mipi.lp11_init = of_property_read_bool(np, - "qcom,mdss-dsi-lp11-init"); - rc = of_property_read_u32(np, "qcom,mdss-dsi-init-delay-us", &tmp); - pinfo->mipi.init_delay = (!rc ? tmp : 0); - - rc = of_property_read_u32(np, "qcom,mdss-dsi-post-init-delay", &tmp); - pinfo->mipi.post_init_delay = (!rc ? tmp : 0); - - mdss_dsi_parse_trigger(np, &(pinfo->mipi.mdp_trigger), - "qcom,mdss-dsi-mdp-trigger"); - - mdss_dsi_parse_trigger(np, &(pinfo->mipi.dma_trigger), - "qcom,mdss-dsi-dma-trigger"); - - mdss_dsi_parse_reset_seq(np, pinfo->rst_seq, &(pinfo->rst_seq_len), - "qcom,mdss-dsi-reset-sequence"); - - mdss_dsi_parse_dcs_cmds(np, &ctrl_pdata->off_cmds, - "qcom,mdss-dsi-off-command", "qcom,mdss-dsi-off-command-state"); - - rc = of_property_read_u32(np, "qcom,adjust-timer-wakeup-ms", &tmp); - pinfo->adjust_timer_delay_ms = (!rc ? tmp : 0); - - pinfo->mipi.force_clk_lane_hs = of_property_read_bool(np, - "qcom,mdss-dsi-force-clock-lane-hs"); - - rc = mdss_dsi_parse_panel_features(np, ctrl_pdata); - if (rc) { - pr_err("%s: failed to parse panel features\n", __func__); - goto error; - } - - mdss_dsi_parse_panel_horizintal_line_idle(np, ctrl_pdata); - - mdss_dsi_parse_dfps_config(np, ctrl_pdata); - - mdss_dsi_set_refresh_rate_range(np, pinfo); - - pinfo->is_dba_panel = of_property_read_bool(np, - "qcom,dba-panel"); + + /* + * If min refresh rate is not specified, set it to the + * default panel refresh rate. + */ + pinfo->min_fps = pinfo->mipi.frame_rate; + rc = 0; + } + + rc = of_property_read_u32(pan_node, + "qcom,mdss-dsi-max-refresh-rate", + &pinfo->max_fps); + if (rc) { + pr_warn("%s:%d, Unable to read max refresh rate\n", + __func__, __LINE__); + + /* + * Since max refresh rate was not specified when dynamic + * fps is enabled, using the default panel refresh rate + * as max refresh rate supported. + */ + pinfo->max_fps = pinfo->mipi.frame_rate; + rc = 0; + } + + pr_info("dyn_fps: min = %d, max = %d\n", + pinfo->min_fps, pinfo->max_fps); + return rc; +} + +static void mdss_dsi_parse_dfps_config(struct device_node *pan_node, + struct mdss_dsi_ctrl_pdata *ctrl_pdata) +{ + const char *data; + bool dynamic_fps, dynamic_bitclk; + struct mdss_panel_info *pinfo = &(ctrl_pdata->panel_data.panel_info); + int rc = 0; + + dynamic_fps = of_property_read_bool(pan_node, + "qcom,mdss-dsi-pan-enable-dynamic-fps"); + + if (!dynamic_fps) + goto dynamic_bitclk; + + pinfo->dynamic_fps = true; + data = of_get_property(pan_node, "qcom,mdss-dsi-pan-fps-update", NULL); + if (data) { + if (!strcmp(data, "dfps_suspend_resume_mode")) { + pinfo->dfps_update = DFPS_SUSPEND_RESUME_MODE; + pr_debug("dfps mode: suspend/resume\n"); + } else if (!strcmp(data, "dfps_immediate_clk_mode")) { + pinfo->dfps_update = DFPS_IMMEDIATE_CLK_UPDATE_MODE; + pr_debug("dfps mode: Immediate clk\n"); + } else if (!strcmp(data, "dfps_immediate_porch_mode_hfp")) { + pinfo->dfps_update = + DFPS_IMMEDIATE_PORCH_UPDATE_MODE_HFP; + pr_debug("dfps mode: Immediate porch HFP\n"); + } else if (!strcmp(data, "dfps_immediate_porch_mode_vfp")) { + pinfo->dfps_update = + DFPS_IMMEDIATE_PORCH_UPDATE_MODE_VFP; + pr_debug("dfps mode: Immediate porch VFP\n"); + } else { + pinfo->dfps_update = DFPS_SUSPEND_RESUME_MODE; + pr_debug("default dfps mode: suspend/resume\n"); + } + } else { + pinfo->dynamic_fps = false; + pr_debug("dfps update mode not configured: disable\n"); + } + pinfo->new_fps = pinfo->mipi.frame_rate; + pinfo->current_fps = pinfo->mipi.frame_rate; + +dynamic_bitclk: + dynamic_bitclk = of_property_read_bool(pan_node, + "qcom,mdss-dsi-pan-enable-dynamic-bitclk"); + if (!dynamic_bitclk) + return; + + of_find_property(pan_node, "qcom,mdss-dsi-dynamic-bitclk_freq", + &pinfo->supp_bitclk_len); + pinfo->supp_bitclk_len = pinfo->supp_bitclk_len/sizeof(u32); + if (pinfo->supp_bitclk_len < 1) + return; + + pinfo->supp_bitclks = kzalloc((sizeof(u32) * pinfo->supp_bitclk_len), + GFP_KERNEL); + if (!pinfo->supp_bitclks) + return; + + rc = of_property_read_u32_array(pan_node, + "qcom,mdss-dsi-dynamic-bitclk_freq", pinfo->supp_bitclks, + pinfo->supp_bitclk_len); + if (rc) { + pr_err("Error from dynamic bitclk freq u64 array read\n"); + return; + } + pinfo->dynamic_bitclk = true; + return; +} + +int mdss_panel_parse_bl_settings(struct device_node *np, + struct mdss_dsi_ctrl_pdata *ctrl_pdata) +{ + const char *data; + int rc = 0; + u32 tmp; + + ctrl_pdata->bklt_ctrl = UNKNOWN_CTRL; + data = of_get_property(np, "qcom,mdss-dsi-bl-pmic-control-type", NULL); + if (data) { + if (!strcmp(data, "bl_ctrl_wled")) { + led_trigger_register_simple("bkl-trigger", + &bl_led_trigger); + pr_debug("%s: SUCCESS-> WLED TRIGGER register\n", + __func__); + ctrl_pdata->bklt_ctrl = BL_WLED; + } else if (!strcmp(data, "bl_ctrl_pwm")) { + ctrl_pdata->bklt_ctrl = BL_PWM; + ctrl_pdata->pwm_pmi = of_property_read_bool(np, + "qcom,mdss-dsi-bl-pwm-pmi"); + rc = of_property_read_u32(np, + "qcom,mdss-dsi-bl-pmic-pwm-frequency", &tmp); + if (rc) { + pr_err("%s:%d, Error, panel pwm_period\n", + __func__, __LINE__); + return -EINVAL; + } + ctrl_pdata->pwm_period = tmp; + if (ctrl_pdata->pwm_pmi) { + ctrl_pdata->pwm_bl = of_pwm_get(np, NULL); + if (IS_ERR(ctrl_pdata->pwm_bl)) { + pr_err("%s: Error, pwm device\n", + __func__); + ctrl_pdata->pwm_bl = NULL; + return -EINVAL; + } + } else { + rc = of_property_read_u32(np, + "qcom,mdss-dsi-bl-pmic-bank-select", + &tmp); + if (rc) { + pr_err("%s:%d, Error, lpg channel\n", + __func__, __LINE__); + return -EINVAL; + } + ctrl_pdata->pwm_lpg_chan = tmp; + tmp = of_get_named_gpio(np, + "qcom,mdss-dsi-pwm-gpio", 0); + ctrl_pdata->pwm_pmic_gpio = tmp; + pr_debug("%s: Configured PWM bklt ctrl\n", + __func__); + } + } else if (!strcmp(data, "bl_ctrl_dcs")) { + ctrl_pdata->bklt_ctrl = BL_DCS_CMD; + data = of_get_property(np, + "qcom,mdss-dsi-bl-dcs-command-state", NULL); + if (data && !strcmp(data, "dsi_hs_mode")) + ctrl_pdata->bklt_dcs_op_mode = DSI_HS_MODE; + else + ctrl_pdata->bklt_dcs_op_mode = DSI_LP_MODE; + + pr_debug("%s: Configured DCS_CMD bklt ctrl\n", + __func__); + } + } + return 0; +} + +int mdss_dsi_panel_timing_switch(struct mdss_dsi_ctrl_pdata *ctrl, + struct mdss_panel_timing *timing) +{ + struct dsi_panel_timing *pt; + struct mdss_panel_info *pinfo = &ctrl->panel_data.panel_info; + int i; + + if (!timing) + return -EINVAL; + + if (timing == ctrl->panel_data.current_timing) { + pr_warn("%s: panel timing \"%s\" already set\n", __func__, + timing->name); + return 0; /* nothing to do */ + } + + pr_debug("%s: ndx=%d switching to panel timing \"%s\"\n", __func__, + ctrl->ndx, timing->name); + + mdss_panel_info_from_timing(timing, pinfo); + + pt = container_of(timing, struct dsi_panel_timing, timing); + pinfo->mipi.t_clk_pre = pt->t_clk_pre; + pinfo->mipi.t_clk_post = pt->t_clk_post; + + for (i = 0; i < ARRAY_SIZE(pt->phy_timing); i++) + pinfo->mipi.dsi_phy_db.timing[i] = pt->phy_timing[i]; + + for (i = 0; i < ARRAY_SIZE(pt->phy_timing_8996); i++) + pinfo->mipi.dsi_phy_db.timing_8996[i] = pt->phy_timing_8996[i]; + + ctrl->on_cmds = pt->on_cmds; + ctrl->post_panel_on_cmds = pt->post_panel_on_cmds; + + ctrl->panel_data.current_timing = timing; + if (!timing->clk_rate) + ctrl->refresh_clk_rate = true; + mdss_dsi_clk_refresh(&ctrl->panel_data, ctrl->update_phy_timing); + + return 0; +} + +void mdss_dsi_unregister_bl_settings(struct mdss_dsi_ctrl_pdata *ctrl_pdata) +{ + if (ctrl_pdata->bklt_ctrl == BL_WLED) + led_trigger_unregister_simple(bl_led_trigger); +} + +static int mdss_dsi_panel_timing_from_dt(struct device_node *np, + struct dsi_panel_timing *pt, + struct mdss_panel_data *panel_data) +{ + u32 tmp; + u64 tmp64; + int rc, i, len; + const char *data; + struct mdss_dsi_ctrl_pdata *ctrl_pdata; + struct mdss_panel_info *pinfo; + bool phy_timings_present = false; + + pinfo = &panel_data->panel_info; + + ctrl_pdata = container_of(panel_data, struct mdss_dsi_ctrl_pdata, + panel_data); + + rc = of_property_read_u32(np, "qcom,mdss-dsi-panel-width", &tmp); + if (rc) { + pr_err("%s:%d, panel width not specified\n", + __func__, __LINE__); + return -EINVAL; + } + pt->timing.xres = tmp; + + rc = of_property_read_u32(np, "qcom,mdss-dsi-panel-height", &tmp); + if (rc) { + pr_err("%s:%d, panel height not specified\n", + __func__, __LINE__); + return -EINVAL; + } + pt->timing.yres = tmp; + + rc = of_property_read_u32(np, "qcom,mdss-dsi-h-front-porch", &tmp); + pt->timing.h_front_porch = (!rc ? tmp : 6); + rc = of_property_read_u32(np, "qcom,mdss-dsi-h-back-porch", &tmp); + pt->timing.h_back_porch = (!rc ? tmp : 6); + rc = of_property_read_u32(np, "qcom,mdss-dsi-h-pulse-width", &tmp); + pt->timing.h_pulse_width = (!rc ? tmp : 2); + rc = of_property_read_u32(np, "qcom,mdss-dsi-h-sync-skew", &tmp); + pt->timing.hsync_skew = (!rc ? tmp : 0); + rc = of_property_read_u32(np, "qcom,mdss-dsi-v-back-porch", &tmp); + pt->timing.v_back_porch = (!rc ? tmp : 6); + rc = of_property_read_u32(np, "qcom,mdss-dsi-v-front-porch", &tmp); + pt->timing.v_front_porch = (!rc ? tmp : 6); + rc = of_property_read_u32(np, "qcom,mdss-dsi-v-pulse-width", &tmp); + pt->timing.v_pulse_width = (!rc ? tmp : 2); + + rc = of_property_read_u32(np, "qcom,mdss-dsi-h-left-border", &tmp); + pt->timing.border_left = !rc ? tmp : 0; + rc = of_property_read_u32(np, "qcom,mdss-dsi-h-right-border", &tmp); + pt->timing.border_right = !rc ? tmp : 0; + + /* overriding left/right borders for split display cases */ + if (mdss_dsi_is_hw_config_split(ctrl_pdata->shared_data)) { + if (panel_data->next) + pt->timing.border_right = 0; + else + pt->timing.border_left = 0; + } + + rc = of_property_read_u32(np, "qcom,mdss-dsi-v-top-border", &tmp); + pt->timing.border_top = !rc ? tmp : 0; + rc = of_property_read_u32(np, "qcom,mdss-dsi-v-bottom-border", &tmp); + pt->timing.border_bottom = !rc ? tmp : 0; + + rc = of_property_read_u32(np, "qcom,mdss-dsi-panel-framerate", &tmp); + pt->timing.frame_rate = !rc ? tmp : DEFAULT_FRAME_RATE; + rc = of_property_read_u64(np, "qcom,mdss-dsi-panel-clockrate", &tmp64); + if (rc == -EOVERFLOW) { + tmp64 = 0; + rc = of_property_read_u32(np, + "qcom,mdss-dsi-panel-clockrate", (u32 *)&tmp64); + } + pt->timing.clk_rate = !rc ? tmp64 : 0; + + data = of_get_property(np, "qcom,mdss-dsi-panel-timings", &len); + if ((!data) || (len != 12)) { + pr_debug("%s:%d, Unable to read Phy timing settings", + __func__, __LINE__); + } else { + for (i = 0; i < len; i++) + pt->phy_timing[i] = data[i]; + phy_timings_present = true; + } + + data = of_get_property(np, "qcom,mdss-dsi-panel-timings-phy-v2", &len); + if ((!data) || (len != 40)) { + pr_debug("%s:%d, Unable to read phy-v2 lane timing settings", + __func__, __LINE__); + } else { + for (i = 0; i < len; i++) + pt->phy_timing_8996[i] = data[i]; + phy_timings_present = true; + } + if (!phy_timings_present) { + pr_err("%s: phy timing settings not present\n", __func__); + return -EINVAL; + } + + rc = of_property_read_u32(np, "qcom,mdss-dsi-t-clk-pre", &tmp); + pt->t_clk_pre = (!rc ? tmp : 0x24); + rc = of_property_read_u32(np, "qcom,mdss-dsi-t-clk-post", &tmp); + pt->t_clk_post = (!rc ? tmp : 0x03); + + if (np->name) { + pt->timing.name = kstrdup(np->name, GFP_KERNEL); + pr_info("%s: found new timing \"%s\" (%pK)\n", __func__, + np->name, &pt->timing); + } + + return 0; +} + +static int mdss_dsi_panel_config_res_properties(struct device_node *np, + struct dsi_panel_timing *pt, + struct mdss_panel_data *panel_data, + bool default_timing) +{ + int rc = 0; + + mdss_dsi_parse_roi_alignment(np, pt); + + mdss_dsi_parse_dcs_cmds(np, &pt->on_cmds, + "qcom,mdss-dsi-on-command", + "qcom,mdss-dsi-on-command-state"); + + mdss_dsi_parse_dcs_cmds(np, &pt->post_panel_on_cmds, + "qcom,mdss-dsi-post-panel-on-command", NULL); + + mdss_dsi_parse_dcs_cmds(np, &pt->switch_cmds, + "qcom,mdss-dsi-timing-switch-command", + "qcom,mdss-dsi-timing-switch-command-state"); + + rc = mdss_dsi_parse_topology_config(np, pt, panel_data, default_timing); + if (rc) { + pr_err("%s: parsing compression params failed. rc:%d\n", + __func__, rc); + return rc; + } + + mdss_panel_parse_te_params(np, &pt->timing); + return rc; +} + +static int mdss_panel_parse_display_timings(struct device_node *np, + struct mdss_panel_data *panel_data) +{ + struct mdss_dsi_ctrl_pdata *ctrl; + struct dsi_panel_timing *modedb; + struct device_node *timings_np; + struct device_node *entry; + int num_timings, rc; + int i = 0, active_ndx = 0; + bool default_timing = false; + + ctrl = container_of(panel_data, struct mdss_dsi_ctrl_pdata, panel_data); + + INIT_LIST_HEAD(&panel_data->timings_list); + + timings_np = of_get_child_by_name(np, "qcom,mdss-dsi-display-timings"); + if (!timings_np) { + struct dsi_panel_timing pt; + memset(&pt, 0, sizeof(struct dsi_panel_timing)); + + /* + * display timings node is not available, fallback to reading + * timings directly from root node instead + */ + pr_debug("reading display-timings from panel node\n"); + rc = mdss_dsi_panel_timing_from_dt(np, &pt, panel_data); + if (!rc) { + mdss_dsi_panel_config_res_properties(np, &pt, + panel_data, true); + rc = mdss_dsi_panel_timing_switch(ctrl, &pt.timing); + } + return rc; + } + + num_timings = of_get_child_count(timings_np); + if (num_timings == 0) { + pr_err("no timings found within display-timings\n"); + rc = -EINVAL; + goto exit; + } + + modedb = kcalloc(num_timings, sizeof(*modedb), GFP_KERNEL); + if (!modedb) { + rc = -ENOMEM; + goto exit; + } + + for_each_child_of_node(timings_np, entry) { + rc = mdss_dsi_panel_timing_from_dt(entry, (modedb + i), + panel_data); + if (rc) { + kfree(modedb); + goto exit; + } + + default_timing = of_property_read_bool(entry, + "qcom,mdss-dsi-timing-default"); + if (default_timing) + active_ndx = i; + + mdss_dsi_panel_config_res_properties(entry, (modedb + i), + panel_data, default_timing); + + list_add(&modedb[i].timing.list, + &panel_data->timings_list); + i++; + } + + /* Configure default timing settings */ + rc = mdss_dsi_panel_timing_switch(ctrl, &modedb[active_ndx].timing); + if (rc) + pr_err("unable to configure default timing settings\n"); + +exit: + of_node_put(timings_np); + + return rc; +} + +static int mdss_panel_parse_dt(struct device_node *np, + struct mdss_dsi_ctrl_pdata *ctrl_pdata) +{ + u32 tmp; + int rc, len = 0; + const char *data; + static const char *pdest; + const char *bridge_chip_name; + struct mdss_panel_info *pinfo = &(ctrl_pdata->panel_data.panel_info); + + if (mdss_dsi_is_hw_config_split(ctrl_pdata->shared_data)) + pinfo->is_split_display = true; + + rc = of_property_read_u32(np, + "qcom,mdss-pan-physical-width-dimension", &tmp); + pinfo->physical_width = (!rc ? tmp : 0); + rc = of_property_read_u32(np, + "qcom,mdss-pan-physical-height-dimension", &tmp); + pinfo->physical_height = (!rc ? tmp : 0); + + rc = of_property_read_u32(np, "qcom,mdss-dsi-bpp", &tmp); + if (rc) { + pr_err("%s:%d, bpp not specified\n", __func__, __LINE__); + return -EINVAL; + } + pinfo->bpp = (!rc ? tmp : 24); + pinfo->mipi.mode = DSI_VIDEO_MODE; + data = of_get_property(np, "qcom,mdss-dsi-panel-type", NULL); + if (data && !strncmp(data, "dsi_cmd_mode", 12)) + pinfo->mipi.mode = DSI_CMD_MODE; + pinfo->mipi.boot_mode = pinfo->mipi.mode; + tmp = 0; + data = of_get_property(np, "qcom,mdss-dsi-pixel-packing", NULL); + if (data && !strcmp(data, "loose")) + pinfo->mipi.pixel_packing = 1; + else + pinfo->mipi.pixel_packing = 0; + rc = mdss_panel_get_dst_fmt(pinfo->bpp, + pinfo->mipi.mode, pinfo->mipi.pixel_packing, + &(pinfo->mipi.dst_format)); + if (rc) { + pr_debug("%s: problem determining dst format. Set Default\n", + __func__); + pinfo->mipi.dst_format = + DSI_VIDEO_DST_FORMAT_RGB888; + } + pdest = of_get_property(np, + "qcom,mdss-dsi-panel-destination", NULL); + + rc = of_property_read_u32(np, + "qcom,mdss-dsi-underflow-color", &tmp); + pinfo->lcdc.underflow_clr = (!rc ? tmp : 0xff); + rc = of_property_read_u32(np, + "qcom,mdss-dsi-border-color", &tmp); + pinfo->lcdc.border_clr = (!rc ? tmp : 0); + data = of_get_property(np, "qcom,mdss-dsi-panel-orientation", NULL); + if (data) { + pr_debug("panel orientation is %s\n", data); + if (!strcmp(data, "180")) + pinfo->panel_orientation = MDP_ROT_180; + else if (!strcmp(data, "hflip")) + pinfo->panel_orientation = MDP_FLIP_LR; + else if (!strcmp(data, "vflip")) + pinfo->panel_orientation = MDP_FLIP_UD; + } + + rc = of_property_read_u32(np, "qcom,mdss-brightness-max-level", &tmp); + pinfo->brightness_max = (!rc ? tmp : MDSS_MAX_BL_BRIGHTNESS); + rc = of_property_read_u32(np, "qcom,mdss-dsi-bl-min-level", &tmp); + pinfo->bl_min = (!rc ? tmp : 0); + rc = of_property_read_u32(np, "qcom,mdss-dsi-bl-max-level", &tmp); + pinfo->bl_max = (!rc ? tmp : 255); + ctrl_pdata->bklt_max = pinfo->bl_max; + + rc = of_property_read_u32(np, "qcom,mdss-dsi-interleave-mode", &tmp); + pinfo->mipi.interleave_mode = (!rc ? tmp : 0); + + pinfo->mipi.vsync_enable = of_property_read_bool(np, + "qcom,mdss-dsi-te-check-enable"); + + if (pinfo->sim_panel_mode == SIM_SW_TE_MODE) + pinfo->mipi.hw_vsync_mode = false; + else + pinfo->mipi.hw_vsync_mode = of_property_read_bool(np, + "qcom,mdss-dsi-te-using-te-pin"); + + rc = of_property_read_u32(np, + "qcom,mdss-dsi-h-sync-pulse", &tmp); + pinfo->mipi.pulse_mode_hsa_he = (!rc ? tmp : false); + + pinfo->mipi.hfp_power_stop = of_property_read_bool(np, + "qcom,mdss-dsi-hfp-power-mode"); + pinfo->mipi.hsa_power_stop = of_property_read_bool(np, + "qcom,mdss-dsi-hsa-power-mode"); + pinfo->mipi.hbp_power_stop = of_property_read_bool(np, + "qcom,mdss-dsi-hbp-power-mode"); + pinfo->mipi.last_line_interleave_en = of_property_read_bool(np, + "qcom,mdss-dsi-last-line-interleave"); + pinfo->mipi.bllp_power_stop = of_property_read_bool(np, + "qcom,mdss-dsi-bllp-power-mode"); + pinfo->mipi.eof_bllp_power_stop = of_property_read_bool( + np, "qcom,mdss-dsi-bllp-eof-power-mode"); + pinfo->mipi.traffic_mode = DSI_NON_BURST_SYNCH_PULSE; + data = of_get_property(np, "qcom,mdss-dsi-traffic-mode", NULL); + if (data) { + if (!strcmp(data, "non_burst_sync_event")) + pinfo->mipi.traffic_mode = DSI_NON_BURST_SYNCH_EVENT; + else if (!strcmp(data, "burst_mode")) + pinfo->mipi.traffic_mode = DSI_BURST_MODE; + } + rc = of_property_read_u32(np, + "qcom,mdss-dsi-te-dcs-command", &tmp); + pinfo->mipi.insert_dcs_cmd = + (!rc ? tmp : 1); + rc = of_property_read_u32(np, + "qcom,mdss-dsi-wr-mem-continue", &tmp); + pinfo->mipi.wr_mem_continue = + (!rc ? tmp : 0x3c); + rc = of_property_read_u32(np, + "qcom,mdss-dsi-wr-mem-start", &tmp); + pinfo->mipi.wr_mem_start = + (!rc ? tmp : 0x2c); + rc = of_property_read_u32(np, + "qcom,mdss-dsi-te-pin-select", &tmp); + pinfo->mipi.te_sel = + (!rc ? tmp : 1); + rc = of_property_read_u32(np, "qcom,mdss-dsi-virtual-channel-id", &tmp); + pinfo->mipi.vc = (!rc ? tmp : 0); + pinfo->mipi.rgb_swap = DSI_RGB_SWAP_RGB; + data = of_get_property(np, "qcom,mdss-dsi-color-order", NULL); + if (data) { + if (!strcmp(data, "rgb_swap_rbg")) + pinfo->mipi.rgb_swap = DSI_RGB_SWAP_RBG; + else if (!strcmp(data, "rgb_swap_bgr")) + pinfo->mipi.rgb_swap = DSI_RGB_SWAP_BGR; + else if (!strcmp(data, "rgb_swap_brg")) + pinfo->mipi.rgb_swap = DSI_RGB_SWAP_BRG; + else if (!strcmp(data, "rgb_swap_grb")) + pinfo->mipi.rgb_swap = DSI_RGB_SWAP_GRB; + else if (!strcmp(data, "rgb_swap_gbr")) + pinfo->mipi.rgb_swap = DSI_RGB_SWAP_GBR; + } + pinfo->mipi.data_lane0 = of_property_read_bool(np, + "qcom,mdss-dsi-lane-0-state"); + pinfo->mipi.data_lane1 = of_property_read_bool(np, + "qcom,mdss-dsi-lane-1-state"); + pinfo->mipi.data_lane2 = of_property_read_bool(np, + "qcom,mdss-dsi-lane-2-state"); + pinfo->mipi.data_lane3 = of_property_read_bool(np, + "qcom,mdss-dsi-lane-3-state"); + + /* parse split link properties */ + rc = mdss_dsi_parse_split_link_settings(np, pinfo); + if (rc) + return rc; + + rc = mdss_panel_parse_display_timings(np, &ctrl_pdata->panel_data); + if (rc) + return rc; + + rc = mdss_dsi_parse_hdr_settings(np, pinfo); + if (rc) + return rc; + + pinfo->mipi.rx_eot_ignore = of_property_read_bool(np, + "qcom,mdss-dsi-rx-eot-ignore"); + pinfo->mipi.tx_eot_append = of_property_read_bool(np, + "qcom,mdss-dsi-tx-eot-append"); + + rc = of_property_read_u32(np, "qcom,mdss-dsi-stream", &tmp); + pinfo->mipi.stream = (!rc ? tmp : 0); + + data = of_get_property(np, "qcom,mdss-dsi-mode-sel-gpio-state", NULL); + if (data) { + if (!strcmp(data, "single_port")) + pinfo->mode_sel_state = MODE_SEL_SINGLE_PORT; + else if (!strcmp(data, "dual_port")) + pinfo->mode_sel_state = MODE_SEL_DUAL_PORT; + else if (!strcmp(data, "high")) + pinfo->mode_sel_state = MODE_GPIO_HIGH; + else if (!strcmp(data, "low")) + pinfo->mode_sel_state = MODE_GPIO_LOW; + } else { + /* Set default mode as SPLIT mode */ + pinfo->mode_sel_state = MODE_SEL_DUAL_PORT; + } + + rc = of_property_read_u32(np, "qcom,mdss-mdp-transfer-time-us", &tmp); + pinfo->mdp_transfer_time_us = (!rc ? tmp : DEFAULT_MDP_TRANSFER_TIME); + + mdss_dsi_parse_mdp_kickoff_threshold(np, pinfo); + + pinfo->mipi.lp11_init = of_property_read_bool(np, + "qcom,mdss-dsi-lp11-init"); + rc = of_property_read_u32(np, "qcom,mdss-dsi-init-delay-us", &tmp); + pinfo->mipi.init_delay = (!rc ? tmp : 0); + + rc = of_property_read_u32(np, "qcom,mdss-dsi-post-init-delay", &tmp); + pinfo->mipi.post_init_delay = (!rc ? tmp : 0); + + mdss_dsi_parse_trigger(np, &(pinfo->mipi.mdp_trigger), + "qcom,mdss-dsi-mdp-trigger"); + + mdss_dsi_parse_trigger(np, &(pinfo->mipi.dma_trigger), + "qcom,mdss-dsi-dma-trigger"); + + mdss_dsi_parse_reset_seq(np, pinfo->rst_seq, &(pinfo->rst_seq_len), + "qcom,mdss-dsi-reset-sequence"); + + mdss_dsi_parse_dcs_cmds(np, &ctrl_pdata->off_cmds, + "qcom,mdss-dsi-off-command", "qcom,mdss-dsi-off-command-state"); + + rc = of_property_read_u32(np, "qcom,adjust-timer-wakeup-ms", &tmp); + pinfo->adjust_timer_delay_ms = (!rc ? tmp : 0); + + pinfo->mipi.force_clk_lane_hs = of_property_read_bool(np, + "qcom,mdss-dsi-force-clock-lane-hs"); + + rc = mdss_dsi_parse_panel_features(np, ctrl_pdata); + if (rc) { + pr_err("%s: failed to parse panel features\n", __func__); + goto error; + } + + mdss_dsi_parse_panel_horizintal_line_idle(np, ctrl_pdata); + + mdss_dsi_parse_dfps_config(np, ctrl_pdata); + + mdss_dsi_set_refresh_rate_range(np, pinfo); + + pinfo->is_dba_panel = of_property_read_bool(np, + "qcom,dba-panel"); #if defined(CONFIG_IRIS2P_FULL_SUPPORT) - iris_init_params_parse(np, ctrl_pdata); - iris_init_cmd_setup(ctrl_pdata); + iris_init_params_parse(np, ctrl_pdata); + iris_init_cmd_setup(ctrl_pdata); #endif mdss_dsi_parse_dcs_cmds(np, &ctrl_pdata->srgb_on_cmds, @@ -3476,108 +3486,106 @@ int mdss_panel_get_dst_fmt(u32 bpp, char mipi_mode, u32 pixel_packing, "qcom,mdss-dsi-panel-dci-p3-off-command", "qcom,mdss-dsi-dci-p3-command-state"); - ctrl_pdata->high_brightness_panel= of_property_read_bool(np, "qcom,mdss-dsi-high-brightness-panel"); pr_err("high brightness panel: %d\n", ctrl_pdata->high_brightness_panel); ctrl_pdata->bl_high2bit = of_property_read_bool(np, "qcom,mdss-bl-high2bit"); - if (pinfo->is_dba_panel) { - bridge_chip_name = of_get_property(np, - "qcom,bridge-name", &len); - if (!bridge_chip_name || len <= 0) { - pr_err("%s:%d Unable to read qcom,bridge_name, data=%pK,len=%d\n", - __func__, __LINE__, bridge_chip_name, len); - rc = -EINVAL; - goto error; - } - strlcpy(ctrl_pdata->bridge_name, bridge_chip_name, - MSM_DBA_CHIP_NAME_MAX_LEN); - } - - rc = of_property_read_u32(np, - "qcom,mdss-dsi-host-esc-clk-freq-hz", - &pinfo->esc_clk_rate_hz); - if (rc) - pinfo->esc_clk_rate_hz = MDSS_DSI_MAX_ESC_CLK_RATE_HZ; - pr_debug("%s: esc clk %d\n", __func__, pinfo->esc_clk_rate_hz); - - return 0; - - error: - return -EINVAL; - } - - int mdss_dsi_panel_init(struct device_node *node, - struct mdss_dsi_ctrl_pdata *ctrl_pdata, - int ndx) - { - int rc = 0; - static const char *panel_name; - struct mdss_panel_info *pinfo; + if (pinfo->is_dba_panel) { + bridge_chip_name = of_get_property(np, + "qcom,bridge-name", &len); + if (!bridge_chip_name || len <= 0) { + pr_err("%s:%d Unable to read qcom,bridge_name, data=%pK,len=%d\n", + __func__, __LINE__, bridge_chip_name, len); + rc = -EINVAL; + goto error; + } + strlcpy(ctrl_pdata->bridge_name, bridge_chip_name, + MSM_DBA_CHIP_NAME_MAX_LEN); + } + + rc = of_property_read_u32(np, + "qcom,mdss-dsi-host-esc-clk-freq-hz", + &pinfo->esc_clk_rate_hz); + if (rc) + pinfo->esc_clk_rate_hz = MDSS_DSI_MAX_ESC_CLK_RATE_HZ; + pr_debug("%s: esc clk %d\n", __func__, pinfo->esc_clk_rate_hz); + + return 0; + +error: + return -EINVAL; +} + +int mdss_dsi_panel_init(struct device_node *node, + struct mdss_dsi_ctrl_pdata *ctrl_pdata, + int ndx) +{ + int rc = 0; + static const char *panel_name; + struct mdss_panel_info *pinfo; static const char *panel_manufacture; static const char *panel_version; static const char *backlight_manufacture; static const char *backlight_version; - - if (!node || !ctrl_pdata) { - pr_err("%s: Invalid arguments\n", __func__); - return -ENODEV; - } - - pinfo = &ctrl_pdata->panel_data.panel_info; - - pr_debug("%s:%d\n", __func__, __LINE__); - pinfo->panel_name[0] = '\0'; - panel_name = of_get_property(node, "qcom,mdss-dsi-panel-name", NULL); - if (!panel_name) { - pr_info("%s:%d, Panel name not specified\n", - __func__, __LINE__); - } else { - pr_info("%s: Panel Name = %s\n", __func__, panel_name); - strlcpy(&pinfo->panel_name[0], panel_name, MDSS_MAX_PANEL_LEN); - } - panel_manufacture = of_get_property(node, - "qcom,mdss-dsi-panel-manufacture", NULL); - if (!panel_manufacture) - pr_info("%s:%d, panel manufacture not specified\n", __func__, __LINE__); - panel_version = of_get_property(node, "qcom,mdss-dsi-panel-version", NULL); - if (!panel_version) - pr_info("%s:%d, panel version not specified\n", __func__, __LINE__); - push_component_info(LCD, (char *)panel_version, - (char *)panel_manufacture); - backlight_manufacture = of_get_property(node, - "qcom,mdss-dsi-backlight-manufacture", NULL); - if (!backlight_manufacture) - pr_info("%s:%d, backlight manufacture not specified\n", __func__, __LINE__); - backlight_version = of_get_property(node, - "qcom,mdss-dsi-backlight-version", NULL); - if (!backlight_version) - pr_info("%s:%d, backlight version not specified\n", __func__, __LINE__); - push_component_info(BACKLIGHT, (char *)backlight_version, - (char *)backlight_manufacture); - mutex_init(&ctrl_pdata->panel_mode_lock); - - rc = mdss_panel_parse_dt(node, ctrl_pdata); - if (rc) { - pr_err("%s:%d panel dt parse failed\n", __func__, __LINE__); - return rc; - } - - pinfo->dynamic_switch_pending = false; - pinfo->is_lpm_mode = false; - pinfo->esd_rdy = false; - pinfo->persist_mode = false; - - ctrl_pdata->on = mdss_dsi_panel_on; - ctrl_pdata->post_panel_on = mdss_dsi_post_panel_on; - ctrl_pdata->off = mdss_dsi_panel_off; - ctrl_pdata->low_power_config = mdss_dsi_panel_low_power_config; - ctrl_pdata->panel_data.set_backlight = mdss_dsi_panel_bl_ctrl; - ctrl_pdata->panel_data.apply_display_setting = - mdss_dsi_panel_apply_display_setting; - ctrl_pdata->switch_mode = mdss_dsi_panel_switch_mode; - - return 0; - } + if (!node || !ctrl_pdata) { + pr_err("%s: Invalid arguments\n", __func__); + return -ENODEV; + } + + pinfo = &ctrl_pdata->panel_data.panel_info; + + pr_debug("%s:%d\n", __func__, __LINE__); + pinfo->panel_name[0] = '\0'; + panel_name = of_get_property(node, "qcom,mdss-dsi-panel-name", NULL); + if (!panel_name) { + pr_info("%s:%d, Panel name not specified\n", + __func__, __LINE__); + } else { + pr_info("%s: Panel Name = %s\n", __func__, panel_name); + strlcpy(&pinfo->panel_name[0], panel_name, MDSS_MAX_PANEL_LEN); + } + panel_manufacture = of_get_property(node, + "qcom,mdss-dsi-panel-manufacture", NULL); + if (!panel_manufacture) + pr_info("%s:%d, panel manufacture not specified\n", __func__, __LINE__); + panel_version = of_get_property(node, "qcom,mdss-dsi-panel-version", NULL); + if (!panel_version) + pr_info("%s:%d, panel version not specified\n", __func__, __LINE__); + push_component_info(LCD, (char *)panel_version, + (char *)panel_manufacture); + backlight_manufacture = of_get_property(node, + "qcom,mdss-dsi-backlight-manufacture", NULL); + if (!backlight_manufacture) + pr_info("%s:%d, backlight manufacture not specified\n", __func__, __LINE__); + backlight_version = of_get_property(node, + "qcom,mdss-dsi-backlight-version", NULL); + if (!backlight_version) + pr_info("%s:%d, backlight version not specified\n", __func__, __LINE__); + push_component_info(BACKLIGHT, (char *)backlight_version, + (char *)backlight_manufacture); + mutex_init(&ctrl_pdata->panel_mode_lock); + + rc = mdss_panel_parse_dt(node, ctrl_pdata); + if (rc) { + pr_err("%s:%d panel dt parse failed\n", __func__, __LINE__); + return rc; + } + + pinfo->dynamic_switch_pending = false; + pinfo->is_lpm_mode = false; + pinfo->esd_rdy = false; + pinfo->persist_mode = false; + + ctrl_pdata->on = mdss_dsi_panel_on; + ctrl_pdata->post_panel_on = mdss_dsi_post_panel_on; + ctrl_pdata->off = mdss_dsi_panel_off; + ctrl_pdata->low_power_config = mdss_dsi_panel_low_power_config; + ctrl_pdata->panel_data.set_backlight = mdss_dsi_panel_bl_ctrl; + ctrl_pdata->panel_data.apply_display_setting = + mdss_dsi_panel_apply_display_setting; + ctrl_pdata->switch_mode = mdss_dsi_panel_switch_mode; + + return 0; +} diff --git a/drivers/video/fbdev/msm/mdss_dsi_phy.c b/drivers/video/fbdev/msm/mdss_dsi_phy.c index 2d2498643c3f..e8e903e6ce88 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_phy.c +++ b/drivers/video/fbdev/msm/mdss_dsi_phy.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2016, 2018 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 @@ -1034,15 +1034,10 @@ static void mdss_dsi_phy_update_timing_param_v3(struct mdss_panel_info *pinfo, } int mdss_dsi_phy_calc_timing_param(struct mdss_panel_info *pinfo, u32 phy_rev, - u32 frate_hz) + u64 clk_rate) { struct dsi_phy_t_clk_param t_clk; struct dsi_phy_timing t_param; - int hsync_period; - int vsync_period; - unsigned long inter_num; - uint32_t lane_config = 0; - unsigned long x, y; int rc = 0; if (!pinfo) { @@ -1050,30 +1045,12 @@ int mdss_dsi_phy_calc_timing_param(struct mdss_panel_info *pinfo, u32 phy_rev, return -EINVAL; } - hsync_period = mdss_panel_get_htotal(pinfo, true); - vsync_period = mdss_panel_get_vtotal(pinfo); - - inter_num = pinfo->bpp * frate_hz; - - if (pinfo->mipi.data_lane0) - lane_config++; - if (pinfo->mipi.data_lane1) - lane_config++; - if (pinfo->mipi.data_lane2) - lane_config++; - if (pinfo->mipi.data_lane3) - lane_config++; - - x = mult_frac(vsync_period * hsync_period, inter_num, lane_config); - y = rounddown(x, 1); - t_clk.bitclk_mbps = rounddown(mult_frac(y, 1, 1000000), 1); + t_clk.bitclk_mbps = rounddown((uint32_t) div_u64(clk_rate, 1000000), 1); t_clk.escclk_numer = ESC_CLK_MHZ; t_clk.escclk_denom = ESCCLK_MMSS_CC_PREDIV; t_clk.tlpx_numer_ns = TLPX_NUMER; t_clk.treot_ns = TR_EOT; - pr_debug("hperiod=%d, vperiod=%d, inter_num=%lu, lane_cfg=%d\n", - hsync_period, vsync_period, inter_num, lane_config); - pr_debug("x=%lu, y=%lu, bitrate=%d\n", x, y, t_clk.bitclk_mbps); + pr_debug("bitrate=%d\n", t_clk.bitclk_mbps); rc = mdss_dsi_phy_initialize_defaults(&t_clk, &t_param, phy_rev); if (rc) { diff --git a/drivers/video/fbdev/msm/mdss_dsi_phy.h b/drivers/video/fbdev/msm/mdss_dsi_phy.h index 03df17d81f69..b0f7d68556d5 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_phy.h +++ b/drivers/video/fbdev/msm/mdss_dsi_phy.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2018, 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 @@ -42,7 +42,7 @@ enum phy_mode { * @frate_hz - Frame rate for which phy timing parameters are to be calculated. */ int mdss_dsi_phy_calc_timing_param(struct mdss_panel_info *pinfo, u32 phy_rev, - u32 frate_hz); + u64 clk_rate); /* * mdss_dsi_phy_v3_init() - initialization sequence for DSI PHY rev v3 diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c index 672547acbd91..28279ccdabaf 100644 --- a/drivers/video/fbdev/msm/mdss_fb.c +++ b/drivers/video/fbdev/msm/mdss_fb.c @@ -1,7 +1,7 @@ /* * Core MDSS framebuffer driver. * - * Copyright (c) 2008-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2008-2018, The Linux Foundation. All rights reserved. * Copyright (C) 2007 Google Incorporated * * This software is licensed under the terms of the GNU General Public @@ -610,7 +610,7 @@ static ssize_t mdss_fb_get_panel_info(struct device *dev, "red_chromaticity_x=%d\nred_chromaticity_y=%d\n" "green_chromaticity_x=%d\ngreen_chromaticity_y=%d\n" "blue_chromaticity_x=%d\nblue_chromaticity_y=%d\n" - "panel_orientation=%d\n", + "panel_orientation=%d\ndyn_bitclk_en=%d\n", pinfo->partial_update_enabled, pinfo->roi_alignment.xstart_pix_align, pinfo->roi_alignment.width_pix_align, @@ -636,7 +636,7 @@ static ssize_t mdss_fb_get_panel_info(struct device *dev, pinfo->hdr_properties.display_primaries[5], pinfo->hdr_properties.display_primaries[6], pinfo->hdr_properties.display_primaries[7], - pinfo->panel_orientation); + pinfo->panel_orientation, pinfo->dynamic_bitclk); return ret; } diff --git a/drivers/video/fbdev/msm/mdss_panel.h b/drivers/video/fbdev/msm/mdss_panel.h index 3340496100d5..439fea65e822 100644 --- a/drivers/video/fbdev/msm/mdss_panel.h +++ b/drivers/video/fbdev/msm/mdss_panel.h @@ -818,6 +818,9 @@ struct mdss_panel_info { int pwm_lpg_chan; int pwm_period; bool dynamic_fps; + bool dynamic_bitclk; + u32 *supp_bitclks; + u32 supp_bitclk_len; bool ulps_feature_enabled; bool ulps_suspend_enabled; bool panel_ack_disabled; diff --git a/drivers/video/fbdev/msm/msm_mdss_io_8974.c b/drivers/video/fbdev/msm/msm_mdss_io_8974.c index bb3b4b3fa929..922c4440ba82 100644 --- a/drivers/video/fbdev/msm/msm_mdss_io_8974.c +++ b/drivers/video/fbdev/msm/msm_mdss_io_8974.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, 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 @@ -1489,13 +1489,19 @@ int mdss_dsi_clk_refresh(struct mdss_panel_data *pdata, bool update_phy) __func__, pinfo->mipi.frame_rate); } - rc = mdss_dsi_clk_div_config(&pdata->panel_info, - pdata->panel_info.mipi.frame_rate); - if (rc) { - pr_err("%s: unable to initialize the clk dividers\n", - __func__); - return rc; + pinfo->clk_rate = mdss_dsi_calc_bitclk(pinfo, pinfo->mipi.frame_rate); + if (!pinfo->clk_rate) { + pr_err("%s: unable to calculate the DSI bit clock\n", __func__); + return -EINVAL; } + + pinfo->mipi.dsi_pclk_rate = mdss_dsi_get_pclk_rate(pinfo, + pinfo->clk_rate); + if (!pinfo->mipi.dsi_pclk_rate) { + pr_err("%s: unable to calculate the DSI pclk\n", __func__); + return -EINVAL; + } + ctrl_pdata->refresh_clk_rate = false; ctrl_pdata->pclk_rate = pdata->panel_info.mipi.dsi_pclk_rate; ctrl_pdata->byte_clk_rate = pdata->panel_info.clk_rate / 8; @@ -1524,7 +1530,7 @@ int mdss_dsi_clk_refresh(struct mdss_panel_data *pdata, bool update_phy) /* phy panel timing calaculation */ rc = mdss_dsi_phy_calc_timing_param(pinfo, ctrl_pdata->shared_data->phy_rev, - pinfo->mipi.frame_rate); + pdata->panel_info.clk_rate); if (rc) { pr_err("Error in calculating phy timings\n"); return rc; @@ -1811,16 +1817,9 @@ bool is_diff_frame_rate(struct mdss_panel_info *panel_info, return (frame_rate != panel_info->mipi.frame_rate); } -int mdss_dsi_clk_div_config(struct mdss_panel_info *panel_info, - int frame_rate) +static u8 mdss_dsi_get_lane_cnt(struct mdss_panel_info *panel_info) { - struct mdss_panel_data *pdata = container_of(panel_info, - struct mdss_panel_data, panel_info); - struct mdss_dsi_ctrl_pdata *ctrl_pdata = container_of(pdata, - struct mdss_dsi_ctrl_pdata, panel_data); - u64 h_period, v_period, clk_rate; - u32 dsi_pclk_rate; - u8 lanes = 0, bpp; + u8 lanes = 0; if (!panel_info) return -EINVAL; @@ -1834,7 +1833,17 @@ int mdss_dsi_clk_div_config(struct mdss_panel_info *panel_info, if (panel_info->mipi.data_lane0) lanes += 1; - switch (panel_info->mipi.dst_format) { + if (!lanes) + lanes = 1; + + return lanes; +} + +static u8 mdss_dsi_get_bpp(char dst_format) +{ + u8 bpp = 0; + + switch (dst_format) { case DSI_CMD_DST_FORMAT_RGB888: case DSI_VIDEO_DST_FORMAT_RGB888: case DSI_VIDEO_DST_FORMAT_RGB666_LOOSE: @@ -1848,6 +1857,21 @@ int mdss_dsi_clk_div_config(struct mdss_panel_info *panel_info, bpp = 3; /* Default format set to RGB888 */ break; } + return bpp; +} + +u64 mdss_dsi_calc_bitclk(struct mdss_panel_info *panel_info, int frame_rate) +{ + struct mdss_panel_data *pdata = container_of(panel_info, + struct mdss_panel_data, panel_info); + struct mdss_dsi_ctrl_pdata *ctrl_pdata = container_of(pdata, + struct mdss_dsi_ctrl_pdata, panel_data); + u64 h_period, v_period, clk_rate = 0; + u8 lanes = 0, bpp; + + lanes = mdss_dsi_get_lane_cnt(panel_info); + + bpp = mdss_dsi_get_bpp(panel_info->mipi.dst_format); h_period = mdss_panel_get_htotal(panel_info, true); if (panel_info->split_link_enabled) @@ -1855,35 +1879,40 @@ int mdss_dsi_clk_div_config(struct mdss_panel_info *panel_info, v_period = mdss_panel_get_vtotal(panel_info); if (ctrl_pdata->refresh_clk_rate || is_diff_frame_rate(panel_info, - frame_rate) || (!panel_info->clk_rate)) { - if (lanes > 0) { - panel_info->clk_rate = h_period * v_period * frame_rate - * bpp * 8; - do_div(panel_info->clk_rate, lanes); - } else { - pr_err("%s: forcing mdss_dsi lanes to 1\n", __func__); - panel_info->clk_rate = - h_period * v_period * frame_rate * bpp * 8; - } + frame_rate) || (!panel_info->clk_rate)) { + clk_rate = h_period * v_period * frame_rate * bpp * 8; + do_div(clk_rate, lanes); + } else if (panel_info->clk_rate) { + clk_rate = panel_info->clk_rate; } - if (panel_info->clk_rate == 0) - panel_info->clk_rate = 454000000; + if (clk_rate == 0) + clk_rate = 454000000; + + return clk_rate; +} + +u32 mdss_dsi_get_pclk_rate(struct mdss_panel_info *panel_info, u64 clk_rate) +{ + u8 lanes = 0, bpp; + u32 pclk_rate = 0; + + lanes = mdss_dsi_get_lane_cnt(panel_info); + + bpp = mdss_dsi_get_bpp(panel_info->mipi.dst_format); - clk_rate = panel_info->clk_rate; do_div(clk_rate, 8 * bpp); if (panel_info->split_link_enabled) - dsi_pclk_rate = (u32) clk_rate * + pclk_rate = (u32) clk_rate * panel_info->mipi.lanes_per_sublink; else - dsi_pclk_rate = (u32) clk_rate * lanes; + pclk_rate = (u32) clk_rate * lanes; - if ((dsi_pclk_rate < 3300000) || (dsi_pclk_rate > 250000000)) - dsi_pclk_rate = 35000000; - panel_info->mipi.dsi_pclk_rate = dsi_pclk_rate; + if ((pclk_rate < 3300000) || (pclk_rate > 250000000)) + pclk_rate = 35000000; - return 0; + return pclk_rate; } static bool mdss_dsi_is_ulps_req_valid(struct mdss_dsi_ctrl_pdata *ctrl,