Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b6d5eacf authored by Jeevan Shriram's avatar Jeevan Shriram Committed by Ingrid Gallardo
Browse files

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: default avatarAdrian Salido-Moreno <adrianm@codeaurora.org>
Signed-off-by: default avatarSandeep Panda <spanda@codeaurora.org>
Signed-off-by: default avatarJeevan Shriram <jshriram@codeaurora.org>
Signed-off-by: default avatarIngrid Gallardo <ingridg@codeaurora.org>
parent 031e28bb
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -42,6 +42,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.o mdss_dsi_host.o mdss_dsi_cmd.o mdss_dsi_status.o
mdss-dsi-objs += mdss_dsi_panel.o
mdss-dsi-objs += mdss_dsi_panel.o
mdss-dsi-objs += msm_mdss_io_8974.o
mdss-dsi-objs += msm_mdss_io_8974.o
mdss-dsi-objs += mdss_dsi_phy.o
mdss-dsi-objs += mdss_dsi_clk.o
mdss-dsi-objs += mdss_dsi_clk.o
obj-$(CONFIG_FB_MSM_MDSS) += mdss-dsi.o
obj-$(CONFIG_FB_MSM_MDSS) += mdss-dsi.o
obj-$(CONFIG_FB_MSM_MDSS) += mdss_panel.o
obj-$(CONFIG_FB_MSM_MDSS) += mdss_panel.o
+777 −0
Original line number Original line Diff line number Diff line
/* 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;
}