Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/gpu/drm/amd/display/amdgpu_dm/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 110 kB image not shown  

Quelle  amdgpu_dm_debugfs.c   Sprache: C

 
/*
 * Copyright 2018 Advanced Micro Devices, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * Authors: AMD
 *
 */


#include <linux/string_helpers.h>
#include <linux/uaccess.h>
#include <media/cec-notifier.h>

#include "dc.h"
#include "amdgpu.h"
#include "amdgpu_dm.h"
#include "amdgpu_dm_debugfs.h"
#include "amdgpu_dm_replay.h"
#include "dm_helpers.h"
#include "dmub/dmub_srv.h"
#include "resource.h"
#include "dsc.h"
#include "link_hwss.h"
#include "dc/dc_dmub_srv.h"
#include "link/protocols/link_dp_capability.h"
#include "inc/hw/dchubbub.h"

#ifdef CONFIG_DRM_AMD_SECURE_DISPLAY
#include "amdgpu_dm_psr.h"
#endif

struct dmub_debugfs_trace_header {
 uint32_t entry_count;
 uint32_t reserved[3];
};

struct dmub_debugfs_trace_entry {
 uint32_t trace_code;
 uint32_t tick_count;
 uint32_t param0;
 uint32_t param1;
};

static const char *const mst_progress_status[] = {
 "probe",
 "remote_edid",
 "allocate_new_payload",
 "clear_allocated_payload",
};

/* parse_write_buffer_into_params - Helper function to parse debugfs write buffer into an array
 *
 * Function takes in attributes passed to debugfs write entry
 * and writes into param array.
 * The user passes max_param_num to identify maximum number of
 * parameters that could be parsed.
 *
 */

static int 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)
{
 char *wr_buf_ptr = NULL;
 uint32_t wr_buf_count = 0;
 int r;
 char *sub_str = NULL;
 const char delimiter[3] = {' ''\n''\0'};
 uint8_t param_index = 0;

 *param_nums = 0;

 wr_buf_ptr = wr_buf;

 /* r is bytes not be copied */
 if (copy_from_user(wr_buf_ptr, buf, wr_buf_size)) {
  DRM_DEBUG_DRIVER("user data could not be read successfully\n");
  return -EFAULT;
 }

 /* check number of parameters. isspace could not differ space and \n */
 while ((*wr_buf_ptr != 0xa) && (wr_buf_count < wr_buf_size)) {
  /* skip space*/
  while (isspace(*wr_buf_ptr) && (wr_buf_count < wr_buf_size)) {
   wr_buf_ptr++;
   wr_buf_count++;
   }

  if (wr_buf_count == wr_buf_size)
   break;

  /* skip non-space*/
  while ((!isspace(*wr_buf_ptr)) && (wr_buf_count < wr_buf_size)) {
   wr_buf_ptr++;
   wr_buf_count++;
  }

  (*param_nums)++;

  if (wr_buf_count == wr_buf_size)
   break;
 }

 if (*param_nums > max_param_num)
  *param_nums = max_param_num;

 wr_buf_ptr = wr_buf; /* reset buf pointer */
 wr_buf_count = 0; /* number of char already checked */

 while (isspace(*wr_buf_ptr) && (wr_buf_count < wr_buf_size)) {
  wr_buf_ptr++;
  wr_buf_count++;
 }

 while (param_index < *param_nums) {
  /* after strsep, wr_buf_ptr will be moved to after space */
  sub_str = strsep(&wr_buf_ptr, delimiter);

  r = kstrtol(sub_str, 16, &(param[param_index]));

  if (r)
   DRM_DEBUG_DRIVER("string to int convert error code: %d\n", r);

  param_index++;
 }

 return 0;
}

/* function description
 * get/ set DP configuration: lane_count, link_rate, spread_spectrum
 *
 * valid lane count value: 1, 2, 4
 * valid link rate value:
 * 06h = 1.62Gbps per lane
 * 0Ah = 2.7Gbps per lane
 * 0Ch = 3.24Gbps per lane
 * 14h = 5.4Gbps per lane
 * 1Eh = 8.1Gbps per lane
 *
 * debugfs is located at /sys/kernel/debug/dri/0/DP-x/link_settings
 *
 * --- to get dp configuration
 *
 * cat /sys/kernel/debug/dri/0/DP-x/link_settings
 *
 * It will list current, verified, reported, preferred dp configuration.
 * current -- for current video mode
 * verified --- maximum configuration which pass link training
 * reported --- DP rx report caps (DPCD register offset 0, 1 2)
 * preferred --- user force settings
 *
 * --- set (or force) dp configuration
 *
 * echo <lane_count>  <link_rate> > link_settings
 *
 * for example, to force to  2 lane, 2.7GHz,
 * echo 4 0xa > /sys/kernel/debug/dri/0/DP-x/link_settings
 *
 * spread_spectrum could not be changed dynamically.
 *
 * in case invalid lane count, link rate are force, no hw programming will be
 * done. please check link settings after force operation to see if HW get
 * programming.
 *
 * cat /sys/kernel/debug/dri/0/DP-x/link_settings
 *
 * check current and preferred settings.
 *
 */

static ssize_t dp_link_settings_read(struct file *f, char __user *buf,
     size_t size, loff_t *pos)
{
 struct amdgpu_dm_connector *connector = file_inode(f)->i_private;
 struct dc_link *link = connector->dc_link;
 char *rd_buf = NULL;
 char *rd_buf_ptr = NULL;
 const uint32_t rd_buf_size = 100;
 uint32_t result = 0;
 uint8_t str_len = 0;
 int r;

 if (*pos & 3 || size & 3)
  return -EINVAL;

 rd_buf = kcalloc(rd_buf_size, sizeof(char), GFP_KERNEL);
 if (!rd_buf)
  return 0;

 rd_buf_ptr = rd_buf;

 str_len = strlen("Current: %d 0x%x %d ");
 snprintf(rd_buf_ptr, str_len, "Current: %d 0x%x %d ",
   link->cur_link_settings.lane_count,
   link->cur_link_settings.link_rate,
   link->cur_link_settings.link_spread);
 rd_buf_ptr += str_len;

 str_len = strlen("Verified: %d 0x%x %d ");
 snprintf(rd_buf_ptr, str_len, "Verified: %d 0x%x %d ",
   link->verified_link_cap.lane_count,
   link->verified_link_cap.link_rate,
   link->verified_link_cap.link_spread);
 rd_buf_ptr += str_len;

 str_len = strlen("Reported: %d 0x%x %d ");
 snprintf(rd_buf_ptr, str_len, "Reported: %d 0x%x %d ",
   link->reported_link_cap.lane_count,
   link->reported_link_cap.link_rate,
   link->reported_link_cap.link_spread);
 rd_buf_ptr += str_len;

 str_len = strlen("Preferred: %d 0x%x %d ");
 snprintf(rd_buf_ptr, str_len, "Preferred: %d 0x%x %d\n",
   link->preferred_link_setting.lane_count,
   link->preferred_link_setting.link_rate,
   link->preferred_link_setting.link_spread);

 while (size) {
  if (*pos >= rd_buf_size)
   break;

  r = put_user(*(rd_buf + result), buf);
  if (r) {
   kfree(rd_buf);
   return r; /* r = -EFAULT */
  }

  buf += 1;
  size -= 1;
  *pos += 1;
  result += 1;
 }

 kfree(rd_buf);
 return result;
}

static ssize_t dp_link_settings_write(struct file *f, const char __user *buf,
     size_t size, loff_t *pos)
{
 struct amdgpu_dm_connector *connector = file_inode(f)->i_private;
 struct dc_link *link = connector->dc_link;
 struct amdgpu_device *adev = drm_to_adev(connector->base.dev);
 struct dc *dc = (struct dc *)link->dc;
 struct dc_link_settings prefer_link_settings = {0};
 char *wr_buf = NULL;
 const uint32_t wr_buf_size = 40;
 /* 0: lane_count; 1: link_rate */
 int max_param_num = 2;
 uint8_t param_nums = 0;
 long param[2];
 bool valid_input = true;

 if (size == 0)
  return -EINVAL;

 wr_buf = kcalloc(wr_buf_size, sizeof(char), GFP_KERNEL);
 if (!wr_buf)
  return -ENOSPC;

 if (parse_write_buffer_into_params(wr_buf, wr_buf_size,
        (long *)param, buf,
        max_param_num,
        ¶m_nums)) {
  kfree(wr_buf);
  return -EINVAL;
 }

 if (param_nums <= 0) {
  kfree(wr_buf);
  DRM_DEBUG_DRIVER("user data not be read\n");
  return -EINVAL;
 }

 switch (param[0]) {
 case LANE_COUNT_ONE:
 case LANE_COUNT_TWO:
 case LANE_COUNT_FOUR:
  break;
 default:
  valid_input = false;
  break;
 }

 switch (param[1]) {
 case LINK_RATE_LOW:
 case LINK_RATE_HIGH:
 case LINK_RATE_RBR2:
 case LINK_RATE_HIGH2:
 case LINK_RATE_HIGH3:
 case LINK_RATE_UHBR10:
 case LINK_RATE_UHBR13_5:
 case LINK_RATE_UHBR20:
  break;
 default:
  valid_input = false;
  break;
 }

 if (!valid_input) {
  kfree(wr_buf);
  DRM_DEBUG_DRIVER("Invalid Input value No HW will be programmed\n");
  mutex_lock(&adev->dm.dc_lock);
  dc_link_set_preferred_training_settings(dc, NULL, NULL, link, false);
  mutex_unlock(&adev->dm.dc_lock);
  return size;
 }

 /* save user force lane_count, link_rate to preferred settings
 * spread spectrum will not be changed
 */

 prefer_link_settings.link_spread = link->cur_link_settings.link_spread;
 prefer_link_settings.use_link_rate_set = false;
 prefer_link_settings.lane_count = param[0];
 prefer_link_settings.link_rate = param[1];

 mutex_lock(&adev->dm.dc_lock);
 dc_link_set_preferred_training_settings(dc, &prefer_link_settings, NULL, link, false);
 mutex_unlock(&adev->dm.dc_lock);

 kfree(wr_buf);
 return size;
}

