diff options
Diffstat (limited to 'drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c')
-rw-r--r-- | drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c | 696 |
1 files changed, 652 insertions, 44 deletions
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c index 360952129b6d..9a13f47022df 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c @@ -25,8 +25,6 @@ #include <linux/uaccess.h> -#include <drm/drm_debugfs.h> - #include "dc.h" #include "amdgpu.h" #include "amdgpu_dm.h" @@ -36,6 +34,8 @@ #include "resource.h" #include "dsc.h" #include "dc_link_dp.h" +#include "link_hwss.h" +#include "dc/dc_dmub_srv.h" struct dmub_debugfs_trace_header { uint32_t entry_count; @@ -150,7 +150,7 @@ static int parse_write_buffer_into_params(char *wr_buf, uint32_t wr_buf_size, * * --- to get dp configuration * - * cat link_settings + * cat /sys/kernel/debug/dri/0/DP-x/link_settings * * It will list current, verified, reported, preferred dp configuration. * current -- for current video mode @@ -163,7 +163,7 @@ static int parse_write_buffer_into_params(char *wr_buf, uint32_t wr_buf_size, * echo <lane_count> <link_rate> > link_settings * * for example, to force to 2 lane, 2.7GHz, - * echo 4 0xa > link_settings + * echo 4 0xa > /sys/kernel/debug/dri/0/DP-x/link_settings * * spread_spectrum could not be changed dynamically. * @@ -171,7 +171,7 @@ static int parse_write_buffer_into_params(char *wr_buf, uint32_t wr_buf_size, * done. please check link settings after force operation to see if HW get * programming. * - * cat link_settings + * cat /sys/kernel/debug/dri/0/DP-x/link_settings * * check current and preferred settings. * @@ -247,7 +247,6 @@ static ssize_t dp_link_settings_write(struct file *f, const char __user *buf, { struct amdgpu_dm_connector *connector = file_inode(f)->i_private; struct dc_link *link = connector->dc_link; - struct dc *dc = (struct dc *)link->dc; struct dc_link_settings prefer_link_settings; char *wr_buf = NULL; const uint32_t wr_buf_size = 40; @@ -255,7 +254,7 @@ static ssize_t dp_link_settings_write(struct file *f, const char __user *buf, int max_param_num = 2; uint8_t param_nums = 0; long param[2]; - bool valid_input = false; + bool valid_input = true; if (size == 0) return -EINVAL; @@ -282,9 +281,9 @@ static ssize_t dp_link_settings_write(struct file *f, const char __user *buf, case LANE_COUNT_ONE: case LANE_COUNT_TWO: case LANE_COUNT_FOUR: - valid_input = true; break; default: + valid_input = false; break; } @@ -294,9 +293,9 @@ static ssize_t dp_link_settings_write(struct file *f, const char __user *buf, case LINK_RATE_RBR2: case LINK_RATE_HIGH2: case LINK_RATE_HIGH3: - valid_input = true; break; default: + valid_input = false; break; } @@ -310,10 +309,11 @@ static ssize_t dp_link_settings_write(struct file *f, const char __user *buf, * 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]; - dc_link_set_preferred_link_settings(dc, &prefer_link_settings, link); + dp_retrain_link_dp_test(link, &prefer_link_settings, false); kfree(wr_buf); return size; @@ -400,6 +400,70 @@ static ssize_t dp_phy_settings_read(struct file *f, char __user *buf, return result; } +static int dp_lttpr_status_show(struct seq_file *m, void *d) +{ + char *data; + struct amdgpu_dm_connector *connector = file_inode(m->file)->i_private; + struct dc_link *link = connector->dc_link; + uint32_t read_size = 1; + uint8_t repeater_count = 0; + + data = kzalloc(read_size, GFP_KERNEL); + if (!data) + return 0; + + dm_helpers_dp_read_dpcd(link->ctx, link, 0xF0002, data, read_size); + + switch ((uint8_t)*data) { + case 0x80: + repeater_count = 1; + break; + case 0x40: + repeater_count = 2; + break; + case 0x20: + repeater_count = 3; + break; + case 0x10: + repeater_count = 4; + break; + case 0x8: + repeater_count = 5; + break; + case 0x4: + repeater_count = 6; + break; + case 0x2: + repeater_count = 7; + break; + case 0x1: + repeater_count = 8; + break; + case 0x0: + repeater_count = 0; + break; + default: + repeater_count = (uint8_t)*data; + break; + } + + seq_printf(m, "phy repeater count: %d\n", repeater_count); + + dm_helpers_dp_read_dpcd(link->ctx, link, 0xF0003, data, read_size); + + if ((uint8_t)*data == 0x55) + seq_printf(m, "phy repeater mode: transparent\n"); + else if ((uint8_t)*data == 0xAA) + seq_printf(m, "phy repeater mode: non-transparent\n"); + else if ((uint8_t)*data == 0x00) + seq_printf(m, "phy repeater mode: non lttpr\n"); + else + seq_printf(m, "phy repeater mode: read error\n"); + + kfree(data); + return 0; +} + static ssize_t dp_phy_settings_write(struct file *f, const char __user *buf, size_t size, loff_t *pos) { @@ -2154,10 +2218,154 @@ static ssize_t dp_dsc_slice_bpg_offset_read(struct file *f, char __user *buf, return result; } + +/* + * function description: Read max_requested_bpc property from the connector + * + * Access it with the following command: + * + * cat /sys/kernel/debug/dri/0/DP-X/max_bpc + * + */ +static ssize_t dp_max_bpc_read(struct file *f, 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 dm_connector_state *state; + ssize_t result = 0; + char *rd_buf = NULL; + char *rd_buf_ptr = NULL; + const uint32_t rd_buf_size = 10; + int r; + + rd_buf = kcalloc(rd_buf_size, sizeof(char), GFP_KERNEL); + + if (!rd_buf) + return -ENOMEM; + + mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); + + if (connector->state == NULL) + goto unlock; + + state = to_dm_connector_state(connector->state); + + rd_buf_ptr = rd_buf; + snprintf(rd_buf_ptr, rd_buf_size, + "%u\n", + state->base.max_requested_bpc); + + while (size) { + if (*pos >= rd_buf_size) + break; + + r = put_user(*(rd_buf + result), buf); + if (r) { + result = r; /* r = -EFAULT */ + goto unlock; + } + buf += 1; + size -= 1; + *pos += 1; + result += 1; + } +unlock: + drm_modeset_unlock(&dev->mode_config.connection_mutex); + mutex_unlock(&dev->mode_config.mutex); + kfree(rd_buf); + return result; +} + + +/* + * function description: Set max_requested_bpc property on the connector + * + * This function will not force the input BPC on connector, it will only + * change the max value. This is equivalent to setting max_bpc through + * xrandr. + * + * The BPC value written must be >= 6 and <= 16. Values outside of this + * range will result in errors. + * + * BPC values: + * 0x6 - 6 BPC + * 0x8 - 8 BPC + * 0xa - 10 BPC + * 0xc - 12 BPC + * 0x10 - 16 BPC + * + * Write the max_bpc in the following way: + * + * echo 0x6 > /sys/kernel/debug/dri/0/DP-X/max_bpc + * + */ +static ssize_t dp_max_bpc_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 dm_connector_state *state; + struct drm_device *dev = connector->dev; + 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, 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; + } + + if (param[0] < 6 || param[0] > 16) { + DRM_DEBUG_DRIVER("bad max_bpc value\n"); + kfree(wr_buf); + return -EINVAL; + } + + mutex_lock(&dev->mode_config.mutex); + drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); + + if (connector->state == NULL) + goto unlock; + + state = to_dm_connector_state(connector->state); + state->base.max_requested_bpc = param[0]; +unlock: + drm_modeset_unlock(&dev->mode_config.connection_mutex); + mutex_unlock(&dev->mode_config.mutex); + + kfree(wr_buf); + return size; +} + DEFINE_SHOW_ATTRIBUTE(dp_dsc_fec_support); DEFINE_SHOW_ATTRIBUTE(dmub_fw_state); DEFINE_SHOW_ATTRIBUTE(dmub_tracebuffer); DEFINE_SHOW_ATTRIBUTE(output_bpc); +DEFINE_SHOW_ATTRIBUTE(dp_lttpr_status); #ifdef CONFIG_DRM_AMD_DC_HDCP DEFINE_SHOW_ATTRIBUTE(hdcp_sink_capability); #endif @@ -2265,12 +2473,20 @@ static const struct file_operations dp_dpcd_data_debugfs_fops = { .llseek = default_llseek }; +static const struct file_operations dp_max_bpc_debugfs_fops = { + .owner = THIS_MODULE, + .read = dp_max_bpc_read, + .write = dp_max_bpc_write, + .llseek = default_llseek +}; + static const struct { char *name; const struct file_operations *fops; } dp_debugfs_entries[] = { {"link_settings", &dp_link_settings_debugfs_fops}, {"phy_settings", &dp_phy_settings_debugfs_fop}, + {"lttpr_status", &dp_lttpr_status_fops}, {"test_pattern", &dp_phy_test_pattern_fops}, #ifdef CONFIG_DRM_AMD_DC_HDCP {"hdcp_sink_capability", &hdcp_sink_capability_fops}, @@ -2287,7 +2503,8 @@ static const struct { {"dsc_pic_height", &dp_dsc_pic_height_debugfs_fops}, {"dsc_chunk_size", &dp_dsc_chunk_size_debugfs_fops}, {"dsc_slice_bpg", &dp_dsc_slice_bpg_offset_debugfs_fops}, - {"dp_dsc_fec_support", &dp_dsc_fec_support_fops} + {"dp_dsc_fec_support", &dp_dsc_fec_support_fops}, + {"max_bpc", &dp_max_bpc_debugfs_fops} }; #ifdef CONFIG_DRM_AMD_DC_HDCP @@ -2341,9 +2558,51 @@ static int psr_get(void *data, u64 *val) return 0; } +/* + * Set dmcub trace event IRQ enable or disable. + * Usage to enable dmcub trace event IRQ: echo 1 > /sys/kernel/debug/dri/0/amdgpu_dm_dmcub_trace_event_en + * Usage to disable dmcub trace event IRQ: echo 0 > /sys/kernel/debug/dri/0/amdgpu_dm_dmcub_trace_event_en + */ +static int dmcub_trace_event_state_set(void *data, u64 val) +{ + struct amdgpu_device *adev = data; + + if (val == 1 || val == 0) { + dc_dmub_trace_event_control(adev->dm.dc, val); + adev->dm.dmcub_trace_event_en = (bool)val; + } else + return 0; + + return 0; +} + +/* + * The interface doesn't need get function, so it will return the + * value of zero + * Usage: cat /sys/kernel/debug/dri/0/amdgpu_dm_dmcub_trace_event_en + */ +static int dmcub_trace_event_state_get(void *data, u64 *val) +{ + struct amdgpu_device *adev = data; + + *val = adev->dm.dmcub_trace_event_en; + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(dmcub_trace_event_state_fops, dmcub_trace_event_state_get, + dmcub_trace_event_state_set, "%llu\n"); DEFINE_DEBUGFS_ATTRIBUTE(psr_fops, psr_get, NULL, "%llu\n"); +static const struct { + char *name; + const struct file_operations *fops; +} connector_debugfs_entries[] = { + {"force_yuv420_output", &force_yuv420_output_fops}, + {"output_bpc", &output_bpc_fops}, + {"trigger_hotplug", &trigger_hotplug_debugfs_fops} +}; + void connector_debugfs_init(struct amdgpu_dm_connector *connector) { int i; @@ -2360,14 +2619,11 @@ void connector_debugfs_init(struct amdgpu_dm_connector *connector) if (connector->base.connector_type == DRM_MODE_CONNECTOR_eDP) debugfs_create_file_unsafe("psr_state", 0444, dir, connector, &psr_fops); - debugfs_create_file_unsafe("force_yuv420_output", 0644, dir, connector, - &force_yuv420_output_fops); - - debugfs_create_file("output_bpc", 0644, dir, connector, - &output_bpc_fops); - - debugfs_create_file("trigger_hotplug", 0644, dir, connector, - &trigger_hotplug_debugfs_fops); + for (i = 0; i < ARRAY_SIZE(connector_debugfs_entries); i++) { + debugfs_create_file(connector_debugfs_entries[i].name, + 0644, dir, connector, + connector_debugfs_entries[i].fops); + } connector->debugfs_dpcd_address = 0; connector->debugfs_dpcd_size = 0; @@ -2383,6 +2639,225 @@ void connector_debugfs_init(struct amdgpu_dm_connector *connector) #endif } +#ifdef CONFIG_DRM_AMD_SECURE_DISPLAY +/* + * Set crc window coordinate x start + */ +static int crc_win_x_start_set(void *data, u64 val) +{ + struct drm_crtc *crtc = data; + struct drm_device *drm_dev = crtc->dev; + struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc); + + spin_lock_irq(&drm_dev->event_lock); + acrtc->dm_irq_params.crc_window.x_start = (uint16_t) val; + acrtc->dm_irq_params.crc_window.update_win = false; + spin_unlock_irq(&drm_dev->event_lock); + + return 0; +} + +/* + * Get crc window coordinate x start + */ +static int crc_win_x_start_get(void *data, u64 *val) +{ + struct drm_crtc *crtc = data; + struct drm_device *drm_dev = crtc->dev; + struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc); + + spin_lock_irq(&drm_dev->event_lock); + *val = acrtc->dm_irq_params.crc_window.x_start; + spin_unlock_irq(&drm_dev->event_lock); + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(crc_win_x_start_fops, crc_win_x_start_get, + crc_win_x_start_set, "%llu\n"); + + +/* + * Set crc window coordinate y start + */ +static int crc_win_y_start_set(void *data, u64 val) +{ + struct drm_crtc *crtc = data; + struct drm_device *drm_dev = crtc->dev; + struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc); + + spin_lock_irq(&drm_dev->event_lock); + acrtc->dm_irq_params.crc_window.y_start = (uint16_t) val; + acrtc->dm_irq_params.crc_window.update_win = false; + spin_unlock_irq(&drm_dev->event_lock); + + return 0; +} + +/* + * Get crc window coordinate y start + */ +static int crc_win_y_start_get(void *data, u64 *val) +{ + struct drm_crtc *crtc = data; + struct drm_device *drm_dev = crtc->dev; + struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc); + + spin_lock_irq(&drm_dev->event_lock); + *val = acrtc->dm_irq_params.crc_window.y_start; + spin_unlock_irq(&drm_dev->event_lock); + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(crc_win_y_start_fops, crc_win_y_start_get, + crc_win_y_start_set, "%llu\n"); + +/* + * Set crc window coordinate x end + */ +static int crc_win_x_end_set(void *data, u64 val) +{ + struct drm_crtc *crtc = data; + struct drm_device *drm_dev = crtc->dev; + struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc); + + spin_lock_irq(&drm_dev->event_lock); + acrtc->dm_irq_params.crc_window.x_end = (uint16_t) val; + acrtc->dm_irq_params.crc_window.update_win = false; + spin_unlock_irq(&drm_dev->event_lock); + + return 0; +} + +/* + * Get crc window coordinate x end + */ +static int crc_win_x_end_get(void *data, u64 *val) +{ + struct drm_crtc *crtc = data; + struct drm_device *drm_dev = crtc->dev; + struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc); + + spin_lock_irq(&drm_dev->event_lock); + *val = acrtc->dm_irq_params.crc_window.x_end; + spin_unlock_irq(&drm_dev->event_lock); + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(crc_win_x_end_fops, crc_win_x_end_get, + crc_win_x_end_set, "%llu\n"); + +/* + * Set crc window coordinate y end + */ +static int crc_win_y_end_set(void *data, u64 val) +{ + struct drm_crtc *crtc = data; + struct drm_device *drm_dev = crtc->dev; + struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc); + + spin_lock_irq(&drm_dev->event_lock); + acrtc->dm_irq_params.crc_window.y_end = (uint16_t) val; + acrtc->dm_irq_params.crc_window.update_win = false; + spin_unlock_irq(&drm_dev->event_lock); + + return 0; +} + +/* + * Get crc window coordinate y end + */ +static int crc_win_y_end_get(void *data, u64 *val) +{ + struct drm_crtc *crtc = data; + struct drm_device *drm_dev = crtc->dev; + struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc); + + spin_lock_irq(&drm_dev->event_lock); + *val = acrtc->dm_irq_params.crc_window.y_end; + spin_unlock_irq(&drm_dev->event_lock); + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(crc_win_y_end_fops, crc_win_y_end_get, + crc_win_y_end_set, "%llu\n"); +/* + * Trigger to commit crc window + */ +static int crc_win_update_set(void *data, u64 val) +{ + struct drm_crtc *new_crtc = data; + struct drm_crtc *old_crtc = NULL; + struct amdgpu_crtc *new_acrtc, *old_acrtc; + struct amdgpu_device *adev = drm_to_adev(new_crtc->dev); + struct crc_rd_work *crc_rd_wrk = adev->dm.crc_rd_wrk; + + if (val) { + spin_lock_irq(&adev_to_drm(adev)->event_lock); + spin_lock_irq(&crc_rd_wrk->crc_rd_work_lock); + if (crc_rd_wrk && crc_rd_wrk->crtc) { + old_crtc = crc_rd_wrk->crtc; + old_acrtc = to_amdgpu_crtc(old_crtc); + } + new_acrtc = to_amdgpu_crtc(new_crtc); + + if (old_crtc && old_crtc != new_crtc) { + old_acrtc->dm_irq_params.crc_window.activated = false; + old_acrtc->dm_irq_params.crc_window.update_win = false; + old_acrtc->dm_irq_params.crc_window.skip_frame_cnt = 0; + + new_acrtc->dm_irq_params.crc_window.activated = true; + new_acrtc->dm_irq_params.crc_window.update_win = true; + new_acrtc->dm_irq_params.crc_window.skip_frame_cnt = 0; + crc_rd_wrk->crtc = new_crtc; + } else { + new_acrtc->dm_irq_params.crc_window.activated = true; + new_acrtc->dm_irq_params.crc_window.update_win = true; + new_acrtc->dm_irq_params.crc_window.skip_frame_cnt = 0; + crc_rd_wrk->crtc = new_crtc; + } + spin_unlock_irq(&crc_rd_wrk->crc_rd_work_lock); + spin_unlock_irq(&adev_to_drm(adev)->event_lock); + } + + return 0; +} + +/* + * Get crc window update flag + */ +static int crc_win_update_get(void *data, u64 *val) +{ + *val = 0; + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(crc_win_update_fops, crc_win_update_get, + crc_win_update_set, "%llu\n"); + +void crtc_debugfs_init(struct drm_crtc *crtc) +{ + struct dentry *dir = debugfs_lookup("crc", crtc->debugfs_entry); + + if (!dir) + return; + + debugfs_create_file_unsafe("crc_win_x_start", 0644, dir, crtc, + &crc_win_x_start_fops); + debugfs_create_file_unsafe("crc_win_y_start", 0644, dir, crtc, + &crc_win_y_start_fops); + debugfs_create_file_unsafe("crc_win_x_end", 0644, dir, crtc, + &crc_win_x_end_fops); + debugfs_create_file_unsafe("crc_win_y_end", 0644, dir, crtc, + &crc_win_y_end_fops); + debugfs_create_file_unsafe("crc_win_update", 0644, dir, crtc, + &crc_win_update_fops); + +} +#endif /* * Writes DTN log state to the user supplied buffer. * Example usage: cat /sys/kernel/debug/dri/0/amdgpu_dm_dtn_log @@ -2450,11 +2925,9 @@ static ssize_t dtn_log_write( * As written to display, taking ABM and backlight lut into account. * Ranges from 0x0 to 0x10000 (= 100% PWM) */ -static int current_backlight_read(struct seq_file *m, void *data) +static int current_backlight_show(struct seq_file *m, void *unused) { - struct drm_info_node *node = (struct drm_info_node *)m->private; - struct drm_device *dev = node->minor->dev; - struct amdgpu_device *adev = drm_to_adev(dev); + struct amdgpu_device *adev = (struct amdgpu_device *)m->private; struct amdgpu_display_manager *dm = &adev->dm; unsigned int backlight = dc_link_get_backlight_level(dm->backlight_link); @@ -2468,11 +2941,9 @@ static int current_backlight_read(struct seq_file *m, void *data) * As written to display, taking ABM and backlight lut into account. * Ranges from 0x0 to 0x10000 (= 100% PWM) */ -static int target_backlight_read(struct seq_file *m, void *data) +static int target_backlight_show(struct seq_file *m, void *unused) { - struct drm_info_node *node = (struct drm_info_node *)m->private; - struct drm_device *dev = node->minor->dev; - struct amdgpu_device *adev = drm_to_adev(dev); + struct amdgpu_device *adev = (struct amdgpu_device *)m->private; struct amdgpu_display_manager *dm = &adev->dm; unsigned int backlight = dc_link_get_target_backlight_pwm(dm->backlight_link); @@ -2481,10 +2952,10 @@ static int target_backlight_read(struct seq_file *m, void *data) return 0; } -static int mst_topo(struct seq_file *m, void *unused) +static int mst_topo_show(struct seq_file *m, void *unused) { - struct drm_info_node *node = (struct drm_info_node *)m->private; - struct drm_device *dev = node->minor->dev; + struct amdgpu_device *adev = (struct amdgpu_device *)m->private; + struct drm_device *dev = adev_to_drm(adev); struct drm_connector *connector; struct drm_connector_list_iter conn_iter; struct amdgpu_dm_connector *aconnector; @@ -2496,6 +2967,10 @@ static int mst_topo(struct seq_file *m, void *unused) aconnector = to_amdgpu_dm_connector(connector); + /* Ensure we're only dumping the topology of a root mst node */ + if (!aconnector->mst_mgr.mst_state) + continue; + seq_printf(m, "\nMST topology for connector %d\n", aconnector->connector_id); drm_dp_mst_dump_topology(m, &aconnector->mst_mgr); } @@ -2504,14 +2979,74 @@ static int mst_topo(struct seq_file *m, void *unused) return 0; } -static const struct drm_info_list amdgpu_dm_debugfs_list[] = { - {"amdgpu_current_backlight_pwm", ¤t_backlight_read}, - {"amdgpu_target_backlight_pwm", &target_backlight_read}, - {"amdgpu_mst_topology", &mst_topo}, -}; +/* + * Sets trigger hpd for MST topologies. + * All connected connectors will be rediscovered and re started as needed if val of 1 is sent. + * All topologies will be disconnected if val of 0 is set . + * Usage to enable topologies: echo 1 > /sys/kernel/debug/dri/0/amdgpu_dm_trigger_hpd_mst + * Usage to disable topologies: echo 0 > /sys/kernel/debug/dri/0/amdgpu_dm_trigger_hpd_mst + */ +static int trigger_hpd_mst_set(void *data, u64 val) +{ + struct amdgpu_device *adev = data; + struct drm_device *dev = adev_to_drm(adev); + struct drm_connector_list_iter iter; + struct amdgpu_dm_connector *aconnector; + struct drm_connector *connector; + struct dc_link *link = NULL; + + if (val == 1) { + drm_connector_list_iter_begin(dev, &iter); + drm_for_each_connector_iter(connector, &iter) { + aconnector = to_amdgpu_dm_connector(connector); + if (aconnector->dc_link->type == dc_connection_mst_branch && + aconnector->mst_mgr.aux) { + dc_link_detect(aconnector->dc_link, DETECT_REASON_HPD); + drm_dp_mst_topology_mgr_set_mst(&aconnector->mst_mgr, true); + } + } + } else if (val == 0) { + drm_connector_list_iter_begin(dev, &iter); + drm_for_each_connector_iter(connector, &iter) { + aconnector = to_amdgpu_dm_connector(connector); + if (!aconnector->dc_link) + continue; + + if (!(aconnector->port && &aconnector->mst_port->mst_mgr)) + continue; + + link = aconnector->dc_link; + dp_receiver_power_ctrl(link, false); + drm_dp_mst_topology_mgr_set_mst(&aconnector->mst_port->mst_mgr, false); + link->mst_stream_alloc_table.stream_count = 0; + memset(link->mst_stream_alloc_table.stream_allocations, 0, + sizeof(link->mst_stream_alloc_table.stream_allocations)); + } + } else { + return 0; + } + drm_kms_helper_hotplug_event(dev); + + return 0; +} /* - * Sets the force_timing_sync debug optino from the given string. + * The interface doesn't need get function, so it will return the + * value of zero + * Usage: cat /sys/kernel/debug/dri/0/amdgpu_dm_trigger_hpd_mst + */ +static int trigger_hpd_mst_get(void *data, u64 *val) +{ + *val = 0; + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(trigger_hpd_mst_ops, trigger_hpd_mst_get, + trigger_hpd_mst_set, "%llu\n"); + + +/* + * Sets the force_timing_sync debug option from the given string. * All connected displays will be force synchronized immediately. * Usage: echo 1 > /sys/kernel/debug/dri/0/amdgpu_dm_force_timing_sync */ @@ -2568,10 +3103,71 @@ static int visual_confirm_get(void *data, u64 *val) return 0; } +DEFINE_SHOW_ATTRIBUTE(current_backlight); +DEFINE_SHOW_ATTRIBUTE(target_backlight); +DEFINE_SHOW_ATTRIBUTE(mst_topo); DEFINE_DEBUGFS_ATTRIBUTE(visual_confirm_fops, visual_confirm_get, visual_confirm_set, "%llu\n"); -int dtn_debugfs_init(struct amdgpu_device *adev) +/* + * Dumps the DCC_EN bit for each pipe. + * Example usage: cat /sys/kernel/debug/dri/0/amdgpu_dm_dcc_en + */ +static ssize_t dcc_en_bits_read( + struct file *f, + char __user *buf, + size_t size, + loff_t *pos) +{ + struct amdgpu_device *adev = file_inode(f)->i_private; + struct dc *dc = adev->dm.dc; + char *rd_buf = NULL; + const uint32_t rd_buf_size = 32; + uint32_t result = 0; + int offset = 0; + int num_pipes = dc->res_pool->pipe_count; + int *dcc_en_bits; + int i, r; + + dcc_en_bits = kcalloc(num_pipes, sizeof(int), GFP_KERNEL); + if (!dcc_en_bits) + return -ENOMEM; + + if (!dc->hwss.get_dcc_en_bits) { + kfree(dcc_en_bits); + return 0; + } + + dc->hwss.get_dcc_en_bits(dc, dcc_en_bits); + + rd_buf = kcalloc(rd_buf_size, sizeof(char), GFP_KERNEL); + if (!rd_buf) + return -ENOMEM; + + for (i = 0; i < num_pipes; i++) + offset += snprintf(rd_buf + offset, rd_buf_size - offset, + "%d ", dcc_en_bits[i]); + rd_buf[strlen(rd_buf)] = '\n'; + + kfree(dcc_en_bits); + + while (size) { + if (*pos >= rd_buf_size) + break; + r = put_user(*(rd_buf + result), buf); + if (r) + return r; /* r = -EFAULT */ + buf += 1; + size -= 1; + *pos += 1; + result += 1; + } + + kfree(rd_buf); + return result; +} + +void dtn_debugfs_init(struct amdgpu_device *adev) { static const struct file_operations dtn_log_fops = { .owner = THIS_MODULE, @@ -2579,16 +3175,21 @@ int dtn_debugfs_init(struct amdgpu_device *adev) .write = dtn_log_write, .llseek = default_llseek }; + static const struct file_operations dcc_en_bits_fops = { + .owner = THIS_MODULE, + .read = dcc_en_bits_read, + .llseek = default_llseek + }; struct drm_minor *minor = adev_to_drm(adev)->primary; struct dentry *root = minor->debugfs_root; - int ret; - - ret = amdgpu_debugfs_add_files(adev, amdgpu_dm_debugfs_list, - ARRAY_SIZE(amdgpu_dm_debugfs_list)); - if (ret) - return ret; + debugfs_create_file("amdgpu_current_backlight_pwm", 0444, + root, adev, ¤t_backlight_fops); + debugfs_create_file("amdgpu_target_backlight_pwm", 0444, + root, adev, &target_backlight_fops); + debugfs_create_file("amdgpu_mst_topology", 0444, root, + adev, &mst_topo_fops); debugfs_create_file("amdgpu_dm_dtn_log", 0644, root, adev, &dtn_log_fops); @@ -2604,5 +3205,12 @@ int dtn_debugfs_init(struct amdgpu_device *adev) debugfs_create_file_unsafe("amdgpu_dm_force_timing_sync", 0644, root, adev, &force_timing_sync_ops); - return 0; + debugfs_create_file_unsafe("amdgpu_dm_dmcub_trace_event_en", 0644, root, + adev, &dmcub_trace_event_state_fops); + + debugfs_create_file_unsafe("amdgpu_dm_trigger_hpd_mst", 0644, root, + adev, &trigger_hpd_mst_ops); + + debugfs_create_file_unsafe("amdgpu_dm_dcc_en", 0644, root, adev, + &dcc_en_bits_fops); } |