diff options
author | Sean Paul <seanpaul@chromium.org> | 2018-08-27 10:00:03 -0400 |
---|---|---|
committer | Sean Paul <seanpaul@chromium.org> | 2018-08-27 10:00:03 -0400 |
commit | bc537a9cc47eec7f4e32b8164c494ddc35dca8ac (patch) | |
tree | a7365f3b76f4e472c43f14e074a284bfb28803e8 /drivers/gpu/drm/amd/display/amdgpu_dm | |
parent | a5ec8332d4280500544e316f76c04a7adc02ce03 (diff) | |
parent | 5b394b2ddf0347bef56e50c69a58773c94343ff3 (diff) | |
download | linux-bc537a9cc47eec7f4e32b8164c494ddc35dca8ac.tar.bz2 |
Merge drm/drm-next into drm-misc-next
Now that 4.19-rc1 is cut, backmerge it into -misc-next.
Signed-off-by: Sean Paul <seanpaul@chromium.org>
Diffstat (limited to 'drivers/gpu/drm/amd/display/amdgpu_dm')
10 files changed, 1530 insertions, 414 deletions
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile b/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile index af16973f2c41..94911871eb9b 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile @@ -28,11 +28,11 @@ AMDGPUDM = amdgpu_dm.o amdgpu_dm_irq.o amdgpu_dm_mst_types.o amdgpu_dm_color.o ifneq ($(CONFIG_DRM_AMD_DC),) -AMDGPUDM += amdgpu_dm_services.o amdgpu_dm_helpers.o +AMDGPUDM += amdgpu_dm_services.o amdgpu_dm_helpers.o amdgpu_dm_pp_smu.o endif ifneq ($(CONFIG_DEBUG_FS),) -AMDGPUDM += amdgpu_dm_crc.o +AMDGPUDM += amdgpu_dm_crc.o amdgpu_dm_debugfs.o endif subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index a5c1617e55c5..11d6dd23eeb8 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -39,6 +39,9 @@ #include "dm_helpers.h" #include "dm_services_types.h" #include "amdgpu_dm_mst_types.h" +#if defined(CONFIG_DEBUG_FS) +#include "amdgpu_dm_debugfs.h" +#endif #include "ivsrcid/ivsrcid_vislands30.h" @@ -55,8 +58,6 @@ #include <drm/drm_fb_helper.h> #include <drm/drm_edid.h> -#include "modules/inc/mod_freesync.h" - #if defined(CONFIG_DRM_AMD_DC_DCN1_0) #include "ivsrcid/irqsrcs_dcn_1_0.h" @@ -1038,7 +1039,7 @@ static void handle_hpd_rx_irq(void *param) if (dc_link->type != dc_connection_mst_branch) mutex_lock(&aconnector->hpd_lock); - if (dc_link_handle_hpd_rx_irq(dc_link, NULL) && + if (dc_link_handle_hpd_rx_irq(dc_link, NULL, NULL) && !is_mst_root_connector) { /* Downstream Port status changed. */ if (dc_link_detect(dc_link, DETECT_REASON_HPDRX)) { @@ -1317,7 +1318,12 @@ static int amdgpu_dm_backlight_update_status(struct backlight_device *bd) static int amdgpu_dm_backlight_get_brightness(struct backlight_device *bd) { - return bd->props.brightness; + struct amdgpu_display_manager *dm = bl_get_data(bd); + int ret = dc_link_get_backlight_level(dm->backlight_link); + + if (ret == DC_ERROR_UNEXPECTED) + return bd->props.brightness; + return ret; } static const struct backlight_ops amdgpu_dm_backlight_ops = { @@ -1332,6 +1338,7 @@ amdgpu_dm_register_backlight_device(struct amdgpu_display_manager *dm) struct backlight_properties props = { 0 }; props.max_brightness = AMDGPU_MAX_BL_LEVEL; + props.brightness = AMDGPU_MAX_BL_LEVEL; props.type = BACKLIGHT_RAW; snprintf(bl_name, sizeof(bl_name), "amdgpu_bl%d", @@ -1529,10 +1536,6 @@ static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev) DRM_ERROR("DM: Failed to initialize IRQ\n"); goto fail; } - /* - * Temporary disable until pplib/smu interaction is implemented - */ - dm->dc->debug.disable_stutter = true; break; #endif default: @@ -1540,6 +1543,9 @@ static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev) goto fail; } + if (adev->asic_type != CHIP_CARRIZO && adev->asic_type != CHIP_STONEY) + dm->dc->debug.disable_stutter = amdgpu_pp_feature_mask & PP_STUTTER_MODE ? false : true; + return 0; fail: kfree(aencoder); @@ -1571,18 +1577,6 @@ static void dm_bandwidth_update(struct amdgpu_device *adev) /* TODO: implement later */ } -static void dm_set_backlight_level(struct amdgpu_encoder *amdgpu_encoder, - u8 level) -{ - /* TODO: translate amdgpu_encoder to display_index and call DAL */ -} - -static u8 dm_get_backlight_level(struct amdgpu_encoder *amdgpu_encoder) -{ - /* TODO: translate amdgpu_encoder to display_index and call DAL */ - return 0; -} - static int amdgpu_notify_freesync(struct drm_device *dev, void *data, struct drm_file *filp) { @@ -1611,10 +1605,8 @@ static int amdgpu_notify_freesync(struct drm_device *dev, void *data, static const struct amdgpu_display_funcs dm_display_funcs = { .bandwidth_update = dm_bandwidth_update, /* called unconditionally */ .vblank_get_counter = dm_vblank_get_counter,/* called unconditionally */ - .backlight_set_level = - dm_set_backlight_level,/* called unconditionally */ - .backlight_get_level = - dm_get_backlight_level,/* called unconditionally */ + .backlight_set_level = NULL, /* never called for DC */ + .backlight_get_level = NULL, /* never called for DC */ .hpd_sense = NULL,/* called unconditionally */ .hpd_set_polarity = NULL, /* called unconditionally */ .hpd_get_gpio_reg = NULL, /* VBIOS parsing. DAL does it. */ @@ -2121,13 +2113,8 @@ convert_color_depth_from_display_info(const struct drm_connector *connector) static enum dc_aspect_ratio get_aspect_ratio(const struct drm_display_mode *mode_in) { - int32_t width = mode_in->crtc_hdisplay * 9; - int32_t height = mode_in->crtc_vdisplay * 16; - - if ((width - height) < 10 && (width - height) > -10) - return ASPECT_RATIO_16_9; - else - return ASPECT_RATIO_4_3; + /* 1-1 mapping, since both enums follow the HDMI spec. */ + return (enum dc_aspect_ratio) mode_in->picture_aspect_ratio; } static enum dc_color_space @@ -2173,6 +2160,46 @@ get_output_color_space(const struct dc_crtc_timing *dc_crtc_timing) return color_space; } +static void reduce_mode_colour_depth(struct dc_crtc_timing *timing_out) +{ + if (timing_out->display_color_depth <= COLOR_DEPTH_888) + return; + + timing_out->display_color_depth--; +} + +static void adjust_colour_depth_from_display_info(struct dc_crtc_timing *timing_out, + const struct drm_display_info *info) +{ + int normalized_clk; + if (timing_out->display_color_depth <= COLOR_DEPTH_888) + return; + do { + normalized_clk = timing_out->pix_clk_khz; + /* YCbCr 4:2:0 requires additional adjustment of 1/2 */ + if (timing_out->pixel_encoding == PIXEL_ENCODING_YCBCR420) + normalized_clk /= 2; + /* Adjusting pix clock following on HDMI spec based on colour depth */ + switch (timing_out->display_color_depth) { + case COLOR_DEPTH_101010: + normalized_clk = (normalized_clk * 30) / 24; + break; + case COLOR_DEPTH_121212: + normalized_clk = (normalized_clk * 36) / 24; + break; + case COLOR_DEPTH_161616: + normalized_clk = (normalized_clk * 48) / 24; + break; + default: + return; + } + if (normalized_clk <= info->max_tmds_clock) + return; + reduce_mode_colour_depth(timing_out); + + } while (timing_out->display_color_depth > COLOR_DEPTH_888); + +} /*****************************************************************************/ static void @@ -2181,6 +2208,7 @@ fill_stream_properties_from_drm_display_mode(struct dc_stream_state *stream, const struct drm_connector *connector) { struct dc_crtc_timing *timing_out = &stream->timing; + const struct drm_display_info *info = &connector->display_info; memset(timing_out, 0, sizeof(struct dc_crtc_timing)); @@ -2189,8 +2217,10 @@ fill_stream_properties_from_drm_display_mode(struct dc_stream_state *stream, timing_out->v_border_top = 0; timing_out->v_border_bottom = 0; /* TODO: un-hardcode */ - - if ((connector->display_info.color_formats & DRM_COLOR_FORMAT_YCRCB444) + if (drm_mode_is_420_only(info, mode_in) + && stream->sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A) + timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420; + else if ((connector->display_info.color_formats & DRM_COLOR_FORMAT_YCRCB444) && stream->sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A) timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR444; else @@ -2226,6 +2256,8 @@ fill_stream_properties_from_drm_display_mode(struct dc_stream_state *stream, stream->out_transfer_func->type = TF_TYPE_PREDEFINED; stream->out_transfer_func->tf = TRANSFER_FUNCTION_SRGB; + if (stream->sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A) + adjust_colour_depth_from_display_info(timing_out, info); } static void fill_audio_info(struct audio_info *audio_info, @@ -3047,14 +3079,24 @@ static int dm_plane_helper_prepare_fb(struct drm_plane *plane, else domain = AMDGPU_GEM_DOMAIN_VRAM; - r = amdgpu_bo_pin(rbo, domain, &afb->address); - amdgpu_bo_unreserve(rbo); - + r = amdgpu_bo_pin(rbo, domain); if (unlikely(r != 0)) { if (r != -ERESTARTSYS) DRM_ERROR("Failed to pin framebuffer with error %d\n", r); + amdgpu_bo_unreserve(rbo); + return r; + } + + r = amdgpu_ttm_alloc_gart(&rbo->tbo); + if (unlikely(r != 0)) { + amdgpu_bo_unpin(rbo); + amdgpu_bo_unreserve(rbo); + DRM_ERROR("%p bind failed\n", rbo); return r; } + amdgpu_bo_unreserve(rbo); + + afb->address = amdgpu_bo_gpu_offset(rbo); amdgpu_bo_ref(rbo); @@ -3452,7 +3494,6 @@ void amdgpu_dm_connector_init_helper(struct amdgpu_display_manager *dm, aconnector->base.stereo_allowed = false; aconnector->base.dpms = DRM_MODE_DPMS_OFF; aconnector->hpd.hpd = AMDGPU_HPD_NONE; /* not used */ - mutex_init(&aconnector->hpd_lock); /* configure support HPD hot plug connector_>polled default value is 0 @@ -3461,9 +3502,13 @@ void amdgpu_dm_connector_init_helper(struct amdgpu_display_manager *dm, switch (connector_type) { case DRM_MODE_CONNECTOR_HDMIA: aconnector->base.polled = DRM_CONNECTOR_POLL_HPD; + aconnector->base.ycbcr_420_allowed = + link->link_enc->features.ycbcr420_supported ? true : false; break; case DRM_MODE_CONNECTOR_DisplayPort: aconnector->base.polled = DRM_CONNECTOR_POLL_HPD; + aconnector->base.ycbcr_420_allowed = + link->link_enc->features.ycbcr420_supported ? true : false; break; case DRM_MODE_CONNECTOR_DVID: aconnector->base.polled = DRM_CONNECTOR_POLL_HPD; @@ -3620,6 +3665,13 @@ static int amdgpu_dm_connector_init(struct amdgpu_display_manager *dm, &aconnector->base, &aencoder->base); drm_connector_register(&aconnector->base); +#if defined(CONFIG_DEBUG_FS) + res = connector_debugfs_init(aconnector); + if (res) { + DRM_ERROR("Failed to create debugfs for connector"); + goto out_free; + } +#endif if (connector_type == DRM_MODE_CONNECTOR_DisplayPort || connector_type == DRM_MODE_CONNECTOR_eDP) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c index b329393307e5..326f6fb7e0bc 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c @@ -231,18 +231,21 @@ void amdgpu_dm_set_ctm(struct dm_crtc_state *crtc) * preparation for hardware commit. If no lut is specified by user, we default * to SRGB degamma. * - * Currently, we only support degamma bypass, or preprogrammed SRGB degamma. - * Programmable degamma is not supported, and an attempt to do so will return - * -EINVAL. + * We support degamma bypass, predefined SRGB, and custom degamma * * RETURNS: - * 0 on success, -EINVAL if custom degamma curve is given. + * 0 on success + * -EINVAL if crtc_state has a degamma_lut of invalid size + * -ENOMEM if gamma allocation fails */ int amdgpu_dm_set_degamma_lut(struct drm_crtc_state *crtc_state, struct dc_plane_state *dc_plane_state) { struct drm_property_blob *blob = crtc_state->degamma_lut; struct drm_color_lut *lut; + uint32_t lut_size; + struct dc_gamma *gamma; + bool ret; if (!blob) { /* Default to SRGB */ @@ -258,11 +261,30 @@ int amdgpu_dm_set_degamma_lut(struct drm_crtc_state *crtc_state, return 0; } - /* Otherwise, assume SRGB, since programmable degamma is not - * supported. - */ - dc_plane_state->in_transfer_func->type = TF_TYPE_PREDEFINED; - dc_plane_state->in_transfer_func->tf = TRANSFER_FUNCTION_SRGB; - return -EINVAL; + gamma = dc_create_gamma(); + if (!gamma) + return -ENOMEM; + + lut_size = blob->length / sizeof(struct drm_color_lut); + gamma->num_entries = lut_size; + if (gamma->num_entries == MAX_COLOR_LUT_ENTRIES) + gamma->type = GAMMA_CUSTOM; + else { + dc_gamma_release(&gamma); + return -EINVAL; + } + + __drm_lut_to_dc_gamma(lut, gamma, false); + + dc_plane_state->in_transfer_func->type = TF_TYPE_DISTRIBUTED_POINTS; + ret = mod_color_calculate_degamma_params(dc_plane_state->in_transfer_func, gamma, true); + dc_gamma_release(&gamma); + if (!ret) { + dc_plane_state->in_transfer_func->type = TF_TYPE_BYPASS; + DRM_ERROR("Out of memory when calculating degamma params\n"); + return -ENOMEM; + } + + return 0; } diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.c index e7ad528f5853..01fc5717b657 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.c @@ -112,10 +112,16 @@ int amdgpu_dm_crtc_set_crc_source(struct drm_crtc *crtc, const char *src_name) */ void amdgpu_dm_crtc_handle_crc_irq(struct drm_crtc *crtc) { - struct dm_crtc_state *crtc_state = to_dm_crtc_state(crtc->state); - struct dc_stream_state *stream_state = crtc_state->stream; + struct dm_crtc_state *crtc_state; + struct dc_stream_state *stream_state; uint32_t crcs[3]; + if (crtc == NULL) + return; + + crtc_state = to_dm_crtc_state(crtc->state); + stream_state = crtc_state->stream; + /* Early return if CRC capture is not enabled. */ if (!crtc_state->crc_enabled) return; 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 new file mode 100644 index 000000000000..0d9e410ca01e --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c @@ -0,0 +1,722 @@ +/* + * 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/debugfs.h> + +#include "dc.h" +#include "amdgpu.h" +#include "amdgpu_dm.h" +#include "amdgpu_dm_debugfs.h" + +/* 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 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 > 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 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 %d %d "); + snprintf(rd_buf_ptr, str_len, "Current: %d %d %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 %d %d "); + snprintf(rd_buf_ptr, str_len, "Verified: %d %d %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 %d %d "); + snprintf(rd_buf_ptr, str_len, "Reported: %d %d %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 %d %d "); + snprintf(rd_buf_ptr, str_len, "Preferred: %d %d %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) + 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 dc *dc = (struct dc *)link->dc; + struct dc_link_settings prefer_link_settings; + char *wr_buf = NULL; + char *wr_buf_ptr = NULL; + const uint32_t wr_buf_size = 40; + int r; + int bytes_from_user; + char *sub_str; + /* 0: lane_count; 1: link_rate */ + uint8_t param_index = 0; + long param[2]; + const char delimiter[3] = {' ', '\n', '\0'}; + bool valid_input = false; + + if (size == 0) + return -EINVAL; + + wr_buf = kcalloc(wr_buf_size, sizeof(char), GFP_KERNEL); + if (!wr_buf) + return -EINVAL; + wr_buf_ptr = wr_buf; + + r = copy_from_user(wr_buf_ptr, buf, wr_buf_size); + + /* r is bytes not be copied */ + if (r >= wr_buf_size) { + kfree(wr_buf); + DRM_DEBUG_DRIVER("user data not read\n"); + return -EINVAL; + } + + bytes_from_user = wr_buf_size - r; + + while (isspace(*wr_buf_ptr)) + wr_buf_ptr++; + + while ((*wr_buf_ptr != '\0') && (param_index < 2)) { + + sub_str = strsep(&wr_buf_ptr, delimiter); + + r = kstrtol(sub_str, 16, ¶m[param_index]); + + if (r) + DRM_DEBUG_DRIVER("string to int convert error code: %d\n", r); + + param_index++; + while (isspace(*wr_buf_ptr)) + wr_buf_ptr++; + } + + switch (param[0]) { + case LANE_COUNT_ONE: + case LANE_COUNT_TWO: + case LANE_COUNT_FOUR: + valid_input = true; + break; + default: + break; + } + + switch (param[1]) { + case LINK_RATE_LOW: + case LINK_RATE_HIGH: + case LINK_RATE_RBR2: + case LINK_RATE_HIGH2: + case LINK_RATE_HIGH3: + valid_input = true; + break; + default: + break; + } + + if (!valid_input) { + kfree(wr_buf); + DRM_DEBUG_DRIVER("Invalid Input value No HW will be programmed\n"); + return bytes_from_user; + } + + /* 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.lane_count = param[0]; + prefer_link_settings.link_rate = param[1]; + + dc_link_set_preferred_link_settings(dc, &prefer_link_settings, link); + + kfree(wr_buf); + return bytes_from_user; +} + +/* 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 ", + link->cur_lane_setting.VOLTAGE_SWING, + link->cur_lane_setting.PRE_EMPHASIS, + link->cur_lane_setting.POST_CURSOR2); + + 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; +} + +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; + char *wr_buf_ptr = NULL; + uint32_t wr_buf_size = 40; + int r; + int bytes_from_user; + char *sub_str; + uint8_t param_index = 0; + long param[3]; + const char delimiter[3] = {' ', '\n', '\0'}; + bool use_prefer_link_setting; + struct link_training_settings link_lane_settings; + + if (size == 0) + return 0; + + wr_buf = kcalloc(wr_buf_size, sizeof(char), GFP_KERNEL); + if (!wr_buf) + return 0; + wr_buf_ptr = wr_buf; + + r = copy_from_user(wr_buf_ptr, buf, wr_buf_size); + + /* r is bytes not be copied */ + if (r >= wr_buf_size) { + kfree(wr_buf); + DRM_DEBUG_DRIVER("user data not be read\n"); + return 0; + } + + bytes_from_user = wr_buf_size - r; + + while (isspace(*wr_buf_ptr)) + wr_buf_ptr++; + + while ((*wr_buf_ptr != '\0') && (param_index < 3)) { + + sub_str = strsep(&wr_buf_ptr, delimiter); + + r = kstrtol(sub_str, 16, ¶m[param_index]); + + if (r) + DRM_DEBUG_DRIVER("string to int convert error code: %d\n", r); + + param_index++; + while (isspace(*wr_buf_ptr)) + wr_buf_ptr++; + } + + 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 bytes_from_user; + } + + /* 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.lane_settings[r].VOLTAGE_SWING = + (enum dc_voltage_swing) (param[0]); + link_lane_settings.lane_settings[r].PRE_EMPHASIS = + (enum dc_pre_emphasis) (param[1]); + link_lane_settings.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 bytes_from_user; +} + +/* 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; + char *wr_buf_ptr = NULL; + uint32_t wr_buf_size = 100; + uint32_t wr_buf_count = 0; + int r; + int bytes_from_user; + char *sub_str = NULL; + uint8_t param_index = 0; + uint8_t param_nums = 0; + long param[11] = {0x0}; + const char delimiter[3] = {' ', '\n', '\0'}; + enum dp_test_pattern test_pattern = DP_TEST_PATTERN_UNSUPPORTED; + bool disable_hpd = false; + bool valid_test_pattern = false; + /* init with defalut 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; + int i; + + if (size == 0) + return 0; + + wr_buf = kcalloc(wr_buf_size, sizeof(char), GFP_KERNEL); + if (!wr_buf) + return 0; + wr_buf_ptr = wr_buf; + + r = copy_from_user(wr_buf_ptr, buf, wr_buf_size); + + /* r is bytes not be copied */ + if (r >= wr_buf_size) { + kfree(wr_buf); + DRM_DEBUG_DRIVER("user data not be read\n"); + return 0; + } + + bytes_from_user = wr_buf_size - r; + + /* 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; + } + + /* max 11 parameters */ + if (param_nums > 11) + param_nums = 11; + + wr_buf_ptr = wr_buf; /* reset buf pinter */ + 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, ¶m[param_index]); + + if (r) + DRM_DEBUG_DRIVER("string to int convert error code: %d\n", r); + + param_index++; + } + + 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 bytes_from_user; + } + + 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.lane_settings[i] = link->cur_lane_setting; + + dc_link_set_test_pattern( + link, + test_pattern, + &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 bytes_from_user; +} + +static const struct file_operations dp_link_settings_debugfs_fops = { + .owner = THIS_MODULE, + .read = dp_link_settings_read, + .write = dp_link_settings_write, + .llseek = default_llseek +}; + +static const struct file_operations dp_phy_settings_debugfs_fop = { + .owner = THIS_MODULE, + .read = dp_phy_settings_read, + .write = dp_phy_settings_write, + .llseek = default_llseek +}; + +static const struct file_operations dp_phy_test_pattern_fops = { + .owner = THIS_MODULE, + .write = dp_phy_test_pattern_debugfs_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}, + {"test_pattern", &dp_phy_test_pattern_fops} +}; + +int connector_debugfs_init(struct amdgpu_dm_connector *connector) +{ + int i; + struct dentry *ent, *dir = connector->base.debugfs_entry; + + if (connector->base.connector_type == DRM_MODE_CONNECTOR_DisplayPort) { + for (i = 0; i < ARRAY_SIZE(dp_debugfs_entries); i++) { + ent = debugfs_create_file(dp_debugfs_entries[i].name, + 0644, + dir, + connector, + dp_debugfs_entries[i].fops); + if (IS_ERR(ent)) + return PTR_ERR(ent); + } + } + + return 0; +} + diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.h new file mode 100644 index 000000000000..d9ed1b2aa811 --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.h @@ -0,0 +1,34 @@ +/* + * 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 + * + */ + +#ifndef __AMDGPU_DM_DEBUGFS_H__ +#define __AMDGPU_DM_DEBUGFS_H__ + +#include "amdgpu.h" +#include "amdgpu_dm.h" + +int connector_debugfs_init(struct amdgpu_dm_connector *connector); + +#endif diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c index b19dc4cfc030..8403b6a9a77b 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c @@ -169,6 +169,11 @@ static void get_payload_table( mutex_unlock(&mst_mgr->payload_lock); } +void dm_helpers_dp_update_branch_info( + struct dc_context *ctx, + const struct dc_link *link) +{} + /* * Writes payload allocation table in immediate downstream device. */ @@ -454,6 +459,22 @@ bool dm_helpers_submit_i2c( return result; } +bool dm_helpers_is_dp_sink_present(struct dc_link *link) +{ + bool dp_sink_present; + struct amdgpu_dm_connector *aconnector = link->priv; + + if (!aconnector) { + BUG_ON("Failed to found connector for link!"); + return true; + } + + mutex_lock(&aconnector->dm_dp_aux.aux.hw_mutex); + dp_sink_present = dc_link_is_dp_sink_present(link); + mutex_unlock(&aconnector->dm_dp_aux.aux.hw_mutex); + return dp_sink_present; +} + enum dc_edid_status dm_helpers_read_local_edid( struct dc_context *ctx, struct dc_link *link, @@ -498,8 +519,8 @@ enum dc_edid_status dm_helpers_read_local_edid( edid_status, aconnector->base.name); if (link->aux_mode) { - union test_request test_request = {0}; - union test_response test_response = {0}; + union test_request test_request = { {0} }; + union test_response test_response = { {0} }; dm_helpers_dp_read_dpcd(ctx, link, diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c index 65f210d3497b..9a300732ba37 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c @@ -80,55 +80,72 @@ static void log_dpcd(uint8_t type, static ssize_t dm_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) { - enum i2c_mot_mode mot = (msg->request & DP_AUX_I2C_MOT) ? - I2C_MOT_TRUE : I2C_MOT_FALSE; - enum ddc_result res; - uint32_t read_bytes = msg->size; + ssize_t result = 0; + enum i2caux_transaction_action action; + enum aux_transaction_type type; if (WARN_ON(msg->size > 16)) return -E2BIG; switch (msg->request & ~DP_AUX_I2C_MOT) { case DP_AUX_NATIVE_READ: - res = dal_ddc_service_read_dpcd_data( - TO_DM_AUX(aux)->ddc_service, - false, - I2C_MOT_UNDEF, - msg->address, - msg->buffer, - msg->size, - &read_bytes); + type = AUX_TRANSACTION_TYPE_DP; + action = I2CAUX_TRANSACTION_ACTION_DP_READ; + + result = dc_link_aux_transfer(TO_DM_AUX(aux)->ddc_service, + msg->address, + &msg->reply, + msg->buffer, + msg->size, + type, + action); break; case DP_AUX_NATIVE_WRITE: - res = dal_ddc_service_write_dpcd_data( - TO_DM_AUX(aux)->ddc_service, - false, - I2C_MOT_UNDEF, - msg->address, - msg->buffer, - msg->size); + type = AUX_TRANSACTION_TYPE_DP; + action = I2CAUX_TRANSACTION_ACTION_DP_WRITE; + + dc_link_aux_transfer(TO_DM_AUX(aux)->ddc_service, + msg->address, + &msg->reply, + msg->buffer, + msg->size, + type, + action); + result = msg->size; break; case DP_AUX_I2C_READ: - res = dal_ddc_service_read_dpcd_data( - TO_DM_AUX(aux)->ddc_service, - true, - mot, - msg->address, - msg->buffer, - msg->size, - &read_bytes); + type = AUX_TRANSACTION_TYPE_I2C; + if (msg->request & DP_AUX_I2C_MOT) + action = I2CAUX_TRANSACTION_ACTION_I2C_READ_MOT; + else + action = I2CAUX_TRANSACTION_ACTION_I2C_READ; + + result = dc_link_aux_transfer(TO_DM_AUX(aux)->ddc_service, + msg->address, + &msg->reply, + msg->buffer, + msg->size, + type, + action); break; case DP_AUX_I2C_WRITE: - res = dal_ddc_service_write_dpcd_data( - TO_DM_AUX(aux)->ddc_service, - true, - mot, - msg->address, - msg->buffer, - msg->size); + type = AUX_TRANSACTION_TYPE_I2C; + if (msg->request & DP_AUX_I2C_MOT) + action = I2CAUX_TRANSACTION_ACTION_I2C_WRITE_MOT; + else + action = I2CAUX_TRANSACTION_ACTION_I2C_WRITE; + + dc_link_aux_transfer(TO_DM_AUX(aux)->ddc_service, + msg->address, + &msg->reply, + msg->buffer, + msg->size, + type, + action); + result = msg->size; break; default: - return 0; + return -EINVAL; } #ifdef TRACE_DPCD @@ -139,9 +156,10 @@ static ssize_t dm_dp_aux_transfer(struct drm_dp_aux *aux, r == DDC_RESULT_SUCESSFULL); #endif - if (res != DDC_RESULT_SUCESSFULL) - return -EIO; - return read_bytes; + if (result < 0) /* DC doesn't know about kernel error codes */ + result = -EIO; + + return result; } static enum drm_connector_status diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_pp_smu.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_pp_smu.c new file mode 100644 index 000000000000..fbe878ae1e8c --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_pp_smu.c @@ -0,0 +1,562 @@ +/* + * 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.h> +#include <linux/acpi.h> + +#include <drm/drmP.h> +#include <drm/drm_crtc_helper.h> +#include <drm/amdgpu_drm.h> +#include "dm_services.h" +#include "amdgpu.h" +#include "amdgpu_dm.h" +#include "amdgpu_dm_irq.h" +#include "amdgpu_pm.h" +#include "dm_pp_smu.h" + + +bool dm_pp_apply_display_requirements( + const struct dc_context *ctx, + const struct dm_pp_display_configuration *pp_display_cfg) +{ + struct amdgpu_device *adev = ctx->driver_context; + int i; + + if (adev->pm.dpm_enabled) { + + memset(&adev->pm.pm_display_cfg, 0, + sizeof(adev->pm.pm_display_cfg)); + + adev->pm.pm_display_cfg.cpu_cc6_disable = + pp_display_cfg->cpu_cc6_disable; + + adev->pm.pm_display_cfg.cpu_pstate_disable = + pp_display_cfg->cpu_pstate_disable; + + adev->pm.pm_display_cfg.cpu_pstate_separation_time = + pp_display_cfg->cpu_pstate_separation_time; + + adev->pm.pm_display_cfg.nb_pstate_switch_disable = + pp_display_cfg->nb_pstate_switch_disable; + + adev->pm.pm_display_cfg.num_display = + pp_display_cfg->display_count; + adev->pm.pm_display_cfg.num_path_including_non_display = + pp_display_cfg->display_count; + + adev->pm.pm_display_cfg.min_core_set_clock = + pp_display_cfg->min_engine_clock_khz/10; + adev->pm.pm_display_cfg.min_core_set_clock_in_sr = + pp_display_cfg->min_engine_clock_deep_sleep_khz/10; + adev->pm.pm_display_cfg.min_mem_set_clock = + pp_display_cfg->min_memory_clock_khz/10; + + adev->pm.pm_display_cfg.min_dcef_deep_sleep_set_clk = + pp_display_cfg->min_engine_clock_deep_sleep_khz/10; + adev->pm.pm_display_cfg.min_dcef_set_clk = + pp_display_cfg->min_dcfclock_khz/10; + + adev->pm.pm_display_cfg.multi_monitor_in_sync = + pp_display_cfg->all_displays_in_sync; + adev->pm.pm_display_cfg.min_vblank_time = + pp_display_cfg->avail_mclk_switch_time_us; + + adev->pm.pm_display_cfg.display_clk = + pp_display_cfg->disp_clk_khz/10; + + adev->pm.pm_display_cfg.dce_tolerable_mclk_in_active_latency = + pp_display_cfg->avail_mclk_switch_time_in_disp_active_us; + + adev->pm.pm_display_cfg.crtc_index = pp_display_cfg->crtc_index; + adev->pm.pm_display_cfg.line_time_in_us = + pp_display_cfg->line_time_in_us; + + adev->pm.pm_display_cfg.vrefresh = pp_display_cfg->disp_configs[0].v_refresh; + adev->pm.pm_display_cfg.crossfire_display_index = -1; + adev->pm.pm_display_cfg.min_bus_bandwidth = 0; + + for (i = 0; i < pp_display_cfg->display_count; i++) { + const struct dm_pp_single_disp_config *dc_cfg = + &pp_display_cfg->disp_configs[i]; + adev->pm.pm_display_cfg.displays[i].controller_id = dc_cfg->pipe_idx + 1; + } + + /* TODO: complete implementation of + * pp_display_configuration_change(). + * Follow example of: + * PHM_StoreDALConfigurationData - powerplay\hwmgr\hardwaremanager.c + * PP_IRI_DisplayConfigurationChange - powerplay\eventmgr\iri.c */ + if (adev->powerplay.pp_funcs->display_configuration_change) + adev->powerplay.pp_funcs->display_configuration_change( + adev->powerplay.pp_handle, + &adev->pm.pm_display_cfg); + + /* TODO: replace by a separate call to 'apply display cfg'? */ + amdgpu_pm_compute_clocks(adev); + } + + return true; +} + +static void get_default_clock_levels( + enum dm_pp_clock_type clk_type, + struct dm_pp_clock_levels *clks) +{ + uint32_t disp_clks_in_khz[6] = { + 300000, 400000, 496560, 626090, 685720, 757900 }; + uint32_t sclks_in_khz[6] = { + 300000, 360000, 423530, 514290, 626090, 720000 }; + uint32_t mclks_in_khz[2] = { 333000, 800000 }; + + switch (clk_type) { + case DM_PP_CLOCK_TYPE_DISPLAY_CLK: + clks->num_levels = 6; + memmove(clks->clocks_in_khz, disp_clks_in_khz, + sizeof(disp_clks_in_khz)); + break; + case DM_PP_CLOCK_TYPE_ENGINE_CLK: + clks->num_levels = 6; + memmove(clks->clocks_in_khz, sclks_in_khz, + sizeof(sclks_in_khz)); + break; + case DM_PP_CLOCK_TYPE_MEMORY_CLK: + clks->num_levels = 2; + memmove(clks->clocks_in_khz, mclks_in_khz, + sizeof(mclks_in_khz)); + break; + default: + clks->num_levels = 0; + break; + } +} + +static enum amd_pp_clock_type dc_to_pp_clock_type( + enum dm_pp_clock_type dm_pp_clk_type) +{ + enum amd_pp_clock_type amd_pp_clk_type = 0; + + switch (dm_pp_clk_type) { + case DM_PP_CLOCK_TYPE_DISPLAY_CLK: + amd_pp_clk_type = amd_pp_disp_clock; + break; + case DM_PP_CLOCK_TYPE_ENGINE_CLK: + amd_pp_clk_type = amd_pp_sys_clock; + break; + case DM_PP_CLOCK_TYPE_MEMORY_CLK: + amd_pp_clk_type = amd_pp_mem_clock; + break; + case DM_PP_CLOCK_TYPE_DCEFCLK: + amd_pp_clk_type = amd_pp_dcef_clock; + break; + case DM_PP_CLOCK_TYPE_DCFCLK: + amd_pp_clk_type = amd_pp_dcf_clock; + break; + case DM_PP_CLOCK_TYPE_PIXELCLK: + amd_pp_clk_type = amd_pp_pixel_clock; + break; + case DM_PP_CLOCK_TYPE_FCLK: + amd_pp_clk_type = amd_pp_f_clock; + break; + case DM_PP_CLOCK_TYPE_DISPLAYPHYCLK: + amd_pp_clk_type = amd_pp_phy_clock; + break; + case DM_PP_CLOCK_TYPE_DPPCLK: + amd_pp_clk_type = amd_pp_dpp_clock; + break; + default: + DRM_ERROR("DM_PPLIB: invalid clock type: %d!\n", + dm_pp_clk_type); + break; + } + + return amd_pp_clk_type; +} + +static enum dm_pp_clocks_state pp_to_dc_powerlevel_state( + enum PP_DAL_POWERLEVEL max_clocks_state) +{ + switch (max_clocks_state) { + case PP_DAL_POWERLEVEL_0: + return DM_PP_CLOCKS_DPM_STATE_LEVEL_0; + case PP_DAL_POWERLEVEL_1: + return DM_PP_CLOCKS_DPM_STATE_LEVEL_1; + case PP_DAL_POWERLEVEL_2: + return DM_PP_CLOCKS_DPM_STATE_LEVEL_2; + case PP_DAL_POWERLEVEL_3: + return DM_PP_CLOCKS_DPM_STATE_LEVEL_3; + case PP_DAL_POWERLEVEL_4: + return DM_PP_CLOCKS_DPM_STATE_LEVEL_4; + case PP_DAL_POWERLEVEL_5: + return DM_PP_CLOCKS_DPM_STATE_LEVEL_5; + case PP_DAL_POWERLEVEL_6: + return DM_PP_CLOCKS_DPM_STATE_LEVEL_6; + case PP_DAL_POWERLEVEL_7: + return DM_PP_CLOCKS_DPM_STATE_LEVEL_7; + default: + DRM_ERROR("DM_PPLIB: invalid powerlevel state: %d!\n", + max_clocks_state); + return DM_PP_CLOCKS_STATE_INVALID; + } +} + +static void pp_to_dc_clock_levels( + const struct amd_pp_clocks *pp_clks, + struct dm_pp_clock_levels *dc_clks, + enum dm_pp_clock_type dc_clk_type) +{ + uint32_t i; + + if (pp_clks->count > DM_PP_MAX_CLOCK_LEVELS) { + DRM_INFO("DM_PPLIB: Warning: %s clock: number of levels %d exceeds maximum of %d!\n", + DC_DECODE_PP_CLOCK_TYPE(dc_clk_type), + pp_clks->count, + DM_PP_MAX_CLOCK_LEVELS); + + dc_clks->num_levels = DM_PP_MAX_CLOCK_LEVELS; + } else + dc_clks->num_levels = pp_clks->count; + + DRM_INFO("DM_PPLIB: values for %s clock\n", + DC_DECODE_PP_CLOCK_TYPE(dc_clk_type)); + + for (i = 0; i < dc_clks->num_levels; i++) { + DRM_INFO("DM_PPLIB:\t %d\n", pp_clks->clock[i]); + dc_clks->clocks_in_khz[i] = pp_clks->clock[i]; + } +} + +static void pp_to_dc_clock_levels_with_latency( + const struct pp_clock_levels_with_latency *pp_clks, + struct dm_pp_clock_levels_with_latency *clk_level_info, + enum dm_pp_clock_type dc_clk_type) +{ + uint32_t i; + + if (pp_clks->num_levels > DM_PP_MAX_CLOCK_LEVELS) { + DRM_INFO("DM_PPLIB: Warning: %s clock: number of levels %d exceeds maximum of %d!\n", + DC_DECODE_PP_CLOCK_TYPE(dc_clk_type), + pp_clks->num_levels, + DM_PP_MAX_CLOCK_LEVELS); + + clk_level_info->num_levels = DM_PP_MAX_CLOCK_LEVELS; + } else + clk_level_info->num_levels = pp_clks->num_levels; + + DRM_DEBUG("DM_PPLIB: values for %s clock\n", + DC_DECODE_PP_CLOCK_TYPE(dc_clk_type)); + + for (i = 0; i < clk_level_info->num_levels; i++) { + DRM_DEBUG("DM_PPLIB:\t %d in kHz\n", pp_clks->data[i].clocks_in_khz); + clk_level_info->data[i].clocks_in_khz = pp_clks->data[i].clocks_in_khz; + clk_level_info->data[i].latency_in_us = pp_clks->data[i].latency_in_us; + } +} + +static void pp_to_dc_clock_levels_with_voltage( + const struct pp_clock_levels_with_voltage *pp_clks, + struct dm_pp_clock_levels_with_voltage *clk_level_info, + enum dm_pp_clock_type dc_clk_type) +{ + uint32_t i; + + if (pp_clks->num_levels > DM_PP_MAX_CLOCK_LEVELS) { + DRM_INFO("DM_PPLIB: Warning: %s clock: number of levels %d exceeds maximum of %d!\n", + DC_DECODE_PP_CLOCK_TYPE(dc_clk_type), + pp_clks->num_levels, + DM_PP_MAX_CLOCK_LEVELS); + + clk_level_info->num_levels = DM_PP_MAX_CLOCK_LEVELS; + } else + clk_level_info->num_levels = pp_clks->num_levels; + + DRM_INFO("DM_PPLIB: values for %s clock\n", + DC_DECODE_PP_CLOCK_TYPE(dc_clk_type)); + + for (i = 0; i < clk_level_info->num_levels; i++) { + DRM_INFO("DM_PPLIB:\t %d in kHz\n", pp_clks->data[i].clocks_in_khz); + clk_level_info->data[i].clocks_in_khz = pp_clks->data[i].clocks_in_khz; + clk_level_info->data[i].voltage_in_mv = pp_clks->data[i].voltage_in_mv; + } +} + +bool dm_pp_get_clock_levels_by_type( + const struct dc_context *ctx, + enum dm_pp_clock_type clk_type, + struct dm_pp_clock_levels *dc_clks) +{ + struct amdgpu_device *adev = ctx->driver_context; + void *pp_handle = adev->powerplay.pp_handle; + struct amd_pp_clocks pp_clks = { 0 }; + struct amd_pp_simple_clock_info validation_clks = { 0 }; + uint32_t i; + + if (adev->powerplay.pp_funcs->get_clock_by_type) { + if (adev->powerplay.pp_funcs->get_clock_by_type(pp_handle, + dc_to_pp_clock_type(clk_type), &pp_clks)) { + /* Error in pplib. Provide default values. */ + get_default_clock_levels(clk_type, dc_clks); + return true; + } + } + + pp_to_dc_clock_levels(&pp_clks, dc_clks, clk_type); + + if (adev->powerplay.pp_funcs->get_display_mode_validation_clocks) { + if (adev->powerplay.pp_funcs->get_display_mode_validation_clocks( + pp_handle, &validation_clks)) { + /* Error in pplib. Provide default values. */ + DRM_INFO("DM_PPLIB: Warning: using default validation clocks!\n"); + validation_clks.engine_max_clock = 72000; + validation_clks.memory_max_clock = 80000; + validation_clks.level = 0; + } + } + + DRM_INFO("DM_PPLIB: Validation clocks:\n"); + DRM_INFO("DM_PPLIB: engine_max_clock: %d\n", + validation_clks.engine_max_clock); + DRM_INFO("DM_PPLIB: memory_max_clock: %d\n", + validation_clks.memory_max_clock); + DRM_INFO("DM_PPLIB: level : %d\n", + validation_clks.level); + + /* Translate 10 kHz to kHz. */ + validation_clks.engine_max_clock *= 10; + validation_clks.memory_max_clock *= 10; + + /* Determine the highest non-boosted level from the Validation Clocks */ + if (clk_type == DM_PP_CLOCK_TYPE_ENGINE_CLK) { + for (i = 0; i < dc_clks->num_levels; i++) { + if (dc_clks->clocks_in_khz[i] > validation_clks.engine_max_clock) { + /* This clock is higher the validation clock. + * Than means the previous one is the highest + * non-boosted one. */ + DRM_INFO("DM_PPLIB: reducing engine clock level from %d to %d\n", + dc_clks->num_levels, i); + dc_clks->num_levels = i > 0 ? i : 1; + break; + } + } + } else if (clk_type == DM_PP_CLOCK_TYPE_MEMORY_CLK) { + for (i = 0; i < dc_clks->num_levels; i++) { + if (dc_clks->clocks_in_khz[i] > validation_clks.memory_max_clock) { + DRM_INFO("DM_PPLIB: reducing memory clock level from %d to %d\n", + dc_clks->num_levels, i); + dc_clks->num_levels = i > 0 ? i : 1; + break; + } + } + } + + return true; +} + +bool dm_pp_get_clock_levels_by_type_with_latency( + const struct dc_context *ctx, + enum dm_pp_clock_type clk_type, + struct dm_pp_clock_levels_with_latency *clk_level_info) +{ + struct amdgpu_device *adev = ctx->driver_context; + void *pp_handle = adev->powerplay.pp_handle; + struct pp_clock_levels_with_latency pp_clks = { 0 }; + const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs; + + if (!pp_funcs || !pp_funcs->get_clock_by_type_with_latency) + return false; + + if (pp_funcs->get_clock_by_type_with_latency(pp_handle, + dc_to_pp_clock_type(clk_type), + &pp_clks)) + return false; + + pp_to_dc_clock_levels_with_latency(&pp_clks, clk_level_info, clk_type); + + return true; +} + +bool dm_pp_get_clock_levels_by_type_with_voltage( + const struct dc_context *ctx, + enum dm_pp_clock_type clk_type, + struct dm_pp_clock_levels_with_voltage *clk_level_info) +{ + struct amdgpu_device *adev = ctx->driver_context; + void *pp_handle = adev->powerplay.pp_handle; + struct pp_clock_levels_with_voltage pp_clk_info = {0}; + const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs; + + if (pp_funcs->get_clock_by_type_with_voltage(pp_handle, + dc_to_pp_clock_type(clk_type), + &pp_clk_info)) + return false; + + pp_to_dc_clock_levels_with_voltage(&pp_clk_info, clk_level_info, clk_type); + + return true; +} + +bool dm_pp_notify_wm_clock_changes( + const struct dc_context *ctx, + struct dm_pp_wm_sets_with_clock_ranges *wm_with_clock_ranges) +{ + /* TODO: to be implemented */ + return false; +} + +bool dm_pp_apply_power_level_change_request( + const struct dc_context *ctx, + struct dm_pp_power_level_change_request *level_change_req) +{ + /* TODO: to be implemented */ + return false; +} + +bool dm_pp_apply_clock_for_voltage_request( + const struct dc_context *ctx, + struct dm_pp_clock_for_voltage_req *clock_for_voltage_req) +{ + struct amdgpu_device *adev = ctx->driver_context; + struct pp_display_clock_request pp_clock_request = {0}; + int ret = 0; + + pp_clock_request.clock_type = dc_to_pp_clock_type(clock_for_voltage_req->clk_type); + pp_clock_request.clock_freq_in_khz = clock_for_voltage_req->clocks_in_khz; + + if (!pp_clock_request.clock_type) + return false; + + if (adev->powerplay.pp_funcs->display_clock_voltage_request) + ret = adev->powerplay.pp_funcs->display_clock_voltage_request( + adev->powerplay.pp_handle, + &pp_clock_request); + if (ret) + return false; + return true; +} + +bool dm_pp_get_static_clocks( + const struct dc_context *ctx, + struct dm_pp_static_clock_info *static_clk_info) +{ + struct amdgpu_device *adev = ctx->driver_context; + struct amd_pp_clock_info pp_clk_info = {0}; + int ret = 0; + + if (adev->powerplay.pp_funcs->get_current_clocks) + ret = adev->powerplay.pp_funcs->get_current_clocks( + adev->powerplay.pp_handle, + &pp_clk_info); + if (ret) + return false; + + static_clk_info->max_clocks_state = pp_to_dc_powerlevel_state(pp_clk_info.max_clocks_state); + static_clk_info->max_mclk_khz = pp_clk_info.max_memory_clock * 10; + static_clk_info->max_sclk_khz = pp_clk_info.max_engine_clock * 10; + + return true; +} + +void pp_rv_set_display_requirement(struct pp_smu *pp, + struct pp_smu_display_requirement_rv *req) +{ + struct dc_context *ctx = pp->ctx; + struct amdgpu_device *adev = ctx->driver_context; + const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs; + + if (!pp_funcs || !pp_funcs->display_configuration_changed) + return; + + amdgpu_dpm_display_configuration_changed(adev); +} + +void pp_rv_set_wm_ranges(struct pp_smu *pp, + struct pp_smu_wm_range_sets *ranges) +{ + struct dc_context *ctx = pp->ctx; + struct amdgpu_device *adev = ctx->driver_context; + void *pp_handle = adev->powerplay.pp_handle; + const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs; + struct dm_pp_wm_sets_with_clock_ranges_soc15 wm_with_clock_ranges; + struct dm_pp_clock_range_for_dmif_wm_set_soc15 *wm_dce_clocks = wm_with_clock_ranges.wm_dmif_clocks_ranges; + struct dm_pp_clock_range_for_mcif_wm_set_soc15 *wm_soc_clocks = wm_with_clock_ranges.wm_mcif_clocks_ranges; + int32_t i; + + wm_with_clock_ranges.num_wm_dmif_sets = ranges->num_reader_wm_sets; + wm_with_clock_ranges.num_wm_mcif_sets = ranges->num_writer_wm_sets; + + for (i = 0; i < wm_with_clock_ranges.num_wm_dmif_sets; i++) { + if (ranges->reader_wm_sets[i].wm_inst > 3) + wm_dce_clocks[i].wm_set_id = WM_SET_A; + else + wm_dce_clocks[i].wm_set_id = + ranges->reader_wm_sets[i].wm_inst; + wm_dce_clocks[i].wm_max_dcfclk_clk_in_khz = + ranges->reader_wm_sets[i].max_drain_clk_khz; + wm_dce_clocks[i].wm_min_dcfclk_clk_in_khz = + ranges->reader_wm_sets[i].min_drain_clk_khz; + wm_dce_clocks[i].wm_max_mem_clk_in_khz = + ranges->reader_wm_sets[i].max_fill_clk_khz; + wm_dce_clocks[i].wm_min_mem_clk_in_khz = + ranges->reader_wm_sets[i].min_fill_clk_khz; + } + + for (i = 0; i < wm_with_clock_ranges.num_wm_mcif_sets; i++) { + if (ranges->writer_wm_sets[i].wm_inst > 3) + wm_soc_clocks[i].wm_set_id = WM_SET_A; + else + wm_soc_clocks[i].wm_set_id = + ranges->writer_wm_sets[i].wm_inst; + wm_soc_clocks[i].wm_max_socclk_clk_in_khz = + ranges->writer_wm_sets[i].max_fill_clk_khz; + wm_soc_clocks[i].wm_min_socclk_clk_in_khz = + ranges->writer_wm_sets[i].min_fill_clk_khz; + wm_soc_clocks[i].wm_max_mem_clk_in_khz = + ranges->writer_wm_sets[i].max_drain_clk_khz; + wm_soc_clocks[i].wm_min_mem_clk_in_khz = + ranges->writer_wm_sets[i].min_drain_clk_khz; + } + + pp_funcs->set_watermarks_for_clocks_ranges(pp_handle, &wm_with_clock_ranges); +} + +void pp_rv_set_pme_wa_enable(struct pp_smu *pp) +{ + struct dc_context *ctx = pp->ctx; + struct amdgpu_device *adev = ctx->driver_context; + void *pp_handle = adev->powerplay.pp_handle; + const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs; + + if (!pp_funcs || !pp_funcs->notify_smu_enable_pwe) + return; + + pp_funcs->notify_smu_enable_pwe(pp_handle); +} + +void dm_pp_get_funcs_rv( + struct dc_context *ctx, + struct pp_smu_funcs_rv *funcs) +{ + funcs->pp_smu.ctx = ctx; + funcs->set_display_requirement = pp_rv_set_display_requirement; + funcs->set_wm_ranges = pp_rv_set_wm_ranges; + funcs->set_pme_wa_enable = pp_rv_set_pme_wa_enable; +} diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_services.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_services.c index e861929dd981..516795342dd2 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_services.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_services.c @@ -35,6 +35,8 @@ #include "amdgpu_dm_irq.h" #include "amdgpu_pm.h" + + unsigned long long dm_get_elapse_time_in_ns(struct dc_context *ctx, unsigned long long current_time_stamp, unsigned long long last_time_stamp) @@ -72,326 +74,3 @@ bool dm_read_persistent_data(struct dc_context *ctx, /**** power component interfaces ****/ -bool dm_pp_apply_display_requirements( - const struct dc_context *ctx, - const struct dm_pp_display_configuration *pp_display_cfg) -{ - struct amdgpu_device *adev = ctx->driver_context; - - if (adev->pm.dpm_enabled) { - - memset(&adev->pm.pm_display_cfg, 0, - sizeof(adev->pm.pm_display_cfg)); - - adev->pm.pm_display_cfg.cpu_cc6_disable = - pp_display_cfg->cpu_cc6_disable; - - adev->pm.pm_display_cfg.cpu_pstate_disable = - pp_display_cfg->cpu_pstate_disable; - - adev->pm.pm_display_cfg.cpu_pstate_separation_time = - pp_display_cfg->cpu_pstate_separation_time; - - adev->pm.pm_display_cfg.nb_pstate_switch_disable = - pp_display_cfg->nb_pstate_switch_disable; - - adev->pm.pm_display_cfg.num_display = - pp_display_cfg->display_count; - adev->pm.pm_display_cfg.num_path_including_non_display = - pp_display_cfg->display_count; - - adev->pm.pm_display_cfg.min_core_set_clock = - pp_display_cfg->min_engine_clock_khz/10; - adev->pm.pm_display_cfg.min_core_set_clock_in_sr = - pp_display_cfg->min_engine_clock_deep_sleep_khz/10; - adev->pm.pm_display_cfg.min_mem_set_clock = - pp_display_cfg->min_memory_clock_khz/10; - - adev->pm.pm_display_cfg.multi_monitor_in_sync = - pp_display_cfg->all_displays_in_sync; - adev->pm.pm_display_cfg.min_vblank_time = - pp_display_cfg->avail_mclk_switch_time_us; - - adev->pm.pm_display_cfg.display_clk = - pp_display_cfg->disp_clk_khz/10; - - adev->pm.pm_display_cfg.dce_tolerable_mclk_in_active_latency = - pp_display_cfg->avail_mclk_switch_time_in_disp_active_us; - - adev->pm.pm_display_cfg.crtc_index = pp_display_cfg->crtc_index; - adev->pm.pm_display_cfg.line_time_in_us = - pp_display_cfg->line_time_in_us; - - adev->pm.pm_display_cfg.vrefresh = pp_display_cfg->disp_configs[0].v_refresh; - adev->pm.pm_display_cfg.crossfire_display_index = -1; - adev->pm.pm_display_cfg.min_bus_bandwidth = 0; - - /* TODO: complete implementation of - * pp_display_configuration_change(). - * Follow example of: - * PHM_StoreDALConfigurationData - powerplay\hwmgr\hardwaremanager.c - * PP_IRI_DisplayConfigurationChange - powerplay\eventmgr\iri.c */ - if (adev->powerplay.pp_funcs->display_configuration_change) - adev->powerplay.pp_funcs->display_configuration_change( - adev->powerplay.pp_handle, - &adev->pm.pm_display_cfg); - - /* TODO: replace by a separate call to 'apply display cfg'? */ - amdgpu_pm_compute_clocks(adev); - } - - return true; -} - -static void get_default_clock_levels( - enum dm_pp_clock_type clk_type, - struct dm_pp_clock_levels *clks) -{ - uint32_t disp_clks_in_khz[6] = { - 300000, 400000, 496560, 626090, 685720, 757900 }; - uint32_t sclks_in_khz[6] = { - 300000, 360000, 423530, 514290, 626090, 720000 }; - uint32_t mclks_in_khz[2] = { 333000, 800000 }; - - switch (clk_type) { - case DM_PP_CLOCK_TYPE_DISPLAY_CLK: - clks->num_levels = 6; - memmove(clks->clocks_in_khz, disp_clks_in_khz, - sizeof(disp_clks_in_khz)); - break; - case DM_PP_CLOCK_TYPE_ENGINE_CLK: - clks->num_levels = 6; - memmove(clks->clocks_in_khz, sclks_in_khz, - sizeof(sclks_in_khz)); - break; - case DM_PP_CLOCK_TYPE_MEMORY_CLK: - clks->num_levels = 2; - memmove(clks->clocks_in_khz, mclks_in_khz, - sizeof(mclks_in_khz)); - break; - default: - clks->num_levels = 0; - break; - } -} - -static enum amd_pp_clock_type dc_to_pp_clock_type( - enum dm_pp_clock_type dm_pp_clk_type) -{ - enum amd_pp_clock_type amd_pp_clk_type = 0; - - switch (dm_pp_clk_type) { - case DM_PP_CLOCK_TYPE_DISPLAY_CLK: - amd_pp_clk_type = amd_pp_disp_clock; - break; - case DM_PP_CLOCK_TYPE_ENGINE_CLK: - amd_pp_clk_type = amd_pp_sys_clock; - break; - case DM_PP_CLOCK_TYPE_MEMORY_CLK: - amd_pp_clk_type = amd_pp_mem_clock; - break; - default: - DRM_ERROR("DM_PPLIB: invalid clock type: %d!\n", - dm_pp_clk_type); - break; - } - - return amd_pp_clk_type; -} - -static void pp_to_dc_clock_levels( - const struct amd_pp_clocks *pp_clks, - struct dm_pp_clock_levels *dc_clks, - enum dm_pp_clock_type dc_clk_type) -{ - uint32_t i; - - if (pp_clks->count > DM_PP_MAX_CLOCK_LEVELS) { - DRM_INFO("DM_PPLIB: Warning: %s clock: number of levels %d exceeds maximum of %d!\n", - DC_DECODE_PP_CLOCK_TYPE(dc_clk_type), - pp_clks->count, - DM_PP_MAX_CLOCK_LEVELS); - - dc_clks->num_levels = DM_PP_MAX_CLOCK_LEVELS; - } else - dc_clks->num_levels = pp_clks->count; - - DRM_INFO("DM_PPLIB: values for %s clock\n", - DC_DECODE_PP_CLOCK_TYPE(dc_clk_type)); - - for (i = 0; i < dc_clks->num_levels; i++) { - DRM_INFO("DM_PPLIB:\t %d\n", pp_clks->clock[i]); - /* translate 10kHz to kHz */ - dc_clks->clocks_in_khz[i] = pp_clks->clock[i] * 10; - } -} - -static void pp_to_dc_clock_levels_with_latency( - const struct pp_clock_levels_with_latency *pp_clks, - struct dm_pp_clock_levels_with_latency *clk_level_info, - enum dm_pp_clock_type dc_clk_type) -{ - uint32_t i; - - if (pp_clks->num_levels > DM_PP_MAX_CLOCK_LEVELS) { - DRM_INFO("DM_PPLIB: Warning: %s clock: number of levels %d exceeds maximum of %d!\n", - DC_DECODE_PP_CLOCK_TYPE(dc_clk_type), - pp_clks->num_levels, - DM_PP_MAX_CLOCK_LEVELS); - - clk_level_info->num_levels = DM_PP_MAX_CLOCK_LEVELS; - } else - clk_level_info->num_levels = pp_clks->num_levels; - - DRM_DEBUG("DM_PPLIB: values for %s clock\n", - DC_DECODE_PP_CLOCK_TYPE(dc_clk_type)); - - for (i = 0; i < clk_level_info->num_levels; i++) { - DRM_DEBUG("DM_PPLIB:\t %d\n", pp_clks->data[i].clocks_in_khz); - clk_level_info->data[i].clocks_in_khz = pp_clks->data[i].clocks_in_khz; - clk_level_info->data[i].latency_in_us = pp_clks->data[i].latency_in_us; - } -} - -bool dm_pp_get_clock_levels_by_type( - const struct dc_context *ctx, - enum dm_pp_clock_type clk_type, - struct dm_pp_clock_levels *dc_clks) -{ - struct amdgpu_device *adev = ctx->driver_context; - void *pp_handle = adev->powerplay.pp_handle; - struct amd_pp_clocks pp_clks = { 0 }; - struct amd_pp_simple_clock_info validation_clks = { 0 }; - uint32_t i; - - if (adev->powerplay.pp_funcs->get_clock_by_type) { - if (adev->powerplay.pp_funcs->get_clock_by_type(pp_handle, - dc_to_pp_clock_type(clk_type), &pp_clks)) { - /* Error in pplib. Provide default values. */ - get_default_clock_levels(clk_type, dc_clks); - return true; - } - } - - pp_to_dc_clock_levels(&pp_clks, dc_clks, clk_type); - - if (adev->powerplay.pp_funcs->get_display_mode_validation_clocks) { - if (adev->powerplay.pp_funcs->get_display_mode_validation_clocks( - pp_handle, &validation_clks)) { - /* Error in pplib. Provide default values. */ - DRM_INFO("DM_PPLIB: Warning: using default validation clocks!\n"); - validation_clks.engine_max_clock = 72000; - validation_clks.memory_max_clock = 80000; - validation_clks.level = 0; - } - } - - DRM_INFO("DM_PPLIB: Validation clocks:\n"); - DRM_INFO("DM_PPLIB: engine_max_clock: %d\n", - validation_clks.engine_max_clock); - DRM_INFO("DM_PPLIB: memory_max_clock: %d\n", - validation_clks.memory_max_clock); - DRM_INFO("DM_PPLIB: level : %d\n", - validation_clks.level); - - /* Translate 10 kHz to kHz. */ - validation_clks.engine_max_clock *= 10; - validation_clks.memory_max_clock *= 10; - - /* Determine the highest non-boosted level from the Validation Clocks */ - if (clk_type == DM_PP_CLOCK_TYPE_ENGINE_CLK) { - for (i = 0; i < dc_clks->num_levels; i++) { - if (dc_clks->clocks_in_khz[i] > validation_clks.engine_max_clock) { - /* This clock is higher the validation clock. - * Than means the previous one is the highest - * non-boosted one. */ - DRM_INFO("DM_PPLIB: reducing engine clock level from %d to %d\n", - dc_clks->num_levels, i); - dc_clks->num_levels = i > 0 ? i : 1; - break; - } - } - } else if (clk_type == DM_PP_CLOCK_TYPE_MEMORY_CLK) { - for (i = 0; i < dc_clks->num_levels; i++) { - if (dc_clks->clocks_in_khz[i] > validation_clks.memory_max_clock) { - DRM_INFO("DM_PPLIB: reducing memory clock level from %d to %d\n", - dc_clks->num_levels, i); - dc_clks->num_levels = i > 0 ? i : 1; - break; - } - } - } - - return true; -} - -bool dm_pp_get_clock_levels_by_type_with_latency( - const struct dc_context *ctx, - enum dm_pp_clock_type clk_type, - struct dm_pp_clock_levels_with_latency *clk_level_info) -{ - struct amdgpu_device *adev = ctx->driver_context; - void *pp_handle = adev->powerplay.pp_handle; - struct pp_clock_levels_with_latency pp_clks = { 0 }; - const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs; - - if (!pp_funcs || !pp_funcs->get_clock_by_type_with_latency) - return false; - - if (pp_funcs->get_clock_by_type_with_latency(pp_handle, - dc_to_pp_clock_type(clk_type), - &pp_clks)) - return false; - - pp_to_dc_clock_levels_with_latency(&pp_clks, clk_level_info, clk_type); - - return true; -} - -bool dm_pp_get_clock_levels_by_type_with_voltage( - const struct dc_context *ctx, - enum dm_pp_clock_type clk_type, - struct dm_pp_clock_levels_with_voltage *clk_level_info) -{ - /* TODO: to be implemented */ - return false; -} - -bool dm_pp_notify_wm_clock_changes( - const struct dc_context *ctx, - struct dm_pp_wm_sets_with_clock_ranges *wm_with_clock_ranges) -{ - /* TODO: to be implemented */ - return false; -} - -bool dm_pp_apply_power_level_change_request( - const struct dc_context *ctx, - struct dm_pp_power_level_change_request *level_change_req) -{ - /* TODO: to be implemented */ - return false; -} - -bool dm_pp_apply_clock_for_voltage_request( - const struct dc_context *ctx, - struct dm_pp_clock_for_voltage_req *clock_for_voltage_req) -{ - /* TODO: to be implemented */ - return false; -} - -bool dm_pp_get_static_clocks( - const struct dc_context *ctx, - struct dm_pp_static_clock_info *static_clk_info) -{ - /* TODO: to be implemented */ - return false; -} - -void dm_pp_get_funcs_rv( - struct dc_context *ctx, - struct pp_smu_funcs_rv *funcs) -{} - -/**** end of power component interfaces ****/ |