--- /dev/null
+From 4f13e01c982b697fff9f8170d4b12aed23a12b94 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 12 Jun 2026 10:21:09 -0400
+Subject: Bluetooth: 6lowpan: Fix using chan->conn as indication to no remote
+ netdev
+
+From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+
+[ Upstream commit d38eaf611839b85ade3dd3db309dbc8aaaaf0095 ]
+
+b66774b48dd9 ("Bluetooth: L2CAP: Fix UAF in channel timeout by holding
+conn ref") don't reset the chan->conn to NULL anymore making the bt#
+netdev not be remove once the last l2cap_chan_del is removed.
+
+Instead of restoring the original behavior this remove the logic of
+keeping the interface after the last channel is removed because it
+never worked as intended and the l2cap_chan_del always detach its
+l2cap_conn which results in always removing the channel anyway.
+
+Fixes: b66774b48dd9 ("Bluetooth: L2CAP: Fix UAF in channel timeout by holding conn ref")
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/bluetooth/6lowpan.c | 18 +++---------------
+ 1 file changed, 3 insertions(+), 15 deletions(-)
+
+diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
+index e2196cf4f6f70..723ead2c04f16 100644
+--- a/net/bluetooth/6lowpan.c
++++ b/net/bluetooth/6lowpan.c
+@@ -781,20 +781,10 @@ static void chan_close_cb(struct l2cap_chan *chan)
+ struct lowpan_btle_dev *dev = NULL;
+ struct lowpan_peer *peer;
+ int err = -ENOENT;
+- bool last = false, remove = true;
++ bool last = false;
+
+ BT_DBG("chan %p conn %p", chan, chan->conn);
+
+- if (chan->conn && chan->conn->hcon) {
+- if (!is_bt_6lowpan(chan->conn->hcon))
+- return;
+-
+- /* If conn is set, then the netdev is also there and we should
+- * not remove it.
+- */
+- remove = false;
+- }
+-
+ spin_lock(&devices_lock);
+
+ list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
+@@ -821,10 +811,8 @@ static void chan_close_cb(struct l2cap_chan *chan)
+
+ ifdown(dev->netdev);
+
+- if (remove) {
+- INIT_WORK(&entry->delete_netdev, delete_netdev);
+- schedule_work(&entry->delete_netdev);
+- }
++ INIT_WORK(&entry->delete_netdev, delete_netdev);
++ schedule_work(&entry->delete_netdev);
+ } else {
+ spin_unlock(&devices_lock);
+ }
+--
+2.53.0
+
--- /dev/null
+From 7b33f1a57bd459f54a4488183a2eeea2fa39ed40 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 6 Aug 2026 00:52:14 +0000
+Subject: Bluetooth: L2CAP: Fix UAF in channel timeout by holding conn ref
+
+From: Marco Elver <elver@google.com>
+
+[ Upstream commit b66774b48dd98f07254951f74ea6f513efe7ff8b ]
+
+l2cap_chan_timeout() runs asynchronously and accesses chan->conn. If
+the connection is torn down while the timer is running or pending,
+chan->conn can be freed, leading to a use-after-free when the timer
+worker attempts to lock conn->lock:
+
+| BUG: KASAN: slab-use-after-free in instrument_atomic_read_write include/linux/instrumented.h:112 [inline]
+| BUG: KASAN: slab-use-after-free in atomic_long_try_cmpxchg_acquire include/linux/atomic/atomic-instrumented.h:4456 [inline]
+| BUG: KASAN: slab-use-after-free in __mutex_trylock_fast kernel/locking/mutex.c:161 [inline]
+| BUG: KASAN: slab-use-after-free in mutex_lock+0x4f/0xa0 kernel/locking/mutex.c:318
+| Write of size 8 at addr ffff8881298d9550 by task kworker/2:1/83
+|
+| CPU: 2 UID: 0 PID: 83 Comm: kworker/2:1 Not tainted 7.1.0-rc6-next-20260601-dirty #6 PREEMPT(full)
+| Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.17.0-debian-1.17.0-1 04/01/2014
+| Workqueue: events l2cap_chan_timeout
+| Call Trace:
+| <TASK>
+| instrument_atomic_read_write include/linux/instrumented.h:112 [inline]
+| atomic_long_try_cmpxchg_acquire include/linux/atomic/atomic-instrumented.h:4456 [inline]
+| __mutex_trylock_fast kernel/locking/mutex.c:161 [inline]
+| mutex_lock+0x4f/0xa0 kernel/locking/mutex.c:318
+| l2cap_chan_timeout+0x5d/0x1b0 net/bluetooth/l2cap_core.c:422
+| process_one_work kernel/workqueue.c:3326 [inline]
+| process_scheduled_works+0x7c8/0xfb0 kernel/workqueue.c:3409
+| worker_thread+0x8a9/0xcf0 kernel/workqueue.c:3490
+| kthread+0x346/0x430 kernel/kthread.c:436
+| ret_from_fork+0x1a3/0x470 arch/x86/kernel/process.c:158
+| ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
+| </TASK>
+|
+| Allocated by task 320:
+| l2cap_conn_add+0xa7/0x820 net/bluetooth/l2cap_core.c:7075
+| l2cap_connect_cfm+0xdb/0xd70 net/bluetooth/l2cap_core.c:7452
+| hci_connect_cfm include/net/bluetooth/hci_core.h:2139 [inline]
+| hci_remote_features_evt+0x52f/0x9f0 net/bluetooth/hci_event.c:3760
+| hci_event_func net/bluetooth/hci_event.c:7796 [inline]
+| hci_event_packet+0x561/0xa70 net/bluetooth/hci_event.c:7847
+| hci_rx_work+0x370/0x890 net/bluetooth/hci_core.c:4040
+| process_one_work kernel/workqueue.c:3326 [inline]
+| process_scheduled_works+0x7c8/0xfb0 kernel/workqueue.c:3409
+| worker_thread+0x8a9/0xcf0 kernel/workqueue.c:3490
+| kthread+0x346/0x430 kernel/kthread.c:436
+| ret_from_fork+0x1a3/0x470 arch/x86/kernel/process.c:158
+| ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
+|
+| Freed by task 322:
+| hci_disconn_cfm include/net/bluetooth/hci_core.h:2154 [inline]
+| hci_conn_hash_flush+0x101/0x1f0 net/bluetooth/hci_conn.c:2736
+| hci_dev_close_sync+0x889/0xde0 net/bluetooth/hci_sync.c:5405
+| hci_dev_do_close net/bluetooth/hci_core.c:502 [inline]
+| hci_unregister_dev+0x1f7/0x370 net/bluetooth/hci_core.c:2679
+| vhci_release+0x12a/0x180 drivers/bluetooth/hci_vhci.c:690
+| __fput+0x369/0x890 fs/file_table.c:510
+| task_work_run+0x160/0x1d0 kernel/task_work.c:233
+| get_signal+0xf5b/0x1120 kernel/signal.c:2810
+| arch_do_signal_or_restart+0x4d/0x600 arch/x86/kernel/signal.c:337
+| __exit_to_user_mode_loop kernel/entry/common.c:64 [inline]
+| exit_to_user_mode_loop+0x85/0x510 kernel/entry/common.c:98
+| do_syscall_64+0x263/0x3d0 arch/x86/entry/syscall_64.c:100
+| entry_SYSCALL_64_after_hwframe+0x77/0x7f
+|
+| The buggy address belongs to the object at ffff8881298d9400
+| which belongs to the cache kmalloc-512 of size 512
+| The buggy address is located 336 bytes inside of
+| freed 512-byte region [ffff8881298d9400, ffff8881298d9600)
+
+Fix it by having chan->conn hold a reference to l2cap_conn (via
+l2cap_conn_get) when the channel is added to the connection, and
+releasing it in the channel destructor. This ensures the l2cap_conn
+remains alive as long as the channel exists.
+
+A new FLAG_DEL channel flag is introduced to indicate that the channel
+has been deleted from its connection. l2cap_chan_del() atomically sets
+this flag using test_and_set_bit() instead of setting chan->conn to
+NULL. All asynchronous workers (l2cap_chan_timeout, l2cap_ack_timeout,
+l2cap_monitor_timeout, l2cap_retrans_timeout) and l2cap_chan_send()
+check FLAG_DEL to determine whether the channel has been torn down,
+rather than testing chan->conn for NULL.
+
+Fixes: 8c8e620467a7 ("Bluetooth: L2CAP: use chan timer to close channels in cleanup_listen()")
+Cc: <stable@vger.kernel.org>
+Cc: Siwei Zhang <oss@fourdim.xyz>
+Cc: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Assisted-by: Gemini:gemini-3.1-pro-preview
+Reported-by: https://sashiko.dev/#/patchset/20260521021249.3258069-1-oss%40fourdim.xyz
+Signed-off-by: Marco Elver <elver@google.com>
+Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Todd Kjos <tkjos@google.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/net/bluetooth/l2cap.h | 1 +
+ net/bluetooth/l2cap_core.c | 33 ++++++++++++++++++++-------------
+ 2 files changed, 21 insertions(+), 13 deletions(-)
+
+diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
+index 97ad02840530e..ba230c3cf4315 100644
+--- a/include/net/bluetooth/l2cap.h
++++ b/include/net/bluetooth/l2cap.h
+@@ -743,6 +743,7 @@ enum {
+ FLAG_ECRED_CONN_REQ_SENT,
+ FLAG_PENDING_SECURITY,
+ FLAG_HOLD_HCI_CONN,
++ FLAG_DEL,
+ };
+
+ /* Lock nesting levels for L2CAP channels. We need these because lockdep
+diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
+index b5603531a9d60..d1881f48cd11c 100644
+--- a/net/bluetooth/l2cap_core.c
++++ b/net/bluetooth/l2cap_core.c
+@@ -415,7 +415,7 @@ static void l2cap_chan_timeout(struct work_struct *work)
+
+ BT_DBG("chan %p state %s", chan, state_to_string(chan->state));
+
+- if (!conn) {
++ if (test_bit(FLAG_DEL, &chan->flags)) {
+ l2cap_chan_put(chan);
+ return;
+ }
+@@ -426,6 +426,9 @@ static void l2cap_chan_timeout(struct work_struct *work)
+ */
+ l2cap_chan_lock(chan);
+
++ if (test_bit(FLAG_DEL, &chan->flags))
++ goto unlock;
++
+ if (chan->state == BT_CONNECTED || chan->state == BT_CONFIG)
+ reason = ECONNREFUSED;
+ else if (chan->state == BT_CONNECT &&
+@@ -438,10 +441,11 @@ static void l2cap_chan_timeout(struct work_struct *work)
+
+ chan->ops->close(chan);
+
++unlock:
+ l2cap_chan_unlock(chan);
+- l2cap_chan_put(chan);
+
+ mutex_unlock(&conn->chan_lock);
++ l2cap_chan_put(chan);
+ }
+
+ struct l2cap_chan *l2cap_chan_create(void)
+@@ -494,6 +498,9 @@ static void l2cap_chan_destroy(struct kref *kref)
+ list_del(&chan->global_l);
+ write_unlock(&chan_list_lock);
+
++ if (chan->conn)
++ l2cap_conn_put(chan->conn);
++
+ kfree(chan);
+ }
+
+@@ -625,7 +632,7 @@ void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
+
+ conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM;
+
+- chan->conn = conn;
++ chan->conn = l2cap_conn_get(conn);
+
+ switch (chan->chan_type) {
+ case L2CAP_CHAN_CONN_ORIENTED:
+@@ -680,30 +687,26 @@ void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
+
+ void l2cap_chan_del(struct l2cap_chan *chan, int err)
+ {
+- struct l2cap_conn *conn = chan->conn;
+-
+ __clear_chan_timer(chan);
+
+- BT_DBG("chan %p, conn %p, err %d, state %s", chan, conn, err,
++ BT_DBG("chan %p, err %d, state %s", chan, err,
+ state_to_string(chan->state));
+
+ chan->ops->teardown(chan, err);
+
+- if (conn) {
++ if (!test_and_set_bit(FLAG_DEL, &chan->flags)) {
+ /* Delete from channel list */
+ list_del(&chan->list);
+
+ l2cap_chan_put(chan);
+
+- chan->conn = NULL;
+-
+ /* Reference was only held for non-fixed channels or
+ * fixed channels that explicitly requested it using the
+ * FLAG_HOLD_HCI_CONN flag.
+ */
+ if (chan->chan_type != L2CAP_CHAN_FIXED ||
+ test_bit(FLAG_HOLD_HCI_CONN, &chan->flags))
+- hci_conn_drop(conn->hcon);
++ hci_conn_drop(chan->conn->hcon);
+ }
+
+ if (test_bit(CONF_NOT_COMPLETE, &chan->conf_state))
+@@ -1930,7 +1933,7 @@ static void l2cap_monitor_timeout(struct work_struct *work)
+
+ l2cap_chan_lock(chan);
+
+- if (!chan->conn) {
++ if (test_bit(FLAG_DEL, &chan->flags)) {
+ l2cap_chan_unlock(chan);
+ l2cap_chan_put(chan);
+ return;
+@@ -1951,7 +1954,7 @@ static void l2cap_retrans_timeout(struct work_struct *work)
+
+ l2cap_chan_lock(chan);
+
+- if (!chan->conn) {
++ if (test_bit(FLAG_DEL, &chan->flags)) {
+ l2cap_chan_unlock(chan);
+ l2cap_chan_put(chan);
+ return;
+@@ -2566,7 +2569,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len)
+ int err;
+ struct sk_buff_head seg_queue;
+
+- if (!chan->conn)
++ if (test_bit(FLAG_DEL, &chan->flags))
+ return -ENOTCONN;
+
+ /* Connectionless channel */
+@@ -3167,12 +3170,16 @@ static void l2cap_ack_timeout(struct work_struct *work)
+
+ l2cap_chan_lock(chan);
+
++ if (test_bit(FLAG_DEL, &chan->flags))
++ goto unlock;
++
+ frames_to_ack = __seq_offset(chan, chan->buffer_seq,
+ chan->last_acked_seq);
+
+ if (frames_to_ack)
+ l2cap_send_rr_or_rnr(chan, 0);
+
++unlock:
+ l2cap_chan_unlock(chan);
+ l2cap_chan_put(chan);
+ }
+--
+2.53.0
+
net-openvswitch-fix-skb-leak-on-flow-key-update-fail.patch
firmware-stratix10-svc-fix-memory-leaks-and-list-cor.patch
gpio-pch-use-raw_spinlock_t-for-the-register-lock.patch
+bluetooth-l2cap-fix-uaf-in-channel-timeout-by-holdin.patch
+bluetooth-6lowpan-fix-using-chan-conn-as-indication-.patch
--- /dev/null
+From 5495b06776985b09d94ff2a2a0992ed1c15c527f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 29 Jul 2026 13:57:48 -0400
+Subject: drm/amd/display: check GRPH_FLIP status before sending event
+
+From: Leo Li <sunpeng.li@amd.com>
+
+[ Upstream commit 48ab86360af117123eb1b15e38f068acf3826400 ]
+
+[Why]
+
+After unifying DCN interrupt sources under VUPDATE_NO_LOCK, we have two
+remaining issues to clean up:
+
+1. On DCN, flip completion is now delivered from VUPDATE_NO_LOCK
+ (dm_crtc_high_irq_handler) instead of GRPH_PFLIP. But VUPDATE_NO_LOCK
+ fires every frame, regardless of whether a flip has latched.
+
+2. There is a window during commit where a flip is armed (pflip_status =
+ SUBMITTED) but not yet programmed into HW. If the VUPDATE_NO_LOCK
+ fires in that window, its handler would deliver a flip event to
+ userspace before HW has latched to it. If userspace then renders to
+ what it believes is now the back buffer (but HW is still latched to
+ it!), it will cause display corruption. This issue seemed to have
+ been introduced by:
+ commit 1159898a88db ("drm/amd/display: Handle commit plane with no FB.")
+ Enabling replay or psr extended the duration of this window, and
+ hence made corruption more likely to be observed.
+
+[How]
+
+* Move acrtc->event/pflip_status arming to after
+ update_planes_and_stream_adapter() has programmed the flip into HW.
+ This closes the window where pflip_status is SUBMITTED but the flip is
+ not yet programmed.
+
+* Add dc_get_flip_pending_on_otg(), which reads the HUBP flip-pending
+ status straight from HW for the pipe(s) bound to an OTG instance. It
+ is keyed only by otg_inst and does not take or mutate a
+ dc_plane_state, so it is safe to call from the OTG interrupt handler
+ without racing a concurrent commit that may be modifying plane state.
+
+* Optimistically query for flip-pending after programming, in the event
+ that HW latched to the new fb between programming start and arming
+ event. If it latched, send the vblank event immediately, rather than
+ wait for the next vblank IRQ.
+
+* In the VUPDATE_NO_LOCK handler, only deliver flip completion once
+ dc_get_flip_pending_on_otg() reports the flip is no longer pending.
+ Otherwise leave the flip armed and retry on the next vupdate.
+
+* For DCE, maintain the existing behavior of arming flips before
+ programming, and relying on GRPH_FLIP to fire at HW latch.
+
+v2:
+* Drop flip_programmed completion object, instead move
+ event/pflip_status arming after programming.
+* For DCN, optimistically query for flip pending immediately after
+ programming, and if it latched, send event right away.
+
+v3:
+* Fix event timestamps on optimistic flip latch detection, where it's
+ possible for it to run *before* the vupdate IRQ updates the timestamp.
+* Add more docstrings for DCN vblank handling.
+* Clean up if conditions in dm_arm_vblank_event().
+* Code style cleanup on braces surrounding multi-line statements.
+
+Fixes: 9b47278cec98 ("drm/amd/display: temp w/a for dGPU to enter idle optimizations")
+Link: https://gitlab.freedesktop.org/drm/amd/-/work_items/3787
+Link: https://gitlab.freedesktop.org/drm/amd/-/work_items/4141
+Assisted-by: Copilot:claude-opus-4.8
+Tested-by: Mario Limonciello (AMD) <superm1@kernel.org>
+Reviewed-by: Mario Limonciello (AMD) <superm1@kernel.org>
+Signed-off-by: Leo Li <sunpeng.li@amd.com>
+Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
+(cherry picked from commit f64a9be5653689ff43e148cd8a6483077488c8e5)
+Cc: stable@vger.kernel.org # 8382cd234981: drm/amd/display: consolidate DCN vblank/flip handling onto vupdate_no_lock
+Cc: stable@vger.kernel.org
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 191 ++++++++++++++----
+ drivers/gpu/drm/amd/display/dc/core/dc.c | 45 +++++
+ drivers/gpu/drm/amd/display/dc/dc.h | 1 +
+ 3 files changed, 197 insertions(+), 40 deletions(-)
+
+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 99759559cc0f6..be267f5b9f8cc 100644
+--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+@@ -676,13 +676,30 @@ static void dm_crtc_high_irq_handler(struct amdgpu_device *adev,
+ * Deliver pageflip completion events (DCN only).
+ *
+ * Since GRPH_PFLIP is not used, VUPDATE_NO_LOCK is the flip latch
+- * point. Deliver any pending pageflip completion event from here.
++ * point. Deliver any pending pageflip completion event from here,
++ * once HW has consumed the new address (the OTG no longer reports a
++ * pending flip).
+ *
+- * NOTE: This can deliver an event for a flip that was armed but not yet
+- * programmed into HW; that race is closed in a follow-up change by
+- * checking the programmed flip status.
++ * Also handle the case here where there aren't any active planes and
++ * DCN HUBP may be clock-gated, so the flip-pending status may be
++ * undefined.
+ */
+- if (is_dcn && acrtc->pflip_status == AMDGPU_FLIP_SUBMITTED) {
++ if (is_dcn && acrtc->pflip_status == AMDGPU_FLIP_SUBMITTED &&
++ acrtc->event) {
++
++ if (!dc_get_flip_pending_on_otg(adev->dm.dc, acrtc->otg_inst)) {
++ drm_crtc_send_vblank_event(&acrtc->base, acrtc->event);
++ acrtc->event = NULL;
++ drm_crtc_vblank_put(&acrtc->base);
++ acrtc->pflip_status = AMDGPU_FLIP_NONE;
++ }
++ /*
++ * If the flip is still pending, leave it armed and
++ * retry on the next vupdate.
++ */
++ } else if (is_dcn && acrtc->pflip_status == AMDGPU_FLIP_SUBMITTED &&
++ acrtc->dm_irq_params.active_planes == 0) {
++
+ if (acrtc->event) {
+ drm_crtc_send_vblank_event(&acrtc->base, acrtc->event);
+ acrtc->event = NULL;
+@@ -9664,6 +9681,28 @@ static void amdgpu_dm_enable_self_refresh(struct amdgpu_crtc *acrtc_attach,
+ }
+ }
+
++static void dm_arm_vblank_event(struct amdgpu_crtc *acrtc,
++ struct dm_crtc_state *acrtc_state,
++ bool pflip_update,
++ bool cursor_update)
++{
++ assert_spin_locked(&acrtc->base.dev->event_lock);
++
++ if (!acrtc->base.state->event || acrtc_state->active_planes == 0)
++ return;
++
++ if (pflip_update) {
++ drm_crtc_vblank_get(&acrtc->base);
++ WARN_ON(acrtc->pflip_status != AMDGPU_FLIP_NONE);
++ /* Arm flip completion handling and event delivery after programming. */
++ prepare_flip_isr(acrtc);
++ } else if (cursor_update) {
++ drm_crtc_vblank_get(&acrtc->base);
++ acrtc->event = acrtc->base.state->event;
++ acrtc->base.state->event = NULL;
++ }
++}
++
+ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
+ struct drm_device *dev,
+ struct amdgpu_display_manager *dm,
+@@ -9687,6 +9726,7 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
+ bool cursor_update = false;
+ bool pflip_present = false;
+ bool immediate_flip = false;
++ bool flip_latched_during_prog = false;
+ bool dirty_rects_changed = false;
+ bool updated_planes_and_streams = false;
+ struct {
+@@ -9922,39 +9962,24 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
+ usleep_range(1000, 1100);
+ }
+
+- /**
+- * Prepare the flip event for the pageflip interrupt to handle.
+- *
+- * This only works in the case where we've already turned on the
+- * appropriate hardware blocks (eg. HUBP) so in the transition case
+- * from 0 -> n planes we have to skip a hardware generated event
+- * and rely on sending it from software.
+- */
+- if (acrtc_attach->base.state->event &&
+- acrtc_state->active_planes > 0) {
+- drm_crtc_vblank_get(pcrtc);
+-
+- spin_lock_irqsave(&pcrtc->dev->event_lock, flags);
+-
+- WARN_ON(acrtc_attach->pflip_status != AMDGPU_FLIP_NONE);
+- prepare_flip_isr(acrtc_attach);
+-
+- spin_unlock_irqrestore(&pcrtc->dev->event_lock, flags);
+- }
+-
+ if (acrtc_state->stream) {
+ if (acrtc_state->freesync_vrr_info_changed)
+ bundle->stream_update.vrr_infopacket =
+ &acrtc_state->stream->vrr_infopacket;
+ }
+- } else if (cursor_update && acrtc_state->active_planes > 0) {
+- spin_lock_irqsave(&pcrtc->dev->event_lock, flags);
+- if (acrtc_attach->base.state->event) {
+- drm_crtc_vblank_get(pcrtc);
+- acrtc_attach->event = acrtc_attach->base.state->event;
+- acrtc_attach->base.state->event = NULL;
++ }
++
++ /*
++ * DCE depends on a combination of GRPH_FLIP, VLINE0, and VUPDATE for
++ * event delivery. Only GRPH_FLIP handler can send pflip events, and it
++ * only fires if HW latched to the flip. Maintain legacy behavior by
++ * arming event before programming.
++ */
++ if (amdgpu_ip_version(dm->adev, DCE_HWIP, 0) == 0) {
++ scoped_guard(spinlock_irqsave, &pcrtc->dev->event_lock) {
++ dm_arm_vblank_event(acrtc_attach, acrtc_state,
++ pflip_present, cursor_update);
+ }
+- spin_unlock_irqrestore(&pcrtc->dev->event_lock, flags);
+ }
+
+ /* Update the planes if changed or disable if we don't have any. */
+@@ -10056,17 +10081,103 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
+ amdgpu_dm_commit_cursors(state);
+
+ /*
+- * On DCN, flip completion is normally delivered from VUPDATE_NO_LOCK.
+- * However, an immediate (tearing / async) flip is latched by HW right
+- * away and does not wait for the next vupdate, so deliver its
+- * completion event here after programming.
++ * DCN specific vblank handling
++ * ============================
++ *
++ * With the event_lock held, arm the vblank event, and determine whether
++ * deliver it immediately, or in VUPDATE_NO_LOCK IRQ (i.e. HW latch
++ * point) handler. Do this *after* programming so that the IRQ handler
++ * will not deliver the event before HW laches onto the programmed
++ * values:
++ *
++ * Commit thread IRQ handler HW
++ * -----------------------------------------------------------------
++ * arm_vblank_event()
++ * vupdate()
++ * vupdate_handler()
++ * cook_timestamp()
++ * # prev flip already latched,
++ * # so flip_latched == true.
++ * if event_armed && flip_latched:
++ * send_vblank_event()
++ * # sent before latch, **BAD!**
++ * hw_program()
++ * vupdate()
++ * **latch**
++ *
++ * There's a consequence of arming after: it's possible for HW to latch
++ * between start of HW programming and acrtc->event/pflip_status arming.
++ * When this happens, the IRQ handler will send the event on the next
++ * immediate latch point, even though HW has already latched. This is
++ * handled by optimistically checking for HW latch after programming,
++ * and if latched, send the event immediately:
+ *
+- * On DCE, GRPH_PFLIP already fires immediately for immediate flips, so
+- * this is DCN-only.
++ * Commit thread IRQ handler HW
++ * -----------------------------------------------------------------
++ * hw_program()
++ * vupdate()
++ * **latch**
++ * vupdate_handler()
++ * cook_timestamp()
++ * # event_armed == false
++ * # **no event sent!**
++ * arm_vblank_event()
++ * if flip_latched:
++ * **send_vblank_event()**
++ * disarm_vblank_event()
++ *
++ * The IRQ handler is expected to cook the timestamp, but we need to
++ * cook the timestamp before optimistic sending as well. That's because
++ * the following sequence is possible:
++ *
++ * Commit thread IRQ handler HW
++ * -----------------------------------------------------------------
++ * hw_program()
++ * arm_vblank_event()
++ * vupdate()
++ * **latch**
++ * if flip_latched:
++ * # Need cook before send!
++ * **cook_timestamp()**
++ * send_vblank_event()
++ * disarm_vblank_event()
++ * vupdate_handler()
++ * cook_timestamp()
++ * # event_armed == false
++ * # no event sent!
++ *
++ * Cooking twice is OK, since DRM scanout accurate timestamps report A)
++ * the previous vactive start if currently in vactive, or B) the next
++ * vactive start if currently in vblank (see &get_vblank_counter). 'A)'
++ * is what we want for the optimistic send, and for 'B)', we'll cook a
++ * timestamp no later than the next IRQ handler run.
++ *
++ * The more correct fix is to wrap programming and arming with the
++ * event_lock and thus serializing it with the IRQ handler. However,
++ * there are various sleep-waits within
++ * update_planes_and_stream_adapter() that makes spin locking illegal.
++ * And on full updates, it can take 1-2 frame-times to return (see
++ * commit_planes_for_stream).
++ *
++ * On DCE, GRPH_PFLIP IRQ is used and takes care of this.
+ */
+- if (immediate_flip && amdgpu_ip_version(dm->adev, DCE_HWIP, 0) != 0) {
++ if (amdgpu_ip_version(dm->adev, DCE_HWIP, 0) != 0) {
+ spin_lock_irqsave(&pcrtc->dev->event_lock, flags);
+- if (acrtc_attach->pflip_status == AMDGPU_FLIP_SUBMITTED &&
++
++ if (updated_planes_and_streams) {
++ flip_latched_during_prog =
++ !dc_get_flip_pending_on_otg(dm->dc, acrtc_attach->otg_inst);
++ }
++
++ dm_arm_vblank_event(acrtc_attach, acrtc_state,
++ pflip_present, cursor_update);
++
++ /*
++ * Deliver the event immediately on immediate flip, or on a
++ * update that has already latched.
++ */
++ if ((immediate_flip || flip_latched_during_prog) &&
++ acrtc_attach->pflip_status == AMDGPU_FLIP_SUBMITTED &&
+ acrtc_attach->event) {
+ drm_crtc_accurate_vblank_count(&acrtc_attach->base);
+ drm_crtc_send_vblank_event(&acrtc_attach->base,
+diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c
+index 0347174173f34..927837249479f 100644
+--- a/drivers/gpu/drm/amd/display/dc/core/dc.c
++++ b/drivers/gpu/drm/amd/display/dc/core/dc.c
+@@ -5427,6 +5427,51 @@ void dc_interrupt_ack(struct dc *dc, enum dc_irq_source src)
+ dal_irq_service_ack(dc->res_pool->irqs, src);
+ }
+
++/*
++ * dc_get_flip_pending_on_otg() - Check if a GRPH_FLIP is still pending on OTG
++ *
++ * @dc: display core context @otg_inst: OTG instance to query
++ *
++ * Reads the HUBP flip-pending status for the pipe(s) bound to @otg_inst,
++ * returning true if any of them has not yet latched its programmed surface
++ * address.
++ *
++ * Unlike dc_plane_get_status(), this does not take or mutate a dc_plane_state,
++ * so it is safe to call from interrupt context without racing a concurrent
++ * commit that may be updating plane state.
++ *
++ * Return: true if a flip is still pending on the OTG, false otherwise.
++ */
++bool dc_get_flip_pending_on_otg(struct dc *dc, int otg_inst)
++{
++ bool flip_pending = false;
++ int i;
++
++ if (!dc || !dc->current_state)
++ return false;
++
++ dc_exit_ips_for_hw_access(dc);
++
++ for (i = 0; i < dc->res_pool->pipe_count; i++) {
++ struct pipe_ctx *pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i];
++ struct hubp *hubp = pipe_ctx->plane_res.hubp;
++
++ if (!pipe_ctx->plane_state || !pipe_ctx->stream_res.tg)
++ continue;
++
++ if (pipe_ctx->stream_res.tg->inst != otg_inst)
++ continue;
++
++ if (hubp && hubp->funcs->hubp_is_flip_pending &&
++ hubp->funcs->hubp_is_flip_pending(hubp)) {
++ flip_pending = true;
++ break;
++ }
++ }
++
++ return flip_pending;
++}
++
+ void dc_power_down_on_boot(struct dc *dc)
+ {
+ if (dc->ctx->dce_environment != DCE_ENV_VIRTUAL_HW &&
+diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h
+index 98f0b6b3c2136..390f948dad393 100644
+--- a/drivers/gpu/drm/amd/display/dc/dc.h
++++ b/drivers/gpu/drm/amd/display/dc/dc.h
+@@ -2611,6 +2611,7 @@ enum dc_irq_source dc_interrupt_to_irq_source(
+ uint32_t ext_id);
+ bool dc_interrupt_set(struct dc *dc, enum dc_irq_source src, bool enable);
+ void dc_interrupt_ack(struct dc *dc, enum dc_irq_source src);
++bool dc_get_flip_pending_on_otg(struct dc *dc, int otg_inst);
+ enum dc_irq_source dc_get_hpd_irq_source_at_index(
+ struct dc *dc, uint32_t link_index);
+
+--
+2.53.0
+
--- /dev/null
+From c257a7c06ea915c5f661704b6a4777fdb7886b99 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 5 Aug 2026 12:33:14 -0400
+Subject: drm/amd/display: Exit idle optimizations before programming
+
+From: Leo Li <sunpeng.li@amd.com>
+
+[ Upstream commit 8419331e64d92a8de5fc4feef0e305f201fb8b33 ]
+
+[Why]
+
+We need to exit PSR/IPS before programming. Before calling DC for
+programming in amdgpu_dm_commit_planes(), there's a
+vblank_control_workqueue flush. This waits for IPS and PSR exit. (See
+drm_vblank_on/off() > amdgpu_dm_crtc_set_vblank() --queue_work()->
+amdgpu_dm_crtc_vblank_control_worker())
+
+Prior to the tagged "Fixes:" change, drm_vblank_get() was called before
+the workqueue flush. This ordering ensures that PSR exit occurred before
+programming. After the "Fixes:" change, drm_vblank_get() is called after
+the workqueue flush, leading to programming while idle optimizations are
+still active. This can lead to incorrect flip_pending detection used by
+vblank event delivery.
+
+[How]
+
+Split the vblank_get() component of `dm_arm_vblank_event()` into
+`dm_arm_vblank_event_pre_programming()`, which is called before
+programming. Call it before the vblank_control_workqueue flush.
+
+Includes a drive-by cleanup of prepare_flip_isr(): the only caller is
+dm_arm_vblank_event() and it's simple enough to roll-in.
+
+v2: Fix checkpatch formatting warning on
+ drm_arm_vblank_event_pre_programming() arg alignment.
+
+Fixes: 48ab86360af1 ("drm/amd/display: check GRPH_FLIP status before sending event")
+Cc: stable@vger.kernel.org
+Link: https://gitlab.freedesktop.org/drm/amd/-/work_items/4141#note_3583205
+Link: https://gitlab.freedesktop.org/drm/amd/-/work_items/5527
+Assisted-by: Codex:gpt-5.6-sol
+Assisted-by: Claude:opus-5
+Suggested-by: David Weber <weber.aulendorf@gmail.com>
+Signed-off-by: Leo Li <sunpeng.li@amd.com>
+Reviewed-by: Mario Limonciello (AMD) <superm1@kernel.org>
+Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
+(cherry picked from commit 05984e29520a28c27f5a2388742c957a6a87ee7a)
+(cherry picked from commit 8419331e64d92a8de5fc4feef0e305f201fb8b33)
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 78 +++++++++++--------
+ 1 file changed, 46 insertions(+), 32 deletions(-)
+
+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 be267f5b9f8cc..4f53297786623 100644
+--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+@@ -9326,25 +9326,6 @@ static void remove_stream(struct amdgpu_device *adev,
+ acrtc->enabled = false;
+ }
+
+-static void prepare_flip_isr(struct amdgpu_crtc *acrtc)
+-{
+-
+- assert_spin_locked(&acrtc->base.dev->event_lock);
+- WARN_ON(acrtc->event);
+-
+- acrtc->event = acrtc->base.state->event;
+-
+- /* Set the flip status */
+- acrtc->pflip_status = AMDGPU_FLIP_SUBMITTED;
+-
+- /* Mark this event as consumed */
+- acrtc->base.state->event = NULL;
+-
+- drm_dbg_state(acrtc->base.dev,
+- "crtc:%d, pflip_stat:AMDGPU_FLIP_SUBMITTED\n",
+- acrtc->crtc_id);
+-}
+-
+ static void update_freesync_state_on_stream(
+ struct amdgpu_display_manager *dm,
+ struct dm_crtc_state *new_crtc_state,
+@@ -9692,17 +9673,47 @@ static void dm_arm_vblank_event(struct amdgpu_crtc *acrtc,
+ return;
+
+ if (pflip_update) {
+- drm_crtc_vblank_get(&acrtc->base);
+ WARN_ON(acrtc->pflip_status != AMDGPU_FLIP_NONE);
+- /* Arm flip completion handling and event delivery after programming. */
+- prepare_flip_isr(acrtc);
++ WARN_ON(acrtc->event);
++
++ acrtc->pflip_status = AMDGPU_FLIP_SUBMITTED;
++ acrtc->event = acrtc->base.state->event;
++ acrtc->base.state->event = NULL;
++
++ drm_dbg_state(acrtc->base.dev,
++ "crtc:%d, pflip_stat:AMDGPU_FLIP_SUBMITTED\n",
++ acrtc->crtc_id);
+ } else if (cursor_update) {
+- drm_crtc_vblank_get(&acrtc->base);
+ acrtc->event = acrtc->base.state->event;
+ acrtc->base.state->event = NULL;
+ }
+ }
+
++/**
++ * dm_arm_vblank_event_pre_programming - Prepare for programming
++ * @acrtc: The amdgpu CRTC to prepare
++ * @acrtc_state: The new CRTC state
++ * @pflip_update: Whether a page flip is being programmed
++ * @cursor_update: Whether a cursor update is being programmed
++ *
++ * Grab a reference on the vblank counter if a page flip or cursor update is to
++ * be programmed. Do this before programming so the HW is not in any
++ * idle-optimized state (such as PSR).
++ */
++static void dm_arm_vblank_event_pre_programming(struct amdgpu_crtc *acrtc,
++ struct dm_crtc_state *acrtc_state,
++ bool pflip_update,
++ bool cursor_update)
++{
++ assert_spin_locked(&acrtc->base.dev->event_lock);
++
++ if (!acrtc->base.state->event || acrtc_state->active_planes == 0)
++ return;
++
++ if (pflip_update || cursor_update)
++ drm_crtc_vblank_get(&acrtc->base);
++}
++
+ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
+ struct drm_device *dev,
+ struct amdgpu_display_manager *dm,
+@@ -9969,16 +9980,19 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
+ }
+ }
+
+- /*
+- * DCE depends on a combination of GRPH_FLIP, VLINE0, and VUPDATE for
+- * event delivery. Only GRPH_FLIP handler can send pflip events, and it
+- * only fires if HW latched to the flip. Maintain legacy behavior by
+- * arming event before programming.
+- */
+- if (amdgpu_ip_version(dm->adev, DCE_HWIP, 0) == 0) {
+- scoped_guard(spinlock_irqsave, &pcrtc->dev->event_lock) {
++ scoped_guard(spinlock_irqsave, &pcrtc->dev->event_lock) {
++ dm_arm_vblank_event_pre_programming(acrtc_attach, acrtc_state,
++ pflip_present,
++ cursor_update);
++ /*
++ * DCE depends on a combination of GRPH_FLIP, VLINE0, and
++ * VUPDATE for event delivery. Only GRPH_FLIP handler can send
++ * pflip events, and it only fires if HW latched to the flip.
++ * Maintain legacy behavior by arming event before programming.
++ */
++ if (amdgpu_ip_version(dm->adev, DCE_HWIP, 0) == 0) {
+ dm_arm_vblank_event(acrtc_attach, acrtc_state,
+- pflip_present, cursor_update);
++ pflip_present, cursor_update);
+ }
+ }
+
+--
+2.53.0
+
net-handshake-hand-off-the-pinned-file-reference-to-.patch
net-handshake-close-the-submit-side-sock_hold-race.patch
net-handshake-drain-pending-requests-at-net-namespac.patch
+drm-amd-display-check-grph_flip-status-before-sendin.patch
+drm-amd-display-exit-idle-optimizations-before-progr.patch
--- /dev/null
+From 03feabd751443ed9e2102ae995226dae041ddb15 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 29 Jul 2026 13:37:43 -0400
+Subject: drm/amd/display: check GRPH_FLIP status before sending event
+
+From: Leo Li <sunpeng.li@amd.com>
+
+[ Upstream commit 48ab86360af117123eb1b15e38f068acf3826400 ]
+
+[Why]
+
+After unifying DCN interrupt sources under VUPDATE_NO_LOCK, we have two
+remaining issues to clean up:
+
+1. On DCN, flip completion is now delivered from VUPDATE_NO_LOCK
+ (dm_crtc_high_irq_handler) instead of GRPH_PFLIP. But VUPDATE_NO_LOCK
+ fires every frame, regardless of whether a flip has latched.
+
+2. There is a window during commit where a flip is armed (pflip_status =
+ SUBMITTED) but not yet programmed into HW. If the VUPDATE_NO_LOCK
+ fires in that window, its handler would deliver a flip event to
+ userspace before HW has latched to it. If userspace then renders to
+ what it believes is now the back buffer (but HW is still latched to
+ it!), it will cause display corruption. This issue seemed to have
+ been introduced by:
+ commit 1159898a88db ("drm/amd/display: Handle commit plane with no FB.")
+ Enabling replay or psr extended the duration of this window, and
+ hence made corruption more likely to be observed.
+
+[How]
+
+* Move acrtc->event/pflip_status arming to after
+ update_planes_and_stream_adapter() has programmed the flip into HW.
+ This closes the window where pflip_status is SUBMITTED but the flip is
+ not yet programmed.
+
+* Add dc_get_flip_pending_on_otg(), which reads the HUBP flip-pending
+ status straight from HW for the pipe(s) bound to an OTG instance. It
+ is keyed only by otg_inst and does not take or mutate a
+ dc_plane_state, so it is safe to call from the OTG interrupt handler
+ without racing a concurrent commit that may be modifying plane state.
+
+* Optimistically query for flip-pending after programming, in the event
+ that HW latched to the new fb between programming start and arming
+ event. If it latched, send the vblank event immediately, rather than
+ wait for the next vblank IRQ.
+
+* In the VUPDATE_NO_LOCK handler, only deliver flip completion once
+ dc_get_flip_pending_on_otg() reports the flip is no longer pending.
+ Otherwise leave the flip armed and retry on the next vupdate.
+
+* For DCE, maintain the existing behavior of arming flips before
+ programming, and relying on GRPH_FLIP to fire at HW latch.
+
+v2:
+* Drop flip_programmed completion object, instead move
+ event/pflip_status arming after programming.
+* For DCN, optimistically query for flip pending immediately after
+ programming, and if it latched, send event right away.
+
+v3:
+* Fix event timestamps on optimistic flip latch detection, where it's
+ possible for it to run *before* the vupdate IRQ updates the timestamp.
+* Add more docstrings for DCN vblank handling.
+* Clean up if conditions in dm_arm_vblank_event().
+* Code style cleanup on braces surrounding multi-line statements.
+
+Fixes: 9b47278cec98 ("drm/amd/display: temp w/a for dGPU to enter idle optimizations")
+Link: https://gitlab.freedesktop.org/drm/amd/-/work_items/3787
+Link: https://gitlab.freedesktop.org/drm/amd/-/work_items/4141
+Assisted-by: Copilot:claude-opus-4.8
+Tested-by: Mario Limonciello (AMD) <superm1@kernel.org>
+Reviewed-by: Mario Limonciello (AMD) <superm1@kernel.org>
+Signed-off-by: Leo Li <sunpeng.li@amd.com>
+Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
+(cherry picked from commit f64a9be5653689ff43e148cd8a6483077488c8e5)
+Cc: stable@vger.kernel.org # 8382cd234981: drm/amd/display: consolidate DCN vblank/flip handling onto vupdate_no_lock
+Cc: stable@vger.kernel.org
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 191 ++++++++++++++----
+ drivers/gpu/drm/amd/display/dc/core/dc.c | 45 +++++
+ drivers/gpu/drm/amd/display/dc/dc.h | 1 +
+ 3 files changed, 197 insertions(+), 40 deletions(-)
+
+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 93cd70183c188..11affddbb2650 100644
+--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+@@ -677,13 +677,30 @@ static void dm_crtc_high_irq_handler(struct amdgpu_device *adev,
+ * Deliver pageflip completion events (DCN only).
+ *
+ * Since GRPH_PFLIP is not used, VUPDATE_NO_LOCK is the flip latch
+- * point. Deliver any pending pageflip completion event from here.
++ * point. Deliver any pending pageflip completion event from here,
++ * once HW has consumed the new address (the OTG no longer reports a
++ * pending flip).
+ *
+- * NOTE: This can deliver an event for a flip that was armed but not yet
+- * programmed into HW; that race is closed in a follow-up change by
+- * checking the programmed flip status.
++ * Also handle the case here where there aren't any active planes and
++ * DCN HUBP may be clock-gated, so the flip-pending status may be
++ * undefined.
+ */
+- if (is_dcn && acrtc->pflip_status == AMDGPU_FLIP_SUBMITTED) {
++ if (is_dcn && acrtc->pflip_status == AMDGPU_FLIP_SUBMITTED &&
++ acrtc->event) {
++
++ if (!dc_get_flip_pending_on_otg(adev->dm.dc, acrtc->otg_inst)) {
++ drm_crtc_send_vblank_event(&acrtc->base, acrtc->event);
++ acrtc->event = NULL;
++ drm_crtc_vblank_put(&acrtc->base);
++ acrtc->pflip_status = AMDGPU_FLIP_NONE;
++ }
++ /*
++ * If the flip is still pending, leave it armed and
++ * retry on the next vupdate.
++ */
++ } else if (is_dcn && acrtc->pflip_status == AMDGPU_FLIP_SUBMITTED &&
++ acrtc->dm_irq_params.active_planes == 0) {
++
+ if (acrtc->event) {
+ drm_crtc_send_vblank_event(&acrtc->base, acrtc->event);
+ acrtc->event = NULL;
+@@ -9995,6 +10012,28 @@ static void amdgpu_dm_enable_self_refresh(struct amdgpu_crtc *acrtc_attach,
+ }
+ }
+
++static void dm_arm_vblank_event(struct amdgpu_crtc *acrtc,
++ struct dm_crtc_state *acrtc_state,
++ bool pflip_update,
++ bool cursor_update)
++{
++ assert_spin_locked(&acrtc->base.dev->event_lock);
++
++ if (!acrtc->base.state->event || acrtc_state->active_planes == 0)
++ return;
++
++ if (pflip_update) {
++ drm_crtc_vblank_get(&acrtc->base);
++ WARN_ON(acrtc->pflip_status != AMDGPU_FLIP_NONE);
++ /* Arm flip completion handling and event delivery after programming. */
++ prepare_flip_isr(acrtc);
++ } else if (cursor_update) {
++ drm_crtc_vblank_get(&acrtc->base);
++ acrtc->event = acrtc->base.state->event;
++ acrtc->base.state->event = NULL;
++ }
++}
++
+ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
+ struct drm_device *dev,
+ struct amdgpu_display_manager *dm,
+@@ -10018,6 +10057,7 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
+ bool cursor_update = false;
+ bool pflip_present = false;
+ bool immediate_flip = false;
++ bool flip_latched_during_prog = false;
+ bool dirty_rects_changed = false;
+ bool updated_planes_and_streams = false;
+ struct {
+@@ -10254,39 +10294,24 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
+ usleep_range(1000, 1100);
+ }
+
+- /**
+- * Prepare the flip event for the pageflip interrupt to handle.
+- *
+- * This only works in the case where we've already turned on the
+- * appropriate hardware blocks (eg. HUBP) so in the transition case
+- * from 0 -> n planes we have to skip a hardware generated event
+- * and rely on sending it from software.
+- */
+- if (acrtc_attach->base.state->event &&
+- acrtc_state->active_planes > 0) {
+- drm_crtc_vblank_get(pcrtc);
+-
+- spin_lock_irqsave(&pcrtc->dev->event_lock, flags);
+-
+- WARN_ON(acrtc_attach->pflip_status != AMDGPU_FLIP_NONE);
+- prepare_flip_isr(acrtc_attach);
+-
+- spin_unlock_irqrestore(&pcrtc->dev->event_lock, flags);
+- }
+-
+ if (acrtc_state->stream) {
+ if (acrtc_state->freesync_vrr_info_changed)
+ bundle->stream_update.vrr_infopacket =
+ &acrtc_state->stream->vrr_infopacket;
+ }
+- } else if (cursor_update && acrtc_state->active_planes > 0) {
+- spin_lock_irqsave(&pcrtc->dev->event_lock, flags);
+- if (acrtc_attach->base.state->event) {
+- drm_crtc_vblank_get(pcrtc);
+- acrtc_attach->event = acrtc_attach->base.state->event;
+- acrtc_attach->base.state->event = NULL;
++ }
++
++ /*
++ * DCE depends on a combination of GRPH_FLIP, VLINE0, and VUPDATE for
++ * event delivery. Only GRPH_FLIP handler can send pflip events, and it
++ * only fires if HW latched to the flip. Maintain legacy behavior by
++ * arming event before programming.
++ */
++ if (amdgpu_ip_version(dm->adev, DCE_HWIP, 0) == 0) {
++ scoped_guard(spinlock_irqsave, &pcrtc->dev->event_lock) {
++ dm_arm_vblank_event(acrtc_attach, acrtc_state,
++ pflip_present, cursor_update);
+ }
+- spin_unlock_irqrestore(&pcrtc->dev->event_lock, flags);
+ }
+
+ /* Update the planes if changed or disable if we don't have any. */
+@@ -10388,17 +10413,103 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
+ amdgpu_dm_commit_cursors(state);
+
+ /*
+- * On DCN, flip completion is normally delivered from VUPDATE_NO_LOCK.
+- * However, an immediate (tearing / async) flip is latched by HW right
+- * away and does not wait for the next vupdate, so deliver its
+- * completion event here after programming.
++ * DCN specific vblank handling
++ * ============================
++ *
++ * With the event_lock held, arm the vblank event, and determine whether
++ * deliver it immediately, or in VUPDATE_NO_LOCK IRQ (i.e. HW latch
++ * point) handler. Do this *after* programming so that the IRQ handler
++ * will not deliver the event before HW laches onto the programmed
++ * values:
++ *
++ * Commit thread IRQ handler HW
++ * -----------------------------------------------------------------
++ * arm_vblank_event()
++ * vupdate()
++ * vupdate_handler()
++ * cook_timestamp()
++ * # prev flip already latched,
++ * # so flip_latched == true.
++ * if event_armed && flip_latched:
++ * send_vblank_event()
++ * # sent before latch, **BAD!**
++ * hw_program()
++ * vupdate()
++ * **latch**
++ *
++ * There's a consequence of arming after: it's possible for HW to latch
++ * between start of HW programming and acrtc->event/pflip_status arming.
++ * When this happens, the IRQ handler will send the event on the next
++ * immediate latch point, even though HW has already latched. This is
++ * handled by optimistically checking for HW latch after programming,
++ * and if latched, send the event immediately:
+ *
+- * On DCE, GRPH_PFLIP already fires immediately for immediate flips, so
+- * this is DCN-only.
++ * Commit thread IRQ handler HW
++ * -----------------------------------------------------------------
++ * hw_program()
++ * vupdate()
++ * **latch**
++ * vupdate_handler()
++ * cook_timestamp()
++ * # event_armed == false
++ * # **no event sent!**
++ * arm_vblank_event()
++ * if flip_latched:
++ * **send_vblank_event()**
++ * disarm_vblank_event()
++ *
++ * The IRQ handler is expected to cook the timestamp, but we need to
++ * cook the timestamp before optimistic sending as well. That's because
++ * the following sequence is possible:
++ *
++ * Commit thread IRQ handler HW
++ * -----------------------------------------------------------------
++ * hw_program()
++ * arm_vblank_event()
++ * vupdate()
++ * **latch**
++ * if flip_latched:
++ * # Need cook before send!
++ * **cook_timestamp()**
++ * send_vblank_event()
++ * disarm_vblank_event()
++ * vupdate_handler()
++ * cook_timestamp()
++ * # event_armed == false
++ * # no event sent!
++ *
++ * Cooking twice is OK, since DRM scanout accurate timestamps report A)
++ * the previous vactive start if currently in vactive, or B) the next
++ * vactive start if currently in vblank (see &get_vblank_counter). 'A)'
++ * is what we want for the optimistic send, and for 'B)', we'll cook a
++ * timestamp no later than the next IRQ handler run.
++ *
++ * The more correct fix is to wrap programming and arming with the
++ * event_lock and thus serializing it with the IRQ handler. However,
++ * there are various sleep-waits within
++ * update_planes_and_stream_adapter() that makes spin locking illegal.
++ * And on full updates, it can take 1-2 frame-times to return (see
++ * commit_planes_for_stream).
++ *
++ * On DCE, GRPH_PFLIP IRQ is used and takes care of this.
+ */
+- if (immediate_flip && amdgpu_ip_version(dm->adev, DCE_HWIP, 0) != 0) {
++ if (amdgpu_ip_version(dm->adev, DCE_HWIP, 0) != 0) {
+ spin_lock_irqsave(&pcrtc->dev->event_lock, flags);
+- if (acrtc_attach->pflip_status == AMDGPU_FLIP_SUBMITTED &&
++
++ if (updated_planes_and_streams) {
++ flip_latched_during_prog =
++ !dc_get_flip_pending_on_otg(dm->dc, acrtc_attach->otg_inst);
++ }
++
++ dm_arm_vblank_event(acrtc_attach, acrtc_state,
++ pflip_present, cursor_update);
++
++ /*
++ * Deliver the event immediately on immediate flip, or on a
++ * update that has already latched.
++ */
++ if ((immediate_flip || flip_latched_during_prog) &&
++ acrtc_attach->pflip_status == AMDGPU_FLIP_SUBMITTED &&
+ acrtc_attach->event) {
+ drm_crtc_accurate_vblank_count(&acrtc_attach->base);
+ drm_crtc_send_vblank_event(&acrtc_attach->base,
+diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c
+index ec194dd3b7985..b58ea80a97659 100644
+--- a/drivers/gpu/drm/amd/display/dc/core/dc.c
++++ b/drivers/gpu/drm/amd/display/dc/core/dc.c
+@@ -5628,6 +5628,51 @@ void dc_interrupt_ack(struct dc *dc, enum dc_irq_source src)
+ dal_irq_service_ack(dc->res_pool->irqs, src);
+ }
+
++/*
++ * dc_get_flip_pending_on_otg() - Check if a GRPH_FLIP is still pending on OTG
++ *
++ * @dc: display core context @otg_inst: OTG instance to query
++ *
++ * Reads the HUBP flip-pending status for the pipe(s) bound to @otg_inst,
++ * returning true if any of them has not yet latched its programmed surface
++ * address.
++ *
++ * Unlike dc_plane_get_status(), this does not take or mutate a dc_plane_state,
++ * so it is safe to call from interrupt context without racing a concurrent
++ * commit that may be updating plane state.
++ *
++ * Return: true if a flip is still pending on the OTG, false otherwise.
++ */
++bool dc_get_flip_pending_on_otg(struct dc *dc, int otg_inst)
++{
++ bool flip_pending = false;
++ int i;
++
++ if (!dc || !dc->current_state)
++ return false;
++
++ dc_exit_ips_for_hw_access(dc);
++
++ for (i = 0; i < dc->res_pool->pipe_count; i++) {
++ struct pipe_ctx *pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i];
++ struct hubp *hubp = pipe_ctx->plane_res.hubp;
++
++ if (!pipe_ctx->plane_state || !pipe_ctx->stream_res.tg)
++ continue;
++
++ if (pipe_ctx->stream_res.tg->inst != otg_inst)
++ continue;
++
++ if (hubp && hubp->funcs->hubp_is_flip_pending &&
++ hubp->funcs->hubp_is_flip_pending(hubp)) {
++ flip_pending = true;
++ break;
++ }
++ }
++
++ return flip_pending;
++}
++
+ void dc_power_down_on_boot(struct dc *dc)
+ {
+ if (dc->ctx->dce_environment != DCE_ENV_VIRTUAL_HW &&
+diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h
+index 61a28e287c1b9..65388a6926b63 100644
+--- a/drivers/gpu/drm/amd/display/dc/dc.h
++++ b/drivers/gpu/drm/amd/display/dc/dc.h
+@@ -2738,6 +2738,7 @@ enum dc_irq_source dc_interrupt_to_irq_source(
+ uint32_t ext_id);
+ bool dc_interrupt_set(struct dc *dc, enum dc_irq_source src, bool enable);
+ void dc_interrupt_ack(struct dc *dc, enum dc_irq_source src);
++bool dc_get_flip_pending_on_otg(struct dc *dc, int otg_inst);
+ enum dc_irq_source dc_get_hpd_irq_source_at_index(
+ struct dc *dc, uint32_t link_index);
+
+--
+2.53.0
+
--- /dev/null
+From 5ea1eebeab0239eeec86e3f0456001f7e5eca9a2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 5 Aug 2026 12:33:14 -0400
+Subject: drm/amd/display: Exit idle optimizations before programming
+
+From: Leo Li <sunpeng.li@amd.com>
+
+[ Upstream commit 8419331e64d92a8de5fc4feef0e305f201fb8b33 ]
+
+[Why]
+
+We need to exit PSR/IPS before programming. Before calling DC for
+programming in amdgpu_dm_commit_planes(), there's a
+vblank_control_workqueue flush. This waits for IPS and PSR exit. (See
+drm_vblank_on/off() > amdgpu_dm_crtc_set_vblank() --queue_work()->
+amdgpu_dm_crtc_vblank_control_worker())
+
+Prior to the tagged "Fixes:" change, drm_vblank_get() was called before
+the workqueue flush. This ordering ensures that PSR exit occurred before
+programming. After the "Fixes:" change, drm_vblank_get() is called after
+the workqueue flush, leading to programming while idle optimizations are
+still active. This can lead to incorrect flip_pending detection used by
+vblank event delivery.
+
+[How]
+
+Split the vblank_get() component of `dm_arm_vblank_event()` into
+`dm_arm_vblank_event_pre_programming()`, which is called before
+programming. Call it before the vblank_control_workqueue flush.
+
+Includes a drive-by cleanup of prepare_flip_isr(): the only caller is
+dm_arm_vblank_event() and it's simple enough to roll-in.
+
+v2: Fix checkpatch formatting warning on
+ drm_arm_vblank_event_pre_programming() arg alignment.
+
+Fixes: 48ab86360af1 ("drm/amd/display: check GRPH_FLIP status before sending event")
+Cc: stable@vger.kernel.org
+Link: https://gitlab.freedesktop.org/drm/amd/-/work_items/4141#note_3583205
+Link: https://gitlab.freedesktop.org/drm/amd/-/work_items/5527
+Assisted-by: Codex:gpt-5.6-sol
+Assisted-by: Claude:opus-5
+Suggested-by: David Weber <weber.aulendorf@gmail.com>
+Signed-off-by: Leo Li <sunpeng.li@amd.com>
+Reviewed-by: Mario Limonciello (AMD) <superm1@kernel.org>
+Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
+(cherry picked from commit 05984e29520a28c27f5a2388742c957a6a87ee7a)
+(cherry picked from commit 8419331e64d92a8de5fc4feef0e305f201fb8b33)
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 78 +++++++++++--------
+ 1 file changed, 46 insertions(+), 32 deletions(-)
+
+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 11affddbb2650..40d82a3eeec05 100644
+--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+@@ -9656,25 +9656,6 @@ static void remove_stream(struct amdgpu_device *adev,
+ acrtc->enabled = false;
+ }
+
+-static void prepare_flip_isr(struct amdgpu_crtc *acrtc)
+-{
+-
+- assert_spin_locked(&acrtc->base.dev->event_lock);
+- WARN_ON(acrtc->event);
+-
+- acrtc->event = acrtc->base.state->event;
+-
+- /* Set the flip status */
+- acrtc->pflip_status = AMDGPU_FLIP_SUBMITTED;
+-
+- /* Mark this event as consumed */
+- acrtc->base.state->event = NULL;
+-
+- drm_dbg_state(acrtc->base.dev,
+- "crtc:%d, pflip_stat:AMDGPU_FLIP_SUBMITTED\n",
+- acrtc->crtc_id);
+-}
+-
+ static void update_freesync_state_on_stream(
+ struct amdgpu_display_manager *dm,
+ struct dm_crtc_state *new_crtc_state,
+@@ -10023,17 +10004,47 @@ static void dm_arm_vblank_event(struct amdgpu_crtc *acrtc,
+ return;
+
+ if (pflip_update) {
+- drm_crtc_vblank_get(&acrtc->base);
+ WARN_ON(acrtc->pflip_status != AMDGPU_FLIP_NONE);
+- /* Arm flip completion handling and event delivery after programming. */
+- prepare_flip_isr(acrtc);
++ WARN_ON(acrtc->event);
++
++ acrtc->pflip_status = AMDGPU_FLIP_SUBMITTED;
++ acrtc->event = acrtc->base.state->event;
++ acrtc->base.state->event = NULL;
++
++ drm_dbg_state(acrtc->base.dev,
++ "crtc:%d, pflip_stat:AMDGPU_FLIP_SUBMITTED\n",
++ acrtc->crtc_id);
+ } else if (cursor_update) {
+- drm_crtc_vblank_get(&acrtc->base);
+ acrtc->event = acrtc->base.state->event;
+ acrtc->base.state->event = NULL;
+ }
+ }
+
++/**
++ * dm_arm_vblank_event_pre_programming - Prepare for programming
++ * @acrtc: The amdgpu CRTC to prepare
++ * @acrtc_state: The new CRTC state
++ * @pflip_update: Whether a page flip is being programmed
++ * @cursor_update: Whether a cursor update is being programmed
++ *
++ * Grab a reference on the vblank counter if a page flip or cursor update is to
++ * be programmed. Do this before programming so the HW is not in any
++ * idle-optimized state (such as PSR).
++ */
++static void dm_arm_vblank_event_pre_programming(struct amdgpu_crtc *acrtc,
++ struct dm_crtc_state *acrtc_state,
++ bool pflip_update,
++ bool cursor_update)
++{
++ assert_spin_locked(&acrtc->base.dev->event_lock);
++
++ if (!acrtc->base.state->event || acrtc_state->active_planes == 0)
++ return;
++
++ if (pflip_update || cursor_update)
++ drm_crtc_vblank_get(&acrtc->base);
++}
++
+ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
+ struct drm_device *dev,
+ struct amdgpu_display_manager *dm,
+@@ -10301,16 +10312,19 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
+ }
+ }
+
+- /*
+- * DCE depends on a combination of GRPH_FLIP, VLINE0, and VUPDATE for
+- * event delivery. Only GRPH_FLIP handler can send pflip events, and it
+- * only fires if HW latched to the flip. Maintain legacy behavior by
+- * arming event before programming.
+- */
+- if (amdgpu_ip_version(dm->adev, DCE_HWIP, 0) == 0) {
+- scoped_guard(spinlock_irqsave, &pcrtc->dev->event_lock) {
++ scoped_guard(spinlock_irqsave, &pcrtc->dev->event_lock) {
++ dm_arm_vblank_event_pre_programming(acrtc_attach, acrtc_state,
++ pflip_present,
++ cursor_update);
++ /*
++ * DCE depends on a combination of GRPH_FLIP, VLINE0, and
++ * VUPDATE for event delivery. Only GRPH_FLIP handler can send
++ * pflip events, and it only fires if HW latched to the flip.
++ * Maintain legacy behavior by arming event before programming.
++ */
++ if (amdgpu_ip_version(dm->adev, DCE_HWIP, 0) == 0) {
+ dm_arm_vblank_event(acrtc_attach, acrtc_state,
+- pflip_present, cursor_update);
++ pflip_present, cursor_update);
+ }
+ }
+
+--
+2.53.0
+
drm-exec-remove-the-index-parameter-from-drm_exec_for_each_locked_obj.patch
drm-xe-wait-on-external-bo-kernel-fences-in-exec-ioctl.patch
media-chips-media-wave5-support-cbp-profile.patch
+drm-amd-display-check-grph_flip-status-before-sendin.patch
+drm-amd-display-exit-idle-optimizations-before-progr.patch