diff options
Diffstat (limited to 'drivers/gpu/drm')
| -rw-r--r-- | drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 8 | ||||
| -rw-r--r-- | drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c | 165 | ||||
| -rw-r--r-- | drivers/gpu/drm/amd/display/dc/dc.h | 1 | ||||
| -rw-r--r-- | drivers/gpu/drm/amd/display/dc/dc_types.h | 4 | ||||
| -rw-r--r-- | drivers/gpu/drm/amd/display/dc/dm_helpers.h | 5 | ||||
| -rw-r--r-- | drivers/gpu/drm/amd/display/dc/link/link_detection.c | 14 |
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) { |
