1dc38fd9dSDavid Francis /*
2dc38fd9dSDavid Francis  * Copyright 2018 Advanced Micro Devices, Inc.
3dc38fd9dSDavid Francis  *
4dc38fd9dSDavid Francis  * Permission is hereby granted, free of charge, to any person obtaining a
5dc38fd9dSDavid Francis  * copy of this software and associated documentation files (the "Software"),
6dc38fd9dSDavid Francis  * to deal in the Software without restriction, including without limitation
7dc38fd9dSDavid Francis  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8dc38fd9dSDavid Francis  * and/or sell copies of the Software, and to permit persons to whom the
9dc38fd9dSDavid Francis  * Software is furnished to do so, subject to the following conditions:
10dc38fd9dSDavid Francis  *
11dc38fd9dSDavid Francis  * The above copyright notice and this permission notice shall be included in
12dc38fd9dSDavid Francis  * all copies or substantial portions of the Software.
13dc38fd9dSDavid Francis  *
14dc38fd9dSDavid Francis  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15dc38fd9dSDavid Francis  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16dc38fd9dSDavid Francis  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17dc38fd9dSDavid Francis  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18dc38fd9dSDavid Francis  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19dc38fd9dSDavid Francis  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20dc38fd9dSDavid Francis  * OTHER DEALINGS IN THE SOFTWARE.
21dc38fd9dSDavid Francis  *
22dc38fd9dSDavid Francis  * Authors: AMD
23dc38fd9dSDavid Francis  *
24dc38fd9dSDavid Francis  */
25dc38fd9dSDavid Francis 
26972aa1a1SLucas De Marchi #include <linux/string_helpers.h>
27f867723bSSam Ravnborg #include <linux/uaccess.h>
28f867723bSSam Ravnborg 
29dc38fd9dSDavid Francis #include "dc.h"
30dc38fd9dSDavid Francis #include "amdgpu.h"
31dc38fd9dSDavid Francis #include "amdgpu_dm.h"
32dc38fd9dSDavid Francis #include "amdgpu_dm_debugfs.h"
33f258fee6SDavid Francis #include "dm_helpers.h"
34cdca3f21SAnthony Koo #include "dmub/dmub_srv.h"
35c06e09b7SEryk Brol #include "resource.h"
36c06e09b7SEryk Brol #include "dsc.h"
37f5b6a20cSMikita Lipski #include "link_hwss.h"
3846a83ebaSLeo (Hanghong) Ma #include "dc/dc_dmub_srv.h"
39642f1b40SHamza Mahfooz #include "link/protocols/link_dp_capability.h"
4060ec1b56SNicholas Kazlauskas 
41c0459bddSAlan Liu #ifdef CONFIG_DRM_AMD_SECURE_DISPLAY
42c0459bddSAlan Liu #include "amdgpu_dm_psr.h"
43c0459bddSAlan Liu #endif
44c0459bddSAlan Liu 
4560ec1b56SNicholas Kazlauskas struct dmub_debugfs_trace_header {
4660ec1b56SNicholas Kazlauskas 	uint32_t entry_count;
4760ec1b56SNicholas Kazlauskas 	uint32_t reserved[3];
4860ec1b56SNicholas Kazlauskas };
4960ec1b56SNicholas Kazlauskas 
5060ec1b56SNicholas Kazlauskas struct dmub_debugfs_trace_entry {
5160ec1b56SNicholas Kazlauskas 	uint32_t trace_code;
5260ec1b56SNicholas Kazlauskas 	uint32_t tick_count;
5360ec1b56SNicholas Kazlauskas 	uint32_t param0;
5460ec1b56SNicholas Kazlauskas 	uint32_t param1;
5560ec1b56SNicholas Kazlauskas };
56dc38fd9dSDavid Francis 
5725f7cde8SWayne Lin static const char *const mst_progress_status[] = {
5825f7cde8SWayne Lin 	"probe",
5925f7cde8SWayne Lin 	"remote_edid",
6025f7cde8SWayne Lin 	"allocate_new_payload",
6125f7cde8SWayne Lin 	"clear_allocated_payload",
6225f7cde8SWayne Lin };
6325f7cde8SWayne Lin 
6404111850SMikita Lipski /* parse_write_buffer_into_params - Helper function to parse debugfs write buffer into an array
6504111850SMikita Lipski  *
6604111850SMikita Lipski  * Function takes in attributes passed to debugfs write entry
6704111850SMikita Lipski  * and writes into param array.
6804111850SMikita Lipski  * The user passes max_param_num to identify maximum number of
6904111850SMikita Lipski  * parameters that could be parsed.
7004111850SMikita Lipski  *
7104111850SMikita Lipski  */
parse_write_buffer_into_params(char * wr_buf,uint32_t wr_buf_size,long * param,const char __user * buf,int max_param_num,uint8_t * param_nums)7204111850SMikita Lipski static int parse_write_buffer_into_params(char *wr_buf, uint32_t wr_buf_size,
7304111850SMikita Lipski 					  long *param, const char __user *buf,
7404111850SMikita Lipski 					  int max_param_num,
7504111850SMikita Lipski 					  uint8_t *param_nums)
7604111850SMikita Lipski {
7704111850SMikita Lipski 	char *wr_buf_ptr = NULL;
7804111850SMikita Lipski 	uint32_t wr_buf_count = 0;
7904111850SMikita Lipski 	int r;
8004111850SMikita Lipski 	char *sub_str = NULL;
8104111850SMikita Lipski 	const char delimiter[3] = {' ', '\n', '\0'};
8204111850SMikita Lipski 	uint8_t param_index = 0;
8304111850SMikita Lipski 
8404111850SMikita Lipski 	*param_nums = 0;
8504111850SMikita Lipski 
8604111850SMikita Lipski 	wr_buf_ptr = wr_buf;
8704111850SMikita Lipski 
8804111850SMikita Lipski 	/* r is bytes not be copied */
8925a1a08fSHarry Wentland 	if (copy_from_user(wr_buf_ptr, buf, wr_buf_size)) {
9025a1a08fSHarry Wentland 		DRM_DEBUG_DRIVER("user data could not be read successfully\n");
9125a1a08fSHarry Wentland 		return -EFAULT;
9204111850SMikita Lipski 	}
9304111850SMikita Lipski 
9404111850SMikita Lipski 	/* check number of parameters. isspace could not differ space and \n */
9504111850SMikita Lipski 	while ((*wr_buf_ptr != 0xa) && (wr_buf_count < wr_buf_size)) {
9604111850SMikita Lipski 		/* skip space*/
9704111850SMikita Lipski 		while (isspace(*wr_buf_ptr) && (wr_buf_count < wr_buf_size)) {
9804111850SMikita Lipski 			wr_buf_ptr++;
9904111850SMikita Lipski 			wr_buf_count++;
10004111850SMikita Lipski 			}
10104111850SMikita Lipski 
10204111850SMikita Lipski 		if (wr_buf_count == wr_buf_size)
10304111850SMikita Lipski 			break;
10404111850SMikita Lipski 
10504111850SMikita Lipski 		/* skip non-space*/
10604111850SMikita Lipski 		while ((!isspace(*wr_buf_ptr)) && (wr_buf_count < wr_buf_size)) {
10704111850SMikita Lipski 			wr_buf_ptr++;
10804111850SMikita Lipski 			wr_buf_count++;
10904111850SMikita Lipski 		}
11004111850SMikita Lipski 
11104111850SMikita Lipski 		(*param_nums)++;
11204111850SMikita Lipski 
11304111850SMikita Lipski 		if (wr_buf_count == wr_buf_size)
11404111850SMikita Lipski 			break;
11504111850SMikita Lipski 	}
11604111850SMikita Lipski 
11704111850SMikita Lipski 	if (*param_nums > max_param_num)
11804111850SMikita Lipski 		*param_nums = max_param_num;
11904111850SMikita Lipski 
12004111850SMikita Lipski 	wr_buf_ptr = wr_buf; /* reset buf pointer */
12104111850SMikita Lipski 	wr_buf_count = 0; /* number of char already checked */
12204111850SMikita Lipski 
12304111850SMikita Lipski 	while (isspace(*wr_buf_ptr) && (wr_buf_count < wr_buf_size)) {
12404111850SMikita Lipski 		wr_buf_ptr++;
12504111850SMikita Lipski 		wr_buf_count++;
12604111850SMikita Lipski 	}
12704111850SMikita Lipski 
12804111850SMikita Lipski 	while (param_index < *param_nums) {
12904111850SMikita Lipski 		/* after strsep, wr_buf_ptr will be moved to after space */
13004111850SMikita Lipski 		sub_str = strsep(&wr_buf_ptr, delimiter);
13104111850SMikita Lipski 
13204111850SMikita Lipski 		r = kstrtol(sub_str, 16, &(param[param_index]));
13304111850SMikita Lipski 
13404111850SMikita Lipski 		if (r)
13504111850SMikita Lipski 			DRM_DEBUG_DRIVER("string to int convert error code: %d\n", r);
13604111850SMikita Lipski 
13704111850SMikita Lipski 		param_index++;
13804111850SMikita Lipski 	}
13904111850SMikita Lipski 
14004111850SMikita Lipski 	return 0;
14104111850SMikita Lipski }
14204111850SMikita Lipski 
14341db5f19SHersen Wu /* function description
14441db5f19SHersen Wu  * get/ set DP configuration: lane_count, link_rate, spread_spectrum
14541db5f19SHersen Wu  *
14641db5f19SHersen Wu  * valid lane count value: 1, 2, 4
14741db5f19SHersen Wu  * valid link rate value:
14841db5f19SHersen Wu  * 06h = 1.62Gbps per lane
14941db5f19SHersen Wu  * 0Ah = 2.7Gbps per lane
15041db5f19SHersen Wu  * 0Ch = 3.24Gbps per lane
15141db5f19SHersen Wu  * 14h = 5.4Gbps per lane
15241db5f19SHersen Wu  * 1Eh = 8.1Gbps per lane
15341db5f19SHersen Wu  *
15441db5f19SHersen Wu  * debugfs is located at /sys/kernel/debug/dri/0/DP-x/link_settings
15541db5f19SHersen Wu  *
15641db5f19SHersen Wu  * --- to get dp configuration
15741db5f19SHersen Wu  *
158c006a1c0SFangzhi Zuo  * cat /sys/kernel/debug/dri/0/DP-x/link_settings
15941db5f19SHersen Wu  *
16041db5f19SHersen Wu  * It will list current, verified, reported, preferred dp configuration.
16141db5f19SHersen Wu  * current -- for current video mode
16241db5f19SHersen Wu  * verified --- maximum configuration which pass link training
16341db5f19SHersen Wu  * reported --- DP rx report caps (DPCD register offset 0, 1 2)
16441db5f19SHersen Wu  * preferred --- user force settings
16541db5f19SHersen Wu  *
16641db5f19SHersen Wu  * --- set (or force) dp configuration
16741db5f19SHersen Wu  *
168f8ac2cf7SHersen Wu  * echo <lane_count>  <link_rate> > link_settings
16941db5f19SHersen Wu  *
17041db5f19SHersen Wu  * for example, to force to  2 lane, 2.7GHz,
171c006a1c0SFangzhi Zuo  * echo 4 0xa > /sys/kernel/debug/dri/0/DP-x/link_settings
17241db5f19SHersen Wu  *
17341db5f19SHersen Wu  * spread_spectrum could not be changed dynamically.
17441db5f19SHersen Wu  *
17541db5f19SHersen Wu  * in case invalid lane count, link rate are force, no hw programming will be
17641db5f19SHersen Wu  * done. please check link settings after force operation to see if HW get
17741db5f19SHersen Wu  * programming.
17841db5f19SHersen Wu  *
179c006a1c0SFangzhi Zuo  * cat /sys/kernel/debug/dri/0/DP-x/link_settings
18041db5f19SHersen Wu  *
18141db5f19SHersen Wu  * check current and preferred settings.
18241db5f19SHersen Wu  *
18341db5f19SHersen Wu  */
dp_link_settings_read(struct file * f,char __user * buf,size_t size,loff_t * pos)18441db5f19SHersen Wu static ssize_t dp_link_settings_read(struct file *f, char __user *buf,
185dc38fd9dSDavid Francis 				 size_t size, loff_t *pos)
186dc38fd9dSDavid Francis {
18741db5f19SHersen Wu 	struct amdgpu_dm_connector *connector = file_inode(f)->i_private;
18841db5f19SHersen Wu 	struct dc_link *link = connector->dc_link;
18941db5f19SHersen Wu 	char *rd_buf = NULL;
19041db5f19SHersen Wu 	char *rd_buf_ptr = NULL;
191f8ac2cf7SHersen Wu 	const uint32_t rd_buf_size = 100;
192f8ac2cf7SHersen Wu 	uint32_t result = 0;
19341db5f19SHersen Wu 	uint8_t str_len = 0;
19441db5f19SHersen Wu 	int r;
19541db5f19SHersen Wu 
196f8ac2cf7SHersen Wu 	if (*pos & 3 || size & 3)
197f8ac2cf7SHersen Wu 		return -EINVAL;
19841db5f19SHersen Wu 
19941db5f19SHersen Wu 	rd_buf = kcalloc(rd_buf_size, sizeof(char), GFP_KERNEL);
20041db5f19SHersen Wu 	if (!rd_buf)
20141db5f19SHersen Wu 		return 0;
20241db5f19SHersen Wu 
20341db5f19SHersen Wu 	rd_buf_ptr = rd_buf;
20441db5f19SHersen Wu 
2051a394b3cSAnson Jacob 	str_len = strlen("Current:  %d  0x%x  %d  ");
2061a394b3cSAnson Jacob 	snprintf(rd_buf_ptr, str_len, "Current:  %d  0x%x  %d  ",
20741db5f19SHersen Wu 			link->cur_link_settings.lane_count,
20841db5f19SHersen Wu 			link->cur_link_settings.link_rate,
20941db5f19SHersen Wu 			link->cur_link_settings.link_spread);
210f8ac2cf7SHersen Wu 	rd_buf_ptr += str_len;
21141db5f19SHersen Wu 
2121a394b3cSAnson Jacob 	str_len = strlen("Verified:  %d  0x%x  %d  ");
2131a394b3cSAnson Jacob 	snprintf(rd_buf_ptr, str_len, "Verified:  %d  0x%x  %d  ",
21441db5f19SHersen Wu 			link->verified_link_cap.lane_count,
21541db5f19SHersen Wu 			link->verified_link_cap.link_rate,
21641db5f19SHersen Wu 			link->verified_link_cap.link_spread);
217f8ac2cf7SHersen Wu 	rd_buf_ptr += str_len;
21841db5f19SHersen Wu 
2191a394b3cSAnson Jacob 	str_len = strlen("Reported:  %d  0x%x  %d  ");
2201a394b3cSAnson Jacob 	snprintf(rd_buf_ptr, str_len, "Reported:  %d  0x%x  %d  ",
22141db5f19SHersen Wu 			link->reported_link_cap.lane_count,
22241db5f19SHersen Wu 			link->reported_link_cap.link_rate,
22341db5f19SHersen Wu 			link->reported_link_cap.link_spread);
224f8ac2cf7SHersen Wu 	rd_buf_ptr += str_len;
22541db5f19SHersen Wu 
2261a394b3cSAnson Jacob 	str_len = strlen("Preferred:  %d  0x%x  %d  ");
2271a394b3cSAnson Jacob 	snprintf(rd_buf_ptr, str_len, "Preferred:  %d  0x%x  %d\n",
22841db5f19SHersen Wu 			link->preferred_link_setting.lane_count,
22941db5f19SHersen Wu 			link->preferred_link_setting.link_rate,
23041db5f19SHersen Wu 			link->preferred_link_setting.link_spread);
23141db5f19SHersen Wu 
232f8ac2cf7SHersen Wu 	while (size) {
233f8ac2cf7SHersen Wu 		if (*pos >= rd_buf_size)
234f8ac2cf7SHersen Wu 			break;
23541db5f19SHersen Wu 
236f8ac2cf7SHersen Wu 		r = put_user(*(rd_buf + result), buf);
2375d5c6dbaSYongzhi Liu 		if (r) {
2385d5c6dbaSYongzhi Liu 			kfree(rd_buf);
239f8ac2cf7SHersen Wu 			return r; /* r = -EFAULT */
2405d5c6dbaSYongzhi Liu 		}
24141db5f19SHersen Wu 
242f8ac2cf7SHersen Wu 		buf += 1;
243f8ac2cf7SHersen Wu 		size -= 1;
244f8ac2cf7SHersen Wu 		*pos += 1;
245f8ac2cf7SHersen Wu 		result += 1;
246dc38fd9dSDavid Francis 	}
247dc38fd9dSDavid Francis 
24841db5f19SHersen Wu 	kfree(rd_buf);
249f8ac2cf7SHersen Wu 	return result;
250dc38fd9dSDavid Francis }
251dc38fd9dSDavid Francis 
dp_link_settings_write(struct file * f,const char __user * buf,size_t size,loff_t * pos)25241db5f19SHersen Wu static ssize_t dp_link_settings_write(struct file *f, const char __user *buf,
253dc38fd9dSDavid Francis 				 size_t size, loff_t *pos)
254dc38fd9dSDavid Francis {
25541db5f19SHersen Wu 	struct amdgpu_dm_connector *connector = file_inode(f)->i_private;
25641db5f19SHersen Wu 	struct dc_link *link = connector->dc_link;
257a18112aeSWayne Lin 	struct amdgpu_device *adev = drm_to_adev(connector->base.dev);
2581131cadfSAnson Jacob 	struct dc *dc = (struct dc *)link->dc;
25941db5f19SHersen Wu 	struct dc_link_settings prefer_link_settings;
26041db5f19SHersen Wu 	char *wr_buf = NULL;
261f8ac2cf7SHersen Wu 	const uint32_t wr_buf_size = 40;
26241db5f19SHersen Wu 	/* 0: lane_count; 1: link_rate */
26304111850SMikita Lipski 	int max_param_num = 2;
26404111850SMikita Lipski 	uint8_t param_nums = 0;
26541db5f19SHersen Wu 	long param[2];
266c006a1c0SFangzhi Zuo 	bool valid_input = true;
26741db5f19SHersen Wu 
26841db5f19SHersen Wu 	if (size == 0)
269f8ac2cf7SHersen Wu 		return -EINVAL;
27041db5f19SHersen Wu 
27141db5f19SHersen Wu 	wr_buf = kcalloc(wr_buf_size, sizeof(char), GFP_KERNEL);
27241db5f19SHersen Wu 	if (!wr_buf)
27304111850SMikita Lipski 		return -ENOSPC;
27441db5f19SHersen Wu 
275f23750b5SThelford Williams 	if (parse_write_buffer_into_params(wr_buf, wr_buf_size,
27604111850SMikita Lipski 					   (long *)param, buf,
27704111850SMikita Lipski 					   max_param_num,
27804111850SMikita Lipski 					   &param_nums)) {
27941db5f19SHersen Wu 		kfree(wr_buf);
280f8ac2cf7SHersen Wu 		return -EINVAL;
281dc38fd9dSDavid Francis 	}
282dc38fd9dSDavid Francis 
28304111850SMikita Lipski 	if (param_nums <= 0) {
28404111850SMikita Lipski 		kfree(wr_buf);
28504111850SMikita Lipski 		DRM_DEBUG_DRIVER("user data not be read\n");
28604111850SMikita Lipski 		return -EINVAL;
28741db5f19SHersen Wu 	}
28841db5f19SHersen Wu 
28941db5f19SHersen Wu 	switch (param[0]) {
29041db5f19SHersen Wu 	case LANE_COUNT_ONE:
29141db5f19SHersen Wu 	case LANE_COUNT_TWO:
29241db5f19SHersen Wu 	case LANE_COUNT_FOUR:
29341db5f19SHersen Wu 		break;
29441db5f19SHersen Wu 	default:
295c006a1c0SFangzhi Zuo 		valid_input = false;
29641db5f19SHersen Wu 		break;
29741db5f19SHersen Wu 	}
29841db5f19SHersen Wu 
29941db5f19SHersen Wu 	switch (param[1]) {
30041db5f19SHersen Wu 	case LINK_RATE_LOW:
30141db5f19SHersen Wu 	case LINK_RATE_HIGH:
30241db5f19SHersen Wu 	case LINK_RATE_RBR2:
30341db5f19SHersen Wu 	case LINK_RATE_HIGH2:
30441db5f19SHersen Wu 	case LINK_RATE_HIGH3:
30541724ea2SBhawanpreet Lakha 	case LINK_RATE_UHBR10:
306b66fca42SFangzhi Zuo 	case LINK_RATE_UHBR13_5:
307b66fca42SFangzhi Zuo 	case LINK_RATE_UHBR20:
30841db5f19SHersen Wu 		break;
30941db5f19SHersen Wu 	default:
310c006a1c0SFangzhi Zuo 		valid_input = false;
31141db5f19SHersen Wu 		break;
31241db5f19SHersen Wu 	}
31341db5f19SHersen Wu 
314245524d9SHersen Wu 	if (!valid_input) {
31541db5f19SHersen Wu 		kfree(wr_buf);
316f8ac2cf7SHersen Wu 		DRM_DEBUG_DRIVER("Invalid Input value No HW will be programmed\n");
317a18112aeSWayne Lin 		mutex_lock(&adev->dm.dc_lock);
318a18112aeSWayne Lin 		dc_link_set_preferred_training_settings(dc, NULL, NULL, link, false);
319a18112aeSWayne Lin 		mutex_unlock(&adev->dm.dc_lock);
32004111850SMikita Lipski 		return size;
32141db5f19SHersen Wu 	}
32241db5f19SHersen Wu 
32341db5f19SHersen Wu 	/* save user force lane_count, link_rate to preferred settings
32441db5f19SHersen Wu 	 * spread spectrum will not be changed
32541db5f19SHersen Wu 	 */
32641db5f19SHersen Wu 	prefer_link_settings.link_spread = link->cur_link_settings.link_spread;
327c006a1c0SFangzhi Zuo 	prefer_link_settings.use_link_rate_set = false;
32841db5f19SHersen Wu 	prefer_link_settings.lane_count = param[0];
32941db5f19SHersen Wu 	prefer_link_settings.link_rate = param[1];
33041db5f19SHersen Wu 
331a18112aeSWayne Lin 	mutex_lock(&adev->dm.dc_lock);
332a18112aeSWayne Lin 	dc_link_set_preferred_training_settings(dc, &prefer_link_settings, NULL, link, false);
333a18112aeSWayne Lin 	mutex_unlock(&adev->dm.dc_lock);
33441db5f19SHersen Wu 
33541db5f19SHersen Wu 	kfree(wr_buf);
33604111850SMikita Lipski 	return size;
337dc38fd9dSDavid Francis }
338dc38fd9dSDavid Francis 
dp_mst_is_end_device(struct amdgpu_dm_connector * aconnector)3390250a714SFangzhi Zuo static bool dp_mst_is_end_device(struct amdgpu_dm_connector *aconnector)
3400250a714SFangzhi Zuo {
3410250a714SFangzhi Zuo 	bool is_end_device = false;
3420250a714SFangzhi Zuo 	struct drm_dp_mst_topology_mgr *mgr = NULL;
3430250a714SFangzhi Zuo 	struct drm_dp_mst_port *port = NULL;
3440250a714SFangzhi Zuo 
3450250a714SFangzhi Zuo 	if (aconnector->mst_root && aconnector->mst_root->mst_mgr.mst_state) {
3460250a714SFangzhi Zuo 		mgr = &aconnector->mst_root->mst_mgr;
3470250a714SFangzhi Zuo 		port = aconnector->mst_output_port;
3480250a714SFangzhi Zuo 
3490250a714SFangzhi Zuo 		drm_modeset_lock(&mgr->base.lock, NULL);
3500250a714SFangzhi Zuo 		if (port->pdt == DP_PEER_DEVICE_SST_SINK ||
3510250a714SFangzhi Zuo 			port->pdt == DP_PEER_DEVICE_DP_LEGACY_CONV)
3520250a714SFangzhi Zuo 			is_end_device = true;
3530250a714SFangzhi Zuo 		drm_modeset_unlock(&mgr->base.lock);
3540250a714SFangzhi Zuo 	}
3550250a714SFangzhi Zuo 
3560250a714SFangzhi Zuo 	return is_end_device;
3570250a714SFangzhi Zuo }
3580250a714SFangzhi Zuo 
3590250a714SFangzhi Zuo /* Change MST link setting
3600250a714SFangzhi Zuo  *
3610250a714SFangzhi Zuo  * valid lane count value: 1, 2, 4
3620250a714SFangzhi Zuo  * valid link rate value:
3630250a714SFangzhi Zuo  * 06h = 1.62Gbps per lane
3640250a714SFangzhi Zuo  * 0Ah = 2.7Gbps per lane
3650250a714SFangzhi Zuo  * 0Ch = 3.24Gbps per lane
3660250a714SFangzhi Zuo  * 14h = 5.4Gbps per lane
3670250a714SFangzhi Zuo  * 1Eh = 8.1Gbps per lane
3680250a714SFangzhi Zuo  * 3E8h = 10.0Gbps per lane
3690250a714SFangzhi Zuo  * 546h = 13.5Gbps per lane
3700250a714SFangzhi Zuo  * 7D0h = 20.0Gbps per lane
3710250a714SFangzhi Zuo  *
3720250a714SFangzhi Zuo  * debugfs is located at /sys/kernel/debug/dri/0/DP-x/mst_link_settings
3730250a714SFangzhi Zuo  *
3740250a714SFangzhi Zuo  * for example, to force to  2 lane, 10.0GHz,
3750250a714SFangzhi Zuo  * echo 2 0x3e8 > /sys/kernel/debug/dri/0/DP-x/mst_link_settings
3760250a714SFangzhi Zuo  *
3770250a714SFangzhi Zuo  * Valid input will trigger hotplug event to get new link setting applied
3780250a714SFangzhi Zuo  * Invalid input will trigger training setting reset
3790250a714SFangzhi Zuo  *
3800250a714SFangzhi Zuo  * The usage can be referred to link_settings entry
3810250a714SFangzhi Zuo  *
3820250a714SFangzhi Zuo  */
dp_mst_link_setting(struct file * f,const char __user * buf,size_t size,loff_t * pos)3830250a714SFangzhi Zuo static ssize_t dp_mst_link_setting(struct file *f, const char __user *buf,
3840250a714SFangzhi Zuo 				 size_t size, loff_t *pos)
3850250a714SFangzhi Zuo {
3860250a714SFangzhi Zuo 	struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private;
3870250a714SFangzhi Zuo 	struct dc_link *link = aconnector->dc_link;
3880250a714SFangzhi Zuo 	struct amdgpu_device *adev = drm_to_adev(aconnector->base.dev);
3890250a714SFangzhi Zuo 	struct dc *dc = (struct dc *)link->dc;
3900250a714SFangzhi Zuo 	struct dc_link_settings prefer_link_settings;
3910250a714SFangzhi Zuo 	char *wr_buf = NULL;
3920250a714SFangzhi Zuo 	const uint32_t wr_buf_size = 40;
3930250a714SFangzhi Zuo 	/* 0: lane_count; 1: link_rate */
3940250a714SFangzhi Zuo 	int max_param_num = 2;
3950250a714SFangzhi Zuo 	uint8_t param_nums = 0;
3960250a714SFangzhi Zuo 	long param[2];
3970250a714SFangzhi Zuo 	bool valid_input = true;
3980250a714SFangzhi Zuo 
3990250a714SFangzhi Zuo 	if (!dp_mst_is_end_device(aconnector))
4000250a714SFangzhi Zuo 		return -EINVAL;
4010250a714SFangzhi Zuo 
4020250a714SFangzhi Zuo 	if (size == 0)
4030250a714SFangzhi Zuo 		return -EINVAL;
4040250a714SFangzhi Zuo 
4050250a714SFangzhi Zuo 	wr_buf = kcalloc(wr_buf_size, sizeof(char), GFP_KERNEL);
4060250a714SFangzhi Zuo 	if (!wr_buf)
4070250a714SFangzhi Zuo 		return -ENOSPC;
4080250a714SFangzhi Zuo 
4090250a714SFangzhi Zuo 	if (parse_write_buffer_into_params(wr_buf, wr_buf_size,
4100250a714SFangzhi Zuo 					   (long *)param, buf,
4110250a714SFangzhi Zuo 					   max_param_num,
4120250a714SFangzhi Zuo 					   &param_nums)) {
4130250a714SFangzhi Zuo 		kfree(wr_buf);
4140250a714SFangzhi Zuo 		return -EINVAL;
4150250a714SFangzhi Zuo 	}
4160250a714SFangzhi Zuo 
4170250a714SFangzhi Zuo 	if (param_nums <= 0) {
4180250a714SFangzhi Zuo 		kfree(wr_buf);
4190250a714SFangzhi Zuo 		DRM_DEBUG_DRIVER("user data not be read\n");
4200250a714SFangzhi Zuo 		return -EINVAL;
4210250a714SFangzhi Zuo 	}
4220250a714SFangzhi Zuo 
4230250a714SFangzhi Zuo 	switch (param[0]) {
4240250a714SFangzhi Zuo 	case LANE_COUNT_ONE:
4250250a714SFangzhi Zuo 	case LANE_COUNT_TWO:
4260250a714SFangzhi Zuo 	case LANE_COUNT_FOUR:
4270250a714SFangzhi Zuo 		break;
4280250a714SFangzhi Zuo 	default:
4290250a714SFangzhi Zuo 		valid_input = false;
4300250a714SFangzhi Zuo 		break;
4310250a714SFangzhi Zuo 	}
4320250a714SFangzhi Zuo 
4330250a714SFangzhi Zuo 	switch (param[1]) {
4340250a714SFangzhi Zuo 	case LINK_RATE_LOW:
4350250a714SFangzhi Zuo 	case LINK_RATE_HIGH:
4360250a714SFangzhi Zuo 	case LINK_RATE_RBR2:
4370250a714SFangzhi Zuo 	case LINK_RATE_HIGH2:
4380250a714SFangzhi Zuo 	case LINK_RATE_HIGH3:
4390250a714SFangzhi Zuo 	case LINK_RATE_UHBR10:
4400250a714SFangzhi Zuo 	case LINK_RATE_UHBR13_5:
4410250a714SFangzhi Zuo 	case LINK_RATE_UHBR20:
4420250a714SFangzhi Zuo 		break;
4430250a714SFangzhi Zuo 	default:
4440250a714SFangzhi Zuo 		valid_input = false;
4450250a714SFangzhi Zuo 		break;
4460250a714SFangzhi Zuo 	}
4470250a714SFangzhi Zuo 
4480250a714SFangzhi Zuo 	if (!valid_input) {
4490250a714SFangzhi Zuo 		kfree(wr_buf);
4500250a714SFangzhi Zuo 		DRM_DEBUG_DRIVER("Invalid Input value No HW will be programmed\n");
4510250a714SFangzhi Zuo 		mutex_lock(&adev->dm.dc_lock);
4520250a714SFangzhi Zuo 		dc_link_set_preferred_training_settings(dc, NULL, NULL, link, false);
4530250a714SFangzhi Zuo 		mutex_unlock(&adev->dm.dc_lock);
4540250a714SFangzhi Zuo 		return -EINVAL;
4550250a714SFangzhi Zuo 	}
4560250a714SFangzhi Zuo 
4570250a714SFangzhi Zuo 	/* save user force lane_count, link_rate to preferred settings
4580250a714SFangzhi Zuo 	 * spread spectrum will not be changed
4590250a714SFangzhi Zuo 	 */
4600250a714SFangzhi Zuo 	prefer_link_settings.link_spread = link->cur_link_settings.link_spread;
4610250a714SFangzhi Zuo 	prefer_link_settings.use_link_rate_set = false;
4620250a714SFangzhi Zuo 	prefer_link_settings.lane_count = param[0];
4630250a714SFangzhi Zuo 	prefer_link_settings.link_rate = param[1];
4640250a714SFangzhi Zuo 
4650250a714SFangzhi Zuo 	/* skip immediate retrain, and train to new link setting after hotplug event triggered */
4660250a714SFangzhi Zuo 	mutex_lock(&adev->dm.dc_lock);
4670250a714SFangzhi Zuo 	dc_link_set_preferred_training_settings(dc, &prefer_link_settings, NULL, link, true);
4680250a714SFangzhi Zuo 	mutex_unlock(&adev->dm.dc_lock);
4690250a714SFangzhi Zuo 
4700250a714SFangzhi Zuo 	mutex_lock(&aconnector->base.dev->mode_config.mutex);
4710250a714SFangzhi Zuo 	aconnector->base.force = DRM_FORCE_OFF;
4720250a714SFangzhi Zuo 	mutex_unlock(&aconnector->base.dev->mode_config.mutex);
4730250a714SFangzhi Zuo 	drm_kms_helper_hotplug_event(aconnector->base.dev);
4740250a714SFangzhi Zuo 
4750250a714SFangzhi Zuo 	msleep(100);
4760250a714SFangzhi Zuo 
4770250a714SFangzhi Zuo 	mutex_lock(&aconnector->base.dev->mode_config.mutex);
4780250a714SFangzhi Zuo 	aconnector->base.force = DRM_FORCE_UNSPECIFIED;
4790250a714SFangzhi Zuo 	mutex_unlock(&aconnector->base.dev->mode_config.mutex);
4800250a714SFangzhi Zuo 	drm_kms_helper_hotplug_event(aconnector->base.dev);
4810250a714SFangzhi Zuo 
4820250a714SFangzhi Zuo 	kfree(wr_buf);
4830250a714SFangzhi Zuo 	return size;
4840250a714SFangzhi Zuo }
4850250a714SFangzhi Zuo 
486f8ac2cf7SHersen Wu /* function: get current DP PHY settings: voltage swing, pre-emphasis,
487f8ac2cf7SHersen Wu  * post-cursor2 (defined by VESA DP specification)
488f8ac2cf7SHersen Wu  *
489f8ac2cf7SHersen Wu  * valid values
490f8ac2cf7SHersen Wu  * voltage swing: 0,1,2,3
491f8ac2cf7SHersen Wu  * pre-emphasis : 0,1,2,3
492f8ac2cf7SHersen Wu  * post cursor2 : 0,1,2,3
493f8ac2cf7SHersen Wu  *
494f8ac2cf7SHersen Wu  *
495f8ac2cf7SHersen Wu  * how to use this debugfs
496f8ac2cf7SHersen Wu  *
497f8ac2cf7SHersen Wu  * debugfs is located at /sys/kernel/debug/dri/0/DP-x
498f8ac2cf7SHersen Wu  *
499f8ac2cf7SHersen Wu  * there will be directories, like DP-1, DP-2,DP-3, etc. for DP display
500f8ac2cf7SHersen Wu  *
501f8ac2cf7SHersen Wu  * To figure out which DP-x is the display for DP to be check,
502f8ac2cf7SHersen Wu  * cd DP-x
503f8ac2cf7SHersen Wu  * ls -ll
504f8ac2cf7SHersen Wu  * There should be debugfs file, like link_settings, phy_settings.
505f8ac2cf7SHersen Wu  * cat link_settings
506f8ac2cf7SHersen Wu  * from lane_count, link_rate to figure which DP-x is for display to be worked
507f8ac2cf7SHersen Wu  * on
508f8ac2cf7SHersen Wu  *
509f8ac2cf7SHersen Wu  * To get current DP PHY settings,
510f8ac2cf7SHersen Wu  * cat phy_settings
511f8ac2cf7SHersen Wu  *
512f8ac2cf7SHersen Wu  * To change DP PHY settings,
513f8ac2cf7SHersen Wu  * echo <voltage_swing> <pre-emphasis> <post_cursor2> > phy_settings
514f8ac2cf7SHersen Wu  * for examle, to change voltage swing to 2, pre-emphasis to 3, post_cursor2 to
515f8ac2cf7SHersen Wu  * 0,
516f8ac2cf7SHersen Wu  * echo 2 3 0 > phy_settings
517f8ac2cf7SHersen Wu  *
518f8ac2cf7SHersen Wu  * To check if change be applied, get current phy settings by
519f8ac2cf7SHersen Wu  * cat phy_settings
520f8ac2cf7SHersen Wu  *
521f8ac2cf7SHersen Wu  * In case invalid values are set by user, like
522f8ac2cf7SHersen Wu  * echo 1 4 0 > phy_settings
523f8ac2cf7SHersen Wu  *
524f8ac2cf7SHersen Wu  * HW will NOT be programmed by these settings.
525f8ac2cf7SHersen Wu  * cat phy_settings will show the previous valid settings.
526f8ac2cf7SHersen Wu  */
dp_phy_settings_read(struct file * f,char __user * buf,size_t size,loff_t * pos)527f8ac2cf7SHersen Wu static ssize_t dp_phy_settings_read(struct file *f, char __user *buf,
528dc38fd9dSDavid Francis 				 size_t size, loff_t *pos)
529dc38fd9dSDavid Francis {
530f8ac2cf7SHersen Wu 	struct amdgpu_dm_connector *connector = file_inode(f)->i_private;
531f8ac2cf7SHersen Wu 	struct dc_link *link = connector->dc_link;
532f8ac2cf7SHersen Wu 	char *rd_buf = NULL;
533f8ac2cf7SHersen Wu 	const uint32_t rd_buf_size = 20;
534f8ac2cf7SHersen Wu 	uint32_t result = 0;
535f8ac2cf7SHersen Wu 	int r;
536f8ac2cf7SHersen Wu 
537f8ac2cf7SHersen Wu 	if (*pos & 3 || size & 3)
538f8ac2cf7SHersen Wu 		return -EINVAL;
539f8ac2cf7SHersen Wu 
540f8ac2cf7SHersen Wu 	rd_buf = kcalloc(rd_buf_size, sizeof(char), GFP_KERNEL);
541f8ac2cf7SHersen Wu 	if (!rd_buf)
542f8ac2cf7SHersen Wu 		return -EINVAL;
543f8ac2cf7SHersen Wu 
5446d7f7353SAnson Jacob 	snprintf(rd_buf, rd_buf_size, "  %d  %d  %d\n",
54552dffe2fSWenjing Liu 			link->cur_lane_setting[0].VOLTAGE_SWING,
54652dffe2fSWenjing Liu 			link->cur_lane_setting[0].PRE_EMPHASIS,
54752dffe2fSWenjing Liu 			link->cur_lane_setting[0].POST_CURSOR2);
548f8ac2cf7SHersen Wu 
549f8ac2cf7SHersen Wu 	while (size) {
550f8ac2cf7SHersen Wu 		if (*pos >= rd_buf_size)
551f8ac2cf7SHersen Wu 			break;
552f8ac2cf7SHersen Wu 
553f8ac2cf7SHersen Wu 		r = put_user((*(rd_buf + result)), buf);
5545d5c6dbaSYongzhi Liu 		if (r) {
5555d5c6dbaSYongzhi Liu 			kfree(rd_buf);
556f8ac2cf7SHersen Wu 			return r; /* r = -EFAULT */
5575d5c6dbaSYongzhi Liu 		}
558f8ac2cf7SHersen Wu 
559f8ac2cf7SHersen Wu 		buf += 1;
560f8ac2cf7SHersen Wu 		size -= 1;
561f8ac2cf7SHersen Wu 		*pos += 1;
562f8ac2cf7SHersen Wu 		result += 1;
563dc38fd9dSDavid Francis 	}
564dc38fd9dSDavid Francis 
565f8ac2cf7SHersen Wu 	kfree(rd_buf);
566f8ac2cf7SHersen Wu 	return result;
567dc38fd9dSDavid Francis }
568dc38fd9dSDavid Francis 
dp_lttpr_status_show(struct seq_file * m,void * unused)569642f1b40SHamza Mahfooz static int dp_lttpr_status_show(struct seq_file *m, void *unused)
57037bedd99SAurabindo Pillai {
571642f1b40SHamza Mahfooz 	struct drm_connector *connector = m->private;
572642f1b40SHamza Mahfooz 	struct amdgpu_dm_connector *aconnector =
573642f1b40SHamza Mahfooz 		to_amdgpu_dm_connector(connector);
574642f1b40SHamza Mahfooz 	struct dc_lttpr_caps caps = aconnector->dc_link->dpcd_caps.lttpr_caps;
57537bedd99SAurabindo Pillai 
576642f1b40SHamza Mahfooz 	if (connector->status != connector_status_connected)
577642f1b40SHamza Mahfooz 		return -ENODEV;
57837bedd99SAurabindo Pillai 
579642f1b40SHamza Mahfooz 	seq_printf(m, "phy repeater count: %u (raw: 0x%x)\n",
580642f1b40SHamza Mahfooz 		   dp_parse_lttpr_repeater_count(caps.phy_repeater_cnt),
581642f1b40SHamza Mahfooz 		   caps.phy_repeater_cnt);
58237bedd99SAurabindo Pillai 
583642f1b40SHamza Mahfooz 	seq_puts(m, "phy repeater mode: ");
584642f1b40SHamza Mahfooz 
585642f1b40SHamza Mahfooz 	switch (caps.mode) {
586642f1b40SHamza Mahfooz 	case DP_PHY_REPEATER_MODE_TRANSPARENT:
587642f1b40SHamza Mahfooz 		seq_puts(m, "transparent");
58837bedd99SAurabindo Pillai 		break;
589642f1b40SHamza Mahfooz 	case DP_PHY_REPEATER_MODE_NON_TRANSPARENT:
590642f1b40SHamza Mahfooz 		seq_puts(m, "non-transparent");
59137bedd99SAurabindo Pillai 		break;
592642f1b40SHamza Mahfooz 	case 0x00:
593642f1b40SHamza Mahfooz 		seq_puts(m, "non lttpr");
59437bedd99SAurabindo Pillai 		break;
59537bedd99SAurabindo Pillai 	default:
596642f1b40SHamza Mahfooz 		seq_printf(m, "read error (raw: 0x%x)", caps.mode);
59737bedd99SAurabindo Pillai 		break;
59837bedd99SAurabindo Pillai 	}
59937bedd99SAurabindo Pillai 
600642f1b40SHamza Mahfooz 	seq_puts(m, "\n");
60137bedd99SAurabindo Pillai 	return 0;
60237bedd99SAurabindo Pillai }
60337bedd99SAurabindo Pillai 
dp_phy_settings_write(struct file * f,const char __user * buf,size_t size,loff_t * pos)604f8ac2cf7SHersen Wu static ssize_t dp_phy_settings_write(struct file *f, const char __user *buf,
605dc38fd9dSDavid Francis 				 size_t size, loff_t *pos)
606dc38fd9dSDavid Francis {
607f8ac2cf7SHersen Wu 	struct amdgpu_dm_connector *connector = file_inode(f)->i_private;
608f8ac2cf7SHersen Wu 	struct dc_link *link = connector->dc_link;
609f8ac2cf7SHersen Wu 	struct dc *dc = (struct dc *)link->dc;
610f8ac2cf7SHersen Wu 	char *wr_buf = NULL;
611f8ac2cf7SHersen Wu 	uint32_t wr_buf_size = 40;
612f8ac2cf7SHersen Wu 	long param[3];
613f8ac2cf7SHersen Wu 	bool use_prefer_link_setting;
614f8ac2cf7SHersen Wu 	struct link_training_settings link_lane_settings;
61504111850SMikita Lipski 	int max_param_num = 3;
61604111850SMikita Lipski 	uint8_t param_nums = 0;
61704111850SMikita Lipski 	int r = 0;
61804111850SMikita Lipski 
619f8ac2cf7SHersen Wu 
620f8ac2cf7SHersen Wu 	if (size == 0)
62104111850SMikita Lipski 		return -EINVAL;
622f8ac2cf7SHersen Wu 
623f8ac2cf7SHersen Wu 	wr_buf = kcalloc(wr_buf_size, sizeof(char), GFP_KERNEL);
624f8ac2cf7SHersen Wu 	if (!wr_buf)
62504111850SMikita Lipski 		return -ENOSPC;
626f8ac2cf7SHersen Wu 
627839e59a3SPatrik Jakobsson 	if (parse_write_buffer_into_params(wr_buf, wr_buf_size,
62804111850SMikita Lipski 					   (long *)param, buf,
62904111850SMikita Lipski 					   max_param_num,
63004111850SMikita Lipski 					   &param_nums)) {
631f8ac2cf7SHersen Wu 		kfree(wr_buf);
63204111850SMikita Lipski 		return -EINVAL;
633dc38fd9dSDavid Francis 	}
634dc38fd9dSDavid Francis 
63504111850SMikita Lipski 	if (param_nums <= 0) {
63604111850SMikita Lipski 		kfree(wr_buf);
63704111850SMikita Lipski 		DRM_DEBUG_DRIVER("user data not be read\n");
63804111850SMikita Lipski 		return -EINVAL;
639f8ac2cf7SHersen Wu 	}
640f8ac2cf7SHersen Wu 
641f8ac2cf7SHersen Wu 	if ((param[0] > VOLTAGE_SWING_MAX_LEVEL) ||
642f8ac2cf7SHersen Wu 			(param[1] > PRE_EMPHASIS_MAX_LEVEL) ||
643f8ac2cf7SHersen Wu 			(param[2] > POST_CURSOR2_MAX_LEVEL)) {
644f8ac2cf7SHersen Wu 		kfree(wr_buf);
645f8ac2cf7SHersen Wu 		DRM_DEBUG_DRIVER("Invalid Input No HW will be programmed\n");
64604111850SMikita Lipski 		return size;
647f8ac2cf7SHersen Wu 	}
648f8ac2cf7SHersen Wu 
649f8ac2cf7SHersen Wu 	/* get link settings: lane count, link rate */
650f8ac2cf7SHersen Wu 	use_prefer_link_setting =
651f8ac2cf7SHersen Wu 		((link->preferred_link_setting.link_rate != LINK_RATE_UNKNOWN) &&
652f8ac2cf7SHersen Wu 		(link->test_pattern_enabled));
653f8ac2cf7SHersen Wu 
654f8ac2cf7SHersen Wu 	memset(&link_lane_settings, 0, sizeof(link_lane_settings));
655f8ac2cf7SHersen Wu 
656f8ac2cf7SHersen Wu 	if (use_prefer_link_setting) {
657f8ac2cf7SHersen Wu 		link_lane_settings.link_settings.lane_count =
658f8ac2cf7SHersen Wu 				link->preferred_link_setting.lane_count;
659f8ac2cf7SHersen Wu 		link_lane_settings.link_settings.link_rate =
660f8ac2cf7SHersen Wu 				link->preferred_link_setting.link_rate;
661f8ac2cf7SHersen Wu 		link_lane_settings.link_settings.link_spread =
662f8ac2cf7SHersen Wu 				link->preferred_link_setting.link_spread;
663f8ac2cf7SHersen Wu 	} else {
664f8ac2cf7SHersen Wu 		link_lane_settings.link_settings.lane_count =
665f8ac2cf7SHersen Wu 				link->cur_link_settings.lane_count;
666f8ac2cf7SHersen Wu 		link_lane_settings.link_settings.link_rate =
667f8ac2cf7SHersen Wu 				link->cur_link_settings.link_rate;
668f8ac2cf7SHersen Wu 		link_lane_settings.link_settings.link_spread =
669f8ac2cf7SHersen Wu 				link->cur_link_settings.link_spread;
670f8ac2cf7SHersen Wu 	}
671f8ac2cf7SHersen Wu 
672f8ac2cf7SHersen Wu 	/* apply phy settings from user */
673f8ac2cf7SHersen Wu 	for (r = 0; r < link_lane_settings.link_settings.lane_count; r++) {
6742b96b036SWenjing Liu 		link_lane_settings.hw_lane_settings[r].VOLTAGE_SWING =
675f8ac2cf7SHersen Wu 				(enum dc_voltage_swing) (param[0]);
6762b96b036SWenjing Liu 		link_lane_settings.hw_lane_settings[r].PRE_EMPHASIS =
677f8ac2cf7SHersen Wu 				(enum dc_pre_emphasis) (param[1]);
6782b96b036SWenjing Liu 		link_lane_settings.hw_lane_settings[r].POST_CURSOR2 =
679f8ac2cf7SHersen Wu 				(enum dc_post_cursor2) (param[2]);
680f8ac2cf7SHersen Wu 	}
681f8ac2cf7SHersen Wu 
682f8ac2cf7SHersen Wu 	/* program ASIC registers and DPCD registers */
683f8ac2cf7SHersen Wu 	dc_link_set_drive_settings(dc, &link_lane_settings, link);
684f8ac2cf7SHersen Wu 
685f8ac2cf7SHersen Wu 	kfree(wr_buf);
68604111850SMikita Lipski 	return size;
687dc38fd9dSDavid Francis }
688dc38fd9dSDavid Francis 
6890a1d5659SHersen Wu /* function description
6900a1d5659SHersen Wu  *
6910a1d5659SHersen Wu  * set PHY layer or Link layer test pattern
6920a1d5659SHersen Wu  * PHY test pattern is used for PHY SI check.
6930a1d5659SHersen Wu  * Link layer test will not affect PHY SI.
6940a1d5659SHersen Wu  *
6950a1d5659SHersen Wu  * Reset Test Pattern:
6960a1d5659SHersen Wu  * 0 = DP_TEST_PATTERN_VIDEO_MODE
6970a1d5659SHersen Wu  *
6980a1d5659SHersen Wu  * PHY test pattern supported:
6990a1d5659SHersen Wu  * 1 = DP_TEST_PATTERN_D102
7000a1d5659SHersen Wu  * 2 = DP_TEST_PATTERN_SYMBOL_ERROR
7010a1d5659SHersen Wu  * 3 = DP_TEST_PATTERN_PRBS7
7020a1d5659SHersen Wu  * 4 = DP_TEST_PATTERN_80BIT_CUSTOM
7030a1d5659SHersen Wu  * 5 = DP_TEST_PATTERN_CP2520_1
7040a1d5659SHersen Wu  * 6 = DP_TEST_PATTERN_CP2520_2 = DP_TEST_PATTERN_HBR2_COMPLIANCE_EYE
7050a1d5659SHersen Wu  * 7 = DP_TEST_PATTERN_CP2520_3
7060a1d5659SHersen Wu  *
7070a1d5659SHersen Wu  * DP PHY Link Training Patterns
7080a1d5659SHersen Wu  * 8 = DP_TEST_PATTERN_TRAINING_PATTERN1
7090a1d5659SHersen Wu  * 9 = DP_TEST_PATTERN_TRAINING_PATTERN2
7100a1d5659SHersen Wu  * a = DP_TEST_PATTERN_TRAINING_PATTERN3
7110a1d5659SHersen Wu  * b = DP_TEST_PATTERN_TRAINING_PATTERN4
7120a1d5659SHersen Wu  *
7130a1d5659SHersen Wu  * DP Link Layer Test pattern
7140a1d5659SHersen Wu  * c = DP_TEST_PATTERN_COLOR_SQUARES
7150a1d5659SHersen Wu  * d = DP_TEST_PATTERN_COLOR_SQUARES_CEA
7160a1d5659SHersen Wu  * e = DP_TEST_PATTERN_VERTICAL_BARS
7170a1d5659SHersen Wu  * f = DP_TEST_PATTERN_HORIZONTAL_BARS
7180a1d5659SHersen Wu  * 10= DP_TEST_PATTERN_COLOR_RAMP
7190a1d5659SHersen Wu  *
7200a1d5659SHersen Wu  * debugfs phy_test_pattern is located at /syskernel/debug/dri/0/DP-x
7210a1d5659SHersen Wu  *
7220a1d5659SHersen Wu  * --- set test pattern
7230a1d5659SHersen Wu  * echo <test pattern #> > test_pattern
7240a1d5659SHersen Wu  *
7250a1d5659SHersen Wu  * If test pattern # is not supported, NO HW programming will be done.
7260a1d5659SHersen Wu  * for DP_TEST_PATTERN_80BIT_CUSTOM, it needs extra 10 bytes of data
7270a1d5659SHersen Wu  * for the user pattern. input 10 bytes data are separated by space
7280a1d5659SHersen Wu  *
7290a1d5659SHersen Wu  * echo 0x4 0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88 0x99 0xaa > test_pattern
7300a1d5659SHersen Wu  *
7310a1d5659SHersen Wu  * --- reset test pattern
7320a1d5659SHersen Wu  * echo 0 > test_pattern
7330a1d5659SHersen Wu  *
7340a1d5659SHersen Wu  * --- HPD detection is disabled when set PHY test pattern
7350a1d5659SHersen Wu  *
7360a1d5659SHersen Wu  * when PHY test pattern (pattern # within [1,7]) is set, HPD pin of HW ASIC
7370a1d5659SHersen Wu  * is disable. User could unplug DP display from DP connected and plug scope to
7380a1d5659SHersen Wu  * check test pattern PHY SI.
7390a1d5659SHersen Wu  * If there is need unplug scope and plug DP display back, do steps below:
7400a1d5659SHersen Wu  * echo 0 > phy_test_pattern
7410a1d5659SHersen Wu  * unplug scope
7420a1d5659SHersen Wu  * plug DP display.
7430a1d5659SHersen Wu  *
7440a1d5659SHersen Wu  * "echo 0 > phy_test_pattern" will re-enable HPD pin again so that video sw
7450a1d5659SHersen Wu  * driver could detect "unplug scope" and "plug DP display"
7460a1d5659SHersen Wu  */
dp_phy_test_pattern_debugfs_write(struct file * f,const char __user * buf,size_t size,loff_t * pos)747dc38fd9dSDavid Francis static ssize_t dp_phy_test_pattern_debugfs_write(struct file *f, const char __user *buf,
748dc38fd9dSDavid Francis 				 size_t size, loff_t *pos)
749dc38fd9dSDavid Francis {
7500a1d5659SHersen Wu 	struct amdgpu_dm_connector *connector = file_inode(f)->i_private;
7510a1d5659SHersen Wu 	struct dc_link *link = connector->dc_link;
7520a1d5659SHersen Wu 	char *wr_buf = NULL;
7530a1d5659SHersen Wu 	uint32_t wr_buf_size = 100;
75453a599deSHersen Wu 	long param[11] = {0x0};
75504111850SMikita Lipski 	int max_param_num = 11;
7560a1d5659SHersen Wu 	enum dp_test_pattern test_pattern = DP_TEST_PATTERN_UNSUPPORTED;
7570a1d5659SHersen Wu 	bool disable_hpd = false;
7580a1d5659SHersen Wu 	bool valid_test_pattern = false;
75904111850SMikita Lipski 	uint8_t param_nums = 0;
760c07a013aSBhaskar Chowdhury 	/* init with default 80bit custom pattern */
76153a599deSHersen Wu 	uint8_t custom_pattern[10] = {
76253a599deSHersen Wu 			0x1f, 0x7c, 0xf0, 0xc1, 0x07,
76353a599deSHersen Wu 			0x1f, 0x7c, 0xf0, 0xc1, 0x07
76453a599deSHersen Wu 			};
7650a1d5659SHersen Wu 	struct dc_link_settings prefer_link_settings = {LANE_COUNT_UNKNOWN,
7660a1d5659SHersen Wu 			LINK_RATE_UNKNOWN, LINK_SPREAD_DISABLED};
7670a1d5659SHersen Wu 	struct dc_link_settings cur_link_settings = {LANE_COUNT_UNKNOWN,
7680a1d5659SHersen Wu 			LINK_RATE_UNKNOWN, LINK_SPREAD_DISABLED};
7690a1d5659SHersen Wu 	struct link_training_settings link_training_settings;
7700a1d5659SHersen Wu 	int i;
7710a1d5659SHersen Wu 
7720a1d5659SHersen Wu 	if (size == 0)
77304111850SMikita Lipski 		return -EINVAL;
7740a1d5659SHersen Wu 
7750a1d5659SHersen Wu 	wr_buf = kcalloc(wr_buf_size, sizeof(char), GFP_KERNEL);
7760a1d5659SHersen Wu 	if (!wr_buf)
77704111850SMikita Lipski 		return -ENOSPC;
7780a1d5659SHersen Wu 
779839e59a3SPatrik Jakobsson 	if (parse_write_buffer_into_params(wr_buf, wr_buf_size,
78004111850SMikita Lipski 					   (long *)param, buf,
78104111850SMikita Lipski 					   max_param_num,
78204111850SMikita Lipski 					   &param_nums)) {
78304111850SMikita Lipski 		kfree(wr_buf);
78404111850SMikita Lipski 		return -EINVAL;
78504111850SMikita Lipski 	}
7860a1d5659SHersen Wu 
78704111850SMikita Lipski 	if (param_nums <= 0) {
7880a1d5659SHersen Wu 		kfree(wr_buf);
7890a1d5659SHersen Wu 		DRM_DEBUG_DRIVER("user data not be read\n");
79004111850SMikita Lipski 		return -EINVAL;
7910a1d5659SHersen Wu 	}
7920a1d5659SHersen Wu 
7930a1d5659SHersen Wu 
7940a1d5659SHersen Wu 	test_pattern = param[0];
7950a1d5659SHersen Wu 
7960a1d5659SHersen Wu 	switch (test_pattern) {
7970a1d5659SHersen Wu 	case DP_TEST_PATTERN_VIDEO_MODE:
7980a1d5659SHersen Wu 	case DP_TEST_PATTERN_COLOR_SQUARES:
7990a1d5659SHersen Wu 	case DP_TEST_PATTERN_COLOR_SQUARES_CEA:
8000a1d5659SHersen Wu 	case DP_TEST_PATTERN_VERTICAL_BARS:
8010a1d5659SHersen Wu 	case DP_TEST_PATTERN_HORIZONTAL_BARS:
8020a1d5659SHersen Wu 	case DP_TEST_PATTERN_COLOR_RAMP:
8030a1d5659SHersen Wu 		valid_test_pattern = true;
8040a1d5659SHersen Wu 		break;
8050a1d5659SHersen Wu 
8060a1d5659SHersen Wu 	case DP_TEST_PATTERN_D102:
8070a1d5659SHersen Wu 	case DP_TEST_PATTERN_SYMBOL_ERROR:
8080a1d5659SHersen Wu 	case DP_TEST_PATTERN_PRBS7:
8090a1d5659SHersen Wu 	case DP_TEST_PATTERN_80BIT_CUSTOM:
8100a1d5659SHersen Wu 	case DP_TEST_PATTERN_HBR2_COMPLIANCE_EYE:
8110a1d5659SHersen Wu 	case DP_TEST_PATTERN_TRAINING_PATTERN4:
8120a1d5659SHersen Wu 		disable_hpd = true;
8130a1d5659SHersen Wu 		valid_test_pattern = true;
8140a1d5659SHersen Wu 		break;
8150a1d5659SHersen Wu 
8160a1d5659SHersen Wu 	default:
8170a1d5659SHersen Wu 		valid_test_pattern = false;
8180a1d5659SHersen Wu 		test_pattern = DP_TEST_PATTERN_UNSUPPORTED;
8190a1d5659SHersen Wu 		break;
8200a1d5659SHersen Wu 	}
8210a1d5659SHersen Wu 
8220a1d5659SHersen Wu 	if (!valid_test_pattern) {
8230a1d5659SHersen Wu 		kfree(wr_buf);
8240a1d5659SHersen Wu 		DRM_DEBUG_DRIVER("Invalid Test Pattern Parameters\n");
82504111850SMikita Lipski 		return size;
8260a1d5659SHersen Wu 	}
8270a1d5659SHersen Wu 
8280a1d5659SHersen Wu 	if (test_pattern == DP_TEST_PATTERN_80BIT_CUSTOM) {
82953a599deSHersen Wu 		for (i = 0; i < 10; i++) {
83053a599deSHersen Wu 			if ((uint8_t) param[i + 1] != 0x0)
83153a599deSHersen Wu 				break;
83253a599deSHersen Wu 		}
83353a599deSHersen Wu 
83453a599deSHersen Wu 		if (i < 10) {
83553a599deSHersen Wu 			/* not use default value */
8360a1d5659SHersen Wu 			for (i = 0; i < 10; i++)
8370a1d5659SHersen Wu 				custom_pattern[i] = (uint8_t) param[i + 1];
8380a1d5659SHersen Wu 		}
83953a599deSHersen Wu 	}
8400a1d5659SHersen Wu 
8410a1d5659SHersen Wu 	/* Usage: set DP physical test pattern using debugfs with normal DP
8420a1d5659SHersen Wu 	 * panel. Then plug out DP panel and connect a scope to measure
8430a1d5659SHersen Wu 	 * For normal video mode and test pattern generated from CRCT,
8440a1d5659SHersen Wu 	 * they are visibile to user. So do not disable HPD.
8450a1d5659SHersen Wu 	 * Video Mode is also set to clear the test pattern, so enable HPD
8460a1d5659SHersen Wu 	 * because it might have been disabled after a test pattern was set.
8470a1d5659SHersen Wu 	 * AUX depends on HPD * sequence dependent, do not move!
8480a1d5659SHersen Wu 	 */
8490a1d5659SHersen Wu 	if (!disable_hpd)
8500a1d5659SHersen Wu 		dc_link_enable_hpd(link);
8510a1d5659SHersen Wu 
8520a1d5659SHersen Wu 	prefer_link_settings.lane_count = link->verified_link_cap.lane_count;
8530a1d5659SHersen Wu 	prefer_link_settings.link_rate = link->verified_link_cap.link_rate;
8540a1d5659SHersen Wu 	prefer_link_settings.link_spread = link->verified_link_cap.link_spread;
8550a1d5659SHersen Wu 
8560a1d5659SHersen Wu 	cur_link_settings.lane_count = link->cur_link_settings.lane_count;
8570a1d5659SHersen Wu 	cur_link_settings.link_rate = link->cur_link_settings.link_rate;
8580a1d5659SHersen Wu 	cur_link_settings.link_spread = link->cur_link_settings.link_spread;
8590a1d5659SHersen Wu 
8600a1d5659SHersen Wu 	link_training_settings.link_settings = cur_link_settings;
8610a1d5659SHersen Wu 
8620a1d5659SHersen Wu 
8630a1d5659SHersen Wu 	if (test_pattern != DP_TEST_PATTERN_VIDEO_MODE) {
8640a1d5659SHersen Wu 		if (prefer_link_settings.lane_count != LANE_COUNT_UNKNOWN &&
8650a1d5659SHersen Wu 			prefer_link_settings.link_rate !=  LINK_RATE_UNKNOWN &&
8660a1d5659SHersen Wu 			(prefer_link_settings.lane_count != cur_link_settings.lane_count ||
8670a1d5659SHersen Wu 			prefer_link_settings.link_rate != cur_link_settings.link_rate))
8680a1d5659SHersen Wu 			link_training_settings.link_settings = prefer_link_settings;
8690a1d5659SHersen Wu 	}
8700a1d5659SHersen Wu 
8710a1d5659SHersen Wu 	for (i = 0; i < (unsigned int)(link_training_settings.link_settings.lane_count); i++)
8722b96b036SWenjing Liu 		link_training_settings.hw_lane_settings[i] = link->cur_lane_setting[i];
8730a1d5659SHersen Wu 
8747ae1dbe6SWenjing Liu 	dc_link_dp_set_test_pattern(
8750a1d5659SHersen Wu 		link,
8760a1d5659SHersen Wu 		test_pattern,
8772057b7e1SWenjing Liu 		DP_TEST_PATTERN_COLOR_SPACE_RGB,
8780a1d5659SHersen Wu 		&link_training_settings,
8790a1d5659SHersen Wu 		custom_pattern,
8800a1d5659SHersen Wu 		10);
8810a1d5659SHersen Wu 
8820a1d5659SHersen Wu 	/* Usage: Set DP physical test pattern using AMDDP with normal DP panel
8830a1d5659SHersen Wu 	 * Then plug out DP panel and connect a scope to measure DP PHY signal.
8840a1d5659SHersen Wu 	 * Need disable interrupt to avoid SW driver disable DP output. This is
8850a1d5659SHersen Wu 	 * done after the test pattern is set.
8860a1d5659SHersen Wu 	 */
8870a1d5659SHersen Wu 	if (valid_test_pattern && disable_hpd)
8880a1d5659SHersen Wu 		dc_link_disable_hpd(link);
8890a1d5659SHersen Wu 
8900a1d5659SHersen Wu 	kfree(wr_buf);
8910a1d5659SHersen Wu 
89204111850SMikita Lipski 	return size;
893dc38fd9dSDavid Francis }
894dc38fd9dSDavid Francis 
89561fd2fd8SLee Jones /*
89660ec1b56SNicholas Kazlauskas  * Returns the DMCUB tracebuffer contents.
89760ec1b56SNicholas Kazlauskas  * Example usage: cat /sys/kernel/debug/dri/0/amdgpu_dm_dmub_tracebuffer
89860ec1b56SNicholas Kazlauskas  */
dmub_tracebuffer_show(struct seq_file * m,void * data)89960ec1b56SNicholas Kazlauskas static int dmub_tracebuffer_show(struct seq_file *m, void *data)
90060ec1b56SNicholas Kazlauskas {
90160ec1b56SNicholas Kazlauskas 	struct amdgpu_device *adev = m->private;
90260ec1b56SNicholas Kazlauskas 	struct dmub_srv_fb_info *fb_info = adev->dm.dmub_fb_info;
90360ec1b56SNicholas Kazlauskas 	struct dmub_debugfs_trace_entry *entries;
90460ec1b56SNicholas Kazlauskas 	uint8_t *tbuf_base;
90560ec1b56SNicholas Kazlauskas 	uint32_t tbuf_size, max_entries, num_entries, i;
90660ec1b56SNicholas Kazlauskas 
90760ec1b56SNicholas Kazlauskas 	if (!fb_info)
90860ec1b56SNicholas Kazlauskas 		return 0;
90960ec1b56SNicholas Kazlauskas 
91060ec1b56SNicholas Kazlauskas 	tbuf_base = (uint8_t *)fb_info->fb[DMUB_WINDOW_5_TRACEBUFF].cpu_addr;
91160ec1b56SNicholas Kazlauskas 	if (!tbuf_base)
91260ec1b56SNicholas Kazlauskas 		return 0;
91360ec1b56SNicholas Kazlauskas 
91460ec1b56SNicholas Kazlauskas 	tbuf_size = fb_info->fb[DMUB_WINDOW_5_TRACEBUFF].size;
91560ec1b56SNicholas Kazlauskas 	max_entries = (tbuf_size - sizeof(struct dmub_debugfs_trace_header)) /
91660ec1b56SNicholas Kazlauskas 		      sizeof(struct dmub_debugfs_trace_entry);
91760ec1b56SNicholas Kazlauskas 
91860ec1b56SNicholas Kazlauskas 	num_entries =
91960ec1b56SNicholas Kazlauskas 		((struct dmub_debugfs_trace_header *)tbuf_base)->entry_count;
92060ec1b56SNicholas Kazlauskas 
92160ec1b56SNicholas Kazlauskas 	num_entries = min(num_entries, max_entries);
92260ec1b56SNicholas Kazlauskas 
92360ec1b56SNicholas Kazlauskas 	entries = (struct dmub_debugfs_trace_entry
92460ec1b56SNicholas Kazlauskas 			   *)(tbuf_base +
92560ec1b56SNicholas Kazlauskas 			      sizeof(struct dmub_debugfs_trace_header));
92660ec1b56SNicholas Kazlauskas 
92760ec1b56SNicholas Kazlauskas 	for (i = 0; i < num_entries; ++i) {
92860ec1b56SNicholas Kazlauskas 		struct dmub_debugfs_trace_entry *entry = &entries[i];
92960ec1b56SNicholas Kazlauskas 
93060ec1b56SNicholas Kazlauskas 		seq_printf(m,
93160ec1b56SNicholas Kazlauskas 			   "trace_code=%u tick_count=%u param0=%u param1=%u\n",
93260ec1b56SNicholas Kazlauskas 			   entry->trace_code, entry->tick_count, entry->param0,
93360ec1b56SNicholas Kazlauskas 			   entry->param1);
93460ec1b56SNicholas Kazlauskas 	}
93560ec1b56SNicholas Kazlauskas 
93660ec1b56SNicholas Kazlauskas 	return 0;
93760ec1b56SNicholas Kazlauskas }
93860ec1b56SNicholas Kazlauskas 
93961fd2fd8SLee Jones /*
94023640767SNicholas Kazlauskas  * Returns the DMCUB firmware state contents.
94123640767SNicholas Kazlauskas  * Example usage: cat /sys/kernel/debug/dri/0/amdgpu_dm_dmub_fw_state
94223640767SNicholas Kazlauskas  */
dmub_fw_state_show(struct seq_file * m,void * data)94323640767SNicholas Kazlauskas static int dmub_fw_state_show(struct seq_file *m, void *data)
94423640767SNicholas Kazlauskas {
94523640767SNicholas Kazlauskas 	struct amdgpu_device *adev = m->private;
94623640767SNicholas Kazlauskas 	struct dmub_srv_fb_info *fb_info = adev->dm.dmub_fb_info;
94723640767SNicholas Kazlauskas 	uint8_t *state_base;
94823640767SNicholas Kazlauskas 	uint32_t state_size;
94923640767SNicholas Kazlauskas 
95023640767SNicholas Kazlauskas 	if (!fb_info)
95123640767SNicholas Kazlauskas 		return 0;
95223640767SNicholas Kazlauskas 
95323640767SNicholas Kazlauskas 	state_base = (uint8_t *)fb_info->fb[DMUB_WINDOW_6_FW_STATE].cpu_addr;
95423640767SNicholas Kazlauskas 	if (!state_base)
95523640767SNicholas Kazlauskas 		return 0;
95623640767SNicholas Kazlauskas 
95723640767SNicholas Kazlauskas 	state_size = fb_info->fb[DMUB_WINDOW_6_FW_STATE].size;
95823640767SNicholas Kazlauskas 
95923640767SNicholas Kazlauskas 	return seq_write(m, state_base, state_size);
96023640767SNicholas Kazlauskas }
96123640767SNicholas Kazlauskas 
9627238b42eSMikita Lipski /* psr_capability_show() - show eDP panel PSR capability
9637238b42eSMikita Lipski  *
9647238b42eSMikita Lipski  * The read function: sink_psr_capability_show
9657238b42eSMikita Lipski  * Shows if sink has PSR capability or not.
9667238b42eSMikita Lipski  * If yes - the PSR version is appended
9677238b42eSMikita Lipski  *
9687238b42eSMikita Lipski  *	cat /sys/kernel/debug/dri/0/eDP-X/psr_capability
9697238b42eSMikita Lipski  *
9707238b42eSMikita Lipski  * Expected output:
9717238b42eSMikita Lipski  * "Sink support: no\n" - if panel doesn't support PSR
9727238b42eSMikita Lipski  * "Sink support: yes [0x01]\n" - if panel supports PSR1
9737238b42eSMikita Lipski  * "Driver support: no\n" - if driver doesn't support PSR
9747238b42eSMikita Lipski  * "Driver support: yes [0x01]\n" - if driver supports PSR1
9757238b42eSMikita Lipski  */
psr_capability_show(struct seq_file * m,void * data)9767238b42eSMikita Lipski static int psr_capability_show(struct seq_file *m, void *data)
9777238b42eSMikita Lipski {
9787238b42eSMikita Lipski 	struct drm_connector *connector = m->private;
9797238b42eSMikita Lipski 	struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
9807238b42eSMikita Lipski 	struct dc_link *link = aconnector->dc_link;
9817238b42eSMikita Lipski 
9827238b42eSMikita Lipski 	if (!link)
9837238b42eSMikita Lipski 		return -ENODEV;
9847238b42eSMikita Lipski 
9857238b42eSMikita Lipski 	if (link->type == dc_connection_none)
9867238b42eSMikita Lipski 		return -ENODEV;
9877238b42eSMikita Lipski 
9887238b42eSMikita Lipski 	if (!(link->connector_signal & SIGNAL_TYPE_EDP))
9897238b42eSMikita Lipski 		return -ENODEV;
9907238b42eSMikita Lipski 
99138a15ad9SDave Airlie 	seq_printf(m, "Sink support: %s", str_yes_no(link->dpcd_caps.psr_info.psr_version != 0));
9923e6084aeSPo Ting Chen 	if (link->dpcd_caps.psr_info.psr_version)
9933e6084aeSPo Ting Chen 		seq_printf(m, " [0x%02x]", link->dpcd_caps.psr_info.psr_version);
9947238b42eSMikita Lipski 	seq_puts(m, "\n");
9957238b42eSMikita Lipski 
996972aa1a1SLucas De Marchi 	seq_printf(m, "Driver support: %s", str_yes_no(link->psr_settings.psr_feature_enabled));
9977238b42eSMikita Lipski 	if (link->psr_settings.psr_version)
9987238b42eSMikita Lipski 		seq_printf(m, " [0x%02x]", link->psr_settings.psr_version);
9997238b42eSMikita Lipski 	seq_puts(m, "\n");
10007238b42eSMikita Lipski 
10017238b42eSMikita Lipski 	return 0;
10027238b42eSMikita Lipski }
10037238b42eSMikita Lipski 
1004727962f0SNicholas Kazlauskas /*
10054cd79f61SBhanuprakash Modem  * Returns the current bpc for the crtc.
10064cd79f61SBhanuprakash Modem  * Example usage: cat /sys/kernel/debug/dri/0/crtc-0/amdgpu_current_bpc
10074b7ef85cSNicholas Kazlauskas  */
amdgpu_current_bpc_show(struct seq_file * m,void * data)10084cd79f61SBhanuprakash Modem static int amdgpu_current_bpc_show(struct seq_file *m, void *data)
10094b7ef85cSNicholas Kazlauskas {
10104cd79f61SBhanuprakash Modem 	struct drm_crtc *crtc = m->private;
10114cd79f61SBhanuprakash Modem 	struct drm_device *dev = crtc->dev;
10124b7ef85cSNicholas Kazlauskas 	struct dm_crtc_state *dm_crtc_state = NULL;
10134b7ef85cSNicholas Kazlauskas 	int res = -ENODEV;
10144b7ef85cSNicholas Kazlauskas 	unsigned int bpc;
10154b7ef85cSNicholas Kazlauskas 
10164b7ef85cSNicholas Kazlauskas 	mutex_lock(&dev->mode_config.mutex);
10174b7ef85cSNicholas Kazlauskas 	drm_modeset_lock(&crtc->mutex, NULL);
10184b7ef85cSNicholas Kazlauskas 	if (crtc->state == NULL)
10194b7ef85cSNicholas Kazlauskas 		goto unlock;
10204b7ef85cSNicholas Kazlauskas 
10214b7ef85cSNicholas Kazlauskas 	dm_crtc_state = to_dm_crtc_state(crtc->state);
10224b7ef85cSNicholas Kazlauskas 	if (dm_crtc_state->stream == NULL)
10234b7ef85cSNicholas Kazlauskas 		goto unlock;
10244b7ef85cSNicholas Kazlauskas 
10254b7ef85cSNicholas Kazlauskas 	switch (dm_crtc_state->stream->timing.display_color_depth) {
10264b7ef85cSNicholas Kazlauskas 	case COLOR_DEPTH_666:
10274b7ef85cSNicholas Kazlauskas 		bpc = 6;
10284b7ef85cSNicholas Kazlauskas 		break;
10294b7ef85cSNicholas Kazlauskas 	case COLOR_DEPTH_888:
10304b7ef85cSNicholas Kazlauskas 		bpc = 8;
10314b7ef85cSNicholas Kazlauskas 		break;
10324b7ef85cSNicholas Kazlauskas 	case COLOR_DEPTH_101010:
10334b7ef85cSNicholas Kazlauskas 		bpc = 10;
10344b7ef85cSNicholas Kazlauskas 		break;
10354b7ef85cSNicholas Kazlauskas 	case COLOR_DEPTH_121212:
10364b7ef85cSNicholas Kazlauskas 		bpc = 12;
10374b7ef85cSNicholas Kazlauskas 		break;
10384b7ef85cSNicholas Kazlauskas 	case COLOR_DEPTH_161616:
10394b7ef85cSNicholas Kazlauskas 		bpc = 16;
10404b7ef85cSNicholas Kazlauskas 		break;
10414b7ef85cSNicholas Kazlauskas 	default:
10424b7ef85cSNicholas Kazlauskas 		goto unlock;
10434b7ef85cSNicholas Kazlauskas 	}
10444b7ef85cSNicholas Kazlauskas 
10454b7ef85cSNicholas Kazlauskas 	seq_printf(m, "Current: %u\n", bpc);
10464b7ef85cSNicholas Kazlauskas 	res = 0;
10474b7ef85cSNicholas Kazlauskas 
10484b7ef85cSNicholas Kazlauskas unlock:
10494b7ef85cSNicholas Kazlauskas 	drm_modeset_unlock(&crtc->mutex);
10504b7ef85cSNicholas Kazlauskas 	mutex_unlock(&dev->mode_config.mutex);
10514b7ef85cSNicholas Kazlauskas 
10524b7ef85cSNicholas Kazlauskas 	return res;
10534b7ef85cSNicholas Kazlauskas }
10544cd79f61SBhanuprakash Modem DEFINE_SHOW_ATTRIBUTE(amdgpu_current_bpc);
10554b7ef85cSNicholas Kazlauskas 
1056fcd1e484SFangzhi Zuo /*
1057fd45b654SHarry Wentland  * Returns the current colorspace for the crtc.
1058fd45b654SHarry Wentland  * Example usage: cat /sys/kernel/debug/dri/0/crtc-0/amdgpu_current_colorspace
1059fd45b654SHarry Wentland  */
amdgpu_current_colorspace_show(struct seq_file * m,void * data)1060fd45b654SHarry Wentland static int amdgpu_current_colorspace_show(struct seq_file *m, void *data)
1061fd45b654SHarry Wentland {
1062fd45b654SHarry Wentland 	struct drm_crtc *crtc = m->private;
1063fd45b654SHarry Wentland 	struct drm_device *dev = crtc->dev;
1064fd45b654SHarry Wentland 	struct dm_crtc_state *dm_crtc_state = NULL;
1065fd45b654SHarry Wentland 	int res = -ENODEV;
1066fd45b654SHarry Wentland 
1067fd45b654SHarry Wentland 	mutex_lock(&dev->mode_config.mutex);
1068fd45b654SHarry Wentland 	drm_modeset_lock(&crtc->mutex, NULL);
1069fd45b654SHarry Wentland 	if (crtc->state == NULL)
1070fd45b654SHarry Wentland 		goto unlock;
1071fd45b654SHarry Wentland 
1072fd45b654SHarry Wentland 	dm_crtc_state = to_dm_crtc_state(crtc->state);
1073fd45b654SHarry Wentland 	if (dm_crtc_state->stream == NULL)
1074fd45b654SHarry Wentland 		goto unlock;
1075fd45b654SHarry Wentland 
1076fd45b654SHarry Wentland 	switch (dm_crtc_state->stream->output_color_space) {
1077fd45b654SHarry Wentland 	case COLOR_SPACE_SRGB:
107888c4d4e9SSrinivasan Shanmugam 		seq_puts(m, "sRGB");
1079fd45b654SHarry Wentland 		break;
1080fd45b654SHarry Wentland 	case COLOR_SPACE_YCBCR601:
1081fd45b654SHarry Wentland 	case COLOR_SPACE_YCBCR601_LIMITED:
108288c4d4e9SSrinivasan Shanmugam 		seq_puts(m, "BT601_YCC");
1083fd45b654SHarry Wentland 		break;
1084fd45b654SHarry Wentland 	case COLOR_SPACE_YCBCR709:
1085fd45b654SHarry Wentland 	case COLOR_SPACE_YCBCR709_LIMITED:
108688c4d4e9SSrinivasan Shanmugam 		seq_puts(m, "BT709_YCC");
1087fd45b654SHarry Wentland 		break;
1088fd45b654SHarry Wentland 	case COLOR_SPACE_ADOBERGB:
108988c4d4e9SSrinivasan Shanmugam 		seq_puts(m, "opRGB");
1090fd45b654SHarry Wentland 		break;
1091fd45b654SHarry Wentland 	case COLOR_SPACE_2020_RGB_FULLRANGE:
109288c4d4e9SSrinivasan Shanmugam 		seq_puts(m, "BT2020_RGB");
1093fd45b654SHarry Wentland 		break;
1094fd45b654SHarry Wentland 	case COLOR_SPACE_2020_YCBCR:
109588c4d4e9SSrinivasan Shanmugam 		seq_puts(m, "BT2020_YCC");
1096fd45b654SHarry Wentland 		break;
1097fd45b654SHarry Wentland 	default:
1098fd45b654SHarry Wentland 		goto unlock;
1099fd45b654SHarry Wentland 	}
1100fd45b654SHarry Wentland 	res = 0;
1101fd45b654SHarry Wentland 
1102fd45b654SHarry Wentland unlock:
1103fd45b654SHarry Wentland 	drm_modeset_unlock(&crtc->mutex);
1104fd45b654SHarry Wentland 	mutex_unlock(&dev->mode_config.mutex);
1105fd45b654SHarry Wentland 
1106fd45b654SHarry Wentland 	return res;
1107fd45b654SHarry Wentland }
1108fd45b654SHarry Wentland DEFINE_SHOW_ATTRIBUTE(amdgpu_current_colorspace);
1109fd45b654SHarry Wentland 
1110fd45b654SHarry Wentland 
1111fd45b654SHarry Wentland /*
1112fcd1e484SFangzhi Zuo  * Example usage:
1113fcd1e484SFangzhi Zuo  * Disable dsc passthrough, i.e.,: have dsc decoding at converver, not external RX
1114fcd1e484SFangzhi Zuo  *   echo 1 /sys/kernel/debug/dri/0/DP-1/dsc_disable_passthrough
1115fcd1e484SFangzhi Zuo  * Enable dsc passthrough, i.e.,: have dsc passthrough to external RX
1116fcd1e484SFangzhi Zuo  *   echo 0 /sys/kernel/debug/dri/0/DP-1/dsc_disable_passthrough
1117fcd1e484SFangzhi Zuo  */
dp_dsc_passthrough_set(struct file * f,const char __user * buf,size_t size,loff_t * pos)1118fcd1e484SFangzhi Zuo static ssize_t dp_dsc_passthrough_set(struct file *f, const char __user *buf,
1119fcd1e484SFangzhi Zuo 				 size_t size, loff_t *pos)
1120fcd1e484SFangzhi Zuo {
1121fcd1e484SFangzhi Zuo 	struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private;
1122fcd1e484SFangzhi Zuo 	char *wr_buf = NULL;
1123fcd1e484SFangzhi Zuo 	uint32_t wr_buf_size = 42;
1124fcd1e484SFangzhi Zuo 	int max_param_num = 1;
1125fcd1e484SFangzhi Zuo 	long param;
1126fcd1e484SFangzhi Zuo 	uint8_t param_nums = 0;
1127fcd1e484SFangzhi Zuo 
1128fcd1e484SFangzhi Zuo 	if (size == 0)
1129fcd1e484SFangzhi Zuo 		return -EINVAL;
1130fcd1e484SFangzhi Zuo 
1131fcd1e484SFangzhi Zuo 	wr_buf = kcalloc(wr_buf_size, sizeof(char), GFP_KERNEL);
1132fcd1e484SFangzhi Zuo 
1133fcd1e484SFangzhi Zuo 	if (!wr_buf) {
1134fcd1e484SFangzhi Zuo 		DRM_DEBUG_DRIVER("no memory to allocate write buffer\n");
1135fcd1e484SFangzhi Zuo 		return -ENOSPC;
1136fcd1e484SFangzhi Zuo 	}
1137fcd1e484SFangzhi Zuo 
1138839e59a3SPatrik Jakobsson 	if (parse_write_buffer_into_params(wr_buf, wr_buf_size,
1139fcd1e484SFangzhi Zuo 					   &param, buf,
1140fcd1e484SFangzhi Zuo 					   max_param_num,
1141fcd1e484SFangzhi Zuo 					   &param_nums)) {
1142fcd1e484SFangzhi Zuo 		kfree(wr_buf);
1143fcd1e484SFangzhi Zuo 		return -EINVAL;
1144fcd1e484SFangzhi Zuo 	}
1145fcd1e484SFangzhi Zuo 
1146fcd1e484SFangzhi Zuo 	aconnector->dsc_settings.dsc_force_disable_passthrough = param;
1147fcd1e484SFangzhi Zuo 
1148fcd1e484SFangzhi Zuo 	kfree(wr_buf);
1149fcd1e484SFangzhi Zuo 	return 0;
1150fcd1e484SFangzhi Zuo }
1151fcd1e484SFangzhi Zuo 
11525f869379SBhawanpreet Lakha /*
11535f869379SBhawanpreet Lakha  * Returns the HDCP capability of the Display (1.4 for now).
11545f869379SBhawanpreet Lakha  *
11555f869379SBhawanpreet Lakha  * NOTE* Not all HDMI displays report their HDCP caps even when they are capable.
11565f869379SBhawanpreet Lakha  * Since its rare for a display to not be HDCP 1.4 capable, we set HDMI as always capable.
11575f869379SBhawanpreet Lakha  *
11585f869379SBhawanpreet Lakha  * Example usage: cat /sys/kernel/debug/dri/0/DP-1/hdcp_sink_capability
11595f869379SBhawanpreet Lakha  *		or cat /sys/kernel/debug/dri/0/HDMI-A-1/hdcp_sink_capability
11605f869379SBhawanpreet Lakha  */
hdcp_sink_capability_show(struct seq_file * m,void * data)11615f869379SBhawanpreet Lakha static int hdcp_sink_capability_show(struct seq_file *m, void *data)
11625f869379SBhawanpreet Lakha {
11635f869379SBhawanpreet Lakha 	struct drm_connector *connector = m->private;
11645f869379SBhawanpreet Lakha 	struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
11655f869379SBhawanpreet Lakha 	bool hdcp_cap, hdcp2_cap;
11665f869379SBhawanpreet Lakha 
11675f869379SBhawanpreet Lakha 	if (connector->status != connector_status_connected)
11685f869379SBhawanpreet Lakha 		return -ENODEV;
11695f869379SBhawanpreet Lakha 
11705f869379SBhawanpreet Lakha 	seq_printf(m, "%s:%d HDCP version: ", connector->name, connector->base.id);
11715f869379SBhawanpreet Lakha 
11720023b7eeSBhawanpreet Lakha 	hdcp_cap = dc_link_is_hdcp14(aconnector->dc_link, aconnector->dc_sink->sink_signal);
11730023b7eeSBhawanpreet Lakha 	hdcp2_cap = dc_link_is_hdcp22(aconnector->dc_link, aconnector->dc_sink->sink_signal);
11745f869379SBhawanpreet Lakha 
11755f869379SBhawanpreet Lakha 
11765f869379SBhawanpreet Lakha 	if (hdcp_cap)
11775f869379SBhawanpreet Lakha 		seq_printf(m, "%s ", "HDCP1.4");
11785f869379SBhawanpreet Lakha 	if (hdcp2_cap)
11795f869379SBhawanpreet Lakha 		seq_printf(m, "%s ", "HDCP2.2");
11805f869379SBhawanpreet Lakha 
11815f869379SBhawanpreet Lakha 	if (!hdcp_cap && !hdcp2_cap)
11825f869379SBhawanpreet Lakha 		seq_printf(m, "%s ", "None");
11835f869379SBhawanpreet Lakha 
11845f869379SBhawanpreet Lakha 	seq_puts(m, "\n");
11855f869379SBhawanpreet Lakha 
11865f869379SBhawanpreet Lakha 	return 0;
11875f869379SBhawanpreet Lakha }
1188b7cc1312SStylon Wang 
1189b7cc1312SStylon Wang /*
1190b7cc1312SStylon Wang  * Returns whether the connected display is internal and not hotpluggable.
1191b7cc1312SStylon Wang  * Example usage: cat /sys/kernel/debug/dri/0/DP-1/internal_display
1192b7cc1312SStylon Wang  */
internal_display_show(struct seq_file * m,void * data)1193b7cc1312SStylon Wang static int internal_display_show(struct seq_file *m, void *data)
1194b7cc1312SStylon Wang {
1195b7cc1312SStylon Wang 	struct drm_connector *connector = m->private;
1196b7cc1312SStylon Wang 	struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
1197b7cc1312SStylon Wang 	struct dc_link *link = aconnector->dc_link;
1198b7cc1312SStylon Wang 
1199b7cc1312SStylon Wang 	seq_printf(m, "Internal: %u\n", link->is_internal_display);
1200b7cc1312SStylon Wang 
1201b7cc1312SStylon Wang 	return 0;
1202b7cc1312SStylon Wang }
1203b7cc1312SStylon Wang 
1204c7ba3653SLeo (Hanghong) Ma /* function description
1205c7ba3653SLeo (Hanghong) Ma  *
1206c7ba3653SLeo (Hanghong) Ma  * generic SDP message access for testing
1207c7ba3653SLeo (Hanghong) Ma  *
1208c7ba3653SLeo (Hanghong) Ma  * debugfs sdp_message is located at /syskernel/debug/dri/0/DP-x
1209c7ba3653SLeo (Hanghong) Ma  *
1210c7ba3653SLeo (Hanghong) Ma  * SDP header
1211c7ba3653SLeo (Hanghong) Ma  * Hb0 : Secondary-Data Packet ID
1212c7ba3653SLeo (Hanghong) Ma  * Hb1 : Secondary-Data Packet type
1213c7ba3653SLeo (Hanghong) Ma  * Hb2 : Secondary-Data-packet-specific header, Byte 0
1214c7ba3653SLeo (Hanghong) Ma  * Hb3 : Secondary-Data-packet-specific header, Byte 1
1215c7ba3653SLeo (Hanghong) Ma  *
1216c7ba3653SLeo (Hanghong) Ma  * for using custom sdp message: input 4 bytes SDP header and 32 bytes raw data
1217c7ba3653SLeo (Hanghong) Ma  */
dp_sdp_message_debugfs_write(struct file * f,const char __user * buf,size_t size,loff_t * pos)1218c7ba3653SLeo (Hanghong) Ma static ssize_t dp_sdp_message_debugfs_write(struct file *f, const char __user *buf,
1219c7ba3653SLeo (Hanghong) Ma 				 size_t size, loff_t *pos)
1220c7ba3653SLeo (Hanghong) Ma {
1221c7ba3653SLeo (Hanghong) Ma 	int r;
1222c7ba3653SLeo (Hanghong) Ma 	uint8_t data[36];
1223c7ba3653SLeo (Hanghong) Ma 	struct amdgpu_dm_connector *connector = file_inode(f)->i_private;
1224c7ba3653SLeo (Hanghong) Ma 	struct dm_crtc_state *acrtc_state;
1225c7ba3653SLeo (Hanghong) Ma 	uint32_t write_size = 36;
1226c7ba3653SLeo (Hanghong) Ma 
1227c7ba3653SLeo (Hanghong) Ma 	if (connector->base.status != connector_status_connected)
1228c7ba3653SLeo (Hanghong) Ma 		return -ENODEV;
1229c7ba3653SLeo (Hanghong) Ma 
1230c7ba3653SLeo (Hanghong) Ma 	if (size == 0)
1231c7ba3653SLeo (Hanghong) Ma 		return 0;
1232c7ba3653SLeo (Hanghong) Ma 
1233c7ba3653SLeo (Hanghong) Ma 	acrtc_state = to_dm_crtc_state(connector->base.state->crtc->state);
1234c7ba3653SLeo (Hanghong) Ma 
1235c7ba3653SLeo (Hanghong) Ma 	r = copy_from_user(data, buf, write_size);
1236c7ba3653SLeo (Hanghong) Ma 
1237c7ba3653SLeo (Hanghong) Ma 	write_size -= r;
1238c7ba3653SLeo (Hanghong) Ma 
1239c7ba3653SLeo (Hanghong) Ma 	dc_stream_send_dp_sdp(acrtc_state->stream, data, write_size);
1240c7ba3653SLeo (Hanghong) Ma 
1241c7ba3653SLeo (Hanghong) Ma 	return write_size;
1242c7ba3653SLeo (Hanghong) Ma }
1243c7ba3653SLeo (Hanghong) Ma 
1244237070fdSEryk Brol /* function: Read link's DSC & FEC capabilities
1245237070fdSEryk Brol  *
1246237070fdSEryk Brol  *
1247237070fdSEryk Brol  * Access it with the following command (you need to specify
1248237070fdSEryk Brol  * connector like DP-1):
1249237070fdSEryk Brol  *
1250237070fdSEryk Brol  *	cat /sys/kernel/debug/dri/0/DP-X/dp_dsc_fec_support
1251237070fdSEryk Brol  *
1252237070fdSEryk Brol  */
dp_dsc_fec_support_show(struct seq_file * m,void * data)1253237070fdSEryk Brol static int dp_dsc_fec_support_show(struct seq_file *m, void *data)
1254237070fdSEryk Brol {
1255237070fdSEryk Brol 	struct drm_connector *connector = m->private;
1256237070fdSEryk Brol 	struct drm_modeset_acquire_ctx ctx;
1257237070fdSEryk Brol 	struct drm_device *dev = connector->dev;
1258237070fdSEryk Brol 	struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
1259237070fdSEryk Brol 	int ret = 0;
1260237070fdSEryk Brol 	bool try_again = false;
1261237070fdSEryk Brol 	bool is_fec_supported = false;
1262237070fdSEryk Brol 	bool is_dsc_supported = false;
1263237070fdSEryk Brol 	struct dpcd_caps dpcd_caps;
1264237070fdSEryk Brol 
1265237070fdSEryk Brol 	drm_modeset_acquire_init(&ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE);
1266237070fdSEryk Brol 	do {
1267237070fdSEryk Brol 		try_again = false;
1268237070fdSEryk Brol 		ret = drm_modeset_lock(&dev->mode_config.connection_mutex, &ctx);
1269237070fdSEryk Brol 		if (ret) {
1270237070fdSEryk Brol 			if (ret == -EDEADLK) {
1271237070fdSEryk Brol 				ret = drm_modeset_backoff(&ctx);
1272237070fdSEryk Brol 				if (!ret) {
1273237070fdSEryk Brol 					try_again = true;
1274237070fdSEryk Brol 					continue;
1275237070fdSEryk Brol 				}
1276237070fdSEryk Brol 			}
1277237070fdSEryk Brol 			break;
1278237070fdSEryk Brol 		}
1279237070fdSEryk Brol 		if (connector->status != connector_status_connected) {
1280237070fdSEryk Brol 			ret = -ENODEV;
1281237070fdSEryk Brol 			break;
1282237070fdSEryk Brol 		}
1283237070fdSEryk Brol 		dpcd_caps = aconnector->dc_link->dpcd_caps;
1284f0127cb1SWayne Lin 		if (aconnector->mst_output_port) {
1285237070fdSEryk Brol 			/* aconnector sets dsc_aux during get_modes call
1286237070fdSEryk Brol 			 * if MST connector has it means it can either
1287237070fdSEryk Brol 			 * enable DSC on the sink device or on MST branch
1288237070fdSEryk Brol 			 * its connected to.
1289237070fdSEryk Brol 			 */
1290237070fdSEryk Brol 			if (aconnector->dsc_aux) {
1291237070fdSEryk Brol 				is_fec_supported = true;
1292237070fdSEryk Brol 				is_dsc_supported = true;
1293237070fdSEryk Brol 			}
1294237070fdSEryk Brol 		} else {
1295237070fdSEryk Brol 			is_fec_supported = dpcd_caps.fec_cap.raw & 0x1;
1296237070fdSEryk Brol 			is_dsc_supported = dpcd_caps.dsc_caps.dsc_basic_caps.raw[0] & 0x1;
1297237070fdSEryk Brol 		}
1298237070fdSEryk Brol 	} while (try_again);
1299237070fdSEryk Brol 
1300237070fdSEryk Brol 	drm_modeset_drop_locks(&ctx);
1301237070fdSEryk Brol 	drm_modeset_acquire_fini(&ctx);
1302237070fdSEryk Brol 
1303972aa1a1SLucas De Marchi 	seq_printf(m, "FEC_Sink_Support: %s\n", str_yes_no(is_fec_supported));
1304972aa1a1SLucas De Marchi 	seq_printf(m, "DSC_Sink_Support: %s\n", str_yes_no(is_dsc_supported));
1305237070fdSEryk Brol 
1306237070fdSEryk Brol 	return ret;
1307237070fdSEryk Brol }
1308237070fdSEryk Brol 
13096f77b2acSEryk Brol /* function: Trigger virtual HPD redetection on connector
13106f77b2acSEryk Brol  *
13116f77b2acSEryk Brol  * This function will perform link rediscovery, link disable
13126f77b2acSEryk Brol  * and enable, and dm connector state update.
13136f77b2acSEryk Brol  *
13146f77b2acSEryk Brol  * Retrigger HPD on an existing connector by echoing 1 into
13156f77b2acSEryk Brol  * its respectful "trigger_hotplug" debugfs entry:
13166f77b2acSEryk Brol  *
13176f77b2acSEryk Brol  *	echo 1 > /sys/kernel/debug/dri/0/DP-X/trigger_hotplug
13186f77b2acSEryk Brol  *
13196b29bb37SEryk Brol  * This function can perform HPD unplug:
13206b29bb37SEryk Brol  *
13216b29bb37SEryk Brol  *	echo 0 > /sys/kernel/debug/dri/0/DP-X/trigger_hotplug
13226b29bb37SEryk Brol  *
13236f77b2acSEryk Brol  */
trigger_hotplug(struct file * f,const char __user * buf,size_t size,loff_t * pos)132402a342e3SStylon Wang static ssize_t trigger_hotplug(struct file *f, const char __user *buf,
13256f77b2acSEryk Brol 							size_t size, loff_t *pos)
13266f77b2acSEryk Brol {
13276f77b2acSEryk Brol 	struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private;
13286f77b2acSEryk Brol 	struct drm_connector *connector = &aconnector->base;
13296b29bb37SEryk Brol 	struct dc_link *link = NULL;
13306f77b2acSEryk Brol 	struct drm_device *dev = connector->dev;
133115c735e7SWayne Lin 	struct amdgpu_device *adev = drm_to_adev(dev);
13326f77b2acSEryk Brol 	enum dc_connection_type new_connection_type = dc_connection_none;
13336f77b2acSEryk Brol 	char *wr_buf = NULL;
13346f77b2acSEryk Brol 	uint32_t wr_buf_size = 42;
13356f77b2acSEryk Brol 	int max_param_num = 1;
13366f77b2acSEryk Brol 	long param[1] = {0};
13376f77b2acSEryk Brol 	uint8_t param_nums = 0;
133815c735e7SWayne Lin 	bool ret = false;
13396f77b2acSEryk Brol 
13406f77b2acSEryk Brol 	if (!aconnector || !aconnector->dc_link)
13416f77b2acSEryk Brol 		return -EINVAL;
13426f77b2acSEryk Brol 
13436f77b2acSEryk Brol 	if (size == 0)
13446f77b2acSEryk Brol 		return -EINVAL;
13456f77b2acSEryk Brol 
13466f77b2acSEryk Brol 	wr_buf = kcalloc(wr_buf_size, sizeof(char), GFP_KERNEL);
13476f77b2acSEryk Brol 
13486f77b2acSEryk Brol 	if (!wr_buf) {
13496f77b2acSEryk Brol 		DRM_DEBUG_DRIVER("no memory to allocate write buffer\n");
13506f77b2acSEryk Brol 		return -ENOSPC;
13516f77b2acSEryk Brol 	}
13526f77b2acSEryk Brol 
1353839e59a3SPatrik Jakobsson 	if (parse_write_buffer_into_params(wr_buf, wr_buf_size,
13546f77b2acSEryk Brol 						(long *)param, buf,
13556f77b2acSEryk Brol 						max_param_num,
1356d75fa679SYueHaibing 						&param_nums)) {
1357d75fa679SYueHaibing 		kfree(wr_buf);
13586f77b2acSEryk Brol 		return -EINVAL;
1359d75fa679SYueHaibing 	}
13606f77b2acSEryk Brol 
1361c8a58ce1SWayne Lin 	kfree(wr_buf);
1362c8a58ce1SWayne Lin 
13636f77b2acSEryk Brol 	if (param_nums <= 0) {
13646f77b2acSEryk Brol 		DRM_DEBUG_DRIVER("user data not be read\n");
1365c8a58ce1SWayne Lin 		return -EINVAL;
1366c8a58ce1SWayne Lin 	}
1367c8a58ce1SWayne Lin 
1368c8a58ce1SWayne Lin 	mutex_lock(&aconnector->hpd_lock);
1369c8a58ce1SWayne Lin 
1370c8a58ce1SWayne Lin 	/* Don't support for mst end device*/
1371f0127cb1SWayne Lin 	if (aconnector->mst_root) {
1372c8a58ce1SWayne Lin 		mutex_unlock(&aconnector->hpd_lock);
13736f77b2acSEryk Brol 		return -EINVAL;
13746f77b2acSEryk Brol 	}
13756f77b2acSEryk Brol 
13766f77b2acSEryk Brol 	if (param[0] == 1) {
13776f77b2acSEryk Brol 
137854618888SWenjing Liu 		if (!dc_link_detect_connection_type(aconnector->dc_link, &new_connection_type) &&
13796f77b2acSEryk Brol 			new_connection_type != dc_connection_none)
13806f77b2acSEryk Brol 			goto unlock;
13816f77b2acSEryk Brol 
138215c735e7SWayne Lin 		mutex_lock(&adev->dm.dc_lock);
138315c735e7SWayne Lin 		ret = dc_link_detect(aconnector->dc_link, DETECT_REASON_HPD);
138415c735e7SWayne Lin 		mutex_unlock(&adev->dm.dc_lock);
138515c735e7SWayne Lin 
138615c735e7SWayne Lin 		if (!ret)
13876f77b2acSEryk Brol 			goto unlock;
13886f77b2acSEryk Brol 
13896f77b2acSEryk Brol 		amdgpu_dm_update_connector_after_detect(aconnector);
13906f77b2acSEryk Brol 
13916f77b2acSEryk Brol 		drm_modeset_lock_all(dev);
13926f77b2acSEryk Brol 		dm_restore_drm_connector_state(dev, connector);
13936f77b2acSEryk Brol 		drm_modeset_unlock_all(dev);
13946f77b2acSEryk Brol 
1395fc320a6fSSimon Ser 		drm_kms_helper_connector_hotplug_event(connector);
13966b29bb37SEryk Brol 	} else if (param[0] == 0) {
13976b29bb37SEryk Brol 		if (!aconnector->dc_link)
13986b29bb37SEryk Brol 			goto unlock;
13996b29bb37SEryk Brol 
14006b29bb37SEryk Brol 		link = aconnector->dc_link;
14016b29bb37SEryk Brol 
14026b29bb37SEryk Brol 		if (link->local_sink) {
14036b29bb37SEryk Brol 			dc_sink_release(link->local_sink);
14046b29bb37SEryk Brol 			link->local_sink = NULL;
14056b29bb37SEryk Brol 		}
14066b29bb37SEryk Brol 
14076b29bb37SEryk Brol 		link->dpcd_sink_count = 0;
14086b29bb37SEryk Brol 		link->type = dc_connection_none;
14096b29bb37SEryk Brol 		link->dongle_max_pix_clk = 0;
14106b29bb37SEryk Brol 
14116b29bb37SEryk Brol 		amdgpu_dm_update_connector_after_detect(aconnector);
14126b29bb37SEryk Brol 
1413c8a58ce1SWayne Lin 		/* If the aconnector is the root node in mst topology */
1414c8a58ce1SWayne Lin 		if (aconnector->mst_mgr.mst_state == true)
141554618888SWenjing Liu 			dc_link_reset_cur_dp_mst_topology(link);
1416c8a58ce1SWayne Lin 
14176b29bb37SEryk Brol 		drm_modeset_lock_all(dev);
14186b29bb37SEryk Brol 		dm_restore_drm_connector_state(dev, connector);
14196b29bb37SEryk Brol 		drm_modeset_unlock_all(dev);
14206b29bb37SEryk Brol 
1421fc320a6fSSimon Ser 		drm_kms_helper_connector_hotplug_event(connector);
14226b29bb37SEryk Brol 	}
14236f77b2acSEryk Brol 
14246f77b2acSEryk Brol unlock:
14256f77b2acSEryk Brol 	mutex_unlock(&aconnector->hpd_lock);
14266f77b2acSEryk Brol 
14276f77b2acSEryk Brol 	return size;
14286f77b2acSEryk Brol }
14296f77b2acSEryk Brol 
1430097e6d98SEryk Brol /* function: read DSC status on the connector
1431097e6d98SEryk Brol  *
1432097e6d98SEryk Brol  * The read function: dp_dsc_clock_en_read
1433097e6d98SEryk Brol  * returns current status of DSC clock on the connector.
1434097e6d98SEryk Brol  * The return is a boolean flag: 1 or 0.
1435097e6d98SEryk Brol  *
1436097e6d98SEryk Brol  * Access it with the following command (you need to specify
1437097e6d98SEryk Brol  * connector like DP-1):
1438097e6d98SEryk Brol  *
1439097e6d98SEryk Brol  *	cat /sys/kernel/debug/dri/0/DP-X/dsc_clock_en
1440097e6d98SEryk Brol  *
1441097e6d98SEryk Brol  * Expected output:
1442097e6d98SEryk Brol  * 1 - means that DSC is currently enabled
1443097e6d98SEryk Brol  * 0 - means that DSC is disabled
1444097e6d98SEryk Brol  */
dp_dsc_clock_en_read(struct file * f,char __user * buf,size_t size,loff_t * pos)1445c06e09b7SEryk Brol static ssize_t dp_dsc_clock_en_read(struct file *f, char __user *buf,
1446c06e09b7SEryk Brol 				    size_t size, loff_t *pos)
1447c06e09b7SEryk Brol {
1448c06e09b7SEryk Brol 	char *rd_buf = NULL;
1449c06e09b7SEryk Brol 	char *rd_buf_ptr = NULL;
1450c06e09b7SEryk Brol 	struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private;
1451c06e09b7SEryk Brol 	struct display_stream_compressor *dsc;
1452c06e09b7SEryk Brol 	struct dcn_dsc_state dsc_state = {0};
1453c06e09b7SEryk Brol 	const uint32_t rd_buf_size = 10;
1454c06e09b7SEryk Brol 	struct pipe_ctx *pipe_ctx;
1455c06e09b7SEryk Brol 	ssize_t result = 0;
1456ad76fd30SSrinivasan Shanmugam 	int i, r, str_len = 10;
1457c06e09b7SEryk Brol 
1458c06e09b7SEryk Brol 	rd_buf = kcalloc(rd_buf_size, sizeof(char), GFP_KERNEL);
1459c06e09b7SEryk Brol 
1460c06e09b7SEryk Brol 	if (!rd_buf)
1461c06e09b7SEryk Brol 		return -ENOMEM;
1462c06e09b7SEryk Brol 
1463c06e09b7SEryk Brol 	rd_buf_ptr = rd_buf;
1464c06e09b7SEryk Brol 
1465c06e09b7SEryk Brol 	for (i = 0; i < MAX_PIPES; i++) {
1466c06e09b7SEryk Brol 		pipe_ctx = &aconnector->dc_link->dc->current_state->res_ctx.pipe_ctx[i];
1467f8e12e77SAlexey Kodanev 		if (pipe_ctx->stream &&
1468af8af93cSHersen Wu 		    pipe_ctx->stream->link == aconnector->dc_link &&
1469af8af93cSHersen Wu 		    pipe_ctx->stream->sink &&
1470af8af93cSHersen Wu 		    pipe_ctx->stream->sink == aconnector->dc_sink)
1471c06e09b7SEryk Brol 			break;
1472c06e09b7SEryk Brol 	}
1473c06e09b7SEryk Brol 
1474c06e09b7SEryk Brol 	dsc = pipe_ctx->stream_res.dsc;
1475c06e09b7SEryk Brol 	if (dsc)
1476c06e09b7SEryk Brol 		dsc->funcs->dsc_read_state(dsc, &dsc_state);
1477c06e09b7SEryk Brol 
1478c06e09b7SEryk Brol 	snprintf(rd_buf_ptr, str_len,
1479c06e09b7SEryk Brol 		"%d\n",
1480c06e09b7SEryk Brol 		dsc_state.dsc_clock_en);
1481c06e09b7SEryk Brol 	rd_buf_ptr += str_len;
1482c06e09b7SEryk Brol 
1483c06e09b7SEryk Brol 	while (size) {
1484c06e09b7SEryk Brol 		if (*pos >= rd_buf_size)
1485c06e09b7SEryk Brol 			break;
1486c06e09b7SEryk Brol 
1487c06e09b7SEryk Brol 		r = put_user(*(rd_buf + result), buf);
14885d5c6dbaSYongzhi Liu 		if (r) {
14895d5c6dbaSYongzhi Liu 			kfree(rd_buf);
1490c06e09b7SEryk Brol 			return r; /* r = -EFAULT */
14915d5c6dbaSYongzhi Liu 		}
1492c06e09b7SEryk Brol 
1493c06e09b7SEryk Brol 		buf += 1;
1494c06e09b7SEryk Brol 		size -= 1;
1495c06e09b7SEryk Brol 		*pos += 1;
1496c06e09b7SEryk Brol 		result += 1;
1497c06e09b7SEryk Brol 	}
1498c06e09b7SEryk Brol 
1499c06e09b7SEryk Brol 	kfree(rd_buf);
1500c06e09b7SEryk Brol 	return result;
1501c06e09b7SEryk Brol }
1502c06e09b7SEryk Brol 
1503097e6d98SEryk Brol /* function: write force DSC on the connector
1504097e6d98SEryk Brol  *
1505097e6d98SEryk Brol  * The write function: dp_dsc_clock_en_write
1506097e6d98SEryk Brol  * enables to force DSC on the connector.
15070749ddebSEryk Brol  * User can write to either force enable or force disable DSC
1508097e6d98SEryk Brol  * on the next modeset or set it to driver default
1509097e6d98SEryk Brol  *
15100749ddebSEryk Brol  * Accepted inputs:
15110749ddebSEryk Brol  * 0 - default DSC enablement policy
15120749ddebSEryk Brol  * 1 - force enable DSC on the connector
15130749ddebSEryk Brol  * 2 - force disable DSC on the connector (might cause fail in atomic_check)
15140749ddebSEryk Brol  *
1515097e6d98SEryk Brol  * Writing DSC settings is done with the following command:
1516097e6d98SEryk Brol  * - To force enable DSC (you need to specify
1517097e6d98SEryk Brol  * connector like DP-1):
1518097e6d98SEryk Brol  *
1519097e6d98SEryk Brol  *	echo 0x1 > /sys/kernel/debug/dri/0/DP-X/dsc_clock_en
1520097e6d98SEryk Brol  *
1521097e6d98SEryk Brol  * - To return to default state set the flag to zero and
1522097e6d98SEryk Brol  * let driver deal with DSC automatically
1523097e6d98SEryk Brol  * (you need to specify connector like DP-1):
1524097e6d98SEryk Brol  *
1525097e6d98SEryk Brol  *	echo 0x0 > /sys/kernel/debug/dri/0/DP-X/dsc_clock_en
1526097e6d98SEryk Brol  *
1527097e6d98SEryk Brol  */
dp_dsc_clock_en_write(struct file * f,const char __user * buf,size_t size,loff_t * pos)1528097e6d98SEryk Brol static ssize_t dp_dsc_clock_en_write(struct file *f, const char __user *buf,
1529097e6d98SEryk Brol 				     size_t size, loff_t *pos)
1530097e6d98SEryk Brol {
1531097e6d98SEryk Brol 	struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private;
1532886876ecSEryk Brol 	struct drm_connector *connector = &aconnector->base;
1533886876ecSEryk Brol 	struct drm_device *dev = connector->dev;
1534886876ecSEryk Brol 	struct drm_crtc *crtc = NULL;
1535886876ecSEryk Brol 	struct dm_crtc_state *dm_crtc_state = NULL;
1536097e6d98SEryk Brol 	struct pipe_ctx *pipe_ctx;
1537097e6d98SEryk Brol 	int i;
1538097e6d98SEryk Brol 	char *wr_buf = NULL;
1539097e6d98SEryk Brol 	uint32_t wr_buf_size = 42;
1540097e6d98SEryk Brol 	int max_param_num = 1;
1541097e6d98SEryk Brol 	long param[1] = {0};
1542097e6d98SEryk Brol 	uint8_t param_nums = 0;
1543097e6d98SEryk Brol 
1544097e6d98SEryk Brol 	if (size == 0)
1545097e6d98SEryk Brol 		return -EINVAL;
1546097e6d98SEryk Brol 
1547097e6d98SEryk Brol 	wr_buf = kcalloc(wr_buf_size, sizeof(char), GFP_KERNEL);
1548097e6d98SEryk Brol 
1549097e6d98SEryk Brol 	if (!wr_buf) {
1550097e6d98SEryk Brol 		DRM_DEBUG_DRIVER("no memory to allocate write buffer\n");
1551097e6d98SEryk Brol 		return -ENOSPC;
1552097e6d98SEryk Brol 	}
1553097e6d98SEryk Brol 
1554839e59a3SPatrik Jakobsson 	if (parse_write_buffer_into_params(wr_buf, wr_buf_size,
1555097e6d98SEryk Brol 					    (long *)param, buf,
1556097e6d98SEryk Brol 					    max_param_num,
1557097e6d98SEryk Brol 					    &param_nums)) {
1558097e6d98SEryk Brol 		kfree(wr_buf);
1559097e6d98SEryk Brol 		return -EINVAL;
1560097e6d98SEryk Brol 	}
1561097e6d98SEryk Brol 
1562097e6d98SEryk Brol 	if (param_nums <= 0) {
1563097e6d98SEryk Brol 		DRM_DEBUG_DRIVER("user data not be read\n");
1564097e6d98SEryk Brol 		kfree(wr_buf);
1565097e6d98SEryk Brol 		return -EINVAL;
1566097e6d98SEryk Brol 	}
1567097e6d98SEryk Brol 
1568097e6d98SEryk Brol 	for (i = 0; i < MAX_PIPES; i++) {
1569097e6d98SEryk Brol 		pipe_ctx = &aconnector->dc_link->dc->current_state->res_ctx.pipe_ctx[i];
1570f8e12e77SAlexey Kodanev 		if (pipe_ctx->stream &&
1571af8af93cSHersen Wu 		    pipe_ctx->stream->link == aconnector->dc_link &&
1572af8af93cSHersen Wu 		    pipe_ctx->stream->sink &&
1573af8af93cSHersen Wu 		    pipe_ctx->stream->sink == aconnector->dc_sink)
1574097e6d98SEryk Brol 			break;
1575097e6d98SEryk Brol 	}
1576097e6d98SEryk Brol 
1577f8e12e77SAlexey Kodanev 	if (!pipe_ctx->stream)
1578097e6d98SEryk Brol 		goto done;
1579097e6d98SEryk Brol 
1580886876ecSEryk Brol 	// Get CRTC state
1581886876ecSEryk Brol 	mutex_lock(&dev->mode_config.mutex);
1582886876ecSEryk Brol 	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
1583886876ecSEryk Brol 
1584886876ecSEryk Brol 	if (connector->state == NULL)
1585886876ecSEryk Brol 		goto unlock;
1586886876ecSEryk Brol 
1587886876ecSEryk Brol 	crtc = connector->state->crtc;
1588886876ecSEryk Brol 	if (crtc == NULL)
1589886876ecSEryk Brol 		goto unlock;
1590886876ecSEryk Brol 
1591886876ecSEryk Brol 	drm_modeset_lock(&crtc->mutex, NULL);
1592886876ecSEryk Brol 	if (crtc->state == NULL)
1593886876ecSEryk Brol 		goto unlock;
1594886876ecSEryk Brol 
1595886876ecSEryk Brol 	dm_crtc_state = to_dm_crtc_state(crtc->state);
1596886876ecSEryk Brol 	if (dm_crtc_state->stream == NULL)
1597886876ecSEryk Brol 		goto unlock;
1598886876ecSEryk Brol 
15990749ddebSEryk Brol 	if (param[0] == 1)
16000749ddebSEryk Brol 		aconnector->dsc_settings.dsc_force_enable = DSC_CLK_FORCE_ENABLE;
16010749ddebSEryk Brol 	else if (param[0] == 2)
16020749ddebSEryk Brol 		aconnector->dsc_settings.dsc_force_enable = DSC_CLK_FORCE_DISABLE;
16030749ddebSEryk Brol 	else
16040749ddebSEryk Brol 		aconnector->dsc_settings.dsc_force_enable = DSC_CLK_FORCE_DEFAULT;
1605097e6d98SEryk Brol 
1606886876ecSEryk Brol 	dm_crtc_state->dsc_force_changed = true;
1607886876ecSEryk Brol 
1608886876ecSEryk Brol unlock:
1609886876ecSEryk Brol 	if (crtc)
1610886876ecSEryk Brol 		drm_modeset_unlock(&crtc->mutex);
1611886876ecSEryk Brol 	drm_modeset_unlock(&dev->mode_config.connection_mutex);
1612886876ecSEryk Brol 	mutex_unlock(&dev->mode_config.mutex);
1613886876ecSEryk Brol 
1614097e6d98SEryk Brol done:
1615097e6d98SEryk Brol 	kfree(wr_buf);
1616097e6d98SEryk Brol 	return size;
1617097e6d98SEryk Brol }
1618097e6d98SEryk Brol 
161927e84dd7SEryk Brol /* function: read DSC slice width parameter on the connector
162027e84dd7SEryk Brol  *
162127e84dd7SEryk Brol  * The read function: dp_dsc_slice_width_read
162227e84dd7SEryk Brol  * returns dsc slice width used in the current configuration
162327e84dd7SEryk Brol  * The return is an integer: 0 or other positive number
162427e84dd7SEryk Brol  *
162527e84dd7SEryk Brol  * Access the status with the following command:
162627e84dd7SEryk Brol  *
162727e84dd7SEryk Brol  *	cat /sys/kernel/debug/dri/0/DP-X/dsc_slice_width
162827e84dd7SEryk Brol  *
162927e84dd7SEryk Brol  * 0 - means that DSC is disabled
163027e84dd7SEryk Brol  *
163127e84dd7SEryk Brol  * Any other number more than zero represents the
163227e84dd7SEryk Brol  * slice width currently used by DSC in pixels
163327e84dd7SEryk Brol  *
163427e84dd7SEryk Brol  */
dp_dsc_slice_width_read(struct file * f,char __user * buf,size_t size,loff_t * pos)1635c06e09b7SEryk Brol static ssize_t dp_dsc_slice_width_read(struct file *f, char __user *buf,
1636c06e09b7SEryk Brol 				    size_t size, loff_t *pos)
1637c06e09b7SEryk Brol {
1638c06e09b7SEryk Brol 	char *rd_buf = NULL;
1639c06e09b7SEryk Brol 	char *rd_buf_ptr = NULL;
1640c06e09b7SEryk Brol 	struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private;
1641c06e09b7SEryk Brol 	struct display_stream_compressor *dsc;
1642c06e09b7SEryk Brol 	struct dcn_dsc_state dsc_state = {0};
1643c06e09b7SEryk Brol 	const uint32_t rd_buf_size = 100;
1644c06e09b7SEryk Brol 	struct pipe_ctx *pipe_ctx;
1645c06e09b7SEryk Brol 	ssize_t result = 0;
1646c06e09b7SEryk Brol 	int i, r, str_len = 30;
1647c06e09b7SEryk Brol 
1648c06e09b7SEryk Brol 	rd_buf = kcalloc(rd_buf_size, sizeof(char), GFP_KERNEL);
1649c06e09b7SEryk Brol 
1650c06e09b7SEryk Brol 	if (!rd_buf)
1651c06e09b7SEryk Brol 		return -ENOMEM;
1652c06e09b7SEryk Brol 
1653c06e09b7SEryk Brol 	rd_buf_ptr = rd_buf;
1654c06e09b7SEryk Brol 
1655c06e09b7SEryk Brol 	for (i = 0; i < MAX_PIPES; i++) {
1656c06e09b7SEryk Brol 		pipe_ctx = &aconnector->dc_link->dc->current_state->res_ctx.pipe_ctx[i];
1657f8e12e77SAlexey Kodanev 		if (pipe_ctx->stream &&
1658af8af93cSHersen Wu 		    pipe_ctx->stream->link == aconnector->dc_link &&
1659af8af93cSHersen Wu 		    pipe_ctx->stream->sink &&
1660af8af93cSHersen Wu 		    pipe_ctx->stream->sink == aconnector->dc_sink)
1661c06e09b7SEryk Brol 			break;
1662c06e09b7SEryk Brol 	}
1663c06e09b7SEryk Brol 
1664c06e09b7SEryk Brol 	dsc = pipe_ctx->stream_res.dsc;
1665c06e09b7SEryk Brol 	if (dsc)
1666c06e09b7SEryk Brol 		dsc->funcs->dsc_read_state(dsc, &dsc_state);
1667c06e09b7SEryk Brol 
1668c06e09b7SEryk Brol 	snprintf(rd_buf_ptr, str_len,
1669c06e09b7SEryk Brol 		"%d\n",
1670c06e09b7SEryk Brol 		dsc_state.dsc_slice_width);
1671c06e09b7SEryk Brol 	rd_buf_ptr += str_len;
1672c06e09b7SEryk Brol 
1673c06e09b7SEryk Brol 	while (size) {
1674c06e09b7SEryk Brol 		if (*pos >= rd_buf_size)
1675c06e09b7SEryk Brol 			break;
1676c06e09b7SEryk Brol 
1677c06e09b7SEryk Brol 		r = put_user(*(rd_buf + result), buf);
16785d5c6dbaSYongzhi Liu 		if (r) {
16795d5c6dbaSYongzhi Liu 			kfree(rd_buf);
1680c06e09b7SEryk Brol 			return r; /* r = -EFAULT */
16815d5c6dbaSYongzhi Liu 		}
1682c06e09b7SEryk Brol 
1683c06e09b7SEryk Brol 		buf += 1;
1684c06e09b7SEryk Brol 		size -= 1;
1685c06e09b7SEryk Brol 		*pos += 1;
1686c06e09b7SEryk Brol 		result += 1;
1687c06e09b7SEryk Brol 	}
1688c06e09b7SEryk Brol 
1689c06e09b7SEryk Brol 	kfree(rd_buf);
1690c06e09b7SEryk Brol 	return result;
1691c06e09b7SEryk Brol }
1692c06e09b7SEryk Brol 
169327e84dd7SEryk Brol /* function: write DSC slice width parameter
169427e84dd7SEryk Brol  *
169527e84dd7SEryk Brol  * The write function: dp_dsc_slice_width_write
169627e84dd7SEryk Brol  * overwrites automatically generated DSC configuration
169727e84dd7SEryk Brol  * of slice width.
169827e84dd7SEryk Brol  *
169927e84dd7SEryk Brol  * The user has to write the slice width divisible by the
170027e84dd7SEryk Brol  * picture width.
170127e84dd7SEryk Brol  *
170227e84dd7SEryk Brol  * Also the user has to write width in hexidecimal
170327e84dd7SEryk Brol  * rather than in decimal.
170427e84dd7SEryk Brol  *
170527e84dd7SEryk Brol  * Writing DSC settings is done with the following command:
170627e84dd7SEryk Brol  * - To force overwrite slice width: (example sets to 1920 pixels)
170727e84dd7SEryk Brol  *
170827e84dd7SEryk Brol  *	echo 0x780 > /sys/kernel/debug/dri/0/DP-X/dsc_slice_width
170927e84dd7SEryk Brol  *
171027e84dd7SEryk Brol  *  - To stop overwriting and let driver find the optimal size,
171127e84dd7SEryk Brol  * set the width to zero:
171227e84dd7SEryk Brol  *
171327e84dd7SEryk Brol  *	echo 0x0 > /sys/kernel/debug/dri/0/DP-X/dsc_slice_width
171427e84dd7SEryk Brol  *
171527e84dd7SEryk Brol  */
dp_dsc_slice_width_write(struct file * f,const char __user * buf,size_t size,loff_t * pos)171627e84dd7SEryk Brol static ssize_t dp_dsc_slice_width_write(struct file *f, const char __user *buf,
171727e84dd7SEryk Brol 				     size_t size, loff_t *pos)
171827e84dd7SEryk Brol {
171927e84dd7SEryk Brol 	struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private;
172027e84dd7SEryk Brol 	struct pipe_ctx *pipe_ctx;
1721886876ecSEryk Brol 	struct drm_connector *connector = &aconnector->base;
1722886876ecSEryk Brol 	struct drm_device *dev = connector->dev;
1723886876ecSEryk Brol 	struct drm_crtc *crtc = NULL;
1724886876ecSEryk Brol 	struct dm_crtc_state *dm_crtc_state = NULL;
172527e84dd7SEryk Brol 	int i;
172627e84dd7SEryk Brol 	char *wr_buf = NULL;
172727e84dd7SEryk Brol 	uint32_t wr_buf_size = 42;
172827e84dd7SEryk Brol 	int max_param_num = 1;
172927e84dd7SEryk Brol 	long param[1] = {0};
173027e84dd7SEryk Brol 	uint8_t param_nums = 0;
173127e84dd7SEryk Brol 
173227e84dd7SEryk Brol 	if (size == 0)
173327e84dd7SEryk Brol 		return -EINVAL;
173427e84dd7SEryk Brol 
173527e84dd7SEryk Brol 	wr_buf = kcalloc(wr_buf_size, sizeof(char), GFP_KERNEL);
173627e84dd7SEryk Brol 
173727e84dd7SEryk Brol 	if (!wr_buf) {
173827e84dd7SEryk Brol 		DRM_DEBUG_DRIVER("no memory to allocate write buffer\n");
173927e84dd7SEryk Brol 		return -ENOSPC;
174027e84dd7SEryk Brol 	}
174127e84dd7SEryk Brol 
1742839e59a3SPatrik Jakobsson 	if (parse_write_buffer_into_params(wr_buf, wr_buf_size,
174327e84dd7SEryk Brol 					    (long *)param, buf,
174427e84dd7SEryk Brol 					    max_param_num,
174527e84dd7SEryk Brol 					    &param_nums)) {
174627e84dd7SEryk Brol 		kfree(wr_buf);
174727e84dd7SEryk Brol 		return -EINVAL;
174827e84dd7SEryk Brol 	}
174927e84dd7SEryk Brol 
175027e84dd7SEryk Brol 	if (param_nums <= 0) {
175127e84dd7SEryk Brol 		DRM_DEBUG_DRIVER("user data not be read\n");
175227e84dd7SEryk Brol 		kfree(wr_buf);
175327e84dd7SEryk Brol 		return -EINVAL;
175427e84dd7SEryk Brol 	}
175527e84dd7SEryk Brol 
175627e84dd7SEryk Brol 	for (i = 0; i < MAX_PIPES; i++) {
175727e84dd7SEryk Brol 		pipe_ctx = &aconnector->dc_link->dc->current_state->res_ctx.pipe_ctx[i];
1758f8e12e77SAlexey Kodanev 		if (pipe_ctx->stream &&
1759af8af93cSHersen Wu 		    pipe_ctx->stream->link == aconnector->dc_link &&
1760af8af93cSHersen Wu 		    pipe_ctx->stream->sink &&
1761af8af93cSHersen Wu 		    pipe_ctx->stream->sink == aconnector->dc_sink)
176227e84dd7SEryk Brol 			break;
176327e84dd7SEryk Brol 	}
176427e84dd7SEryk Brol 
1765f8e12e77SAlexey Kodanev 	if (!pipe_ctx->stream)
176627e84dd7SEryk Brol 		goto done;
176727e84dd7SEryk Brol 
1768886876ecSEryk Brol 	// Safely get CRTC state
1769886876ecSEryk Brol 	mutex_lock(&dev->mode_config.mutex);
1770886876ecSEryk Brol 	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
1771886876ecSEryk Brol 
1772886876ecSEryk Brol 	if (connector->state == NULL)
1773886876ecSEryk Brol 		goto unlock;
1774886876ecSEryk Brol 
1775886876ecSEryk Brol 	crtc = connector->state->crtc;
1776886876ecSEryk Brol 	if (crtc == NULL)
1777886876ecSEryk Brol 		goto unlock;
1778886876ecSEryk Brol 
1779886876ecSEryk Brol 	drm_modeset_lock(&crtc->mutex, NULL);
1780886876ecSEryk Brol 	if (crtc->state == NULL)
1781886876ecSEryk Brol 		goto unlock;
1782886876ecSEryk Brol 
1783886876ecSEryk Brol 	dm_crtc_state = to_dm_crtc_state(crtc->state);
1784886876ecSEryk Brol 	if (dm_crtc_state->stream == NULL)
1785886876ecSEryk Brol 		goto unlock;
1786886876ecSEryk Brol 
178728b2f656SEryk Brol 	if (param[0] > 0)
178828b2f656SEryk Brol 		aconnector->dsc_settings.dsc_num_slices_h = DIV_ROUND_UP(
178928b2f656SEryk Brol 					pipe_ctx->stream->timing.h_addressable,
179028b2f656SEryk Brol 					param[0]);
179128b2f656SEryk Brol 	else
179228b2f656SEryk Brol 		aconnector->dsc_settings.dsc_num_slices_h = 0;
179327e84dd7SEryk Brol 
1794886876ecSEryk Brol 	dm_crtc_state->dsc_force_changed = true;
1795886876ecSEryk Brol 
1796886876ecSEryk Brol unlock:
1797886876ecSEryk Brol 	if (crtc)
1798886876ecSEryk Brol 		drm_modeset_unlock(&crtc->mutex);
1799886876ecSEryk Brol 	drm_modeset_unlock(&dev->mode_config.connection_mutex);
1800886876ecSEryk Brol 	mutex_unlock(&dev->mode_config.mutex);
1801886876ecSEryk Brol 
180227e84dd7SEryk Brol done:
180327e84dd7SEryk Brol 	kfree(wr_buf);
180427e84dd7SEryk Brol 	return size;
180527e84dd7SEryk Brol }
180627e84dd7SEryk Brol 
1807734e4c97SEryk Brol /* function: read DSC slice height parameter on the connector
1808734e4c97SEryk Brol  *
1809734e4c97SEryk Brol  * The read function: dp_dsc_slice_height_read
1810734e4c97SEryk Brol  * returns dsc slice height used in the current configuration
1811734e4c97SEryk Brol  * The return is an integer: 0 or other positive number
1812734e4c97SEryk Brol  *
1813734e4c97SEryk Brol  * Access the status with the following command:
1814734e4c97SEryk Brol  *
1815734e4c97SEryk Brol  *	cat /sys/kernel/debug/dri/0/DP-X/dsc_slice_height
1816734e4c97SEryk Brol  *
1817734e4c97SEryk Brol  * 0 - means that DSC is disabled
1818734e4c97SEryk Brol  *
1819734e4c97SEryk Brol  * Any other number more than zero represents the
1820734e4c97SEryk Brol  * slice height currently used by DSC in pixels
1821734e4c97SEryk Brol  *
1822734e4c97SEryk Brol  */
dp_dsc_slice_height_read(struct file * f,char __user * buf,size_t size,loff_t * pos)1823c06e09b7SEryk Brol static ssize_t dp_dsc_slice_height_read(struct file *f, char __user *buf,
1824c06e09b7SEryk Brol 				    size_t size, loff_t *pos)
1825c06e09b7SEryk Brol {
1826c06e09b7SEryk Brol 	char *rd_buf = NULL;
1827c06e09b7SEryk Brol 	char *rd_buf_ptr = NULL;
1828c06e09b7SEryk Brol 	struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private;
1829c06e09b7SEryk Brol 	struct display_stream_compressor *dsc;
1830c06e09b7SEryk Brol 	struct dcn_dsc_state dsc_state = {0};
1831c06e09b7SEryk Brol 	const uint32_t rd_buf_size = 100;
1832c06e09b7SEryk Brol 	struct pipe_ctx *pipe_ctx;
1833c06e09b7SEryk Brol 	ssize_t result = 0;
1834c06e09b7SEryk Brol 	int i, r, str_len = 30;
1835c06e09b7SEryk Brol 
1836c06e09b7SEryk Brol 	rd_buf = kcalloc(rd_buf_size, sizeof(char), GFP_KERNEL);
1837c06e09b7SEryk Brol 
1838c06e09b7SEryk Brol 	if (!rd_buf)
1839c06e09b7SEryk Brol 		return -ENOMEM;
1840c06e09b7SEryk Brol 
1841c06e09b7SEryk Brol 	rd_buf_ptr = rd_buf;
1842c06e09b7SEryk Brol 
1843c06e09b7SEryk Brol 	for (i = 0; i < MAX_PIPES; i++) {
1844c06e09b7SEryk Brol 		pipe_ctx = &aconnector->dc_link->dc->current_state->res_ctx.pipe_ctx[i];
1845f8e12e77SAlexey Kodanev 		if (pipe_ctx->stream &&
1846af8af93cSHersen Wu 		    pipe_ctx->stream->link == aconnector->dc_link &&
1847af8af93cSHersen Wu 		    pipe_ctx->stream->sink &&
1848af8af93cSHersen Wu 		    pipe_ctx->stream->sink == aconnector->dc_sink)
1849c06e09b7SEryk Brol 			break;
1850c06e09b7SEryk Brol 	}
1851c06e09b7SEryk Brol 
1852c06e09b7SEryk Brol 	dsc = pipe_ctx->stream_res.dsc;
1853c06e09b7SEryk Brol 	if (dsc)
1854c06e09b7SEryk Brol 		dsc->funcs->dsc_read_state(dsc, &dsc_state);
1855c06e09b7SEryk Brol 
1856c06e09b7SEryk Brol 	snprintf(rd_buf_ptr, str_len,
1857c06e09b7SEryk Brol 		"%d\n",
1858c06e09b7SEryk Brol 		dsc_state.dsc_slice_height);
1859c06e09b7SEryk Brol 	rd_buf_ptr += str_len;
1860c06e09b7SEryk Brol 
1861c06e09b7SEryk Brol 	while (size) {
1862c06e09b7SEryk Brol 		if (*pos >= rd_buf_size)
1863c06e09b7SEryk Brol 			break;
1864c06e09b7SEryk Brol 
1865c06e09b7SEryk Brol 		r = put_user(*(rd_buf + result), buf);
18665d5c6dbaSYongzhi Liu 		if (r) {
18675d5c6dbaSYongzhi Liu 			kfree(rd_buf);
1868c06e09b7SEryk Brol 			return r; /* r = -EFAULT */
18695d5c6dbaSYongzhi Liu 		}
1870c06e09b7SEryk Brol 
1871c06e09b7SEryk Brol 		buf += 1;
1872c06e09b7SEryk Brol 		size -= 1;
1873c06e09b7SEryk Brol 		*pos += 1;
1874c06e09b7SEryk Brol 		result += 1;
1875c06e09b7SEryk Brol 	}
1876c06e09b7SEryk Brol 
1877c06e09b7SEryk Brol 	kfree(rd_buf);
1878c06e09b7SEryk Brol 	return result;
1879c06e09b7SEryk Brol }
1880c06e09b7SEryk Brol 
1881734e4c97SEryk Brol /* function: write DSC slice height parameter
1882734e4c97SEryk Brol  *
1883734e4c97SEryk Brol  * The write function: dp_dsc_slice_height_write
1884734e4c97SEryk Brol  * overwrites automatically generated DSC configuration
1885734e4c97SEryk Brol  * of slice height.
1886734e4c97SEryk Brol  *
1887734e4c97SEryk Brol  * The user has to write the slice height divisible by the
1888734e4c97SEryk Brol  * picture height.
1889734e4c97SEryk Brol  *
1890734e4c97SEryk Brol  * Also the user has to write height in hexidecimal
1891734e4c97SEryk Brol  * rather than in decimal.
1892734e4c97SEryk Brol  *
1893734e4c97SEryk Brol  * Writing DSC settings is done with the following command:
1894734e4c97SEryk Brol  * - To force overwrite slice height (example sets to 128 pixels):
1895734e4c97SEryk Brol  *
1896734e4c97SEryk Brol  *	echo 0x80 > /sys/kernel/debug/dri/0/DP-X/dsc_slice_height
1897734e4c97SEryk Brol  *
1898734e4c97SEryk Brol  *  - To stop overwriting and let driver find the optimal size,
1899734e4c97SEryk Brol  * set the height to zero:
1900734e4c97SEryk Brol  *
1901734e4c97SEryk Brol  *	echo 0x0 > /sys/kernel/debug/dri/0/DP-X/dsc_slice_height
1902734e4c97SEryk Brol  *
1903734e4c97SEryk Brol  */
dp_dsc_slice_height_write(struct file * f,const char __user * buf,size_t size,loff_t * pos)1904734e4c97SEryk Brol static ssize_t dp_dsc_slice_height_write(struct file *f, const char __user *buf,
1905734e4c97SEryk Brol 				     size_t size, loff_t *pos)
1906734e4c97SEryk Brol {
1907734e4c97SEryk Brol 	struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private;
1908886876ecSEryk Brol 	struct drm_connector *connector = &aconnector->base;
1909886876ecSEryk Brol 	struct drm_device *dev = connector->dev;
1910886876ecSEryk Brol 	struct drm_crtc *crtc = NULL;
1911886876ecSEryk Brol 	struct dm_crtc_state *dm_crtc_state = NULL;
1912734e4c97SEryk Brol 	struct pipe_ctx *pipe_ctx;
1913734e4c97SEryk Brol 	int i;
1914734e4c97SEryk Brol 	char *wr_buf = NULL;
1915734e4c97SEryk Brol 	uint32_t wr_buf_size = 42;
1916734e4c97SEryk Brol 	int max_param_num = 1;
1917734e4c97SEryk Brol 	uint8_t param_nums = 0;
1918734e4c97SEryk Brol 	long param[1] = {0};
1919734e4c97SEryk Brol 
1920734e4c97SEryk Brol 	if (size == 0)
1921734e4c97SEryk Brol 		return -EINVAL;
1922734e4c97SEryk Brol 
1923734e4c97SEryk Brol 	wr_buf = kcalloc(wr_buf_size, sizeof(char), GFP_KERNEL);
1924734e4c97SEryk Brol 
1925734e4c97SEryk Brol 	if (!wr_buf) {
1926734e4c97SEryk Brol 		DRM_DEBUG_DRIVER("no memory to allocate write buffer\n");
1927734e4c97SEryk Brol 		return -ENOSPC;
1928734e4c97SEryk Brol 	}
1929734e4c97SEryk Brol 
1930839e59a3SPatrik Jakobsson 	if (parse_write_buffer_into_params(wr_buf, wr_buf_size,
1931734e4c97SEryk Brol 					    (long *)param, buf,
1932734e4c97SEryk Brol 					    max_param_num,
1933734e4c97SEryk Brol 					    &param_nums)) {
1934734e4c97SEryk Brol 		kfree(wr_buf);
1935734e4c97SEryk Brol 		return -EINVAL;
1936734e4c97SEryk Brol 	}
1937734e4c97SEryk Brol 
1938734e4c97SEryk Brol 	if (param_nums <= 0) {
1939734e4c97SEryk Brol 		DRM_DEBUG_DRIVER("user data not be read\n");
1940734e4c97SEryk Brol 		kfree(wr_buf);
1941734e4c97SEryk Brol 		return -EINVAL;
1942734e4c97SEryk Brol 	}
1943734e4c97SEryk Brol 
1944734e4c97SEryk Brol 	for (i = 0; i < MAX_PIPES; i++) {
1945734e4c97SEryk Brol 		pipe_ctx = &aconnector->dc_link->dc->current_state->res_ctx.pipe_ctx[i];
1946f8e12e77SAlexey Kodanev 		if (pipe_ctx->stream &&
1947af8af93cSHersen Wu 		    pipe_ctx->stream->link == aconnector->dc_link &&
1948af8af93cSHersen Wu 		    pipe_ctx->stream->sink &&
1949af8af93cSHersen Wu 		    pipe_ctx->stream->sink == aconnector->dc_sink)
1950734e4c97SEryk Brol 			break;
1951734e4c97SEryk Brol 	}
1952734e4c97SEryk Brol 
1953f8e12e77SAlexey Kodanev 	if (!pipe_ctx->stream)
1954734e4c97SEryk Brol 		goto done;
1955734e4c97SEryk Brol 
1956886876ecSEryk Brol 	// Get CRTC state
1957886876ecSEryk Brol 	mutex_lock(&dev->mode_config.mutex);
1958886876ecSEryk Brol 	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
1959886876ecSEryk Brol 
1960886876ecSEryk Brol 	if (connector->state == NULL)
1961886876ecSEryk Brol 		goto unlock;
1962886876ecSEryk Brol 
1963886876ecSEryk Brol 	crtc = connector->state->crtc;
1964886876ecSEryk Brol 	if (crtc == NULL)
1965886876ecSEryk Brol 		goto unlock;
1966886876ecSEryk Brol 
1967886876ecSEryk Brol 	drm_modeset_lock(&crtc->mutex, NULL);
1968886876ecSEryk Brol 	if (crtc->state == NULL)
1969886876ecSEryk Brol 		goto unlock;
1970886876ecSEryk Brol 
1971886876ecSEryk Brol 	dm_crtc_state = to_dm_crtc_state(crtc->state);
1972886876ecSEryk Brol 	if (dm_crtc_state->stream == NULL)
1973886876ecSEryk Brol 		goto unlock;
1974886876ecSEryk Brol 
197528b2f656SEryk Brol 	if (param[0] > 0)
197628b2f656SEryk Brol 		aconnector->dsc_settings.dsc_num_slices_v = DIV_ROUND_UP(
197728b2f656SEryk Brol 					pipe_ctx->stream->timing.v_addressable,
197828b2f656SEryk Brol 					param[0]);
197928b2f656SEryk Brol 	else
198028b2f656SEryk Brol 		aconnector->dsc_settings.dsc_num_slices_v = 0;
1981734e4c97SEryk Brol 
1982886876ecSEryk Brol 	dm_crtc_state->dsc_force_changed = true;
1983886876ecSEryk Brol 
1984886876ecSEryk Brol unlock:
1985886876ecSEryk Brol 	if (crtc)
1986886876ecSEryk Brol 		drm_modeset_unlock(&crtc->mutex);
1987886876ecSEryk Brol 	drm_modeset_unlock(&dev->mode_config.connection_mutex);
1988886876ecSEryk Brol 	mutex_unlock(&dev->mode_config.mutex);
1989886876ecSEryk Brol 
1990734e4c97SEryk Brol done:
1991734e4c97SEryk Brol 	kfree(wr_buf);
1992734e4c97SEryk Brol 	return size;
1993734e4c97SEryk Brol }
1994734e4c97SEryk Brol 
19955268bf13SEryk Brol /* function: read DSC target rate on the connector in bits per pixel
19965268bf13SEryk Brol  *
19975268bf13SEryk Brol  * The read function: dp_dsc_bits_per_pixel_read
19985268bf13SEryk Brol  * returns target rate of compression in bits per pixel
19995268bf13SEryk Brol  * The return is an integer: 0 or other positive integer
20005268bf13SEryk Brol  *
20015268bf13SEryk Brol  * Access it with the following command:
20025268bf13SEryk Brol  *
20035268bf13SEryk Brol  *	cat /sys/kernel/debug/dri/0/DP-X/dsc_bits_per_pixel
20045268bf13SEryk Brol  *
20055268bf13SEryk Brol  *  0 - means that DSC is disabled
20065268bf13SEryk Brol  */
dp_dsc_bits_per_pixel_read(struct file * f,char __user * buf,size_t size,loff_t * pos)2007f92e25e5SEryk Brol static ssize_t dp_dsc_bits_per_pixel_read(struct file *f, char __user *buf,
2008c06e09b7SEryk Brol 				    size_t size, loff_t *pos)
2009c06e09b7SEryk Brol {
2010c06e09b7SEryk Brol 	char *rd_buf = NULL;
2011c06e09b7SEryk Brol 	char *rd_buf_ptr = NULL;
2012c06e09b7SEryk Brol 	struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private;
2013c06e09b7SEryk Brol 	struct display_stream_compressor *dsc;
2014c06e09b7SEryk Brol 	struct dcn_dsc_state dsc_state = {0};
2015c06e09b7SEryk Brol 	const uint32_t rd_buf_size = 100;
2016c06e09b7SEryk Brol 	struct pipe_ctx *pipe_ctx;
2017c06e09b7SEryk Brol 	ssize_t result = 0;
2018c06e09b7SEryk Brol 	int i, r, str_len = 30;
2019c06e09b7SEryk Brol 
2020c06e09b7SEryk Brol 	rd_buf = kcalloc(rd_buf_size, sizeof(char), GFP_KERNEL);
2021c06e09b7SEryk Brol 
2022c06e09b7SEryk Brol 	if (!rd_buf)
2023c06e09b7SEryk Brol 		return -ENOMEM;
2024c06e09b7SEryk Brol 
2025c06e09b7SEryk Brol 	rd_buf_ptr = rd_buf;
2026c06e09b7SEryk Brol 
2027c06e09b7SEryk Brol 	for (i = 0; i < MAX_PIPES; i++) {
2028c06e09b7SEryk Brol 		pipe_ctx = &aconnector->dc_link->dc->current_state->res_ctx.pipe_ctx[i];
2029f8e12e77SAlexey Kodanev 		if (pipe_ctx->stream &&
2030af8af93cSHersen Wu 		    pipe_ctx->stream->link == aconnector->dc_link &&
2031af8af93cSHersen Wu 		    pipe_ctx->stream->sink &&
2032af8af93cSHersen Wu 		    pipe_ctx->stream->sink == aconnector->dc_sink)
2033c06e09b7SEryk Brol 			break;
2034c06e09b7SEryk Brol 	}
2035c06e09b7SEryk Brol 
2036c06e09b7SEryk Brol 	dsc = pipe_ctx->stream_res.dsc;
2037c06e09b7SEryk Brol 	if (dsc)
2038c06e09b7SEryk Brol 		dsc->funcs->dsc_read_state(dsc, &dsc_state);
2039c06e09b7SEryk Brol 
2040c06e09b7SEryk Brol 	snprintf(rd_buf_ptr, str_len,
2041c06e09b7SEryk Brol 		"%d\n",
204291b2e45bSEryk Brol 		dsc_state.dsc_bits_per_pixel);
2043c06e09b7SEryk Brol 	rd_buf_ptr += str_len;
2044c06e09b7SEryk Brol 
2045c06e09b7SEryk Brol 	while (size) {
2046c06e09b7SEryk Brol 		if (*pos >= rd_buf_size)
2047c06e09b7SEryk Brol 			break;
2048c06e09b7SEryk Brol 
2049c06e09b7SEryk Brol 		r = put_user(*(rd_buf + result), buf);
20505d5c6dbaSYongzhi Liu 		if (r) {
20515d5c6dbaSYongzhi Liu 			kfree(rd_buf);
2052c06e09b7SEryk Brol 			return r; /* r = -EFAULT */
20535d5c6dbaSYongzhi Liu 		}
2054c06e09b7SEryk Brol 
2055c06e09b7SEryk Brol 		buf += 1;
2056c06e09b7SEryk Brol 		size -= 1;
2057c06e09b7SEryk Brol 		*pos += 1;
2058c06e09b7SEryk Brol 		result += 1;
2059c06e09b7SEryk Brol 	}
2060c06e09b7SEryk Brol 
2061c06e09b7SEryk Brol 	kfree(rd_buf);
2062c06e09b7SEryk Brol 	return result;
2063c06e09b7SEryk Brol }
2064c06e09b7SEryk Brol 
20655268bf13SEryk Brol /* function: write DSC target rate in bits per pixel
20665268bf13SEryk Brol  *
20675268bf13SEryk Brol  * The write function: dp_dsc_bits_per_pixel_write
20685268bf13SEryk Brol  * overwrites automatically generated DSC configuration
20695268bf13SEryk Brol  * of DSC target bit rate.
20705268bf13SEryk Brol  *
20715268bf13SEryk Brol  * Also the user has to write bpp in hexidecimal
20725268bf13SEryk Brol  * rather than in decimal.
20735268bf13SEryk Brol  *
20745268bf13SEryk Brol  * Writing DSC settings is done with the following command:
20755268bf13SEryk Brol  * - To force overwrite rate (example sets to 256 bpp x 1/16):
20765268bf13SEryk Brol  *
20775268bf13SEryk Brol  *	echo 0x100 > /sys/kernel/debug/dri/0/DP-X/dsc_bits_per_pixel
20785268bf13SEryk Brol  *
20795268bf13SEryk Brol  *  - To stop overwriting and let driver find the optimal rate,
20805268bf13SEryk Brol  * set the rate to zero:
20815268bf13SEryk Brol  *
20825268bf13SEryk Brol  *	echo 0x0 > /sys/kernel/debug/dri/0/DP-X/dsc_bits_per_pixel
20835268bf13SEryk Brol  *
20845268bf13SEryk Brol  */
dp_dsc_bits_per_pixel_write(struct file * f,const char __user * buf,size_t size,loff_t * pos)20855268bf13SEryk Brol static ssize_t dp_dsc_bits_per_pixel_write(struct file *f, const char __user *buf,
20865268bf13SEryk Brol 				     size_t size, loff_t *pos)
20875268bf13SEryk Brol {
20885268bf13SEryk Brol 	struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private;
2089886876ecSEryk Brol 	struct drm_connector *connector = &aconnector->base;
2090886876ecSEryk Brol 	struct drm_device *dev = connector->dev;
2091886876ecSEryk Brol 	struct drm_crtc *crtc = NULL;
2092886876ecSEryk Brol 	struct dm_crtc_state *dm_crtc_state = NULL;
20935268bf13SEryk Brol 	struct pipe_ctx *pipe_ctx;
20945268bf13SEryk Brol 	int i;
20955268bf13SEryk Brol 	char *wr_buf = NULL;
20965268bf13SEryk Brol 	uint32_t wr_buf_size = 42;
20975268bf13SEryk Brol 	int max_param_num = 1;
20985268bf13SEryk Brol 	uint8_t param_nums = 0;
20995268bf13SEryk Brol 	long param[1] = {0};
21005268bf13SEryk Brol 
21015268bf13SEryk Brol 	if (size == 0)
21025268bf13SEryk Brol 		return -EINVAL;
21035268bf13SEryk Brol 
21045268bf13SEryk Brol 	wr_buf = kcalloc(wr_buf_size, sizeof(char), GFP_KERNEL);
21055268bf13SEryk Brol 
21065268bf13SEryk Brol 	if (!wr_buf) {
21075268bf13SEryk Brol 		DRM_DEBUG_DRIVER("no memory to allocate write buffer\n");
21085268bf13SEryk Brol 		return -ENOSPC;
21095268bf13SEryk Brol 	}
21105268bf13SEryk Brol 
2111839e59a3SPatrik Jakobsson 	if (parse_write_buffer_into_params(wr_buf, wr_buf_size,
21125268bf13SEryk Brol 					    (long *)param, buf,
21135268bf13SEryk Brol 					    max_param_num,
21145268bf13SEryk Brol 					    &param_nums)) {
21155268bf13SEryk Brol 		kfree(wr_buf);
21165268bf13SEryk Brol 		return -EINVAL;
21175268bf13SEryk Brol 	}
21185268bf13SEryk Brol 
21195268bf13SEryk Brol 	if (param_nums <= 0) {
21205268bf13SEryk Brol 		DRM_DEBUG_DRIVER("user data not be read\n");
21215268bf13SEryk Brol 		kfree(wr_buf);
21225268bf13SEryk Brol 		return -EINVAL;
21235268bf13SEryk Brol 	}
21245268bf13SEryk Brol 
21255268bf13SEryk Brol 	for (i = 0; i < MAX_PIPES; i++) {
21265268bf13SEryk Brol 		pipe_ctx = &aconnector->dc_link->dc->current_state->res_ctx.pipe_ctx[i];
2127f8e12e77SAlexey Kodanev 		if (pipe_ctx->stream &&
2128af8af93cSHersen Wu 		    pipe_ctx->stream->link == aconnector->dc_link &&
2129af8af93cSHersen Wu 		    pipe_ctx->stream->sink &&
2130af8af93cSHersen Wu 		    pipe_ctx->stream->sink == aconnector->dc_sink)
21315268bf13SEryk Brol 			break;
21325268bf13SEryk Brol 	}
21335268bf13SEryk Brol 
2134f8e12e77SAlexey Kodanev 	if (!pipe_ctx->stream)
21355268bf13SEryk Brol 		goto done;
21365268bf13SEryk Brol 
2137886876ecSEryk Brol 	// Get CRTC state
2138886876ecSEryk Brol 	mutex_lock(&dev->mode_config.mutex);
2139886876ecSEryk Brol 	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
2140886876ecSEryk Brol 
2141886876ecSEryk Brol 	if (connector->state == NULL)
2142886876ecSEryk Brol 		goto unlock;
2143886876ecSEryk Brol 
2144886876ecSEryk Brol 	crtc = connector->state->crtc;
2145886876ecSEryk Brol 	if (crtc == NULL)
2146886876ecSEryk Brol 		goto unlock;
2147886876ecSEryk Brol 
2148886876ecSEryk Brol 	drm_modeset_lock(&crtc->mutex, NULL);
2149886876ecSEryk Brol 	if (crtc->state == NULL)
2150886876ecSEryk Brol 		goto unlock;
2151886876ecSEryk Brol 
2152886876ecSEryk Brol 	dm_crtc_state = to_dm_crtc_state(crtc->state);
2153886876ecSEryk Brol 	if (dm_crtc_state->stream == NULL)
2154886876ecSEryk Brol 		goto unlock;
2155886876ecSEryk Brol 
21565268bf13SEryk Brol 	aconnector->dsc_settings.dsc_bits_per_pixel = param[0];
21575268bf13SEryk Brol 
2158886876ecSEryk Brol 	dm_crtc_state->dsc_force_changed = true;
2159886876ecSEryk Brol 
2160886876ecSEryk Brol unlock:
2161886876ecSEryk Brol 	if (crtc)
2162886876ecSEryk Brol 		drm_modeset_unlock(&crtc->mutex);
2163886876ecSEryk Brol 	drm_modeset_unlock(&dev->mode_config.connection_mutex);
2164886876ecSEryk Brol 	mutex_unlock(&dev->mode_config.mutex);
2165886876ecSEryk Brol 
21665268bf13SEryk Brol done:
21675268bf13SEryk Brol 	kfree(wr_buf);
21685268bf13SEryk Brol 	return size;
21695268bf13SEryk Brol }
21705268bf13SEryk Brol 
217187353ae8SEryk Brol /* function: read DSC picture width parameter on the connector
217287353ae8SEryk Brol  *
217387353ae8SEryk Brol  * The read function: dp_dsc_pic_width_read
217487353ae8SEryk Brol  * returns dsc picture width used in the current configuration
217587353ae8SEryk Brol  * It is the same as h_addressable of the current
217687353ae8SEryk Brol  * display's timing
217787353ae8SEryk Brol  * The return is an integer: 0 or other positive integer
217887353ae8SEryk Brol  * If 0 then DSC is disabled.
217987353ae8SEryk Brol  *
218087353ae8SEryk Brol  * Access it with the following command:
218187353ae8SEryk Brol  *
218287353ae8SEryk Brol  *	cat /sys/kernel/debug/dri/0/DP-X/dsc_pic_width
218387353ae8SEryk Brol  *
218487353ae8SEryk Brol  * 0 - means that DSC is disabled
218587353ae8SEryk Brol  */
dp_dsc_pic_width_read(struct file * f,char __user * buf,size_t size,loff_t * pos)2186c06e09b7SEryk Brol static ssize_t dp_dsc_pic_width_read(struct file *f, char __user *buf,
2187c06e09b7SEryk Brol 				    size_t size, loff_t *pos)
2188c06e09b7SEryk Brol {
2189c06e09b7SEryk Brol 	char *rd_buf = NULL;
2190c06e09b7SEryk Brol 	char *rd_buf_ptr = NULL;
2191c06e09b7SEryk Brol 	struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private;
2192c06e09b7SEryk Brol 	struct display_stream_compressor *dsc;
2193c06e09b7SEryk Brol 	struct dcn_dsc_state dsc_state = {0};
2194c06e09b7SEryk Brol 	const uint32_t rd_buf_size = 100;
2195c06e09b7SEryk Brol 	struct pipe_ctx *pipe_ctx;
2196c06e09b7SEryk Brol 	ssize_t result = 0;
2197c06e09b7SEryk Brol 	int i, r, str_len = 30;
2198c06e09b7SEryk Brol 
2199c06e09b7SEryk Brol 	rd_buf = kcalloc(rd_buf_size, sizeof(char), GFP_KERNEL);
2200c06e09b7SEryk Brol 
2201c06e09b7SEryk Brol 	if (!rd_buf)
2202c06e09b7SEryk Brol 		return -ENOMEM;
2203c06e09b7SEryk Brol 
2204c06e09b7SEryk Brol 	rd_buf_ptr = rd_buf;
2205c06e09b7SEryk Brol 
2206c06e09b7SEryk Brol 	for (i = 0; i < MAX_PIPES; i++) {
2207c06e09b7SEryk Brol 		pipe_ctx = &aconnector->dc_link->dc->current_state->res_ctx.pipe_ctx[i];
2208f8e12e77SAlexey Kodanev 		if (pipe_ctx->stream &&
2209af8af93cSHersen Wu 		    pipe_ctx->stream->link == aconnector->dc_link &&
2210af8af93cSHersen Wu 		    pipe_ctx->stream->sink &&
2211af8af93cSHersen Wu 		    pipe_ctx->stream->sink == aconnector->dc_sink)
2212c06e09b7SEryk Brol 			break;
2213c06e09b7SEryk Brol 	}
2214c06e09b7SEryk Brol 
2215c06e09b7SEryk Brol 	dsc = pipe_ctx->stream_res.dsc;
2216c06e09b7SEryk Brol 	if (dsc)
2217c06e09b7SEryk Brol 		dsc->funcs->dsc_read_state(dsc, &dsc_state);
2218c06e09b7SEryk Brol 
2219c06e09b7SEryk Brol 	snprintf(rd_buf_ptr, str_len,
2220c06e09b7SEryk Brol 		"%d\n",
2221c06e09b7SEryk Brol 		dsc_state.dsc_pic_width);
2222c06e09b7SEryk Brol 	rd_buf_ptr += str_len;
2223c06e09b7SEryk Brol 
2224c06e09b7SEryk Brol 	while (size) {
2225c06e09b7SEryk Brol 		if (*pos >= rd_buf_size)
2226c06e09b7SEryk Brol 			break;
2227c06e09b7SEryk Brol 
2228c06e09b7SEryk Brol 		r = put_user(*(rd_buf + result), buf);
22295d5c6dbaSYongzhi Liu 		if (r) {
22305d5c6dbaSYongzhi Liu 			kfree(rd_buf);
2231c06e09b7SEryk Brol 			return r; /* r = -EFAULT */
22325d5c6dbaSYongzhi Liu 		}
2233c06e09b7SEryk Brol 
2234c06e09b7SEryk Brol 		buf += 1;
2235c06e09b7SEryk Brol 		size -= 1;
2236c06e09b7SEryk Brol 		*pos += 1;
2237c06e09b7SEryk Brol 		result += 1;
2238c06e09b7SEryk Brol 	}
2239c06e09b7SEryk Brol 
2240c06e09b7SEryk Brol 	kfree(rd_buf);
2241c06e09b7SEryk Brol 	return result;
2242c06e09b7SEryk Brol }
2243c06e09b7SEryk Brol 
dp_dsc_pic_height_read(struct file * f,char __user * buf,size_t size,loff_t * pos)2244c06e09b7SEryk Brol static ssize_t dp_dsc_pic_height_read(struct file *f, char __user *buf,
2245c06e09b7SEryk Brol 				    size_t size, loff_t *pos)
2246c06e09b7SEryk Brol {
2247c06e09b7SEryk Brol 	char *rd_buf = NULL;
2248c06e09b7SEryk Brol 	char *rd_buf_ptr = NULL;
2249c06e09b7SEryk Brol 	struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private;
2250c06e09b7SEryk Brol 	struct display_stream_compressor *dsc;
2251c06e09b7SEryk Brol 	struct dcn_dsc_state dsc_state = {0};
2252c06e09b7SEryk Brol 	const uint32_t rd_buf_size = 100;
2253c06e09b7SEryk Brol 	struct pipe_ctx *pipe_ctx;
2254c06e09b7SEryk Brol 	ssize_t result = 0;
2255c06e09b7SEryk Brol 	int i, r, str_len = 30;
2256c06e09b7SEryk Brol 
2257c06e09b7SEryk Brol 	rd_buf = kcalloc(rd_buf_size, sizeof(char), GFP_KERNEL);
2258c06e09b7SEryk Brol 
2259c06e09b7SEryk Brol 	if (!rd_buf)
2260c06e09b7SEryk Brol 		return -ENOMEM;
2261c06e09b7SEryk Brol 
2262c06e09b7SEryk Brol 	rd_buf_ptr = rd_buf;
2263c06e09b7SEryk Brol 
2264c06e09b7SEryk Brol 	for (i = 0; i < MAX_PIPES; i++) {
2265c06e09b7SEryk Brol 		pipe_ctx = &aconnector->dc_link->dc->current_state->res_ctx.pipe_ctx[i];
2266f8e12e77SAlexey Kodanev 		if (pipe_ctx->stream &&
2267af8af93cSHersen Wu 		    pipe_ctx->stream->link == aconnector->dc_link &&
2268af8af93cSHersen Wu 		    pipe_ctx->stream->sink &&
2269af8af93cSHersen Wu 		    pipe_ctx->stream->sink == aconnector->dc_sink)
2270c06e09b7SEryk Brol 			break;
2271c06e09b7SEryk Brol 	}
2272c06e09b7SEryk Brol 
2273c06e09b7SEryk Brol 	dsc = pipe_ctx->stream_res.dsc;
2274c06e09b7SEryk Brol 	if (dsc)
2275c06e09b7SEryk Brol 		dsc->funcs->dsc_read_state(dsc, &dsc_state);
2276c06e09b7SEryk Brol 
2277c06e09b7SEryk Brol 	snprintf(rd_buf_ptr, str_len,
2278c06e09b7SEryk Brol 		"%d\n",
2279c06e09b7SEryk Brol 		dsc_state.dsc_pic_height);
2280c06e09b7SEryk Brol 	rd_buf_ptr += str_len;
2281c06e09b7SEryk Brol 
2282c06e09b7SEryk Brol 	while (size) {
2283c06e09b7SEryk Brol 		if (*pos >= rd_buf_size)
2284c06e09b7SEryk Brol 			break;
2285c06e09b7SEryk Brol 
2286c06e09b7SEryk Brol 		r = put_user(*(rd_buf + result), buf);
22875d5c6dbaSYongzhi Liu 		if (r) {
22885d5c6dbaSYongzhi Liu 			kfree(rd_buf);
2289c06e09b7SEryk Brol 			return r; /* r = -EFAULT */
22905d5c6dbaSYongzhi Liu 		}
2291c06e09b7SEryk Brol 
2292c06e09b7SEryk Brol 		buf += 1;
2293c06e09b7SEryk Brol 		size -= 1;
2294c06e09b7SEryk Brol 		*pos += 1;
2295c06e09b7SEryk Brol 		result += 1;
2296c06e09b7SEryk Brol 	}
2297c06e09b7SEryk Brol 
2298c06e09b7SEryk Brol 	kfree(rd_buf);
2299c06e09b7SEryk Brol 	return result;
2300c06e09b7SEryk Brol }
2301c06e09b7SEryk Brol 
230287353ae8SEryk Brol /* function: read DSC chunk size parameter on the connector
230387353ae8SEryk Brol  *
230487353ae8SEryk Brol  * The read function: dp_dsc_chunk_size_read
230587353ae8SEryk Brol  * returns dsc chunk size set in the current configuration
230687353ae8SEryk Brol  * The value is calculated automatically by DSC code
230787353ae8SEryk Brol  * and depends on slice parameters and bpp target rate
230887353ae8SEryk Brol  * The return is an integer: 0 or other positive integer
230987353ae8SEryk Brol  * If 0 then DSC is disabled.
231087353ae8SEryk Brol  *
231187353ae8SEryk Brol  * Access it with the following command:
231287353ae8SEryk Brol  *
231387353ae8SEryk Brol  *	cat /sys/kernel/debug/dri/0/DP-X/dsc_chunk_size
231487353ae8SEryk Brol  *
231587353ae8SEryk Brol  * 0 - means that DSC is disabled
231687353ae8SEryk Brol  */
dp_dsc_chunk_size_read(struct file * f,char __user * buf,size_t size,loff_t * pos)2317c06e09b7SEryk Brol static ssize_t dp_dsc_chunk_size_read(struct file *f, char __user *buf,
2318c06e09b7SEryk Brol 				    size_t size, loff_t *pos)
2319c06e09b7SEryk Brol {
2320c06e09b7SEryk Brol 	char *rd_buf = NULL;
2321c06e09b7SEryk Brol 	char *rd_buf_ptr = NULL;
2322c06e09b7SEryk Brol 	struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private;
2323c06e09b7SEryk Brol 	struct display_stream_compressor *dsc;
2324c06e09b7SEryk Brol 	struct dcn_dsc_state dsc_state = {0};
2325c06e09b7SEryk Brol 	const uint32_t rd_buf_size = 100;
2326c06e09b7SEryk Brol 	struct pipe_ctx *pipe_ctx;
2327c06e09b7SEryk Brol 	ssize_t result = 0;
2328c06e09b7SEryk Brol 	int i, r, str_len = 30;
2329c06e09b7SEryk Brol 
2330c06e09b7SEryk Brol 	rd_buf = kcalloc(rd_buf_size, sizeof(char), GFP_KERNEL);
2331c06e09b7SEryk Brol 
2332c06e09b7SEryk Brol 	if (!rd_buf)
2333c06e09b7SEryk Brol 		return -ENOMEM;
2334c06e09b7SEryk Brol 
2335c06e09b7SEryk Brol 	rd_buf_ptr = rd_buf;
2336c06e09b7SEryk Brol 
2337c06e09b7SEryk Brol 	for (i = 0; i < MAX_PIPES; i++) {
2338c06e09b7SEryk Brol 		pipe_ctx = &aconnector->dc_link->dc->current_state->res_ctx.pipe_ctx[i];
2339f8e12e77SAlexey Kodanev 		if (pipe_ctx->stream &&
2340af8af93cSHersen Wu 		    pipe_ctx->stream->link == aconnector->dc_link &&
2341af8af93cSHersen Wu 		    pipe_ctx->stream->sink &&
2342af8af93cSHersen Wu 		    pipe_ctx->stream->sink == aconnector->dc_sink)
2343c06e09b7SEryk Brol 			break;
2344c06e09b7SEryk Brol 	}
2345c06e09b7SEryk Brol 
2346c06e09b7SEryk Brol 	dsc = pipe_ctx->stream_res.dsc;
2347c06e09b7SEryk Brol 	if (dsc)
2348c06e09b7SEryk Brol 		dsc->funcs->dsc_read_state(dsc, &dsc_state);
2349c06e09b7SEryk Brol 
2350c06e09b7SEryk Brol 	snprintf(rd_buf_ptr, str_len,
2351c06e09b7SEryk Brol 		"%d\n",
2352c06e09b7SEryk Brol 		dsc_state.dsc_chunk_size);
2353c06e09b7SEryk Brol 	rd_buf_ptr += str_len;
2354c06e09b7SEryk Brol 
2355c06e09b7SEryk Brol 	while (size) {
2356c06e09b7SEryk Brol 		if (*pos >= rd_buf_size)
2357c06e09b7SEryk Brol 			break;
2358c06e09b7SEryk Brol 
2359c06e09b7SEryk Brol 		r = put_user(*(rd_buf + result), buf);
23605d5c6dbaSYongzhi Liu 		if (r) {
23615d5c6dbaSYongzhi Liu 			kfree(rd_buf);
2362c06e09b7SEryk Brol 			return r; /* r = -EFAULT */
23635d5c6dbaSYongzhi Liu 		}
2364c06e09b7SEryk Brol 
2365c06e09b7SEryk Brol 		buf += 1;
2366c06e09b7SEryk Brol 		size -= 1;
2367c06e09b7SEryk Brol 		*pos += 1;
2368c06e09b7SEryk Brol 		result += 1;
2369c06e09b7SEryk Brol 	}
2370c06e09b7SEryk Brol 
2371c06e09b7SEryk Brol 	kfree(rd_buf);
2372c06e09b7SEryk Brol 	return result;
2373c06e09b7SEryk Brol }
2374c06e09b7SEryk Brol 
237587353ae8SEryk Brol /* function: read DSC slice bpg offset on the connector
237687353ae8SEryk Brol  *
237787353ae8SEryk Brol  * The read function: dp_dsc_slice_bpg_offset_read
237887353ae8SEryk Brol  * returns dsc bpg slice offset set in the current configuration
237987353ae8SEryk Brol  * The value is calculated automatically by DSC code
238087353ae8SEryk Brol  * and depends on slice parameters and bpp target rate
238187353ae8SEryk Brol  * The return is an integer: 0 or other positive integer
238287353ae8SEryk Brol  * If 0 then DSC is disabled.
238387353ae8SEryk Brol  *
238487353ae8SEryk Brol  * Access it with the following command:
238587353ae8SEryk Brol  *
238687353ae8SEryk Brol  *	cat /sys/kernel/debug/dri/0/DP-X/dsc_slice_bpg_offset
238787353ae8SEryk Brol  *
238887353ae8SEryk Brol  * 0 - means that DSC is disabled
238987353ae8SEryk Brol  */
dp_dsc_slice_bpg_offset_read(struct file * f,char __user * buf,size_t size,loff_t * pos)2390c06e09b7SEryk Brol static ssize_t dp_dsc_slice_bpg_offset_read(struct file *f, char __user *buf,
2391c06e09b7SEryk Brol 				    size_t size, loff_t *pos)
2392c06e09b7SEryk Brol {
2393c06e09b7SEryk Brol 	char *rd_buf = NULL;
2394c06e09b7SEryk Brol 	char *rd_buf_ptr = NULL;
2395c06e09b7SEryk Brol 	struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private;
2396c06e09b7SEryk Brol 	struct display_stream_compressor *dsc;
2397c06e09b7SEryk Brol 	struct dcn_dsc_state dsc_state = {0};
2398c06e09b7SEryk Brol 	const uint32_t rd_buf_size = 100;
2399c06e09b7SEryk Brol 	struct pipe_ctx *pipe_ctx;
2400c06e09b7SEryk Brol 	ssize_t result = 0;
2401c06e09b7SEryk Brol 	int i, r, str_len = 30;
2402c06e09b7SEryk Brol 
2403c06e09b7SEryk Brol 	rd_buf = kcalloc(rd_buf_size, sizeof(char), GFP_KERNEL);
2404c06e09b7SEryk Brol 
2405c06e09b7SEryk Brol 	if (!rd_buf)
2406c06e09b7SEryk Brol 		return -ENOMEM;
2407c06e09b7SEryk Brol 
2408c06e09b7SEryk Brol 	rd_buf_ptr = rd_buf;
2409c06e09b7SEryk Brol 
2410c06e09b7SEryk Brol 	for (i = 0; i < MAX_PIPES; i++) {
2411c06e09b7SEryk Brol 		pipe_ctx = &aconnector->dc_link->dc->current_state->res_ctx.pipe_ctx[i];
2412f8e12e77SAlexey Kodanev 		if (pipe_ctx->stream &&
2413af8af93cSHersen Wu 		    pipe_ctx->stream->link == aconnector->dc_link &&
2414af8af93cSHersen Wu 		    pipe_ctx->stream->sink &&
2415af8af93cSHersen Wu 		    pipe_ctx->stream->sink == aconnector->dc_sink)
2416c06e09b7SEryk Brol 			break;
2417c06e09b7SEryk Brol 	}
2418c06e09b7SEryk Brol 
2419c06e09b7SEryk Brol 	dsc = pipe_ctx->stream_res.dsc;
2420c06e09b7SEryk Brol 	if (dsc)
2421c06e09b7SEryk Brol 		dsc->funcs->dsc_read_state(dsc, &dsc_state);
2422c06e09b7SEryk Brol 
2423c06e09b7SEryk Brol 	snprintf(rd_buf_ptr, str_len,
2424c06e09b7SEryk Brol 		"%d\n",
2425c06e09b7SEryk Brol 		dsc_state.dsc_slice_bpg_offset);
2426c06e09b7SEryk Brol 	rd_buf_ptr += str_len;
2427c06e09b7SEryk Brol 
2428c06e09b7SEryk Brol 	while (size) {
2429c06e09b7SEryk Brol 		if (*pos >= rd_buf_size)
2430c06e09b7SEryk Brol 			break;
2431c06e09b7SEryk Brol 
2432c06e09b7SEryk Brol 		r = put_user(*(rd_buf + result), buf);
24335d5c6dbaSYongzhi Liu 		if (r) {
24345d5c6dbaSYongzhi Liu 			kfree(rd_buf);
2435c06e09b7SEryk Brol 			return r; /* r = -EFAULT */
24365d5c6dbaSYongzhi Liu 		}
2437c06e09b7SEryk Brol 
2438c06e09b7SEryk Brol 		buf += 1;
2439c06e09b7SEryk Brol 		size -= 1;
2440c06e09b7SEryk Brol 		*pos += 1;
2441c06e09b7SEryk Brol 		result += 1;
2442c06e09b7SEryk Brol 	}
2443c06e09b7SEryk Brol 
2444c06e09b7SEryk Brol 	kfree(rd_buf);
2445c06e09b7SEryk Brol 	return result;
2446c06e09b7SEryk Brol }
2447c06e09b7SEryk Brol 
2448cca912e0SEryk Brol 
2449cca912e0SEryk Brol /*
2450cca912e0SEryk Brol  * function description: Read max_requested_bpc property from the connector
2451cca912e0SEryk Brol  *
2452cca912e0SEryk Brol  * Access it with the following command:
2453cca912e0SEryk Brol  *
2454cca912e0SEryk Brol  *	cat /sys/kernel/debug/dri/0/DP-X/max_bpc
2455cca912e0SEryk Brol  *
2456cca912e0SEryk Brol  */
dp_max_bpc_read(struct file * f,char __user * buf,size_t size,loff_t * pos)2457cca912e0SEryk Brol static ssize_t dp_max_bpc_read(struct file *f, char __user *buf,
2458cca912e0SEryk Brol 		size_t size, loff_t *pos)
2459cca912e0SEryk Brol {
2460cca912e0SEryk Brol 	struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private;
2461cca912e0SEryk Brol 	struct drm_connector *connector = &aconnector->base;
2462cca912e0SEryk Brol 	struct drm_device *dev = connector->dev;
2463cca912e0SEryk Brol 	struct dm_connector_state *state;
2464cca912e0SEryk Brol 	ssize_t result = 0;
2465cca912e0SEryk Brol 	char *rd_buf = NULL;
2466cca912e0SEryk Brol 	char *rd_buf_ptr = NULL;
2467cca912e0SEryk Brol 	const uint32_t rd_buf_size = 10;
2468cca912e0SEryk Brol 	int r;
2469cca912e0SEryk Brol 
2470cca912e0SEryk Brol 	rd_buf = kcalloc(rd_buf_size, sizeof(char), GFP_KERNEL);
2471cca912e0SEryk Brol 
2472cca912e0SEryk Brol 	if (!rd_buf)
2473cca912e0SEryk Brol 		return -ENOMEM;
2474cca912e0SEryk Brol 
2475cca912e0SEryk Brol 	mutex_lock(&dev->mode_config.mutex);
2476cca912e0SEryk Brol 	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
2477cca912e0SEryk Brol 
2478cca912e0SEryk Brol 	if (connector->state == NULL)
2479cca912e0SEryk Brol 		goto unlock;
2480cca912e0SEryk Brol 
2481cca912e0SEryk Brol 	state = to_dm_connector_state(connector->state);
2482cca912e0SEryk Brol 
2483cca912e0SEryk Brol 	rd_buf_ptr = rd_buf;
2484cca912e0SEryk Brol 	snprintf(rd_buf_ptr, rd_buf_size,
2485cca912e0SEryk Brol 		"%u\n",
2486cca912e0SEryk Brol 		state->base.max_requested_bpc);
2487cca912e0SEryk Brol 
2488cca912e0SEryk Brol 	while (size) {
2489cca912e0SEryk Brol 		if (*pos >= rd_buf_size)
2490cca912e0SEryk Brol 			break;
2491cca912e0SEryk Brol 
2492cca912e0SEryk Brol 		r = put_user(*(rd_buf + result), buf);
2493cca912e0SEryk Brol 		if (r) {
2494cca912e0SEryk Brol 			result = r; /* r = -EFAULT */
2495cca912e0SEryk Brol 			goto unlock;
2496cca912e0SEryk Brol 		}
2497cca912e0SEryk Brol 		buf += 1;
2498cca912e0SEryk Brol 		size -= 1;
2499cca912e0SEryk Brol 		*pos += 1;
2500cca912e0SEryk Brol 		result += 1;
2501cca912e0SEryk Brol 	}
2502cca912e0SEryk Brol unlock:
2503cca912e0SEryk Brol 	drm_modeset_unlock(&dev->mode_config.connection_mutex);
2504cca912e0SEryk Brol 	mutex_unlock(&dev->mode_config.mutex);
2505cca912e0SEryk Brol 	kfree(rd_buf);
2506cca912e0SEryk Brol 	return result;
2507cca912e0SEryk Brol }
2508cca912e0SEryk Brol 
2509cca912e0SEryk Brol 
2510cca912e0SEryk Brol /*
2511cca912e0SEryk Brol  * function description: Set max_requested_bpc property on the connector
2512cca912e0SEryk Brol  *
2513cca912e0SEryk Brol  * This function will not force the input BPC on connector, it will only
2514cca912e0SEryk Brol  * change the max value. This is equivalent to setting max_bpc through
2515cca912e0SEryk Brol  * xrandr.
2516cca912e0SEryk Brol  *
2517cca912e0SEryk Brol  * The BPC value written must be >= 6 and <= 16. Values outside of this
2518cca912e0SEryk Brol  * range will result in errors.
2519cca912e0SEryk Brol  *
2520cca912e0SEryk Brol  * BPC values:
2521cca912e0SEryk Brol  *	0x6 - 6 BPC
2522cca912e0SEryk Brol  *	0x8 - 8 BPC
2523cca912e0SEryk Brol  *	0xa - 10 BPC
2524cca912e0SEryk Brol  *	0xc - 12 BPC
2525cca912e0SEryk Brol  *	0x10 - 16 BPC
2526cca912e0SEryk Brol  *
2527cca912e0SEryk Brol  * Write the max_bpc in the following way:
2528cca912e0SEryk Brol  *
2529cca912e0SEryk Brol  * echo 0x6 > /sys/kernel/debug/dri/0/DP-X/max_bpc
2530cca912e0SEryk Brol  *
2531cca912e0SEryk Brol  */
dp_max_bpc_write(struct file * f,const char __user * buf,size_t size,loff_t * pos)2532cca912e0SEryk Brol static ssize_t dp_max_bpc_write(struct file *f, const char __user *buf,
2533cca912e0SEryk Brol 				     size_t size, loff_t *pos)
2534cca912e0SEryk Brol {
2535cca912e0SEryk Brol 	struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private;
2536cca912e0SEryk Brol 	struct drm_connector *connector = &aconnector->base;
2537cca912e0SEryk Brol 	struct dm_connector_state *state;
2538cca912e0SEryk Brol 	struct drm_device *dev = connector->dev;
2539cca912e0SEryk Brol 	char *wr_buf = NULL;
2540cca912e0SEryk Brol 	uint32_t wr_buf_size = 42;
2541cca912e0SEryk Brol 	int max_param_num = 1;
2542cca912e0SEryk Brol 	long param[1] = {0};
2543cca912e0SEryk Brol 	uint8_t param_nums = 0;
2544cca912e0SEryk Brol 
2545cca912e0SEryk Brol 	if (size == 0)
2546cca912e0SEryk Brol 		return -EINVAL;
2547cca912e0SEryk Brol 
2548cca912e0SEryk Brol 	wr_buf = kcalloc(wr_buf_size, sizeof(char), GFP_KERNEL);
2549cca912e0SEryk Brol 
2550cca912e0SEryk Brol 	if (!wr_buf) {
2551cca912e0SEryk Brol 		DRM_DEBUG_DRIVER("no memory to allocate write buffer\n");
2552cca912e0SEryk Brol 		return -ENOSPC;
2553cca912e0SEryk Brol 	}
2554cca912e0SEryk Brol 
2555839e59a3SPatrik Jakobsson 	if (parse_write_buffer_into_params(wr_buf, wr_buf_size,
2556cca912e0SEryk Brol 					   (long *)param, buf,
2557cca912e0SEryk Brol 					   max_param_num,
2558cca912e0SEryk Brol 					   &param_nums)) {
2559cca912e0SEryk Brol 		kfree(wr_buf);
2560cca912e0SEryk Brol 		return -EINVAL;
2561cca912e0SEryk Brol 	}
2562cca912e0SEryk Brol 
2563cca912e0SEryk Brol 	if (param_nums <= 0) {
2564cca912e0SEryk Brol 		DRM_DEBUG_DRIVER("user data not be read\n");
2565cca912e0SEryk Brol 		kfree(wr_buf);
2566cca912e0SEryk Brol 		return -EINVAL;
2567cca912e0SEryk Brol 	}
2568cca912e0SEryk Brol 
2569cca912e0SEryk Brol 	if (param[0] < 6 || param[0] > 16) {
2570cca912e0SEryk Brol 		DRM_DEBUG_DRIVER("bad max_bpc value\n");
2571cca912e0SEryk Brol 		kfree(wr_buf);
2572cca912e0SEryk Brol 		return -EINVAL;
2573cca912e0SEryk Brol 	}
2574cca912e0SEryk Brol 
2575cca912e0SEryk Brol 	mutex_lock(&dev->mode_config.mutex);
2576cca912e0SEryk Brol 	drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
2577cca912e0SEryk Brol 
2578cca912e0SEryk Brol 	if (connector->state == NULL)
2579cca912e0SEryk Brol 		goto unlock;
2580cca912e0SEryk Brol 
2581cca912e0SEryk Brol 	state = to_dm_connector_state(connector->state);
2582cca912e0SEryk Brol 	state->base.max_requested_bpc = param[0];
2583cca912e0SEryk Brol unlock:
2584cca912e0SEryk Brol 	drm_modeset_unlock(&dev->mode_config.connection_mutex);
2585cca912e0SEryk Brol 	mutex_unlock(&dev->mode_config.mutex);
2586cca912e0SEryk Brol 
2587cca912e0SEryk Brol 	kfree(wr_buf);
2588cca912e0SEryk Brol 	return size;
2589cca912e0SEryk Brol }
2590cca912e0SEryk Brol 
2591118b4627SMikita Lipski /*
2592118b4627SMikita Lipski  * Backlight at this moment.  Read only.
2593118b4627SMikita Lipski  * As written to display, taking ABM and backlight lut into account.
2594118b4627SMikita Lipski  * Ranges from 0x0 to 0x10000 (= 100% PWM)
2595118b4627SMikita Lipski  *
2596118b4627SMikita Lipski  * Example usage: cat /sys/kernel/debug/dri/0/eDP-1/current_backlight
2597118b4627SMikita Lipski  */
current_backlight_show(struct seq_file * m,void * unused)2598118b4627SMikita Lipski static int current_backlight_show(struct seq_file *m, void *unused)
2599118b4627SMikita Lipski {
2600118b4627SMikita Lipski 	struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(m->private);
2601118b4627SMikita Lipski 	struct dc_link *link = aconnector->dc_link;
2602118b4627SMikita Lipski 	unsigned int backlight;
2603118b4627SMikita Lipski 
2604118b4627SMikita Lipski 	backlight = dc_link_get_backlight_level(link);
2605118b4627SMikita Lipski 	seq_printf(m, "0x%x\n", backlight);
2606118b4627SMikita Lipski 
2607118b4627SMikita Lipski 	return 0;
2608118b4627SMikita Lipski }
2609118b4627SMikita Lipski 
2610118b4627SMikita Lipski /*
2611118b4627SMikita Lipski  * Backlight value that is being approached.  Read only.
2612118b4627SMikita Lipski  * As written to display, taking ABM and backlight lut into account.
2613118b4627SMikita Lipski  * Ranges from 0x0 to 0x10000 (= 100% PWM)
2614118b4627SMikita Lipski  *
2615118b4627SMikita Lipski  * Example usage: cat /sys/kernel/debug/dri/0/eDP-1/target_backlight
2616118b4627SMikita Lipski  */
target_backlight_show(struct seq_file * m,void * unused)2617118b4627SMikita Lipski static int target_backlight_show(struct seq_file *m, void *unused)
2618118b4627SMikita Lipski {
2619118b4627SMikita Lipski 	struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(m->private);
2620118b4627SMikita Lipski 	struct dc_link *link = aconnector->dc_link;
2621118b4627SMikita Lipski 	unsigned int backlight;
2622118b4627SMikita Lipski 
2623118b4627SMikita Lipski 	backlight = dc_link_get_target_backlight_pwm(link);
2624118b4627SMikita Lipski 	seq_printf(m, "0x%x\n", backlight);
2625118b4627SMikita Lipski 
2626118b4627SMikita Lipski 	return 0;
2627118b4627SMikita Lipski }
2628118b4627SMikita Lipski 
26298b076fa7SWayne Lin /*
26308b076fa7SWayne Lin  * function description: Determine if the connector is mst connector
26318b076fa7SWayne Lin  *
26328b076fa7SWayne Lin  * This function helps to determine whether a connector is a mst connector.
26338b076fa7SWayne Lin  * - "root" stands for the root connector of the topology
26348b076fa7SWayne Lin  * - "branch" stands for branch device of the topology
26358b076fa7SWayne Lin  * - "end" stands for leaf node connector of the topology
26368b076fa7SWayne Lin  * - "no" stands for the connector is not a device of a mst topology
26378b076fa7SWayne Lin  * Access it with the following command:
26388b076fa7SWayne Lin  *
26398b076fa7SWayne Lin  *	cat /sys/kernel/debug/dri/0/DP-X/is_mst_connector
26408b076fa7SWayne Lin  *
26418b076fa7SWayne Lin  */
dp_is_mst_connector_show(struct seq_file * m,void * unused)26428b076fa7SWayne Lin static int dp_is_mst_connector_show(struct seq_file *m, void *unused)
26438b076fa7SWayne Lin {
26448b076fa7SWayne Lin 	struct drm_connector *connector = m->private;
26458b076fa7SWayne Lin 	struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
26468b076fa7SWayne Lin 	struct drm_dp_mst_topology_mgr *mgr = NULL;
26478b076fa7SWayne Lin 	struct drm_dp_mst_port *port = NULL;
26488b076fa7SWayne Lin 	char *role = NULL;
26498b076fa7SWayne Lin 
26508b076fa7SWayne Lin 	mutex_lock(&aconnector->hpd_lock);
26518b076fa7SWayne Lin 
26528b076fa7SWayne Lin 	if (aconnector->mst_mgr.mst_state) {
26538b076fa7SWayne Lin 		role = "root";
2654f0127cb1SWayne Lin 	} else if (aconnector->mst_root &&
2655f0127cb1SWayne Lin 		aconnector->mst_root->mst_mgr.mst_state) {
26568b076fa7SWayne Lin 
26578b076fa7SWayne Lin 		role = "end";
26588b076fa7SWayne Lin 
2659f0127cb1SWayne Lin 		mgr = &aconnector->mst_root->mst_mgr;
2660f0127cb1SWayne Lin 		port = aconnector->mst_output_port;
26618b076fa7SWayne Lin 
26628b076fa7SWayne Lin 		drm_modeset_lock(&mgr->base.lock, NULL);
26638b076fa7SWayne Lin 		if (port->pdt == DP_PEER_DEVICE_MST_BRANCHING &&
26648b076fa7SWayne Lin 			port->mcs)
26658b076fa7SWayne Lin 			role = "branch";
26668b076fa7SWayne Lin 		drm_modeset_unlock(&mgr->base.lock);
26678b076fa7SWayne Lin 
26688b076fa7SWayne Lin 	} else {
26698b076fa7SWayne Lin 		role = "no";
26708b076fa7SWayne Lin 	}
26718b076fa7SWayne Lin 
26728b076fa7SWayne Lin 	seq_printf(m, "%s\n", role);
26738b076fa7SWayne Lin 
26748b076fa7SWayne Lin 	mutex_unlock(&aconnector->hpd_lock);
26758b076fa7SWayne Lin 
26768b076fa7SWayne Lin 	return 0;
26778b076fa7SWayne Lin }
26788b076fa7SWayne Lin 
267925f7cde8SWayne Lin /*
268025f7cde8SWayne Lin  * function description: Read out the mst progress status
268125f7cde8SWayne Lin  *
268225f7cde8SWayne Lin  * This function helps to determine the mst progress status of
268325f7cde8SWayne Lin  * a mst connector.
268425f7cde8SWayne Lin  *
268525f7cde8SWayne Lin  * Access it with the following command:
268625f7cde8SWayne Lin  *
268725f7cde8SWayne Lin  *	cat /sys/kernel/debug/dri/0/DP-X/mst_progress_status
268825f7cde8SWayne Lin  *
268925f7cde8SWayne Lin  */
dp_mst_progress_status_show(struct seq_file * m,void * unused)269025f7cde8SWayne Lin static int dp_mst_progress_status_show(struct seq_file *m, void *unused)
269125f7cde8SWayne Lin {
269225f7cde8SWayne Lin 	struct drm_connector *connector = m->private;
269325f7cde8SWayne Lin 	struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
269425f7cde8SWayne Lin 	struct amdgpu_device *adev = drm_to_adev(connector->dev);
269525f7cde8SWayne Lin 	int i;
269625f7cde8SWayne Lin 
269725f7cde8SWayne Lin 	mutex_lock(&aconnector->hpd_lock);
269825f7cde8SWayne Lin 	mutex_lock(&adev->dm.dc_lock);
269925f7cde8SWayne Lin 
270025f7cde8SWayne Lin 	if (aconnector->mst_status == MST_STATUS_DEFAULT) {
270125f7cde8SWayne Lin 		seq_puts(m, "disabled\n");
270225f7cde8SWayne Lin 	} else {
270325f7cde8SWayne Lin 		for (i = 0; i < sizeof(mst_progress_status)/sizeof(char *); i++)
270425f7cde8SWayne Lin 			seq_printf(m, "%s:%s\n",
270525f7cde8SWayne Lin 				mst_progress_status[i],
270625f7cde8SWayne Lin 				aconnector->mst_status & BIT(i) ? "done" : "not_done");
270725f7cde8SWayne Lin 	}
270825f7cde8SWayne Lin 
270925f7cde8SWayne Lin 	mutex_unlock(&adev->dm.dc_lock);
271025f7cde8SWayne Lin 	mutex_unlock(&aconnector->hpd_lock);
271125f7cde8SWayne Lin 
271225f7cde8SWayne Lin 	return 0;
271325f7cde8SWayne Lin }
27148b076fa7SWayne Lin 
27157a259c6dSStylon Wang /*
27167a259c6dSStylon Wang  * Reports whether the connected display is a USB4 DPIA tunneled display
27177a259c6dSStylon Wang  * Example usage: cat /sys/kernel/debug/dri/0/DP-8/is_dpia_link
27187a259c6dSStylon Wang  */
is_dpia_link_show(struct seq_file * m,void * data)27197a259c6dSStylon Wang static int is_dpia_link_show(struct seq_file *m, void *data)
27207a259c6dSStylon Wang {
27217a259c6dSStylon Wang 	struct drm_connector *connector = m->private;
27227a259c6dSStylon Wang 	struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
27237a259c6dSStylon Wang 	struct dc_link *link = aconnector->dc_link;
27247a259c6dSStylon Wang 
27257a259c6dSStylon Wang 	if (connector->status != connector_status_connected)
27267a259c6dSStylon Wang 		return -ENODEV;
27277a259c6dSStylon Wang 
27287a259c6dSStylon Wang 	seq_printf(m, "%s\n", (link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA) ? "yes" :
27297a259c6dSStylon Wang 				(link->ep_type == DISPLAY_ENDPOINT_PHY) ? "no" : "unknown");
27307a259c6dSStylon Wang 
27317a259c6dSStylon Wang 	return 0;
27327a259c6dSStylon Wang }
27337a259c6dSStylon Wang 
2734237070fdSEryk Brol DEFINE_SHOW_ATTRIBUTE(dp_dsc_fec_support);
273523640767SNicholas Kazlauskas DEFINE_SHOW_ATTRIBUTE(dmub_fw_state);
273660ec1b56SNicholas Kazlauskas DEFINE_SHOW_ATTRIBUTE(dmub_tracebuffer);
273737bedd99SAurabindo Pillai DEFINE_SHOW_ATTRIBUTE(dp_lttpr_status);
27385f869379SBhawanpreet Lakha DEFINE_SHOW_ATTRIBUTE(hdcp_sink_capability);
2739b7cc1312SStylon Wang DEFINE_SHOW_ATTRIBUTE(internal_display);
27407238b42eSMikita Lipski DEFINE_SHOW_ATTRIBUTE(psr_capability);
27418b076fa7SWayne Lin DEFINE_SHOW_ATTRIBUTE(dp_is_mst_connector);
274225f7cde8SWayne Lin DEFINE_SHOW_ATTRIBUTE(dp_mst_progress_status);
27437a259c6dSStylon Wang DEFINE_SHOW_ATTRIBUTE(is_dpia_link);
27444b7ef85cSNicholas Kazlauskas 
2745c06e09b7SEryk Brol static const struct file_operations dp_dsc_clock_en_debugfs_fops = {
2746c06e09b7SEryk Brol 	.owner = THIS_MODULE,
2747c06e09b7SEryk Brol 	.read = dp_dsc_clock_en_read,
2748097e6d98SEryk Brol 	.write = dp_dsc_clock_en_write,
2749c06e09b7SEryk Brol 	.llseek = default_llseek
2750c06e09b7SEryk Brol };
2751c06e09b7SEryk Brol 
2752c06e09b7SEryk Brol static const struct file_operations dp_dsc_slice_width_debugfs_fops = {
2753c06e09b7SEryk Brol 	.owner = THIS_MODULE,
2754c06e09b7SEryk Brol 	.read = dp_dsc_slice_width_read,
275527e84dd7SEryk Brol 	.write = dp_dsc_slice_width_write,
2756c06e09b7SEryk Brol 	.llseek = default_llseek
2757c06e09b7SEryk Brol };
2758c06e09b7SEryk Brol 
2759c06e09b7SEryk Brol static const struct file_operations dp_dsc_slice_height_debugfs_fops = {
2760c06e09b7SEryk Brol 	.owner = THIS_MODULE,
2761c06e09b7SEryk Brol 	.read = dp_dsc_slice_height_read,
2762734e4c97SEryk Brol 	.write = dp_dsc_slice_height_write,
2763c06e09b7SEryk Brol 	.llseek = default_llseek
2764c06e09b7SEryk Brol };
2765c06e09b7SEryk Brol 
2766f92e25e5SEryk Brol static const struct file_operations dp_dsc_bits_per_pixel_debugfs_fops = {
2767c06e09b7SEryk Brol 	.owner = THIS_MODULE,
2768f92e25e5SEryk Brol 	.read = dp_dsc_bits_per_pixel_read,
27695268bf13SEryk Brol 	.write = dp_dsc_bits_per_pixel_write,
2770c06e09b7SEryk Brol 	.llseek = default_llseek
2771c06e09b7SEryk Brol };
2772c06e09b7SEryk Brol 
2773c06e09b7SEryk Brol static const struct file_operations dp_dsc_pic_width_debugfs_fops = {
2774c06e09b7SEryk Brol 	.owner = THIS_MODULE,
2775c06e09b7SEryk Brol 	.read = dp_dsc_pic_width_read,
2776c06e09b7SEryk Brol 	.llseek = default_llseek
2777c06e09b7SEryk Brol };
2778c06e09b7SEryk Brol 
2779c06e09b7SEryk Brol static const struct file_operations dp_dsc_pic_height_debugfs_fops = {
2780c06e09b7SEryk Brol 	.owner = THIS_MODULE,
2781c06e09b7SEryk Brol 	.read = dp_dsc_pic_height_read,
2782c06e09b7SEryk Brol 	.llseek = default_llseek
2783c06e09b7SEryk Brol };
2784c06e09b7SEryk Brol 
2785c06e09b7SEryk Brol static const struct file_operations dp_dsc_chunk_size_debugfs_fops = {
2786c06e09b7SEryk Brol 	.owner = THIS_MODULE,
2787c06e09b7SEryk Brol 	.read = dp_dsc_chunk_size_read,
2788c06e09b7SEryk Brol 	.llseek = default_llseek
2789c06e09b7SEryk Brol };
2790c06e09b7SEryk Brol 
2791c06e09b7SEryk Brol static const struct file_operations dp_dsc_slice_bpg_offset_debugfs_fops = {
2792c06e09b7SEryk Brol 	.owner = THIS_MODULE,
2793c06e09b7SEryk Brol 	.read = dp_dsc_slice_bpg_offset_read,
2794c06e09b7SEryk Brol 	.llseek = default_llseek
2795c06e09b7SEryk Brol };
2796c06e09b7SEryk Brol 
279702a342e3SStylon Wang static const struct file_operations trigger_hotplug_debugfs_fops = {
27986f77b2acSEryk Brol 	.owner = THIS_MODULE,
279902a342e3SStylon Wang 	.write = trigger_hotplug,
28006f77b2acSEryk Brol 	.llseek = default_llseek
28016f77b2acSEryk Brol };
28026f77b2acSEryk Brol 
280341db5f19SHersen Wu static const struct file_operations dp_link_settings_debugfs_fops = {
2804dc38fd9dSDavid Francis 	.owner = THIS_MODULE,
280541db5f19SHersen Wu 	.read = dp_link_settings_read,
280641db5f19SHersen Wu 	.write = dp_link_settings_write,
2807dc38fd9dSDavid Francis 	.llseek = default_llseek
2808dc38fd9dSDavid Francis };
2809dc38fd9dSDavid Francis 
2810f8ac2cf7SHersen Wu static const struct file_operations dp_phy_settings_debugfs_fop = {
2811dc38fd9dSDavid Francis 	.owner = THIS_MODULE,
2812f8ac2cf7SHersen Wu 	.read = dp_phy_settings_read,
2813f8ac2cf7SHersen Wu 	.write = dp_phy_settings_write,
2814dc38fd9dSDavid Francis 	.llseek = default_llseek
2815dc38fd9dSDavid Francis };
2816dc38fd9dSDavid Francis 
2817dc38fd9dSDavid Francis static const struct file_operations dp_phy_test_pattern_fops = {
2818dc38fd9dSDavid Francis 	.owner = THIS_MODULE,
2819dc38fd9dSDavid Francis 	.write = dp_phy_test_pattern_debugfs_write,
2820dc38fd9dSDavid Francis 	.llseek = default_llseek
2821dc38fd9dSDavid Francis };
2822dc38fd9dSDavid Francis 
2823c7ba3653SLeo (Hanghong) Ma static const struct file_operations sdp_message_fops = {
2824c7ba3653SLeo (Hanghong) Ma 	.owner = THIS_MODULE,
2825c7ba3653SLeo (Hanghong) Ma 	.write = dp_sdp_message_debugfs_write,
2826c7ba3653SLeo (Hanghong) Ma 	.llseek = default_llseek
2827c7ba3653SLeo (Hanghong) Ma };
2828c7ba3653SLeo (Hanghong) Ma 
2829cca912e0SEryk Brol static const struct file_operations dp_max_bpc_debugfs_fops = {
2830cca912e0SEryk Brol 	.owner = THIS_MODULE,
2831cca912e0SEryk Brol 	.read = dp_max_bpc_read,
2832cca912e0SEryk Brol 	.write = dp_max_bpc_write,
2833cca912e0SEryk Brol 	.llseek = default_llseek
2834cca912e0SEryk Brol };
2835cca912e0SEryk Brol 
2836fcd1e484SFangzhi Zuo static const struct file_operations dp_dsc_disable_passthrough_debugfs_fops = {
2837fcd1e484SFangzhi Zuo 	.owner = THIS_MODULE,
2838fcd1e484SFangzhi Zuo 	.write = dp_dsc_passthrough_set,
2839fcd1e484SFangzhi Zuo 	.llseek = default_llseek
2840fcd1e484SFangzhi Zuo };
2841fcd1e484SFangzhi Zuo 
28420250a714SFangzhi Zuo static const struct file_operations dp_mst_link_settings_debugfs_fops = {
28430250a714SFangzhi Zuo 	.owner = THIS_MODULE,
28440250a714SFangzhi Zuo 	.write = dp_mst_link_setting,
28450250a714SFangzhi Zuo 	.llseek = default_llseek
28460250a714SFangzhi Zuo };
28470250a714SFangzhi Zuo 
2848dc38fd9dSDavid Francis static const struct {
2849dc38fd9dSDavid Francis 	char *name;
2850dc38fd9dSDavid Francis 	const struct file_operations *fops;
2851dc38fd9dSDavid Francis } dp_debugfs_entries[] = {
285241db5f19SHersen Wu 		{"link_settings", &dp_link_settings_debugfs_fops},
2853f8ac2cf7SHersen Wu 		{"phy_settings", &dp_phy_settings_debugfs_fop},
285437bedd99SAurabindo Pillai 		{"lttpr_status", &dp_lttpr_status_fops},
2855727962f0SNicholas Kazlauskas 		{"test_pattern", &dp_phy_test_pattern_fops},
28565f869379SBhawanpreet Lakha 		{"hdcp_sink_capability", &hdcp_sink_capability_fops},
2857f258fee6SDavid Francis 		{"sdp_message", &sdp_message_fops},
2858c06e09b7SEryk Brol 		{"dsc_clock_en", &dp_dsc_clock_en_debugfs_fops},
2859c06e09b7SEryk Brol 		{"dsc_slice_width", &dp_dsc_slice_width_debugfs_fops},
2860c06e09b7SEryk Brol 		{"dsc_slice_height", &dp_dsc_slice_height_debugfs_fops},
2861f92e25e5SEryk Brol 		{"dsc_bits_per_pixel", &dp_dsc_bits_per_pixel_debugfs_fops},
2862c06e09b7SEryk Brol 		{"dsc_pic_width", &dp_dsc_pic_width_debugfs_fops},
2863c06e09b7SEryk Brol 		{"dsc_pic_height", &dp_dsc_pic_height_debugfs_fops},
2864c06e09b7SEryk Brol 		{"dsc_chunk_size", &dp_dsc_chunk_size_debugfs_fops},
2865237070fdSEryk Brol 		{"dsc_slice_bpg", &dp_dsc_slice_bpg_offset_debugfs_fops},
2866cca912e0SEryk Brol 		{"dp_dsc_fec_support", &dp_dsc_fec_support_fops},
2867fcd1e484SFangzhi Zuo 		{"max_bpc", &dp_max_bpc_debugfs_fops},
2868fcd1e484SFangzhi Zuo 		{"dsc_disable_passthrough", &dp_dsc_disable_passthrough_debugfs_fops},
286925f7cde8SWayne Lin 		{"is_mst_connector", &dp_is_mst_connector_fops},
28707a259c6dSStylon Wang 		{"mst_progress_status", &dp_mst_progress_status_fops},
28710250a714SFangzhi Zuo 		{"is_dpia_link", &is_dpia_link_fops},
28720250a714SFangzhi Zuo 		{"mst_link_settings", &dp_mst_link_settings_debugfs_fops}
2873dc38fd9dSDavid Francis };
2874dc38fd9dSDavid Francis 
28755f869379SBhawanpreet Lakha static const struct {
28765f869379SBhawanpreet Lakha 	char *name;
28775f869379SBhawanpreet Lakha 	const struct file_operations *fops;
28785f869379SBhawanpreet Lakha } hdmi_debugfs_entries[] = {
28795f869379SBhawanpreet Lakha 		{"hdcp_sink_capability", &hdcp_sink_capability_fops}
28805f869379SBhawanpreet Lakha };
28811e88eb1bSHarry Wentland 
2882d4252eeeSStylon Wang /*
2883d4252eeeSStylon Wang  * Force YUV420 output if available from the given mode
2884d4252eeeSStylon Wang  */
force_yuv420_output_set(void * data,u64 val)2885d4252eeeSStylon Wang static int force_yuv420_output_set(void *data, u64 val)
2886d4252eeeSStylon Wang {
2887d4252eeeSStylon Wang 	struct amdgpu_dm_connector *connector = data;
2888d4252eeeSStylon Wang 
2889d4252eeeSStylon Wang 	connector->force_yuv420_output = (bool)val;
2890d4252eeeSStylon Wang 
2891d4252eeeSStylon Wang 	return 0;
2892d4252eeeSStylon Wang }
2893d4252eeeSStylon Wang 
2894d4252eeeSStylon Wang /*
2895d4252eeeSStylon Wang  * Check if YUV420 is forced when available from the given mode
2896d4252eeeSStylon Wang  */
force_yuv420_output_get(void * data,u64 * val)2897d4252eeeSStylon Wang static int force_yuv420_output_get(void *data, u64 *val)
2898d4252eeeSStylon Wang {
2899d4252eeeSStylon Wang 	struct amdgpu_dm_connector *connector = data;
2900d4252eeeSStylon Wang 
2901d4252eeeSStylon Wang 	*val = connector->force_yuv420_output;
2902d4252eeeSStylon Wang 
2903d4252eeeSStylon Wang 	return 0;
2904d4252eeeSStylon Wang }
2905d4252eeeSStylon Wang 
2906d4252eeeSStylon Wang DEFINE_DEBUGFS_ATTRIBUTE(force_yuv420_output_fops, force_yuv420_output_get,
2907d4252eeeSStylon Wang 			 force_yuv420_output_set, "%llu\n");
2908d4252eeeSStylon Wang 
2909e0d08a40SRoman Li /*
2910e0d08a40SRoman Li  *  Read PSR state
2911e0d08a40SRoman Li  */
psr_get(void * data,u64 * val)2912e0d08a40SRoman Li static int psr_get(void *data, u64 *val)
2913e0d08a40SRoman Li {
2914e0d08a40SRoman Li 	struct amdgpu_dm_connector *connector = data;
2915e0d08a40SRoman Li 	struct dc_link *link = connector->dc_link;
29161d496907SKrunoslav Kovac 	enum dc_psr_state state = PSR_STATE0;
2917e0d08a40SRoman Li 
29181d496907SKrunoslav Kovac 	dc_link_get_psr_state(link, &state);
2919e0d08a40SRoman Li 
29201d496907SKrunoslav Kovac 	*val = state;
2921e0d08a40SRoman Li 
2922e0d08a40SRoman Li 	return 0;
2923e0d08a40SRoman Li }
2924e0d08a40SRoman Li 
292546a83ebaSLeo (Hanghong) Ma /*
292622f1482aSShirish S  *  Read PSR state residency
292722f1482aSShirish S  */
psr_read_residency(void * data,u64 * val)292822f1482aSShirish S static int psr_read_residency(void *data, u64 *val)
292922f1482aSShirish S {
293022f1482aSShirish S 	struct amdgpu_dm_connector *connector = data;
293122f1482aSShirish S 	struct dc_link *link = connector->dc_link;
293222f1482aSShirish S 	u32 residency;
293322f1482aSShirish S 
293498ce7d32SWenjing Liu 	link->dc->link_srv->edp_get_psr_residency(link, &residency);
293522f1482aSShirish S 
293622f1482aSShirish S 	*val = (u64)residency;
293722f1482aSShirish S 
293822f1482aSShirish S 	return 0;
293922f1482aSShirish S }
294022f1482aSShirish S 
2941ee83c930SHersen Wu /* read allow_edp_hotplug_detection */
allow_edp_hotplug_detection_get(void * data,u64 * val)2942ee83c930SHersen Wu static int allow_edp_hotplug_detection_get(void *data, u64 *val)
2943ee83c930SHersen Wu {
2944ee83c930SHersen Wu 	struct amdgpu_dm_connector *aconnector = data;
2945ee83c930SHersen Wu 	struct drm_connector *connector = &aconnector->base;
2946ee83c930SHersen Wu 	struct drm_device *dev = connector->dev;
2947ee83c930SHersen Wu 	struct amdgpu_device *adev = drm_to_adev(dev);
2948ee83c930SHersen Wu 
2949ee83c930SHersen Wu 	*val = adev->dm.dc->config.allow_edp_hotplug_detection;
2950ee83c930SHersen Wu 
2951ee83c930SHersen Wu 	return 0;
2952ee83c930SHersen Wu }
2953ee83c930SHersen Wu 
2954ee83c930SHersen Wu /* set allow_edp_hotplug_detection */
allow_edp_hotplug_detection_set(void * data,u64 val)2955ee83c930SHersen Wu static int allow_edp_hotplug_detection_set(void *data, u64 val)
2956ee83c930SHersen Wu {
2957ee83c930SHersen Wu 	struct amdgpu_dm_connector *aconnector = data;
2958ee83c930SHersen Wu 	struct drm_connector *connector = &aconnector->base;
2959ee83c930SHersen Wu 	struct drm_device *dev = connector->dev;
2960ee83c930SHersen Wu 	struct amdgpu_device *adev = drm_to_adev(dev);
2961ee83c930SHersen Wu 
2962ee83c930SHersen Wu 	adev->dm.dc->config.allow_edp_hotplug_detection = (uint32_t) val;
2963ee83c930SHersen Wu 
2964ee83c930SHersen Wu 	return 0;
2965ee83c930SHersen Wu }
2966ee83c930SHersen Wu 
296722f1482aSShirish S /*
296846a83ebaSLeo (Hanghong) Ma  * Set dmcub trace event IRQ enable or disable.
296946a83ebaSLeo (Hanghong) Ma  * Usage to enable dmcub trace event IRQ: echo 1 > /sys/kernel/debug/dri/0/amdgpu_dm_dmcub_trace_event_en
297046a83ebaSLeo (Hanghong) Ma  * Usage to disable dmcub trace event IRQ: echo 0 > /sys/kernel/debug/dri/0/amdgpu_dm_dmcub_trace_event_en
297146a83ebaSLeo (Hanghong) Ma  */
dmcub_trace_event_state_set(void * data,u64 val)297246a83ebaSLeo (Hanghong) Ma static int dmcub_trace_event_state_set(void *data, u64 val)
297346a83ebaSLeo (Hanghong) Ma {
297446a83ebaSLeo (Hanghong) Ma 	struct amdgpu_device *adev = data;
297546a83ebaSLeo (Hanghong) Ma 
297646a83ebaSLeo (Hanghong) Ma 	if (val == 1 || val == 0) {
297746a83ebaSLeo (Hanghong) Ma 		dc_dmub_trace_event_control(adev->dm.dc, val);
297846a83ebaSLeo (Hanghong) Ma 		adev->dm.dmcub_trace_event_en = (bool)val;
297946a83ebaSLeo (Hanghong) Ma 	} else
298046a83ebaSLeo (Hanghong) Ma 		return 0;
298146a83ebaSLeo (Hanghong) Ma 
298246a83ebaSLeo (Hanghong) Ma 	return 0;
298346a83ebaSLeo (Hanghong) Ma }
298446a83ebaSLeo (Hanghong) Ma 
298546a83ebaSLeo (Hanghong) Ma /*
298646a83ebaSLeo (Hanghong) Ma  * The interface doesn't need get function, so it will return the
298746a83ebaSLeo (Hanghong) Ma  * value of zero
298846a83ebaSLeo (Hanghong) Ma  * Usage: cat /sys/kernel/debug/dri/0/amdgpu_dm_dmcub_trace_event_en
298946a83ebaSLeo (Hanghong) Ma  */
dmcub_trace_event_state_get(void * data,u64 * val)299046a83ebaSLeo (Hanghong) Ma static int dmcub_trace_event_state_get(void *data, u64 *val)
299146a83ebaSLeo (Hanghong) Ma {
299246a83ebaSLeo (Hanghong) Ma 	struct amdgpu_device *adev = data;
299346a83ebaSLeo (Hanghong) Ma 
299446a83ebaSLeo (Hanghong) Ma 	*val = adev->dm.dmcub_trace_event_en;
299546a83ebaSLeo (Hanghong) Ma 	return 0;
299646a83ebaSLeo (Hanghong) Ma }
299746a83ebaSLeo (Hanghong) Ma 
299846a83ebaSLeo (Hanghong) Ma DEFINE_DEBUGFS_ATTRIBUTE(dmcub_trace_event_state_fops, dmcub_trace_event_state_get,
299946a83ebaSLeo (Hanghong) Ma 			 dmcub_trace_event_state_set, "%llu\n");
3000e0d08a40SRoman Li 
3001e0d08a40SRoman Li DEFINE_DEBUGFS_ATTRIBUTE(psr_fops, psr_get, NULL, "%llu\n");
300222f1482aSShirish S DEFINE_DEBUGFS_ATTRIBUTE(psr_residency_fops, psr_read_residency, NULL,
300322f1482aSShirish S 			 "%llu\n");
3004e0d08a40SRoman Li 
3005ee83c930SHersen Wu DEFINE_DEBUGFS_ATTRIBUTE(allow_edp_hotplug_detection_fops,
3006ee83c930SHersen Wu 			allow_edp_hotplug_detection_get,
3007ee83c930SHersen Wu 			allow_edp_hotplug_detection_set, "%llu\n");
3008ee83c930SHersen Wu 
3009118b4627SMikita Lipski DEFINE_SHOW_ATTRIBUTE(current_backlight);
3010118b4627SMikita Lipski DEFINE_SHOW_ATTRIBUTE(target_backlight);
3011118b4627SMikita Lipski 
3012c69eb740SStylon Wang static const struct {
3013c69eb740SStylon Wang 	char *name;
3014c69eb740SStylon Wang 	const struct file_operations *fops;
3015c69eb740SStylon Wang } connector_debugfs_entries[] = {
3016c69eb740SStylon Wang 		{"force_yuv420_output", &force_yuv420_output_fops},
3017b7cc1312SStylon Wang 		{"trigger_hotplug", &trigger_hotplug_debugfs_fops},
3018b7cc1312SStylon Wang 		{"internal_display", &internal_display_fops}
3019c69eb740SStylon Wang };
3020c69eb740SStylon Wang 
302170487a99SWayne Lin /*
302270487a99SWayne Lin  * Returns supported customized link rates by this eDP panel.
302370487a99SWayne Lin  * Example usage: cat /sys/kernel/debug/dri/0/eDP-x/ilr_setting
302470487a99SWayne Lin  */
edp_ilr_show(struct seq_file * m,void * unused)302570487a99SWayne Lin static int edp_ilr_show(struct seq_file *m, void *unused)
302670487a99SWayne Lin {
302770487a99SWayne Lin 	struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(m->private);
302870487a99SWayne Lin 	struct dc_link *link = aconnector->dc_link;
302970487a99SWayne Lin 	uint8_t supported_link_rates[16];
303070487a99SWayne Lin 	uint32_t link_rate_in_khz;
303170487a99SWayne Lin 	uint32_t entry = 0;
303270487a99SWayne Lin 	uint8_t dpcd_rev;
303370487a99SWayne Lin 
303470487a99SWayne Lin 	memset(supported_link_rates, 0, sizeof(supported_link_rates));
303570487a99SWayne Lin 	dm_helpers_dp_read_dpcd(link->ctx, link, DP_SUPPORTED_LINK_RATES,
303670487a99SWayne Lin 		supported_link_rates, sizeof(supported_link_rates));
303770487a99SWayne Lin 
303870487a99SWayne Lin 	dpcd_rev = link->dpcd_caps.dpcd_rev.raw;
303970487a99SWayne Lin 
304070487a99SWayne Lin 	if (dpcd_rev >= DP_DPCD_REV_13 &&
304170487a99SWayne Lin 		(supported_link_rates[entry+1] != 0 || supported_link_rates[entry] != 0)) {
304270487a99SWayne Lin 
304370487a99SWayne Lin 		for (entry = 0; entry < 16; entry += 2) {
304470487a99SWayne Lin 			link_rate_in_khz = (supported_link_rates[entry+1] * 0x100 +
304570487a99SWayne Lin 										supported_link_rates[entry]) * 200;
304670487a99SWayne Lin 			seq_printf(m, "[%d] %d kHz\n", entry/2, link_rate_in_khz);
304770487a99SWayne Lin 		}
304870487a99SWayne Lin 	} else {
304988c4d4e9SSrinivasan Shanmugam 		seq_puts(m, "ILR is not supported by this eDP panel.\n");
305070487a99SWayne Lin 	}
305170487a99SWayne Lin 
305270487a99SWayne Lin 	return 0;
305370487a99SWayne Lin }
305470487a99SWayne Lin 
305570487a99SWayne Lin /*
305670487a99SWayne Lin  * Set supported customized link rate to eDP panel.
305770487a99SWayne Lin  *
305870487a99SWayne Lin  * echo <lane_count>  <link_rate option> > ilr_setting
305970487a99SWayne Lin  *
306070487a99SWayne Lin  * for example, supported ILR : [0] 1620000 kHz [1] 2160000 kHz [2] 2430000 kHz ...
306170487a99SWayne Lin  * echo 4 1 > /sys/kernel/debug/dri/0/eDP-x/ilr_setting
306270487a99SWayne Lin  * to set 4 lanes and 2.16 GHz
306370487a99SWayne Lin  */
edp_ilr_write(struct file * f,const char __user * buf,size_t size,loff_t * pos)306470487a99SWayne Lin static ssize_t edp_ilr_write(struct file *f, const char __user *buf,
306570487a99SWayne Lin 				 size_t size, loff_t *pos)
306670487a99SWayne Lin {
306770487a99SWayne Lin 	struct amdgpu_dm_connector *connector = file_inode(f)->i_private;
306870487a99SWayne Lin 	struct dc_link *link = connector->dc_link;
306970487a99SWayne Lin 	struct amdgpu_device *adev = drm_to_adev(connector->base.dev);
307070487a99SWayne Lin 	struct dc *dc = (struct dc *)link->dc;
307170487a99SWayne Lin 	struct dc_link_settings prefer_link_settings;
307270487a99SWayne Lin 	char *wr_buf = NULL;
307370487a99SWayne Lin 	const uint32_t wr_buf_size = 40;
307470487a99SWayne Lin 	/* 0: lane_count; 1: link_rate */
307570487a99SWayne Lin 	int max_param_num = 2;
307670487a99SWayne Lin 	uint8_t param_nums = 0;
307770487a99SWayne Lin 	long param[2];
307870487a99SWayne Lin 	bool valid_input = true;
307970487a99SWayne Lin 
308070487a99SWayne Lin 	if (size == 0)
308170487a99SWayne Lin 		return -EINVAL;
308270487a99SWayne Lin 
308370487a99SWayne Lin 	wr_buf = kcalloc(wr_buf_size, sizeof(char), GFP_KERNEL);
308470487a99SWayne Lin 	if (!wr_buf)
308570487a99SWayne Lin 		return -ENOMEM;
308670487a99SWayne Lin 
308770487a99SWayne Lin 	if (parse_write_buffer_into_params(wr_buf, wr_buf_size,
308870487a99SWayne Lin 					   (long *)param, buf,
308970487a99SWayne Lin 					   max_param_num,
309070487a99SWayne Lin 					   &param_nums)) {
309170487a99SWayne Lin 		kfree(wr_buf);
309270487a99SWayne Lin 		return -EINVAL;
309370487a99SWayne Lin 	}
309470487a99SWayne Lin 
309570487a99SWayne Lin 	if (param_nums <= 0) {
309670487a99SWayne Lin 		kfree(wr_buf);
309770487a99SWayne Lin 		return -EINVAL;
309870487a99SWayne Lin 	}
309970487a99SWayne Lin 
310070487a99SWayne Lin 	switch (param[0]) {
310170487a99SWayne Lin 	case LANE_COUNT_ONE:
310270487a99SWayne Lin 	case LANE_COUNT_TWO:
310370487a99SWayne Lin 	case LANE_COUNT_FOUR:
310470487a99SWayne Lin 		break;
310570487a99SWayne Lin 	default:
310670487a99SWayne Lin 		valid_input = false;
310770487a99SWayne Lin 		break;
310870487a99SWayne Lin 	}
310970487a99SWayne Lin 
311070487a99SWayne Lin 	if (param[1] >= link->dpcd_caps.edp_supported_link_rates_count)
311170487a99SWayne Lin 		valid_input = false;
311270487a99SWayne Lin 
311370487a99SWayne Lin 	if (!valid_input) {
311470487a99SWayne Lin 		kfree(wr_buf);
311570487a99SWayne Lin 		DRM_DEBUG_DRIVER("Invalid Input value. No HW will be programmed\n");
311670487a99SWayne Lin 		prefer_link_settings.use_link_rate_set = false;
3117a18112aeSWayne Lin 		mutex_lock(&adev->dm.dc_lock);
3118d52e77a3SWayne Lin 		dc_link_set_preferred_training_settings(dc, NULL, NULL, link, false);
3119a18112aeSWayne Lin 		mutex_unlock(&adev->dm.dc_lock);
312070487a99SWayne Lin 		return size;
312170487a99SWayne Lin 	}
312270487a99SWayne Lin 
312370487a99SWayne Lin 	/* save user force lane_count, link_rate to preferred settings
312470487a99SWayne Lin 	 * spread spectrum will not be changed
312570487a99SWayne Lin 	 */
312670487a99SWayne Lin 	prefer_link_settings.link_spread = link->cur_link_settings.link_spread;
312770487a99SWayne Lin 	prefer_link_settings.lane_count = param[0];
312870487a99SWayne Lin 	prefer_link_settings.use_link_rate_set = true;
312970487a99SWayne Lin 	prefer_link_settings.link_rate_set = param[1];
313070487a99SWayne Lin 	prefer_link_settings.link_rate = link->dpcd_caps.edp_supported_link_rates[param[1]];
313170487a99SWayne Lin 
313270487a99SWayne Lin 	mutex_lock(&adev->dm.dc_lock);
313370487a99SWayne Lin 	dc_link_set_preferred_training_settings(dc, &prefer_link_settings,
313470487a99SWayne Lin 						NULL, link, false);
313570487a99SWayne Lin 	mutex_unlock(&adev->dm.dc_lock);
313670487a99SWayne Lin 
313770487a99SWayne Lin 	kfree(wr_buf);
313870487a99SWayne Lin 	return size;
313970487a99SWayne Lin }
314070487a99SWayne Lin 
edp_ilr_open(struct inode * inode,struct file * file)314170487a99SWayne Lin static int edp_ilr_open(struct inode *inode, struct file *file)
314270487a99SWayne Lin {
314370487a99SWayne Lin 	return single_open(file, edp_ilr_show, inode->i_private);
314470487a99SWayne Lin }
314570487a99SWayne Lin 
314670487a99SWayne Lin static const struct file_operations edp_ilr_debugfs_fops = {
314770487a99SWayne Lin 	.owner = THIS_MODULE,
314870487a99SWayne Lin 	.open = edp_ilr_open,
314970487a99SWayne Lin 	.read = seq_read,
315070487a99SWayne Lin 	.llseek = seq_lseek,
315170487a99SWayne Lin 	.release = single_release,
315270487a99SWayne Lin 	.write = edp_ilr_write
315370487a99SWayne Lin };
315470487a99SWayne Lin 
connector_debugfs_init(struct amdgpu_dm_connector * connector)31554be8be78SGreg Kroah-Hartman void connector_debugfs_init(struct amdgpu_dm_connector *connector)
3156dc38fd9dSDavid Francis {
3157dc38fd9dSDavid Francis 	int i;
31584be8be78SGreg Kroah-Hartman 	struct dentry *dir = connector->base.debugfs_entry;
3159dc38fd9dSDavid Francis 
31608c6259beShersen wu 	if (connector->base.connector_type == DRM_MODE_CONNECTOR_DisplayPort ||
31618c6259beShersen wu 	    connector->base.connector_type == DRM_MODE_CONNECTOR_eDP) {
3162dc38fd9dSDavid Francis 		for (i = 0; i < ARRAY_SIZE(dp_debugfs_entries); i++) {
31634be8be78SGreg Kroah-Hartman 			debugfs_create_file(dp_debugfs_entries[i].name,
31644be8be78SGreg Kroah-Hartman 					    0644, dir, connector,
3165dc38fd9dSDavid Francis 					    dp_debugfs_entries[i].fops);
3166dc38fd9dSDavid Francis 		}
3167dc38fd9dSDavid Francis 	}
3168118b4627SMikita Lipski 	if (connector->base.connector_type == DRM_MODE_CONNECTOR_eDP) {
31697238b42eSMikita Lipski 		debugfs_create_file_unsafe("psr_capability", 0444, dir, connector, &psr_capability_fops);
3170e0d08a40SRoman Li 		debugfs_create_file_unsafe("psr_state", 0444, dir, connector, &psr_fops);
317122f1482aSShirish S 		debugfs_create_file_unsafe("psr_residency", 0444, dir,
317222f1482aSShirish S 					   connector, &psr_residency_fops);
3173118b4627SMikita Lipski 		debugfs_create_file("amdgpu_current_backlight_pwm", 0444, dir, connector,
3174118b4627SMikita Lipski 				    &current_backlight_fops);
3175118b4627SMikita Lipski 		debugfs_create_file("amdgpu_target_backlight_pwm", 0444, dir, connector,
3176118b4627SMikita Lipski 				    &target_backlight_fops);
317770487a99SWayne Lin 		debugfs_create_file("ilr_setting", 0644, dir, connector,
317870487a99SWayne Lin 					&edp_ilr_debugfs_fops);
3179ee83c930SHersen Wu 		debugfs_create_file("allow_edp_hotplug_detection", 0644, dir, connector,
3180ee83c930SHersen Wu 					&allow_edp_hotplug_detection_fops);
3181118b4627SMikita Lipski 	}
3182d4252eeeSStylon Wang 
3183c69eb740SStylon Wang 	for (i = 0; i < ARRAY_SIZE(connector_debugfs_entries); i++) {
3184c69eb740SStylon Wang 		debugfs_create_file(connector_debugfs_entries[i].name,
3185c69eb740SStylon Wang 				    0644, dir, connector,
3186c69eb740SStylon Wang 				    connector_debugfs_entries[i].fops);
3187c69eb740SStylon Wang 	}
318802a342e3SStylon Wang 
31895f869379SBhawanpreet Lakha 	if (connector->base.connector_type == DRM_MODE_CONNECTOR_HDMIA) {
31905f869379SBhawanpreet Lakha 		for (i = 0; i < ARRAY_SIZE(hdmi_debugfs_entries); i++) {
31915f869379SBhawanpreet Lakha 			debugfs_create_file(hdmi_debugfs_entries[i].name,
31925f869379SBhawanpreet Lakha 					    0644, dir, connector,
31935f869379SBhawanpreet Lakha 					    hdmi_debugfs_entries[i].fops);
31945f869379SBhawanpreet Lakha 		}
31955f869379SBhawanpreet Lakha 	}
3196dc38fd9dSDavid Francis }
3197dc38fd9dSDavid Francis 
319886bc2219SWayne Lin #ifdef CONFIG_DRM_AMD_SECURE_DISPLAY
319986bc2219SWayne Lin /*
320086bc2219SWayne Lin  * Set crc window coordinate x start
320186bc2219SWayne Lin  */
crc_win_x_start_set(void * data,u64 val)320286bc2219SWayne Lin static int crc_win_x_start_set(void *data, u64 val)
320386bc2219SWayne Lin {
320486bc2219SWayne Lin 	struct drm_crtc *crtc = data;
320586bc2219SWayne Lin 	struct drm_device *drm_dev = crtc->dev;
320686bc2219SWayne Lin 	struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc);
320786bc2219SWayne Lin 
320886bc2219SWayne Lin 	spin_lock_irq(&drm_dev->event_lock);
320962fa035bSAlan Liu 	acrtc->dm_irq_params.window_param.x_start = (uint16_t) val;
3210c0459bddSAlan Liu 	acrtc->dm_irq_params.window_param.update_win = false;
321186bc2219SWayne Lin 	spin_unlock_irq(&drm_dev->event_lock);
321286bc2219SWayne Lin 
321386bc2219SWayne Lin 	return 0;
321486bc2219SWayne Lin }
321586bc2219SWayne Lin 
321686bc2219SWayne Lin /*
321786bc2219SWayne Lin  * Get crc window coordinate x start
321886bc2219SWayne Lin  */
crc_win_x_start_get(void * data,u64 * val)321986bc2219SWayne Lin static int crc_win_x_start_get(void *data, u64 *val)
322086bc2219SWayne Lin {
322186bc2219SWayne Lin 	struct drm_crtc *crtc = data;
322286bc2219SWayne Lin 	struct drm_device *drm_dev = crtc->dev;
322386bc2219SWayne Lin 	struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc);
322486bc2219SWayne Lin 
322586bc2219SWayne Lin 	spin_lock_irq(&drm_dev->event_lock);
322662fa035bSAlan Liu 	*val = acrtc->dm_irq_params.window_param.x_start;
322786bc2219SWayne Lin 	spin_unlock_irq(&drm_dev->event_lock);
322886bc2219SWayne Lin 
322986bc2219SWayne Lin 	return 0;
323086bc2219SWayne Lin }
323186bc2219SWayne Lin 
323286bc2219SWayne Lin DEFINE_DEBUGFS_ATTRIBUTE(crc_win_x_start_fops, crc_win_x_start_get,
323386bc2219SWayne Lin 			 crc_win_x_start_set, "%llu\n");
323486bc2219SWayne Lin 
323586bc2219SWayne Lin 
323686bc2219SWayne Lin /*
323786bc2219SWayne Lin  * Set crc window coordinate y start
323886bc2219SWayne Lin  */
crc_win_y_start_set(void * data,u64 val)323986bc2219SWayne Lin static int crc_win_y_start_set(void *data, u64 val)
324086bc2219SWayne Lin {
324186bc2219SWayne Lin 	struct drm_crtc *crtc = data;
324286bc2219SWayne Lin 	struct drm_device *drm_dev = crtc->dev;
324386bc2219SWayne Lin 	struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc);
324486bc2219SWayne Lin 
324586bc2219SWayne Lin 	spin_lock_irq(&drm_dev->event_lock);
324662fa035bSAlan Liu 	acrtc->dm_irq_params.window_param.y_start = (uint16_t) val;
3247c0459bddSAlan Liu 	acrtc->dm_irq_params.window_param.update_win = false;
324886bc2219SWayne Lin 	spin_unlock_irq(&drm_dev->event_lock);
324986bc2219SWayne Lin 
325086bc2219SWayne Lin 	return 0;
325186bc2219SWayne Lin }
325286bc2219SWayne Lin 
325386bc2219SWayne Lin /*
325486bc2219SWayne Lin  * Get crc window coordinate y start
325586bc2219SWayne Lin  */
crc_win_y_start_get(void * data,u64 * val)325686bc2219SWayne Lin static int crc_win_y_start_get(void *data, u64 *val)
325786bc2219SWayne Lin {
325886bc2219SWayne Lin 	struct drm_crtc *crtc = data;
325986bc2219SWayne Lin 	struct drm_device *drm_dev = crtc->dev;
326086bc2219SWayne Lin 	struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc);
326186bc2219SWayne Lin 
326286bc2219SWayne Lin 	spin_lock_irq(&drm_dev->event_lock);
326362fa035bSAlan Liu 	*val = acrtc->dm_irq_params.window_param.y_start;
326486bc2219SWayne Lin 	spin_unlock_irq(&drm_dev->event_lock);
326586bc2219SWayne Lin 
326686bc2219SWayne Lin 	return 0;
326786bc2219SWayne Lin }
326886bc2219SWayne Lin 
326986bc2219SWayne Lin DEFINE_DEBUGFS_ATTRIBUTE(crc_win_y_start_fops, crc_win_y_start_get,
327086bc2219SWayne Lin 			 crc_win_y_start_set, "%llu\n");
327186bc2219SWayne Lin 
327286bc2219SWayne Lin /*
327386bc2219SWayne Lin  * Set crc window coordinate x end
327486bc2219SWayne Lin  */
crc_win_x_end_set(void * data,u64 val)327586bc2219SWayne Lin static int crc_win_x_end_set(void *data, u64 val)
327686bc2219SWayne Lin {
327786bc2219SWayne Lin 	struct drm_crtc *crtc = data;
327886bc2219SWayne Lin 	struct drm_device *drm_dev = crtc->dev;
327986bc2219SWayne Lin 	struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc);
328086bc2219SWayne Lin 
328186bc2219SWayne Lin 	spin_lock_irq(&drm_dev->event_lock);
328262fa035bSAlan Liu 	acrtc->dm_irq_params.window_param.x_end = (uint16_t) val;
3283c0459bddSAlan Liu 	acrtc->dm_irq_params.window_param.update_win = false;
328486bc2219SWayne Lin 	spin_unlock_irq(&drm_dev->event_lock);
328586bc2219SWayne Lin 
328686bc2219SWayne Lin 	return 0;
328786bc2219SWayne Lin }
328886bc2219SWayne Lin 
328986bc2219SWayne Lin /*
329086bc2219SWayne Lin  * Get crc window coordinate x end
329186bc2219SWayne Lin  */
crc_win_x_end_get(void * data,u64 * val)329286bc2219SWayne Lin static int crc_win_x_end_get(void *data, u64 *val)
329386bc2219SWayne Lin {
329486bc2219SWayne Lin 	struct drm_crtc *crtc = data;
329586bc2219SWayne Lin 	struct drm_device *drm_dev = crtc->dev;
329686bc2219SWayne Lin 	struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc);
329786bc2219SWayne Lin 
329886bc2219SWayne Lin 	spin_lock_irq(&drm_dev->event_lock);
329962fa035bSAlan Liu 	*val = acrtc->dm_irq_params.window_param.x_end;
330086bc2219SWayne Lin 	spin_unlock_irq(&drm_dev->event_lock);
330186bc2219SWayne Lin 
330286bc2219SWayne Lin 	return 0;
330386bc2219SWayne Lin }
330486bc2219SWayne Lin 
330586bc2219SWayne Lin DEFINE_DEBUGFS_ATTRIBUTE(crc_win_x_end_fops, crc_win_x_end_get,
330686bc2219SWayne Lin 			 crc_win_x_end_set, "%llu\n");
330786bc2219SWayne Lin 
330886bc2219SWayne Lin /*
330986bc2219SWayne Lin  * Set crc window coordinate y end
331086bc2219SWayne Lin  */
crc_win_y_end_set(void * data,u64 val)331186bc2219SWayne Lin static int crc_win_y_end_set(void *data, u64 val)
331286bc2219SWayne Lin {
331386bc2219SWayne Lin 	struct drm_crtc *crtc = data;
331486bc2219SWayne Lin 	struct drm_device *drm_dev = crtc->dev;
331586bc2219SWayne Lin 	struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc);
331686bc2219SWayne Lin 
331786bc2219SWayne Lin 	spin_lock_irq(&drm_dev->event_lock);
331862fa035bSAlan Liu 	acrtc->dm_irq_params.window_param.y_end = (uint16_t) val;
3319c0459bddSAlan Liu 	acrtc->dm_irq_params.window_param.update_win = false;
332086bc2219SWayne Lin 	spin_unlock_irq(&drm_dev->event_lock);
332186bc2219SWayne Lin 
332286bc2219SWayne Lin 	return 0;
332386bc2219SWayne Lin }
332486bc2219SWayne Lin 
332586bc2219SWayne Lin /*
332686bc2219SWayne Lin  * Get crc window coordinate y end
332786bc2219SWayne Lin  */
crc_win_y_end_get(void * data,u64 * val)332886bc2219SWayne Lin static int crc_win_y_end_get(void *data, u64 *val)
332986bc2219SWayne Lin {
333086bc2219SWayne Lin 	struct drm_crtc *crtc = data;
333186bc2219SWayne Lin 	struct drm_device *drm_dev = crtc->dev;
333286bc2219SWayne Lin 	struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc);
333386bc2219SWayne Lin 
333486bc2219SWayne Lin 	spin_lock_irq(&drm_dev->event_lock);
333562fa035bSAlan Liu 	*val = acrtc->dm_irq_params.window_param.y_end;
333686bc2219SWayne Lin 	spin_unlock_irq(&drm_dev->event_lock);
333786bc2219SWayne Lin 
333886bc2219SWayne Lin 	return 0;
333986bc2219SWayne Lin }
334086bc2219SWayne Lin 
334186bc2219SWayne Lin DEFINE_DEBUGFS_ATTRIBUTE(crc_win_y_end_fops, crc_win_y_end_get,
334286bc2219SWayne Lin 			 crc_win_y_end_set, "%llu\n");
334386bc2219SWayne Lin /*
334486bc2219SWayne Lin  * Trigger to commit crc window
334586bc2219SWayne Lin  */
crc_win_update_set(void * data,u64 val)334686bc2219SWayne Lin static int crc_win_update_set(void *data, u64 val)
334786bc2219SWayne Lin {
33481b11ff76SAlan Liu 	struct drm_crtc *crtc = data;
33491b11ff76SAlan Liu 	struct amdgpu_crtc *acrtc;
33501b11ff76SAlan Liu 	struct amdgpu_device *adev = drm_to_adev(crtc->dev);
33514bef85d4SWayne Lin 
335286bc2219SWayne Lin 	if (val) {
33531b11ff76SAlan Liu 		acrtc = to_amdgpu_crtc(crtc);
3354c0459bddSAlan Liu 		mutex_lock(&adev->dm.dc_lock);
3355c0459bddSAlan Liu 		/* PSR may write to OTG CRC window control register,
3356c0459bddSAlan Liu 		 * so close it before starting secure_display.
3357c0459bddSAlan Liu 		 */
33581b11ff76SAlan Liu 		amdgpu_dm_psr_disable(acrtc->dm_irq_params.stream);
3359c0459bddSAlan Liu 
33609a65df19SWayne Lin 		spin_lock_irq(&adev_to_drm(adev)->event_lock);
33619a65df19SWayne Lin 
33621b11ff76SAlan Liu 		acrtc->dm_irq_params.window_param.activated = true;
33631b11ff76SAlan Liu 		acrtc->dm_irq_params.window_param.update_win = true;
33641b11ff76SAlan Liu 		acrtc->dm_irq_params.window_param.skip_frame_cnt = 0;
33659a65df19SWayne Lin 
3366cd95ef00SWayne Lin 		spin_unlock_irq(&adev_to_drm(adev)->event_lock);
3367c0459bddSAlan Liu 		mutex_unlock(&adev->dm.dc_lock);
336886bc2219SWayne Lin 	}
336986bc2219SWayne Lin 
337086bc2219SWayne Lin 	return 0;
337186bc2219SWayne Lin }
337286bc2219SWayne Lin 
337386bc2219SWayne Lin /*
337486bc2219SWayne Lin  * Get crc window update flag
337586bc2219SWayne Lin  */
crc_win_update_get(void * data,u64 * val)337686bc2219SWayne Lin static int crc_win_update_get(void *data, u64 *val)
337786bc2219SWayne Lin {
337886bc2219SWayne Lin 	*val = 0;
337986bc2219SWayne Lin 	return 0;
338086bc2219SWayne Lin }
338186bc2219SWayne Lin 
338286bc2219SWayne Lin DEFINE_DEBUGFS_ATTRIBUTE(crc_win_update_fops, crc_win_update_get,
338386bc2219SWayne Lin 			 crc_win_update_set, "%llu\n");
33844cd79f61SBhanuprakash Modem #endif
crtc_debugfs_init(struct drm_crtc * crtc)338586bc2219SWayne Lin void crtc_debugfs_init(struct drm_crtc *crtc)
338686bc2219SWayne Lin {
33874cd79f61SBhanuprakash Modem #ifdef CONFIG_DRM_AMD_SECURE_DISPLAY
338886bc2219SWayne Lin 	struct dentry *dir = debugfs_lookup("crc", crtc->debugfs_entry);
338986bc2219SWayne Lin 
339086bc2219SWayne Lin 	if (!dir)
339186bc2219SWayne Lin 		return;
339286bc2219SWayne Lin 
339386bc2219SWayne Lin 	debugfs_create_file_unsafe("crc_win_x_start", 0644, dir, crtc,
339486bc2219SWayne Lin 				   &crc_win_x_start_fops);
339586bc2219SWayne Lin 	debugfs_create_file_unsafe("crc_win_y_start", 0644, dir, crtc,
339686bc2219SWayne Lin 				   &crc_win_y_start_fops);
339786bc2219SWayne Lin 	debugfs_create_file_unsafe("crc_win_x_end", 0644, dir, crtc,
339886bc2219SWayne Lin 				   &crc_win_x_end_fops);
339986bc2219SWayne Lin 	debugfs_create_file_unsafe("crc_win_y_end", 0644, dir, crtc,
340086bc2219SWayne Lin 				   &crc_win_y_end_fops);
340186bc2219SWayne Lin 	debugfs_create_file_unsafe("crc_win_update", 0644, dir, crtc,
340286bc2219SWayne Lin 				   &crc_win_update_fops);
3403cbfac7faSGreg Kroah-Hartman 	dput(dir);
340486bc2219SWayne Lin #endif
34054cd79f61SBhanuprakash Modem 	debugfs_create_file("amdgpu_current_bpc", 0644, crtc->debugfs_entry,
34064cd79f61SBhanuprakash Modem 			    crtc, &amdgpu_current_bpc_fops);
3407fd45b654SHarry Wentland 	debugfs_create_file("amdgpu_current_colorspace", 0644, crtc->debugfs_entry,
3408fd45b654SHarry Wentland 			    crtc, &amdgpu_current_colorspace_fops);
34094cd79f61SBhanuprakash Modem }
34104cd79f61SBhanuprakash Modem 
341146659a83SNicholas Kazlauskas /*
341246659a83SNicholas Kazlauskas  * Writes DTN log state to the user supplied buffer.
341346659a83SNicholas Kazlauskas  * Example usage: cat /sys/kernel/debug/dri/0/amdgpu_dm_dtn_log
341446659a83SNicholas Kazlauskas  */
dtn_log_read(struct file * f,char __user * buf,size_t size,loff_t * pos)3415e498eb71SNicholas Kazlauskas static ssize_t dtn_log_read(
3416e498eb71SNicholas Kazlauskas 	struct file *f,
3417e498eb71SNicholas Kazlauskas 	char __user *buf,
3418e498eb71SNicholas Kazlauskas 	size_t size,
3419e498eb71SNicholas Kazlauskas 	loff_t *pos)
3420e498eb71SNicholas Kazlauskas {
342146659a83SNicholas Kazlauskas 	struct amdgpu_device *adev = file_inode(f)->i_private;
342246659a83SNicholas Kazlauskas 	struct dc *dc = adev->dm.dc;
342346659a83SNicholas Kazlauskas 	struct dc_log_buffer_ctx log_ctx = { 0 };
342446659a83SNicholas Kazlauskas 	ssize_t result = 0;
342546659a83SNicholas Kazlauskas 
342646659a83SNicholas Kazlauskas 	if (!buf || !size)
342746659a83SNicholas Kazlauskas 		return -EINVAL;
342846659a83SNicholas Kazlauskas 
342946659a83SNicholas Kazlauskas 	if (!dc->hwss.log_hw_state)
3430e498eb71SNicholas Kazlauskas 		return 0;
343146659a83SNicholas Kazlauskas 
343246659a83SNicholas Kazlauskas 	dc->hwss.log_hw_state(dc, &log_ctx);
343346659a83SNicholas Kazlauskas 
343446659a83SNicholas Kazlauskas 	if (*pos < log_ctx.pos) {
343546659a83SNicholas Kazlauskas 		size_t to_copy = log_ctx.pos - *pos;
343646659a83SNicholas Kazlauskas 
343746659a83SNicholas Kazlauskas 		to_copy = min(to_copy, size);
343846659a83SNicholas Kazlauskas 
343946659a83SNicholas Kazlauskas 		if (!copy_to_user(buf, log_ctx.buf + *pos, to_copy)) {
344046659a83SNicholas Kazlauskas 			*pos += to_copy;
344146659a83SNicholas Kazlauskas 			result = to_copy;
344246659a83SNicholas Kazlauskas 		}
3443e498eb71SNicholas Kazlauskas 	}
3444e498eb71SNicholas Kazlauskas 
344546659a83SNicholas Kazlauskas 	kfree(log_ctx.buf);
344646659a83SNicholas Kazlauskas 
344746659a83SNicholas Kazlauskas 	return result;
344846659a83SNicholas Kazlauskas }
344946659a83SNicholas Kazlauskas 
345046659a83SNicholas Kazlauskas /*
345146659a83SNicholas Kazlauskas  * Writes DTN log state to dmesg when triggered via a write.
345246659a83SNicholas Kazlauskas  * Example usage: echo 1 > /sys/kernel/debug/dri/0/amdgpu_dm_dtn_log
345346659a83SNicholas Kazlauskas  */
dtn_log_write(struct file * f,const char __user * buf,size_t size,loff_t * pos)3454e498eb71SNicholas Kazlauskas static ssize_t dtn_log_write(
3455e498eb71SNicholas Kazlauskas 	struct file *f,
3456e498eb71SNicholas Kazlauskas 	const char __user *buf,
3457e498eb71SNicholas Kazlauskas 	size_t size,
3458e498eb71SNicholas Kazlauskas 	loff_t *pos)
3459e498eb71SNicholas Kazlauskas {
3460e498eb71SNicholas Kazlauskas 	struct amdgpu_device *adev = file_inode(f)->i_private;
3461e498eb71SNicholas Kazlauskas 	struct dc *dc = adev->dm.dc;
3462e498eb71SNicholas Kazlauskas 
3463e498eb71SNicholas Kazlauskas 	/* Write triggers log output via dmesg. */
3464e498eb71SNicholas Kazlauskas 	if (size == 0)
3465e498eb71SNicholas Kazlauskas 		return 0;
3466e498eb71SNicholas Kazlauskas 
3467e498eb71SNicholas Kazlauskas 	if (dc->hwss.log_hw_state)
346846659a83SNicholas Kazlauskas 		dc->hwss.log_hw_state(dc, NULL);
3469e498eb71SNicholas Kazlauskas 
3470e498eb71SNicholas Kazlauskas 	return size;
3471e498eb71SNicholas Kazlauskas }
3472e498eb71SNicholas Kazlauskas 
mst_topo_show(struct seq_file * m,void * unused)3473afd3a359SNirmoy Das static int mst_topo_show(struct seq_file *m, void *unused)
34740ec7d06bSDavid Francis {
3475afd3a359SNirmoy Das 	struct amdgpu_device *adev = (struct amdgpu_device *)m->private;
3476afd3a359SNirmoy Das 	struct drm_device *dev = adev_to_drm(adev);
34770ec7d06bSDavid Francis 	struct drm_connector *connector;
34780ec7d06bSDavid Francis 	struct drm_connector_list_iter conn_iter;
34790ec7d06bSDavid Francis 	struct amdgpu_dm_connector *aconnector;
34800ec7d06bSDavid Francis 
34810ec7d06bSDavid Francis 	drm_connector_list_iter_begin(dev, &conn_iter);
34820ec7d06bSDavid Francis 	drm_for_each_connector_iter(connector, &conn_iter) {
34830ec7d06bSDavid Francis 		if (connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort)
34840ec7d06bSDavid Francis 			continue;
34850ec7d06bSDavid Francis 
34860ec7d06bSDavid Francis 		aconnector = to_amdgpu_dm_connector(connector);
34870ec7d06bSDavid Francis 
3488b9db4123SEryk Brol 		/* Ensure we're only dumping the topology of a root mst node */
3489b9db4123SEryk Brol 		if (!aconnector->mst_mgr.mst_state)
3490b9db4123SEryk Brol 			continue;
3491b9db4123SEryk Brol 
34920ec7d06bSDavid Francis 		seq_printf(m, "\nMST topology for connector %d\n", aconnector->connector_id);
34930ec7d06bSDavid Francis 		drm_dp_mst_dump_topology(m, &aconnector->mst_mgr);
34940ec7d06bSDavid Francis 	}
34950ec7d06bSDavid Francis 	drm_connector_list_iter_end(&conn_iter);
34960ec7d06bSDavid Francis 
34970ec7d06bSDavid Francis 	return 0;
34980ec7d06bSDavid Francis }
34990ec7d06bSDavid Francis 
3500b62f95d1SNicholas Kazlauskas /*
350141efcd38SMikita Lipski  * Sets trigger hpd for MST topologies.
350241efcd38SMikita Lipski  * All connected connectors will be rediscovered and re started as needed if val of 1 is sent.
350341efcd38SMikita Lipski  * All topologies will be disconnected if val of 0 is set .
350441efcd38SMikita Lipski  * Usage to enable topologies: echo 1 > /sys/kernel/debug/dri/0/amdgpu_dm_trigger_hpd_mst
350541efcd38SMikita Lipski  * Usage to disable topologies: echo 0 > /sys/kernel/debug/dri/0/amdgpu_dm_trigger_hpd_mst
350641efcd38SMikita Lipski  */
trigger_hpd_mst_set(void * data,u64 val)350741efcd38SMikita Lipski static int trigger_hpd_mst_set(void *data, u64 val)
350841efcd38SMikita Lipski {
350941efcd38SMikita Lipski 	struct amdgpu_device *adev = data;
351041efcd38SMikita Lipski 	struct drm_device *dev = adev_to_drm(adev);
351141efcd38SMikita Lipski 	struct drm_connector_list_iter iter;
351241efcd38SMikita Lipski 	struct amdgpu_dm_connector *aconnector;
351341efcd38SMikita Lipski 	struct drm_connector *connector;
351441efcd38SMikita Lipski 	struct dc_link *link = NULL;
351541efcd38SMikita Lipski 
351641efcd38SMikita Lipski 	if (val == 1) {
351741efcd38SMikita Lipski 		drm_connector_list_iter_begin(dev, &iter);
351841efcd38SMikita Lipski 		drm_for_each_connector_iter(connector, &iter) {
351941efcd38SMikita Lipski 			aconnector = to_amdgpu_dm_connector(connector);
352041efcd38SMikita Lipski 			if (aconnector->dc_link->type == dc_connection_mst_branch &&
352141efcd38SMikita Lipski 			    aconnector->mst_mgr.aux) {
352215c735e7SWayne Lin 				mutex_lock(&adev->dm.dc_lock);
352341efcd38SMikita Lipski 				dc_link_detect(aconnector->dc_link, DETECT_REASON_HPD);
352415c735e7SWayne Lin 				mutex_unlock(&adev->dm.dc_lock);
352515c735e7SWayne Lin 
352641efcd38SMikita Lipski 				drm_dp_mst_topology_mgr_set_mst(&aconnector->mst_mgr, true);
352741efcd38SMikita Lipski 			}
352841efcd38SMikita Lipski 		}
352941efcd38SMikita Lipski 	} else if (val == 0) {
353041efcd38SMikita Lipski 		drm_connector_list_iter_begin(dev, &iter);
353141efcd38SMikita Lipski 		drm_for_each_connector_iter(connector, &iter) {
353241efcd38SMikita Lipski 			aconnector = to_amdgpu_dm_connector(connector);
353341efcd38SMikita Lipski 			if (!aconnector->dc_link)
353441efcd38SMikita Lipski 				continue;
353541efcd38SMikita Lipski 
3536f0127cb1SWayne Lin 			if (!aconnector->mst_root)
353741efcd38SMikita Lipski 				continue;
353841efcd38SMikita Lipski 
353941efcd38SMikita Lipski 			link = aconnector->dc_link;
354094dfeaa4SWenjing Liu 			dc_link_dp_receiver_power_ctrl(link, false);
3541f0127cb1SWayne Lin 			drm_dp_mst_topology_mgr_set_mst(&aconnector->mst_root->mst_mgr, false);
354241efcd38SMikita Lipski 			link->mst_stream_alloc_table.stream_count = 0;
354341efcd38SMikita Lipski 			memset(link->mst_stream_alloc_table.stream_allocations, 0,
354441efcd38SMikita Lipski 					sizeof(link->mst_stream_alloc_table.stream_allocations));
354541efcd38SMikita Lipski 		}
354641efcd38SMikita Lipski 	} else {
354741efcd38SMikita Lipski 		return 0;
354841efcd38SMikita Lipski 	}
354941efcd38SMikita Lipski 	drm_kms_helper_hotplug_event(dev);
355041efcd38SMikita Lipski 
355141efcd38SMikita Lipski 	return 0;
355241efcd38SMikita Lipski }
355341efcd38SMikita Lipski 
355441efcd38SMikita Lipski /*
355541efcd38SMikita Lipski  * The interface doesn't need get function, so it will return the
355641efcd38SMikita Lipski  * value of zero
355741efcd38SMikita Lipski  * Usage: cat /sys/kernel/debug/dri/0/amdgpu_dm_trigger_hpd_mst
355841efcd38SMikita Lipski  */
trigger_hpd_mst_get(void * data,u64 * val)355941efcd38SMikita Lipski static int trigger_hpd_mst_get(void *data, u64 *val)
356041efcd38SMikita Lipski {
356141efcd38SMikita Lipski 	*val = 0;
356241efcd38SMikita Lipski 	return 0;
356341efcd38SMikita Lipski }
356441efcd38SMikita Lipski 
356541efcd38SMikita Lipski DEFINE_DEBUGFS_ATTRIBUTE(trigger_hpd_mst_ops, trigger_hpd_mst_get,
356641efcd38SMikita Lipski 			 trigger_hpd_mst_set, "%llu\n");
356741efcd38SMikita Lipski 
356841efcd38SMikita Lipski 
356941efcd38SMikita Lipski /*
357041efcd38SMikita Lipski  * Sets the force_timing_sync debug option from the given string.
35713d4e52d0SVictor Lu  * All connected displays will be force synchronized immediately.
35723d4e52d0SVictor Lu  * Usage: echo 1 > /sys/kernel/debug/dri/0/amdgpu_dm_force_timing_sync
35733d4e52d0SVictor Lu  */
force_timing_sync_set(void * data,u64 val)35743d4e52d0SVictor Lu static int force_timing_sync_set(void *data, u64 val)
35753d4e52d0SVictor Lu {
35763d4e52d0SVictor Lu 	struct amdgpu_device *adev = data;
35773d4e52d0SVictor Lu 
35783d4e52d0SVictor Lu 	adev->dm.force_timing_sync = (bool)val;
35793d4e52d0SVictor Lu 
35804a580877SLuben Tuikov 	amdgpu_dm_trigger_timing_sync(adev_to_drm(adev));
35813d4e52d0SVictor Lu 
35823d4e52d0SVictor Lu 	return 0;
35833d4e52d0SVictor Lu }
35843d4e52d0SVictor Lu 
35853d4e52d0SVictor Lu /*
35863d4e52d0SVictor Lu  * Gets the force_timing_sync debug option value into the given buffer.
35873d4e52d0SVictor Lu  * Usage: cat /sys/kernel/debug/dri/0/amdgpu_dm_force_timing_sync
35883d4e52d0SVictor Lu  */
force_timing_sync_get(void * data,u64 * val)35893d4e52d0SVictor Lu static int force_timing_sync_get(void *data, u64 *val)
35903d4e52d0SVictor Lu {
35913d4e52d0SVictor Lu 	struct amdgpu_device *adev = data;
35923d4e52d0SVictor Lu 
35933d4e52d0SVictor Lu 	*val = adev->dm.force_timing_sync;
35943d4e52d0SVictor Lu 
35953d4e52d0SVictor Lu 	return 0;
35963d4e52d0SVictor Lu }
35973d4e52d0SVictor Lu 
35983d4e52d0SVictor Lu DEFINE_DEBUGFS_ATTRIBUTE(force_timing_sync_ops, force_timing_sync_get,
35993d4e52d0SVictor Lu 			 force_timing_sync_set, "%llu\n");
36003d4e52d0SVictor Lu 
3601b972b4f9SHarry Wentland 
3602b972b4f9SHarry Wentland /*
3603b972b4f9SHarry Wentland  * Disables all HPD and HPD RX interrupt handling in the
3604b972b4f9SHarry Wentland  * driver when set to 1. Default is 0.
3605b972b4f9SHarry Wentland  */
disable_hpd_set(void * data,u64 val)3606b972b4f9SHarry Wentland static int disable_hpd_set(void *data, u64 val)
3607b972b4f9SHarry Wentland {
3608b972b4f9SHarry Wentland 	struct amdgpu_device *adev = data;
3609b972b4f9SHarry Wentland 
3610b972b4f9SHarry Wentland 	adev->dm.disable_hpd_irq = (bool)val;
3611b972b4f9SHarry Wentland 
3612b972b4f9SHarry Wentland 	return 0;
3613b972b4f9SHarry Wentland }
3614b972b4f9SHarry Wentland 
3615b972b4f9SHarry Wentland 
3616b972b4f9SHarry Wentland /*
3617b972b4f9SHarry Wentland  * Returns 1 if HPD and HPRX interrupt handling is disabled,
3618b972b4f9SHarry Wentland  * 0 otherwise.
3619b972b4f9SHarry Wentland  */
disable_hpd_get(void * data,u64 * val)3620b972b4f9SHarry Wentland static int disable_hpd_get(void *data, u64 *val)
3621b972b4f9SHarry Wentland {
3622b972b4f9SHarry Wentland 	struct amdgpu_device *adev = data;
3623b972b4f9SHarry Wentland 
3624b972b4f9SHarry Wentland 	*val = adev->dm.disable_hpd_irq;
3625b972b4f9SHarry Wentland 
3626b972b4f9SHarry Wentland 	return 0;
3627b972b4f9SHarry Wentland }
3628b972b4f9SHarry Wentland 
3629b972b4f9SHarry Wentland DEFINE_DEBUGFS_ATTRIBUTE(disable_hpd_ops, disable_hpd_get,
3630b972b4f9SHarry Wentland 			 disable_hpd_set, "%llu\n");
3631b972b4f9SHarry Wentland 
36327e4aeed8SFangzhi Zuo /*
36337e4aeed8SFangzhi Zuo  * Temporary w/a to force sst sequence in M42D DP2 mst receiver
36347e4aeed8SFangzhi Zuo  * Example usage: echo 1 > /sys/kernel/debug/dri/0/amdgpu_dm_dp_set_mst_en_for_sst
36357e4aeed8SFangzhi Zuo  */
dp_force_sst_set(void * data,u64 val)36367e4aeed8SFangzhi Zuo static int dp_force_sst_set(void *data, u64 val)
36377e4aeed8SFangzhi Zuo {
36387e4aeed8SFangzhi Zuo 	struct amdgpu_device *adev = data;
36397e4aeed8SFangzhi Zuo 
36407e4aeed8SFangzhi Zuo 	adev->dm.dc->debug.set_mst_en_for_sst = val;
36417e4aeed8SFangzhi Zuo 
36427e4aeed8SFangzhi Zuo 	return 0;
36437e4aeed8SFangzhi Zuo }
36447e4aeed8SFangzhi Zuo 
dp_force_sst_get(void * data,u64 * val)36457e4aeed8SFangzhi Zuo static int dp_force_sst_get(void *data, u64 *val)
36467e4aeed8SFangzhi Zuo {
36477e4aeed8SFangzhi Zuo 	struct amdgpu_device *adev = data;
36487e4aeed8SFangzhi Zuo 
36497e4aeed8SFangzhi Zuo 	*val = adev->dm.dc->debug.set_mst_en_for_sst;
36507e4aeed8SFangzhi Zuo 
36517e4aeed8SFangzhi Zuo 	return 0;
36527e4aeed8SFangzhi Zuo }
36537e4aeed8SFangzhi Zuo DEFINE_DEBUGFS_ATTRIBUTE(dp_set_mst_en_for_sst_ops, dp_force_sst_get,
36547e4aeed8SFangzhi Zuo 			 dp_force_sst_set, "%llu\n");
3655bd682a78SFangzhi Zuo 
3656bd682a78SFangzhi Zuo /*
3657bd682a78SFangzhi Zuo  * Force DP2 sequence without VESA certified cable.
3658bd682a78SFangzhi Zuo  * Example usage: echo 1 > /sys/kernel/debug/dri/0/amdgpu_dm_dp_ignore_cable_id
3659bd682a78SFangzhi Zuo  */
dp_ignore_cable_id_set(void * data,u64 val)3660bd682a78SFangzhi Zuo static int dp_ignore_cable_id_set(void *data, u64 val)
3661bd682a78SFangzhi Zuo {
3662bd682a78SFangzhi Zuo 	struct amdgpu_device *adev = data;
3663bd682a78SFangzhi Zuo 
3664bd682a78SFangzhi Zuo 	adev->dm.dc->debug.ignore_cable_id = val;
3665bd682a78SFangzhi Zuo 
3666bd682a78SFangzhi Zuo 	return 0;
3667bd682a78SFangzhi Zuo }
3668bd682a78SFangzhi Zuo 
dp_ignore_cable_id_get(void * data,u64 * val)3669bd682a78SFangzhi Zuo static int dp_ignore_cable_id_get(void *data, u64 *val)
3670bd682a78SFangzhi Zuo {
3671bd682a78SFangzhi Zuo 	struct amdgpu_device *adev = data;
3672bd682a78SFangzhi Zuo 
3673bd682a78SFangzhi Zuo 	*val = adev->dm.dc->debug.ignore_cable_id;
3674bd682a78SFangzhi Zuo 
3675bd682a78SFangzhi Zuo 	return 0;
3676bd682a78SFangzhi Zuo }
3677bd682a78SFangzhi Zuo DEFINE_DEBUGFS_ATTRIBUTE(dp_ignore_cable_id_ops, dp_ignore_cable_id_get,
3678bd682a78SFangzhi Zuo 			 dp_ignore_cable_id_set, "%llu\n");
36797e4aeed8SFangzhi Zuo 
36803d4e52d0SVictor Lu /*
3681b62f95d1SNicholas Kazlauskas  * Sets the DC visual confirm debug option from the given string.
3682b62f95d1SNicholas Kazlauskas  * Example usage: echo 1 > /sys/kernel/debug/dri/0/amdgpu_visual_confirm
3683b62f95d1SNicholas Kazlauskas  */
visual_confirm_set(void * data,u64 val)3684b62f95d1SNicholas Kazlauskas static int visual_confirm_set(void *data, u64 val)
3685b62f95d1SNicholas Kazlauskas {
3686b62f95d1SNicholas Kazlauskas 	struct amdgpu_device *adev = data;
3687b62f95d1SNicholas Kazlauskas 
3688b62f95d1SNicholas Kazlauskas 	adev->dm.dc->debug.visual_confirm = (enum visual_confirm)val;
3689b62f95d1SNicholas Kazlauskas 
3690b62f95d1SNicholas Kazlauskas 	return 0;
3691b62f95d1SNicholas Kazlauskas }
3692b62f95d1SNicholas Kazlauskas 
3693b62f95d1SNicholas Kazlauskas /*
3694b62f95d1SNicholas Kazlauskas  * Reads the DC visual confirm debug option value into the given buffer.
3695b62f95d1SNicholas Kazlauskas  * Example usage: cat /sys/kernel/debug/dri/0/amdgpu_dm_visual_confirm
3696b62f95d1SNicholas Kazlauskas  */
visual_confirm_get(void * data,u64 * val)3697b62f95d1SNicholas Kazlauskas static int visual_confirm_get(void *data, u64 *val)
3698b62f95d1SNicholas Kazlauskas {
3699b62f95d1SNicholas Kazlauskas 	struct amdgpu_device *adev = data;
3700b62f95d1SNicholas Kazlauskas 
3701b62f95d1SNicholas Kazlauskas 	*val = adev->dm.dc->debug.visual_confirm;
3702b62f95d1SNicholas Kazlauskas 
3703b62f95d1SNicholas Kazlauskas 	return 0;
3704b62f95d1SNicholas Kazlauskas }
3705b62f95d1SNicholas Kazlauskas 
3706afd3a359SNirmoy Das DEFINE_SHOW_ATTRIBUTE(mst_topo);
3707b62f95d1SNicholas Kazlauskas DEFINE_DEBUGFS_ATTRIBUTE(visual_confirm_fops, visual_confirm_get,
3708b62f95d1SNicholas Kazlauskas 			 visual_confirm_set, "%llu\n");
3709b62f95d1SNicholas Kazlauskas 
3710634c67eeShersen wu 
3711634c67eeShersen wu /*
3712634c67eeShersen wu  * Sets the DC skip_detection_link_training debug option from the given string.
3713634c67eeShersen wu  * Example usage: echo 1 > /sys/kernel/debug/dri/0/amdgpu_skip_detection_link_training
3714634c67eeShersen wu  */
skip_detection_link_training_set(void * data,u64 val)3715634c67eeShersen wu static int skip_detection_link_training_set(void *data, u64 val)
3716634c67eeShersen wu {
3717634c67eeShersen wu 	struct amdgpu_device *adev = data;
3718634c67eeShersen wu 
3719634c67eeShersen wu 	if (val == 0)
3720634c67eeShersen wu 		adev->dm.dc->debug.skip_detection_link_training = false;
3721634c67eeShersen wu 	else
3722634c67eeShersen wu 		adev->dm.dc->debug.skip_detection_link_training = true;
3723634c67eeShersen wu 
3724634c67eeShersen wu 	return 0;
3725634c67eeShersen wu }
3726634c67eeShersen wu 
3727634c67eeShersen wu /*
3728634c67eeShersen wu  * Reads the DC skip_detection_link_training debug option value into the given buffer.
3729634c67eeShersen wu  * Example usage: cat /sys/kernel/debug/dri/0/amdgpu_dm_skip_detection_link_training
3730634c67eeShersen wu  */
skip_detection_link_training_get(void * data,u64 * val)3731634c67eeShersen wu static int skip_detection_link_training_get(void *data, u64 *val)
3732634c67eeShersen wu {
3733634c67eeShersen wu 	struct amdgpu_device *adev = data;
3734634c67eeShersen wu 
3735634c67eeShersen wu 	*val = adev->dm.dc->debug.skip_detection_link_training;
3736634c67eeShersen wu 
3737634c67eeShersen wu 	return 0;
3738634c67eeShersen wu }
3739634c67eeShersen wu 
3740634c67eeShersen wu DEFINE_DEBUGFS_ATTRIBUTE(skip_detection_link_training_fops,
3741634c67eeShersen wu 			 skip_detection_link_training_get,
3742634c67eeShersen wu 			 skip_detection_link_training_set, "%llu\n");
3743634c67eeShersen wu 
3744712343cdSVictor Lu /*
3745712343cdSVictor Lu  * Dumps the DCC_EN bit for each pipe.
3746712343cdSVictor Lu  * Example usage: cat /sys/kernel/debug/dri/0/amdgpu_dm_dcc_en
3747712343cdSVictor Lu  */
dcc_en_bits_read(struct file * f,char __user * buf,size_t size,loff_t * pos)3748712343cdSVictor Lu static ssize_t dcc_en_bits_read(
3749712343cdSVictor Lu 	struct file *f,
3750712343cdSVictor Lu 	char __user *buf,
3751712343cdSVictor Lu 	size_t size,
3752712343cdSVictor Lu 	loff_t *pos)
3753712343cdSVictor Lu {
3754712343cdSVictor Lu 	struct amdgpu_device *adev = file_inode(f)->i_private;
3755712343cdSVictor Lu 	struct dc *dc = adev->dm.dc;
3756712343cdSVictor Lu 	char *rd_buf = NULL;
3757712343cdSVictor Lu 	const uint32_t rd_buf_size = 32;
3758712343cdSVictor Lu 	uint32_t result = 0;
3759712343cdSVictor Lu 	int offset = 0;
3760712343cdSVictor Lu 	int num_pipes = dc->res_pool->pipe_count;
3761712343cdSVictor Lu 	int *dcc_en_bits;
3762712343cdSVictor Lu 	int i, r;
3763712343cdSVictor Lu 
3764712343cdSVictor Lu 	dcc_en_bits = kcalloc(num_pipes, sizeof(int), GFP_KERNEL);
3765712343cdSVictor Lu 	if (!dcc_en_bits)
3766712343cdSVictor Lu 		return -ENOMEM;
3767712343cdSVictor Lu 
3768712343cdSVictor Lu 	if (!dc->hwss.get_dcc_en_bits) {
3769712343cdSVictor Lu 		kfree(dcc_en_bits);
3770712343cdSVictor Lu 		return 0;
3771712343cdSVictor Lu 	}
3772712343cdSVictor Lu 
3773712343cdSVictor Lu 	dc->hwss.get_dcc_en_bits(dc, dcc_en_bits);
3774712343cdSVictor Lu 
3775712343cdSVictor Lu 	rd_buf = kcalloc(rd_buf_size, sizeof(char), GFP_KERNEL);
37765d5c6dbaSYongzhi Liu 	if (!rd_buf) {
37775d5c6dbaSYongzhi Liu 		kfree(dcc_en_bits);
3778712343cdSVictor Lu 		return -ENOMEM;
37795d5c6dbaSYongzhi Liu 	}
3780712343cdSVictor Lu 
3781712343cdSVictor Lu 	for (i = 0; i < num_pipes; i++)
3782712343cdSVictor Lu 		offset += snprintf(rd_buf + offset, rd_buf_size - offset,
3783712343cdSVictor Lu 				   "%d  ", dcc_en_bits[i]);
3784712343cdSVictor Lu 	rd_buf[strlen(rd_buf)] = '\n';
3785712343cdSVictor Lu 
3786712343cdSVictor Lu 	kfree(dcc_en_bits);
3787712343cdSVictor Lu 
3788712343cdSVictor Lu 	while (size) {
3789712343cdSVictor Lu 		if (*pos >= rd_buf_size)
3790712343cdSVictor Lu 			break;
3791712343cdSVictor Lu 		r = put_user(*(rd_buf + result), buf);
37925d5c6dbaSYongzhi Liu 		if (r) {
37935d5c6dbaSYongzhi Liu 			kfree(rd_buf);
3794712343cdSVictor Lu 			return r; /* r = -EFAULT */
37955d5c6dbaSYongzhi Liu 		}
3796712343cdSVictor Lu 		buf += 1;
3797712343cdSVictor Lu 		size -= 1;
3798712343cdSVictor Lu 		*pos += 1;
3799712343cdSVictor Lu 		result += 1;
3800712343cdSVictor Lu 	}
3801712343cdSVictor Lu 
3802712343cdSVictor Lu 	kfree(rd_buf);
3803712343cdSVictor Lu 	return result;
3804712343cdSVictor Lu }
3805712343cdSVictor Lu 
dtn_debugfs_init(struct amdgpu_device * adev)3806afd3a359SNirmoy Das void dtn_debugfs_init(struct amdgpu_device *adev)
3807e498eb71SNicholas Kazlauskas {
3808e498eb71SNicholas Kazlauskas 	static const struct file_operations dtn_log_fops = {
3809e498eb71SNicholas Kazlauskas 		.owner = THIS_MODULE,
3810e498eb71SNicholas Kazlauskas 		.read = dtn_log_read,
3811e498eb71SNicholas Kazlauskas 		.write = dtn_log_write,
3812e498eb71SNicholas Kazlauskas 		.llseek = default_llseek
3813e498eb71SNicholas Kazlauskas 	};
3814712343cdSVictor Lu 	static const struct file_operations dcc_en_bits_fops = {
3815712343cdSVictor Lu 		.owner = THIS_MODULE,
3816712343cdSVictor Lu 		.read = dcc_en_bits_read,
3817712343cdSVictor Lu 		.llseek = default_llseek
3818712343cdSVictor Lu 	};
3819e498eb71SNicholas Kazlauskas 
38204a580877SLuben Tuikov 	struct drm_minor *minor = adev_to_drm(adev)->primary;
38214be8be78SGreg Kroah-Hartman 	struct dentry *root = minor->debugfs_root;
3822e498eb71SNicholas Kazlauskas 
3823afd3a359SNirmoy Das 	debugfs_create_file("amdgpu_mst_topology", 0444, root,
3824afd3a359SNirmoy Das 			    adev, &mst_topo_fops);
38254be8be78SGreg Kroah-Hartman 	debugfs_create_file("amdgpu_dm_dtn_log", 0644, root, adev,
3826e498eb71SNicholas Kazlauskas 			    &dtn_log_fops);
38277e4aeed8SFangzhi Zuo 	debugfs_create_file("amdgpu_dm_dp_set_mst_en_for_sst", 0644, root, adev,
38287e4aeed8SFangzhi Zuo 				&dp_set_mst_en_for_sst_ops);
3829bd682a78SFangzhi Zuo 	debugfs_create_file("amdgpu_dm_dp_ignore_cable_id", 0644, root, adev,
3830bd682a78SFangzhi Zuo 				&dp_ignore_cable_id_ops);
3831e498eb71SNicholas Kazlauskas 
38324be8be78SGreg Kroah-Hartman 	debugfs_create_file_unsafe("amdgpu_dm_visual_confirm", 0644, root, adev,
38334be8be78SGreg Kroah-Hartman 				   &visual_confirm_fops);
3834b62f95d1SNicholas Kazlauskas 
3835634c67eeShersen wu 	debugfs_create_file_unsafe("amdgpu_dm_skip_detection_link_training", 0644, root, adev,
3836634c67eeShersen wu 				   &skip_detection_link_training_fops);
3837634c67eeShersen wu 
383860ec1b56SNicholas Kazlauskas 	debugfs_create_file_unsafe("amdgpu_dm_dmub_tracebuffer", 0644, root,
383960ec1b56SNicholas Kazlauskas 				   adev, &dmub_tracebuffer_fops);
384060ec1b56SNicholas Kazlauskas 
384123640767SNicholas Kazlauskas 	debugfs_create_file_unsafe("amdgpu_dm_dmub_fw_state", 0644, root,
384223640767SNicholas Kazlauskas 				   adev, &dmub_fw_state_fops);
384323640767SNicholas Kazlauskas 
38443d4e52d0SVictor Lu 	debugfs_create_file_unsafe("amdgpu_dm_force_timing_sync", 0644, root,
38453d4e52d0SVictor Lu 				   adev, &force_timing_sync_ops);
384646a83ebaSLeo (Hanghong) Ma 
384746a83ebaSLeo (Hanghong) Ma 	debugfs_create_file_unsafe("amdgpu_dm_dmcub_trace_event_en", 0644, root,
384846a83ebaSLeo (Hanghong) Ma 				   adev, &dmcub_trace_event_state_fops);
3849712343cdSVictor Lu 
385041efcd38SMikita Lipski 	debugfs_create_file_unsafe("amdgpu_dm_trigger_hpd_mst", 0644, root,
385141efcd38SMikita Lipski 				   adev, &trigger_hpd_mst_ops);
385241efcd38SMikita Lipski 
3853712343cdSVictor Lu 	debugfs_create_file_unsafe("amdgpu_dm_dcc_en", 0644, root, adev,
3854712343cdSVictor Lu 				   &dcc_en_bits_fops);
3855b972b4f9SHarry Wentland 
3856b972b4f9SHarry Wentland 	debugfs_create_file_unsafe("amdgpu_dm_disable_hpd", 0644, root, adev,
3857b972b4f9SHarry Wentland 				   &disable_hpd_ops);
3858b972b4f9SHarry Wentland 
3859e498eb71SNicholas Kazlauskas }
3860