static bool dp_mst_is_end_device(struct amdgpu_dm_connector *aconnector)
{
 bool is_end_device = false;
 struct drm_dp_mst_topology_mgr *mgr = NULL;
 struct drm_dp_mst_port *port = NULL;

 if (aconnector->mst_root && aconnector->mst_root->mst_mgr.mst_state) {
  mgr = &aconnector->mst_root->mst_mgr;
  port = aconnector->mst_output_port;

  drm_modeset_lock(&mgr->base.lock, NULL);
  if (port->pdt == DP_PEER_DEVICE_SST_SINK ||
   port->pdt == DP_PEER_DEVICE_DP_LEGACY_CONV)
   is_end_device = true;
  drm_modeset_unlock(&mgr->base.lock);
 }

 return is_end_device;
}

/* Change MST link setting
 *
 * valid lane count value: 1, 2, 4
 * valid link rate value:
 * 06h = 1.62Gbps per lane
 * 0Ah = 2.7Gbps per lane
 * 0Ch = 3.24Gbps per lane
 * 14h = 5.4Gbps per lane
 * 1Eh = 8.1Gbps per lane
 * 3E8h = 10.0Gbps per lane
 * 546h = 13.5Gbps per lane
 * 7D0h = 20.0Gbps per lane
 *
 * debugfs is located at /sys/kernel/debug/dri/0/DP-x/mst_link_settings
 *
 * for example, to force to  2 lane, 10.0GHz,
 * echo 2 0x3e8 > /sys/kernel/debug/dri/0/DP-x/mst_link_settings
 *
 * Valid input will trigger hotplug event to get new link setting applied
 * Invalid input will trigger training setting reset
 *
 * The usage can be referred to link_settings entry
 *
 */

static ssize_t dp_mst_link_setting(struct file *f, const char __user *buf,
     size_t size, loff_t *pos)
{
 struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private;
 struct dc_link *link = aconnector->dc_link;
 struct amdgpu_device *adev = drm_to_adev(aconnector->base.dev);
 struct dc *dc = (struct dc *)link->dc;
 struct dc_link_settings prefer_link_settings = {0};
 char *wr_buf = NULL;
 const uint32_t wr_buf_size = 40;
 /* 0: lane_count; 1: link_rate */
 int max_param_num = 2;
 uint8_t param_nums = 0;
 long param[2];
 bool valid_input = true;

 if (!dp_mst_is_end_device(aconnector))
  return -EINVAL;

 if (size == 0)
  return -EINVAL;

 wr_buf = kcalloc(wr_buf_size, sizeof(char), GFP_KERNEL);
 if (!wr_buf)
  return -ENOSPC;

 if (parse_write_buffer_into_params(wr_buf, wr_buf_size,
        (long *)param, buf,
        max_param_num,
        ¶m_nums)) {
  kfree(wr_buf);
  return -EINVAL;
 }

 if (param_nums <= 0) {
  kfree(wr_buf);
  DRM_DEBUG_DRIVER("user data not be read\n");
  return -EINVAL;
 }

 switch (param[0]) {
 case LANE_COUNT_ONE:
 case LANE_COUNT_TWO:
 case LANE_COUNT_FOUR:
  break;
 default:
  valid_input = false;
  break;
 }

 switch (param[1]) {
 case LINK_RATE_LOW:
 case LINK_RATE_HIGH:
 case LINK_RATE_RBR2:
 case LINK_RATE_HIGH2:
 case LINK_RATE_HIGH3:
 case LINK_RATE_UHBR10:
 case LINK_RATE_UHBR13_5:
 case LINK_RATE_UHBR20:
  break;
 default:
  valid_input = false;
  break;
 }

 if (!valid_input) {
  kfree(wr_buf);
  DRM_DEBUG_DRIVER("Invalid Input value No HW will be programmed\n");
  mutex_lock(&adev->dm.dc_lock);
  dc_link_set_preferred_training_settings(dc, NULL, NULL, link, false);
  mutex_unlock(&adev->dm.dc_lock);
  return -EINVAL;
 }

 /* save user force lane_count, link_rate to preferred settings
 * spread spectrum will not be changed
 */

 prefer_link_settings.link_spread = link->cur_link_settings.link_spread;
 prefer_link_settings.use_link_rate_set = false;
 prefer_link_settings.lane_count = param[0];
 prefer_link_settings.link_rate = param[1];

 /* skip immediate retrain, and train to new link setting after hotplug event triggered */
 mutex_lock(&adev->dm.dc_lock);
 dc_link_set_preferred_training_settings(dc, &prefer_link_settings, NULL, link, true);
 mutex_unlock(&adev->dm.dc_lock);

 mutex_lock(&aconnector->base.dev->mode_config.mutex);
 aconnector->base.force = DRM_FORCE_OFF;
 mutex_unlock(&aconnector->base.dev->mode_config.mutex);
 drm_kms_helper_hotplug_event(aconnector->base.dev);

 msleep(100);

 mutex_lock(&aconnector->base.dev->mode_config.mutex);
 aconnector->base.force = DRM_FORCE_UNSPECIFIED;
 mutex_unlock(&aconnector->base.dev->mode_config.mutex);
 drm_kms_helper_hotplug_event(aconnector->base.dev);

 kfree(wr_buf);
 return size;
}

/* function: get current DP PHY settings: voltage swing, pre-emphasis,
 * post-cursor2 (defined by VESA DP specification)
 *
 * valid values
 * voltage swing: 0,1,2,3
 * pre-emphasis : 0,1,2,3
 * post cursor2 : 0,1,2,3
 *
 *
 * how to use this debugfs
 *
 * debugfs is located at /sys/kernel/debug/dri/0/DP-x
 *
 * there will be directories, like DP-1, DP-2,DP-3, etc. for DP display
 *
 * To figure out which DP-x is the display for DP to be check,
 * cd DP-x
 * ls -ll
 * There should be debugfs file, like link_settings, phy_settings.
 * cat link_settings
 * from lane_count, link_rate to figure which DP-x is for display to be worked
 * on
 *
 * To get current DP PHY settings,
 * cat phy_settings
 *
 * To change DP PHY settings,
 * echo <voltage_swing> <pre-emphasis> <post_cursor2> > phy_settings
 * for examle, to change voltage swing to 2, pre-emphasis to 3, post_cursor2 to
 * 0,
 * echo 2 3 0 > phy_settings
 *
 * To check if change be applied, get current phy settings by
 * cat phy_settings
 *
 * In case invalid values are set by user, like
 * echo 1 4 0 > phy_settings
 *
 * HW will NOT be programmed by these settings.
 * cat phy_settings will show the previous valid settings.
 */

static ssize_t dp_phy_settings_read(struct file *f, char __user *buf,
     size_t size, loff_t *pos)
{
 struct amdgpu_dm_connector *connector = file_inode(f)->i_private;
 struct dc_link *link = connector->dc_link;
 char *rd_buf = NULL;
 const uint32_t rd_buf_size = 20;
 uint32_t result = 0;
 int r;

 if (*pos & 3 || size & 3)
  return -EINVAL;

 rd_buf = kcalloc(rd_buf_size, sizeof(char), GFP_KERNEL);
 if (!rd_buf)
  return -EINVAL;

 snprintf(rd_buf, rd_buf_size, " %d %d %d\n",
   link->cur_lane_setting[0].VOLTAGE_SWING,
   link->cur_lane_setting[0].PRE_EMPHASIS,
   link->cur_lane_setting[0].POST_CURSOR2);

 while (size) {
  if (*pos >= rd_buf_size)
   break;

  r = put_user((*(rd_buf + result)), buf);
  if (r) {
   kfree(rd_buf);
   return r; /* r = -EFAULT */
  }

  buf += 1;
  size -= 1;
  *pos += 1;
  result += 1;
 }

 kfree(rd_buf);
 return result;
}

static int dp_lttpr_status_show(struct seq_file *m, void *unused)
{
 struct drm_connector *connector = m->private;
 struct amdgpu_dm_connector *aconnector =
  to_amdgpu_dm_connector(connector);
 struct dc_lttpr_caps caps = aconnector->dc_link->dpcd_caps.lttpr_caps;

 if (connector->status != connector_status_connected)
  return -ENODEV;

 seq_printf(m, "phy repeater count: %u (raw: 0x%x)\n",
     dp_parse_lttpr_repeater_count(caps.phy_repeater_cnt),
     caps.phy_repeater_cnt);

 seq_puts(m, "phy repeater mode: ");

 switch (caps.mode) {
 case DP_PHY_REPEATER_MODE_TRANSPARENT:
  seq_puts(m, "transparent");
  break;
 case DP_PHY_REPEATER_MODE_NON_TRANSPARENT:
  seq_puts(m, "non-transparent");
  break;
 case 0x00:
  seq_puts(m, "non lttpr");
  break;
 default:
  seq_printf(m, "read error (raw: 0x%x)", caps.mode);
  break;
 }

 seq_puts(m, "\n");
 return 0;
}

