980 lines · 30.3 KB · C
RawDownload
// SPDX-License-Identifier: MIT/* * Copyright 2015 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 <drm/drm_crtc.h>#include <drm/drm_vblank.h> #include "amdgpu.h"#include "amdgpu_dm.h"#include "dc.h"#include "amdgpu_securedisplay.h"#include "amdgpu_dm_psr.h"#include "amdgpu_dm_replay.h" static const char *const pipe_crc_sources[] = {	"none",	"crtc",	"crtc dither",	"dprx",	"dprx dither",	"auto",}; static enum amdgpu_dm_pipe_crc_source dm_parse_crc_source(const char *source){	if (!source || !strcmp(source, "none"))		return AMDGPU_DM_PIPE_CRC_SOURCE_NONE;	if (!strcmp(source, "auto") || !strcmp(source, "crtc"))		return AMDGPU_DM_PIPE_CRC_SOURCE_CRTC;	if (!strcmp(source, "dprx"))		return AMDGPU_DM_PIPE_CRC_SOURCE_DPRX;	if (!strcmp(source, "crtc dither"))		return AMDGPU_DM_PIPE_CRC_SOURCE_CRTC_DITHER;	if (!strcmp(source, "dprx dither"))		return AMDGPU_DM_PIPE_CRC_SOURCE_DPRX_DITHER; 	return AMDGPU_DM_PIPE_CRC_SOURCE_INVALID;} static bool dm_is_crc_source_crtc(enum amdgpu_dm_pipe_crc_source src){	return (src == AMDGPU_DM_PIPE_CRC_SOURCE_CRTC) ||	       (src == AMDGPU_DM_PIPE_CRC_SOURCE_CRTC_DITHER);} static bool dm_is_crc_source_dprx(enum amdgpu_dm_pipe_crc_source src){	return (src == AMDGPU_DM_PIPE_CRC_SOURCE_DPRX) ||	       (src == AMDGPU_DM_PIPE_CRC_SOURCE_DPRX_DITHER);} static bool dm_need_crc_dither(enum amdgpu_dm_pipe_crc_source src){	return (src == AMDGPU_DM_PIPE_CRC_SOURCE_CRTC_DITHER) ||	       (src == AMDGPU_DM_PIPE_CRC_SOURCE_DPRX_DITHER) ||	       (src == AMDGPU_DM_PIPE_CRC_SOURCE_NONE);} const char *const *amdgpu_dm_crtc_get_crc_sources(struct drm_crtc *crtc,						  size_t *count){	*count = ARRAY_SIZE(pipe_crc_sources);	return pipe_crc_sources;} #ifdef CONFIG_DRM_AMD_SECURE_DISPLAYstatic void update_phy_id_mapping(struct amdgpu_device *adev){	struct drm_device *ddev = adev_to_drm(adev);	struct amdgpu_display_manager *dm = &adev->dm;	struct drm_connector *connector;	struct amdgpu_dm_connector *aconnector;	struct amdgpu_dm_connector *sort_connector[AMDGPU_DM_MAX_CRTC] = {NULL};	struct drm_connector_list_iter iter;	uint8_t idx = 0, idx_2 = 0, connector_cnt = 0; 	dm->secure_display_ctx.phy_mapping_updated = false; 	mutex_lock(&ddev->mode_config.mutex);	drm_connector_list_iter_begin(ddev, &iter);	drm_for_each_connector_iter(connector, &iter) { 		if (connector->status != connector_status_connected)			continue; 		if (idx >= AMDGPU_DM_MAX_CRTC) {			drm_warn(adev_to_drm(adev),				"%s connected connectors exceed max crtc\n",				__func__);			mutex_unlock(&ddev->mode_config.mutex);			return;		} 		aconnector = to_amdgpu_dm_connector(connector); 		sort_connector[idx] = aconnector;		idx++;		connector_cnt++;	}	drm_connector_list_iter_end(&iter); 	/* sort connectors by link_enc_hw_instance first */	for (idx = connector_cnt; idx > 1 ; idx--) {		for (idx_2 = 0; idx_2 < (idx - 1); idx_2++) {			if (sort_connector[idx_2]->dc_link->link_enc_hw_inst >			    sort_connector[idx_2 + 1]->dc_link->link_enc_hw_inst)				swap(sort_connector[idx_2], sort_connector[idx_2 + 1]);		}	} 	/*	 * Sort mst connectors by RAD. mst connectors with the same enc_hw_instance are already	 * sorted together above.	 */	for (idx = 0; idx < connector_cnt; /*Do nothing*/) {		if (sort_connector[idx]->mst_root) {			uint8_t i, j, k;			uint8_t mst_con_cnt = 1; 			for (idx_2 = (idx + 1); idx_2 < connector_cnt; idx_2++) {				if (sort_connector[idx_2]->mst_root == sort_connector[idx]->mst_root)					mst_con_cnt++;				else					break;			} 			for (i = mst_con_cnt; i > 1; i--) {				for (j = idx; j < (idx + i - 2); j++) {					int mstb_lct = sort_connector[j]->mst_output_port->parent->lct;					int next_mstb_lct = sort_connector[j + 1]->mst_output_port->parent->lct;					u8 *rad;					u8 *next_rad;					bool swap = false; 					/* Sort by mst tree depth first. Then compare RAD if depth is the same*/					if (mstb_lct > next_mstb_lct) {						swap = true;					} else if (mstb_lct == next_mstb_lct) {						if (mstb_lct == 1) {							if (sort_connector[j]->mst_output_port->port_num > sort_connector[j + 1]->mst_output_port->port_num)								swap = true;						} else if (mstb_lct > 1) {							rad = sort_connector[j]->mst_output_port->parent->rad;							next_rad = sort_connector[j + 1]->mst_output_port->parent->rad; 							for (k = 0; k < mstb_lct - 1; k++) {								int shift = (k % 2) ? 0 : 4;								int port_num = (rad[k / 2] >> shift) & 0xf;								int next_port_num = (next_rad[k / 2] >> shift) & 0xf; 								if (port_num > next_port_num) {									swap = true;									break;								}							}						} else {							DRM_ERROR("MST LCT shouldn't be set as < 1");							mutex_unlock(&ddev->mode_config.mutex);							return;						}					} 					if (swap)						swap(sort_connector[j], sort_connector[j + 1]);				}			} 			idx += mst_con_cnt;		} else {			idx++;		}	} 	/* Complete sorting. Assign relavant result to dm->secure_display_ctx.phy_id_mapping[]*/	memset(dm->secure_display_ctx.phy_id_mapping, 0, sizeof(dm->secure_display_ctx.phy_id_mapping));	for (idx = 0; idx < connector_cnt; idx++) {		aconnector = sort_connector[idx]; 		dm->secure_display_ctx.phy_id_mapping[idx].assigned = true;		dm->secure_display_ctx.phy_id_mapping[idx].is_mst = false;		dm->secure_display_ctx.phy_id_mapping[idx].enc_hw_inst = aconnector->dc_link->link_enc_hw_inst; 		if (sort_connector[idx]->mst_root) {			dm->secure_display_ctx.phy_id_mapping[idx].is_mst = true;			dm->secure_display_ctx.phy_id_mapping[idx].lct = aconnector->mst_output_port->parent->lct;			dm->secure_display_ctx.phy_id_mapping[idx].port_num = aconnector->mst_output_port->port_num;			memcpy(dm->secure_display_ctx.phy_id_mapping[idx].rad,				aconnector->mst_output_port->parent->rad, sizeof(aconnector->mst_output_port->parent->rad));		}	}	mutex_unlock(&ddev->mode_config.mutex); 	dm->secure_display_ctx.phy_id_mapping_cnt = connector_cnt;	dm->secure_display_ctx.phy_mapping_updated = true;} static bool get_phy_id(struct amdgpu_display_manager *dm,			struct amdgpu_dm_connector *aconnector, uint8_t *phy_id){	int idx, idx_2;	bool found = false; 	/*	 * Assume secure display start after all connectors are probed. The connection	 * config is static as well	 */	if (!dm->secure_display_ctx.phy_mapping_updated) {		DRM_WARN("%s Should update the phy id table before get it's value", __func__);		return false;	} 	for (idx = 0; idx < dm->secure_display_ctx.phy_id_mapping_cnt; idx++) {		if (!dm->secure_display_ctx.phy_id_mapping[idx].assigned) {			DRM_ERROR("phy_id_mapping[%d] should be assigned", idx);			return false;		} 		if (aconnector->dc_link->link_enc_hw_inst ==				dm->secure_display_ctx.phy_id_mapping[idx].enc_hw_inst) {			if (!dm->secure_display_ctx.phy_id_mapping[idx].is_mst) {				found = true;				goto out;			} else {				/* Could caused by wrongly pass mst root connector */				if (!aconnector->mst_output_port) {					DRM_ERROR("%s Check mst case but connector without a port assigned", __func__);					return false;				} 				if (aconnector->mst_root &&					aconnector->mst_root->mst_mgr.mst_primary == NULL) {					DRM_WARN("%s pass in a stale mst connector", __func__);				} 				if (aconnector->mst_output_port->parent->lct == dm->secure_display_ctx.phy_id_mapping[idx].lct &&					aconnector->mst_output_port->port_num == dm->secure_display_ctx.phy_id_mapping[idx].port_num) {					if (aconnector->mst_output_port->parent->lct == 1) {						found = true;						goto out;					} else if (aconnector->mst_output_port->parent->lct > 1) {						/* Check RAD */						for (idx_2 = 0; idx_2 < aconnector->mst_output_port->parent->lct - 1; idx_2++) {							int shift = (idx_2 % 2) ? 0 : 4;							int port_num = (aconnector->mst_output_port->parent->rad[idx_2 / 2] >> shift) & 0xf;							int port_num2 = (dm->secure_display_ctx.phy_id_mapping[idx].rad[idx_2 / 2] >> shift) & 0xf; 							if (port_num != port_num2)								break;						} 						if (idx_2 == aconnector->mst_output_port->parent->lct - 1) {							found = true;							goto out;						}					} else {						DRM_ERROR("lCT should be >= 1");						return false;					}				}			}		}	} out:	if (found) {		DRM_DEBUG_DRIVER("Associated secure display PHY ID as %d", idx);		*phy_id = idx;	} else {		DRM_WARN("Can't find associated phy ID");		return false;	} 	return true;} static void amdgpu_dm_set_crc_window_default(struct drm_crtc *crtc, struct dc_stream_state *stream){	struct drm_device *drm_dev = crtc->dev;	struct amdgpu_display_manager *dm = &drm_to_adev(drm_dev)->dm;	struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc);	struct amdgpu_dm_connector *aconnector;	bool was_activated;	uint8_t phy_id;	unsigned long flags;	int i; 	spin_lock_irqsave(&drm_dev->event_lock, flags);	was_activated = acrtc->dm_irq_params.crc_window_activated;	for (i = 0; i < MAX_CRC_WINDOW_NUM; i++) {		acrtc->dm_irq_params.window_param[i].x_start = 0;		acrtc->dm_irq_params.window_param[i].y_start = 0;		acrtc->dm_irq_params.window_param[i].x_end = 0;		acrtc->dm_irq_params.window_param[i].y_end = 0;		acrtc->dm_irq_params.window_param[i].enable = false;		acrtc->dm_irq_params.window_param[i].update_win = false;		acrtc->dm_irq_params.window_param[i].skip_frame_cnt = 0;	}	acrtc->dm_irq_params.crc_window_activated = false;	spin_unlock_irqrestore(&drm_dev->event_lock, flags); 	/* Disable secure_display if it was enabled */	if (was_activated && dm->secure_display_ctx.op_mode == LEGACY_MODE) {		/* stop ROI update on this crtc */		flush_work(&dm->secure_display_ctx.crtc_ctx[crtc->index].notify_ta_work);		flush_work(&dm->secure_display_ctx.crtc_ctx[crtc->index].forward_roi_work);		aconnector = (struct amdgpu_dm_connector *)stream->dm_stream_context; 		if (aconnector && get_phy_id(dm, aconnector, &phy_id)) {			if (dm->secure_display_ctx.support_mul_roi)				dc_stream_forward_multiple_crc_window(stream, NULL, phy_id, true);			else				dc_stream_forward_crc_window(stream, NULL, phy_id, true);		} else {			DRM_DEBUG_DRIVER("%s Can't find matching phy id", __func__);		}	}} static void amdgpu_dm_crtc_notify_ta_to_read(struct work_struct *work){	struct secure_display_crtc_context *crtc_ctx;	struct psp_context *psp;	struct ta_securedisplay_cmd *securedisplay_cmd;	struct drm_crtc *crtc;	struct dc_stream_state *stream;	struct amdgpu_dm_connector *aconnector;	uint8_t phy_inst;	struct amdgpu_display_manager *dm;	struct crc_data crc_cpy[MAX_CRC_WINDOW_NUM];	unsigned long flags;	uint8_t roi_idx = 0;	int ret;	int i; 	crtc_ctx = container_of(work, struct secure_display_crtc_context, notify_ta_work);	crtc = crtc_ctx->crtc; 	if (!crtc)		return; 	psp = &drm_to_adev(crtc->dev)->psp; 	if (!psp->securedisplay_context.context.initialized) {		DRM_DEBUG_DRIVER("Secure Display fails to notify PSP TA\n");		return;	} 	dm = &drm_to_adev(crtc->dev)->dm;	stream = to_amdgpu_crtc(crtc)->dm_irq_params.stream;	aconnector = (struct amdgpu_dm_connector *)stream->dm_stream_context;	if (!aconnector)		return; 	mutex_lock(&crtc->dev->mode_config.mutex);	if (!get_phy_id(dm, aconnector, &phy_inst)) {		DRM_WARN("%s Can't find mapping phy id!", __func__);		mutex_unlock(&crtc->dev->mode_config.mutex);		return;	}	mutex_unlock(&crtc->dev->mode_config.mutex); 	spin_lock_irqsave(&crtc->dev->event_lock, flags);	memcpy(crc_cpy, crtc_ctx->crc_info.crc, sizeof(struct crc_data) * MAX_CRC_WINDOW_NUM);	spin_unlock_irqrestore(&crtc->dev->event_lock, flags); 	/* need lock for multiple crtcs to use the command buffer */	mutex_lock(&psp->securedisplay_context.mutex);	/* PSP TA is expected to finish data transmission over I2C within current frame,	 * even there are up to 4 crtcs request to send in this frame.	 */	if (dm->secure_display_ctx.support_mul_roi) {		psp_prep_securedisplay_cmd_buf(psp, &securedisplay_cmd,							TA_SECUREDISPLAY_COMMAND__SEND_ROI_CRC_V2); 		securedisplay_cmd->securedisplay_in_message.send_roi_crc_v2.phy_id = phy_inst; 		for (i = 0; i < MAX_CRC_WINDOW_NUM; i++) {			if (crc_cpy[i].crc_ready)				roi_idx |= 1 << i;		}		securedisplay_cmd->securedisplay_in_message.send_roi_crc_v2.roi_idx = roi_idx; 		ret = psp_securedisplay_invoke(psp, TA_SECUREDISPLAY_COMMAND__SEND_ROI_CRC_V2);	} else {		psp_prep_securedisplay_cmd_buf(psp, &securedisplay_cmd,							TA_SECUREDISPLAY_COMMAND__SEND_ROI_CRC); 		securedisplay_cmd->securedisplay_in_message.send_roi_crc.phy_id = phy_inst; 		ret = psp_securedisplay_invoke(psp, TA_SECUREDISPLAY_COMMAND__SEND_ROI_CRC);	} 	if (!ret) {		if (securedisplay_cmd->status != TA_SECUREDISPLAY_STATUS__SUCCESS)			psp_securedisplay_parse_resp_status(psp, securedisplay_cmd->status);	} 	mutex_unlock(&psp->securedisplay_context.mutex);} static voidamdgpu_dm_forward_crc_window(struct work_struct *work){	struct secure_display_crtc_context *crtc_ctx;	struct amdgpu_display_manager *dm;	struct drm_crtc *crtc;	struct dc_stream_state *stream;	struct amdgpu_dm_connector *aconnector;	struct crc_window roi_cpy[MAX_CRC_WINDOW_NUM];	unsigned long flags;	uint8_t phy_id; 	crtc_ctx = container_of(work, struct secure_display_crtc_context, forward_roi_work);	crtc = crtc_ctx->crtc; 	if (!crtc)		return; 	dm = &drm_to_adev(crtc->dev)->dm;	stream = to_amdgpu_crtc(crtc)->dm_irq_params.stream;	aconnector = (struct amdgpu_dm_connector *)stream->dm_stream_context; 	if (!aconnector)		return; 	mutex_lock(&crtc->dev->mode_config.mutex);	if (!get_phy_id(dm, aconnector, &phy_id)) {		DRM_WARN("%s Can't find mapping phy id!", __func__);		mutex_unlock(&crtc->dev->mode_config.mutex);		return;	}	mutex_unlock(&crtc->dev->mode_config.mutex); 	spin_lock_irqsave(&crtc->dev->event_lock, flags);	memcpy(roi_cpy, crtc_ctx->roi, sizeof(struct crc_window) * MAX_CRC_WINDOW_NUM);	spin_unlock_irqrestore(&crtc->dev->event_lock, flags); 	mutex_lock(&dm->dc_lock);	if (dm->secure_display_ctx.support_mul_roi)		dc_stream_forward_multiple_crc_window(stream, roi_cpy,			phy_id, false);	else		dc_stream_forward_crc_window(stream, &roi_cpy[0].rect,			phy_id, false);	mutex_unlock(&dm->dc_lock);} bool amdgpu_dm_crc_window_is_activated(struct drm_crtc *crtc){	struct drm_device *drm_dev = crtc->dev;	struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc);	bool ret = false; 	spin_lock_irq(&drm_dev->event_lock);	ret = acrtc->dm_irq_params.crc_window_activated;	spin_unlock_irq(&drm_dev->event_lock); 	return ret;}#endif intamdgpu_dm_crtc_verify_crc_source(struct drm_crtc *crtc, const char *src_name,				 size_t *values_cnt){	enum amdgpu_dm_pipe_crc_source source = dm_parse_crc_source(src_name); 	if (source < 0) {		DRM_DEBUG_DRIVER("Unknown CRC source %s for CRTC%d\n",				 src_name, crtc->index);		return -EINVAL;	} 	*values_cnt = 3;	return 0;} int amdgpu_dm_crtc_configure_crc_source(struct drm_crtc *crtc,

Lines 1–500 of 980.