mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Richard Lane <richardlanedev@gmail.com>
To: Mario Limonciello <mario.limonciello@amd.com>,
	amd-gfx@lists.freedesktop.org
Cc: "Alex Deucher" <alexander.deucher@amd.com>,
	"Christian König" <christian.koenig@amd.com>,
	"Harry Wentland" <harry.wentland@amd.com>,
	"Leo Li" <sunpeng.li@amd.com>,
	"Rodrigo Siqueira" <siqueira@igalia.com>,
	"David Airlie" <airlied@gmail.com>,
	"Simona Vetter" <simona@ffwll.ch>,
	dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH 2/3] drm/amd/display: unlock secondary tile on Apple 5K iMac panels
Date: Thu, 29 Oct 2026 00:19:52 +0000	[thread overview]
Message-ID: <9a6529e2-997a-48eb-9e65-c26d9070108b@gmail.com> (raw)
In-Reply-To: <471336a8-35eb-4830-9f75-071f5b98bae4@amd.com>

On 27/10/2026 17:13, Mario Limonciello wrote:
> 
> 
> On 10/26/26 17:48, Richard Lane wrote:
>> 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:
>> - Pair the two internal dc_link instances during EDID quirk matching for
>>    DCE < 12 iMac15,1, iMac17,1, iMac18,3, and Polaris-based iMac19,1
>>    (identifying roles via 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.
>> - 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.
>> - Override the secondary tile's connector type to DRM_MODE_CONNECTOR_eDP
>>    so userspace compositors treat both tiles as a single built-in display.
>> - 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 | 31 +++++++++++++++++
>>   .../display/amdgpu_dm/amdgpu_dm_connector.c   |  6 ++++
>>   .../amd/display/amdgpu_dm/amdgpu_dm_helpers.c | 34 +++++++++++++++++++
>>   drivers/gpu/drm/amd/display/dc/dc.h           | 12 +++++++
>>   .../drm/amd/display/dc/link/link_detection.c  | 14 +++++++-
>>   .../dc/link/protocols/link_dp_capability.c    |  7 ++++
>>   .../display/dc/link/protocols/link_dp_phy.c   |  8 +++++
>>   7 files changed, 111 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..548f975c7324 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,35 @@ STATIC_IFN_KUNIT void dm_destroy_cached_state(struct amdgpu_device *adev)
>>   }
>>   EXPORT_IF_KUNIT(dm_destroy_cached_state);
>>   +/*
>> + * Unlatch Apple 5K panel tiles and reset DPCD state on suspend/shutdown.
>> + * This returns the TCON to single-pipe mode so the EFI boot logo is not
>> + * skewed on reboot.
>> + */
>> +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 (dc_link_is_apple_5k_secondary(link)) {
>> +            /*
>> +             * Clear Boot Camp source table (0x310), eDP config
>> +             * (0x10A), and secondary tile latch (0x4F1).
>> +             */
>> +            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);
>> +
>> +            /* Unlatch primary root tile */
>> +            core_link_write_dpcd(link->tiled_peer, 0x4F1, zeros, 1);
>> +
>> +            break;
>> +        }
>> +    }
>> +}
>> +
>>   static int dm_suspend(struct amdgpu_ip_block *ip_block)
>>   {
>>       struct amdgpu_device *adev = ip_block->adev;
>> @@ -1674,6 +1703,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_connector.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c
>> index ded8e21897f1..27de7965f366 100644
>> --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c
>> +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c
>> @@ -3460,6 +3460,12 @@ int amdgpu_dm_connector_init(struct amdgpu_display_manager *dm,
>>       }
>>         connector_type = to_drm_connector_type(link->connector_signal, link->link_id.id);
>> +    /*
>> +     * Override Apple 5K secondary tile to eDP so compositors treat it as
>> +     * built-in.
>> +     */
>> +    if (dc_link_is_apple_5k_secondary(link))
>> +        connector_type = DRM_MODE_CONNECTOR_eDP;
>>         res = drm_connector_init_with_ddc(
>>               dm->ddev,
>> 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..db6ac803e14f 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
>> @@ -97,6 +97,21 @@ STATIC_IFN_KUNIT u32 edid_extract_panel_id(struct edid *edid)
>>   }
>>   EXPORT_IF_KUNIT(edid_extract_panel_id);
>>   +/*
>> + * Pairs the two internal hardware links driving the Apple 5K panel,
>> + * allowing the driver to synchronize both tiles as a single display.
>> + */
>> +static void dm_helpers_wire_tiled_peer(struct dc_link *link)
>> +{
>> +    u32 peer_index = link->link_index ^ 1;
>> +
>> +    if (link->ctx->dce_version < DCE_VERSION_12_0 &&
>> +        peer_index < link->dc->link_count) {
>> +        link->tiled_peer = link->dc->links[peer_index];
>> +        link->tiled_peer->tiled_peer = link;
>> +    }
>> +}
>> +
>>   STATIC_IFN_KUNIT void apply_edid_quirks(struct dc_link *link, struct edid *edid,
>>                     struct dc_edid_caps *edid_caps)
>>   {
>> @@ -173,6 +188,25 @@ 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. Wire the peer
>> +     * links to latch and synchronize both tiles, using connector_signal
>> +     * for role assignment 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 (AE25/26), 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):
>> +        dm_helpers_wire_tiled_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..498c11c0616f 100644
>> --- a/drivers/gpu/drm/amd/display/dc/dc.h
>> +++ b/drivers/gpu/drm/amd/display/dc/dc.h
>> @@ -2003,6 +2003,12 @@ struct dc_scratch_space {
>>       bool skip_implict_edp_power_control;
>>       bool forced_psr_active;
>>       enum backlight_control_type backlight_control_type;
>> +
>> +    /*
>> +     * Partner link for dual-tile Apple 5K panels, wired during EDID quirk
>> +     * matching.
>> +     */
>> +    struct dc_link *tiled_peer;
> 
> This feels like it's re-inventing drm_connector's has_tile, and tile_group.
> 
> There is also a special "tile_is_single_monitor".
> 
> Can you use those instead?

I tested dropping the DRM_MODE_CONNECTOR_eDP override in
amdgpu_dm_connector.c and confirmed that once the secondary link's EDID
is read, DRM's DisplayID parser populates has_tile, tile_group, and
tile_is_single_monitor on both connectors, which Mutter handles
correctly. So I will drop the addition to amdgpu_dm_connector.c in v2.

However, drm_connector's has_tile and tile_group cannot be used for the
initial DPCD bringup in DC because those properties are only populated
after the secondary link's EDID has been read. At boot, the panel TCON
operates in single-pipe mode and holds the secondary DP link's HPD line
low. Until DC writes 0x01 to DPCD 0x4F1 on the primary eDP link (when
probing the secondary link in link_detect_connection_type()), bypasses
the low HPD state, and writes the 3-byte sequence to DPCD 0x310 on the
secondary link, the secondary link cannot be detected or read its EDID.

Therefore, link->tiled_peer is still needed in dc_link to reference the
partner link for the pre-EDID DPCD 0x4F1/0x310 TCON latch/unlatch and D3
sequences.

Richard

> 
>>   };
>>     struct dc {
>> @@ -3172,6 +3178,12 @@ struct dc_sink {
>>     void dc_sink_retain(struct dc_sink *sink);
>>   void dc_sink_release(struct dc_sink *sink);
>> +/* Helper to identify secondary (DP) tile of the Apple 5K panel. */
>> +static inline bool dc_link_is_apple_5k_secondary(const struct dc_link *link)
>> +{
>> +    return link && link->tiled_peer &&
>> +           link->connector_signal == SIGNAL_TYPE_DISPLAY_PORT;
>> +}
>>     struct dc_sink_init_data {
>>       enum signal_type sink_signal;
>> 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..25662ed3e612 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,8 @@ 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 (!dc_link_is_apple_5k_secondary(link) &&
>> +                !dm_helpers_is_dp_sink_present(link))
>>                   result = SIGNAL_TYPE_DVI_SINGLE_LINK;
>>           }
>>           break;
>> @@ -1625,6 +1626,17 @@ bool link_detect_connection_type(struct dc_link *link, enum dc_connection_type *
>>           }
>>       }
>>   +    /*
>> +     * Internal tiled DP secondary reports HPD low until latched. Latch
>> +     * the secondary tile via the root eDP link's DPCD (0x4F1) and force a
>> +     * single-sink connection.
>> +     */
>> +    if (*type == dc_connection_none &&
>> +        dc_link_is_apple_5k_secondary(link)) {
>> +        core_link_write_dpcd(link->tiled_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..15f223e48091 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,13 @@ void dpcd_set_source_specific_data(struct dc_link *link)
>>                   link->dc->vendor_signature.data.raw,
>>                   sizeof(link->dc->vendor_signature.data.raw));
>>       }
>> +    /*
>> +     * Write the 3-byte Boot Camp source table (0x310) required to unlock
>> +     * the secondary tile.
>> +     */
>> +    if (dc_link_is_apple_5k_secondary(link))
>> +        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..abac117d0514 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,14 @@ void dpcd_write_rx_power_ctrl(struct dc_link *link, bool on)
>>       if (link->sync_lt_in_progress)
>>           return;
>>   +    /*
>> +     * Prevent Apple 5K secondary tile from entering D3 power-down.
>> +     * Powering it off causes the TCON to drop HPD and stop responding to
>> +     * AUX.
>> +     */
>> +    if (!on && dc_link_is_apple_5k_secondary(link))
>> +        return;
>> +
>>       core_link_write_dpcd(link, DP_SET_POWER, &state,
>>                            sizeof(state));
>>   
> 


      reply	other threads:[~2026-10-29  0:19 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-26 22:48 Richard Lane
2026-10-27 17:13 ` Mario Limonciello
2026-10-29  0:19   ` Richard Lane [this message]

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=9a6529e2-997a-48eb-9e65-c26d9070108b@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®