--- /dev/null
+From 6ee27ee27ba8b2e725886951ba2d2d87f113bece Mon Sep 17 00:00:00 2001
+From: Evan Quan <evan.quan@amd.com>
+Date: Fri, 5 Nov 2021 15:25:30 +0800
+Subject: drm/amd/pm: avoid duplicate powergate/ungate setting
+
+From: Evan Quan <evan.quan@amd.com>
+
+commit 6ee27ee27ba8b2e725886951ba2d2d87f113bece upstream.
+
+Just bail out if the target IP block is already in the desired
+powergate/ungate state. This can avoid some duplicate settings
+which sometimes may cause unexpected issues.
+
+Link: https://lore.kernel.org/all/YV81vidWQLWvATMM@zn.tnic/
+Bug: https://bugzilla.kernel.org/show_bug.cgi?id=214921
+Bug: https://bugzilla.kernel.org/show_bug.cgi?id=215025
+Bug: https://gitlab.freedesktop.org/drm/amd/-/issues/1789
+Fixes: bf756fb833cb ("drm/amdgpu: add missing cleanups for Polaris12 UVD/VCE on suspend")
+Signed-off-by: Evan Quan <evan.quan@amd.com>
+Tested-by: Borislav Petkov <bp@suse.de>
+Reviewed-by: Lijo Lazar <lijo.lazar@amd.com>
+Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
+Cc: stable@vger.kernel.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 3 +++
+ drivers/gpu/drm/amd/include/amd_shared.h | 3 ++-
+ drivers/gpu/drm/amd/pm/amdgpu_dpm.c | 10 ++++++++++
+ drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h | 8 ++++++++
+ 4 files changed, 23 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
++++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+@@ -3532,6 +3532,9 @@ int amdgpu_device_init(struct amdgpu_dev
+ adev->rmmio_size = pci_resource_len(adev->pdev, 2);
+ }
+
++ for (i = 0; i < AMD_IP_BLOCK_TYPE_NUM; i++)
++ atomic_set(&adev->pm.pwr_state[i], POWER_STATE_UNKNOWN);
++
+ adev->rmmio = ioremap(adev->rmmio_base, adev->rmmio_size);
+ if (adev->rmmio == NULL) {
+ return -ENOMEM;
+--- a/drivers/gpu/drm/amd/include/amd_shared.h
++++ b/drivers/gpu/drm/amd/include/amd_shared.h
+@@ -98,7 +98,8 @@ enum amd_ip_block_type {
+ AMD_IP_BLOCK_TYPE_ACP,
+ AMD_IP_BLOCK_TYPE_VCN,
+ AMD_IP_BLOCK_TYPE_MES,
+- AMD_IP_BLOCK_TYPE_JPEG
++ AMD_IP_BLOCK_TYPE_JPEG,
++ AMD_IP_BLOCK_TYPE_NUM,
+ };
+
+ enum amd_clockgating_state {
+--- a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c
++++ b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c
+@@ -927,6 +927,13 @@ int amdgpu_dpm_set_powergating_by_smu(st
+ {
+ int ret = 0;
+ const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs;
++ enum ip_power_state pwr_state = gate ? POWER_STATE_OFF : POWER_STATE_ON;
++
++ if (atomic_read(&adev->pm.pwr_state[block_type]) == pwr_state) {
++ dev_dbg(adev->dev, "IP block%d already in the target %s state!",
++ block_type, gate ? "gate" : "ungate");
++ return 0;
++ }
+
+ switch (block_type) {
+ case AMD_IP_BLOCK_TYPE_UVD:
+@@ -979,6 +986,9 @@ int amdgpu_dpm_set_powergating_by_smu(st
+ break;
+ }
+
++ if (!ret)
++ atomic_set(&adev->pm.pwr_state[block_type], pwr_state);
++
+ return ret;
+ }
+
+--- a/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h
++++ b/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h
+@@ -417,6 +417,12 @@ struct amdgpu_dpm {
+ enum amd_dpm_forced_level forced_level;
+ };
+
++enum ip_power_state {
++ POWER_STATE_UNKNOWN,
++ POWER_STATE_ON,
++ POWER_STATE_OFF,
++};
++
+ struct amdgpu_pm {
+ struct mutex mutex;
+ u32 current_sclk;
+@@ -452,6 +458,8 @@ struct amdgpu_pm {
+ struct i2c_adapter smu_i2c;
+ struct mutex smu_i2c_mutex;
+ struct list_head pm_attr_list;
++
++ atomic_t pwr_state[AMD_IP_BLOCK_TYPE_NUM];
+ };
+
+ #define R600_SSTU_DFLT 0
--- /dev/null
+From bf552083916a7f8800477b5986940d1c9a31b953 Mon Sep 17 00:00:00 2001
+From: hongao <hongao@uniontech.com>
+Date: Thu, 11 Nov 2021 11:32:07 +0800
+Subject: drm/amdgpu: fix set scaling mode Full/Full aspect/Center not works on vga and dvi connectors
+
+From: hongao <hongao@uniontech.com>
+
+commit bf552083916a7f8800477b5986940d1c9a31b953 upstream.
+
+amdgpu_connector_vga_get_modes missed function amdgpu_get_native_mode
+which assign amdgpu_encoder->native_mode with *preferred_mode result in
+amdgpu_encoder->native_mode.clock always be 0. That will cause
+amdgpu_connector_set_property returned early on:
+if ((rmx_type != DRM_MODE_SCALE_NONE) &&
+ (amdgpu_encoder->native_mode.clock == 0))
+when we try to set scaling mode Full/Full aspect/Center.
+Add the missing function to amdgpu_connector_vga_get_mode can fix this.
+It also works on dvi connectors because
+amdgpu_connector_dvi_helper_funcs.get_mode use the same method.
+
+Signed-off-by: hongao <hongao@uniontech.com>
+Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
+Cc: stable@vger.kernel.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c
++++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_connectors.c
+@@ -827,6 +827,7 @@ static int amdgpu_connector_vga_get_mode
+
+ amdgpu_connector_get_edid(connector);
+ ret = amdgpu_connector_ddc_get_modes(connector);
++ amdgpu_get_native_mode(connector);
+
+ return ret;
+ }
--- /dev/null
+From cc99bc62ff6902688ee7bd3a7b25eefc620fbb6a Mon Sep 17 00:00:00 2001
+From: Imre Deak <imre.deak@intel.com>
+Date: Mon, 18 Oct 2021 12:41:51 +0300
+Subject: drm/i915/dp: Ensure max link params are always valid
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Imre Deak <imre.deak@intel.com>
+
+commit cc99bc62ff6902688ee7bd3a7b25eefc620fbb6a upstream.
+
+Atm until the DPCD for a connector is read the max link rate and lane
+count params are invalid. If the connector is modeset, in
+intel_dp_compute_config(), intel_dp_common_len_rate_limit(max_link_rate)
+will return 0, leading to a intel_dp->common_rates[-1] access.
+
+Fix the above by making sure the max link params are always valid.
+
+The above access leads to an undefined behaviour by definition, though
+not causing a user visible problem to my best knowledge, see the previous
+patch why. Nevertheless it is an undefined behaviour and it triggers a
+BUG() in CONFIG_UBSAN builds, hence CC:stable.
+
+Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Imre Deak <imre.deak@intel.com>
+Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
+Acked-by: Jani Nikula <jani.nikula@intel.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20211018094154.1407705-4-imre.deak@intel.com
+(cherry picked from commit 9ad87de4735620ffc555592e8c5f580478fa3ed0)
+Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/i915/display/intel_dp.c | 18 ++++++++++--------
+ 1 file changed, 10 insertions(+), 8 deletions(-)
+
+--- a/drivers/gpu/drm/i915/display/intel_dp.c
++++ b/drivers/gpu/drm/i915/display/intel_dp.c
+@@ -1773,6 +1773,12 @@ void intel_dp_set_link_params(struct int
+ intel_dp->lane_count = lane_count;
+ }
+
++static void intel_dp_reset_max_link_params(struct intel_dp *intel_dp)
++{
++ intel_dp->max_link_lane_count = intel_dp_max_common_lane_count(intel_dp);
++ intel_dp->max_link_rate = intel_dp_max_common_rate(intel_dp);
++}
++
+ /* Enable backlight PWM and backlight PP control. */
+ void intel_edp_backlight_on(const struct intel_crtc_state *crtc_state,
+ const struct drm_connector_state *conn_state)
+@@ -1932,8 +1938,7 @@ void intel_dp_sync_state(struct intel_en
+ if (intel_dp->dpcd[DP_DPCD_REV] == 0)
+ intel_dp_get_dpcd(intel_dp);
+
+- intel_dp->max_link_lane_count = intel_dp_max_common_lane_count(intel_dp);
+- intel_dp->max_link_rate = intel_dp_max_common_rate(intel_dp);
++ intel_dp_reset_max_link_params(intel_dp);
+ }
+
+ bool intel_dp_initial_fastset_check(struct intel_encoder *encoder,
+@@ -2506,6 +2511,7 @@ intel_edp_init_dpcd(struct intel_dp *int
+ intel_dp_set_sink_rates(intel_dp);
+
+ intel_dp_set_common_rates(intel_dp);
++ intel_dp_reset_max_link_params(intel_dp);
+
+ /* Read the eDP DSC DPCD registers */
+ if (DISPLAY_VER(dev_priv) >= 10)
+@@ -4249,12 +4255,7 @@ intel_dp_detect(struct drm_connector *co
+ * supports link training fallback params.
+ */
+ if (intel_dp->reset_link_params || intel_dp->is_mst) {
+- /* Initial max link lane count */
+- intel_dp->max_link_lane_count = intel_dp_max_common_lane_count(intel_dp);
+-
+- /* Initial max link rate */
+- intel_dp->max_link_rate = intel_dp_max_common_rate(intel_dp);
+-
++ intel_dp_reset_max_link_params(intel_dp);
+ intel_dp->reset_link_params = false;
+ }
+
+@@ -5307,6 +5308,7 @@ intel_dp_init_connector(struct intel_dig
+ intel_dp_set_source_rates(intel_dp);
+ intel_dp_set_default_sink_rates(intel_dp);
+ intel_dp_set_common_rates(intel_dp);
++ intel_dp_reset_max_link_params(intel_dp);
+
+ intel_dp->reset_link_params = true;
+ intel_dp->pps.pps_pipe = INVALID_PIPE;
--- /dev/null
+From 6c34bd4532a3f39952952ddc102737595729afc4 Mon Sep 17 00:00:00 2001
+From: Imre Deak <imre.deak@intel.com>
+Date: Mon, 18 Oct 2021 17:34:17 +0300
+Subject: drm/i915/dp: Ensure sink rate values are always valid
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Imre Deak <imre.deak@intel.com>
+
+commit 6c34bd4532a3f39952952ddc102737595729afc4 upstream.
+
+Atm, there are no sink rate values set for DP (vs. eDP) sinks until the
+DPCD capabilities are successfully read from the sink. During this time
+intel_dp->num_common_rates is 0 which can lead to a
+
+intel_dp->common_rates[-1] (*)
+
+access, which is an undefined behaviour, in the following cases:
+
+- In intel_dp_sync_state(), if the encoder is enabled without a sink
+ connected to the encoder's connector (BIOS enabled a monitor, but the
+ user unplugged the monitor until the driver loaded).
+- In intel_dp_sync_state() if the encoder is enabled with a sink
+ connected, but for some reason the DPCD read has failed.
+- In intel_dp_compute_link_config() if modesetting a connector without
+ a sink connected on it.
+- In intel_dp_compute_link_config() if modesetting a connector with a
+ a sink connected on it, but before probing the connector first.
+
+To avoid the (*) access in all the above cases, make sure that the sink
+rate table - and hence the common rate table - is always valid, by
+setting a default minimum sink rate when registering the connector
+before anything could use it.
+
+I also considered setting all the DP link rates by default, so that
+modesetting with higher resolution modes also succeeds in the last two
+cases above. However in case a sink is not connected that would stop
+working after the first modeset, due to the LT fallback logic. So this
+would need more work, beyond the scope of this fix.
+
+As I mentioned in the previous patch, I don't think the issue this patch
+fixes is user visible, however it is an undefined behaviour by
+definition and triggers a BUG() in CONFIG_UBSAN builds, hence CC:stable.
+
+v2: Clear the default sink rates, before initializing these for eDP.
+
+Closes: https://gitlab.freedesktop.org/drm/intel/-/issues/4297
+References: https://gitlab.freedesktop.org/drm/intel/-/issues/4298
+Suggested-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
+Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Imre Deak <imre.deak@intel.com>
+Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
+Acked-by: Jani Nikula <jani.nikula@intel.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20211018143417.1452632-1-imre.deak@intel.com
+(cherry picked from commit 3f61ef9777c0ab0f03f4af0ed6fd3e5250537a8d)
+Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/i915/display/intel_dp.c | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+--- a/drivers/gpu/drm/i915/display/intel_dp.c
++++ b/drivers/gpu/drm/i915/display/intel_dp.c
+@@ -111,6 +111,12 @@ bool intel_dp_is_edp(struct intel_dp *in
+ static void intel_dp_unset_edid(struct intel_dp *intel_dp);
+ static int intel_dp_dsc_compute_bpp(struct intel_dp *intel_dp, u8 dsc_max_bpc);
+
++static void intel_dp_set_default_sink_rates(struct intel_dp *intel_dp)
++{
++ intel_dp->sink_rates[0] = 162000;
++ intel_dp->num_sink_rates = 1;
++}
++
+ /* update sink rates from dpcd */
+ static void intel_dp_set_sink_rates(struct intel_dp *intel_dp)
+ {
+@@ -2462,6 +2468,9 @@ intel_edp_init_dpcd(struct intel_dp *int
+ */
+ intel_psr_init_dpcd(intel_dp);
+
++ /* Clear the default sink rates */
++ intel_dp->num_sink_rates = 0;
++
+ /* Read the eDP 1.4+ supported link rates. */
+ if (intel_dp->edp_dpcd[0] >= DP_EDP_14) {
+ __le16 sink_rates[DP_MAX_SUPPORTED_RATES];
+@@ -5296,6 +5305,8 @@ intel_dp_init_connector(struct intel_dig
+ return false;
+
+ intel_dp_set_source_rates(intel_dp);
++ intel_dp_set_default_sink_rates(intel_dp);
++ intel_dp_set_common_rates(intel_dp);
+
+ intel_dp->reset_link_params = true;
+ intel_dp->pps.pps_pipe = INVALID_PIPE;
--- /dev/null
+From 1977e8eb40ed53f0cac7db1a78295726f4ac0b24 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= <ville.syrjala@linux.intel.com>
+Date: Mon, 25 Oct 2021 17:21:47 +0300
+Subject: drm/i915: Fix type1 DVI DP dual mode adapter heuristic for modern platforms
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Ville Syrjälä <ville.syrjala@linux.intel.com>
+
+commit 1977e8eb40ed53f0cac7db1a78295726f4ac0b24 upstream.
+
+Looks like we never updated intel_bios_is_port_dp_dual_mode() when
+the VBT port mapping became erratic on modern platforms. This
+is causing us to look up the wrong child device and thus throwing
+the heuristic off (ie. we might end looking at a child device for
+a genuine DP++ port when we were supposed to look at one for a
+native HDMI port).
+
+Fix it up by not using the outdated port_mapping[] in
+intel_bios_is_port_dp_dual_mode() and rely on
+intel_bios_encoder_data_lookup() instead.
+
+Cc: stable@vger.kernel.org
+Tested-by: Randy Dunlap <rdunlap@infradead.org>
+Closes: https://gitlab.freedesktop.org/drm/intel/-/issues/4138
+Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20211025142147.23897-1-ville.syrjala@linux.intel.com
+Reviewed-by: Jani Nikula <jani.nikula@intel.com>
+(cherry picked from commit 32c2bc89c7420fad2959ee23ef5b6be8b05d2bde)
+Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/i915/display/intel_bios.c | 85 ++++++++++++++++++++++--------
+ 1 file changed, 63 insertions(+), 22 deletions(-)
+
+--- a/drivers/gpu/drm/i915/display/intel_bios.c
++++ b/drivers/gpu/drm/i915/display/intel_bios.c
+@@ -1692,6 +1692,39 @@ static u8 map_ddc_pin(struct drm_i915_pr
+ return 0;
+ }
+
++static u8 dvo_port_type(u8 dvo_port)
++{
++ switch (dvo_port) {
++ case DVO_PORT_HDMIA:
++ case DVO_PORT_HDMIB:
++ case DVO_PORT_HDMIC:
++ case DVO_PORT_HDMID:
++ case DVO_PORT_HDMIE:
++ case DVO_PORT_HDMIF:
++ case DVO_PORT_HDMIG:
++ case DVO_PORT_HDMIH:
++ case DVO_PORT_HDMII:
++ return DVO_PORT_HDMIA;
++ case DVO_PORT_DPA:
++ case DVO_PORT_DPB:
++ case DVO_PORT_DPC:
++ case DVO_PORT_DPD:
++ case DVO_PORT_DPE:
++ case DVO_PORT_DPF:
++ case DVO_PORT_DPG:
++ case DVO_PORT_DPH:
++ case DVO_PORT_DPI:
++ return DVO_PORT_DPA;
++ case DVO_PORT_MIPIA:
++ case DVO_PORT_MIPIB:
++ case DVO_PORT_MIPIC:
++ case DVO_PORT_MIPID:
++ return DVO_PORT_MIPIA;
++ default:
++ return dvo_port;
++ }
++}
++
+ static enum port __dvo_port_to_port(int n_ports, int n_dvo,
+ const int port_mapping[][3], u8 dvo_port)
+ {
+@@ -2622,35 +2655,17 @@ bool intel_bios_is_port_edp(struct drm_i
+ return false;
+ }
+
+-static bool child_dev_is_dp_dual_mode(const struct child_device_config *child,
+- enum port port)
++static bool child_dev_is_dp_dual_mode(const struct child_device_config *child)
+ {
+- static const struct {
+- u16 dp, hdmi;
+- } port_mapping[] = {
+- /*
+- * Buggy VBTs may declare DP ports as having
+- * HDMI type dvo_port :( So let's check both.
+- */
+- [PORT_B] = { DVO_PORT_DPB, DVO_PORT_HDMIB, },
+- [PORT_C] = { DVO_PORT_DPC, DVO_PORT_HDMIC, },
+- [PORT_D] = { DVO_PORT_DPD, DVO_PORT_HDMID, },
+- [PORT_E] = { DVO_PORT_DPE, DVO_PORT_HDMIE, },
+- [PORT_F] = { DVO_PORT_DPF, DVO_PORT_HDMIF, },
+- };
+-
+- if (port == PORT_A || port >= ARRAY_SIZE(port_mapping))
+- return false;
+-
+ if ((child->device_type & DEVICE_TYPE_DP_DUAL_MODE_BITS) !=
+ (DEVICE_TYPE_DP_DUAL_MODE & DEVICE_TYPE_DP_DUAL_MODE_BITS))
+ return false;
+
+- if (child->dvo_port == port_mapping[port].dp)
++ if (dvo_port_type(child->dvo_port) == DVO_PORT_DPA)
+ return true;
+
+ /* Only accept a HDMI dvo_port as DP++ if it has an AUX channel */
+- if (child->dvo_port == port_mapping[port].hdmi &&
++ if (dvo_port_type(child->dvo_port) == DVO_PORT_HDMIA &&
+ child->aux_channel != 0)
+ return true;
+
+@@ -2660,10 +2675,36 @@ static bool child_dev_is_dp_dual_mode(co
+ bool intel_bios_is_port_dp_dual_mode(struct drm_i915_private *i915,
+ enum port port)
+ {
++ static const struct {
++ u16 dp, hdmi;
++ } port_mapping[] = {
++ /*
++ * Buggy VBTs may declare DP ports as having
++ * HDMI type dvo_port :( So let's check both.
++ */
++ [PORT_B] = { DVO_PORT_DPB, DVO_PORT_HDMIB, },
++ [PORT_C] = { DVO_PORT_DPC, DVO_PORT_HDMIC, },
++ [PORT_D] = { DVO_PORT_DPD, DVO_PORT_HDMID, },
++ [PORT_E] = { DVO_PORT_DPE, DVO_PORT_HDMIE, },
++ [PORT_F] = { DVO_PORT_DPF, DVO_PORT_HDMIF, },
++ };
+ const struct intel_bios_encoder_data *devdata;
+
++ if (HAS_DDI(i915)) {
++ const struct intel_bios_encoder_data *devdata;
++
++ devdata = intel_bios_encoder_data_lookup(i915, port);
++
++ return devdata && child_dev_is_dp_dual_mode(&devdata->child);
++ }
++
++ if (port == PORT_A || port >= ARRAY_SIZE(port_mapping))
++ return false;
++
+ list_for_each_entry(devdata, &i915->vbt.display_devices, node) {
+- if (child_dev_is_dp_dual_mode(&devdata->child, port))
++ if ((devdata->child.dvo_port == port_mapping[port].dp ||
++ devdata->child.dvo_port == port_mapping[port].hdmi) &&
++ child_dev_is_dp_dual_mode(&devdata->child))
+ return true;
+ }
+
--- /dev/null
+From abae9164a421bc4a41a3769f01ebcd1f9d955e0e Mon Sep 17 00:00:00 2001
+From: Jeremy Cline <jcline@redhat.com>
+Date: Wed, 25 Nov 2020 15:26:47 -0500
+Subject: drm/nouveau: Add a dedicated mutex for the clients list
+
+From: Jeremy Cline <jcline@redhat.com>
+
+commit abae9164a421bc4a41a3769f01ebcd1f9d955e0e upstream.
+
+Rather than protecting the nouveau_drm clients list with the lock within
+the "client" nouveau_cli, add a dedicated lock to serialize access to
+the list. This is both clearer and necessary to avoid lockdep being
+upset with us when we need to iterate through all the clients in the
+list and potentially lock their mutex, which is the same class as the
+lock protecting the entire list.
+
+Cc: stable@vger.kernel.org # 5.4+
+Signed-off-by: Jeremy Cline <jcline@redhat.com>
+Reviewed-by: Lyude Paul <lyude@redhat.com>
+Reviewed-by: Ben Skeggs <bskeggs@redhat.com>
+Tested-by: Karol Herbst <kherbst@redhat.com>
+Signed-off-by: Karol Herbst <kherbst@redhat.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20201125202648.5220-3-jcline@redhat.com
+Link: https://gitlab.freedesktop.org/drm/nouveau/-/merge_requests/14
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/nouveau/nouveau_drm.c | 10 ++++++----
+ drivers/gpu/drm/nouveau/nouveau_drv.h | 5 +++++
+ 2 files changed, 11 insertions(+), 4 deletions(-)
+
+--- a/drivers/gpu/drm/nouveau/nouveau_drm.c
++++ b/drivers/gpu/drm/nouveau/nouveau_drm.c
+@@ -562,6 +562,7 @@ nouveau_drm_device_init(struct drm_devic
+ nvkm_dbgopt(nouveau_debug, "DRM");
+
+ INIT_LIST_HEAD(&drm->clients);
++ mutex_init(&drm->clients_lock);
+ spin_lock_init(&drm->tile.lock);
+
+ /* workaround an odd issue on nvc1 by disabling the device's
+@@ -659,6 +660,7 @@ nouveau_drm_device_fini(struct drm_devic
+ nouveau_cli_fini(&drm->client);
+ nouveau_cli_fini(&drm->master);
+ nvif_parent_dtor(&drm->parent);
++ mutex_destroy(&drm->clients_lock);
+ kfree(drm);
+ }
+
+@@ -1090,9 +1092,9 @@ nouveau_drm_open(struct drm_device *dev,
+
+ fpriv->driver_priv = cli;
+
+- mutex_lock(&drm->client.mutex);
++ mutex_lock(&drm->clients_lock);
+ list_add(&cli->head, &drm->clients);
+- mutex_unlock(&drm->client.mutex);
++ mutex_unlock(&drm->clients_lock);
+
+ done:
+ if (ret && cli) {
+@@ -1118,9 +1120,9 @@ nouveau_drm_postclose(struct drm_device
+ nouveau_abi16_fini(cli->abi16);
+ mutex_unlock(&cli->mutex);
+
+- mutex_lock(&drm->client.mutex);
++ mutex_lock(&drm->clients_lock);
+ list_del(&cli->head);
+- mutex_unlock(&drm->client.mutex);
++ mutex_unlock(&drm->clients_lock);
+
+ nouveau_cli_fini(cli);
+ kfree(cli);
+--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
++++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
+@@ -139,6 +139,11 @@ struct nouveau_drm {
+
+ struct list_head clients;
+
++ /**
++ * @clients_lock: Protects access to the @clients list of &struct nouveau_cli.
++ */
++ struct mutex clients_lock;
++
+ u8 old_pm_cap;
+
+ struct {
--- /dev/null
+From f55aaf63bde0d0336c3823bb3713bd4a464abbcf Mon Sep 17 00:00:00 2001
+From: Jeremy Cline <jcline@redhat.com>
+Date: Wed, 25 Nov 2020 15:26:48 -0500
+Subject: drm/nouveau: clean up all clients on device removal
+
+From: Jeremy Cline <jcline@redhat.com>
+
+commit f55aaf63bde0d0336c3823bb3713bd4a464abbcf upstream.
+
+The postclose handler can run after the device has been removed (or the
+driver has been unbound) since userspace clients are free to hold the
+file open as long as they want. Because the device removal callback
+frees the entire nouveau_drm structure, any reference to it in the
+postclose handler will result in a use-after-free.
+
+To reproduce this, one must simply open the device file, unbind the
+driver (or physically remove the device), and then close the device
+file. This was found and can be reproduced easily with the IGT
+core_hotunplug tests.
+
+To avoid this, all clients are cleaned up in the device finalization
+rather than deferring it to the postclose handler, and the postclose
+handler is protected by a critical section which ensures the
+drm_dev_unplug() and the postclose handler won't race.
+
+This is not an ideal fix, since as I understand the proposed plan for
+the kernel<->userspace interface for hotplug support, destroying the
+client before the file is closed will cause problems. However, I believe
+to properly fix this issue, the lifetime of the nouveau_drm structure
+needs to be extended to match the drm_device, and this proved to be a
+rather invasive change. Thus, I've broken this out so the fix can be
+easily backported.
+
+This fixes with the two previous commits CVE-2020-27820 (Karol).
+
+Cc: stable@vger.kernel.org # 5.4+
+Signed-off-by: Jeremy Cline <jcline@redhat.com>
+Reviewed-by: Lyude Paul <lyude@redhat.com>
+Reviewed-by: Ben Skeggs <bskeggs@redhat.com>
+Tested-by: Karol Herbst <kherbst@redhat.com>
+Signed-off-by: Karol Herbst <kherbst@redhat.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20201125202648.5220-4-jcline@redhat.com
+Link: https://gitlab.freedesktop.org/drm/nouveau/-/merge_requests/14
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/nouveau/nouveau_drm.c | 30 ++++++++++++++++++++++++++++++
+ 1 file changed, 30 insertions(+)
+
+--- a/drivers/gpu/drm/nouveau/nouveau_drm.c
++++ b/drivers/gpu/drm/nouveau/nouveau_drm.c
+@@ -633,6 +633,7 @@ fail_alloc:
+ static void
+ nouveau_drm_device_fini(struct drm_device *dev)
+ {
++ struct nouveau_cli *cli, *temp_cli;
+ struct nouveau_drm *drm = nouveau_drm(dev);
+
+ if (nouveau_pmops_runtime()) {
+@@ -657,6 +658,24 @@ nouveau_drm_device_fini(struct drm_devic
+ nouveau_ttm_fini(drm);
+ nouveau_vga_fini(drm);
+
++ /*
++ * There may be existing clients from as-yet unclosed files. For now,
++ * clean them up here rather than deferring until the file is closed,
++ * but this likely not correct if we want to support hot-unplugging
++ * properly.
++ */
++ mutex_lock(&drm->clients_lock);
++ list_for_each_entry_safe(cli, temp_cli, &drm->clients, head) {
++ list_del(&cli->head);
++ mutex_lock(&cli->mutex);
++ if (cli->abi16)
++ nouveau_abi16_fini(cli->abi16);
++ mutex_unlock(&cli->mutex);
++ nouveau_cli_fini(cli);
++ kfree(cli);
++ }
++ mutex_unlock(&drm->clients_lock);
++
+ nouveau_cli_fini(&drm->client);
+ nouveau_cli_fini(&drm->master);
+ nvif_parent_dtor(&drm->parent);
+@@ -1112,6 +1131,16 @@ nouveau_drm_postclose(struct drm_device
+ {
+ struct nouveau_cli *cli = nouveau_cli(fpriv);
+ struct nouveau_drm *drm = nouveau_drm(dev);
++ int dev_index;
++
++ /*
++ * The device is gone, and as it currently stands all clients are
++ * cleaned up in the removal codepath. In the future this may change
++ * so that we can support hot-unplugging, but for now we immediately
++ * return to avoid a double-free situation.
++ */
++ if (!drm_dev_enter(dev, &dev_index))
++ return;
+
+ pm_runtime_get_sync(dev->dev);
+
+@@ -1128,6 +1157,7 @@ nouveau_drm_postclose(struct drm_device
+ kfree(cli);
+ pm_runtime_mark_last_busy(dev->dev);
+ pm_runtime_put_autosuspend(dev->dev);
++ drm_dev_exit(dev_index);
+ }
+
+ static const struct drm_ioctl_desc
--- /dev/null
+From aff2299e0d81b26304ccc6a1ec0170e437f38efc Mon Sep 17 00:00:00 2001
+From: Jeremy Cline <jcline@redhat.com>
+Date: Wed, 25 Nov 2020 15:26:46 -0500
+Subject: drm/nouveau: use drm_dev_unplug() during device removal
+
+From: Jeremy Cline <jcline@redhat.com>
+
+commit aff2299e0d81b26304ccc6a1ec0170e437f38efc upstream.
+
+Nouveau does not currently support hot-unplugging, but it still makes
+sense to switch from drm_dev_unregister() to drm_dev_unplug().
+drm_dev_unplug() calls drm_dev_unregister() after marking the device as
+unplugged, but only after any device critical sections are finished.
+
+Since nouveau isn't using drm_dev_enter() and drm_dev_exit(), there are
+no critical sections so this is nearly functionally equivalent. However,
+the DRM layer does check to see if the device is unplugged, and if it is
+returns appropriate error codes.
+
+In the future nouveau can add critical sections in order to truly
+support hot-unplugging.
+
+Cc: stable@vger.kernel.org # 5.4+
+Signed-off-by: Jeremy Cline <jcline@redhat.com>
+Reviewed-by: Lyude Paul <lyude@redhat.com>
+Reviewed-by: Ben Skeggs <bskeggs@redhat.com>
+Tested-by: Karol Herbst <kherbst@redhat.com>
+Signed-off-by: Karol Herbst <kherbst@redhat.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20201125202648.5220-2-jcline@redhat.com
+Link: https://gitlab.freedesktop.org/drm/nouveau/-/merge_requests/14
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/nouveau/nouveau_drm.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/nouveau/nouveau_drm.c
++++ b/drivers/gpu/drm/nouveau/nouveau_drm.c
+@@ -798,7 +798,7 @@ nouveau_drm_device_remove(struct drm_dev
+ struct nvkm_client *client;
+ struct nvkm_device *device;
+
+- drm_dev_unregister(dev);
++ drm_dev_unplug(dev);
+
+ client = nvxx_client(&drm->client.base);
+ device = nvkm_device_find(client->device);
--- /dev/null
+From 8244a3bc27b3efd057da154b8d7e414670d5044f Mon Sep 17 00:00:00 2001
+From: Anand K Mistry <amistry@google.com>
+Date: Thu, 30 Sep 2021 09:00:07 +1000
+Subject: drm/prime: Fix use after free in mmap with drm_gem_ttm_mmap
+
+From: Anand K Mistry <amistry@google.com>
+
+commit 8244a3bc27b3efd057da154b8d7e414670d5044f upstream.
+
+drm_gem_ttm_mmap() drops a reference to the gem object on success. If
+the gem object's refcount == 1 on entry to drm_gem_prime_mmap(), that
+drop will free the gem object, and the subsequent drm_gem_object_get()
+will be a UAF. Fix by grabbing a reference before calling the mmap
+helper.
+
+This issue was forseen when the reference dropping was adding in
+commit 9786b65bc61ac ("drm/ttm: fix mmap refcounting"):
+ "For that to work properly the drm_gem_object_get() call in
+ drm_gem_ttm_mmap() must be moved so it happens before calling
+ obj->funcs->mmap(), otherwise the gem refcount would go down
+ to zero."
+
+Signed-off-by: Anand K Mistry <amistry@google.com>
+Fixes: 9786b65bc61a ("drm/ttm: fix mmap refcounting")
+Cc: Gerd Hoffmann <kraxel@redhat.com>
+Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
+Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
+Cc: Maxime Ripard <mripard@kernel.org>
+Cc: Thomas Zimmermann <tzimmermann@suse.de>
+Cc: David Airlie <airlied@linux.ie>
+Cc: Daniel Vetter <daniel@ffwll.ch>
+Cc: dri-devel@lists.freedesktop.org
+Cc: <stable@vger.kernel.org> # v5.5+
+Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
+Link: https://patchwork.freedesktop.org/patch/msgid/20210930085932.1.I8043d61cc238e0168e2f4ca5f4783223434aa587@changeid
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/drm_prime.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpu/drm/drm_prime.c
++++ b/drivers/gpu/drm/drm_prime.c
+@@ -719,11 +719,13 @@ int drm_gem_prime_mmap(struct drm_gem_ob
+ if (obj->funcs && obj->funcs->mmap) {
+ vma->vm_ops = obj->funcs->vm_ops;
+
++ drm_gem_object_get(obj);
+ ret = obj->funcs->mmap(obj, vma);
+- if (ret)
++ if (ret) {
++ drm_gem_object_put(obj);
+ return ret;
++ }
+ vma->vm_private_data = obj;
+- drm_gem_object_get(obj);
+ return 0;
+ }
+
drm-i915-guc-don-t-drop-ce-guc_active.lock-when-unwinding-context.patch
drm-i915-guc-unwind-context-requests-in-reverse-order.patch
drm-udl-fix-control-message-timeout.patch
+drm-prime-fix-use-after-free-in-mmap-with-drm_gem_ttm_mmap.patch
+drm-nouveau-add-a-dedicated-mutex-for-the-clients-list.patch
+drm-nouveau-use-drm_dev_unplug-during-device-removal.patch
+drm-nouveau-clean-up-all-clients-on-device-removal.patch
+drm-i915-dp-ensure-sink-rate-values-are-always-valid.patch
+drm-i915-dp-ensure-max-link-params-are-always-valid.patch
+drm-i915-fix-type1-dvi-dp-dual-mode-adapter-heuristic-for-modern-platforms.patch
+drm-amdgpu-fix-set-scaling-mode-full-full-aspect-center-not-works-on-vga-and-dvi-connectors.patch
+drm-amd-pm-avoid-duplicate-powergate-ungate-setting.patch