[PATCH 11/16] drm/amd/display: Refactor edid read.

Harry Wentland harry.wentland at amd.com
Mon Apr 3 23:07:25 UTC 2017


From: Andrey Grodzovsky <Andrey.Grodzovsky at amd.com>

Allow Linux to use DRM provided EDID read functioality
by moving  DAL edid implementation to module hence
removing this code from DC by this cleaning up DC
code for upstream.

Change-Id: I7c73ae63102fa06f86b347f21ee28902ca4f7c58
Signed-off-by: Andrey Grodzovsky <Andrey.Grodzovsky at amd.com>
Acked-by: Harry Wentland <Harry.Wentland at amd.com>
Reviewed-by: Tony Cheng <Tony.Cheng at amd.com>
---
 .../drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c  |  47 +++
 .../amd/display/amdgpu_dm/amdgpu_dm_mst_types.c    |  47 ++-
 .../amd/display/amdgpu_dm/amdgpu_dm_mst_types.h    |   2 +-
 .../drm/amd/display/amdgpu_dm/amdgpu_dm_types.c    |  10 +-
 drivers/gpu/drm/amd/display/dc/core/dc.c           |  78 ++++-
 drivers/gpu/drm/amd/display/dc/core/dc_link.c      |  53 +--
 drivers/gpu/drm/amd/display/dc/core/dc_link_ddc.c  | 337 +-----------------
 drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c   |  30 +-
 drivers/gpu/drm/amd/display/dc/dc.h                |  21 +-
 drivers/gpu/drm/amd/display/dc/dc_types.h          |   6 +
 drivers/gpu/drm/amd/display/dc/dm_helpers.h        |  10 +
 drivers/gpu/drm/amd/display/dc/i2caux/i2caux.c     |   9 +-
 drivers/gpu/drm/amd/display/dc/inc/dc_link_ddc.h   |  38 +-
 .../drm/amd/display/include/ddc_service_types.h    |  28 --
 .../gpu/drm/amd/display/include/i2caux_interface.h |   3 +
 .../amd/display/modules/ddc_service/ddc_service.c  | 381 +++++++++++++++++++++
 .../drm/amd/display/modules/inc/mod_ddc_service.h  |  64 ++++
 17 files changed, 699 insertions(+), 465 deletions(-)
 create mode 100644 drivers/gpu/drm/amd/display/modules/ddc_service/ddc_service.c
 create mode 100644 drivers/gpu/drm/amd/display/modules/inc/mod_ddc_service.h

diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
index ca4fa5c8d8bf..3401780af2d3 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
@@ -434,3 +434,50 @@ bool dm_helpers_submit_i2c(
 
 	return result;
 }
+
+enum dc_edid_status dm_helpers_read_local_edid(
+		struct dc_context *ctx,
+		struct dc_link *link,
+		struct dc_sink *sink)
+{
+	struct amdgpu_connector *aconnector = link->priv;
+	struct i2c_adapter *ddc;
+	int retry = 3;
+	enum dc_edid_status edid_status;
+	struct edid *edid;
+
+	if (link->aux_mode)
+		ddc = &aconnector->dm_dp_aux.aux.ddc;
+	else
+		ddc = &aconnector->i2c->base;
+
+	/* some dongles read edid incorrectly the first time,
+	 * do check sum and retry to make sure read correct edid.
+	 */
+	do {
+
+		edid = drm_get_edid(&aconnector->base, ddc);
+
+		if (!edid)
+			return EDID_NO_RESPONSE;
+
+		sink->dc_edid.length = EDID_LENGTH * (edid->extensions + 1);
+		memmove(sink->dc_edid.raw_edid, (uint8_t *)edid, sink->dc_edid.length);
+
+		/* We don't need the original edid anymore */
+		kfree(edid);
+
+		edid_status = dm_helpers_parse_edid_caps(
+						ctx,
+						&sink->dc_edid,
+						&sink->edid_caps);
+
+	} while (edid_status == EDID_BAD_CHECKSUM && --retry > 0);
+
+	if (edid_status != EDID_OK)
+		DRM_ERROR("EDID err: %d, on connector: %s",
+				edid_status,
+				aconnector->base.name);
+
+	return edid_status;
+}
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
index 91b3610a3654..0e79ba920b06 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
@@ -81,24 +81,43 @@ static ssize_t dm_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg
 	struct drm_device *drm_dev = pci_get_drvdata(pdev);
 	struct amdgpu_device *adev = drm_dev->dev_private;
 	struct dc *dc = adev->dm.dc;
+	enum i2c_mot_mode mot = (msg->request & DP_AUX_I2C_MOT) ? I2C_MOT_TRUE : I2C_MOT_FALSE;
 	bool res;
 
