summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm')
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c8
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c165
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc.h1
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_types.h4
-rw-r--r--drivers/gpu/drm/amd/display/dc/dm_helpers.h5
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/link_detection.c14
6 files changed, 197 insertions, 0 deletions
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index 1e5953d8a90d..c7dd75c5d921 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -13376,6 +13376,14 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector,
}
}
+ /* Handle MCCS */
+ dm_helpers_read_mccs_caps(adev->dm.dc->ctx, amdgpu_dm_connector->dc_link, sink);
+ if ((sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A ||
+ as_type == FREESYNC_TYPE_PCON_IN_WHITELIST) &&
+ (!sink->edid_caps.freesync_vcp_code ||
+ (sink->edid_caps.freesync_vcp_code && !sink->mccs_caps.freesync_supported)))
+ freesync_capable = false;
+
update:
if (dm_con_state)
dm_con_state->freesync_capable = freesync_capable;
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 d5ea4fe84b73..1332969c7491 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
@@ -49,6 +49,45 @@
#include "ddc_service_types.h"
#include "clk_mgr.h"
+#define MCCS_DEST_ADDR (0x6E >> 1)
+#define MCCS_SRC_ADDR 0x51
+#define MCCS_LENGTH_OFFSET 0x80
+#define MCCS_MAX_DATA_SIZE 0x20
+
+enum mccs_op_code {
+ MCCS_OP_CODE_VCP_REQUEST = 0x01,
+ MCCS_OP_CODE_VCP_REPLY = 0x02,
+ MCCS_OP_CODE_VCP_SET = 0x03,
+ MCCS_OP_CODE_VCP_RESET = 0x09,
+ MCCS_OP_CODE_CAP_REQUEST = 0xF3,
+ MCCS_OP_CODE_CAP_REPLY = 0xE3
+};
+
+enum mccs_op_buff_size {
+ MCCS_OP_BUFF_SIZE__WR_VCP_REQUEST = 5,
+ MCCS_OP_BUFF_SIZE_RD_VCP_REQUEST = 11,
+ MCCS_OP_BUFF_SIZE_WR_VCP_SET = 7,
+};
+
+enum vcp_reply_mask {
+ FREESYNC_SUPPORTED = 0x1
+};
+
+union vcp_reply {
+ struct {
+ unsigned char src_addr;
+ unsigned char length; /* Length is offset by MccsLengthOffs = 0x80 */
+ unsigned char reply_op_code; /* Should return MCCS_OP_CODE_VCP_REPLY = 0x02 */
+ unsigned char result_code; /* 00h No Error, 01h Unsupported VCP Code */
+ unsigned char request_code; /* Should return mccs vcp code sent in the vcp request */
+ unsigned char type_code; /* VCP type code: 00h Set parameter, 01h Momentary */
+ unsigned char max_value[2]; /* 2 bytes returning max value current value */
+ unsigned char present_value[2]; /* NOTE: Byte0 is MSB, Byte1 is LSB */
+ unsigned char check_sum;
+ } bytes;
+ unsigned char raw[11];
+};
+
static u32 edid_extract_panel_id(struct edid *edid)
{
return (u32)edid->mfg_id[0] << 24 |
@@ -1441,3 +1480,129 @@ bool dm_helpers_is_hdr_on(struct dc_context *ctx, struct dc_stream_state *stream
// TODO
return false;
}
+
+static int mccs_operation_vcp_request(unsigned int vcp_code, struct dc_link *link,
+ union vcp_reply *reply)
+{
+ const unsigned char retry_interval_ms = 40;
+ unsigned char retry = 5;
+ struct amdgpu_dm_connector *aconnector = link->priv;
+ struct i2c_adapter *ddc;
+ struct i2c_msg msg = {0};
+ int ret = 0;
+ int idx;
+
+ unsigned char wr_data[MCCS_OP_BUFF_SIZE__WR_VCP_REQUEST] = {
+ MCCS_SRC_ADDR, /* Byte0 - Src Addr */
+ MCCS_LENGTH_OFFSET + 2, /* Byte1 - Length */
+ MCCS_OP_CODE_VCP_REQUEST, /* Byte2 - MCCS Command */
+ (unsigned char) vcp_code, /* Byte3 - VCP Code */
+ MCCS_DEST_ADDR << 1 /* Byte4 - CheckSum */
+ };
+
+ /* calculate checksum */
+ for (idx = 0; idx < (MCCS_OP_BUFF_SIZE__WR_VCP_REQUEST - 1); idx++)
+ wr_data[(MCCS_OP_BUFF_SIZE__WR_VCP_REQUEST-1)] ^= wr_data[idx];
+
+ if (link->aux_mode)
+ ddc = &aconnector->dm_dp_aux.aux.ddc;
+ else
+ ddc = &aconnector->i2c->base;
+
+ do {
+ msg.addr = MCCS_DEST_ADDR;
+ msg.flags = 0;
+ msg.len = MCCS_OP_BUFF_SIZE__WR_VCP_REQUEST;
+ msg.buf = wr_data;
+
+ ret = i2c_transfer(ddc, &msg, 1);
+ if (ret != 1)
+ goto mccs_retry;
+
+ msleep(retry_interval_ms);
+
+ msg.addr = MCCS_DEST_ADDR;
+ msg.flags = I2C_M_RD;
+ msg.len = MCCS_OP_BUFF_SIZE_RD_VCP_REQUEST;
+ msg.buf = reply->raw;
+
+ ret = i2c_transfer(ddc, &msg, 1);
+
+ /* sink might reply with null msg if it can't reply in time */
+ if (ret == 1 && reply->bytes.length > MCCS_LENGTH_OFFSET)
+ break;
+mccs_retry:
+ retry--;
+ msleep(retry_interval_ms);
+ } while (retry);
+
+ if (!retry) {
+ drm_dbg_driver(aconnector->base.dev,
+ "%s: MCCS VCP request failed after retries", __func__);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+void dm_helpers_read_mccs_caps(struct dc_context *ctx, struct dc_link *link,
+ struct dc_sink *sink)
+{
+ bool mccs_op = false;
+ struct dpcd_caps *dpcd_caps;
+ struct drm_device *dev;
+ uint16_t freesync_vcp_value = 0;
+ union vcp_reply vcp_reply_value = {0};
+
+ if (!ctx)
+ return;
+ dev = adev_to_drm(ctx->driver_context);
+
+ if (!link || !sink) {
+ drm_dbg_driver(dev, "%s: link or sink is NULL", __func__);
+ return;
+ }
+
+ sink->mccs_caps.freesync_supported = false;
+ dpcd_caps = &link->dpcd_caps;
+
+ if (sink->edid_caps.freesync_vcp_code != 0) {
+ if (dc_is_dp_signal(link->connector_signal)) {
+ if ((dpcd_caps->dpcd_rev.raw >= DPCD_REV_14) &&
+ (dpcd_caps->dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER) &&
+ dm_is_freesync_pcon_whitelist(dpcd_caps->branch_dev_id) &&
+ (dpcd_caps->adaptive_sync_caps.dp_adap_sync_caps.bits.ADAPTIVE_SYNC_SDP_SUPPORT == true))
+ mccs_op = true;
+
+ if ((dpcd_caps->dongle_type != DISPLAY_DONGLE_NONE &&
+ dpcd_caps->dongle_type != DISPLAY_DONGLE_DP_HDMI_CONVERTER)) {
+ if (mccs_op == false)
+ drm_dbg_driver(dev, "%s: Legacy Pcon support", __func__);
+ mccs_op = true;
+ }
+
+ if (link->connector_signal == SIGNAL_TYPE_DISPLAY_PORT_MST) {
+ // Todo: Freesync over MST
+ mccs_op = false;
+ }
+ }
+
+ if (dc_is_hdmi_signal(link->connector_signal)) {
+ drm_dbg_driver(dev, "%s: Local HDMI sink", __func__);
+ mccs_op = true;
+ }
+
+ if (mccs_op == true) {
+ // MCCS VCP request to get VCP value
+ if (!mccs_operation_vcp_request(sink->edid_caps.freesync_vcp_code, link,
+ &vcp_reply_value)) {
+ freesync_vcp_value = vcp_reply_value.bytes.present_value[1];
+ freesync_vcp_value |= (uint16_t) vcp_reply_value.bytes.present_value[0] << 8;
+ }
+ // If VCP Value bit 0 is 1, freesyncSupport = true
+ sink->mccs_caps.freesync_supported =
+ (freesync_vcp_value & FREESYNC_SUPPORTED) ? true : false;
+ }
+ }
+}
+
diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h
index 5ceadcdca524..7d170eaeb163 100644
--- a/drivers/gpu/drm/amd/display/dc/dc.h
+++ b/drivers/gpu/drm/amd/display/dc/dc.h
@@ -2725,6 +2725,7 @@ struct dc_sink {
struct stereo_3d_features features_3d[TIMING_3D_FORMAT_MAX];
bool converter_disable_audio;
+ struct mccs_caps mccs_caps;
struct scdc_caps scdc_caps;
struct dc_sink_dsc_caps dsc_caps;
struct dc_sink_fec_caps fec_caps;
diff --git a/drivers/gpu/drm/amd/display/dc/dc_types.h b/drivers/gpu/drm/amd/display/dc/dc_types.h
index 5b7490a7dc7a..7672ee88be82 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_types.h
+++ b/drivers/gpu/drm/amd/display/dc/dc_types.h
@@ -1315,6 +1315,10 @@ struct dc_panel_config {
} rio;
};
+struct mccs_caps {
+ bool freesync_supported;
+};
+
#define MAX_SINKS_PER_LINK 4
/*
diff --git a/drivers/gpu/drm/amd/display/dc/dm_helpers.h b/drivers/gpu/drm/amd/display/dc/dm_helpers.h
index 2818df555e62..3aa2b11f559b 100644
--- a/drivers/gpu/drm/amd/display/dc/dm_helpers.h
+++ b/drivers/gpu/drm/amd/display/dc/dm_helpers.h
@@ -181,6 +181,11 @@ enum dc_edid_status dm_helpers_read_local_edid(
struct dc_link *link,
struct dc_sink *sink);
+void dm_helpers_read_mccs_caps(
+ struct dc_context *ctx,
+ struct dc_link *link,
+ struct dc_sink *sink);
+
bool dm_helpers_dp_handle_test_pattern_request(
struct dc_context *ctx,
const struct dc_link *link,
diff --git a/drivers/gpu/drm/amd/display/dc/link/link_detection.c b/drivers/gpu/drm/amd/display/dc/link/link_detection.c
index 714370e773c1..794dd6a95918 100644
--- a/drivers/gpu/drm/amd/display/dc/link/link_detection.c
+++ b/drivers/gpu/drm/amd/display/dc/link/link_detection.c
@@ -1234,6 +1234,20 @@ static bool detect_link_and_local_sink(struct dc_link *link,
if (dc_is_hdmi_signal(link->connector_signal))
read_scdc_caps(link->ddc, link->local_sink);
+ /* When FreeSync is toggled through OSD,
+ * we see same EDID no matter what. Check MCCS caps
+ * to see if we should update FreeSync caps now.
+ */
+ dm_helpers_read_mccs_caps(
+ link->ctx,
+ link,
+ sink);
+
+ if (prev_sink != NULL) {
+ if (memcmp(&sink->mccs_caps, &prev_sink->mccs_caps, sizeof(struct mccs_caps)))
+ same_edid = false;
+ }
+
if (link->connector_signal == SIGNAL_TYPE_DISPLAY_PORT &&
sink_caps.transaction_type ==
DDC_TRANSACTION_TYPE_I2C_OVER_AUX) {