static ssize_t dp_phy_settings_write(struct file *f, const char __user *buf,
     size_t size, loff_t *pos)
{
 struct amdgpu_dm_connector *connector = file_inode(f)->i_private;
 struct dc_link *link = connector->dc_link;
 struct dc *dc = (struct dc *)link->dc;
 char *wr_buf = NULL;
 uint32_t wr_buf_size = 40;
 long param[3];
 bool use_prefer_link_setting;
 struct link_training_settings link_lane_settings = {0};
 int max_param_num = 3;
 uint8_t param_nums = 0;
 int r = 0;


 if (size == 0)
  return -EINVAL;

 wr_buf = kcalloc(wr_buf_size, sizeof(char), GFP_KERNEL);
 if (!wr_buf)
  return -ENOSPC;

 if (parse_write_buffer_into_params(wr_buf, wr_buf_size,
        (long *)param, buf,
        max_param_num,
        ¶m_nums)) {
  kfree(wr_buf);
  return -EINVAL;
 }

 if (param_nums <= 0) {
  kfree(wr_buf);
  DRM_DEBUG_DRIVER("user data not be read\n");
  return -EINVAL;
 }

 if ((param[0] > VOLTAGE_SWING_MAX_LEVEL) ||
   (param[1] > PRE_EMPHASIS_MAX_LEVEL) ||
   (param[2] > POST_CURSOR2_MAX_LEVEL)) {
  kfree(wr_buf);
  DRM_DEBUG_DRIVER("Invalid Input No HW will be programmed\n");
  return size;
 }

 /* get link settings: lane count, link rate */
 use_prefer_link_setting =
  ((link->preferred_link_setting.link_rate != LINK_RATE_UNKNOWN) &&
  (link->test_pattern_enabled));

 memset(&link_lane_settings, 0, sizeof(link_lane_settings));

 if (use_prefer_link_setting) {
  link_lane_settings.link_settings.lane_count =
    link->preferred_link_setting.lane_count;
  link_lane_settings.link_settings.link_rate =
    link->preferred_link_setting.link_rate;
  link_lane_settings.link_settings.link_spread =
    link->preferred_link_setting.link_spread;
 } else {
  link_lane_settings.link_settings.lane_count =
    link->cur_link_settings.lane_count;
  link_lane_settings.link_settings.link_rate =
    link->cur_link_settings.link_rate;
  link_lane_settings.link_settings.link_spread =
    link->cur_link_settings.link_spread;
 }

 /* apply phy settings from user */
 for (r = 0; r < link_lane_settings.link_settings.lane_count; r++) {
  link_lane_settings.hw_lane_settings[r].VOLTAGE_SWING =
    (enum dc_voltage_swing) (param[0]);
  link_lane_settings.hw_lane_settings[r].PRE_EMPHASIS =
    (enum dc_pre_emphasis) (param[1]);
  link_lane_settings.hw_lane_settings[r].POST_CURSOR2 =
    (enum dc_post_cursor2) (param[2]);
 }

 /* program ASIC registers and DPCD registers */
 dc_link_set_drive_settings(dc, &link_lane_settings, link);

 kfree(wr_buf);
 return size;
}

/* function description
 *
 * set PHY layer or Link layer test pattern
 * PHY test pattern is used for PHY SI check.
 * Link layer test will not affect PHY SI.
 *
 * Reset Test Pattern:
 * 0 = DP_TEST_PATTERN_VIDEO_MODE
 *
 * PHY test pattern supported:
 * 1 = DP_TEST_PATTERN_D102
 * 2 = DP_TEST_PATTERN_SYMBOL_ERROR
 * 3 = DP_TEST_PATTERN_PRBS7
 * 4 = DP_TEST_PATTERN_80BIT_CUSTOM
 * 5 = DP_TEST_PATTERN_CP2520_1
 * 6 = DP_TEST_PATTERN_CP2520_2 = DP_TEST_PATTERN_HBR2_COMPLIANCE_EYE
 * 7 = DP_TEST_PATTERN_CP2520_3
 *
 * DP PHY Link Training Patterns
 * 8 = DP_TEST_PATTERN_TRAINING_PATTERN1
 * 9 = DP_TEST_PATTERN_TRAINING_PATTERN2
 * a = DP_TEST_PATTERN_TRAINING_PATTERN3
 * b = DP_TEST_PATTERN_TRAINING_PATTERN4
 *
 * DP Link Layer Test pattern
 * c = DP_TEST_PATTERN_COLOR_SQUARES
 * d = DP_TEST_PATTERN_COLOR_SQUARES_CEA
 * e = DP_TEST_PATTERN_VERTICAL_BARS
 * f = DP_TEST_PATTERN_HORIZONTAL_BARS
 * 10= DP_TEST_PATTERN_COLOR_RAMP
 *
 * debugfs phy_test_pattern is located at /syskernel/debug/dri/0/DP-x
 *
 * --- set test pattern
 * echo <test pattern #> > test_pattern
 *
 * If test pattern # is not supported, NO HW programming will be done.
 * for DP_TEST_PATTERN_80BIT_CUSTOM, it needs extra 10 bytes of data
 * for the user pattern. input 10 bytes data are separated by space
 *
 * echo 0x4 0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88 0x99 0xaa > test_pattern
 *
 * --- reset test pattern
 * echo 0 > test_pattern
 *
 * --- HPD detection is disabled when set PHY test pattern
 *
 * when PHY test pattern (pattern # within [1,7]) is set, HPD pin of HW ASIC
 * is disable. User could unplug DP display from DP connected and plug scope to
 * check test pattern PHY SI.
 * If there is need unplug scope and plug DP display back, do steps below:
 * echo 0 > phy_test_pattern
 * unplug scope
 * plug DP display.
 *
 * "echo 0 > phy_test_pattern" will re-enable HPD pin again so that video sw
 * driver could detect "unplug scope" and "plug DP display"
 */

static ssize_t dp_phy_test_pattern_debugfs_write(struct file *f, const char __user *buf,
     size_t size, loff_t *pos)
{
 struct amdgpu_dm_connector *connector = file_inode(f)->i_private;
 struct dc_link *link = connector->dc_link;
 char *wr_buf = NULL;
 uint32_t wr_buf_size = 100;
 long param[11] = {0x0};
 int max_param_num = 11;
 enum dp_test_pattern test_pattern = DP_TEST_PATTERN_UNSUPPORTED;
 bool disable_hpd = false;
 bool valid_test_pattern = false;
 uint8_t param_nums = 0;
 /* init with default 80bit custom pattern */
 uint8_t custom_pattern[10] = {
   0x1f, 0x7c, 0xf0, 0xc1, 0x07,
   0x1f, 0x7c, 0xf0, 0xc1, 0x07
   };
 struct dc_link_settings prefer_link_settings = {LANE_COUNT_UNKNOWN,
   LINK_RATE_UNKNOWN, LINK_SPREAD_DISABLED};
 struct dc_link_settings cur_link_settings = {LANE_COUNT_UNKNOWN,
   LINK_RATE_UNKNOWN, LINK_SPREAD_DISABLED};
 struct link_training_settings link_training_settings = {0};
 int i;

 if (size == 0)
  return -EINVAL;

 wr_buf = kcalloc(wr_buf_size, sizeof(char), GFP_KERNEL);
 if (!wr_buf)
  return -ENOSPC;

 if (parse_write_buffer_into_params(wr_buf, wr_buf_size,
        (long *)param, buf,
        max_param_num,
        ¶m_nums)) {
  kfree(wr_buf);
  return -EINVAL;
 }

 if (param_nums <= 0) {
  kfree(wr_buf);
  DRM_DEBUG_DRIVER("user data not be read\n");
  return -EINVAL;
 }


 test_pattern = param[0];

 switch (test_pattern) {
 case DP_TEST_PATTERN_VIDEO_MODE:
 case DP_TEST_PATTERN_COLOR_SQUARES:
 case DP_TEST_PATTERN_COLOR_SQUARES_CEA:
 case DP_TEST_PATTERN_VERTICAL_BARS:
 case DP_TEST_PATTERN_HORIZONTAL_BARS:
 case DP_TEST_PATTERN_COLOR_RAMP:
  valid_test_pattern = true;
  break;

 case DP_TEST_PATTERN_D102:
 case DP_TEST_PATTERN_SYMBOL_ERROR:
 case DP_TEST_PATTERN_PRBS7:
 case DP_TEST_PATTERN_80BIT_CUSTOM:
 case DP_TEST_PATTERN_HBR2_COMPLIANCE_EYE:
 case DP_TEST_PATTERN_TRAINING_PATTERN4:
  disable_hpd = true;
  valid_test_pattern = true;
  break;

 default:
  valid_test_pattern = false;
  test_pattern = DP_TEST_PATTERN_UNSUPPORTED;
  break;
 }

 if (!valid_test_pattern) {
  kfree(wr_buf);
  DRM_DEBUG_DRIVER("Invalid Test Pattern Parameters\n");
  return size;
 }

 if (test_pattern == DP_TEST_PATTERN_80BIT_CUSTOM) {
  for (i = 0; i < 10; i++) {
   if ((uint8_t) param[i + 1] != 0x0)
    break;
  }

  if (i < 10) {
   /* not use default value */
   for (i = 0; i < 10; i++)
    custom_pattern[i] = (uint8_t) param[i + 1];
  }
 }

 /* Usage: set DP physical test pattern using debugfs with normal DP
 * panel. Then plug out DP panel and connect a scope to measure
 * For normal video mode and test pattern generated from CRCT,
 * they are visibile to user. So do not disable HPD.
 * Video Mode is also set to clear the test pattern, so enable HPD
 * because it might have been disabled after a test pattern was set.
 * AUX depends on HPD * sequence dependent, do not move!
 */

 if (!disable_hpd)
  dc_link_enable_hpd(link);

 prefer_link_settings.lane_count = link->verified_link_cap.lane_count;
 prefer_link_settings.link_rate = link->verified_link_cap.link_rate;
 prefer_link_settings.link_spread = link->verified_link_cap.link_spread;

 cur_link_settings.lane_count = link->cur_link_settings.lane_count;
 cur_link_settings.link_rate = link->cur_link_settings.link_rate;
 cur_link_settings.link_spread = link->cur_link_settings.link_spread;

 link_training_settings.link_settings = cur_link_settings;


 if (test_pattern != DP_TEST_PATTERN_VIDEO_MODE) {
  if (prefer_link_settings.lane_count != LANE_COUNT_UNKNOWN &&
   prefer_link_settings.link_rate !=  LINK_RATE_UNKNOWN &&
   (prefer_link_settings.lane_count != cur_link_settings.lane_count ||
   prefer_link_settings.link_rate != cur_link_settings.link_rate))
   link_training_settings.link_settings = prefer_link_settings;
 }

 for (i = 0; i < (unsigned int)(link_training_settings.link_settings.lane_count); i++)
  link_training_settings.hw_lane_settings[i] = link->cur_lane_setting[i];

 dc_link_dp_set_test_pattern(
  link,
  test_pattern,
  DP_TEST_PATTERN_COLOR_SPACE_RGB,
  &link_training_settings,
  custom_pattern,
  10);

 /* Usage: Set DP physical test pattern using AMDDP with normal DP panel
 * Then plug out DP panel and connect a scope to measure DP PHY signal.
 * Need disable interrupt to avoid SW driver disable DP output. This is
 * done after the test pattern is set.
 */

 if (valid_test_pattern && disable_hpd)
  dc_link_disable_hpd(link);

 kfree(wr_buf);

 return size;
}