-	switch (msg->request) {
+	switch (msg->request & ~DP_AUX_I2C_MOT) {
 	case DP_AUX_NATIVE_READ:
-		res = dc_read_dpcd(
-			dc,
-			TO_DM_AUX(aux)->link_index,
-			msg->address,
-			msg->buffer,
-			msg->size);
+		res = dc_read_aux_dpcd(
+				dc,
+				TO_DM_AUX(aux)->link_index,
+				msg->address,
+				msg->buffer,
+				msg->size);
 		break;
 	case DP_AUX_NATIVE_WRITE:
-		res = dc_write_dpcd(
-			dc,
-			TO_DM_AUX(aux)->link_index,
-			msg->address,
-			msg->buffer,
-			msg->size);
+		res = dc_write_aux_dpcd(
+				dc,
+				TO_DM_AUX(aux)->link_index,
+				msg->address,
+				msg->buffer,
+				msg->size);
+		break;
+	case DP_AUX_I2C_READ:
+		res = dc_read_aux_i2c(
+				dc,
+				TO_DM_AUX(aux)->link_index,
+				mot,
+				msg->address,
+				msg->buffer,
+				msg->size);
+		break;
+	case DP_AUX_I2C_WRITE:
+		res = dc_write_aux_i2c(
+				dc,
+				TO_DM_AUX(aux)->link_index,
+				mot,
+				msg->address,
+				msg->buffer,
+				msg->size);
 		break;
 	default:
 		return 0;
@@ -420,7 +439,7 @@ static const struct drm_dp_mst_topology_cbs dm_mst_cbs = {
 	.register_connector = dm_dp_mst_register_connector
 };
 
-void amdgpu_dm_initialize_mst_connector(
+void amdgpu_dm_initialize_dp_connector(
 	struct amdgpu_display_manager *dm,
 	struct amdgpu_connector *aconnector)
 {
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h
index 6130d62ac65c..418061f3b46b 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h
@@ -29,7 +29,7 @@
 struct amdgpu_display_manager;
 struct amdgpu_connector;
 
-void amdgpu_dm_initialize_mst_connector(
+void amdgpu_dm_initialize_dp_connector(
 	struct amdgpu_display_manager *dm,
 	struct amdgpu_connector *aconnector);
 
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_types.c
index a260bb5fad3b..4904d1157621 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_types.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_types.c
@@ -2220,7 +2220,7 @@ int amdgpu_dm_connector_init(
 
 	if (connector_type == DRM_MODE_CONNECTOR_DisplayPort
 		|| connector_type == DRM_MODE_CONNECTOR_eDP)
-		amdgpu_dm_initialize_mst_connector(dm, aconnector);
+		amdgpu_dm_initialize_dp_connector(dm, aconnector);
 
 #if defined(CONFIG_BACKLIGHT_CLASS_DEVICE) ||\
 	defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE)
@@ -3211,9 +3211,11 @@ static bool is_dp_capable_without_timing_msa(
 	uint8_t dpcd_data;
 	bool capable = false;
 	if (amdgpu_connector->dc_link &&
-	    dc_read_dpcd(dc, amdgpu_connector->dc_link->link_index,
-			 DP_DOWN_STREAM_PORT_COUNT,
-			 &dpcd_data, sizeof(dpcd_data)) )
+		dc_read_aux_dpcd(
+			dc,
+			amdgpu_connector->dc_link->link_index,
+			DP_DOWN_STREAM_PORT_COUNT,
+			&dpcd_data, sizeof(dpcd_data)))
 		capable = (dpcd_data & DP_MSA_TIMING_PAR_IGNORED) ? true:false;
 
 	return capable;
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c
index b3891228b499..40a800155fe6 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc.c
@@ -1553,7 +1553,7 @@ void dc_resume(const struct dc *dc)
 		core_link_resume(core_dc->links[i]);
 }
 
-bool dc_read_dpcd(
+bool dc_read_aux_dpcd(
 		struct dc *dc,
 		uint32_t link_index,
 		uint32_t address,
@@ -1565,56 +1565,100 @@ bool dc_read_dpcd(
 	struct core_link *link = core_dc->links[link_index];
 	enum ddc_result r = dal_ddc_service_read_dpcd_data(
 			link->ddc,
+			false,
+			I2C_MOT_UNDEF,
 			address,
 			data,
 			size);
 	return r == DDC_RESULT_SUCESSFULL;
 }
 
-bool dc_query_ddc_data(
+bool dc_write_aux_dpcd(
 		struct dc *dc,
 		uint32_t link_index,
 		uint32_t address,
-		uint8_t *write_buf,
-		uint32_t write_size,
-		uint8_t *read_buf,
-		uint32_t read_size) {
-
+		const uint8_t *data,
+		uint32_t size)
+{
 	struct core_dc *core_dc = DC_TO_CORE(dc);
-
 	struct core_link *link = core_dc->links[link_index];
 
-	bool result = dal_ddc_service_query_ddc_data(
+	enum ddc_result r = dal_ddc_service_write_dpcd_data(
 			link->ddc,
+			false,
+			I2C_MOT_UNDEF,
 			address,
-			write_buf,
-			write_size,
-			read_buf,
-			read_size);
-
-	return result;
+			data,
+			size);
+	return r == DDC_RESULT_SUCESSFULL;
 }
 
+bool dc_read_aux_i2c(
+		struct dc *dc,
+		uint32_t link_index,
+		enum i2c_mot_mode mot,
+		uint32_t address,
+		uint8_t *data,
+		uint32_t size)
+{
+	struct core_dc *core_dc = DC_TO_CORE(dc);
 
-bool dc_write_dpcd(
+		struct core_link *link = core_dc->links[link_index];
+		enum ddc_result r = dal_ddc_service_read_dpcd_data(
+			link->ddc,
+			true,
+			mot,
+			address,
+			data,
+			size);
+		return r == DDC_RESULT_SUCESSFULL;
+}
+
+bool dc_write_aux_i2c(
 		struct dc *dc,
 		uint32_t link_index,
+		enum i2c_mot_mode mot,
 		uint32_t address,
 		const uint8_t *data,
 		uint32_t size)
 {
 	struct core_dc *core_dc = DC_TO_CORE(dc);
-
 	struct core_link *link = core_dc->links[link_index];
 
 	enum ddc_result r = dal_ddc_service_write_dpcd_data(
 			link->ddc,
+			true,
+			mot,
 			address,
 			data,
 			size);
 	return r == DDC_RESULT_SUCESSFULL;
 }
 
+bool dc_query_ddc_data(
+		struct dc *dc,
+		uint32_t link_index,
+		uint32_t address,
+		uint8_t *write_buf,
+		uint32_t write_size,
+		uint8_t *read_buf,
+		uint32_t read_size) {
+
+	struct core_dc *core_dc = DC_TO_CORE(dc);
+
+	struct core_link *link = core_dc->links[link_index];
+
+	bool result = dal_ddc_service_query_ddc_data(
+			link->ddc,
+			address,
+			write_buf,
+			write_size,
+			read_buf,
+			read_size);
+
+	return result;
+}
+
 bool dc_submit_i2c(
 		struct dc *dc,
 		uint32_t link_index,
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link.c b/drivers/gpu/drm/amd/display/dc/core/dc_link.c
index 74dd272d7452..0f825f6326ab 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_link.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_link.c
@@ -464,39 +464,6 @@ static void link_disconnect_sink(struct core_link *link)
 	link->dpcd_sink_count = 0;
 }
 
-static enum dc_edid_status read_edid(
-	struct core_link *link,
-	struct core_sink *sink)
-{
-	uint32_t edid_retry = 3;
-	enum dc_edid_status edid_status;
-
-	/* some dongles read edid incorrectly the first time,
-	 * do check sum and retry to make sure read correct edid.
-	 */
-	do {
-		sink->public.dc_edid.length =
-				dal_ddc_service_edid_query(link->ddc);
-
-		if (0 == sink->public.dc_edid.length)
-			return EDID_NO_RESPONSE;
-
-		dal_ddc_service_get_edid_buf(link->ddc,
-				sink->public.dc_edid.raw_edid);
-		edid_status = dm_helpers_parse_edid_caps(
-				sink->ctx,
-				&sink->public.dc_edid,
-				&sink->public.edid_caps);
-		--edid_retry;
-		if (edid_status == EDID_BAD_CHECKSUM)
-			dm_logger_write(link->ctx->logger, LOG_WARNING,
-					"Bad EDID checksum, retry remain: %d\n",
-					edid_retry);
-	} while (edid_status == EDID_BAD_CHECKSUM && edid_retry > 0);
-
-	return edid_status;
-}
-
 static void detect_dp(
 	struct core_link *link,
 	struct display_sink_capability *sink_caps,
@@ -673,6 +640,9 @@ bool dc_link_detect(const struct dc_link *dc_link, bool boot)
 						link->ddc,
 						sink_caps.transaction_type);
 
+		link->public.aux_mode = dal_ddc_service_is_in_aux_transaction_mode(
+				link->ddc);
+
 		sink_init_data.link = &link->public;
 		sink_init_data.sink_signal = sink_caps.signal;
 
@@ -688,7 +658,10 @@ bool dc_link_detect(const struct dc_link *dc_link, bool boot)
 		sink = DC_SINK_TO_CORE(dc_sink);
 		link->public.local_sink = &sink->public;
 
-		edid_status = read_edid(link, sink);
+		edid_status = dm_helpers_read_local_edid(
+				link->ctx,
+				&link->public,
+				&sink->public);
 
 		switch (edid_status) {
 		case EDID_BAD_CHECKSUM:
@@ -1500,11 +1473,13 @@ bool dc_link_setup_psr(const struct dc_link *dc_link,
 			 */
 			psr_configuration.bits.IRQ_HPD_WITH_CRC_ERROR    = 1;
 		}
-		dal_ddc_service_write_dpcd_data(
-					link->ddc,
-					368,
-					&psr_configuration.raw,
-					sizeof(psr_configuration.raw));
+
+		dm_helpers_dp_write_dpcd(
+			link->ctx,
+			dc_link,
+			368,
+			&psr_configuration.raw,
+			sizeof(psr_configuration.raw));
 
 		psr_context.channel = link->ddc->ddc_pin->hw_info.ddc_channel;
 		if (psr_context.channel == 0)
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link_ddc.c b/drivers/gpu/drm/amd/display/dc/core/dc_link_ddc.c
index 4e9465b630d1..2f5a89c5b063 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_link_ddc.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_link_ddc.c
@@ -58,30 +58,6 @@ struct dp_hdmi_dongle_signature_data {
 	uint8_t eot;/* end of transmition '\x4' */
 };
 
-/* Address range from 0x00 to 0x1F.*/
-#define DP_ADAPTOR_TYPE2_SIZE 0x20
-#define DP_ADAPTOR_TYPE2_REG_ID 0x10
-#define DP_ADAPTOR_TYPE2_REG_MAX_TMDS_CLK 0x1D
-/* Identifies adaptor as Dual-mode adaptor */
-#define DP_ADAPTOR_TYPE2_ID 0xA0
-/* MHz*/
-#define DP_ADAPTOR_TYPE2_MAX_TMDS_CLK 600
-/* MHz*/
-#define DP_ADAPTOR_TYPE2_MIN_TMDS_CLK 25
-/* kHZ*/
-#define DP_ADAPTOR_DVI_MAX_TMDS_CLK 165000
-/* kHZ*/
-#define DP_ADAPTOR_HDMI_SAFE_MAX_TMDS_CLK 165000
-
-#define DDC_I2C_COMMAND_ENGINE I2C_COMMAND_ENGINE_SW
-
-enum edid_read_result {
-	EDID_READ_RESULT_EDID_MATCH = 0,
-	EDID_READ_RESULT_EDID_MISMATCH,
-	EDID_READ_RESULT_CHECKSUM_READ_ERR,
-	EDID_READ_RESULT_VENDOR_READ_ERR
-};
-
 /* SCDC Address defines (HDMI 2.0)*/
 #define HDMI_SCDC_WRITE_UPDATE_0_ARRAY 3
 #define HDMI_SCDC_ADDRESS  0x54
@@ -392,7 +368,7 @@ static uint32_t defer_delay_converter_wa(
 
 #define DP_TRANSLATOR_DELAY 5
 
-static uint32_t get_defer_delay(struct ddc_service *ddc)
+uint32_t get_defer_delay(struct ddc_service *ddc)
 {
 	uint32_t defer_delay = 0;
 
@@ -451,307 +427,6 @@ static bool i2c_read(
 			&command);
 }
 
-static uint8_t aux_read_edid_block(
-	struct ddc_service *ddc,
-	uint8_t address,
-	uint8_t index,
-	uint8_t *buf)
-{
-	struct aux_command cmd = {
-		.payloads = NULL,
-		.number_of_payloads = 0,
-		.defer_delay = get_defer_delay(ddc),
-		.max_defer_write_retry = 0 };
-
-	uint8_t retrieved = 0;
-	uint8_t base_offset =
-		(index % DDC_EDID_BLOCKS_PER_SEGMENT) * DDC_EDID_BLOCK_SIZE;
-	uint8_t segment = index / DDC_EDID_BLOCKS_PER_SEGMENT;
-
-	for (retrieved = 0; retrieved < DDC_EDID_BLOCK_SIZE;
-		retrieved += DEFAULT_AUX_MAX_DATA_SIZE) {
-
-		uint8_t offset = base_offset + retrieved;
-
-		struct aux_payload payloads[3] = {
-			{
-			.i2c_over_aux = true,
-			.write = true,
-			.address = DDC_EDID_SEGMENT_ADDRESS,
-			.length = 1,
-			.data = &segment },
-			{
-			.i2c_over_aux = true,
-			.write = true,
-			.address = address,
-			.length = 1,
-			.data = &offset },
-			{
-			.i2c_over_aux = true,
-			.write = false,
-			.address = address,
-			.length = DEFAULT_AUX_MAX_DATA_SIZE,
-			.data = &buf[retrieved] } };
-
-		if (segment == 0) {
-			cmd.payloads = &payloads[1];
-			cmd.number_of_payloads = 2;
-		} else {
-			cmd.payloads = payloads;
-			cmd.number_of_payloads = 3;
-		}
-
-		if (!dal_i2caux_submit_aux_command(
-			ddc->ctx->i2caux,
-			ddc->ddc_pin,
-			&cmd))
-			/* cannot read, break*/
-			break;
-	}
-
-	/* Reset segment to 0. Needed by some panels */
-	if (0 != segment) {
-		struct aux_payload payloads[1] = { {
-			.i2c_over_aux = true,
-			.write = true,
-			.address = DDC_EDID_SEGMENT_ADDRESS,
-			.length = 1,
-			.data = &segment } };
-		bool result = false;
-
-		segment = 0;
-
-		cmd.number_of_payloads = ARRAY_SIZE(payloads);
-		cmd.payloads = payloads;
-
-		result = dal_i2caux_submit_aux_command(
-			ddc->ctx->i2caux,
-			ddc->ddc_pin,
-			&cmd);
-
-		if (false == result)
-			dm_logger_write(
-				ddc->ctx->logger, LOG_ERROR,
-				"%s: Writing of EDID Segment (0x30) failed!\n",
-				__func__);
-	}
-
-	return retrieved;
-}
-
-static uint8_t i2c_read_edid_block(
-	struct ddc_service *ddc,
-	uint8_t address,
-	uint8_t index,
-	uint8_t *buf)
-{
-	bool ret = false;
-	uint8_t offset = (index % DDC_EDID_BLOCKS_PER_SEGMENT) *
-		DDC_EDID_BLOCK_SIZE;
-	uint8_t segment = index / DDC_EDID_BLOCKS_PER_SEGMENT;
-
-	struct i2c_command cmd = {
-		.payloads = NULL,
-		.number_of_payloads = 0,
-		.engine = DDC_I2C_COMMAND_ENGINE,
-		.speed = ddc->ctx->dc->caps.i2c_speed_in_khz };
-
-	struct i2c_payload payloads[3] = {
-		{
-		.write = true,
-		.address = DDC_EDID_SEGMENT_ADDRESS,
-		.length = 1,
-		.data = &segment },
-		{
-		.write = true,
-		.address = address,
-		.length = 1,
-		.data = &offset },
-		{
-		.write = false,
-		.address = address,
-		.length = DDC_EDID_BLOCK_SIZE,
-		.data = buf } };
-/*
- * Some I2C engines don't handle stop/start between write-offset and read-data
- * commands properly. For those displays, we have to force the newer E-DDC
- * behavior of repeated-start which can be enabled by runtime parameter. */
-/* Originally implemented for OnLive using NXP receiver chip */
-
-	if (index == 0 && !ddc->flags.FORCE_READ_REPEATED_START) {
-		/* base block, use use DDC2B, submit as 2 commands */
-		cmd.payloads = &payloads[1];
-		cmd.number_of_payloads = 1;
-
-		if (dm_helpers_submit_i2c(
-			ddc->ctx,
-			&ddc->link->public,
-			&cmd)) {
-
-			cmd.payloads = &payloads[2];
-			cmd.number_of_payloads = 1;
-
-			ret = dm_helpers_submit_i2c(
-					ddc->ctx,
-					&ddc->link->public,
-					&cmd);
-		}
-
-	} else {
-		/*
-		 * extension block use E-DDC, submit as 1 command
-		 * or if repeated-start is forced by runtime parameter
-		 */
-		if (segment != 0) {
-			/* include segment offset in command*/
-			cmd.payloads = payloads;
-			cmd.number_of_payloads = 3;
-		} else {
-			/* we are reading first segment,
-			 * segment offset is not required */
-			cmd.payloads = &payloads[1];
-			cmd.number_of_payloads = 2;
-		}
-
-		ret = dm_helpers_submit_i2c(
-				ddc->ctx,
-				&ddc->link->public,
-				&cmd);
-	}
-
-	return ret ? DDC_EDID_BLOCK_SIZE : 0;
-}
-
-static uint32_t query_edid_block(
-	struct ddc_service *ddc,
-	uint8_t address,
-	uint8_t index,
-	uint8_t *buf,
-	uint32_t size)
-{
-	uint32_t size_retrieved = 0;
-
-	if (size < DDC_EDID_BLOCK_SIZE)
-		return 0;
-
-	if (dal_ddc_service_is_in_aux_transaction_mode(ddc)) {
-		size_retrieved =
-			aux_read_edid_block(ddc, address, index, buf);
-	} else {
-		size_retrieved =
-			i2c_read_edid_block(ddc, address, index, buf);
-	}
-
-	return size_retrieved;
-}
-
-#define DDC_DPCD_EDID_CHECKSUM_WRITE_ADDRESS 0x261
-#define DDC_TEST_ACK_ADDRESS 0x260
-#define DDC_DPCD_EDID_TEST_ACK 0x04
-#define DDC_DPCD_EDID_TEST_MASK 0x04
-#define DDC_DPCD_TEST_REQUEST_ADDRESS 0x218
-
-/* AG TODO GO throug DM callback here like for DPCD */
-
-static void write_dp_edid_checksum(
-	struct ddc_service *ddc,
-	uint8_t checksum)
-{
-	uint8_t dpcd_data;
-
-	dal_ddc_service_read_dpcd_data(
-		ddc,
-		DDC_DPCD_TEST_REQUEST_ADDRESS,
-		&dpcd_data,
-		1);
-
-	if (dpcd_data & DDC_DPCD_EDID_TEST_MASK) {
-
-		dal_ddc_service_write_dpcd_data(
-			ddc,
-			DDC_DPCD_EDID_CHECKSUM_WRITE_ADDRESS,
-			&checksum,
-			1);
-
-		dpcd_data = DDC_DPCD_EDID_TEST_ACK;
-
-		dal_ddc_service_write_dpcd_data(
-			ddc,
-			DDC_TEST_ACK_ADDRESS,
-			&dpcd_data,
-			1);
-	}
-}
-
-uint32_t dal_ddc_service_edid_query(struct ddc_service *ddc)
-{
-	uint32_t bytes_read = 0;
-	uint32_t ext_cnt = 0;
-
-	uint8_t address;
-	uint32_t i;
-
-	for (address = DDC_EDID_ADDRESS_START;
-		address <= DDC_EDID_ADDRESS_END; ++address) {
-
-		bytes_read = query_edid_block(
-			ddc,
-			address,
-			0,
-			ddc->edid_buf,
-			sizeof(ddc->edid_buf) - bytes_read);
-
-		if (bytes_read != DDC_EDID_BLOCK_SIZE)
-			continue;
-
-		/* get the number of ext blocks*/
-		ext_cnt = ddc->edid_buf[DDC_EDID_EXT_COUNT_OFFSET];
-
-		/* EDID 2.0, need to read 1 more block because EDID2.0 is
-		 * 256 byte in size*/
-		if (ddc->edid_buf[DDC_EDID_20_SIGNATURE_OFFSET] ==
-			DDC_EDID_20_SIGNATURE)
-				ext_cnt = 1;
-
-		for (i = 0; i < ext_cnt; i++) {
-			/* read additional ext blocks accordingly */
-			bytes_read += query_edid_block(
-					ddc,
-					address,
-					i+1,
-					&ddc->edid_buf[bytes_read],
-					sizeof(ddc->edid_buf) - bytes_read);
-		}
-
-		/*this is special code path for DP compliance*/
-		if (DDC_TRANSACTION_TYPE_I2C_OVER_AUX == ddc->transaction_type)
-			write_dp_edid_checksum(
-				ddc,
-				ddc->edid_buf[(ext_cnt * DDC_EDID_BLOCK_SIZE) +
-				DDC_EDID1X_CHECKSUM_OFFSET]);
-
-		/*remembers the address where we fetch the EDID from
-		 * for later signature check use */
-		ddc->address = address;
-
-		break;/* already read edid, done*/
-	}
-
-	ddc->edid_buf_len = bytes_read;
-	return bytes_read;
-}
-
-uint32_t dal_ddc_service_get_edid_buf_len(struct ddc_service *ddc)
-{
-	return ddc->edid_buf_len;
-}
-
-void dal_ddc_service_get_edid_buf(struct ddc_service *ddc, uint8_t *edid_buf)
-{
-	memmove(edid_buf,
-			ddc->edid_buf, ddc->edid_buf_len);
-}
-
 void dal_ddc_service_i2c_query_dp_dual_mode_adaptor(
 	struct ddc_service *ddc,
 	struct display_sink_capability *sink_cap)
@@ -960,12 +635,14 @@ bool dal_ddc_service_query_ddc_data(
 
 enum ddc_result dal_ddc_service_read_dpcd_data(
 	struct ddc_service *ddc,
+	bool i2c,
+	enum i2c_mot_mode mot,
 	uint32_t address,
 	uint8_t *data,
 	uint32_t len)
 {
 	struct aux_payload read_payload = {
-		.i2c_over_aux = false,
+		.i2c_over_aux = i2c,
 		.write = false,
 		.address = address,
 		.length = len,
@@ -976,6 +653,7 @@ enum ddc_result dal_ddc_service_read_dpcd_data(
 		.number_of_payloads = 1,
 		.defer_delay = 0,
 		.max_defer_write_retry = 0,
+		.mot = mot
 	};
 
 	if (len > DEFAULT_AUX_MAX_DATA_SIZE) {
@@ -994,12 +672,14 @@ enum ddc_result dal_ddc_service_read_dpcd_data(
 
 enum ddc_result dal_ddc_service_write_dpcd_data(
 	struct ddc_service *ddc,
+	bool i2c,
+	enum i2c_mot_mode mot,
 	uint32_t address,
 	const uint8_t *data,
 	uint32_t len)
 {
 	struct aux_payload write_payload = {
-		.i2c_over_aux = false,
+		.i2c_over_aux = i2c,
 		.write = true,
 		.address = address,
 		.length = len,
@@ -1010,6 +690,7 @@ enum ddc_result dal_ddc_service_write_dpcd_data(
 		.number_of_payloads = 1,
 		.defer_delay = 0,
 		.max_defer_write_retry = 0,
+		.mot = mot
 	};
 
 	if (len > DEFAULT_AUX_MAX_DATA_SIZE) {
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c b/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c
index 6cfd88086044..802d8cc99ea3 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_link_dp.c
@@ -1481,22 +1481,25 @@ static bool handle_hpd_irq_psr_sink(const struct core_link *link)
 	if (link->public.psr_caps.psr_version == 0)
 		return false;
 
-	dal_ddc_service_read_dpcd_data(
-					link->ddc,
-					368 /*DpcdAddress_PSR_Enable_Cfg*/,
-					&psr_configuration.raw,
-					sizeof(psr_configuration.raw));
+	dm_helpers_dp_read_dpcd(
+		link->ctx,
+		&link->public,
+		368,/*DpcdAddress_PSR_Enable_Cfg*/
+		&psr_configuration.raw,
+		sizeof(psr_configuration.raw));
+
 
 	if (psr_configuration.bits.ENABLE) {
 		unsigned char dpcdbuf[3] = {0};
 		union psr_error_status psr_error_status;
 		union psr_sink_psr_status psr_sink_psr_status;
 
-		dal_ddc_service_read_dpcd_data(
-					link->ddc,
-					0x2006 /*DpcdAddress_PSR_Error_Status*/,
-					(unsigned char *) dpcdbuf,
-					sizeof(dpcdbuf));
+		dm_helpers_dp_read_dpcd(
+			link->ctx,
+			&link->public,
+			0x2006, /*DpcdAddress_PSR_Error_Status*/
+			(unsigned char *) dpcdbuf,
+			sizeof(dpcdbuf));
 
 		/*DPCD 2006h   ERROR STATUS*/
 		psr_error_status.raw = dpcdbuf[0];
@@ -1506,9 +1509,10 @@ static bool handle_hpd_irq_psr_sink(const struct core_link *link)
 		if (psr_error_status.bits.LINK_CRC_ERROR ||
 				psr_error_status.bits.RFB_STORAGE_ERROR) {
 			/* Acknowledge and clear error bits */
-			dal_ddc_service_write_dpcd_data(
-				link->ddc,
-				8198 /*DpcdAddress_PSR_Error_Status*/,
+			dm_helpers_dp_write_dpcd(
+				link->ctx,
+				&link->public,
+				8198,/*DpcdAddress_PSR_Error_Status*/
 				&psr_error_status.raw,
 				sizeof(psr_error_status.raw));
 
diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h
index d2960552c78e..a27a6aba0df1 100644
--- a/drivers/gpu/drm/amd/display/dc/dc.h
+++ b/drivers/gpu/drm/amd/display/dc/dc.h
@@ -594,6 +594,7 @@ struct dc_link {
 	union compliance_test_state compliance_test_state;
 
 	void *priv;
+	bool aux_mode;
 };
 
 struct dpcd_caps {
@@ -788,20 +789,36 @@ const struct ddc_service *dc_get_ddc_at_index(
  * DPCD access interfaces
  */
 
-bool dc_read_dpcd(
+bool dc_read_aux_dpcd(
 		struct dc *dc,
 		uint32_t link_index,
 		uint32_t address,
 		uint8_t *data,
 		uint32_t size);
 
-bool dc_write_dpcd(
+bool dc_write_aux_dpcd(
 		struct dc *dc,
 		uint32_t link_index,
 		uint32_t address,
 		const uint8_t *data,
 		uint32_t size);
 
+bool dc_read_aux_i2c(
+		struct dc *dc,
+		uint32_t link_index,
+		enum i2c_mot_mode mot,
+		uint32_t address,
+		uint8_t *data,
+		uint32_t size);
+
+bool dc_write_aux_i2c(
+		struct dc *dc,
+		uint32_t link_index,
+		enum i2c_mot_mode mot,
+		uint32_t address,
+		const uint8_t *data,
+		uint32_t size);
+
 bool dc_query_ddc_data(
 		struct dc *dc,
 		uint32_t link_index,
diff --git a/drivers/gpu/drm/amd/display/dc/dc_types.h b/drivers/gpu/drm/amd/display/dc/dc_types.h
index 242dd7b3b6b1..e0436e317341 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_types.h
+++ b/drivers/gpu/drm/amd/display/dc/dc_types.h
@@ -489,4 +489,10 @@ struct psr_caps {
 	unsigned int psr_sdp_transmit_line_num_deadline;
 };
 
+enum i2c_mot_mode {
+	I2C_MOT_UNDEF,
+	I2C_MOT_TRUE,
+	I2C_MOT_FALSE
+};
+
 #endif /* DC_TYPES_H_ */
diff --git a/drivers/gpu/drm/amd/display/dc/dm_helpers.h b/drivers/gpu/drm/amd/display/dc/dm_helpers.h
index d6c52d31f0f0..c15a25ce8049 100644
--- a/drivers/gpu/drm/amd/display/dc/dm_helpers.h
+++ b/drivers/gpu/drm/amd/display/dc/dm_helpers.h
@@ -98,4 +98,14 @@ bool dm_helpers_submit_i2c(
 		struct i2c_command *cmd);
 
 
+
+
+
+
+enum dc_edid_status dm_helpers_read_local_edid(
+		struct dc_context *ctx,
+		struct dc_link *link,
+		struct dc_sink *sink);
+
+
 #endif /* __DM_HELPERS__ */
diff --git a/drivers/gpu/drm/amd/display/dc/i2caux/i2caux.c b/drivers/gpu/drm/amd/display/dc/i2caux/i2caux.c
index bd84b932aaae..0743265e933c 100644
--- a/drivers/gpu/drm/amd/display/dc/i2caux/i2caux.c
+++ b/drivers/gpu/drm/amd/display/dc/i2caux/i2caux.c
@@ -185,6 +185,7 @@ bool dal_i2caux_submit_aux_command(
 	struct aux_engine *engine;
 	uint8_t index_of_payload = 0;
 	bool result;
+	bool mot;
 
 	if (!ddc) {
 		BREAK_TO_DEBUGGER();
@@ -207,12 +208,14 @@ bool dal_i2caux_submit_aux_command(
 	result = true;
 
 	while (index_of_payload < cmd->number_of_payloads) {
-		bool mot = (index_of_payload != cmd->number_of_payloads - 1);
-
 		struct aux_payload *payload = cmd->payloads + index_of_payload;
-
 		struct i2caux_transaction_request request = { 0 };
 
+		if (cmd->mot == I2C_MOT_UNDEF)
+			mot = (index_of_payload != cmd->number_of_payloads - 1);
+		else
+			mot = (cmd->mot == I2C_MOT_TRUE);
+
 		request.operation = payload->write ?
 			I2CAUX_TRANSACTION_WRITE :
 			I2CAUX_TRANSACTION_READ;
diff --git a/drivers/gpu/drm/amd/display/dc/inc/dc_link_ddc.h b/drivers/gpu/drm/amd/display/dc/inc/dc_link_ddc.h
index 830fc3d039c9..9c2f670c3dc3 100644
--- a/drivers/gpu/drm/amd/display/dc/inc/dc_link_ddc.h
+++ b/drivers/gpu/drm/amd/display/dc/inc/dc_link_ddc.h
@@ -31,6 +31,23 @@
 
 #define EDID_SEGMENT_SIZE 256
 
+/* Address range from 0x00 to 0x1F.*/
+#define DP_ADAPTOR_TYPE2_SIZE 0x20
+#define DP_ADAPTOR_TYPE2_REG_ID 0x10
+#define DP_ADAPTOR_TYPE2_REG_MAX_TMDS_CLK 0x1D
+/* Identifies adaptor as Dual-mode adaptor */
+#define DP_ADAPTOR_TYPE2_ID 0xA0
+/* MHz*/
+#define DP_ADAPTOR_TYPE2_MAX_TMDS_CLK 600
+/* MHz*/
+#define DP_ADAPTOR_TYPE2_MIN_TMDS_CLK 25
+/* kHZ*/
+#define DP_ADAPTOR_DVI_MAX_TMDS_CLK 165000
+/* kHZ*/
+#define DP_ADAPTOR_HDMI_SAFE_MAX_TMDS_CLK 165000
+
+#define DDC_I2C_COMMAND_ENGINE I2C_COMMAND_ENGINE_SW
+
 struct ddc_service;
 struct graphics_object_id;
 enum ddc_result;
@@ -83,12 +100,6 @@ void dal_ddc_service_set_transaction_type(
 
 bool dal_ddc_service_is_in_aux_transaction_mode(struct ddc_service *ddc);
 
-uint32_t dal_ddc_service_edid_query(struct ddc_service *ddc);
-
-uint32_t dal_ddc_service_get_edid_buf_len(struct ddc_service *ddc);
-
-void dal_ddc_service_get_edid_buf(struct ddc_service *ddc, uint8_t *edid_buf);
-
 void dal_ddc_service_i2c_query_dp_dual_mode_adaptor(
 		struct ddc_service *ddc,
 		struct display_sink_capability *sink_cap);
@@ -103,12 +114,16 @@ bool dal_ddc_service_query_ddc_data(
 
 enum ddc_result dal_ddc_service_read_dpcd_data(
 		struct ddc_service *ddc,
+		bool i2c,
+		enum i2c_mot_mode mot,
 		uint32_t address,
 		uint8_t *data,
 		uint32_t len);
 
 enum ddc_result dal_ddc_service_write_dpcd_data(
 		struct ddc_service *ddc,
+		bool i2c,
+		enum i2c_mot_mode mot,
 		uint32_t address,
 		const uint8_t *data,
 		uint32_t len);
@@ -130,16 +145,7 @@ void dal_ddc_service_set_ddc_pin(
 
 struct ddc *dal_ddc_service_get_ddc_pin(struct ddc_service *ddc_service);
 
-enum ddc_result dal_ddc_service_read_dpcd_data(
-		struct ddc_service *ddc,
-		uint32_t address,
-		uint8_t *data,
-		uint32_t len);
-enum ddc_result dal_ddc_service_write_dpcd_data(
-		struct ddc_service *ddc,
-		uint32_t address,
-		const uint8_t *data,
-		uint32_t len);
+uint32_t get_defer_delay(struct ddc_service *ddc);
 
 #endif /* __DAL_DDC_SERVICE_H__ */
 
diff --git a/drivers/gpu/drm/amd/display/include/ddc_service_types.h b/drivers/gpu/drm/amd/display/include/ddc_service_types.h
index effe03b8f418..0ff2a899b8f7 100644
--- a/drivers/gpu/drm/amd/display/include/ddc_service_types.h
+++ b/drivers/gpu/drm/amd/display/include/ddc_service_types.h
@@ -115,34 +115,6 @@ struct av_sync_data {
 	uint8_t aud_del_ins3;/* DPCD 0002Dh */
 };
 
-/** EDID retrieval related constants, also used by MstMgr **/
-
-#define DDC_EDID_SEGMENT_SIZE 256
-#define DDC_EDID_BLOCK_SIZE 128
-#define DDC_EDID_BLOCKS_PER_SEGMENT \
-	(DDC_EDID_SEGMENT_SIZE / DDC_EDID_BLOCK_SIZE)
-
-#define DDC_EDID_EXT_COUNT_OFFSET 0x7E
-
-#define DDC_EDID_ADDRESS_START 0x50
-#define DDC_EDID_ADDRESS_END 0x52
-#define DDC_EDID_SEGMENT_ADDRESS 0x30
-
-/* signatures for Edid 1x */
-#define DDC_EDID1X_VENDORID_SIGNATURE_OFFSET 8
-#define DDC_EDID1X_VENDORID_SIGNATURE_LEN 4
-#define DDC_EDID1X_EXT_CNT_AND_CHECKSUM_OFFSET 126
-#define DDC_EDID1X_EXT_CNT_AND_CHECKSUM_LEN 2
-#define DDC_EDID1X_CHECKSUM_OFFSET 127
-/* signatures for Edid 20*/
-#define DDC_EDID_20_SIGNATURE_OFFSET 0
-#define DDC_EDID_20_SIGNATURE 0x20
-
-#define DDC_EDID20_VENDORID_SIGNATURE_OFFSET 1
-#define DDC_EDID20_VENDORID_SIGNATURE_LEN 4
-#define DDC_EDID20_CHECKSUM_OFFSET 255
-#define DDC_EDID20_CHECKSUM_LEN 1
-
 /*DP to VGA converter*/
 static const uint8_t DP_VGA_CONVERTER_ID_1[] = "mVGAa";
 /*DP to Dual link DVI converter*/
diff --git a/drivers/gpu/drm/amd/display/include/i2caux_interface.h b/drivers/gpu/drm/amd/display/include/i2caux_interface.h
index d2ec04d1c592..13a3c82d118f 100644
--- a/drivers/gpu/drm/amd/display/include/i2caux_interface.h
+++ b/drivers/gpu/drm/amd/display/include/i2caux_interface.h
@@ -26,6 +26,7 @@
 #ifndef __DAL_I2CAUX_INTERFACE_H__
 #define __DAL_I2CAUX_INTERFACE_H__
 
+#include "dc_types.h"
 #include "gpio_service_interface.h"
 
 
@@ -54,6 +55,8 @@ struct aux_command {
 
 	/* zero means "use default value" */
 	uint32_t max_defer_write_retry;
+
+	enum i2c_mot_mode mot;
 };
 
 union aux_config {
diff --git a/drivers/gpu/drm/amd/display/modules/ddc_service/ddc_service.c b/drivers/gpu/drm/amd/display/modules/ddc_service/ddc_service.c
new file mode 100644
index 000000000000..02177527e205
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/modules/ddc_service/ddc_service.c
@@ -0,0 +1,381 @@
+/*
+ * Copyright 2016 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: AMD
+ *
+ */
+
+#include "dm_services.h"
+#include "dm_helpers.h"
+#include "gpio_service_interface.h"
+#include "include/ddc_service_types.h"
+#include "include/grph_object_id.h"
+#include "include/dpcd_defs.h"
+#include "include/logger_interface.h"
+#include "include/vector.h"
+#include "core_types.h"
+#include "dc_link_ddc.h"
+#include "mod_ddc_service.h"
+
+enum edid_read_result {
+	EDID_READ_RESULT_EDID_MATCH = 0,
+	EDID_READ_RESULT_EDID_MISMATCH,
+	EDID_READ_RESULT_CHECKSUM_READ_ERR,
+	EDID_READ_RESULT_VENDOR_READ_ERR
+};
+
+static uint8_t aux_read_edid_block(
+	struct ddc_service *ddc,
+	uint8_t address,
+	uint8_t index,
+	uint8_t *buf)
+{
+	struct aux_command cmd = {
+		.payloads = NULL,
+		.number_of_payloads = 0,
+		.defer_delay = get_defer_delay(ddc),
+		.max_defer_write_retry = 0 };
+
+	uint8_t retrieved = 0;
+	uint8_t base_offset =
+		(index % DDC_EDID_BLOCKS_PER_SEGMENT) * DDC_EDID_BLOCK_SIZE;
+	uint8_t segment = index / DDC_EDID_BLOCKS_PER_SEGMENT;
+
+	for (retrieved = 0; retrieved < DDC_EDID_BLOCK_SIZE;
+		retrieved += DEFAULT_AUX_MAX_DATA_SIZE) {
+
+		uint8_t offset = base_offset + retrieved;
+
+		struct aux_payload payloads[3] = {
+			{
+			.i2c_over_aux = true,
+			.write = true,
+			.address = DDC_EDID_SEGMENT_ADDRESS,
+			.length = 1,
+			.data = &segment },
+			{
+			.i2c_over_aux = true,
+			.write = true,
+			.address = address,
+			.length = 1,
+			.data = &offset },
+			{
+			.i2c_over_aux = true,
+			.write = false,
+			.address = address,
+			.length = DEFAULT_AUX_MAX_DATA_SIZE,
+			.data = &buf[retrieved] } };
+
+		if (segment == 0) {
+			cmd.payloads = &payloads[1];
+			cmd.number_of_payloads = 2;
+		} else {
+			cmd.payloads = payloads;
+			cmd.number_of_payloads = 3;
+		}
+
+		if (!dal_i2caux_submit_aux_command(
+			ddc->ctx->i2caux,
+			ddc->ddc_pin,
+			&cmd))
+			/* cannot read, break*/
+			break;
+	}
+
+	/* Reset segment to 0. Needed by some panels */
+	if (0 != segment) {
+		struct aux_payload payloads[1] = { {
+			.i2c_over_aux = true,
+			.write = true,
+			.address = DDC_EDID_SEGMENT_ADDRESS,
+			.length = 1,
+			.data = &segment } };
+		bool result = false;
+
+		segment = 0;
+
+		cmd.number_of_payloads = ARRAY_SIZE(payloads);
+		cmd.payloads = payloads;
+
+		result = dal_i2caux_submit_aux_command(
+			ddc->ctx->i2caux,
+			ddc->ddc_pin,
+			&cmd);
+
+		if (false == result)
+			dm_logger_write(
+				ddc->ctx->logger, LOG_ERROR,
+				"%s: Writing of EDID Segment (0x30) failed!\n",
+				__func__);
+	}
+
+	return retrieved;
+}
+
+static uint8_t i2c_read_edid_block(
+	struct ddc_service *ddc,
+	uint8_t address,
+	uint8_t index,
+	uint8_t *buf)
+{
+	bool ret = false;
+	uint8_t offset = (index % DDC_EDID_BLOCKS_PER_SEGMENT) *
+		DDC_EDID_BLOCK_SIZE;
+	uint8_t segment = index / DDC_EDID_BLOCKS_PER_SEGMENT;
+
+	struct i2c_command cmd = {
+		.payloads = NULL,
+		.number_of_payloads = 0,
+		.engine = DDC_I2C_COMMAND_ENGINE,
+		.speed = ddc->ctx->dc->caps.i2c_speed_in_khz };
+
+	struct i2c_payload payloads[3] = {
+		{
+		.write = true,
+		.address = DDC_EDID_SEGMENT_ADDRESS,
+		.length = 1,
+		.data = &segment },
+		{
+		.write = true,
+		.address = address,
+		.length = 1,
+		.data = &offset },
+		{
+		.write = false,
+		.address = address,
+		.length = DDC_EDID_BLOCK_SIZE,
+		.data = buf } };
+/*
+ * Some I2C engines don't handle stop/start between write-offset and read-data
+ * commands properly. For those displays, we have to force the newer E-DDC
+ * behavior of repeated-start which can be enabled by runtime parameter. */
+/* Originally implemented for OnLive using NXP receiver chip */
+
+	if (index == 0 && !ddc->flags.FORCE_READ_REPEATED_START) {
+		/* base block, use use DDC2B, submit as 2 commands */
+		cmd.payloads = &payloads[1];
+		cmd.number_of_payloads = 1;
+
+		if (dm_helpers_submit_i2c(
+			ddc->ctx,
+			&ddc->link->public,
+			&cmd)) {
+
+			cmd.payloads = &payloads[2];
+			cmd.number_of_payloads = 1;
+
+			ret = dm_helpers_submit_i2c(
+					ddc->ctx,
+					&ddc->link->public,
+					&cmd);
+		}
+
+	} else {
+		/*
+		 * extension block use E-DDC, submit as 1 command
+		 * or if repeated-start is forced by runtime parameter
+		 */
+		if (segment != 0) {
+			/* include segment offset in command*/
+			cmd.payloads = payloads;
+			cmd.number_of_payloads = 3;
+		} else {
+			/* we are reading first segment,
+			 * segment offset is not required */
+			cmd.payloads = &payloads[1];
+			cmd.number_of_payloads = 2;
+		}
+
+		ret = dm_helpers_submit_i2c(
+				ddc->ctx,
+				&ddc->link->public,
+				&cmd);
+	}
+
+	return ret ? DDC_EDID_BLOCK_SIZE : 0;
+}
+
+static uint32_t query_edid_block(
+	struct ddc_service *ddc,
+	uint8_t address,
+	uint8_t index,
+	uint8_t *buf,
+	uint32_t size)
+{
+	uint32_t size_retrieved = 0;
+
+	if (size < DDC_EDID_BLOCK_SIZE)
+		return 0;
+
+	if (dal_ddc_service_is_in_aux_transaction_mode(ddc)) {
+		size_retrieved =
+			aux_read_edid_block(ddc, address, index, buf);
+	} else {
+		size_retrieved =
+			i2c_read_edid_block(ddc, address, index, buf);
+	}
+
+	return size_retrieved;
+}
+
+#define DDC_DPCD_EDID_CHECKSUM_WRITE_ADDRESS 0x261
+#define DDC_TEST_ACK_ADDRESS 0x260
+#define DDC_DPCD_EDID_TEST_ACK 0x04
+#define DDC_DPCD_EDID_TEST_MASK 0x04
+#define DDC_DPCD_TEST_REQUEST_ADDRESS 0x218
+
+/* AG TODO GO throug DM callback here like for DPCD */
+
+static void write_dp_edid_checksum(
+	struct ddc_service *ddc,
+	uint8_t checksum)
+{
+	uint8_t dpcd_data;
+
+	dal_ddc_service_read_dpcd_data(
+		ddc,
+		false,
+		I2C_MOT_UNDEF,
+		DDC_DPCD_TEST_REQUEST_ADDRESS,
+		&dpcd_data,
+		1);
+
+	if (dpcd_data & DDC_DPCD_EDID_TEST_MASK) {
+
+		dal_ddc_service_write_dpcd_data(
+			ddc,
+			true,
+			I2C_MOT_UNDEF,
+			DDC_DPCD_EDID_CHECKSUM_WRITE_ADDRESS,
+			&checksum,
+			1);
+
+		dpcd_data = DDC_DPCD_EDID_TEST_ACK;
+
+		dal_ddc_service_write_dpcd_data(
+			ddc,
+			true,
+			I2C_MOT_UNDEF,
+			DDC_TEST_ACK_ADDRESS,
+			&dpcd_data,
+			1);
+	}
+}
+
+void dal_ddc_service_get_edid_buf(struct ddc_service *ddc, uint8_t *edid_buf)
+{
+	memmove(edid_buf,
+			ddc->edid_buf, ddc->edid_buf_len);
+}
+
+uint32_t dal_ddc_service_edid_query(struct ddc_service *ddc)
+{
+	uint32_t bytes_read = 0;
+	uint32_t ext_cnt = 0;
+
+	uint8_t address;
+	uint32_t i;
+
+	for (address = DDC_EDID_ADDRESS_START;
+		address <= DDC_EDID_ADDRESS_END; ++address) {
+
+		bytes_read = query_edid_block(
+			ddc,
+			address,
+			0,
+			ddc->edid_buf,
+			sizeof(ddc->edid_buf) - bytes_read);
+
+		if (bytes_read != DDC_EDID_BLOCK_SIZE)
+			continue;
+
+		/* get the number of ext blocks*/
+		ext_cnt = ddc->edid_buf[DDC_EDID_EXT_COUNT_OFFSET];
+
+		/* EDID 2.0, need to read 1 more block because EDID2.0 is
+		 * 256 byte in size*/
+		if (ddc->edid_buf[DDC_EDID_20_SIGNATURE_OFFSET] ==
+			DDC_EDID_20_SIGNATURE)
+				ext_cnt = 1;
+
+		for (i = 0; i < ext_cnt; i++) {
+			/* read additional ext blocks accordingly */
+			bytes_read += query_edid_block(
+					ddc,
+					address,
+					i+1,
+					&ddc->edid_buf[bytes_read],
+					sizeof(ddc->edid_buf) - bytes_read);
+		}
+
+		/*this is special code path for DP compliance*/
+		if (DDC_TRANSACTION_TYPE_I2C_OVER_AUX == ddc->transaction_type)
+			write_dp_edid_checksum(
+				ddc,
+				ddc->edid_buf[(ext_cnt * DDC_EDID_BLOCK_SIZE) +
+				DDC_EDID1X_CHECKSUM_OFFSET]);
+
+		/*remembers the address where we fetch the EDID from
+		 * for later signature check use */
+		ddc->address = address;
+
+		break;/* already read edid, done*/
+	}
+
+	ddc->edid_buf_len = bytes_read;
+	return bytes_read;
+}
+
+enum dc_edid_status read_edid(
+	struct dc_context *ctx,
+	struct dc_link *dc_link,
+	struct dc_sink *dc_sink)
+{
+	uint32_t edid_retry = 3;
+	enum dc_edid_status edid_status;
+	struct core_link *link = DC_LINK_TO_LINK(dc_link);
+
+	/* some dongles read edid incorrectly the first time,
+	 * do check sum and retry to make sure read correct edid.
+	 */
+	do {
+		dc_sink->dc_edid.length =
+				dal_ddc_service_edid_query(link->ddc);
+
+		if (0 == dc_sink->dc_edid.length)
+			return EDID_NO_RESPONSE;
+
+		dal_ddc_service_get_edid_buf(link->ddc,
+				dc_sink->dc_edid.raw_edid);
+		edid_status = dm_helpers_parse_edid_caps(
+				ctx,
+				&dc_sink->dc_edid,
+				&dc_sink->edid_caps);
+		--edid_retry;
+		if (edid_status == EDID_BAD_CHECKSUM)
+			dm_logger_write(link->ctx->logger, LOG_WARNING,
+					"Bad EDID checksum, retry remain: %d\n",
+					edid_retry);
+	} while (edid_status == EDID_BAD_CHECKSUM && edid_retry > 0);
+
+	return edid_status;
+}
+
diff --git a/drivers/gpu/drm/amd/display/modules/inc/mod_ddc_service.h b/drivers/gpu/drm/amd/display/modules/inc/mod_ddc_service.h
new file mode 100644
index 000000000000..b26e4c43cfbd
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/modules/inc/mod_ddc_service.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2016 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: AMD
+ *
+ */
+
+
+#ifndef MOD_DDC_SERVICE_H_
+#define MOD_DDC_SERVICE_H_
+
+
+/** EDID retrieval related constants, also used by MstMgr **/
+
+#define DDC_EDID_SEGMENT_SIZE 256
+#define DDC_EDID_BLOCK_SIZE 128
+#define DDC_EDID_BLOCKS_PER_SEGMENT \
+	(DDC_EDID_SEGMENT_SIZE / DDC_EDID_BLOCK_SIZE)
+
+#define DDC_EDID_EXT_COUNT_OFFSET 0x7E
+
+#define DDC_EDID_ADDRESS_START 0x50
+#define DDC_EDID_ADDRESS_END 0x52
+#define DDC_EDID_SEGMENT_ADDRESS 0x30
+
+/* signatures for Edid 1x */
+#define DDC_EDID1X_VENDORID_SIGNATURE_OFFSET 8
+#define DDC_EDID1X_VENDORID_SIGNATURE_LEN 4
+#define DDC_EDID1X_EXT_CNT_AND_CHECKSUM_OFFSET 126
+#define DDC_EDID1X_EXT_CNT_AND_CHECKSUM_LEN 2
+#define DDC_EDID1X_CHECKSUM_OFFSET 127
+/* signatures for Edid 20*/
+#define DDC_EDID_20_SIGNATURE_OFFSET 0
+#define DDC_EDID_20_SIGNATURE 0x20
+
+#define DDC_EDID20_VENDORID_SIGNATURE_OFFSET 1
+#define DDC_EDID20_VENDORID_SIGNATURE_LEN 4
+#define DDC_EDID20_CHECKSUM_OFFSET 255
+#define DDC_EDID20_CHECKSUM_LEN 1
+
+enum dc_edid_status read_edid(
+	struct dc_context *ctx,
+	struct dc_link *dc_link,
+	struct dc_sink *dc_sink);
+
+#endif
-- 
2.11.0



More information about the amd-gfx mailing list