Commit ce801e5d authored by Dominik Kaszewski's avatar Dominik Kaszewski Committed by Alex Deucher
Browse files

drm/amd/display: HDCP Locality check using DMUB Fused IO



[Why]
HDCP locality check has strict timing requirements, currently broken
due to reliance on msleep which does not guarantee accuracy.
The PR moves the write-poll-read sequence into DMUB using new generic
Fused IO interface, where the timing accuracy is greatly improved.
New flow is enabled using DCN resource capability bit (none for now),
or using a debug flag.

[How]
* Extended mod_hdcp_config with new function for requesting DMUB
to execute a sequence of fused I2C/AUX commands and synchronously
wait until an outbox reply arrives or a timeout expires.
* If the timeout expires, send an abort to DMUB.
* Update HDCP to use the DMUB for locality check if supported.
* Add DC_HDCP_LC_FORCE_FW_ENABLE and DC_HDCP_LC_ENABLE_SW_FALLBACK.
* Make the first enable new flow regardless of resource capabilities.
* Make the second enable fallback to old SW flow.
* Clean up makefile source file listings for easier updates.

Reviewed-by: default avatarAlvin Lee <alvin.lee2@amd.com>
Signed-off-by: default avatarDominik Kaszewski <dominik.kaszewski@amd.com>
Signed-off-by: default avatarRoman Li <roman.li@amd.com>
Tested-by: default avatarDaniel Wheeler <daniel.wheeler@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent d01a7306
Loading
Loading
Loading
Loading
+138 −10
Original line number Diff line number Diff line
@@ -115,6 +115,8 @@
#include "modules/inc/mod_freesync.h"
#include "modules/power/power_helpers.h"

static_assert(AMDGPU_DMUB_NOTIFICATION_MAX == DMUB_NOTIFICATION_MAX, "AMDGPU_DMUB_NOTIFICATION_MAX mismatch");

#define FIRMWARE_RENOIR_DMUB "amdgpu/renoir_dmcub.bin"
MODULE_FIRMWARE(FIRMWARE_RENOIR_DMUB);
#define FIRMWARE_SIENNA_CICHLID_DMUB "amdgpu/sienna_cichlid_dmcub.bin"
@@ -749,6 +751,29 @@ static void dmub_aux_setconfig_callback(struct amdgpu_device *adev,
		complete(&adev->dm.dmub_aux_transfer_done);
}

static void dmub_aux_fused_io_callback(struct amdgpu_device *adev,
					struct dmub_notification *notify)
{
	if (!adev || !notify) {
		ASSERT(false);
		return;
	}

	const struct dmub_cmd_fused_request *req = &notify->fused_request;
	const uint8_t ddc_line = req->u.aux.ddc_line;

	if (ddc_line >= ARRAY_SIZE(adev->dm.fused_io)) {
		ASSERT(false);
		return;
	}

	struct fused_io_sync *sync = &adev->dm.fused_io[ddc_line];

	static_assert(sizeof(*req) <= sizeof(sync->reply_data), "Size mismatch");
	memcpy(sync->reply_data, req, sizeof(*req));
	complete(&sync->replied);
}