/*
 * Returns the DMCUB tracebuffer contents.
 * Example usage: cat /sys/kernel/debug/dri/0/amdgpu_dm_dmub_tracebuffer
 */

static int dmub_tracebuffer_show(struct seq_file *m, void *data)
{
 struct amdgpu_device *adev = m->private;
 struct dmub_srv_fb_info *fb_info = adev->dm.dmub_fb_info;
 struct dmub_fw_meta_info *fw_meta_info = NULL;
 struct dmub_debugfs_trace_entry *entries;
 uint8_t *tbuf_base;
 uint32_t tbuf_size, max_entries, num_entries, first_entry, i;

 if (!fb_info)
  return 0;

 tbuf_base = (uint8_t *)fb_info->fb[DMUB_WINDOW_5_TRACEBUFF].cpu_addr;
 if (!tbuf_base)
  return 0;

 if (adev->dm.dmub_srv)
  fw_meta_info = &adev->dm.dmub_srv->meta_info;

 tbuf_size = fw_meta_info ? fw_meta_info->trace_buffer_size :
       DMUB_TRACE_BUFFER_SIZE;
 max_entries = (tbuf_size - sizeof(struct dmub_debugfs_trace_header)) /
        sizeof(struct dmub_debugfs_trace_entry);

 num_entries =
  ((struct dmub_debugfs_trace_header *)tbuf_base)->entry_count;

 /* DMCUB tracebuffer is a ring. If it rolled over, print a hint that
 * entries are being overwritten.
 */

 if (num_entries > max_entries)
  seq_printf(m, "...\n");

 first_entry = num_entries % max_entries;
 num_entries = min(num_entries, max_entries);

 entries = (struct dmub_debugfs_trace_entry
      *)(tbuf_base +
         sizeof(struct dmub_debugfs_trace_header));

 /* To print entries chronologically, start from the first entry till the
 * top of buffer, then from base of buffer to first entry.
 */

 for (i = first_entry; i < num_entries; ++i) {
  struct dmub_debugfs_trace_entry *entry = &entries[i];

  seq_printf(m,
      "trace_code=%u tick_count=%u param0=%u param1=%u\n",
      entry->trace_code, entry->tick_count, entry->param0,
      entry->param1);
 }
 for (i = 0; i < first_entry; ++i) {
  struct dmub_debugfs_trace_entry *entry = &entries[i];

  seq_printf(m,
      "trace_code=%u tick_count=%u param0=%u param1=%u\n",
      entry->trace_code, entry->tick_count, entry->param0,
      entry->param1);
 }

 return 0;
}

/*
 * Returns the DMCUB firmware state contents.
 * Example usage: cat /sys/kernel/debug/dri/0/amdgpu_dm_dmub_fw_state
 */

static int dmub_fw_state_show(struct seq_file *m, void *data)
{
 struct amdgpu_device *adev = m->private;
 struct dmub_srv_fb_info *fb_info = adev->dm.dmub_fb_info;
 uint8_t *state_base;
 uint32_t state_size;

 if (!fb_info)
  return 0;

 state_base = (uint8_t *)fb_info->fb[DMUB_WINDOW_6_FW_STATE].cpu_addr;
 if (!state_base)
  return 0;

 state_size = fb_info->fb[DMUB_WINDOW_6_FW_STATE].size;

 return seq_write(m, state_base, state_size);
}

/* replay_capability_show() - show eDP panel replay capability
 *
 * The read function: replay_capability_show
 * Shows if sink and driver has Replay capability or not.
 *
 * cat /sys/kernel/debug/dri/0/eDP-X/replay_capability
 *
 * Expected output:
 * "Sink support: no\n" - if panel doesn't support Replay
 * "Sink support: yes\n" - if panel supports Replay
 * "Driver support: no\n" - if driver doesn't support Replay
 * "Driver support: yes\n" - if driver supports Replay
 */

static int replay_capability_show(struct seq_file *m, void *data)
{
 struct drm_connector *connector = m->private;
 struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
 struct dc_link *link = aconnector->dc_link;
 bool sink_support_replay = false;
 bool driver_support_replay = false;

 if (!link)
  return -ENODEV;

 if (link->type == dc_connection_none)
  return -ENODEV;

 if (!(link->connector_signal & SIGNAL_TYPE_EDP))
  return -ENODEV;

 /* If Replay is already set to support, skip the checks */
 if (link->replay_settings.config.replay_supported) {
  sink_support_replay = true;
  driver_support_replay = true;
 } else if ((amdgpu_dc_debug_mask & DC_DISABLE_REPLAY)) {
  sink_support_replay = amdgpu_dm_link_supports_replay(link, aconnector);
 } else {
  struct dc *dc = link->ctx->dc;

  sink_support_replay = amdgpu_dm_link_supports_replay(link, aconnector);
  if (dc->ctx->dmub_srv && dc->ctx->dmub_srv->dmub)
   driver_support_replay =
    (bool)dc->ctx->dmub_srv->dmub->feature_caps.replay_supported;
 }

 seq_printf(m, "Sink support: %s\n", str_yes_no(sink_support_replay));
 seq_printf(m, "Driver support: %s\n", str_yes_no(driver_support_replay));
 seq_printf(m, "Config support: %s\n", str_yes_no(link->replay_settings.config.replay_supported));

 return 0;
}

/* psr_capability_show() - show eDP panel PSR capability
 *
 * The read function: sink_psr_capability_show
 * Shows if sink has PSR capability or not.
 * If yes - the PSR version is appended
 *
 * cat /sys/kernel/debug/dri/0/eDP-X/psr_capability
 *
 * Expected output:
 * "Sink support: no\n" - if panel doesn't support PSR
 * "Sink support: yes [0x01]\n" - if panel supports PSR1
 * "Driver support: no\n" - if driver doesn't support PSR
 * "Driver support: yes [0x01]\n" - if driver supports PSR1
 */

static int psr_capability_show(struct seq_file *m, void *data)
{
 struct drm_connector *connector = m->private;
 struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
 struct dc_link *link = aconnector->dc_link;

 if (!link)
  return -ENODEV;

 if (link->type == dc_connection_none)
  return -ENODEV;

 if (!(link->connector_signal & SIGNAL_TYPE_EDP))
  return -ENODEV;

 seq_printf(m, "Sink support: %s", str_yes_no(link->dpcd_caps.psr_info.psr_version != 0));
 if (link->dpcd_caps.psr_info.psr_version)
  seq_printf(m, " [0x%02x]", link->dpcd_caps.psr_info.psr_version);
 seq_puts(m, "\n");

 seq_printf(m, "Driver support: %s", str_yes_no(link->psr_settings.psr_feature_enabled));
 if (link->psr_settings.psr_version)
  seq_printf(m, " [0x%02x]", link->psr_settings.psr_version);
 seq_puts(m, "\n");

 return 0;
}

/*
 * Returns the current bpc for the crtc.
 * Example usage: cat /sys/kernel/debug/dri/0/crtc-0/amdgpu_current_bpc
 */

static int amdgpu_current_bpc_show(struct seq_file *m, void *data)
{
 struct drm_crtc *crtc = m->private;
 struct drm_device *dev = crtc->dev;
 struct dm_crtc_state *dm_crtc_state = NULL;
 int res = -ENODEV;
 unsigned int bpc;

 mutex_lock(&dev->mode_config.mutex);
 drm_modeset_lock(&crtc->mutex, NULL);
 if (crtc->state == NULL)
  goto unlock;

 dm_crtc_state = to_dm_crtc_state(crtc->state);
 if (dm_crtc_state->stream == NULL)
  goto unlock;

 switch (dm_crtc_state->stream->timing.display_color_depth) {
 case COLOR_DEPTH_666:
  bpc = 6;
  break;
 case COLOR_DEPTH_888:
  bpc = 8;
  break;
 case COLOR_DEPTH_101010:
  bpc = 10;
  break;
 case COLOR_DEPTH_121212:
  bpc = 12;
  break;
 case COLOR_DEPTH_161616:
  bpc = 16;
  break;
 default:
  goto unlock;
 }

 seq_printf(m, "Current: %u\n", bpc);
 res = 0;

unlock:
 drm_modeset_unlock(&crtc->mutex);
 mutex_unlock(&dev->mode_config.mutex);

 return res;
}
DEFINE_SHOW_ATTRIBUTE(amdgpu_current_bpc);

/*
 * Returns the current colorspace for the crtc.
 * Example usage: cat /sys/kernel/debug/dri/0/crtc-0/amdgpu_current_colorspace
 */

static int amdgpu_current_colorspace_show(struct seq_file *m, void *data)
{
 struct drm_crtc *crtc = m->private;
 struct drm_device *dev = crtc->dev;
 struct dm_crtc_state *dm_crtc_state = NULL;
 int res = -ENODEV;

 mutex_lock(&dev->mode_config.mutex);
 drm_modeset_lock(&crtc->mutex, NULL);
 if (crtc->state == NULL)
  goto unlock;

 dm_crtc_state = to_dm_crtc_state(crtc->state);
 if (dm_crtc_state->stream == NULL)
  goto unlock;

 switch (dm_crtc_state->stream->output_color_space) {
 case COLOR_SPACE_SRGB:
  seq_puts(m, "sRGB");
  break;
 case COLOR_SPACE_YCBCR601:
 case COLOR_SPACE_YCBCR601_LIMITED:
  seq_puts(m, "BT601_YCC");
  break;
 case COLOR_SPACE_YCBCR709:
 case COLOR_SPACE_YCBCR709_LIMITED:
  seq_puts(m, "BT709_YCC");
  break;
 case COLOR_SPACE_ADOBERGB:
  seq_puts(m, "opRGB");
  break;
 case COLOR_SPACE_2020_RGB_FULLRANGE:
  seq_puts(m, "BT2020_RGB");
  break;
 case COLOR_SPACE_2020_YCBCR_LIMITED:
  seq_puts(m, "BT2020_YCC");
  break;
 default:
  goto unlock;
 }
 res = 0;

unlock:
 drm_modeset_unlock(&crtc->mutex);
 mutex_unlock(&dev->mode_config.mutex);

 return res;
}
DEFINE_SHOW_ATTRIBUTE(amdgpu_current_colorspace);


