drm/amd/display: Implement MPO PSR SU
[linux-2.6-microblaze.git] / drivers / gpu / drm / amd / display / amdgpu_dm / amdgpu_dm.c
index 62139ff..2bb316f 100644 (file)
@@ -48,7 +48,7 @@
 #include "amdgpu_dm.h"
 #ifdef CONFIG_DRM_AMD_DC_HDCP
 #include "amdgpu_dm_hdcp.h"
-#include <drm/drm_hdcp.h>
+#include <drm/display/drm_hdcp_helper.h>
 #endif
 #include "amdgpu_pm.h"
 #include "amdgpu_atombios.h"
 #include <linux/firmware.h>
 #include <linux/component.h>
 
+#include <drm/display/drm_dp_mst_helper.h>
+#include <drm/display/drm_hdmi_helper.h>
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_uapi.h>
 #include <drm/drm_atomic_helper.h>
-#include <drm/dp/drm_dp_mst_helper.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_fourcc.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_vblank.h>
 #include <drm/drm_audio_component.h>
 
-#if defined(CONFIG_DRM_AMD_DC_DCN)
 #include "ivsrcid/dcn/irqsrcs_dcn_1_0.h"
 
 #include "dcn/dcn_1_0_offset.h"
 #include "dcn/dcn_1_0_sh_mask.h"
 #include "soc15_hw_ip.h"
+#include "soc15_common.h"
 #include "vega10_ip_offset.h"
 
 #include "soc15_common.h"
-#endif
+
+#include "gc/gc_11_0_0_offset.h"
+#include "gc/gc_11_0_0_sh_mask.h"
 
 #include "modules/inc/mod_freesync.h"
 #include "modules/power/power_helpers.h"
@@ -119,6 +122,11 @@ MODULE_FIRMWARE(FIRMWARE_DCN_315_DMUB);
 #define FIRMWARE_DCN316_DMUB "amdgpu/dcn_3_1_6_dmcub.bin"
 MODULE_FIRMWARE(FIRMWARE_DCN316_DMUB);
 
+#define FIRMWARE_DCN_V3_2_0_DMCUB "amdgpu/dcn_3_2_0_dmcub.bin"
+MODULE_FIRMWARE(FIRMWARE_DCN_V3_2_0_DMCUB);
+#define FIRMWARE_DCN_V3_2_1_DMCUB "amdgpu/dcn_3_2_1_dmcub.bin"
+MODULE_FIRMWARE(FIRMWARE_DCN_V3_2_1_DMCUB);
+
 #define FIRMWARE_RAVEN_DMCU            "amdgpu/raven_dmcu.bin"
 MODULE_FIRMWARE(FIRMWARE_RAVEN_DMCU);
 
@@ -603,7 +611,6 @@ 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)
 #if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
 /**
  * dm_dcn_vertical_interrupt0_high_irq() - Handles OTG Vertical interrupt0 for
@@ -771,7 +778,7 @@ static void dm_dmub_outbox1_low_irq(void *interrupt_params)
 
                do {
                        dc_stat_get_dmub_notification(adev->dm.dc, &notify);
-                       if (notify.type > ARRAY_SIZE(dm->dmub_thread_offload)) {
+                       if (notify.type >= ARRAY_SIZE(dm->dmub_thread_offload)) {
                                DRM_ERROR("DM: notify type %d invalid!", notify.type);
                                continue;
                        }
@@ -827,7 +834,6 @@ static void dm_dmub_outbox1_low_irq(void *interrupt_params)
        if (count > DMUB_TRACE_MAX_READ)
                DRM_DEBUG_DRIVER("Warning : count > DMUB_TRACE_MAX_READ");
 }
-#endif /* CONFIG_DRM_AMD_DC_DCN */
 
 static int dm_set_clockgating_state(void *handle,
                  enum amd_clockgating_state state)
@@ -1125,9 +1131,7 @@ static int dm_dmub_hw_init(struct amdgpu_device *adev)
        switch (adev->ip_versions[DCE_HWIP][0]) {
        case IP_VERSION(3, 1, 3): /* Only for this asic hw internal rev B0 */
                hw_params.dpia_supported = true;
-#if defined(CONFIG_DRM_AMD_DC_DCN)
                hw_params.disable_dpia = adev->dm.dc->debug.dpia_debug.bits.disable_dpia;
-#endif
                break;
        default:
                break;
@@ -1189,7 +1193,6 @@ static void dm_dmub_hw_resume(struct amdgpu_device *adev)
        }
 }
 