/**
 * dmub_hpd_callback - DMUB HPD interrupt processing callback.
 * @adev: amdgpu_device pointer
@@ -885,6 +910,30 @@ static void dm_handle_hpd_work(struct work_struct *work)

}

static const char *dmub_notification_type_str(enum dmub_notification_type e)
{
	switch (e) {
	case DMUB_NOTIFICATION_NO_DATA:
		return "NO_DATA";
	case DMUB_NOTIFICATION_AUX_REPLY:
		return "AUX_REPLY";
	case DMUB_NOTIFICATION_HPD:
		return "HPD";
	case DMUB_NOTIFICATION_HPD_IRQ:
		return "HPD_IRQ";
	case DMUB_NOTIFICATION_SET_CONFIG_REPLY:
		return "SET_CONFIG_REPLY";
	case DMUB_NOTIFICATION_DPIA_NOTIFICATION:
		return "DPIA_NOTIFICATION";
	case DMUB_NOTIFICATION_HPD_SENSE_NOTIFY:
		return "HPD_SENSE_NOTIFY";
	case DMUB_NOTIFICATION_FUSED_IO:
		return "FUSED_IO";
	default:
		return "<unknown>";
	}
}

#define DMUB_TRACE_MAX_READ 64
/**
 * dm_dmub_outbox1_low_irq() - Handles Outbox interrupt
@@ -902,15 +951,6 @@ static void dm_dmub_outbox1_low_irq(void *interrupt_params)
	struct dmcub_trace_buf_entry entry = { 0 };
	u32 count = 0;
	struct dmub_hpd_work *dmub_hpd_wrk;
	static const char *const event_type[] = {
		"NO_DATA",
		"AUX_REPLY",
		"HPD",
		"HPD_IRQ",
		"SET_CONFIGC_REPLY",
		"DPIA_NOTIFICATION",
		"HPD_SENSE_NOTIFY",
	};

	do {
		if (dc_dmub_srv_get_dmub_outbox0_msg(dm->dc, &entry)) {
@@ -940,7 +980,7 @@ static void dm_dmub_outbox1_low_irq(void *interrupt_params)
			}
			if (!dm->dmub_callback[notify.type]) {
				drm_warn(adev_to_drm(adev), "DMUB notification skipped due to no handler: type=%s\n",
					event_type[notify.type]);
					dmub_notification_type_str(notify.type));
				continue;
			}
			if (dm->dmub_thread_offload[notify.type] == true) {
@@ -2131,6 +2171,12 @@ static int amdgpu_dm_init(struct amdgpu_device *adev)
		adev->dm.dc->debug.using_dml21 = true;
	}

	if (amdgpu_dc_debug_mask & DC_HDCP_LC_FORCE_FW_ENABLE)
		adev->dm.dc->debug.hdcp_lc_force_fw_enable = true;

	if (amdgpu_dc_debug_mask & DC_HDCP_LC_ENABLE_SW_FALLBACK)
		adev->dm.dc->debug.hdcp_lc_enable_sw_fallback = true;

	adev->dm.dc->debug.visual_confirm = amdgpu_dc_visual_confirm;

	/* TODO: Remove after DP2 receiver gets proper support of Cable ID feature */