/*
 * Example usage:
 * Disable dsc passthrough, i.e.,: have dsc decoding at converver, not external RX
 *   echo 1 /sys/kernel/debug/dri/0/DP-1/dsc_disable_passthrough
 * Enable dsc passthrough, i.e.,: have dsc passthrough to external RX
 *   echo 0 /sys/kernel/debug/dri/0/DP-1/dsc_disable_passthrough
 */

static ssize_t dp_dsc_passthrough_set(struct file *f, const char __user *buf,
     size_t size, loff_t *pos)
{
 struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private;
 char *wr_buf = NULL;
 uint32_t wr_buf_size = 42;
 int max_param_num = 1;
 long param;
 uint8_t param_nums = 0;

 if (size == 0)
  return -EINVAL;

 wr_buf = kcalloc(wr_buf_size, sizeof(char), GFP_KERNEL);

 if (!wr_buf) {
  DRM_DEBUG_DRIVER("no memory to allocate write buffer\n");
  return -ENOSPC;
 }

 if (parse_write_buffer_into_params(wr_buf, wr_buf_size,
        ¶m, buf,
        max_param_num,
        ¶m_nums)) {
  kfree(wr_buf);
  return -EINVAL;
 }

 aconnector->dsc_settings.dsc_force_disable_passthrough = param;

 kfree(wr_buf);
 return 0;
}

/*
 * Returns the HDCP capability of the Display (1.4 for now).
 *
 * NOTE* Not all HDMI displays report their HDCP caps even when they are capable.
 * Since its rare for a display to not be HDCP 1.4 capable, we set HDMI as always capable.
 *
 * Example usage: cat /sys/kernel/debug/dri/0/DP-1/hdcp_sink_capability
 * or cat /sys/kernel/debug/dri/0/HDMI-A-1/hdcp_sink_capability
 */

static int hdcp_sink_capability_show(struct seq_file *m, void *data)
{
 struct drm_connector *connector = m->private;
 struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
 bool hdcp_cap, hdcp2_cap;

 if (connector->status != connector_status_connected)
  return -ENODEV;

 seq_printf(m, "%s:%d HDCP version: ", connector->name, connector->base.id);

 hdcp_cap = dc_link_is_hdcp14(aconnector->dc_link, aconnector->dc_sink->sink_signal);
 hdcp2_cap = dc_link_is_hdcp22(aconnector->dc_link, aconnector->dc_sink->sink_signal);


 if (hdcp_cap)
  seq_printf(m, "%s ""HDCP1.4");
 if (hdcp2_cap)
  seq_printf(m, "%s ""HDCP2.2");

 if (!hdcp_cap && !hdcp2_cap)
  seq_printf(m, "%s ""None");

 seq_puts(m, "\n");

 return 0;
}

/*
 * Returns whether the connected display is internal and not hotpluggable.
 * Example usage: cat /sys/kernel/debug/dri/0/DP-1/internal_display
 */

static int internal_display_show(struct seq_file *m, void *data)
{
 struct drm_connector *connector = m->private;
 struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
 struct dc_link *link = aconnector->dc_link;

 seq_printf(m, "Internal: %u\n", link->is_internal_display);

 return 0;
}

/*
 * Returns the number of segments used if ODM Combine mode is enabled.
 * Example usage: cat /sys/kernel/debug/dri/0/DP-1/odm_combine_segments
 */

static int odm_combine_segments_show(struct seq_file *m, void *unused)
{
 struct drm_connector *connector = m->private;
 struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
 struct dc_link *link = aconnector->dc_link;
 struct pipe_ctx *pipe_ctx = NULL;
 int i, segments = -EOPNOTSUPP;

 for (i = 0; i < MAX_PIPES; i++) {
  pipe_ctx = &link->dc->current_state->res_ctx.pipe_ctx[i];
  if (pipe_ctx->stream &&
      pipe_ctx->stream->link == link)
   break;
 }

 if (connector->status != connector_status_connected)
  return -ENODEV;

 if (pipe_ctx && pipe_ctx->stream_res.tg &&
     pipe_ctx->stream_res.tg->funcs->get_odm_combine_segments)
  pipe_ctx->stream_res.tg->funcs->get_odm_combine_segments(pipe_ctx->stream_res.tg, &segments);

 seq_printf(m, "%d\n", segments);
 return 0;
}

/* function description
 *
 * generic SDP message access for testing
 *
 * debugfs sdp_message is located at /syskernel/debug/dri/0/DP-x
 *
 * SDP header
 * Hb0 : Secondary-Data Packet ID
 * Hb1 : Secondary-Data Packet type
 * Hb2 : Secondary-Data-packet-specific header, Byte 0
 * Hb3 : Secondary-Data-packet-specific header, Byte 1
 *
 * for using custom sdp message: input 4 bytes SDP header and 32 bytes raw data
 */

static ssize_t dp_sdp_message_debugfs_write(struct file *f, const char __user *buf,
     size_t size, loff_t *pos)
{
 int r;
 uint8_t data[36] = {0};
 struct amdgpu_dm_connector *connector = file_inode(f)->i_private;
 struct dm_crtc_state *acrtc_state;
 uint32_t write_size = 36;

 if (connector->base.status != connector_status_connected)
  return -ENODEV;

 if (size == 0)
  return 0;

 acrtc_state = to_dm_crtc_state(connector->base.state->crtc->state);

 r = copy_from_user(data, buf, write_size);

 write_size -= r;

 dc_stream_send_dp_sdp(acrtc_state->stream, data, write_size);

 return write_size;
}

/* function: Read link's DSC & FEC capabilities
 *
 *
 * Access it with the following command (you need to specify
 * connector like DP-1):
 *
 * cat /sys/kernel/debug/dri/0/DP-X/dp_dsc_fec_support
 *
 */

static int dp_dsc_fec_support_show(struct seq_file *m, void *data)
{
 struct drm_connector *connector = m->private;
 struct drm_modeset_acquire_ctx ctx;
 struct drm_device *dev = connector->dev;
 struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
 int ret = 0;
 bool try_again = false;
 bool is_fec_supported = false;
 bool is_dsc_supported = false;
 struct dpcd_caps dpcd_caps;

 drm_modeset_acquire_init(&ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE);
 do {
  try_again = false;
  ret = drm_modeset_lock(&dev->mode_config.connection_mutex, &ctx);
  if (ret) {
   if (ret == -EDEADLK) {
    ret = drm_modeset_backoff(&ctx);
    if (!ret) {
     try_again = true;
     continue;
    }
   }
   break;
  }
  if (connector->status != connector_status_connected) {
   ret = -ENODEV;
   break;
  }
  dpcd_caps = aconnector->dc_link->dpcd_caps;
  if (aconnector->mst_output_port) {
   /* aconnector sets dsc_aux during get_modes call
 * if MST connector has it means it can either
 * enable DSC on the sink device or on MST branch
 * its connected to.
 */

   if (aconnector->dsc_aux) {
    is_fec_supported = true;
    is_dsc_supported = true;
   }
  } else {
   is_fec_supported = dpcd_caps.fec_cap.raw & 0x1;
   is_dsc_supported = dpcd_caps.dsc_caps.dsc_basic_caps.raw[0] & 0x1;
  }
 } while (try_again);

 drm_modeset_drop_locks(&ctx);
 drm_modeset_acquire_fini(&ctx);

 seq_printf(m, "FEC_Sink_Support: %s\n", str_yes_no(is_fec_supported));
 seq_printf(m, "DSC_Sink_Support: %s\n", str_yes_no(is_dsc_supported));

 return ret;
}

/* function: Trigger virtual HPD redetection on connector
 *
 * This function will perform link rediscovery, link disable
 * and enable, and dm connector state update.
 *
 * Retrigger HPD on an existing connector by echoing 1 into
 * its respectful "trigger_hotplug" debugfs entry:
 *
 * echo 1 > /sys/kernel/debug/dri/0/DP-X/trigger_hotplug
 *
 * This function can perform HPD unplug:
 *
 * echo 0 > /sys/kernel/debug/dri/0/DP-X/trigger_hotplug
 *
 */

static ssize_t trigger_hotplug(struct file *f, const char __user *buf,
       size_t size, loff_t *pos)
{
 struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private;
 struct drm_connector *connector = &aconnector->base;
 struct dc_link *link = NULL;
 struct drm_device *dev = connector->dev;
 struct amdgpu_device *adev = drm_to_adev(dev);
 enum dc_connection_type new_connection_type = dc_connection_none;
 char *wr_buf = NULL;
 uint32_t wr_buf_size = 42;
 int max_param_num = 1;
 long param[1] = {0};
 uint8_t param_nums = 0;
 bool ret = false;

 if (!aconnector->dc_link)
  return -EINVAL;

 if (size == 0)
  return -EINVAL;

 wr_buf = kcalloc(wr_buf_size, sizeof(char), GFP_KERNEL);

 if (!wr_buf) {
  DRM_DEBUG_DRIVER("no memory to allocate write buffer\n");
  return -ENOSPC;
 }

 if (parse_write_buffer_into_params(wr_buf, wr_buf_size,
      (long *)param, buf,
      max_param_num,
      ¶m_nums)) {
  kfree(wr_buf);
  return -EINVAL;
 }

 kfree(wr_buf);

 if (param_nums <= 0) {
  DRM_DEBUG_DRIVER("user data not be read\n");
  return -EINVAL;
 }

 mutex_lock(&aconnector->hpd_lock);

 /* Don't support for mst end device*/
 if (aconnector->mst_root) {
  mutex_unlock(&aconnector->hpd_lock);
  return -EINVAL;
 }

 if (param[0] == 1) {

  if (!dc_link_detect_connection_type(aconnector->dc_link, &new_connection_type) &&
   new_connection_type != dc_connection_none)
   goto unlock;

  mutex_lock(&adev->dm.dc_lock);
  ret = dc_link_detect(aconnector->dc_link, DETECT_REASON_HPD);
  mutex_unlock(&adev->dm.dc_lock);

  if (!ret)
   goto unlock;

  amdgpu_dm_update_connector_after_detect(aconnector);

  drm_modeset_lock_all(dev);
  dm_restore_drm_connector_state(dev, connector);
  drm_modeset_unlock_all(dev);

  drm_kms_helper_connector_hotplug_event(connector);
 } else if (param[0] == 0) {
  if (!aconnector->dc_link)
   goto unlock;

  link = aconnector->dc_link;

  if (link->local_sink) {
   dc_sink_release(link->local_sink);
   link->local_sink = NULL;
  }

  link->dpcd_sink_count = 0;
  link->type = dc_connection_none;
  link->dongle_max_pix_clk = 0;

  amdgpu_dm_update_connector_after_detect(aconnector);

  /* If the aconnector is the root node in mst topology */
  if (aconnector->mst_mgr.mst_state == true)
   dc_link_reset_cur_dp_mst_topology(link);

  drm_modeset_lock_all(dev);
  dm_restore_drm_connector_state(dev, connector);
  drm_modeset_unlock_all(dev);

  drm_kms_helper_connector_hotplug_event(connector);
 }

unlock:
 mutex_unlock(&aconnector->hpd_lock);

 return size;
}

