From: Richard Lane <richardlanedev@gmail.com>
To: amd-gfx@lists.freedesktop.org
Cc: "Alex Deucher" <alexander.deucher@amd.com>,
"Christian König" <christian.koenig@amd.com>,
"David Airlie" <airlied@gmail.com>,
"Simona Vetter" <simona@ffwll.ch>,
"Harry Wentland" <harry.wentland@amd.com>,
"Leo Li" <sunpeng.li@amd.com>,
"Rodrigo Siqueira" <siqueira@igalia.com>,
"Mario Limonciello" <mario.limonciello@amd.com>,
dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org,
"Richard Lane" <richardlanedev@gmail.com>
Subject: [PATCH v2 2/3] drm/amd/display: unlock secondary tile on Apple 5K, iMac panels
Date: Sun, 1 Nov 2026 23:24:45 +0000 [thread overview]
Message-ID: <94672259-c526-4ec7-a69c-7dda8d934593@gmail.com> (raw)
Apple 27" 5K iMacs (2014-2019, iMac15,1 through Polaris-based iMac19,1)
expose their internal 5120x2880 panel as a 2x1 tiled display over an eDP
root link and a secondary DP link. At boot, the panel TCON operates in
single-pipe mode and holds the secondary DP link's HPD line low until
unlocked via DPCD registers.
Enable native tiled detection and link training for DCE < 12 Apple 5K
panels:
- Point the secondary DP dc_link to the root eDP dc_link (apple_5k_peer)
during EDID quirk matching on DCE < 12 iMac15,1, iMac17,1, iMac18,3,
and Polaris-based iMac19,1 (checking connector_signal since panel IDs
can flip across the two links after S3 wake). DCE >= 12 support for
Vega-based iMac19,1, iMacPro1,1, and iMac20,x is documented as TODO.
- Avoid falling back to DVI and latch the secondary tile via DPCD 0x4F1
on the root eDP link when probing the secondary link, allowing the
secondary link to read its EDID and populate DRM's DisplayID tile
properties (has_tile, tile_group, and tile_is_single_monitor).
- Write the 3-byte Boot Camp source table (0x04, 0x1d, 0x03) to DPCD
0x310 in dpcd_set_source_specific_data() to unlock the secondary tile.
- Prevent the secondary tile from entering DP_SET_POWER D3, which causes
the TCON to drop HPD and stop responding to AUX transactions.
- Unlatch both tiles (0x4F1) and clear DP_SOURCE_TABLE_REVISION (0x310)
and DP_EDP_CONFIGURATION_SET (0x10A) in dm_suspend() so the TCON
returns to single-pipe mode for the EFI boot screen across reboots.
Signed-off-by: Richard Lane <richardlanedev@gmail.com>
---
.../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 19 ++++++++++++++++
.../amd/display/amdgpu_dm/amdgpu_dm_helpers.c | 22 +++++++++++++++++++
drivers/gpu/drm/amd/display/dc/dc.h | 1 +
.../drm/amd/display/dc/link/link_detection.c | 7 +++++-
.../dc/link/protocols/link_dp_capability.c | 4 ++++
.../display/dc/link/protocols/link_dp_phy.c | 3 +++
6 files changed, 55 insertions(+), 1 deletion(-)
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 bdabb4bf14da..74cd88d8d580 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -1607,6 +1607,23 @@ STATIC_IFN_KUNIT void dm_destroy_cached_state(struct amdgpu_device *adev)
}
EXPORT_IF_KUNIT(dm_destroy_cached_state);
+static void amdgpu_dm_apple5k_unlatch(struct dc *dc)
+{
+ const u8 zeros[3] = { 0 };
+
+ for (int i = 0; i < dc->link_count; i++) {
+ struct dc_link *link = dc->links[i];
+
+ if (link->apple_5k_peer) {
+ core_link_write_dpcd(link, DP_SOURCE_TABLE_REVISION, zeros, 3);
+ core_link_write_dpcd(link, DP_EDP_CONFIGURATION_SET, zeros, 1);
+ core_link_write_dpcd(link, 0x4F1, zeros, 1);
+ core_link_write_dpcd(link->apple_5k_peer, 0x4F1, zeros, 1);
+ break;
+ }
+ }
+}
+
static int dm_suspend(struct amdgpu_ip_block *ip_block)
{
struct amdgpu_device *adev = ip_block->adev;
@@ -1674,6 +1691,8 @@ static int dm_suspend(struct amdgpu_ip_block *ip_block)
amdgpu_dm_hpd_rx_irq_work_suspend(dm);
+ amdgpu_dm_apple5k_unlatch(dm->dc);
+
dc_set_power_state(dm->dc, DC_ACPI_CM_POWER_STATE_D3);
if (dm->dc->caps.ips_support && adev->in_s0ix)
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
index 9774ea33272d..532748127f72 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
@@ -173,6 +173,28 @@ STATIC_IFN_KUNIT void apply_edid_quirks(struct dc_link *link, struct edid *edid,
drm_dbg_driver(dev, "Force FreeSync min to 60 Hz on panel id %X\n", panel_id);
edid_caps->panel_patch.force_freesync_min_hz = 60;
break;
+ /*
+ * Workaround for Apple 27" 5K iMacs which expose a 2x1 tiled internal
+ * panel over an eDP root link and a secondary DP link. Point the
+ * secondary link to the root link to latch/unlatch the TCON via DPCD,
+ * checking connector_signal as panel IDs can flip after wake.
+ * IDs (Root/Tile): iMac15,1 (AE01/02), iMac17,1 (AE05/06),
+ * iMac18,3 (AE11/12), iMac19,1 (AE25/26); TODO (DCE >= 12): iMac19,1
+ * Vega, iMacPro1,1 (AE1D/1E), iMac20,x (AE31/32).
+ */
+ case drm_edid_encode_panel_id('A', 'P', 'P', 0xAE01):
+ case drm_edid_encode_panel_id('A', 'P', 'P', 0xAE02):
+ case drm_edid_encode_panel_id('A', 'P', 'P', 0xAE05):
+ case drm_edid_encode_panel_id('A', 'P', 'P', 0xAE06):
+ case drm_edid_encode_panel_id('A', 'P', 'P', 0xAE11):
+ case drm_edid_encode_panel_id('A', 'P', 'P', 0xAE12):
+ case drm_edid_encode_panel_id('A', 'P', 'P', 0xAE25):
+ case drm_edid_encode_panel_id('A', 'P', 'P', 0xAE26):
+ if (link->connector_signal == SIGNAL_TYPE_EDP &&
+ link->ctx->dce_version < DCE_VERSION_12_0 &&
+ (link->link_index ^ 1) < link->dc->link_count)
+ link->dc->links[link->link_index ^ 1]->apple_5k_peer = link;
+ break;
default:
return;
}
diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h
index f4fefa7c9ab8..aa1a9eea99d1 100644
--- a/drivers/gpu/drm/amd/display/dc/dc.h
+++ b/drivers/gpu/drm/amd/display/dc/dc.h
@@ -2003,6 +2003,7 @@ struct dc_scratch_space {
bool skip_implict_edp_power_control;
bool forced_psr_active;
enum backlight_control_type backlight_control_type;
+ struct dc_link *apple_5k_peer;
};
struct dc {
diff --git a/drivers/gpu/drm/amd/display/dc/link/link_detection.c b/drivers/gpu/drm/amd/display/dc/link/link_detection.c
index b4546f93af0c..47755f8b9c3e 100644
--- a/drivers/gpu/drm/amd/display/dc/link/link_detection.c
+++ b/drivers/gpu/drm/amd/display/dc/link/link_detection.c
@@ -218,7 +218,7 @@ static enum signal_type link_detect_sink_signal_type(struct dc_link *link,
* we assume signal is DVI; it could be corrected
* to HDMI after dongle detection
*/
- if (!dm_helpers_is_dp_sink_present(link))
+ if (!link->apple_5k_peer && !dm_helpers_is_dp_sink_present(link))
result = SIGNAL_TYPE_DVI_SINGLE_LINK;
}
break;
@@ -1625,6 +1625,11 @@ bool link_detect_connection_type(struct dc_link *link, enum dc_connection_type *
}
}
+ if (*type == dc_connection_none && link->apple_5k_peer) {
+ core_link_write_dpcd(link->apple_5k_peer, 0x4F1, &(const uint8_t){1}, 1);
+ *type = dc_connection_single;
+ }
+
return true;
}
diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c
index ad1a499b0d9d..d646b156c90b 100644
--- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c
+++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c
@@ -1496,6 +1496,10 @@ void dpcd_set_source_specific_data(struct dc_link *link)
link->dc->vendor_signature.data.raw,
sizeof(link->dc->vendor_signature.data.raw));
}
+
+ if (link->apple_5k_peer)
+ core_link_write_dpcd(link, DP_SOURCE_TABLE_REVISION,
+ (const uint8_t[]){0x04, 0x1d, 0x03}, 3);
}
void dpcd_write_cable_id_to_dprx(struct dc_link *link)
diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_phy.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_phy.c
index 49521ac4b0e8..56fe2710ee2c 100644
--- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_phy.c
+++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_phy.c
@@ -50,6 +50,9 @@ void dpcd_write_rx_power_ctrl(struct dc_link *link, bool on)
if (link->sync_lt_in_progress)
return;
+ if (!on && link->apple_5k_peer)
+ return;
+
core_link_write_dpcd(link, DP_SET_POWER, &state,
sizeof(state));
--
2.55.0
reply other threads:[~2026-11-01 23:24 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=94672259-c526-4ec7-a69c-7dda8d934593@gmail.com \
--to=richardlanedev@gmail.com \
--cc=airlied@gmail.com \
--cc=alexander.deucher@amd.com \
--cc=amd-gfx@lists.freedesktop.org \
--cc=christian.koenig@amd.com \
--cc=dri-devel@lists.freedesktop.org \
--cc=harry.wentland@amd.com \
--cc=linux-kernel@vger.kernel.org \
--cc=mario.limonciello@amd.com \
--cc=simona@ffwll.ch \
--cc=siqueira@igalia.com \
--cc=sunpeng.li@amd.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®