162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright 2007-8 Advanced Micro Devices, Inc.
362306a36Sopenharmony_ci * Copyright 2008 Red Hat Inc.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
662306a36Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
762306a36Sopenharmony_ci * to deal in the Software without restriction, including without limitation
862306a36Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
962306a36Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
1062306a36Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * The above copyright notice and this permission notice shall be included in
1362306a36Sopenharmony_ci * all copies or substantial portions of the Software.
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1662306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1762306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
1862306a36Sopenharmony_ci * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
1962306a36Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
2062306a36Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
2162306a36Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE.
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci * Authors: Dave Airlie
2462306a36Sopenharmony_ci *          Alex Deucher
2562306a36Sopenharmony_ci *          Jerome Glisse
2662306a36Sopenharmony_ci */
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include <drm/amdgpu_drm.h>
2962306a36Sopenharmony_ci#include <drm/display/drm_dp_helper.h>
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#include "amdgpu.h"
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#include "atom.h"
3462306a36Sopenharmony_ci#include "atom-bits.h"
3562306a36Sopenharmony_ci#include "atombios_encoders.h"
3662306a36Sopenharmony_ci#include "atombios_dp.h"
3762306a36Sopenharmony_ci#include "amdgpu_connectors.h"
3862306a36Sopenharmony_ci#include "amdgpu_atombios.h"
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci/* move these to drm_dp_helper.c/h */
4162306a36Sopenharmony_ci#define DP_LINK_CONFIGURATION_SIZE 9
4262306a36Sopenharmony_ci#define DP_DPCD_SIZE DP_RECEIVER_CAP_SIZE
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic char *voltage_names[] = {
4562306a36Sopenharmony_ci	"0.4V", "0.6V", "0.8V", "1.2V"
4662306a36Sopenharmony_ci};
4762306a36Sopenharmony_cistatic char *pre_emph_names[] = {
4862306a36Sopenharmony_ci	"0dB", "3.5dB", "6dB", "9.5dB"
4962306a36Sopenharmony_ci};
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci/***** amdgpu AUX functions *****/
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ciunion aux_channel_transaction {
5462306a36Sopenharmony_ci	PROCESS_AUX_CHANNEL_TRANSACTION_PS_ALLOCATION v1;
5562306a36Sopenharmony_ci	PROCESS_AUX_CHANNEL_TRANSACTION_PARAMETERS_V2 v2;
5662306a36Sopenharmony_ci};
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic int amdgpu_atombios_dp_process_aux_ch(struct amdgpu_i2c_chan *chan,
5962306a36Sopenharmony_ci				      u8 *send, int send_bytes,
6062306a36Sopenharmony_ci				      u8 *recv, int recv_size,
6162306a36Sopenharmony_ci				      u8 delay, u8 *ack)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	struct drm_device *dev = chan->dev;
6462306a36Sopenharmony_ci	struct amdgpu_device *adev = drm_to_adev(dev);
6562306a36Sopenharmony_ci	union aux_channel_transaction args;
6662306a36Sopenharmony_ci	int index = GetIndexIntoMasterTable(COMMAND, ProcessAuxChannelTransaction);
6762306a36Sopenharmony_ci	unsigned char *base;
6862306a36Sopenharmony_ci	int recv_bytes;
6962306a36Sopenharmony_ci	int r = 0;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	memset(&args, 0, sizeof(args));
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	mutex_lock(&chan->mutex);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	base = (unsigned char *)(adev->mode_info.atom_context->scratch + 1);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	amdgpu_atombios_copy_swap(base, send, send_bytes, true);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	args.v2.lpAuxRequest = cpu_to_le16((u16)(0 + 4));
8062306a36Sopenharmony_ci	args.v2.lpDataOut = cpu_to_le16((u16)(16 + 4));
8162306a36Sopenharmony_ci	args.v2.ucDataOutLen = 0;
8262306a36Sopenharmony_ci	args.v2.ucChannelID = chan->rec.i2c_id;
8362306a36Sopenharmony_ci	args.v2.ucDelay = delay / 10;
8462306a36Sopenharmony_ci	args.v2.ucHPD_ID = chan->rec.hpd;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	*ack = args.v2.ucReplyStatus;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	/* timeout */
9162306a36Sopenharmony_ci	if (args.v2.ucReplyStatus == 1) {
9262306a36Sopenharmony_ci		r = -ETIMEDOUT;
9362306a36Sopenharmony_ci		goto done;
9462306a36Sopenharmony_ci	}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	/* flags not zero */
9762306a36Sopenharmony_ci	if (args.v2.ucReplyStatus == 2) {
9862306a36Sopenharmony_ci		DRM_DEBUG_KMS("dp_aux_ch flags not zero\n");
9962306a36Sopenharmony_ci		r = -EIO;
10062306a36Sopenharmony_ci		goto done;
10162306a36Sopenharmony_ci	}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	/* error */
10462306a36Sopenharmony_ci	if (args.v2.ucReplyStatus == 3) {
10562306a36Sopenharmony_ci		DRM_DEBUG_KMS("dp_aux_ch error\n");
10662306a36Sopenharmony_ci		r = -EIO;
10762306a36Sopenharmony_ci		goto done;
10862306a36Sopenharmony_ci	}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	recv_bytes = args.v1.ucDataOutLen;
11162306a36Sopenharmony_ci	if (recv_bytes > recv_size)
11262306a36Sopenharmony_ci		recv_bytes = recv_size;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	if (recv && recv_size)
11562306a36Sopenharmony_ci		amdgpu_atombios_copy_swap(recv, base + 16, recv_bytes, false);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	r = recv_bytes;
11862306a36Sopenharmony_cidone:
11962306a36Sopenharmony_ci	mutex_unlock(&chan->mutex);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	return r;
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci#define BARE_ADDRESS_SIZE 3
12562306a36Sopenharmony_ci#define HEADER_SIZE (BARE_ADDRESS_SIZE + 1)
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic ssize_t
12862306a36Sopenharmony_ciamdgpu_atombios_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	struct amdgpu_i2c_chan *chan =
13162306a36Sopenharmony_ci		container_of(aux, struct amdgpu_i2c_chan, aux);
13262306a36Sopenharmony_ci	int ret;
13362306a36Sopenharmony_ci	u8 tx_buf[20];
13462306a36Sopenharmony_ci	size_t tx_size;
13562306a36Sopenharmony_ci	u8 ack, delay = 0;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	if (WARN_ON(msg->size > 16))
13862306a36Sopenharmony_ci		return -E2BIG;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	tx_buf[0] = msg->address & 0xff;
14162306a36Sopenharmony_ci	tx_buf[1] = msg->address >> 8;
14262306a36Sopenharmony_ci	tx_buf[2] = (msg->request << 4) |
14362306a36Sopenharmony_ci		((msg->address >> 16) & 0xf);
14462306a36Sopenharmony_ci	tx_buf[3] = msg->size ? (msg->size - 1) : 0;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	switch (msg->request & ~DP_AUX_I2C_MOT) {
14762306a36Sopenharmony_ci	case DP_AUX_NATIVE_WRITE:
14862306a36Sopenharmony_ci	case DP_AUX_I2C_WRITE:
14962306a36Sopenharmony_ci		/* tx_size needs to be 4 even for bare address packets since the atom
15062306a36Sopenharmony_ci		 * table needs the info in tx_buf[3].
15162306a36Sopenharmony_ci		 */
15262306a36Sopenharmony_ci		tx_size = HEADER_SIZE + msg->size;
15362306a36Sopenharmony_ci		if (msg->size == 0)
15462306a36Sopenharmony_ci			tx_buf[3] |= BARE_ADDRESS_SIZE << 4;
15562306a36Sopenharmony_ci		else
15662306a36Sopenharmony_ci			tx_buf[3] |= tx_size << 4;
15762306a36Sopenharmony_ci		memcpy(tx_buf + HEADER_SIZE, msg->buffer, msg->size);
15862306a36Sopenharmony_ci		ret = amdgpu_atombios_dp_process_aux_ch(chan,
15962306a36Sopenharmony_ci						 tx_buf, tx_size, NULL, 0, delay, &ack);
16062306a36Sopenharmony_ci		if (ret >= 0)
16162306a36Sopenharmony_ci			/* Return payload size. */
16262306a36Sopenharmony_ci			ret = msg->size;
16362306a36Sopenharmony_ci		break;
16462306a36Sopenharmony_ci	case DP_AUX_NATIVE_READ:
16562306a36Sopenharmony_ci	case DP_AUX_I2C_READ:
16662306a36Sopenharmony_ci		/* tx_size needs to be 4 even for bare address packets since the atom
16762306a36Sopenharmony_ci		 * table needs the info in tx_buf[3].
16862306a36Sopenharmony_ci		 */
16962306a36Sopenharmony_ci		tx_size = HEADER_SIZE;
17062306a36Sopenharmony_ci		if (msg->size == 0)
17162306a36Sopenharmony_ci			tx_buf[3] |= BARE_ADDRESS_SIZE << 4;
17262306a36Sopenharmony_ci		else
17362306a36Sopenharmony_ci			tx_buf[3] |= tx_size << 4;
17462306a36Sopenharmony_ci		ret = amdgpu_atombios_dp_process_aux_ch(chan,
17562306a36Sopenharmony_ci						 tx_buf, tx_size, msg->buffer, msg->size, delay, &ack);
17662306a36Sopenharmony_ci		break;
17762306a36Sopenharmony_ci	default:
17862306a36Sopenharmony_ci		ret = -EINVAL;
17962306a36Sopenharmony_ci		break;
18062306a36Sopenharmony_ci	}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	if (ret >= 0)
18362306a36Sopenharmony_ci		msg->reply = ack >> 4;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	return ret;
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_civoid amdgpu_atombios_dp_aux_init(struct amdgpu_connector *amdgpu_connector)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	amdgpu_connector->ddc_bus->rec.hpd = amdgpu_connector->hpd.hpd;
19162306a36Sopenharmony_ci	amdgpu_connector->ddc_bus->aux.transfer = amdgpu_atombios_dp_aux_transfer;
19262306a36Sopenharmony_ci	amdgpu_connector->ddc_bus->aux.drm_dev = amdgpu_connector->base.dev;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	drm_dp_aux_init(&amdgpu_connector->ddc_bus->aux);
19562306a36Sopenharmony_ci	amdgpu_connector->ddc_bus->has_aux = true;
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci/***** general DP utility functions *****/
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci#define DP_VOLTAGE_MAX         DP_TRAIN_VOLTAGE_SWING_LEVEL_3
20162306a36Sopenharmony_ci#define DP_PRE_EMPHASIS_MAX    DP_TRAIN_PRE_EMPH_LEVEL_3
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistatic void amdgpu_atombios_dp_get_adjust_train(const u8 link_status[DP_LINK_STATUS_SIZE],
20462306a36Sopenharmony_ci						int lane_count,
20562306a36Sopenharmony_ci						u8 train_set[4])
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	u8 v = 0;
20862306a36Sopenharmony_ci	u8 p = 0;
20962306a36Sopenharmony_ci	int lane;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	for (lane = 0; lane < lane_count; lane++) {
21262306a36Sopenharmony_ci		u8 this_v = drm_dp_get_adjust_request_voltage(link_status, lane);
21362306a36Sopenharmony_ci		u8 this_p = drm_dp_get_adjust_request_pre_emphasis(link_status, lane);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci		DRM_DEBUG_KMS("requested signal parameters: lane %d voltage %s pre_emph %s\n",
21662306a36Sopenharmony_ci			  lane,
21762306a36Sopenharmony_ci			  voltage_names[this_v >> DP_TRAIN_VOLTAGE_SWING_SHIFT],
21862306a36Sopenharmony_ci			  pre_emph_names[this_p >> DP_TRAIN_PRE_EMPHASIS_SHIFT]);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci		if (this_v > v)
22162306a36Sopenharmony_ci			v = this_v;
22262306a36Sopenharmony_ci		if (this_p > p)
22362306a36Sopenharmony_ci			p = this_p;
22462306a36Sopenharmony_ci	}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	if (v >= DP_VOLTAGE_MAX)
22762306a36Sopenharmony_ci		v |= DP_TRAIN_MAX_SWING_REACHED;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	if (p >= DP_PRE_EMPHASIS_MAX)
23062306a36Sopenharmony_ci		p |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	DRM_DEBUG_KMS("using signal parameters: voltage %s pre_emph %s\n",
23362306a36Sopenharmony_ci		  voltage_names[(v & DP_TRAIN_VOLTAGE_SWING_MASK) >> DP_TRAIN_VOLTAGE_SWING_SHIFT],
23462306a36Sopenharmony_ci		  pre_emph_names[(p & DP_TRAIN_PRE_EMPHASIS_MASK) >> DP_TRAIN_PRE_EMPHASIS_SHIFT]);
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	for (lane = 0; lane < 4; lane++)
23762306a36Sopenharmony_ci		train_set[lane] = v | p;
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci/* convert bits per color to bits per pixel */
24162306a36Sopenharmony_ci/* get bpc from the EDID */
24262306a36Sopenharmony_cistatic unsigned amdgpu_atombios_dp_convert_bpc_to_bpp(int bpc)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	if (bpc == 0)
24562306a36Sopenharmony_ci		return 24;
24662306a36Sopenharmony_ci	else
24762306a36Sopenharmony_ci		return bpc * 3;
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci/***** amdgpu specific DP functions *****/
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic int amdgpu_atombios_dp_get_dp_link_config(struct drm_connector *connector,
25362306a36Sopenharmony_ci						 const u8 dpcd[DP_DPCD_SIZE],
25462306a36Sopenharmony_ci						 unsigned pix_clock,
25562306a36Sopenharmony_ci						 unsigned *dp_lanes, unsigned *dp_rate)
25662306a36Sopenharmony_ci{
25762306a36Sopenharmony_ci	unsigned bpp =
25862306a36Sopenharmony_ci		amdgpu_atombios_dp_convert_bpc_to_bpp(amdgpu_connector_get_monitor_bpc(connector));
25962306a36Sopenharmony_ci	static const unsigned link_rates[3] = { 162000, 270000, 540000 };
26062306a36Sopenharmony_ci	unsigned max_link_rate = drm_dp_max_link_rate(dpcd);
26162306a36Sopenharmony_ci	unsigned max_lane_num = drm_dp_max_lane_count(dpcd);
26262306a36Sopenharmony_ci	unsigned lane_num, i, max_pix_clock;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	if (amdgpu_connector_encoder_get_dp_bridge_encoder_id(connector) ==
26562306a36Sopenharmony_ci	    ENCODER_OBJECT_ID_NUTMEG) {
26662306a36Sopenharmony_ci		for (lane_num = 1; lane_num <= max_lane_num; lane_num <<= 1) {
26762306a36Sopenharmony_ci			max_pix_clock = (lane_num * 270000 * 8) / bpp;
26862306a36Sopenharmony_ci			if (max_pix_clock >= pix_clock) {
26962306a36Sopenharmony_ci				*dp_lanes = lane_num;
27062306a36Sopenharmony_ci				*dp_rate = 270000;
27162306a36Sopenharmony_ci				return 0;
27262306a36Sopenharmony_ci			}
27362306a36Sopenharmony_ci		}
27462306a36Sopenharmony_ci	} else {
27562306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(link_rates) && link_rates[i] <= max_link_rate; i++) {
27662306a36Sopenharmony_ci			for (lane_num = 1; lane_num <= max_lane_num; lane_num <<= 1) {
27762306a36Sopenharmony_ci				max_pix_clock = (lane_num * link_rates[i] * 8) / bpp;
27862306a36Sopenharmony_ci				if (max_pix_clock >= pix_clock) {
27962306a36Sopenharmony_ci					*dp_lanes = lane_num;
28062306a36Sopenharmony_ci					*dp_rate = link_rates[i];
28162306a36Sopenharmony_ci					return 0;
28262306a36Sopenharmony_ci				}
28362306a36Sopenharmony_ci			}
28462306a36Sopenharmony_ci		}
28562306a36Sopenharmony_ci	}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	return -EINVAL;
28862306a36Sopenharmony_ci}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_cistatic u8 amdgpu_atombios_dp_encoder_service(struct amdgpu_device *adev,
29162306a36Sopenharmony_ci				      int action, int dp_clock,
29262306a36Sopenharmony_ci				      u8 ucconfig, u8 lane_num)
29362306a36Sopenharmony_ci{
29462306a36Sopenharmony_ci	DP_ENCODER_SERVICE_PARAMETERS args;
29562306a36Sopenharmony_ci	int index = GetIndexIntoMasterTable(COMMAND, DPEncoderService);
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	memset(&args, 0, sizeof(args));
29862306a36Sopenharmony_ci	args.ucLinkClock = dp_clock / 10;
29962306a36Sopenharmony_ci	args.ucConfig = ucconfig;
30062306a36Sopenharmony_ci	args.ucAction = action;
30162306a36Sopenharmony_ci	args.ucLaneNum = lane_num;
30262306a36Sopenharmony_ci	args.ucStatus = 0;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	amdgpu_atom_execute_table(adev->mode_info.atom_context, index, (uint32_t *)&args);
30562306a36Sopenharmony_ci	return args.ucStatus;
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ciu8 amdgpu_atombios_dp_get_sinktype(struct amdgpu_connector *amdgpu_connector)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	struct drm_device *dev = amdgpu_connector->base.dev;
31162306a36Sopenharmony_ci	struct amdgpu_device *adev = drm_to_adev(dev);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	return amdgpu_atombios_dp_encoder_service(adev, ATOM_DP_ACTION_GET_SINK_TYPE, 0,
31462306a36Sopenharmony_ci					   amdgpu_connector->ddc_bus->rec.i2c_id, 0);
31562306a36Sopenharmony_ci}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_cistatic void amdgpu_atombios_dp_probe_oui(struct amdgpu_connector *amdgpu_connector)
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	struct amdgpu_connector_atom_dig *dig_connector = amdgpu_connector->con_priv;
32062306a36Sopenharmony_ci	u8 buf[3];
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	if (!(dig_connector->dpcd[DP_DOWN_STREAM_PORT_COUNT] & DP_OUI_SUPPORT))
32362306a36Sopenharmony_ci		return;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	if (drm_dp_dpcd_read(&amdgpu_connector->ddc_bus->aux, DP_SINK_OUI, buf, 3) == 3)
32662306a36Sopenharmony_ci		DRM_DEBUG_KMS("Sink OUI: %02hx%02hx%02hx\n",
32762306a36Sopenharmony_ci			      buf[0], buf[1], buf[2]);
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	if (drm_dp_dpcd_read(&amdgpu_connector->ddc_bus->aux, DP_BRANCH_OUI, buf, 3) == 3)
33062306a36Sopenharmony_ci		DRM_DEBUG_KMS("Branch OUI: %02hx%02hx%02hx\n",
33162306a36Sopenharmony_ci			      buf[0], buf[1], buf[2]);
33262306a36Sopenharmony_ci}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_cistatic void amdgpu_atombios_dp_ds_ports(struct amdgpu_connector *amdgpu_connector)
33562306a36Sopenharmony_ci{
33662306a36Sopenharmony_ci	struct amdgpu_connector_atom_dig *dig_connector = amdgpu_connector->con_priv;
33762306a36Sopenharmony_ci	int ret;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	if (dig_connector->dpcd[DP_DPCD_REV] > 0x10) {
34062306a36Sopenharmony_ci		ret = drm_dp_dpcd_read(&amdgpu_connector->ddc_bus->aux,
34162306a36Sopenharmony_ci				       DP_DOWNSTREAM_PORT_0,
34262306a36Sopenharmony_ci				       dig_connector->downstream_ports,
34362306a36Sopenharmony_ci				       DP_MAX_DOWNSTREAM_PORTS);
34462306a36Sopenharmony_ci		if (ret)
34562306a36Sopenharmony_ci			memset(dig_connector->downstream_ports, 0,
34662306a36Sopenharmony_ci			       DP_MAX_DOWNSTREAM_PORTS);
34762306a36Sopenharmony_ci	}
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ciint amdgpu_atombios_dp_get_dpcd(struct amdgpu_connector *amdgpu_connector)
35162306a36Sopenharmony_ci{
35262306a36Sopenharmony_ci	struct amdgpu_connector_atom_dig *dig_connector = amdgpu_connector->con_priv;
35362306a36Sopenharmony_ci	u8 msg[DP_DPCD_SIZE];
35462306a36Sopenharmony_ci	int ret;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	ret = drm_dp_dpcd_read(&amdgpu_connector->ddc_bus->aux, DP_DPCD_REV,
35762306a36Sopenharmony_ci			       msg, DP_DPCD_SIZE);
35862306a36Sopenharmony_ci	if (ret == DP_DPCD_SIZE) {
35962306a36Sopenharmony_ci		memcpy(dig_connector->dpcd, msg, DP_DPCD_SIZE);
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci		DRM_DEBUG_KMS("DPCD: %*ph\n", (int)sizeof(dig_connector->dpcd),
36262306a36Sopenharmony_ci			      dig_connector->dpcd);
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci		amdgpu_atombios_dp_probe_oui(amdgpu_connector);
36562306a36Sopenharmony_ci		amdgpu_atombios_dp_ds_ports(amdgpu_connector);
36662306a36Sopenharmony_ci		return 0;
36762306a36Sopenharmony_ci	}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	dig_connector->dpcd[0] = 0;
37062306a36Sopenharmony_ci	return -EINVAL;
37162306a36Sopenharmony_ci}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ciint amdgpu_atombios_dp_get_panel_mode(struct drm_encoder *encoder,
37462306a36Sopenharmony_ci			       struct drm_connector *connector)
37562306a36Sopenharmony_ci{
37662306a36Sopenharmony_ci	struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
37762306a36Sopenharmony_ci	int panel_mode = DP_PANEL_MODE_EXTERNAL_DP_MODE;
37862306a36Sopenharmony_ci	u16 dp_bridge = amdgpu_connector_encoder_get_dp_bridge_encoder_id(connector);
37962306a36Sopenharmony_ci	u8 tmp;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	if (!amdgpu_connector->con_priv)
38262306a36Sopenharmony_ci		return panel_mode;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	if (dp_bridge != ENCODER_OBJECT_ID_NONE) {
38562306a36Sopenharmony_ci		/* DP bridge chips */
38662306a36Sopenharmony_ci		if (drm_dp_dpcd_readb(&amdgpu_connector->ddc_bus->aux,
38762306a36Sopenharmony_ci				      DP_EDP_CONFIGURATION_CAP, &tmp) == 1) {
38862306a36Sopenharmony_ci			if (tmp & 1)
38962306a36Sopenharmony_ci				panel_mode = DP_PANEL_MODE_INTERNAL_DP2_MODE;
39062306a36Sopenharmony_ci			else if ((dp_bridge == ENCODER_OBJECT_ID_NUTMEG) ||
39162306a36Sopenharmony_ci				 (dp_bridge == ENCODER_OBJECT_ID_TRAVIS))
39262306a36Sopenharmony_ci				panel_mode = DP_PANEL_MODE_INTERNAL_DP1_MODE;
39362306a36Sopenharmony_ci			else
39462306a36Sopenharmony_ci				panel_mode = DP_PANEL_MODE_EXTERNAL_DP_MODE;
39562306a36Sopenharmony_ci		}
39662306a36Sopenharmony_ci	} else if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) {
39762306a36Sopenharmony_ci		/* eDP */
39862306a36Sopenharmony_ci		if (drm_dp_dpcd_readb(&amdgpu_connector->ddc_bus->aux,
39962306a36Sopenharmony_ci				      DP_EDP_CONFIGURATION_CAP, &tmp) == 1) {
40062306a36Sopenharmony_ci			if (tmp & 1)
40162306a36Sopenharmony_ci				panel_mode = DP_PANEL_MODE_INTERNAL_DP2_MODE;
40262306a36Sopenharmony_ci		}
40362306a36Sopenharmony_ci	}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	return panel_mode;
40662306a36Sopenharmony_ci}
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_civoid amdgpu_atombios_dp_set_link_config(struct drm_connector *connector,
40962306a36Sopenharmony_ci				 const struct drm_display_mode *mode)
41062306a36Sopenharmony_ci{
41162306a36Sopenharmony_ci	struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
41262306a36Sopenharmony_ci	struct amdgpu_connector_atom_dig *dig_connector;
41362306a36Sopenharmony_ci	int ret;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	if (!amdgpu_connector->con_priv)
41662306a36Sopenharmony_ci		return;
41762306a36Sopenharmony_ci	dig_connector = amdgpu_connector->con_priv;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	if ((dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) ||
42062306a36Sopenharmony_ci	    (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP)) {
42162306a36Sopenharmony_ci		ret = amdgpu_atombios_dp_get_dp_link_config(connector, dig_connector->dpcd,
42262306a36Sopenharmony_ci							    mode->clock,
42362306a36Sopenharmony_ci							    &dig_connector->dp_lane_count,
42462306a36Sopenharmony_ci							    &dig_connector->dp_clock);
42562306a36Sopenharmony_ci		if (ret) {
42662306a36Sopenharmony_ci			dig_connector->dp_clock = 0;
42762306a36Sopenharmony_ci			dig_connector->dp_lane_count = 0;
42862306a36Sopenharmony_ci		}
42962306a36Sopenharmony_ci	}
43062306a36Sopenharmony_ci}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ciint amdgpu_atombios_dp_mode_valid_helper(struct drm_connector *connector,
43362306a36Sopenharmony_ci				  struct drm_display_mode *mode)
43462306a36Sopenharmony_ci{
43562306a36Sopenharmony_ci	struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
43662306a36Sopenharmony_ci	struct amdgpu_connector_atom_dig *dig_connector;
43762306a36Sopenharmony_ci	unsigned dp_lanes, dp_clock;
43862306a36Sopenharmony_ci	int ret;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	if (!amdgpu_connector->con_priv)
44162306a36Sopenharmony_ci		return MODE_CLOCK_HIGH;
44262306a36Sopenharmony_ci	dig_connector = amdgpu_connector->con_priv;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	ret = amdgpu_atombios_dp_get_dp_link_config(connector, dig_connector->dpcd,
44562306a36Sopenharmony_ci						    mode->clock, &dp_lanes, &dp_clock);
44662306a36Sopenharmony_ci	if (ret)
44762306a36Sopenharmony_ci		return MODE_CLOCK_HIGH;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	if ((dp_clock == 540000) &&
45062306a36Sopenharmony_ci	    (!amdgpu_connector_is_dp12_capable(connector)))
45162306a36Sopenharmony_ci		return MODE_CLOCK_HIGH;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	return MODE_OK;
45462306a36Sopenharmony_ci}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_cibool amdgpu_atombios_dp_needs_link_train(struct amdgpu_connector *amdgpu_connector)
45762306a36Sopenharmony_ci{
45862306a36Sopenharmony_ci	u8 link_status[DP_LINK_STATUS_SIZE];
45962306a36Sopenharmony_ci	struct amdgpu_connector_atom_dig *dig = amdgpu_connector->con_priv;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	if (drm_dp_dpcd_read_link_status(&amdgpu_connector->ddc_bus->aux, link_status)
46262306a36Sopenharmony_ci	    <= 0)
46362306a36Sopenharmony_ci		return false;
46462306a36Sopenharmony_ci	if (drm_dp_channel_eq_ok(link_status, dig->dp_lane_count))
46562306a36Sopenharmony_ci		return false;
46662306a36Sopenharmony_ci	return true;
46762306a36Sopenharmony_ci}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_civoid amdgpu_atombios_dp_set_rx_power_state(struct drm_connector *connector,
47062306a36Sopenharmony_ci				    u8 power_state)
47162306a36Sopenharmony_ci{
47262306a36Sopenharmony_ci	struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);
47362306a36Sopenharmony_ci	struct amdgpu_connector_atom_dig *dig_connector;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	if (!amdgpu_connector->con_priv)
47662306a36Sopenharmony_ci		return;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	dig_connector = amdgpu_connector->con_priv;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	/* power up/down the sink */
48162306a36Sopenharmony_ci	if (dig_connector->dpcd[0] >= 0x11) {
48262306a36Sopenharmony_ci		drm_dp_dpcd_writeb(&amdgpu_connector->ddc_bus->aux,
48362306a36Sopenharmony_ci				   DP_SET_POWER, power_state);
48462306a36Sopenharmony_ci		usleep_range(1000, 2000);
48562306a36Sopenharmony_ci	}
48662306a36Sopenharmony_ci}
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_cistruct amdgpu_atombios_dp_link_train_info {
48962306a36Sopenharmony_ci	struct amdgpu_device *adev;
49062306a36Sopenharmony_ci	struct drm_encoder *encoder;
49162306a36Sopenharmony_ci	struct drm_connector *connector;
49262306a36Sopenharmony_ci	int dp_clock;
49362306a36Sopenharmony_ci	int dp_lane_count;
49462306a36Sopenharmony_ci	bool tp3_supported;
49562306a36Sopenharmony_ci	u8 dpcd[DP_RECEIVER_CAP_SIZE];
49662306a36Sopenharmony_ci	u8 train_set[4];
49762306a36Sopenharmony_ci	u8 link_status[DP_LINK_STATUS_SIZE];
49862306a36Sopenharmony_ci	u8 tries;
49962306a36Sopenharmony_ci	struct drm_dp_aux *aux;
50062306a36Sopenharmony_ci};
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_cistatic void
50362306a36Sopenharmony_ciamdgpu_atombios_dp_update_vs_emph(struct amdgpu_atombios_dp_link_train_info *dp_info)
50462306a36Sopenharmony_ci{
50562306a36Sopenharmony_ci	/* set the initial vs/emph on the source */
50662306a36Sopenharmony_ci	amdgpu_atombios_encoder_setup_dig_transmitter(dp_info->encoder,
50762306a36Sopenharmony_ci					       ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH,
50862306a36Sopenharmony_ci					       0, dp_info->train_set[0]); /* sets all lanes at once */
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	/* set the vs/emph on the sink */
51162306a36Sopenharmony_ci	drm_dp_dpcd_write(dp_info->aux, DP_TRAINING_LANE0_SET,
51262306a36Sopenharmony_ci			  dp_info->train_set, dp_info->dp_lane_count);
51362306a36Sopenharmony_ci}
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_cistatic void
51662306a36Sopenharmony_ciamdgpu_atombios_dp_set_tp(struct amdgpu_atombios_dp_link_train_info *dp_info, int tp)
51762306a36Sopenharmony_ci{
51862306a36Sopenharmony_ci	int rtp = 0;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	/* set training pattern on the source */
52162306a36Sopenharmony_ci	switch (tp) {
52262306a36Sopenharmony_ci	case DP_TRAINING_PATTERN_1:
52362306a36Sopenharmony_ci		rtp = ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN1;
52462306a36Sopenharmony_ci		break;
52562306a36Sopenharmony_ci	case DP_TRAINING_PATTERN_2:
52662306a36Sopenharmony_ci		rtp = ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN2;
52762306a36Sopenharmony_ci		break;
52862306a36Sopenharmony_ci	case DP_TRAINING_PATTERN_3:
52962306a36Sopenharmony_ci		rtp = ATOM_ENCODER_CMD_DP_LINK_TRAINING_PATTERN3;
53062306a36Sopenharmony_ci			break;
53162306a36Sopenharmony_ci	}
53262306a36Sopenharmony_ci	amdgpu_atombios_encoder_setup_dig_encoder(dp_info->encoder, rtp, 0);
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	/* enable training pattern on the sink */
53562306a36Sopenharmony_ci	drm_dp_dpcd_writeb(dp_info->aux, DP_TRAINING_PATTERN_SET, tp);
53662306a36Sopenharmony_ci}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_cistatic int
53962306a36Sopenharmony_ciamdgpu_atombios_dp_link_train_init(struct amdgpu_atombios_dp_link_train_info *dp_info)
54062306a36Sopenharmony_ci{
54162306a36Sopenharmony_ci	struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(dp_info->encoder);
54262306a36Sopenharmony_ci	struct amdgpu_encoder_atom_dig *dig = amdgpu_encoder->enc_priv;
54362306a36Sopenharmony_ci	u8 tmp;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	/* power up the sink */
54662306a36Sopenharmony_ci	amdgpu_atombios_dp_set_rx_power_state(dp_info->connector, DP_SET_POWER_D0);
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	/* possibly enable downspread on the sink */
54962306a36Sopenharmony_ci	if (dp_info->dpcd[3] & 0x1)
55062306a36Sopenharmony_ci		drm_dp_dpcd_writeb(dp_info->aux,
55162306a36Sopenharmony_ci				   DP_DOWNSPREAD_CTRL, DP_SPREAD_AMP_0_5);
55262306a36Sopenharmony_ci	else
55362306a36Sopenharmony_ci		drm_dp_dpcd_writeb(dp_info->aux,
55462306a36Sopenharmony_ci				   DP_DOWNSPREAD_CTRL, 0);
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	if (dig->panel_mode == DP_PANEL_MODE_INTERNAL_DP2_MODE)
55762306a36Sopenharmony_ci		drm_dp_dpcd_writeb(dp_info->aux, DP_EDP_CONFIGURATION_SET, 1);
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	/* set the lane count on the sink */
56062306a36Sopenharmony_ci	tmp = dp_info->dp_lane_count;
56162306a36Sopenharmony_ci	if (drm_dp_enhanced_frame_cap(dp_info->dpcd))
56262306a36Sopenharmony_ci		tmp |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
56362306a36Sopenharmony_ci	drm_dp_dpcd_writeb(dp_info->aux, DP_LANE_COUNT_SET, tmp);
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	/* set the link rate on the sink */
56662306a36Sopenharmony_ci	tmp = drm_dp_link_rate_to_bw_code(dp_info->dp_clock);
56762306a36Sopenharmony_ci	drm_dp_dpcd_writeb(dp_info->aux, DP_LINK_BW_SET, tmp);
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	/* start training on the source */
57062306a36Sopenharmony_ci	amdgpu_atombios_encoder_setup_dig_encoder(dp_info->encoder,
57162306a36Sopenharmony_ci					   ATOM_ENCODER_CMD_DP_LINK_TRAINING_START, 0);
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci	/* disable the training pattern on the sink */
57462306a36Sopenharmony_ci	drm_dp_dpcd_writeb(dp_info->aux,
57562306a36Sopenharmony_ci			   DP_TRAINING_PATTERN_SET,
57662306a36Sopenharmony_ci			   DP_TRAINING_PATTERN_DISABLE);
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	return 0;
57962306a36Sopenharmony_ci}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_cistatic int
58262306a36Sopenharmony_ciamdgpu_atombios_dp_link_train_finish(struct amdgpu_atombios_dp_link_train_info *dp_info)
58362306a36Sopenharmony_ci{
58462306a36Sopenharmony_ci	udelay(400);
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	/* disable the training pattern on the sink */
58762306a36Sopenharmony_ci	drm_dp_dpcd_writeb(dp_info->aux,
58862306a36Sopenharmony_ci			   DP_TRAINING_PATTERN_SET,
58962306a36Sopenharmony_ci			   DP_TRAINING_PATTERN_DISABLE);
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	/* disable the training pattern on the source */
59262306a36Sopenharmony_ci	amdgpu_atombios_encoder_setup_dig_encoder(dp_info->encoder,
59362306a36Sopenharmony_ci					   ATOM_ENCODER_CMD_DP_LINK_TRAINING_COMPLETE, 0);
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	return 0;
59662306a36Sopenharmony_ci}
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_cistatic int
59962306a36Sopenharmony_ciamdgpu_atombios_dp_link_train_cr(struct amdgpu_atombios_dp_link_train_info *dp_info)
60062306a36Sopenharmony_ci{
60162306a36Sopenharmony_ci	bool clock_recovery;
60262306a36Sopenharmony_ci	u8 voltage;
60362306a36Sopenharmony_ci	int i;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	amdgpu_atombios_dp_set_tp(dp_info, DP_TRAINING_PATTERN_1);
60662306a36Sopenharmony_ci	memset(dp_info->train_set, 0, 4);
60762306a36Sopenharmony_ci	amdgpu_atombios_dp_update_vs_emph(dp_info);
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	udelay(400);
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	/* clock recovery loop */
61262306a36Sopenharmony_ci	clock_recovery = false;
61362306a36Sopenharmony_ci	dp_info->tries = 0;
61462306a36Sopenharmony_ci	voltage = 0xff;
61562306a36Sopenharmony_ci	while (1) {
61662306a36Sopenharmony_ci		drm_dp_link_train_clock_recovery_delay(dp_info->aux, dp_info->dpcd);
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci		if (drm_dp_dpcd_read_link_status(dp_info->aux,
61962306a36Sopenharmony_ci						 dp_info->link_status) <= 0) {
62062306a36Sopenharmony_ci			DRM_ERROR("displayport link status failed\n");
62162306a36Sopenharmony_ci			break;
62262306a36Sopenharmony_ci		}
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci		if (drm_dp_clock_recovery_ok(dp_info->link_status, dp_info->dp_lane_count)) {
62562306a36Sopenharmony_ci			clock_recovery = true;
62662306a36Sopenharmony_ci			break;
62762306a36Sopenharmony_ci		}
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci		for (i = 0; i < dp_info->dp_lane_count; i++) {
63062306a36Sopenharmony_ci			if ((dp_info->train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0)
63162306a36Sopenharmony_ci				break;
63262306a36Sopenharmony_ci		}
63362306a36Sopenharmony_ci		if (i == dp_info->dp_lane_count) {
63462306a36Sopenharmony_ci			DRM_ERROR("clock recovery reached max voltage\n");
63562306a36Sopenharmony_ci			break;
63662306a36Sopenharmony_ci		}
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci		if ((dp_info->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) {
63962306a36Sopenharmony_ci			++dp_info->tries;
64062306a36Sopenharmony_ci			if (dp_info->tries == 5) {
64162306a36Sopenharmony_ci				DRM_ERROR("clock recovery tried 5 times\n");
64262306a36Sopenharmony_ci				break;
64362306a36Sopenharmony_ci			}
64462306a36Sopenharmony_ci		} else
64562306a36Sopenharmony_ci			dp_info->tries = 0;
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci		voltage = dp_info->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci		/* Compute new train_set as requested by sink */
65062306a36Sopenharmony_ci		amdgpu_atombios_dp_get_adjust_train(dp_info->link_status, dp_info->dp_lane_count,
65162306a36Sopenharmony_ci					     dp_info->train_set);
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci		amdgpu_atombios_dp_update_vs_emph(dp_info);
65462306a36Sopenharmony_ci	}
65562306a36Sopenharmony_ci	if (!clock_recovery) {
65662306a36Sopenharmony_ci		DRM_ERROR("clock recovery failed\n");
65762306a36Sopenharmony_ci		return -1;
65862306a36Sopenharmony_ci	} else {
65962306a36Sopenharmony_ci		DRM_DEBUG_KMS("clock recovery at voltage %d pre-emphasis %d\n",
66062306a36Sopenharmony_ci			  dp_info->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK,
66162306a36Sopenharmony_ci			  (dp_info->train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK) >>
66262306a36Sopenharmony_ci			  DP_TRAIN_PRE_EMPHASIS_SHIFT);
66362306a36Sopenharmony_ci		return 0;
66462306a36Sopenharmony_ci	}
66562306a36Sopenharmony_ci}
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_cistatic int
66862306a36Sopenharmony_ciamdgpu_atombios_dp_link_train_ce(struct amdgpu_atombios_dp_link_train_info *dp_info)
66962306a36Sopenharmony_ci{
67062306a36Sopenharmony_ci	bool channel_eq;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	if (dp_info->tp3_supported)
67362306a36Sopenharmony_ci		amdgpu_atombios_dp_set_tp(dp_info, DP_TRAINING_PATTERN_3);
67462306a36Sopenharmony_ci	else
67562306a36Sopenharmony_ci		amdgpu_atombios_dp_set_tp(dp_info, DP_TRAINING_PATTERN_2);
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	/* channel equalization loop */
67862306a36Sopenharmony_ci	dp_info->tries = 0;
67962306a36Sopenharmony_ci	channel_eq = false;
68062306a36Sopenharmony_ci	while (1) {
68162306a36Sopenharmony_ci		drm_dp_link_train_channel_eq_delay(dp_info->aux, dp_info->dpcd);
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci		if (drm_dp_dpcd_read_link_status(dp_info->aux,
68462306a36Sopenharmony_ci						 dp_info->link_status) <= 0) {
68562306a36Sopenharmony_ci			DRM_ERROR("displayport link status failed\n");
68662306a36Sopenharmony_ci			break;
68762306a36Sopenharmony_ci		}
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci		if (drm_dp_channel_eq_ok(dp_info->link_status, dp_info->dp_lane_count)) {
69062306a36Sopenharmony_ci			channel_eq = true;
69162306a36Sopenharmony_ci			break;
69262306a36Sopenharmony_ci		}
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci		/* Try 5 times */
69562306a36Sopenharmony_ci		if (dp_info->tries > 5) {
69662306a36Sopenharmony_ci			DRM_ERROR("channel eq failed: 5 tries\n");
69762306a36Sopenharmony_ci			break;
69862306a36Sopenharmony_ci		}
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci		/* Compute new train_set as requested by sink */
70162306a36Sopenharmony_ci		amdgpu_atombios_dp_get_adjust_train(dp_info->link_status, dp_info->dp_lane_count,
70262306a36Sopenharmony_ci					     dp_info->train_set);
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci		amdgpu_atombios_dp_update_vs_emph(dp_info);
70562306a36Sopenharmony_ci		dp_info->tries++;
70662306a36Sopenharmony_ci	}
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	if (!channel_eq) {
70962306a36Sopenharmony_ci		DRM_ERROR("channel eq failed\n");
71062306a36Sopenharmony_ci		return -1;
71162306a36Sopenharmony_ci	} else {
71262306a36Sopenharmony_ci		DRM_DEBUG_KMS("channel eq at voltage %d pre-emphasis %d\n",
71362306a36Sopenharmony_ci			  dp_info->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK,
71462306a36Sopenharmony_ci			  (dp_info->train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK)
71562306a36Sopenharmony_ci			  >> DP_TRAIN_PRE_EMPHASIS_SHIFT);
71662306a36Sopenharmony_ci		return 0;
71762306a36Sopenharmony_ci	}
71862306a36Sopenharmony_ci}
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_civoid amdgpu_atombios_dp_link_train(struct drm_encoder *encoder,
72162306a36Sopenharmony_ci			    struct drm_connector *connector)
72262306a36Sopenharmony_ci{
72362306a36Sopenharmony_ci	struct drm_device *dev = encoder->dev;
72462306a36Sopenharmony_ci	struct amdgpu_device *adev = drm_to_adev(dev);
72562306a36Sopenharmony_ci	struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
72662306a36Sopenharmony_ci	struct amdgpu_connector *amdgpu_connector;
72762306a36Sopenharmony_ci	struct amdgpu_connector_atom_dig *dig_connector;
72862306a36Sopenharmony_ci	struct amdgpu_atombios_dp_link_train_info dp_info;
72962306a36Sopenharmony_ci	u8 tmp;
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	if (!amdgpu_encoder->enc_priv)
73262306a36Sopenharmony_ci		return;
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	amdgpu_connector = to_amdgpu_connector(connector);
73562306a36Sopenharmony_ci	if (!amdgpu_connector->con_priv)
73662306a36Sopenharmony_ci		return;
73762306a36Sopenharmony_ci	dig_connector = amdgpu_connector->con_priv;
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	if ((dig_connector->dp_sink_type != CONNECTOR_OBJECT_ID_DISPLAYPORT) &&
74062306a36Sopenharmony_ci	    (dig_connector->dp_sink_type != CONNECTOR_OBJECT_ID_eDP))
74162306a36Sopenharmony_ci		return;
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	if (drm_dp_dpcd_readb(&amdgpu_connector->ddc_bus->aux, DP_MAX_LANE_COUNT, &tmp)
74462306a36Sopenharmony_ci	    == 1) {
74562306a36Sopenharmony_ci		if (tmp & DP_TPS3_SUPPORTED)
74662306a36Sopenharmony_ci			dp_info.tp3_supported = true;
74762306a36Sopenharmony_ci		else
74862306a36Sopenharmony_ci			dp_info.tp3_supported = false;
74962306a36Sopenharmony_ci	} else {
75062306a36Sopenharmony_ci		dp_info.tp3_supported = false;
75162306a36Sopenharmony_ci	}
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	memcpy(dp_info.dpcd, dig_connector->dpcd, DP_RECEIVER_CAP_SIZE);
75462306a36Sopenharmony_ci	dp_info.adev = adev;
75562306a36Sopenharmony_ci	dp_info.encoder = encoder;
75662306a36Sopenharmony_ci	dp_info.connector = connector;
75762306a36Sopenharmony_ci	dp_info.dp_lane_count = dig_connector->dp_lane_count;
75862306a36Sopenharmony_ci	dp_info.dp_clock = dig_connector->dp_clock;
75962306a36Sopenharmony_ci	dp_info.aux = &amdgpu_connector->ddc_bus->aux;
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	if (amdgpu_atombios_dp_link_train_init(&dp_info))
76262306a36Sopenharmony_ci		goto done;
76362306a36Sopenharmony_ci	if (amdgpu_atombios_dp_link_train_cr(&dp_info))
76462306a36Sopenharmony_ci		goto done;
76562306a36Sopenharmony_ci	if (amdgpu_atombios_dp_link_train_ce(&dp_info))
76662306a36Sopenharmony_ci		goto done;
76762306a36Sopenharmony_cidone:
76862306a36Sopenharmony_ci	if (amdgpu_atombios_dp_link_train_finish(&dp_info))
76962306a36Sopenharmony_ci		return;
77062306a36Sopenharmony_ci}
771