162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#define pr_fmt(fmt)	"[drm-dp] %s: " fmt, __func__
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/types.h>
962306a36Sopenharmony_ci#include <linux/completion.h>
1062306a36Sopenharmony_ci#include <linux/delay.h>
1162306a36Sopenharmony_ci#include <linux/phy/phy.h>
1262306a36Sopenharmony_ci#include <linux/phy/phy-dp.h>
1362306a36Sopenharmony_ci#include <linux/pm_opp.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <drm/display/drm_dp_helper.h>
1662306a36Sopenharmony_ci#include <drm/drm_fixed.h>
1762306a36Sopenharmony_ci#include <drm/drm_print.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include "dp_reg.h"
2062306a36Sopenharmony_ci#include "dp_ctrl.h"
2162306a36Sopenharmony_ci#include "dp_link.h"
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#define DP_KHZ_TO_HZ 1000
2462306a36Sopenharmony_ci#define IDLE_PATTERN_COMPLETION_TIMEOUT_JIFFIES	(30 * HZ / 1000) /* 30 ms */
2562306a36Sopenharmony_ci#define PSR_OPERATION_COMPLETION_TIMEOUT_JIFFIES       (300 * HZ / 1000) /* 300 ms */
2662306a36Sopenharmony_ci#define WAIT_FOR_VIDEO_READY_TIMEOUT_JIFFIES (HZ / 2)
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define DP_CTRL_INTR_READY_FOR_VIDEO     BIT(0)
2962306a36Sopenharmony_ci#define DP_CTRL_INTR_IDLE_PATTERN_SENT  BIT(3)
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define MR_LINK_TRAINING1  0x8
3262306a36Sopenharmony_ci#define MR_LINK_SYMBOL_ERM 0x80
3362306a36Sopenharmony_ci#define MR_LINK_PRBS7 0x100
3462306a36Sopenharmony_ci#define MR_LINK_CUSTOM80 0x200
3562306a36Sopenharmony_ci#define MR_LINK_TRAINING4  0x40
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cienum {
3862306a36Sopenharmony_ci	DP_TRAINING_NONE,
3962306a36Sopenharmony_ci	DP_TRAINING_1,
4062306a36Sopenharmony_ci	DP_TRAINING_2,
4162306a36Sopenharmony_ci};
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistruct dp_tu_calc_input {
4462306a36Sopenharmony_ci	u64 lclk;        /* 162, 270, 540 and 810 */
4562306a36Sopenharmony_ci	u64 pclk_khz;    /* in KHz */
4662306a36Sopenharmony_ci	u64 hactive;     /* active h-width */
4762306a36Sopenharmony_ci	u64 hporch;      /* bp + fp + pulse */
4862306a36Sopenharmony_ci	int nlanes;      /* no.of.lanes */
4962306a36Sopenharmony_ci	int bpp;         /* bits */
5062306a36Sopenharmony_ci	int pixel_enc;   /* 444, 420, 422 */
5162306a36Sopenharmony_ci	int dsc_en;     /* dsc on/off */
5262306a36Sopenharmony_ci	int async_en;   /* async mode */
5362306a36Sopenharmony_ci	int fec_en;     /* fec */
5462306a36Sopenharmony_ci	int compress_ratio; /* 2:1 = 200, 3:1 = 300, 3.75:1 = 375 */
5562306a36Sopenharmony_ci	int num_of_dsc_slices; /* number of slices per line */
5662306a36Sopenharmony_ci};
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistruct dp_vc_tu_mapping_table {
5962306a36Sopenharmony_ci	u32 vic;
6062306a36Sopenharmony_ci	u8 lanes;
6162306a36Sopenharmony_ci	u8 lrate; /* DP_LINK_RATE -> 162(6), 270(10), 540(20), 810 (30) */
6262306a36Sopenharmony_ci	u8 bpp;
6362306a36Sopenharmony_ci	u8 valid_boundary_link;
6462306a36Sopenharmony_ci	u16 delay_start_link;
6562306a36Sopenharmony_ci	bool boundary_moderation_en;
6662306a36Sopenharmony_ci	u8 valid_lower_boundary_link;
6762306a36Sopenharmony_ci	u8 upper_boundary_count;
6862306a36Sopenharmony_ci	u8 lower_boundary_count;
6962306a36Sopenharmony_ci	u8 tu_size_minus1;
7062306a36Sopenharmony_ci};
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistruct dp_ctrl_private {
7362306a36Sopenharmony_ci	struct dp_ctrl dp_ctrl;
7462306a36Sopenharmony_ci	struct drm_device *drm_dev;
7562306a36Sopenharmony_ci	struct device *dev;
7662306a36Sopenharmony_ci	struct drm_dp_aux *aux;
7762306a36Sopenharmony_ci	struct dp_panel *panel;
7862306a36Sopenharmony_ci	struct dp_link *link;
7962306a36Sopenharmony_ci	struct dp_power *power;
8062306a36Sopenharmony_ci	struct dp_parser *parser;
8162306a36Sopenharmony_ci	struct dp_catalog *catalog;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	struct completion idle_comp;
8462306a36Sopenharmony_ci	struct completion psr_op_comp;
8562306a36Sopenharmony_ci	struct completion video_comp;
8662306a36Sopenharmony_ci};
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic int dp_aux_link_configure(struct drm_dp_aux *aux,
8962306a36Sopenharmony_ci					struct dp_link_info *link)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	u8 values[2];
9262306a36Sopenharmony_ci	int err;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	values[0] = drm_dp_link_rate_to_bw_code(link->rate);
9562306a36Sopenharmony_ci	values[1] = link->num_lanes;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	if (link->capabilities & DP_LINK_CAP_ENHANCED_FRAMING)
9862306a36Sopenharmony_ci		values[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	err = drm_dp_dpcd_write(aux, DP_LINK_BW_SET, values, sizeof(values));
10162306a36Sopenharmony_ci	if (err < 0)
10262306a36Sopenharmony_ci		return err;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	return 0;
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_civoid dp_ctrl_push_idle(struct dp_ctrl *dp_ctrl)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	struct dp_ctrl_private *ctrl;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	reinit_completion(&ctrl->idle_comp);
11462306a36Sopenharmony_ci	dp_catalog_ctrl_state_ctrl(ctrl->catalog, DP_STATE_CTRL_PUSH_IDLE);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	if (!wait_for_completion_timeout(&ctrl->idle_comp,
11762306a36Sopenharmony_ci			IDLE_PATTERN_COMPLETION_TIMEOUT_JIFFIES))
11862306a36Sopenharmony_ci		pr_warn("PUSH_IDLE pattern timedout\n");
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	drm_dbg_dp(ctrl->drm_dev, "mainlink off\n");
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic void dp_ctrl_config_ctrl(struct dp_ctrl_private *ctrl)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	u32 config = 0, tbd;
12662306a36Sopenharmony_ci	const u8 *dpcd = ctrl->panel->dpcd;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	/* Default-> LSCLK DIV: 1/4 LCLK  */
12962306a36Sopenharmony_ci	config |= (2 << DP_CONFIGURATION_CTRL_LSCLK_DIV_SHIFT);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	/* Scrambler reset enable */
13262306a36Sopenharmony_ci	if (drm_dp_alternate_scrambler_reset_cap(dpcd))
13362306a36Sopenharmony_ci		config |= DP_CONFIGURATION_CTRL_ASSR;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	tbd = dp_link_get_test_bits_depth(ctrl->link,
13662306a36Sopenharmony_ci			ctrl->panel->dp_mode.bpp);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	config |= tbd << DP_CONFIGURATION_CTRL_BPC_SHIFT;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	/* Num of Lanes */
14162306a36Sopenharmony_ci	config |= ((ctrl->link->link_params.num_lanes - 1)
14262306a36Sopenharmony_ci			<< DP_CONFIGURATION_CTRL_NUM_OF_LANES_SHIFT);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	if (drm_dp_enhanced_frame_cap(dpcd))
14562306a36Sopenharmony_ci		config |= DP_CONFIGURATION_CTRL_ENHANCED_FRAMING;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	config |= DP_CONFIGURATION_CTRL_P_INTERLACED; /* progressive video */
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	/* sync clock & static Mvid */
15062306a36Sopenharmony_ci	config |= DP_CONFIGURATION_CTRL_STATIC_DYNAMIC_CN;
15162306a36Sopenharmony_ci	config |= DP_CONFIGURATION_CTRL_SYNC_ASYNC_CLK;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	if (ctrl->panel->psr_cap.version)
15462306a36Sopenharmony_ci		config |= DP_CONFIGURATION_CTRL_SEND_VSC;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	dp_catalog_ctrl_config_ctrl(ctrl->catalog, config);
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_cistatic void dp_ctrl_configure_source_params(struct dp_ctrl_private *ctrl)
16062306a36Sopenharmony_ci{
16162306a36Sopenharmony_ci	u32 cc, tb;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	dp_catalog_ctrl_lane_mapping(ctrl->catalog);
16462306a36Sopenharmony_ci	dp_catalog_ctrl_mainlink_ctrl(ctrl->catalog, true);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	dp_ctrl_config_ctrl(ctrl);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	tb = dp_link_get_test_bits_depth(ctrl->link,
16962306a36Sopenharmony_ci		ctrl->panel->dp_mode.bpp);
17062306a36Sopenharmony_ci	cc = dp_link_get_colorimetry_config(ctrl->link);
17162306a36Sopenharmony_ci	dp_catalog_ctrl_config_misc(ctrl->catalog, cc, tb);
17262306a36Sopenharmony_ci	dp_panel_timing_cfg(ctrl->panel);
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci/*
17662306a36Sopenharmony_ci * The structure and few functions present below are IP/Hardware
17762306a36Sopenharmony_ci * specific implementation. Most of the implementation will not
17862306a36Sopenharmony_ci * have coding comments
17962306a36Sopenharmony_ci */
18062306a36Sopenharmony_cistruct tu_algo_data {
18162306a36Sopenharmony_ci	s64 lclk_fp;
18262306a36Sopenharmony_ci	s64 pclk_fp;
18362306a36Sopenharmony_ci	s64 lwidth;
18462306a36Sopenharmony_ci	s64 lwidth_fp;
18562306a36Sopenharmony_ci	s64 hbp_relative_to_pclk;
18662306a36Sopenharmony_ci	s64 hbp_relative_to_pclk_fp;
18762306a36Sopenharmony_ci	int nlanes;
18862306a36Sopenharmony_ci	int bpp;
18962306a36Sopenharmony_ci	int pixelEnc;
19062306a36Sopenharmony_ci	int dsc_en;
19162306a36Sopenharmony_ci	int async_en;
19262306a36Sopenharmony_ci	int bpc;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	uint delay_start_link_extra_pixclk;
19562306a36Sopenharmony_ci	int extra_buffer_margin;
19662306a36Sopenharmony_ci	s64 ratio_fp;
19762306a36Sopenharmony_ci	s64 original_ratio_fp;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	s64 err_fp;
20062306a36Sopenharmony_ci	s64 n_err_fp;
20162306a36Sopenharmony_ci	s64 n_n_err_fp;
20262306a36Sopenharmony_ci	int tu_size;
20362306a36Sopenharmony_ci	int tu_size_desired;
20462306a36Sopenharmony_ci	int tu_size_minus1;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	int valid_boundary_link;
20762306a36Sopenharmony_ci	s64 resulting_valid_fp;
20862306a36Sopenharmony_ci	s64 total_valid_fp;
20962306a36Sopenharmony_ci	s64 effective_valid_fp;
21062306a36Sopenharmony_ci	s64 effective_valid_recorded_fp;
21162306a36Sopenharmony_ci	int n_tus;
21262306a36Sopenharmony_ci	int n_tus_per_lane;
21362306a36Sopenharmony_ci	int paired_tus;
21462306a36Sopenharmony_ci	int remainder_tus;
21562306a36Sopenharmony_ci	int remainder_tus_upper;
21662306a36Sopenharmony_ci	int remainder_tus_lower;
21762306a36Sopenharmony_ci	int extra_bytes;
21862306a36Sopenharmony_ci	int filler_size;
21962306a36Sopenharmony_ci	int delay_start_link;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	int extra_pclk_cycles;
22262306a36Sopenharmony_ci	int extra_pclk_cycles_in_link_clk;
22362306a36Sopenharmony_ci	s64 ratio_by_tu_fp;
22462306a36Sopenharmony_ci	s64 average_valid2_fp;
22562306a36Sopenharmony_ci	int new_valid_boundary_link;
22662306a36Sopenharmony_ci	int remainder_symbols_exist;
22762306a36Sopenharmony_ci	int n_symbols;
22862306a36Sopenharmony_ci	s64 n_remainder_symbols_per_lane_fp;
22962306a36Sopenharmony_ci	s64 last_partial_tu_fp;
23062306a36Sopenharmony_ci	s64 TU_ratio_err_fp;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	int n_tus_incl_last_incomplete_tu;
23362306a36Sopenharmony_ci	int extra_pclk_cycles_tmp;
23462306a36Sopenharmony_ci	int extra_pclk_cycles_in_link_clk_tmp;
23562306a36Sopenharmony_ci	int extra_required_bytes_new_tmp;
23662306a36Sopenharmony_ci	int filler_size_tmp;
23762306a36Sopenharmony_ci	int lower_filler_size_tmp;
23862306a36Sopenharmony_ci	int delay_start_link_tmp;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	bool boundary_moderation_en;
24162306a36Sopenharmony_ci	int boundary_mod_lower_err;
24262306a36Sopenharmony_ci	int upper_boundary_count;
24362306a36Sopenharmony_ci	int lower_boundary_count;
24462306a36Sopenharmony_ci	int i_upper_boundary_count;
24562306a36Sopenharmony_ci	int i_lower_boundary_count;
24662306a36Sopenharmony_ci	int valid_lower_boundary_link;
24762306a36Sopenharmony_ci	int even_distribution_BF;
24862306a36Sopenharmony_ci	int even_distribution_legacy;
24962306a36Sopenharmony_ci	int even_distribution;
25062306a36Sopenharmony_ci	int min_hblank_violated;
25162306a36Sopenharmony_ci	s64 delay_start_time_fp;
25262306a36Sopenharmony_ci	s64 hbp_time_fp;
25362306a36Sopenharmony_ci	s64 hactive_time_fp;
25462306a36Sopenharmony_ci	s64 diff_abs_fp;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	s64 ratio;
25762306a36Sopenharmony_ci};
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistatic int _tu_param_compare(s64 a, s64 b)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	u32 a_sign;
26262306a36Sopenharmony_ci	u32 b_sign;
26362306a36Sopenharmony_ci	s64 a_temp, b_temp, minus_1;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	if (a == b)
26662306a36Sopenharmony_ci		return 0;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	minus_1 = drm_fixp_from_fraction(-1, 1);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	a_sign = (a >> 32) & 0x80000000 ? 1 : 0;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	b_sign = (b >> 32) & 0x80000000 ? 1 : 0;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	if (a_sign > b_sign)
27562306a36Sopenharmony_ci		return 2;
27662306a36Sopenharmony_ci	else if (b_sign > a_sign)
27762306a36Sopenharmony_ci		return 1;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	if (!a_sign && !b_sign) { /* positive */
28062306a36Sopenharmony_ci		if (a > b)
28162306a36Sopenharmony_ci			return 1;
28262306a36Sopenharmony_ci		else
28362306a36Sopenharmony_ci			return 2;
28462306a36Sopenharmony_ci	} else { /* negative */
28562306a36Sopenharmony_ci		a_temp = drm_fixp_mul(a, minus_1);
28662306a36Sopenharmony_ci		b_temp = drm_fixp_mul(b, minus_1);
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci		if (a_temp > b_temp)
28962306a36Sopenharmony_ci			return 2;
29062306a36Sopenharmony_ci		else
29162306a36Sopenharmony_ci			return 1;
29262306a36Sopenharmony_ci	}
29362306a36Sopenharmony_ci}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_cistatic void dp_panel_update_tu_timings(struct dp_tu_calc_input *in,
29662306a36Sopenharmony_ci					struct tu_algo_data *tu)
29762306a36Sopenharmony_ci{
29862306a36Sopenharmony_ci	int nlanes = in->nlanes;
29962306a36Sopenharmony_ci	int dsc_num_slices = in->num_of_dsc_slices;
30062306a36Sopenharmony_ci	int dsc_num_bytes  = 0;
30162306a36Sopenharmony_ci	int numerator;
30262306a36Sopenharmony_ci	s64 pclk_dsc_fp;
30362306a36Sopenharmony_ci	s64 dwidth_dsc_fp;
30462306a36Sopenharmony_ci	s64 hbp_dsc_fp;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	int tot_num_eoc_symbols = 0;
30762306a36Sopenharmony_ci	int tot_num_hor_bytes   = 0;
30862306a36Sopenharmony_ci	int tot_num_dummy_bytes = 0;
30962306a36Sopenharmony_ci	int dwidth_dsc_bytes    = 0;
31062306a36Sopenharmony_ci	int  eoc_bytes           = 0;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	s64 temp1_fp, temp2_fp, temp3_fp;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	tu->lclk_fp              = drm_fixp_from_fraction(in->lclk, 1);
31562306a36Sopenharmony_ci	tu->pclk_fp              = drm_fixp_from_fraction(in->pclk_khz, 1000);
31662306a36Sopenharmony_ci	tu->lwidth               = in->hactive;
31762306a36Sopenharmony_ci	tu->hbp_relative_to_pclk = in->hporch;
31862306a36Sopenharmony_ci	tu->nlanes               = in->nlanes;
31962306a36Sopenharmony_ci	tu->bpp                  = in->bpp;
32062306a36Sopenharmony_ci	tu->pixelEnc             = in->pixel_enc;
32162306a36Sopenharmony_ci	tu->dsc_en               = in->dsc_en;
32262306a36Sopenharmony_ci	tu->async_en             = in->async_en;
32362306a36Sopenharmony_ci	tu->lwidth_fp            = drm_fixp_from_fraction(in->hactive, 1);
32462306a36Sopenharmony_ci	tu->hbp_relative_to_pclk_fp = drm_fixp_from_fraction(in->hporch, 1);
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	if (tu->pixelEnc == 420) {
32762306a36Sopenharmony_ci		temp1_fp = drm_fixp_from_fraction(2, 1);
32862306a36Sopenharmony_ci		tu->pclk_fp = drm_fixp_div(tu->pclk_fp, temp1_fp);
32962306a36Sopenharmony_ci		tu->lwidth_fp = drm_fixp_div(tu->lwidth_fp, temp1_fp);
33062306a36Sopenharmony_ci		tu->hbp_relative_to_pclk_fp =
33162306a36Sopenharmony_ci				drm_fixp_div(tu->hbp_relative_to_pclk_fp, 2);
33262306a36Sopenharmony_ci	}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	if (tu->pixelEnc == 422) {
33562306a36Sopenharmony_ci		switch (tu->bpp) {
33662306a36Sopenharmony_ci		case 24:
33762306a36Sopenharmony_ci			tu->bpp = 16;
33862306a36Sopenharmony_ci			tu->bpc = 8;
33962306a36Sopenharmony_ci			break;
34062306a36Sopenharmony_ci		case 30:
34162306a36Sopenharmony_ci			tu->bpp = 20;
34262306a36Sopenharmony_ci			tu->bpc = 10;
34362306a36Sopenharmony_ci			break;
34462306a36Sopenharmony_ci		default:
34562306a36Sopenharmony_ci			tu->bpp = 16;
34662306a36Sopenharmony_ci			tu->bpc = 8;
34762306a36Sopenharmony_ci			break;
34862306a36Sopenharmony_ci		}
34962306a36Sopenharmony_ci	} else {
35062306a36Sopenharmony_ci		tu->bpc = tu->bpp/3;
35162306a36Sopenharmony_ci	}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	if (!in->dsc_en)
35462306a36Sopenharmony_ci		goto fec_check;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	temp1_fp = drm_fixp_from_fraction(in->compress_ratio, 100);
35762306a36Sopenharmony_ci	temp2_fp = drm_fixp_from_fraction(in->bpp, 1);
35862306a36Sopenharmony_ci	temp3_fp = drm_fixp_div(temp2_fp, temp1_fp);
35962306a36Sopenharmony_ci	temp2_fp = drm_fixp_mul(tu->lwidth_fp, temp3_fp);
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	temp1_fp = drm_fixp_from_fraction(8, 1);
36262306a36Sopenharmony_ci	temp3_fp = drm_fixp_div(temp2_fp, temp1_fp);
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	numerator = drm_fixp2int(temp3_fp);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	dsc_num_bytes  = numerator / dsc_num_slices;
36762306a36Sopenharmony_ci	eoc_bytes           = dsc_num_bytes % nlanes;
36862306a36Sopenharmony_ci	tot_num_eoc_symbols = nlanes * dsc_num_slices;
36962306a36Sopenharmony_ci	tot_num_hor_bytes   = dsc_num_bytes * dsc_num_slices;
37062306a36Sopenharmony_ci	tot_num_dummy_bytes = (nlanes - eoc_bytes) * dsc_num_slices;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	if (dsc_num_bytes == 0)
37362306a36Sopenharmony_ci		pr_info("incorrect no of bytes per slice=%d\n", dsc_num_bytes);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	dwidth_dsc_bytes = (tot_num_hor_bytes +
37662306a36Sopenharmony_ci				tot_num_eoc_symbols +
37762306a36Sopenharmony_ci				(eoc_bytes == 0 ? 0 : tot_num_dummy_bytes));
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	dwidth_dsc_fp = drm_fixp_from_fraction(dwidth_dsc_bytes, 3);
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	temp2_fp = drm_fixp_mul(tu->pclk_fp, dwidth_dsc_fp);
38262306a36Sopenharmony_ci	temp1_fp = drm_fixp_div(temp2_fp, tu->lwidth_fp);
38362306a36Sopenharmony_ci	pclk_dsc_fp = temp1_fp;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	temp1_fp = drm_fixp_div(pclk_dsc_fp, tu->pclk_fp);
38662306a36Sopenharmony_ci	temp2_fp = drm_fixp_mul(tu->hbp_relative_to_pclk_fp, temp1_fp);
38762306a36Sopenharmony_ci	hbp_dsc_fp = temp2_fp;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	/* output */
39062306a36Sopenharmony_ci	tu->pclk_fp = pclk_dsc_fp;
39162306a36Sopenharmony_ci	tu->lwidth_fp = dwidth_dsc_fp;
39262306a36Sopenharmony_ci	tu->hbp_relative_to_pclk_fp = hbp_dsc_fp;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_cifec_check:
39562306a36Sopenharmony_ci	if (in->fec_en) {
39662306a36Sopenharmony_ci		temp1_fp = drm_fixp_from_fraction(976, 1000); /* 0.976 */
39762306a36Sopenharmony_ci		tu->lclk_fp = drm_fixp_mul(tu->lclk_fp, temp1_fp);
39862306a36Sopenharmony_ci	}
39962306a36Sopenharmony_ci}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_cistatic void _tu_valid_boundary_calc(struct tu_algo_data *tu)
40262306a36Sopenharmony_ci{
40362306a36Sopenharmony_ci	s64 temp1_fp, temp2_fp, temp, temp1, temp2;
40462306a36Sopenharmony_ci	int compare_result_1, compare_result_2, compare_result_3;
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	temp1_fp = drm_fixp_from_fraction(tu->tu_size, 1);
40762306a36Sopenharmony_ci	temp2_fp = drm_fixp_mul(tu->ratio_fp, temp1_fp);
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	tu->new_valid_boundary_link = drm_fixp2int_ceil(temp2_fp);
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	temp = (tu->i_upper_boundary_count *
41262306a36Sopenharmony_ci				tu->new_valid_boundary_link +
41362306a36Sopenharmony_ci				tu->i_lower_boundary_count *
41462306a36Sopenharmony_ci				(tu->new_valid_boundary_link-1));
41562306a36Sopenharmony_ci	tu->average_valid2_fp = drm_fixp_from_fraction(temp,
41662306a36Sopenharmony_ci					(tu->i_upper_boundary_count +
41762306a36Sopenharmony_ci					tu->i_lower_boundary_count));
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	temp1_fp = drm_fixp_from_fraction(tu->bpp, 8);
42062306a36Sopenharmony_ci	temp2_fp = tu->lwidth_fp;
42162306a36Sopenharmony_ci	temp1_fp = drm_fixp_mul(temp2_fp, temp1_fp);
42262306a36Sopenharmony_ci	temp2_fp = drm_fixp_div(temp1_fp, tu->average_valid2_fp);
42362306a36Sopenharmony_ci	tu->n_tus = drm_fixp2int(temp2_fp);
42462306a36Sopenharmony_ci	if ((temp2_fp & 0xFFFFFFFF) > 0xFFFFF000)
42562306a36Sopenharmony_ci		tu->n_tus += 1;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	temp1_fp = drm_fixp_from_fraction(tu->n_tus, 1);
42862306a36Sopenharmony_ci	temp2_fp = drm_fixp_mul(temp1_fp, tu->average_valid2_fp);
42962306a36Sopenharmony_ci	temp1_fp = drm_fixp_from_fraction(tu->n_symbols, 1);
43062306a36Sopenharmony_ci	temp2_fp = temp1_fp - temp2_fp;
43162306a36Sopenharmony_ci	temp1_fp = drm_fixp_from_fraction(tu->nlanes, 1);
43262306a36Sopenharmony_ci	temp2_fp = drm_fixp_div(temp2_fp, temp1_fp);
43362306a36Sopenharmony_ci	tu->n_remainder_symbols_per_lane_fp = temp2_fp;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	temp1_fp = drm_fixp_from_fraction(tu->tu_size, 1);
43662306a36Sopenharmony_ci	tu->last_partial_tu_fp =
43762306a36Sopenharmony_ci			drm_fixp_div(tu->n_remainder_symbols_per_lane_fp,
43862306a36Sopenharmony_ci					temp1_fp);
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	if (tu->n_remainder_symbols_per_lane_fp != 0)
44162306a36Sopenharmony_ci		tu->remainder_symbols_exist = 1;
44262306a36Sopenharmony_ci	else
44362306a36Sopenharmony_ci		tu->remainder_symbols_exist = 0;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	temp1_fp = drm_fixp_from_fraction(tu->n_tus, tu->nlanes);
44662306a36Sopenharmony_ci	tu->n_tus_per_lane = drm_fixp2int(temp1_fp);
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	tu->paired_tus = (int)((tu->n_tus_per_lane) /
44962306a36Sopenharmony_ci					(tu->i_upper_boundary_count +
45062306a36Sopenharmony_ci					 tu->i_lower_boundary_count));
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	tu->remainder_tus = tu->n_tus_per_lane - tu->paired_tus *
45362306a36Sopenharmony_ci						(tu->i_upper_boundary_count +
45462306a36Sopenharmony_ci						tu->i_lower_boundary_count);
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	if ((tu->remainder_tus - tu->i_upper_boundary_count) > 0) {
45762306a36Sopenharmony_ci		tu->remainder_tus_upper = tu->i_upper_boundary_count;
45862306a36Sopenharmony_ci		tu->remainder_tus_lower = tu->remainder_tus -
45962306a36Sopenharmony_ci						tu->i_upper_boundary_count;
46062306a36Sopenharmony_ci	} else {
46162306a36Sopenharmony_ci		tu->remainder_tus_upper = tu->remainder_tus;
46262306a36Sopenharmony_ci		tu->remainder_tus_lower = 0;
46362306a36Sopenharmony_ci	}
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	temp = tu->paired_tus * (tu->i_upper_boundary_count *
46662306a36Sopenharmony_ci				tu->new_valid_boundary_link +
46762306a36Sopenharmony_ci				tu->i_lower_boundary_count *
46862306a36Sopenharmony_ci				(tu->new_valid_boundary_link - 1)) +
46962306a36Sopenharmony_ci				(tu->remainder_tus_upper *
47062306a36Sopenharmony_ci				 tu->new_valid_boundary_link) +
47162306a36Sopenharmony_ci				(tu->remainder_tus_lower *
47262306a36Sopenharmony_ci				(tu->new_valid_boundary_link - 1));
47362306a36Sopenharmony_ci	tu->total_valid_fp = drm_fixp_from_fraction(temp, 1);
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	if (tu->remainder_symbols_exist) {
47662306a36Sopenharmony_ci		temp1_fp = tu->total_valid_fp +
47762306a36Sopenharmony_ci				tu->n_remainder_symbols_per_lane_fp;
47862306a36Sopenharmony_ci		temp2_fp = drm_fixp_from_fraction(tu->n_tus_per_lane, 1);
47962306a36Sopenharmony_ci		temp2_fp = temp2_fp + tu->last_partial_tu_fp;
48062306a36Sopenharmony_ci		temp1_fp = drm_fixp_div(temp1_fp, temp2_fp);
48162306a36Sopenharmony_ci	} else {
48262306a36Sopenharmony_ci		temp2_fp = drm_fixp_from_fraction(tu->n_tus_per_lane, 1);
48362306a36Sopenharmony_ci		temp1_fp = drm_fixp_div(tu->total_valid_fp, temp2_fp);
48462306a36Sopenharmony_ci	}
48562306a36Sopenharmony_ci	tu->effective_valid_fp = temp1_fp;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	temp1_fp = drm_fixp_from_fraction(tu->tu_size, 1);
48862306a36Sopenharmony_ci	temp2_fp = drm_fixp_mul(tu->ratio_fp, temp1_fp);
48962306a36Sopenharmony_ci	tu->n_n_err_fp = tu->effective_valid_fp - temp2_fp;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	temp1_fp = drm_fixp_from_fraction(tu->tu_size, 1);
49262306a36Sopenharmony_ci	temp2_fp = drm_fixp_mul(tu->ratio_fp, temp1_fp);
49362306a36Sopenharmony_ci	tu->n_err_fp = tu->average_valid2_fp - temp2_fp;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	tu->even_distribution = tu->n_tus % tu->nlanes == 0 ? 1 : 0;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	temp1_fp = drm_fixp_from_fraction(tu->bpp, 8);
49862306a36Sopenharmony_ci	temp2_fp = tu->lwidth_fp;
49962306a36Sopenharmony_ci	temp1_fp = drm_fixp_mul(temp2_fp, temp1_fp);
50062306a36Sopenharmony_ci	temp2_fp = drm_fixp_div(temp1_fp, tu->average_valid2_fp);
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	if (temp2_fp)
50362306a36Sopenharmony_ci		tu->n_tus_incl_last_incomplete_tu = drm_fixp2int_ceil(temp2_fp);
50462306a36Sopenharmony_ci	else
50562306a36Sopenharmony_ci		tu->n_tus_incl_last_incomplete_tu = 0;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	temp1 = 0;
50862306a36Sopenharmony_ci	temp1_fp = drm_fixp_from_fraction(tu->tu_size, 1);
50962306a36Sopenharmony_ci	temp2_fp = drm_fixp_mul(tu->original_ratio_fp, temp1_fp);
51062306a36Sopenharmony_ci	temp1_fp = tu->average_valid2_fp - temp2_fp;
51162306a36Sopenharmony_ci	temp2_fp = drm_fixp_from_fraction(tu->n_tus_incl_last_incomplete_tu, 1);
51262306a36Sopenharmony_ci	temp1_fp = drm_fixp_mul(temp2_fp, temp1_fp);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	if (temp1_fp)
51562306a36Sopenharmony_ci		temp1 = drm_fixp2int_ceil(temp1_fp);
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	temp = tu->i_upper_boundary_count * tu->nlanes;
51862306a36Sopenharmony_ci	temp1_fp = drm_fixp_from_fraction(tu->tu_size, 1);
51962306a36Sopenharmony_ci	temp2_fp = drm_fixp_mul(tu->original_ratio_fp, temp1_fp);
52062306a36Sopenharmony_ci	temp1_fp = drm_fixp_from_fraction(tu->new_valid_boundary_link, 1);
52162306a36Sopenharmony_ci	temp2_fp = temp1_fp - temp2_fp;
52262306a36Sopenharmony_ci	temp1_fp = drm_fixp_from_fraction(temp, 1);
52362306a36Sopenharmony_ci	temp2_fp = drm_fixp_mul(temp1_fp, temp2_fp);
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	if (temp2_fp)
52662306a36Sopenharmony_ci		temp2 = drm_fixp2int_ceil(temp2_fp);
52762306a36Sopenharmony_ci	else
52862306a36Sopenharmony_ci		temp2 = 0;
52962306a36Sopenharmony_ci	tu->extra_required_bytes_new_tmp = (int)(temp1 + temp2);
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	temp1_fp = drm_fixp_from_fraction(8, tu->bpp);
53262306a36Sopenharmony_ci	temp2_fp = drm_fixp_from_fraction(
53362306a36Sopenharmony_ci	tu->extra_required_bytes_new_tmp, 1);
53462306a36Sopenharmony_ci	temp1_fp = drm_fixp_mul(temp2_fp, temp1_fp);
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	if (temp1_fp)
53762306a36Sopenharmony_ci		tu->extra_pclk_cycles_tmp = drm_fixp2int_ceil(temp1_fp);
53862306a36Sopenharmony_ci	else
53962306a36Sopenharmony_ci		tu->extra_pclk_cycles_tmp = 0;
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	temp1_fp = drm_fixp_from_fraction(tu->extra_pclk_cycles_tmp, 1);
54262306a36Sopenharmony_ci	temp2_fp = drm_fixp_div(tu->lclk_fp, tu->pclk_fp);
54362306a36Sopenharmony_ci	temp1_fp = drm_fixp_mul(temp1_fp, temp2_fp);
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	if (temp1_fp)
54662306a36Sopenharmony_ci		tu->extra_pclk_cycles_in_link_clk_tmp =
54762306a36Sopenharmony_ci						drm_fixp2int_ceil(temp1_fp);
54862306a36Sopenharmony_ci	else
54962306a36Sopenharmony_ci		tu->extra_pclk_cycles_in_link_clk_tmp = 0;
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	tu->filler_size_tmp = tu->tu_size - tu->new_valid_boundary_link;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	tu->lower_filler_size_tmp = tu->filler_size_tmp + 1;
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	tu->delay_start_link_tmp = tu->extra_pclk_cycles_in_link_clk_tmp +
55662306a36Sopenharmony_ci					tu->lower_filler_size_tmp +
55762306a36Sopenharmony_ci					tu->extra_buffer_margin;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	temp1_fp = drm_fixp_from_fraction(tu->delay_start_link_tmp, 1);
56062306a36Sopenharmony_ci	tu->delay_start_time_fp = drm_fixp_div(temp1_fp, tu->lclk_fp);
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	compare_result_1 = _tu_param_compare(tu->n_n_err_fp, tu->diff_abs_fp);
56362306a36Sopenharmony_ci	if (compare_result_1 == 2)
56462306a36Sopenharmony_ci		compare_result_1 = 1;
56562306a36Sopenharmony_ci	else
56662306a36Sopenharmony_ci		compare_result_1 = 0;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	compare_result_2 = _tu_param_compare(tu->n_n_err_fp, tu->err_fp);
56962306a36Sopenharmony_ci	if (compare_result_2 == 2)
57062306a36Sopenharmony_ci		compare_result_2 = 1;
57162306a36Sopenharmony_ci	else
57262306a36Sopenharmony_ci		compare_result_2 = 0;
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	compare_result_3 = _tu_param_compare(tu->hbp_time_fp,
57562306a36Sopenharmony_ci					tu->delay_start_time_fp);
57662306a36Sopenharmony_ci	if (compare_result_3 == 2)
57762306a36Sopenharmony_ci		compare_result_3 = 0;
57862306a36Sopenharmony_ci	else
57962306a36Sopenharmony_ci		compare_result_3 = 1;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	if (((tu->even_distribution == 1) ||
58262306a36Sopenharmony_ci			((tu->even_distribution_BF == 0) &&
58362306a36Sopenharmony_ci			(tu->even_distribution_legacy == 0))) &&
58462306a36Sopenharmony_ci			tu->n_err_fp >= 0 && tu->n_n_err_fp >= 0 &&
58562306a36Sopenharmony_ci			compare_result_2 &&
58662306a36Sopenharmony_ci			(compare_result_1 || (tu->min_hblank_violated == 1)) &&
58762306a36Sopenharmony_ci			(tu->new_valid_boundary_link - 1) > 0 &&
58862306a36Sopenharmony_ci			compare_result_3 &&
58962306a36Sopenharmony_ci			(tu->delay_start_link_tmp <= 1023)) {
59062306a36Sopenharmony_ci		tu->upper_boundary_count = tu->i_upper_boundary_count;
59162306a36Sopenharmony_ci		tu->lower_boundary_count = tu->i_lower_boundary_count;
59262306a36Sopenharmony_ci		tu->err_fp = tu->n_n_err_fp;
59362306a36Sopenharmony_ci		tu->boundary_moderation_en = true;
59462306a36Sopenharmony_ci		tu->tu_size_desired = tu->tu_size;
59562306a36Sopenharmony_ci		tu->valid_boundary_link = tu->new_valid_boundary_link;
59662306a36Sopenharmony_ci		tu->effective_valid_recorded_fp = tu->effective_valid_fp;
59762306a36Sopenharmony_ci		tu->even_distribution_BF = 1;
59862306a36Sopenharmony_ci		tu->delay_start_link = tu->delay_start_link_tmp;
59962306a36Sopenharmony_ci	} else if (tu->boundary_mod_lower_err == 0) {
60062306a36Sopenharmony_ci		compare_result_1 = _tu_param_compare(tu->n_n_err_fp,
60162306a36Sopenharmony_ci							tu->diff_abs_fp);
60262306a36Sopenharmony_ci		if (compare_result_1 == 2)
60362306a36Sopenharmony_ci			tu->boundary_mod_lower_err = 1;
60462306a36Sopenharmony_ci	}
60562306a36Sopenharmony_ci}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_cistatic void _dp_ctrl_calc_tu(struct dp_ctrl_private *ctrl,
60862306a36Sopenharmony_ci				struct dp_tu_calc_input *in,
60962306a36Sopenharmony_ci				struct dp_vc_tu_mapping_table *tu_table)
61062306a36Sopenharmony_ci{
61162306a36Sopenharmony_ci	struct tu_algo_data *tu;
61262306a36Sopenharmony_ci	int compare_result_1, compare_result_2;
61362306a36Sopenharmony_ci	u64 temp = 0;
61462306a36Sopenharmony_ci	s64 temp_fp = 0, temp1_fp = 0, temp2_fp = 0;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	s64 LCLK_FAST_SKEW_fp = drm_fixp_from_fraction(6, 10000); /* 0.0006 */
61762306a36Sopenharmony_ci	s64 const_p49_fp = drm_fixp_from_fraction(49, 100); /* 0.49 */
61862306a36Sopenharmony_ci	s64 const_p56_fp = drm_fixp_from_fraction(56, 100); /* 0.56 */
61962306a36Sopenharmony_ci	s64 RATIO_SCALE_fp = drm_fixp_from_fraction(1001, 1000);
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	u8 DP_BRUTE_FORCE = 1;
62262306a36Sopenharmony_ci	s64 BRUTE_FORCE_THRESHOLD_fp = drm_fixp_from_fraction(1, 10); /* 0.1 */
62362306a36Sopenharmony_ci	uint EXTRA_PIXCLK_CYCLE_DELAY = 4;
62462306a36Sopenharmony_ci	uint HBLANK_MARGIN = 4;
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	tu = kzalloc(sizeof(*tu), GFP_KERNEL);
62762306a36Sopenharmony_ci	if (!tu)
62862306a36Sopenharmony_ci		return;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	dp_panel_update_tu_timings(in, tu);
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	tu->err_fp = drm_fixp_from_fraction(1000, 1); /* 1000 */
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	temp1_fp = drm_fixp_from_fraction(4, 1);
63562306a36Sopenharmony_ci	temp2_fp = drm_fixp_mul(temp1_fp, tu->lclk_fp);
63662306a36Sopenharmony_ci	temp_fp = drm_fixp_div(temp2_fp, tu->pclk_fp);
63762306a36Sopenharmony_ci	tu->extra_buffer_margin = drm_fixp2int_ceil(temp_fp);
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	temp1_fp = drm_fixp_from_fraction(tu->bpp, 8);
64062306a36Sopenharmony_ci	temp2_fp = drm_fixp_mul(tu->pclk_fp, temp1_fp);
64162306a36Sopenharmony_ci	temp1_fp = drm_fixp_from_fraction(tu->nlanes, 1);
64262306a36Sopenharmony_ci	temp2_fp = drm_fixp_div(temp2_fp, temp1_fp);
64362306a36Sopenharmony_ci	tu->ratio_fp = drm_fixp_div(temp2_fp, tu->lclk_fp);
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	tu->original_ratio_fp = tu->ratio_fp;
64662306a36Sopenharmony_ci	tu->boundary_moderation_en = false;
64762306a36Sopenharmony_ci	tu->upper_boundary_count = 0;
64862306a36Sopenharmony_ci	tu->lower_boundary_count = 0;
64962306a36Sopenharmony_ci	tu->i_upper_boundary_count = 0;
65062306a36Sopenharmony_ci	tu->i_lower_boundary_count = 0;
65162306a36Sopenharmony_ci	tu->valid_lower_boundary_link = 0;
65262306a36Sopenharmony_ci	tu->even_distribution_BF = 0;
65362306a36Sopenharmony_ci	tu->even_distribution_legacy = 0;
65462306a36Sopenharmony_ci	tu->even_distribution = 0;
65562306a36Sopenharmony_ci	tu->delay_start_time_fp = 0;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	tu->err_fp = drm_fixp_from_fraction(1000, 1);
65862306a36Sopenharmony_ci	tu->n_err_fp = 0;
65962306a36Sopenharmony_ci	tu->n_n_err_fp = 0;
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	tu->ratio = drm_fixp2int(tu->ratio_fp);
66262306a36Sopenharmony_ci	temp1_fp = drm_fixp_from_fraction(tu->nlanes, 1);
66362306a36Sopenharmony_ci	div64_u64_rem(tu->lwidth_fp, temp1_fp, &temp2_fp);
66462306a36Sopenharmony_ci	if (temp2_fp != 0 &&
66562306a36Sopenharmony_ci			!tu->ratio && tu->dsc_en == 0) {
66662306a36Sopenharmony_ci		tu->ratio_fp = drm_fixp_mul(tu->ratio_fp, RATIO_SCALE_fp);
66762306a36Sopenharmony_ci		tu->ratio = drm_fixp2int(tu->ratio_fp);
66862306a36Sopenharmony_ci		if (tu->ratio)
66962306a36Sopenharmony_ci			tu->ratio_fp = drm_fixp_from_fraction(1, 1);
67062306a36Sopenharmony_ci	}
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	if (tu->ratio > 1)
67362306a36Sopenharmony_ci		tu->ratio = 1;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	if (tu->ratio == 1)
67662306a36Sopenharmony_ci		goto tu_size_calc;
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	compare_result_1 = _tu_param_compare(tu->ratio_fp, const_p49_fp);
67962306a36Sopenharmony_ci	if (!compare_result_1 || compare_result_1 == 1)
68062306a36Sopenharmony_ci		compare_result_1 = 1;
68162306a36Sopenharmony_ci	else
68262306a36Sopenharmony_ci		compare_result_1 = 0;
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	compare_result_2 = _tu_param_compare(tu->ratio_fp, const_p56_fp);
68562306a36Sopenharmony_ci	if (!compare_result_2 || compare_result_2 == 2)
68662306a36Sopenharmony_ci		compare_result_2 = 1;
68762306a36Sopenharmony_ci	else
68862306a36Sopenharmony_ci		compare_result_2 = 0;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	if (tu->dsc_en && compare_result_1 && compare_result_2) {
69162306a36Sopenharmony_ci		HBLANK_MARGIN += 4;
69262306a36Sopenharmony_ci		drm_dbg_dp(ctrl->drm_dev,
69362306a36Sopenharmony_ci			"increase HBLANK_MARGIN to %d\n", HBLANK_MARGIN);
69462306a36Sopenharmony_ci	}
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_citu_size_calc:
69762306a36Sopenharmony_ci	for (tu->tu_size = 32; tu->tu_size <= 64; tu->tu_size++) {
69862306a36Sopenharmony_ci		temp1_fp = drm_fixp_from_fraction(tu->tu_size, 1);
69962306a36Sopenharmony_ci		temp2_fp = drm_fixp_mul(tu->ratio_fp, temp1_fp);
70062306a36Sopenharmony_ci		temp = drm_fixp2int_ceil(temp2_fp);
70162306a36Sopenharmony_ci		temp1_fp = drm_fixp_from_fraction(temp, 1);
70262306a36Sopenharmony_ci		tu->n_err_fp = temp1_fp - temp2_fp;
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci		if (tu->n_err_fp < tu->err_fp) {
70562306a36Sopenharmony_ci			tu->err_fp = tu->n_err_fp;
70662306a36Sopenharmony_ci			tu->tu_size_desired = tu->tu_size;
70762306a36Sopenharmony_ci		}
70862306a36Sopenharmony_ci	}
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	tu->tu_size_minus1 = tu->tu_size_desired - 1;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	temp1_fp = drm_fixp_from_fraction(tu->tu_size_desired, 1);
71362306a36Sopenharmony_ci	temp2_fp = drm_fixp_mul(tu->ratio_fp, temp1_fp);
71462306a36Sopenharmony_ci	tu->valid_boundary_link = drm_fixp2int_ceil(temp2_fp);
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	temp1_fp = drm_fixp_from_fraction(tu->bpp, 8);
71762306a36Sopenharmony_ci	temp2_fp = tu->lwidth_fp;
71862306a36Sopenharmony_ci	temp2_fp = drm_fixp_mul(temp2_fp, temp1_fp);
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	temp1_fp = drm_fixp_from_fraction(tu->valid_boundary_link, 1);
72162306a36Sopenharmony_ci	temp2_fp = drm_fixp_div(temp2_fp, temp1_fp);
72262306a36Sopenharmony_ci	tu->n_tus = drm_fixp2int(temp2_fp);
72362306a36Sopenharmony_ci	if ((temp2_fp & 0xFFFFFFFF) > 0xFFFFF000)
72462306a36Sopenharmony_ci		tu->n_tus += 1;
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	tu->even_distribution_legacy = tu->n_tus % tu->nlanes == 0 ? 1 : 0;
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	drm_dbg_dp(ctrl->drm_dev,
72962306a36Sopenharmony_ci			"n_sym = %d, num_of_tus = %d\n",
73062306a36Sopenharmony_ci			tu->valid_boundary_link, tu->n_tus);
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	temp1_fp = drm_fixp_from_fraction(tu->tu_size_desired, 1);
73362306a36Sopenharmony_ci	temp2_fp = drm_fixp_mul(tu->original_ratio_fp, temp1_fp);
73462306a36Sopenharmony_ci	temp1_fp = drm_fixp_from_fraction(tu->valid_boundary_link, 1);
73562306a36Sopenharmony_ci	temp2_fp = temp1_fp - temp2_fp;
73662306a36Sopenharmony_ci	temp1_fp = drm_fixp_from_fraction(tu->n_tus + 1, 1);
73762306a36Sopenharmony_ci	temp2_fp = drm_fixp_mul(temp1_fp, temp2_fp);
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	temp = drm_fixp2int(temp2_fp);
74062306a36Sopenharmony_ci	if (temp && temp2_fp)
74162306a36Sopenharmony_ci		tu->extra_bytes = drm_fixp2int_ceil(temp2_fp);
74262306a36Sopenharmony_ci	else
74362306a36Sopenharmony_ci		tu->extra_bytes = 0;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	temp1_fp = drm_fixp_from_fraction(tu->extra_bytes, 1);
74662306a36Sopenharmony_ci	temp2_fp = drm_fixp_from_fraction(8, tu->bpp);
74762306a36Sopenharmony_ci	temp1_fp = drm_fixp_mul(temp1_fp, temp2_fp);
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	if (temp && temp1_fp)
75062306a36Sopenharmony_ci		tu->extra_pclk_cycles = drm_fixp2int_ceil(temp1_fp);
75162306a36Sopenharmony_ci	else
75262306a36Sopenharmony_ci		tu->extra_pclk_cycles = drm_fixp2int(temp1_fp);
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	temp1_fp = drm_fixp_div(tu->lclk_fp, tu->pclk_fp);
75562306a36Sopenharmony_ci	temp2_fp = drm_fixp_from_fraction(tu->extra_pclk_cycles, 1);
75662306a36Sopenharmony_ci	temp1_fp = drm_fixp_mul(temp2_fp, temp1_fp);
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	if (temp1_fp)
75962306a36Sopenharmony_ci		tu->extra_pclk_cycles_in_link_clk = drm_fixp2int_ceil(temp1_fp);
76062306a36Sopenharmony_ci	else
76162306a36Sopenharmony_ci		tu->extra_pclk_cycles_in_link_clk = drm_fixp2int(temp1_fp);
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	tu->filler_size = tu->tu_size_desired - tu->valid_boundary_link;
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	temp1_fp = drm_fixp_from_fraction(tu->tu_size_desired, 1);
76662306a36Sopenharmony_ci	tu->ratio_by_tu_fp = drm_fixp_mul(tu->ratio_fp, temp1_fp);
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	tu->delay_start_link = tu->extra_pclk_cycles_in_link_clk +
76962306a36Sopenharmony_ci				tu->filler_size + tu->extra_buffer_margin;
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	tu->resulting_valid_fp =
77262306a36Sopenharmony_ci			drm_fixp_from_fraction(tu->valid_boundary_link, 1);
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	temp1_fp = drm_fixp_from_fraction(tu->tu_size_desired, 1);
77562306a36Sopenharmony_ci	temp2_fp = drm_fixp_div(tu->resulting_valid_fp, temp1_fp);
77662306a36Sopenharmony_ci	tu->TU_ratio_err_fp = temp2_fp - tu->original_ratio_fp;
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	temp1_fp = drm_fixp_from_fraction(HBLANK_MARGIN, 1);
77962306a36Sopenharmony_ci	temp1_fp = tu->hbp_relative_to_pclk_fp - temp1_fp;
78062306a36Sopenharmony_ci	tu->hbp_time_fp = drm_fixp_div(temp1_fp, tu->pclk_fp);
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	temp1_fp = drm_fixp_from_fraction(tu->delay_start_link, 1);
78362306a36Sopenharmony_ci	tu->delay_start_time_fp = drm_fixp_div(temp1_fp, tu->lclk_fp);
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	compare_result_1 = _tu_param_compare(tu->hbp_time_fp,
78662306a36Sopenharmony_ci					tu->delay_start_time_fp);
78762306a36Sopenharmony_ci	if (compare_result_1 == 2) /* if (hbp_time_fp < delay_start_time_fp) */
78862306a36Sopenharmony_ci		tu->min_hblank_violated = 1;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	tu->hactive_time_fp = drm_fixp_div(tu->lwidth_fp, tu->pclk_fp);
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	compare_result_2 = _tu_param_compare(tu->hactive_time_fp,
79362306a36Sopenharmony_ci						tu->delay_start_time_fp);
79462306a36Sopenharmony_ci	if (compare_result_2 == 2)
79562306a36Sopenharmony_ci		tu->min_hblank_violated = 1;
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	tu->delay_start_time_fp = 0;
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	/* brute force */
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	tu->delay_start_link_extra_pixclk = EXTRA_PIXCLK_CYCLE_DELAY;
80262306a36Sopenharmony_ci	tu->diff_abs_fp = tu->resulting_valid_fp - tu->ratio_by_tu_fp;
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	temp = drm_fixp2int(tu->diff_abs_fp);
80562306a36Sopenharmony_ci	if (!temp && tu->diff_abs_fp <= 0xffff)
80662306a36Sopenharmony_ci		tu->diff_abs_fp = 0;
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	/* if(diff_abs < 0) diff_abs *= -1 */
80962306a36Sopenharmony_ci	if (tu->diff_abs_fp < 0)
81062306a36Sopenharmony_ci		tu->diff_abs_fp = drm_fixp_mul(tu->diff_abs_fp, -1);
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	tu->boundary_mod_lower_err = 0;
81362306a36Sopenharmony_ci	if ((tu->diff_abs_fp != 0 &&
81462306a36Sopenharmony_ci			((tu->diff_abs_fp > BRUTE_FORCE_THRESHOLD_fp) ||
81562306a36Sopenharmony_ci			 (tu->even_distribution_legacy == 0) ||
81662306a36Sopenharmony_ci			 (DP_BRUTE_FORCE == 1))) ||
81762306a36Sopenharmony_ci			(tu->min_hblank_violated == 1)) {
81862306a36Sopenharmony_ci		do {
81962306a36Sopenharmony_ci			tu->err_fp = drm_fixp_from_fraction(1000, 1);
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci			temp1_fp = drm_fixp_div(tu->lclk_fp, tu->pclk_fp);
82262306a36Sopenharmony_ci			temp2_fp = drm_fixp_from_fraction(
82362306a36Sopenharmony_ci					tu->delay_start_link_extra_pixclk, 1);
82462306a36Sopenharmony_ci			temp1_fp = drm_fixp_mul(temp2_fp, temp1_fp);
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci			if (temp1_fp)
82762306a36Sopenharmony_ci				tu->extra_buffer_margin =
82862306a36Sopenharmony_ci					drm_fixp2int_ceil(temp1_fp);
82962306a36Sopenharmony_ci			else
83062306a36Sopenharmony_ci				tu->extra_buffer_margin = 0;
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci			temp1_fp = drm_fixp_from_fraction(tu->bpp, 8);
83362306a36Sopenharmony_ci			temp1_fp = drm_fixp_mul(tu->lwidth_fp, temp1_fp);
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci			if (temp1_fp)
83662306a36Sopenharmony_ci				tu->n_symbols = drm_fixp2int_ceil(temp1_fp);
83762306a36Sopenharmony_ci			else
83862306a36Sopenharmony_ci				tu->n_symbols = 0;
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci			for (tu->tu_size = 32; tu->tu_size <= 64; tu->tu_size++) {
84162306a36Sopenharmony_ci				for (tu->i_upper_boundary_count = 1;
84262306a36Sopenharmony_ci					tu->i_upper_boundary_count <= 15;
84362306a36Sopenharmony_ci					tu->i_upper_boundary_count++) {
84462306a36Sopenharmony_ci					for (tu->i_lower_boundary_count = 1;
84562306a36Sopenharmony_ci						tu->i_lower_boundary_count <= 15;
84662306a36Sopenharmony_ci						tu->i_lower_boundary_count++) {
84762306a36Sopenharmony_ci						_tu_valid_boundary_calc(tu);
84862306a36Sopenharmony_ci					}
84962306a36Sopenharmony_ci				}
85062306a36Sopenharmony_ci			}
85162306a36Sopenharmony_ci			tu->delay_start_link_extra_pixclk--;
85262306a36Sopenharmony_ci		} while (tu->boundary_moderation_en != true &&
85362306a36Sopenharmony_ci			tu->boundary_mod_lower_err == 1 &&
85462306a36Sopenharmony_ci			tu->delay_start_link_extra_pixclk != 0);
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci		if (tu->boundary_moderation_en == true) {
85762306a36Sopenharmony_ci			temp1_fp = drm_fixp_from_fraction(
85862306a36Sopenharmony_ci					(tu->upper_boundary_count *
85962306a36Sopenharmony_ci					tu->valid_boundary_link +
86062306a36Sopenharmony_ci					tu->lower_boundary_count *
86162306a36Sopenharmony_ci					(tu->valid_boundary_link - 1)), 1);
86262306a36Sopenharmony_ci			temp2_fp = drm_fixp_from_fraction(
86362306a36Sopenharmony_ci					(tu->upper_boundary_count +
86462306a36Sopenharmony_ci					tu->lower_boundary_count), 1);
86562306a36Sopenharmony_ci			tu->resulting_valid_fp =
86662306a36Sopenharmony_ci					drm_fixp_div(temp1_fp, temp2_fp);
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci			temp1_fp = drm_fixp_from_fraction(
86962306a36Sopenharmony_ci					tu->tu_size_desired, 1);
87062306a36Sopenharmony_ci			tu->ratio_by_tu_fp =
87162306a36Sopenharmony_ci				drm_fixp_mul(tu->original_ratio_fp, temp1_fp);
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci			tu->valid_lower_boundary_link =
87462306a36Sopenharmony_ci				tu->valid_boundary_link - 1;
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci			temp1_fp = drm_fixp_from_fraction(tu->bpp, 8);
87762306a36Sopenharmony_ci			temp1_fp = drm_fixp_mul(tu->lwidth_fp, temp1_fp);
87862306a36Sopenharmony_ci			temp2_fp = drm_fixp_div(temp1_fp,
87962306a36Sopenharmony_ci						tu->resulting_valid_fp);
88062306a36Sopenharmony_ci			tu->n_tus = drm_fixp2int(temp2_fp);
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci			tu->tu_size_minus1 = tu->tu_size_desired - 1;
88362306a36Sopenharmony_ci			tu->even_distribution_BF = 1;
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci			temp1_fp =
88662306a36Sopenharmony_ci				drm_fixp_from_fraction(tu->tu_size_desired, 1);
88762306a36Sopenharmony_ci			temp2_fp =
88862306a36Sopenharmony_ci				drm_fixp_div(tu->resulting_valid_fp, temp1_fp);
88962306a36Sopenharmony_ci			tu->TU_ratio_err_fp = temp2_fp - tu->original_ratio_fp;
89062306a36Sopenharmony_ci		}
89162306a36Sopenharmony_ci	}
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	temp2_fp = drm_fixp_mul(LCLK_FAST_SKEW_fp, tu->lwidth_fp);
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	if (temp2_fp)
89662306a36Sopenharmony_ci		temp = drm_fixp2int_ceil(temp2_fp);
89762306a36Sopenharmony_ci	else
89862306a36Sopenharmony_ci		temp = 0;
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	temp1_fp = drm_fixp_from_fraction(tu->nlanes, 1);
90162306a36Sopenharmony_ci	temp2_fp = drm_fixp_mul(tu->original_ratio_fp, temp1_fp);
90262306a36Sopenharmony_ci	temp1_fp = drm_fixp_from_fraction(tu->bpp, 8);
90362306a36Sopenharmony_ci	temp2_fp = drm_fixp_div(temp1_fp, temp2_fp);
90462306a36Sopenharmony_ci	temp1_fp = drm_fixp_from_fraction(temp, 1);
90562306a36Sopenharmony_ci	temp2_fp = drm_fixp_mul(temp1_fp, temp2_fp);
90662306a36Sopenharmony_ci	temp = drm_fixp2int(temp2_fp);
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	if (tu->async_en)
90962306a36Sopenharmony_ci		tu->delay_start_link += (int)temp;
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	temp1_fp = drm_fixp_from_fraction(tu->delay_start_link, 1);
91262306a36Sopenharmony_ci	tu->delay_start_time_fp = drm_fixp_div(temp1_fp, tu->lclk_fp);
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	/* OUTPUTS */
91562306a36Sopenharmony_ci	tu_table->valid_boundary_link       = tu->valid_boundary_link;
91662306a36Sopenharmony_ci	tu_table->delay_start_link          = tu->delay_start_link;
91762306a36Sopenharmony_ci	tu_table->boundary_moderation_en    = tu->boundary_moderation_en;
91862306a36Sopenharmony_ci	tu_table->valid_lower_boundary_link = tu->valid_lower_boundary_link;
91962306a36Sopenharmony_ci	tu_table->upper_boundary_count      = tu->upper_boundary_count;
92062306a36Sopenharmony_ci	tu_table->lower_boundary_count      = tu->lower_boundary_count;
92162306a36Sopenharmony_ci	tu_table->tu_size_minus1            = tu->tu_size_minus1;
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	drm_dbg_dp(ctrl->drm_dev, "TU: valid_boundary_link: %d\n",
92462306a36Sopenharmony_ci				tu_table->valid_boundary_link);
92562306a36Sopenharmony_ci	drm_dbg_dp(ctrl->drm_dev, "TU: delay_start_link: %d\n",
92662306a36Sopenharmony_ci				tu_table->delay_start_link);
92762306a36Sopenharmony_ci	drm_dbg_dp(ctrl->drm_dev, "TU: boundary_moderation_en: %d\n",
92862306a36Sopenharmony_ci			tu_table->boundary_moderation_en);
92962306a36Sopenharmony_ci	drm_dbg_dp(ctrl->drm_dev, "TU: valid_lower_boundary_link: %d\n",
93062306a36Sopenharmony_ci			tu_table->valid_lower_boundary_link);
93162306a36Sopenharmony_ci	drm_dbg_dp(ctrl->drm_dev, "TU: upper_boundary_count: %d\n",
93262306a36Sopenharmony_ci			tu_table->upper_boundary_count);
93362306a36Sopenharmony_ci	drm_dbg_dp(ctrl->drm_dev, "TU: lower_boundary_count: %d\n",
93462306a36Sopenharmony_ci			tu_table->lower_boundary_count);
93562306a36Sopenharmony_ci	drm_dbg_dp(ctrl->drm_dev, "TU: tu_size_minus1: %d\n",
93662306a36Sopenharmony_ci			tu_table->tu_size_minus1);
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	kfree(tu);
93962306a36Sopenharmony_ci}
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_cistatic void dp_ctrl_calc_tu_parameters(struct dp_ctrl_private *ctrl,
94262306a36Sopenharmony_ci		struct dp_vc_tu_mapping_table *tu_table)
94362306a36Sopenharmony_ci{
94462306a36Sopenharmony_ci	struct dp_tu_calc_input in;
94562306a36Sopenharmony_ci	struct drm_display_mode *drm_mode;
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci	drm_mode = &ctrl->panel->dp_mode.drm_mode;
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci	in.lclk = ctrl->link->link_params.rate / 1000;
95062306a36Sopenharmony_ci	in.pclk_khz = drm_mode->clock;
95162306a36Sopenharmony_ci	in.hactive = drm_mode->hdisplay;
95262306a36Sopenharmony_ci	in.hporch = drm_mode->htotal - drm_mode->hdisplay;
95362306a36Sopenharmony_ci	in.nlanes = ctrl->link->link_params.num_lanes;
95462306a36Sopenharmony_ci	in.bpp = ctrl->panel->dp_mode.bpp;
95562306a36Sopenharmony_ci	in.pixel_enc = 444;
95662306a36Sopenharmony_ci	in.dsc_en = 0;
95762306a36Sopenharmony_ci	in.async_en = 0;
95862306a36Sopenharmony_ci	in.fec_en = 0;
95962306a36Sopenharmony_ci	in.num_of_dsc_slices = 0;
96062306a36Sopenharmony_ci	in.compress_ratio = 100;
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	_dp_ctrl_calc_tu(ctrl, &in, tu_table);
96362306a36Sopenharmony_ci}
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_cistatic void dp_ctrl_setup_tr_unit(struct dp_ctrl_private *ctrl)
96662306a36Sopenharmony_ci{
96762306a36Sopenharmony_ci	u32 dp_tu = 0x0;
96862306a36Sopenharmony_ci	u32 valid_boundary = 0x0;
96962306a36Sopenharmony_ci	u32 valid_boundary2 = 0x0;
97062306a36Sopenharmony_ci	struct dp_vc_tu_mapping_table tu_calc_table;
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	dp_ctrl_calc_tu_parameters(ctrl, &tu_calc_table);
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	dp_tu |= tu_calc_table.tu_size_minus1;
97562306a36Sopenharmony_ci	valid_boundary |= tu_calc_table.valid_boundary_link;
97662306a36Sopenharmony_ci	valid_boundary |= (tu_calc_table.delay_start_link << 16);
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci	valid_boundary2 |= (tu_calc_table.valid_lower_boundary_link << 1);
97962306a36Sopenharmony_ci	valid_boundary2 |= (tu_calc_table.upper_boundary_count << 16);
98062306a36Sopenharmony_ci	valid_boundary2 |= (tu_calc_table.lower_boundary_count << 20);
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	if (tu_calc_table.boundary_moderation_en)
98362306a36Sopenharmony_ci		valid_boundary2 |= BIT(0);
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	pr_debug("dp_tu=0x%x, valid_boundary=0x%x, valid_boundary2=0x%x\n",
98662306a36Sopenharmony_ci			dp_tu, valid_boundary, valid_boundary2);
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	dp_catalog_ctrl_update_transfer_unit(ctrl->catalog,
98962306a36Sopenharmony_ci				dp_tu, valid_boundary, valid_boundary2);
99062306a36Sopenharmony_ci}
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_cistatic int dp_ctrl_wait4video_ready(struct dp_ctrl_private *ctrl)
99362306a36Sopenharmony_ci{
99462306a36Sopenharmony_ci	int ret = 0;
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	if (!wait_for_completion_timeout(&ctrl->video_comp,
99762306a36Sopenharmony_ci				WAIT_FOR_VIDEO_READY_TIMEOUT_JIFFIES)) {
99862306a36Sopenharmony_ci		DRM_ERROR("wait4video timedout\n");
99962306a36Sopenharmony_ci		ret = -ETIMEDOUT;
100062306a36Sopenharmony_ci	}
100162306a36Sopenharmony_ci	return ret;
100262306a36Sopenharmony_ci}
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_cistatic int dp_ctrl_update_vx_px(struct dp_ctrl_private *ctrl)
100562306a36Sopenharmony_ci{
100662306a36Sopenharmony_ci	struct dp_link *link = ctrl->link;
100762306a36Sopenharmony_ci	int ret = 0, lane, lane_cnt;
100862306a36Sopenharmony_ci	u8 buf[4];
100962306a36Sopenharmony_ci	u32 max_level_reached = 0;
101062306a36Sopenharmony_ci	u32 voltage_swing_level = link->phy_params.v_level;
101162306a36Sopenharmony_ci	u32 pre_emphasis_level = link->phy_params.p_level;
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci	drm_dbg_dp(ctrl->drm_dev,
101462306a36Sopenharmony_ci		"voltage level: %d emphasis level: %d\n",
101562306a36Sopenharmony_ci			voltage_swing_level, pre_emphasis_level);
101662306a36Sopenharmony_ci	ret = dp_catalog_ctrl_update_vx_px(ctrl->catalog,
101762306a36Sopenharmony_ci		voltage_swing_level, pre_emphasis_level);
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	if (ret)
102062306a36Sopenharmony_ci		return ret;
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	if (voltage_swing_level >= DP_TRAIN_VOLTAGE_SWING_MAX) {
102362306a36Sopenharmony_ci		drm_dbg_dp(ctrl->drm_dev,
102462306a36Sopenharmony_ci				"max. voltage swing level reached %d\n",
102562306a36Sopenharmony_ci				voltage_swing_level);
102662306a36Sopenharmony_ci		max_level_reached |= DP_TRAIN_MAX_SWING_REACHED;
102762306a36Sopenharmony_ci	}
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	if (pre_emphasis_level >= DP_TRAIN_PRE_EMPHASIS_MAX) {
103062306a36Sopenharmony_ci		drm_dbg_dp(ctrl->drm_dev,
103162306a36Sopenharmony_ci				"max. pre-emphasis level reached %d\n",
103262306a36Sopenharmony_ci				pre_emphasis_level);
103362306a36Sopenharmony_ci		max_level_reached  |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
103462306a36Sopenharmony_ci	}
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	pre_emphasis_level <<= DP_TRAIN_PRE_EMPHASIS_SHIFT;
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	lane_cnt = ctrl->link->link_params.num_lanes;
103962306a36Sopenharmony_ci	for (lane = 0; lane < lane_cnt; lane++)
104062306a36Sopenharmony_ci		buf[lane] = voltage_swing_level | pre_emphasis_level
104162306a36Sopenharmony_ci				| max_level_reached;
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	drm_dbg_dp(ctrl->drm_dev, "sink: p|v=0x%x\n",
104462306a36Sopenharmony_ci			voltage_swing_level | pre_emphasis_level);
104562306a36Sopenharmony_ci	ret = drm_dp_dpcd_write(ctrl->aux, DP_TRAINING_LANE0_SET,
104662306a36Sopenharmony_ci					buf, lane_cnt);
104762306a36Sopenharmony_ci	if (ret == lane_cnt)
104862306a36Sopenharmony_ci		ret = 0;
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci	return ret;
105162306a36Sopenharmony_ci}
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_cistatic bool dp_ctrl_train_pattern_set(struct dp_ctrl_private *ctrl,
105462306a36Sopenharmony_ci		u8 pattern)
105562306a36Sopenharmony_ci{
105662306a36Sopenharmony_ci	u8 buf;
105762306a36Sopenharmony_ci	int ret = 0;
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	drm_dbg_dp(ctrl->drm_dev, "sink: pattern=%x\n", pattern);
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci	buf = pattern;
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci	if (pattern && pattern != DP_TRAINING_PATTERN_4)
106462306a36Sopenharmony_ci		buf |= DP_LINK_SCRAMBLING_DISABLE;
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	ret = drm_dp_dpcd_writeb(ctrl->aux, DP_TRAINING_PATTERN_SET, buf);
106762306a36Sopenharmony_ci	return ret == 1;
106862306a36Sopenharmony_ci}
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_cistatic int dp_ctrl_read_link_status(struct dp_ctrl_private *ctrl,
107162306a36Sopenharmony_ci				    u8 *link_status)
107262306a36Sopenharmony_ci{
107362306a36Sopenharmony_ci	int ret = 0, len;
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci	len = drm_dp_dpcd_read_link_status(ctrl->aux, link_status);
107662306a36Sopenharmony_ci	if (len != DP_LINK_STATUS_SIZE) {
107762306a36Sopenharmony_ci		DRM_ERROR("DP link status read failed, err: %d\n", len);
107862306a36Sopenharmony_ci		ret = -EINVAL;
107962306a36Sopenharmony_ci	}
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	return ret;
108262306a36Sopenharmony_ci}
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_cistatic int dp_ctrl_link_train_1(struct dp_ctrl_private *ctrl,
108562306a36Sopenharmony_ci			int *training_step)
108662306a36Sopenharmony_ci{
108762306a36Sopenharmony_ci	int tries, old_v_level, ret = 0;
108862306a36Sopenharmony_ci	u8 link_status[DP_LINK_STATUS_SIZE];
108962306a36Sopenharmony_ci	int const maximum_retries = 4;
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	dp_catalog_ctrl_state_ctrl(ctrl->catalog, 0);
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci	*training_step = DP_TRAINING_1;
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	ret = dp_catalog_ctrl_set_pattern_state_bit(ctrl->catalog, 1);
109662306a36Sopenharmony_ci	if (ret)
109762306a36Sopenharmony_ci		return ret;
109862306a36Sopenharmony_ci	dp_ctrl_train_pattern_set(ctrl, DP_TRAINING_PATTERN_1 |
109962306a36Sopenharmony_ci		DP_LINK_SCRAMBLING_DISABLE);
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	ret = dp_ctrl_update_vx_px(ctrl);
110262306a36Sopenharmony_ci	if (ret)
110362306a36Sopenharmony_ci		return ret;
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci	tries = 0;
110662306a36Sopenharmony_ci	old_v_level = ctrl->link->phy_params.v_level;
110762306a36Sopenharmony_ci	for (tries = 0; tries < maximum_retries; tries++) {
110862306a36Sopenharmony_ci		drm_dp_link_train_clock_recovery_delay(ctrl->aux, ctrl->panel->dpcd);
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci		ret = dp_ctrl_read_link_status(ctrl, link_status);
111162306a36Sopenharmony_ci		if (ret)
111262306a36Sopenharmony_ci			return ret;
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci		if (drm_dp_clock_recovery_ok(link_status,
111562306a36Sopenharmony_ci			ctrl->link->link_params.num_lanes)) {
111662306a36Sopenharmony_ci			return 0;
111762306a36Sopenharmony_ci		}
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci		if (ctrl->link->phy_params.v_level >=
112062306a36Sopenharmony_ci			DP_TRAIN_VOLTAGE_SWING_MAX) {
112162306a36Sopenharmony_ci			DRM_ERROR_RATELIMITED("max v_level reached\n");
112262306a36Sopenharmony_ci			return -EAGAIN;
112362306a36Sopenharmony_ci		}
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci		if (old_v_level != ctrl->link->phy_params.v_level) {
112662306a36Sopenharmony_ci			tries = 0;
112762306a36Sopenharmony_ci			old_v_level = ctrl->link->phy_params.v_level;
112862306a36Sopenharmony_ci		}
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci		dp_link_adjust_levels(ctrl->link, link_status);
113162306a36Sopenharmony_ci		ret = dp_ctrl_update_vx_px(ctrl);
113262306a36Sopenharmony_ci		if (ret)
113362306a36Sopenharmony_ci			return ret;
113462306a36Sopenharmony_ci	}
113562306a36Sopenharmony_ci
113662306a36Sopenharmony_ci	DRM_ERROR("max tries reached\n");
113762306a36Sopenharmony_ci	return -ETIMEDOUT;
113862306a36Sopenharmony_ci}
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_cistatic int dp_ctrl_link_rate_down_shift(struct dp_ctrl_private *ctrl)
114162306a36Sopenharmony_ci{
114262306a36Sopenharmony_ci	int ret = 0;
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	switch (ctrl->link->link_params.rate) {
114562306a36Sopenharmony_ci	case 810000:
114662306a36Sopenharmony_ci		ctrl->link->link_params.rate = 540000;
114762306a36Sopenharmony_ci		break;
114862306a36Sopenharmony_ci	case 540000:
114962306a36Sopenharmony_ci		ctrl->link->link_params.rate = 270000;
115062306a36Sopenharmony_ci		break;
115162306a36Sopenharmony_ci	case 270000:
115262306a36Sopenharmony_ci		ctrl->link->link_params.rate = 162000;
115362306a36Sopenharmony_ci		break;
115462306a36Sopenharmony_ci	case 162000:
115562306a36Sopenharmony_ci	default:
115662306a36Sopenharmony_ci		ret = -EINVAL;
115762306a36Sopenharmony_ci		break;
115862306a36Sopenharmony_ci	}
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci	if (!ret) {
116162306a36Sopenharmony_ci		drm_dbg_dp(ctrl->drm_dev, "new rate=0x%x\n",
116262306a36Sopenharmony_ci				ctrl->link->link_params.rate);
116362306a36Sopenharmony_ci	}
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci	return ret;
116662306a36Sopenharmony_ci}
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_cistatic int dp_ctrl_link_lane_down_shift(struct dp_ctrl_private *ctrl)
116962306a36Sopenharmony_ci{
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	if (ctrl->link->link_params.num_lanes == 1)
117262306a36Sopenharmony_ci		return -1;
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci	ctrl->link->link_params.num_lanes /= 2;
117562306a36Sopenharmony_ci	ctrl->link->link_params.rate = ctrl->panel->link_info.rate;
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	ctrl->link->phy_params.p_level = 0;
117862306a36Sopenharmony_ci	ctrl->link->phy_params.v_level = 0;
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	return 0;
118162306a36Sopenharmony_ci}
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_cistatic void dp_ctrl_clear_training_pattern(struct dp_ctrl_private *ctrl)
118462306a36Sopenharmony_ci{
118562306a36Sopenharmony_ci	dp_ctrl_train_pattern_set(ctrl, DP_TRAINING_PATTERN_DISABLE);
118662306a36Sopenharmony_ci	drm_dp_link_train_channel_eq_delay(ctrl->aux, ctrl->panel->dpcd);
118762306a36Sopenharmony_ci}
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_cistatic int dp_ctrl_link_train_2(struct dp_ctrl_private *ctrl,
119062306a36Sopenharmony_ci			int *training_step)
119162306a36Sopenharmony_ci{
119262306a36Sopenharmony_ci	int tries = 0, ret = 0;
119362306a36Sopenharmony_ci	u8 pattern;
119462306a36Sopenharmony_ci	u32 state_ctrl_bit;
119562306a36Sopenharmony_ci	int const maximum_retries = 5;
119662306a36Sopenharmony_ci	u8 link_status[DP_LINK_STATUS_SIZE];
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci	dp_catalog_ctrl_state_ctrl(ctrl->catalog, 0);
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci	*training_step = DP_TRAINING_2;
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci	if (drm_dp_tps4_supported(ctrl->panel->dpcd)) {
120362306a36Sopenharmony_ci		pattern = DP_TRAINING_PATTERN_4;
120462306a36Sopenharmony_ci		state_ctrl_bit = 4;
120562306a36Sopenharmony_ci	} else if (drm_dp_tps3_supported(ctrl->panel->dpcd)) {
120662306a36Sopenharmony_ci		pattern = DP_TRAINING_PATTERN_3;
120762306a36Sopenharmony_ci		state_ctrl_bit = 3;
120862306a36Sopenharmony_ci	} else {
120962306a36Sopenharmony_ci		pattern = DP_TRAINING_PATTERN_2;
121062306a36Sopenharmony_ci		state_ctrl_bit = 2;
121162306a36Sopenharmony_ci	}
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci	ret = dp_catalog_ctrl_set_pattern_state_bit(ctrl->catalog, state_ctrl_bit);
121462306a36Sopenharmony_ci	if (ret)
121562306a36Sopenharmony_ci		return ret;
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci	dp_ctrl_train_pattern_set(ctrl, pattern);
121862306a36Sopenharmony_ci
121962306a36Sopenharmony_ci	for (tries = 0; tries <= maximum_retries; tries++) {
122062306a36Sopenharmony_ci		drm_dp_link_train_channel_eq_delay(ctrl->aux, ctrl->panel->dpcd);
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci		ret = dp_ctrl_read_link_status(ctrl, link_status);
122362306a36Sopenharmony_ci		if (ret)
122462306a36Sopenharmony_ci			return ret;
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ci		if (drm_dp_channel_eq_ok(link_status,
122762306a36Sopenharmony_ci			ctrl->link->link_params.num_lanes)) {
122862306a36Sopenharmony_ci			return 0;
122962306a36Sopenharmony_ci		}
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci		dp_link_adjust_levels(ctrl->link, link_status);
123262306a36Sopenharmony_ci		ret = dp_ctrl_update_vx_px(ctrl);
123362306a36Sopenharmony_ci		if (ret)
123462306a36Sopenharmony_ci			return ret;
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci	}
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ci	return -ETIMEDOUT;
123962306a36Sopenharmony_ci}
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_cistatic int dp_ctrl_link_train(struct dp_ctrl_private *ctrl,
124262306a36Sopenharmony_ci			int *training_step)
124362306a36Sopenharmony_ci{
124462306a36Sopenharmony_ci	int ret = 0;
124562306a36Sopenharmony_ci	const u8 *dpcd = ctrl->panel->dpcd;
124662306a36Sopenharmony_ci	u8 encoding[] = { 0, DP_SET_ANSI_8B10B };
124762306a36Sopenharmony_ci	u8 assr;
124862306a36Sopenharmony_ci	struct dp_link_info link_info = {0};
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_ci	dp_ctrl_config_ctrl(ctrl);
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci	link_info.num_lanes = ctrl->link->link_params.num_lanes;
125362306a36Sopenharmony_ci	link_info.rate = ctrl->link->link_params.rate;
125462306a36Sopenharmony_ci	link_info.capabilities = DP_LINK_CAP_ENHANCED_FRAMING;
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	dp_aux_link_configure(ctrl->aux, &link_info);
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci	if (drm_dp_max_downspread(dpcd))
125962306a36Sopenharmony_ci		encoding[0] |= DP_SPREAD_AMP_0_5;
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci	/* config DOWNSPREAD_CTRL and MAIN_LINK_CHANNEL_CODING_SET */
126262306a36Sopenharmony_ci	drm_dp_dpcd_write(ctrl->aux, DP_DOWNSPREAD_CTRL, encoding, 2);
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_ci	if (drm_dp_alternate_scrambler_reset_cap(dpcd)) {
126562306a36Sopenharmony_ci		assr = DP_ALTERNATE_SCRAMBLER_RESET_ENABLE;
126662306a36Sopenharmony_ci		drm_dp_dpcd_write(ctrl->aux, DP_EDP_CONFIGURATION_SET,
126762306a36Sopenharmony_ci				&assr, 1);
126862306a36Sopenharmony_ci	}
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci	ret = dp_ctrl_link_train_1(ctrl, training_step);
127162306a36Sopenharmony_ci	if (ret) {
127262306a36Sopenharmony_ci		DRM_ERROR("link training #1 failed. ret=%d\n", ret);
127362306a36Sopenharmony_ci		goto end;
127462306a36Sopenharmony_ci	}
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci	/* print success info as this is a result of user initiated action */
127762306a36Sopenharmony_ci	drm_dbg_dp(ctrl->drm_dev, "link training #1 successful\n");
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci	ret = dp_ctrl_link_train_2(ctrl, training_step);
128062306a36Sopenharmony_ci	if (ret) {
128162306a36Sopenharmony_ci		DRM_ERROR("link training #2 failed. ret=%d\n", ret);
128262306a36Sopenharmony_ci		goto end;
128362306a36Sopenharmony_ci	}
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ci	/* print success info as this is a result of user initiated action */
128662306a36Sopenharmony_ci	drm_dbg_dp(ctrl->drm_dev, "link training #2 successful\n");
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ciend:
128962306a36Sopenharmony_ci	dp_catalog_ctrl_state_ctrl(ctrl->catalog, 0);
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci	return ret;
129262306a36Sopenharmony_ci}
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_cistatic int dp_ctrl_setup_main_link(struct dp_ctrl_private *ctrl,
129562306a36Sopenharmony_ci			int *training_step)
129662306a36Sopenharmony_ci{
129762306a36Sopenharmony_ci	int ret = 0;
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci	dp_catalog_ctrl_mainlink_ctrl(ctrl->catalog, true);
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci	if (ctrl->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN)
130262306a36Sopenharmony_ci		return ret;
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci	/*
130562306a36Sopenharmony_ci	 * As part of previous calls, DP controller state might have
130662306a36Sopenharmony_ci	 * transitioned to PUSH_IDLE. In order to start transmitting
130762306a36Sopenharmony_ci	 * a link training pattern, we have to first do soft reset.
130862306a36Sopenharmony_ci	 */
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_ci	ret = dp_ctrl_link_train(ctrl, training_step);
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci	return ret;
131362306a36Sopenharmony_ci}
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_cistatic void dp_ctrl_set_clock_rate(struct dp_ctrl_private *ctrl,
131662306a36Sopenharmony_ci			enum dp_pm_type module, char *name, unsigned long rate)
131762306a36Sopenharmony_ci{
131862306a36Sopenharmony_ci	u32 num = ctrl->parser->mp[module].num_clk;
131962306a36Sopenharmony_ci	struct clk_bulk_data *cfg = ctrl->parser->mp[module].clocks;
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci	while (num && strcmp(cfg->id, name)) {
132262306a36Sopenharmony_ci		num--;
132362306a36Sopenharmony_ci		cfg++;
132462306a36Sopenharmony_ci	}
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	drm_dbg_dp(ctrl->drm_dev, "setting rate=%lu on clk=%s\n",
132762306a36Sopenharmony_ci						rate, name);
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	if (num)
133062306a36Sopenharmony_ci		clk_set_rate(cfg->clk, rate);
133162306a36Sopenharmony_ci	else
133262306a36Sopenharmony_ci		DRM_ERROR("%s clock doesn't exit to set rate %lu\n",
133362306a36Sopenharmony_ci				name, rate);
133462306a36Sopenharmony_ci}
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_cistatic int dp_ctrl_enable_mainlink_clocks(struct dp_ctrl_private *ctrl)
133762306a36Sopenharmony_ci{
133862306a36Sopenharmony_ci	int ret = 0;
133962306a36Sopenharmony_ci	struct dp_io *dp_io = &ctrl->parser->io;
134062306a36Sopenharmony_ci	struct phy *phy = dp_io->phy;
134162306a36Sopenharmony_ci	struct phy_configure_opts_dp *opts_dp = &dp_io->phy_opts.dp;
134262306a36Sopenharmony_ci	const u8 *dpcd = ctrl->panel->dpcd;
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci	opts_dp->lanes = ctrl->link->link_params.num_lanes;
134562306a36Sopenharmony_ci	opts_dp->link_rate = ctrl->link->link_params.rate / 100;
134662306a36Sopenharmony_ci	opts_dp->ssc = drm_dp_max_downspread(dpcd);
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci	phy_configure(phy, &dp_io->phy_opts);
134962306a36Sopenharmony_ci	phy_power_on(phy);
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_ci	dev_pm_opp_set_rate(ctrl->dev, ctrl->link->link_params.rate * 1000);
135262306a36Sopenharmony_ci	ret = dp_power_clk_enable(ctrl->power, DP_CTRL_PM, true);
135362306a36Sopenharmony_ci	if (ret)
135462306a36Sopenharmony_ci		DRM_ERROR("Unable to start link clocks. ret=%d\n", ret);
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci	drm_dbg_dp(ctrl->drm_dev, "link rate=%d\n", ctrl->link->link_params.rate);
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci	return ret;
135962306a36Sopenharmony_ci}
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_civoid dp_ctrl_reset_irq_ctrl(struct dp_ctrl *dp_ctrl, bool enable)
136262306a36Sopenharmony_ci{
136362306a36Sopenharmony_ci	struct dp_ctrl_private *ctrl;
136462306a36Sopenharmony_ci
136562306a36Sopenharmony_ci	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_ci	dp_catalog_ctrl_reset(ctrl->catalog);
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci	/*
137062306a36Sopenharmony_ci	 * all dp controller programmable registers will not
137162306a36Sopenharmony_ci	 * be reset to default value after DP_SW_RESET
137262306a36Sopenharmony_ci	 * therefore interrupt mask bits have to be updated
137362306a36Sopenharmony_ci	 * to enable/disable interrupts
137462306a36Sopenharmony_ci	 */
137562306a36Sopenharmony_ci	dp_catalog_ctrl_enable_irq(ctrl->catalog, enable);
137662306a36Sopenharmony_ci}
137762306a36Sopenharmony_ci
137862306a36Sopenharmony_civoid dp_ctrl_config_psr(struct dp_ctrl *dp_ctrl)
137962306a36Sopenharmony_ci{
138062306a36Sopenharmony_ci	u8 cfg;
138162306a36Sopenharmony_ci	struct dp_ctrl_private *ctrl = container_of(dp_ctrl,
138262306a36Sopenharmony_ci			struct dp_ctrl_private, dp_ctrl);
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci	if (!ctrl->panel->psr_cap.version)
138562306a36Sopenharmony_ci		return;
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci	dp_catalog_ctrl_config_psr(ctrl->catalog);
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci	cfg = DP_PSR_ENABLE;
139062306a36Sopenharmony_ci	drm_dp_dpcd_write(ctrl->aux, DP_PSR_EN_CFG, &cfg, 1);
139162306a36Sopenharmony_ci}
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_civoid dp_ctrl_set_psr(struct dp_ctrl *dp_ctrl, bool enter)
139462306a36Sopenharmony_ci{
139562306a36Sopenharmony_ci	struct dp_ctrl_private *ctrl = container_of(dp_ctrl,
139662306a36Sopenharmony_ci			struct dp_ctrl_private, dp_ctrl);
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ci	if (!ctrl->panel->psr_cap.version)
139962306a36Sopenharmony_ci		return;
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci	/*
140262306a36Sopenharmony_ci	 * When entering PSR,
140362306a36Sopenharmony_ci	 * 1. Send PSR enter SDP and wait for the PSR_UPDATE_INT
140462306a36Sopenharmony_ci	 * 2. Turn off video
140562306a36Sopenharmony_ci	 * 3. Disable the mainlink
140662306a36Sopenharmony_ci	 *
140762306a36Sopenharmony_ci	 * When exiting PSR,
140862306a36Sopenharmony_ci	 * 1. Enable the mainlink
140962306a36Sopenharmony_ci	 * 2. Send the PSR exit SDP
141062306a36Sopenharmony_ci	 */
141162306a36Sopenharmony_ci	if (enter) {
141262306a36Sopenharmony_ci		reinit_completion(&ctrl->psr_op_comp);
141362306a36Sopenharmony_ci		dp_catalog_ctrl_set_psr(ctrl->catalog, true);
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ci		if (!wait_for_completion_timeout(&ctrl->psr_op_comp,
141662306a36Sopenharmony_ci			PSR_OPERATION_COMPLETION_TIMEOUT_JIFFIES)) {
141762306a36Sopenharmony_ci			DRM_ERROR("PSR_ENTRY timedout\n");
141862306a36Sopenharmony_ci			dp_catalog_ctrl_set_psr(ctrl->catalog, false);
141962306a36Sopenharmony_ci			return;
142062306a36Sopenharmony_ci		}
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_ci		dp_ctrl_push_idle(dp_ctrl);
142362306a36Sopenharmony_ci		dp_catalog_ctrl_state_ctrl(ctrl->catalog, 0);
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_ci		dp_catalog_ctrl_psr_mainlink_enable(ctrl->catalog, false);
142662306a36Sopenharmony_ci	} else {
142762306a36Sopenharmony_ci		dp_catalog_ctrl_psr_mainlink_enable(ctrl->catalog, true);
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci		dp_catalog_ctrl_set_psr(ctrl->catalog, false);
143062306a36Sopenharmony_ci		dp_catalog_ctrl_state_ctrl(ctrl->catalog, DP_STATE_CTRL_SEND_VIDEO);
143162306a36Sopenharmony_ci		dp_ctrl_wait4video_ready(ctrl);
143262306a36Sopenharmony_ci		dp_catalog_ctrl_state_ctrl(ctrl->catalog, 0);
143362306a36Sopenharmony_ci	}
143462306a36Sopenharmony_ci}
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_civoid dp_ctrl_phy_init(struct dp_ctrl *dp_ctrl)
143762306a36Sopenharmony_ci{
143862306a36Sopenharmony_ci	struct dp_ctrl_private *ctrl;
143962306a36Sopenharmony_ci	struct dp_io *dp_io;
144062306a36Sopenharmony_ci	struct phy *phy;
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
144362306a36Sopenharmony_ci	dp_io = &ctrl->parser->io;
144462306a36Sopenharmony_ci	phy = dp_io->phy;
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_ci	dp_catalog_ctrl_phy_reset(ctrl->catalog);
144762306a36Sopenharmony_ci	phy_init(phy);
144862306a36Sopenharmony_ci
144962306a36Sopenharmony_ci	drm_dbg_dp(ctrl->drm_dev, "phy=%p init=%d power_on=%d\n",
145062306a36Sopenharmony_ci			phy, phy->init_count, phy->power_count);
145162306a36Sopenharmony_ci}
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_civoid dp_ctrl_phy_exit(struct dp_ctrl *dp_ctrl)
145462306a36Sopenharmony_ci{
145562306a36Sopenharmony_ci	struct dp_ctrl_private *ctrl;
145662306a36Sopenharmony_ci	struct dp_io *dp_io;
145762306a36Sopenharmony_ci	struct phy *phy;
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_ci	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
146062306a36Sopenharmony_ci	dp_io = &ctrl->parser->io;
146162306a36Sopenharmony_ci	phy = dp_io->phy;
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci	dp_catalog_ctrl_phy_reset(ctrl->catalog);
146462306a36Sopenharmony_ci	phy_exit(phy);
146562306a36Sopenharmony_ci	drm_dbg_dp(ctrl->drm_dev, "phy=%p init=%d power_on=%d\n",
146662306a36Sopenharmony_ci			phy, phy->init_count, phy->power_count);
146762306a36Sopenharmony_ci}
146862306a36Sopenharmony_ci
146962306a36Sopenharmony_cistatic bool dp_ctrl_use_fixed_nvid(struct dp_ctrl_private *ctrl)
147062306a36Sopenharmony_ci{
147162306a36Sopenharmony_ci	const u8 *dpcd = ctrl->panel->dpcd;
147262306a36Sopenharmony_ci
147362306a36Sopenharmony_ci	/*
147462306a36Sopenharmony_ci	 * For better interop experience, used a fixed NVID=0x8000
147562306a36Sopenharmony_ci	 * whenever connected to a VGA dongle downstream.
147662306a36Sopenharmony_ci	 */
147762306a36Sopenharmony_ci	if (drm_dp_is_branch(dpcd))
147862306a36Sopenharmony_ci		return (drm_dp_has_quirk(&ctrl->panel->desc,
147962306a36Sopenharmony_ci					 DP_DPCD_QUIRK_CONSTANT_N));
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci	return false;
148262306a36Sopenharmony_ci}
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_cistatic int dp_ctrl_reinitialize_mainlink(struct dp_ctrl_private *ctrl)
148562306a36Sopenharmony_ci{
148662306a36Sopenharmony_ci	int ret = 0;
148762306a36Sopenharmony_ci	struct dp_io *dp_io = &ctrl->parser->io;
148862306a36Sopenharmony_ci	struct phy *phy = dp_io->phy;
148962306a36Sopenharmony_ci	struct phy_configure_opts_dp *opts_dp = &dp_io->phy_opts.dp;
149062306a36Sopenharmony_ci
149162306a36Sopenharmony_ci	dp_catalog_ctrl_mainlink_ctrl(ctrl->catalog, false);
149262306a36Sopenharmony_ci	opts_dp->lanes = ctrl->link->link_params.num_lanes;
149362306a36Sopenharmony_ci	phy_configure(phy, &dp_io->phy_opts);
149462306a36Sopenharmony_ci	/*
149562306a36Sopenharmony_ci	 * Disable and re-enable the mainlink clock since the
149662306a36Sopenharmony_ci	 * link clock might have been adjusted as part of the
149762306a36Sopenharmony_ci	 * link maintenance.
149862306a36Sopenharmony_ci	 */
149962306a36Sopenharmony_ci	dev_pm_opp_set_rate(ctrl->dev, 0);
150062306a36Sopenharmony_ci	ret = dp_power_clk_enable(ctrl->power, DP_CTRL_PM, false);
150162306a36Sopenharmony_ci	if (ret) {
150262306a36Sopenharmony_ci		DRM_ERROR("Failed to disable clocks. ret=%d\n", ret);
150362306a36Sopenharmony_ci		return ret;
150462306a36Sopenharmony_ci	}
150562306a36Sopenharmony_ci	phy_power_off(phy);
150662306a36Sopenharmony_ci	/* hw recommended delay before re-enabling clocks */
150762306a36Sopenharmony_ci	msleep(20);
150862306a36Sopenharmony_ci
150962306a36Sopenharmony_ci	ret = dp_ctrl_enable_mainlink_clocks(ctrl);
151062306a36Sopenharmony_ci	if (ret) {
151162306a36Sopenharmony_ci		DRM_ERROR("Failed to enable mainlink clks. ret=%d\n", ret);
151262306a36Sopenharmony_ci		return ret;
151362306a36Sopenharmony_ci	}
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_ci	return ret;
151662306a36Sopenharmony_ci}
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_cistatic int dp_ctrl_deinitialize_mainlink(struct dp_ctrl_private *ctrl)
151962306a36Sopenharmony_ci{
152062306a36Sopenharmony_ci	struct dp_io *dp_io;
152162306a36Sopenharmony_ci	struct phy *phy;
152262306a36Sopenharmony_ci	int ret;
152362306a36Sopenharmony_ci
152462306a36Sopenharmony_ci	dp_io = &ctrl->parser->io;
152562306a36Sopenharmony_ci	phy = dp_io->phy;
152662306a36Sopenharmony_ci
152762306a36Sopenharmony_ci	dp_catalog_ctrl_mainlink_ctrl(ctrl->catalog, false);
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci	dp_catalog_ctrl_reset(ctrl->catalog);
153062306a36Sopenharmony_ci
153162306a36Sopenharmony_ci	dev_pm_opp_set_rate(ctrl->dev, 0);
153262306a36Sopenharmony_ci	ret = dp_power_clk_enable(ctrl->power, DP_CTRL_PM, false);
153362306a36Sopenharmony_ci	if (ret) {
153462306a36Sopenharmony_ci		DRM_ERROR("Failed to disable link clocks. ret=%d\n", ret);
153562306a36Sopenharmony_ci	}
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_ci	phy_power_off(phy);
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_ci	/* aux channel down, reinit phy */
154062306a36Sopenharmony_ci	phy_exit(phy);
154162306a36Sopenharmony_ci	phy_init(phy);
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ci	drm_dbg_dp(ctrl->drm_dev, "phy=%p init=%d power_on=%d\n",
154462306a36Sopenharmony_ci			phy, phy->init_count, phy->power_count);
154562306a36Sopenharmony_ci	return 0;
154662306a36Sopenharmony_ci}
154762306a36Sopenharmony_ci
154862306a36Sopenharmony_cistatic int dp_ctrl_link_maintenance(struct dp_ctrl_private *ctrl)
154962306a36Sopenharmony_ci{
155062306a36Sopenharmony_ci	int ret = 0;
155162306a36Sopenharmony_ci	int training_step = DP_TRAINING_NONE;
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_ci	dp_ctrl_push_idle(&ctrl->dp_ctrl);
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ci	ctrl->link->phy_params.p_level = 0;
155662306a36Sopenharmony_ci	ctrl->link->phy_params.v_level = 0;
155762306a36Sopenharmony_ci
155862306a36Sopenharmony_ci	ret = dp_ctrl_setup_main_link(ctrl, &training_step);
155962306a36Sopenharmony_ci	if (ret)
156062306a36Sopenharmony_ci		goto end;
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ci	dp_ctrl_clear_training_pattern(ctrl);
156362306a36Sopenharmony_ci
156462306a36Sopenharmony_ci	dp_catalog_ctrl_state_ctrl(ctrl->catalog, DP_STATE_CTRL_SEND_VIDEO);
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_ci	ret = dp_ctrl_wait4video_ready(ctrl);
156762306a36Sopenharmony_ciend:
156862306a36Sopenharmony_ci	return ret;
156962306a36Sopenharmony_ci}
157062306a36Sopenharmony_ci
157162306a36Sopenharmony_cistatic bool dp_ctrl_send_phy_test_pattern(struct dp_ctrl_private *ctrl)
157262306a36Sopenharmony_ci{
157362306a36Sopenharmony_ci	bool success = false;
157462306a36Sopenharmony_ci	u32 pattern_sent = 0x0;
157562306a36Sopenharmony_ci	u32 pattern_requested = ctrl->link->phy_params.phy_test_pattern_sel;
157662306a36Sopenharmony_ci
157762306a36Sopenharmony_ci	drm_dbg_dp(ctrl->drm_dev, "request: 0x%x\n", pattern_requested);
157862306a36Sopenharmony_ci
157962306a36Sopenharmony_ci	if (dp_catalog_ctrl_update_vx_px(ctrl->catalog,
158062306a36Sopenharmony_ci			ctrl->link->phy_params.v_level,
158162306a36Sopenharmony_ci			ctrl->link->phy_params.p_level)) {
158262306a36Sopenharmony_ci		DRM_ERROR("Failed to set v/p levels\n");
158362306a36Sopenharmony_ci		return false;
158462306a36Sopenharmony_ci	}
158562306a36Sopenharmony_ci	dp_catalog_ctrl_send_phy_pattern(ctrl->catalog, pattern_requested);
158662306a36Sopenharmony_ci	dp_ctrl_update_vx_px(ctrl);
158762306a36Sopenharmony_ci	dp_link_send_test_response(ctrl->link);
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci	pattern_sent = dp_catalog_ctrl_read_phy_pattern(ctrl->catalog);
159062306a36Sopenharmony_ci
159162306a36Sopenharmony_ci	switch (pattern_sent) {
159262306a36Sopenharmony_ci	case MR_LINK_TRAINING1:
159362306a36Sopenharmony_ci		success = (pattern_requested ==
159462306a36Sopenharmony_ci				DP_PHY_TEST_PATTERN_D10_2);
159562306a36Sopenharmony_ci		break;
159662306a36Sopenharmony_ci	case MR_LINK_SYMBOL_ERM:
159762306a36Sopenharmony_ci		success = ((pattern_requested ==
159862306a36Sopenharmony_ci			DP_PHY_TEST_PATTERN_ERROR_COUNT) ||
159962306a36Sopenharmony_ci				(pattern_requested ==
160062306a36Sopenharmony_ci				DP_PHY_TEST_PATTERN_CP2520));
160162306a36Sopenharmony_ci		break;
160262306a36Sopenharmony_ci	case MR_LINK_PRBS7:
160362306a36Sopenharmony_ci		success = (pattern_requested ==
160462306a36Sopenharmony_ci				DP_PHY_TEST_PATTERN_PRBS7);
160562306a36Sopenharmony_ci		break;
160662306a36Sopenharmony_ci	case MR_LINK_CUSTOM80:
160762306a36Sopenharmony_ci		success = (pattern_requested ==
160862306a36Sopenharmony_ci				DP_PHY_TEST_PATTERN_80BIT_CUSTOM);
160962306a36Sopenharmony_ci		break;
161062306a36Sopenharmony_ci	case MR_LINK_TRAINING4:
161162306a36Sopenharmony_ci		success = (pattern_requested ==
161262306a36Sopenharmony_ci				DP_PHY_TEST_PATTERN_SEL_MASK);
161362306a36Sopenharmony_ci		break;
161462306a36Sopenharmony_ci	default:
161562306a36Sopenharmony_ci		success = false;
161662306a36Sopenharmony_ci	}
161762306a36Sopenharmony_ci
161862306a36Sopenharmony_ci	drm_dbg_dp(ctrl->drm_dev, "%s: test->0x%x\n",
161962306a36Sopenharmony_ci		success ? "success" : "failed", pattern_requested);
162062306a36Sopenharmony_ci	return success;
162162306a36Sopenharmony_ci}
162262306a36Sopenharmony_ci
162362306a36Sopenharmony_cistatic int dp_ctrl_process_phy_test_request(struct dp_ctrl_private *ctrl)
162462306a36Sopenharmony_ci{
162562306a36Sopenharmony_ci	int ret;
162662306a36Sopenharmony_ci	unsigned long pixel_rate;
162762306a36Sopenharmony_ci
162862306a36Sopenharmony_ci	if (!ctrl->link->phy_params.phy_test_pattern_sel) {
162962306a36Sopenharmony_ci		drm_dbg_dp(ctrl->drm_dev,
163062306a36Sopenharmony_ci			"no test pattern selected by sink\n");
163162306a36Sopenharmony_ci		return 0;
163262306a36Sopenharmony_ci	}
163362306a36Sopenharmony_ci
163462306a36Sopenharmony_ci	/*
163562306a36Sopenharmony_ci	 * The global reset will need DP link related clocks to be
163662306a36Sopenharmony_ci	 * running. Add the global reset just before disabling the
163762306a36Sopenharmony_ci	 * link clocks and core clocks.
163862306a36Sopenharmony_ci	 */
163962306a36Sopenharmony_ci	ret = dp_ctrl_off(&ctrl->dp_ctrl);
164062306a36Sopenharmony_ci	if (ret) {
164162306a36Sopenharmony_ci		DRM_ERROR("failed to disable DP controller\n");
164262306a36Sopenharmony_ci		return ret;
164362306a36Sopenharmony_ci	}
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_ci	ret = dp_ctrl_on_link(&ctrl->dp_ctrl);
164662306a36Sopenharmony_ci	if (ret) {
164762306a36Sopenharmony_ci		DRM_ERROR("failed to enable DP link controller\n");
164862306a36Sopenharmony_ci		return ret;
164962306a36Sopenharmony_ci	}
165062306a36Sopenharmony_ci
165162306a36Sopenharmony_ci	pixel_rate = ctrl->panel->dp_mode.drm_mode.clock;
165262306a36Sopenharmony_ci	dp_ctrl_set_clock_rate(ctrl, DP_STREAM_PM, "stream_pixel", pixel_rate * 1000);
165362306a36Sopenharmony_ci
165462306a36Sopenharmony_ci	ret = dp_power_clk_enable(ctrl->power, DP_STREAM_PM, true);
165562306a36Sopenharmony_ci	if (ret) {
165662306a36Sopenharmony_ci		DRM_ERROR("Failed to start pixel clocks. ret=%d\n", ret);
165762306a36Sopenharmony_ci		return ret;
165862306a36Sopenharmony_ci	}
165962306a36Sopenharmony_ci
166062306a36Sopenharmony_ci	dp_ctrl_send_phy_test_pattern(ctrl);
166162306a36Sopenharmony_ci
166262306a36Sopenharmony_ci	return 0;
166362306a36Sopenharmony_ci}
166462306a36Sopenharmony_ci
166562306a36Sopenharmony_civoid dp_ctrl_handle_sink_request(struct dp_ctrl *dp_ctrl)
166662306a36Sopenharmony_ci{
166762306a36Sopenharmony_ci	struct dp_ctrl_private *ctrl;
166862306a36Sopenharmony_ci	u32 sink_request = 0x0;
166962306a36Sopenharmony_ci
167062306a36Sopenharmony_ci	if (!dp_ctrl) {
167162306a36Sopenharmony_ci		DRM_ERROR("invalid input\n");
167262306a36Sopenharmony_ci		return;
167362306a36Sopenharmony_ci	}
167462306a36Sopenharmony_ci
167562306a36Sopenharmony_ci	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
167662306a36Sopenharmony_ci	sink_request = ctrl->link->sink_request;
167762306a36Sopenharmony_ci
167862306a36Sopenharmony_ci	if (sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) {
167962306a36Sopenharmony_ci		drm_dbg_dp(ctrl->drm_dev, "PHY_TEST_PATTERN request\n");
168062306a36Sopenharmony_ci		if (dp_ctrl_process_phy_test_request(ctrl)) {
168162306a36Sopenharmony_ci			DRM_ERROR("process phy_test_req failed\n");
168262306a36Sopenharmony_ci			return;
168362306a36Sopenharmony_ci		}
168462306a36Sopenharmony_ci	}
168562306a36Sopenharmony_ci
168662306a36Sopenharmony_ci	if (sink_request & DP_LINK_STATUS_UPDATED) {
168762306a36Sopenharmony_ci		if (dp_ctrl_link_maintenance(ctrl)) {
168862306a36Sopenharmony_ci			DRM_ERROR("LM failed: TEST_LINK_TRAINING\n");
168962306a36Sopenharmony_ci			return;
169062306a36Sopenharmony_ci		}
169162306a36Sopenharmony_ci	}
169262306a36Sopenharmony_ci
169362306a36Sopenharmony_ci	if (sink_request & DP_TEST_LINK_TRAINING) {
169462306a36Sopenharmony_ci		dp_link_send_test_response(ctrl->link);
169562306a36Sopenharmony_ci		if (dp_ctrl_link_maintenance(ctrl)) {
169662306a36Sopenharmony_ci			DRM_ERROR("LM failed: TEST_LINK_TRAINING\n");
169762306a36Sopenharmony_ci			return;
169862306a36Sopenharmony_ci		}
169962306a36Sopenharmony_ci	}
170062306a36Sopenharmony_ci}
170162306a36Sopenharmony_ci
170262306a36Sopenharmony_cistatic bool dp_ctrl_clock_recovery_any_ok(
170362306a36Sopenharmony_ci			const u8 link_status[DP_LINK_STATUS_SIZE],
170462306a36Sopenharmony_ci			int lane_count)
170562306a36Sopenharmony_ci{
170662306a36Sopenharmony_ci	int reduced_cnt;
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_ci	if (lane_count <= 1)
170962306a36Sopenharmony_ci		return false;
171062306a36Sopenharmony_ci
171162306a36Sopenharmony_ci	/*
171262306a36Sopenharmony_ci	 * only interested in the lane number after reduced
171362306a36Sopenharmony_ci	 * lane_count = 4, then only interested in 2 lanes
171462306a36Sopenharmony_ci	 * lane_count = 2, then only interested in 1 lane
171562306a36Sopenharmony_ci	 */
171662306a36Sopenharmony_ci	reduced_cnt = lane_count >> 1;
171762306a36Sopenharmony_ci
171862306a36Sopenharmony_ci	return drm_dp_clock_recovery_ok(link_status, reduced_cnt);
171962306a36Sopenharmony_ci}
172062306a36Sopenharmony_ci
172162306a36Sopenharmony_cistatic bool dp_ctrl_channel_eq_ok(struct dp_ctrl_private *ctrl)
172262306a36Sopenharmony_ci{
172362306a36Sopenharmony_ci	u8 link_status[DP_LINK_STATUS_SIZE];
172462306a36Sopenharmony_ci	int num_lanes = ctrl->link->link_params.num_lanes;
172562306a36Sopenharmony_ci
172662306a36Sopenharmony_ci	dp_ctrl_read_link_status(ctrl, link_status);
172762306a36Sopenharmony_ci
172862306a36Sopenharmony_ci	return drm_dp_channel_eq_ok(link_status, num_lanes);
172962306a36Sopenharmony_ci}
173062306a36Sopenharmony_ci
173162306a36Sopenharmony_ciint dp_ctrl_on_link(struct dp_ctrl *dp_ctrl)
173262306a36Sopenharmony_ci{
173362306a36Sopenharmony_ci	int rc = 0;
173462306a36Sopenharmony_ci	struct dp_ctrl_private *ctrl;
173562306a36Sopenharmony_ci	u32 rate;
173662306a36Sopenharmony_ci	int link_train_max_retries = 5;
173762306a36Sopenharmony_ci	u32 const phy_cts_pixel_clk_khz = 148500;
173862306a36Sopenharmony_ci	u8 link_status[DP_LINK_STATUS_SIZE];
173962306a36Sopenharmony_ci	unsigned int training_step;
174062306a36Sopenharmony_ci	unsigned long pixel_rate;
174162306a36Sopenharmony_ci
174262306a36Sopenharmony_ci	if (!dp_ctrl)
174362306a36Sopenharmony_ci		return -EINVAL;
174462306a36Sopenharmony_ci
174562306a36Sopenharmony_ci	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
174662306a36Sopenharmony_ci
174762306a36Sopenharmony_ci	rate = ctrl->panel->link_info.rate;
174862306a36Sopenharmony_ci	pixel_rate = ctrl->panel->dp_mode.drm_mode.clock;
174962306a36Sopenharmony_ci
175062306a36Sopenharmony_ci	dp_power_clk_enable(ctrl->power, DP_CORE_PM, true);
175162306a36Sopenharmony_ci
175262306a36Sopenharmony_ci	if (ctrl->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) {
175362306a36Sopenharmony_ci		drm_dbg_dp(ctrl->drm_dev,
175462306a36Sopenharmony_ci				"using phy test link parameters\n");
175562306a36Sopenharmony_ci		if (!pixel_rate)
175662306a36Sopenharmony_ci			pixel_rate = phy_cts_pixel_clk_khz;
175762306a36Sopenharmony_ci	} else {
175862306a36Sopenharmony_ci		ctrl->link->link_params.rate = rate;
175962306a36Sopenharmony_ci		ctrl->link->link_params.num_lanes =
176062306a36Sopenharmony_ci			ctrl->panel->link_info.num_lanes;
176162306a36Sopenharmony_ci	}
176262306a36Sopenharmony_ci
176362306a36Sopenharmony_ci	drm_dbg_dp(ctrl->drm_dev, "rate=%d, num_lanes=%d, pixel_rate=%lu\n",
176462306a36Sopenharmony_ci		ctrl->link->link_params.rate, ctrl->link->link_params.num_lanes,
176562306a36Sopenharmony_ci		pixel_rate);
176662306a36Sopenharmony_ci
176762306a36Sopenharmony_ci	rc = dp_ctrl_enable_mainlink_clocks(ctrl);
176862306a36Sopenharmony_ci	if (rc)
176962306a36Sopenharmony_ci		return rc;
177062306a36Sopenharmony_ci
177162306a36Sopenharmony_ci	while (--link_train_max_retries) {
177262306a36Sopenharmony_ci		training_step = DP_TRAINING_NONE;
177362306a36Sopenharmony_ci		rc = dp_ctrl_setup_main_link(ctrl, &training_step);
177462306a36Sopenharmony_ci		if (rc == 0) {
177562306a36Sopenharmony_ci			/* training completed successfully */
177662306a36Sopenharmony_ci			break;
177762306a36Sopenharmony_ci		} else if (training_step == DP_TRAINING_1) {
177862306a36Sopenharmony_ci			/* link train_1 failed */
177962306a36Sopenharmony_ci			if (!dp_catalog_link_is_connected(ctrl->catalog))
178062306a36Sopenharmony_ci				break;
178162306a36Sopenharmony_ci
178262306a36Sopenharmony_ci			dp_ctrl_read_link_status(ctrl, link_status);
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_ci			rc = dp_ctrl_link_rate_down_shift(ctrl);
178562306a36Sopenharmony_ci			if (rc < 0) { /* already in RBR = 1.6G */
178662306a36Sopenharmony_ci				if (dp_ctrl_clock_recovery_any_ok(link_status,
178762306a36Sopenharmony_ci					ctrl->link->link_params.num_lanes)) {
178862306a36Sopenharmony_ci					/*
178962306a36Sopenharmony_ci					 * some lanes are ready,
179062306a36Sopenharmony_ci					 * reduce lane number
179162306a36Sopenharmony_ci					 */
179262306a36Sopenharmony_ci					rc = dp_ctrl_link_lane_down_shift(ctrl);
179362306a36Sopenharmony_ci					if (rc < 0) { /* lane == 1 already */
179462306a36Sopenharmony_ci						/* end with failure */
179562306a36Sopenharmony_ci						break;
179662306a36Sopenharmony_ci					}
179762306a36Sopenharmony_ci				} else {
179862306a36Sopenharmony_ci					/* end with failure */
179962306a36Sopenharmony_ci					break; /* lane == 1 already */
180062306a36Sopenharmony_ci				}
180162306a36Sopenharmony_ci			}
180262306a36Sopenharmony_ci		} else if (training_step == DP_TRAINING_2) {
180362306a36Sopenharmony_ci			/* link train_2 failed */
180462306a36Sopenharmony_ci			if (!dp_catalog_link_is_connected(ctrl->catalog))
180562306a36Sopenharmony_ci				break;
180662306a36Sopenharmony_ci
180762306a36Sopenharmony_ci			dp_ctrl_read_link_status(ctrl, link_status);
180862306a36Sopenharmony_ci
180962306a36Sopenharmony_ci			if (!drm_dp_clock_recovery_ok(link_status,
181062306a36Sopenharmony_ci					ctrl->link->link_params.num_lanes))
181162306a36Sopenharmony_ci				rc = dp_ctrl_link_rate_down_shift(ctrl);
181262306a36Sopenharmony_ci			else
181362306a36Sopenharmony_ci				rc = dp_ctrl_link_lane_down_shift(ctrl);
181462306a36Sopenharmony_ci
181562306a36Sopenharmony_ci			if (rc < 0) {
181662306a36Sopenharmony_ci				/* end with failure */
181762306a36Sopenharmony_ci				break; /* lane == 1 already */
181862306a36Sopenharmony_ci			}
181962306a36Sopenharmony_ci
182062306a36Sopenharmony_ci			/* stop link training before start re training  */
182162306a36Sopenharmony_ci			dp_ctrl_clear_training_pattern(ctrl);
182262306a36Sopenharmony_ci		}
182362306a36Sopenharmony_ci
182462306a36Sopenharmony_ci		rc = dp_ctrl_reinitialize_mainlink(ctrl);
182562306a36Sopenharmony_ci		if (rc) {
182662306a36Sopenharmony_ci			DRM_ERROR("Failed to reinitialize mainlink. rc=%d\n", rc);
182762306a36Sopenharmony_ci			break;
182862306a36Sopenharmony_ci		}
182962306a36Sopenharmony_ci	}
183062306a36Sopenharmony_ci
183162306a36Sopenharmony_ci	if (ctrl->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN)
183262306a36Sopenharmony_ci		return rc;
183362306a36Sopenharmony_ci
183462306a36Sopenharmony_ci	if (rc == 0) {  /* link train successfully */
183562306a36Sopenharmony_ci		/*
183662306a36Sopenharmony_ci		 * do not stop train pattern here
183762306a36Sopenharmony_ci		 * stop link training at on_stream
183862306a36Sopenharmony_ci		 * to pass compliance test
183962306a36Sopenharmony_ci		 */
184062306a36Sopenharmony_ci	} else  {
184162306a36Sopenharmony_ci		/*
184262306a36Sopenharmony_ci		 * link training failed
184362306a36Sopenharmony_ci		 * end txing train pattern here
184462306a36Sopenharmony_ci		 */
184562306a36Sopenharmony_ci		dp_ctrl_clear_training_pattern(ctrl);
184662306a36Sopenharmony_ci
184762306a36Sopenharmony_ci		dp_ctrl_deinitialize_mainlink(ctrl);
184862306a36Sopenharmony_ci		rc = -ECONNRESET;
184962306a36Sopenharmony_ci	}
185062306a36Sopenharmony_ci
185162306a36Sopenharmony_ci	return rc;
185262306a36Sopenharmony_ci}
185362306a36Sopenharmony_ci
185462306a36Sopenharmony_cistatic int dp_ctrl_link_retrain(struct dp_ctrl_private *ctrl)
185562306a36Sopenharmony_ci{
185662306a36Sopenharmony_ci	int training_step = DP_TRAINING_NONE;
185762306a36Sopenharmony_ci
185862306a36Sopenharmony_ci	return dp_ctrl_setup_main_link(ctrl, &training_step);
185962306a36Sopenharmony_ci}
186062306a36Sopenharmony_ci
186162306a36Sopenharmony_ciint dp_ctrl_on_stream(struct dp_ctrl *dp_ctrl, bool force_link_train)
186262306a36Sopenharmony_ci{
186362306a36Sopenharmony_ci	int ret = 0;
186462306a36Sopenharmony_ci	bool mainlink_ready = false;
186562306a36Sopenharmony_ci	struct dp_ctrl_private *ctrl;
186662306a36Sopenharmony_ci	unsigned long pixel_rate;
186762306a36Sopenharmony_ci	unsigned long pixel_rate_orig;
186862306a36Sopenharmony_ci
186962306a36Sopenharmony_ci	if (!dp_ctrl)
187062306a36Sopenharmony_ci		return -EINVAL;
187162306a36Sopenharmony_ci
187262306a36Sopenharmony_ci	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
187362306a36Sopenharmony_ci
187462306a36Sopenharmony_ci	pixel_rate = pixel_rate_orig = ctrl->panel->dp_mode.drm_mode.clock;
187562306a36Sopenharmony_ci
187662306a36Sopenharmony_ci	if (dp_ctrl->wide_bus_en)
187762306a36Sopenharmony_ci		pixel_rate >>= 1;
187862306a36Sopenharmony_ci
187962306a36Sopenharmony_ci	drm_dbg_dp(ctrl->drm_dev, "rate=%d, num_lanes=%d, pixel_rate=%lu\n",
188062306a36Sopenharmony_ci		ctrl->link->link_params.rate,
188162306a36Sopenharmony_ci		ctrl->link->link_params.num_lanes, pixel_rate);
188262306a36Sopenharmony_ci
188362306a36Sopenharmony_ci	if (!dp_power_clk_status(ctrl->power, DP_CTRL_PM)) { /* link clk is off */
188462306a36Sopenharmony_ci		ret = dp_ctrl_enable_mainlink_clocks(ctrl);
188562306a36Sopenharmony_ci		if (ret) {
188662306a36Sopenharmony_ci			DRM_ERROR("Failed to start link clocks. ret=%d\n", ret);
188762306a36Sopenharmony_ci			goto end;
188862306a36Sopenharmony_ci		}
188962306a36Sopenharmony_ci	}
189062306a36Sopenharmony_ci
189162306a36Sopenharmony_ci	dp_ctrl_set_clock_rate(ctrl, DP_STREAM_PM, "stream_pixel", pixel_rate * 1000);
189262306a36Sopenharmony_ci
189362306a36Sopenharmony_ci	ret = dp_power_clk_enable(ctrl->power, DP_STREAM_PM, true);
189462306a36Sopenharmony_ci	if (ret) {
189562306a36Sopenharmony_ci		DRM_ERROR("Unable to start pixel clocks. ret=%d\n", ret);
189662306a36Sopenharmony_ci		goto end;
189762306a36Sopenharmony_ci	}
189862306a36Sopenharmony_ci
189962306a36Sopenharmony_ci	if (force_link_train || !dp_ctrl_channel_eq_ok(ctrl))
190062306a36Sopenharmony_ci		dp_ctrl_link_retrain(ctrl);
190162306a36Sopenharmony_ci
190262306a36Sopenharmony_ci	/* stop txing train pattern to end link training */
190362306a36Sopenharmony_ci	dp_ctrl_clear_training_pattern(ctrl);
190462306a36Sopenharmony_ci
190562306a36Sopenharmony_ci	/*
190662306a36Sopenharmony_ci	 * Set up transfer unit values and set controller state to send
190762306a36Sopenharmony_ci	 * video.
190862306a36Sopenharmony_ci	 */
190962306a36Sopenharmony_ci	reinit_completion(&ctrl->video_comp);
191062306a36Sopenharmony_ci
191162306a36Sopenharmony_ci	dp_ctrl_configure_source_params(ctrl);
191262306a36Sopenharmony_ci
191362306a36Sopenharmony_ci	dp_catalog_ctrl_config_msa(ctrl->catalog,
191462306a36Sopenharmony_ci		ctrl->link->link_params.rate,
191562306a36Sopenharmony_ci		pixel_rate_orig, dp_ctrl_use_fixed_nvid(ctrl));
191662306a36Sopenharmony_ci
191762306a36Sopenharmony_ci	dp_ctrl_setup_tr_unit(ctrl);
191862306a36Sopenharmony_ci
191962306a36Sopenharmony_ci	dp_catalog_ctrl_state_ctrl(ctrl->catalog, DP_STATE_CTRL_SEND_VIDEO);
192062306a36Sopenharmony_ci
192162306a36Sopenharmony_ci	ret = dp_ctrl_wait4video_ready(ctrl);
192262306a36Sopenharmony_ci	if (ret)
192362306a36Sopenharmony_ci		return ret;
192462306a36Sopenharmony_ci
192562306a36Sopenharmony_ci	mainlink_ready = dp_catalog_ctrl_mainlink_ready(ctrl->catalog);
192662306a36Sopenharmony_ci	drm_dbg_dp(ctrl->drm_dev,
192762306a36Sopenharmony_ci		"mainlink %s\n", mainlink_ready ? "READY" : "NOT READY");
192862306a36Sopenharmony_ci
192962306a36Sopenharmony_ciend:
193062306a36Sopenharmony_ci	return ret;
193162306a36Sopenharmony_ci}
193262306a36Sopenharmony_ci
193362306a36Sopenharmony_ciint dp_ctrl_off_link_stream(struct dp_ctrl *dp_ctrl)
193462306a36Sopenharmony_ci{
193562306a36Sopenharmony_ci	struct dp_ctrl_private *ctrl;
193662306a36Sopenharmony_ci	struct dp_io *dp_io;
193762306a36Sopenharmony_ci	struct phy *phy;
193862306a36Sopenharmony_ci	int ret;
193962306a36Sopenharmony_ci
194062306a36Sopenharmony_ci	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
194162306a36Sopenharmony_ci	dp_io = &ctrl->parser->io;
194262306a36Sopenharmony_ci	phy = dp_io->phy;
194362306a36Sopenharmony_ci
194462306a36Sopenharmony_ci	/* set dongle to D3 (power off) mode */
194562306a36Sopenharmony_ci	dp_link_psm_config(ctrl->link, &ctrl->panel->link_info, true);
194662306a36Sopenharmony_ci
194762306a36Sopenharmony_ci	dp_catalog_ctrl_mainlink_ctrl(ctrl->catalog, false);
194862306a36Sopenharmony_ci
194962306a36Sopenharmony_ci	if (dp_power_clk_status(ctrl->power, DP_STREAM_PM)) {
195062306a36Sopenharmony_ci		ret = dp_power_clk_enable(ctrl->power, DP_STREAM_PM, false);
195162306a36Sopenharmony_ci		if (ret) {
195262306a36Sopenharmony_ci			DRM_ERROR("Failed to disable pclk. ret=%d\n", ret);
195362306a36Sopenharmony_ci			return ret;
195462306a36Sopenharmony_ci		}
195562306a36Sopenharmony_ci	}
195662306a36Sopenharmony_ci
195762306a36Sopenharmony_ci	dev_pm_opp_set_rate(ctrl->dev, 0);
195862306a36Sopenharmony_ci	ret = dp_power_clk_enable(ctrl->power, DP_CTRL_PM, false);
195962306a36Sopenharmony_ci	if (ret) {
196062306a36Sopenharmony_ci		DRM_ERROR("Failed to disable link clocks. ret=%d\n", ret);
196162306a36Sopenharmony_ci		return ret;
196262306a36Sopenharmony_ci	}
196362306a36Sopenharmony_ci
196462306a36Sopenharmony_ci	phy_power_off(phy);
196562306a36Sopenharmony_ci
196662306a36Sopenharmony_ci	/* aux channel down, reinit phy */
196762306a36Sopenharmony_ci	phy_exit(phy);
196862306a36Sopenharmony_ci	phy_init(phy);
196962306a36Sopenharmony_ci
197062306a36Sopenharmony_ci	drm_dbg_dp(ctrl->drm_dev, "phy=%p init=%d power_on=%d\n",
197162306a36Sopenharmony_ci			phy, phy->init_count, phy->power_count);
197262306a36Sopenharmony_ci	return ret;
197362306a36Sopenharmony_ci}
197462306a36Sopenharmony_ci
197562306a36Sopenharmony_ciint dp_ctrl_off_link(struct dp_ctrl *dp_ctrl)
197662306a36Sopenharmony_ci{
197762306a36Sopenharmony_ci	struct dp_ctrl_private *ctrl;
197862306a36Sopenharmony_ci	struct dp_io *dp_io;
197962306a36Sopenharmony_ci	struct phy *phy;
198062306a36Sopenharmony_ci	int ret;
198162306a36Sopenharmony_ci
198262306a36Sopenharmony_ci	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
198362306a36Sopenharmony_ci	dp_io = &ctrl->parser->io;
198462306a36Sopenharmony_ci	phy = dp_io->phy;
198562306a36Sopenharmony_ci
198662306a36Sopenharmony_ci	dp_catalog_ctrl_mainlink_ctrl(ctrl->catalog, false);
198762306a36Sopenharmony_ci
198862306a36Sopenharmony_ci	ret = dp_power_clk_enable(ctrl->power, DP_CTRL_PM, false);
198962306a36Sopenharmony_ci	if (ret) {
199062306a36Sopenharmony_ci		DRM_ERROR("Failed to disable link clocks. ret=%d\n", ret);
199162306a36Sopenharmony_ci	}
199262306a36Sopenharmony_ci
199362306a36Sopenharmony_ci	DRM_DEBUG_DP("Before, phy=%p init_count=%d power_on=%d\n",
199462306a36Sopenharmony_ci		phy, phy->init_count, phy->power_count);
199562306a36Sopenharmony_ci
199662306a36Sopenharmony_ci	phy_power_off(phy);
199762306a36Sopenharmony_ci
199862306a36Sopenharmony_ci	DRM_DEBUG_DP("After, phy=%p init_count=%d power_on=%d\n",
199962306a36Sopenharmony_ci		phy, phy->init_count, phy->power_count);
200062306a36Sopenharmony_ci
200162306a36Sopenharmony_ci	return ret;
200262306a36Sopenharmony_ci}
200362306a36Sopenharmony_ci
200462306a36Sopenharmony_ciint dp_ctrl_off(struct dp_ctrl *dp_ctrl)
200562306a36Sopenharmony_ci{
200662306a36Sopenharmony_ci	struct dp_ctrl_private *ctrl;
200762306a36Sopenharmony_ci	struct dp_io *dp_io;
200862306a36Sopenharmony_ci	struct phy *phy;
200962306a36Sopenharmony_ci	int ret = 0;
201062306a36Sopenharmony_ci
201162306a36Sopenharmony_ci	if (!dp_ctrl)
201262306a36Sopenharmony_ci		return -EINVAL;
201362306a36Sopenharmony_ci
201462306a36Sopenharmony_ci	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
201562306a36Sopenharmony_ci	dp_io = &ctrl->parser->io;
201662306a36Sopenharmony_ci	phy = dp_io->phy;
201762306a36Sopenharmony_ci
201862306a36Sopenharmony_ci	dp_catalog_ctrl_mainlink_ctrl(ctrl->catalog, false);
201962306a36Sopenharmony_ci
202062306a36Sopenharmony_ci	dp_catalog_ctrl_reset(ctrl->catalog);
202162306a36Sopenharmony_ci
202262306a36Sopenharmony_ci	ret = dp_power_clk_enable(ctrl->power, DP_STREAM_PM, false);
202362306a36Sopenharmony_ci	if (ret)
202462306a36Sopenharmony_ci		DRM_ERROR("Failed to disable pixel clocks. ret=%d\n", ret);
202562306a36Sopenharmony_ci
202662306a36Sopenharmony_ci	dev_pm_opp_set_rate(ctrl->dev, 0);
202762306a36Sopenharmony_ci	ret = dp_power_clk_enable(ctrl->power, DP_CTRL_PM, false);
202862306a36Sopenharmony_ci	if (ret) {
202962306a36Sopenharmony_ci		DRM_ERROR("Failed to disable link clocks. ret=%d\n", ret);
203062306a36Sopenharmony_ci	}
203162306a36Sopenharmony_ci
203262306a36Sopenharmony_ci	phy_power_off(phy);
203362306a36Sopenharmony_ci	drm_dbg_dp(ctrl->drm_dev, "phy=%p init=%d power_on=%d\n",
203462306a36Sopenharmony_ci			phy, phy->init_count, phy->power_count);
203562306a36Sopenharmony_ci
203662306a36Sopenharmony_ci	return ret;
203762306a36Sopenharmony_ci}
203862306a36Sopenharmony_ci
203962306a36Sopenharmony_ciirqreturn_t dp_ctrl_isr(struct dp_ctrl *dp_ctrl)
204062306a36Sopenharmony_ci{
204162306a36Sopenharmony_ci	struct dp_ctrl_private *ctrl;
204262306a36Sopenharmony_ci	u32 isr;
204362306a36Sopenharmony_ci	irqreturn_t ret = IRQ_NONE;
204462306a36Sopenharmony_ci
204562306a36Sopenharmony_ci	if (!dp_ctrl)
204662306a36Sopenharmony_ci		return IRQ_NONE;
204762306a36Sopenharmony_ci
204862306a36Sopenharmony_ci	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
204962306a36Sopenharmony_ci
205062306a36Sopenharmony_ci	if (ctrl->panel->psr_cap.version) {
205162306a36Sopenharmony_ci		isr = dp_catalog_ctrl_read_psr_interrupt_status(ctrl->catalog);
205262306a36Sopenharmony_ci
205362306a36Sopenharmony_ci		if (isr)
205462306a36Sopenharmony_ci			complete(&ctrl->psr_op_comp);
205562306a36Sopenharmony_ci
205662306a36Sopenharmony_ci		if (isr & PSR_EXIT_INT)
205762306a36Sopenharmony_ci			drm_dbg_dp(ctrl->drm_dev, "PSR exit done\n");
205862306a36Sopenharmony_ci
205962306a36Sopenharmony_ci		if (isr & PSR_UPDATE_INT)
206062306a36Sopenharmony_ci			drm_dbg_dp(ctrl->drm_dev, "PSR frame update done\n");
206162306a36Sopenharmony_ci
206262306a36Sopenharmony_ci		if (isr & PSR_CAPTURE_INT)
206362306a36Sopenharmony_ci			drm_dbg_dp(ctrl->drm_dev, "PSR frame capture done\n");
206462306a36Sopenharmony_ci	}
206562306a36Sopenharmony_ci
206662306a36Sopenharmony_ci	isr = dp_catalog_ctrl_get_interrupt(ctrl->catalog);
206762306a36Sopenharmony_ci
206862306a36Sopenharmony_ci
206962306a36Sopenharmony_ci	if (isr & DP_CTRL_INTR_READY_FOR_VIDEO) {
207062306a36Sopenharmony_ci		drm_dbg_dp(ctrl->drm_dev, "dp_video_ready\n");
207162306a36Sopenharmony_ci		complete(&ctrl->video_comp);
207262306a36Sopenharmony_ci		ret = IRQ_HANDLED;
207362306a36Sopenharmony_ci	}
207462306a36Sopenharmony_ci
207562306a36Sopenharmony_ci	if (isr & DP_CTRL_INTR_IDLE_PATTERN_SENT) {
207662306a36Sopenharmony_ci		drm_dbg_dp(ctrl->drm_dev, "idle_patterns_sent\n");
207762306a36Sopenharmony_ci		complete(&ctrl->idle_comp);
207862306a36Sopenharmony_ci		ret = IRQ_HANDLED;
207962306a36Sopenharmony_ci	}
208062306a36Sopenharmony_ci
208162306a36Sopenharmony_ci	return ret;
208262306a36Sopenharmony_ci}
208362306a36Sopenharmony_ci
208462306a36Sopenharmony_cistruct dp_ctrl *dp_ctrl_get(struct device *dev, struct dp_link *link,
208562306a36Sopenharmony_ci			struct dp_panel *panel,	struct drm_dp_aux *aux,
208662306a36Sopenharmony_ci			struct dp_power *power, struct dp_catalog *catalog,
208762306a36Sopenharmony_ci			struct dp_parser *parser)
208862306a36Sopenharmony_ci{
208962306a36Sopenharmony_ci	struct dp_ctrl_private *ctrl;
209062306a36Sopenharmony_ci	int ret;
209162306a36Sopenharmony_ci
209262306a36Sopenharmony_ci	if (!dev || !panel || !aux ||
209362306a36Sopenharmony_ci	    !link || !catalog) {
209462306a36Sopenharmony_ci		DRM_ERROR("invalid input\n");
209562306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
209662306a36Sopenharmony_ci	}
209762306a36Sopenharmony_ci
209862306a36Sopenharmony_ci	ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
209962306a36Sopenharmony_ci	if (!ctrl) {
210062306a36Sopenharmony_ci		DRM_ERROR("Mem allocation failure\n");
210162306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
210262306a36Sopenharmony_ci	}
210362306a36Sopenharmony_ci
210462306a36Sopenharmony_ci	ret = devm_pm_opp_set_clkname(dev, "ctrl_link");
210562306a36Sopenharmony_ci	if (ret) {
210662306a36Sopenharmony_ci		dev_err(dev, "invalid DP OPP table in device tree\n");
210762306a36Sopenharmony_ci		/* caller do PTR_ERR(opp_table) */
210862306a36Sopenharmony_ci		return (struct dp_ctrl *)ERR_PTR(ret);
210962306a36Sopenharmony_ci	}
211062306a36Sopenharmony_ci
211162306a36Sopenharmony_ci	/* OPP table is optional */
211262306a36Sopenharmony_ci	ret = devm_pm_opp_of_add_table(dev);
211362306a36Sopenharmony_ci	if (ret)
211462306a36Sopenharmony_ci		dev_err(dev, "failed to add DP OPP table\n");
211562306a36Sopenharmony_ci
211662306a36Sopenharmony_ci	init_completion(&ctrl->idle_comp);
211762306a36Sopenharmony_ci	init_completion(&ctrl->psr_op_comp);
211862306a36Sopenharmony_ci	init_completion(&ctrl->video_comp);
211962306a36Sopenharmony_ci
212062306a36Sopenharmony_ci	/* in parameters */
212162306a36Sopenharmony_ci	ctrl->parser   = parser;
212262306a36Sopenharmony_ci	ctrl->panel    = panel;
212362306a36Sopenharmony_ci	ctrl->power    = power;
212462306a36Sopenharmony_ci	ctrl->aux      = aux;
212562306a36Sopenharmony_ci	ctrl->link     = link;
212662306a36Sopenharmony_ci	ctrl->catalog  = catalog;
212762306a36Sopenharmony_ci	ctrl->dev      = dev;
212862306a36Sopenharmony_ci
212962306a36Sopenharmony_ci	return &ctrl->dp_ctrl;
213062306a36Sopenharmony_ci}
2131