--- /dev/null
+From 74e920410b79e1694a692f5daa73617a0e8b8fe5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 27 Jul 2023 10:22:20 -0500
+Subject: drm/amd: Disable S/G for APUs when 64GB or more host memory
+
+From: Mario Limonciello <mario.limonciello@amd.com>
+
+[ Upstream commit 70e64c4d522b732e31c6475a3be2349de337d321 ]
+
+Users report a white flickering screen on multiple systems that
+is tied to having 64GB or more memory. When S/G is enabled pages
+will get pinned to both VRAM carve out and system RAM leading to
+this.
+
+Until it can be fixed properly, disable S/G when 64GB of memory or
+more is detected. This will force pages to be pinned into VRAM.
+This should fix white screen flickers but if VRAM pressure is
+encountered may lead to black screens. It's a trade-off for now.
+
+Fixes: 81d0bcf99009 ("drm/amdgpu: make display pinning more flexible (v2)")
+Cc: Hamza Mahfooz <Hamza.Mahfooz@amd.com>
+Cc: Roman Li <roman.li@amd.com>
+Cc: <stable@vger.kernel.org> # 6.1.y: bf0207e172703 ("drm/amdgpu: add S/G display parameter")
+Cc: <stable@vger.kernel.org> # 6.4.y
+Link: https://gitlab.freedesktop.org/drm/amd/-/issues/2735
+Link: https://gitlab.freedesktop.org/drm/amd/-/issues/2354
+Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
+Reviewed-by: Alex Deucher <alexander.deucher@amd.com>
+Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/amd/amdgpu/amdgpu.h | 1 +
+ drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 26 +++++++++++++++++++
+ .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 5 ++--
+ 3 files changed, 29 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
+index a3b86b86dc477..6dc950c1b6893 100644
+--- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h
++++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
+@@ -1296,6 +1296,7 @@ int amdgpu_device_gpu_recover(struct amdgpu_device *adev,
+ void amdgpu_device_pci_config_reset(struct amdgpu_device *adev);
+ int amdgpu_device_pci_reset(struct amdgpu_device *adev);
+ bool amdgpu_device_need_post(struct amdgpu_device *adev);
++bool amdgpu_sg_display_supported(struct amdgpu_device *adev);
+ bool amdgpu_device_pcie_dynamic_switching_supported(void);
+ bool amdgpu_device_should_use_aspm(struct amdgpu_device *adev);
+ bool amdgpu_device_aspm_support_quirk(void);
+diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+index 2168dc92c6704..6e5e4603a51a1 100644
+--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
++++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+@@ -1461,6 +1461,32 @@ bool amdgpu_device_need_post(struct amdgpu_device *adev)
+ return true;
+ }
+
++/*
++ * On APUs with >= 64GB white flickering has been observed w/ SG enabled.
++ * Disable S/G on such systems until we have a proper fix.
++ * https://gitlab.freedesktop.org/drm/amd/-/issues/2354
++ * https://gitlab.freedesktop.org/drm/amd/-/issues/2735
++ */
++bool amdgpu_sg_display_supported(struct amdgpu_device *adev)
++{
++ switch (amdgpu_sg_display) {
++ case -1:
++ break;
++ case 0:
++ return false;
++ case 1:
++ return true;
++ default:
++ return false;
++ }
++ if ((totalram_pages() << (PAGE_SHIFT - 10)) +
++ (adev->gmc.real_vram_size / 1024) >= 64000000) {
++ DRM_WARN("Disabling S/G due to >=64GB RAM\n");
++ return false;
++ }
++ return true;
++}
++
+ /*
+ * Intel hosts such as Raptor Lake and Sapphire Rapids don't support dynamic
+ * speed switching. Until we have confirmation from Intel that a specific host
+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 e0d556cf919f7..8d09f262c14a3 100644
+--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+@@ -1642,9 +1642,8 @@ static int amdgpu_dm_init(struct amdgpu_device *adev)
+ }
+ break;
+ }
+- if (init_data.flags.gpu_vm_support &&
+- (amdgpu_sg_display == 0))
+- init_data.flags.gpu_vm_support = false;
++ if (init_data.flags.gpu_vm_support)
++ init_data.flags.gpu_vm_support = amdgpu_sg_display_supported(adev);
+
+ if (init_data.flags.gpu_vm_support)
+ adev->mode_info.gpu_vm_support = true;
+--
+2.40.1
+
--- /dev/null
+From 6a5907f12d546e261efd8c69c32da2546c0662d4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 24 Jul 2023 13:12:58 -0400
+Subject: drm/amd/display: fix a regression in blank pixel data caused by
+ coding mistake
+
+From: Wenjing Liu <wenjing.liu@amd.com>
+
+[ Upstream commit f77d1a49902bc70625e3d101a16d8a687f7e97db ]
+
+[why]
+There was unfortunately a coding mistake. It gets caught with an ultrawide monitor
+that requires ODM 4:1 combine. We are blanking or unblanking pixel data we
+are supposed to enumerate through all ODM pipes and program DPG for each
+of those pipes. However the coding mistake causes us to program only the
+first and last ODM pipes.
+
+Cc: Mario Limonciello <mario.limonciello@amd.com>
+Cc: Alex Deucher <alexander.deucher@amd.com>
+Cc: stable@vger.kernel.org
+Reviewed-by: Martin Leung <martin.leung@amd.com>
+Acked-by: Tom Chung <chiahsuan.chung@amd.com>
+Signed-off-by: Wenjing Liu <wenjing.liu@amd.com>
+Tested-by: Daniel Wheeler <daniel.wheeler@amd.com>
+Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hwseq.c | 2 +-
+ drivers/gpu/drm/amd/display/dc/link/accessories/link_dp_cts.c | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hwseq.c b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hwseq.c
+index 704d02d89fb34..62a077adcdbfa 100644
+--- a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hwseq.c
++++ b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hwseq.c
+@@ -1084,7 +1084,7 @@ void dcn20_blank_pixel_data(
+
+ while (odm_pipe->next_odm_pipe) {
+ dc->hwss.set_disp_pattern_generator(dc,
+- pipe_ctx,
++ odm_pipe,
+ test_pattern,
+ test_pattern_color_space,
+ stream->timing.display_color_depth,
+diff --git a/drivers/gpu/drm/amd/display/dc/link/accessories/link_dp_cts.c b/drivers/gpu/drm/amd/display/dc/link/accessories/link_dp_cts.c
+index bce0428ad6123..9fd68a11fad23 100644
+--- a/drivers/gpu/drm/amd/display/dc/link/accessories/link_dp_cts.c
++++ b/drivers/gpu/drm/amd/display/dc/link/accessories/link_dp_cts.c
+@@ -513,7 +513,7 @@ static void set_crtc_test_pattern(struct dc_link *link,
+ odm_opp = odm_pipe->stream_res.opp;
+ odm_opp->funcs->opp_program_bit_depth_reduction(odm_opp, ¶ms);
+ link->dc->hwss.set_disp_pattern_generator(link->dc,
+- pipe_ctx,
++ odm_pipe,
+ controller_test_pattern,
+ controller_color_space,
+ color_depth,
+--
+2.40.1
+
--- /dev/null
+From e4d6f4677ed285f7653440be8fc319afd2d60cb4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 13 Jul 2023 18:40:51 -0400
+Subject: drm/amd/display: Update DPG test pattern programming
+
+From: Wenjing Liu <wenjing.liu@amd.com>
+
+[ Upstream commit ad4455c614b27e6b24a4e6bd70114545c1660ff9 ]
+
+[Why]
+Last ODM slice could be slightly larger than other slice because it can be
+including the residual.
+
+[How]
+Update DPG pattern programming sequence to use a different width for
+last odm slice.
+
+Reviewed-by: Chris Park <chris.park@amd.com>
+Acked-by: Alex Hung <alex.hung@amd.com>
+Signed-off-by: Wenjing Liu <wenjing.liu@amd.com>
+Tested-by: Daniel Wheeler <daniel.wheeler@amd.com>
+Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
+Stable-dep-of: f77d1a49902b ("drm/amd/display: fix a regression in blank pixel data caused by coding mistake")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../drm/amd/display/dc/dcn20/dcn20_hwseq.c | 45 ++++----
+ .../display/dc/link/accessories/link_dp_cts.c | 107 +++++++++---------
+ 2 files changed, 78 insertions(+), 74 deletions(-)
+
+diff --git a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hwseq.c b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hwseq.c
+index 7c344132a0072..704d02d89fb34 100644
+--- a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hwseq.c
++++ b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hwseq.c
+@@ -1054,9 +1054,9 @@ void dcn20_blank_pixel_data(
+ enum controller_dp_color_space test_pattern_color_space = CONTROLLER_DP_COLOR_SPACE_UDEFINED;
+ struct pipe_ctx *odm_pipe;
+ int odm_cnt = 1;
+-
+- int width = stream->timing.h_addressable + stream->timing.h_border_left + stream->timing.h_border_right;
+- int height = stream->timing.v_addressable + stream->timing.v_border_bottom + stream->timing.v_border_top;
++ int h_active = stream->timing.h_addressable + stream->timing.h_border_left + stream->timing.h_border_right;
++ int v_active = stream->timing.v_addressable + stream->timing.v_border_bottom + stream->timing.v_border_top;
++ int odm_slice_width, last_odm_slice_width, offset = 0;
+
+ if (stream->link->test_pattern_enabled)
+ return;
+@@ -1066,8 +1066,8 @@ void dcn20_blank_pixel_data(
+
+ for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe)
+ odm_cnt++;
+-
+- width = width / odm_cnt;
++ odm_slice_width = h_active / odm_cnt;
++ last_odm_slice_width = h_active - odm_slice_width * (odm_cnt - 1);
+
+ if (blank) {
+ dc->hwss.set_abm_immediate_disable(pipe_ctx);
+@@ -1080,29 +1080,32 @@ void dcn20_blank_pixel_data(
+ test_pattern = CONTROLLER_DP_TEST_PATTERN_VIDEOMODE;
+ }
+
+- dc->hwss.set_disp_pattern_generator(dc,
+- pipe_ctx,
+- test_pattern,
+- test_pattern_color_space,
+- stream->timing.display_color_depth,
+- &black_color,
+- width,
+- height,
+- 0);
++ odm_pipe = pipe_ctx;
+
+- for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) {
++ while (odm_pipe->next_odm_pipe) {
+ dc->hwss.set_disp_pattern_generator(dc,
+- odm_pipe,
+- dc->debug.visual_confirm != VISUAL_CONFIRM_DISABLE && blank ?
+- CONTROLLER_DP_TEST_PATTERN_COLORRAMP : test_pattern,
++ pipe_ctx,
++ test_pattern,
+ test_pattern_color_space,
+ stream->timing.display_color_depth,
+ &black_color,
+- width,
+- height,
+- 0);
++ odm_slice_width,
++ v_active,
++ offset);
++ offset += odm_slice_width;
++ odm_pipe = odm_pipe->next_odm_pipe;
+ }
+
++ dc->hwss.set_disp_pattern_generator(dc,
++ odm_pipe,
++ test_pattern,
++ test_pattern_color_space,
++ stream->timing.display_color_depth,
++ &black_color,
++ last_odm_slice_width,
++ v_active,
++ offset);
++
+ if (!blank && dc->debug.enable_single_display_2to1_odm_policy) {
+ /* when exiting dynamic ODM need to reinit DPG state for unused pipes */
+ struct pipe_ctx *old_odm_pipe = dc->current_state->res_ctx.pipe_ctx[pipe_ctx->pipe_idx].next_odm_pipe;
+diff --git a/drivers/gpu/drm/amd/display/dc/link/accessories/link_dp_cts.c b/drivers/gpu/drm/amd/display/dc/link/accessories/link_dp_cts.c
+index db9f1baa27e5e..bce0428ad6123 100644
+--- a/drivers/gpu/drm/amd/display/dc/link/accessories/link_dp_cts.c
++++ b/drivers/gpu/drm/amd/display/dc/link/accessories/link_dp_cts.c
+@@ -428,15 +428,24 @@ static void set_crtc_test_pattern(struct dc_link *link,
+ stream->timing.display_color_depth;
+ struct bit_depth_reduction_params params;
+ struct output_pixel_processor *opp = pipe_ctx->stream_res.opp;
+- int width = pipe_ctx->stream->timing.h_addressable +
++ struct pipe_ctx *odm_pipe;
++ int odm_cnt = 1;
++ int h_active = pipe_ctx->stream->timing.h_addressable +
+ pipe_ctx->stream->timing.h_border_left +
+ pipe_ctx->stream->timing.h_border_right;
+- int height = pipe_ctx->stream->timing.v_addressable +
++ int v_active = pipe_ctx->stream->timing.v_addressable +
+ pipe_ctx->stream->timing.v_border_bottom +
+ pipe_ctx->stream->timing.v_border_top;
++ int odm_slice_width, last_odm_slice_width, offset = 0;
+
+ memset(¶ms, 0, sizeof(params));
+
++ for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe)
++ odm_cnt++;
++
++ odm_slice_width = h_active / odm_cnt;
++ last_odm_slice_width = h_active - odm_slice_width * (odm_cnt - 1);
++
+ switch (test_pattern) {
+ case DP_TEST_PATTERN_COLOR_SQUARES:
+ controller_test_pattern =
+@@ -473,16 +482,13 @@ static void set_crtc_test_pattern(struct dc_link *link,
+ {
+ /* disable bit depth reduction */
+ pipe_ctx->stream->bit_depth_params = params;
+- opp->funcs->opp_program_bit_depth_reduction(opp, ¶ms);
+- if (pipe_ctx->stream_res.tg->funcs->set_test_pattern)
++ if (pipe_ctx->stream_res.tg->funcs->set_test_pattern) {
++ opp->funcs->opp_program_bit_depth_reduction(opp, ¶ms);
+ pipe_ctx->stream_res.tg->funcs->set_test_pattern(pipe_ctx->stream_res.tg,
+ controller_test_pattern, color_depth);
+- else if (link->dc->hwss.set_disp_pattern_generator) {
+- struct pipe_ctx *odm_pipe;
++ } else if (link->dc->hwss.set_disp_pattern_generator) {
+ enum controller_dp_color_space controller_color_space;
+- int opp_cnt = 1;
+- int offset = 0;
+- int dpg_width = width;
++ struct output_pixel_processor *odm_opp;
+
+ switch (test_pattern_color_space) {
+ case DP_TEST_PATTERN_COLOR_SPACE_RGB:
+@@ -502,36 +508,33 @@ static void set_crtc_test_pattern(struct dc_link *link,
+ break;
+ }
+
+- for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe)
+- opp_cnt++;
+- dpg_width = width / opp_cnt;
+- offset = dpg_width;
+-
+- link->dc->hwss.set_disp_pattern_generator(link->dc,
+- pipe_ctx,
+- controller_test_pattern,
+- controller_color_space,
+- color_depth,
+- NULL,
+- dpg_width,
+- height,
+- 0);
+-
+- for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) {
+- struct output_pixel_processor *odm_opp = odm_pipe->stream_res.opp;
+-
++ odm_pipe = pipe_ctx;
++ while (odm_pipe->next_odm_pipe) {
++ odm_opp = odm_pipe->stream_res.opp;
+ odm_opp->funcs->opp_program_bit_depth_reduction(odm_opp, ¶ms);
+ link->dc->hwss.set_disp_pattern_generator(link->dc,
+- odm_pipe,
++ pipe_ctx,
+ controller_test_pattern,
+ controller_color_space,
+ color_depth,
+ NULL,
+- dpg_width,
+- height,
++ odm_slice_width,
++ v_active,
+ offset);
+- offset += offset;
++ offset += odm_slice_width;
++ odm_pipe = odm_pipe->next_odm_pipe;
+ }
++ odm_opp = odm_pipe->stream_res.opp;
++ odm_opp->funcs->opp_program_bit_depth_reduction(odm_opp, ¶ms);
++ link->dc->hwss.set_disp_pattern_generator(link->dc,
++ odm_pipe,
++ controller_test_pattern,
++ controller_color_space,
++ color_depth,
++ NULL,
++ last_odm_slice_width,
++ v_active,
++ offset);
+ }
+ }
+ break;
+@@ -540,23 +543,17 @@ static void set_crtc_test_pattern(struct dc_link *link,
+ /* restore bitdepth reduction */
+ resource_build_bit_depth_reduction_params(pipe_ctx->stream, ¶ms);
+ pipe_ctx->stream->bit_depth_params = params;
+- opp->funcs->opp_program_bit_depth_reduction(opp, ¶ms);
+- if (pipe_ctx->stream_res.tg->funcs->set_test_pattern)
++ if (pipe_ctx->stream_res.tg->funcs->set_test_pattern) {
++ opp->funcs->opp_program_bit_depth_reduction(opp, ¶ms);
+ pipe_ctx->stream_res.tg->funcs->set_test_pattern(pipe_ctx->stream_res.tg,
+- CONTROLLER_DP_TEST_PATTERN_VIDEOMODE,
+- color_depth);
+- else if (link->dc->hwss.set_disp_pattern_generator) {
+- struct pipe_ctx *odm_pipe;
+- int opp_cnt = 1;
+- int dpg_width;
+-
+- for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe)
+- opp_cnt++;
+-
+- dpg_width = width / opp_cnt;
+- for (odm_pipe = pipe_ctx->next_odm_pipe; odm_pipe; odm_pipe = odm_pipe->next_odm_pipe) {
+- struct output_pixel_processor *odm_opp = odm_pipe->stream_res.opp;
++ CONTROLLER_DP_TEST_PATTERN_VIDEOMODE,
++ color_depth);
++ } else if (link->dc->hwss.set_disp_pattern_generator) {
++ struct output_pixel_processor *odm_opp;
+
++ odm_pipe = pipe_ctx;
++ while (odm_pipe->next_odm_pipe) {
++ odm_opp = odm_pipe->stream_res.opp;
+ odm_opp->funcs->opp_program_bit_depth_reduction(odm_opp, ¶ms);
+ link->dc->hwss.set_disp_pattern_generator(link->dc,
+ odm_pipe,
+@@ -564,19 +561,23 @@ static void set_crtc_test_pattern(struct dc_link *link,
+ CONTROLLER_DP_COLOR_SPACE_UDEFINED,
+ color_depth,
+ NULL,
+- dpg_width,
+- height,
+- 0);
++ odm_slice_width,
++ v_active,
++ offset);
++ offset += odm_slice_width;
++ odm_pipe = odm_pipe->next_odm_pipe;
+ }
++ odm_opp = odm_pipe->stream_res.opp;
++ odm_opp->funcs->opp_program_bit_depth_reduction(odm_opp, ¶ms);
+ link->dc->hwss.set_disp_pattern_generator(link->dc,
+- pipe_ctx,
++ odm_pipe,
+ CONTROLLER_DP_TEST_PATTERN_VIDEOMODE,
+ CONTROLLER_DP_COLOR_SPACE_UDEFINED,
+ color_depth,
+ NULL,
+- dpg_width,
+- height,
+- 0);
++ last_odm_slice_width,
++ v_active,
++ offset);
+ }
+ }
+ break;
+--
+2.40.1
+