/* function: read DSC status on the connector
 *
 * The read function: dp_dsc_clock_en_read
 * returns current status of DSC clock on the connector.
 * The return is a boolean flag: 1 or 0.
 *
 * Access it with the following command (you need to specify
 * connector like DP-1):
 *
 * cat /sys/kernel/debug/dri/0/DP-X/dsc_clock_en
 *
 * Expected output:
 * 1 - means that DSC is currently enabled
 * 0 - means that DSC is disabled
 */

static ssize_t dp_dsc_clock_en_read(struct file *f, char __user *buf,
        size_t size, loff_t *pos)
{
 char *rd_buf = NULL;
 struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private;
 struct display_stream_compressor *dsc;
 struct dcn_dsc_state dsc_state = {0};
 const uint32_t rd_buf_size = 10;
 struct pipe_ctx *pipe_ctx;
 ssize_t result = 0;
 int i, r, str_len = 10;

 rd_buf = kcalloc(rd_buf_size, sizeof(char), GFP_KERNEL);

 if (!rd_buf)
  return -ENOMEM;

 for (i = 0; i < MAX_PIPES; i++) {
  pipe_ctx = &aconnector->dc_link->dc->current_state->res_ctx.pipe_ctx[i];
  if (pipe_ctx->stream &&
      pipe_ctx->stream->link == aconnector->dc_link &&
      pipe_ctx->stream->sink &&
      pipe_ctx->stream->sink == aconnector->dc_sink)
   break;
 }

 dsc = pipe_ctx->stream_res.dsc;
 if (dsc)
  dsc->funcs->dsc_read_state(dsc, &dsc_state);

 snprintf(rd_buf, str_len,
  "%d\n",
  dsc_state.dsc_clock_en);

 while (size) {
  if (*pos >= rd_buf_size)
   break;

  r = put_user(*(rd_buf + result), buf);
  if (r) {
   kfree(rd_buf);
   return r; /* r = -EFAULT */
  }

  buf += 1;
  size -= 1;
  *pos += 1;
  result += 1;
 }

 kfree(rd_buf);
 return result;
}

/* function: write force DSC on the connector
 *
 * The write function: dp_dsc_clock_en_write
 * enables to force DSC on the connector.
 * User can write to either force enable or force disable DSC
 * on the next modeset or set it to driver default
 *
 * Accepted inputs:
 * 0 - default DSC enablement policy
 * 1 - force enable DSC on the connector
 * 2 - force disable DSC on the connector (might cause fail in atomic_check)
 *
 * Writing DSC settings is done with the following command:
 * - To force enable DSC (you need to specify
 * connector like DP-1):
 *
 * echo 0x1 > /sys/kernel/debug/dri/0/DP-X/dsc_clock_en
 *
 * - To return to default state set the flag to zero and
 * let driver deal with DSC automatically
 * (you need to specify connector like DP-1):
 *
 * echo 0x0 > /sys/kernel/debug/dri/0/DP-X/dsc_clock_en
 *
 */

static ssize_t dp_dsc_clock_en_write(struct file *f, const char __user *buf,
         size_t size, loff_t *pos)
{
 struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private;
 struct drm_connector *connector = &aconnector->base;
 struct drm_device *dev = connector->dev;
 struct drm_crtc *crtc = NULL;
 struct dm_crtc_state *dm_crtc_state = NULL;
 struct pipe_ctx *pipe_ctx;
 int i;
 char *wr_buf = NULL;
 uint32_t wr_buf_size = 42;
 int max_param_num = 1;
 long param[1] = {0};
 uint8_t param_nums = 0;

 if (size == 0)
  return -EINVAL;

 wr_buf = kcalloc(wr_buf_size, sizeof(char), GFP_KERNEL);

 if (!wr_buf) {
  DRM_DEBUG_DRIVER("no memory to allocate write buffer\n");
  return -ENOSPC;
 }

 if (parse_write_buffer_into_params(wr_buf, wr_buf_size,
         (long *)param, buf,
         max_param_num,
         ¶m_nums)) {
  kfree(wr_buf);
  return -EINVAL;
 }

 if (param_nums <= 0) {
  DRM_DEBUG_DRIVER("user data not be read\n");
  kfree(wr_buf);
  return -EINVAL;
 }

 for (i = 0; i < MAX_PIPES; i++) {
  pipe_ctx = &aconnector->dc_link->dc->current_state->res_ctx.pipe_ctx[i];
  if (pipe_ctx->stream &&
      pipe_ctx->stream->link == aconnector->dc_link &&
      pipe_ctx->stream->sink &&
      pipe_ctx->stream->sink == aconnector->dc_sink)
   break;
 }

 if (!pipe_ctx->stream)
  goto done;

 // Get CRTC state
 mutex_lock(&dev->mode_config.mutex);
 drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);

 if (connector->state == NULL)
  goto unlock;

 crtc = connector->state->crtc;
 if (crtc == NULL)
  goto unlock;

 drm_modeset_lock(&crtc->mutex, NULL);
 if (crtc->state == NULL)
  goto unlock;

 dm_crtc_state = to_dm_crtc_state(crtc->state);
 if (dm_crtc_state->stream == NULL)
  goto unlock;

 if (param[0] == 1)
  aconnector->dsc_settings.dsc_force_enable = DSC_CLK_FORCE_ENABLE;
 else if (param[0] == 2)
  aconnector->dsc_settings.dsc_force_enable = DSC_CLK_FORCE_DISABLE;
 else
  aconnector->dsc_settings.dsc_force_enable = DSC_CLK_FORCE_DEFAULT;

 dm_crtc_state->dsc_force_changed = true;

unlock:
 if (crtc)
  drm_modeset_unlock(&crtc->mutex);
 drm_modeset_unlock(&dev->mode_config.connection_mutex);
 mutex_unlock(&dev->mode_config.mutex);

done:
 kfree(wr_buf);
 return size;
}

/* function: read DSC slice width parameter on the connector
 *
 * The read function: dp_dsc_slice_width_read
 * returns dsc slice width used in the current configuration
 * The return is an integer: 0 or other positive number
 *
 * Access the status with the following command:
 *
 * cat /sys/kernel/debug/dri/0/DP-X/dsc_slice_width
 *
 * 0 - means that DSC is disabled
 *
 * Any other number more than zero represents the
 * slice width currently used by DSC in pixels
 *
 */

static ssize_t dp_dsc_slice_width_read(struct file *f, char __user *buf,
        size_t size, loff_t *pos)
{
 char *rd_buf = NULL;
 struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private;
 struct display_stream_compressor *dsc;
 struct dcn_dsc_state dsc_state = {0};
 const uint32_t rd_buf_size = 100;
 struct pipe_ctx *pipe_ctx;
 ssize_t result = 0;
 int i, r, str_len = 30;

 rd_buf = kcalloc(rd_buf_size, sizeof(char), GFP_KERNEL);

 if (!rd_buf)
  return -ENOMEM;

 for (i = 0; i < MAX_PIPES; i++) {
  pipe_ctx = &aconnector->dc_link->dc->current_state->res_ctx.pipe_ctx[i];
  if (pipe_ctx->stream &&
      pipe_ctx->stream->link == aconnector->dc_link &&
      pipe_ctx->stream->sink &&
      pipe_ctx->stream->sink == aconnector->dc_sink)
   break;
 }

 dsc = pipe_ctx->stream_res.dsc;
 if (dsc)
  dsc->funcs->dsc_read_state(dsc, &dsc_state);

 snprintf(rd_buf, str_len,
  "%d\n",
  dsc_state.dsc_slice_width);

 while (size) {
  if (*pos >= rd_buf_size)
   break;

  r = put_user(*(rd_buf + result), buf);
  if (r) {
   kfree(rd_buf);
   return r; /* r = -EFAULT */
  }

  buf += 1;
  size -= 1;
  *pos += 1;
  result += 1;
 }

 kfree(rd_buf);
 return result;
}

/* function: write DSC slice width parameter
 *
 * The write function: dp_dsc_slice_width_write
 * overwrites automatically generated DSC configuration
 * of slice width.
 *
 * The user has to write the slice width divisible by the
 * picture width.
 *
 * Also the user has to write width in hexidecimal
 * rather than in decimal.
 *
 * Writing DSC settings is done with the following command:
 * - To force overwrite slice width: (example sets to 1920 pixels)
 *
 * echo 0x780 > /sys/kernel/debug/dri/0/DP-X/dsc_slice_width
 *
 *  - To stop overwriting and let driver find the optimal size,
 * set the width to zero:
 *
 * echo 0x0 > /sys/kernel/debug/dri/0/DP-X/dsc_slice_width
 *
 */

