aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/amd/display/amdgpu_dm
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/amd/display/amdgpu_dm')
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/Makefile4
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c374
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h27
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c18
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c16
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq_params.h1
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c23
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_trace.h24
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/dc_fpu.c144
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/dc_fpu.h34
10 files changed, 507 insertions, 158 deletions
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile b/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile
index 91fb72c96545..718e123a3230 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile
@@ -27,6 +27,10 @@
AMDGPUDM = amdgpu_dm.o amdgpu_dm_irq.o amdgpu_dm_mst_types.o amdgpu_dm_color.o
+ifdef CONFIG_DRM_AMD_DC_DCN
+AMDGPUDM += dc_fpu.o
+endif
+
ifneq ($(CONFIG_DRM_AMD_DC),)
AMDGPUDM += amdgpu_dm_services.o amdgpu_dm_helpers.o amdgpu_dm_pp_smu.o amdgpu_dm_psr.o
endif
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 afa96c8f721b..9b1fc54555ee 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -618,6 +618,7 @@ static void dm_dcn_vertical_interrupt0_high_irq(void *interrupt_params)
}
#endif
+#define DMUB_TRACE_MAX_READ 64
/**
* dm_dmub_outbox1_low_irq() - Handles Outbox interrupt
* @interrupt_params: used for determining the Outbox instance
@@ -625,7 +626,6 @@ static void dm_dcn_vertical_interrupt0_high_irq(void *interrupt_params)
* Handles the Outbox Interrupt
* event handler.
*/
-#define DMUB_TRACE_MAX_READ 64
static void dm_dmub_outbox1_low_irq(void *interrupt_params)
{
struct dmub_notification notify;
@@ -1044,10 +1044,10 @@ static void mmhub_read_system_context(struct amdgpu_device *adev, struct dc_phy_
}
#endif
#if defined(CONFIG_DRM_AMD_DC_DCN)
-static void event_mall_stutter(struct work_struct *work)
+static void vblank_control_worker(struct work_struct *work)
{
-
- struct vblank_workqueue *vblank_work = container_of(work, struct vblank_workqueue, mall_work);
+ struct vblank_control_work *vblank_work =
+ container_of(work, struct vblank_control_work, work);
struct amdgpu_display_manager *dm = vblank_work->dm;
mutex_lock(&dm->dc_lock);
@@ -1061,27 +1061,25 @@ static void event_mall_stutter(struct work_struct *work)
DRM_DEBUG_KMS("Allow idle optimizations (MALL): %d\n", dm->active_vblank_irq_count == 0);
- mutex_unlock(&dm->dc_lock);
-}
-
-static struct vblank_workqueue *vblank_create_workqueue(struct amdgpu_device *adev, struct dc *dc)
-{
-
- int max_caps = dc->caps.max_links;
- struct vblank_workqueue *vblank_work;
- int i = 0;
-
- vblank_work = kcalloc(max_caps, sizeof(*vblank_work), GFP_KERNEL);
- if (ZERO_OR_NULL_PTR(vblank_work)) {
- kfree(vblank_work);
- return NULL;
+ /* Control PSR based on vblank requirements from OS */
+ if (vblank_work->stream && vblank_work->stream->link) {
+ if (vblank_work->enable) {
+ if (vblank_work->stream->link->psr_settings.psr_allow_active)
+ amdgpu_dm_psr_disable(vblank_work->stream);
+ } else if (vblank_work->stream->link->psr_settings.psr_feature_enabled &&
+ !vblank_work->stream->link->psr_settings.psr_allow_active &&
+ vblank_work->acrtc->dm_irq_params.allow_psr_entry) {
+ amdgpu_dm_psr_enable(vblank_work->stream);
+ }
}
- for (i = 0; i < max_caps; i++)
- INIT_WORK(&vblank_work[i].mall_work, event_mall_stutter);
+ mutex_unlock(&dm->dc_lock);
+
+ dc_stream_release(vblank_work->stream);
- return vblank_work;
+ kfree(vblank_work);
}
+
#endif
static int amdgpu_dm_init(struct amdgpu_device *adev)
{
@@ -1202,7 +1200,7 @@ static int amdgpu_dm_init(struct amdgpu_device *adev)
dc_hardware_init(adev->dm.dc);
#if defined(CONFIG_DRM_AMD_DC_DCN)
- if (adev->apu_flags) {
+ if ((adev->flags & AMD_IS_APU) && (adev->asic_type >= CHIP_CARRIZO)) {
struct dc_phy_addr_space_config pa_config;
mmhub_read_system_context(adev, &pa_config);
@@ -1224,12 +1222,10 @@ static int amdgpu_dm_init(struct amdgpu_device *adev)
#if defined(CONFIG_DRM_AMD_DC_DCN)
if (adev->dm.dc->caps.max_links > 0) {
- adev->dm.vblank_workqueue = vblank_create_workqueue(adev, adev->dm.dc);
-
- if (!adev->dm.vblank_workqueue)
+ adev->dm.vblank_control_workqueue =
+ create_singlethread_workqueue("dm_vblank_control_workqueue");
+ if (!adev->dm.vblank_control_workqueue)
DRM_ERROR("amdgpu: failed to initialize vblank_workqueue.\n");
- else
- DRM_DEBUG_DRIVER("amdgpu: vblank_workqueue init done %p.\n", adev->dm.vblank_workqueue);
}
#endif
@@ -1302,6 +1298,13 @@ static void amdgpu_dm_fini(struct amdgpu_device *adev)
{
int i;
+#if defined(CONFIG_DRM_AMD_DC_DCN)
+ if (adev->dm.vblank_control_workqueue) {
+ destroy_workqueue(adev->dm.vblank_control_workqueue);
+ adev->dm.vblank_control_workqueue = NULL;
+ }
+#endif
+
for (i = 0; i < adev->dm.display_indexes_num; i++) {
drm_encoder_cleanup(&adev->dm.mst_encoders[i].base);
}
@@ -1325,14 +1328,6 @@ static void amdgpu_dm_fini(struct amdgpu_device *adev)
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
-
dc_dmub_srv_destroy(&adev->dm.dc->ctx->dmub_srv);
if (dc_enable_dmub_notifications(adev->dm.dc)) {
@@ -2412,6 +2407,7 @@ static void update_connector_ext_caps(struct amdgpu_dm_connector *aconnector)
static const u8 pre_computed_values[] = {
50, 51, 52, 53, 55, 56, 57, 58, 59, 61, 62, 63, 65, 66, 68, 69,
71, 72, 74, 75, 77, 79, 81, 82, 84, 86, 88, 90, 92, 94, 96, 98};
+ int i;
if (!aconnector || !aconnector->dc_link)
return;
@@ -2423,7 +2419,13 @@ static void update_connector_ext_caps(struct amdgpu_dm_connector *aconnector)
conn_base = &aconnector->base;
adev = drm_to_adev(conn_base->dev);
dm = &adev->dm;
- caps = &dm->backlight_caps;
+ for (i = 0; i < dm->num_of_edps; i++) {
+ if (link == dm->backlight_link[i])
+ break;
+ }
+ if (i >= dm->num_of_edps)
+ return;
+ caps = &dm->backlight_caps[i];
caps->ext_caps = &aconnector->dc_link->dpcd_sink_ext_caps;
caps->aux_support = false;
max_cll = conn_base->hdr_sink_metadata.hdmi_type1.max_cll;
@@ -3423,35 +3425,36 @@ static int amdgpu_dm_mode_config_init(struct amdgpu_device *adev)
#if defined(CONFIG_BACKLIGHT_CLASS_DEVICE) ||\
defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE)
-static void amdgpu_dm_update_backlight_caps(struct amdgpu_display_manager *dm)
+static void amdgpu_dm_update_backlight_caps(struct amdgpu_display_manager *dm,
+ int bl_idx)
{
#if defined(CONFIG_ACPI)
struct amdgpu_dm_backlight_caps caps;
memset(&caps, 0, sizeof(caps));
- if (dm->backlight_caps.caps_valid)
+ if (dm->backlight_caps[bl_idx].caps_valid)
return;
amdgpu_acpi_get_backlight_caps(&caps);
if (caps.caps_valid) {
- dm->backlight_caps.caps_valid = true;
+ dm->backlight_caps[bl_idx].caps_valid = true;
if (caps.aux_support)
return;
- dm->backlight_caps.min_input_signal = caps.min_input_signal;
- dm->backlight_caps.max_input_signal = caps.max_input_signal;
+ dm->backlight_caps[bl_idx].min_input_signal = caps.min_input_signal;
+ dm->backlight_caps[bl_idx].max_input_signal = caps.max_input_signal;
} else {
- dm->backlight_caps.min_input_signal =
+ dm->backlight_caps[bl_idx].min_input_signal =
AMDGPU_DM_DEFAULT_MIN_BACKLIGHT;
- dm->backlight_caps.max_input_signal =
+ dm->backlight_caps[bl_idx].max_input_signal =
AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
}
#else
- if (dm->backlight_caps.aux_support)
+ if (dm->backlight_caps[bl_idx].aux_support)
return;
- dm->backlight_caps.min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT;
- dm->backlight_caps.max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
+ dm->backlight_caps[bl_idx].min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT;
+ dm->backlight_caps[bl_idx].max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
#endif
}
@@ -3502,41 +3505,31 @@ static u32 convert_brightness_to_user(const struct amdgpu_dm_backlight_caps *cap
}
static int amdgpu_dm_backlight_set_level(struct amdgpu_display_manager *dm,
+ int bl_idx,
u32 user_brightness)
{
struct amdgpu_dm_backlight_caps caps;
- struct dc_link *link[AMDGPU_DM_MAX_NUM_EDP];
- u32 brightness[AMDGPU_DM_MAX_NUM_EDP];
+ struct dc_link *link;
+ u32 brightness;
bool rc;
- int i;
- amdgpu_dm_update_backlight_caps(dm);
- caps = dm->backlight_caps;
+ amdgpu_dm_update_backlight_caps(dm, bl_idx);
+ caps = dm->backlight_caps[bl_idx];
- for (i = 0; i < dm->num_of_edps; i++) {
- dm->brightness[i] = user_brightness;
- brightness[i] = convert_brightness_from_user(&caps, dm->brightness[i]);
- link[i] = (struct dc_link *)dm->backlight_link[i];
- }
+ dm->brightness[bl_idx] = user_brightness;
+ brightness = convert_brightness_from_user(&caps, dm->brightness[bl_idx]);
+ link = (struct dc_link *)dm->backlight_link[bl_idx];
/* Change brightness based on AUX property */
if (caps.aux_support) {
- for (i = 0; i < dm->num_of_edps; i++) {
- rc = dc_link_set_backlight_level_nits(link[i], true, brightness[i],
- AUX_BL_DEFAULT_TRANSITION_TIME_MS);
- if (!rc) {
- DRM_DEBUG("DM: Failed to update backlight via AUX on eDP[%d]\n", i);
- break;
- }
- }
+ rc = dc_link_set_backlight_level_nits(link, true, brightness,
+ AUX_BL_DEFAULT_TRANSITION_TIME_MS);
+ if (!rc)
+ DRM_DEBUG("DM: Failed to update backlight via AUX on eDP[%d]\n", bl_idx);
} else {
- for (i = 0; i < dm->num_of_edps; i++) {
- rc = dc_link_set_backlight_level(dm->backlight_link[i], brightness[i], 0);
- if (!rc) {
- DRM_DEBUG("DM: Failed to update backlight on eDP[%d]\n", i);
- break;
- }
- }
+ rc = dc_link_set_backlight_level(link, brightness, 0);
+ if (!rc)
+ DRM_DEBUG("DM: Failed to update backlight on eDP[%d]\n", bl_idx);
}
return rc ? 0 : 1;
@@ -3545,33 +3538,41 @@ static int amdgpu_dm_backlight_set_level(struct amdgpu_display_manager *dm,
static int amdgpu_dm_backlight_update_status(struct backlight_device *bd)
{
struct amdgpu_display_manager *dm = bl_get_data(bd);
+ int i;
- amdgpu_dm_backlight_set_level(dm, bd->props.brightness);
+ for (i = 0; i < dm->num_of_edps; i++) {
+ if (bd == dm->backlight_dev[i])
+ break;
+ }
+ if (i >= AMDGPU_DM_MAX_NUM_EDP)
+ i = 0;
+ amdgpu_dm_backlight_set_level(dm, i, bd->props.brightness);
return 0;
}
-static u32 amdgpu_dm_backlight_get_level(struct amdgpu_display_manager *dm)
+static u32 amdgpu_dm_backlight_get_level(struct amdgpu_display_manager *dm,
+ int bl_idx)
{
struct amdgpu_dm_backlight_caps caps;
+ struct dc_link *link = (struct dc_link *)dm->backlight_link[bl_idx];
- amdgpu_dm_update_backlight_caps(dm);
- caps = dm->backlight_caps;
+ amdgpu_dm_update_backlight_caps(dm, bl_idx);
+ caps = dm->backlight_caps[bl_idx];
if (caps.aux_support) {
- struct dc_link *link = (struct dc_link *)dm->backlight_link[0];
u32 avg, peak;
bool rc;
rc = dc_link_get_backlight_level_nits(link, &avg, &peak);
if (!rc)
- return dm->brightness[0];
+ return dm->brightness[bl_idx];
return convert_brightness_to_user(&caps, avg);
} else {
- int ret = dc_link_get_backlight_level(dm->backlight_link[0]);
+ int ret = dc_link_get_backlight_level(link);
if (ret == DC_ERROR_UNEXPECTED)
- return dm->brightness[0];
+ return dm->brightness[bl_idx];
return convert_brightness_to_user(&caps, ret);
}
}
@@ -3579,8 +3580,15 @@ static u32 amdgpu_dm_backlight_get_level(struct amdgpu_display_manager *dm)
static int amdgpu_dm_backlight_get_brightness(struct backlight_device *bd)
{
struct amdgpu_display_manager *dm = bl_get_data(bd);
+ int i;
- return amdgpu_dm_backlight_get_level(dm);
+ for (i = 0; i < dm->num_of_edps; i++) {
+ if (bd == dm->backlight_dev[i])
+ break;
+ }
+ if (i >= AMDGPU_DM_MAX_NUM_EDP)
+ i = 0;
+ return amdgpu_dm_backlight_get_level(dm, i);
}
static const struct backlight_ops amdgpu_dm_backlight_ops = {
@@ -3594,31 +3602,28 @@ amdgpu_dm_register_backlight_device(struct amdgpu_display_manager *dm)
{
char bl_name[16];
struct backlight_properties props = { 0 };
- int i;
- amdgpu_dm_update_backlight_caps(dm);
- for (i = 0; i < dm->num_of_edps; i++)
- dm->brightness[i] = AMDGPU_MAX_BL_LEVEL;
+ amdgpu_dm_update_backlight_caps(dm, dm->num_of_edps);
+ dm->brightness[dm->num_of_edps] = AMDGPU_MAX_BL_LEVEL;
props.max_brightness = AMDGPU_MAX_BL_LEVEL;
props.brightness = AMDGPU_MAX_BL_LEVEL;
props.type = BACKLIGHT_RAW;
snprintf(bl_name, sizeof(bl_name), "amdgpu_bl%d",
- adev_to_drm(dm->adev)->primary->index);
+ adev_to_drm(dm->adev)->primary->index + dm->num_of_edps);
- dm->backlight_dev = backlight_device_register(bl_name,
- adev_to_drm(dm->adev)->dev,
- dm,
- &amdgpu_dm_backlight_ops,
- &props);
+ dm->backlight_dev[dm->num_of_edps] = backlight_device_register(bl_name,
+ adev_to_drm(dm->adev)->dev,
+ dm,
+ &amdgpu_dm_backlight_ops,
+ &props);
- if (IS_ERR(dm->backlight_dev))
+ if (IS_ERR(dm->backlight_dev[dm->num_of_edps]))
DRM_ERROR("DM: Backlight registration failed!\n");
else
DRM_DEBUG_DRIVER("DM: Registered Backlight device: %s\n", bl_name);
}
-
#endif
static int initialize_plane(struct amdgpu_display_manager *dm,
@@ -3675,10 +3680,10 @@ static void register_backlight_device(struct amdgpu_display_manager *dm,
* DM initialization because not having a backlight control
* is better then a black screen.
*/
- if (!dm->backlight_dev)
+ if (!dm->backlight_dev[dm->num_of_edps])
amdgpu_dm_register_backlight_device(dm);
- if (dm->backlight_dev) {
+ if (dm->backlight_dev[dm->num_of_edps]) {
dm->backlight_link[dm->num_of_edps] = link;
dm->num_of_edps++;
}
@@ -4747,7 +4752,7 @@ fill_gfx9_plane_attributes_from_modifiers(struct amdgpu_device *adev,
const bool force_disable_dcc)
{
const uint64_t modifier = afb->base.modifier;
- int ret;
+ int ret = 0;
fill_gfx9_tiling_info_from_modifier(adev, tiling_info, modifier);
tiling_info->gfx9.swizzle = modifier_gfx9_swizzle_mode(modifier);
@@ -4765,9 +4770,9 @@ fill_gfx9_plane_attributes_from_modifiers(struct amdgpu_device *adev,
ret = validate_dcc(adev, format, rotation, tiling_info, dcc, address, plane_size);
if (ret)
- return ret;
+ drm_dbg_kms(adev_to_drm(adev), "validate_dcc: returned error: %d\n", ret);
- return 0;
+ return ret;
}
static int
@@ -5994,7 +5999,7 @@ static inline int dm_set_vblank(struct drm_crtc *crtc, bool enable)
struct dm_crtc_state *acrtc_state = to_dm_crtc_state(crtc->state);
#if defined(CONFIG_DRM_AMD_DC_DCN)
struct amdgpu_display_manager *dm = &adev->dm;
- unsigned long flags;
+ struct vblank_control_work *work;
#endif
int rc = 0;
@@ -6019,12 +6024,21 @@ static inline int dm_set_vblank(struct drm_crtc *crtc, bool enable)
return 0;
#if defined(CONFIG_DRM_AMD_DC_DCN)
- spin_lock_irqsave(&dm->vblank_lock, flags);
- dm->vblank_workqueue->dm = dm;
- dm->vblank_workqueue->otg_inst = acrtc->otg_inst;
- dm->vblank_workqueue->enable = enable;
- spin_unlock_irqrestore(&dm->vblank_lock, flags);
- schedule_work(&dm->vblank_workqueue->mall_work);
+ work = kzalloc(sizeof(*work), GFP_ATOMIC);
+ if (!work)
+ return -ENOMEM;
+
+ INIT_WORK(&work->work, vblank_control_worker);
+ work->dm = dm;
+ work->acrtc = acrtc;
+ work->enable = enable;
+
+ if (acrtc_state->stream) {
+ dc_stream_retain(acrtc_state->stream);
+ work->stream = acrtc_state->stream;
+ }
+
+ queue_work(dm->vblank_control_workqueue, &work->work);
#endif
return 0;
@@ -6198,6 +6212,7 @@ static void amdgpu_dm_connector_destroy(struct drm_connector *connector)
const struct dc_link *link = aconnector->dc_link;
struct amdgpu_device *adev = drm_to_adev(connector->dev);
struct amdgpu_display_manager *dm = &adev->dm;
+ int i;
/*
* Call only if mst_mgr was iniitalized before since it's not done
@@ -6208,12 +6223,11 @@ static void amdgpu_dm_connector_destroy(struct drm_connector *connector)
#if defined(CONFIG_BACKLIGHT_CLASS_DEVICE) ||\
defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE)
-
- if ((link->connector_signal & (SIGNAL_TYPE_EDP | SIGNAL_TYPE_LVDS)) &&
- link->type != dc_connection_none &&
- dm->backlight_dev) {
- backlight_device_unregister(dm->backlight_dev);
- dm->backlight_dev = NULL;
+ for (i = 0; i < dm->num_of_edps; i++) {
+ if ((link == dm->backlight_link[i]) && dm->backlight_dev[i]) {
+ backlight_device_unregister(dm->backlight_dev[i]);
+ dm->backlight_dev[i] = NULL;
+ }
}
#endif
@@ -7570,8 +7584,10 @@ static uint add_fs_modes(struct amdgpu_dm_connector *aconnector)
* 60 - Commonly used
* 48,72,96 - Multiples of 24
*/
- const uint32_t common_rates[] = { 23976, 24000, 25000, 29970, 30000,
- 48000, 50000, 60000, 72000, 96000 };
+ static 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
@@ -8627,6 +8643,14 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
/* Update the planes if changed or disable if we don't have any. */
if ((planes_count || acrtc_state->active_planes == 0) &&
acrtc_state->stream) {
+#if defined(CONFIG_DRM_AMD_DC_DCN)
+ /*
+ * If PSR or idle optimizations are enabled then flush out
+ * any pending work before hardware programming.
+ */
+ flush_workqueue(dm->vblank_control_workqueue);
+#endif
+
bundle->stream_update.stream = acrtc_state->stream;
if (new_pcrtc_state->mode_changed) {
bundle->stream_update.src = acrtc_state->stream->src;
@@ -8695,16 +8719,20 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
acrtc_state->stream->link->psr_settings.psr_version != DC_PSR_VERSION_UNSUPPORTED &&
!acrtc_state->stream->link->psr_settings.psr_feature_enabled)
amdgpu_dm_link_setup_psr(acrtc_state->stream);
- else if ((acrtc_state->update_type == UPDATE_TYPE_FAST) &&
- acrtc_state->stream->link->psr_settings.psr_feature_enabled &&
- !acrtc_state->stream->link->psr_settings.psr_allow_active) {
- struct amdgpu_dm_connector *aconn = (struct amdgpu_dm_connector *)
- acrtc_state->stream->dm_stream_context;
+
+ /* Decrement skip count when PSR is enabled and we're doing fast updates. */
+ if (acrtc_state->update_type == UPDATE_TYPE_FAST &&
+ acrtc_state->stream->link->psr_settings.psr_feature_enabled) {
+ struct amdgpu_dm_connector *aconn =
+ (struct amdgpu_dm_connector *)acrtc_state->stream->dm_stream_context;
if (aconn->psr_skip_count > 0)
aconn->psr_skip_count--;
- else
- amdgpu_dm_psr_enable(acrtc_state->stream);
+
+ /* Allow PSR when skip count is 0. */
+ acrtc_attach->dm_irq_params.allow_psr_entry = !aconn->psr_skip_count;
+ } else {
+ acrtc_attach->dm_irq_params.allow_psr_entry = false;
}
mutex_unlock(&dm->dc_lock);
@@ -8953,8 +8981,12 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state)
if (dc_state) {
/* if there mode set or reset, disable eDP PSR */
- if (mode_set_reset_required)
+ if (mode_set_reset_required) {
+#if defined(CONFIG_DRM_AMD_DC_DCN)
+ flush_workqueue(dm->vblank_control_workqueue);
+#endif
amdgpu_dm_psr_disable_all(dm);
+ }
dm_enable_per_frame_crtc_master_sync(dc_state);
mutex_lock(&dm->dc_lock);
@@ -9191,8 +9223,11 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state)
#if defined(CONFIG_BACKLIGHT_CLASS_DEVICE) || \
defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE)
/* restore the backlight level */
- if (dm->backlight_dev && (amdgpu_dm_backlight_get_level(dm) != dm->brightness[0]))
- amdgpu_dm_backlight_set_level(dm, dm->brightness[0]);
+ for (i = 0; i < dm->num_of_edps; i++) {
+ if (dm->backlight_dev[i] &&
+ (amdgpu_dm_backlight_get_level(dm, i) != dm->brightness[i]))
+ amdgpu_dm_backlight_set_level(dm, i, dm->brightness[i]);
+ }
#endif
/*
* send vblank event on all events not handled in flip and
@@ -10554,13 +10589,68 @@ static bool is_dp_capable_without_timing_msa(struct dc *dc,
return capable;
}
-static bool parse_edid_cea(struct amdgpu_dm_connector *aconnector,
+static bool dm_edid_parser_send_cea(struct amdgpu_display_manager *dm,
+ unsigned int offset,
+ unsigned int total_length,
+ uint8_t *data,
+ unsigned int length,
+ struct amdgpu_hdmi_vsdb_info *vsdb)
+{
+ bool res;
+ union dmub_rb_cmd cmd;
+ struct dmub_cmd_send_edid_cea *input;
+ struct dmub_cmd_edid_cea_output *output;
+
+ if (length > DMUB_EDID_CEA_DATA_CHUNK_BYTES)
+ return false;
+
+ memset(&cmd, 0, sizeof(cmd));
+
+ input = &cmd.edid_cea.data.input;
+
+ cmd.edid_cea.header.type = DMUB_CMD__EDID_CEA;
+ cmd.edid_cea.header.sub_type = 0;
+ cmd.edid_cea.header.payload_bytes =
+ sizeof(cmd.edid_cea) - sizeof(cmd.edid_cea.header);
+ input->offset = offset;
+ input->length = length;
+ input->total_length = total_length;
+ memcpy(input->payload, data, length);
+
+ res = dc_dmub_srv_cmd_with_reply_data(dm->dc->ctx->dmub_srv, &cmd);
+ if (!res) {
+ DRM_ERROR("EDID CEA parser failed\n");
+ return false;
+ }
+
+ output = &cmd.edid_cea.data.output;
+
+ if (output->type == DMUB_CMD__EDID_CEA_ACK) {
+ if (!output->ack.success) {
+ DRM_ERROR("EDID CEA ack failed at offset %d\n",
+ output->ack.offset);
+ }
+ } else if (output->type == DMUB_CMD__EDID_CEA_AMD_VSDB) {
+ if (!output->amd_vsdb.vsdb_found)
+ return false;
+
+ vsdb->freesync_supported = output->amd_vsdb.freesync_supported;
+ vsdb->amd_vsdb_version = output->amd_vsdb.amd_vsdb_version;
+ vsdb->min_refresh_rate_hz = output->amd_vsdb.min_frame_rate;
+ vsdb->max_refresh_rate_hz = output->amd_vsdb.max_frame_rate;
+ } else {
+ DRM_WARN("Unknown EDID CEA parser results\n");
+ return false;
+ }
+
+ return true;
+}
+
+static bool parse_edid_cea_dmcu(struct amdgpu_display_manager *dm,
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) {
@@ -10568,14 +10658,14 @@ static bool parse_edid_cea(struct amdgpu_dm_connector *aconnector,
int offset;
/* send 8 bytes a time */
- if (!dc_edid_parser_send_cea(dc, i, len, &edid_ext[i], 8))
+ if (!dc_edid_parser_send_cea(dm->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);
+ res = dc_edid_parser_recv_amd_vsdb(dm->dc, &version, &min_rate, &max_rate);
if (res) {
/* amd vsdb found */
vsdb_info->freesync_supported = 1;
@@ -10589,7 +10679,7 @@ static bool parse_edid_cea(struct amdgpu_dm_connector *aconnector,
}
/* check for ack*/
- res = dc_edid_parser_recv_cea_ack(dc, &offset);
+ res = dc_edid_parser_recv_cea_ack(dm->dc, &offset);
if (!res)
return false;
}
@@ -10597,6 +10687,34 @@ static bool parse_edid_cea(struct amdgpu_dm_connector *aconnector,
return false;
}
+static bool parse_edid_cea_dmub(struct amdgpu_display_manager *dm,
+ uint8_t *edid_ext, int len,
+ struct amdgpu_hdmi_vsdb_info *vsdb_info)
+{
+ int i;
+
+ /* send extension block to DMCU for parsing */
+ for (i = 0; i < len; i += 8) {
+ /* send 8 bytes a time */
+ if (!dm_edid_parser_send_cea(dm, i, len, &edid_ext[i], 8, vsdb_info))
+ return false;
+ }
+
+ return vsdb_info->freesync_supported;
+}
+
+static bool parse_edid_cea(struct amdgpu_dm_connector *aconnector,
+ uint8_t *edid_ext, int len,
+ struct amdgpu_hdmi_vsdb_info *vsdb_info)
+{
+ struct amdgpu_device *adev = drm_to_adev(aconnector->base.dev);
+
+ if (adev->dm.dmub_srv)
+ return parse_edid_cea_dmub(&adev->dm, edid_ext, len, vsdb_info);
+ else
+ return parse_edid_cea_dmcu(&adev->dm, edid_ext, len, vsdb_info);
+}
+
static int parse_hdmi_amd_vsdb(struct amdgpu_dm_connector *aconnector,
struct edid *edid, struct amdgpu_hdmi_vsdb_info *vsdb_info)
{
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 9522d4ca299e..d1d353a7c77d 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
@@ -60,6 +60,7 @@ enum aux_return_code_type;
/* Forward declarations */
struct amdgpu_device;
+struct amdgpu_crtc;
struct drm_device;
struct dc;
struct amdgpu_bo;
@@ -86,16 +87,18 @@ struct dm_compressor_info {
};
/**
- * struct vblank_workqueue - Works to be executed in a separate thread during vblank
- * @mall_work: work for mall stutter
+ * struct vblank_control_work - Work data for vblank control
+ * @work: Kernel work data for the work event
* @dm: amdgpu display manager device
- * @otg_inst: otg instance of which vblank is being set
- * @enable: true if enable vblank
+ * @acrtc: amdgpu CRTC instance for which the event has occurred
+ * @stream: DC stream for which the event has occurred
+ * @enable: true if enabling vblank
*/
-struct vblank_workqueue {
- struct work_struct mall_work;
+struct vblank_control_work {
+ struct work_struct work;
struct amdgpu_display_manager *dm;
- int otg_inst;
+ struct amdgpu_crtc *acrtc;
+ struct dc_stream_state *stream;
bool enable;
};
@@ -365,13 +368,13 @@ struct amdgpu_display_manager {
spinlock_t irq_handler_list_table_lock;
- struct backlight_device *backlight_dev;
+ struct backlight_device *backlight_dev[AMDGPU_DM_MAX_NUM_EDP];
const struct dc_link *backlight_link[AMDGPU_DM_MAX_NUM_EDP];
uint8_t num_of_edps;
- struct amdgpu_dm_backlight_caps backlight_caps;
+ struct amdgpu_dm_backlight_caps backlight_caps[AMDGPU_DM_MAX_NUM_EDP];
struct mod_freesync *freesync_module;
#ifdef CONFIG_DRM_AMD_DC_HDCP
@@ -380,11 +383,11 @@ struct amdgpu_display_manager {
#if defined(CONFIG_DRM_AMD_DC_DCN)
/**
- * @vblank_workqueue:
+ * @vblank_control_workqueue:
*
- * amdgpu workqueue during vblank
+ * Deferred work for vblank control events.
*/
- struct vblank_workqueue *vblank_workqueue;
+ struct workqueue_struct *vblank_control_workqueue;
#endif
struct drm_atomic_state *cached_state;
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 f1145086a468..87daa78a32b8 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
@@ -197,29 +197,29 @@ static ssize_t dp_link_settings_read(struct file *f, char __user *buf,
rd_buf_ptr = rd_buf;
- str_len = strlen("Current: %d %d %d ");
- snprintf(rd_buf_ptr, str_len, "Current: %d %d %d ",
+ str_len = strlen("Current: %d 0x%x %d ");
+ snprintf(rd_buf_ptr, str_len, "Current: %d 0x%x %d ",
link->cur_link_settings.lane_count,
link->cur_link_settings.link_rate,
link->cur_link_settings.link_spread);
rd_buf_ptr += str_len;
- str_len = strlen("Verified: %d %d %d ");
- snprintf(rd_buf_ptr, str_len, "Verified: %d %d %d ",
+ str_len = strlen("Verified: %d 0x%x %d ");
+ snprintf(rd_buf_ptr, str_len, "Verified: %d 0x%x %d ",
link->verified_link_cap.lane_count,
link->verified_link_cap.link_rate,
link->verified_link_cap.link_spread);
rd_buf_ptr += str_len;
- str_len = strlen("Reported: %d %d %d ");
- snprintf(rd_buf_ptr, str_len, "Reported: %d %d %d ",
+ str_len = strlen("Reported: %d 0x%x %d ");
+ snprintf(rd_buf_ptr, str_len, "Reported: %d 0x%x %d ",
link->reported_link_cap.lane_count,
link->reported_link_cap.link_rate,
link->reported_link_cap.link_spread);
rd_buf_ptr += str_len;
- str_len = strlen("Preferred: %d %d %d ");
- snprintf(rd_buf_ptr, str_len, "Preferred: %d %d %d\n",
+ str_len = strlen("Preferred: %d 0x%x %d ");
+ snprintf(rd_buf_ptr, str_len, "Preferred: %d 0x%x %d\n",
link->preferred_link_setting.lane_count,
link->preferred_link_setting.link_rate,
link->preferred_link_setting.link_spread);
@@ -377,7 +377,7 @@ static ssize_t dp_phy_settings_read(struct file *f, char __user *buf,
if (!rd_buf)
return -EINVAL;
- snprintf(rd_buf, rd_buf_size, " %d %d %d ",
+ snprintf(rd_buf, rd_buf_size, " %d %d %d\n",
link->cur_lane_setting.VOLTAGE_SWING,
link->cur_lane_setting.PRE_EMPHASIS,
link->cur_lane_setting.POST_CURSOR2);
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 e63c6885c757..c5f1dc3b5961 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
@@ -79,12 +79,12 @@ static uint8_t *psp_get_srm(struct psp_context *psp, uint32_t *srm_version, uint
struct ta_hdcp_shared_memory *hdcp_cmd;
- if (!psp->hdcp_context.hdcp_initialized) {
+ if (!psp->hdcp_context.context.initialized) {
DRM_WARN("Failed to get hdcp srm. HDCP TA is not initialized.");
return NULL;
}
- hdcp_cmd = (struct ta_hdcp_shared_memory *)psp->hdcp_context.hdcp_shared_buf;
+ hdcp_cmd = (struct ta_hdcp_shared_memory *)psp->hdcp_context.context.mem_context.shared_buf;
memset(hdcp_cmd, 0, sizeof(struct ta_hdcp_shared_memory));
hdcp_cmd->cmd_id = TA_HDCP_COMMAND__HDCP_GET_SRM;
@@ -105,12 +105,12 @@ static int psp_set_srm(struct psp_context *psp, uint8_t *srm, uint32_t srm_size,
struct ta_hdcp_shared_memory *hdcp_cmd;
- if (!psp->hdcp_context.hdcp_initialized) {
+ if (!psp->hdcp_context.context.initialized) {
DRM_WARN("Failed to get hdcp srm. HDCP TA is not initialized.");
return -EINVAL;
}
- hdcp_cmd = (struct ta_hdcp_shared_memory *)psp->hdcp_context.hdcp_shared_buf;
+ hdcp_cmd = (struct ta_hdcp_shared_memory *)psp->hdcp_context.context.mem_context.shared_buf;
memset(hdcp_cmd, 0, sizeof(struct ta_hdcp_shared_memory));
memcpy(hdcp_cmd->in_msg.hdcp_set_srm.srm_buf, srm, srm_size);
@@ -414,12 +414,12 @@ static bool enable_assr(void *handle, struct dc_link *link)
struct ta_dtm_shared_memory *dtm_cmd;
bool res = true;
- if (!psp->dtm_context.dtm_initialized) {
+ if (!psp->dtm_context.context.initialized) {
DRM_INFO("Failed to enable ASSR, DTM TA is not initialized.");
return false;
}
- dtm_cmd = (struct ta_dtm_shared_memory *)psp->dtm_context.dtm_shared_buf;
+ dtm_cmd = (struct ta_dtm_shared_memory *)psp->dtm_context.context.mem_context.shared_buf;
mutex_lock(&psp->dtm_context.mutex);
memset(dtm_cmd, 0, sizeof(struct ta_dtm_shared_memory));
@@ -655,10 +655,8 @@ struct hdcp_workqueue *hdcp_create_workqueue(struct amdgpu_device *adev, struct
INIT_DELAYED_WORK(&hdcp_work[i].property_validate_dwork, event_property_validate);
hdcp_work[i].hdcp.config.psp.handle = &adev->psp;
- if (dc->ctx->dce_version == DCN_VERSION_3_1) {
+ if (dc->ctx->dce_version == DCN_VERSION_3_1)
hdcp_work[i].hdcp.config.psp.caps.dtm_v3_supported = 1;
- hdcp_work[i].hdcp.config.psp.caps.opm_state_query_supported = false;
- }
hdcp_work[i].hdcp.config.ddc.handle = dc_get_link_at_index(dc, i);
hdcp_work[i].hdcp.config.ddc.funcs.write_i2c = lp_write_i2c;
hdcp_work[i].hdcp.config.ddc.funcs.read_i2c = lp_read_i2c;
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 f3b93ba69a27..79b5f9999fec 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
@@ -33,6 +33,7 @@ struct dm_irq_params {
struct mod_vrr_params vrr_params;
struct dc_stream_state *stream;
int active_planes;
+ bool allow_psr_entry;
struct mod_freesync_config freesync_config;
#ifdef CONFIG_DEBUG_FS
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 5568d4e518e6..1bcba6943fd7 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
@@ -213,6 +213,29 @@ static int dm_dp_mst_get_modes(struct drm_connector *connector)
drm_connector_update_edid_property(
&aconnector->base,
NULL);
+
+ DRM_DEBUG_KMS("Can't get EDID of %s. Add default remote sink.", connector->name);
+ if (!aconnector->dc_sink) {
+ struct dc_sink *dc_sink;
+ struct dc_sink_init_data init_params = {
+ .link = aconnector->dc_link,
+ .sink_signal = SIGNAL_TYPE_DISPLAY_PORT_MST };
+
+ dc_sink = dc_link_add_remote_sink(
+ aconnector->dc_link,
+ NULL,
+ 0,
+ &init_params);
+
+ if (!dc_sink) {
+ DRM_ERROR("Unable to add a remote sink\n");
+ return 0;
+ }
+
+ dc_sink->priv = aconnector;
+ aconnector->dc_sink = dc_sink;
+ }
+
return ret;
}
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 46a33f64cf8e..fdcaea22b456 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
@@ -637,6 +637,30 @@ TRACE_EVENT(amdgpu_refresh_rate_track,
__entry->refresh_rate_ns)
);
+TRACE_EVENT(dcn_fpu,
+ TP_PROTO(bool begin, const char *function, const int line, const int recursion_depth),
+ TP_ARGS(begin, function, line, recursion_depth),
+
+ TP_STRUCT__entry(
+ __field(bool, begin)
+ __field(const char *, function)
+ __field(int, line)
+ __field(int, recursion_depth)
+ ),
+ TP_fast_assign(
+ __entry->begin = begin;
+ __entry->function = function;
+ __entry->line = line;
+ __entry->recursion_depth = recursion_depth;
+ ),
+ TP_printk("%s: recursion_depth: %d: %s()+%d:",
+ __entry->begin ? "begin" : "end",
+ __entry->recursion_depth,
+ __entry->function,
+ __entry->line
+ )
+);
+
#endif /* _AMDGPU_DM_TRACE_H_ */
#undef TRACE_INCLUDE_PATH
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/dc_fpu.c b/drivers/gpu/drm/amd/display/amdgpu_dm/dc_fpu.c
new file mode 100644
index 000000000000..c9f47d167472
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/dc_fpu.c
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright 2021 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: AMD
+ *
+ */
+
+#include "dc_trace.h"
+
+#if defined(CONFIG_X86)
+#include <asm/fpu/api.h>
+#elif defined(CONFIG_PPC64)
+#include <asm/switch_to.h>
+#include <asm/cputable.h>
+#endif
+
+/**
+ * DOC: DC FPU manipulation overview
+ *
+ * DC core uses FPU operations in multiple parts of the code, which requires a
+ * more specialized way to manage these areas' entrance. To fulfill this
+ * requirement, we created some wrapper functions that encapsulate
+ * kernel_fpu_begin/end to better fit our need in the display component. In
+ * summary, in this file, you can find functions related to FPU operation
+ * management.
+ */
+
+static DEFINE_PER_CPU(int, fpu_recursion_depth);
+
+/**
+ * dc_assert_fp_enabled - Check if FPU protection is enabled
+ *
+ * This function tells if the code is already under FPU protection or not. A
+ * function that works as an API for a set of FPU operations can use this
+ * function for checking if the caller invoked it after DC_FP_START(). For
+ * example, take a look at dcn2x.c file.
+ */
+inline void dc_assert_fp_enabled(void)
+{
+ int *pcpu, depth = 0;
+
+ pcpu = get_cpu_ptr(&fpu_recursion_depth);
+ depth = *pcpu;
+ put_cpu_ptr(&fpu_recursion_depth);
+
+ ASSERT(depth > 1);
+}
+
+/**
+ * dc_fpu_begin - Enables FPU protection
+ * @function_name: A string containing the function name for debug purposes
+ * (usually __func__)
+ *
+ * @line: A line number where DC_FP_START was invoked for debug purpose
+ * (usually __LINE__)
+ *
+ * This function is responsible for managing the use of kernel_fpu_begin() with
+ * the advantage of providing an event trace for debugging.
+ *
+ * Note: Do not call this function directly; always use DC_FP_START().
+ */
+void dc_fpu_begin(const char *function_name, const int line)
+{
+ int *pcpu;
+
+ pcpu = get_cpu_ptr(&fpu_recursion_depth);
+ *pcpu += 1;
+
+ if (*pcpu == 1) {
+#if defined(CONFIG_X86)
+ kernel_fpu_begin();
+#elif defined(CONFIG_PPC64)
+ if (cpu_has_feature(CPU_FTR_VSX_COMP)) {
+ preempt_disable();
+ enable_kernel_vsx();
+ } else if (cpu_has_feature(CPU_FTR_ALTIVEC_COMP)) {
+ preempt_disable();
+ enable_kernel_altivec();
+ } else if (!cpu_has_feature(CPU_FTR_FPU_UNAVAILABLE)) {
+ preempt_disable();
+ enable_kernel_fp();
+ }
+#endif
+ }
+
+ TRACE_DCN_FPU(true, function_name, line, *pcpu);
+ put_cpu_ptr(&fpu_recursion_depth);
+}
+
+/**
+ * dc_fpu_end - Disable FPU protection
+ * @function_name: A string containing the function name for debug purposes
+ * @line: A-line number where DC_FP_END was invoked for debug purpose
+ *
+ * This function is responsible for managing the use of kernel_fpu_end() with
+ * the advantage of providing an event trace for debugging.
+ *
+ * Note: Do not call this function directly; always use DC_FP_END().
+ */
+void dc_fpu_end(const char *function_name, const int line)
+{
+ int *pcpu;
+
+ pcpu = get_cpu_ptr(&fpu_recursion_depth);
+ *pcpu -= 1;
+ if (*pcpu <= 0) {
+#if defined(CONFIG_X86)
+ kernel_fpu_end();
+#elif defined(CONFIG_PPC64)
+ if (cpu_has_feature(CPU_FTR_VSX_COMP)) {
+ disable_kernel_vsx();
+ preempt_enable();
+ } else if (cpu_has_feature(CPU_FTR_ALTIVEC_COMP)) {
+ disable_kernel_altivec();
+ preempt_enable();
+ } else if (!cpu_has_feature(CPU_FTR_FPU_UNAVAILABLE)) {
+ disable_kernel_fp();
+ preempt_enable();
+ }
+#endif
+ }
+
+ TRACE_DCN_FPU(false, function_name, line, *pcpu);
+ put_cpu_ptr(&fpu_recursion_depth);
+}
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/dc_fpu.h b/drivers/gpu/drm/amd/display/amdgpu_dm/dc_fpu.h
new file mode 100644
index 000000000000..b8275b397920
--- /dev/null
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/dc_fpu.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright 2021 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: AMD
+ *
+ */
+
+#ifndef __DC_FPU_H__
+#define __DC_FPU_H__
+
+void dc_assert_fp_enabled(void);
+void dc_fpu_begin(const char *function_name, const int line);
+void dc_fpu_end(const char *function_name, const int line);
+
+#endif /* __DC_FPU_H__ */