diff options
Diffstat (limited to 'drivers/gpu/drm/amd/display/amdgpu_dm')
13 files changed, 2123 insertions, 342 deletions
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 55e39b462a5e..a0c8c41e4e57 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -34,6 +34,7 @@ #include "dc/inc/hw/dmcu.h" #include "dc/inc/hw/abm.h" #include "dc/dc_dmub_srv.h" +#include "dc/dc_edid_parser.h" #include "amdgpu_dm_trace.h" #include "vid.h" @@ -75,7 +76,6 @@ #include <drm/drm_edid.h> #include <drm/drm_vblank.h> #include <drm/drm_audio_component.h> -#include <drm/drm_hdcp.h> #if defined(CONFIG_DRM_AMD_DC_DCN) #include "ivsrcid/dcn/irqsrcs_dcn_1_0.h" @@ -121,7 +121,7 @@ MODULE_FIRMWARE(FIRMWARE_NAVI12_DMCU); * DOC: overview * * The AMDgpu display manager, **amdgpu_dm** (or even simpler, - * **dm**) sits between DRM and DC. It acts as a liason, converting DRM + * **dm**) sits between DRM and DC. It acts as a liaison, converting DRM * requests into DC requests, and DC responses into DRM responses. * * The root control structure is &struct amdgpu_display_manager. @@ -130,6 +130,7 @@ MODULE_FIRMWARE(FIRMWARE_NAVI12_DMCU); /* basic init/fini API */ static int amdgpu_dm_init(struct amdgpu_device *adev); static void amdgpu_dm_fini(struct amdgpu_device *adev); +static bool is_freesync_video_mode(const struct drm_display_mode *mode, struct amdgpu_dm_connector *aconnector); static enum drm_mode_subconnector get_subconnector_type(struct dc_link *link) { @@ -212,6 +213,9 @@ static bool amdgpu_dm_psr_disable_all(struct amdgpu_display_manager *dm); static const struct drm_format_info * amd_get_format_info(const struct drm_mode_fb_cmd2 *cmd); +static bool +is_timing_unchanged_for_freesync(struct drm_crtc_state *old_crtc_state, + struct drm_crtc_state *new_crtc_state); /* * dm_vblank_get_counter * @@ -335,6 +339,17 @@ static inline bool amdgpu_dm_vrr_active(struct dm_crtc_state *dm_state) dm_state->freesync_config.state == VRR_STATE_ACTIVE_FIXED; } +static inline bool is_dc_timing_adjust_needed(struct dm_crtc_state *old_state, + struct dm_crtc_state *new_state) +{ + if (new_state->freesync_config.state == VRR_STATE_ACTIVE_FIXED) + return true; + else if (amdgpu_dm_vrr_active(old_state) != amdgpu_dm_vrr_active(new_state)) + return true; + else + return false; +} + /** * dm_pflip_high_irq() - Handle pageflip interrupt * @interrupt_params: ignored @@ -357,14 +372,14 @@ static void dm_pflip_high_irq(void *interrupt_params) /* IRQ could occur when in initial stage */ /* TODO work and BO cleanup */ if (amdgpu_crtc == NULL) { - DRM_DEBUG_DRIVER("CRTC is null, returning.\n"); + DC_LOG_PFLIP("CRTC is null, returning.\n"); return; } spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags); if (amdgpu_crtc->pflip_status != AMDGPU_FLIP_SUBMITTED){ - DRM_DEBUG_DRIVER("amdgpu_crtc->pflip_status = %d !=AMDGPU_FLIP_SUBMITTED(%d) on crtc:%d[%p] \n", + DC_LOG_PFLIP("amdgpu_crtc->pflip_status = %d !=AMDGPU_FLIP_SUBMITTED(%d) on crtc:%d[%p] \n", amdgpu_crtc->pflip_status, AMDGPU_FLIP_SUBMITTED, amdgpu_crtc->crtc_id, @@ -435,9 +450,9 @@ static void dm_pflip_high_irq(void *interrupt_params) amdgpu_crtc->pflip_status = AMDGPU_FLIP_NONE; spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags); - DRM_DEBUG_DRIVER("crtc:%d[%p], pflip_stat:AMDGPU_FLIP_NONE, vrr[%d]-fp %d\n", - amdgpu_crtc->crtc_id, amdgpu_crtc, - vrr_active, (int) !e); + DC_LOG_PFLIP("crtc:%d[%p], pflip_stat:AMDGPU_FLIP_NONE, vrr[%d]-fp %d\n", + amdgpu_crtc->crtc_id, amdgpu_crtc, + vrr_active, (int) !e); } static void dm_vupdate_high_irq(void *interrupt_params) @@ -445,6 +460,9 @@ static void dm_vupdate_high_irq(void *interrupt_params) struct common_irq_params *irq_params = interrupt_params; struct amdgpu_device *adev = irq_params->adev; struct amdgpu_crtc *acrtc; + struct drm_device *drm_dev; + struct drm_vblank_crtc *vblank; + ktime_t frame_duration_ns, previous_timestamp; unsigned long flags; int vrr_active; @@ -452,8 +470,19 @@ static void dm_vupdate_high_irq(void *interrupt_params) if (acrtc) { vrr_active = amdgpu_dm_vrr_active_irq(acrtc); + drm_dev = acrtc->base.dev; + vblank = &drm_dev->vblank[acrtc->base.index]; + previous_timestamp = atomic64_read(&irq_params->previous_timestamp); + frame_duration_ns = vblank->time - previous_timestamp; + + if (frame_duration_ns > 0) { + trace_amdgpu_refresh_rate_track(acrtc->base.index, + frame_duration_ns, + ktime_divns(NSEC_PER_SEC, frame_duration_ns)); + atomic64_set(&irq_params->previous_timestamp, vblank->time); + } - DRM_DEBUG_VBL("crtc:%d, vupdate-vrr:%d\n", + DC_LOG_VBLANK("crtc:%d, vupdate-vrr:%d\n", acrtc->crtc_id, vrr_active); @@ -506,7 +535,7 @@ static void dm_crtc_high_irq(void *interrupt_params) vrr_active = amdgpu_dm_vrr_active_irq(acrtc); - DRM_DEBUG_VBL("crtc:%d, vupdate-vrr:%d, planes:%d\n", acrtc->crtc_id, + DC_LOG_VBLANK("crtc:%d, vupdate-vrr:%d, planes:%d\n", acrtc->crtc_id, vrr_active, acrtc->dm_irq_params.active_planes); /** @@ -566,6 +595,31 @@ static void dm_crtc_high_irq(void *interrupt_params) spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags); } +#if defined(CONFIG_DRM_AMD_DC_DCN) +/** + * dm_dcn_vertical_interrupt0_high_irq() - Handles OTG Vertical interrupt0 for + * DCN generation ASICs + * @interrupt params - interrupt parameters + * + * Used to set crc window/read out crc value at vertical line 0 position + */ +#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY) +static void dm_dcn_vertical_interrupt0_high_irq(void *interrupt_params) +{ + struct common_irq_params *irq_params = interrupt_params; + struct amdgpu_device *adev = irq_params->adev; + struct amdgpu_crtc *acrtc; + + acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VLINE0); + + if (!acrtc) + return; + + amdgpu_dm_crtc_handle_crc_window_irq(&acrtc->base); +} +#endif +#endif + static int dm_set_clockgating_state(void *handle, enum amd_clockgating_state state) { @@ -884,6 +938,32 @@ static int dm_dmub_hw_init(struct amdgpu_device *adev) } #if defined(CONFIG_DRM_AMD_DC_DCN) +#define DMUB_TRACE_MAX_READ 64 +static void dm_dmub_trace_high_irq(void *interrupt_params) +{ + struct common_irq_params *irq_params = interrupt_params; + struct amdgpu_device *adev = irq_params->adev; + struct amdgpu_display_manager *dm = &adev->dm; + struct dmcub_trace_buf_entry entry = { 0 }; + uint32_t count = 0; + + do { + if (dc_dmub_srv_get_dmub_outbox0_msg(dm->dc, &entry)) { + trace_amdgpu_dmub_trace_high_irq(entry.trace_code, entry.tick_count, + entry.param0, entry.param1); + + DRM_DEBUG_DRIVER("trace_code:%u, tick_count:%u, param0:%u, param1:%u\n", + entry.trace_code, entry.tick_count, entry.param0, entry.param1); + } else + break; + + count++; + + } while (count <= DMUB_TRACE_MAX_READ); + + ASSERT(count <= DMUB_TRACE_MAX_READ); +} + static void mmhub_read_system_context(struct amdgpu_device *adev, struct dc_phy_addr_space_config *pa_config) { uint64_t pt_base; @@ -948,15 +1028,12 @@ static void event_mall_stutter(struct work_struct *work) if (vblank_work->enable) dm->active_vblank_irq_count++; - else + else if(dm->active_vblank_irq_count) dm->active_vblank_irq_count--; + dc_allow_idle_optimizations(dm->dc, dm->active_vblank_irq_count == 0); - dc_allow_idle_optimizations( - dm->dc, dm->active_vblank_irq_count == 0 ? true : false); - - DRM_DEBUG_DRIVER("Allow idle optimizations (MALL): %d\n", dm->active_vblank_irq_count == 0); - + DRM_DEBUG_KMS("Allow idle optimizations (MALL): %d\n", dm->active_vblank_irq_count == 0); mutex_unlock(&dm->dc_lock); } @@ -1060,6 +1137,7 @@ static int amdgpu_dm_init(struct amdgpu_device *adev) init_data.flags.power_down_display_on_boot = true; + INIT_LIST_HEAD(&adev->dm.da_list); /* Display Core create. */ adev->dm.dc = dc_create(&init_data); @@ -1139,6 +1217,9 @@ static int amdgpu_dm_init(struct amdgpu_device *adev) dc_init_callbacks(adev->dm.dc, &init_params); } #endif +#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY) + adev->dm.crc_rd_wrk = amdgpu_dm_crtc_secure_display_create_work(); +#endif if (amdgpu_dm_initialize_drm_device(adev)) { DRM_ERROR( "amdgpu: failed to initialize sw for display support.\n"); @@ -1182,6 +1263,13 @@ static void amdgpu_dm_fini(struct amdgpu_device *adev) amdgpu_dm_destroy_drm_device(&adev->dm); +#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY) + if (adev->dm.crc_rd_wrk) { + flush_work(&adev->dm.crc_rd_wrk->notify_ta_work); + kfree(adev->dm.crc_rd_wrk); + adev->dm.crc_rd_wrk = NULL; + } +#endif #ifdef CONFIG_DRM_AMD_DC_HDCP if (adev->dm.hdcp_workqueue) { hdcp_destroy(&adev->dev->kobj, adev->dm.hdcp_workqueue); @@ -1191,6 +1279,15 @@ static void amdgpu_dm_fini(struct amdgpu_device *adev) if (adev->dm.dc) dc_deinit_callbacks(adev->dm.dc); #endif + +#if defined(CONFIG_DRM_AMD_DC_DCN) + if (adev->dm.vblank_workqueue) { + adev->dm.vblank_workqueue->dm = NULL; + kfree(adev->dm.vblank_workqueue); + adev->dm.vblank_workqueue = NULL; + } +#endif + if (adev->dm.dc->ctx->dmub_srv) { dc_dmub_srv_destroy(&adev->dm.dc->ctx->dmub_srv); adev->dm.dc->ctx->dmub_srv = NULL; @@ -1752,8 +1849,8 @@ static void dm_gpureset_toggle_interrupts(struct amdgpu_device *adev, if (acrtc && state->stream_status[i].plane_count != 0) { irq_source = IRQ_TYPE_PFLIP + acrtc->otg_inst; rc = dc_interrupt_set(adev->dm.dc, irq_source, enable) ? 0 : -EBUSY; - DRM_DEBUG("crtc %d - vupdate irq %sabling: r=%d\n", - acrtc->crtc_id, enable ? "en" : "dis", rc); + DRM_DEBUG_VBL("crtc %d - vupdate irq %sabling: r=%d\n", + acrtc->crtc_id, enable ? "en" : "dis", rc); if (rc) DRM_WARN("Failed to %s pflip interrupts\n", enable ? "enable" : "disable"); @@ -1847,6 +1944,9 @@ static int dm_suspend(void *handle) return ret; } +#ifdef CONFIG_DRM_AMD_SECURE_DISPLAY + amdgpu_dm_crtc_secure_display_suspend(adev); +#endif WARN_ON(adev->dm.cached_state); adev->dm.cached_state = drm_atomic_helper_suspend(adev_to_drm(adev)); @@ -2171,6 +2271,10 @@ static int dm_resume(void *handle) dm->cached_state = NULL; +#ifdef CONFIG_DRM_AMD_SECURE_DISPLAY + amdgpu_dm_crtc_secure_display_resume(adev); +#endif + amdgpu_dm_irq_resume_late(adev); amdgpu_dm_smu_write_watermarks_table(adev); @@ -2907,6 +3011,16 @@ static int dcn10_register_irq_handlers(struct amdgpu_device *adev) struct dc_interrupt_params int_params = {0}; int r; int i; +#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY) + static const unsigned int vrtl_int_srcid[] = { + DCN_1_0__SRCID__OTG1_VERTICAL_INTERRUPT0_CONTROL, + DCN_1_0__SRCID__OTG2_VERTICAL_INTERRUPT0_CONTROL, + DCN_1_0__SRCID__OTG3_VERTICAL_INTERRUPT0_CONTROL, + DCN_1_0__SRCID__OTG4_VERTICAL_INTERRUPT0_CONTROL, + DCN_1_0__SRCID__OTG5_VERTICAL_INTERRUPT0_CONTROL, + DCN_1_0__SRCID__OTG6_VERTICAL_INTERRUPT0_CONTROL + }; +#endif int_params.requested_polarity = INTERRUPT_POLARITY_DEFAULT; int_params.current_polarity = INTERRUPT_POLARITY_DEFAULT; @@ -2947,6 +3061,37 @@ static int dcn10_register_irq_handlers(struct amdgpu_device *adev) adev, &int_params, dm_crtc_high_irq, c_irq_params); } + /* Use otg vertical line interrupt */ +#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY) + for (i = 0; i <= adev->mode_info.num_crtc - 1; i++) { + r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, + vrtl_int_srcid[i], &adev->vline0_irq); + + if (r) { + DRM_ERROR("Failed to add vline0 irq id!\n"); + return r; + } + + int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT; + int_params.irq_source = + dc_interrupt_to_irq_source(dc, vrtl_int_srcid[i], 0); + + if (int_params.irq_source == DC_IRQ_SOURCE_INVALID) { + DRM_ERROR("Failed to register vline0 irq %d!\n", vrtl_int_srcid[i]); + break; + } + + c_irq_params = &adev->dm.vline0_params[int_params.irq_source + - DC_IRQ_SOURCE_DC1_VLINE0]; + + c_irq_params->adev = adev; + c_irq_params->irq_src = int_params.irq_source; + + amdgpu_dm_irq_register_interrupt(adev, &int_params, + dm_dcn_vertical_interrupt0_high_irq, c_irq_params); + } +#endif + /* Use VUPDATE_NO_LOCK interrupt on DCN, which seems to correspond to * the regular VUPDATE interrupt on DCE. We want DC_IRQ_SOURCE_VUPDATEx * to trigger at end of each vblank, regardless of state of the lock, @@ -2999,6 +3144,28 @@ static int dcn10_register_irq_handlers(struct amdgpu_device *adev) } + if (dc->ctx->dmub_srv) { + i = DCN_1_0__SRCID__DMCUB_OUTBOX_HIGH_PRIORITY_READY_INT; + r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, i, &adev->dmub_trace_irq); + + if (r) { + DRM_ERROR("Failed to add dmub trace irq id!\n"); + return r; + } + + int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT; + int_params.irq_source = + dc_interrupt_to_irq_source(dc, i, 0); + + c_irq_params = &adev->dm.dmub_trace_params[0]; + + c_irq_params->adev = adev; + c_irq_params->irq_src = int_params.irq_source; + + amdgpu_dm_irq_register_interrupt(adev, &int_params, + dm_dmub_trace_high_irq, c_irq_params); + } + /* HPD */ r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, DCN_1_0__SRCID__DC_HPD1_INT, &adev->hpd_irq); @@ -4787,8 +4954,8 @@ static void update_stream_scaling_settings(const struct drm_display_mode *mode, stream->src = src; stream->dst = dst; - DRM_DEBUG_DRIVER("Destination Rectangle x:%d y:%d width:%d height:%d\n", - dst.x, dst.y, dst.width, dst.height); + DRM_DEBUG_KMS("Destination Rectangle x:%d y:%d width:%d height:%d\n", + dst.x, dst.y, dst.width, dst.height); } @@ -5001,19 +5168,28 @@ static void fill_stream_properties_from_drm_display_mode( timing_out->hdmi_vic = hv_frame.vic; } - timing_out->h_addressable = mode_in->crtc_hdisplay; - timing_out->h_total = mode_in->crtc_htotal; - timing_out->h_sync_width = - mode_in->crtc_hsync_end - mode_in->crtc_hsync_start; - timing_out->h_front_porch = - mode_in->crtc_hsync_start - mode_in->crtc_hdisplay; - timing_out->v_total = mode_in->crtc_vtotal; - timing_out->v_addressable = mode_in->crtc_vdisplay; - timing_out->v_front_porch = - mode_in->crtc_vsync_start - mode_in->crtc_vdisplay; - timing_out->v_sync_width = - mode_in->crtc_vsync_end - mode_in->crtc_vsync_start; - timing_out->pix_clk_100hz = mode_in->crtc_clock * 10; + if (is_freesync_video_mode(mode_in, aconnector)) { + timing_out->h_addressable = mode_in->hdisplay; + timing_out->h_total = mode_in->htotal; + timing_out->h_sync_width = mode_in->hsync_end - mode_in->hsync_start; + timing_out->h_front_porch = mode_in->hsync_start - mode_in->hdisplay; + timing_out->v_total = mode_in->vtotal; + timing_out->v_addressable = mode_in->vdisplay; + timing_out->v_front_porch = mode_in->vsync_start - mode_in->vdisplay; + timing_out->v_sync_width = mode_in->vsync_end - mode_in->vsync_start; + timing_out->pix_clk_100hz = mode_in->clock * 10; + } else { + timing_out->h_addressable = mode_in->crtc_hdisplay; + timing_out->h_total = mode_in->crtc_htotal; + timing_out->h_sync_width = mode_in->crtc_hsync_end - mode_in->crtc_hsync_start; + timing_out->h_front_porch = mode_in->crtc_hsync_start - mode_in->crtc_hdisplay; + timing_out->v_total = mode_in->crtc_vtotal; + timing_out->v_addressable = mode_in->crtc_vdisplay; + timing_out->v_front_porch = mode_in->crtc_vsync_start - mode_in->crtc_vdisplay; + timing_out->v_sync_width = mode_in->crtc_vsync_end - mode_in->crtc_vsync_start; + timing_out->pix_clk_100hz = mode_in->crtc_clock * 10; + } + timing_out->aspect_ratio = get_aspect_ratio(mode_in); stream->output_color_space = get_output_color_space(timing_out); @@ -5132,9 +5308,14 @@ create_fake_sink(struct amdgpu_dm_connector *aconnector) static void set_multisync_trigger_params( struct dc_stream_state *stream) { + struct dc_stream_state *master = NULL; + if (stream->triggered_crtc_reset.enabled) { - stream->triggered_crtc_reset.event = CRTC_EVENT_VSYNC_RISING; - stream->triggered_crtc_reset.delay = TRIGGER_DELAY_NEXT_LINE; + master = stream->triggered_crtc_reset.event_source; + stream->triggered_crtc_reset.event = + master->timing.flags.VSYNC_POSITIVE_POLARITY ? + CRTC_EVENT_VSYNC_RISING : CRTC_EVENT_VSYNC_FALLING; + stream->triggered_crtc_reset.delay = TRIGGER_DELAY_NEXT_PIXEL; } } @@ -5164,6 +5345,7 @@ static void set_master_stream(struct dc_stream_state *stream_set[], static void dm_enable_per_frame_crtc_master_sync(struct dc_state *context) { int i = 0; + struct dc_stream_state *stream; if (context->stream_count < 2) return; @@ -5175,9 +5357,98 @@ static void dm_enable_per_frame_crtc_master_sync(struct dc_state *context) * crtc_sync_master.multi_sync_enabled flag * For now it's set to false */ - set_multisync_trigger_params(context->streams[i]); } + set_master_stream(context->streams, context->stream_count); + + for (i = 0; i < context->stream_count ; i++) { + stream = context->streams[i]; + + if (!stream) + continue; + + set_multisync_trigger_params(stream); + } +} + +static struct drm_display_mode * +get_highest_refresh_rate_mode(struct amdgpu_dm_connector *aconnector, + bool use_probed_modes) +{ + struct drm_display_mode *m, *m_pref = NULL; + u16 current_refresh, highest_refresh; + struct list_head *list_head = use_probed_modes ? + &aconnector->base.probed_modes : + &aconnector->base.modes; + + if (aconnector->freesync_vid_base.clock != 0) + return &aconnector->freesync_vid_base; + + /* Find the preferred mode */ + list_for_each_entry (m, list_head, head) { + if (m->type & DRM_MODE_TYPE_PREFERRED) { + m_pref = m; + break; + } + } + + if (!m_pref) { + /* Probably an EDID with no preferred mode. Fallback to first entry */ + m_pref = list_first_entry_or_null( + &aconnector->base.modes, struct drm_display_mode, head); + if (!m_pref) { + DRM_DEBUG_DRIVER("No preferred mode found in EDID\n"); + return NULL; + } + } + + highest_refresh = drm_mode_vrefresh(m_pref); + + /* + * Find the mode with highest refresh rate with same resolution. + * For some monitors, preferred mode is not the mode with highest + * supported refresh rate. + */ + list_for_each_entry (m, list_head, head) { + current_refresh = drm_mode_vrefresh(m); + + if (m->hdisplay == m_pref->hdisplay && + m->vdisplay == m_pref->vdisplay && + highest_refresh < current_refresh) { + highest_refresh = current_refresh; + m_pref = m; + } + } + + aconnector->freesync_vid_base = *m_pref; + return m_pref; +} + +static bool is_freesync_video_mode(const struct drm_display_mode *mode, + struct amdgpu_dm_connector *aconnector) +{ + struct drm_display_mode *high_mode; + int timing_diff; + + high_mode = get_highest_refresh_rate_mode(aconnector, false); + if (!high_mode || !mode) + return false; + + timing_diff = high_mode->vtotal - mode->vtotal; + + if (high_mode->clock == 0 || high_mode->clock != mode->clock || + high_mode->hdisplay != mode->hdisplay || + high_mode->vdisplay != mode->vdisplay || + high_mode->hsync_start != mode->hsync_start || + high_mode->hsync_end != mode->hsync_end || + high_mode->htotal != mode->htotal || + high_mode->hskew != mode->hskew || + high_mode->vscan != mode->vscan || + high_mode->vsync_start - mode->vsync_start != timing_diff || + high_mode->vsync_end - mode->vsync_end != timing_diff) + return false; + else + return true; } static struct dc_stream_state * @@ -5193,8 +5464,10 @@ create_stream_for_sink(struct amdgpu_dm_connector *aconnector, dm_state ? &dm_state->base : NULL; struct dc_stream_state *stream = NULL; struct drm_display_mode mode = *drm_mode; + struct drm_display_mode saved_mode; + struct drm_display_mode *freesync_mode = NULL; bool native_mode_found = false; - bool scale = dm_state ? (dm_state->scaling != RMX_OFF) : false; + bool recalculate_timing = dm_state ? (dm_state->scaling != RMX_OFF) : false; int mode_refresh; int preferred_refresh = 0; #if defined(CONFIG_DRM_AMD_DC_DCN) @@ -5202,6 +5475,9 @@ create_stream_for_sink(struct amdgpu_dm_connector *aconnector, uint32_t link_bandwidth_kbps; #endif struct dc_sink *sink = NULL; + + memset(&saved_mode, 0, sizeof(saved_mode)); + if (aconnector == NULL) { DRM_ERROR("aconnector is NULL!\n"); return stream; @@ -5254,25 +5530,38 @@ create_stream_for_sink(struct amdgpu_dm_connector *aconnector, */ DRM_DEBUG_DRIVER("No preferred mode found\n"); } else { - decide_crtc_timing_for_drm_display_mode( + recalculate_timing |= amdgpu_freesync_vid_mode && + is_freesync_video_mode(&mode, aconnector); + if (recalculate_timing) { + freesync_mode = get_highest_refresh_rate_mode(aconnector, false); + saved_mode = mode; + mode = *freesync_mode; + } else { + decide_crtc_timing_for_drm_display_mode( &mode, preferred_mode, dm_state ? (dm_state->scaling != RMX_OFF) : false); + } + preferred_refresh = drm_mode_vrefresh(preferred_mode); } - if (!dm_state) + if (recalculate_timing) + drm_mode_set_crtcinfo(&saved_mode, 0); + else if (!dm_state) drm_mode_set_crtcinfo(&mode, 0); - /* + /* * If scaling is enabled and refresh rate didn't change * we copy the vic and polarities of the old timings */ - if (!scale || mode_refresh != preferred_refresh) - fill_stream_properties_from_drm_display_mode(stream, - &mode, &aconnector->base, con_state, NULL, requested_bpc); + if (!recalculate_timing || mode_refresh != preferred_refresh) + fill_stream_properties_from_drm_display_mode( + stream, &mode, &aconnector->base, con_state, NULL, + requested_bpc); else - fill_stream_properties_from_drm_display_mode(stream, - &mode, &aconnector->base, con_state, old_stream, requested_bpc); + fill_stream_properties_from_drm_display_mode( + stream, &mode, &aconnector->base, con_state, old_stream, + requested_bpc); stream->timing.flags.DSC = 0; @@ -5409,15 +5698,22 @@ dm_crtc_duplicate_state(struct drm_crtc *crtc) state->abm_level = cur->abm_level; state->vrr_supported = cur->vrr_supported; state->freesync_config = cur->freesync_config; - state->crc_src = cur->crc_src; state->cm_has_degamma = cur->cm_has_degamma; state->cm_is_degamma_srgb = cur->cm_is_degamma_srgb; - /* TODO Duplicate dc_stream after objects are stream object is flattened */ return &state->base; } +#ifdef CONFIG_DRM_AMD_SECURE_DISPLAY +static int amdgpu_dm_crtc_late_register(struct drm_crtc *crtc) +{ + crtc_debugfs_init(crtc); + + return 0; +} +#endif + static inline int dm_set_vupdate_irq(struct drm_crtc *crtc, bool enable) { enum dc_irq_source irq_source; @@ -5429,8 +5725,8 @@ static inline int dm_set_vupdate_irq(struct drm_crtc *crtc, bool enable) rc = dc_interrupt_set(adev->dm.dc, irq_source, enable) ? 0 : -EBUSY; - DRM_DEBUG_DRIVER("crtc %d - vupdate irq %sabling: r=%d\n", - acrtc->crtc_id, enable ? "en" : "dis", rc); + DRM_DEBUG_VBL("crtc %d - vupdate irq %sabling: r=%d\n", + acrtc->crtc_id, enable ? "en" : "dis", rc); return rc; } @@ -5503,6 +5799,9 @@ static const struct drm_crtc_funcs amdgpu_dm_crtc_funcs = { .enable_vblank = dm_enable_vblank, .disable_vblank = dm_disable_vblank, .get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp, +#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY) + .late_register = amdgpu_dm_crtc_late_register, +#endif }; static enum drm_connector_status @@ -5865,6 +6164,15 @@ create_validate_stream_for_sink(struct amdgpu_dm_connector *aconnector, } while (stream == NULL && requested_bpc >= 6); + if (dc_result == DC_FAIL_ENC_VALIDATE && !aconnector->force_yuv420_output) { + DRM_DEBUG_KMS("Retry forcing YCbCr420 encoding\n"); + + aconnector->force_yuv420_output = true; + stream = create_validate_stream_for_sink(aconnector, drm_mode, + dm_state, old_stream); + aconnector->force_yuv420_output = false; + } + return stream; } @@ -6367,7 +6675,7 @@ static int dm_plane_helper_prepare_fb(struct drm_plane *plane, int r; if (!new_state->fb) { - DRM_DEBUG_DRIVER("No FB bound\n"); + DRM_DEBUG_KMS("No FB bound\n"); return 0; } @@ -6488,13 +6796,17 @@ static int dm_plane_helper_check_state(struct drm_plane_state *state, else if (state->crtc_y + state->crtc_h > new_crtc_state->mode.crtc_vdisplay) viewport_height = new_crtc_state->mode.crtc_vdisplay - state->crtc_y; - /* If completely outside of screen, viewport_width and/or viewport_height will be negative, - * which is still OK to satisfy the condition below, thereby also covering these cases - * (when plane is completely outside of screen). - * x2 for width is because of pipe-split. - */ - if (viewport_width < MIN_VIEWPORT_SIZE*2 || viewport_height < MIN_VIEWPORT_SIZE) + if (viewport_width < 0 || viewport_height < 0) { + DRM_DEBUG_ATOMIC("Plane completely outside of screen\n"); + return -EINVAL; + } else if (viewport_width < MIN_VIEWPORT_SIZE*2) { /* x2 for width is because of pipe-split. */ + DRM_DEBUG_ATOMIC("Viewport width %d smaller than %d\n", viewport_width, MIN_VIEWPORT_SIZE*2); return -EINVAL; + } else if (viewport_height < MIN_VIEWPORT_SIZE) { + DRM_DEBUG_ATOMIC("Viewport height %d smaller than %d\n", viewport_height, MIN_VIEWPORT_SIZE); + return -EINVAL; + } + } /* Get min/max allowed scaling factors from plane caps. */ @@ -6975,11 +7287,118 @@ static void amdgpu_dm_connector_ddc_get_modes(struct drm_connector *connector, */ drm_mode_sort(&connector->probed_modes); amdgpu_dm_get_native_mode(connector); + + /* Freesync capabilities are reset by calling + * drm_add_edid_modes() and need to be + * restored here. + */ + amdgpu_dm_update_freesync_caps(connector, edid); } else { amdgpu_dm_connector->num_modes = 0; } } +static bool is_duplicate_mode(struct amdgpu_dm_connector *aconnector, + struct drm_display_mode *mode) +{ + struct drm_display_mode *m; + + list_for_each_entry (m, &aconnector->base.probed_modes, head) { + if (drm_mode_equal(m, mode)) + return true; + } + + return false; +} + +static uint add_fs_modes(struct amdgpu_dm_connector *aconnector) +{ + const struct drm_display_mode *m; + struct drm_display_mode *new_mode; + uint i; + uint32_t new_modes_count = 0; + + /* Standard FPS values + * + * 23.976 - TV/NTSC + * 24 - Cinema + * 25 - TV/PAL + * 29.97 - TV/NTSC + * 30 - TV/NTSC + * 48 - Cinema HFR + * 50 - TV/PAL + * 60 - Commonly used + * 48,72,96 - Multiples of 24 + */ + const uint32_t common_rates[] = { 23976, 24000, 25000, 29970, 30000, + 48000, 50000, 60000, 72000, 96000 }; + + /* + * Find mode with highest refresh rate with the same resolution + * as the preferred mode. Some monitors report a preferred mode + * with lower resolution than the highest refresh rate supported. + */ + + m = get_highest_refresh_rate_mode(aconnector, true); + if (!m) + return 0; + + for (i = 0; i < ARRAY_SIZE(common_rates); i++) { + uint64_t target_vtotal, target_vtotal_diff; + uint64_t num, den; + + if (drm_mode_vrefresh(m) * 1000 < common_rates[i]) + continue; + + if (common_rates[i] < aconnector->min_vfreq * 1000 || + common_rates[i] > aconnector->max_vfreq * 1000) + continue; + + num = (unsigned long long)m->clock * 1000 * 1000; + den = common_rates[i] * (unsigned long long)m->htotal; + target_vtotal = div_u64(num, den); + target_vtotal_diff = target_vtotal - m->vtotal; + + /* Check for illegal modes */ + if (m->vsync_start + target_vtotal_diff < m->vdisplay || + m->vsync_end + target_vtotal_diff < m->vsync_start || + m->vtotal + target_vtotal_diff < m->vsync_end) + continue; + + new_mode = drm_mode_duplicate(aconnector->base.dev, m); + if (!new_mode) + goto out; + + new_mode->vtotal += (u16)target_vtotal_diff; + new_mode->vsync_start += (u16)target_vtotal_diff; + new_mode->vsync_end += (u16)target_vtotal_diff; + new_mode->type &= ~DRM_MODE_TYPE_PREFERRED; + new_mode->type |= DRM_MODE_TYPE_DRIVER; + + if (!is_duplicate_mode(aconnector, new_mode)) { + drm_mode_probed_add(&aconnector->base, new_mode); + new_modes_count += 1; + } else + drm_mode_destroy(aconnector->base.dev, new_mode); + } + out: + return new_modes_count; +} + +static void amdgpu_dm_connector_add_freesync_modes(struct drm_connector *connector, + struct edid *edid) +{ + struct amdgpu_dm_connector *amdgpu_dm_connector = + to_amdgpu_dm_connector(connector); + + if (!(amdgpu_freesync_vid_mode && edid)) + return; + + if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10) + amdgpu_dm_connector->num_modes += + add_fs_modes(amdgpu_dm_connector); +} + static int amdgpu_dm_connector_get_modes(struct drm_connector *connector) { struct amdgpu_dm_connector *amdgpu_dm_connector = @@ -6995,6 +7414,7 @@ static int amdgpu_dm_connector_get_modes(struct drm_connector *connector) } else { amdgpu_dm_connector_ddc_get_modes(connector, edid); amdgpu_dm_connector_add_common_modes(encoder, connector); + amdgpu_dm_connector_add_freesync_modes(connector, edid); } amdgpu_dm_fbc_init(connector); @@ -7299,8 +7719,19 @@ static void manage_dm_interrupts(struct amdgpu_device *adev, adev, &adev->pageflip_irq, irq_type); +#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY) + amdgpu_irq_get( + adev, + &adev->vline0_irq, + irq_type); +#endif } else { - +#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY) + amdgpu_irq_put( + adev, + &adev->vline0_irq, + irq_type); +#endif amdgpu_irq_put( adev, &adev->pageflip_irq, @@ -7424,10 +7855,6 @@ static int get_cursor_position(struct drm_plane *plane, struct drm_crtc *crtc, int x, y; int xorigin = 0, yorigin = 0; - position->enable = false; - position->x = 0; - position->y = 0; - if (!crtc || !plane->state->fb) return 0; @@ -7474,18 +7901,18 @@ static void handle_cursor_update(struct drm_plane *plane, struct dm_crtc_state *crtc_state = crtc ? to_dm_crtc_state(crtc->state) : NULL; struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); uint64_t address = afb ? afb->address : 0; - struct dc_cursor_position position; + struct dc_cursor_position position = {0}; struct dc_cursor_attributes attributes; int ret; if (!plane->state->fb && !old_plane_state->fb) return; - DRM_DEBUG_DRIVER("%s: crtc_id=%d with size %d to %d\n", - __func__, - amdgpu_crtc->crtc_id, - plane->state->crtc_w, - plane->state->crtc_h); + DC_LOG_CURSOR("%s: crtc_id=%d with size %d to %d\n", + __func__, + amdgpu_crtc->crtc_id, + plane->state->crtc_w, + plane->state->crtc_h); ret = get_cursor_position(plane, crtc, &position); if (ret) @@ -7543,8 +7970,8 @@ static void prepare_flip_isr(struct amdgpu_crtc *acrtc) /* Mark this event as consumed */ acrtc->base.state->event = NULL; - DRM_DEBUG_DRIVER("crtc:%d, pflip_stat:AMDGPU_FLIP_SUBMITTED\n", - acrtc->crtc_id); + DC_LOG_PFLIP("crtc:%d, pflip_stat:AMDGPU_FLIP_SUBMITTED\n", + acrtc->crtc_id); } static void update_freesync_state_on_stream( @@ -7559,6 +7986,7 @@ static void update_freesync_state_on_stream( struct amdgpu_device *adev = dm->adev; struct amdgpu_crtc *acrtc = to_amdgpu_crtc(new_crtc_state->base.crtc); unsigned long flags; + bool pack_sdp_v1_3 = false; if (!new_stream) return; @@ -7600,7 +8028,8 @@ static void update_freesync_state_on_stream( &vrr_params, PACKET_TYPE_VRR, TRANSFER_FUNC_UNKNOWN, - &vrr_infopacket); + &vrr_infopacket, + pack_sdp_v1_3); new_crtc_state->freesync_timing_changed |= (memcmp(&acrtc->dm_irq_params.vrr_params.adjust, @@ -7654,9 +8083,22 @@ static void update_stream_irq_parameters( if (new_crtc_state->vrr_supported && config.min_refresh_in_uhz && config.max_refresh_in_uhz) { - config.state = new_crtc_state->base.vrr_enabled ? - VRR_STATE_ACTIVE_VARIABLE : - VRR_STATE_INACTIVE; + /* + * if freesync compatible mode was set, config.state will be set + * in atomic check + */ + if (config.state == VRR_STATE_ACTIVE_FIXED && config.fixed_refresh_in_uhz && + (!drm_atomic_crtc_needs_modeset(&new_crtc_state->base) || + new_crtc_state->freesync_config.state == VRR_STATE_ACTIVE_FIXED)) { + vrr_params.max_refresh_in_uhz = config.max_refresh_in_uhz; + vrr_params.min_refresh_in_uhz = config.min_refresh_in_uhz; + vrr_params.fixed_refresh_in_uhz = config.fixed_refresh_in_uhz; + vrr_params.state = VRR_STATE_ACTIVE_FIXED; + } else { + config.state = new_crtc_state->base.vrr_enabled ? + VRR_STATE_ACTIVE_VARIABLE : + VRR_STATE_INACTIVE; + } } else { config.state = VRR_STATE_UNSUPPORTED; } @@ -7835,7 +8277,7 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, &bundle->flip_addrs[planes_count].address, afb->tmz_surface, false); - DRM_DEBUG_DRIVER("plane: id=%d dcc_en=%d\n", + DRM_DEBUG_ATOMIC("plane: id=%d dcc_en=%d\n", new_plane_state->plane->index, bundle->plane_infos[planes_count].dcc.enable); @@ -7869,7 +8311,7 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, dc_plane, bundle->flip_addrs[planes_count].flip_timestamp_in_us); - DRM_DEBUG_DRIVER("%s Flipping to hi: 0x%x, low: 0x%x\n", + DRM_DEBUG_ATOMIC("%s Flipping to hi: 0x%x, low: 0x%x\n", __func__, bundle->flip_addrs[planes_count].address.grph.addr.high_part, bundle->flip_addrs[planes_count].address.grph.addr.low_part); @@ -7977,8 +8419,7 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, * re-adjust the min/max bounds now that DC doesn't handle this * as part of commit. */ - if (amdgpu_dm_vrr_active(dm_old_crtc_state) != - amdgpu_dm_vrr_active(acrtc_state)) { + if (is_dc_timing_adjust_needed(dm_old_crtc_state, acrtc_state)) { spin_lock_irqsave(&pcrtc->dev->event_lock, flags); dc_stream_adjust_vmin_vmax( dm->dc, acrtc_state->stream, @@ -8192,7 +8633,7 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state) dm_new_crtc_state = to_dm_crtc_state(new_crtc_state); dm_old_crtc_state = to_dm_crtc_state(old_crtc_state); - DRM_DEBUG_DRIVER( + DRM_DEBUG_ATOMIC( "amdgpu_crtc id:%d crtc_state_flags: enable:%d, active:%d, " "planes_changed:%d, mode_changed:%d,active_changed:%d," "connectors_changed:%d\n", @@ -8226,7 +8667,7 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state) if (modeset_required(new_crtc_state, dm_new_crtc_state->stream, dm_old_crtc_state->stream)) { - DRM_DEBUG_DRIVER("Atomic commit: SET crtc id %d: [%p]\n", acrtc->crtc_id, acrtc); + DRM_DEBUG_ATOMIC("Atomic commit: SET crtc id %d: [%p]\n", acrtc->crtc_id, acrtc); if (!dm_new_crtc_state->stream) { /* @@ -8259,10 +8700,11 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state) crtc->hwmode = new_crtc_state->mode; mode_set_reset_required = true; } else if (modereset_required(new_crtc_state)) { - DRM_DEBUG_DRIVER("Atomic commit: RESET. crtc id %d:[%p]\n", acrtc->crtc_id, acrtc); + DRM_DEBUG_ATOMIC("Atomic commit: RESET. crtc id %d:[%p]\n", acrtc->crtc_id, acrtc); /* i.e. reset mode */ if (dm_old_crtc_state->stream) remove_stream(adev, acrtc, dm_old_crtc_state->stream); + mode_set_reset_required = true; } } /* for_each_crtc_in_state() */ @@ -8275,6 +8717,11 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state) dm_enable_per_frame_crtc_master_sync(dc_state); mutex_lock(&dm->dc_lock); WARN_ON(!dc_commit_state(dm->dc, dc_state)); +#if defined(CONFIG_DRM_AMD_DC_DCN) + /* Allow idle optimization when vblank count is 0 for display off */ + if (dm->active_vblank_irq_count == 0) + dc_allow_idle_optimizations(dm->dc,true); +#endif mutex_unlock(&dm->dc_lock); } @@ -8321,8 +8768,7 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state) hdcp_update_display( adev->dm.hdcp_workqueue, aconnector->dc_link->link_index, aconnector, new_con_state->hdcp_content_type, - new_con_state->content_protection == DRM_MODE_CONTENT_PROTECTION_DESIRED ? true - : false); + new_con_state->content_protection == DRM_MODE_CONTENT_PROTECTION_DESIRED); } #endif @@ -8432,7 +8878,10 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state) */ for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc); - +#ifdef CONFIG_DEBUG_FS + bool configure_crc = false; + enum amdgpu_dm_pipe_crc_source cur_crc_src; +#endif dm_new_crtc_state = to_dm_crtc_state(new_crtc_state); if (new_crtc_state->active && @@ -8448,12 +8897,21 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state) * settings for the stream. */ dm_new_crtc_state = to_dm_crtc_state(new_crtc_state); + spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags); + cur_crc_src = acrtc->dm_irq_params.crc_src; + spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags); + + if (amdgpu_dm_is_valid_crc_source(cur_crc_src)) { + configure_crc = true; +#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY) + if (amdgpu_dm_crc_window_is_activated(crtc)) + configure_crc = false; +#endif + } - if (amdgpu_dm_is_valid_crc_source(dm_new_crtc_state->crc_src)) { + if (configure_crc) amdgpu_dm_crtc_configure_crc_source( - crtc, dm_new_crtc_state, - dm_new_crtc_state->crc_src); - } + crtc, dm_new_crtc_state, cur_crc_src); #endif } } @@ -8662,6 +9120,7 @@ static void get_freesync_config_for_crtc( to_amdgpu_dm_connector(new_con_state->base.connector); struct drm_display_mode *mode = &new_crtc_state->base.mode; int vrefresh = drm_mode_vrefresh(mode); + bool fs_vid_mode = false; new_crtc_state->vrr_supported = new_con_state->freesync_capable && vrefresh >= aconnector->min_vfreq && @@ -8669,17 +9128,24 @@ static void get_freesync_config_for_crtc( if (new_crtc_state->vrr_supported) { new_crtc_state->stream->ignore_msa_timing_param = true; - config.state = new_crtc_state->base.vrr_enabled ? - VRR_STATE_ACTIVE_VARIABLE : - VRR_STATE_INACTIVE; - config.min_refresh_in_uhz = - aconnector->min_vfreq * 1000000; - config.max_refresh_in_uhz = - aconnector->max_vfreq * 1000000; + fs_vid_mode = new_crtc_state->freesync_config.state == VRR_STATE_ACTIVE_FIXED; + + config.min_refresh_in_uhz = aconnector->min_vfreq * 1000000; + config.max_refresh_in_uhz = aconnector->max_vfreq * 1000000; config.vsif_supported = true; config.btr = true; - } + if (fs_vid_mode) { + config.state = VRR_STATE_ACTIVE_FIXED; + config.fixed_refresh_in_uhz = new_crtc_state->freesync_config.fixed_refresh_in_uhz; + goto out; + } else if (new_crtc_state->base.vrr_enabled) { + config.state = VRR_STATE_ACTIVE_VARIABLE; + } else { + config.state = VRR_STATE_INACTIVE; + } + } +out: new_crtc_state->freesync_config = config; } @@ -8692,6 +9158,50 @@ static void reset_freesync_config_for_crtc( sizeof(new_crtc_state->vrr_infopacket)); } +static bool +is_timing_unchanged_for_freesync(struct drm_crtc_state *old_crtc_state, + struct drm_crtc_state *new_crtc_state) +{ + struct drm_display_mode old_mode, new_mode; + + if (!old_crtc_state || !new_crtc_state) + return false; + + old_mode = old_crtc_state->mode; + new_mode = new_crtc_state->mode; + + if (old_mode.clock == new_mode.clock && + old_mode.hdisplay == new_mode.hdisplay && + old_mode.vdisplay == new_mode.vdisplay && + old_mode.htotal == new_mode.htotal && + old_mode.vtotal != new_mode.vtotal && + old_mode.hsync_start == new_mode.hsync_start && + old_mode.vsync_start != new_mode.vsync_start && + old_mode.hsync_end == new_mode.hsync_end && + old_mode.vsync_end != new_mode.vsync_end && + old_mode.hskew == new_mode.hskew && + old_mode.vscan == new_mode.vscan && + (old_mode.vsync_end - old_mode.vsync_start) == + (new_mode.vsync_end - new_mode.vsync_start)) + return true; + + return false; +} + +static void set_freesync_fixed_config(struct dm_crtc_state *dm_new_crtc_state) { + uint64_t num, den, res; + struct drm_crtc_state *new_crtc_state = &dm_new_crtc_state->base; + + dm_new_crtc_state->freesync_config.state = VRR_STATE_ACTIVE_FIXED; + + num = (unsigned long long)new_crtc_state->mode.clock * 1000 * 1000000; + den = (unsigned long long)new_crtc_state->mode.htotal * + (unsigned long long)new_crtc_state->mode.vtotal; + + res = div_u64(num, den); + dm_new_crtc_state->freesync_config.fixed_refresh_in_uhz = res; +} + static int dm_update_crtc_state(struct amdgpu_display_manager *dm, struct drm_atomic_state *state, struct drm_crtc *crtc, @@ -8782,6 +9292,11 @@ static int dm_update_crtc_state(struct amdgpu_display_manager *dm, * TODO: Refactor this function to allow this check to work * in all conditions. */ + if (amdgpu_freesync_vid_mode && + dm_new_crtc_state->stream && + is_timing_unchanged_for_freesync(new_crtc_state, old_crtc_state)) + goto skip_modeset; + if (dm_new_crtc_state->stream && dc_is_stream_unchanged(new_stream, dm_old_crtc_state->stream) && dc_is_stream_scaling_unchanged(new_stream, dm_old_crtc_state->stream)) { @@ -8795,7 +9310,7 @@ static int dm_update_crtc_state(struct amdgpu_display_manager *dm, if (!drm_atomic_crtc_needs_modeset(new_crtc_state)) goto skip_modeset; - DRM_DEBUG_DRIVER( + DRM_DEBUG_ATOMIC( "amdgpu_crtc id:%d crtc_state_flags: enable:%d, active:%d, " "planes_changed:%d, mode_changed:%d,active_changed:%d," "connectors_changed:%d\n", @@ -8813,6 +9328,24 @@ static int dm_update_crtc_state(struct amdgpu_display_manager *dm, if (!dm_old_crtc_state->stream) goto skip_modeset; + if (amdgpu_freesync_vid_mode && dm_new_crtc_state->stream && + is_timing_unchanged_for_freesync(new_crtc_state, + old_crtc_state)) { + new_crtc_state->mode_changed = false; + DRM_DEBUG_DRIVER( + "Mode change not required for front porch change, " + "setting mode_changed to %d", + new_crtc_state->mode_changed); + + set_freesync_fixed_config(dm_new_crtc_state); + + goto skip_modeset; + } else if (amdgpu_freesync_vid_mode && aconnector && + is_freesync_video_mode(&new_crtc_state->mode, + aconnector)) { + set_freesync_fixed_config(dm_new_crtc_state); + } + ret = dm_atomic_get_state(state, &dm_state); if (ret) goto fail; @@ -8861,8 +9394,8 @@ static int dm_update_crtc_state(struct amdgpu_display_manager *dm, dc_stream_retain(new_stream); - DRM_DEBUG_DRIVER("Enabling DRM crtc: %d\n", - crtc->base.id); + DRM_DEBUG_ATOMIC("Enabling DRM crtc: %d\n", + crtc->base.id); if (dc_add_stream_to_ctx( dm->dc, @@ -9207,8 +9740,8 @@ static int dm_update_plane_state(struct dc *dc, if (!dc_new_plane_state) return -ENOMEM; - DRM_DEBUG_DRIVER("Enabling DRM plane: %d on DRM crtc %d\n", - plane->base.id, new_plane_crtc->base.id); + DRM_DEBUG_ATOMIC("Enabling DRM plane: %d on DRM crtc %d\n", + plane->base.id, new_plane_crtc->base.id); ret = fill_dc_plane_attributes( drm_to_adev(new_plane_crtc->dev), @@ -9271,7 +9804,8 @@ static int dm_check_crtc_cursor(struct drm_atomic_state *state, new_cursor_state = drm_atomic_get_new_plane_state(state, crtc->cursor); new_primary_state = drm_atomic_get_new_plane_state(state, crtc->primary); - if (!new_cursor_state || !new_primary_state || !new_cursor_state->fb) { + if (!new_cursor_state || !new_primary_state || + !new_cursor_state->fb || !new_primary_state->fb) { return 0; } @@ -9390,7 +9924,7 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev, } #if defined(CONFIG_DRM_AMD_DC_DCN) - if (adev->asic_type >= CHIP_NAVI10) { + if (dc_resource_is_dsc_encoding_supported(dc)) { for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { if (drm_atomic_crtc_needs_modeset(new_crtc_state)) { ret = add_affected_mst_dsc_crtcs(state, crtc); @@ -9696,11 +10230,85 @@ static bool is_dp_capable_without_timing_msa(struct dc *dc, return capable; } + +static bool parse_edid_cea(struct amdgpu_dm_connector *aconnector, + uint8_t *edid_ext, int len, + struct amdgpu_hdmi_vsdb_info *vsdb_info) +{ + int i; + struct amdgpu_device *adev = drm_to_adev(aconnector->base.dev); + struct dc *dc = adev->dm.dc; + + /* send extension block to DMCU for parsing */ + for (i = 0; i < len; i += 8) { + bool res; + int offset; + + /* send 8 bytes a time */ + if (!dc_edid_parser_send_cea(dc, i, len, &edid_ext[i], 8)) + return false; + + if (i+8 == len) { + /* EDID block sent completed, expect result */ + int version, min_rate, max_rate; + + res = dc_edid_parser_recv_amd_vsdb(dc, &version, &min_rate, &max_rate); + if (res) { + /* amd vsdb found */ + vsdb_info->freesync_supported = 1; + vsdb_info->amd_vsdb_version = version; + vsdb_info->min_refresh_rate_hz = min_rate; + vsdb_info->max_refresh_rate_hz = max_rate; + return true; + } + /* not amd vsdb */ + return false; + } + + /* check for ack*/ + res = dc_edid_parser_recv_cea_ack(dc, &offset); + if (!res) + return false; + } + + return false; +} + +static int parse_hdmi_amd_vsdb(struct amdgpu_dm_connector *aconnector, + struct edid *edid, struct amdgpu_hdmi_vsdb_info *vsdb_info) +{ + uint8_t *edid_ext = NULL; + int i; + bool valid_vsdb_found = false; + + /*----- drm_find_cea_extension() -----*/ + /* No EDID or EDID extensions */ + if (edid == NULL || edid->extensions == 0) + return -ENODEV; + + /* Find CEA extension */ + for (i = 0; i < edid->extensions; i++) { + edid_ext = (uint8_t *)edid + EDID_LENGTH * (i + 1); + if (edid_ext[0] == CEA_EXT) + break; + } + + if (i == edid->extensions) + return -ENODEV; + + /*----- cea_db_offsets() -----*/ + if (edid_ext[0] != CEA_EXT) + return -ENODEV; + + valid_vsdb_found = parse_edid_cea(aconnector, edid_ext, EDID_LENGTH, vsdb_info); + + return valid_vsdb_found ? i : -ENODEV; +} + void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, struct edid *edid) { - int i; - bool edid_check_required; + int i = 0; struct detailed_timing *timing; struct detailed_non_pixel *data; struct detailed_data_monitor_range *range; @@ -9711,6 +10319,7 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, struct drm_device *dev = connector->dev; struct amdgpu_device *adev = drm_to_adev(dev); bool freesync_capable = false; + struct amdgpu_hdmi_vsdb_info vsdb_info = {0}; if (!connector->state) { DRM_ERROR("%s - Connector has no state", __func__); @@ -9729,60 +10338,75 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, dm_con_state = to_dm_connector_state(connector->state); - edid_check_required = false; if (!amdgpu_dm_connector->dc_sink) { DRM_ERROR("dc_sink NULL, could not add free_sync module.\n"); goto update; } if (!adev->dm.freesync_module) goto update; - /* - * if edid non zero restrict freesync only for dp and edp - */ - if (edid) { - if (amdgpu_dm_connector->dc_sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT - || amdgpu_dm_connector->dc_sink->sink_signal == SIGNAL_TYPE_EDP) { + + + if (amdgpu_dm_connector->dc_sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT + || amdgpu_dm_connector->dc_sink->sink_signal == SIGNAL_TYPE_EDP) { + bool edid_check_required = false; + + if (edid) { edid_check_required = is_dp_capable_without_timing_msa( adev->dm.dc, amdgpu_dm_connector); } - } - if (edid_check_required == true && (edid->version > 1 || - (edid->version == 1 && edid->revision > 1))) { - for (i = 0; i < 4; i++) { - timing = &edid->detailed_timings[i]; - data = &timing->data.other_data; - range = &data->data.range; - /* - * Check if monitor has continuous frequency mode - */ - if (data->type != EDID_DETAIL_MONITOR_RANGE) - continue; - /* - * Check for flag range limits only. If flag == 1 then - * no additional timing information provided. - * Default GTF, GTF Secondary curve and CVT are not - * supported - */ - if (range->flags != 1) - continue; + if (edid_check_required == true && (edid->version > 1 || + (edid->version == 1 && edid->revision > 1))) { + for (i = 0; i < 4; i++) { - amdgpu_dm_connector->min_vfreq = range->min_vfreq; - amdgpu_dm_connector->max_vfreq = range->max_vfreq; - amdgpu_dm_connector->pixel_clock_mhz = - range->pixel_clock_mhz * 10; + timing = &edid->detailed_timings[i]; + data = &timing->data.other_data; + range = &data->data.range; + /* + * Check if monitor has continuous frequency mode + */ + if (data->type != EDID_DETAIL_MONITOR_RANGE) + continue; + /* + * Check for flag range limits only. If flag == 1 then + * no additional timing information provided. + * Default GTF, GTF Secondary curve and CVT are not + * supported + */ + if (range->flags != 1) + continue; - connector->display_info.monitor_range.min_vfreq = range->min_vfreq; - connector->display_info.monitor_range.max_vfreq = range->max_vfreq; + amdgpu_dm_connector->min_vfreq = range->min_vfreq; + amdgpu_dm_connector->max_vfreq = range->max_vfreq; + amdgpu_dm_connector->pixel_clock_mhz = + range->pixel_clock_mhz * 10; - break; - } + connector->display_info.monitor_range.min_vfreq = range->min_vfreq; + connector->display_info.monitor_range.max_vfreq = range->max_vfreq; - if (amdgpu_dm_connector->max_vfreq - - amdgpu_dm_connector->min_vfreq > 10) { + break; + } + + if (amdgpu_dm_connector->max_vfreq - + amdgpu_dm_connector->min_vfreq > 10) { - freesync_capable = true; + freesync_capable = true; + } + } + } else if (edid && amdgpu_dm_connector->dc_sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A) { + i = parse_hdmi_amd_vsdb(amdgpu_dm_connector, edid, &vsdb_info); + if (i >= 0 && vsdb_info.freesync_supported) { + timing = &edid->detailed_timings[i]; + data = &timing->data.other_data; + + amdgpu_dm_connector->min_vfreq = vsdb_info.min_refresh_rate_hz; + amdgpu_dm_connector->max_vfreq = vsdb_info.max_refresh_rate_hz; + if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10) + freesync_capable = true; + + connector->display_info.monitor_range.min_vfreq = vsdb_info.min_refresh_rate_hz; + connector->display_info.monitor_range.max_vfreq = vsdb_info.max_refresh_rate_hz; } } diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h index 8bfe901cf237..018943113025 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h @@ -66,18 +66,7 @@ struct dc_plane_state; struct common_irq_params { struct amdgpu_device *adev; enum dc_irq_source irq_src; -}; - -/** - * struct irq_list_head - Linked-list for low context IRQ handlers. - * - * @head: The list_head within &struct handler_data - * @work: A work_struct containing the deferred handler work - */ -struct irq_list_head { - struct list_head head; - /* In case this interrupt needs post-processing, 'work' will be queued*/ - struct work_struct work; + atomic64_t previous_timestamp; }; /** @@ -145,6 +134,16 @@ struct amdgpu_dm_backlight_caps { }; /** + * struct dal_allocation - Tracks mapped FB memory for SMU communication + */ +struct dal_allocation { + struct list_head list; + struct amdgpu_bo *bo; + void *cpu_ptr; + u64 gpu_addr; +}; + +/** * struct amdgpu_display_manager - Central amdgpu display manager device * * @dc: Display Core control structure @@ -257,12 +256,12 @@ struct amdgpu_display_manager { */ struct mutex audio_lock; +#if defined(CONFIG_DRM_AMD_DC_DCN) /** - * @vblank_work_lock: + * @vblank_lock: * * Guards access to deferred vblank work state. */ -#if defined(CONFIG_DRM_AMD_DC_DCN) spinlock_t vblank_lock; #endif @@ -293,7 +292,7 @@ struct amdgpu_display_manager { * Note that handlers are called in the same order as they were * registered (FIFO). */ - struct irq_list_head irq_handler_list_low_tab[DAL_IRQ_SOURCES_NUMBER]; + struct list_head irq_handler_list_low_tab[DAL_IRQ_SOURCES_NUMBER]; /** * @irq_handler_list_high_tab: @@ -324,6 +323,15 @@ struct amdgpu_display_manager { vblank_params[DC_IRQ_SOURCE_VBLANK6 - DC_IRQ_SOURCE_VBLANK1 + 1]; /** + * @vline0_params: + * + * OTG vertical interrupt0 IRQ parameters, passed to registered + * handlers when triggered. + */ + struct common_irq_params + vline0_params[DC_IRQ_SOURCE_DC6_VLINE0 - DC_IRQ_SOURCE_DC1_VLINE0 + 1]; + + /** * @vupdate_params: * * Vertical update IRQ parameters, passed to registered handlers when @@ -332,6 +340,15 @@ struct amdgpu_display_manager { struct common_irq_params vupdate_params[DC_IRQ_SOURCE_VUPDATE6 - DC_IRQ_SOURCE_VUPDATE1 + 1]; + /** + * @dmub_trace_params: + * + * DMUB trace event IRQ parameters, passed to registered handlers when + * triggered. + */ + struct common_irq_params + dmub_trace_params[1]; + spinlock_t irq_handler_list_table_lock; struct backlight_device *backlight_dev; @@ -345,6 +362,11 @@ struct amdgpu_display_manager { #endif #if defined(CONFIG_DRM_AMD_DC_DCN) + /** + * @vblank_workqueue: + * + * amdgpu workqueue during vblank + */ struct vblank_workqueue *vblank_workqueue; #endif @@ -363,12 +385,23 @@ struct amdgpu_display_manager { */ const struct gpu_info_soc_bounding_box_v1_0 *soc_bounding_box; +#if defined(CONFIG_DRM_AMD_DC_DCN) /** * @active_vblank_irq_count: * * number of currently active vblank irqs */ uint32_t active_vblank_irq_count; +#endif + +#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY) + /** + * @crc_rd_wrk: + * + * Work to be executed in a separate thread to communicate with PSP. + */ + struct crc_rd_work *crc_rd_wrk; +#endif /** * @mst_encoders: @@ -377,6 +410,13 @@ struct amdgpu_display_manager { */ struct amdgpu_encoder mst_encoders[AMDGPU_DM_MAX_CRTC]; bool force_timing_sync; + bool dmcub_trace_event_en; + /** + * @da_list: + * + * DAL fb memory allocation list, for communication with SMU. + */ + struct list_head da_list; }; enum dsc_clock_force_state { @@ -440,6 +480,8 @@ struct amdgpu_dm_connector { #endif bool force_yuv420_output; struct dsc_preferred_settings dsc_settings; + /* Cached display modes */ + struct drm_display_mode freesync_vid_base; }; #define to_amdgpu_dm_connector(x) container_of(x, struct amdgpu_dm_connector, base) @@ -462,7 +504,6 @@ struct dm_crtc_state { int active_planes; int crc_skip_count; - enum amdgpu_dm_pipe_crc_source crc_src; bool freesync_timing_changed; bool freesync_vrr_info_changed; @@ -501,6 +542,14 @@ struct dm_connector_state { uint64_t pbn; }; +struct amdgpu_hdmi_vsdb_info { + unsigned int amd_vsdb_version; /* VSDB version, should be used to determine which VSIF to send */ + bool freesync_supported; /* FreeSync Supported */ + unsigned int min_refresh_rate_hz; /* FreeSync Minimum Refresh Rate in Hz */ + unsigned int max_refresh_rate_hz; /* FreeSync Maximum Refresh Rate in Hz */ +}; + + #define to_dm_connector_state(x)\ container_of((x), struct dm_connector_state, base) 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 66cb8730586b..5cd788b20c21 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 @@ -29,6 +29,7 @@ #include "amdgpu.h" #include "amdgpu_dm.h" #include "dc.h" +#include "amdgpu_securedisplay.h" static const char *const pipe_crc_sources[] = { "none", @@ -81,6 +82,73 @@ const char *const *amdgpu_dm_crtc_get_crc_sources(struct drm_crtc *crtc, return pipe_crc_sources; } +#ifdef CONFIG_DRM_AMD_SECURE_DISPLAY +static void amdgpu_dm_set_crc_window_default(struct drm_crtc *crtc) +{ + 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 = 0; + acrtc->dm_irq_params.crc_window.y_start = 0; + acrtc->dm_irq_params.crc_window.x_end = 0; + acrtc->dm_irq_params.crc_window.y_end = 0; + acrtc->dm_irq_params.crc_window.activated = false; + acrtc->dm_irq_params.crc_window.update_win = false; + acrtc->dm_irq_params.crc_window.skip_frame_cnt = 0; + spin_unlock_irq(&drm_dev->event_lock); +} + +static void amdgpu_dm_crtc_notify_ta_to_read(struct work_struct *work) +{ + struct crc_rd_work *crc_rd_wrk; + struct amdgpu_device *adev; + struct psp_context *psp; + struct securedisplay_cmd *securedisplay_cmd; + struct drm_crtc *crtc; + uint8_t phy_id; + int ret; + + crc_rd_wrk = container_of(work, struct crc_rd_work, notify_ta_work); + spin_lock_irq(&crc_rd_wrk->crc_rd_work_lock); + crtc = crc_rd_wrk->crtc; + + if (!crtc) { + spin_unlock_irq(&crc_rd_wrk->crc_rd_work_lock); + return; + } + + adev = drm_to_adev(crtc->dev); + psp = &adev->psp; + phy_id = crc_rd_wrk->phy_inst; + spin_unlock_irq(&crc_rd_wrk->crc_rd_work_lock); + + psp_prep_securedisplay_cmd_buf(psp, &securedisplay_cmd, + TA_SECUREDISPLAY_COMMAND__SEND_ROI_CRC); + securedisplay_cmd->securedisplay_in_message.send_roi_crc.phy_id = + phy_id; + ret = psp_securedisplay_invoke(psp, TA_SECUREDISPLAY_COMMAND__SEND_ROI_CRC); + if (!ret) { + if (securedisplay_cmd->status != TA_SECUREDISPLAY_STATUS__SUCCESS) { + psp_securedisplay_parse_resp_status(psp, securedisplay_cmd->status); + } + } +} + +bool amdgpu_dm_crc_window_is_activated(struct drm_crtc *crtc) +{ + struct drm_device *drm_dev = crtc->dev; + struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc); + bool ret = false; + + spin_lock_irq(&drm_dev->event_lock); + ret = acrtc->dm_irq_params.crc_window.activated; + spin_unlock_irq(&drm_dev->event_lock); + + return ret; +} +#endif + int amdgpu_dm_crtc_verify_crc_source(struct drm_crtc *crtc, const char *src_name, size_t *values_cnt) @@ -114,6 +182,20 @@ int amdgpu_dm_crtc_configure_crc_source(struct drm_crtc *crtc, /* Enable CRTC CRC generation if necessary. */ if (dm_is_crc_source_crtc(source) || source == AMDGPU_DM_PIPE_CRC_SOURCE_NONE) { +#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY) + if (!enable) { + if (adev->dm.crc_rd_wrk) { + flush_work(&adev->dm.crc_rd_wrk->notify_ta_work); + spin_lock_irq(&adev->dm.crc_rd_wrk->crc_rd_work_lock); + if (adev->dm.crc_rd_wrk->crtc == crtc) { + dc_stream_stop_dmcu_crc_win_update(stream_state->ctx->dc, + dm_crtc_state->stream); + adev->dm.crc_rd_wrk->crtc = NULL; + } + spin_unlock_irq(&adev->dm.crc_rd_wrk->crc_rd_work_lock); + } + } +#endif if (!dc_stream_configure_crc(stream_state->ctx->dc, stream_state, NULL, enable, enable)) { ret = -EINVAL; @@ -142,8 +224,11 @@ unlock: int amdgpu_dm_crtc_set_crc_source(struct drm_crtc *crtc, const char *src_name) { enum amdgpu_dm_pipe_crc_source source = dm_parse_crc_source(src_name); + enum amdgpu_dm_pipe_crc_source cur_crc_src; struct drm_crtc_commit *commit; struct dm_crtc_state *crtc_state; + struct drm_device *drm_dev = crtc->dev; + struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc); struct drm_dp_aux *aux = NULL; bool enable = false; bool enabled = false; @@ -182,6 +267,9 @@ int amdgpu_dm_crtc_set_crc_source(struct drm_crtc *crtc, const char *src_name) enable = amdgpu_dm_is_valid_crc_source(source); crtc_state = to_dm_crtc_state(crtc->state); + spin_lock_irq(&drm_dev->event_lock); + cur_crc_src = acrtc->dm_irq_params.crc_src; + spin_unlock_irq(&drm_dev->event_lock); /* * USER REQ SRC | CURRENT SRC | BEHAVIOR @@ -198,7 +286,7 @@ int amdgpu_dm_crtc_set_crc_source(struct drm_crtc *crtc, const char *src_name) */ if (dm_is_crc_source_dprx(source) || (source == AMDGPU_DM_PIPE_CRC_SOURCE_NONE && - dm_is_crc_source_dprx(crtc_state->crc_src))) { + dm_is_crc_source_dprx(cur_crc_src))) { struct amdgpu_dm_connector *aconn = NULL; struct drm_connector *connector; struct drm_connector_list_iter conn_iter; @@ -219,7 +307,7 @@ int amdgpu_dm_crtc_set_crc_source(struct drm_crtc *crtc, const char *src_name) goto cleanup; } - aux = &aconn->dm_dp_aux.aux; + aux = (aconn->port) ? &aconn->port->aux : &aconn->dm_dp_aux.aux; if (!aux) { DRM_DEBUG_DRIVER("No dp aux for amd connector\n"); @@ -228,6 +316,10 @@ int amdgpu_dm_crtc_set_crc_source(struct drm_crtc *crtc, const char *src_name) } } +#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY) + amdgpu_dm_set_crc_window_default(crtc); +#endif + if (amdgpu_dm_crtc_configure_crc_source(crtc, crtc_state, source)) { ret = -EINVAL; goto cleanup; @@ -237,7 +329,7 @@ int amdgpu_dm_crtc_set_crc_source(struct drm_crtc *crtc, const char *src_name) * Reading the CRC requires the vblank interrupt handler to be * enabled. Keep a reference until CRC capture stops. */ - enabled = amdgpu_dm_is_valid_crc_source(crtc_state->crc_src); + enabled = amdgpu_dm_is_valid_crc_source(cur_crc_src); if (!enabled && enable) { ret = drm_crtc_vblank_get(crtc); if (ret) @@ -261,7 +353,9 @@ int amdgpu_dm_crtc_set_crc_source(struct drm_crtc *crtc, const char *src_name) } } - crtc_state->crc_src = source; + spin_lock_irq(&drm_dev->event_lock); + acrtc->dm_irq_params.crc_src = source; + spin_unlock_irq(&drm_dev->event_lock); /* Reset crc_skipped on dm state */ crtc_state->crc_skip_count = 0; @@ -286,16 +380,26 @@ void amdgpu_dm_crtc_handle_crc_irq(struct drm_crtc *crtc) { struct dm_crtc_state *crtc_state; struct dc_stream_state *stream_state; + struct drm_device *drm_dev = NULL; + enum amdgpu_dm_pipe_crc_source cur_crc_src; + struct amdgpu_crtc *acrtc = NULL; uint32_t crcs[3]; + unsigned long flags; if (crtc == NULL) return; crtc_state = to_dm_crtc_state(crtc->state); stream_state = crtc_state->stream; + acrtc = to_amdgpu_crtc(crtc); + drm_dev = crtc->dev; + + spin_lock_irqsave(&drm_dev->event_lock, flags); + cur_crc_src = acrtc->dm_irq_params.crc_src; + spin_unlock_irqrestore(&drm_dev->event_lock, flags); /* Early return if CRC capture is not enabled. */ - if (!amdgpu_dm_is_valid_crc_source(crtc_state->crc_src)) + if (!amdgpu_dm_is_valid_crc_source(cur_crc_src)) return; /* @@ -309,7 +413,7 @@ void amdgpu_dm_crtc_handle_crc_irq(struct drm_crtc *crtc) return; } - if (dm_is_crc_source_crtc(crtc_state->crc_src)) { + if (dm_is_crc_source_crtc(cur_crc_src)) { if (!dc_stream_get_crc(stream_state->ctx->dc, stream_state, &crcs[0], &crcs[1], &crcs[2])) return; @@ -318,3 +422,182 @@ void amdgpu_dm_crtc_handle_crc_irq(struct drm_crtc *crtc) drm_crtc_accurate_vblank_count(crtc), crcs); } } + +#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY) +void amdgpu_dm_crtc_handle_crc_window_irq(struct drm_crtc *crtc) +{ + struct dc_stream_state *stream_state; + struct drm_device *drm_dev = NULL; + enum amdgpu_dm_pipe_crc_source cur_crc_src; + struct amdgpu_crtc *acrtc = NULL; + struct amdgpu_device *adev = NULL; + struct crc_rd_work *crc_rd_wrk = NULL; + struct crc_params *crc_window = NULL, tmp_window; + unsigned long flags1, flags2; + struct crtc_position position; + uint32_t v_blank; + uint32_t v_back_porch; + uint32_t crc_window_latch_up_line; + struct dc_crtc_timing *timing_out; + + if (crtc == NULL) + return; + + acrtc = to_amdgpu_crtc(crtc); + adev = drm_to_adev(crtc->dev); + drm_dev = crtc->dev; + + spin_lock_irqsave(&drm_dev->event_lock, flags1); + stream_state = acrtc->dm_irq_params.stream; + cur_crc_src = acrtc->dm_irq_params.crc_src; + timing_out = &stream_state->timing; + + /* Early return if CRC capture is not enabled. */ + if (!amdgpu_dm_is_valid_crc_source(cur_crc_src)) + goto cleanup; + + if (dm_is_crc_source_crtc(cur_crc_src)) { + if (acrtc->dm_irq_params.crc_window.activated) { + if (acrtc->dm_irq_params.crc_window.update_win) { + if (acrtc->dm_irq_params.crc_window.skip_frame_cnt) { + acrtc->dm_irq_params.crc_window.skip_frame_cnt -= 1; + goto cleanup; + } + crc_window = &tmp_window; + + tmp_window.windowa_x_start = + acrtc->dm_irq_params.crc_window.x_start; + tmp_window.windowa_y_start = + acrtc->dm_irq_params.crc_window.y_start; + tmp_window.windowa_x_end = + acrtc->dm_irq_params.crc_window.x_end; + tmp_window.windowa_y_end = + acrtc->dm_irq_params.crc_window.y_end; + tmp_window.windowb_x_start = + acrtc->dm_irq_params.crc_window.x_start; + tmp_window.windowb_y_start = + acrtc->dm_irq_params.crc_window.y_start; + tmp_window.windowb_x_end = + acrtc->dm_irq_params.crc_window.x_end; + tmp_window.windowb_y_end = + acrtc->dm_irq_params.crc_window.y_end; + + dc_stream_forward_dmcu_crc_window(stream_state->ctx->dc, + stream_state, crc_window); + + acrtc->dm_irq_params.crc_window.update_win = false; + + dc_stream_get_crtc_position(stream_state->ctx->dc, &stream_state, 1, + &position.vertical_count, + &position.nominal_vcount); + + v_blank = timing_out->v_total - timing_out->v_border_top - + timing_out->v_addressable - timing_out->v_border_bottom; + + v_back_porch = v_blank - timing_out->v_front_porch - + timing_out->v_sync_width; + + crc_window_latch_up_line = v_back_porch + timing_out->v_sync_width; + + /* take 3 lines margin*/ + if ((position.vertical_count + 3) >= crc_window_latch_up_line) + acrtc->dm_irq_params.crc_window.skip_frame_cnt = 1; + else + acrtc->dm_irq_params.crc_window.skip_frame_cnt = 0; + } else { + if (acrtc->dm_irq_params.crc_window.skip_frame_cnt == 0) { + if (adev->dm.crc_rd_wrk) { + crc_rd_wrk = adev->dm.crc_rd_wrk; + spin_lock_irqsave(&crc_rd_wrk->crc_rd_work_lock, flags2); + crc_rd_wrk->phy_inst = + stream_state->link->link_enc_hw_inst; + spin_unlock_irqrestore(&crc_rd_wrk->crc_rd_work_lock, flags2); + schedule_work(&crc_rd_wrk->notify_ta_work); + } + } else { + acrtc->dm_irq_params.crc_window.skip_frame_cnt -= 1; + } + } + } + } + +cleanup: + spin_unlock_irqrestore(&drm_dev->event_lock, flags1); +} + +void amdgpu_dm_crtc_secure_display_resume(struct amdgpu_device *adev) +{ + struct drm_crtc *crtc; + enum amdgpu_dm_pipe_crc_source cur_crc_src; + struct crc_rd_work *crc_rd_wrk = adev->dm.crc_rd_wrk; + struct crc_window_parm cur_crc_window; + struct amdgpu_crtc *acrtc = NULL; + + drm_for_each_crtc(crtc, &adev->ddev) { + acrtc = to_amdgpu_crtc(crtc); + + spin_lock_irq(&adev_to_drm(adev)->event_lock); + cur_crc_src = acrtc->dm_irq_params.crc_src; + cur_crc_window = acrtc->dm_irq_params.crc_window; + spin_unlock_irq(&adev_to_drm(adev)->event_lock); + + if (amdgpu_dm_is_valid_crc_source(cur_crc_src)) { + amdgpu_dm_crtc_set_crc_source(crtc, + pipe_crc_sources[cur_crc_src]); + spin_lock_irq(&adev_to_drm(adev)->event_lock); + acrtc->dm_irq_params.crc_window = cur_crc_window; + if (acrtc->dm_irq_params.crc_window.activated) { + acrtc->dm_irq_params.crc_window.update_win = true; + acrtc->dm_irq_params.crc_window.skip_frame_cnt = 1; + spin_lock_irq(&crc_rd_wrk->crc_rd_work_lock); + crc_rd_wrk->crtc = crtc; + spin_unlock_irq(&crc_rd_wrk->crc_rd_work_lock); + } + spin_unlock_irq(&adev_to_drm(adev)->event_lock); + } + } +} + +void amdgpu_dm_crtc_secure_display_suspend(struct amdgpu_device *adev) +{ + struct drm_crtc *crtc; + struct crc_window_parm cur_crc_window; + enum amdgpu_dm_pipe_crc_source cur_crc_src; + struct amdgpu_crtc *acrtc = NULL; + + drm_for_each_crtc(crtc, &adev->ddev) { + acrtc = to_amdgpu_crtc(crtc); + + spin_lock_irq(&adev_to_drm(adev)->event_lock); + cur_crc_src = acrtc->dm_irq_params.crc_src; + cur_crc_window = acrtc->dm_irq_params.crc_window; + cur_crc_window.update_win = false; + spin_unlock_irq(&adev_to_drm(adev)->event_lock); + + if (amdgpu_dm_is_valid_crc_source(cur_crc_src)) { + amdgpu_dm_crtc_set_crc_source(crtc, NULL); + spin_lock_irq(&adev_to_drm(adev)->event_lock); + /* For resume to set back crc source*/ + acrtc->dm_irq_params.crc_src = cur_crc_src; + acrtc->dm_irq_params.crc_window = cur_crc_window; + spin_unlock_irq(&adev_to_drm(adev)->event_lock); + } + } + +} + +struct crc_rd_work *amdgpu_dm_crtc_secure_display_create_work(void) +{ + struct crc_rd_work *crc_rd_wrk = NULL; + + crc_rd_wrk = kzalloc(sizeof(*crc_rd_wrk), GFP_KERNEL); + + if (!crc_rd_wrk) + return NULL; + + spin_lock_init(&crc_rd_wrk->crc_rd_work_lock); + INIT_WORK(&crc_rd_wrk->notify_ta_work, amdgpu_dm_crtc_notify_ta_to_read); + + return crc_rd_wrk; +} +#endif diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.h index f7d731797d3f..737e701fb0f0 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.h @@ -39,6 +39,29 @@ enum amdgpu_dm_pipe_crc_source { AMDGPU_DM_PIPE_CRC_SOURCE_INVALID = -1, }; +#ifdef CONFIG_DRM_AMD_SECURE_DISPLAY +struct crc_window_parm { + uint16_t x_start; + uint16_t y_start; + uint16_t x_end; + uint16_t y_end; + /* CRC windwo is activated or not*/ + bool activated; + /* Update crc window during vertical blank or not */ + bool update_win; + /* skip reading/writing for few frames */ + int skip_frame_cnt; +}; + +struct crc_rd_work { + struct work_struct notify_ta_work; + /* To protect crc_rd_work carried fields*/ + spinlock_t crc_rd_work_lock; + struct drm_crtc *crtc; + uint8_t phy_inst; +}; +#endif + static inline bool amdgpu_dm_is_valid_crc_source(enum amdgpu_dm_pipe_crc_source source) { return (source > AMDGPU_DM_PIPE_CRC_SOURCE_NONE) && @@ -64,4 +87,18 @@ void amdgpu_dm_crtc_handle_crc_irq(struct drm_crtc *crtc); #define amdgpu_dm_crtc_handle_crc_irq(x) #endif +#ifdef CONFIG_DRM_AMD_SECURE_DISPLAY +bool amdgpu_dm_crc_window_is_activated(struct drm_crtc *crtc); +void amdgpu_dm_crtc_handle_crc_window_irq(struct drm_crtc *crtc); +struct crc_rd_work *amdgpu_dm_crtc_secure_display_create_work(void); +void amdgpu_dm_crtc_secure_display_resume(struct amdgpu_device *adev); +void amdgpu_dm_crtc_secure_display_suspend(struct amdgpu_device *adev); +#else +#define amdgpu_dm_crc_window_is_activated(x) +#define amdgpu_dm_crtc_handle_crc_window_irq(x) +#define amdgpu_dm_crtc_secure_display_create_work() +#define amdgpu_dm_crtc_secure_display_resume(x) +#define amdgpu_dm_crtc_secure_display_suspend(x) +#endif + #endif /* AMD_DAL_DEV_AMDGPU_DM_AMDGPU_DM_CRC_H_ */ 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); } 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 index 5e5b2b2afa31..3366cb644053 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.h @@ -30,6 +30,9 @@ #include "amdgpu_dm.h" void connector_debugfs_init(struct amdgpu_dm_connector *connector); -int dtn_debugfs_init(struct amdgpu_device *adev); +void dtn_debugfs_init(struct amdgpu_device *adev); +#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY) +void crtc_debugfs_init(struct drm_crtc *crtc); +#endif #endif diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c index 0cdbfcd475ec..60f91853bd82 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c @@ -191,7 +191,7 @@ void hdcp_update_display(struct hdcp_workqueue *hdcp_work, psp_set_srm(hdcp_work->hdcp.config.psp.handle, hdcp_work->srm, hdcp_work->srm_size, &hdcp_work->srm_version); - display->adjust.disable = 0; + display->adjust.disable = MOD_HDCP_DISPLAY_NOT_DISABLE; if (content_type == DRM_MODE_HDCP_CONTENT_TYPE0) { hdcp_w->link.adjust.hdcp1.disable = 0; hdcp_w->link.adjust.hdcp2.force_type = MOD_HDCP_FORCE_TYPE_0; @@ -203,7 +203,7 @@ void hdcp_update_display(struct hdcp_workqueue *hdcp_work, schedule_delayed_work(&hdcp_w->property_validate_dwork, msecs_to_jiffies(DRM_HDCP_CHECK_PERIOD_MS)); } else { - display->adjust.disable = 1; + display->adjust.disable = MOD_HDCP_DISPLAY_DISABLE_AUTHENTICATION; hdcp_w->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF; cancel_delayed_work(&hdcp_w->property_validate_dwork); } @@ -456,7 +456,7 @@ static void update_config(void *handle, struct cp_psp_stream_config *config) link->dp.rev = aconnector->dc_link->dpcd_caps.dpcd_rev.raw; link->dp.assr_enabled = config->assr_enabled; link->dp.mst_enabled = config->mst_enabled; - display->adjust.disable = 1; + display->adjust.disable = MOD_HDCP_DISPLAY_DISABLE_AUTHENTICATION; link->adjust.auth_delay = 3; link->adjust.hdcp1.disable = 0; 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 5750818db8f6..103e29905b57 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 @@ -652,8 +652,31 @@ void *dm_helpers_allocate_gpu_mem( size_t size, long long *addr) { - // TODO - return NULL; + struct amdgpu_device *adev = ctx->driver_context; + struct dal_allocation *da; + u32 domain = (type == DC_MEM_ALLOC_TYPE_GART) ? + AMDGPU_GEM_DOMAIN_GTT : AMDGPU_GEM_DOMAIN_VRAM; + int ret; + + da = kzalloc(sizeof(struct dal_allocation), GFP_KERNEL); + if (!da) + return NULL; + + ret = amdgpu_bo_create_kernel(adev, size, PAGE_SIZE, + domain, &da->bo, + &da->gpu_addr, &da->cpu_ptr); + + *addr = da->gpu_addr; + + if (ret) { + kfree(da); + return NULL; + } + + /* add da to list in dm */ + list_add(&da->list, &adev->dm.da_list); + + return da->cpu_ptr; } void dm_helpers_free_gpu_mem( @@ -661,5 +684,30 @@ void dm_helpers_free_gpu_mem( enum dc_gpu_mem_alloc_type type, void *pvMem) { - // TODO + struct amdgpu_device *adev = ctx->driver_context; + struct dal_allocation *da; + + /* walk the da list in DM */ + list_for_each_entry(da, &adev->dm.da_list, list) { + if (pvMem == da->cpu_ptr) { + amdgpu_bo_free_kernel(&da->bo, &da->gpu_addr, &da->cpu_ptr); + list_del(&da->list); + kfree(da); + break; + } + } +} + +bool dm_helpers_dmub_outbox0_interrupt_control(struct dc_context *ctx, bool enable) +{ + enum dc_irq_source irq_source; + bool ret; + + irq_source = DC_IRQ_SOURCE_DMCUB_OUTBOX0; + + ret = dc_interrupt_set(ctx->dc, irq_source, enable); + + DRM_DEBUG_DRIVER("Dmub trace irq %sabling: r=%d\n", + enable ? "en" : "dis", ret); + return ret; } diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c index e0000c180ed1..b3ed7e777720 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c @@ -73,6 +73,7 @@ * @handler_arg: Argument passed to the handler when triggered * @dm: DM which this handler belongs to * @irq_source: DC interrupt source that this handler is registered for + * @work: work struct */ struct amdgpu_dm_irq_handler_data { struct list_head list; @@ -82,6 +83,7 @@ struct amdgpu_dm_irq_handler_data { struct amdgpu_display_manager *dm; /* DAL irq source which registered for this interrupt. */ enum dc_irq_source irq_source; + struct work_struct work; }; #define DM_IRQ_TABLE_LOCK(adev, flags) \ @@ -111,20 +113,10 @@ static void init_handler_common_data(struct amdgpu_dm_irq_handler_data *hcd, */ static void dm_irq_work_func(struct work_struct *work) { - struct irq_list_head *irq_list_head = - container_of(work, struct irq_list_head, work); - struct list_head *handler_list = &irq_list_head->head; - struct amdgpu_dm_irq_handler_data *handler_data; - - list_for_each_entry(handler_data, handler_list, list) { - DRM_DEBUG_KMS("DM_IRQ: work_func: for dal_src=%d\n", - handler_data->irq_source); + struct amdgpu_dm_irq_handler_data *handler_data = + container_of(work, struct amdgpu_dm_irq_handler_data, work); - DRM_DEBUG_KMS("DM_IRQ: schedule_work: for dal_src=%d\n", - handler_data->irq_source); - - handler_data->handler(handler_data->handler_arg); - } + handler_data->handler(handler_data->handler_arg); /* Call a DAL subcomponent which registered for interrupt notification * at INTERRUPT_LOW_IRQ_CONTEXT. @@ -156,7 +148,7 @@ static struct list_head *remove_irq_handler(struct amdgpu_device *adev, break; case INTERRUPT_LOW_IRQ_CONTEXT: default: - hnd_list = &adev->dm.irq_handler_list_low_tab[irq_source].head; + hnd_list = &adev->dm.irq_handler_list_low_tab[irq_source]; break; } @@ -193,6 +185,55 @@ static struct list_head *remove_irq_handler(struct amdgpu_device *adev, return hnd_list; } +/** + * unregister_all_irq_handlers() - Cleans up handlers from the DM IRQ table + * @adev: The base driver device containing the DM device + * + * Go through low and high context IRQ tables and deallocate handlers. + */ +static void unregister_all_irq_handlers(struct amdgpu_device *adev) +{ + struct list_head *hnd_list_low; + struct list_head *hnd_list_high; + struct list_head *entry, *tmp; + struct amdgpu_dm_irq_handler_data *handler; + unsigned long irq_table_flags; + int i; + + DM_IRQ_TABLE_LOCK(adev, irq_table_flags); + + for (i = 0; i < DAL_IRQ_SOURCES_NUMBER; i++) { + hnd_list_low = &adev->dm.irq_handler_list_low_tab[i]; + hnd_list_high = &adev->dm.irq_handler_list_high_tab[i]; + + list_for_each_safe(entry, tmp, hnd_list_low) { + + handler = list_entry(entry, struct amdgpu_dm_irq_handler_data, + list); + + if (handler == NULL || handler->handler == NULL) + continue; + + list_del(&handler->list); + kfree(handler); + } + + list_for_each_safe(entry, tmp, hnd_list_high) { + + handler = list_entry(entry, struct amdgpu_dm_irq_handler_data, + list); + + if (handler == NULL || handler->handler == NULL) + continue; + + list_del(&handler->list); + kfree(handler); + } + } + + DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags); +} + static bool validate_irq_registration_params(struct dc_interrupt_params *int_params, void (*ih)(void *)) @@ -290,7 +331,8 @@ void *amdgpu_dm_irq_register_interrupt(struct amdgpu_device *adev, break; case INTERRUPT_LOW_IRQ_CONTEXT: default: - hnd_list = &adev->dm.irq_handler_list_low_tab[irq_source].head; + hnd_list = &adev->dm.irq_handler_list_low_tab[irq_source]; + INIT_WORK(&handler_data->work, dm_irq_work_func); break; } @@ -372,7 +414,7 @@ void amdgpu_dm_irq_unregister_interrupt(struct amdgpu_device *adev, int amdgpu_dm_irq_init(struct amdgpu_device *adev) { int src; - struct irq_list_head *lh; + struct list_head *lh; DRM_DEBUG_KMS("DM_IRQ\n"); @@ -381,9 +423,7 @@ int amdgpu_dm_irq_init(struct amdgpu_device *adev) for (src = 0; src < DAL_IRQ_SOURCES_NUMBER; src++) { /* low context handler list init */ lh = &adev->dm.irq_handler_list_low_tab[src]; - INIT_LIST_HEAD(&lh->head); - INIT_WORK(&lh->work, dm_irq_work_func); - + INIT_LIST_HEAD(lh); /* high context handler init */ INIT_LIST_HEAD(&adev->dm.irq_handler_list_high_tab[src]); } @@ -400,8 +440,11 @@ int amdgpu_dm_irq_init(struct amdgpu_device *adev) void amdgpu_dm_irq_fini(struct amdgpu_device *adev) { int src; - struct irq_list_head *lh; + struct list_head *lh; + struct list_head *entry, *tmp; + struct amdgpu_dm_irq_handler_data *handler; unsigned long irq_table_flags; + DRM_DEBUG_KMS("DM_IRQ: releasing resources.\n"); for (src = 0; src < DAL_IRQ_SOURCES_NUMBER; src++) { DM_IRQ_TABLE_LOCK(adev, irq_table_flags); @@ -410,8 +453,19 @@ void amdgpu_dm_irq_fini(struct amdgpu_device *adev) * (because no code can schedule a new one). */ lh = &adev->dm.irq_handler_list_low_tab[src]; DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags); - flush_work(&lh->work); + + if (!list_empty(lh)) { + list_for_each_safe(entry, tmp, lh) { + handler = list_entry( + entry, + struct amdgpu_dm_irq_handler_data, + list); + flush_work(&handler->work); + } + } } + /* Deallocate handlers from the table. */ + unregister_all_irq_handlers(adev); } int amdgpu_dm_irq_suspend(struct amdgpu_device *adev) @@ -420,6 +474,8 @@ int amdgpu_dm_irq_suspend(struct amdgpu_device *adev) struct list_head *hnd_list_h; struct list_head *hnd_list_l; unsigned long irq_table_flags; + struct list_head *entry, *tmp; + struct amdgpu_dm_irq_handler_data *handler; DM_IRQ_TABLE_LOCK(adev, irq_table_flags); @@ -430,14 +486,22 @@ int amdgpu_dm_irq_suspend(struct amdgpu_device *adev) * will be disabled from manage_dm_interrupts on disable CRTC. */ for (src = DC_IRQ_SOURCE_HPD1; src <= DC_IRQ_SOURCE_HPD6RX; src++) { - hnd_list_l = &adev->dm.irq_handler_list_low_tab[src].head; + hnd_list_l = &adev->dm.irq_handler_list_low_tab[src]; hnd_list_h = &adev->dm.irq_handler_list_high_tab[src]; if (!list_empty(hnd_list_l) || !list_empty(hnd_list_h)) dc_interrupt_set(adev->dm.dc, src, false); DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags); - flush_work(&adev->dm.irq_handler_list_low_tab[src].work); + if (!list_empty(hnd_list_l)) { + list_for_each_safe (entry, tmp, hnd_list_l) { + handler = list_entry( + entry, + struct amdgpu_dm_irq_handler_data, + list); + flush_work(&handler->work); + } + } DM_IRQ_TABLE_LOCK(adev, irq_table_flags); } @@ -457,7 +521,7 @@ int amdgpu_dm_irq_resume_early(struct amdgpu_device *adev) /* re-enable short pulse interrupts HW interrupt */ for (src = DC_IRQ_SOURCE_HPD1RX; src <= DC_IRQ_SOURCE_HPD6RX; src++) { - hnd_list_l = &adev->dm.irq_handler_list_low_tab[src].head; + hnd_list_l = &adev->dm.irq_handler_list_low_tab[src]; hnd_list_h = &adev->dm.irq_handler_list_high_tab[src]; if (!list_empty(hnd_list_l) || !list_empty(hnd_list_h)) dc_interrupt_set(adev->dm.dc, src, true); @@ -483,7 +547,7 @@ int amdgpu_dm_irq_resume_late(struct amdgpu_device *adev) * will be enabled from manage_dm_interrupts on enable CRTC. */ for (src = DC_IRQ_SOURCE_HPD1; src <= DC_IRQ_SOURCE_HPD6; src++) { - hnd_list_l = &adev->dm.irq_handler_list_low_tab[src].head; + hnd_list_l = &adev->dm.irq_handler_list_low_tab[src]; hnd_list_h = &adev->dm.irq_handler_list_high_tab[src]; if (!list_empty(hnd_list_l) || !list_empty(hnd_list_h)) dc_interrupt_set(adev->dm.dc, src, true); @@ -500,22 +564,51 @@ int amdgpu_dm_irq_resume_late(struct amdgpu_device *adev) static void amdgpu_dm_irq_schedule_work(struct amdgpu_device *adev, enum dc_irq_source irq_source) { - unsigned long irq_table_flags; - struct work_struct *work = NULL; + struct list_head *handler_list = &adev->dm.irq_handler_list_low_tab[irq_source]; + struct amdgpu_dm_irq_handler_data *handler_data; + bool work_queued = false; - DM_IRQ_TABLE_LOCK(adev, irq_table_flags); + if (list_empty(handler_list)) + return; - if (!list_empty(&adev->dm.irq_handler_list_low_tab[irq_source].head)) - work = &adev->dm.irq_handler_list_low_tab[irq_source].work; + list_for_each_entry (handler_data, handler_list, list) { + if (queue_work(system_highpri_wq, &handler_data->work)) { + work_queued = true; + break; + } + } - DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags); + if (!work_queued) { + struct amdgpu_dm_irq_handler_data *handler_data_add; + /*get the amdgpu_dm_irq_handler_data of first item pointed by handler_list*/ + handler_data = container_of(handler_list->next, struct amdgpu_dm_irq_handler_data, list); - if (work) { - if (!schedule_work(work)) - DRM_INFO("amdgpu_dm_irq_schedule_work FAILED src %d\n", - irq_source); - } + /*allocate a new amdgpu_dm_irq_handler_data*/ + handler_data_add = kzalloc(sizeof(*handler_data), GFP_KERNEL); + if (!handler_data_add) { + DRM_ERROR("DM_IRQ: failed to allocate irq handler!\n"); + return; + } + /*copy new amdgpu_dm_irq_handler_data members from handler_data*/ + handler_data_add->handler = handler_data->handler; + handler_data_add->handler_arg = handler_data->handler_arg; + handler_data_add->dm = handler_data->dm; + handler_data_add->irq_source = irq_source; + + list_add_tail(&handler_data_add->list, handler_list); + + INIT_WORK(&handler_data_add->work, dm_irq_work_func); + + if (queue_work(system_highpri_wq, &handler_data_add->work)) + DRM_DEBUG("Queued work for handling interrupt from " + "display for IRQ source %d\n", + irq_source); + else + DRM_ERROR("Failed to queue work for handling interrupt " + "from display for IRQ source %d\n", + irq_source); + } } /* @@ -690,6 +783,18 @@ static int amdgpu_dm_set_vupdate_irq_state(struct amdgpu_device *adev, __func__); } +static int amdgpu_dm_set_dmub_trace_irq_state(struct amdgpu_device *adev, + struct amdgpu_irq_src *source, + unsigned int type, + enum amdgpu_interrupt_state state) +{ + enum dc_irq_source irq_source = DC_IRQ_SOURCE_DMCUB_OUTBOX0; + bool st = (state == AMDGPU_IRQ_STATE_ENABLE); + + dc_interrupt_set(adev->dm.dc, irq_source, st); + return 0; +} + static const struct amdgpu_irq_src_funcs dm_crtc_irq_funcs = { .set = amdgpu_dm_set_crtc_irq_state, .process = amdgpu_dm_irq_handler, @@ -705,6 +810,11 @@ static const struct amdgpu_irq_src_funcs dm_vupdate_irq_funcs = { .process = amdgpu_dm_irq_handler, }; +static const struct amdgpu_irq_src_funcs dm_dmub_trace_irq_funcs = { + .set = amdgpu_dm_set_dmub_trace_irq_state, + .process = amdgpu_dm_irq_handler, +}; + static const struct amdgpu_irq_src_funcs dm_pageflip_irq_funcs = { .set = amdgpu_dm_set_pflip_irq_state, .process = amdgpu_dm_irq_handler, @@ -727,6 +837,9 @@ void amdgpu_dm_set_irq_funcs(struct amdgpu_device *adev) adev->vupdate_irq.num_types = adev->mode_info.num_crtc; adev->vupdate_irq.funcs = &dm_vupdate_irq_funcs; + adev->dmub_trace_irq.num_types = 1; + adev->dmub_trace_irq.funcs = &dm_dmub_trace_irq_funcs; + adev->pageflip_irq.num_types = adev->mode_info.num_crtc; adev->pageflip_irq.funcs = &dm_pageflip_irq_funcs; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq_params.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq_params.h index 45825a34f8eb..f3b93ba69a27 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq_params.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq_params.h @@ -26,12 +26,21 @@ #ifndef __AMDGPU_DM_IRQ_PARAMS_H__ #define __AMDGPU_DM_IRQ_PARAMS_H__ +#include "amdgpu_dm_crc.h" + struct dm_irq_params { u32 last_flip_vblank; struct mod_vrr_params vrr_params; struct dc_stream_state *stream; int active_planes; struct mod_freesync_config freesync_config; + +#ifdef CONFIG_DEBUG_FS + enum amdgpu_dm_pipe_crc_source crc_src; +#ifdef CONFIG_DRM_AMD_SECURE_DISPLAY + struct crc_window_parm crc_window; +#endif +#endif }; #endif /* __AMDGPU_DM_IRQ_PARAMS_H__ */ 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 41b09ab22233..73cdb9fe981a 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 @@ -38,6 +38,7 @@ #include "dc_link_ddc.h" #include "i2caux_interface.h" +#include "dmub_cmd.h" #if defined(CONFIG_DEBUG_FS) #include "amdgpu_dm_debugfs.h" #endif @@ -51,7 +52,7 @@ static ssize_t dm_dp_aux_transfer(struct drm_dp_aux *aux, { ssize_t result = 0; struct aux_payload payload; - enum aux_channel_operation_result operation_result; + enum aux_return_code_type operation_result; if (WARN_ON(msg->size > 16)) return -E2BIG; @@ -73,17 +74,19 @@ static ssize_t dm_dp_aux_transfer(struct drm_dp_aux *aux, if (result < 0) switch (operation_result) { - case AUX_CHANNEL_OPERATION_SUCCEEDED: + case AUX_RET_SUCCESS: break; - case AUX_CHANNEL_OPERATION_FAILED_HPD_DISCON: - case AUX_CHANNEL_OPERATION_FAILED_REASON_UNKNOWN: + case AUX_RET_ERROR_HPD_DISCON: + case AUX_RET_ERROR_UNKNOWN: + case AUX_RET_ERROR_INVALID_OPERATION: + case AUX_RET_ERROR_PROTOCOL_ERROR: result = -EIO; break; - case AUX_CHANNEL_OPERATION_FAILED_INVALID_REPLY: - case AUX_CHANNEL_OPERATION_FAILED_ENGINE_ACQUIRE: + case AUX_RET_ERROR_INVALID_REPLY: + case AUX_RET_ERROR_ENGINE_ACQUIRE: result = -EBUSY; break; - case AUX_CHANNEL_OPERATION_FAILED_TIMEOUT: + case AUX_RET_ERROR_TIMEOUT: result = -ETIMEDOUT; break; } 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 index 607ec0999445..eba270121698 100644 --- 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 @@ -32,15 +32,12 @@ #include "amdgpu_dm_irq.h" #include "amdgpu_pm.h" #include "dm_pp_smu.h" -#include "amdgpu_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; - struct smu_context *smu = &adev->smu; int i; if (adev->pm.dpm_enabled) { @@ -106,9 +103,6 @@ bool dm_pp_apply_display_requirements( adev->powerplay.pp_funcs->display_configuration_change( adev->powerplay.pp_handle, &adev->pm.pm_display_cfg); - else if (adev->smu.ppt_funcs) - smu_display_configuration_change(smu, - &adev->pm.pm_display_cfg); amdgpu_pm_compute_clocks(adev); } @@ -148,36 +142,6 @@ static void get_default_clock_levels( } } -static enum smu_clk_type dc_to_smu_clock_type( - enum dm_pp_clock_type dm_pp_clk_type) -{ - enum smu_clk_type smu_clk_type = SMU_CLK_COUNT; - - switch (dm_pp_clk_type) { - case DM_PP_CLOCK_TYPE_DISPLAY_CLK: - smu_clk_type = SMU_DISPCLK; - break; - case DM_PP_CLOCK_TYPE_ENGINE_CLK: - smu_clk_type = SMU_GFXCLK; - break; - case DM_PP_CLOCK_TYPE_MEMORY_CLK: - smu_clk_type = SMU_MCLK; - break; - case DM_PP_CLOCK_TYPE_DCEFCLK: - smu_clk_type = SMU_DCEFCLK; - break; - case DM_PP_CLOCK_TYPE_SOCCLK: - smu_clk_type = SMU_SOCCLK; - break; - default: - DRM_ERROR("DM_PPLIB: invalid clock type: %d!\n", - dm_pp_clk_type); - break; - } - - return smu_clk_type; -} - static enum amd_pp_clock_type dc_to_pp_clock_type( enum dm_pp_clock_type dm_pp_clk_type) { @@ -417,14 +381,8 @@ bool dm_pp_get_clock_levels_by_type_with_latency( &pp_clks); if (ret) return false; - } else if (adev->smu.ppt_funcs && adev->smu.ppt_funcs->get_clock_by_type_with_latency) { - if (smu_get_clock_by_type_with_latency(&adev->smu, - dc_to_smu_clock_type(clk_type), - &pp_clks)) - return false; } - pp_to_dc_clock_levels_with_latency(&pp_clks, clk_level_info, clk_type); return true; @@ -502,10 +460,6 @@ bool dm_pp_apply_clock_for_voltage_request( ret = adev->powerplay.pp_funcs->display_clock_voltage_request( adev->powerplay.pp_handle, &pp_clock_request); - else if (adev->smu.ppt_funcs && - adev->smu.ppt_funcs->display_clock_voltage_request) - ret = smu_display_clock_voltage_request(&adev->smu, - &pp_clock_request); if (ret) return false; return true; @@ -655,8 +609,11 @@ static enum pp_smu_status pp_nv_set_wm_ranges(struct pp_smu *pp, { const struct dc_context *ctx = pp->dm; 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; - smu_set_watermarks_for_clock_ranges(&adev->smu, ranges); + if (pp_funcs && pp_funcs->set_watermarks_for_clocks_ranges) + pp_funcs->set_watermarks_for_clocks_ranges(pp_handle, ranges); return PP_SMU_RESULT_OK; } @@ -665,13 +622,14 @@ static enum pp_smu_status pp_nv_set_display_count(struct pp_smu *pp, int count) { const struct dc_context *ctx = pp->dm; struct amdgpu_device *adev = ctx->driver_context; - struct smu_context *smu = &adev->smu; + void *pp_handle = adev->powerplay.pp_handle; + const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs; - if (!smu->ppt_funcs) + if (!pp_funcs || !pp_funcs->set_active_display_count) return PP_SMU_RESULT_UNSUPPORTED; /* 0: successful or smu.ppt_funcs->set_display_count = NULL; 1: fail */ - if (smu_set_display_count(smu, count)) + if (pp_funcs->set_active_display_count(pp_handle, count)) return PP_SMU_RESULT_FAIL; return PP_SMU_RESULT_OK; @@ -682,13 +640,14 @@ pp_nv_set_min_deep_sleep_dcfclk(struct pp_smu *pp, int mhz) { const struct dc_context *ctx = pp->dm; struct amdgpu_device *adev = ctx->driver_context; - struct smu_context *smu = &adev->smu; + void *pp_handle = adev->powerplay.pp_handle; + const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs; - if (!smu->ppt_funcs) + if (!pp_funcs || !pp_funcs->set_min_deep_sleep_dcefclk) return PP_SMU_RESULT_UNSUPPORTED; /* 0: successful or smu.ppt_funcs->set_deep_sleep_dcefclk = NULL;1: fail */ - if (smu_set_deep_sleep_dcefclk(smu, mhz)) + if (pp_funcs->set_min_deep_sleep_dcefclk(pp_handle, mhz)) return PP_SMU_RESULT_FAIL; return PP_SMU_RESULT_OK; @@ -699,10 +658,11 @@ static enum pp_smu_status pp_nv_set_hard_min_dcefclk_by_freq( { const struct dc_context *ctx = pp->dm; struct amdgpu_device *adev = ctx->driver_context; - struct smu_context *smu = &adev->smu; + void *pp_handle = adev->powerplay.pp_handle; + const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs; struct pp_display_clock_request clock_req; - if (!smu->ppt_funcs) + if (!pp_funcs || !pp_funcs->display_clock_voltage_request) return PP_SMU_RESULT_UNSUPPORTED; clock_req.clock_type = amd_pp_dcef_clock; @@ -711,7 +671,7 @@ static enum pp_smu_status pp_nv_set_hard_min_dcefclk_by_freq( /* 0: successful or smu.ppt_funcs->display_clock_voltage_request = NULL * 1: fail */ - if (smu_display_clock_voltage_request(smu, &clock_req)) + if (pp_funcs->display_clock_voltage_request(pp_handle, &clock_req)) return PP_SMU_RESULT_FAIL; return PP_SMU_RESULT_OK; @@ -722,10 +682,11 @@ pp_nv_set_hard_min_uclk_by_freq(struct pp_smu *pp, int mhz) { const struct dc_context *ctx = pp->dm; struct amdgpu_device *adev = ctx->driver_context; - struct smu_context *smu = &adev->smu; + void *pp_handle = adev->powerplay.pp_handle; + const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs; struct pp_display_clock_request clock_req; - if (!smu->ppt_funcs) + if (!pp_funcs || !pp_funcs->display_clock_voltage_request) return PP_SMU_RESULT_UNSUPPORTED; clock_req.clock_type = amd_pp_mem_clock; @@ -734,7 +695,7 @@ pp_nv_set_hard_min_uclk_by_freq(struct pp_smu *pp, int mhz) /* 0: successful or smu.ppt_funcs->display_clock_voltage_request = NULL * 1: fail */ - if (smu_display_clock_voltage_request(smu, &clock_req)) + if (pp_funcs->display_clock_voltage_request(pp_handle, &clock_req)) return PP_SMU_RESULT_FAIL; return PP_SMU_RESULT_OK; @@ -745,10 +706,14 @@ static enum pp_smu_status pp_nv_set_pstate_handshake_support( { const struct dc_context *ctx = pp->dm; struct amdgpu_device *adev = ctx->driver_context; - struct smu_context *smu = &adev->smu; + void *pp_handle = adev->powerplay.pp_handle; + const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs; - if (smu_display_disable_memory_clock_switch(smu, !pstate_handshake_supported)) - return PP_SMU_RESULT_FAIL; + if (pp_funcs && pp_funcs->display_disable_memory_clock_switch) { + if (pp_funcs->display_disable_memory_clock_switch(pp_handle, + !pstate_handshake_supported)) + return PP_SMU_RESULT_FAIL; + } return PP_SMU_RESULT_OK; } @@ -758,10 +723,11 @@ static enum pp_smu_status pp_nv_set_voltage_by_freq(struct pp_smu *pp, { const struct dc_context *ctx = pp->dm; struct amdgpu_device *adev = ctx->driver_context; - struct smu_context *smu = &adev->smu; + void *pp_handle = adev->powerplay.pp_handle; + const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs; struct pp_display_clock_request clock_req; - if (!smu->ppt_funcs) + if (!pp_funcs || !pp_funcs->display_clock_voltage_request) return PP_SMU_RESULT_UNSUPPORTED; switch (clock_id) { @@ -782,7 +748,7 @@ static enum pp_smu_status pp_nv_set_voltage_by_freq(struct pp_smu *pp, /* 0: successful or smu.ppt_funcs->display_clock_voltage_request = NULL * 1: fail */ - if (smu_display_clock_voltage_request(smu, &clock_req)) + if (pp_funcs->display_clock_voltage_request(pp_handle, &clock_req)) return PP_SMU_RESULT_FAIL; return PP_SMU_RESULT_OK; @@ -793,15 +759,13 @@ static enum pp_smu_status pp_nv_get_maximum_sustainable_clocks( { const struct dc_context *ctx = pp->dm; struct amdgpu_device *adev = ctx->driver_context; - struct smu_context *smu = &adev->smu; - - if (!smu->ppt_funcs) - return PP_SMU_RESULT_UNSUPPORTED; + void *pp_handle = adev->powerplay.pp_handle; + const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs; - if (!smu->ppt_funcs->get_max_sustainable_clocks_by_dc) + if (!pp_funcs || !pp_funcs->get_max_sustainable_clocks_by_dc) return PP_SMU_RESULT_UNSUPPORTED; - if (!smu_get_max_sustainable_clocks_by_dc(smu, max_clocks)) + if (!pp_funcs->get_max_sustainable_clocks_by_dc(pp_handle, max_clocks)) return PP_SMU_RESULT_OK; return PP_SMU_RESULT_FAIL; @@ -812,16 +776,15 @@ static enum pp_smu_status pp_nv_get_uclk_dpm_states(struct pp_smu *pp, { const struct dc_context *ctx = pp->dm; struct amdgpu_device *adev = ctx->driver_context; - struct smu_context *smu = &adev->smu; - - if (!smu->ppt_funcs) - return PP_SMU_RESULT_UNSUPPORTED; + void *pp_handle = adev->powerplay.pp_handle; + const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs; - if (!smu->ppt_funcs->get_uclk_dpm_states) + if (!pp_funcs || !pp_funcs->get_uclk_dpm_states) return PP_SMU_RESULT_UNSUPPORTED; - if (!smu_get_uclk_dpm_states(smu, - clock_values_in_khz, num_states)) + if (!pp_funcs->get_uclk_dpm_states(pp_handle, + clock_values_in_khz, + num_states)) return PP_SMU_RESULT_OK; return PP_SMU_RESULT_FAIL; @@ -832,15 +795,13 @@ static enum pp_smu_status pp_rn_get_dpm_clock_table( { const struct dc_context *ctx = pp->dm; struct amdgpu_device *adev = ctx->driver_context; - struct smu_context *smu = &adev->smu; - - if (!smu->ppt_funcs) - return PP_SMU_RESULT_UNSUPPORTED; + void *pp_handle = adev->powerplay.pp_handle; + const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs; - if (!smu->ppt_funcs->get_dpm_clock_table) + if (!pp_funcs || !pp_funcs->get_dpm_clock_table) return PP_SMU_RESULT_UNSUPPORTED; - if (!smu_get_dpm_clock_table(smu, clock_table)) + if (!pp_funcs->get_dpm_clock_table(pp_handle, clock_table)) return PP_SMU_RESULT_OK; return PP_SMU_RESULT_FAIL; @@ -851,8 +812,11 @@ static enum pp_smu_status pp_rn_set_wm_ranges(struct pp_smu *pp, { const struct dc_context *ctx = pp->dm; 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; - smu_set_watermarks_for_clock_ranges(&adev->smu, ranges); + if (pp_funcs && pp_funcs->set_watermarks_for_clocks_ranges) + pp_funcs->set_watermarks_for_clocks_ranges(pp_handle, ranges); return PP_SMU_RESULT_OK; } diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_trace.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_trace.h index 86960476823c..46a33f64cf8e 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_trace.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_trace.h @@ -597,6 +597,46 @@ TRACE_EVENT(amdgpu_dm_dce_clocks_state, ) ); +TRACE_EVENT(amdgpu_dmub_trace_high_irq, + TP_PROTO(uint32_t trace_code, uint32_t tick_count, uint32_t param0, + uint32_t param1), + TP_ARGS(trace_code, tick_count, param0, param1), + TP_STRUCT__entry( + __field(uint32_t, trace_code) + __field(uint32_t, tick_count) + __field(uint32_t, param0) + __field(uint32_t, param1) + ), + TP_fast_assign( + __entry->trace_code = trace_code; + __entry->tick_count = tick_count; + __entry->param0 = param0; + __entry->param1 = param1; + ), + TP_printk("trace_code=%u tick_count=%u param0=%u param1=%u", + __entry->trace_code, __entry->tick_count, + __entry->param0, __entry->param1) +); + +TRACE_EVENT(amdgpu_refresh_rate_track, + TP_PROTO(int crtc_index, ktime_t refresh_rate_ns, uint32_t refresh_rate_hz), + TP_ARGS(crtc_index, refresh_rate_ns, refresh_rate_hz), + TP_STRUCT__entry( + __field(int, crtc_index) + __field(ktime_t, refresh_rate_ns) + __field(uint32_t, refresh_rate_hz) + ), + TP_fast_assign( + __entry->crtc_index = crtc_index; + __entry->refresh_rate_ns = refresh_rate_ns; + __entry->refresh_rate_hz = refresh_rate_hz; + ), + TP_printk("crtc_index=%d refresh_rate=%dHz (%lld)", + __entry->crtc_index, + __entry->refresh_rate_hz, + __entry->refresh_rate_ns) +); + #endif /* _AMDGPU_DM_TRACE_H_ */ #undef TRACE_INCLUDE_PATH |