static ssize_t dp_dsc_slice_width_write(struct file *f, const char __user *buf,
         size_t size, loff_t *pos)
{
 struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private;
 struct pipe_ctx *pipe_ctx;
 struct drm_connector *connector = &aconnector->base;
 struct drm_device *dev = connector->dev;
 struct drm_crtc *crtc = NULL;
 struct dm_crtc_state *dm_crtc_state = NULL;
 int i;
 char *wr_buf = NULL;
 uint32_t wr_buf_size = 42;
 int max_param_num = 1;
 long param[1] = {0};
 uint8_t param_nums = 0;

 if (size == 0)
  return -EINVAL;

 wr_buf = kcalloc(wr_buf_size, sizeof(char), GFP_KERNEL);

 if (!wr_buf) {
  DRM_DEBUG_DRIVER("no memory to allocate write buffer\n");
  return -ENOSPC;
 }

 if (parse_write_buffer_into_params(wr_buf, wr_buf_size,
         (long *)param, buf,
         max_param_num,
         ¶m_nums)) {
  kfree(wr_buf);
  return -EINVAL;
 }

 if (param_nums <= 0) {
  DRM_DEBUG_DRIVER("user data not be read\n");
  kfree(wr_buf);
  return -EINVAL;
 }

 for (i = 0; i < MAX_PIPES; i++) {
  pipe_ctx = &aconnector->dc_link->dc->current_state->res_ctx.pipe_ctx[i];
  if (pipe_ctx->stream &&
      pipe_ctx->stream->link == aconnector->dc_link &&
      pipe_ctx->stream->sink &&
      pipe_ctx->stream->sink == aconnector->dc_sink)
   break;
 }

 if (!pipe_ctx->stream)
  goto done;

 // Safely get CRTC state
 mutex_lock(&dev->mode_config.mutex);
 drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);

 if (connector->state == NULL)
  goto unlock;

 crtc = connector->state->crtc;
 if (crtc == NULL)
  goto unlock;

 drm_modeset_lock(&crtc->mutex, NULL);
 if (crtc->state == NULL)
  goto unlock;

 dm_crtc_state = to_dm_crtc_state(crtc->state);
 if (dm_crtc_state->stream == NULL)
  goto unlock;

 if (param[0] > 0)
  aconnector->dsc_settings.dsc_num_slices_h = DIV_ROUND_UP(
     pipe_ctx->stream->timing.h_addressable,
     param[0]);
 else
  aconnector->dsc_settings.dsc_num_slices_h = 0;

 dm_crtc_state->dsc_force_changed = true;

unlock:
 if (crtc)
  drm_modeset_unlock(&crtc->mutex);
 drm_modeset_unlock(&dev->mode_config.connection_mutex);
 mutex_unlock(&dev->mode_config.mutex);

done:
 kfree(wr_buf);
 return size;
}

/* function: read DSC slice height parameter on the connector
 *
 * The read function: dp_dsc_slice_height_read
 * returns dsc slice height used in the current configuration
 * The return is an integer: 0 or other positive number
 *
 * Access the status with the following command:
 *
 * cat /sys/kernel/debug/dri/0/DP-X/dsc_slice_height
 *
 * 0 - means that DSC is disabled
 *
 * Any other number more than zero represents the
 * slice height currently used by DSC in pixels
 *
 */

static ssize_t dp_dsc_slice_height_read(struct file *f, char __user *buf,
        size_t size, loff_t *pos)
{
 char *rd_buf = NULL;
 struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private;
 struct display_stream_compressor *dsc;
 struct dcn_dsc_state dsc_state = {0};
 const uint32_t rd_buf_size = 100;
 struct pipe_ctx *pipe_ctx;
 ssize_t result = 0;
 int i, r, str_len = 30;

 rd_buf = kcalloc(rd_buf_size, sizeof(char), GFP_KERNEL);

 if (!rd_buf)
  return -ENOMEM;

 for (i = 0; i < MAX_PIPES; i++) {
  pipe_ctx = &aconnector->dc_link->dc->current_state->res_ctx.pipe_ctx[i];
  if (pipe_ctx->stream &&
      pipe_ctx->stream->link == aconnector->dc_link &&
      pipe_ctx->stream->sink &&
      pipe_ctx->stream->sink == aconnector->dc_sink)
   break;
 }

 dsc = pipe_ctx->stream_res.dsc;
 if (dsc)
  dsc->funcs->dsc_read_state(dsc, &dsc_state);

 snprintf(rd_buf, str_len,
  "%d\n",
  dsc_state.dsc_slice_height);

 while (size) {
  if (*pos >= rd_buf_size)
   break;

  r = put_user(*(rd_buf + result), buf);
  if (r) {
   kfree(rd_buf);
   return r; /* r = -EFAULT */
  }

  buf += 1;
  size -= 1;
  *pos += 1;
  result += 1;
 }

 kfree(rd_buf);
 return result;
}

/* function: write DSC slice height parameter
 *
 * The write function: dp_dsc_slice_height_write
 * overwrites automatically generated DSC configuration
 * of slice height.
 *
 * The user has to write the slice height divisible by the
 * picture height.
 *
 * Also the user has to write height in hexidecimal
 * rather than in decimal.
 *
 * Writing DSC settings is done with the following command:
 * - To force overwrite slice height (example sets to 128 pixels):
 *
 * echo 0x80 > /sys/kernel/debug/dri/0/DP-X/dsc_slice_height
 *
 *  - To stop overwriting and let driver find the optimal size,
 * set the height to zero:
 *
 * echo 0x0 > /sys/kernel/debug/dri/0/DP-X/dsc_slice_height
 *
 */

static ssize_t dp_dsc_slice_height_write(struct file *f, const char __user *buf,
         size_t size, loff_t *pos)
{
 struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private;
 struct drm_connector *connector = &aconnector->base;
 struct drm_device *dev = connector->dev;
 struct drm_crtc *crtc = NULL;
 struct dm_crtc_state *dm_crtc_state = NULL;
 struct pipe_ctx *pipe_ctx;
 int i;
 char *wr_buf = NULL;
 uint32_t wr_buf_size = 42;
 int max_param_num = 1;
 uint8_t param_nums = 0;
 long param[1] = {0};

 if (size == 0)
  return -EINVAL;

 wr_buf = kcalloc(wr_buf_size, sizeof(char), GFP_KERNEL);

 if (!wr_buf) {
  DRM_DEBUG_DRIVER("no memory to allocate write buffer\n");
  return -ENOSPC;
 }

 if (parse_write_buffer_into_params(wr_buf, wr_buf_size,
         (long *)param, buf,
         max_param_num,
         ¶m_nums)) {
  kfree(wr_buf);
  return -EINVAL;
 }

 if (param_nums <= 0) {
  DRM_DEBUG_DRIVER("user data not be read\n");
  kfree(wr_buf);
  return -EINVAL;
 }

 for (i = 0; i < MAX_PIPES; i++) {
  pipe_ctx = &aconnector->dc_link->dc->current_state->res_ctx.pipe_ctx[i];
  if (pipe_ctx->stream &&
      pipe_ctx->stream->link == aconnector->dc_link &&
      pipe_ctx->stream->sink &&
      pipe_ctx->stream->sink == aconnector->dc_sink)
   break;
 }

 if (!pipe_ctx->stream)
  goto done;

 // Get CRTC state
 mutex_lock(&dev->mode_config.mutex);
 drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);

 if (connector->state == NULL)
  goto unlock;

 crtc = connector->state->crtc;
 if (crtc == NULL)
  goto unlock;

 drm_modeset_lock(&crtc->mutex, NULL);
 if (crtc->state == NULL)
  goto unlock;

 dm_crtc_state = to_dm_crtc_state(crtc->state);
 if (dm_crtc_state->stream == NULL)
  goto unlock;

 if (param[0] > 0)
  aconnector->dsc_settings.dsc_num_slices_v = DIV_ROUND_UP(
     pipe_ctx->stream->timing.v_addressable,
     param[0]);
 else
  aconnector->dsc_settings.dsc_num_slices_v = 0;

 dm_crtc_state->dsc_force_changed = true;

unlock:
 if (crtc)
  drm_modeset_unlock(&crtc->mutex);
 drm_modeset_unlock(&dev->mode_config.connection_mutex);
 mutex_unlock(&dev->mode_config.mutex);

done:
 kfree(wr_buf);
 return size;
}

/* function: read DSC target rate on the connector in bits per pixel
 *
 * The read function: dp_dsc_bits_per_pixel_read
 * returns target rate of compression in bits per pixel
 * The return is an integer: 0 or other positive integer
 *
 * Access it with the following command:
 *
 * cat /sys/kernel/debug/dri/0/DP-X/dsc_bits_per_pixel
 *
 *  0 - means that DSC is disabled
 */

static ssize_t dp_dsc_bits_per_pixel_read(struct file *f, char __user *buf,
        size_t size, loff_t *pos)
{
 char *rd_buf = NULL;
 struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private;
 struct display_stream_compressor *dsc;
 struct dcn_dsc_state dsc_state = {0};
 const uint32_t rd_buf_size = 100;
 struct pipe_ctx *pipe_ctx;
 ssize_t result = 0;
 int i, r, str_len = 30;

 rd_buf = kcalloc(rd_buf_size, sizeof(char), GFP_KERNEL);

 if (!rd_buf)
  return -ENOMEM;

 for (i = 0; i < MAX_PIPES; i++) {
  pipe_ctx = &aconnector->dc_link->dc->current_state->res_ctx.pipe_ctx[i];
  if (pipe_ctx->stream &&
      pipe_ctx->stream->link == aconnector->dc_link &&
      pipe_ctx->stream->sink &&
      pipe_ctx->stream->sink == aconnector->dc_sink)
   break;
 }

 dsc = pipe_ctx->stream_res.dsc;
 if (dsc)
  dsc->funcs->dsc_read_state(dsc, &dsc_state);

 snprintf(rd_buf, str_len,
  "%d\n",
  dsc_state.dsc_bits_per_pixel);

 while (size) {
  if (*pos >= rd_buf_size)
   break;

  r = put_user(*(rd_buf + result), buf);
  if (r) {
   kfree(rd_buf);
   return r; /* r = -EFAULT */
  }

  buf += 1;
  size -= 1;
  *pos += 1;
  result += 1;
 }

 kfree(rd_buf);
 return result;
}

