summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeevan Shriram <jshriram@codeaurora.org>2015-09-10 13:08:54 -0700
committerDavid Keitel <dkeitel@codeaurora.org>2016-03-23 20:47:53 -0700
commitd83084cfb78d7e0bf83844b442fbed182121bcf0 (patch)
tree542b7d23f339bee74475bb7caee63eb3487e87f3
parent92409ccc31cbb8b0ebc073008eb93067849fa467 (diff)
msm: mdss: add support for dynamic dsi phy timing calculation
The programming of the DSI Phy timing registers are dependent on link frequency. In the current implementation, these values are pre-computed statically based on the link rate required for a particular panel. This approach does not scale very well, especially for use cases when dynamically changing the refresh rate or resolution for a panel. To address this, add support to dynamically compute the value for dsi phy timing registers based on a particular link rate. Change-Id: If1ff545318a540baee66f5357c519d7f428510c1 Signed-off-by: Adrian Salido-Moreno <adrianm@codeaurora.org> Signed-off-by: Sandeep Panda <spanda@codeaurora.org> Signed-off-by: Jeevan Shriram <jshriram@codeaurora.org> Signed-off-by: Ingrid Gallardo <ingridg@codeaurora.org> [cip@codeaurora.org: Moved new file location] Signed-off-by: Clarence Ip <cip@codeaurora.org>
-rw-r--r--drivers/video/fbdev/msm/Makefile1
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi_phy.c777
-rw-r--r--drivers/video/fbdev/msm/mdss_dsi_phy.h39
3 files changed, 817 insertions, 0 deletions
diff --git a/drivers/video/fbdev/msm/Makefile b/drivers/video/fbdev/msm/Makefile
index ac83251cbfe6..2c805005db16 100644
--- a/drivers/video/fbdev/msm/Makefile
+++ b/drivers/video/fbdev/msm/Makefile
@@ -37,6 +37,7 @@ endif
mdss-dsi-objs := mdss_dsi.o mdss_dsi_host.o mdss_dsi_cmd.o mdss_dsi_status.o
mdss-dsi-objs += mdss_dsi_panel.o
mdss-dsi-objs += msm_mdss_io_8974.o
+mdss-dsi-objs += mdss_dsi_phy.o
mdss-dsi-objs += mdss_dsi_clk.o
obj-$(CONFIG_FB_MSM_MDSS) += mdss-dsi.o
obj-$(CONFIG_FB_MSM_MDSS) += mdss_panel.o
diff --git a/drivers/video/fbdev/msm/mdss_dsi_phy.c b/drivers/video/fbdev/msm/mdss_dsi_phy.c
new file mode 100644
index 000000000000..ac88481e02f5
--- /dev/null
+++ b/drivers/video/fbdev/msm/mdss_dsi_phy.c
@@ -0,0 +1,777 @@
+/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "mdss_dsi_phy.h"
+
+#define ESC_CLK_MHZ 192
+#define ESCCLK_MMSS_CC_PREDIV 10
+
+#define TLPX_NUMER 1000
+#define TR_EOT 20
+#define TA_GO 3
+#define TA_SURE 0
+#define TA_GET 4
+
+#define CLK_PREPARE_SPEC_MIN 38
+#define CLK_PREPARE_SPEC_MAX 95
+#define CLK_TRAIL_SPEC_MIN 60
+#define HS_EXIT_SPEC_MIN 100
+#define HS_EXIT_RECO_MAX 255
+#define HS_RQST_SPEC_MIN 50
+#define CLK_ZERO_RECO_MAX1 511
+#define CLK_ZERO_RECO_MAX2 255
+
+struct timing_entry {
+ int32_t mipi_min;
+ int32_t mipi_max;
+ int32_t rec_min;
+ int32_t rec_max;
+ int32_t rec;
+ char program_value;
+};
+
+struct dsi_phy_timing {
+ struct timing_entry clk_prepare;
+ struct timing_entry clk_zero;
+ struct timing_entry clk_trail;
+ struct timing_entry hs_prepare;
+ struct timing_entry hs_zero;
+ struct timing_entry hs_trail;
+ struct timing_entry hs_rqst;
+ struct timing_entry hs_rqst_clk;
+ struct timing_entry hs_exit;
+ struct timing_entry ta_go;
+ struct timing_entry ta_sure;
+ struct timing_entry ta_get;
+ struct timing_entry clk_post;
+ struct timing_entry clk_pre;
+};
+
+struct dsi_phy_t_clk_param {
+ uint32_t bitclk_mbps;
+ uint32_t escclk_numer;
+ uint32_t escclk_denom;
+ uint32_t tlpx_numer_ns;
+ uint32_t treot_ns;
+};
+
+static int mdss_dsi_phy_common_validate_and_set(struct timing_entry *te,
+ char const *te_name)
+{
+ if (te->rec & 0xffffff00) {
+ /* Output value can only be 8 bits */
+ pr_err("Incorrect %s calculations - %d\n", te_name, te->rec);
+ return -EINVAL;
+ }
+ pr_debug("%s program value=%d\n", te_name, te->rec);
+ te->program_value = te->rec;
+ return 0;
+}
+
+static int mdss_dsi_phy_validate_and_set(struct timing_entry *te,
+ char const *te_name)
+{
+ if (te->rec < 0)
+ te->program_value = 0;
+ else
+ return mdss_dsi_phy_common_validate_and_set(te, te_name);
+
+ return 0;
+}
+
+static int mdss_dsi_phy_initialize_defaults(struct dsi_phy_t_clk_param *t_clk,
+ struct dsi_phy_timing *t_param, u32 phy_rev)
+{
+
+ if (phy_rev <= DSI_PHY_REV_UNKNOWN || phy_rev >= DSI_PHY_REV_MAX) {
+ pr_err("Invalid PHY %d revision\n", phy_rev);
+ return -EINVAL;
+ }
+
+ t_param->clk_prepare.mipi_min = CLK_PREPARE_SPEC_MIN;
+ t_param->clk_prepare.mipi_max = CLK_PREPARE_SPEC_MAX;
+ t_param->clk_trail.mipi_min = CLK_TRAIL_SPEC_MIN;
+ t_param->hs_exit.mipi_min = HS_EXIT_SPEC_MIN;
+ t_param->hs_exit.rec_max = HS_EXIT_RECO_MAX;
+
+ if (phy_rev == DSI_PHY_REV_20) {
+ t_param->clk_prepare.rec_min =
+ DIV_ROUND_UP((t_param->clk_prepare.mipi_min
+ * t_clk->bitclk_mbps),
+ (8 * t_clk->tlpx_numer_ns));
+ t_param->clk_prepare.rec_max =
+ rounddown(mult_frac(t_param->clk_prepare.mipi_max
+ * t_clk->bitclk_mbps, 1,
+ (8 * t_clk->tlpx_numer_ns)), 1);
+ t_param->hs_rqst.mipi_min = HS_RQST_SPEC_MIN;
+ t_param->hs_rqst_clk.mipi_min = HS_RQST_SPEC_MIN;
+ } else if (phy_rev == DSI_PHY_REV_10) {
+ t_param->clk_prepare.rec_min =
+ (DIV_ROUND_UP(t_param->clk_prepare.mipi_min *
+ t_clk->bitclk_mbps,
+ t_clk->tlpx_numer_ns)) - 2;
+ t_param->clk_prepare.rec_max =
+ (DIV_ROUND_UP(t_param->clk_prepare.mipi_max *
+ t_clk->bitclk_mbps,
+ t_clk->tlpx_numer_ns)) - 2;
+ }
+
+ pr_debug("clk_prepare: min=%d, max=%d\n", t_param->clk_prepare.rec_min,
+ t_param->clk_prepare.rec_max);
+
+ return 0;
+}
+
+static int mdss_dsi_phy_calc_param_phy_rev_2(struct dsi_phy_t_clk_param *t_clk,
+ struct dsi_phy_timing *t_param)
+{
+ /* recommended fraction for PHY REV 2.0 */
+ u32 const min_prepare_frac = 50;
+ u32 const hs_exit_min_frac = 10;
+ u32 const phy_timing_frac = 30;
+ u32 const hs_zero_min_frac = 10;
+ u32 const clk_zero_min_frac = 2;
+ int tmp;
+ int t_hs_prep_actual;
+ int teot_clk_lane, teot_data_lane;
+ u64 dividend;
+ u64 temp, rc = 0;
+ u64 multiplier = BIT(20);
+ u64 temp_multiple;
+ s64 mipi_min, mipi_max, mipi_max_tr, rec_min, rec_prog;
+ s64 clk_prep_actual;
+ s64 actual_intermediate;
+ s32 actual_frac;
+ s64 rec_temp1, rec_temp2, rec_temp3;
+
+ /* clk_prepare calculations */
+ dividend = ((t_param->clk_prepare.rec_max
+ - t_param->clk_prepare.rec_min)
+ * min_prepare_frac * multiplier);
+ temp = roundup(div_s64(dividend, 100), multiplier);
+ temp += (t_param->clk_prepare.rec_min * multiplier);
+ t_param->clk_prepare.rec = div_s64(temp, multiplier);
+
+ rc = mdss_dsi_phy_common_validate_and_set(&t_param->clk_prepare,
+ "clk prepare");
+ if (rc)
+ goto error;
+
+ /* clk_ prepare theoretical value*/
+ temp_multiple = (8 * t_param->clk_prepare.program_value
+ * t_clk->tlpx_numer_ns * multiplier);
+ actual_intermediate = div_s64(temp_multiple, t_clk->bitclk_mbps);
+ div_s64_rem(temp_multiple, t_clk->bitclk_mbps, &actual_frac);
+ clk_prep_actual =
+ div_s64((actual_intermediate + actual_frac), multiplier);
+
+ pr_debug("CLK PREPARE: mipi_min=%d, max=%d, rec_min=%d, rec_max=%d",
+ t_param->clk_prepare.mipi_min,
+ t_param->clk_prepare.mipi_max,
+ t_param->clk_prepare.rec_min,
+ t_param->clk_prepare.rec_max);
+ pr_debug("prog value = %d, actual=%lld\n",
+ t_param->clk_prepare.rec, clk_prep_actual);
+
+ /* clk zero calculations */
+ /* Mipi spec min*/
+ mipi_min = (300 * multiplier) - (actual_intermediate + actual_frac);
+ t_param->clk_zero.mipi_min = div_s64(mipi_min, multiplier);
+
+ /* recommended min */
+ rec_temp1 = div_s64(mipi_min * t_clk->bitclk_mbps,
+ t_clk->tlpx_numer_ns);
+ rec_temp2 = rec_temp1 - (11 * multiplier);
+ rec_temp3 = roundup(div_s64(rec_temp2, 8), multiplier);
+ rec_min = div_s64(rec_temp3, multiplier) - 3;
+ t_param->clk_zero.rec_min = rec_min;
+
+ /* recommended max */
+ t_param->clk_zero.rec_max =
+ ((t_param->clk_zero.rec_min > 255) ? 511 : 255);
+
+ /* Programmed value */
+ t_param->clk_zero.rec = DIV_ROUND_UP(
+ (t_param->clk_zero.rec_max - t_param->clk_zero.rec_min)
+ * clk_zero_min_frac
+ + (t_param->clk_zero.rec_min * 100), 100);
+
+ rc = mdss_dsi_phy_common_validate_and_set(&t_param->clk_zero,
+ "clk zero");
+ if (rc)
+ goto error;
+
+ pr_debug("CLK ZERO: mipi_min=%d, max=%d, rec_min=%d, rec_max=%d, prog value = %d\n",
+ t_param->clk_zero.mipi_min, t_param->clk_zero.mipi_max,
+ t_param->clk_zero.rec_min, t_param->clk_zero.rec_max,
+ t_param->clk_zero.rec);
+
+ /* clk trail calculations */
+ temp_multiple = div_s64(12 * multiplier * t_clk->tlpx_numer_ns,
+ t_clk->bitclk_mbps);
+ div_s64_rem(temp_multiple, multiplier, &actual_frac);
+
+ mipi_max_tr = 105 * multiplier + (temp_multiple + actual_frac);
+ teot_clk_lane = div_s64(mipi_max_tr, multiplier);
+
+ mipi_max = mipi_max_tr - (t_clk->treot_ns * multiplier);
+
+ t_param->clk_trail.mipi_max = div_s64(mipi_max, multiplier);
+
+ /* recommended min*/
+ temp_multiple = div_s64(t_param->clk_trail.mipi_min * multiplier *
+ t_clk->bitclk_mbps, t_clk->tlpx_numer_ns);
+ div_s64_rem(temp_multiple, multiplier, &actual_frac);
+ rec_temp1 = temp_multiple + actual_frac + 3 * multiplier;
+ rec_temp2 = div_s64(rec_temp1, 8);
+ rec_temp3 = roundup(rec_temp2, multiplier);
+
+ t_param->clk_trail.rec_min = div_s64(rec_temp3, multiplier);
+
+ /* recommended max */
+ rec_temp1 = div_s64(mipi_max * t_clk->bitclk_mbps,
+ t_clk->tlpx_numer_ns);
+ rec_temp2 = rec_temp1 + 3 * multiplier;
+ rec_temp3 = rec_temp2 / 8;
+ t_param->clk_trail.rec_max = div_s64(rec_temp3, multiplier);
+
+ /* Programmed value */
+ t_param->clk_trail.rec = DIV_ROUND_UP(
+ (t_param->clk_trail.rec_max - t_param->clk_trail.rec_min)
+ * phy_timing_frac
+ + (t_param->clk_trail.rec_min * 100), 100);
+
+ rc = mdss_dsi_phy_common_validate_and_set(&t_param->clk_trail,
+ "clk trail");
+ if (rc)
+ goto error;
+
+ pr_debug("CLK TRAIL: mipi_min=%d, max=%d, rec_min=%d, rec_max=%d, prog value = %d\n",
+ t_param->clk_trail.mipi_min,
+ t_param->clk_trail.mipi_max,
+ t_param->clk_trail.rec_min,
+ t_param->clk_trail.rec_max,
+ t_param->clk_trail.rec);
+
+ /* hs prepare calculations */
+ /* mipi min */
+ temp_multiple = div_s64(4 * t_clk->tlpx_numer_ns * multiplier,
+ t_clk->bitclk_mbps);
+ div_s64_rem(temp_multiple, multiplier, &actual_frac);
+ mipi_min = 40 * multiplier + (temp_multiple + actual_frac);
+ t_param->hs_prepare.mipi_min = div_s64(mipi_min, multiplier);
+
+ /* mipi max */
+ temp_multiple = div_s64(6 * t_clk->tlpx_numer_ns * multiplier,
+ t_clk->bitclk_mbps);
+ div_s64_rem(temp_multiple, multiplier, &actual_frac);
+ mipi_max = 85 * multiplier + temp_multiple;
+ t_param->hs_prepare.mipi_max = div_s64(mipi_max, multiplier);
+
+ /* recommended min */
+ temp_multiple = div_s64(mipi_min * t_clk->bitclk_mbps,
+ t_clk->tlpx_numer_ns);
+ div_s64_rem(temp_multiple, multiplier, &actual_frac);
+ rec_temp1 = roundup((temp_multiple + actual_frac)/8, multiplier);
+ t_param->hs_prepare.rec_min = div_s64(rec_temp1, multiplier);
+
+ /* recommended max*/
+ temp_multiple = div_s64(mipi_max * t_clk->bitclk_mbps,
+ t_clk->tlpx_numer_ns);
+ div_s64_rem(temp_multiple, multiplier, &actual_frac);
+ rec_temp2 = rounddown((temp_multiple + actual_frac)/8, multiplier);
+ t_param->hs_prepare.rec_max = div_s64(rec_temp2, multiplier);
+
+ /* prog value*/
+ dividend = (rec_temp2 - rec_temp1) * min_prepare_frac;
+ temp = roundup(div_u64(dividend, 100), multiplier);
+ rec_prog = temp + rec_temp1;
+ t_param->hs_prepare.rec = div_s64(rec_prog, multiplier);
+
+ rc = mdss_dsi_phy_common_validate_and_set(&t_param->hs_prepare,
+ "HS prepare");
+ if (rc)
+ goto error;
+
+ /* theoretical Value */
+ temp_multiple = div_s64(8 * rec_prog * t_clk->tlpx_numer_ns,
+ t_clk->bitclk_mbps);
+ div_s64_rem(temp_multiple, multiplier, &actual_frac);
+ t_hs_prep_actual = div_s64(temp_multiple, multiplier);
+ pr_debug("HS PREPARE: mipi_min=%d, max=%d, rec_min=%d, rec_max=%d, prog value = %d, actual=%d\n",
+ t_param->hs_prepare.mipi_min,
+ t_param->hs_prepare.mipi_max,
+ t_param->hs_prepare.rec_min,
+ t_param->hs_prepare.rec_max,
+ t_param->hs_prepare.rec, t_hs_prep_actual);
+
+ /* hs zero calculations */
+ /* mipi min*/
+ mipi_min = div_s64(10 * t_clk->tlpx_numer_ns * multiplier,
+ t_clk->bitclk_mbps);
+ rec_temp1 = (145 * multiplier) + mipi_min - temp_multiple;
+ t_param->hs_zero.mipi_min = div_s64(rec_temp1, multiplier);
+
+ /* recommended min */
+ rec_temp1 = div_s64(rec_temp1 * t_clk->bitclk_mbps,
+ t_clk->tlpx_numer_ns);
+ rec_temp2 = rec_temp1 - (11 * multiplier);
+ rec_temp3 = roundup((rec_temp2/8), multiplier);
+ rec_min = rec_temp3 - (3 * multiplier);
+ t_param->hs_zero.rec_min = div_s64(rec_min, multiplier);
+
+ t_param->hs_zero.rec_max =
+ ((t_param->hs_zero.rec_min > 255) ? 511 : 255);
+
+ /* prog value */
+ t_param->hs_zero.rec = DIV_ROUND_UP(
+ (t_param->hs_zero.rec_max - t_param->hs_zero.rec_min)
+ * hs_zero_min_frac + (t_param->hs_zero.rec_min * 100),
+ 100);
+
+ rc = mdss_dsi_phy_common_validate_and_set(&t_param->hs_zero, "HS zero");
+ if (rc)
+ goto error;
+
+ pr_debug("HS ZERO: mipi_min=%d, max=%d, rec_min=%d, rec_max=%d, prog value = %d\n",
+ t_param->hs_zero.mipi_min, t_param->hs_zero.mipi_max,
+ t_param->hs_zero.rec_min, t_param->hs_zero.rec_max,
+ t_param->hs_zero.rec);
+
+ /* hs_trail calculations */
+ teot_data_lane = teot_clk_lane;
+ t_param->hs_trail.mipi_min = 60 +
+ mult_frac(t_clk->tlpx_numer_ns, 4, t_clk->bitclk_mbps);
+ t_param->hs_trail.mipi_max = teot_clk_lane - t_clk->treot_ns;
+ t_param->hs_trail.rec_min = DIV_ROUND_UP(
+ ((t_param->hs_trail.mipi_min * t_clk->bitclk_mbps)
+ + 3 * t_clk->tlpx_numer_ns), (8 * t_clk->tlpx_numer_ns));
+ tmp = ((t_param->hs_trail.mipi_max * t_clk->bitclk_mbps)
+ + (3 * t_clk->tlpx_numer_ns));
+ t_param->hs_trail.rec_max = tmp/(8 * t_clk->tlpx_numer_ns);
+ tmp = DIV_ROUND_UP((t_param->hs_trail.rec_max
+ - t_param->hs_trail.rec_min) * phy_timing_frac,
+ 100);
+ t_param->hs_trail.rec = tmp + t_param->hs_trail.rec_min;
+
+
+ rc = mdss_dsi_phy_common_validate_and_set(&t_param->hs_trail,
+ "HS trail");
+ if (rc)
+ goto error;
+
+ pr_debug("HS TRAIL: mipi_min=%d, max=%d, rec_min=%d, rec_max=%d, prog value = %d\n",
+ t_param->hs_trail.mipi_min, t_param->hs_trail.mipi_max,
+ t_param->hs_trail.rec_min, t_param->hs_trail.rec_max,
+ t_param->hs_trail.rec);
+
+ /* hs rqst calculations for Data lane */
+ t_param->hs_rqst.rec = DIV_ROUND_UP(
+ (t_param->hs_rqst.mipi_min * t_clk->bitclk_mbps)
+ - (8 * t_clk->tlpx_numer_ns), (8 * t_clk->tlpx_numer_ns));
+
+ rc = mdss_dsi_phy_common_validate_and_set(&t_param->hs_rqst, "HS rqst");
+ if (rc)
+ goto error;
+
+ pr_debug("HS RQST-DATA: mipi_min=%d, max=%d, rec_min=%d, rec_max=%d, prog value = %d\n",
+ t_param->hs_rqst.mipi_min, t_param->hs_rqst.mipi_max,
+ t_param->hs_rqst.rec_min, t_param->hs_rqst.rec_max,
+ t_param->hs_rqst.rec);
+
+ /* hs exit calculations */
+ t_param->hs_exit.rec_min = DIV_ROUND_UP(
+ (t_param->hs_exit.mipi_min * t_clk->bitclk_mbps),
+ (8 * t_clk->tlpx_numer_ns)) - 1;
+ t_param->hs_exit.rec = DIV_ROUND_UP(
+ (t_param->hs_exit.rec_max - t_param->hs_exit.rec_min)
+ * hs_exit_min_frac
+ + (t_param->hs_exit.rec_min * 100), 100);
+
+ rc = mdss_dsi_phy_common_validate_and_set(&t_param->hs_exit, "HS exit");
+ if (rc)
+ goto error;
+
+ pr_debug("HS EXIT: mipi_min=%d, max=%d, rec_min=%d, rec_max=%d, prog value = %d\n",
+ t_param->hs_exit.mipi_min, t_param->hs_exit.mipi_max,
+ t_param->hs_exit.rec_min, t_param->hs_exit.rec_max,
+ t_param->hs_exit.rec);
+
+ /* hs rqst calculations for Clock lane */
+ t_param->hs_rqst_clk.rec = DIV_ROUND_UP(
+ (t_param->hs_rqst_clk.mipi_min * t_clk->bitclk_mbps)
+ - (8 * t_clk->tlpx_numer_ns), (8 * t_clk->tlpx_numer_ns));
+
+ rc = mdss_dsi_phy_common_validate_and_set(&t_param->hs_rqst_clk,
+ "HS rqst clk");
+ if (rc)
+ goto error;
+
+ pr_debug("HS RQST-CLK: mipi_min=%d, max=%d, rec_min=%d, rec_max=%d, prog value = %d\n",
+ t_param->hs_rqst_clk.mipi_min,
+ t_param->hs_rqst_clk.mipi_max,
+ t_param->hs_rqst_clk.rec_min,
+ t_param->hs_rqst_clk.rec_max,
+ t_param->hs_rqst_clk.rec);
+ pr_debug("teot_clk=%d, data=%d\n", teot_clk_lane, teot_data_lane);
+ return 0;
+
+error:
+ return -EINVAL;
+}
+
+static int mdss_dsi_phy_calc_hs_param_phy_rev_1(
+ struct dsi_phy_t_clk_param *t_clk,
+ struct dsi_phy_timing *t_param)
+{
+ int percent_min = 10;
+ int percent_allowable_phy = 0;
+ int percent_min_ths;
+ int tmp, rc = 0;
+ int b6, h10, h11, h17;
+
+ if (t_clk->bitclk_mbps > 1200)
+ percent_min_ths = 15;
+ else
+ percent_min_ths = 10;
+
+ if (t_clk->bitclk_mbps > 180)
+ percent_allowable_phy = 10;
+ else
+ percent_allowable_phy = 40;
+
+ t_param->hs_prepare.rec_min =
+ DIV_ROUND_UP((40 * t_clk->bitclk_mbps)
+ + (4 * t_clk->tlpx_numer_ns), t_clk->tlpx_numer_ns) - 2;
+ t_param->hs_prepare.rec_max =
+ DIV_ROUND_UP((85 * t_clk->bitclk_mbps)
+ + (6 * t_clk->tlpx_numer_ns), t_clk->tlpx_numer_ns) - 2;
+ tmp = DIV_ROUND_UP((t_param->hs_prepare.rec_max
+ - t_param->hs_prepare.rec_min) * percent_min_ths, 100);
+ tmp += t_param->hs_prepare.rec_min;
+ t_param->hs_prepare.rec = (tmp & ~0x1);
+
+ rc = mdss_dsi_phy_validate_and_set(&t_param->hs_prepare, "HS prepare");
+ if (rc)
+ goto error;
+
+ tmp = (t_param->hs_prepare.program_value / 2) + 1;
+ t_param->hs_zero.rec_min = DIV_ROUND_UP((145 * t_clk->bitclk_mbps)
+ + ((10 - (2 * (tmp + 1))) * 1000), 1000) - 2;
+ t_param->hs_zero.rec_max = 255;
+ tmp = DIV_ROUND_UP((t_param->hs_zero.rec_max
+ - t_param->hs_zero.rec_min) * percent_min, 100);
+ tmp += t_param->hs_zero.rec_min;
+ t_param->hs_zero.rec = (tmp & ~0x1);
+
+ rc = mdss_dsi_phy_validate_and_set(&t_param->hs_zero, "HS zero");
+ if (rc)
+ goto error;
+
+ t_param->hs_trail.rec_min = DIV_ROUND_UP((60 * t_clk->bitclk_mbps)
+ + 4000, 1000) - 2;
+ t_param->hs_trail.rec_max = DIV_ROUND_UP((105 - t_clk->treot_ns)
+ * t_clk->bitclk_mbps + 12000, 1000) - 2;
+ tmp = DIV_ROUND_UP((t_param->hs_trail.rec_max
+ - t_param->hs_trail.rec_min) * percent_allowable_phy, 100);
+ tmp += t_param->hs_trail.rec_min;
+ t_param->hs_trail.rec = tmp & ~0x1;
+
+ rc = mdss_dsi_phy_validate_and_set(&t_param->hs_trail, "HS trail");
+ if (rc)
+ goto error;
+
+ t_param->hs_exit.rec_min = DIV_ROUND_UP(100 * t_clk->bitclk_mbps,
+ t_clk->tlpx_numer_ns) - 2;
+ t_param->hs_exit.rec_max = 255;
+ tmp = DIV_ROUND_UP((t_param->hs_exit.rec_max
+ - t_param->hs_exit.rec_min) * percent_min, 100);
+ tmp += t_param->hs_exit.rec_min;
+ t_param->hs_exit.rec = (tmp & ~0x1);
+
+ rc = mdss_dsi_phy_validate_and_set(&t_param->hs_exit, "HS exit");
+ if (rc)
+ goto error;
+
+ /* clk post and pre value calculation */
+ h17 = (t_param->hs_exit.program_value / 2) + 1;
+ tmp = ((60 * (int)t_clk->bitclk_mbps) + (52 * 1000)
+ - (24 * 1000) - (h17 * 2 * 1000));
+ /* clk_post minimum value can be a negetive number */
+ if (tmp % (8 * 1000) != 0) {
+ if (tmp < 0)
+ tmp = (tmp / (8 * 1000)) - 1;
+ else
+ tmp = (tmp / (8 * 1000)) + 1;
+ } else {
+ tmp = tmp / (8 * 1000);
+ }
+ tmp = tmp - 1;
+
+ t_param->clk_post.program_value =
+ DIV_ROUND_UP((63 - tmp) * percent_min, 100);
+ t_param->clk_post.program_value += tmp;
+
+ if (t_param->clk_post.program_value & 0xffffff00) {
+ pr_err("Invalid clk post calculations - %d\n",
+ t_param->clk_post.program_value);
+ goto error;
+ }
+
+ t_param->clk_post.rec_min = tmp;
+
+ h10 = (t_param->clk_prepare.program_value / 2) + 1;
+ h11 = (t_param->clk_zero.program_value / 2) + 1;
+ b6 = 10000/t_clk->escclk_numer;
+
+ t_param->clk_pre.rec_min =
+ DIV_ROUND_UP((b6 * t_clk->bitclk_mbps) + (8 * 1000)
+ + (h10 * 2 * 1000) + (h11 * 2 * 1000), 8 * 1000) - 1;
+ if (t_param->clk_pre.rec_min > 63) {
+ t_param->clk_pre.program_value =
+ DIV_ROUND_UP((2 * 63 - t_param->clk_pre.rec_min)
+ * percent_min, 100);
+ t_param->clk_pre.program_value += t_param->clk_pre.rec_min;
+ } else {
+ t_param->clk_pre.program_value =
+ DIV_ROUND_UP((63 - t_param->clk_pre.rec_min)
+ * percent_min, 100);
+ t_param->clk_pre.program_value += t_param->clk_pre.rec_min;
+ }
+
+ if (t_param->clk_pre.program_value & 0xffffff00) {
+ pr_err("Invalid clk pre calculations - %d\n",
+ t_param->clk_pre.program_value);
+ goto error;
+ }
+ pr_debug("t_clk_post: %d t_clk_pre: %d\n",
+ t_param->clk_post.program_value,
+ t_param->clk_pre.program_value);
+
+ return 0;
+
+error:
+ return -EINVAL;
+
+}
+
+static int mdss_dsi_phy_calc_param_phy_rev_1(struct dsi_phy_t_clk_param *t_clk,
+ struct dsi_phy_timing *t_param)
+{
+ int percent_allowable_phy = 0;
+ int percent_min_t_clk = 10;
+ int tmp, rc = 0;
+ int clk_prep_actual;
+ int teot_clk_lane;
+ u32 temp = 0;
+
+ if (t_clk->bitclk_mbps > 180)
+ percent_allowable_phy = 10;
+ else
+ percent_allowable_phy = 40;
+
+ tmp = DIV_ROUND_UP((t_param->clk_prepare.rec_max -
+ t_param->clk_prepare.rec_min) * percent_min_t_clk, 100);
+ tmp += t_param->clk_prepare.rec_min;
+
+ t_param->clk_prepare.rec = (tmp & ~0x1);
+
+ rc = mdss_dsi_phy_common_validate_and_set(&t_param->clk_prepare,
+ "clk prepare");
+ if (rc)
+ goto error;
+
+ clk_prep_actual = 2 * ((t_param->clk_prepare.program_value
+ / 2) + 1) * t_clk->tlpx_numer_ns;
+ clk_prep_actual /= t_clk->bitclk_mbps;
+
+ tmp = t_clk->bitclk_mbps * t_clk->escclk_denom
+ / t_clk->escclk_numer;
+ t_param->hs_rqst.rec = tmp;
+ if (!(tmp & 0x1))
+ t_param->hs_rqst.rec -= 2;
+
+ rc = mdss_dsi_phy_common_validate_and_set(&t_param->hs_rqst, "HS rqst");
+ if (rc)
+ goto error;
+
+ if (t_param->hs_rqst.program_value < 0)
+ t_param->hs_rqst.program_value = 0;
+
+ /* t_clk_zero calculation */
+ t_param->clk_zero.mipi_min = (300 - clk_prep_actual);
+ t_param->clk_zero.rec_min = (DIV_ROUND_UP(t_param->clk_zero.mipi_min
+ * t_clk->bitclk_mbps, t_clk->tlpx_numer_ns)) - 2;
+
+ if (t_param->clk_zero.rec_min > 255) {
+ t_param->clk_zero.rec_max = CLK_ZERO_RECO_MAX1;
+ t_param->clk_zero.rec =
+ DIV_ROUND_UP(t_param->clk_zero.rec_min * 10
+ + (t_param->clk_zero.rec_min * 100), 100);
+ } else {
+ t_param->clk_zero.rec_max = CLK_ZERO_RECO_MAX2;
+ temp = t_param->clk_zero.rec_max - t_param->clk_zero.rec_min;
+ t_param->clk_zero.rec = DIV_ROUND_UP(temp * 10
+ + (t_param->clk_zero.rec_min * 100), 100);
+ }
+
+ t_param->clk_zero.rec &= ~0x1;
+
+ if (((t_param->hs_rqst.rec + t_param->clk_zero.rec +
+ t_param->clk_prepare.rec) % 8) != 0)
+ t_param->clk_zero.rec +=
+ (8 - ((t_param->hs_rqst.rec + t_param->clk_zero.rec +
+ t_param->clk_prepare.rec) % 8));
+
+ rc = mdss_dsi_phy_common_validate_and_set(&t_param->clk_zero,
+ "clk zero");
+ if (rc)
+ goto error;
+
+ pr_debug("hs_rqst.rec: %d clk_zero.rec: %d clk_prepare.rec: %d\n",
+ t_param->hs_rqst.rec, t_param->clk_zero.rec,
+ t_param->clk_prepare.rec);
+ teot_clk_lane = 105 + (12 * t_clk->tlpx_numer_ns
+ / t_clk->bitclk_mbps);
+ t_param->clk_trail.mipi_max = teot_clk_lane - t_clk->treot_ns;
+ t_param->clk_trail.rec_min = DIV_ROUND_UP(t_param->clk_trail.mipi_min *
+ t_clk->bitclk_mbps, t_clk->tlpx_numer_ns) - 2;
+ t_param->clk_trail.rec_max = DIV_ROUND_UP(t_param->clk_trail.mipi_max *
+ t_clk->bitclk_mbps, t_clk->tlpx_numer_ns) - 2;
+
+ tmp = DIV_ROUND_UP((t_param->clk_trail.rec_max -
+ t_param->clk_trail.rec_min) * percent_allowable_phy, 100);
+ tmp += t_param->clk_trail.rec_min;
+ t_param->clk_trail.rec = (tmp & ~0x1);
+
+ rc = mdss_dsi_phy_validate_and_set(&t_param->clk_trail, "clk trail");
+ if (rc)
+ goto error;
+
+ rc = mdss_dsi_phy_calc_hs_param_phy_rev_1(t_clk, t_param);
+ if (rc)
+ pr_err("Invalid HS param calculations\n");
+
+error:
+ return rc;
+}
+
+static void mdss_dsi_phy_update_timing_param(struct mdss_panel_info *pinfo,
+ struct dsi_phy_timing *t_param)
+{
+ struct mdss_dsi_phy_ctrl *reg;
+
+ reg = &(pinfo->mipi.dsi_phy_db);
+
+ pinfo->mipi.t_clk_post = t_param->clk_post.program_value;
+ pinfo->mipi.t_clk_pre = t_param->clk_pre.program_value;
+
+ if (t_param->clk_zero.rec > 255) {
+ reg->timing[0] = t_param->clk_zero.program_value - 255;
+ reg->timing[3] = 1;
+ } else {
+ reg->timing[0] = t_param->clk_zero.program_value;
+ reg->timing[3] = 0;
+ }
+ reg->timing[1] = t_param->clk_trail.program_value;
+ reg->timing[2] = t_param->clk_prepare.program_value;
+ reg->timing[4] = t_param->hs_exit.program_value;
+ reg->timing[5] = t_param->hs_zero.program_value;
+ reg->timing[6] = t_param->hs_prepare.program_value;
+ reg->timing[7] = t_param->hs_trail.program_value;
+ reg->timing[8] = t_param->hs_rqst.program_value;
+ reg->timing[9] = (TA_SURE << 16) + TA_GO;
+ reg->timing[10] = TA_GET;
+ reg->timing[11] = 0;
+
+ pr_debug("[%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x]\n",
+ reg->timing[0], reg->timing[1], reg->timing[2], reg->timing[3],
+ reg->timing[4], reg->timing[5], reg->timing[6], reg->timing[7],
+ reg->timing[8], reg->timing[9], reg->timing[10],
+ reg->timing[11]);
+}
+
+int mdss_dsi_phy_calc_timing_param(struct mdss_panel_info *pinfo, u32 phy_rev,
+ u32 frate_hz)
+{
+ 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) {
+ pr_err("invalid panel info\n");
+ 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.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);
+
+ switch (phy_rev) {
+ case DSI_PHY_REV_10:
+ rc = mdss_dsi_phy_initialize_defaults(&t_clk, &t_param,
+ phy_rev);
+ if (rc) {
+ pr_err("phy%d initialization failed\n", phy_rev);
+ goto timing_calc_end;
+ }
+ mdss_dsi_phy_calc_param_phy_rev_1(&t_clk, &t_param);
+ mdss_dsi_phy_update_timing_param(pinfo, &t_param);
+ break;
+ case DSI_PHY_REV_20:
+ rc = mdss_dsi_phy_initialize_defaults(&t_clk, &t_param,
+ phy_rev);
+ if (rc) {
+ pr_err("phy%d initialization failed\n", phy_rev);
+ goto timing_calc_end;
+ }
+
+ rc = mdss_dsi_phy_calc_param_phy_rev_2(&t_clk, &t_param);
+ if (rc) {
+ pr_err("Phy timing calculations failed\n");
+ break;
+ }
+ break;
+ default:
+ pr_err("phy rev %d not supported\n", phy_rev);
+ return -EINVAL;
+ }
+
+timing_calc_end:
+ return rc;
+}
diff --git a/drivers/video/fbdev/msm/mdss_dsi_phy.h b/drivers/video/fbdev/msm/mdss_dsi_phy.h
new file mode 100644
index 000000000000..21ed499fb808
--- /dev/null
+++ b/drivers/video/fbdev/msm/mdss_dsi_phy.h
@@ -0,0 +1,39 @@
+/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef MDSS_DSI_PHY_H
+#define MDSS_DSI_PHY_H
+
+#include <linux/types.h>
+
+#include "mdss_panel.h"
+
+enum phy_rev {
+ DSI_PHY_REV_UNKNOWN = 0x00,
+ DSI_PHY_REV_10 = 0x01, /* REV 1.0 - 20nm, 28nm */
+ DSI_PHY_REV_20 = 0x02, /* REV 2.0 - 14nm */
+ DSI_PHY_REV_MAX,
+};
+
+/*
+ * mdss_dsi_phy_calc_timing_param() - calculates clock timing and hs timing
+ * parameters for the given phy revision.
+ *
+ * @pinfo - structure containing panel specific information which will be
+ * used in calculating the phy timing parameters.
+ * @phy_rev - phy revision for which phy timings need to be calculated.
+ * @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);
+
+#endif /* MDSS_DSI_PHY_H */