@@ -2213,6 +2259,15 @@ static int amdgpu_dm_init(struct amdgpu_device *adev)
			drm_err(adev_to_drm(adev), "amdgpu: fail to register dmub aux callback");
			goto error;
		}

		for (size_t i = 0; i < ARRAY_SIZE(adev->dm.fused_io); i++)
			init_completion(&adev->dm.fused_io[i].replied);

		if (!register_dmub_notify_callback(adev, DMUB_NOTIFICATION_FUSED_IO,
			dmub_aux_fused_io_callback, false)) {
			drm_err(adev_to_drm(adev), "amdgpu: fail to register dmub fused io callback");
			goto error;
		}
		/* Enable outbox notification only after IRQ handlers are registered and DMUB is alive.
		 * It is expected that DMUB will resend any pending notifications at this point. Note
		 * that hpd and hpd_irq handler registration are deferred to register_hpd_handlers() to
@@ -12812,6 +12867,79 @@ int amdgpu_dm_process_dmub_aux_transfer_sync(
	return ret;
}

static void abort_fused_io(
		struct dc_context *ctx,
		const struct dmub_cmd_fused_request *request
)
{
	union dmub_rb_cmd command = { 0 };
	struct dmub_rb_cmd_fused_io *io = &command.fused_io;

	io->header.type = DMUB_CMD__FUSED_IO;
	io->header.sub_type = DMUB_CMD__FUSED_IO_ABORT;
	io->header.payload_bytes = sizeof(*io) - sizeof(io->header);
	io->request = *request;
	dm_execute_dmub_cmd(ctx, &command, DM_DMUB_WAIT_TYPE_NO_WAIT);
}

static bool execute_fused_io(
		struct amdgpu_device *dev,
		struct dc_context *ctx,
		union dmub_rb_cmd *commands,
		uint8_t count,
		uint32_t timeout_us
)
{
	const uint8_t ddc_line = commands[0].fused_io.request.u.aux.ddc_line;

	if (ddc_line >= ARRAY_SIZE(dev->dm.fused_io))
		return false;

	struct fused_io_sync *sync = &dev->dm.fused_io[ddc_line];
	struct dmub_rb_cmd_fused_io *first = &commands[0].fused_io;
	const bool result = dm_execute_dmub_cmd_list(ctx, count, commands, DM_DMUB_WAIT_TYPE_WAIT_WITH_REPLY)
			&& first->header.ret_status
			&& first->request.status == FUSED_REQUEST_STATUS_SUCCESS;

	if (!result)
		return false;

	while (wait_for_completion_timeout(&sync->replied, usecs_to_jiffies(timeout_us))) {
		reinit_completion(&sync->replied);

		struct dmub_cmd_fused_request *reply = (struct dmub_cmd_fused_request *) sync->reply_data;

		static_assert(sizeof(*reply) <= sizeof(sync->reply_data), "Size mismatch");

		if (reply->identifier == first->request.identifier) {
			first->request = *reply;
			return true;
		}
	}

	reinit_completion(&sync->replied);
	first->request.status = FUSED_REQUEST_STATUS_TIMEOUT;
	abort_fused_io(ctx, &first->request);
	return false;
}

bool amdgpu_dm_execute_fused_io(
		struct amdgpu_device *dev,
		struct dc_link *link,
		union dmub_rb_cmd *commands,
		uint8_t count,
		uint32_t timeout_us)
{
	struct amdgpu_display_manager *dm = &dev->dm;

	mutex_lock(&dm->dpia_aux_lock);

	const bool result = execute_fused_io(dev, link->ctx, commands, count, timeout_us);

	mutex_unlock(&dm->dpia_aux_lock);
	return result;
}

int amdgpu_dm_process_dmub_set_config_sync(
		struct dc_context *ctx,
		unsigned int link_index,
+15 −1
Original line number Diff line number Diff line
@@ -50,7 +50,7 @@

#define AMDGPU_DM_MAX_NUM_EDP 2

#define AMDGPU_DMUB_NOTIFICATION_MAX 7
#define AMDGPU_DMUB_NOTIFICATION_MAX 8

#define HDMI_AMD_VENDOR_SPECIFIC_DATA_BLOCK_IEEE_REGISTRATION_ID 0x00001A
#define AMD_VSDB_VERSION_3_FEATURECAP_REPLAYMODE 0x40
@@ -81,6 +81,7 @@ struct amdgpu_bo;
struct dmub_srv;
struct dc_plane_state;
struct dmub_notification;
struct dmub_cmd_fused_request;

struct amd_vsdb_block {
	unsigned char ieee_id[3];
@@ -637,6 +638,11 @@ struct amdgpu_display_manager {
	 * OEM i2c bus
	 */
	struct amdgpu_i2c_adapter *oem_i2c;

	struct fused_io_sync {
		struct completion replied;
		char reply_data[0x40];  // Cannot include dmub_cmd here
	} fused_io[8];
};

enum dsc_clock_force_state {
@@ -1016,6 +1022,14 @@ extern const struct drm_encoder_helper_funcs amdgpu_dm_encoder_helper_funcs;
int amdgpu_dm_process_dmub_aux_transfer_sync(struct dc_context *ctx, unsigned int link_index,
					struct aux_payload *payload, enum aux_return_code_type *operation_result);

bool amdgpu_dm_execute_fused_io(
		struct amdgpu_device *dev,
		struct dc_link *link,
		union dmub_rb_cmd *commands,
		uint8_t count,
		uint32_t timeout_us
);

int amdgpu_dm_process_dmub_set_config_sync(struct dc_context *ctx, unsigned int link_index,
					struct set_config_cmd_payload *payload, enum set_config_status *operation_result);

+49 −7
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@
#include "amdgpu_dm_hdcp.h"
#include "amdgpu.h"
#include "amdgpu_dm.h"
#include "dc_fused_io.h"
#include "dm_helpers.h"
#include <drm/display/drm_hdcp_helper.h>
#include "hdcp_psp.h"
@@ -76,6 +77,34 @@ lp_read_dpcd(void *handle, uint32_t address, uint8_t *data, uint32_t size)
	return dm_helpers_dp_read_dpcd(link->ctx, link, address, data, size);
}