-#if defined(CONFIG_DRM_AMD_DC_DCN)
 static void mmhub_read_system_context(struct amdgpu_device *adev, struct dc_phy_addr_space_config *pa_config)
 {
        uint64_t pt_base;
@@ -1244,8 +1247,7 @@ static void mmhub_read_system_context(struct amdgpu_device *adev, struct dc_phy_
        pa_config->is_hvm_enabled = 0;
 
 }
-#endif
-#if defined(CONFIG_DRM_AMD_DC_DCN)
+
 static void vblank_control_worker(struct work_struct *work)
 {
        struct vblank_control_work *vblank_work =
@@ -1263,10 +1265,20 @@ static void vblank_control_worker(struct work_struct *work)
 
        DRM_DEBUG_KMS("Allow idle optimizations (MALL): %d\n", dm->active_vblank_irq_count == 0);
 
-       /* Control PSR based on vblank requirements from OS */
+       /*
+        * Control PSR based on vblank requirements from OS
+        *
+        * If panel supports PSR SU, there's no need to disable PSR when OS is
+        * submitting fast atomic commits (we infer this by whether the OS
+        * requests vblank events). Fast atomic commits will simply trigger a
+        * full-frame-update (FFU); a specific case of selective-update (SU)
+        * where the SU region is the full hactive*vactive region. See
+        * fill_dc_dirty_rects().
+        */
        if (vblank_work->stream && vblank_work->stream->link) {
                if (vblank_work->enable) {
-                       if (vblank_work->stream->link->psr_settings.psr_allow_active)
+                       if (vblank_work->stream->link->psr_settings.psr_version < DC_PSR_VERSION_SU_1 &&
+                           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 &&
@@ -1282,8 +1294,6 @@ static void vblank_control_worker(struct work_struct *work)
        kfree(vblank_work);
 }
 
-#endif
-
 static void dm_handle_hpd_rx_offload_work(struct work_struct *work)
 {
        struct hpd_rx_irq_offload_work *offload_work;
@@ -1410,9 +1420,7 @@ static int amdgpu_dm_init(struct amdgpu_device *adev)
 
        mutex_init(&adev->dm.dc_lock);
        mutex_init(&adev->dm.audio_lock);
-#if defined(CONFIG_DRM_AMD_DC_DCN)
        spin_lock_init(&adev->dm.vblank_lock);
-#endif
 
        if(amdgpu_dm_irq_init(adev)) {
                DRM_ERROR("amdgpu: failed to initialize DM IRQ support.\n");
@@ -1505,12 +1513,10 @@ static int amdgpu_dm_init(struct amdgpu_device *adev)
        if (amdgpu_dc_feature_mask & DC_EDP_NO_POWER_SEQUENCING)
                init_data.flags.edp_no_power_sequencing = true;
 
-#ifdef CONFIG_DRM_AMD_DC_DCN
        if (amdgpu_dc_feature_mask & DC_DISABLE_LTTPR_DP1_4A)
                init_data.flags.allow_lttpr_non_transparent_mode.bits.DP1_4A = true;
        if (amdgpu_dc_feature_mask & DC_DISABLE_LTTPR_DP2_0)
                init_data.flags.allow_lttpr_non_transparent_mode.bits.DP2_0 = true;
-#endif
 
        init_data.flags.seamless_boot_edp_requested = false;
 
@@ -1520,6 +1526,8 @@ static int amdgpu_dm_init(struct amdgpu_device *adev)
                DRM_INFO("Seamless boot condition check passed\n");
        }
 
+       init_data.flags.enable_mipi_converter_optimization = true;
+
        INIT_LIST_HEAD(&adev->dm.da_list);
        /* Display Core create. */
        adev->dm.dc = dc_create(&init_data);
@@ -1566,7 +1574,6 @@ static int amdgpu_dm_init(struct amdgpu_device *adev)
                goto error;
        }
 
-#if defined(CONFIG_DRM_AMD_DC_DCN)
        if ((adev->flags & AMD_IS_APU) && (adev->asic_type >= CHIP_CARRIZO)) {
                struct dc_phy_addr_space_config pa_config;
 
@@ -1575,7 +1582,6 @@ static int amdgpu_dm_init(struct amdgpu_device *adev)
                // Call the DC init_memory func
                dc_setup_system_context(adev->dm.dc, &pa_config);
        }
-#endif
 
        adev->dm.freesync_module = mod_freesync_create(adev->dm.dc);
        if (!adev->dm.freesync_module) {
@@ -1587,14 +1593,12 @@ static int amdgpu_dm_init(struct amdgpu_device *adev)
 
        amdgpu_dm_init_color_mod();
 
-#if defined(CONFIG_DRM_AMD_DC_DCN)
        if (adev->dm.dc->caps.max_links > 0) {
                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");
        }
-#endif
 
 #ifdef CONFIG_DRM_AMD_DC_HDCP
        if (adev->dm.dc->caps.max_links > 0 && adev->family >= AMDGPU_FAMILY_RV) {
@@ -1626,7 +1630,6 @@ static int amdgpu_dm_init(struct amdgpu_device *adev)
                }
 
                amdgpu_dm_outbox_init(adev);
-#if defined(CONFIG_DRM_AMD_DC_DCN)
                if (!register_dmub_notify_callback(adev, DMUB_NOTIFICATION_AUX_REPLY,
                        dmub_aux_setconfig_callback, false)) {
                        DRM_ERROR("amdgpu: fail to register dmub aux callback");
@@ -1640,7 +1643,6 @@ static int amdgpu_dm_init(struct amdgpu_device *adev)
                        DRM_ERROR("amdgpu: fail to register dmub hpd callback");
                        goto error;
                }
-#endif /* CONFIG_DRM_AMD_DC_DCN */
        }
 
        if (amdgpu_dm_initialize_drm_device(adev)) {
@@ -1687,12 +1689,10 @@ 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);
@@ -1822,6 +1822,8 @@ static int load_dmcu_fw(struct amdgpu_device *adev)
                case IP_VERSION(3, 1, 3):
                case IP_VERSION(3, 1, 5):
                case IP_VERSION(3, 1, 6):
+               case IP_VERSION(3, 2, 0):
+               case IP_VERSION(3, 2, 1):
                        return 0;
                default:
                        break;
@@ -1945,6 +1947,14 @@ static int dm_dmub_sw_init(struct amdgpu_device *adev)
                dmub_asic = DMUB_ASIC_DCN316;
                fw_name_dmub = FIRMWARE_DCN316_DMUB;
                break;
+       case IP_VERSION(3, 2, 0):
+               dmub_asic = DMUB_ASIC_DCN32;
+               fw_name_dmub = FIRMWARE_DCN_V3_2_0_DMCUB;
+               break;
+       case IP_VERSION(3, 2, 1):
+               dmub_asic = DMUB_ASIC_DCN321;
+               fw_name_dmub = FIRMWARE_DCN_V3_2_1_DMCUB;
+               break;
        default:
                /* ASIC doesn't support DMUB. */
                return 0;
@@ -2403,9 +2413,7 @@ static int dm_suspend(void *handle)
        if (amdgpu_in_reset(adev)) {
                mutex_lock(&dm->dc_lock);
 
-#if defined(CONFIG_DRM_AMD_DC_DCN)
                dc_allow_idle_optimizations(adev->dm.dc, false);
-#endif
 
                dm->cached_dc_state = dc_copy_state(dm->dc->current_state);
 
@@ -3558,7 +3566,6 @@ static int dce110_register_irq_handlers(struct amdgpu_device *adev)
        return 0;
 }
 
-#if defined(CONFIG_DRM_AMD_DC_DCN)
 /* Register IRQ sources and initialize IRQ callbacks */
 static int dcn10_register_irq_handlers(struct amdgpu_device *adev)
 {
@@ -3747,7 +3754,6 @@ static int register_outbox_irq_handlers(struct amdgpu_device *adev)
 
        return 0;
 }
-#endif
 
 /*
  * Acquires the lock for the atomic state object and returns
@@ -4251,7 +4257,6 @@ static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev)
                        goto fail;
                }
 
-#if defined(CONFIG_DRM_AMD_DC_DCN)
        /* Use Outbox interrupt */
        switch (adev->ip_versions[DCE_HWIP][0]) {
        case IP_VERSION(3, 0, 0):
@@ -4259,6 +4264,8 @@ static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev)
        case IP_VERSION(3, 1, 3):
        case IP_VERSION(3, 1, 5):
        case IP_VERSION(3, 1, 6):
+       case IP_VERSION(3, 2, 0):
+       case IP_VERSION(3, 2, 1):
        case IP_VERSION(2, 1, 0):
                if (register_outbox_irq_handlers(dm->adev)) {
                        DRM_ERROR("DM: Failed to initialize IRQ\n");
@@ -4277,6 +4284,8 @@ static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev)
                case IP_VERSION(3, 1, 3):
                case IP_VERSION(3, 1, 5):
                case IP_VERSION(3, 1, 6):
+               case IP_VERSION(3, 2, 0):
+               case IP_VERSION(3, 2, 1):
                        psr_feature_enabled = true;
                        break;
                default:
@@ -4284,7 +4293,6 @@ static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev)
                        break;
                }
        }
-#endif
 
        /* Disable vblank IRQs aggressively for power-saving. */
        adev_to_drm(adev)->vblank_disable_immediate = true;
@@ -4380,7 +4388,6 @@ static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev)
                }
                break;
        default:
-#if defined(CONFIG_DRM_AMD_DC_DCN)
                switch (adev->ip_versions[DCE_HWIP][0]) {
                case IP_VERSION(1, 0, 0):
                case IP_VERSION(1, 0, 1):
@@ -4396,6 +4403,8 @@ static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev)
                case IP_VERSION(3, 1, 3):
                case IP_VERSION(3, 1, 5):
                case IP_VERSION(3, 1, 6):
+               case IP_VERSION(3, 2, 0):
+               case IP_VERSION(3, 2, 1):
                        if (dcn10_register_irq_handlers(dm->adev)) {
                                DRM_ERROR("DM: Failed to initialize IRQ\n");
                                goto fail;
@@ -4406,7 +4415,6 @@ static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev)
                                        adev->ip_versions[DCE_HWIP][0]);
                        goto fail;
                }
-#endif
                break;
        }
 
@@ -4555,7 +4563,7 @@ static int dm_early_init(void *handle)
                adev->mode_info.num_dig = 6;
                break;
        default:
-#if defined(CONFIG_DRM_AMD_DC_DCN)
+
                switch (adev->ip_versions[DCE_HWIP][0]) {
                case IP_VERSION(2, 0, 2):
                case IP_VERSION(3, 0, 0):
@@ -4583,6 +4591,8 @@ static int dm_early_init(void *handle)
                case IP_VERSION(3, 1, 3):
                case IP_VERSION(3, 1, 5):
                case IP_VERSION(3, 1, 6):
+               case IP_VERSION(3, 2, 0):
+               case IP_VERSION(3, 2, 1):
                        adev->mode_info.num_crtc = 4;
                        adev->mode_info.num_hpd = 4;
                        adev->mode_info.num_dig = 4;
@@ -4592,7 +4602,6 @@ static int dm_early_init(void *handle)
                                        adev->ip_versions[DCE_HWIP][0]);
                        return -EINVAL;
                }
