--- /dev/null
+From 503eccd1d48069fb548593d9b900b3c9181ff279 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 14 Mar 2025 17:20:38 +0100
+Subject: ARM: dts: opos6ul: add ksz8081 phy properties
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Sébastien Szymanski <sebastien.szymanski@armadeus.com>
+
+[ Upstream commit 6e1a7bc8382b0d4208258f7d2a4474fae788dd90 ]
+
+Commit c7e73b5051d6 ("ARM: imx: mach-imx6ul: remove 14x14 EVK specific
+PHY fixup") removed a PHY fixup that setted the clock mode and the LED
+mode.
+Make the Ethernet interface work again by doing as advised in the
+commit's log, set clock mode and the LED mode in the device tree.
+
+Fixes: c7e73b5051d6 ("ARM: imx: mach-imx6ul: remove 14x14 EVK specific PHY fixup")
+Signed-off-by: Sébastien Szymanski <sebastien.szymanski@armadeus.com>
+Reviewed-by: Oleksij Rempel <o.rempel@pengutronix.de>
+Signed-off-by: Shawn Guo <shawnguo@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/arm/boot/dts/imx6ul-imx6ull-opos6ul.dtsi | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/arch/arm/boot/dts/imx6ul-imx6ull-opos6ul.dtsi b/arch/arm/boot/dts/imx6ul-imx6ull-opos6ul.dtsi
+index f2386dcb9ff2c..dda4fa91b2f2c 100644
+--- a/arch/arm/boot/dts/imx6ul-imx6ull-opos6ul.dtsi
++++ b/arch/arm/boot/dts/imx6ul-imx6ull-opos6ul.dtsi
+@@ -40,6 +40,9 @@
+ reg = <1>;
+ interrupt-parent = <&gpio4>;
+ interrupts = <16 IRQ_TYPE_LEVEL_LOW>;
++ micrel,led-mode = <1>;
++ clocks = <&clks IMX6UL_CLK_ENET_REF>;
++ clock-names = "rmii-ref";
+ status = "okay";
+ };
+ };
+--
+2.39.5
+
--- /dev/null
+From 8a3dcc949925103d1f19b2acb48f86a5a5311af4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jan 2025 09:21:27 +0100
+Subject: ASoC: soc-core: Stop using of_property_read_bool() for non-boolean
+ properties
+
+From: Geert Uytterhoeven <geert+renesas@glider.be>
+
+[ Upstream commit 6eab7034579917f207ca6d8e3f4e11e85e0ab7d5 ]
+
+On R-Car:
+
+ OF: /sound: Read of boolean property 'simple-audio-card,bitclock-master' with a value.
+ OF: /sound: Read of boolean property 'simple-audio-card,frame-master' with a value.
+
+or:
+
+ OF: /soc/sound@ec500000/ports/port@0/endpoint: Read of boolean property 'bitclock-master' with a value.
+ OF: /soc/sound@ec500000/ports/port@0/endpoint: Read of boolean property 'frame-master' with a value.
+
+The use of of_property_read_bool() for non-boolean properties is
+deprecated in favor of of_property_present() when testing for property
+presence.
+
+Replace testing for presence before calling of_property_read_u32() by
+testing for an -EINVAL return value from the latter, to simplify the
+code.
+
+Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
+Link: https://patch.msgid.link/db10e96fbda121e7456d70e97a013cbfc9755f4d.1737533954.git.geert+renesas@glider.be
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/soc/soc-core.c | 32 +++++++++++++-------------------
+ 1 file changed, 13 insertions(+), 19 deletions(-)
+
+diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
+index 58e07296144e0..b13370d2ec1d7 100644
+--- a/sound/soc/soc-core.c
++++ b/sound/soc/soc-core.c
+@@ -2837,7 +2837,7 @@ int snd_soc_of_parse_pin_switches(struct snd_soc_card *card, const char *prop)
+ unsigned int i, nb_controls;
+ int ret;
+
+- if (!of_property_read_bool(dev->of_node, prop))
++ if (!of_property_present(dev->of_node, prop))
+ return 0;
+
+ strings = devm_kcalloc(dev, nb_controls_max,
+@@ -2911,23 +2911,17 @@ int snd_soc_of_parse_tdm_slot(struct device_node *np,
+ if (rx_mask)
+ snd_soc_of_get_slot_mask(np, "dai-tdm-slot-rx-mask", rx_mask);
+
+- if (of_property_read_bool(np, "dai-tdm-slot-num")) {
+- ret = of_property_read_u32(np, "dai-tdm-slot-num", &val);
+- if (ret)
+- return ret;
+-
+- if (slots)
+- *slots = val;
+- }
+-
+- if (of_property_read_bool(np, "dai-tdm-slot-width")) {
+- ret = of_property_read_u32(np, "dai-tdm-slot-width", &val);
+- if (ret)
+- return ret;
++ ret = of_property_read_u32(np, "dai-tdm-slot-num", &val);
++ if (ret && ret != -EINVAL)
++ return ret;
++ if (!ret && slots)
++ *slots = val;
+
+- if (slot_width)
+- *slot_width = val;
+- }
++ ret = of_property_read_u32(np, "dai-tdm-slot-width", &val);
++ if (ret && ret != -EINVAL)
++ return ret;
++ if (!ret && slot_width)
++ *slot_width = val;
+
+ return 0;
+ }
+@@ -3183,12 +3177,12 @@ unsigned int snd_soc_daifmt_parse_clock_provider_raw(struct device_node *np,
+ * check "[prefix]frame-master"
+ */
+ snprintf(prop, sizeof(prop), "%sbitclock-master", prefix);
+- bit = of_property_read_bool(np, prop);
++ bit = of_property_present(np, prop);
+ if (bit && bitclkmaster)
+ *bitclkmaster = of_parse_phandle(np, prop, 0);
+
+ snprintf(prop, sizeof(prop), "%sframe-master", prefix);
+- frame = of_property_read_bool(np, prop);
++ frame = of_property_present(np, prop);
+ if (frame && framemaster)
+ *framemaster = of_parse_phandle(np, prop, 0);
+
+--
+2.39.5
+
--- /dev/null
+From 4acb7b8349c6eeb995c887f03f9d39c90707270b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 31 Jul 2024 13:12:58 -0600
+Subject: ASoC: Use of_property_read_bool()
+
+From: Rob Herring (Arm) <robh@kernel.org>
+
+[ Upstream commit 69dd15a8ef0ae494179fd15023aa8172188db6b7 ]
+
+Use of_property_read_bool() to read boolean properties rather than
+of_get_property(). This is part of a larger effort to remove callers
+of of_get_property() and similar functions. of_get_property() leaks
+the DT property data pointer which is a problem for dynamically
+allocated nodes which may be freed.
+
+Signed-off-by: Rob Herring (Arm) <robh@kernel.org>
+Link: https://patch.msgid.link/20240731191312.1710417-20-robh@kernel.org
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Stable-dep-of: 6eab70345799 ("ASoC: soc-core: Stop using of_property_read_bool() for non-boolean properties")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/soc/codecs/ak4613.c | 4 ++--
+ sound/soc/soc-core.c | 8 ++++----
+ 2 files changed, 6 insertions(+), 6 deletions(-)
+
+diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c
+index f75c19ef35511..3f790d1f11a94 100644
+--- a/sound/soc/codecs/ak4613.c
++++ b/sound/soc/codecs/ak4613.c
+@@ -840,14 +840,14 @@ static void ak4613_parse_of(struct ak4613_priv *priv,
+ /* Input 1 - 2 */
+ for (i = 0; i < 2; i++) {
+ snprintf(prop, sizeof(prop), "asahi-kasei,in%d-single-end", i + 1);
+- if (!of_get_property(np, prop, NULL))
++ if (!of_property_read_bool(np, prop))
+ priv->ic |= 1 << i;
+ }
+
+ /* Output 1 - 6 */
+ for (i = 0; i < 6; i++) {
+ snprintf(prop, sizeof(prop), "asahi-kasei,out%d-single-end", i + 1);
+- if (!of_get_property(np, prop, NULL))
++ if (!of_property_read_bool(np, prop))
+ priv->oc |= 1 << i;
+ }
+
+diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
+index 6a4101dc15a54..58e07296144e0 100644
+--- a/sound/soc/soc-core.c
++++ b/sound/soc/soc-core.c
+@@ -3143,10 +3143,10 @@ unsigned int snd_soc_daifmt_parse_format(struct device_node *np,
+ * SND_SOC_DAIFMT_INV_MASK area
+ */
+ snprintf(prop, sizeof(prop), "%sbitclock-inversion", prefix);
+- bit = !!of_get_property(np, prop, NULL);
++ bit = of_property_read_bool(np, prop);
+
+ snprintf(prop, sizeof(prop), "%sframe-inversion", prefix);
+- frame = !!of_get_property(np, prop, NULL);
++ frame = of_property_read_bool(np, prop);
+
+ switch ((bit << 4) + frame) {
+ case 0x11:
+@@ -3183,12 +3183,12 @@ unsigned int snd_soc_daifmt_parse_clock_provider_raw(struct device_node *np,
+ * check "[prefix]frame-master"
+ */
+ snprintf(prop, sizeof(prop), "%sbitclock-master", prefix);
+- bit = !!of_get_property(np, prop, NULL);
++ bit = of_property_read_bool(np, prop);
+ if (bit && bitclkmaster)
+ *bitclkmaster = of_parse_phandle(np, prop, 0);
+
+ snprintf(prop, sizeof(prop), "%sframe-master", prefix);
+- frame = !!of_get_property(np, prop, NULL);
++ frame = of_property_read_bool(np, prop);
+ if (frame && framemaster)
+ *framemaster = of_parse_phandle(np, prop, 0);
+
+--
+2.39.5
+
--- /dev/null
+From 05f4584ff713d6064d7a12ded2952a58cc5c7678 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 25 Mar 2024 18:02:42 +0100
+Subject: cpufreq: intel_pstate: Do not update global.turbo_disabled after
+ initialization
+
+From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+
+[ Upstream commit 0940f1a8011fd69be5082015068e0dc31c800c20 ]
+
+The global.turbo_disabled is updated quite often, especially in the
+passive mode in which case it is updated every time the scheduler calls
+into the driver. However, this is generally not necessary and it adds
+MSR read overhead to scheduler code paths (and that particular MSR is
+slow to read).
+
+For this reason, make the driver read MSR_IA32_MISC_ENABLE_TURBO_DISABLE
+just once at the cpufreq driver registration time and remove all of the
+in-flight updates of global.turbo_disabled.
+
+Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+Acked-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
+Stable-dep-of: ac4e04d9e378 ("cpufreq: intel_pstate: Unchecked MSR aceess in legacy mode")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/cpufreq/intel_pstate.c | 51 ++++++----------------------------
+ 1 file changed, 8 insertions(+), 43 deletions(-)
+
+diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c
+index e26c0b0096080..18d6d2a357ce1 100644
+--- a/drivers/cpufreq/intel_pstate.c
++++ b/drivers/cpufreq/intel_pstate.c
+@@ -172,7 +172,6 @@ struct vid_data {
+ * based on the MSR_IA32_MISC_ENABLE value and whether or
+ * not the maximum reported turbo P-state is different from
+ * the maximum reported non-turbo one.
+- * @turbo_disabled_mf: The @turbo_disabled value reflected by cpuinfo.max_freq.
+ * @min_perf_pct: Minimum capacity limit in percent of the maximum turbo
+ * P-state capacity.
+ * @max_perf_pct: Maximum capacity limit in percent of the maximum turbo
+@@ -181,7 +180,6 @@ struct vid_data {
+ struct global_params {
+ bool no_turbo;
+ bool turbo_disabled;
+- bool turbo_disabled_mf;
+ int max_perf_pct;
+ int min_perf_pct;
+ };
+@@ -559,12 +557,13 @@ static void intel_pstate_hybrid_hwp_adjust(struct cpudata *cpu)
+ cpu->pstate.min_pstate = intel_pstate_freq_to_hwp(cpu, freq);
+ }
+
+-static inline void update_turbo_state(void)
++static bool turbo_is_disabled(void)
+ {
+ u64 misc_en;
+
+ rdmsrl(MSR_IA32_MISC_ENABLE, misc_en);
+- global.turbo_disabled = misc_en & MSR_IA32_MISC_ENABLE_TURBO_DISABLE;
++
++ return !!(misc_en & MSR_IA32_MISC_ENABLE_TURBO_DISABLE);
+ }
+
+ static int min_perf_pct_min(void)
+@@ -1119,40 +1118,16 @@ static void intel_pstate_update_policies(void)
+ static void __intel_pstate_update_max_freq(struct cpudata *cpudata,
+ struct cpufreq_policy *policy)
+ {
+- policy->cpuinfo.max_freq = global.turbo_disabled_mf ?
++ policy->cpuinfo.max_freq = global.turbo_disabled ?
+ cpudata->pstate.max_freq : cpudata->pstate.turbo_freq;
+ refresh_frequency_limits(policy);
+ }
+
+-static void intel_pstate_update_max_freq(unsigned int cpu)
+-{
+- struct cpufreq_policy *policy = cpufreq_cpu_acquire(cpu);
+-
+- if (!policy)
+- return;
+-
+- __intel_pstate_update_max_freq(all_cpu_data[cpu], policy);
+-
+- cpufreq_cpu_release(policy);
+-}
+-
+ static void intel_pstate_update_limits(unsigned int cpu)
+ {
+ mutex_lock(&intel_pstate_driver_lock);
+
+- update_turbo_state();
+- /*
+- * If turbo has been turned on or off globally, policy limits for
+- * all CPUs need to be updated to reflect that.
+- */
+- if (global.turbo_disabled_mf != global.turbo_disabled) {
+- global.turbo_disabled_mf = global.turbo_disabled;
+- arch_set_max_freq_ratio(global.turbo_disabled);
+- for_each_possible_cpu(cpu)
+- intel_pstate_update_max_freq(cpu);
+- } else {
+- cpufreq_update_policy(cpu);
+- }
++ cpufreq_update_policy(cpu);
+
+ mutex_unlock(&intel_pstate_driver_lock);
+ }
+@@ -1252,7 +1227,6 @@ static ssize_t show_no_turbo(struct kobject *kobj,
+ return -EAGAIN;
+ }
+
+- update_turbo_state();
+ if (global.turbo_disabled)
+ ret = sprintf(buf, "%u\n", global.turbo_disabled);
+ else
+@@ -1282,7 +1256,6 @@ static ssize_t store_no_turbo(struct kobject *a, struct kobj_attribute *b,
+
+ mutex_lock(&intel_pstate_limits_lock);
+
+- update_turbo_state();
+ if (global.turbo_disabled) {
+ pr_notice_once("Turbo disabled by BIOS or unavailable on processor\n");
+ mutex_unlock(&intel_pstate_limits_lock);
+@@ -2253,8 +2226,6 @@ static void intel_pstate_adjust_pstate(struct cpudata *cpu)
+ struct sample *sample;
+ int target_pstate;
+
+- update_turbo_state();
+-
+ target_pstate = get_target_pstate(cpu);
+ target_pstate = intel_pstate_prepare_request(cpu, target_pstate);
+ trace_cpu_frequency(target_pstate * cpu->pstate.scaling, cpu->cpu);
+@@ -2572,7 +2543,6 @@ static int intel_pstate_set_policy(struct cpufreq_policy *policy)
+ * be invoked on them.
+ */
+ intel_pstate_clear_update_util_hook(policy->cpu);
+- update_turbo_state();
+ intel_pstate_set_pstate(cpu, pstate);
+ } else {
+ intel_pstate_set_update_util_hook(policy->cpu);
+@@ -2616,7 +2586,6 @@ static void intel_pstate_verify_cpu_policy(struct cpudata *cpu,
+ {
+ int max_freq;
+
+- update_turbo_state();
+ if (hwp_active) {
+ intel_pstate_get_hwp_cap(cpu);
+ max_freq = global.no_turbo || global.turbo_disabled ?
+@@ -2713,8 +2682,6 @@ static int __intel_pstate_cpu_init(struct cpufreq_policy *policy)
+
+ /* cpuinfo and default policy values */
+ policy->cpuinfo.min_freq = cpu->pstate.min_freq;
+- update_turbo_state();
+- global.turbo_disabled_mf = global.turbo_disabled;
+ policy->cpuinfo.max_freq = global.turbo_disabled ?
+ cpu->pstate.max_freq : cpu->pstate.turbo_freq;
+
+@@ -2880,8 +2847,6 @@ static int intel_cpufreq_target(struct cpufreq_policy *policy,
+ struct cpufreq_freqs freqs;
+ int target_pstate;
+
+- update_turbo_state();
+-
+ freqs.old = policy->cur;
+ freqs.new = target_freq;
+
+@@ -2903,8 +2868,6 @@ static unsigned int intel_cpufreq_fast_switch(struct cpufreq_policy *policy,
+ struct cpudata *cpu = all_cpu_data[policy->cpu];
+ int target_pstate;
+
+- update_turbo_state();
+-
+ target_pstate = intel_pstate_freq_to_hwp(cpu, target_freq);
+
+ target_pstate = intel_cpufreq_update_pstate(policy, target_pstate, true);
+@@ -2922,7 +2885,6 @@ static void intel_cpufreq_adjust_perf(unsigned int cpunum,
+ int old_pstate = cpu->pstate.current_pstate;
+ int cap_pstate, min_pstate, max_pstate, target_pstate;
+
+- update_turbo_state();
+ cap_pstate = global.turbo_disabled ? HWP_GUARANTEED_PERF(hwp_cap) :
+ HWP_HIGHEST_PERF(hwp_cap);
+
+@@ -3112,6 +3074,9 @@ static int intel_pstate_register_driver(struct cpufreq_driver *driver)
+
+ memset(&global, 0, sizeof(global));
+ global.max_perf_pct = 100;
++ global.turbo_disabled = turbo_is_disabled();
++
++ arch_set_max_freq_ratio(global.turbo_disabled);
+
+ intel_pstate_driver = driver;
+ ret = cpufreq_register_driver(intel_pstate_driver);
+--
+2.39.5
+
--- /dev/null
+From 5920bf68fdd391d9ca9c68a0da71b7b0e616aa22 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 25 Mar 2024 18:01:58 +0100
+Subject: cpufreq: intel_pstate: Fold intel_pstate_max_within_limits() into
+ caller
+
+From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+
+[ Upstream commit 032c5565eb80edb6f2faeb31939540c897987119 ]
+
+Fold intel_pstate_max_within_limits() into its only caller.
+
+No functional impact.
+
+Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+Acked-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
+Stable-dep-of: ac4e04d9e378 ("cpufreq: intel_pstate: Unchecked MSR aceess in legacy mode")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/cpufreq/intel_pstate.c | 13 ++++---------
+ 1 file changed, 4 insertions(+), 9 deletions(-)
+
+diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c
+index faeeef4acdf70..e26c0b0096080 100644
+--- a/drivers/cpufreq/intel_pstate.c
++++ b/drivers/cpufreq/intel_pstate.c
+@@ -1984,14 +1984,6 @@ static void intel_pstate_set_min_pstate(struct cpudata *cpu)
+ intel_pstate_set_pstate(cpu, cpu->pstate.min_pstate);
+ }
+
+-static void intel_pstate_max_within_limits(struct cpudata *cpu)
+-{
+- int pstate = max(cpu->pstate.min_pstate, cpu->max_perf_ratio);
+-
+- update_turbo_state();
+- intel_pstate_set_pstate(cpu, pstate);
+-}
+-
+ static void intel_pstate_get_cpu_pstates(struct cpudata *cpu)
+ {
+ int perf_ctl_max_phys = pstate_funcs.get_max_physical(cpu->cpu);
+@@ -2573,12 +2565,15 @@ static int intel_pstate_set_policy(struct cpufreq_policy *policy)
+ intel_pstate_update_perf_limits(cpu, policy->min, policy->max);
+
+ if (cpu->policy == CPUFREQ_POLICY_PERFORMANCE) {
++ int pstate = max(cpu->pstate.min_pstate, cpu->max_perf_ratio);
++
+ /*
+ * NOHZ_FULL CPUs need this as the governor callback may not
+ * be invoked on them.
+ */
+ intel_pstate_clear_update_util_hook(policy->cpu);
+- intel_pstate_max_within_limits(cpu);
++ update_turbo_state();
++ intel_pstate_set_pstate(cpu, pstate);
+ } else {
+ intel_pstate_set_update_util_hook(policy->cpu);
+ }
+--
+2.39.5
+
--- /dev/null
+From a7b500eb867264fb33adcfc8ba370ccd893b0cd2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 7 Sep 2023 11:02:07 -0700
+Subject: cpufreq: intel_pstate: Revise global turbo disable check
+
+From: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
+
+[ Upstream commit 37b6ddba967c601479bea418a7ac6ff16b6232b7 ]
+
+Setting global turbo flag based on CPU 0 P-state limits is problematic
+as it limits max P-state request on every CPU on the system just based
+on its P-state limits.
+
+There are two cases in which global.turbo_disabled flag is set:
+- When the MSR_IA32_MISC_ENABLE_TURBO_DISABLE bit is set to 1
+in the MSR MSR_IA32_MISC_ENABLE. This bit can be only changed by
+the system BIOS before power up.
+- When the max non turbo P-state is same as max turbo P-state for CPU 0.
+
+The second check is not a valid to decide global turbo state based on
+the CPU 0. CPU 0 max turbo P-state can be same as max non turbo P-state,
+but for other CPUs this may not be true.
+
+There is no guarantee that max P-state limits are same for every CPU. This
+is possible that during fusing max P-state for a CPU is constrained. Also
+with the Intel Speed Select performance profile, CPU 0 may not be present
+in all profiles. In this case the max non turbo and turbo P-state can be
+set to the lowest possible P-state by the hardware when switched to
+such profile. Since max non turbo and turbo P-state is same,
+global.turbo_disabled flag will be set.
+
+Once global.turbo_disabled is set, any scaling max and min frequency
+update for any CPU will result in its max P-state constrained to the max
+non turbo P-state.
+
+Hence remove the check of max non turbo P-state equal to max turbo P-state
+of CPU 0 to set global turbo disabled flag.
+
+Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
+[ rjw: Subject edit ]
+Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+Stable-dep-of: ac4e04d9e378 ("cpufreq: intel_pstate: Unchecked MSR aceess in legacy mode")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/cpufreq/intel_pstate.c | 6 +-----
+ 1 file changed, 1 insertion(+), 5 deletions(-)
+
+diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c
+index d471d74df3bbb..faeeef4acdf70 100644
+--- a/drivers/cpufreq/intel_pstate.c
++++ b/drivers/cpufreq/intel_pstate.c
+@@ -562,13 +562,9 @@ static void intel_pstate_hybrid_hwp_adjust(struct cpudata *cpu)
+ static inline void update_turbo_state(void)
+ {
+ u64 misc_en;
+- struct cpudata *cpu;
+
+- cpu = all_cpu_data[0];
+ rdmsrl(MSR_IA32_MISC_ENABLE, misc_en);
+- global.turbo_disabled =
+- (misc_en & MSR_IA32_MISC_ENABLE_TURBO_DISABLE ||
+- cpu->pstate.max_pstate == cpu->pstate.turbo_pstate);
++ global.turbo_disabled = misc_en & MSR_IA32_MISC_ENABLE_TURBO_DISABLE;
+ }
+
+ static int min_perf_pct_min(void)
+--
+2.39.5
+
--- /dev/null
+From eda97920501cd72b95367e79b7345efb45705636 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 29 Apr 2025 14:07:11 -0700
+Subject: cpufreq: intel_pstate: Unchecked MSR aceess in legacy mode
+
+From: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
+
+[ Upstream commit ac4e04d9e378f5aa826c2406ad7871ae1b6a6fb9 ]
+
+When turbo mode is unavailable on a Skylake-X system, executing the
+command:
+
+ # echo 1 > /sys/devices/system/cpu/intel_pstate/no_turbo
+
+results in an unchecked MSR access error:
+
+ WRMSR to 0x199 (attempted to write 0x0000000100001300).
+
+This issue was reproduced on an OEM (Original Equipment Manufacturer)
+system and is not a common problem across all Skylake-X systems.
+
+This error occurs because the MSR 0x199 Turbo Engage Bit (bit 32) is set
+when turbo mode is disabled. The issue arises when intel_pstate fails to
+detect that turbo mode is disabled. Here intel_pstate relies on
+MSR_IA32_MISC_ENABLE bit 38 to determine the status of turbo mode.
+However, on this system, bit 38 is not set even when turbo mode is
+disabled.
+
+According to the Intel Software Developer's Manual (SDM), the BIOS sets
+this bit during platform initialization to enable or disable
+opportunistic processor performance operations. Logically, this bit
+should be set in such cases. However, the SDM also specifies that "OS
+and applications must use CPUID leaf 06H to detect processors with
+opportunistic processor performance operations enabled."
+
+Therefore, in addition to checking MSR_IA32_MISC_ENABLE bit 38, verify
+that CPUID.06H:EAX[1] is 0 to accurately determine if turbo mode is
+disabled.
+
+Fixes: 4521e1a0ce17 ("cpufreq: intel_pstate: Reflect current no_turbo state correctly")
+Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
+Cc: All applicable <stable@vger.kernel.org>
+Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/cpufreq/intel_pstate.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c
+index 18d6d2a357ce1..bc0dec57bc8e2 100644
+--- a/drivers/cpufreq/intel_pstate.c
++++ b/drivers/cpufreq/intel_pstate.c
+@@ -561,6 +561,9 @@ static bool turbo_is_disabled(void)
+ {
+ u64 misc_en;
+
++ if (!cpu_feature_enabled(X86_FEATURE_IDA))
++ return true;
++
+ rdmsrl(MSR_IA32_MISC_ENABLE, misc_en);
+
+ return !!(misc_en & MSR_IA32_MISC_ENABLE_TURBO_DISABLE);
+--
+2.39.5
+
--- /dev/null
+From d238016ec4e321aa99127854f07f86691d7305a0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 28 Feb 2025 13:30:01 -0600
+Subject: drm/amd/display: Add scoped mutexes for amdgpu_dm_dhcp
+
+From: Mario Limonciello <mario.limonciello@amd.com>
+
+[ Upstream commit 6b675ab8efbf2bcee25be29e865455c56e246401 ]
+
+[Why]
+Guards automatically release mutex when it goes out of scope making
+code easier to follow.
+
+[How]
+Replace all use of mutex_lock()/mutex_unlock() with guard(mutex).
+
+Reviewed-by: Alex Hung <alex.hung@amd.com>
+Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
+Signed-off-by: Tom Chung <chiahsuan.chung@amd.com>
+Tested-by: Daniel Wheeler <daniel.wheeler@amd.com>
+Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
+Stable-dep-of: be593d9d91c5 ("drm/amd/display: Fix slab-use-after-free in hdcp")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../amd/display/amdgpu_dm/amdgpu_dm_hdcp.c | 37 +++++--------------
+ 1 file changed, 10 insertions(+), 27 deletions(-)
+
+diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c
+index 7c67bb771f996..6222d5a168832 100644
+--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c
++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c
+@@ -172,7 +172,7 @@ void hdcp_update_display(struct hdcp_workqueue *hdcp_work,
+ struct mod_hdcp_display_adjustment display_adjust;
+ unsigned int conn_index = aconnector->base.index;
+
+- mutex_lock(&hdcp_w->mutex);
++ guard(mutex)(&hdcp_w->mutex);
+ hdcp_w->aconnector[conn_index] = aconnector;
+
+ memset(&link_adjust, 0, sizeof(link_adjust));
+@@ -209,7 +209,6 @@ void hdcp_update_display(struct hdcp_workqueue *hdcp_work,
+ mod_hdcp_update_display(&hdcp_w->hdcp, conn_index, &link_adjust, &display_adjust, &hdcp_w->output);
+
+ process_output(hdcp_w);
+- mutex_unlock(&hdcp_w->mutex);
+ }
+
+ static void hdcp_remove_display(struct hdcp_workqueue *hdcp_work,
+@@ -220,7 +219,7 @@ static void hdcp_remove_display(struct hdcp_workqueue *hdcp_work,
+ struct drm_connector_state *conn_state = aconnector->base.state;
+ unsigned int conn_index = aconnector->base.index;
+
+- mutex_lock(&hdcp_w->mutex);
++ guard(mutex)(&hdcp_w->mutex);
+ hdcp_w->aconnector[conn_index] = aconnector;
+
+ /* the removal of display will invoke auth reset -> hdcp destroy and
+@@ -239,7 +238,6 @@ static void hdcp_remove_display(struct hdcp_workqueue *hdcp_work,
+ mod_hdcp_remove_display(&hdcp_w->hdcp, aconnector->base.index, &hdcp_w->output);
+
+ process_output(hdcp_w);
+- mutex_unlock(&hdcp_w->mutex);
+ }
+
+ void hdcp_reset_display(struct hdcp_workqueue *hdcp_work, unsigned int link_index)
+@@ -247,7 +245,7 @@ void hdcp_reset_display(struct hdcp_workqueue *hdcp_work, unsigned int link_inde
+ struct hdcp_workqueue *hdcp_w = &hdcp_work[link_index];
+ unsigned int conn_index;
+
+- mutex_lock(&hdcp_w->mutex);
++ guard(mutex)(&hdcp_w->mutex);
+
+ mod_hdcp_reset_connection(&hdcp_w->hdcp, &hdcp_w->output);
+
+@@ -259,8 +257,6 @@ void hdcp_reset_display(struct hdcp_workqueue *hdcp_work, unsigned int link_inde
+ }
+
+ process_output(hdcp_w);
+-
+- mutex_unlock(&hdcp_w->mutex);
+ }
+
+ void hdcp_handle_cpirq(struct hdcp_workqueue *hdcp_work, unsigned int link_index)
+@@ -277,7 +273,7 @@ static void event_callback(struct work_struct *work)
+ hdcp_work = container_of(to_delayed_work(work), struct hdcp_workqueue,
+ callback_dwork);
+
+- mutex_lock(&hdcp_work->mutex);
++ guard(mutex)(&hdcp_work->mutex);
+
+ cancel_delayed_work(&hdcp_work->callback_dwork);
+
+@@ -285,8 +281,6 @@ static void event_callback(struct work_struct *work)
+ &hdcp_work->output);
+
+ process_output(hdcp_work);
+-
+- mutex_unlock(&hdcp_work->mutex);
+ }
+
+ static void event_property_update(struct work_struct *work)
+@@ -326,7 +320,7 @@ static void event_property_update(struct work_struct *work)
+ continue;
+
+ drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
+- mutex_lock(&hdcp_work->mutex);
++ guard(mutex)(&hdcp_work->mutex);
+
+ if (conn_state->commit) {
+ ret = wait_for_completion_interruptible_timeout(&conn_state->commit->hw_done,
+@@ -358,7 +352,6 @@ static void event_property_update(struct work_struct *work)
+ drm_hdcp_update_content_protection(connector,
+ DRM_MODE_CONTENT_PROTECTION_DESIRED);
+ }
+- mutex_unlock(&hdcp_work->mutex);
+ drm_modeset_unlock(&dev->mode_config.connection_mutex);
+ }
+ }
+@@ -371,7 +364,7 @@ static void event_property_validate(struct work_struct *work)
+ struct amdgpu_dm_connector *aconnector;
+ unsigned int conn_index;
+
+- mutex_lock(&hdcp_work->mutex);
++ guard(mutex)(&hdcp_work->mutex);
+
+ for (conn_index = 0; conn_index < AMDGPU_DM_MAX_DISPLAY_INDEX;
+ conn_index++) {
+@@ -415,8 +408,6 @@ static void event_property_validate(struct work_struct *work)
+ schedule_work(&hdcp_work->property_update_work);
+ }
+ }
+-
+- mutex_unlock(&hdcp_work->mutex);
+ }
+
+ static void event_watchdog_timer(struct work_struct *work)
+@@ -427,7 +418,7 @@ static void event_watchdog_timer(struct work_struct *work)
+ struct hdcp_workqueue,
+ watchdog_timer_dwork);
+
+- mutex_lock(&hdcp_work->mutex);
++ guard(mutex)(&hdcp_work->mutex);
+
+ cancel_delayed_work(&hdcp_work->watchdog_timer_dwork);
+
+@@ -436,8 +427,6 @@ static void event_watchdog_timer(struct work_struct *work)
+ &hdcp_work->output);
+
+ process_output(hdcp_work);
+-
+- mutex_unlock(&hdcp_work->mutex);
+ }
+
+ static void event_cpirq(struct work_struct *work)
+@@ -446,13 +435,11 @@ static void event_cpirq(struct work_struct *work)
+
+ hdcp_work = container_of(work, struct hdcp_workqueue, cpirq_work);
+
+- mutex_lock(&hdcp_work->mutex);
++ guard(mutex)(&hdcp_work->mutex);
+
+ mod_hdcp_process_event(&hdcp_work->hdcp, MOD_HDCP_EVENT_CPIRQ, &hdcp_work->output);
+
+ process_output(hdcp_work);
+-
+- mutex_unlock(&hdcp_work->mutex);
+ }
+
+ void hdcp_destroy(struct kobject *kobj, struct hdcp_workqueue *hdcp_work)
+@@ -486,7 +473,7 @@ static bool enable_assr(void *handle, struct dc_link *link)
+
+ dtm_cmd = (struct ta_dtm_shared_memory *)psp->dtm_context.context.mem_context.shared_buf;
+
+- mutex_lock(&psp->dtm_context.mutex);
++ guard(mutex)(&psp->dtm_context.mutex);
+ memset(dtm_cmd, 0, sizeof(struct ta_dtm_shared_memory));
+
+ dtm_cmd->cmd_id = TA_DTM_COMMAND__TOPOLOGY_ASSR_ENABLE;
+@@ -501,8 +488,6 @@ static bool enable_assr(void *handle, struct dc_link *link)
+ res = false;
+ }
+
+- mutex_unlock(&psp->dtm_context.mutex);
+-
+ return res;
+ }
+
+@@ -563,13 +548,11 @@ static void update_config(void *handle, struct cp_psp_stream_config *config)
+ (!!aconnector->base.state) ?
+ aconnector->base.state->hdcp_content_type : -1);
+
+- mutex_lock(&hdcp_w->mutex);
++ guard(mutex)(&hdcp_w->mutex);
+
+ mod_hdcp_add_display(&hdcp_w->hdcp, link, display, &hdcp_w->output);
+
+ process_output(hdcp_w);
+- mutex_unlock(&hdcp_w->mutex);
+-
+ }
+
+ /**
+--
+2.39.5
+
--- /dev/null
+From 4f489c9cefe14311c127a2b8341ca34945a7f5a3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 24 Jul 2023 16:32:47 -0400
+Subject: drm/amd/display: Change HDCP update sequence for DM
+
+From: Bhawanpreet Lakha <bhawanpreet.lakha@amd.com>
+
+[ Upstream commit 393e83484839970e4975dfa1f0666f939a6f3e3d ]
+
+Refactor the sequence in hdcp_update_display() to use
+mod_hdcp_update_display().
+
+Previous sequence:
+ - remove()->add()
+
+This Sequence was used to update the display, (mod_hdcp_update_display
+didn't exist at the time). This meant for any hdcp updates (type changes,
+enable/disable) we would remove, reconstruct, and add. This leads to
+unnecessary calls to psp eventually
+
+New Sequence using mod_hdcp_update_display():
+ - add() once when stream is enabled
+ - use update() for all updates
+
+The update function checks for prev == new states and will not
+unnecessarily end up calling psp via add/remove.
+
+Reviewed-by: Qingqing Zhuo <qingqing.zhuo@amd.com>
+Acked-by: Tom Chung <chiahsuan.chung@amd.com>
+Signed-off-by: Bhawanpreet Lakha <bhawanpreet.lakha@amd.com>
+Tested-by: Daniel Wheeler <daniel.wheeler@amd.com>
+Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
+Stable-dep-of: be593d9d91c5 ("drm/amd/display: Fix slab-use-after-free in hdcp")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../amd/display/amdgpu_dm/amdgpu_dm_hdcp.c | 80 +++++++++----------
+ 1 file changed, 38 insertions(+), 42 deletions(-)
+
+diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c
+index 15537f554ca86..7c67bb771f996 100644
+--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c
++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c
+@@ -168,53 +168,45 @@ void hdcp_update_display(struct hdcp_workqueue *hdcp_work,
+ bool enable_encryption)
+ {
+ struct hdcp_workqueue *hdcp_w = &hdcp_work[link_index];
+- struct mod_hdcp_display *display = &hdcp_work[link_index].display;
+- struct mod_hdcp_link *link = &hdcp_work[link_index].link;
+- struct mod_hdcp_display_query query;
++ struct mod_hdcp_link_adjustment link_adjust;
++ struct mod_hdcp_display_adjustment display_adjust;
+ unsigned int conn_index = aconnector->base.index;
+
+ mutex_lock(&hdcp_w->mutex);
+ hdcp_w->aconnector[conn_index] = aconnector;
+
+- query.display = NULL;
+- mod_hdcp_query_display(&hdcp_w->hdcp, aconnector->base.index, &query);
+-
+- if (query.display) {
+- memcpy(display, query.display, sizeof(struct mod_hdcp_display));
+- mod_hdcp_remove_display(&hdcp_w->hdcp, aconnector->base.index, &hdcp_w->output);
+-
+- hdcp_w->link.adjust.hdcp2.force_type = MOD_HDCP_FORCE_TYPE_0;
+-
+- if (enable_encryption) {
+- /* Explicitly set the saved SRM as sysfs call will be after
+- * we already enabled hdcp (s3 resume case)
+- */
+- if (hdcp_work->srm_size > 0)
+- psp_set_srm(hdcp_work->hdcp.config.psp.handle, hdcp_work->srm,
+- hdcp_work->srm_size,
+- &hdcp_work->srm_version);
+-
+- display->adjust.disable = MOD_HDCP_DISPLAY_NOT_DISABLE;
+- if (content_type == DRM_MODE_HDCP_CONTENT_TYPE0) {
+- hdcp_w->link.adjust.hdcp1.disable = 0;
+- hdcp_w->link.adjust.hdcp2.force_type = MOD_HDCP_FORCE_TYPE_0;
+- } else if (content_type == DRM_MODE_HDCP_CONTENT_TYPE1) {
+- hdcp_w->link.adjust.hdcp1.disable = 1;
+- hdcp_w->link.adjust.hdcp2.force_type = MOD_HDCP_FORCE_TYPE_1;
+- }
++ memset(&link_adjust, 0, sizeof(link_adjust));
++ memset(&display_adjust, 0, sizeof(display_adjust));
+
+- schedule_delayed_work(&hdcp_w->property_validate_dwork,
+- msecs_to_jiffies(DRM_HDCP_CHECK_PERIOD_MS));
+- } else {
+- display->adjust.disable = MOD_HDCP_DISPLAY_DISABLE_AUTHENTICATION;
+- hdcp_w->encryption_status[conn_index] = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF;
+- cancel_delayed_work(&hdcp_w->property_validate_dwork);
++ if (enable_encryption) {
++ /* Explicitly set the saved SRM as sysfs call will be after we already enabled hdcp
++ * (s3 resume case)
++ */
++ if (hdcp_work->srm_size > 0)
++ psp_set_srm(hdcp_work->hdcp.config.psp.handle, hdcp_work->srm,
++ hdcp_work->srm_size,
++ &hdcp_work->srm_version);
++
++ display_adjust.disable = MOD_HDCP_DISPLAY_NOT_DISABLE;
++
++ link_adjust.auth_delay = 2;
++
++ if (content_type == DRM_MODE_HDCP_CONTENT_TYPE0) {
++ link_adjust.hdcp2.force_type = MOD_HDCP_FORCE_TYPE_0;
++ } else if (content_type == DRM_MODE_HDCP_CONTENT_TYPE1) {
++ link_adjust.hdcp1.disable = 1;
++ link_adjust.hdcp2.force_type = MOD_HDCP_FORCE_TYPE_1;
+ }
+
+- display->state = MOD_HDCP_DISPLAY_ACTIVE;
++ schedule_delayed_work(&hdcp_w->property_validate_dwork,
++ msecs_to_jiffies(DRM_HDCP_CHECK_PERIOD_MS));
++ } else {
++ display_adjust.disable = MOD_HDCP_DISPLAY_DISABLE_AUTHENTICATION;
++ hdcp_w->encryption_status[conn_index] = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF;
++ cancel_delayed_work(&hdcp_w->property_validate_dwork);
+ }
+
+- mod_hdcp_add_display(&hdcp_w->hdcp, link, display, &hdcp_w->output);
++ mod_hdcp_update_display(&hdcp_w->hdcp, conn_index, &link_adjust, &display_adjust, &hdcp_w->output);
+
+ process_output(hdcp_w);
+ mutex_unlock(&hdcp_w->mutex);
+@@ -521,7 +513,7 @@ static void update_config(void *handle, struct cp_psp_stream_config *config)
+ int link_index = aconnector->dc_link->link_index;
+ struct mod_hdcp_display *display = &hdcp_work[link_index].display;
+ struct mod_hdcp_link *link = &hdcp_work[link_index].link;
+- struct drm_connector_state *conn_state;
++ struct hdcp_workqueue *hdcp_w = &hdcp_work[link_index];
+ struct dc_sink *sink = NULL;
+ bool link_is_hdcp14 = false;
+
+@@ -563,7 +555,7 @@ static void update_config(void *handle, struct cp_psp_stream_config *config)
+ display->adjust.disable = MOD_HDCP_DISPLAY_DISABLE_AUTHENTICATION;
+ link->adjust.auth_delay = 3;
+ link->adjust.hdcp1.disable = 0;
+- conn_state = aconnector->base.state;
++ hdcp_w->encryption_status[display->index] = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF;
+
+ DRM_DEBUG_DRIVER("[HDCP_DM] display %d, CP %d, type %d\n", aconnector->base.index,
+ (!!aconnector->base.state) ?
+@@ -571,9 +563,13 @@ static void update_config(void *handle, struct cp_psp_stream_config *config)
+ (!!aconnector->base.state) ?
+ aconnector->base.state->hdcp_content_type : -1);
+
+- if (conn_state)
+- hdcp_update_display(hdcp_work, link_index, aconnector,
+- conn_state->hdcp_content_type, false);
++ mutex_lock(&hdcp_w->mutex);
++
++ mod_hdcp_add_display(&hdcp_w->hdcp, link, display, &hdcp_w->output);
++
++ process_output(hdcp_w);
++ mutex_unlock(&hdcp_w->mutex);
++
+ }
+
+ /**
+--
+2.39.5
+
--- /dev/null
+From 51ee25db9428ced71fa4d874256222d611a6770f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 13 Jul 2023 17:10:57 +0530
+Subject: drm/amd/display: Clean up style problems in amdgpu_dm_hdcp.c
+
+From: Srinivasan Shanmugam <srinivasan.shanmugam@amd.com>
+
+[ Upstream commit a19de9dbb4d293c064b02cec8ef134cb9812d639 ]
+
+Conform to Linux kernel coding style.
+
+And promote sysfs entry for set/get srm to kdoc.
+
+Suggested-by: Rodrigo Siqueira <Rodrigo.Siqueira@amd.com>
+Cc: Rodrigo Siqueira <Rodrigo.Siqueira@amd.com>
+Cc: Aurabindo Pillai <aurabindo.pillai@amd.com>
+Signed-off-by: Srinivasan Shanmugam <srinivasan.shanmugam@amd.com>
+Reviewed-by: Aurabindo Pillai <aurabindo.pillai@amd.com>
+Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
+Stable-dep-of: be593d9d91c5 ("drm/amd/display: Fix slab-use-after-free in hdcp")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../amd/display/amdgpu_dm/amdgpu_dm_hdcp.c | 185 +++++++++---------
+ 1 file changed, 89 insertions(+), 96 deletions(-)
+
+diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c
+index 7fc26ca30dcd6..15537f554ca86 100644
+--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c
++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c
+@@ -39,10 +39,10 @@
+ static bool
+ lp_write_i2c(void *handle, uint32_t address, const uint8_t *data, uint32_t size)
+ {
+-
+ struct dc_link *link = handle;
+ struct i2c_payload i2c_payloads[] = {{true, address, size, (void *)data} };
+- struct i2c_command cmd = {i2c_payloads, 1, I2C_COMMAND_ENGINE_HW, link->dc->caps.i2c_speed_in_khz};
++ struct i2c_command cmd = {i2c_payloads, 1, I2C_COMMAND_ENGINE_HW,
++ link->dc->caps.i2c_speed_in_khz};
+
+ return dm_helpers_submit_i2c(link->ctx, link, &cmd);
+ }
+@@ -52,8 +52,10 @@ lp_read_i2c(void *handle, uint32_t address, uint8_t offset, uint8_t *data, uint3
+ {
+ struct dc_link *link = handle;
+
+- struct i2c_payload i2c_payloads[] = {{true, address, 1, &offset}, {false, address, size, data} };
+- struct i2c_command cmd = {i2c_payloads, 2, I2C_COMMAND_ENGINE_HW, link->dc->caps.i2c_speed_in_khz};
++ struct i2c_payload i2c_payloads[] = {{true, address, 1, &offset},
++ {false, address, size, data} };
++ struct i2c_command cmd = {i2c_payloads, 2, I2C_COMMAND_ENGINE_HW,
++ link->dc->caps.i2c_speed_in_khz};
+
+ return dm_helpers_submit_i2c(link->ctx, link, &cmd);
+ }
+@@ -76,7 +78,6 @@ lp_read_dpcd(void *handle, uint32_t address, uint8_t *data, uint32_t size)
+
+ static uint8_t *psp_get_srm(struct psp_context *psp, uint32_t *srm_version, uint32_t *srm_size)
+ {
+-
+ struct ta_hdcp_shared_memory *hdcp_cmd;
+
+ if (!psp->hdcp_context.context.initialized) {
+@@ -96,13 +97,12 @@ static uint8_t *psp_get_srm(struct psp_context *psp, uint32_t *srm_version, uint
+ *srm_version = hdcp_cmd->out_msg.hdcp_get_srm.srm_version;
+ *srm_size = hdcp_cmd->out_msg.hdcp_get_srm.srm_buf_size;
+
+-
+ return hdcp_cmd->out_msg.hdcp_get_srm.srm_buf;
+ }
+
+-static int psp_set_srm(struct psp_context *psp, uint8_t *srm, uint32_t srm_size, uint32_t *srm_version)
++static int psp_set_srm(struct psp_context *psp,
++ u8 *srm, uint32_t srm_size, uint32_t *srm_version)
+ {
+-
+ struct ta_hdcp_shared_memory *hdcp_cmd;
+
+ if (!psp->hdcp_context.context.initialized) {
+@@ -119,7 +119,8 @@ static int psp_set_srm(struct psp_context *psp, uint8_t *srm, uint32_t srm_size,
+
+ psp_hdcp_invoke(psp, hdcp_cmd->cmd_id);
+
+- if (hdcp_cmd->hdcp_status != TA_HDCP_STATUS__SUCCESS || hdcp_cmd->out_msg.hdcp_set_srm.valid_signature != 1 ||
++ if (hdcp_cmd->hdcp_status != TA_HDCP_STATUS__SUCCESS ||
++ hdcp_cmd->out_msg.hdcp_set_srm.valid_signature != 1 ||
+ hdcp_cmd->out_msg.hdcp_set_srm.srm_version == PSP_SRM_VERSION_MAX)
+ return -EINVAL;
+
+@@ -150,7 +151,6 @@ static void process_output(struct hdcp_workqueue *hdcp_work)
+
+ static void link_lock(struct hdcp_workqueue *work, bool lock)
+ {
+-
+ int i = 0;
+
+ for (i = 0; i < work->max_link; i++) {
+@@ -160,10 +160,11 @@ static void link_lock(struct hdcp_workqueue *work, bool lock)
+ mutex_unlock(&work[i].mutex);
+ }
+ }
++
+ void hdcp_update_display(struct hdcp_workqueue *hdcp_work,
+ unsigned int link_index,
+ struct amdgpu_dm_connector *aconnector,
+- uint8_t content_type,
++ u8 content_type,
+ bool enable_encryption)
+ {
+ struct hdcp_workqueue *hdcp_w = &hdcp_work[link_index];
+@@ -178,18 +179,19 @@ void hdcp_update_display(struct hdcp_workqueue *hdcp_work,
+ query.display = NULL;
+ mod_hdcp_query_display(&hdcp_w->hdcp, aconnector->base.index, &query);
+
+- if (query.display != NULL) {
++ if (query.display) {
+ memcpy(display, query.display, sizeof(struct mod_hdcp_display));
+ mod_hdcp_remove_display(&hdcp_w->hdcp, aconnector->base.index, &hdcp_w->output);
+
+ hdcp_w->link.adjust.hdcp2.force_type = MOD_HDCP_FORCE_TYPE_0;
+
+ if (enable_encryption) {
+- /* Explicitly set the saved SRM as sysfs call will be after we already enabled hdcp
+- * (s3 resume case)
++ /* Explicitly set the saved SRM as sysfs call will be after
++ * we already enabled hdcp (s3 resume case)
+ */
+ if (hdcp_work->srm_size > 0)
+- psp_set_srm(hdcp_work->hdcp.config.psp.handle, hdcp_work->srm, hdcp_work->srm_size,
++ psp_set_srm(hdcp_work->hdcp.config.psp.handle, hdcp_work->srm,
++ hdcp_work->srm_size,
+ &hdcp_work->srm_version);
+
+ display->adjust.disable = MOD_HDCP_DISPLAY_NOT_DISABLE;
+@@ -219,7 +221,7 @@ void hdcp_update_display(struct hdcp_workqueue *hdcp_work,
+ }
+
+ static void hdcp_remove_display(struct hdcp_workqueue *hdcp_work,
+- unsigned int link_index,
++ unsigned int link_index,
+ struct amdgpu_dm_connector *aconnector)
+ {
+ struct hdcp_workqueue *hdcp_w = &hdcp_work[link_index];
+@@ -238,7 +240,8 @@ static void hdcp_remove_display(struct hdcp_workqueue *hdcp_work,
+ conn_state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+
+ DRM_DEBUG_DRIVER("[HDCP_DM] display %d, CP 2 -> 1, type %u, DPMS %u\n",
+- aconnector->base.index, conn_state->hdcp_content_type, aconnector->base.dpms);
++ aconnector->base.index, conn_state->hdcp_content_type,
++ aconnector->base.dpms);
+ }
+
+ mod_hdcp_remove_display(&hdcp_w->hdcp, aconnector->base.index, &hdcp_w->output);
+@@ -246,6 +249,7 @@ static void hdcp_remove_display(struct hdcp_workqueue *hdcp_work,
+ process_output(hdcp_w);
+ mutex_unlock(&hdcp_w->mutex);
+ }
++
+ void hdcp_reset_display(struct hdcp_workqueue *hdcp_work, unsigned int link_index)
+ {
+ struct hdcp_workqueue *hdcp_w = &hdcp_work[link_index];
+@@ -274,15 +278,12 @@ void hdcp_handle_cpirq(struct hdcp_workqueue *hdcp_work, unsigned int link_index
+ schedule_work(&hdcp_w->cpirq_work);
+ }
+
+-
+-
+-
+ static void event_callback(struct work_struct *work)
+ {
+ struct hdcp_workqueue *hdcp_work;
+
+ hdcp_work = container_of(to_delayed_work(work), struct hdcp_workqueue,
+- callback_dwork);
++ callback_dwork);
+
+ mutex_lock(&hdcp_work->mutex);
+
+@@ -294,13 +295,12 @@ static void event_callback(struct work_struct *work)
+ process_output(hdcp_work);
+
+ mutex_unlock(&hdcp_work->mutex);
+-
+-
+ }
+
+ static void event_property_update(struct work_struct *work)
+ {
+- struct hdcp_workqueue *hdcp_work = container_of(work, struct hdcp_workqueue, property_update_work);
++ struct hdcp_workqueue *hdcp_work = container_of(work, struct hdcp_workqueue,
++ property_update_work);
+ struct amdgpu_dm_connector *aconnector = NULL;
+ struct drm_device *dev;
+ long ret;
+@@ -337,11 +337,10 @@ static void event_property_update(struct work_struct *work)
+ mutex_lock(&hdcp_work->mutex);
+
+ if (conn_state->commit) {
+- ret = wait_for_completion_interruptible_timeout(
+- &conn_state->commit->hw_done, 10 * HZ);
++ ret = wait_for_completion_interruptible_timeout(&conn_state->commit->hw_done,
++ 10 * HZ);
+ if (ret == 0) {
+- DRM_ERROR(
+- "HDCP state unknown! Setting it to DESIRED");
++ DRM_ERROR("HDCP state unknown! Setting it to DESIRED\n");
+ hdcp_work->encryption_status[conn_index] =
+ MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF;
+ }
+@@ -352,24 +351,20 @@ static void event_property_update(struct work_struct *work)
+ DRM_MODE_HDCP_CONTENT_TYPE0 &&
+ hdcp_work->encryption_status[conn_index] <=
+ MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE0_ON) {
+-
+ DRM_DEBUG_DRIVER("[HDCP_DM] DRM_MODE_CONTENT_PROTECTION_ENABLED\n");
+- drm_hdcp_update_content_protection(
+- connector,
+- DRM_MODE_CONTENT_PROTECTION_ENABLED);
++ drm_hdcp_update_content_protection(connector,
++ DRM_MODE_CONTENT_PROTECTION_ENABLED);
+ } else if (conn_state->hdcp_content_type ==
+ DRM_MODE_HDCP_CONTENT_TYPE1 &&
+ hdcp_work->encryption_status[conn_index] ==
+ MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON) {
+- drm_hdcp_update_content_protection(
+- connector,
+- DRM_MODE_CONTENT_PROTECTION_ENABLED);
++ drm_hdcp_update_content_protection(connector,
++ DRM_MODE_CONTENT_PROTECTION_ENABLED);
+ }
+ } else {
+ DRM_DEBUG_DRIVER("[HDCP_DM] DRM_MODE_CONTENT_PROTECTION_DESIRED\n");
+- drm_hdcp_update_content_protection(
+- connector, DRM_MODE_CONTENT_PROTECTION_DESIRED);
+-
++ drm_hdcp_update_content_protection(connector,
++ DRM_MODE_CONTENT_PROTECTION_DESIRED);
+ }
+ mutex_unlock(&hdcp_work->mutex);
+ drm_modeset_unlock(&dev->mode_config.connection_mutex);
+@@ -409,7 +404,7 @@ static void event_property_validate(struct work_struct *work)
+ &query);
+
+ DRM_DEBUG_DRIVER("[HDCP_DM] disp %d, connector->CP %u, (query, work): (%d, %d)\n",
+- aconnector->base.index,
++ aconnector->base.index,
+ aconnector->base.state->content_protection,
+ query.encryption_status,
+ hdcp_work->encryption_status[conn_index]);
+@@ -417,7 +412,8 @@ static void event_property_validate(struct work_struct *work)
+ if (query.encryption_status !=
+ hdcp_work->encryption_status[conn_index]) {
+ DRM_DEBUG_DRIVER("[HDCP_DM] encryption_status change from %x to %x\n",
+- hdcp_work->encryption_status[conn_index], query.encryption_status);
++ hdcp_work->encryption_status[conn_index],
++ query.encryption_status);
+
+ hdcp_work->encryption_status[conn_index] =
+ query.encryption_status;
+@@ -436,7 +432,7 @@ static void event_watchdog_timer(struct work_struct *work)
+ struct hdcp_workqueue *hdcp_work;
+
+ hdcp_work = container_of(to_delayed_work(work),
+- struct hdcp_workqueue,
++ struct hdcp_workqueue,
+ watchdog_timer_dwork);
+
+ mutex_lock(&hdcp_work->mutex);
+@@ -450,7 +446,6 @@ static void event_watchdog_timer(struct work_struct *work)
+ process_output(hdcp_work);
+
+ mutex_unlock(&hdcp_work->mutex);
+-
+ }
+
+ static void event_cpirq(struct work_struct *work)
+@@ -466,10 +461,8 @@ static void event_cpirq(struct work_struct *work)
+ process_output(hdcp_work);
+
+ mutex_unlock(&hdcp_work->mutex);
+-
+ }
+
+-
+ void hdcp_destroy(struct kobject *kobj, struct hdcp_workqueue *hdcp_work)
+ {
+ int i = 0;
+@@ -486,10 +479,8 @@ void hdcp_destroy(struct kobject *kobj, struct hdcp_workqueue *hdcp_work)
+ kfree(hdcp_work);
+ }
+
+-
+ static bool enable_assr(void *handle, struct dc_link *link)
+ {
+-
+ struct hdcp_workqueue *hdcp_work = handle;
+ struct mod_hdcp hdcp = hdcp_work->hdcp;
+ struct psp_context *psp = hdcp.config.psp.handle;
+@@ -507,7 +498,8 @@ static bool enable_assr(void *handle, struct dc_link *link)
+ memset(dtm_cmd, 0, sizeof(struct ta_dtm_shared_memory));
+
+ dtm_cmd->cmd_id = TA_DTM_COMMAND__TOPOLOGY_ASSR_ENABLE;
+- dtm_cmd->dtm_in_message.topology_assr_enable.display_topology_dig_be_index = link->link_enc_hw_inst;
++ dtm_cmd->dtm_in_message.topology_assr_enable.display_topology_dig_be_index =
++ link->link_enc_hw_inst;
+ dtm_cmd->dtm_status = TA_DTM_STATUS__GENERIC_FAILURE;
+
+ psp_dtm_invoke(psp, dtm_cmd->cmd_id);
+@@ -549,7 +541,7 @@ static void update_config(void *handle, struct cp_psp_stream_config *config)
+ else if (aconnector->dc_em_sink)
+ sink = aconnector->dc_em_sink;
+
+- if (sink != NULL)
++ if (sink)
+ link->mode = mod_hdcp_signal_type_to_operation_mode(sink->sink_signal);
+
+ display->controller = CONTROLLER_ID_D0 + config->otg_inst;
+@@ -574,16 +566,20 @@ static void update_config(void *handle, struct cp_psp_stream_config *config)
+ conn_state = aconnector->base.state;
+
+ DRM_DEBUG_DRIVER("[HDCP_DM] display %d, CP %d, type %d\n", aconnector->base.index,
+- (!!aconnector->base.state) ? aconnector->base.state->content_protection : -1,
+- (!!aconnector->base.state) ? aconnector->base.state->hdcp_content_type : -1);
++ (!!aconnector->base.state) ?
++ aconnector->base.state->content_protection : -1,
++ (!!aconnector->base.state) ?
++ aconnector->base.state->hdcp_content_type : -1);
+
+ if (conn_state)
+ hdcp_update_display(hdcp_work, link_index, aconnector,
+- conn_state->hdcp_content_type, false);
++ conn_state->hdcp_content_type, false);
+ }
+
+-
+-/* NOTE: From the usermodes prospective you only need to call write *ONCE*, the kernel
++/**
++ * DOC: Add sysfs interface for set/get srm
++ *
++ * NOTE: From the usermodes prospective you only need to call write *ONCE*, the kernel
+ * will automatically call once or twice depending on the size
+ *
+ * call: "cat file > /sys/class/drm/card0/device/hdcp_srm" from usermode no matter what the size is
+@@ -594,23 +590,23 @@ static void update_config(void *handle, struct cp_psp_stream_config *config)
+ * sysfs interface doesn't tell us the size we will get so we are sending partial SRMs to psp and on
+ * the last call we will send the full SRM. PSP will fail on every call before the last.
+ *
+- * This means we don't know if the SRM is good until the last call. And because of this limitation we
+- * cannot throw errors early as it will stop the kernel from writing to sysfs
++ * This means we don't know if the SRM is good until the last call. And because of this
++ * limitation we cannot throw errors early as it will stop the kernel from writing to sysfs
+ *
+ * Example 1:
+- * Good SRM size = 5096
+- * first call to write 4096 -> PSP fails
+- * Second call to write 1000 -> PSP Pass -> SRM is set
++ * Good SRM size = 5096
++ * first call to write 4096 -> PSP fails
++ * Second call to write 1000 -> PSP Pass -> SRM is set
+ *
+ * Example 2:
+- * Bad SRM size = 4096
+- * first call to write 4096 -> PSP fails (This is the same as above, but we don't know if this
+- * is the last call)
++ * Bad SRM size = 4096
++ * first call to write 4096 -> PSP fails (This is the same as above, but we don't know if this
++ * is the last call)
+ *
+ * Solution?:
+- * 1: Parse the SRM? -> It is signed so we don't know the EOF
+- * 2: We can have another sysfs that passes the size before calling set. -> simpler solution
+- * below
++ * 1: Parse the SRM? -> It is signed so we don't know the EOF
++ * 2: We can have another sysfs that passes the size before calling set. -> simpler solution
++ * below
+ *
+ * Easy Solution:
+ * Always call get after Set to verify if set was successful.
+@@ -619,20 +615,21 @@ static void update_config(void *handle, struct cp_psp_stream_config *config)
+ * +----------------------+
+ * PSP will only update its srm if its older than the one we are trying to load.
+ * Always do set first than get.
+- * -if we try to "1. SET" a older version PSP will reject it and we can "2. GET" the newer
+- * version and save it
++ * -if we try to "1. SET" a older version PSP will reject it and we can "2. GET" the newer
++ * version and save it
+ *
+- * -if we try to "1. SET" a newer version PSP will accept it and we can "2. GET" the
+- * same(newer) version back and save it
++ * -if we try to "1. SET" a newer version PSP will accept it and we can "2. GET" the
++ * same(newer) version back and save it
+ *
+- * -if we try to "1. SET" a newer version and PSP rejects it. That means the format is
+- * incorrect/corrupted and we should correct our SRM by getting it from PSP
++ * -if we try to "1. SET" a newer version and PSP rejects it. That means the format is
++ * incorrect/corrupted and we should correct our SRM by getting it from PSP
+ */
+-static ssize_t srm_data_write(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buffer,
++static ssize_t srm_data_write(struct file *filp, struct kobject *kobj,
++ struct bin_attribute *bin_attr, char *buffer,
+ loff_t pos, size_t count)
+ {
+ struct hdcp_workqueue *work;
+- uint32_t srm_version = 0;
++ u32 srm_version = 0;
+
+ work = container_of(bin_attr, struct hdcp_workqueue, attr);
+ link_lock(work, true);
+@@ -646,19 +643,19 @@ static ssize_t srm_data_write(struct file *filp, struct kobject *kobj, struct bi
+ work->srm_version = srm_version;
+ }
+
+-
+ link_lock(work, false);
+
+ return count;
+ }
+
+-static ssize_t srm_data_read(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buffer,
++static ssize_t srm_data_read(struct file *filp, struct kobject *kobj,
++ struct bin_attribute *bin_attr, char *buffer,
+ loff_t pos, size_t count)
+ {
+ struct hdcp_workqueue *work;
+- uint8_t *srm = NULL;
+- uint32_t srm_version;
+- uint32_t srm_size;
++ u8 *srm = NULL;
++ u32 srm_version;
++ u32 srm_size;
+ size_t ret = count;
+
+ work = container_of(bin_attr, struct hdcp_workqueue, attr);
+@@ -691,12 +688,12 @@ static ssize_t srm_data_read(struct file *filp, struct kobject *kobj, struct bin
+ /* From the hdcp spec (5.Renewability) SRM needs to be stored in a non-volatile memory.
+ *
+ * For example,
+- * if Application "A" sets the SRM (ver 2) and we reboot/suspend and later when Application "B"
+- * needs to use HDCP, the version in PSP should be SRM(ver 2). So SRM should be persistent
+- * across boot/reboots/suspend/resume/shutdown
++ * if Application "A" sets the SRM (ver 2) and we reboot/suspend and later when Application "B"
++ * needs to use HDCP, the version in PSP should be SRM(ver 2). So SRM should be persistent
++ * across boot/reboots/suspend/resume/shutdown
+ *
+- * Currently when the system goes down (suspend/shutdown) the SRM is cleared from PSP. For HDCP we need
+- * to make the SRM persistent.
++ * Currently when the system goes down (suspend/shutdown) the SRM is cleared from PSP. For HDCP
++ * we need to make the SRM persistent.
+ *
+ * -PSP owns the checking of SRM but doesn't have the ability to store it in a non-volatile memory.
+ * -The kernel cannot write to the file systems.
+@@ -706,8 +703,8 @@ static ssize_t srm_data_read(struct file *filp, struct kobject *kobj, struct bin
+ *
+ * Usermode can read/write to/from PSP using the sysfs interface
+ * For example:
+- * to save SRM from PSP to storage : cat /sys/class/drm/card0/device/hdcp_srm > srmfile
+- * to load from storage to PSP: cat srmfile > /sys/class/drm/card0/device/hdcp_srm
++ * to save SRM from PSP to storage : cat /sys/class/drm/card0/device/hdcp_srm > srmfile
++ * to load from storage to PSP: cat srmfile > /sys/class/drm/card0/device/hdcp_srm
+ */
+ static const struct bin_attribute data_attr = {
+ .attr = {.name = "hdcp_srm", .mode = 0664},
+@@ -716,10 +713,9 @@ static const struct bin_attribute data_attr = {
+ .read = srm_data_read,
+ };
+
+-
+-struct hdcp_workqueue *hdcp_create_workqueue(struct amdgpu_device *adev, struct cp_psp *cp_psp, struct dc *dc)
++struct hdcp_workqueue *hdcp_create_workqueue(struct amdgpu_device *adev,
++ struct cp_psp *cp_psp, struct dc *dc)
+ {
+-
+ int max_caps = dc->caps.max_links;
+ struct hdcp_workqueue *hdcp_work;
+ int i = 0;
+@@ -728,14 +724,16 @@ struct hdcp_workqueue *hdcp_create_workqueue(struct amdgpu_device *adev, struct
+ if (ZERO_OR_NULL_PTR(hdcp_work))
+ return NULL;
+
+- hdcp_work->srm = kcalloc(PSP_HDCP_SRM_FIRST_GEN_MAX_SIZE, sizeof(*hdcp_work->srm), GFP_KERNEL);
++ hdcp_work->srm = kcalloc(PSP_HDCP_SRM_FIRST_GEN_MAX_SIZE,
++ sizeof(*hdcp_work->srm), GFP_KERNEL);
+
+- if (hdcp_work->srm == NULL)
++ if (!hdcp_work->srm)
+ goto fail_alloc_context;
+
+- hdcp_work->srm_temp = kcalloc(PSP_HDCP_SRM_FIRST_GEN_MAX_SIZE, sizeof(*hdcp_work->srm_temp), GFP_KERNEL);
++ hdcp_work->srm_temp = kcalloc(PSP_HDCP_SRM_FIRST_GEN_MAX_SIZE,
++ sizeof(*hdcp_work->srm_temp), GFP_KERNEL);
+
+- if (hdcp_work->srm_temp == NULL)
++ if (!hdcp_work->srm_temp)
+ goto fail_alloc_context;
+
+ hdcp_work->max_link = max_caps;
+@@ -788,10 +786,5 @@ struct hdcp_workqueue *hdcp_create_workqueue(struct amdgpu_device *adev, struct
+ kfree(hdcp_work);
+
+ return NULL;
+-
+-
+-
+ }
+
+-
+-
+--
+2.39.5
+
--- /dev/null
+From e29a7d118e5e0feb8cda94167b19c7bdd49c58dc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 17 Apr 2025 16:50:05 -0500
+Subject: drm/amd/display: Fix slab-use-after-free in hdcp
+
+From: Chris Bainbridge <chris.bainbridge@gmail.com>
+
+[ Upstream commit be593d9d91c5a3a363d456b9aceb71029aeb3f1d ]
+
+The HDCP code in amdgpu_dm_hdcp.c copies pointers to amdgpu_dm_connector
+objects without incrementing the kref reference counts. When using a
+USB-C dock, and the dock is unplugged, the corresponding
+amdgpu_dm_connector objects are freed, creating dangling pointers in the
+HDCP code. When the dock is plugged back, the dangling pointers are
+dereferenced, resulting in a slab-use-after-free:
+
+[ 66.775837] BUG: KASAN: slab-use-after-free in event_property_validate+0x42f/0x6c0 [amdgpu]
+[ 66.776171] Read of size 4 at addr ffff888127804120 by task kworker/0:1/10
+
+[ 66.776179] CPU: 0 UID: 0 PID: 10 Comm: kworker/0:1 Not tainted 6.14.0-rc7-00180-g54505f727a38-dirty #233
+[ 66.776183] Hardware name: HP HP Pavilion Aero Laptop 13-be0xxx/8916, BIOS F.17 12/18/2024
+[ 66.776186] Workqueue: events event_property_validate [amdgpu]
+[ 66.776494] Call Trace:
+[ 66.776496] <TASK>
+[ 66.776497] dump_stack_lvl+0x70/0xa0
+[ 66.776504] print_report+0x175/0x555
+[ 66.776507] ? __virt_addr_valid+0x243/0x450
+[ 66.776510] ? kasan_complete_mode_report_info+0x66/0x1c0
+[ 66.776515] kasan_report+0xeb/0x1c0
+[ 66.776518] ? event_property_validate+0x42f/0x6c0 [amdgpu]
+[ 66.776819] ? event_property_validate+0x42f/0x6c0 [amdgpu]
+[ 66.777121] __asan_report_load4_noabort+0x14/0x20
+[ 66.777124] event_property_validate+0x42f/0x6c0 [amdgpu]
+[ 66.777342] ? __lock_acquire+0x6b40/0x6b40
+[ 66.777347] ? enable_assr+0x250/0x250 [amdgpu]
+[ 66.777571] process_one_work+0x86b/0x1510
+[ 66.777575] ? pwq_dec_nr_in_flight+0xcf0/0xcf0
+[ 66.777578] ? assign_work+0x16b/0x280
+[ 66.777580] ? lock_is_held_type+0xa3/0x130
+[ 66.777583] worker_thread+0x5c0/0xfa0
+[ 66.777587] ? process_one_work+0x1510/0x1510
+[ 66.777588] kthread+0x3a2/0x840
+[ 66.777591] ? kthread_is_per_cpu+0xd0/0xd0
+[ 66.777594] ? trace_hardirqs_on+0x4f/0x60
+[ 66.777597] ? _raw_spin_unlock_irq+0x27/0x60
+[ 66.777599] ? calculate_sigpending+0x77/0xa0
+[ 66.777602] ? kthread_is_per_cpu+0xd0/0xd0
+[ 66.777605] ret_from_fork+0x40/0x90
+[ 66.777607] ? kthread_is_per_cpu+0xd0/0xd0
+[ 66.777609] ret_from_fork_asm+0x11/0x20
+[ 66.777614] </TASK>
+
+[ 66.777643] Allocated by task 10:
+[ 66.777646] kasan_save_stack+0x39/0x60
+[ 66.777649] kasan_save_track+0x14/0x40
+[ 66.777652] kasan_save_alloc_info+0x37/0x50
+[ 66.777655] __kasan_kmalloc+0xbb/0xc0
+[ 66.777658] __kmalloc_cache_noprof+0x1c8/0x4b0
+[ 66.777661] dm_dp_add_mst_connector+0xdd/0x5c0 [amdgpu]
+[ 66.777880] drm_dp_mst_port_add_connector+0x47e/0x770 [drm_display_helper]
+[ 66.777892] drm_dp_send_link_address+0x1554/0x2bf0 [drm_display_helper]
+[ 66.777901] drm_dp_check_and_send_link_address+0x187/0x1f0 [drm_display_helper]
+[ 66.777909] drm_dp_mst_link_probe_work+0x2b8/0x410 [drm_display_helper]
+[ 66.777917] process_one_work+0x86b/0x1510
+[ 66.777919] worker_thread+0x5c0/0xfa0
+[ 66.777922] kthread+0x3a2/0x840
+[ 66.777925] ret_from_fork+0x40/0x90
+[ 66.777927] ret_from_fork_asm+0x11/0x20
+
+[ 66.777932] Freed by task 1713:
+[ 66.777935] kasan_save_stack+0x39/0x60
+[ 66.777938] kasan_save_track+0x14/0x40
+[ 66.777940] kasan_save_free_info+0x3b/0x60
+[ 66.777944] __kasan_slab_free+0x52/0x70
+[ 66.777946] kfree+0x13f/0x4b0
+[ 66.777949] dm_dp_mst_connector_destroy+0xfa/0x150 [amdgpu]
+[ 66.778179] drm_connector_free+0x7d/0xb0
+[ 66.778184] drm_mode_object_put.part.0+0xee/0x160
+[ 66.778188] drm_mode_object_put+0x37/0x50
+[ 66.778191] drm_atomic_state_default_clear+0x220/0xd60
+[ 66.778194] __drm_atomic_state_free+0x16e/0x2a0
+[ 66.778197] drm_mode_atomic_ioctl+0x15ed/0x2ba0
+[ 66.778200] drm_ioctl_kernel+0x17a/0x310
+[ 66.778203] drm_ioctl+0x584/0xd10
+[ 66.778206] amdgpu_drm_ioctl+0xd2/0x1c0 [amdgpu]
+[ 66.778375] __x64_sys_ioctl+0x139/0x1a0
+[ 66.778378] x64_sys_call+0xee7/0xfb0
+[ 66.778381] do_syscall_64+0x87/0x140
+[ 66.778385] entry_SYSCALL_64_after_hwframe+0x4b/0x53
+
+Fix this by properly incrementing and decrementing the reference counts
+when making and deleting copies of the amdgpu_dm_connector pointers.
+
+(Mario: rebase on current code and update fixes tag)
+
+Closes: https://gitlab.freedesktop.org/drm/amd/-/issues/4006
+Signed-off-by: Chris Bainbridge <chris.bainbridge@gmail.com>
+Fixes: da3fd7ac0bcf3 ("drm/amd/display: Update CP property based on HW query")
+Reviewed-by: Alex Hung <alex.hung@amd.com>
+Link: https://lore.kernel.org/r/20250417215005.37964-1-mario.limonciello@amd.com
+Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
+Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
+(cherry picked from commit d4673f3c3b3dcb74e36e53cdfc880baa7a87b330)
+Cc: stable@vger.kernel.org
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../amd/display/amdgpu_dm/amdgpu_dm_hdcp.c | 19 ++++++++++++++++---
+ 1 file changed, 16 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c
+index 6222d5a168832..6110d88efdbba 100644
+--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c
++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c
+@@ -173,6 +173,9 @@ void hdcp_update_display(struct hdcp_workqueue *hdcp_work,
+ unsigned int conn_index = aconnector->base.index;
+
+ guard(mutex)(&hdcp_w->mutex);
++ drm_connector_get(&aconnector->base);
++ if (hdcp_w->aconnector[conn_index])
++ drm_connector_put(&hdcp_w->aconnector[conn_index]->base);
+ hdcp_w->aconnector[conn_index] = aconnector;
+
+ memset(&link_adjust, 0, sizeof(link_adjust));
+@@ -220,7 +223,6 @@ static void hdcp_remove_display(struct hdcp_workqueue *hdcp_work,
+ unsigned int conn_index = aconnector->base.index;
+
+ guard(mutex)(&hdcp_w->mutex);
+- hdcp_w->aconnector[conn_index] = aconnector;
+
+ /* the removal of display will invoke auth reset -> hdcp destroy and
+ * we'd expect the Content Protection (CP) property changed back to
+@@ -236,7 +238,10 @@ static void hdcp_remove_display(struct hdcp_workqueue *hdcp_work,
+ }
+
+ mod_hdcp_remove_display(&hdcp_w->hdcp, aconnector->base.index, &hdcp_w->output);
+-
++ if (hdcp_w->aconnector[conn_index]) {
++ drm_connector_put(&hdcp_w->aconnector[conn_index]->base);
++ hdcp_w->aconnector[conn_index] = NULL;
++ }
+ process_output(hdcp_w);
+ }
+
+@@ -254,6 +259,10 @@ void hdcp_reset_display(struct hdcp_workqueue *hdcp_work, unsigned int link_inde
+ for (conn_index = 0; conn_index < AMDGPU_DM_MAX_DISPLAY_INDEX; conn_index++) {
+ hdcp_w->encryption_status[conn_index] =
+ MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF;
++ if (hdcp_w->aconnector[conn_index]) {
++ drm_connector_put(&hdcp_w->aconnector[conn_index]->base);
++ hdcp_w->aconnector[conn_index] = NULL;
++ }
+ }
+
+ process_output(hdcp_w);
+@@ -496,6 +505,7 @@ static void update_config(void *handle, struct cp_psp_stream_config *config)
+ struct hdcp_workqueue *hdcp_work = handle;
+ struct amdgpu_dm_connector *aconnector = config->dm_stream_ctx;
+ int link_index = aconnector->dc_link->link_index;
++ unsigned int conn_index = aconnector->base.index;
+ struct mod_hdcp_display *display = &hdcp_work[link_index].display;
+ struct mod_hdcp_link *link = &hdcp_work[link_index].link;
+ struct hdcp_workqueue *hdcp_w = &hdcp_work[link_index];
+@@ -551,7 +561,10 @@ static void update_config(void *handle, struct cp_psp_stream_config *config)
+ guard(mutex)(&hdcp_w->mutex);
+
+ mod_hdcp_add_display(&hdcp_w->hdcp, link, display, &hdcp_w->output);
+-
++ drm_connector_get(&aconnector->base);
++ if (hdcp_w->aconnector[conn_index])
++ drm_connector_put(&hdcp_w->aconnector[conn_index]->base);
++ hdcp_w->aconnector[conn_index] = aconnector;
+ process_output(hdcp_w);
+ }
+
+--
+2.39.5
+
--- /dev/null
+From 713c15fbc2d53a2a0b7b6689297e4d23a3d9c96b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 14 Nov 2022 14:29:56 -0500
+Subject: drm/amd/display: phase2 enable mst hdcp multiple displays
+
+From: hersen wu <hersenxs.wu@amd.com>
+
+[ Upstream commit aa9fdd5d5add50305d2022fa072fe6f189283415 ]
+
+[why]
+For MST topology with 1 physical link and multiple connectors (>=2),
+e.g. daisy cahined MST + SST, or 1-to-multi MST hub, if userspace
+set to enable the HDCP simultaneously on all connected outputs, the
+commit tail iteratively call the hdcp_update_display() for each
+display (connector). However, the hdcp workqueue data structure for
+each link has only one DM connector and encryption status members,
+which means the work queue of property_validate/update() would only
+be triggered for the last connector within this physical link, and
+therefore the HDCP property value of other connectors would stay on
+DESIRED instead of switching to ENABLED, which is NOT as expected.
+
+[how]
+Use array of AMDGPU_DM_MAX_DISPLAY_INDEX for both aconnector and
+encryption status in hdcp workqueue data structure for each physical
+link. For property validate/update work queue, we iterates over the
+array and do similar operation/check for each connected display.
+
+Tested-by: Daniel Wheeler <Daniel.Wheeler@amd.com>
+Signed-off-by: hersen wu <hersenxs.wu@amd.com>
+Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
+Stable-dep-of: be593d9d91c5 ("drm/amd/display: Fix slab-use-after-free in hdcp")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../amd/display/amdgpu_dm/amdgpu_dm_hdcp.c | 160 +++++++++++++-----
+ .../amd/display/amdgpu_dm/amdgpu_dm_hdcp.h | 5 +-
+ 2 files changed, 122 insertions(+), 43 deletions(-)
+
+diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c
+index 3f211c0308a2f..7fc26ca30dcd6 100644
+--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c
++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c
+@@ -170,9 +170,10 @@ void hdcp_update_display(struct hdcp_workqueue *hdcp_work,
+ struct mod_hdcp_display *display = &hdcp_work[link_index].display;
+ struct mod_hdcp_link *link = &hdcp_work[link_index].link;
+ struct mod_hdcp_display_query query;
++ unsigned int conn_index = aconnector->base.index;
+
+ mutex_lock(&hdcp_w->mutex);
+- hdcp_w->aconnector = aconnector;
++ hdcp_w->aconnector[conn_index] = aconnector;
+
+ query.display = NULL;
+ mod_hdcp_query_display(&hdcp_w->hdcp, aconnector->base.index, &query);
+@@ -204,7 +205,7 @@ void hdcp_update_display(struct hdcp_workqueue *hdcp_work,
+ msecs_to_jiffies(DRM_HDCP_CHECK_PERIOD_MS));
+ } else {
+ display->adjust.disable = MOD_HDCP_DISPLAY_DISABLE_AUTHENTICATION;
+- hdcp_w->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF;
++ hdcp_w->encryption_status[conn_index] = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF;
+ cancel_delayed_work(&hdcp_w->property_validate_dwork);
+ }
+
+@@ -223,9 +224,10 @@ static void hdcp_remove_display(struct hdcp_workqueue *hdcp_work,
+ {
+ struct hdcp_workqueue *hdcp_w = &hdcp_work[link_index];
+ struct drm_connector_state *conn_state = aconnector->base.state;
++ unsigned int conn_index = aconnector->base.index;
+
+ mutex_lock(&hdcp_w->mutex);
+- hdcp_w->aconnector = aconnector;
++ hdcp_w->aconnector[conn_index] = aconnector;
+
+ /* the removal of display will invoke auth reset -> hdcp destroy and
+ * we'd expect the Content Protection (CP) property changed back to
+@@ -247,13 +249,18 @@ static void hdcp_remove_display(struct hdcp_workqueue *hdcp_work,
+ void hdcp_reset_display(struct hdcp_workqueue *hdcp_work, unsigned int link_index)
+ {
+ struct hdcp_workqueue *hdcp_w = &hdcp_work[link_index];
++ unsigned int conn_index;
+
+ mutex_lock(&hdcp_w->mutex);
+
+ mod_hdcp_reset_connection(&hdcp_w->hdcp, &hdcp_w->output);
+
+ cancel_delayed_work(&hdcp_w->property_validate_dwork);
+- hdcp_w->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF;
++
++ for (conn_index = 0; conn_index < AMDGPU_DM_MAX_DISPLAY_INDEX; conn_index++) {
++ hdcp_w->encryption_status[conn_index] =
++ MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF;
++ }
+
+ process_output(hdcp_w);
+
+@@ -290,49 +297,83 @@ static void event_callback(struct work_struct *work)
+
+
+ }
++
+ static void event_property_update(struct work_struct *work)
+ {
+-
+ struct hdcp_workqueue *hdcp_work = container_of(work, struct hdcp_workqueue, property_update_work);
+- struct amdgpu_dm_connector *aconnector = hdcp_work->aconnector;
+- struct drm_device *dev = hdcp_work->aconnector->base.dev;
++ struct amdgpu_dm_connector *aconnector = NULL;
++ struct drm_device *dev;
+ long ret;
++ unsigned int conn_index;
++ struct drm_connector *connector;
++ struct drm_connector_state *conn_state;
+
+- drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
+- mutex_lock(&hdcp_work->mutex);
++ for (conn_index = 0; conn_index < AMDGPU_DM_MAX_DISPLAY_INDEX; conn_index++) {
++ aconnector = hdcp_work->aconnector[conn_index];
+
++ if (!aconnector)
++ continue;
+
+- if (aconnector->base.state && aconnector->base.state->commit) {
+- ret = wait_for_completion_interruptible_timeout(&aconnector->base.state->commit->hw_done, 10 * HZ);
++ if (!aconnector->base.index)
++ continue;
+
+- if (ret == 0) {
+- DRM_ERROR("HDCP state unknown! Setting it to DESIRED");
+- hdcp_work->encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF;
+- }
+- }
++ connector = &aconnector->base;
++
++ /* check if display connected */
++ if (connector->status != connector_status_connected)
++ continue;
+
+- if (aconnector->base.state) {
+- if (hdcp_work->encryption_status != MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF) {
+- if (aconnector->base.state->hdcp_content_type ==
++ conn_state = aconnector->base.state;
++
++ if (!conn_state)
++ continue;
++
++ dev = connector->dev;
++
++ if (!dev)
++ continue;
++
++ drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
++ mutex_lock(&hdcp_work->mutex);
++
++ if (conn_state->commit) {
++ ret = wait_for_completion_interruptible_timeout(
++ &conn_state->commit->hw_done, 10 * HZ);
++ if (ret == 0) {
++ DRM_ERROR(
++ "HDCP state unknown! Setting it to DESIRED");
++ hdcp_work->encryption_status[conn_index] =
++ MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF;
++ }
++ }
++ if (hdcp_work->encryption_status[conn_index] !=
++ MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF) {
++ if (conn_state->hdcp_content_type ==
+ DRM_MODE_HDCP_CONTENT_TYPE0 &&
+- hdcp_work->encryption_status <=
+- MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE0_ON)
+- drm_hdcp_update_content_protection(&aconnector->base,
++ hdcp_work->encryption_status[conn_index] <=
++ MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE0_ON) {
++
++ DRM_DEBUG_DRIVER("[HDCP_DM] DRM_MODE_CONTENT_PROTECTION_ENABLED\n");
++ drm_hdcp_update_content_protection(
++ connector,
+ DRM_MODE_CONTENT_PROTECTION_ENABLED);
+- else if (aconnector->base.state->hdcp_content_type ==
++ } else if (conn_state->hdcp_content_type ==
+ DRM_MODE_HDCP_CONTENT_TYPE1 &&
+- hdcp_work->encryption_status ==
+- MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON)
+- drm_hdcp_update_content_protection(&aconnector->base,
++ hdcp_work->encryption_status[conn_index] ==
++ MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON) {
++ drm_hdcp_update_content_protection(
++ connector,
+ DRM_MODE_CONTENT_PROTECTION_ENABLED);
++ }
+ } else {
+- drm_hdcp_update_content_protection(&aconnector->base,
+- DRM_MODE_CONTENT_PROTECTION_DESIRED);
++ DRM_DEBUG_DRIVER("[HDCP_DM] DRM_MODE_CONTENT_PROTECTION_DESIRED\n");
++ drm_hdcp_update_content_protection(
++ connector, DRM_MODE_CONTENT_PROTECTION_DESIRED);
++
+ }
++ mutex_unlock(&hdcp_work->mutex);
++ drm_modeset_unlock(&dev->mode_config.connection_mutex);
+ }
+-
+- mutex_unlock(&hdcp_work->mutex);
+- drm_modeset_unlock(&dev->mode_config.connection_mutex);
+ }
+
+ static void event_property_validate(struct work_struct *work)
+@@ -340,19 +381,51 @@ static void event_property_validate(struct work_struct *work)
+ struct hdcp_workqueue *hdcp_work =
+ container_of(to_delayed_work(work), struct hdcp_workqueue, property_validate_dwork);
+ struct mod_hdcp_display_query query;
+- struct amdgpu_dm_connector *aconnector = hdcp_work->aconnector;
+-
+- if (!aconnector)
+- return;
++ struct amdgpu_dm_connector *aconnector;
++ unsigned int conn_index;
+
+ mutex_lock(&hdcp_work->mutex);
+
+- query.encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF;
+- mod_hdcp_query_display(&hdcp_work->hdcp, aconnector->base.index, &query);
++ for (conn_index = 0; conn_index < AMDGPU_DM_MAX_DISPLAY_INDEX;
++ conn_index++) {
++ aconnector = hdcp_work->aconnector[conn_index];
++
++
++ if (!aconnector)
++ continue;
++
++ if (!aconnector->base.index)
++ continue;
++
++ /* check if display connected */
++ if (aconnector->base.status != connector_status_connected)
++ continue;
+
+- if (query.encryption_status != hdcp_work->encryption_status) {
+- hdcp_work->encryption_status = query.encryption_status;
+- schedule_work(&hdcp_work->property_update_work);
++ if (!aconnector->base.state)
++ continue;
++
++ query.encryption_status = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF;
++ mod_hdcp_query_display(&hdcp_work->hdcp, aconnector->base.index,
++ &query);
++
++ DRM_DEBUG_DRIVER("[HDCP_DM] disp %d, connector->CP %u, (query, work): (%d, %d)\n",
++ aconnector->base.index,
++ aconnector->base.state->content_protection,
++ query.encryption_status,
++ hdcp_work->encryption_status[conn_index]);
++
++ if (query.encryption_status !=
++ hdcp_work->encryption_status[conn_index]) {
++ DRM_DEBUG_DRIVER("[HDCP_DM] encryption_status change from %x to %x\n",
++ hdcp_work->encryption_status[conn_index], query.encryption_status);
++
++ hdcp_work->encryption_status[conn_index] =
++ query.encryption_status;
++
++ DRM_DEBUG_DRIVER("[HDCP_DM] trigger property_update_work\n");
++
++ schedule_work(&hdcp_work->property_update_work);
++ }
+ }
+
+ mutex_unlock(&hdcp_work->mutex);
+@@ -687,6 +760,13 @@ struct hdcp_workqueue *hdcp_create_workqueue(struct amdgpu_device *adev, struct
+ hdcp_work[i].hdcp.config.ddc.funcs.read_i2c = lp_read_i2c;
+ hdcp_work[i].hdcp.config.ddc.funcs.write_dpcd = lp_write_dpcd;
+ hdcp_work[i].hdcp.config.ddc.funcs.read_dpcd = lp_read_dpcd;
++
++ memset(hdcp_work[i].aconnector, 0,
++ sizeof(struct amdgpu_dm_connector *) *
++ AMDGPU_DM_MAX_DISPLAY_INDEX);
++ memset(hdcp_work[i].encryption_status, 0,
++ sizeof(enum mod_hdcp_encryption_status) *
++ AMDGPU_DM_MAX_DISPLAY_INDEX);
+ }
+
+ cp_psp->funcs.update_stream_config = update_config;
+diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.h
+index bbbf7d0eff82f..69b445b011c8c 100644
+--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.h
++++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.h
+@@ -43,7 +43,7 @@ struct hdcp_workqueue {
+ struct delayed_work callback_dwork;
+ struct delayed_work watchdog_timer_dwork;
+ struct delayed_work property_validate_dwork;
+- struct amdgpu_dm_connector *aconnector;
++ struct amdgpu_dm_connector *aconnector[AMDGPU_DM_MAX_DISPLAY_INDEX];
+ struct mutex mutex;
+
+ struct mod_hdcp hdcp;
+@@ -51,8 +51,7 @@ struct hdcp_workqueue {
+ struct mod_hdcp_display display;
+ struct mod_hdcp_link link;
+
+- enum mod_hdcp_encryption_status encryption_status;
+-
++ enum mod_hdcp_encryption_status encryption_status[AMDGPU_DM_MAX_DISPLAY_INDEX];
+ /* when display is unplugged from mst hub, connctor will be
+ * destroyed within dm_dp_mst_connector_destroy. connector
+ * hdcp perperties, like type, undesired, desired, enabled,
+--
+2.39.5
+
--- /dev/null
+From 3d1bbe519609a18914363c561cdbc3f27d88a923 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 21 Mar 2025 11:57:00 +0000
+Subject: firmware: arm_ffa: Skip Rx buffer ownership release if not acquired
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Sudeep Holla <sudeep.holla@arm.com>
+
+[ Upstream commit 4567bdaaaaa1744da3d7da07d9aca2f941f5b4e5 ]
+
+Completion of the FFA_PARTITION_INFO_GET ABI transfers the ownership of
+the caller’s Rx buffer from the producer(typically partition mnager) to
+the consumer(this driver/OS). FFA_RX_RELEASE transfers the ownership
+from the consumer back to the producer.
+
+However, when we set the flag to just return the count of partitions
+deployed in the system corresponding to the specified UUID while
+invoking FFA_PARTITION_INFO_GET, the Rx buffer ownership shouldn't be
+transferred to this driver. We must be able to skip transferring back
+the ownership to the partition manager when we request just to get the
+count of the partitions as the buffers are not acquired in this case.
+
+Firmware may return FFA_RET_DENIED or other error for the ffa_rx_release()
+in such cases.
+
+Fixes: bb1be7498500 ("firmware: arm_ffa: Add v1.1 get_partition_info support")
+Message-Id: <20250321115700.3525197-1-sudeep.holla@arm.com>
+Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/firmware/arm_ffa/driver.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c
+index e9f86b7573012..e1e278d431e97 100644
+--- a/drivers/firmware/arm_ffa/driver.c
++++ b/drivers/firmware/arm_ffa/driver.c
+@@ -306,7 +306,8 @@ __ffa_partition_info_get(u32 uuid0, u32 uuid1, u32 uuid2, u32 uuid3,
+ memcpy(buffer + idx, drv_info->rx_buffer + idx * sz,
+ buf_sz);
+
+- ffa_rx_release();
++ if (!(flags & PARTITION_INFO_GET_RETURN_COUNT_ONLY))
++ ffa_rx_release();
+
+ mutex_unlock(&drv_info->rx_lock);
+
+--
+2.39.5
+
--- /dev/null
+From 694e80a2b44920eb504fe9d9f510154d0e23b62b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 6 Mar 2025 18:54:47 +0000
+Subject: firmware: arm_scmi: Balance device refcount when destroying devices
+
+From: Cristian Marussi <cristian.marussi@arm.com>
+
+[ Upstream commit 9ca67840c0ddf3f39407339624cef824a4f27599 ]
+
+Using device_find_child() to lookup the proper SCMI device to destroy
+causes an unbalance in device refcount, since device_find_child() calls an
+implicit get_device(): this, in turns, inhibits the call of the provided
+release methods upon devices destruction.
+
+As a consequence, one of the structures that is not freed properly upon
+destruction is the internal struct device_private dev->p populated by the
+drivers subsystem core.
+
+KMemleak detects this situation since loading/unloding some SCMI driver
+causes related devices to be created/destroyed without calling any
+device_release method.
+
+unreferenced object 0xffff00000f583800 (size 512):
+ comm "insmod", pid 227, jiffies 4294912190
+ hex dump (first 32 bytes):
+ 00 00 00 00 ad 4e ad de ff ff ff ff 00 00 00 00 .....N..........
+ ff ff ff ff ff ff ff ff 60 36 1d 8a 00 80 ff ff ........`6......
+ backtrace (crc 114e2eed):
+ kmemleak_alloc+0xbc/0xd8
+ __kmalloc_cache_noprof+0x2dc/0x398
+ device_add+0x954/0x12d0
+ device_register+0x28/0x40
+ __scmi_device_create.part.0+0x1bc/0x380
+ scmi_device_create+0x2d0/0x390
+ scmi_create_protocol_devices+0x74/0xf8
+ scmi_device_request_notifier+0x1f8/0x2a8
+ notifier_call_chain+0x110/0x3b0
+ blocking_notifier_call_chain+0x70/0xb0
+ scmi_driver_register+0x350/0x7f0
+ 0xffff80000a3b3038
+ do_one_initcall+0x12c/0x730
+ do_init_module+0x1dc/0x640
+ load_module+0x4b20/0x5b70
+ init_module_from_file+0xec/0x158
+
+$ ./scripts/faddr2line ./vmlinux device_add+0x954/0x12d0
+device_add+0x954/0x12d0:
+kmalloc_noprof at include/linux/slab.h:901
+(inlined by) kzalloc_noprof at include/linux/slab.h:1037
+(inlined by) device_private_init at drivers/base/core.c:3510
+(inlined by) device_add at drivers/base/core.c:3561
+
+Balance device refcount by issuing a put_device() on devices found via
+device_find_child().
+
+Reported-by: Alice Ryhl <aliceryhl@google.com>
+Closes: https://lore.kernel.org/linux-arm-kernel/Z8nK3uFkspy61yjP@arm.com/T/#mc1f73a0ea5e41014fa145147b7b839fc988ada8f
+CC: Sudeep Holla <sudeep.holla@arm.com>
+CC: Catalin Marinas <catalin.marinas@arm.com>
+Fixes: d4f9dddd21f3 ("firmware: arm_scmi: Add dynamic scmi devices creation")
+Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
+Tested-by: Alice Ryhl <aliceryhl@google.com>
+Message-Id: <20250306185447.2039336-1-cristian.marussi@arm.com>
+Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/firmware/arm_scmi/bus.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/drivers/firmware/arm_scmi/bus.c b/drivers/firmware/arm_scmi/bus.c
+index 35bb70724d44b..8c6f99d15f22f 100644
+--- a/drivers/firmware/arm_scmi/bus.c
++++ b/drivers/firmware/arm_scmi/bus.c
+@@ -73,6 +73,9 @@ struct scmi_device *scmi_child_dev_find(struct device *parent,
+ if (!dev)
+ return NULL;
+
++ /* Drop the refcnt bumped implicitly by device_find_child */
++ put_device(dev);
++
+ return to_scmi_dev(dev);
+ }
+
+--
+2.39.5
+
--- /dev/null
+From d4de1adc22717fd9c0e1369a984274ec2de9f6eb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 15 Apr 2025 11:56:20 -0700
+Subject: iommu/arm-smmu-v3: Fix iommu_device_probe bug due to duplicated
+ stream ids
+
+From: Nicolin Chen <nicolinc@nvidia.com>
+
+[ Upstream commit b00d24997a11c10d3e420614f0873b83ce358a34 ]
+
+ASPEED VGA card has two built-in devices:
+ 0008:06:00.0 PCI bridge: ASPEED Technology, Inc. AST1150 PCI-to-PCI Bridge (rev 06)
+ 0008:07:00.0 VGA compatible controller: ASPEED Technology, Inc. ASPEED Graphics Family (rev 52)
+
+Its toplogy looks like this:
+ +-[0008:00]---00.0-[01-09]--+-00.0-[02-09]--+-00.0-[03]----00.0 Sandisk Corp Device 5017
+ | +-01.0-[04]--
+ | +-02.0-[05]----00.0 NVIDIA Corporation Device
+ | +-03.0-[06-07]----00.0-[07]----00.0 ASPEED Technology, Inc. ASPEED Graphics Family
+ | +-04.0-[08]----00.0 Renesas Technology Corp. uPD720201 USB 3.0 Host Controller
+ | \-05.0-[09]----00.0 Realtek Semiconductor Co., Ltd. RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller
+ \-00.1 PMC-Sierra Inc. Device 4028
+
+The IORT logic populaties two identical IDs into the fwspec->ids array via
+DMA aliasing in iort_pci_iommu_init() called by pci_for_each_dma_alias().
+
+Though the SMMU driver had been able to handle this situation since commit
+563b5cbe334e ("iommu/arm-smmu-v3: Cope with duplicated Stream IDs"), that
+got broken by the later commit cdf315f907d4 ("iommu/arm-smmu-v3: Maintain
+a SID->device structure"), which ended up with allocating separate streams
+with the same stuffing.
+
+On a kernel prior to v6.15-rc1, there has been an overlooked warning:
+ pci 0008:07:00.0: vgaarb: setting as boot VGA device
+ pci 0008:07:00.0: vgaarb: bridge control possible
+ pci 0008:07:00.0: vgaarb: VGA device added: decodes=io+mem,owns=none,locks=none
+ pcieport 0008:06:00.0: Adding to iommu group 14
+ ast 0008:07:00.0: stream 67328 already in tree <===== WARNING
+ ast 0008:07:00.0: enabling device (0002 -> 0003)
+ ast 0008:07:00.0: Using default configuration
+ ast 0008:07:00.0: AST 2600 detected
+ ast 0008:07:00.0: [drm] Using analog VGA
+ ast 0008:07:00.0: [drm] dram MCLK=396 Mhz type=1 bus_width=16
+ [drm] Initialized ast 0.1.0 for 0008:07:00.0 on minor 0
+ ast 0008:07:00.0: [drm] fb0: astdrmfb frame buffer device
+
+With v6.15-rc, since the commit bcb81ac6ae3c ("iommu: Get DT/ACPI parsing
+into the proper probe path"), the error returned with the warning is moved
+to the SMMU device probe flow:
+ arm_smmu_probe_device+0x15c/0x4c0
+ __iommu_probe_device+0x150/0x4f8
+ probe_iommu_group+0x44/0x80
+ bus_for_each_dev+0x7c/0x100
+ bus_iommu_probe+0x48/0x1a8
+ iommu_device_register+0xb8/0x178
+ arm_smmu_device_probe+0x1350/0x1db0
+which then fails the entire SMMU driver probe:
+ pci 0008:06:00.0: Adding to iommu group 21
+ pci 0008:07:00.0: stream 67328 already in tree
+ arm-smmu-v3 arm-smmu-v3.9.auto: Failed to register iommu
+ arm-smmu-v3 arm-smmu-v3.9.auto: probe with driver arm-smmu-v3 failed with error -22
+
+Since SMMU driver had been already expecting a potential duplicated Stream
+ID in arm_smmu_install_ste_for_dev(), change the arm_smmu_insert_master()
+routine to ignore a duplicated ID from the fwspec->sids array as well.
+
+Note: this has been failing the iommu_device_probe() since 2021, although a
+recent iommu commit in v6.15-rc1 that moves iommu_device_probe() started to
+fail the SMMU driver probe. Since nobody has cared about DMA Alias support,
+leave that as it was but fix the fundamental iommu_device_probe() breakage.
+
+Fixes: cdf315f907d4 ("iommu/arm-smmu-v3: Maintain a SID->device structure")
+Cc: stable@vger.kernel.org
+Suggested-by: Jason Gunthorpe <jgg@nvidia.com>
+Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
+Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
+Link: https://lore.kernel.org/r/20250415185620.504299-1-nicolinc@nvidia.com
+Signed-off-by: Will Deacon <will@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 19 +++++++++++++++----
+ 1 file changed, 15 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+index 1ab2abab46800..6a60bad48b277 100644
+--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
++++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+@@ -2612,6 +2612,7 @@ static int arm_smmu_insert_master(struct arm_smmu_device *smmu,
+ mutex_lock(&smmu->streams_mutex);
+ for (i = 0; i < fwspec->num_ids; i++) {
+ struct arm_smmu_stream *new_stream = &master->streams[i];
++ struct rb_node *existing;
+ u32 sid = fwspec->ids[i];
+
+ new_stream->id = sid;
+@@ -2622,10 +2623,20 @@ static int arm_smmu_insert_master(struct arm_smmu_device *smmu,
+ break;
+
+ /* Insert into SID tree */
+- if (rb_find_add(&new_stream->node, &smmu->streams,
+- arm_smmu_streams_cmp_node)) {
+- dev_warn(master->dev, "stream %u already in tree\n",
+- sid);
++ existing = rb_find_add(&new_stream->node, &smmu->streams,
++ arm_smmu_streams_cmp_node);
++ if (existing) {
++ struct arm_smmu_master *existing_master =
++ rb_entry(existing, struct arm_smmu_stream, node)
++ ->master;
++
++ /* Bridged PCI devices may end up with duplicated IDs */
++ if (existing_master == master)
++ continue;
++
++ dev_warn(master->dev,
++ "stream %u already in tree from dev %s\n", sid,
++ dev_name(existing_master->dev));
+ ret = -EINVAL;
+ break;
+ }
+--
+2.39.5
+
--- /dev/null
+From 8d69ac0bba7c2b38564e68d8c571cef24d65f101 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 6 Aug 2024 20:31:15 -0300
+Subject: iommu/arm-smmu-v3: Use the new rb tree helpers
+
+From: Jason Gunthorpe <jgg@nvidia.com>
+
+[ Upstream commit a2bb820e862d61f9ca1499e500915f9f505a2655 ]
+
+Since v5.12 the rbtree has gained some simplifying helpers aimed at making
+rb tree users write less convoluted boiler plate code. Instead the caller
+provides a single comparison function and the helpers generate the prior
+open-coded stuff.
+
+Update smmu->streams to use rb_find_add() and rb_find().
+
+Tested-by: Nicolin Chen <nicolinc@nvidia.com>
+Reviewed-by: Mostafa Saleh <smostafa@google.com>
+Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
+Link: https://lore.kernel.org/r/1-v3-9fef8cdc2ff6+150d1-smmuv3_tidy_jgg@nvidia.com
+Signed-off-by: Will Deacon <will@kernel.org>
+Stable-dep-of: b00d24997a11 ("iommu/arm-smmu-v3: Fix iommu_device_probe bug due to duplicated stream ids")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 68 ++++++++++-----------
+ 1 file changed, 31 insertions(+), 37 deletions(-)
+
+diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+index 96b72f3dad0d0..1ab2abab46800 100644
+--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
++++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+@@ -1443,26 +1443,37 @@ static int arm_smmu_init_l2_strtab(struct arm_smmu_device *smmu, u32 sid)
+ return 0;
+ }
+
++static int arm_smmu_streams_cmp_key(const void *lhs, const struct rb_node *rhs)
++{
++ struct arm_smmu_stream *stream_rhs =
++ rb_entry(rhs, struct arm_smmu_stream, node);
++ const u32 *sid_lhs = lhs;
++
++ if (*sid_lhs < stream_rhs->id)
++ return -1;
++ if (*sid_lhs > stream_rhs->id)
++ return 1;
++ return 0;
++}
++
++static int arm_smmu_streams_cmp_node(struct rb_node *lhs,
++ const struct rb_node *rhs)
++{
++ return arm_smmu_streams_cmp_key(
++ &rb_entry(lhs, struct arm_smmu_stream, node)->id, rhs);
++}
++
+ static struct arm_smmu_master *
+ arm_smmu_find_master(struct arm_smmu_device *smmu, u32 sid)
+ {
+ struct rb_node *node;
+- struct arm_smmu_stream *stream;
+
+ lockdep_assert_held(&smmu->streams_mutex);
+
+- node = smmu->streams.rb_node;
+- while (node) {
+- stream = rb_entry(node, struct arm_smmu_stream, node);
+- if (stream->id < sid)
+- node = node->rb_right;
+- else if (stream->id > sid)
+- node = node->rb_left;
+- else
+- return stream->master;
+- }
+-
+- return NULL;
++ node = rb_find(&sid, &smmu->streams, arm_smmu_streams_cmp_key);
++ if (!node)
++ return NULL;
++ return rb_entry(node, struct arm_smmu_stream, node)->master;
+ }
+
+ /* IRQ and event handlers */
+@@ -2590,8 +2601,6 @@ static int arm_smmu_insert_master(struct arm_smmu_device *smmu,
+ {
+ int i;
+ int ret = 0;
+- struct arm_smmu_stream *new_stream, *cur_stream;
+- struct rb_node **new_node, *parent_node = NULL;
+ struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(master->dev);
+
+ master->streams = kcalloc(fwspec->num_ids, sizeof(*master->streams),
+@@ -2602,9 +2611,9 @@ static int arm_smmu_insert_master(struct arm_smmu_device *smmu,
+
+ mutex_lock(&smmu->streams_mutex);
+ for (i = 0; i < fwspec->num_ids; i++) {
++ struct arm_smmu_stream *new_stream = &master->streams[i];
+ u32 sid = fwspec->ids[i];
+
+- new_stream = &master->streams[i];
+ new_stream->id = sid;
+ new_stream->master = master;
+
+@@ -2613,28 +2622,13 @@ static int arm_smmu_insert_master(struct arm_smmu_device *smmu,
+ break;
+
+ /* Insert into SID tree */
+- new_node = &(smmu->streams.rb_node);
+- while (*new_node) {
+- cur_stream = rb_entry(*new_node, struct arm_smmu_stream,
+- node);
+- parent_node = *new_node;
+- if (cur_stream->id > new_stream->id) {
+- new_node = &((*new_node)->rb_left);
+- } else if (cur_stream->id < new_stream->id) {
+- new_node = &((*new_node)->rb_right);
+- } else {
+- dev_warn(master->dev,
+- "stream %u already in tree\n",
+- cur_stream->id);
+- ret = -EINVAL;
+- break;
+- }
+- }
+- if (ret)
++ if (rb_find_add(&new_stream->node, &smmu->streams,
++ arm_smmu_streams_cmp_node)) {
++ dev_warn(master->dev, "stream %u already in tree\n",
++ sid);
++ ret = -EINVAL;
+ break;
+-
+- rb_link_node(&new_stream->node, parent_node, new_node);
+- rb_insert_color(&new_stream->node, &smmu->streams);
++ }
+ }
+
+ if (ret) {
+--
+2.39.5
+
--- /dev/null
+From 6b3dd655c1d4d29b0c1e15f6fa8f68f3be8f3c54 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 21 Nov 2022 15:39:33 +0100
+Subject: irqchip/gic-v2m: Mark a few functions __init
+
+From: Thomas Gleixner <tglx@linutronix.de>
+
+[ Upstream commit d51a15af37ce8cf59e73de51dcdce3c9f4944974 ]
+
+They are all part of the init sequence.
+
+Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
+Acked-by: Marc Zyngier <maz@kernel.org>
+Link: https://lore.kernel.org/r/20221121140048.534395323@linutronix.de
+Stable-dep-of: 3318dc299b07 ("irqchip/gic-v2m: Prevent use after free of gicv2m_get_fwnode()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/irqchip/irq-gic-v2m.c | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c
+index 414cd925064f4..c2e52c9a38546 100644
+--- a/drivers/irqchip/irq-gic-v2m.c
++++ b/drivers/irqchip/irq-gic-v2m.c
+@@ -262,7 +262,7 @@ static struct msi_domain_info gicv2m_pmsi_domain_info = {
+ .chip = &gicv2m_pmsi_irq_chip,
+ };
+
+-static void gicv2m_teardown(void)
++static void __init gicv2m_teardown(void)
+ {
+ struct v2m_data *v2m, *tmp;
+
+@@ -277,7 +277,7 @@ static void gicv2m_teardown(void)
+ }
+ }
+
+-static int gicv2m_allocate_domains(struct irq_domain *parent)
++static __init int gicv2m_allocate_domains(struct irq_domain *parent)
+ {
+ struct irq_domain *inner_domain, *pci_domain, *plat_domain;
+ struct v2m_data *v2m;
+@@ -404,7 +404,7 @@ static int __init gicv2m_init_one(struct fwnode_handle *fwnode,
+ return ret;
+ }
+
+-static const struct of_device_id gicv2m_device_id[] = {
++static __initconst struct of_device_id gicv2m_device_id[] = {
+ { .compatible = "arm,gic-v2m-frame", },
+ {},
+ };
+@@ -454,7 +454,7 @@ static int __init gicv2m_of_init(struct fwnode_handle *parent_handle,
+ #ifdef CONFIG_ACPI
+ static int acpi_num_msi;
+
+-static struct fwnode_handle *gicv2m_get_fwnode(struct device *dev)
++static __init struct fwnode_handle *gicv2m_get_fwnode(struct device *dev)
+ {
+ struct v2m_data *data;
+
+@@ -469,7 +469,7 @@ static struct fwnode_handle *gicv2m_get_fwnode(struct device *dev)
+ return data->fwnode;
+ }
+
+-static bool acpi_check_amazon_graviton_quirks(void)
++static __init bool acpi_check_amazon_graviton_quirks(void)
+ {
+ static struct acpi_table_madt *madt;
+ acpi_status status;
+--
+2.39.5
+
--- /dev/null
+From d3b764f43a2f9040bd08c4edd77873671eab55bf Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 22 Apr 2025 17:16:16 +0100
+Subject: irqchip/gic-v2m: Prevent use after free of gicv2m_get_fwnode()
+
+From: Suzuki K Poulose <suzuki.poulose@arm.com>
+
+[ Upstream commit 3318dc299b072a0511d6dfd8367f3304fb6d9827 ]
+
+With ACPI in place, gicv2m_get_fwnode() is registered with the pci
+subsystem as pci_msi_get_fwnode_cb(), which may get invoked at runtime
+during a PCI host bridge probe. But, the call back is wrongly marked as
+__init, causing it to be freed, while being registered with the PCI
+subsystem and could trigger:
+
+ Unable to handle kernel paging request at virtual address ffff8000816c0400
+ gicv2m_get_fwnode+0x0/0x58 (P)
+ pci_set_bus_msi_domain+0x74/0x88
+ pci_register_host_bridge+0x194/0x548
+
+This is easily reproducible on a Juno board with ACPI boot.
+
+Retain the function for later use.
+
+Fixes: 0644b3daca28 ("irqchip/gic-v2m: acpi: Introducing GICv2m ACPI support")
+Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
+Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
+Signed-off-by: Ingo Molnar <mingo@kernel.org>
+Reviewed-by: Marc Zyngier <maz@kernel.org>
+Cc: stable@vger.kernel.org
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/irqchip/irq-gic-v2m.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c
+index c2e52c9a38546..c04f2481068bb 100644
+--- a/drivers/irqchip/irq-gic-v2m.c
++++ b/drivers/irqchip/irq-gic-v2m.c
+@@ -454,7 +454,7 @@ static int __init gicv2m_of_init(struct fwnode_handle *parent_handle,
+ #ifdef CONFIG_ACPI
+ static int acpi_num_msi;
+
+-static __init struct fwnode_handle *gicv2m_get_fwnode(struct device *dev)
++static struct fwnode_handle *gicv2m_get_fwnode(struct device *dev)
+ {
+ struct v2m_data *data;
+
+--
+2.39.5
+
--- /dev/null
+From 23250aec43b05632c5aa65fc4a8c7061677e49e2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 10 Mar 2025 16:09:34 -0700
+Subject: memcg: drain obj stock on cpu hotplug teardown
+
+From: Shakeel Butt <shakeel.butt@linux.dev>
+
+[ Upstream commit 9f01b4954490d4ccdbcc2b9be34a9921ceee9cbb ]
+
+Currently on cpu hotplug teardown, only memcg stock is drained but we
+need to drain the obj stock as well otherwise we will miss the stats
+accumulated on the target cpu as well as the nr_bytes cached. The stats
+include MEMCG_KMEM, NR_SLAB_RECLAIMABLE_B & NR_SLAB_UNRECLAIMABLE_B. In
+addition we are leaking reference to struct obj_cgroup object.
+
+Link: https://lkml.kernel.org/r/20250310230934.2913113-1-shakeel.butt@linux.dev
+Fixes: bf4f059954dc ("mm: memcg/slab: obj_cgroup API")
+Signed-off-by: Shakeel Butt <shakeel.butt@linux.dev>
+Reviewed-by: Roman Gushchin <roman.gushchin@linux.dev>
+Acked-by: Johannes Weiner <hannes@cmpxchg.org>
+Cc: Michal Hocko <mhocko@kernel.org>
+Cc: Muchun Song <muchun.song@linux.dev>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ mm/memcontrol.c | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+diff --git a/mm/memcontrol.c b/mm/memcontrol.c
+index 3f7cab196eb62..8c586133abb7c 100644
+--- a/mm/memcontrol.c
++++ b/mm/memcontrol.c
+@@ -2368,9 +2368,18 @@ static void drain_all_stock(struct mem_cgroup *root_memcg)
+ static int memcg_hotplug_cpu_dead(unsigned int cpu)
+ {
+ struct memcg_stock_pcp *stock;
++ struct obj_cgroup *old;
++ unsigned long flags;
+
+ stock = &per_cpu(memcg_stock, cpu);
++
++ /* drain_obj_stock requires stock_lock */
++ local_lock_irqsave(&memcg_stock.stock_lock, flags);
++ old = drain_obj_stock(stock);
++ local_unlock_irqrestore(&memcg_stock.stock_lock, flags);
++
+ drain_stock(stock);
++ obj_cgroup_put(old);
+
+ return 0;
+ }
+--
+2.39.5
+
--- /dev/null
+From 57c6431bc0f9c39d8128e45de7ec69e307d696b8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 16 Apr 2025 12:24:13 +0200
+Subject: net: phy: microchip: force IRQ polling mode for lan88xx
+
+From: Fiona Klute <fiona.klute@gmx.de>
+
+[ Upstream commit 30a41ed32d3088cd0d682a13d7f30b23baed7e93 ]
+
+With lan88xx based devices the lan78xx driver can get stuck in an
+interrupt loop while bringing the device up, flooding the kernel log
+with messages like the following:
+
+lan78xx 2-3:1.0 enp1s0u3: kevent 4 may have been dropped
+
+Removing interrupt support from the lan88xx PHY driver forces the
+driver to use polling instead, which avoids the problem.
+
+The issue has been observed with Raspberry Pi devices at least since
+4.14 (see [1], bug report for their downstream kernel), as well as
+with Nvidia devices [2] in 2020, where disabling interrupts was the
+vendor-suggested workaround (together with the claim that phylib
+changes in 4.9 made the interrupt handling in lan78xx incompatible).
+
+Iperf reports well over 900Mbits/sec per direction with client in
+--dualtest mode, so there does not seem to be a significant impact on
+throughput (lan88xx device connected via switch to the peer).
+
+[1] https://github.com/raspberrypi/linux/issues/2447
+[2] https://forums.developer.nvidia.com/t/jetson-xavier-and-lan7800-problem/142134/11
+
+Link: https://lore.kernel.org/0901d90d-3f20-4a10-b680-9c978e04ddda@lunn.ch
+Fixes: 792aec47d59d ("add microchip LAN88xx phy driver")
+Signed-off-by: Fiona Klute <fiona.klute@gmx.de>
+Cc: kernel-list@raspberrypi.com
+Cc: stable@vger.kernel.org
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Link: https://patch.msgid.link/20250416102413.30654-1-fiona.klute@gmx.de
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/phy/microchip.c | 46 +++----------------------------------
+ 1 file changed, 3 insertions(+), 43 deletions(-)
+
+diff --git a/drivers/net/phy/microchip.c b/drivers/net/phy/microchip.c
+index 0b88635f4fbca..623607fd2cefd 100644
+--- a/drivers/net/phy/microchip.c
++++ b/drivers/net/phy/microchip.c
+@@ -31,47 +31,6 @@ static int lan88xx_write_page(struct phy_device *phydev, int page)
+ return __phy_write(phydev, LAN88XX_EXT_PAGE_ACCESS, page);
+ }
+
+-static int lan88xx_phy_config_intr(struct phy_device *phydev)
+-{
+- int rc;
+-
+- if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
+- /* unmask all source and clear them before enable */
+- rc = phy_write(phydev, LAN88XX_INT_MASK, 0x7FFF);
+- rc = phy_read(phydev, LAN88XX_INT_STS);
+- rc = phy_write(phydev, LAN88XX_INT_MASK,
+- LAN88XX_INT_MASK_MDINTPIN_EN_ |
+- LAN88XX_INT_MASK_LINK_CHANGE_);
+- } else {
+- rc = phy_write(phydev, LAN88XX_INT_MASK, 0);
+- if (rc)
+- return rc;
+-
+- /* Ack interrupts after they have been disabled */
+- rc = phy_read(phydev, LAN88XX_INT_STS);
+- }
+-
+- return rc < 0 ? rc : 0;
+-}
+-
+-static irqreturn_t lan88xx_handle_interrupt(struct phy_device *phydev)
+-{
+- int irq_status;
+-
+- irq_status = phy_read(phydev, LAN88XX_INT_STS);
+- if (irq_status < 0) {
+- phy_error(phydev);
+- return IRQ_NONE;
+- }
+-
+- if (!(irq_status & LAN88XX_INT_STS_LINK_CHANGE_))
+- return IRQ_NONE;
+-
+- phy_trigger_machine(phydev);
+-
+- return IRQ_HANDLED;
+-}
+-
+ static int lan88xx_suspend(struct phy_device *phydev)
+ {
+ struct lan88xx_priv *priv = phydev->priv;
+@@ -392,8 +351,9 @@ static struct phy_driver microchip_phy_driver[] = {
+ .config_aneg = lan88xx_config_aneg,
+ .link_change_notify = lan88xx_link_change_notify,
+
+- .config_intr = lan88xx_phy_config_intr,
+- .handle_interrupt = lan88xx_handle_interrupt,
++ /* Interrupt handling is broken, do not define related
++ * functions to force polling.
++ */
+
+ .suspend = lan88xx_suspend,
+ .resume = genphy_resume,
+--
+2.39.5
+
--- /dev/null
+From af710f97542c389eaa50bc48fc02e7b19c1acebf Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 21 Apr 2025 22:12:59 +0200
+Subject: Revert "drm/meson: vclk: fix calculation of 59.94 fractional rates"
+
+From: Christian Hewitt <christianshewitt@gmail.com>
+
+[ Upstream commit f37bb5486ea536c1d61df89feeaeff3f84f0b560 ]
+
+This reverts commit bfbc68e.
+
+The patch does permit the offending YUV420 @ 59.94 phy_freq and
+vclk_freq mode to match in calculations. It also results in all
+fractional rates being unavailable for use. This was unintended
+and requires the patch to be reverted.
+
+Fixes: bfbc68e4d869 ("drm/meson: vclk: fix calculation of 59.94 fractional rates")
+Cc: stable@vger.kernel.org
+Signed-off-by: Christian Hewitt <christianshewitt@gmail.com>
+Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
+Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
+Link: https://lore.kernel.org/r/20250421201300.778955-2-martin.blumenstingl@googlemail.com
+Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
+Link: https://lore.kernel.org/r/20250421201300.778955-2-martin.blumenstingl@googlemail.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/meson/meson_vclk.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/gpu/drm/meson/meson_vclk.c b/drivers/gpu/drm/meson/meson_vclk.c
+index 2a942dc6a6dc2..2a82119eb58ed 100644
+--- a/drivers/gpu/drm/meson/meson_vclk.c
++++ b/drivers/gpu/drm/meson/meson_vclk.c
+@@ -790,13 +790,13 @@ meson_vclk_vic_supported_freq(struct meson_drm *priv, unsigned int phy_freq,
+ FREQ_1000_1001(params[i].pixel_freq));
+ DRM_DEBUG_DRIVER("i = %d phy_freq = %d alt = %d\n",
+ i, params[i].phy_freq,
+- FREQ_1000_1001(params[i].phy_freq/1000)*1000);
++ FREQ_1000_1001(params[i].phy_freq/10)*10);
+ /* Match strict frequency */
+ if (phy_freq == params[i].phy_freq &&
+ vclk_freq == params[i].vclk_freq)
+ return MODE_OK;
+ /* Match 1000/1001 variant */
+- if (phy_freq == (FREQ_1000_1001(params[i].phy_freq/1000)*1000) &&
++ if (phy_freq == (FREQ_1000_1001(params[i].phy_freq/10)*10) &&
+ vclk_freq == FREQ_1000_1001(params[i].vclk_freq))
+ return MODE_OK;
+ }
+@@ -1070,7 +1070,7 @@ void meson_vclk_setup(struct meson_drm *priv, unsigned int target,
+
+ for (freq = 0 ; params[freq].pixel_freq ; ++freq) {
+ if ((phy_freq == params[freq].phy_freq ||
+- phy_freq == FREQ_1000_1001(params[freq].phy_freq/1000)*1000) &&
++ phy_freq == FREQ_1000_1001(params[freq].phy_freq/10)*10) &&
+ (vclk_freq == params[freq].vclk_freq ||
+ vclk_freq == FREQ_1000_1001(params[freq].vclk_freq))) {
+ if (vclk_freq != params[freq].vclk_freq)
+--
+2.39.5
+
--- /dev/null
+From 07d22744abb51b806341b22c08041e12965cfd8f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 19 Apr 2025 13:14:00 +0200
+Subject: riscv: uprobes: Add missing fence.i after building the XOL buffer
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Björn Töpel <bjorn@rivosinc.com>
+
+[ Upstream commit 7d1d19a11cfbfd8bae1d89cc010b2cc397cd0c48 ]
+
+The XOL (execute out-of-line) buffer is used to single-step the
+replaced instruction(s) for uprobes. The RISC-V port was missing a
+proper fence.i (i$ flushing) after constructing the XOL buffer, which
+can result in incorrect execution of stale/broken instructions.
+
+This was found running the BPF selftests "test_progs:
+uprobe_autoattach, attach_probe" on the Spacemit K1/X60, where the
+uprobes tests randomly blew up.
+
+Reviewed-by: Guo Ren <guoren@kernel.org>
+Fixes: 74784081aac8 ("riscv: Add uprobes supported")
+Signed-off-by: Björn Töpel <bjorn@rivosinc.com>
+Link: https://lore.kernel.org/r/20250419111402.1660267-2-bjorn@kernel.org
+Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/riscv/kernel/probes/uprobes.c | 10 ++--------
+ 1 file changed, 2 insertions(+), 8 deletions(-)
+
+diff --git a/arch/riscv/kernel/probes/uprobes.c b/arch/riscv/kernel/probes/uprobes.c
+index 194f166b2cc40..0d18ee53fd649 100644
+--- a/arch/riscv/kernel/probes/uprobes.c
++++ b/arch/riscv/kernel/probes/uprobes.c
+@@ -161,6 +161,7 @@ void arch_uprobe_copy_ixol(struct page *page, unsigned long vaddr,
+ /* Initialize the slot */
+ void *kaddr = kmap_atomic(page);
+ void *dst = kaddr + (vaddr & ~PAGE_MASK);
++ unsigned long start = (unsigned long)dst;
+
+ memcpy(dst, src, len);
+
+@@ -170,13 +171,6 @@ void arch_uprobe_copy_ixol(struct page *page, unsigned long vaddr,
+ *(uprobe_opcode_t *)dst = __BUG_INSN_32;
+ }
+
++ flush_icache_range(start, start + len);
+ kunmap_atomic(kaddr);
+-
+- /*
+- * We probably need flush_icache_user_page() but it needs vma.
+- * This should work on most of architectures by default. If
+- * architecture needs to do something different it can define
+- * its own version of the function.
+- */
+- flush_dcache_page(page);
+ }
+--
+2.39.5
+
sch_qfq-make-qfq_qlen_notify-idempotent.patch
sch_ets-make-est_qlen_notify-idempotent.patch
revert-x86-kexec-allocate-pgd-for-x86_64-transition-page-tables-separately.patch
+firmware-arm_scmi-balance-device-refcount-when-destr.patch
+firmware-arm_ffa-skip-rx-buffer-ownership-release-if.patch
+arm-dts-opos6ul-add-ksz8081-phy-properties.patch
+net-phy-microchip-force-irq-polling-mode-for-lan88xx.patch
+revert-drm-meson-vclk-fix-calculation-of-59.94-fract.patch
+irqchip-gic-v2m-mark-a-few-functions-__init.patch
+irqchip-gic-v2m-prevent-use-after-free-of-gicv2m_get.patch
+memcg-drain-obj-stock-on-cpu-hotplug-teardown.patch
+riscv-uprobes-add-missing-fence.i-after-building-the.patch
+cpufreq-intel_pstate-revise-global-turbo-disable-che.patch
+cpufreq-intel_pstate-fold-intel_pstate_max_within_li.patch
+cpufreq-intel_pstate-do-not-update-global.turbo_disa.patch
+cpufreq-intel_pstate-unchecked-msr-aceess-in-legacy-.patch
+spi-tegra114-remove-unnecessary-null-pointer-checks.patch
+spi-tegra114-don-t-fail-set_cs_timing-when-delays-ar.patch
+iommu-arm-smmu-v3-use-the-new-rb-tree-helpers.patch
+iommu-arm-smmu-v3-fix-iommu_device_probe-bug-due-to-.patch
+drm-amd-display-phase2-enable-mst-hdcp-multiple-disp.patch
+drm-amd-display-clean-up-style-problems-in-amdgpu_dm.patch
+drm-amd-display-change-hdcp-update-sequence-for-dm.patch
+drm-amd-display-add-scoped-mutexes-for-amdgpu_dm_dhc.patch
+drm-amd-display-fix-slab-use-after-free-in-hdcp.patch
+asoc-use-of_property_read_bool.patch
+asoc-soc-core-stop-using-of_property_read_bool-for-n.patch
--- /dev/null
+From 7cbc152533a52b99ee873db1e13646947d8ccc93 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 23 Apr 2025 21:03:03 -0500
+Subject: spi: tegra114: Don't fail set_cs_timing when delays are zero
+
+From: Aaron Kling <webgeek1234@gmail.com>
+
+[ Upstream commit 4426e6b4ecf632bb75d973051e1179b8bfac2320 ]
+
+The original code would skip null delay pointers, but when the pointers
+were converted to point within the spi_device struct, the check was not
+updated to skip delays of zero. Hence all spi devices that didn't set
+delays would fail to probe.
+
+Fixes: 04e6bb0d6bb1 ("spi: modify set_cs_timing parameter")
+Cc: stable@vger.kernel.org
+Signed-off-by: Aaron Kling <webgeek1234@gmail.com>
+Link: https://patch.msgid.link/20250423-spi-tegra114-v1-1-2d608bcc12f9@gmail.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/spi/spi-tegra114.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c
+index 6b56108308fc5..60799ab60eb45 100644
+--- a/drivers/spi/spi-tegra114.c
++++ b/drivers/spi/spi-tegra114.c
+@@ -729,9 +729,9 @@ static int tegra_spi_set_hw_cs_timing(struct spi_device *spi)
+ u32 inactive_cycles;
+ u8 cs_state;
+
+- if (setup->unit != SPI_DELAY_UNIT_SCK ||
+- hold->unit != SPI_DELAY_UNIT_SCK ||
+- inactive->unit != SPI_DELAY_UNIT_SCK) {
++ if ((setup->unit && setup->unit != SPI_DELAY_UNIT_SCK) ||
++ (hold->unit && hold->unit != SPI_DELAY_UNIT_SCK) ||
++ (inactive->unit && inactive->unit != SPI_DELAY_UNIT_SCK)) {
+ dev_err(&spi->dev,
+ "Invalid delay unit %d, should be SPI_DELAY_UNIT_SCK\n",
+ SPI_DELAY_UNIT_SCK);
+--
+2.39.5
+
--- /dev/null
+From 9f6421ddc72eaeff8e254d44e7d3e650d26f0c5a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 15 Aug 2023 12:20:58 +0300
+Subject: spi: tegra114: Remove unnecessary NULL-pointer checks
+
+From: Alexander Danilenko <al.b.danilenko@gmail.com>
+
+[ Upstream commit 373c36bf7914e3198ac2654dede499f340c52950 ]
+
+cs_setup, cs_hold and cs_inactive points to fields of spi_device struct,
+so there is no sense in checking them for NULL.
+
+Found by Linux Verification Center (linuxtesting.org) with SVACE.
+
+Fixes: 04e6bb0d6bb1 ("spi: modify set_cs_timing parameter")
+Signed-off-by: Alexander Danilenko <al.b.danilenko@gmail.com>
+Link: https://lore.kernel.org/r/20230815092058.4083-1-al.b.danilenko@gmail.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Stable-dep-of: 4426e6b4ecf6 ("spi: tegra114: Don't fail set_cs_timing when delays are zero")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/spi/spi-tegra114.c | 18 +++++++-----------
+ 1 file changed, 7 insertions(+), 11 deletions(-)
+
+diff --git a/drivers/spi/spi-tegra114.c b/drivers/spi/spi-tegra114.c
+index d9be80e3e1bcb..6b56108308fc5 100644
+--- a/drivers/spi/spi-tegra114.c
++++ b/drivers/spi/spi-tegra114.c
+@@ -723,27 +723,23 @@ static int tegra_spi_set_hw_cs_timing(struct spi_device *spi)
+ struct spi_delay *setup = &spi->cs_setup;
+ struct spi_delay *hold = &spi->cs_hold;
+ struct spi_delay *inactive = &spi->cs_inactive;
+- u8 setup_dly, hold_dly, inactive_dly;
++ u8 setup_dly, hold_dly;
+ u32 setup_hold;
+ u32 spi_cs_timing;
+ u32 inactive_cycles;
+ u8 cs_state;
+
+- if ((setup && setup->unit != SPI_DELAY_UNIT_SCK) ||
+- (hold && hold->unit != SPI_DELAY_UNIT_SCK) ||
+- (inactive && inactive->unit != SPI_DELAY_UNIT_SCK)) {
++ if (setup->unit != SPI_DELAY_UNIT_SCK ||
++ hold->unit != SPI_DELAY_UNIT_SCK ||
++ inactive->unit != SPI_DELAY_UNIT_SCK) {
+ dev_err(&spi->dev,
+ "Invalid delay unit %d, should be SPI_DELAY_UNIT_SCK\n",
+ SPI_DELAY_UNIT_SCK);
+ return -EINVAL;
+ }
+
+- setup_dly = setup ? setup->value : 0;
+- hold_dly = hold ? hold->value : 0;
+- inactive_dly = inactive ? inactive->value : 0;
+-
+- setup_dly = min_t(u8, setup_dly, MAX_SETUP_HOLD_CYCLES);
+- hold_dly = min_t(u8, hold_dly, MAX_SETUP_HOLD_CYCLES);
++ setup_dly = min_t(u8, setup->value, MAX_SETUP_HOLD_CYCLES);
++ hold_dly = min_t(u8, hold->value, MAX_SETUP_HOLD_CYCLES);
+ if (setup_dly && hold_dly) {
+ setup_hold = SPI_SETUP_HOLD(setup_dly - 1, hold_dly - 1);
+ spi_cs_timing = SPI_CS_SETUP_HOLD(tspi->spi_cs_timing1,
+@@ -755,7 +751,7 @@ static int tegra_spi_set_hw_cs_timing(struct spi_device *spi)
+ }
+ }
+
+- inactive_cycles = min_t(u8, inactive_dly, MAX_INACTIVE_CYCLES);
++ inactive_cycles = min_t(u8, inactive->value, MAX_INACTIVE_CYCLES);
+ if (inactive_cycles)
+ inactive_cycles--;
+ cs_state = inactive_cycles ? 0 : 1;
+--
+2.39.5
+