--- /dev/null
+From 7db8c5f5f47c0929261eb5653f1d6e7711951213 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 2 May 2024 13:32:17 -0500
+Subject: dm/amd/pm: Fix problems with reboot/shutdown for some SMU
+ 13.0.4/13.0.11 users
+
+From: Mario Limonciello <mario.limonciello@amd.com>
+
+[ Upstream commit cd94d1b182d2986378550c9087571991bfee01d4 ]
+
+Limit the workaround introduced by commit 31729e8c21ec ("drm/amd/pm: fixes
+a random hang in S4 for SMU v13.0.4/11") to only run in the s4 path.
+
+Cc: Tim Huang <Tim.Huang@amd.com>
+Fixes: 31729e8c21ec ("drm/amd/pm: fixes a random hang in S4 for SMU v13.0.4/11")
+Closes: https://gitlab.freedesktop.org/drm/amd/-/issues/3351
+Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
+Acked-by: Alex Deucher <alexander.deucher@amd.com>
+Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c
+index 21b374d121819..5de31961319a2 100644
+--- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c
++++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c
+@@ -222,7 +222,7 @@ static int smu_v13_0_4_system_features_control(struct smu_context *smu, bool en)
+ struct amdgpu_device *adev = smu->adev;
+ int ret = 0;
+
+- if (!en && !adev->in_s0ix) {
++ if (!en && adev->in_s4) {
+ /* Adds a GFX reset as workaround just before sending the
+ * MP1_UNLOAD message to prevent GC/RLC/PMFW from entering
+ * an invalid state.
+--
+2.43.0
+
--- /dev/null
+From 2cec74df8726d6168fa21139ac575585e013436c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 2 May 2024 15:32:35 -0700
+Subject: drm/connector: Add \n to message about demoting connector
+ force-probes
+
+From: Douglas Anderson <dianders@chromium.org>
+
+[ Upstream commit 6897204ea3df808d342c8e4613135728bc538bcd ]
+
+The debug print clearly lacks a \n at the end. Add it.
+
+Fixes: 8f86c82aba8b ("drm/connector: demote connector force-probes for non-master clients")
+Reviewed-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
+Reviewed-by: Simon Ser <contact@emersion.fr>
+Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
+Signed-off-by: Douglas Anderson <dianders@chromium.org>
+Link: https://patchwork.freedesktop.org/patch/msgid/20240502153234.1.I2052f01c8d209d9ae9c300b87c6e4f60bd3cc99e@changeid
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/drm_connector.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
+index 27de2a97f1d11..3d18d840ef3b6 100644
+--- a/drivers/gpu/drm/drm_connector.c
++++ b/drivers/gpu/drm/drm_connector.c
+@@ -2707,7 +2707,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
+ dev->mode_config.max_width,
+ dev->mode_config.max_height);
+ else
+- drm_dbg_kms(dev, "User-space requested a forced probe on [CONNECTOR:%d:%s] but is not the DRM master, demoting to read-only probe",
++ drm_dbg_kms(dev, "User-space requested a forced probe on [CONNECTOR:%d:%s] but is not the DRM master, demoting to read-only probe\n",
+ connector->base.id, connector->name);
+ }
+
+--
+2.43.0
+
--- /dev/null
+From 090becb1cae45046b79f2336406a40951808c8c6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Apr 2024 18:02:54 +0200
+Subject: drm/meson: dw-hdmi: add bandgap setting for g12
+
+From: Jerome Brunet <jbrunet@baylibre.com>
+
+[ Upstream commit 08001033121dd92b8297a5b7333636b466c30f13 ]
+
+When no mode is set, the utility pin appears to be grounded. No signal
+is getting through.
+
+This is problematic because ARC and eARC use this line and may do so even
+if no display mode is set.
+
+This change enable the bandgap setting on g12 chip, which fix the problem
+with the utility pin. This is done by restoring init values on PHY init and
+disable.
+
+Fixes: 3b7c1237a72a ("drm/meson: Add G12A support for the DW-HDMI Glue")
+Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
+Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
+Link: https://lore.kernel.org/r/20240426160256.3089978-3-jbrunet@baylibre.com
+Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
+Link: https://patchwork.freedesktop.org/patch/msgid/20240426160256.3089978-3-jbrunet@baylibre.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/meson/meson_dw_hdmi.c | 43 ++++++++++++++++-----------
+ 1 file changed, 26 insertions(+), 17 deletions(-)
+
+diff --git a/drivers/gpu/drm/meson/meson_dw_hdmi.c b/drivers/gpu/drm/meson/meson_dw_hdmi.c
+index f8dd22d6e6c62..2c8e978eb9ab9 100644
+--- a/drivers/gpu/drm/meson/meson_dw_hdmi.c
++++ b/drivers/gpu/drm/meson/meson_dw_hdmi.c
+@@ -105,6 +105,8 @@
+ #define HHI_HDMI_CLK_CNTL 0x1cc /* 0x73 */
+ #define HHI_HDMI_PHY_CNTL0 0x3a0 /* 0xe8 */
+ #define HHI_HDMI_PHY_CNTL1 0x3a4 /* 0xe9 */
++#define PHY_CNTL1_INIT 0x03900000
++#define PHY_INVERT BIT(17)
+ #define HHI_HDMI_PHY_CNTL2 0x3a8 /* 0xea */
+ #define HHI_HDMI_PHY_CNTL3 0x3ac /* 0xeb */
+ #define HHI_HDMI_PHY_CNTL4 0x3b0 /* 0xec */
+@@ -129,6 +131,8 @@ struct meson_dw_hdmi_data {
+ unsigned int addr);
+ void (*dwc_write)(struct meson_dw_hdmi *dw_hdmi,
+ unsigned int addr, unsigned int data);
++ u32 cntl0_init;
++ u32 cntl1_init;
+ };
+
+ struct meson_dw_hdmi {
+@@ -458,7 +462,9 @@ static void dw_hdmi_phy_disable(struct dw_hdmi *hdmi,
+
+ DRM_DEBUG_DRIVER("\n");
+
+- regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0);
++ /* Fallback to init mode */
++ regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL1, dw_hdmi->data->cntl1_init);
++ regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, dw_hdmi->data->cntl0_init);
+ }
+
+ static enum drm_connector_status dw_hdmi_read_hpd(struct dw_hdmi *hdmi,
+@@ -576,11 +582,22 @@ static const struct regmap_config meson_dw_hdmi_regmap_config = {
+ .fast_io = true,
+ };
+
+-static const struct meson_dw_hdmi_data meson_dw_hdmi_gx_data = {
++static const struct meson_dw_hdmi_data meson_dw_hdmi_gxbb_data = {
+ .top_read = dw_hdmi_top_read,
+ .top_write = dw_hdmi_top_write,
+ .dwc_read = dw_hdmi_dwc_read,
+ .dwc_write = dw_hdmi_dwc_write,
++ .cntl0_init = 0x0,
++ .cntl1_init = PHY_CNTL1_INIT | PHY_INVERT,
++};
++
++static const struct meson_dw_hdmi_data meson_dw_hdmi_gxl_data = {
++ .top_read = dw_hdmi_top_read,
++ .top_write = dw_hdmi_top_write,
++ .dwc_read = dw_hdmi_dwc_read,
++ .dwc_write = dw_hdmi_dwc_write,
++ .cntl0_init = 0x0,
++ .cntl1_init = PHY_CNTL1_INIT,
+ };
+
+ static const struct meson_dw_hdmi_data meson_dw_hdmi_g12a_data = {
+@@ -588,6 +605,8 @@ static const struct meson_dw_hdmi_data meson_dw_hdmi_g12a_data = {
+ .top_write = dw_hdmi_g12a_top_write,
+ .dwc_read = dw_hdmi_g12a_dwc_read,
+ .dwc_write = dw_hdmi_g12a_dwc_write,
++ .cntl0_init = 0x000b4242, /* Bandgap */
++ .cntl1_init = PHY_CNTL1_INIT,
+ };
+
+ static void meson_dw_hdmi_init(struct meson_dw_hdmi *meson_dw_hdmi)
+@@ -626,18 +645,8 @@ static void meson_dw_hdmi_init(struct meson_dw_hdmi *meson_dw_hdmi)
+ meson_dw_hdmi->data->top_write(meson_dw_hdmi, HDMITX_TOP_BIST_CNTL, BIT(12));
+
+ /* Setup PHY */
+- regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1,
+- 0xffff << 16, 0x0390 << 16);
+-
+- /* BIT_INVERT */
+- if (dw_hdmi_is_compatible(meson_dw_hdmi, "amlogic,meson-gxl-dw-hdmi") ||
+- dw_hdmi_is_compatible(meson_dw_hdmi, "amlogic,meson-gxm-dw-hdmi") ||
+- dw_hdmi_is_compatible(meson_dw_hdmi, "amlogic,meson-g12a-dw-hdmi"))
+- regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1,
+- BIT(17), 0);
+- else
+- regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1,
+- BIT(17), BIT(17));
++ regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL1, meson_dw_hdmi->data->cntl1_init);
++ regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, meson_dw_hdmi->data->cntl0_init);
+
+ /* Enable HDMI-TX Interrupt */
+ meson_dw_hdmi->data->top_write(meson_dw_hdmi, HDMITX_TOP_INTR_STAT_CLR,
+@@ -866,11 +875,11 @@ static const struct dev_pm_ops meson_dw_hdmi_pm_ops = {
+
+ static const struct of_device_id meson_dw_hdmi_of_table[] = {
+ { .compatible = "amlogic,meson-gxbb-dw-hdmi",
+- .data = &meson_dw_hdmi_gx_data },
++ .data = &meson_dw_hdmi_gxbb_data },
+ { .compatible = "amlogic,meson-gxl-dw-hdmi",
+- .data = &meson_dw_hdmi_gx_data },
++ .data = &meson_dw_hdmi_gxl_data },
+ { .compatible = "amlogic,meson-gxm-dw-hdmi",
+- .data = &meson_dw_hdmi_gx_data },
++ .data = &meson_dw_hdmi_gxl_data },
+ { .compatible = "amlogic,meson-g12a-dw-hdmi",
+ .data = &meson_dw_hdmi_g12a_data },
+ { }
+--
+2.43.0
+
--- /dev/null
+From 87349332241afc89095b3afaa1beba4445955157 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 26 Apr 2024 18:02:53 +0200
+Subject: drm/meson: dw-hdmi: power up phy on device init
+
+From: Jerome Brunet <jbrunet@baylibre.com>
+
+[ Upstream commit 04703bfd7f99c016a823c74712b97f8b5590ce87 ]
+
+The phy is not in a useful state right after init. It will become useful,
+including for auxiliary function such as CEC or ARC, after the first mode
+is set. This is a problem on systems where the display is using another
+interface like DSI or CVBS.
+
+This change refactor the init and mode change callback to power up the PHY
+on init and leave only what is necessary for mode changes in the related
+function. This is enough to fix CEC operation when HDMI display is not
+enabled.
+
+Fixes: 3f68be7d8e96 ("drm/meson: Add support for HDMI encoder and DW-HDMI bridge + PHY")
+Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
+Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
+Link: https://lore.kernel.org/r/20240426160256.3089978-2-jbrunet@baylibre.com
+Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
+Link: https://patchwork.freedesktop.org/patch/msgid/20240426160256.3089978-2-jbrunet@baylibre.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/meson/meson_dw_hdmi.c | 51 +++++++++------------------
+ 1 file changed, 17 insertions(+), 34 deletions(-)
+
+diff --git a/drivers/gpu/drm/meson/meson_dw_hdmi.c b/drivers/gpu/drm/meson/meson_dw_hdmi.c
+index 5cd2b2ebbbd33..f8dd22d6e6c62 100644
+--- a/drivers/gpu/drm/meson/meson_dw_hdmi.c
++++ b/drivers/gpu/drm/meson/meson_dw_hdmi.c
+@@ -384,26 +384,6 @@ static int dw_hdmi_phy_init(struct dw_hdmi *hdmi, void *data,
+ drm_mode_is_420_also(display, mode)))
+ mode_is_420 = true;
+
+- /* Enable clocks */
+- regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL, 0xffff, 0x100);
+-
+- /* Bring HDMITX MEM output of power down */
+- regmap_update_bits(priv->hhi, HHI_MEM_PD_REG0, 0xff << 8, 0);
+-
+- /* Bring out of reset */
+- dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_SW_RESET, 0);
+-
+- /* Enable internal pixclk, tmds_clk, spdif_clk, i2s_clk, cecclk */
+- dw_hdmi_top_write_bits(dw_hdmi, HDMITX_TOP_CLK_CNTL,
+- 0x3, 0x3);
+-
+- /* Enable cec_clk and hdcp22_tmdsclk_en */
+- dw_hdmi_top_write_bits(dw_hdmi, HDMITX_TOP_CLK_CNTL,
+- 0x3 << 4, 0x3 << 4);
+-
+- /* Enable normal output to PHY */
+- dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_BIST_CNTL, BIT(12));
+-
+ /* TMDS pattern setup */
+ if (mode->clock > 340000 && !mode_is_420) {
+ dw_hdmi->data->top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_01,
+@@ -425,20 +405,6 @@ static int dw_hdmi_phy_init(struct dw_hdmi *hdmi, void *data,
+ /* Setup PHY parameters */
+ meson_hdmi_phy_setup_mode(dw_hdmi, mode, mode_is_420);
+
+- /* Setup PHY */
+- regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1,
+- 0xffff << 16, 0x0390 << 16);
+-
+- /* BIT_INVERT */
+- if (dw_hdmi_is_compatible(dw_hdmi, "amlogic,meson-gxl-dw-hdmi") ||
+- dw_hdmi_is_compatible(dw_hdmi, "amlogic,meson-gxm-dw-hdmi") ||
+- dw_hdmi_is_compatible(dw_hdmi, "amlogic,meson-g12a-dw-hdmi"))
+- regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1,
+- BIT(17), 0);
+- else
+- regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1,
+- BIT(17), BIT(17));
+-
+ /* Disable clock, fifo, fifo_wr */
+ regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1, 0xf, 0);
+
+@@ -656,6 +622,23 @@ static void meson_dw_hdmi_init(struct meson_dw_hdmi *meson_dw_hdmi)
+ meson_dw_hdmi->data->top_write(meson_dw_hdmi,
+ HDMITX_TOP_CLK_CNTL, 0xff);
+
++ /* Enable normal output to PHY */
++ meson_dw_hdmi->data->top_write(meson_dw_hdmi, HDMITX_TOP_BIST_CNTL, BIT(12));
++
++ /* Setup PHY */
++ regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1,
++ 0xffff << 16, 0x0390 << 16);
++
++ /* BIT_INVERT */
++ if (dw_hdmi_is_compatible(meson_dw_hdmi, "amlogic,meson-gxl-dw-hdmi") ||
++ dw_hdmi_is_compatible(meson_dw_hdmi, "amlogic,meson-gxm-dw-hdmi") ||
++ dw_hdmi_is_compatible(meson_dw_hdmi, "amlogic,meson-g12a-dw-hdmi"))
++ regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1,
++ BIT(17), 0);
++ else
++ regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1,
++ BIT(17), BIT(17));
++
+ /* Enable HDMI-TX Interrupt */
+ meson_dw_hdmi->data->top_write(meson_dw_hdmi, HDMITX_TOP_INTR_STAT_CLR,
+ HDMITX_TOP_INTR_CORE);
+--
+2.43.0
+
--- /dev/null
+From 2c81ffa7940adaf7dc9066df192897fc9b355048 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 7 Oct 2022 16:44:44 +0300
+Subject: gpiolib: cdev: Add missing header(s)
+
+From: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+
+[ Upstream commit 52ee7c02f67808afa533c523fa3e4b66c54ea758 ]
+
+Do not imply that some of the generic headers may be always included.
+Instead, include explicitly what we are direct user of.
+
+While at it, sort headers alphabetically.
+
+Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Rewiewed-by: Kent Gibson <warthog618@gmail.com>
+Stable-dep-of: ee0166b637a5 ("gpiolib: cdev: fix uninitialised kfifo")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpio/gpiolib-cdev.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c
+index e40c93f0960b4..d2027212901fd 100644
+--- a/drivers/gpio/gpiolib-cdev.c
++++ b/drivers/gpio/gpiolib-cdev.c
+@@ -12,6 +12,7 @@
+ #include <linux/file.h>
+ #include <linux/gpio.h>
+ #include <linux/gpio/driver.h>
++#include <linux/hte.h>
+ #include <linux/interrupt.h>
+ #include <linux/irqreturn.h>
+ #include <linux/kernel.h>
+@@ -20,11 +21,12 @@
+ #include <linux/mutex.h>
+ #include <linux/pinctrl/consumer.h>
+ #include <linux/poll.h>
++#include <linux/seq_file.h>
+ #include <linux/spinlock.h>
+ #include <linux/timekeeping.h>
+ #include <linux/uaccess.h>
+ #include <linux/workqueue.h>
+-#include <linux/hte.h>
++
+ #include <uapi/linux/gpio.h>
+
+ #include "gpiolib.h"
+--
+2.43.0
+
--- /dev/null
+From 26d491f510ffd0020ee6dae46ff2451c87486208 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 10 May 2024 14:53:42 +0800
+Subject: gpiolib: cdev: fix uninitialised kfifo
+
+From: Kent Gibson <warthog618@gmail.com>
+
+[ Upstream commit ee0166b637a5e376118e9659e5b4148080f1d27e ]
+
+If a line is requested with debounce, and that results in debouncing
+in software, and the line is subsequently reconfigured to enable edge
+detection then the allocation of the kfifo to contain edge events is
+overlooked. This results in events being written to and read from an
+uninitialised kfifo. Read events are returned to userspace.
+
+Initialise the kfifo in the case where the software debounce is
+already active.
+
+Fixes: 65cff7046406 ("gpiolib: cdev: support setting debounce")
+Signed-off-by: Kent Gibson <warthog618@gmail.com>
+Link: https://lore.kernel.org/r/20240510065342.36191-1-warthog618@gmail.com
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpio/gpiolib-cdev.c | 14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+
+diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c
+index 6ee1074d49152..97e8335716b01 100644
+--- a/drivers/gpio/gpiolib-cdev.c
++++ b/drivers/gpio/gpiolib-cdev.c
+@@ -1201,6 +1201,8 @@ static int edge_detector_update(struct line *line,
+ struct gpio_v2_line_config *lc,
+ unsigned int line_idx, u64 edflags)
+ {
++ u64 eflags;
++ int ret;
+ u64 active_edflags = READ_ONCE(line->edflags);
+ unsigned int debounce_period_us =
+ gpio_v2_line_config_debounce_period(lc, line_idx);
+@@ -1212,6 +1214,18 @@ static int edge_detector_update(struct line *line,
+ /* sw debounced and still will be...*/
+ if (debounce_period_us && READ_ONCE(line->sw_debounced)) {
+ line_set_debounce_period(line, debounce_period_us);
++ /*
++ * ensure event fifo is initialised if edge detection
++ * is now enabled.
++ */
++ eflags = edflags & GPIO_V2_LINE_EDGE_FLAGS;
++ if (eflags && !kfifo_initialized(&line->req->events)) {
++ ret = kfifo_alloc(&line->req->events,
++ line->req->event_buffer_size,
++ GFP_KERNEL);
++ if (ret)
++ return ret;
++ }
+ return 0;
+ }
+
+--
+2.43.0
+
--- /dev/null
+From 9a429c745b3bd6c169b7066ae2593c90fae8da6b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 19 Dec 2023 08:41:54 +0800
+Subject: gpiolib: cdev: relocate debounce_period_us from struct gpio_desc
+
+From: Kent Gibson <warthog618@gmail.com>
+
+[ Upstream commit 9344e34e7992fec95ce6210d95ac01437dd327ab ]
+
+Store the debounce period for a requested line locally, rather than in
+the debounce_period_us field in the gpiolib struct gpio_desc.
+
+Add a global tree of lines containing supplemental line information
+to make the debounce period available to be reported by the
+GPIO_V2_GET_LINEINFO_IOCTL and the line change notifier.
+
+Signed-off-by: Kent Gibson <warthog618@gmail.com>
+Reviewed-by: Andy Shevchenko <andy@kernel.org>
+Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
+Stable-dep-of: ee0166b637a5 ("gpiolib: cdev: fix uninitialised kfifo")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpio/gpiolib-cdev.c | 165 +++++++++++++++++++++++++++++++-----
+ 1 file changed, 142 insertions(+), 23 deletions(-)
+
+diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c
+index d2027212901fd..6ee1074d49152 100644
+--- a/drivers/gpio/gpiolib-cdev.c
++++ b/drivers/gpio/gpiolib-cdev.c
+@@ -5,6 +5,7 @@
+ #include <linux/bitmap.h>
+ #include <linux/build_bug.h>
+ #include <linux/cdev.h>
++#include <linux/cleanup.h>
+ #include <linux/compat.h>
+ #include <linux/compiler.h>
+ #include <linux/device.h>
+@@ -21,6 +22,7 @@
+ #include <linux/mutex.h>
+ #include <linux/pinctrl/consumer.h>
+ #include <linux/poll.h>
++#include <linux/rbtree.h>
+ #include <linux/seq_file.h>
+ #include <linux/spinlock.h>
+ #include <linux/timekeeping.h>
+@@ -465,6 +467,7 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
+
+ /**
+ * struct line - contains the state of a requested line
++ * @node: to store the object in supinfo_tree if supplemental
+ * @desc: the GPIO descriptor for this line.
+ * @req: the corresponding line request
+ * @irq: the interrupt triggered in response to events on this GPIO
+@@ -477,6 +480,7 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
+ * @line_seqno: the seqno for the current edge event in the sequence of
+ * events for this line.
+ * @work: the worker that implements software debouncing
++ * @debounce_period_us: the debounce period in microseconds
+ * @sw_debounced: flag indicating if the software debouncer is active
+ * @level: the current debounced physical level of the line
+ * @hdesc: the Hardware Timestamp Engine (HTE) descriptor
+@@ -485,6 +489,7 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
+ * @last_seqno: the last sequence number before debounce period expires
+ */
+ struct line {
++ struct rb_node node;
+ struct gpio_desc *desc;
+ /*
+ * -- edge detector specific fields --
+@@ -518,6 +523,15 @@ struct line {
+ * -- debouncer specific fields --
+ */
+ struct delayed_work work;
++ /*
++ * debounce_period_us is accessed by debounce_irq_handler() and
++ * process_hw_ts() which are disabled when modified by
++ * debounce_setup(), edge_detector_setup() or edge_detector_stop()
++ * or can live with a stale version when updated by
++ * edge_detector_update().
++ * The modifying functions are themselves mutually exclusive.
++ */
++ unsigned int debounce_period_us;
+ /*
+ * sw_debounce is accessed by linereq_set_config(), which is the
+ * only setter, and linereq_get_values(), which can live with a
+@@ -550,6 +564,17 @@ struct line {
+ #endif /* CONFIG_HTE */
+ };
+
++/*
++ * a rbtree of the struct lines containing supplemental info.
++ * Used to populate gpio_v2_line_info with cdev specific fields not contained
++ * in the struct gpio_desc.
++ * A line is determined to contain supplemental information by
++ * line_has_supinfo().
++ */
++static struct rb_root supinfo_tree = RB_ROOT;
++/* covers supinfo_tree */
++static DEFINE_SPINLOCK(supinfo_lock);
++
+ /**
+ * struct linereq - contains the state of a userspace line request
+ * @gdev: the GPIO device the line request pertains to
+@@ -562,7 +587,8 @@ struct line {
+ * this line request. Note that this is not used when @num_lines is 1, as
+ * the line_seqno is then the same and is cheaper to calculate.
+ * @config_mutex: mutex for serializing ioctl() calls to ensure consistency
+- * of configuration, particularly multi-step accesses to desc flags.
++ * of configuration, particularly multi-step accesses to desc flags and
++ * changes to supinfo status.
+ * @lines: the lines held by this line request, with @num_lines elements.
+ */
+ struct linereq {
+@@ -577,6 +603,103 @@ struct linereq {
+ struct line lines[];
+ };
+
++static void supinfo_insert(struct line *line)
++{
++ struct rb_node **new = &(supinfo_tree.rb_node), *parent = NULL;
++ struct line *entry;
++
++ guard(spinlock)(&supinfo_lock);
++
++ while (*new) {
++ entry = container_of(*new, struct line, node);
++
++ parent = *new;
++ if (line->desc < entry->desc) {
++ new = &((*new)->rb_left);
++ } else if (line->desc > entry->desc) {
++ new = &((*new)->rb_right);
++ } else {
++ /* this should never happen */
++ WARN(1, "duplicate line inserted");
++ return;
++ }
++ }
++
++ rb_link_node(&line->node, parent, new);
++ rb_insert_color(&line->node, &supinfo_tree);
++}
++
++static void supinfo_erase(struct line *line)
++{
++ guard(spinlock)(&supinfo_lock);
++
++ rb_erase(&line->node, &supinfo_tree);
++}
++
++static struct line *supinfo_find(struct gpio_desc *desc)
++{
++ struct rb_node *node = supinfo_tree.rb_node;
++ struct line *line;
++
++ while (node) {
++ line = container_of(node, struct line, node);
++ if (desc < line->desc)
++ node = node->rb_left;
++ else if (desc > line->desc)
++ node = node->rb_right;
++ else
++ return line;
++ }
++ return NULL;
++}
++
++static void supinfo_to_lineinfo(struct gpio_desc *desc,
++ struct gpio_v2_line_info *info)
++{
++ struct gpio_v2_line_attribute *attr;
++ struct line *line;
++
++ guard(spinlock)(&supinfo_lock);
++
++ line = supinfo_find(desc);
++ if (!line)
++ return;
++
++ attr = &info->attrs[info->num_attrs];
++ attr->id = GPIO_V2_LINE_ATTR_ID_DEBOUNCE;
++ attr->debounce_period_us = READ_ONCE(line->debounce_period_us);
++ info->num_attrs++;
++}
++
++static inline bool line_has_supinfo(struct line *line)
++{
++ return READ_ONCE(line->debounce_period_us);
++}
++
++/*
++ * Checks line_has_supinfo() before and after the change to avoid unnecessary
++ * supinfo_tree access.
++ * Called indirectly by linereq_create() or linereq_set_config() so line
++ * is already protected from concurrent changes.
++ */
++static void line_set_debounce_period(struct line *line,
++ unsigned int debounce_period_us)
++{
++ bool was_suppl = line_has_supinfo(line);
++
++ WRITE_ONCE(line->debounce_period_us, debounce_period_us);
++
++ /* if supinfo status is unchanged then we're done */
++ if (line_has_supinfo(line) == was_suppl)
++ return;
++
++ /* supinfo status has changed, so update the tree */
++ if (was_suppl)
++ supinfo_erase(line);
++ else
++ supinfo_insert(line);
++}
++
+ #define GPIO_V2_LINE_BIAS_FLAGS \
+ (GPIO_V2_LINE_FLAG_BIAS_PULL_UP | \
+ GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN | \
+@@ -714,7 +837,7 @@ static enum hte_return process_hw_ts(struct hte_ts_data *ts, void *p)
+ line->total_discard_seq++;
+ line->last_seqno = ts->seq;
+ mod_delayed_work(system_wq, &line->work,
+- usecs_to_jiffies(READ_ONCE(line->desc->debounce_period_us)));
++ usecs_to_jiffies(READ_ONCE(line->debounce_period_us)));
+ } else {
+ if (unlikely(ts->seq < line->line_seqno))
+ return HTE_CB_HANDLED;
+@@ -855,7 +978,7 @@ static irqreturn_t debounce_irq_handler(int irq, void *p)
+ struct line *line = p;
+
+ mod_delayed_work(system_wq, &line->work,
+- usecs_to_jiffies(READ_ONCE(line->desc->debounce_period_us)));
++ usecs_to_jiffies(READ_ONCE(line->debounce_period_us)));
+
+ return IRQ_HANDLED;
+ }
+@@ -937,7 +1060,7 @@ static int debounce_setup(struct line *line, unsigned int debounce_period_us)
+ /* try hardware */
+ ret = gpiod_set_debounce(line->desc, debounce_period_us);
+ if (!ret) {
+- WRITE_ONCE(line->desc->debounce_period_us, debounce_period_us);
++ line_set_debounce_period(line, debounce_period_us);
+ return ret;
+ }
+ if (ret != -ENOTSUPP)
+@@ -1016,8 +1139,7 @@ static void edge_detector_stop(struct line *line)
+ cancel_delayed_work_sync(&line->work);
+ WRITE_ONCE(line->sw_debounced, 0);
+ WRITE_ONCE(line->edflags, 0);
+- if (line->desc)
+- WRITE_ONCE(line->desc->debounce_period_us, 0);
++ line_set_debounce_period(line, 0);
+ /* do not change line->level - see comment in debounced_value() */
+ }
+
+@@ -1042,7 +1164,7 @@ static int edge_detector_setup(struct line *line,
+ ret = debounce_setup(line, debounce_period_us);
+ if (ret)
+ return ret;
+- WRITE_ONCE(line->desc->debounce_period_us, debounce_period_us);
++ line_set_debounce_period(line, debounce_period_us);
+ }
+
+ /* detection disabled or sw debouncer will provide edge detection */
+@@ -1084,12 +1206,12 @@ static int edge_detector_update(struct line *line,
+ gpio_v2_line_config_debounce_period(lc, line_idx);
+
+ if ((active_edflags == edflags) &&
+- (READ_ONCE(line->desc->debounce_period_us) == debounce_period_us))
++ (READ_ONCE(line->debounce_period_us) == debounce_period_us))
+ return 0;
+
+ /* sw debounced and still will be...*/
+ if (debounce_period_us && READ_ONCE(line->sw_debounced)) {
+- WRITE_ONCE(line->desc->debounce_period_us, debounce_period_us);
++ line_set_debounce_period(line, debounce_period_us);
+ return 0;
+ }
+
+@@ -1566,13 +1688,18 @@ static ssize_t linereq_read(struct file *file, char __user *buf,
+
+ static void linereq_free(struct linereq *lr)
+ {
++ struct line *line;
+ unsigned int i;
+
+ for (i = 0; i < lr->num_lines; i++) {
+- if (lr->lines[i].desc) {
+- edge_detector_stop(&lr->lines[i]);
+- gpiod_free(lr->lines[i].desc);
+- }
++ line = &lr->lines[i];
++ if (!line->desc)
++ continue;
++
++ edge_detector_stop(line);
++ if (line_has_supinfo(line))
++ supinfo_erase(line);
++ gpiod_free(line->desc);
+ }
+ kfifo_free(&lr->events);
+ kfree(lr->label);
+@@ -2239,8 +2366,6 @@ static void gpio_desc_to_lineinfo(struct gpio_desc *desc,
+ struct gpio_chip *gc = desc->gdev->chip;
+ bool ok_for_pinctrl;
+ unsigned long flags;
+- u32 debounce_period_us;
+- unsigned int num_attrs = 0;
+
+ memset(info, 0, sizeof(*info));
+ info->offset = gpio_chip_hwgpio(desc);
+@@ -2307,14 +2432,6 @@ static void gpio_desc_to_lineinfo(struct gpio_desc *desc,
+ else if (test_bit(FLAG_EVENT_CLOCK_HTE, &desc->flags))
+ info->flags |= GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE;
+
+- debounce_period_us = READ_ONCE(desc->debounce_period_us);
+- if (debounce_period_us) {
+- info->attrs[num_attrs].id = GPIO_V2_LINE_ATTR_ID_DEBOUNCE;
+- info->attrs[num_attrs].debounce_period_us = debounce_period_us;
+- num_attrs++;
+- }
+- info->num_attrs = num_attrs;
+-
+ spin_unlock_irqrestore(&gpio_lock, flags);
+ }
+
+@@ -2420,6 +2537,7 @@ static int lineinfo_get(struct gpio_chardev_data *cdev, void __user *ip,
+ return -EBUSY;
+ }
+ gpio_desc_to_lineinfo(desc, &lineinfo);
++ supinfo_to_lineinfo(desc, &lineinfo);
+
+ if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) {
+ if (watch)
+@@ -2523,6 +2641,7 @@ static int lineinfo_changed_notify(struct notifier_block *nb,
+ chg.event_type = action;
+ chg.timestamp_ns = ktime_get_ns();
+ gpio_desc_to_lineinfo(desc, &chg.info);
++ supinfo_to_lineinfo(desc, &chg.info);
+
+ ret = kfifo_in_spinlocked(&cdev->events, &chg, 1, &cdev->wait.lock);
+ if (ret)
+--
+2.43.0
+
net-hns3-use-appropriate-barrier-function-after-sett.patch
net-hns3-fix-port-vlan-filter-not-disabled-issue.patch
net-hns3-fix-kernel-crash-when-devlink-reload-during.patch
+drm-meson-dw-hdmi-power-up-phy-on-device-init.patch
+drm-meson-dw-hdmi-add-bandgap-setting-for-g12.patch
+drm-connector-add-n-to-message-about-demoting-connec.patch
+dm-amd-pm-fix-problems-with-reboot-shutdown-for-some.patch
+gpiolib-cdev-add-missing-header-s.patch
+gpiolib-cdev-relocate-debounce_period_us-from-struct.patch
+gpiolib-cdev-fix-uninitialised-kfifo.patch