static bool lp_atomic_write_poll_read_i2c(
		void *handle,
		const struct mod_hdcp_atomic_op_i2c *write,
		const struct mod_hdcp_atomic_op_i2c *poll,
		struct mod_hdcp_atomic_op_i2c *read,
		uint32_t poll_timeout_us,
		uint8_t poll_mask_msb
)
{
	struct dc_link *link = handle;

	return dm_atomic_write_poll_read_i2c(link, write, poll, read, poll_timeout_us, poll_mask_msb);
}

static bool lp_atomic_write_poll_read_aux(
		void *handle,
		const struct mod_hdcp_atomic_op_aux *write,
		const struct mod_hdcp_atomic_op_aux *poll,
		struct mod_hdcp_atomic_op_aux *read,
		uint32_t poll_timeout_us,
		uint8_t poll_mask_msb
)
{
	struct dc_link *link = handle;

	return dm_atomic_write_poll_read_aux(link, write, poll, read, poll_timeout_us, poll_mask_msb);
}

static uint8_t *psp_get_srm(struct psp_context *psp, uint32_t *srm_version, uint32_t *srm_size)
{
	struct ta_hdcp_shared_memory *hdcp_cmd;
@@ -719,7 +748,10 @@ struct hdcp_workqueue *hdcp_create_workqueue(struct amdgpu_device *adev,
		INIT_DELAYED_WORK(&hdcp_work[i].watchdog_timer_dwork, event_watchdog_timer);
		INIT_DELAYED_WORK(&hdcp_work[i].property_validate_dwork, event_property_validate);

		hdcp_work[i].hdcp.config.psp.handle = &adev->psp;
		struct mod_hdcp_config *config = &hdcp_work[i].hdcp.config;
		struct mod_hdcp_ddc_funcs *ddc_funcs = &config->ddc.funcs;

		config->psp.handle = &adev->psp;
		if (dc->ctx->dce_version == DCN_VERSION_3_1 ||
		    dc->ctx->dce_version == DCN_VERSION_3_14 ||
		    dc->ctx->dce_version == DCN_VERSION_3_15 ||
@@ -727,12 +759,22 @@ struct hdcp_workqueue *hdcp_create_workqueue(struct amdgpu_device *adev,
		    dc->ctx->dce_version == DCN_VERSION_3_51 ||
		    dc->ctx->dce_version == DCN_VERSION_3_6 ||
		    dc->ctx->dce_version == DCN_VERSION_3_16)
			hdcp_work[i].hdcp.config.psp.caps.dtm_v3_supported = 1;
		hdcp_work[i].hdcp.config.ddc.handle = dc_get_link_at_index(dc, i);
		hdcp_work[i].hdcp.config.ddc.funcs.write_i2c = lp_write_i2c;
		hdcp_work[i].hdcp.config.ddc.funcs.read_i2c = lp_read_i2c;
		hdcp_work[i].hdcp.config.ddc.funcs.write_dpcd = lp_write_dpcd;
		hdcp_work[i].hdcp.config.ddc.funcs.read_dpcd = lp_read_dpcd;
			config->psp.caps.dtm_v3_supported = 1;
		config->ddc.handle = dc_get_link_at_index(dc, i);

		ddc_funcs->write_i2c = lp_write_i2c;
		ddc_funcs->read_i2c = lp_read_i2c;
		ddc_funcs->write_dpcd = lp_write_dpcd;
		ddc_funcs->read_dpcd = lp_read_dpcd;

		config->debug.lc_enable_sw_fallback = dc->debug.hdcp_lc_enable_sw_fallback;
		if (dc->caps.fused_io_supported || dc->debug.hdcp_lc_force_fw_enable) {
			ddc_funcs->atomic_write_poll_read_i2c = lp_atomic_write_poll_read_i2c;
			ddc_funcs->atomic_write_poll_read_aux = lp_atomic_write_poll_read_aux;
		} else {
			ddc_funcs->atomic_write_poll_read_i2c = NULL;
			ddc_funcs->atomic_write_poll_read_aux = NULL;
		}

		memset(hdcp_work[i].aconnector, 0,
		       sizeof(struct amdgpu_dm_connector *) *
+13 −0
Original line number Diff line number Diff line
@@ -630,6 +630,19 @@ bool dm_helpers_submit_i2c(
	return result;
}

bool dm_helpers_execute_fused_io(
		struct dc_context *ctx,
		struct dc_link *link,
		union dmub_rb_cmd *commands,
		uint8_t count,
		uint32_t timeout_us
)
{
	struct amdgpu_device *dev = ctx->driver_context;

	return amdgpu_dm_execute_fused_io(dev, link, commands, count, timeout_us);
}

static bool execute_synaptics_rc_command(struct drm_dp_aux *aux,
		bool is_write_cmd,
		unsigned char cmd,
+20 −21
Original line number Diff line number Diff line
@@ -53,31 +53,30 @@ DC_LIBS += hdcp

ifdef CONFIG_DRM_AMD_DC_FP
DC_LIBS += sspl
DC_SPL_TRANS += dc_spl_translate.o
AMD_DISPLAY_FILES += $(addprefix $(AMDDALPATH)/dc/, dc_spl_translate.o)
endif

AMD_DC = $(addsuffix /Makefile, $(addprefix $(FULL_AMD_DISPLAY_PATH)/dc/,$(DC_LIBS)))

include $(AMD_DC)

DISPLAY_CORE = dc.o dc_stat.o dc_resource.o dc_hw_sequencer.o dc_sink.o \
dc_surface.o dc_debug.o dc_stream.o dc_link_enc_cfg.o dc_link_exports.o dc_state.o
FILES =
FILES += dc_dmub_srv.o
FILES += dc_edid_parser.o
FILES += dc_fused_io.o
FILES += dc_helper.o
FILES += core/dc.o
FILES += core/dc_debug.o
FILES += core/dc_hw_sequencer.o
FILES += core/dc_link_enc_cfg.o
FILES += core/dc_link_exports.o
FILES += core/dc_resource.o
FILES += core/dc_sink.o
FILES += core/dc_stat.o
FILES += core/dc_state.o
FILES += core/dc_stream.o
FILES += core/dc_surface.o
FILES += core/dc_vm_helper.o

AMD_DISPLAY_FILES += $(addprefix $(AMDDALPATH)/dc/, $(FILES))
DISPLAY_CORE += dc_vm_helper.o

AMD_DISPLAY_CORE = $(addprefix $(AMDDALPATH)/dc/core/,$(DISPLAY_CORE))

AMD_DM_REG_UPDATE = $(addprefix $(AMDDALPATH)/dc/,dc_helper.o)

AMD_DC_SPL_TRANS = $(addprefix $(AMDDALPATH)/dc/,$(DC_SPL_TRANS))

AMD_DISPLAY_FILES += $(AMD_DISPLAY_CORE)
AMD_DISPLAY_FILES += $(AMD_DM_REG_UPDATE)

DC_DMUB += dc_dmub_srv.o
DC_EDID += dc_edid_parser.o
AMD_DISPLAY_DMUB = $(addprefix $(AMDDALPATH)/dc/,$(DC_DMUB))
AMD_DISPLAY_EDID = $(addprefix $(AMDDALPATH)/dc/,$(DC_EDID))
AMD_DISPLAY_FILES += $(AMD_DISPLAY_DMUB) $(AMD_DISPLAY_EDID)

AMD_DISPLAY_FILES += $(AMD_DC_SPL_TRANS)
Loading