diff options
| author | Jeevan Shriram <jshriram@codeaurora.org> | 2015-09-10 13:08:54 -0700 |
|---|---|---|
| committer | David Keitel <dkeitel@codeaurora.org> | 2016-03-23 20:47:53 -0700 |
| commit | d83084cfb78d7e0bf83844b442fbed182121bcf0 (patch) | |
| tree | 542b7d23f339bee74475bb7caee63eb3487e87f3 | |
| parent | 92409ccc31cbb8b0ebc073008eb93067849fa467 (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/Makefile | 1 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_dsi_phy.c | 777 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_dsi_phy.h | 39 |
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 */ |