-#endif
                break;
        }
 
@@ -4893,7 +4902,9 @@ fill_gfx9_tiling_info_from_modifier(const struct amdgpu_device *adev,
        unsigned int mod_bank_xor_bits = AMD_FMT_MOD_GET(BANK_XOR_BITS, modifier);
        unsigned int mod_pipe_xor_bits = AMD_FMT_MOD_GET(PIPE_XOR_BITS, modifier);
        unsigned int pkrs_log2 = AMD_FMT_MOD_GET(PACKERS, modifier);
-       unsigned int pipes_log2 = min(4u, mod_pipe_xor_bits);
+       unsigned int pipes_log2;
+
+       pipes_log2 = min(5u, mod_pipe_xor_bits);
 
        fill_gfx9_tiling_info_from_device(adev, tiling_info);
 
@@ -5229,8 +5240,69 @@ add_gfx10_3_modifiers(const struct amdgpu_device *adev,
                    AMD_FMT_MOD_SET(TILE_VERSION, AMD_FMT_MOD_TILE_VER_GFX9));
 }
 
+static void
+add_gfx11_modifiers(struct amdgpu_device *adev,
+                     uint64_t **mods, uint64_t *size, uint64_t *capacity)
+{
+       int num_pipes = 0;
+       int pipe_xor_bits = 0;
+       int num_pkrs = 0;
+       int pkrs = 0;
+       u32 gb_addr_config;
+       unsigned swizzle_r_x;
+       uint64_t modifier_r_x;
+       uint64_t modifier_dcc_best;
+       uint64_t modifier_dcc_4k;
+
+       /* TODO: GFX11 IP HW init hasnt finish and we get zero if we read from
+        * adev->gfx.config.gb_addr_config_fields.num_{pkrs,pipes} */
+       gb_addr_config = RREG32_SOC15(GC, 0, regGB_ADDR_CONFIG);
+       ASSERT(gb_addr_config != 0);
+
+       num_pkrs = 1 << REG_GET_FIELD(gb_addr_config, GB_ADDR_CONFIG, NUM_PKRS);
+       pkrs = ilog2(num_pkrs);
+       num_pipes = 1 << REG_GET_FIELD(gb_addr_config, GB_ADDR_CONFIG, NUM_PIPES);
+       pipe_xor_bits = ilog2(num_pipes);
+
+       /* R_X swizzle modes are the best for rendering and DCC requires them. */
+       swizzle_r_x = num_pipes > 16 ? AMD_FMT_MOD_TILE_GFX11_256K_R_X :
+                                              AMD_FMT_MOD_TILE_GFX9_64K_R_X;
+
+       modifier_r_x = AMD_FMT_MOD |
+               AMD_FMT_MOD_SET(TILE_VERSION, AMD_FMT_MOD_TILE_VER_GFX11) |
+               AMD_FMT_MOD_SET(TILE, swizzle_r_x) |
+               AMD_FMT_MOD_SET(PIPE_XOR_BITS, pipe_xor_bits) |
+               AMD_FMT_MOD_SET(PACKERS, pkrs);
+
+       /* DCC_CONSTANT_ENCODE is not set because it can't vary with gfx11 (it's implied to be 1). */
+       modifier_dcc_best = modifier_r_x |
+               AMD_FMT_MOD_SET(DCC, 1) |
+               AMD_FMT_MOD_SET(DCC_INDEPENDENT_64B, 0) |
+               AMD_FMT_MOD_SET(DCC_INDEPENDENT_128B, 1) |
+               AMD_FMT_MOD_SET(DCC_MAX_COMPRESSED_BLOCK, AMD_FMT_MOD_DCC_BLOCK_128B);
+
+       /* DCC settings for 4K and greater resolutions. (required by display hw) */
+       modifier_dcc_4k = modifier_r_x |
+                       AMD_FMT_MOD_SET(DCC, 1) |
+                       AMD_FMT_MOD_SET(DCC_INDEPENDENT_64B, 1) |
+                       AMD_FMT_MOD_SET(DCC_INDEPENDENT_128B, 1) |
+                       AMD_FMT_MOD_SET(DCC_MAX_COMPRESSED_BLOCK, AMD_FMT_MOD_DCC_BLOCK_64B);
+
+       add_modifier(mods, size, capacity, modifier_dcc_best);
+       add_modifier(mods, size, capacity, modifier_dcc_4k);
+
+       add_modifier(mods, size, capacity, modifier_dcc_best | AMD_FMT_MOD_SET(DCC_RETILE, 1));
+       add_modifier(mods, size, capacity, modifier_dcc_4k | AMD_FMT_MOD_SET(DCC_RETILE, 1));
+
+       add_modifier(mods, size, capacity, modifier_r_x);
+
+       add_modifier(mods, size, capacity, AMD_FMT_MOD |
+             AMD_FMT_MOD_SET(TILE_VERSION, AMD_FMT_MOD_TILE_VER_GFX11) |
+                        AMD_FMT_MOD_SET(TILE, AMD_FMT_MOD_TILE_GFX9_64K_D));
+}
+
 static int
