--- /dev/null
+From stable+bounces-260838-greg=kroah.com@vger.kernel.org Sat Jun 6 04:18:53 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 5 Jun 2026 22:18:39 -0400
+Subject: drm/i915/psr: Use DC_OFF wake reference to block DC6 on vblank enable
+To: stable@vger.kernel.org
+Cc: "Jouni Högander" <jouni.hogander@intel.com>, "Michał Grzelak" <michal.grzelak@intel.com>, "Tvrtko Ursulin" <tursulin@ursulin.net>, "Sasha Levin" <sashal@kernel.org>
+Message-ID: <20260606021839.2487627-2-sashal@kernel.org>
+
+From: Jouni Högander <jouni.hogander@intel.com>
+
+[ Upstream commit 3549a9649dc7c5fc586ab12f675279283cdcb2a7 ]
+
+We are observing following warnings:
+
+*ERROR* power well DC_off state mismatch (refcount 0/enabled 1)
+
+gen9_dc_off_power_well_enabled is considering target state DC_STATE_DISABLE
+as DC_OFF power well being enabled. Fix this by using wakeref for the
+purpose.
+
+To achieve this we need to modify notification code as well. Currently it
+is possible that PSR gets notified vblank enable/disable twice on same
+status. This is currently not a problem as it is just triggering call to
+intel_display_power_set_target_dc_state with same target state as a
+parameter. When using wakeref this becomes a problem due to reference
+counting. Fix this storing vbank status on last notification and use that
+to ensure there are no more than one notification with same vblank status.
+
+v2: ensure there is no subsequent notifications with same status
+
+Fixes: aa451abcffb5 ("drm/i915/display: Prevent DC6 while vblank is enabled for Panel Replay")
+Cc: <stable@vger.kernel.org> # v6.13+
+Signed-off-by: Jouni Högander <jouni.hogander@intel.com>
+Reviewed-by: Michał Grzelak <michal.grzelak@intel.com>
+Link: https://patch.msgid.link/20260520104944.239797-2-jouni.hogander@intel.com
+(cherry picked from commit 35485ac56d878192a3829a58cb26503125ec7104)
+Signed-off-by: Tvrtko Ursulin <tursulin@ursulin.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/i915/display/intel_display_core.h | 1
+ drivers/gpu/drm/i915/display/intel_display_irq.c | 8 +++++--
+ drivers/gpu/drm/i915/display/intel_display_types.h | 2 +
+ drivers/gpu/drm/i915/display/intel_psr.c | 24 +++++++--------------
+ 4 files changed, 18 insertions(+), 17 deletions(-)
+
+--- a/drivers/gpu/drm/i915/display/intel_display_core.h
++++ b/drivers/gpu/drm/i915/display/intel_display_core.h
+@@ -472,6 +472,7 @@ struct intel_display {
+ u8 vblank_enabled;
+
+ int vblank_enable_count;
++ bool vblank_status_last_notified;
+
+ struct work_struct vblank_notify_work;
+
+--- a/drivers/gpu/drm/i915/display/intel_display_irq.c
++++ b/drivers/gpu/drm/i915/display/intel_display_irq.c
+@@ -1707,8 +1707,12 @@ static void intel_display_vblank_notify_
+ struct intel_display *display =
+ container_of(work, typeof(*display), irq.vblank_notify_work);
+ int vblank_enable_count = READ_ONCE(display->irq.vblank_enable_count);
++ bool vblank_status = !!vblank_enable_count;
+
+- intel_psr_notify_vblank_enable_disable(display, vblank_enable_count);
++ if (display->irq.vblank_status_last_notified != vblank_status) {
++ intel_psr_notify_vblank_enable_disable(display, vblank_status);
++ display->irq.vblank_status_last_notified = vblank_status;
++ }
+ }
+
+ int bdw_enable_vblank(struct drm_crtc *_crtc)
+@@ -1721,10 +1725,10 @@ int bdw_enable_vblank(struct drm_crtc *_
+ if (gen11_dsi_configure_te(crtc, true))
+ return 0;
+
++ spin_lock_irqsave(&display->irq.lock, irqflags);
+ if (crtc->vblank_psr_notify && display->irq.vblank_enable_count++ == 0)
+ schedule_work(&display->irq.vblank_notify_work);
+
+- spin_lock_irqsave(&display->irq.lock, irqflags);
+ bdw_enable_pipe_irq(display, pipe, GEN8_PIPE_VBLANK);
+ spin_unlock_irqrestore(&display->irq.lock, irqflags);
+
+--- a/drivers/gpu/drm/i915/display/intel_display_types.h
++++ b/drivers/gpu/drm/i915/display/intel_display_types.h
+@@ -1690,6 +1690,8 @@ struct intel_psr {
+ bool pkg_c_latency_used;
+
+ u8 active_non_psr_pipes;
++
++ struct ref_tracker *vblank_wakeref;
+ };
+
+ struct intel_dp {
+--- a/drivers/gpu/drm/i915/display/intel_psr.c
++++ b/drivers/gpu/drm/i915/display/intel_psr.c
+@@ -3982,14 +3982,20 @@ void intel_psr_notify_vblank_enable_disa
+ bool enable)
+ {
+ struct intel_encoder *encoder;
+- bool block_dc_states = false;
+
+ for_each_intel_encoder_with_psr(display->drm, encoder) {
+ struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
+
+ mutex_lock(&intel_dp->psr.lock);
+- if (CAN_PANEL_REPLAY(intel_dp))
+- block_dc_states = true;
++ if (CAN_PANEL_REPLAY(intel_dp)) {
++ if (enable)
++ intel_dp->psr.vblank_wakeref =
++ intel_display_power_get(display,
++ POWER_DOMAIN_DC_OFF);
++ else
++ intel_display_power_put(display, POWER_DOMAIN_DC_OFF,
++ intel_dp->psr.vblank_wakeref);
++ }
+
+ if (intel_dp->psr.enabled && !intel_dp->psr.panel_replay_enabled &&
+ intel_dp->psr.pkg_c_latency_used)
+@@ -3997,18 +4003,6 @@ void intel_psr_notify_vblank_enable_disa
+
+ mutex_unlock(&intel_dp->psr.lock);
+ }
+-
+- /*
+- * NOTE: intel_display_power_set_target_dc_state is used
+- * only by PSR code for DC3CO handling. DC3CO target
+- * state is currently disabled in * PSR code. If DC3CO
+- * is taken into use we need take that into account here
+- * as well.
+- */
+- if (block_dc_states)
+- intel_display_power_set_target_dc_state(display, enable ?
+- DC_STATE_DISABLE :
+- DC_STATE_EN_UPTO_DC6);
+ }
+
+ static void
--- /dev/null
+From c58e9456e30c7098cbcd9f04571992be8a2e4e63 Mon Sep 17 00:00:00 2001
+From: Jassi Brar <jassisinghbrar@gmail.com>
+Date: Fri, 27 Mar 2026 17:00:40 -0500
+Subject: mailbox: Fix NULL message support in mbox_send_message()
+
+From: Jassi Brar <jassisinghbrar@gmail.com>
+
+commit c58e9456e30c7098cbcd9f04571992be8a2e4e63 upstream.
+
+The active_req field serves double duty as both the "is a TX in
+flight" flag (NULL means idle) and the storage for the in-flight
+message pointer. When a client sends NULL via mbox_send_message(),
+active_req is set to NULL, which the framework misinterprets as
+"no active request". This breaks the TX state machine by:
+
+ - tx_tick() short-circuits on (!mssg), skipping the tx_done
+ callback and the tx_complete completion
+ - txdone_hrtimer() skips the channel entirely since active_req
+ is NULL, so poll-based TX-done detection never fires.
+
+Fix this by introducing a MBOX_NO_MSG sentinel value that means
+"no active request," freeing NULL to be valid message data. The
+sentinel is defined in the subsystem-internal mailbox.h so that
+controller drivers within drivers/mailbox/ can reference it, but
+it is not exposed to clients outside the subsystem.
+
+Fifteen in-tree callers send NULL (doorbell-style IPCs on Qualcomm,
+Tegra, TI, Xilinx, i.MX, SCMI, and PCC platforms). All were
+audited for regression:
+
+ - Most already work around the bug via knows_txdone=true with a
+ manual mbox_client_txdone() call, making the framework's
+ tracking irrelevant. These are unaffected.
+
+ - Poll-based callers (Xilinx zynqmp/r5) are strictly better off:
+ the poll timer now correctly detects NULL-active channels
+ instead of silently skipping them.
+
+ - irq-qcom-mpm.c was a pre-existing bug -- the only Qualcomm
+ caller that omitted the knows_txdone + mbox_client_txdone()
+ pattern. Fixed in a companion commit ("irqchip/qcom-mpm: Fix
+ missing mailbox TX done acknowledgment").
+
+ - No caller sets both a tx_done callback and sends NULL, nor
+ combines tx_block=true with NULL sends, so the newly reachable
+ callback/completion paths are never exercised.
+
+Also update tegra-hsp's flush callback, which directly inspects
+active_req to wait for the channel to drain: the old "!= NULL"
+check becomes "!= MBOX_NO_MSG", otherwise flush spins until
+timeout since the sentinel is non-NULL.
+
+The only tradeoff is that 'MBOX_NO_MSG' can not be used as a message
+by clients.
+
+Reported-by: Joonwon Kang <joonwonkang@google.com>
+Reviewed-by: Douglas Anderson <dianders@chromium.org>
+Signed-off-by: Jassi Brar <jassisinghbrar@gmail.com>
+Signed-off-by: Joonwon Kang <joonwonkang@google.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/mailbox/mailbox.c | 15 ++++++++-------
+ drivers/mailbox/tegra-hsp.c | 2 +-
+ include/linux/mailbox_controller.h | 3 +++
+ 3 files changed, 12 insertions(+), 8 deletions(-)
+
+--- a/drivers/mailbox/mailbox.c
++++ b/drivers/mailbox/mailbox.c
+@@ -52,7 +52,7 @@ static void msg_submit(struct mbox_chan
+ int err = -EBUSY;
+
+ scoped_guard(spinlock_irqsave, &chan->lock) {
+- if (!chan->msg_count || chan->active_req)
++ if (!chan->msg_count || chan->active_req != MBOX_NO_MSG)
+ break;
+
+ count = chan->msg_count;
+@@ -87,13 +87,13 @@ static void tx_tick(struct mbox_chan *ch
+
+ scoped_guard(spinlock_irqsave, &chan->lock) {
+ mssg = chan->active_req;
+- chan->active_req = NULL;
++ chan->active_req = MBOX_NO_MSG;
+ }
+
+ /* Submit next message */
+ msg_submit(chan);
+
+- if (!mssg)
++ if (mssg == MBOX_NO_MSG)
+ return;
+
+ /* Notify the client */
+@@ -114,7 +114,7 @@ static enum hrtimer_restart txdone_hrtim
+ for (i = 0; i < mbox->num_chans; i++) {
+ struct mbox_chan *chan = &mbox->chans[i];
+
+- if (chan->active_req && chan->cl) {
++ if (chan->active_req != MBOX_NO_MSG && chan->cl) {
+ txdone = chan->mbox->ops->last_tx_done(chan);
+ if (txdone)
+ tx_tick(chan, 0);
+@@ -246,7 +246,7 @@ int mbox_send_message(struct mbox_chan *
+ {
+ int t;
+
+- if (!chan || !chan->cl)
++ if (!chan || !chan->cl || mssg == MBOX_NO_MSG)
+ return -EINVAL;
+
+ t = add_to_rbuf(chan, mssg);
+@@ -319,7 +319,7 @@ static int __mbox_bind_client(struct mbo
+ scoped_guard(spinlock_irqsave, &chan->lock) {
+ chan->msg_free = 0;
+ chan->msg_count = 0;
+- chan->active_req = NULL;
++ chan->active_req = MBOX_NO_MSG;
+ chan->cl = cl;
+ init_completion(&chan->tx_complete);
+
+@@ -477,7 +477,7 @@ void mbox_free_channel(struct mbox_chan
+ /* The queued TX requests are simply aborted, no callbacks are made */
+ scoped_guard(spinlock_irqsave, &chan->lock) {
+ chan->cl = NULL;
+- chan->active_req = NULL;
++ chan->active_req = MBOX_NO_MSG;
+ if (chan->txdone_method == TXDONE_BY_ACK)
+ chan->txdone_method = TXDONE_BY_POLL;
+ }
+@@ -531,6 +531,7 @@ int mbox_controller_register(struct mbox
+
+ chan->cl = NULL;
+ chan->mbox = mbox;
++ chan->active_req = MBOX_NO_MSG;
+ chan->txdone_method = txdone;
+ spin_lock_init(&chan->lock);
+ }
+--- a/drivers/mailbox/tegra-hsp.c
++++ b/drivers/mailbox/tegra-hsp.c
+@@ -497,7 +497,7 @@ static int tegra_hsp_mailbox_flush(struc
+ mbox_chan_txdone(chan, 0);
+
+ /* Wait until channel is empty */
+- if (chan->active_req != NULL)
++ if (chan->active_req != MBOX_NO_MSG)
+ continue;
+
+ return 0;
+--- a/include/linux/mailbox_controller.h
++++ b/include/linux/mailbox_controller.h
+@@ -11,6 +11,9 @@
+
+ struct mbox_chan;
+
++/* Sentinel value distinguishing "no active request" from "NULL message data" */
++#define MBOX_NO_MSG ((void *)-1)
++
+ /**
+ * struct mbox_chan_ops - methods to control mailbox channels
+ * @send_data: The API asks the MBOX controller driver, in atomic
mm-slub-hold-cpus_read_lock-around-flush_rcu_sheaves_on_cache.patch
net-phy-micrel-fix-lan8814-qsgmii-soft-reset.patch
xhci-tegra-fix-ghost-usb-device-on-dual-role-port-un.patch
+mailbox-fix-null-message-support-in-mbox_send_message.patch
+drm-i915-psr-use-dc_off-wake-reference-to-block-dc6-on-vblank-enable.patch
+thunderbolt-property-cap-recursion-depth-in-__tb_property_parse_dir.patch
--- /dev/null
+From stable+bounces-260779-greg=kroah.com@vger.kernel.org Fri Jun 5 21:33:43 2026
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 5 Jun 2026 15:30:29 -0400
+Subject: thunderbolt: property: Cap recursion depth in __tb_property_parse_dir()
+To: stable@vger.kernel.org
+Cc: Michael Bommarito <michael.bommarito@gmail.com>, Mika Westerberg <mika.westerberg@linux.intel.com>, Sasha Levin <sashal@kernel.org>
+Message-ID: <20260605193029.2155718-1-sashal@kernel.org>
+
+From: Michael Bommarito <michael.bommarito@gmail.com>
+
+[ Upstream commit 928abe19fbf0127003abcb1ea69cabc1c897d0ab ]
+
+A DIRECTORY entry's value field is used as the dir_offset for a
+recursive call into __tb_property_parse_dir() with no depth counter.
+A crafted peer that chains DIRECTORY entries into a back-reference
+loop drives the parser until the kernel stack is exhausted and the
+guard page fires. Any untrusted XDomain peer (cable, dock, in-line
+inspector, adjacent host) that reaches the PROPERTIES_REQUEST
+control-plane exchange can trigger this without authentication.
+
+Thread a depth counter through tb_property_parse() and
+__tb_property_parse_dir(), and reject blocks that exceed
+TB_PROPERTY_MAX_DEPTH = 8. That is comfortably larger than any
+observed legitimate XDomain layout.
+
+Operators who do not need XDomain host-to-host discovery can disable
+the path entirely with thunderbolt.xdomain=0 on the kernel command
+line.
+
+Fixes: cdae7c07e3e3 ("thunderbolt: Add support for XDomain properties")
+Cc: stable@vger.kernel.org
+Assisted-by: Claude:claude-opus-4-6
+Assisted-by: Codex:gpt-5-4
+Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
+Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/thunderbolt/property.c | 18 ++++++++++++------
+ 1 file changed, 12 insertions(+), 6 deletions(-)
+
+--- a/drivers/thunderbolt/property.c
++++ b/drivers/thunderbolt/property.c
+@@ -35,10 +35,11 @@ struct tb_property_dir_entry {
+ };
+
+ #define TB_PROPERTY_ROOTDIR_MAGIC 0x55584401
++#define TB_PROPERTY_MAX_DEPTH 8
+
+ static struct tb_property_dir *__tb_property_parse_dir(const u32 *block,
+ size_t block_len, unsigned int dir_offset, size_t dir_len,
+- bool is_root);
++ bool is_root, unsigned int depth);
+
+ static inline void parse_dwdata(void *dst, const void *src, size_t dwords)
+ {
+@@ -97,7 +98,8 @@ tb_property_alloc(const char *key, enum
+ }
+
+ static struct tb_property *tb_property_parse(const u32 *block, size_t block_len,
+- const struct tb_property_entry *entry)
++ const struct tb_property_entry *entry,
++ unsigned int depth)
+ {
+ char key[TB_PROPERTY_KEY_SIZE + 1];
+ struct tb_property *property;
+@@ -118,7 +120,7 @@ static struct tb_property *tb_property_p
+ switch (property->type) {
+ case TB_PROPERTY_TYPE_DIRECTORY:
+ dir = __tb_property_parse_dir(block, block_len, entry->value,
+- entry->length, false);
++ entry->length, false, depth + 1);
+ if (!dir) {
+ kfree(property);
+ return NULL;
+@@ -163,13 +165,17 @@ static struct tb_property *tb_property_p
+ }
+
+ static struct tb_property_dir *__tb_property_parse_dir(const u32 *block,
+- size_t block_len, unsigned int dir_offset, size_t dir_len, bool is_root)
++ size_t block_len, unsigned int dir_offset, size_t dir_len, bool is_root,
++ unsigned int depth)
+ {
+ const struct tb_property_entry *entries;
+ size_t i, content_len, nentries;
+ unsigned int content_offset;
+ struct tb_property_dir *dir;
+
++ if (depth > TB_PROPERTY_MAX_DEPTH)
++ return NULL;
++
+ dir = kzalloc(sizeof(*dir), GFP_KERNEL);
+ if (!dir)
+ return NULL;
+@@ -200,7 +206,7 @@ static struct tb_property_dir *__tb_prop
+ for (i = 0; i < nentries; i++) {
+ struct tb_property *property;
+
+- property = tb_property_parse(block, block_len, &entries[i]);
++ property = tb_property_parse(block, block_len, &entries[i], depth);
+ if (!property) {
+ tb_property_free_dir(dir);
+ return NULL;
+@@ -239,7 +245,7 @@ struct tb_property_dir *tb_property_pars
+ return NULL;
+
+ return __tb_property_parse_dir(block, block_len, 0, rootdir->length,
+- true);
++ true, 0);
+ }
+
+ /**