/* function: write DSC target rate in bits per pixel
 *
 * The write function: dp_dsc_bits_per_pixel_write
 * overwrites automatically generated DSC configuration
 * of DSC target bit rate.
 *
 * Also the user has to write bpp in hexidecimal
 * rather than in decimal.
 *
 * Writing DSC settings is done with the following command:
 * - To force overwrite rate (example sets to 256 bpp x 1/16):
 *
 * echo 0x100 > /sys/kernel/debug/dri/0/DP-X/dsc_bits_per_pixel
 *
 *  - To stop overwriting and let driver find the optimal rate,
 * set the rate to zero:
 *
 * echo 0x0 > /sys/kernel/debug/dri/0/DP-X/dsc_bits_per_pixel
 *
 */

static ssize_t dp_dsc_bits_per_pixel_write(struct file *f, const char __user *buf,
         size_t size, loff_t *pos)
{
 struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private;
 struct drm_connector *connector = &aconnector->base;
 struct drm_device *dev = connector->dev;
 struct drm_crtc *crtc = NULL;
 struct dm_crtc_state *dm_crtc_state = NULL;
 struct pipe_ctx *pipe_ctx;
 int i;
 char *wr_buf = NULL;
 uint32_t wr_buf_size = 42;
 int max_param_num = 1;
 uint8_t param_nums = 0;
 long param[1] = {0};

 if (size == 0)
  return -EINVAL;

 wr_buf = kcalloc(wr_buf_size, sizeof(char), GFP_KERNEL);

 if (!wr_buf) {
  DRM_DEBUG_DRIVER("no memory to allocate write buffer\n");
  return -ENOSPC;
 }

 if (parse_write_buffer_into_params(wr_buf, wr_buf_size,
         (long *)param, buf,
         max_param_num,
         ¶m_nums)) {
  kfree(wr_buf);
  return -EINVAL;
 }

 if (param_nums <= 0) {
  DRM_DEBUG_DRIVER("user data not be read\n");
  kfree(wr_buf);
  return -EINVAL;
 }

 for (i = 0; i < MAX_PIPES; i++) {
  pipe_ctx = &aconnector->dc_link->dc->current_state->res_ctx.pipe_ctx[i];
  if (pipe_ctx->stream &&
      pipe_ctx->stream->link == aconnector->dc_link &&
      pipe_ctx->stream->sink &&
      pipe_ctx->stream->sink == aconnector->dc_sink)
   break;
 }

 if (!pipe_ctx->stream)
  goto done;

 // Get CRTC state
 mutex_lock(&dev->mode_config.mutex);
 drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);

 if (connector->state == NULL)
  goto unlock;

 crtc = connector->state->crtc;
 if (crtc == NULL)
  goto unlock;

 drm_modeset_lock(&crtc->mutex, NULL);
 if (crtc->state == NULL)
  goto unlock;

 dm_crtc_state = to_dm_crtc_state(crtc->state);
 if (dm_crtc_state->stream == NULL)
  goto unlock;

 aconnector->dsc_settings.dsc_bits_per_pixel = param[0];

 dm_crtc_state->dsc_force_changed = true;

unlock:
 if (crtc)
  drm_modeset_unlock(&crtc->mutex);
 drm_modeset_unlock(&dev->mode_config.connection_mutex);
 mutex_unlock(&dev->mode_config.mutex);

done:
 kfree(wr_buf);
 return size;
}

/* function: read DSC picture width parameter on the connector
 *
 * The read function: dp_dsc_pic_width_read
 * returns dsc picture width used in the current configuration
 * It is the same as h_addressable of the current
 * display's timing
 * The return is an integer: 0 or other positive integer
 * If 0 then DSC is disabled.
 *
 * Access it with the following command:
 *
 * cat /sys/kernel/debug/dri/0/DP-X/dsc_pic_width
 *
 * 0 - means that DSC is disabled
 */

static ssize_t dp_dsc_pic_width_read(struct file *f, char __user *buf,
        size_t size, loff_t *pos)
{
 char *rd_buf = NULL;
 struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private;
 struct display_stream_compressor *dsc;
 struct dcn_dsc_state dsc_state = {0};
 const uint32_t rd_buf_size = 100;
 struct pipe_ctx *pipe_ctx;
 ssize_t result = 0;
 int i, r, str_len = 30;

 rd_buf = kcalloc(rd_buf_size, sizeof(char), GFP_KERNEL);

 if (!rd_buf)
  return -ENOMEM;

 for (i = 0; i < MAX_PIPES; i++) {
  pipe_ctx = &aconnector->dc_link->dc->current_state->res_ctx.pipe_ctx[i];
  if (pipe_ctx->stream &&
      pipe_ctx->stream->link == aconnector->dc_link &&
      pipe_ctx->stream->sink &&
      pipe_ctx->stream->sink == aconnector->dc_sink)
   break;
 }

 dsc = pipe_ctx->stream_res.dsc;
 if (dsc)
  dsc->funcs->dsc_read_state(dsc, &dsc_state);

 snprintf(rd_buf, str_len,
  "%d\n",
  dsc_state.dsc_pic_width);

 while (size) {
  if (*pos >= rd_buf_size)
   break;

  r = put_user(*(rd_buf + result), buf);
  if (r) {
   kfree(rd_buf);
   return r; /* r = -EFAULT */
  }

  buf += 1;
  size -= 1;
  *pos += 1;
  result += 1;
 }

 kfree(rd_buf);
 return result;
}

static ssize_t dp_dsc_pic_height_read(struct file *f, char __user *buf,
        size_t size, loff_t *pos)
{
 char *rd_buf = NULL;
 struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private;
 struct display_stream_compressor *dsc;
 struct dcn_dsc_state dsc_state = {0};
 const uint32_t rd_buf_size = 100;
 struct pipe_ctx *pipe_ctx;
 ssize_t result = 0;
 int i, r, str_len = 30;

 rd_buf = kcalloc(rd_buf_size, sizeof(char), GFP_KERNEL);

 if (!rd_buf)
  return -ENOMEM;

 for (i = 0; i < MAX_PIPES; i++) {
  pipe_ctx = &aconnector->dc_link->dc->current_state->res_ctx.pipe_ctx[i];
  if (pipe_ctx->stream &&
      pipe_ctx->stream->link == aconnector->dc_link &&
      pipe_ctx->stream->sink &&
      pipe_ctx->stream->sink == aconnector->dc_sink)
   break;
 }

 dsc = pipe_ctx->stream_res.dsc;
 if (dsc)
  dsc->funcs->dsc_read_state(dsc, &dsc_state);

 snprintf(rd_buf, str_len,
  "%d\n",
  dsc_state.dsc_pic_height);

 while (size) {
  if (*pos >= rd_buf_size)
   break;

  r = put_user(*(rd_buf + result), buf);
  if (r) {
   kfree(rd_buf);
   return r; /* r = -EFAULT */
  }

  buf += 1;
  size -= 1;
  *pos += 1;
  result += 1;
 }

 kfree(rd_buf);
 return result;
}

/* function: read DSC chunk size parameter on the connector
 *
 * The read function: dp_dsc_chunk_size_read
 * returns dsc chunk size set in the current configuration
 * The value is calculated automatically by DSC code
 * and depends on slice parameters and bpp target rate
 * The return is an integer: 0 or other positive integer
 * If 0 then DSC is disabled.
 *
 * Access it with the following command:
 *
 * cat /sys/kernel/debug/dri/0/DP-X/dsc_chunk_size
 *
 * 0 - means that DSC is disabled
 */

static ssize_t dp_dsc_chunk_size_read(struct file *f, char __user *buf,
        size_t size, loff_t *pos)
{
 char *rd_buf = NULL;
 struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private;
 struct display_stream_compressor *dsc;
 struct dcn_dsc_state dsc_state = {0};
 const uint32_t rd_buf_size = 100;
 struct pipe_ctx *pipe_ctx;
 ssize_t result = 0;
 int i, r, str_len = 30;

 rd_buf = kcalloc(rd_buf_size, sizeof(char), GFP_KERNEL);

 if (!rd_buf)
  return -ENOMEM;

 for (i = 0; i < MAX_PIPES; i++) {
  pipe_ctx = &aconnector->dc_link->dc->current_state->res_ctx.pipe_ctx[i];
  if (pipe_ctx->stream &&
      pipe_ctx->stream->link == aconnector->dc_link &&
      pipe_ctx->stream->sink &&
      pipe_ctx->stream->sink == aconnector->dc_sink)
   break;
 }

 dsc = pipe_ctx->stream_res.dsc;
 if (dsc)
  dsc->funcs->dsc_read_state(dsc, &dsc_state);

 snprintf(rd_buf, str_len,
  "%d\n",
  dsc_state.dsc_chunk_size);

 while (size) {
  if (*pos >= rd_buf_size)
   break;

  r = put_user(*(rd_buf + result), buf);
  if (r) {
   kfree(rd_buf);
   return r; /* r = -EFAULT */
  }

  buf += 1;
  size -= 1;
  *pos += 1;
  result += 1;
 }

 kfree(rd_buf);
 return result;
}

/* function: read DSC slice bpg offset on the connector
 *
 * The read function: dp_dsc_slice_bpg_offset_read
 * returns dsc bpg slice offset set in the current configuration
 * The value is calculated automatically by DSC code
 * and depends on slice parameters and bpp target rate
 * The return is an integer: 0 or other positive integer
 * If 0 then DSC is disabled.
 *
 * Access it with the following command:
 *
 * cat /sys/kernel/debug/dri/0/DP-X/dsc_slice_bpg_offset
 *
 * 0 - means that DSC is disabled
 */

static ssize_t dp_dsc_slice_bpg_offset_read(struct file *f, char __user *buf,
        size_t size, loff_t *pos)
{
 char *rd_buf = NULL;
 struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private;
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=96 H=95 G=95

¤ Dauer der Verarbeitung: 0.20 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.