131aec354SLeo (Sunpeng) Li /*
231aec354SLeo (Sunpeng) Li  * Copyright 2015 Advanced Micro Devices, Inc.
331aec354SLeo (Sunpeng) Li  *
431aec354SLeo (Sunpeng) Li  * Permission is hereby granted, free of charge, to any person obtaining a
531aec354SLeo (Sunpeng) Li  * copy of this software and associated documentation files (the "Software"),
631aec354SLeo (Sunpeng) Li  * to deal in the Software without restriction, including without limitation
731aec354SLeo (Sunpeng) Li  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
831aec354SLeo (Sunpeng) Li  * and/or sell copies of the Software, and to permit persons to whom the
931aec354SLeo (Sunpeng) Li  * Software is furnished to do so, subject to the following conditions:
1031aec354SLeo (Sunpeng) Li  *
1131aec354SLeo (Sunpeng) Li  * The above copyright notice and this permission notice shall be included in
1231aec354SLeo (Sunpeng) Li  * all copies or substantial portions of the Software.
1331aec354SLeo (Sunpeng) Li  *
1431aec354SLeo (Sunpeng) Li  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1531aec354SLeo (Sunpeng) Li  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1631aec354SLeo (Sunpeng) Li  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
1731aec354SLeo (Sunpeng) Li  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
1831aec354SLeo (Sunpeng) Li  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
1931aec354SLeo (Sunpeng) Li  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
2031aec354SLeo (Sunpeng) Li  * OTHER DEALINGS IN THE SOFTWARE.
2131aec354SLeo (Sunpeng) Li  *
2231aec354SLeo (Sunpeng) Li  * Authors: AMD
2331aec354SLeo (Sunpeng) Li  *
2431aec354SLeo (Sunpeng) Li  */
2531aec354SLeo (Sunpeng) Li 
2631aec354SLeo (Sunpeng) Li #include <drm/drm_crtc.h>
27f867723bSSam Ravnborg #include <drm/drm_vblank.h>
2831aec354SLeo (Sunpeng) Li 
2931aec354SLeo (Sunpeng) Li #include "amdgpu.h"
3031aec354SLeo (Sunpeng) Li #include "amdgpu_dm.h"
3131aec354SLeo (Sunpeng) Li #include "dc.h"
329a65df19SWayne Lin #include "amdgpu_securedisplay.h"
3331aec354SLeo (Sunpeng) Li 
348fb843d1SDingchen Zhang static const char *const pipe_crc_sources[] = {
358fb843d1SDingchen Zhang 	"none",
368fb843d1SDingchen Zhang 	"crtc",
37f1cdc98fSDingchen Zhang 	"crtc dither",
388fb843d1SDingchen Zhang 	"dprx",
39f1cdc98fSDingchen Zhang 	"dprx dither",
408fb843d1SDingchen Zhang 	"auto",
418fb843d1SDingchen Zhang };
428fb843d1SDingchen Zhang 
dm_parse_crc_source(const char * source)4331aec354SLeo (Sunpeng) Li static enum amdgpu_dm_pipe_crc_source dm_parse_crc_source(const char *source)
4431aec354SLeo (Sunpeng) Li {
4531aec354SLeo (Sunpeng) Li 	if (!source || !strcmp(source, "none"))
4631aec354SLeo (Sunpeng) Li 		return AMDGPU_DM_PIPE_CRC_SOURCE_NONE;
4714b25846SDingchen Zhang 	if (!strcmp(source, "auto") || !strcmp(source, "crtc"))
4814b25846SDingchen Zhang 		return AMDGPU_DM_PIPE_CRC_SOURCE_CRTC;
4914b25846SDingchen Zhang 	if (!strcmp(source, "dprx"))
5014b25846SDingchen Zhang 		return AMDGPU_DM_PIPE_CRC_SOURCE_DPRX;
51f1cdc98fSDingchen Zhang 	if (!strcmp(source, "crtc dither"))
52f1cdc98fSDingchen Zhang 		return AMDGPU_DM_PIPE_CRC_SOURCE_CRTC_DITHER;
53f1cdc98fSDingchen Zhang 	if (!strcmp(source, "dprx dither"))
54f1cdc98fSDingchen Zhang 		return AMDGPU_DM_PIPE_CRC_SOURCE_DPRX_DITHER;
5531aec354SLeo (Sunpeng) Li 
5631aec354SLeo (Sunpeng) Li 	return AMDGPU_DM_PIPE_CRC_SOURCE_INVALID;
5731aec354SLeo (Sunpeng) Li }
5831aec354SLeo (Sunpeng) Li 
dm_is_crc_source_crtc(enum amdgpu_dm_pipe_crc_source src)59f1cdc98fSDingchen Zhang static bool dm_is_crc_source_crtc(enum amdgpu_dm_pipe_crc_source src)
60f1cdc98fSDingchen Zhang {
61f1cdc98fSDingchen Zhang 	return (src == AMDGPU_DM_PIPE_CRC_SOURCE_CRTC) ||
62f1cdc98fSDingchen Zhang 	       (src == AMDGPU_DM_PIPE_CRC_SOURCE_CRTC_DITHER);
63f1cdc98fSDingchen Zhang }
64f1cdc98fSDingchen Zhang 
dm_is_crc_source_dprx(enum amdgpu_dm_pipe_crc_source src)65f1cdc98fSDingchen Zhang static bool dm_is_crc_source_dprx(enum amdgpu_dm_pipe_crc_source src)
66f1cdc98fSDingchen Zhang {
67f1cdc98fSDingchen Zhang 	return (src == AMDGPU_DM_PIPE_CRC_SOURCE_DPRX) ||
68f1cdc98fSDingchen Zhang 	       (src == AMDGPU_DM_PIPE_CRC_SOURCE_DPRX_DITHER);
69f1cdc98fSDingchen Zhang }
70f1cdc98fSDingchen Zhang 
dm_need_crc_dither(enum amdgpu_dm_pipe_crc_source src)71f1cdc98fSDingchen Zhang static bool dm_need_crc_dither(enum amdgpu_dm_pipe_crc_source src)
72f1cdc98fSDingchen Zhang {
73f1cdc98fSDingchen Zhang 	return (src == AMDGPU_DM_PIPE_CRC_SOURCE_CRTC_DITHER) ||
74f1cdc98fSDingchen Zhang 	       (src == AMDGPU_DM_PIPE_CRC_SOURCE_DPRX_DITHER) ||
75f1cdc98fSDingchen Zhang 	       (src == AMDGPU_DM_PIPE_CRC_SOURCE_NONE);
76f1cdc98fSDingchen Zhang }
77f1cdc98fSDingchen Zhang 
amdgpu_dm_crtc_get_crc_sources(struct drm_crtc * crtc,size_t * count)788fb843d1SDingchen Zhang const char *const *amdgpu_dm_crtc_get_crc_sources(struct drm_crtc *crtc,
798fb843d1SDingchen Zhang 						  size_t *count)
808fb843d1SDingchen Zhang {
818fb843d1SDingchen Zhang 	*count = ARRAY_SIZE(pipe_crc_sources);
828fb843d1SDingchen Zhang 	return pipe_crc_sources;
838fb843d1SDingchen Zhang }
848fb843d1SDingchen Zhang 
8586bc2219SWayne Lin #ifdef CONFIG_DRM_AMD_SECURE_DISPLAY
amdgpu_dm_set_crc_window_default(struct drm_crtc * crtc,struct dc_stream_state * stream)86785b250eSAlan Liu static void amdgpu_dm_set_crc_window_default(struct drm_crtc *crtc, struct dc_stream_state *stream)
8786bc2219SWayne Lin {
8886bc2219SWayne Lin 	struct drm_device *drm_dev = crtc->dev;
89785b250eSAlan Liu 	struct amdgpu_display_manager *dm = &drm_to_adev(drm_dev)->dm;
9086bc2219SWayne Lin 	struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc);
91785b250eSAlan Liu 	bool was_activated;
9286bc2219SWayne Lin 
9386bc2219SWayne Lin 	spin_lock_irq(&drm_dev->event_lock);
94785b250eSAlan Liu 	was_activated = acrtc->dm_irq_params.window_param.activated;
9562fa035bSAlan Liu 	acrtc->dm_irq_params.window_param.x_start = 0;
9662fa035bSAlan Liu 	acrtc->dm_irq_params.window_param.y_start = 0;
9762fa035bSAlan Liu 	acrtc->dm_irq_params.window_param.x_end = 0;
9862fa035bSAlan Liu 	acrtc->dm_irq_params.window_param.y_end = 0;
99c0459bddSAlan Liu 	acrtc->dm_irq_params.window_param.activated = false;
100c0459bddSAlan Liu 	acrtc->dm_irq_params.window_param.update_win = false;
101c0459bddSAlan Liu 	acrtc->dm_irq_params.window_param.skip_frame_cnt = 0;
10286bc2219SWayne Lin 	spin_unlock_irq(&drm_dev->event_lock);
103785b250eSAlan Liu 
104785b250eSAlan Liu 	/* Disable secure_display if it was enabled */
105785b250eSAlan Liu 	if (was_activated) {
106785b250eSAlan Liu 		/* stop ROI update on this crtc */
107785b250eSAlan Liu 		flush_work(&dm->secure_display_ctxs[crtc->index].notify_ta_work);
108785b250eSAlan Liu 		flush_work(&dm->secure_display_ctxs[crtc->index].forward_roi_work);
109785b250eSAlan Liu 		dc_stream_forward_crc_window(stream, NULL, true);
110785b250eSAlan Liu 	}
11186bc2219SWayne Lin }
11286bc2219SWayne Lin 
amdgpu_dm_crtc_notify_ta_to_read(struct work_struct * work)1139a65df19SWayne Lin static void amdgpu_dm_crtc_notify_ta_to_read(struct work_struct *work)
1149a65df19SWayne Lin {
1151b11ff76SAlan Liu 	struct secure_display_context *secure_display_ctx;
1169a65df19SWayne Lin 	struct psp_context *psp;
117f6e856e7SAaron Liu 	struct ta_securedisplay_cmd *securedisplay_cmd;
1189a65df19SWayne Lin 	struct drm_crtc *crtc;
1191b11ff76SAlan Liu 	struct dc_stream_state *stream;
1201b11ff76SAlan Liu 	uint8_t phy_inst;
1219a65df19SWayne Lin 	int ret;
1229a65df19SWayne Lin 
1231b11ff76SAlan Liu 	secure_display_ctx = container_of(work, struct secure_display_context, notify_ta_work);
1241b11ff76SAlan Liu 	crtc = secure_display_ctx->crtc;
1259a65df19SWayne Lin 
126*30a97a21SSrinivasan Shanmugam 	if (!crtc)
1279a65df19SWayne Lin 		return;
1289a65df19SWayne Lin 
1291b11ff76SAlan Liu 	psp = &drm_to_adev(crtc->dev)->psp;
130cbd8f20bSAlan Liu 
131cbd8f20bSAlan Liu 	if (!psp->securedisplay_context.context.initialized) {
132cbd8f20bSAlan Liu 		DRM_DEBUG_DRIVER("Secure Display fails to notify PSP TA\n");
133cbd8f20bSAlan Liu 		return;
134cbd8f20bSAlan Liu 	}
135cbd8f20bSAlan Liu 
1361b11ff76SAlan Liu 	stream = to_amdgpu_crtc(crtc)->dm_irq_params.stream;
1371b11ff76SAlan Liu 	phy_inst = stream->link->link_enc_hw_inst;
1389a65df19SWayne Lin 
1391b11ff76SAlan Liu 	/* need lock for multiple crtcs to use the command buffer */
1407117007eSAlan Liu 	mutex_lock(&psp->securedisplay_context.mutex);
1417117007eSAlan Liu 
1429a65df19SWayne Lin 	psp_prep_securedisplay_cmd_buf(psp, &securedisplay_cmd,
1439a65df19SWayne Lin 						TA_SECUREDISPLAY_COMMAND__SEND_ROI_CRC);
1441b11ff76SAlan Liu 
1451b11ff76SAlan Liu 	securedisplay_cmd->securedisplay_in_message.send_roi_crc.phy_id = phy_inst;
1461b11ff76SAlan Liu 
1471b11ff76SAlan Liu 	/* PSP TA is expected to finish data transmission over I2C within current frame,
1481b11ff76SAlan Liu 	 * even there are up to 4 crtcs request to send in this frame.
1491b11ff76SAlan Liu 	 */
1509a65df19SWayne Lin 	ret = psp_securedisplay_invoke(psp, TA_SECUREDISPLAY_COMMAND__SEND_ROI_CRC);
1511b11ff76SAlan Liu 
1529a65df19SWayne Lin 	if (!ret) {
153*30a97a21SSrinivasan Shanmugam 		if (securedisplay_cmd->status != TA_SECUREDISPLAY_STATUS__SUCCESS)
1549a65df19SWayne Lin 			psp_securedisplay_parse_resp_status(psp, securedisplay_cmd->status);
1559a65df19SWayne Lin 	}
1567117007eSAlan Liu 
1577117007eSAlan Liu 	mutex_unlock(&psp->securedisplay_context.mutex);
1589a65df19SWayne Lin }
1599a65df19SWayne Lin 
160c0459bddSAlan Liu static void
amdgpu_dm_forward_crc_window(struct work_struct * work)161c0459bddSAlan Liu amdgpu_dm_forward_crc_window(struct work_struct *work)
162c0459bddSAlan Liu {
1631b11ff76SAlan Liu 	struct secure_display_context *secure_display_ctx;
164c0459bddSAlan Liu 	struct amdgpu_display_manager *dm;
1651b11ff76SAlan Liu 	struct drm_crtc *crtc;
1661b11ff76SAlan Liu 	struct dc_stream_state *stream;
167c0459bddSAlan Liu 
1681b11ff76SAlan Liu 	secure_display_ctx = container_of(work, struct secure_display_context, forward_roi_work);
1691b11ff76SAlan Liu 	crtc = secure_display_ctx->crtc;
1701b11ff76SAlan Liu 
1711b11ff76SAlan Liu 	if (!crtc)
1721b11ff76SAlan Liu 		return;
1731b11ff76SAlan Liu 
1741b11ff76SAlan Liu 	dm = &drm_to_adev(crtc->dev)->dm;
1751b11ff76SAlan Liu 	stream = to_amdgpu_crtc(crtc)->dm_irq_params.stream;
176c0459bddSAlan Liu 
177c0459bddSAlan Liu 	mutex_lock(&dm->dc_lock);
1781b11ff76SAlan Liu 	dc_stream_forward_crc_window(stream, &secure_display_ctx->rect, false);
179c0459bddSAlan Liu 	mutex_unlock(&dm->dc_lock);
180c0459bddSAlan Liu }
181c0459bddSAlan Liu 
amdgpu_dm_crc_window_is_activated(struct drm_crtc * crtc)18286bc2219SWayne Lin bool amdgpu_dm_crc_window_is_activated(struct drm_crtc *crtc)
18386bc2219SWayne Lin {
18486bc2219SWayne Lin 	struct drm_device *drm_dev = crtc->dev;
18586bc2219SWayne Lin 	struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc);
18686bc2219SWayne Lin 	bool ret = false;
18786bc2219SWayne Lin 
18886bc2219SWayne Lin 	spin_lock_irq(&drm_dev->event_lock);
189c0459bddSAlan Liu 	ret = acrtc->dm_irq_params.window_param.activated;
19086bc2219SWayne Lin 	spin_unlock_irq(&drm_dev->event_lock);
19186bc2219SWayne Lin 
19286bc2219SWayne Lin 	return ret;
19386bc2219SWayne Lin }
19486bc2219SWayne Lin #endif
19586bc2219SWayne Lin 
1963b3b8448SMahesh Kumar int
amdgpu_dm_crtc_verify_crc_source(struct drm_crtc * crtc,const char * src_name,size_t * values_cnt)1973b3b8448SMahesh Kumar amdgpu_dm_crtc_verify_crc_source(struct drm_crtc *crtc, const char *src_name,
1983b3b8448SMahesh Kumar 				 size_t *values_cnt)
1993b3b8448SMahesh Kumar {
2003b3b8448SMahesh Kumar 	enum amdgpu_dm_pipe_crc_source source = dm_parse_crc_source(src_name);
2013b3b8448SMahesh Kumar 
2023b3b8448SMahesh Kumar 	if (source < 0) {
2033b3b8448SMahesh Kumar 		DRM_DEBUG_DRIVER("Unknown CRC source %s for CRTC%d\n",
2043b3b8448SMahesh Kumar 				 src_name, crtc->index);
2053b3b8448SMahesh Kumar 		return -EINVAL;
2063b3b8448SMahesh Kumar 	}
2073b3b8448SMahesh Kumar 
2083b3b8448SMahesh Kumar 	*values_cnt = 3;
2093b3b8448SMahesh Kumar 	return 0;
2103b3b8448SMahesh Kumar }
2113b3b8448SMahesh Kumar 
amdgpu_dm_crtc_configure_crc_source(struct drm_crtc * crtc,struct dm_crtc_state * dm_crtc_state,enum amdgpu_dm_pipe_crc_source source)21257638021SNicholas Kazlauskas int amdgpu_dm_crtc_configure_crc_source(struct drm_crtc *crtc,
21357638021SNicholas Kazlauskas 					struct dm_crtc_state *dm_crtc_state,
21457638021SNicholas Kazlauskas 					enum amdgpu_dm_pipe_crc_source source)
21531aec354SLeo (Sunpeng) Li {
2161348969aSLuben Tuikov 	struct amdgpu_device *adev = drm_to_adev(crtc->dev);
21757638021SNicholas Kazlauskas 	struct dc_stream_state *stream_state = dm_crtc_state->stream;
21857638021SNicholas Kazlauskas 	bool enable = amdgpu_dm_is_valid_crc_source(source);
21957638021SNicholas Kazlauskas 	int ret = 0;
22057638021SNicholas Kazlauskas 
22157638021SNicholas Kazlauskas 	/* Configuration will be deferred to stream enable. */
22257638021SNicholas Kazlauskas 	if (!stream_state)
223bbc49fc0SWayne Lin 		return -EINVAL;
22457638021SNicholas Kazlauskas 
22557638021SNicholas Kazlauskas 	mutex_lock(&adev->dm.dc_lock);
22657638021SNicholas Kazlauskas 
2271b11ff76SAlan Liu 	/* Enable or disable CRTC CRC generation */
2281c26a1bfSWayne Lin 	if (dm_is_crc_source_crtc(source) || source == AMDGPU_DM_PIPE_CRC_SOURCE_NONE) {
22957638021SNicholas Kazlauskas 		if (!dc_stream_configure_crc(stream_state->ctx->dc,
230e2881d6dSRodrigo Siqueira 					     stream_state, NULL, enable, enable)) {
23157638021SNicholas Kazlauskas 			ret = -EINVAL;
23257638021SNicholas Kazlauskas 			goto unlock;
23357638021SNicholas Kazlauskas 		}
23457638021SNicholas Kazlauskas 	}
23557638021SNicholas Kazlauskas 
23657638021SNicholas Kazlauskas 	/* Configure dithering */
23790d26874SRobin Singh 	if (!dm_need_crc_dither(source)) {
23857638021SNicholas Kazlauskas 		dc_stream_set_dither_option(stream_state, DITHER_OPTION_TRUN8);
23990d26874SRobin Singh 		dc_stream_set_dyn_expansion(stream_state->ctx->dc, stream_state,
24090d26874SRobin Singh 					    DYN_EXPANSION_DISABLE);
24190d26874SRobin Singh 	} else {
24257638021SNicholas Kazlauskas 		dc_stream_set_dither_option(stream_state,
24357638021SNicholas Kazlauskas 					    DITHER_OPTION_DEFAULT);
24490d26874SRobin Singh 		dc_stream_set_dyn_expansion(stream_state->ctx->dc, stream_state,
24590d26874SRobin Singh 					    DYN_EXPANSION_AUTO);
24690d26874SRobin Singh 	}
24757638021SNicholas Kazlauskas 
24857638021SNicholas Kazlauskas unlock:
24957638021SNicholas Kazlauskas 	mutex_unlock(&adev->dm.dc_lock);
25057638021SNicholas Kazlauskas 
25157638021SNicholas Kazlauskas 	return ret;
25257638021SNicholas Kazlauskas }
25357638021SNicholas Kazlauskas 
amdgpu_dm_crtc_set_crc_source(struct drm_crtc * crtc,const char * src_name)25457638021SNicholas Kazlauskas int amdgpu_dm_crtc_set_crc_source(struct drm_crtc *crtc, const char *src_name)
25557638021SNicholas Kazlauskas {
256452575c5SNicholas Kazlauskas 	enum amdgpu_dm_pipe_crc_source source = dm_parse_crc_source(src_name);
2578e7b6feeSWayne Lin 	enum amdgpu_dm_pipe_crc_source cur_crc_src;
258452575c5SNicholas Kazlauskas 	struct drm_crtc_commit *commit;
259452575c5SNicholas Kazlauskas 	struct dm_crtc_state *crtc_state;
2608e7b6feeSWayne Lin 	struct drm_device *drm_dev = crtc->dev;
2618e7b6feeSWayne Lin 	struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc);
26214b25846SDingchen Zhang 	struct drm_dp_aux *aux = NULL;
26314b25846SDingchen Zhang 	bool enable = false;
26414b25846SDingchen Zhang 	bool enabled = false;
265452575c5SNicholas Kazlauskas 	int ret = 0;
26631aec354SLeo (Sunpeng) Li 
26731aec354SLeo (Sunpeng) Li 	if (source < 0) {
26831aec354SLeo (Sunpeng) Li 		DRM_DEBUG_DRIVER("Unknown CRC source %s for CRTC%d\n",
26931aec354SLeo (Sunpeng) Li 				 src_name, crtc->index);
27031aec354SLeo (Sunpeng) Li 		return -EINVAL;
27131aec354SLeo (Sunpeng) Li 	}
27231aec354SLeo (Sunpeng) Li 
273452575c5SNicholas Kazlauskas 	ret = drm_modeset_lock(&crtc->mutex, NULL);
274452575c5SNicholas Kazlauskas 	if (ret)
275452575c5SNicholas Kazlauskas 		return ret;
276452575c5SNicholas Kazlauskas 
277452575c5SNicholas Kazlauskas 	spin_lock(&crtc->commit_lock);
278452575c5SNicholas Kazlauskas 	commit = list_first_entry_or_null(&crtc->commit_list,
279452575c5SNicholas Kazlauskas 					  struct drm_crtc_commit, commit_entry);
280452575c5SNicholas Kazlauskas 	if (commit)
281452575c5SNicholas Kazlauskas 		drm_crtc_commit_get(commit);
282452575c5SNicholas Kazlauskas 	spin_unlock(&crtc->commit_lock);
283452575c5SNicholas Kazlauskas 
284452575c5SNicholas Kazlauskas 	if (commit) {
285452575c5SNicholas Kazlauskas 		/*
286452575c5SNicholas Kazlauskas 		 * Need to wait for all outstanding programming to complete
287452575c5SNicholas Kazlauskas 		 * in commit tail since it can modify CRC related fields and
288452575c5SNicholas Kazlauskas 		 * hardware state. Since we're holding the CRTC lock we're
289452575c5SNicholas Kazlauskas 		 * guaranteed that no other commit work can be queued off
290452575c5SNicholas Kazlauskas 		 * before we modify the state below.
291452575c5SNicholas Kazlauskas 		 */
292452575c5SNicholas Kazlauskas 		ret = wait_for_completion_interruptible_timeout(
293452575c5SNicholas Kazlauskas 			&commit->hw_done, 10 * HZ);
294452575c5SNicholas Kazlauskas 		if (ret)
295452575c5SNicholas Kazlauskas 			goto cleanup;
296452575c5SNicholas Kazlauskas 	}
297452575c5SNicholas Kazlauskas 
29814b25846SDingchen Zhang 	enable = amdgpu_dm_is_valid_crc_source(source);
299452575c5SNicholas Kazlauskas 	crtc_state = to_dm_crtc_state(crtc->state);
3008e7b6feeSWayne Lin 	spin_lock_irq(&drm_dev->event_lock);
3018e7b6feeSWayne Lin 	cur_crc_src = acrtc->dm_irq_params.crc_src;
3028e7b6feeSWayne Lin 	spin_unlock_irq(&drm_dev->event_lock);
303428da2bdSNicholas Kazlauskas 
30414b25846SDingchen Zhang 	/*
30514b25846SDingchen Zhang 	 * USER REQ SRC | CURRENT SRC | BEHAVIOR
30614b25846SDingchen Zhang 	 * -----------------------------
30714b25846SDingchen Zhang 	 * None         | None        | Do nothing
308f1cdc98fSDingchen Zhang 	 * None         | CRTC        | Disable CRTC CRC, set default to dither
309f1cdc98fSDingchen Zhang 	 * None         | DPRX        | Disable DPRX CRC, need 'aux', set default to dither
310f1cdc98fSDingchen Zhang 	 * None         | CRTC DITHER | Disable CRTC CRC
311f1cdc98fSDingchen Zhang 	 * None         | DPRX DITHER | Disable DPRX CRC, need 'aux'
312f1cdc98fSDingchen Zhang 	 * CRTC         | XXXX        | Enable CRTC CRC, no dither
313f1cdc98fSDingchen Zhang 	 * DPRX         | XXXX        | Enable DPRX CRC, need 'aux', no dither
314f1cdc98fSDingchen Zhang 	 * CRTC DITHER  | XXXX        | Enable CRTC CRC, set dither
315f1cdc98fSDingchen Zhang 	 * DPRX DITHER  | XXXX        | Enable DPRX CRC, need 'aux', set dither
31614b25846SDingchen Zhang 	 */
317f1cdc98fSDingchen Zhang 	if (dm_is_crc_source_dprx(source) ||
31814b25846SDingchen Zhang 	    (source == AMDGPU_DM_PIPE_CRC_SOURCE_NONE &&
3198e7b6feeSWayne Lin 	     dm_is_crc_source_dprx(cur_crc_src))) {
320df61eae4SNicholas Kazlauskas 		struct amdgpu_dm_connector *aconn = NULL;
321df61eae4SNicholas Kazlauskas 		struct drm_connector *connector;
322df61eae4SNicholas Kazlauskas 		struct drm_connector_list_iter conn_iter;
323df61eae4SNicholas Kazlauskas 
324df61eae4SNicholas Kazlauskas 		drm_connector_list_iter_begin(crtc->dev, &conn_iter);
325df61eae4SNicholas Kazlauskas 		drm_for_each_connector_iter(connector, &conn_iter) {
326df61eae4SNicholas Kazlauskas 			if (!connector->state || connector->state->crtc != crtc)
327df61eae4SNicholas Kazlauskas 				continue;
328df61eae4SNicholas Kazlauskas 
329df61eae4SNicholas Kazlauskas 			aconn = to_amdgpu_dm_connector(connector);
330df61eae4SNicholas Kazlauskas 			break;
331df61eae4SNicholas Kazlauskas 		}
332df61eae4SNicholas Kazlauskas 		drm_connector_list_iter_end(&conn_iter);
33314b25846SDingchen Zhang 
33414b25846SDingchen Zhang 		if (!aconn) {
33514b25846SDingchen Zhang 			DRM_DEBUG_DRIVER("No amd connector matching CRTC-%d\n", crtc->index);
336452575c5SNicholas Kazlauskas 			ret = -EINVAL;
337452575c5SNicholas Kazlauskas 			goto cleanup;
33814b25846SDingchen Zhang 		}
33914b25846SDingchen Zhang 
340f0127cb1SWayne Lin 		aux = (aconn->mst_output_port) ? &aconn->mst_output_port->aux : &aconn->dm_dp_aux.aux;
34114b25846SDingchen Zhang 
34214b25846SDingchen Zhang 		if (!aux) {
34314b25846SDingchen Zhang 			DRM_DEBUG_DRIVER("No dp aux for amd connector\n");
344452575c5SNicholas Kazlauskas 			ret = -EINVAL;
345452575c5SNicholas Kazlauskas 			goto cleanup;
34643a6a02eSNicholas Kazlauskas 		}
3472da34b7bSPerry Yuan 
3482da34b7bSPerry Yuan 		if ((aconn->base.connector_type != DRM_MODE_CONNECTOR_DisplayPort) &&
3492da34b7bSPerry Yuan 				(aconn->base.connector_type != DRM_MODE_CONNECTOR_eDP)) {
3502da34b7bSPerry Yuan 			DRM_DEBUG_DRIVER("No DP connector available for CRC source\n");
3512da34b7bSPerry Yuan 			ret = -EINVAL;
3522da34b7bSPerry Yuan 			goto cleanup;
3532da34b7bSPerry Yuan 		}
3542da34b7bSPerry Yuan 
35514b25846SDingchen Zhang 	}
356428da2bdSNicholas Kazlauskas 
35786bc2219SWayne Lin #if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
3581b11ff76SAlan Liu 	/* Reset secure_display when we change crc source from debugfs */
359785b250eSAlan Liu 	amdgpu_dm_set_crc_window_default(crtc, crtc_state->stream);
36086bc2219SWayne Lin #endif
36186bc2219SWayne Lin 
362452575c5SNicholas Kazlauskas 	if (amdgpu_dm_crtc_configure_crc_source(crtc, crtc_state, source)) {
363452575c5SNicholas Kazlauskas 		ret = -EINVAL;
364452575c5SNicholas Kazlauskas 		goto cleanup;
365452575c5SNicholas Kazlauskas 	}
36643a6a02eSNicholas Kazlauskas 
367428da2bdSNicholas Kazlauskas 	/*
368428da2bdSNicholas Kazlauskas 	 * Reading the CRC requires the vblank interrupt handler to be
369428da2bdSNicholas Kazlauskas 	 * enabled. Keep a reference until CRC capture stops.
370428da2bdSNicholas Kazlauskas 	 */
3718e7b6feeSWayne Lin 	enabled = amdgpu_dm_is_valid_crc_source(cur_crc_src);
37214b25846SDingchen Zhang 	if (!enabled && enable) {
3737a235125SNicholas Kazlauskas 		ret = drm_crtc_vblank_get(crtc);
3747a235125SNicholas Kazlauskas 		if (ret)
375452575c5SNicholas Kazlauskas 			goto cleanup;
3767a235125SNicholas Kazlauskas 
377f1cdc98fSDingchen Zhang 		if (dm_is_crc_source_dprx(source)) {
37814b25846SDingchen Zhang 			if (drm_dp_start_crc(aux, crtc)) {
37914b25846SDingchen Zhang 				DRM_DEBUG_DRIVER("dp start crc failed\n");
380452575c5SNicholas Kazlauskas 				ret = -EINVAL;
381452575c5SNicholas Kazlauskas 				goto cleanup;
38214b25846SDingchen Zhang 			}
38314b25846SDingchen Zhang 		}
38414b25846SDingchen Zhang 	} else if (enabled && !enable) {
385428da2bdSNicholas Kazlauskas 		drm_crtc_vblank_put(crtc);
386f1cdc98fSDingchen Zhang 		if (dm_is_crc_source_dprx(source)) {
38714b25846SDingchen Zhang 			if (drm_dp_stop_crc(aux)) {
38814b25846SDingchen Zhang 				DRM_DEBUG_DRIVER("dp stop crc failed\n");
389452575c5SNicholas Kazlauskas 				ret = -EINVAL;
390452575c5SNicholas Kazlauskas 				goto cleanup;
39114b25846SDingchen Zhang 			}
39214b25846SDingchen Zhang 		}
39314b25846SDingchen Zhang 	}
394428da2bdSNicholas Kazlauskas 
3958e7b6feeSWayne Lin 	spin_lock_irq(&drm_dev->event_lock);
3968e7b6feeSWayne Lin 	acrtc->dm_irq_params.crc_src = source;
3978e7b6feeSWayne Lin 	spin_unlock_irq(&drm_dev->event_lock);
39831aec354SLeo (Sunpeng) Li 
399a0a31ec4SLeo (Sunpeng) Li 	/* Reset crc_skipped on dm state */
400a0a31ec4SLeo (Sunpeng) Li 	crtc_state->crc_skip_count = 0;
401452575c5SNicholas Kazlauskas 
402452575c5SNicholas Kazlauskas cleanup:
403452575c5SNicholas Kazlauskas 	if (commit)
404452575c5SNicholas Kazlauskas 		drm_crtc_commit_put(commit);
405452575c5SNicholas Kazlauskas 
406452575c5SNicholas Kazlauskas 	drm_modeset_unlock(&crtc->mutex);
407452575c5SNicholas Kazlauskas 
408452575c5SNicholas Kazlauskas 	return ret;
40931aec354SLeo (Sunpeng) Li }
41031aec354SLeo (Sunpeng) Li 
41131aec354SLeo (Sunpeng) Li /**
41231aec354SLeo (Sunpeng) Li  * amdgpu_dm_crtc_handle_crc_irq: Report to DRM the CRC on given CRTC.
41331aec354SLeo (Sunpeng) Li  * @crtc: DRM CRTC object.
41431aec354SLeo (Sunpeng) Li  *
41531aec354SLeo (Sunpeng) Li  * This function should be called at the end of a vblank, when the fb has been
41631aec354SLeo (Sunpeng) Li  * fully processed through the pipe.
41731aec354SLeo (Sunpeng) Li  */
amdgpu_dm_crtc_handle_crc_irq(struct drm_crtc * crtc)41831aec354SLeo (Sunpeng) Li void amdgpu_dm_crtc_handle_crc_irq(struct drm_crtc *crtc)
41931aec354SLeo (Sunpeng) Li {
420dddc0557SNicholas Kazlauskas 	struct dm_crtc_state *crtc_state;
421dddc0557SNicholas Kazlauskas 	struct dc_stream_state *stream_state;
4228e7b6feeSWayne Lin 	struct drm_device *drm_dev = NULL;
4238e7b6feeSWayne Lin 	enum amdgpu_dm_pipe_crc_source cur_crc_src;
4248e7b6feeSWayne Lin 	struct amdgpu_crtc *acrtc = NULL;
42531aec354SLeo (Sunpeng) Li 	uint32_t crcs[3];
4268e7b6feeSWayne Lin 	unsigned long flags;
42731aec354SLeo (Sunpeng) Li 
428dddc0557SNicholas Kazlauskas 	if (crtc == NULL)
429dddc0557SNicholas Kazlauskas 		return;
430dddc0557SNicholas Kazlauskas 
431dddc0557SNicholas Kazlauskas 	crtc_state = to_dm_crtc_state(crtc->state);
432dddc0557SNicholas Kazlauskas 	stream_state = crtc_state->stream;
4338e7b6feeSWayne Lin 	acrtc = to_amdgpu_crtc(crtc);
4348e7b6feeSWayne Lin 	drm_dev = crtc->dev;
4358e7b6feeSWayne Lin 
4368e7b6feeSWayne Lin 	spin_lock_irqsave(&drm_dev->event_lock, flags);
4378e7b6feeSWayne Lin 	cur_crc_src = acrtc->dm_irq_params.crc_src;
4388e7b6feeSWayne Lin 	spin_unlock_irqrestore(&drm_dev->event_lock, flags);
439dddc0557SNicholas Kazlauskas 
440d1bd7d61SLeo (Sunpeng) Li 	/* Early return if CRC capture is not enabled. */
4418e7b6feeSWayne Lin 	if (!amdgpu_dm_is_valid_crc_source(cur_crc_src))
442d1bd7d61SLeo (Sunpeng) Li 		return;
443d1bd7d61SLeo (Sunpeng) Li 
44431aec354SLeo (Sunpeng) Li 	/*
44531aec354SLeo (Sunpeng) Li 	 * Since flipping and crc enablement happen asynchronously, we - more
44631aec354SLeo (Sunpeng) Li 	 * often than not - will be returning an 'uncooked' crc on first frame.
447a0a31ec4SLeo (Sunpeng) Li 	 * Probably because hw isn't ready yet. For added security, skip the
448a0a31ec4SLeo (Sunpeng) Li 	 * first two CRC values.
44931aec354SLeo (Sunpeng) Li 	 */
450a0a31ec4SLeo (Sunpeng) Li 	if (crtc_state->crc_skip_count < 2) {
451a0a31ec4SLeo (Sunpeng) Li 		crtc_state->crc_skip_count += 1;
45231aec354SLeo (Sunpeng) Li 		return;
45331aec354SLeo (Sunpeng) Li 	}
45431aec354SLeo (Sunpeng) Li 
4558e7b6feeSWayne Lin 	if (dm_is_crc_source_crtc(cur_crc_src)) {
45631aec354SLeo (Sunpeng) Li 		if (!dc_stream_get_crc(stream_state->ctx->dc, stream_state,
45731aec354SLeo (Sunpeng) Li 				       &crcs[0], &crcs[1], &crcs[2]))
45831aec354SLeo (Sunpeng) Li 			return;
45931aec354SLeo (Sunpeng) Li 
46031aec354SLeo (Sunpeng) Li 		drm_crtc_add_crc_entry(crtc, true,
46131aec354SLeo (Sunpeng) Li 				       drm_crtc_accurate_vblank_count(crtc), crcs);
46231aec354SLeo (Sunpeng) Li 	}
46314b25846SDingchen Zhang }
46486bc2219SWayne Lin 
46586bc2219SWayne Lin #if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
amdgpu_dm_crtc_handle_crc_window_irq(struct drm_crtc * crtc)46686bc2219SWayne Lin void amdgpu_dm_crtc_handle_crc_window_irq(struct drm_crtc *crtc)
46786bc2219SWayne Lin {
46886bc2219SWayne Lin 	struct drm_device *drm_dev = NULL;
46986bc2219SWayne Lin 	enum amdgpu_dm_pipe_crc_source cur_crc_src;
47086bc2219SWayne Lin 	struct amdgpu_crtc *acrtc = NULL;
4719a65df19SWayne Lin 	struct amdgpu_device *adev = NULL;
4721b11ff76SAlan Liu 	struct secure_display_context *secure_display_ctx = NULL;
4731b11ff76SAlan Liu 	unsigned long flags1;
47486bc2219SWayne Lin 
47586bc2219SWayne Lin 	if (crtc == NULL)
47686bc2219SWayne Lin 		return;
47786bc2219SWayne Lin 
47886bc2219SWayne Lin 	acrtc = to_amdgpu_crtc(crtc);
4799a65df19SWayne Lin 	adev = drm_to_adev(crtc->dev);
48086bc2219SWayne Lin 	drm_dev = crtc->dev;
48186bc2219SWayne Lin 
482cd95ef00SWayne Lin 	spin_lock_irqsave(&drm_dev->event_lock, flags1);
48386bc2219SWayne Lin 	cur_crc_src = acrtc->dm_irq_params.crc_src;
48486bc2219SWayne Lin 
48586bc2219SWayne Lin 	/* Early return if CRC capture is not enabled. */
4861b11ff76SAlan Liu 	if (!amdgpu_dm_is_valid_crc_source(cur_crc_src) ||
4871b11ff76SAlan Liu 		!dm_is_crc_source_crtc(cur_crc_src))
488c0459bddSAlan Liu 		goto cleanup;
489c0459bddSAlan Liu 
490c0459bddSAlan Liu 	if (!acrtc->dm_irq_params.window_param.activated)
491c0459bddSAlan Liu 		goto cleanup;
492c0459bddSAlan Liu 
493c0459bddSAlan Liu 	if (acrtc->dm_irq_params.window_param.skip_frame_cnt) {
494c0459bddSAlan Liu 		acrtc->dm_irq_params.window_param.skip_frame_cnt -= 1;
4959a65df19SWayne Lin 		goto cleanup;
4969a65df19SWayne Lin 	}
49786bc2219SWayne Lin 
4981b11ff76SAlan Liu 	secure_display_ctx = &adev->dm.secure_display_ctxs[acrtc->crtc_id];
499b8ff7e08SAlan Liu 	if (WARN_ON(secure_display_ctx->crtc != crtc)) {
500b8ff7e08SAlan Liu 		/* We have set the crtc when creating secure_display_context,
501b8ff7e08SAlan Liu 		 * don't expect it to be changed here.
502b8ff7e08SAlan Liu 		 */
5031b11ff76SAlan Liu 		secure_display_ctx->crtc = crtc;
504b8ff7e08SAlan Liu 	}
50586bc2219SWayne Lin 
5061b11ff76SAlan Liu 	if (acrtc->dm_irq_params.window_param.update_win) {
5071b11ff76SAlan Liu 		/* prepare work for dmub to update ROI */
5081b11ff76SAlan Liu 		secure_display_ctx->rect.x = acrtc->dm_irq_params.window_param.x_start;
5091b11ff76SAlan Liu 		secure_display_ctx->rect.y = acrtc->dm_irq_params.window_param.y_start;
5101b11ff76SAlan Liu 		secure_display_ctx->rect.width = acrtc->dm_irq_params.window_param.x_end -
51162fa035bSAlan Liu 								acrtc->dm_irq_params.window_param.x_start;
5121b11ff76SAlan Liu 		secure_display_ctx->rect.height = acrtc->dm_irq_params.window_param.y_end -
51362fa035bSAlan Liu 								acrtc->dm_irq_params.window_param.y_start;
5141b11ff76SAlan Liu 		schedule_work(&secure_display_ctx->forward_roi_work);
51586bc2219SWayne Lin 
516c0459bddSAlan Liu 		acrtc->dm_irq_params.window_param.update_win = false;
5171b11ff76SAlan Liu 
5181b11ff76SAlan Liu 		/* Statically skip 1 frame, because we may need to wait below things
5191b11ff76SAlan Liu 		 * before sending ROI to dmub:
5201b11ff76SAlan Liu 		 * 1. We defer the work by using system workqueue.
5211b11ff76SAlan Liu 		 * 2. We may need to wait for dc_lock before accessing dmub.
5221b11ff76SAlan Liu 		 */
523c0459bddSAlan Liu 		acrtc->dm_irq_params.window_param.skip_frame_cnt = 1;
5249a65df19SWayne Lin 
5259a65df19SWayne Lin 	} else {
5261b11ff76SAlan Liu 		/* prepare work for psp to read ROI/CRC and send to I2C */
5271b11ff76SAlan Liu 		schedule_work(&secure_display_ctx->notify_ta_work);
52886bc2219SWayne Lin 	}
52986bc2219SWayne Lin 
53086bc2219SWayne Lin cleanup:
531cd95ef00SWayne Lin 	spin_unlock_irqrestore(&drm_dev->event_lock, flags1);
53286bc2219SWayne Lin }
5339a65df19SWayne Lin 
5341b11ff76SAlan Liu struct secure_display_context *
amdgpu_dm_crtc_secure_display_create_contexts(struct amdgpu_device * adev)535b8ff7e08SAlan Liu amdgpu_dm_crtc_secure_display_create_contexts(struct amdgpu_device *adev)
5369a65df19SWayne Lin {
5371b11ff76SAlan Liu 	struct secure_display_context *secure_display_ctxs = NULL;
5381b11ff76SAlan Liu 	int i;
5399a65df19SWayne Lin 
540c3d74960SHamza Mahfooz 	secure_display_ctxs = kcalloc(adev->mode_info.num_crtc,
541c3d74960SHamza Mahfooz 				      sizeof(struct secure_display_context),
542c3d74960SHamza Mahfooz 				      GFP_KERNEL);
5439a65df19SWayne Lin 
5441b11ff76SAlan Liu 	if (!secure_display_ctxs)
5459a65df19SWayne Lin 		return NULL;
5469a65df19SWayne Lin 
547c3d74960SHamza Mahfooz 	for (i = 0; i < adev->mode_info.num_crtc; i++) {
5481b11ff76SAlan Liu 		INIT_WORK(&secure_display_ctxs[i].forward_roi_work, amdgpu_dm_forward_crc_window);
5491b11ff76SAlan Liu 		INIT_WORK(&secure_display_ctxs[i].notify_ta_work, amdgpu_dm_crtc_notify_ta_to_read);
550b8ff7e08SAlan Liu 		secure_display_ctxs[i].crtc = &adev->mode_info.crtcs[i]->base;
5511b11ff76SAlan Liu 	}
5529a65df19SWayne Lin 
5531b11ff76SAlan Liu 	return secure_display_ctxs;
5549a65df19SWayne Lin }
55586bc2219SWayne Lin #endif
556