-get_plane_modifiers(const struct amdgpu_device *adev, unsigned int plane_type, uint64_t **mods)
+get_plane_modifiers(struct amdgpu_device *adev, unsigned int plane_type, uint64_t **mods)
 {
        uint64_t size = 0, capacity = 128;
        *mods = NULL;
@@ -5262,6 +5334,9 @@ get_plane_modifiers(const struct amdgpu_device *adev, unsigned int plane_type, u
                else
                        add_gfx10_1_modifiers(adev, mods, &size, &capacity);
                break;
+       case AMDGPU_FAMILY_GC_11_0_0:
+               add_gfx11_modifiers(adev, mods, &size, &capacity);
+               break;
        }
 
        add_modifier(mods, &size, &capacity, DRM_FORMAT_MOD_LINEAR);
@@ -5300,7 +5375,7 @@ fill_gfx9_plane_attributes_from_modifiers(struct amdgpu_device *adev,
                dcc->enable = 1;
                dcc->meta_pitch = afb->base.pitches[1];
                dcc->independent_64b_blks = independent_64b_blks;
-               if (AMD_FMT_MOD_GET(TILE_VERSION, modifier) == AMD_FMT_MOD_TILE_VER_GFX10_RBPLUS) {
+               if (AMD_FMT_MOD_GET(TILE_VERSION, modifier) >= AMD_FMT_MOD_TILE_VER_GFX10_RBPLUS) {
                        if (independent_64b_blks && independent_128b_blks)
                                dcc->dcc_ind_blk = hubp_ind_block_64b_no_128bcl;
                        else if (independent_128b_blks)
@@ -5411,17 +5486,19 @@ fill_plane_buffer_attributes(struct amdgpu_device *adev,
 
 static void
 fill_blending_from_plane_state(const struct drm_plane_state *plane_state,
-                              bool *per_pixel_alpha, bool *global_alpha,
-                              int *global_alpha_value)
+                              bool *per_pixel_alpha, bool *pre_multiplied_alpha,
+                              bool *global_alpha, int *global_alpha_value)
 {
        *per_pixel_alpha = false;
+       *pre_multiplied_alpha = true;
        *global_alpha = false;
        *global_alpha_value = 0xff;
 
        if (plane_state->plane->type != DRM_PLANE_TYPE_OVERLAY)
                return;
 
-       if (plane_state->pixel_blend_mode == DRM_MODE_BLEND_PREMULTI) {
+       if (plane_state->pixel_blend_mode == DRM_MODE_BLEND_PREMULTI ||
+               plane_state->pixel_blend_mode == DRM_MODE_BLEND_COVERAGE) {
                static const uint32_t alpha_formats[] = {
                        DRM_FORMAT_ARGB8888,
                        DRM_FORMAT_RGBA8888,
@@ -5436,6 +5513,9 @@ fill_blending_from_plane_state(const struct drm_plane_state *plane_state,
                                break;
                        }
                }
+
+               if (per_pixel_alpha && plane_state->pixel_blend_mode == DRM_MODE_BLEND_COVERAGE)
+                       *pre_multiplied_alpha = false;
        }
 
        if (plane_state->alpha < 0xffff) {
@@ -5598,7 +5678,7 @@ fill_dc_plane_info_and_addr(struct amdgpu_device *adev,
                return ret;
 
        fill_blending_from_plane_state(
-               plane_state, &plane_info->per_pixel_alpha,
+               plane_state, &plane_info->per_pixel_alpha, &plane_info->pre_multiplied_alpha,
                &plane_info->global_alpha, &plane_info->global_alpha_value);
 
        return 0;
@@ -5645,6 +5725,7 @@ static int fill_dc_plane_attributes(struct amdgpu_device *adev,
        dc_plane_state->tiling_info = plane_info.tiling_info;
        dc_plane_state->visible = plane_info.visible;
        dc_plane_state->per_pixel_alpha = plane_info.per_pixel_alpha;
+       dc_plane_state->pre_multiplied_alpha = plane_info.pre_multiplied_alpha;
        dc_plane_state->global_alpha = plane_info.global_alpha;
        dc_plane_state->global_alpha_value = plane_info.global_alpha_value;
        dc_plane_state->dcc = plane_info.dcc;
@@ -5662,6 +5743,117 @@ static int fill_dc_plane_attributes(struct amdgpu_device *adev,
        return 0;
 }
 
+/**
+ * fill_dc_dirty_rects() - Fill DC dirty regions for PSR selective updates
+ *
+ * @plane: DRM plane containing dirty regions that need to be flushed to the eDP
+ *         remote fb
+ * @old_plane_state: Old state of @plane
+ * @new_plane_state: New state of @plane
+ * @crtc_state: New state of CRTC connected to the @plane
+ * @flip_addrs: DC flip tracking struct, which also tracts dirty rects
+ *
+ * For PSR SU, DC informs the DMUB uController of dirty rectangle regions
+ * (referred to as "damage clips" in DRM nomenclature) that require updating on
+ * the eDP remote buffer. The responsibility of specifying the dirty regions is
+ * amdgpu_dm's.
+ *
+ * A damage-aware DRM client should fill the FB_DAMAGE_CLIPS property on the
+ * plane with regions that require flushing to the eDP remote buffer. In
+ * addition, certain use cases - such as cursor and multi-plane overlay (MPO) -
+ * implicitly provide damage clips without any client support via the plane
+ * bounds.
+ *
+ * Today, amdgpu_dm only supports the MPO and cursor usecase.
+ *
+ * TODO: Also enable for FB_DAMAGE_CLIPS
+ */
+static void fill_dc_dirty_rects(struct drm_plane *plane,
+                               struct drm_plane_state *old_plane_state,
+                               struct drm_plane_state *new_plane_state,
+                               struct drm_crtc_state *crtc_state,
+                               struct dc_flip_addrs *flip_addrs)
+{
+       struct dm_crtc_state *dm_crtc_state = to_dm_crtc_state(crtc_state);
+       struct rect *dirty_rects = flip_addrs->dirty_rects;
+       uint32_t num_clips;
+       bool bb_changed;
+       bool fb_changed;
+       uint32_t i = 0;
+
+       flip_addrs->dirty_rect_count = 0;
+
+       /*
+        * Cursor plane has it's own dirty rect update interface. See
+        * dcn10_dmub_update_cursor_data and dmub_cmd_update_cursor_info_data
+        */
+       if (plane->type == DRM_PLANE_TYPE_CURSOR)
+               return;
+
+       /*
+        * Today, we only consider MPO use-case for PSR SU. If MPO not
+        * requested, and there is a plane update, do FFU.
+        */
+       if (!dm_crtc_state->mpo_requested) {
+               dirty_rects[0].x = 0;
+               dirty_rects[0].y = 0;
+               dirty_rects[0].width = dm_crtc_state->base.mode.crtc_hdisplay;
+               dirty_rects[0].height = dm_crtc_state->base.mode.crtc_vdisplay;
+               flip_addrs->dirty_rect_count = 1;
+               DRM_DEBUG_DRIVER("[PLANE:%d] PSR FFU dirty rect size (%d, %d)\n",
+                                new_plane_state->plane->base.id,
+                                dm_crtc_state->base.mode.crtc_hdisplay,
+                                dm_crtc_state->base.mode.crtc_vdisplay);
+               return;
+       }
+
+       /*
+        * MPO is requested. Add entire plane bounding box to dirty rects if
+        * flipped to or damaged.
+        *
+        * If plane is moved or resized, also add old bounding box to dirty
+        * rects.
+        */
+       num_clips = drm_plane_get_damage_clips_count(new_plane_state);
+       fb_changed = old_plane_state->fb->base.id !=
+                    new_plane_state->fb->base.id;
+       bb_changed = (old_plane_state->crtc_x != new_plane_state->crtc_x ||
+                     old_plane_state->crtc_y != new_plane_state->crtc_y ||
+                     old_plane_state->crtc_w != new_plane_state->crtc_w ||
+                     old_plane_state->crtc_h != new_plane_state->crtc_h);
+
+       DRM_DEBUG_DRIVER("[PLANE:%d] PSR bb_changed:%d fb_changed:%d num_clips:%d\n",
+                        new_plane_state->plane->base.id,
+                        bb_changed, fb_changed, num_clips);
+
+       if (num_clips || fb_changed || bb_changed) {
+               dirty_rects[i].x = new_plane_state->crtc_x;
+               dirty_rects[i].y = new_plane_state->crtc_y;
+               dirty_rects[i].width = new_plane_state->crtc_w;
+               dirty_rects[i].height = new_plane_state->crtc_h;
+               DRM_DEBUG_DRIVER("[PLANE:%d] PSR SU dirty rect at (%d, %d) size (%d, %d)\n",
+                                new_plane_state->plane->base.id,
+                                dirty_rects[i].x, dirty_rects[i].y,
+                                dirty_rects[i].width, dirty_rects[i].height);
+               i += 1;
+       }
+
+       /* Add old plane bounding-box if plane is moved or resized */
+       if (bb_changed) {
+               dirty_rects[i].x = old_plane_state->crtc_x;
+               dirty_rects[i].y = old_plane_state->crtc_y;
+               dirty_rects[i].width = old_plane_state->crtc_w;
+               dirty_rects[i].height = old_plane_state->crtc_h;
+               DRM_DEBUG_DRIVER("[PLANE:%d] PSR SU dirty rect at (%d, %d) size (%d, %d)\n",
+                               old_plane_state->plane->base.id,
+                               dirty_rects[i].x, dirty_rects[i].y,
+                               dirty_rects[i].width, dirty_rects[i].height);
+               i += 1;
+       }
+
+       flip_addrs->dirty_rect_count = i;
+}
+
 static void update_stream_scaling_settings(const struct drm_display_mode *mode,
                                           const struct dm_connector_state *dm_state,
                                           struct dc_stream_state *stream)
@@ -6610,6 +6802,7 @@ dm_crtc_duplicate_state(struct drm_crtc *crtc)
        state->cm_has_degamma = cur->cm_has_degamma;
        state->cm_is_degamma_srgb = cur->cm_is_degamma_srgb;
        state->force_dpms_off = cur->force_dpms_off;
+       state->mpo_requested = cur->mpo_requested;
        /* TODO Duplicate dc_stream after objects are stream object is flattened */
 
        return &state->base;
@@ -6646,10 +6839,8 @@ static inline int dm_set_vblank(struct drm_crtc *crtc, bool enable)
        struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc);
        struct amdgpu_device *adev = drm_to_adev(crtc->dev);
        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;
        struct vblank_control_work *work;
-#endif
        int rc = 0;
 
        if (enable) {
@@ -6672,7 +6863,6 @@ static inline int dm_set_vblank(struct drm_crtc *crtc, bool enable)
        if (amdgpu_in_reset(adev))
                return 0;
 
-#if defined(CONFIG_DRM_AMD_DC_DCN)
        if (dm->vblank_control_workqueue) {
                work = kzalloc(sizeof(*work), GFP_ATOMIC);
                if (!work)
@@ -6690,7 +6880,6 @@ static inline int dm_set_vblank(struct drm_crtc *crtc, bool enable)
 
                queue_work(dm->vblank_control_workqueue, &work->work);
        }
-#endif
 
        return 0;
 }
@@ -6705,7 +6894,7 @@ static void dm_disable_vblank(struct drm_crtc *crtc)
        dm_set_vblank(crtc, false);
 }
 
-/* Implemented only the options currently availible for the driver */
+/* Implemented only the options currently available for the driver */
 static const struct drm_crtc_funcs amdgpu_dm_crtc_funcs = {
        .reset = dm_crtc_reset_state,
        .destroy = amdgpu_dm_crtc_destroy,
@@ -7583,9 +7772,6 @@ static int dm_plane_helper_prepare_fb(struct drm_plane *plane,
        struct amdgpu_device *adev;
        struct amdgpu_bo *rbo;
        struct dm_plane_state *dm_plane_state_new, *dm_plane_state_old;
-       struct list_head list;
-       struct ttm_validate_buffer tv;
-       struct ww_acquire_ctx ticket;
        uint32_t domain;
        int r;
 
@@ -7598,18 +7784,19 @@ static int dm_plane_helper_prepare_fb(struct drm_plane *plane,
        obj = new_state->fb->obj[0];
        rbo = gem_to_amdgpu_bo(obj);
        adev = amdgpu_ttm_adev(rbo->tbo.bdev);
-       INIT_LIST_HEAD(&list);
-
-       tv.bo = &rbo->tbo;
-       tv.num_shared = 1;
-       list_add(&tv.head, &list);
 
-       r = ttm_eu_reserve_buffers(&ticket, &list, false, NULL);
+       r = amdgpu_bo_reserve(rbo, true);
        if (r) {
                dev_err(adev->dev, "fail to reserve bo (%d)\n", r);
                return r;
        }
 
+       r = dma_resv_reserve_fences(rbo->tbo.base.resv, 1);
+       if (r) {
+               dev_err(adev->dev, "reserving fence slot failed (%d)\n", r);
+               goto error_unlock;
+       }
+
        if (plane->type != DRM_PLANE_TYPE_CURSOR)
                domain = amdgpu_display_supported_domains(adev, rbo->flags);
        else
@@ -7619,19 +7806,16 @@ static int dm_plane_helper_prepare_fb(struct drm_plane *plane,
        if (unlikely(r != 0)) {
                if (r != -ERESTARTSYS)
                        DRM_ERROR("Failed to pin framebuffer with error %d\n", r);
-               ttm_eu_backoff_reservation(&ticket, &list);
-               return r;
+               goto error_unlock;
        }
 
        r = amdgpu_ttm_alloc_gart(&rbo->tbo);
        if (unlikely(r != 0)) {
-               amdgpu_bo_unpin(rbo);
-               ttm_eu_backoff_reservation(&ticket, &list);
                DRM_ERROR("%p bind failed\n", rbo);
-               return r;
+               goto error_unpin;
        }
 
-       ttm_eu_backoff_reservation(&ticket, &list);
+       amdgpu_bo_unreserve(rbo);
 
        afb->address = amdgpu_bo_gpu_offset(rbo);
 
@@ -7663,6 +7847,13 @@ static int dm_plane_helper_prepare_fb(struct drm_plane *plane,
        }
 
        return 0;
+
+error_unpin:
+       amdgpu_bo_unpin(rbo);
+
+error_unlock:
+       amdgpu_bo_unreserve(rbo);
+       return r;
 }
 
 static void dm_plane_helper_cleanup_fb(struct drm_plane *plane,
@@ -7943,7 +8134,8 @@ static int amdgpu_dm_plane_init(struct amdgpu_display_manager *dm,
        if (plane->type == DRM_PLANE_TYPE_OVERLAY &&
            plane_cap && plane_cap->per_pixel_alpha) {
                unsigned int blend_caps = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
-                                         BIT(DRM_MODE_BLEND_PREMULTI);
+                                         BIT(DRM_MODE_BLEND_PREMULTI) |
+                                         BIT(DRM_MODE_BLEND_COVERAGE);
 
                drm_plane_create_alpha_property(plane);
                drm_plane_create_blend_mode_property(plane, blend_caps);
@@ -9238,7 +9430,8 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
                 * deadlock during GPU reset when this fence will not signal
                 * but we hold reservation lock for the BO.
                 */
-               r = dma_resv_wait_timeout(abo->tbo.base.resv, true, false,
+               r = dma_resv_wait_timeout(abo->tbo.base.resv,
+                                         DMA_RESV_USAGE_WRITE, false,
                                          msecs_to_jiffies(5000));
                if (unlikely(r <= 0))
                        DRM_ERROR("Waiting for fences timed out!");
@@ -9250,13 +9443,17 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
                        &bundle->flip_addrs[planes_count].address,
                        afb->tmz_surface, false);
 
-               DRM_DEBUG_ATOMIC("plane: id=%d dcc_en=%d\n",
+               drm_dbg_state(state->dev, "plane: id=%d dcc_en=%d\n",
                                 new_plane_state->plane->index,
                                 bundle->plane_infos[planes_count].dcc.enable);
 
                bundle->surface_updates[planes_count].plane_info =
                        &bundle->plane_infos[planes_count];
 
+               fill_dc_dirty_rects(plane, old_plane_state, new_plane_state,
+                                   new_crtc_state,
+                                   &bundle->flip_addrs[planes_count]);
+
                /*
                 * Only allow immediate flips for fast updates that don't
                 * change FB pitch, DCC state, rotation or mirroing.
@@ -9284,7 +9481,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_ATOMIC("%s Flipping to hi: 0x%x, low: 0x%x\n",
+               drm_dbg_state(state->dev, "%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);
@@ -9365,14 +9562,12 @@ 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.
                 */
                if (dm->vblank_control_workqueue)
                        flush_workqueue(dm->vblank_control_workqueue);
-#endif
 
                bundle->stream_update.stream = acrtc_state->stream;
                if (new_pcrtc_state->mode_changed) {
@@ -9454,6 +9649,18 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
 
                        /* Allow PSR when skip count is 0. */
                        acrtc_attach->dm_irq_params.allow_psr_entry = !aconn->psr_skip_count;
+
+                       /*
+                        * If sink supports PSR SU, there is no need to rely on
+                        * a vblank event disable request to enable PSR. PSR SU
+                        * can be enabled immediately once OS demonstrates an
+                        * adequate number of fast atomic commits to notify KMD
+                        * of update events. See `vblank_control_worker()`.
+                        */
+                       if (acrtc_state->stream->link->psr_settings.psr_version >= DC_PSR_VERSION_SU_1 &&
+                           acrtc_attach->dm_irq_params.allow_psr_entry &&
+                           !acrtc_state->stream->link->psr_settings.psr_allow_active)
+                               amdgpu_dm_psr_enable(acrtc_state->stream);
                } else {
                        acrtc_attach->dm_irq_params.allow_psr_entry = false;
                }
@@ -9626,7 +9833,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_ATOMIC(
+               drm_dbg_state(state->dev,
                        "amdgpu_crtc id:%d crtc_state_flags: enable:%d, active:%d, "
                        "planes_changed:%d, mode_changed:%d,active_changed:%d,"
                        "connectors_changed:%d\n",
@@ -9705,21 +9912,19 @@ 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 defined(CONFIG_DRM_AMD_DC_DCN)
                        if (dm->vblank_control_workqueue)
                                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);
                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
+
+               /* 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);
                mutex_unlock(&dm->dc_lock);
        }
 
@@ -10330,7 +10535,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_ATOMIC(
+       drm_dbg_state(state->dev,
                "amdgpu_crtc id:%d crtc_state_flags: enable:%d, active:%d, "
                "planes_changed:%d, mode_changed:%d,active_changed:%d,"
                "connectors_changed:%d\n",