* linux-next: manual merge of the drm tree with the drm-fixes tree
@ 2026-11-13 16:07 Mark Brown
0 siblings, 0 replies; 48+ messages in thread
From: Mark Brown @ 2026-11-13 16:07 UTC (permalink / raw)
To: Dave Airlie, DRI; +Cc: Linux Kernel Mailing List, Linux Next Mailing List
[-- Attachment #1: Type: text/plain, Size: 110722 bytes --]
Hi all,
Today's linux-next merge of the drm tree got conflicts in:
drivers/gpu/drm/xe/xe_amc.c
drivers/gpu/drm/xe/xe_configfs.h
drivers/gpu/drm/xe/xe_device.c
drivers/gpu/drm/xe/xe_device.h
drivers/gpu/drm/xe/xe_device_types.h
drivers/gpu/drm/xe/xe_drm_ras.c
drivers/gpu/drm/xe/xe_gsc.c
drivers/gpu/drm/xe/xe_gt.c
drivers/gpu/drm/xe/xe_gt_sriov_vf.c
drivers/gpu/drm/xe/xe_guc_submit.c
drivers/gpu/drm/xe/xe_hwmon.c
drivers/gpu/drm/xe/xe_late_bind_fw.c
drivers/gpu/drm/xe/xe_mert.c
drivers/gpu/drm/xe/xe_pat.c
drivers/gpu/drm/xe/xe_pci.c
drivers/gpu/drm/xe/xe_pci_error.c
drivers/gpu/drm/xe/xe_pm.c
drivers/gpu/drm/xe/xe_ras.c
drivers/gpu/drm/xe/xe_survivability_mode.c
between commits from the drm-fixes tree and commits from the drm tree
which it didn't seem worthwhile to identify :/ .
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
diff --git a/drivers/gpu/drm/xe/xe_amc.c b/drivers/gpu/drm/xe/xe_amc.c
index d6fe59669a74b..37694e34271a1 100644
--- a/drivers/gpu/drm/xe/xe_amc.c
+++ b/drivers/gpu/drm/xe/xe_amc.c
@@ -20,6 +20,7 @@
#include "xe_i2c.h"
#include "xe_mmio.h"
#include "xe_pm.h"
+#include "xe_wedge.h"
/**
* DOC: Add-In Management Controller (AMC)
@@ -165,8 +166,8 @@ static void xe_amc_work(struct work_struct *work)
case AMC_ALERT_OOB_RESET:
case AMC_ALERT_CATERR:
dev_warn(amc->i2c->drm_dev, "AMC Alert: %s\n", amc_alert[alert_reason]);
- xe_device_declare_wedged(i2c_client_to_xe_device(client),
- DRM_WEDGE_RECOVERY_REBIND | DRM_WEDGE_RECOVERY_BUS_RESET);
+ xe_wedge_declare(i2c_client_to_xe_device(client),
+ DRM_WEDGE_RECOVERY_REBIND | DRM_WEDGE_RECOVERY_BUS_RESET);
break;
default:
dev_warn(amc->i2c->drm_dev, "unknown AMC alert: %d\n", alert_reason);
diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c
index c41533644c928..273e2c45fea9b 100644
--- a/drivers/gpu/drm/xe/xe_device.c
+++ b/drivers/gpu/drm/xe/xe_device.c
@@ -81,6 +81,7 @@
#include "xe_vsec.h"
#include "xe_wait_user_fence.h"
#include "xe_wa.h"
+#include "xe_wedge.h"
#include <generated/xe_device_wa_oob.h>
#include <generated/xe_wa_oob.h>
@@ -238,7 +239,7 @@ static long xe_drm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
struct xe_device *xe = to_xe_device(file_priv->minor->dev);
long ret;
- if (xe_device_wedged(xe))
+ if (xe_wedged(xe))
return -ECANCELED;
ACQUIRE(xe_pm_runtime_ioctl, pm)(xe);
@@ -256,7 +257,7 @@ static long xe_drm_compat_ioctl(struct file *file, unsigned int cmd, unsigned lo
struct xe_device *xe = to_xe_device(file_priv->minor->dev);
long ret;
- if (xe_device_wedged(xe))
+ if (xe_wedged(xe))
return -ECANCELED;
ACQUIRE(xe_pm_runtime_ioctl, pm)(xe);
@@ -373,6 +374,9 @@ bool xe_device_is_admin_only(const struct xe_device *xe)
}
#endif
+/* Number of allocated struct xe_device */
+static atomic_t xe_device_count;
+
static void xe_device_destroy(struct drm_device *dev, void *dummy)
{
struct xe_device *xe = to_xe_device(dev);
@@ -392,6 +396,9 @@ static void xe_device_destroy(struct drm_device *dev, void *dummy)
destroy_workqueue(xe->destroy_wq);
ttm_device_fini(&xe->ttm);
+
+ if (atomic_dec_and_test(&xe_device_count))
+ wake_up_var(&xe_device_count);
}
/**
@@ -462,6 +469,7 @@ int xe_device_init_early(struct xe_device *xe)
return err;
xe_bo_dev_init(&xe->bo_device);
+ atomic_inc(&xe_device_count);
err = drmm_add_action_or_reset(&xe->drm, xe_device_destroy, NULL);
if (err)
return err;
@@ -668,7 +676,7 @@ static void vf_update_device_info(struct xe_device *xe)
xe->info.has_heci_cscfi = 0;
xe->info.has_heci_gscfi = 0;
xe->info.has_i2c = 0;
- xe->info.has_late_bind = 0;
+ xe->info.late_bind_mask = 0;
xe->info.skip_guc_pc = 1;
xe->info.skip_pcode = 1;
xe->info.has_drm_ras = false;
@@ -690,16 +698,6 @@ static int xe_device_vram_alloc(struct xe_device *xe)
return 0;
}
-static void wedged_work(struct work_struct *work);
-static void xe_device_wedged_fini(struct drm_device *drm, void *arg);
-
-static int xe_device_wedged_init(struct xe_device *xe)
-{
- INIT_WORK(&xe->wedged.work, wedged_work);
-
- return drmm_add_action_or_reset(&xe->drm, xe_device_wedged_fini, xe);
-}
-
/**
* xe_device_probe_early: Device early probe
* @xe: xe device instance
@@ -759,16 +757,11 @@ int xe_device_probe_early(struct xe_device *xe)
*/
assert_lmem_ready(xe);
- xe->wedged.mode = xe_device_validate_wedged_mode(xe, xe_modparam.wedged_mode) ?
- XE_DEFAULT_WEDGED_MODE : xe_modparam.wedged_mode;
- drm_dbg(&xe->drm, "wedged_mode: setting mode (%u) %s\n",
- xe->wedged.mode, xe_wedged_mode_to_string(xe->wedged.mode));
-
err = xe_device_vram_alloc(xe);
if (err)
return err;
- err = xe_device_wedged_init(xe);
+ err = xe_wedge_init(xe);
if (err)
return err;
@@ -776,6 +769,25 @@ int xe_device_probe_early(struct xe_device *xe)
}
ALLOW_ERROR_INJECTION(xe_device_probe_early, ERRNO); /* See xe_pci_probe() */
+static void override_has_cached_pt(struct xe_device *xe)
+{
+ struct xe_gt *gt;
+ u8 id;
+
+ for_each_gt(gt, xe, id) {
+ /*
+ * Wa_16029380221: The affected GT will always use
+ * non-coherent access to page tables, so we must do
+ * uncached writes from the CPU.
+ */
+ if (XE_GT_WA(gt, 16029380221))
+ xe->info.has_cached_pt = false;
+
+ if (XE_GT_WA(gt, 14026539277))
+ xe->info.has_cached_pt = false;
+ }
+}
+
static int probe_has_flat_ccs(struct xe_device *xe)
{
struct xe_gt *gt;
@@ -858,14 +870,6 @@ static void detect_preproduction_hw(struct xe_device *xe)
}
}
-static void xe_device_wedged_fini(struct drm_device *drm, void *arg)
-{
- struct xe_device *xe = arg;
-
- if (atomic_read(&xe->wedged.flag))
- xe_pm_runtime_put(xe);
-}
-
#ifdef CONFIG_DRM_XE_DEBUG_PAGE_SIZE
static int xe_debug_page_size_alloc_ctrl_init(struct xe_device *xe)
{
@@ -926,14 +930,7 @@ int xe_device_probe(struct xe_device *xe)
return err;
}
- /*
- * Wa_16029380221: The affected GT will always use non-coherent
- * access to page tables, so we must do uncached writes from the
- * CPU.
- */
- for_each_gt(gt, xe, id)
- if (XE_GT_WA(gt, 16029380221))
- xe->info.has_cached_pt = false;
+ override_has_cached_pt(xe);
for_each_tile(tile, xe, id) {
err = xe_ggtt_init_early(tile->mem.ggtt);
@@ -983,7 +980,9 @@ int xe_device_probe(struct xe_device *xe)
if (err)
return err;
- xe_ras_init(xe);
+ err = xe_ras_init(xe);
+ if (err)
+ return err;
/*
* Now that GT is initialized (TTM in particular),
@@ -1104,13 +1103,6 @@ int xe_device_probe(struct xe_device *xe)
detect_preproduction_hw(xe);
- /*
- * Process and log any errors detected by hardware. Possible results can
- * include declaring the device as wedged, which must be done only after
- * xe_device_wedged_fini() is registered.
- */
- xe_ras_process_errors(xe);
-
err = devm_add_action_or_reset(xe->drm.dev, xe_device_sanitize, xe);
if (err)
goto err_unregister_display;
@@ -1346,153 +1338,6 @@ u64 xe_device_uncanonicalize_addr(struct xe_device *xe, u64 address)
return address & GENMASK_ULL(xe->info.va_bits - 1, 0);
}
-/**
- * DOC: Xe Device Wedging
- *
- * Xe driver uses drm device wedged uevent as documented in Documentation/gpu/drm-uapi.rst.
- * When device is in wedged state, every IOCTL will be blocked and GT cannot
- * be used. The conditions under which the driver declares the device wedged
- * depend on the wedged mode configuration (see &enum xe_wedged_mode). The
- * default recovery method for a wedged state is rebind/bus-reset.
- *
- * Another recovery method is vendor-specific. Below are the cases that send
- * ``WEDGED=vendor-specific`` recovery method in drm device wedged uevent.
- *
- * Case: Firmware Flash
- * --------------------
- *
- * Identification Hint
- * +++++++++++++++++++
- *
- * ``WEDGED=vendor-specific`` drm device wedged uevent with
- * :ref:`Runtime Survivability mode <xe-survivability-mode>` is used to notify
- * admin/userspace consumer about the need for a firmware flash.
- *
- * Recovery Procedure
- * ++++++++++++++++++
- *
- * Once ``WEDGED=vendor-specific`` drm device wedged uevent is received, follow
- * the below steps
- *
- * - Check Runtime Survivability mode sysfs.
- * If enabled, firmware flash is required to recover the device.
- *
- * /sys/bus/pci/devices/<device>/survivability_mode
- *
- * - Admin/userspace consumer can use firmware flashing tools like fwupd to flash
- * firmware and restore device to normal operation.
- */
-
-static void wedged_work(struct work_struct *work)
-{
- struct xe_device *xe = container_of(work, struct xe_device, wedged.work);
-
- /* Notify userspace of wedged device */
- drm_dev_wedged_event(&xe->drm, xe->wedged.method, NULL);
-}
-
-#define WEDGED_URL "https://docs.kernel.org/gpu/drm-uapi.html#device-wedging"
-#define XE_BUG_URL "https://gitlab.freedesktop.org/drm/xe/kernel/issues/new"
-
-/**
- * xe_device_declare_wedged - Declare device wedged
- * @xe: xe device instance
- * @method: method(s) to be used for recovery
- *
- * This is a final state that can only be cleared with the recovery method
- * specified in the drm wedged uevent.
- *
- * In this state every IOCTL will be blocked so the GT cannot be used.
- * In general it will be called upon any critical error such as gt reset
- * failure or guc loading failure. Userspace will be notified of this state
- * through device wedged uevent.
- * If xe.wedged module parameter is set to 2, this function will be called
- * on every single execution timeout (a.k.a. GPU hang) right after devcoredump
- * snapshot capture. In this mode, GT reset won't be attempted so the state of
- * the issue is preserved for further debugging.
- */
-void xe_device_declare_wedged(struct xe_device *xe, unsigned long method)
-{
- struct xe_gt *gt;
- u8 id;
-
- if (xe->wedged.mode == XE_WEDGED_MODE_NEVER) {
- drm_dbg(&xe->drm, "Wedged mode is forcibly disabled\n");
- return;
- }
-
- if (!atomic_xchg(&xe->wedged.flag, 1)) {
- xe->needs_flr_on_fini = true;
- xe_pm_runtime_get_noresume(xe);
-
- xe_log_err_fatal(xe, WEDGED, -EIO, "Device declared wedged!\n");
- xe_err_once(xe, "IOCTLs and executions are now blocked!\n"
- "For recovery procedure, refer to %s\n"
- "Please file a _new_ bug report at %s\n",
- WEDGED_URL, XE_BUG_URL);
-
- for_each_initialized_gt(gt, xe, id)
- xe_gt_declare_wedged(gt);
-
- /*
- * XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET is intended for debugging
- * hangs, so wedge the device with 'none' recovery method and have
- * it available to the user for debugging.
- */
- if (xe->wedged.mode == XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET)
- xe->wedged.method = DRM_WEDGE_RECOVERY_NONE;
- else
- xe->wedged.method = method;
-
- schedule_work(&xe->wedged.work);
- }
-}
-
-/**
- * xe_device_validate_wedged_mode - Check if given mode is supported
- * @xe: the &xe_device
- * @mode: requested mode to validate
- *
- * Check whether the provided wedged mode is supported.
- *
- * Return: 0 if mode is supported, error code otherwise.
- */
-int xe_device_validate_wedged_mode(struct xe_device *xe, unsigned int mode)
-{
- if (mode > XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET) {
- drm_dbg(&xe->drm, "wedged_mode: invalid value (%u)\n", mode);
- return -EINVAL;
- } else if (mode == XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET && (IS_SRIOV_VF(xe) ||
- (IS_SRIOV_PF(xe) && !IS_ENABLED(CONFIG_DRM_XE_DEBUG)))) {
- drm_dbg(&xe->drm, "wedged_mode: (%u) %s mode is not supported for %s\n",
- mode, xe_wedged_mode_to_string(mode),
- xe_sriov_mode_to_string(xe_device_sriov_mode(xe)));
- return -EPERM;
- }
-
- return 0;
-}
-
-/**
- * xe_wedged_mode_to_string - Convert enum value to string.
- * @mode: the &xe_wedged_mode to convert
- *
- * Returns: wedged mode as a user friendly string.
- */
-const char *xe_wedged_mode_to_string(enum xe_wedged_mode mode)
-{
- switch (mode) {
- case XE_WEDGED_MODE_NEVER:
- return "never";
- case XE_WEDGED_MODE_UPON_CRITICAL_ERROR:
- return "upon-critical-error";
- case XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET:
- return "upon-any-hang-no-reset";
- default:
- return "<invalid>";
- }
-}
-
/**
* xe_device_asid_to_vm() - Find VM from ASID
* @xe: the &xe_device
@@ -1517,3 +1362,25 @@ struct xe_vm *xe_device_asid_to_vm(struct xe_device *xe, u32 asid)
return vm;
}
+
+/**
+ * xe_device_exit() - Device subsystem exit function.
+ *
+ * Exit function to be called at module unload time.
+ */
+void xe_device_exit(void)
+{
+ /*
+ * Wait for all devices to be freed. 20s is well above the typical
+ * maximum dma_fence signalling time, so warn and keep waiting if
+ * we're still not done by then, since it may indicate a leaked
+ * xe_device reference is stalling module unload.
+ */
+ if (!wait_var_event_timeout(&xe_device_count,
+ !atomic_read(&xe_device_count),
+ HZ * 20)) {
+ pr_warn("%s: Waiting for %d xe device(s) to be freed before unloading.\n",
+ DRIVER_NAME, atomic_read(&xe_device_count));
+ wait_var_event(&xe_device_count, !atomic_read(&xe_device_count));
+ }
+}
diff --git a/drivers/gpu/drm/xe/xe_device.h b/drivers/gpu/drm/xe/xe_device.h
index 3ce1481d0e573..6df60edcb8f81 100644
--- a/drivers/gpu/drm/xe/xe_device.h
+++ b/drivers/gpu/drm/xe/xe_device.h
@@ -211,11 +211,6 @@ bool xe_device_is_l2_flush_optimized(struct xe_device *xe);
void xe_device_td_flush(struct xe_device *xe);
void xe_device_l2_flush(struct xe_device *xe, bool force);
-static inline bool xe_device_wedged(struct xe_device *xe)
-{
- return atomic_read(&xe->wedged.flag);
-}
-
#ifdef CONFIG_DRM_XE_DEBUG_PAGE_SIZE
static inline bool xe_debug_page_size_supported(struct xe_device *xe)
{
@@ -264,10 +259,6 @@ static inline bool xe_debug_page_size_mode_is_mixed(struct xe_device *xe)
}
#endif
-void xe_device_declare_wedged(struct xe_device *xe, unsigned long method);
-int xe_device_validate_wedged_mode(struct xe_device *xe, unsigned int mode);
-const char *xe_wedged_mode_to_string(enum xe_wedged_mode mode);
-
struct xe_file *xe_file_get(struct xe_file *xef);
void xe_file_put(struct xe_file *xef);
@@ -286,6 +277,8 @@ static inline bool xe_device_is_admin_only(const struct xe_device *xe)
}
#endif
+void xe_device_exit(void);
+
/*
* Occasionally it is seen that the G2H worker starts running after a delay of more than
* a second even after being queued and activated by the Linux workqueue subsystem. This
diff --git a/drivers/gpu/drm/xe/xe_device_types.h b/drivers/gpu/drm/xe/xe_device_types.h
index 5aa52975ebb46..d99881c6e1d53 100644
--- a/drivers/gpu/drm/xe/xe_device_types.h
+++ b/drivers/gpu/drm/xe/xe_device_types.h
@@ -12,6 +12,7 @@
#include <drm/drm_file.h>
#include <drm/ttm/ttm_device.h>
+#include "xe_cper_types.h"
#include "xe_devcoredump_types.h"
#include "xe_drm_ras_types.h"
#include "xe_heci_gsc.h"
@@ -21,6 +22,7 @@
#include "xe_platform_types.h"
#include "xe_pmu_types.h"
#include "xe_pt_types.h"
+#include "xe_ras_types.h"
#include "xe_sriov_pf_types.h"
#include "xe_sriov_types.h"
#include "xe_sriov_vf_types.h"
@@ -30,6 +32,7 @@
#include "xe_sysctrl_types.h"
#include "xe_tile_types.h"
#include "xe_validation.h"
+#include "xe_wedge_types.h"
#if IS_ENABLED(CONFIG_DRM_XE_DEBUG)
#define TEST_VM_OPS_ERROR
@@ -47,22 +50,6 @@ struct xe_pxp;
struct xe_ttm_stolen_mgr;
struct xe_vram_region;
-/**
- * enum xe_wedged_mode - possible wedged modes
- * @XE_WEDGED_MODE_NEVER: Device will never be declared wedged.
- * @XE_WEDGED_MODE_UPON_CRITICAL_ERROR: Device will be declared wedged only
- * when critical error occurs like GT reset failure or firmware failure.
- * This is the default mode.
- * @XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET: Device will be declared wedged on
- * any hang. In this mode, engine resets are disabled to avoid automatic
- * recovery attempts. This mode is primarily intended for debugging hangs.
- */
-enum xe_wedged_mode {
- XE_WEDGED_MODE_NEVER = 0,
- XE_WEDGED_MODE_UPON_CRITICAL_ERROR = 1,
- XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET = 2,
-};
-
#ifdef CONFIG_DRM_XE_DEBUG_PAGE_SIZE
/**
* enum xe_page_size_alloc_ctrl_mode - User BO page-size allocation control modes
@@ -164,6 +151,8 @@ struct xe_device {
u8 vm_max_level;
/** @info.va_bits: Maximum bits of a virtual address */
u8 va_bits;
+ /** @info.late_bind_mask: Indicates supported late binding firmwares */
+ u8 late_bind_mask;
/*
* Keep all flags below alphabetically sorted
@@ -185,6 +174,8 @@ struct xe_device {
u8 has_drm_ras:1;
/** @info.has_fan_control: Device supports fan control */
u8 has_fan_control:1;
+ /** @info.has_fixed_vram_channels: Device has fixed VRAM temperature channels */
+ u8 has_fixed_vram_channels:1;
/** @info.has_flat_ccs: Whether flat CCS metadata is used */
u8 has_flat_ccs:1;
/** @info.has_gsc_nvm: Device has gsc non-volatile memory */
@@ -195,8 +186,6 @@ struct xe_device {
u8 has_heci_gscfi:1;
/** @info.has_i2c: Device has I2C controller */
u8 has_i2c:1;
- /** @info.has_late_bind: Device has firmware late binding support */
- u8 has_late_bind:1;
/** @info.has_llc: Device has a shared CPU+GPU last level cache */
u8 has_llc:1;
/** @info.has_mbx_power_limits: Device has support to manage power limits using
@@ -553,18 +542,7 @@ struct xe_device {
atomic_t in_reset;
/** @wedged: Struct to control Wedged States and mode */
- struct {
- /** @wedged.flag: Xe device faced a critical error and is now blocked. */
- atomic_t flag;
- /** @wedged.mode: Mode controlled by kernel parameter and debugfs */
- enum xe_wedged_mode mode;
- /** @wedged.method: Recovery method to be sent in the drm device wedged uevent */
- unsigned long method;
- /** @wedged.inconsistent_reset: Inconsistent reset policy state between GTs */
- bool inconsistent_reset;
- /** @wedged.work: Worker to defer non-atomic calls */
- struct work_struct work;
- } wedged;
+ struct xe_wedge wedged;
/** @devres_group: devres group */
void *devres_group;
@@ -580,8 +558,17 @@ struct xe_device {
/** @pmu: performance monitoring unit */
struct xe_pmu pmu;
- /** @ras: RAS structure for device */
- struct xe_drm_ras ras;
+ /** @ras: RAS (Reliability, Availability, Serviceability) structures */
+ struct {
+ /** @ras.nl_data: drm-ras netlink data */
+ struct xe_drm_ras nl_data;
+
+ /** @ras.state: RAS device and firmware state */
+ struct xe_ras_state state;
+ } ras;
+
+ /** @cper: CPER state */
+ struct xe_cper cper;
/** @i2c: I2C host controller */
struct xe_i2c *i2c;
diff --git a/drivers/gpu/drm/xe/xe_drm_ras.c b/drivers/gpu/drm/xe/xe_drm_ras.c
index 30cf40ee5fe4d..7554803aae11a 100644
--- a/drivers/gpu/drm/xe/xe_drm_ras.c
+++ b/drivers/gpu/drm/xe/xe_drm_ras.c
@@ -31,7 +31,7 @@ static int query_error_counter(struct xe_device *xe,
enum drm_xe_ras_error_severity severity,
u32 error_id, const char **name, u32 *val)
{
- struct xe_drm_ras *ras = &xe->ras;
+ struct xe_drm_ras *ras = &xe->ras.nl_data;
struct xe_drm_ras_counter *info = ras->info[severity];
if (!info || !info[error_id].name)
@@ -52,7 +52,7 @@ static int clear_error_counter(struct xe_device *xe,
enum drm_xe_ras_error_severity severity,
u32 error_id)
{
- struct xe_drm_ras *ras = &xe->ras;
+ struct xe_drm_ras *ras = &xe->ras.nl_data;
struct xe_drm_ras_counter *info = ras->info[severity];
if (!info || !info[error_id].name)
@@ -101,7 +101,7 @@ static int query_correctable_error_threshold(struct drm_ras_node *ep, u32 error_
const char **name, u32 *threshold)
{
struct xe_device *xe = ep->priv;
- struct xe_drm_ras *ras = &xe->ras;
+ struct xe_drm_ras *ras = &xe->ras.nl_data;
struct xe_drm_ras_counter *info = ras->info[DRM_XE_RAS_ERR_SEV_CORRECTABLE];
if (!info || !info[error_id].name)
@@ -117,7 +117,7 @@ static int query_correctable_error_threshold(struct drm_ras_node *ep, u32 error_
static int set_correctable_error_threshold(struct drm_ras_node *ep, u32 error_id, u32 threshold)
{
struct xe_device *xe = ep->priv;
- struct xe_drm_ras *ras = &xe->ras;
+ struct xe_drm_ras *ras = &xe->ras.nl_data;
struct xe_drm_ras_counter *info = ras->info[DRM_XE_RAS_ERR_SEV_CORRECTABLE];
if (!info || !info[error_id].name)
@@ -153,7 +153,7 @@ static int assign_node_params(struct xe_device *xe, struct drm_ras_node *node,
const enum drm_xe_ras_error_severity severity)
{
struct pci_dev *pdev = to_pci_dev(xe->drm.dev);
- struct xe_drm_ras *ras = &xe->ras;
+ struct xe_drm_ras *ras = &xe->ras.nl_data;
const char *device_name;
device_name = kasprintf(GFP_KERNEL, "%04x:%02x:%02x.%d",
@@ -201,7 +201,7 @@ static void cleanup_node(struct drm_device *drm, void *node)
static int register_nodes(struct xe_device *xe)
{
- struct xe_drm_ras *ras = &xe->ras;
+ struct xe_drm_ras *ras = &xe->ras.nl_data;
struct drm_ras_node *node;
int i, ret;
@@ -241,7 +241,7 @@ static int register_nodes(struct xe_device *xe)
*/
void xe_drm_ras_event(struct xe_device *xe, u32 component, u32 severity, u32 value)
{
- struct xe_drm_ras *ras = &xe->ras;
+ struct xe_drm_ras *ras = &xe->ras.nl_data;
struct xe_drm_ras_counter *info;
struct drm_ras_node *node;
int ret;
@@ -275,7 +275,7 @@ void xe_drm_ras_event(struct xe_device *xe, u32 component, u32 severity, u32 val
*/
int xe_drm_ras_init(struct xe_device *xe)
{
- struct xe_drm_ras *ras = &xe->ras;
+ struct xe_drm_ras *ras = &xe->ras.nl_data;
struct drm_ras_node *node;
int err;
diff --git a/drivers/gpu/drm/xe/xe_gsc.c b/drivers/gpu/drm/xe/xe_gsc.c
index 2bbe0f20ff8d3..db669a953886a 100644
--- a/drivers/gpu/drm/xe/xe_gsc.c
+++ b/drivers/gpu/drm/xe/xe_gsc.c
@@ -31,6 +31,7 @@
#include "xe_sched_job.h"
#include "xe_uc_fw.h"
#include "xe_wa.h"
+#include "xe_wedge.h"
#include "instructions/xe_gsc_commands.h"
#include "regs/xe_gsc_regs.h"
#include "regs/xe_gt_regs.h"
@@ -340,8 +341,8 @@ static int gsc_er_complete(struct xe_gt *gt)
* declare the device as wedged.
*/
xe_gt_err(gt, "GSC ER timed out!\n");
- xe_device_declare_wedged(gt_to_xe(gt),
- DRM_WEDGE_RECOVERY_REBIND | DRM_WEDGE_RECOVERY_BUS_RESET);
+ xe_wedge_declare(gt_to_xe(gt),
+ DRM_WEDGE_RECOVERY_REBIND | DRM_WEDGE_RECOVERY_BUS_RESET);
return -EIO;
}
diff --git a/drivers/gpu/drm/xe/xe_gt.c b/drivers/gpu/drm/xe/xe_gt.c
index 286aee5775c0d..b751b5044f3ca 100644
--- a/drivers/gpu/drm/xe/xe_gt.c
+++ b/drivers/gpu/drm/xe/xe_gt.c
@@ -12,7 +12,6 @@
#include <drm/drm_managed.h>
#include <uapi/drm/xe_drm.h>
-#include <generated/xe_device_wa_oob.h>
#include <generated/xe_wa_oob.h>
#include "instructions/xe_alu_commands.h"
@@ -68,6 +67,7 @@
#include "xe_uc_fw.h"
#include "xe_vm.h"
#include "xe_wa.h"
+#include "xe_wedge.h"
#include "xe_wopcm.h"
#define GRDOM_RESET_TIMEOUT_MS 5
@@ -459,35 +459,6 @@ int xe_gt_record_default_lrcs(struct xe_gt *gt)
return err;
}
-static void wa_14026539277(struct xe_gt *gt)
-{
- struct xe_device *xe = gt_to_xe(gt);
- u32 val;
-
- /*
- * FIXME: We currently can't use FUNC(xe_rtp_match_not_sriov_vf) in the
- * rules for Wa_14026539277 due to xe_wa_process_device_oob() being
- * called before xe_sriov_probe_early(); and we can't move the call to
- * the former to happen after the latter because MMIO read functions
- * already depend on a device OOB workaround. This needs to be fixed by
- * allowing workaround checks to happen at different stages of driver
- * initialization.
- */
- if (IS_SRIOV_VF(xe))
- return;
-
- if (!XE_DEVICE_WA(xe, 14026539277))
- return;
-
- if (!xe_gt_is_main_type(gt))
- return;
-
- val = xe_gt_mcr_unicast_read_any(gt, L2COMPUTESIDECTRL);
- val &= ~CECTRL;
- val |= CECTRL_CENODATA_ALWAYS;
- xe_gt_mcr_multicast_write(gt, L2COMPUTESIDECTRL, val);
-}
-
int xe_gt_init_early(struct xe_gt *gt)
{
int err;
@@ -621,15 +592,6 @@ static int gt_init_with_gt_forcewake(struct xe_gt *gt)
else
gt->info.has_wmtp_disabled = 0;
- /*
- * Wa_14026539277 can't be implemented as a regular GT workaround (i.e.
- * as an entry in gt_was[]) for two reasons: it is actually a device
- * workaround that happens to involve programming a GT register; and it
- * needs to be applied early to avoid getting the hardware in a bad
- * state before we have a chance to do the necessary programming.
- */
- wa_14026539277(gt);
-
return 0;
}
@@ -948,7 +910,7 @@ static void gt_reset_worker(struct work_struct *w)
unsigned int fw_ref;
int err;
- if (xe_device_wedged(gt_to_xe(gt)))
+ if (xe_wedged(gt_to_xe(gt)))
goto err_pm_put;
if (xe_device_is_in_reset(gt_to_xe(gt)))
@@ -1007,8 +969,7 @@ static void gt_reset_worker(struct work_struct *w)
err_fail:
xe_log_err_fatal(gt, GT, err, "reset failed\n");
- xe_device_declare_wedged(gt_to_xe(gt),
- DRM_WEDGE_RECOVERY_REBIND | DRM_WEDGE_RECOVERY_BUS_RESET);
+ xe_wedge_declare(gt_to_xe(gt), DRM_WEDGE_RECOVERY_REBIND | DRM_WEDGE_RECOVERY_BUS_RESET);
err_pm_put:
xe_pm_runtime_put(gt_to_xe(gt));
}
@@ -1091,6 +1052,19 @@ int xe_gt_sanitize_freq(struct xe_gt *gt)
return ret;
}
+/**
+ * xe_gt_resume_early() - Early init for GT resume
+ * @gt: the GT object
+ *
+ * Handles GT related init before GT restart after a suspend cycle.
+ */
+void xe_gt_resume_early(struct xe_gt *gt)
+{
+ xe_gt_idle_disable_c6(gt);
+
+ xe_uc_resume_early(>->uc);
+}
+
int xe_gt_resume(struct xe_gt *gt)
{
int err;
@@ -1215,19 +1189,19 @@ static void gt_wedge_worker(struct work_struct *w)
{
struct xe_gt *gt = container_of(w, typeof(*gt), wedge.worker);
- xe_uc_declare_wedged(>->uc);
+ xe_uc_wedge_declare(>->uc);
xe_tlb_inval_reset(>->tlb_inval);
xe_pm_runtime_put(gt_to_xe(gt));
}
/**
- * xe_gt_declare_wedged() - Declare GT wedged
+ * xe_gt_wedge_declare() - Declare GT wedged
* @gt: the GT object
*
* Wedge the GT which stops all submission, saves desired debug state, and
* cleans up anything which could timeout.
*/
-void xe_gt_declare_wedged(struct xe_gt *gt)
+void xe_gt_wedge_declare(struct xe_gt *gt)
{
struct xe_device *xe = gt_to_xe(gt);
diff --git a/drivers/gpu/drm/xe/xe_gt_sriov_vf.c b/drivers/gpu/drm/xe/xe_gt_sriov_vf.c
index 28ec83bb8100d..2a5f2064ad3e8 100644
--- a/drivers/gpu/drm/xe/xe_gt_sriov_vf.c
+++ b/drivers/gpu/drm/xe/xe_gt_sriov_vf.c
@@ -37,6 +37,7 @@
#include "xe_tile_sriov_vf.h"
#include "xe_tlb_inval.h"
#include "xe_uc_fw.h"
+#include "xe_wedge.h"
#include "xe_wopcm.h"
#define make_u64_from_u32(hi, lo) ((u64)((u64)(u32)(hi) << 32 | (u32)(lo)))
@@ -1462,7 +1463,7 @@ static void vf_post_migration_recovery(struct xe_gt *gt)
fail:
vf_post_migration_abort(gt);
xe_gt_sriov_err(gt, "migration recovery failed (%pe)\n", ERR_PTR(err));
- xe_device_declare_wedged(xe, DRM_WEDGE_RECOVERY_REBIND | DRM_WEDGE_RECOVERY_BUS_RESET);
+ xe_wedge_declare(xe, DRM_WEDGE_RECOVERY_REBIND | DRM_WEDGE_RECOVERY_BUS_RESET);
return;
queue:
diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c
index 01f79f8883ba6..b14b37b767f5e 100644
--- a/drivers/gpu/drm/xe/xe_guc_submit.c
+++ b/drivers/gpu/drm/xe/xe_guc_submit.c
@@ -50,6 +50,7 @@
#include "xe_trace.h"
#include "xe_uc_fw.h"
#include "xe_vm.h"
+#include "xe_wedge.h"
#define XE_GUC_EXEC_QUEUE_CGP_CONTEXT_ERROR_LEN 6
@@ -286,20 +287,26 @@ static void primelockdep(struct xe_guc *guc)
fs_reclaim_release(GFP_KERNEL);
}
+static int guc_id_mgr_init(struct xe_guc *guc)
+{
+ if (IS_SRIOV_PF(guc_to_xe(guc)))
+ return xe_guc_id_mgr_init_shared(&guc->submission_state.idm);
+ else if (IS_SRIOV_VF(guc_to_xe(guc)))
+ return xe_guc_id_mgr_init_small(&guc->submission_state.idm,
+ xe_gt_sriov_vf_guc_ids(guc_to_gt(guc)));
+ else
+ return xe_guc_id_mgr_init(&guc->submission_state.idm);
+}
+
/**
* xe_guc_submit_init() - Initialize GuC submission.
* @guc: the &xe_guc to initialize
- * @num_ids: number of GuC context IDs to use
*
- * The bare-metal or PF driver can pass ~0 as &num_ids to indicate that all
- * GuC context IDs supported by the GuC firmware should be used for submission.
- *
- * Only VF drivers will have to provide explicit number of GuC context IDs
- * that they can use for submission.
+ * This function initializes the GuC submission state.
*
* Return: 0 on success or a negative error code on failure.
*/
-int xe_guc_submit_init(struct xe_guc *guc, unsigned int num_ids)
+int xe_guc_submit_init(struct xe_guc *guc)
{
struct xe_device *xe = guc_to_xe(guc);
struct xe_gt *gt = guc_to_gt(guc);
@@ -309,7 +316,7 @@ int xe_guc_submit_init(struct xe_guc *guc, unsigned int num_ids)
if (err)
return err;
- err = xe_guc_id_mgr_init(&guc->submission_state.idm, num_ids);
+ err = guc_id_mgr_init(guc);
if (err)
return err;
@@ -415,8 +422,7 @@ static int alloc_guc_id(struct xe_guc *guc, struct xe_exec_queue *q)
int ret, i;
mutex_lock(&guc->submission_state.lock);
- ret = xe_guc_id_mgr_reserve_locked(&guc->submission_state.idm,
- q->width);
+ ret = xe_guc_id_mgr_reserve_usable_locked(&guc->submission_state.idm, q->width);
mutex_unlock(&guc->submission_state.lock);
if (ret < 0)
return ret;
@@ -1450,10 +1456,10 @@ static bool guc_submit_hint_wedged(struct xe_guc *guc)
if (xe->wedged.mode != XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET)
return false;
- if (xe_device_wedged(xe))
+ if (xe_wedged(xe))
return true;
- xe_device_declare_wedged(xe, DRM_WEDGE_RECOVERY_REBIND | DRM_WEDGE_RECOVERY_BUS_RESET);
+ xe_wedge_declare(xe, DRM_WEDGE_RECOVERY_REBIND | DRM_WEDGE_RECOVERY_BUS_RESET);
return true;
}
@@ -1666,7 +1672,7 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job)
if (!exec_queue_killed(q))
wedged = guc_submit_hint_wedged(exec_queue_to_guc(q));
else
- wedged = xe_device_wedged(xe);
+ wedged = xe_wedged(xe);
/*
* Only tag as GPU hang if this is the original timeout, not a
@@ -1786,8 +1792,8 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job)
}
if (wedge_device)
- xe_device_declare_wedged(gt_to_xe(q->gt),
- DRM_WEDGE_RECOVERY_REBIND | DRM_WEDGE_RECOVERY_BUS_RESET);
+ xe_wedge_declare(gt_to_xe(q->gt),
+ DRM_WEDGE_RECOVERY_REBIND | DRM_WEDGE_RECOVERY_BUS_RESET);
/*
* We want the job added back to the pending list so it gets freed; this
@@ -2552,7 +2558,7 @@ static int guc_exec_queue_wait_suspend_done(struct xe_exec_queue *q, bool blocki
WAIT_COND, HZ * 5);
}
- if (!blocking && vf_recovery(guc) && !xe_device_wedged(xe))
+ if (!blocking && vf_recovery(guc) && !xe_wedged(xe))
return -EAGAIN;
if (!ret)
@@ -2812,7 +2818,7 @@ int xe_guc_submit_reset_prepare(struct xe_guc *guc)
void xe_guc_submit_reset_wait(struct xe_guc *guc)
{
- wait_event(guc->ct.wq, xe_device_wedged(guc_to_xe(guc)) ||
+ wait_event(guc->ct.wq, xe_wedged(guc_to_xe(guc)) ||
!xe_guc_read_stopped(guc));
}
@@ -3307,7 +3313,7 @@ g2h_exec_queue_lookup(struct xe_guc *guc, u32 guc_id)
struct xe_gt *gt = guc_to_gt(guc);
struct xe_exec_queue *q;
- if (unlikely(guc_id >= GUC_ID_MAX)) {
+ if (unlikely(guc_id >= xe_guc_id_mgr_max_usable(&guc->submission_state.idm))) {
xe_gt_err(gt, "Invalid guc_id %u\n", guc_id);
return NULL;
}
diff --git a/drivers/gpu/drm/xe/xe_hwmon.c b/drivers/gpu/drm/xe/xe_hwmon.c
index 20c1cfffc5a03..defe8aaca7c11 100644
--- a/drivers/gpu/drm/xe/xe_hwmon.c
+++ b/drivers/gpu/drm/xe/xe_hwmon.c
@@ -6,6 +6,7 @@
#include <linux/hwmon-sysfs.h>
#include <linux/hwmon.h>
#include <linux/jiffies.h>
+#include <linux/overflow.h>
#include <linux/types.h>
#include <linux/units.h>
@@ -15,6 +16,7 @@
#include "regs/xe_pcode_regs.h"
#include "xe_device.h"
#include "xe_hwmon.h"
+#include "xe_log.h"
#include "xe_mmio.h"
#include "xe_pcode.h"
#include "xe_pcode_api.h"
@@ -39,7 +41,11 @@ enum xe_hwmon_reg_operation {
REG_READ64,
};
-#define MAX_VRAM_CHANNELS (16)
+#define MAX_VRAM_CHANNELS 80
+#define FIXED_VRAM_CHANNELS 16
+
+/* Each MSU enable bit maps to one VRAM subsystem of 4 channels. */
+#define VRAM_CHANNELS_PER_MSU 4
enum xe_hwmon_channel {
CHANNEL_CARD,
@@ -48,6 +54,7 @@ enum xe_hwmon_channel {
CHANNEL_MCTRL,
CHANNEL_PCIE,
CHANNEL_VRAM_N,
+ /* Compile-time upper bound; actual channel count is hwmon->temp.vram_count */
CHANNEL_VRAM_N_MAX = CHANNEL_VRAM_N + MAX_VRAM_CHANNELS - 1,
CHANNEL_MAX,
};
@@ -101,8 +108,15 @@ enum sensor_attr_power {
#define PWR_ATTR_TO_STR(attr) (((attr) == hwmon_power_max) ? "PL1" : "PL2")
-/* Index of memory controller in READ_THERMAL_DATA output */
+/* Sensor byte indices in READ_THERMAL_DATA output for BMG. */
#define TEMP_INDEX_MCTRL 2
+#define TEMP_INDEX_PCIE 10
+#define BMG_G21_TEMP_INDEX_PCIE 8
+
+/* Sensor byte indices in READ_THERMAL_DATA output for CRI. */
+#define CRI_TEMP_INDEX_MCTRL 16
+#define CRI_TEMP_MCTRL_COUNT 4
+#define CRI_TEMP_INDEX_PCIE 20
/* Mailbox sentinel reported for an unavailable temperature sensor */
#define TEMP_MBX_INVALID 0xFF
@@ -144,16 +158,22 @@ struct xe_hwmon_thermal_info {
/** @data: temperature limits as little-endian dwords */
__le32 data[DIV_ROUND_UP(TEMP_LIMIT_MAX, sizeof(u32))];
};
- /** @count: no of temperature sensors available for the platform */
+ /** @count: temperature sensors count from READ_THERMAL_CONFIG */
u8 count;
+ /** @vram_count: exclusive upper bound for VRAM temperature channel indices */
+ u8 vram_count;
+ /** @available: temperature sensor availability cached at registration */
+ bool available[CHANNEL_MAX];
+ /** @msu_mask: VRAM subsystem enable mask read from MMIO */
+ u32 msu_mask;
union {
/** @value: per-sensor raw mailbox temperature; bit7=sign, bits6:0=magnitude */
u8 value[U8_MAX + 1];
/** @dword: sensor values as little-endian dwords, u32-aligned for pcode reads */
__le32 dword[DIV_ROUND_UP(U8_MAX + 1, sizeof(u32))];
};
- /** @vram_label: vram label names */
- char vram_label[MAX_VRAM_CHANNELS][MAX_LABEL_SIZE];
+ /** @vram_label: vram label names, dynamically allocated based on vram_count */
+ char (*vram_label)[MAX_LABEL_SIZE];
};
/**
@@ -296,6 +316,14 @@ static int xe_hwmon_pcode_set_default_power_limits(struct xe_hwmon *hwmon)
return ret;
}
+static bool xe_hwmon_vram_channel_enabled(const struct xe_hwmon *hwmon, int index)
+{
+ if (hwmon->xe->info.platform == XE_CRESCENTISLAND)
+ return hwmon->temp.msu_mask & BIT(index / VRAM_CHANNELS_PER_MSU);
+
+ return true;
+}
+
static struct xe_reg xe_hwmon_get_reg(struct xe_hwmon *hwmon, enum xe_hwmon_reg hwmon_reg,
int channel)
{
@@ -303,12 +331,21 @@ static struct xe_reg xe_hwmon_get_reg(struct xe_hwmon *hwmon, enum xe_hwmon_reg
switch (hwmon_reg) {
case REG_TEMP:
- if (xe->info.platform == XE_BATTLEMAGE || xe->info.platform == XE_CRESCENTISLAND) {
+ if (xe->info.platform == XE_CRESCENTISLAND) {
+ if (channel == CHANNEL_PKG)
+ return CRI_PACKAGE_TEMPERATURE;
+ else if (channel == CHANNEL_VRAM)
+ return CRI_VRAM_TEMPERATURE;
+ else if (in_range(channel, CHANNEL_VRAM_N, hwmon->temp.vram_count) &&
+ xe_hwmon_vram_channel_enabled(hwmon, channel - CHANNEL_VRAM_N))
+ return CRI_VRAM_TEMPERATURE_N(channel - CHANNEL_VRAM_N);
+ } else if (xe->info.platform == XE_BATTLEMAGE) {
if (channel == CHANNEL_PKG)
return BMG_PACKAGE_TEMPERATURE;
else if (channel == CHANNEL_VRAM)
return BMG_VRAM_TEMPERATURE;
- else if (in_range(channel, CHANNEL_VRAM_N, MAX_VRAM_CHANNELS))
+ else if (in_range(channel, CHANNEL_VRAM_N, hwmon->temp.vram_count) &&
+ xe_hwmon_vram_channel_enabled(hwmon, channel - CHANNEL_VRAM_N))
return BMG_VRAM_TEMPERATURE_N(channel - CHANNEL_VRAM_N);
} else if (xe->info.platform == XE_DG2) {
if (channel == CHANNEL_PKG)
@@ -375,17 +412,20 @@ static struct xe_reg xe_hwmon_get_reg(struct xe_hwmon *hwmon, enum xe_hwmon_reg
* same pattern for sysfs, allow arbitrary PL1 limits to be set but display
* clamped values when read.
*/
-static void xe_hwmon_power_max_read(struct xe_hwmon *hwmon, u32 attr, int channel, long *value)
+static int xe_hwmon_power_max_read(struct xe_hwmon *hwmon, u32 attr, int channel, long *value)
{
u32 reg_val = 0;
struct xe_device *xe = hwmon->xe;
struct xe_reg rapl_limit, pkg_power_sku;
struct xe_mmio *mmio = xe_root_tile_mmio(xe);
+ int ret = 0;
mutex_lock(&hwmon->hwmon_lock);
if (hwmon->xe->info.has_mbx_power_limits) {
- xe_hwmon_pcode_read_power_limit(hwmon, attr, channel, ®_val);
+ ret = xe_hwmon_pcode_read_power_limit(hwmon, attr, channel, ®_val);
+ if (ret)
+ goto unlock;
} else {
rapl_limit = xe_hwmon_get_reg(hwmon, REG_PKG_RAPL_LIMIT, channel);
pkg_power_sku = xe_hwmon_get_reg(hwmon, REG_PKG_POWER_SKU, channel);
@@ -417,6 +457,7 @@ static void xe_hwmon_power_max_read(struct xe_hwmon *hwmon, u32 attr, int channe
}
unlock:
mutex_unlock(&hwmon->hwmon_lock);
+ return ret;
}
static int xe_hwmon_power_max_write(struct xe_hwmon *hwmon, u32 attr, int channel, long value)
@@ -439,7 +480,9 @@ static int xe_hwmon_power_max_write(struct xe_hwmon *hwmon, u32 attr, int channe
ret = xe_hwmon_pcode_rmw_power_limit(hwmon, attr, channel, PWR_LIM_EN, 0);
if (ret)
goto unlock;
- xe_hwmon_pcode_read_power_limit(hwmon, attr, channel, ®_val);
+ ret = xe_hwmon_pcode_read_power_limit(hwmon, attr, channel, ®_val);
+ if (ret)
+ goto unlock;
} else {
reg_val = xe_mmio_rmw32(mmio, rapl_limit, PWR_LIM_EN, 0);
reg_val = xe_mmio_read32(mmio, rapl_limit);
@@ -496,15 +539,18 @@ static int xe_hwmon_power_max_write(struct xe_hwmon *hwmon, u32 attr, int channe
return ret;
}
-static void xe_hwmon_power_rated_max_read(struct xe_hwmon *hwmon, u32 attr, int channel,
- long *value)
+static int xe_hwmon_power_rated_max_read(struct xe_hwmon *hwmon, u32 attr, int channel,
+ long *value)
{
struct xe_mmio *mmio = xe_root_tile_mmio(hwmon->xe);
u32 reg_val;
+ int ret = 0;
if (hwmon->xe->info.has_mbx_power_limits) {
/* PL1 is rated max if supported. */
- xe_hwmon_pcode_read_power_limit(hwmon, PL1_HWMON_ATTR, channel, ®_val);
+ ret = xe_hwmon_pcode_read_power_limit(hwmon, PL1_HWMON_ATTR, channel, ®_val);
+ if (ret)
+ return ret;
} else {
/*
* This sysfs file won't be visible if REG_PKG_POWER_SKU is invalid, so valid check
@@ -518,6 +564,7 @@ static void xe_hwmon_power_rated_max_read(struct xe_hwmon *hwmon, u32 attr, int
reg_val = REG_FIELD_GET(PKG_TDP, reg_val);
*value = mul_u64_u32_shr(reg_val, SF_POWER, hwmon->scl_shift_power);
+ return ret;
}
/*
@@ -540,7 +587,7 @@ static void xe_hwmon_power_rated_max_read(struct xe_hwmon *hwmon, u32 attr, int
* hwmon->scl_shift_energy of 14 bits we have 57 (63 - 20 + 14) bits before
* energyN_input overflows. This at 1000 W is an overflow duration of 278 years.
*/
-static void
+static int
xe_hwmon_energy_get(struct xe_hwmon *hwmon, int channel, long *energy)
{
struct xe_mmio *mmio = xe_root_tile_mmio(hwmon->xe);
@@ -551,7 +598,7 @@ xe_hwmon_energy_get(struct xe_hwmon *hwmon, int channel, long *energy)
/* Energy is supported only for card and pkg */
if (channel > CHANNEL_PKG) {
*energy = 0;
- return;
+ return 0;
}
if (hwmon->xe->info.platform == XE_BATTLEMAGE) {
@@ -563,7 +610,7 @@ xe_hwmon_energy_get(struct xe_hwmon *hwmon, int channel, long *energy)
if (ret != sizeof(pmt_val)) {
drm_warn(&hwmon->xe->drm, "energy read from pmt failed, ret %d\n", ret);
*energy = 0;
- return;
+ return ret < 0 ? ret : -EIO;
}
if (channel == CHANNEL_PKG)
@@ -580,6 +627,7 @@ xe_hwmon_energy_get(struct xe_hwmon *hwmon, int channel, long *energy)
*energy = mul_u64_u32_shr(ei->accum_energy, SF_ENERGY,
hwmon->scl_shift_energy);
+ return 0;
}
static ssize_t
@@ -602,10 +650,11 @@ xe_hwmon_power_max_interval_show(struct device *dev, struct device_attribute *at
if (hwmon->xe->info.has_mbx_power_limits) {
ret = xe_hwmon_pcode_read_power_limit(hwmon, power_attr, channel, ®_val);
if (ret) {
- drm_err(&hwmon->xe->drm,
- "power interval read fail, ch %d, attr %d, val 0x%08x, ret %d\n",
- channel, power_attr, reg_val, ret);
- reg_val = 0;
+ xe_log_err(hwmon->xe, HWMON, ret,
+ "Power interval read fail, ch %d, attr %d, val 0x%08x\n",
+ channel, power_attr, reg_val);
+ mutex_unlock(&hwmon->hwmon_lock);
+ return ret;
}
} else {
reg_val = xe_mmio_read32(mmio, xe_hwmon_get_reg(hwmon, REG_PKG_RAPL_LIMIT,
@@ -797,30 +846,24 @@ static const struct attribute_group *hwmon_groups[] = {
NULL
};
+static const u32 hwmon_temp_config[CHANNEL_MAX + 1] = {
+ [CHANNEL_CARD] = HWMON_T_LABEL,
+ [CHANNEL_PKG] = HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT |
+ HWMON_T_LABEL | HWMON_T_MAX,
+ [CHANNEL_VRAM ... CHANNEL_PCIE] = HWMON_T_CRIT | HWMON_T_EMERGENCY |
+ HWMON_T_INPUT | HWMON_T_LABEL,
+ [CHANNEL_VRAM_N ... CHANNEL_VRAM_N_MAX] = HWMON_T_CRIT | HWMON_T_EMERGENCY |
+ HWMON_T_INPUT | HWMON_T_LABEL,
+ [CHANNEL_MAX] = 0, /* hwmon config terminator */
+};
+
+static const struct hwmon_channel_info hwmon_temp_info = {
+ .type = hwmon_temp,
+ .config = hwmon_temp_config,
+};
+
static const struct hwmon_channel_info * const hwmon_info[] = {
- HWMON_CHANNEL_INFO(temp,
- HWMON_T_LABEL,
- HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL |
- HWMON_T_MAX,
- HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL,
- HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL,
- HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL,
- HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL,
- HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL,
- HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL,
- HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL,
- HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL,
- HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL,
- HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL,
- HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL,
- HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL,
- HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL,
- HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL,
- HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL,
- HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL,
- HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL,
- HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL,
- HWMON_T_CRIT | HWMON_T_EMERGENCY | HWMON_T_INPUT | HWMON_T_LABEL),
+ &hwmon_temp_info,
HWMON_CHANNEL_INFO(power, HWMON_P_MAX | HWMON_P_RATED_MAX | HWMON_P_LABEL | HWMON_P_CRIT |
HWMON_P_CAP,
HWMON_P_MAX | HWMON_P_RATED_MAX | HWMON_P_LABEL | HWMON_P_CAP),
@@ -834,9 +877,31 @@ static const struct hwmon_channel_info * const hwmon_info[] = {
static int xe_hwmon_pcode_read_thermal_info(struct xe_hwmon *hwmon)
{
struct xe_tile *root_tile = xe_device_get_root_tile(hwmon->xe);
+ struct xe_mmio *mmio = xe_root_tile_mmio(hwmon->xe);
u32 config = 0, data0 = 0, data1 = 0;
int ret;
+ /*
+ * Only CRI reports dynamic VRAM channel state. Fixed-channel platforms
+ * set the count up front so those sensors stay visible even if the thermal
+ * mailbox reads below fail.
+ */
+ if (hwmon->xe->info.has_fixed_vram_channels)
+ hwmon->temp.vram_count = FIXED_VRAM_CHANNELS;
+
+ if (hwmon->xe->info.platform == XE_CRESCENTISLAND) {
+ hwmon->temp.msu_mask = xe_mmio_read32(mmio, CRI_MSU_VRAM_ENABLE);
+ hwmon->temp.vram_count = fls(hwmon->temp.msu_mask) * VRAM_CHANNELS_PER_MSU;
+ drm_dbg(&hwmon->xe->drm, "MSU VRAM enable mask 0x%x, VRAM channel bound %d\n",
+ hwmon->temp.msu_mask, hwmon->temp.vram_count);
+ if (hwmon->temp.vram_count > MAX_VRAM_CHANNELS) {
+ drm_warn(&hwmon->xe->drm,
+ "VRAM channel bound %d exceeds max %d, clamping\n",
+ hwmon->temp.vram_count, MAX_VRAM_CHANNELS);
+ hwmon->temp.vram_count = MAX_VRAM_CHANNELS;
+ }
+ }
+
ret = xe_pcode_read(root_tile, PCODE_MBOX(PCODE_THERMAL_INFO, READ_THERMAL_LIMITS, 0),
&data0, &data1);
if (ret)
@@ -889,17 +954,43 @@ static int mbx_temp_decode(u8 value)
return (value & TEMP_MBX_SIGN) ? -temp : temp;
}
+static u8 xe_hwmon_pcie_temp_index(const struct xe_hwmon *hwmon)
+{
+ if (hwmon->xe->info.platform == XE_CRESCENTISLAND)
+ return CRI_TEMP_INDEX_PCIE;
+
+ if (hwmon->xe->info.subplatform == XE_SUBPLATFORM_BATTLEMAGE_G21)
+ return BMG_G21_TEMP_INDEX_PCIE;
+
+ return TEMP_INDEX_PCIE;
+}
+
static int get_mc_temp(struct xe_hwmon *hwmon, long *val)
{
struct xe_tile *root_tile = xe_device_get_root_tile(hwmon->xe);
- int ret, i, count = 0;
+ int ret, i, start, end, count = 0;
s32 average = 0;
+ u32 data = 0;
guard(mutex)(&hwmon->hwmon_lock);
- for (i = 0; i < DIV_ROUND_UP(hwmon->temp.count, sizeof(u32)); i++) {
- u32 data = 0;
+ /* CRI relocated the memory controller temperatures to READ_THERMAL_DATA bytes 16..19. */
+ if (hwmon->xe->info.platform == XE_CRESCENTISLAND) {
+ start = CRI_TEMP_INDEX_MCTRL;
+ end = CRI_TEMP_INDEX_MCTRL + CRI_TEMP_MCTRL_COUNT;
+ } else {
+ start = TEMP_INDEX_MCTRL;
+ end = min_t(int, hwmon->temp.count - 1, xe_hwmon_pcie_temp_index(hwmon));
+ }
+ if (end <= start || end > hwmon->temp.count) {
+ drm_warn(&hwmon->xe->drm,
+ "memory controller range [%d, %d) is invalid for temperature count %d\n",
+ start, end, hwmon->temp.count);
+ return -ENODATA;
+ }
+
+ for (i = start / sizeof(u32); i < DIV_ROUND_UP(end, sizeof(u32)); i++) {
ret = xe_pcode_read(root_tile, PCODE_MBOX(PCODE_THERMAL_INFO, READ_THERMAL_DATA, i),
&data, NULL);
if (ret)
@@ -908,7 +999,7 @@ static int get_mc_temp(struct xe_hwmon *hwmon, long *val)
hwmon->temp.dword[i] = cpu_to_le32(data);
}
- for (i = TEMP_INDEX_MCTRL; i < hwmon->temp.count - 1; i++) {
+ for (i = start; i < end; i++) {
if (!mbx_temp_valid(hwmon, hwmon->temp.value[i]))
continue;
average += mbx_temp_decode(hwmon->temp.value[i]);
@@ -928,23 +1019,36 @@ static int get_mc_temp(struct xe_hwmon *hwmon, long *val)
return 0;
}
-static int get_pcie_temp(struct xe_hwmon *hwmon, long *val)
+static int get_pcie_temp(const struct xe_hwmon *hwmon, long *val)
{
struct xe_tile *root_tile = xe_device_get_root_tile(hwmon->xe);
u32 data = 0;
+ int index;
int ret;
+ index = xe_hwmon_pcie_temp_index(hwmon);
+ if (index >= hwmon->temp.count) {
+ drm_warn(&hwmon->xe->drm,
+ "PCIe temperature index %d out of range for temperature count %d\n",
+ index, hwmon->temp.count);
+ return -ENODATA;
+ }
+
ret = xe_pcode_read(root_tile, PCODE_MBOX(PCODE_THERMAL_INFO, READ_THERMAL_DATA,
- PCIE_SENSOR_GROUP_ID), &data, NULL);
+ index / sizeof(data)),
+ &data, NULL);
if (ret)
return ret;
- /* Sensor offset is different for G21 */
- if (hwmon->xe->info.subplatform != XE_SUBPLATFORM_BATTLEMAGE_G21)
- data = REG_FIELD_GET(PCIE_SENSOR_MASK, data);
-
+ data >>= (index % sizeof(data)) * BITS_PER_BYTE;
data = REG_FIELD_GET(TEMP_MASK, data);
- *val = mbx_temp_decode(data) * MILLIDEGREE_PER_DEGREE;
+ if (!mbx_temp_valid(hwmon, data)) {
+ drm_dbg(&hwmon->xe->drm, "PCIe temperature not available\n");
+ return -ENXIO;
+ }
+
+ if (val)
+ *val = mbx_temp_decode(data) * MILLIDEGREE_PER_DEGREE;
return 0;
}
@@ -1043,19 +1147,70 @@ static void xe_hwmon_get_voltage(struct xe_hwmon *hwmon, int channel, long *valu
*value = DIV_ROUND_CLOSEST(REG_FIELD_GET(VOLTAGE_MASK, reg_val) * 2500, SF_VOLTAGE);
}
-static inline bool is_vram_ch_available(struct xe_hwmon *hwmon, int channel)
+static bool xe_hwmon_temp_probe(struct xe_hwmon *hwmon, int channel)
{
struct xe_mmio *mmio = xe_root_tile_mmio(hwmon->xe);
- int vram_id = channel - CHANNEL_VRAM_N;
- struct xe_reg vram_reg;
+ struct xe_reg reg;
+ u32 reg_val;
- vram_reg = xe_hwmon_get_reg(hwmon, REG_TEMP, channel);
- if (!xe_reg_is_valid(vram_reg) || !xe_mmio_read32(mmio, vram_reg))
+ switch (channel) {
+ case CHANNEL_PKG:
+ case CHANNEL_VRAM:
+ case CHANNEL_VRAM_N...CHANNEL_VRAM_N_MAX:
+ reg = xe_hwmon_get_reg(hwmon, REG_TEMP, channel);
+ if (!xe_reg_is_valid(reg))
+ return false;
+
+ /*
+ * On CRI, VRAM channel presence comes from the MSU enable mask
+ * (applied in xe_hwmon_get_reg()); the temperature register's
+ * 0xffffffff sentinel is not reliable for VRAM channels.
+ */
+ if (hwmon->xe->info.platform == XE_CRESCENTISLAND && channel >= CHANNEL_VRAM_N)
+ return true;
+
+ reg_val = xe_mmio_read32(mmio, reg);
+ if (!mmio_temp_valid(hwmon, reg_val)) {
+ drm_dbg(&hwmon->xe->drm,
+ "channel %d temperature unavailable, val 0x%x\n", channel, reg_val);
+ return false;
+ }
+
+ return true;
+ case CHANNEL_MCTRL:
+ return hwmon->temp.count && !get_mc_temp(hwmon, NULL);
+ case CHANNEL_PCIE:
+ return hwmon->temp.count && !get_pcie_temp(hwmon, NULL);
+ default:
return false;
+ }
+}
- /* Create label only for available vram channel */
- sprintf(hwmon->temp.vram_label[vram_id], "vram_ch_%d", vram_id);
- return true;
+static int xe_hwmon_init_temp_info(struct xe_hwmon *hwmon)
+{
+ struct device *dev = hwmon->xe->drm.dev;
+ int channel;
+
+ if (hwmon->xe->info.has_mbx_thermal_info && xe_hwmon_pcode_read_thermal_info(hwmon))
+ drm_warn(&hwmon->xe->drm, "Thermal mailbox not supported by card firmware\n");
+
+ /* vram_count is known only after reading thermal info above. */
+ if (hwmon->temp.vram_count) {
+ hwmon->temp.vram_label = devm_kcalloc(dev, hwmon->temp.vram_count,
+ MAX_LABEL_SIZE, GFP_KERNEL);
+ if (!hwmon->temp.vram_label)
+ return -ENOMEM;
+ }
+
+ for (channel = 0; channel < CHANNEL_MAX; channel++) {
+ hwmon->temp.available[channel] = xe_hwmon_temp_probe(hwmon, channel);
+ if (hwmon->temp.available[channel] && hwmon->temp.vram_label &&
+ in_range(channel, CHANNEL_VRAM_N, hwmon->temp.vram_count))
+ snprintf(hwmon->temp.vram_label[channel - CHANNEL_VRAM_N], MAX_LABEL_SIZE,
+ "vram_ch_%d", channel - CHANNEL_VRAM_N);
+ }
+
+ return 0;
}
static umode_t
@@ -1065,14 +1220,13 @@ xe_hwmon_temp_is_visible(struct xe_hwmon *hwmon, u32 attr, int channel)
case hwmon_temp_emergency:
switch (channel) {
case CHANNEL_PKG:
- return hwmon->temp.limit[TEMP_LIMIT_PKG_SHUTDOWN] ? 0444 : 0;
- case CHANNEL_VRAM:
- return hwmon->temp.limit[TEMP_LIMIT_MEM_SHUTDOWN] ? 0444 : 0;
case CHANNEL_MCTRL:
case CHANNEL_PCIE:
- return hwmon->temp.count ? 0444 : 0;
+ return (hwmon->temp.available[channel] &&
+ hwmon->temp.limit[TEMP_LIMIT_PKG_SHUTDOWN]) ? 0444 : 0;
+ case CHANNEL_VRAM:
case CHANNEL_VRAM_N...CHANNEL_VRAM_N_MAX:
- return (is_vram_ch_available(hwmon, channel) &&
+ return (hwmon->temp.available[channel] &&
hwmon->temp.limit[TEMP_LIMIT_MEM_SHUTDOWN]) ? 0444 : 0;
default:
return 0;
@@ -1080,14 +1234,13 @@ xe_hwmon_temp_is_visible(struct xe_hwmon *hwmon, u32 attr, int channel)
case hwmon_temp_crit:
switch (channel) {
case CHANNEL_PKG:
- return hwmon->temp.limit[TEMP_LIMIT_PKG_CRIT] ? 0444 : 0;
- case CHANNEL_VRAM:
- return hwmon->temp.limit[TEMP_LIMIT_MEM_CRIT] ? 0444 : 0;
case CHANNEL_MCTRL:
case CHANNEL_PCIE:
- return hwmon->temp.count ? 0444 : 0;
+ return (hwmon->temp.available[channel] &&
+ hwmon->temp.limit[TEMP_LIMIT_PKG_CRIT]) ? 0444 : 0;
+ case CHANNEL_VRAM:
case CHANNEL_VRAM_N...CHANNEL_VRAM_N_MAX:
- return (is_vram_ch_available(hwmon, channel) &&
+ return (hwmon->temp.available[channel] &&
hwmon->temp.limit[TEMP_LIMIT_MEM_CRIT]) ? 0444 : 0;
default:
return 0;
@@ -1095,7 +1248,8 @@ xe_hwmon_temp_is_visible(struct xe_hwmon *hwmon, u32 attr, int channel)
case hwmon_temp_max:
switch (channel) {
case CHANNEL_PKG:
- return hwmon->temp.limit[TEMP_LIMIT_PKG_MAX] ? 0444 : 0;
+ return (hwmon->temp.available[channel] &&
+ hwmon->temp.limit[TEMP_LIMIT_PKG_MAX]) ? 0444 : 0;
default:
return 0;
}
@@ -1104,13 +1258,10 @@ xe_hwmon_temp_is_visible(struct xe_hwmon *hwmon, u32 attr, int channel)
switch (channel) {
case CHANNEL_PKG:
case CHANNEL_VRAM:
- return xe_reg_is_valid(xe_hwmon_get_reg(hwmon, REG_TEMP,
- channel)) ? 0444 : 0;
case CHANNEL_MCTRL:
case CHANNEL_PCIE:
- return hwmon->temp.count ? 0444 : 0;
case CHANNEL_VRAM_N...CHANNEL_VRAM_N_MAX:
- return is_vram_ch_available(hwmon, channel) ? 0444 : 0;
+ return hwmon->temp.available[channel] ? 0444 : 0;
default:
return 0;
}
@@ -1119,6 +1270,70 @@ xe_hwmon_temp_is_visible(struct xe_hwmon *hwmon, u32 attr, int channel)
}
}
+#define IEEE754_SIGN REG_BIT(31)
+#define IEEE754_EXP_MASK REG_GENMASK(30, 23)
+#define IEEE754_MANTISSA_MASK REG_GENMASK(22, 0)
+#define IEEE754_EXP_BIAS 127 /* Exponent bias to obtain the actual exponent */
+#define IEEE754_MANTISSA_BITS 23 /* Mantissa width and implicit leading-1 bit position */
+#define IEEE754_EXP_SPECIAL 0xff /* reserved exponent value encoding infinity or NaN */
+
+/**
+ * xe_hwmon_temperature_decode - Decode an IEEE-754 temperature register value
+ * @reg_val: The raw register value to decode
+ * @temperature: Pointer to store the decoded temperature in millidegrees Celsius.
+ *
+ * IEEE-754 single-precision format:
+ *
+ * 31 30 23 22 0
+ * +--+-----------------------------+----------------------------+
+ * | S| exponent | fraction |
+ * +--+-----------------------------+----------------------------+
+ *
+ * value = (-1)^S * 1.fraction * 2^(exponent - 127)
+ *
+ * Return: 0 on success or an error if @reg_val is non-finite or cannot be
+ * represented in millidegrees Celsius.
+ */
+static int xe_hwmon_temperature_decode(u32 reg_val, long *temperature)
+{
+ u32 exponent = REG_FIELD_GET(IEEE754_EXP_MASK, reg_val);
+ u32 mantissa = REG_FIELD_GET(IEEE754_MANTISSA_MASK, reg_val);
+ s64 value;
+ int shift;
+
+ /* Exponent 0xff encodes infinity or NaN. */
+ if (exponent == IEEE754_EXP_SPECIAL)
+ return -EINVAL;
+
+ /* Subnormal values are smaller than one millidegree Celsius. */
+ if (!exponent) {
+ *temperature = 0;
+ return 0;
+ }
+
+ value = (s64)(mantissa | BIT(IEEE754_MANTISSA_BITS)) * MILLIDEGREE_PER_DEGREE;
+ shift = exponent - (IEEE754_EXP_BIAS + IEEE754_MANTISSA_BITS);
+ if (shift >= 0) {
+ if (shift >= BITS_PER_LONG || value > (LONG_MAX >> shift))
+ return -ERANGE;
+ value <<= shift;
+ } else if (shift > -64) {
+ value >>= -shift;
+ } else {
+ value = 0;
+ }
+
+ /* Ensure the decoded value fits in a long. */
+ if (overflows_type(value, *temperature))
+ return -ERANGE;
+
+ if (reg_val & IEEE754_SIGN)
+ value = -value;
+
+ *temperature = value;
+ return 0;
+}
+
static int
xe_hwmon_temp_read(struct xe_hwmon *hwmon, u32 attr, int channel, long *val)
{
@@ -1128,26 +1343,33 @@ xe_hwmon_temp_read(struct xe_hwmon *hwmon, u32 attr, int channel, long *val)
switch (attr) {
case hwmon_temp_input:
switch (channel) {
- case CHANNEL_PKG:
- case CHANNEL_VRAM:
- reg_val = xe_mmio_read32(mmio, xe_hwmon_get_reg(hwmon, REG_TEMP, channel));
-
- /* HW register value is in degrees Celsius, convert to millidegrees. */
- *val = REG_FIELD_GET(TEMP_MASK, reg_val) * MILLIDEGREE_PER_DEGREE;
- return 0;
case CHANNEL_MCTRL:
return get_mc_temp(hwmon, val);
case CHANNEL_PCIE:
return get_pcie_temp(hwmon, val);
+ case CHANNEL_PKG:
+ case CHANNEL_VRAM:
case CHANNEL_VRAM_N...CHANNEL_VRAM_N_MAX:
reg_val = xe_mmio_read32(mmio, xe_hwmon_get_reg(hwmon, REG_TEMP, channel));
- /*
- * This temperature format is 24 bit [31:8] signed integer and 8 bit
- * [7:0] fraction.
- */
- *val = (s32)(REG_FIELD_GET(TEMP_MASK_VRAM_N, reg_val)) *
- (REG_FIELD_GET(TEMP_SIGN_MASK, reg_val) ? -1 : 1) *
- MILLIDEGREE_PER_DEGREE;
+ if (!mmio_temp_valid(hwmon, reg_val)) {
+ drm_dbg(&hwmon->xe->drm,
+ "channel %d temperature invalid, val 0x%llx\n",
+ channel, reg_val);
+ return -ENXIO;
+ }
+ if (hwmon->xe->info.platform >= XE_CRESCENTISLAND) {
+ return xe_hwmon_temperature_decode((u32)reg_val, val);
+ } else if (channel >= CHANNEL_VRAM_N) {
+ /*
+ * This temperature format is 24 bit [31:8] signed integer and 8 bit
+ * [7:0] fraction for platforms before CRI.
+ */
+ *val = (s32)REG_FIELD_GET(TEMP_MASK_VRAM_N, reg_val) *
+ (REG_FIELD_GET(TEMP_SIGN_MASK, reg_val) ? -1 : 1) *
+ MILLIDEGREE_PER_DEGREE;
+ } else {
+ *val = REG_FIELD_GET(TEMP_MASK, reg_val) * MILLIDEGREE_PER_DEGREE;
+ }
return 0;
default:
return -EOPNOTSUPP;
@@ -1229,7 +1451,8 @@ xe_hwmon_power_is_visible(struct xe_hwmon *hwmon, u32 attr, int channel)
}
case hwmon_power_crit:
if (channel == CHANNEL_CARD) {
- xe_hwmon_pcode_read_i1(hwmon, &uval);
+ if (xe_hwmon_pcode_read_i1(hwmon, &uval))
+ return 0;
return (uval & POWER_SETUP_I1_WATTS) ? 0644 : 0;
}
break;
@@ -1248,7 +1471,8 @@ xe_hwmon_power_is_visible(struct xe_hwmon *hwmon, u32 attr, int channel)
}
}
if ((!(uval & PWR_LIM_EN)) && channel == CHANNEL_CARD) {
- xe_hwmon_pcode_read_i1(hwmon, &uval);
+ if (xe_hwmon_pcode_read_i1(hwmon, &uval))
+ return 0;
return (uval & POWER_SETUP_I1_WATTS) ? 0444 : 0;
}
return (uval) ? 0444 : 0;
@@ -1264,11 +1488,9 @@ xe_hwmon_power_read(struct xe_hwmon *hwmon, u32 attr, int channel, long *val)
switch (attr) {
case hwmon_power_max:
case hwmon_power_cap:
- xe_hwmon_power_max_read(hwmon, attr, channel, val);
- return 0;
+ return xe_hwmon_power_max_read(hwmon, attr, channel, val);
case hwmon_power_rated_max:
- xe_hwmon_power_rated_max_read(hwmon, attr, channel, val);
- return 0;
+ return xe_hwmon_power_rated_max_read(hwmon, attr, channel, val);
case hwmon_power_crit:
return xe_hwmon_power_curr_crit_read(hwmon, channel, val, SF_POWER);
default:
@@ -1385,8 +1607,7 @@ xe_hwmon_energy_read(struct xe_hwmon *hwmon, u32 attr, int channel, long *val)
{
switch (attr) {
case hwmon_energy_input:
- xe_hwmon_energy_get(hwmon, channel, val);
- return 0;
+ return xe_hwmon_energy_get(hwmon, channel, val);
default:
return -EOPNOTSUPP;
}
@@ -1555,8 +1776,10 @@ static int xe_hwmon_read_label(struct device *dev,
*str = "mctrl";
else if (channel == CHANNEL_PCIE)
*str = "pcie";
- else if (in_range(channel, CHANNEL_VRAM_N, MAX_VRAM_CHANNELS))
+ else if (in_range(channel, CHANNEL_VRAM_N, hwmon->temp.vram_count))
*str = hwmon->temp.vram_label[channel - CHANNEL_VRAM_N];
+ else
+ return -EOPNOTSUPP;
return 0;
case hwmon_power:
case hwmon_energy:
@@ -1584,7 +1807,7 @@ static const struct hwmon_chip_info hwmon_chip_info = {
.info = hwmon_info,
};
-static void
+static int
xe_hwmon_get_preregistration_info(struct xe_hwmon *hwmon)
{
struct xe_mmio *mmio = xe_root_tile_mmio(hwmon->xe);
@@ -1641,8 +1864,7 @@ xe_hwmon_get_preregistration_info(struct xe_hwmon *hwmon)
if (xe_hwmon_is_visible(hwmon, hwmon_fan, hwmon_fan_input, channel))
xe_hwmon_fan_input_read(hwmon, channel, &fan_speed);
- if (hwmon->xe->info.has_mbx_thermal_info && xe_hwmon_pcode_read_thermal_info(hwmon))
- drm_warn(&hwmon->xe->drm, "Thermal mailbox not supported by card firmware\n");
+ return xe_hwmon_init_temp_info(hwmon);
}
int xe_hwmon_register(struct xe_device *xe)
@@ -1671,7 +1893,9 @@ int xe_hwmon_register(struct xe_device *xe)
hwmon->xe = xe;
xe->hwmon = hwmon;
- xe_hwmon_get_preregistration_info(hwmon);
+ ret = xe_hwmon_get_preregistration_info(hwmon);
+ if (ret)
+ goto err_null_hwmon;
drm_dbg(&xe->drm, "Register xe hwmon interface\n");
@@ -1680,11 +1904,15 @@ int xe_hwmon_register(struct xe_device *xe)
&hwmon_chip_info,
hwmon_groups);
if (IS_ERR(hwmon->hwmon_dev)) {
- drm_err(&xe->drm, "Failed to register xe hwmon (%pe)\n", hwmon->hwmon_dev);
- xe->hwmon = NULL;
- return PTR_ERR(hwmon->hwmon_dev);
+ ret = PTR_ERR(hwmon->hwmon_dev);
+ xe_log_err(xe, HWMON, ret, "Failed to register xe hwmon\n");
+ goto err_null_hwmon;
}
return 0;
+
+err_null_hwmon:
+ xe->hwmon = NULL;
+ return ret;
}
MODULE_IMPORT_NS("INTEL_PMT_TELEMETRY");
diff --git a/drivers/gpu/drm/xe/xe_late_bind_fw.c b/drivers/gpu/drm/xe/xe_late_bind_fw.c
index 1bddfaf0fb9f0..355d7c841871d 100644
--- a/drivers/gpu/drm/xe/xe_late_bind_fw.c
+++ b/drivers/gpu/drm/xe/xe_late_bind_fw.c
@@ -17,6 +17,12 @@
#include "xe_pcode.h"
#include "xe_pcode_api.h"
#include "xe_pm.h"
+#include "xe_sysctrl.h"
+
+/*
+ * TODO: Add MODULE_FIRMWARE() for xe/cri_ocode_<ver>.bin once the
+ * Ocode firmware binary is available upstream.
+ */
/*
* The component should load quite quickly in most cases, but it could take
@@ -33,10 +39,12 @@
static const u32 fw_id_to_type[] = {
[XE_LB_FW_FAN_CONTROL] = INTEL_LB_TYPE_FAN_CONTROL,
+ [XE_LB_FW_OCODE] = INTEL_LB_TYPE_OCODE,
};
static const char * const fw_id_to_name[] = {
[XE_LB_FW_FAN_CONTROL] = "fan_control",
+ [XE_LB_FW_OCODE] = "ocode",
};
static struct xe_device *
@@ -104,6 +112,7 @@ static int parse_cpd_header(struct xe_late_bind_fw *lb_fw,
manifest = data + offset;
lb_fw->version = manifest->fw_version;
+ lb_fw->sdk_api_version = manifest->ocode_data.sdk_api_version;
return 0;
}
@@ -179,6 +188,34 @@ static const char *xe_late_bind_parse_status(uint32_t status)
return "Invalid Payload";
case INTEL_LB_STATUS_TIMEOUT:
return "Timeout";
+ case INTEL_LB_STATUS_BUFFER_TOO_SMALL:
+ return "Buffer too small";
+ case INTEL_LB_STATUS_INTERNAL_ERROR:
+ return "Internal Error";
+ case INTEL_LB_STATUS_INVALID_FPT_TABLE:
+ return "Invalid FPT Table";
+ case INTEL_LB_STATUS_SIGNED_PAYLOAD_VERIFICATION_ERROR:
+ return "Signed Payload Verification Error";
+ case INTEL_LB_STATUS_SIGNED_PAYLOAD_INVALID_CPD:
+ return "Signed Payload Invalid CPD";
+ case INTEL_LB_STATUS_SIGNED_PAYLOAD_FW_VERSION_MISMATCH:
+ return "Signed Payload FW Version Mismatch";
+ case INTEL_LB_STATUS_SIGNED_PAYLOAD_INVALID_MANIFEST:
+ return "Signed Payload Invalid Manifest";
+ case INTEL_LB_STATUS_SIGNED_PAYLOAD_INVALID_HASH:
+ return "Signed Payload Invalid Hash";
+ case INTEL_LB_STATUS_SIGNED_PAYLOAD_BINDING_TYPE_MISMATCH:
+ return "Signed Payload Binding type Mismatch";
+ case INTEL_LB_STATUS_SIGNED_PAYLOAD_HANDLE_SVN_FAILED:
+ return "Signed Payload Handle SVN Failed";
+ case INTEL_LB_STATUS_DESTINATION_MBOX_FAILURE:
+ return "Destination MBOX Failure";
+ case INTEL_LB_STATUS_MISSING_LOADING_PATCH:
+ return "Missing Loading Patch";
+ case INTEL_LB_STATUS_INVALID_COMMAND:
+ return "Invalid Command";
+ case INTEL_LB_STATUS_INVALID_HECI_HEADER:
+ return "Invalid HECI Header";
default:
return "Unknown error";
}
@@ -193,7 +230,7 @@ static int xe_late_bind_fw_num_fans(struct xe_late_bind *late_bind, u32 *num_fan
PCODE_MBOX(FAN_SPEED_CONTROL, FSC_READ_NUM_FANS, 0), num_fans, NULL);
}
-void xe_late_bind_wait_for_worker_completion(struct xe_late_bind *late_bind)
+static void wait_for_worker_completion(struct xe_late_bind *late_bind)
{
struct xe_device *xe = late_bind_to_xe(late_bind);
struct xe_late_bind_fw *lbfw;
@@ -269,18 +306,12 @@ static void xe_late_bind_work(struct work_struct *work)
xe_pm_runtime_put(xe);
}
-int xe_late_bind_fw_load(struct xe_late_bind *late_bind)
+static void xe_late_bind_fw_load(struct xe_late_bind *late_bind)
{
struct xe_device *xe = late_bind_to_xe(late_bind);
struct xe_late_bind_fw *lbfw;
int fw_id;
- if (!late_bind->component_added)
- return -ENODEV;
-
- if (late_bind->disable)
- return 0;
-
for (fw_id = 0; fw_id < XE_LB_FW_MAX_ID; fw_id++) {
lbfw = &late_bind->late_bind_fw[fw_id];
if (lbfw->payload) {
@@ -288,7 +319,6 @@ int xe_late_bind_fw_load(struct xe_late_bind *late_bind)
queue_work(late_bind->wq, &lbfw->work);
}
}
- return 0;
}
static int __xe_late_bind_fw_init(struct xe_late_bind *late_bind, u32 fw_id)
@@ -298,6 +328,7 @@ static int __xe_late_bind_fw_init(struct xe_late_bind *late_bind, u32 fw_id)
struct xe_late_bind_fw *lb_fw;
const struct firmware *fw;
u32 num_fans;
+ u16 sdk_ver = 0;
int ret;
if (fw_id >= XE_LB_FW_MAX_ID)
@@ -320,9 +351,23 @@ static int __xe_late_bind_fw_init(struct xe_late_bind *late_bind, u32 fw_id)
return 0;
}
- snprintf(lb_fw->blob_path, sizeof(lb_fw->blob_path), "xe/%s_8086_%04x_%04x_%04x.bin",
- fw_id_to_name[lb_fw->id], pdev->device,
- pdev->subsystem_vendor, pdev->subsystem_device);
+ if (lb_fw->type == INTEL_LB_TYPE_OCODE) {
+ /* Get SDK API version */
+ ret = xe_sysctrl_sdk_api_version(xe, &sdk_ver);
+ if (ret) {
+ drm_warn(&xe->drm, "Failed to get OCODE SDK API version: %pe\n",
+ ERR_PTR(ret));
+ return 0;
+ }
+
+ snprintf(lb_fw->blob_path, sizeof(lb_fw->blob_path),
+ "xe/cri_ocode_%u.bin", sdk_ver);
+ } else {
+ snprintf(lb_fw->blob_path, sizeof(lb_fw->blob_path),
+ "xe/%s_8086_%04x_%04x_%04x.bin",
+ fw_id_to_name[lb_fw->id], pdev->device,
+ pdev->subsystem_vendor, pdev->subsystem_device);
+ }
drm_dbg(&xe->drm, "Request late binding firmware %s\n", lb_fw->blob_path);
ret = firmware_request_nowarn(&fw, lb_fw->blob_path, xe->drm.dev);
@@ -332,17 +377,17 @@ static int __xe_late_bind_fw_init(struct xe_late_bind *late_bind, u32 fw_id)
return 0;
}
- if (fw->size > XE_LB_MAX_PAYLOAD_SIZE) {
- drm_err(&xe->drm, "Firmware %s size %zu is larger than max pay load size %u\n",
- lb_fw->blob_path, fw->size, XE_LB_MAX_PAYLOAD_SIZE);
- ret = -ENODATA;
- goto release_fw;
- }
-
ret = parse_lb_layout(lb_fw, fw->data, fw->size, "LTES");
if (ret)
goto release_fw;
+ if (lb_fw->type == INTEL_LB_TYPE_OCODE && lb_fw->sdk_api_version != sdk_ver) {
+ drm_warn(&xe->drm, "%s late binding fw: SDK API version mismatch %u != %u\n",
+ fw_id_to_name[lb_fw->id], lb_fw->sdk_api_version, sdk_ver);
+ ret = 0;
+ goto release_fw;
+ }
+
lb_fw->payload_size = fw->size;
lb_fw->payload = drmm_kzalloc(&xe->drm, lb_fw->payload_size, GFP_KERNEL);
if (!lb_fw->payload) {
@@ -355,6 +400,10 @@ static int __xe_late_bind_fw_init(struct xe_late_bind *late_bind, u32 fw_id)
lb_fw->version.major, lb_fw->version.minor,
lb_fw->version.hotfix, lb_fw->version.build);
+ if (lb_fw->type == INTEL_LB_TYPE_OCODE)
+ drm_info(&xe->drm, "%s firmware SDK API version %u\n",
+ fw_id_to_name[lb_fw->id], lb_fw->sdk_api_version);
+
memcpy((void *)lb_fw->payload, fw->data, lb_fw->payload_size);
INIT_WORK(&lb_fw->work, xe_late_bind_work);
@@ -365,6 +414,7 @@ static int __xe_late_bind_fw_init(struct xe_late_bind *late_bind, u32 fw_id)
static int xe_late_bind_fw_init(struct xe_late_bind *late_bind)
{
+ struct xe_device *xe = late_bind_to_xe(late_bind);
int ret;
int fw_id;
@@ -373,6 +423,8 @@ static int xe_late_bind_fw_init(struct xe_late_bind *late_bind)
return -ENOMEM;
for (fw_id = 0; fw_id < XE_LB_FW_MAX_ID; fw_id++) {
+ if (!(xe->info.late_bind_mask & BIT(fw_id)))
+ continue;
ret = __xe_late_bind_fw_init(late_bind, fw_id);
if (ret)
return ret;
@@ -399,7 +451,7 @@ static void xe_late_bind_component_unbind(struct device *xe_kdev,
struct xe_device *xe = kdev_to_xe_device(xe_kdev);
struct xe_late_bind *late_bind = &xe->late_bind;
- xe_late_bind_wait_for_worker_completion(late_bind);
+ wait_for_worker_completion(late_bind);
late_bind->component.ops = NULL;
}
@@ -414,7 +466,7 @@ static void xe_late_bind_remove(void *arg)
struct xe_late_bind *late_bind = arg;
struct xe_device *xe = late_bind_to_xe(late_bind);
- xe_late_bind_wait_for_worker_completion(late_bind);
+ wait_for_worker_completion(late_bind);
late_bind->component_added = false;
@@ -425,6 +477,43 @@ static void xe_late_bind_remove(void *arg)
}
}
+static bool late_bind_mei_enabled(struct xe_device *xe)
+{
+ if (!IS_ENABLED(CONFIG_INTEL_MEI_LB))
+ return false;
+
+ /* MEI exposed as xe auxiliary device */
+ if (xe->info.has_heci_cscfi)
+ return IS_ENABLED(CONFIG_INTEL_MEI_GSC);
+
+ /* Standalone MEI PCI function */
+ return IS_ENABLED(CONFIG_INTEL_MEI_CSC);
+}
+
+/**
+ * xe_late_bind_pm_suspend() - Wait for pending late binding fw loads
+ * @late_bind: pointer to late bind structure.
+ */
+void xe_late_bind_pm_suspend(struct xe_late_bind *late_bind)
+{
+ if (!late_bind->component_added)
+ return;
+
+ wait_for_worker_completion(late_bind);
+}
+
+/**
+ * xe_late_bind_pm_resume() - Reload late binding fw after resume
+ * @late_bind: pointer to late bind structure.
+ */
+void xe_late_bind_pm_resume(struct xe_late_bind *late_bind)
+{
+ if (!late_bind->component_added || late_bind->disable)
+ return;
+
+ xe_late_bind_fw_load(late_bind);
+}
+
/**
* xe_late_bind_init() - add xe mei late binding component
* @late_bind: pointer to late bind structure.
@@ -436,10 +525,10 @@ int xe_late_bind_init(struct xe_late_bind *late_bind)
struct xe_device *xe = late_bind_to_xe(late_bind);
int err;
- if (!xe->info.has_late_bind)
+ if (!xe->info.late_bind_mask)
return 0;
- if (!IS_ENABLED(CONFIG_INTEL_MEI_LB) || !IS_ENABLED(CONFIG_INTEL_MEI_GSC)) {
+ if (!late_bind_mei_enabled(xe)) {
drm_info(&xe->drm, "Can't init xe mei late bind missing mei component\n");
return 0;
}
@@ -461,5 +550,7 @@ int xe_late_bind_init(struct xe_late_bind *late_bind)
if (err)
return err;
- return xe_late_bind_fw_load(late_bind);
+ xe_late_bind_fw_load(late_bind);
+
+ return 0;
}
diff --git a/drivers/gpu/drm/xe/xe_mert.c b/drivers/gpu/drm/xe/xe_mert.c
index 934be933d57f7..a2eae6088c1ac 100644
--- a/drivers/gpu/drm/xe/xe_mert.c
+++ b/drivers/gpu/drm/xe/xe_mert.c
@@ -11,6 +11,7 @@
#include "xe_mmio.h"
#include "xe_sriov_printk.h"
#include "xe_tile.h"
+#include "xe_wedge.h"
/**
* xe_mert_init_early() - Initialize MERT data
@@ -74,8 +75,7 @@ static void mert_handle_cat_error(struct xe_device *xe)
break;
case CATERR_UNMAPPED_GGTT:
xe_sriov_err(xe, "MERT: CAT_ERR: Access to an unmapped GGTT!\n");
- xe_device_declare_wedged(xe,
- DRM_WEDGE_RECOVERY_REBIND | DRM_WEDGE_RECOVERY_BUS_RESET);
+ xe_wedge_declare(xe, DRM_WEDGE_RECOVERY_REBIND | DRM_WEDGE_RECOVERY_BUS_RESET);
break;
case CATERR_LMTT_FAULT:
xe_sriov_dbg_ratelimited(xe, "MERT: CAT_ERR: VF%u LMTT fault!\n", vfid);
@@ -83,8 +83,7 @@ static void mert_handle_cat_error(struct xe_device *xe)
break;
default:
xe_sriov_err(xe, "MERT: Unexpected CAT_ERR code=%#x!\n", code);
- xe_device_declare_wedged(xe,
- DRM_WEDGE_RECOVERY_REBIND | DRM_WEDGE_RECOVERY_BUS_RESET);
+ xe_wedge_declare(xe, DRM_WEDGE_RECOVERY_REBIND | DRM_WEDGE_RECOVERY_BUS_RESET);
break;
}
}
diff --git a/drivers/gpu/drm/xe/xe_pat.c b/drivers/gpu/drm/xe/xe_pat.c
index 0ac64a6a3098a..5938fef22579a 100644
--- a/drivers/gpu/drm/xe/xe_pat.c
+++ b/drivers/gpu/drm/xe/xe_pat.c
@@ -16,6 +16,7 @@
#include "xe_force_wake.h"
#include "xe_gt.h"
#include "xe_gt_mcr.h"
+#include "xe_log.h"
#include "xe_mmio.h"
#include "xe_sriov.h"
#include "xe_wa.h"
@@ -93,29 +94,29 @@ struct xe_pat_ops {
};
static const struct xe_pat_table_entry xelp_pat_table[] = {
- [0] = { XELP_PAT_WB, XE_COH_1WAY },
- [1] = { XELP_PAT_WC, XE_COH_NONE },
- [2] = { XELP_PAT_WT, XE_COH_NONE },
- [3] = { XELP_PAT_UC, XE_COH_NONE },
+ [0] = { XELP_PAT_WB, XE_COH_1WAY, 1 },
+ [1] = { XELP_PAT_WC, XE_COH_NONE, 1 },
+ [2] = { XELP_PAT_WT, XE_COH_NONE, 1 },
+ [3] = { XELP_PAT_UC, XE_COH_NONE, 1 },
};
static const struct xe_pat_table_entry xehpc_pat_table[] = {
- [0] = { XELP_PAT_UC, XE_COH_NONE },
- [1] = { XELP_PAT_WC, XE_COH_NONE },
- [2] = { XELP_PAT_WT, XE_COH_NONE },
- [3] = { XELP_PAT_WB, XE_COH_1WAY },
- [4] = { XEHPC_PAT_CLOS(1) | XELP_PAT_WT, XE_COH_NONE },
- [5] = { XEHPC_PAT_CLOS(1) | XELP_PAT_WB, XE_COH_1WAY },
- [6] = { XEHPC_PAT_CLOS(2) | XELP_PAT_WT, XE_COH_NONE },
- [7] = { XEHPC_PAT_CLOS(2) | XELP_PAT_WB, XE_COH_1WAY },
+ [0] = { XELP_PAT_UC, XE_COH_NONE, 1 },
+ [1] = { XELP_PAT_WC, XE_COH_NONE, 1 },
+ [2] = { XELP_PAT_WT, XE_COH_NONE, 1 },
+ [3] = { XELP_PAT_WB, XE_COH_1WAY, 1 },
+ [4] = { XEHPC_PAT_CLOS(1) | XELP_PAT_WT, XE_COH_NONE, 1 },
+ [5] = { XEHPC_PAT_CLOS(1) | XELP_PAT_WB, XE_COH_1WAY, 1 },
+ [6] = { XEHPC_PAT_CLOS(2) | XELP_PAT_WT, XE_COH_NONE, 1 },
+ [7] = { XEHPC_PAT_CLOS(2) | XELP_PAT_WB, XE_COH_1WAY, 1 },
};
static const struct xe_pat_table_entry xelpg_pat_table[] = {
- [0] = { XELPG_PAT_0_WB, XE_COH_NONE },
- [1] = { XELPG_PAT_1_WT, XE_COH_NONE },
- [2] = { XELPG_PAT_3_UC, XE_COH_NONE },
- [3] = { XELPG_PAT_0_WB | XELPG_2_COH_1W, XE_COH_1WAY },
- [4] = { XELPG_PAT_0_WB | XELPG_3_COH_2W, XE_COH_2WAY },
+ [0] = { XELPG_PAT_0_WB, XE_COH_NONE, 1 },
+ [1] = { XELPG_PAT_1_WT, XE_COH_NONE, 1 },
+ [2] = { XELPG_PAT_3_UC, XE_COH_NONE, 1 },
+ [3] = { XELPG_PAT_0_WB | XELPG_2_COH_1W, XE_COH_1WAY, 1 },
+ [4] = { XELPG_PAT_0_WB | XELPG_3_COH_2W, XE_COH_2WAY, 1 },
};
/*
@@ -294,25 +295,69 @@ static const struct xe_pat_table_entry xe3p_lpg_pat_table[] = {
[31] = XE2_PAT( 0, 0, 3, 0, 3, 3 ),
};
+bool xe_pat_index_is_valid(struct xe_device *xe, u16 pat_index)
+{
+ if (WARN_ON(pat_index >= xe->pat.n_entries))
+ return false;
+
+ return xe->pat.table[pat_index].valid;
+}
+
u16 xe_pat_index_get_coh_mode(struct xe_device *xe, u16 pat_index)
{
- WARN_ON(pat_index >= xe->pat.n_entries);
+ WARN_ON(!xe_pat_index_is_valid(xe, pat_index));
+ WARN_ON(!xe->pat.table[pat_index].coh_mode ||
+ xe->pat.table[pat_index].coh_mode > XE_COH_2WAY);
return xe->pat.table[pat_index].coh_mode;
}
bool xe_pat_index_get_comp_en(struct xe_device *xe, u16 pat_index)
{
- WARN_ON(pat_index >= xe->pat.n_entries);
+ WARN_ON(!xe_pat_index_is_valid(xe, pat_index));
return !!(xe->pat.table[pat_index].value & XE2_COMP_EN);
}
u16 xe_pat_index_get_l3_policy(struct xe_device *xe, u16 pat_index)
{
- WARN_ON(pat_index >= xe->pat.n_entries);
+ WARN_ON(!xe_pat_index_is_valid(xe, pat_index));
return REG_FIELD_GET(XE2_L3_POLICY, xe->pat.table[pat_index].value);
}
+bool xe_pat_wa_14026539277_reserved(struct xe_device *xe, u16 pat_index)
+{
+ struct xe_gt *gt;
+ u8 id;
+ bool has_wa = false;
+
+ for_each_gt(gt, xe, id) {
+ if (XE_GT_WA(gt, 14026539277)) {
+ has_wa = true;
+ break;
+ }
+ }
+
+ /*
+ * Disallow 2-way coherency for any L3 cache policy because
+ * XE_L3_POLICY_UC could still be promoted to a cached policy
+ * by MOCS indices.
+ */
+ return has_wa && xe_pat_index_get_coh_mode(xe, pat_index) == XE_COH_2WAY;
+}
+
+static u32 wa_14026539277_fixup_pat_value(struct xe_gt *gt, u32 value)
+{
+ if (XE_GT_WA(gt, 14026539277)) {
+ if (REG_FIELD_GET(XE2_COH_MODE, value) == XE_COH_2WAY) {
+ value &= ~(XE2_L3_POLICY | XE2_COH_MODE);
+ value |= REG_FIELD_PREP(XE2_L3_POLICY, XE_L3_POLICY_UC) |
+ REG_FIELD_PREP(XE2_COH_MODE, XE_COH_1WAY);
+ }
+ }
+
+ return value;
+}
+
static const struct xe_pat_table_entry *gt_pta_entry(struct xe_gt *gt)
{
struct xe_device *xe = gt_to_xe(gt);
@@ -373,18 +418,22 @@ static void program_pat_mcr(struct xe_gt *gt, const struct xe_pat_table_entry ta
for (int i = 0; i < n_entries; i++) {
struct xe_reg_mcr reg_mcr = XE_REG_MCR(_PAT_INDEX(i));
+ u32 pat = wa_14026539277_fixup_pat_value(gt, table[i].value);
- xe_gt_mcr_multicast_write(gt, reg_mcr, table[i].value);
+ xe_gt_mcr_multicast_write(gt, reg_mcr, pat);
}
if (xe->pat.pat_ats)
- xe_gt_mcr_multicast_write(gt, XE_REG_MCR(_PAT_ATS), xe->pat.pat_ats->value);
+ xe_gt_mcr_multicast_write(gt, XE_REG_MCR(_PAT_ATS),
+ wa_14026539277_fixup_pat_value(gt, xe->pat.pat_ats->value));
if (pta_entry)
- xe_gt_mcr_multicast_write(gt, XE_REG_MCR(_PAT_PTA), pta_entry->value);
+ xe_gt_mcr_multicast_write(gt, XE_REG_MCR(_PAT_PTA),
+ wa_14026539277_fixup_pat_value(gt, pta_entry->value));
if (tr_pta_entry)
- xe_gt_mcr_multicast_write(gt, XE_REG_MCR(_PAT_TR_PTA), tr_pta_entry->value);
+ xe_gt_mcr_multicast_write(gt, XE_REG_MCR(_PAT_TR_PTA),
+ wa_14026539277_fixup_pat_value(gt, tr_pta_entry->value));
}
static int xelp_dump(struct xe_gt *gt, struct drm_printer *p)
@@ -534,13 +583,16 @@ static int xe2_dump(struct xe_gt *gt, struct drm_printer *p)
drm_printf(p, "PAT table: (* = reserved entry)\n");
for (i = 0; i < xe->pat.n_entries; i++) {
+ bool rsvd = !xe->pat.table[i].valid ||
+ xe_pat_wa_14026539277_reserved(xe, i);
+
if (xe_gt_is_media_type(gt))
pat = xe_mmio_read32(>->mmio, XE_REG(_PAT_INDEX(i)));
else
pat = xe_gt_mcr_unicast_read_any(gt, XE_REG_MCR(_PAT_INDEX(i)));
xe_pat_index_label(label, sizeof(label), i);
- xe->pat.ops->entry_dump(p, label, pat, !xe->pat.table[i].valid);
+ xe->pat.ops->entry_dump(p, label, pat, rsvd);
}
/*
@@ -692,8 +744,9 @@ int xe_pat_init_early(struct xe_device *xe)
* raise an error rather than trying to silently inherit the
* most recent platform's behavior.
*/
- drm_err(&xe->drm, "Missing PAT table for platform with graphics version %d.%02d!\n",
- GRAPHICS_VER(xe), GRAPHICS_VERx100(xe) % 100);
+ xe_log_err(xe, PAT, -ENODATA,
+ "Missing PAT table for platform with graphics version %d.%02d!\n",
+ GRAPHICS_VER(xe), GRAPHICS_VERx100(xe) % 100);
return -ENODATA;
}
@@ -756,9 +809,14 @@ int xe_pat_dump_sw_config(struct xe_gt *gt, struct drm_printer *p)
for (u32 i = 0; i < xe->pat.n_entries; i++) {
u32 pat = xe->pat.table[i].value;
+ pat = wa_14026539277_fixup_pat_value(gt, pat);
+
if (GRAPHICS_VER(xe) >= 20) {
+ bool rsvd = !xe->pat.table[i].valid ||
+ xe_pat_wa_14026539277_reserved(xe, i);
+
xe_pat_index_label(label, sizeof(label), i);
- xe->pat.ops->entry_dump(p, label, pat, !xe->pat.table[i].valid);
+ xe->pat.ops->entry_dump(p, label, pat, rsvd);
} else if (xe->info.platform == XE_METEORLAKE) {
xelpg_pat_entry_dump(p, i, pat);
} else if (xe->info.platform == XE_PVC) {
@@ -773,6 +831,8 @@ int xe_pat_dump_sw_config(struct xe_gt *gt, struct drm_printer *p)
if (pta_entry) {
u32 pat = pta_entry->value;
+ pat = wa_14026539277_fixup_pat_value(gt, pat);
+
drm_printf(p, "Page Table Access:\n");
xe->pat.ops->entry_dump(p, "PTA_MODE", pat, false);
}
@@ -780,6 +840,8 @@ int xe_pat_dump_sw_config(struct xe_gt *gt, struct drm_printer *p)
if (tr_pta_entry) {
u32 pat = tr_pta_entry->value;
+ pat = wa_14026539277_fixup_pat_value(gt, pat);
+
drm_printf(p, "TRTT Page Table Access:\n");
xe->pat.ops->entry_dump(p, "TR_PTA_MODE", pat, false);
}
@@ -787,6 +849,8 @@ int xe_pat_dump_sw_config(struct xe_gt *gt, struct drm_printer *p)
if (xe->pat.pat_ats) {
u32 pat = xe->pat.pat_ats->value;
+ pat = wa_14026539277_fixup_pat_value(gt, pat);
+
drm_printf(p, "PCIe ATS/PASID:\n");
xe->pat.ops->entry_dump(p, "PAT_ATS ", pat, false);
}
diff --git a/drivers/gpu/drm/xe/xe_pci.c b/drivers/gpu/drm/xe/xe_pci.c
index 12ff5c9025478..7fba5295c33e6 100644
--- a/drivers/gpu/drm/xe/xe_pci.c
+++ b/drivers/gpu/drm/xe/xe_pci.c
@@ -417,16 +417,17 @@ static const struct xe_device_desc bmg_desc = {
.dma_mask_size = 46,
.has_display = true,
.has_fan_control = true,
+ .has_fixed_vram_channels = true,
.has_flat_ccs = 1,
.has_mbx_power_limits = true,
.has_mbx_thermal_info = true,
.has_gsc_nvm = 1,
.has_heci_cscfi = 1,
.has_i2c = true,
- .has_late_bind = true,
.has_pre_prod_wa = 1,
.has_soc_remapper_telem = true,
.has_sriov = true,
+ .late_bind_mask = BIT(XE_LB_FW_FAN_CONTROL),
.max_gt_per_tile = 2,
MULTI_LRC_MASK,
.needs_scratch = true,
@@ -487,6 +488,8 @@ static const struct xe_device_desc cri_desc = {
.has_sriov = true,
.has_sysctrl = true,
.has_temp_sentinel = true,
+ .late_bind_mask = BIT(XE_LB_FW_FAN_CONTROL) |
+ BIT(XE_LB_FW_OCODE),
.max_gt_per_tile = 2,
MULTI_LRC_MASK,
.require_force_probe = true,
@@ -722,8 +725,9 @@ static int handle_gmdid(struct xe_device *xe,
*graphics_ip = find_graphics_ip(ver);
if (!*graphics_ip) {
- drm_err(&xe->drm, "Hardware reports unknown graphics version %u.%02u\n",
- ver / 100, ver % 100);
+ xe_log_err(xe, REGS, -EINVAL,
+ "Hardware reports unknown graphics version %u.%02u\n",
+ ver / 100, ver % 100);
}
ret = read_gmdid(xe, GMDID_MEDIA, &ver, media_revid);
@@ -736,8 +740,9 @@ static int handle_gmdid(struct xe_device *xe,
*media_ip = find_media_ip(ver);
if (!*media_ip) {
- drm_err(&xe->drm, "Hardware reports unknown media version %u.%02u\n",
- ver / 100, ver % 100);
+ xe_log_err(xe, REGS, -EINVAL,
+ "Hardware reports unknown media version %u.%02u\n",
+ ver / 100, ver % 100);
}
return 0;
@@ -798,6 +803,7 @@ static int xe_info_init_early(struct xe_device *xe,
xe->info.has_device_uid = desc->has_device_uid;
xe->info.has_drm_ras = desc->has_drm_ras;
xe->info.has_fan_control = desc->has_fan_control;
+ xe->info.has_fixed_vram_channels = desc->has_fixed_vram_channels;
/* runtime fusing may force flat_ccs to disabled later */
xe->info.has_flat_ccs = desc->has_flat_ccs;
xe->info.has_mbx_power_limits = desc->has_mbx_power_limits;
@@ -806,7 +812,6 @@ static int xe_info_init_early(struct xe_device *xe,
xe->info.has_heci_gscfi = desc->has_heci_gscfi;
xe->info.has_heci_cscfi = desc->has_heci_cscfi;
xe->info.has_i2c = desc->has_i2c;
- xe->info.has_late_bind = desc->has_late_bind;
xe->info.has_llc = desc->has_llc;
xe->info.has_mert = desc->has_mert;
xe->info.has_page_reclaim_hw_assist = desc->has_page_reclaim_hw_assist;
@@ -824,6 +829,7 @@ static int xe_info_init_early(struct xe_device *xe,
xe->info.needs_scratch = desc->needs_scratch;
xe->info.needs_shared_vf_gt_wq = desc->needs_shared_vf_gt_wq;
xe->info.multi_lrc_mask = desc->multi_lrc_mask;
+ xe->info.late_bind_mask = desc->late_bind_mask;
xe->info.probe_display = IS_ENABLED(CONFIG_DRM_XE_DISPLAY) &&
xe_modparam.probe_display &&
diff --git a/drivers/gpu/drm/xe/xe_pci_error.c b/drivers/gpu/drm/xe/xe_pci_error.c
index a5c8fd67738cf..9bf0da3a3d152 100644
--- a/drivers/gpu/drm/xe/xe_pci_error.c
+++ b/drivers/gpu/drm/xe/xe_pci_error.c
@@ -13,6 +13,7 @@
#include "xe_printk.h"
#include "xe_ras.h"
#include "xe_survivability_mode.h"
+#include "xe_wedge.h"
static void prepare_device_for_reset(struct pci_dev *pdev)
{
@@ -22,7 +23,7 @@ static void prepare_device_for_reset(struct pci_dev *pdev)
/*
* Wedge the device to prevent userspace access but do not send the uevent.
- * xe_device_wedged_fini() releases runtime pm if wedged flag is set, so acquire a runtime
+ * xe_wedge_fini() releases runtime pm if wedged flag is set, so acquire a runtime
* pm reference to avoid underflow.
*/
if (!atomic_xchg(&xe->wedged.flag, 1))
@@ -31,7 +32,7 @@ static void prepare_device_for_reset(struct pci_dev *pdev)
xe_device_set_in_reset(xe);
for_each_gt(gt, xe, id) {
- xe_gt_declare_wedged(gt);
+ xe_gt_wedge_declare(gt);
flush_work(>->wedge.worker);
}
@@ -63,7 +64,7 @@ static pci_ers_result_t xe_pci_error_detected(struct pci_dev *pdev, pci_channel_
return PCI_ERS_RESULT_DISCONNECT;
/* If the device is already wedged or in survivability mode, do not attempt recovery */
- if (xe_survivability_mode_is_boot_enabled(xe) || xe_device_wedged(xe))
+ if (xe_survivability_mode_is_boot_enabled(xe) || xe_wedged(xe))
return PCI_ERS_RESULT_DISCONNECT;
switch (state) {
@@ -88,7 +89,7 @@ static pci_ers_result_t xe_pci_error_mmio_enabled(struct pci_dev *pdev)
/* User wants to debug the error, prevent reset */
if (xe->wedged.mode == XE_WEDGED_MODE_UPON_ANY_HANG_NO_RESET) {
- xe_device_declare_wedged(xe, DRM_WEDGE_RECOVERY_NONE);
+ xe_wedge_declare(xe, DRM_WEDGE_RECOVERY_NONE);
return PCI_ERS_RESULT_DISCONNECT;
}
diff --git a/drivers/gpu/drm/xe/xe_pm.c b/drivers/gpu/drm/xe/xe_pm.c
index d95f0d128ed49..61d68c09189f7 100644
--- a/drivers/gpu/drm/xe/xe_pm.c
+++ b/drivers/gpu/drm/xe/xe_pm.c
@@ -186,7 +186,7 @@ int xe_pm_suspend(struct xe_device *xe)
if (err)
goto err;
- xe_late_bind_wait_for_worker_completion(&xe->late_bind);
+ xe_late_bind_pm_suspend(&xe->late_bind);
for_each_gt(gt, xe, id)
xe_gt_suspend_prepare(gt);
@@ -249,7 +249,7 @@ int xe_pm_resume(struct xe_device *xe)
xe_wa_apply_tile_workarounds(tile);
for_each_gt(gt, xe, id)
- xe_gt_idle_disable_c6(gt);
+ xe_gt_resume_early(gt);
xe_display_pm_resume_early(xe);
@@ -290,7 +290,7 @@ int xe_pm_resume(struct xe_device *xe)
if (IS_VF_CCS_READY(xe))
xe_sriov_vf_ccs_register_context(xe);
- xe_late_bind_fw_load(&xe->late_bind);
+ xe_late_bind_pm_resume(&xe->late_bind);
drm_dbg(&xe->drm, "Device resumed\n");
xe_pm_block_end_signalling();
@@ -707,7 +707,7 @@ int xe_pm_runtime_resume(struct xe_device *xe)
xe_wa_apply_tile_workarounds(tile);
for_each_gt(gt, xe, id)
- xe_gt_idle_disable_c6(gt);
+ xe_gt_resume_early(gt);
xe_display_pm_resume_early(xe);
@@ -755,7 +755,7 @@ int xe_pm_runtime_resume(struct xe_device *xe)
xe_sriov_vf_ccs_register_context(xe);
if (xe->d3cold.allowed)
- xe_late_bind_fw_load(&xe->late_bind);
+ xe_late_bind_pm_resume(&xe->late_bind);
out:
xe_rpm_lockmap_release(xe);
diff --git a/drivers/gpu/drm/xe/xe_ras.c b/drivers/gpu/drm/xe/xe_ras.c
index fe16a41060607..2c01c183d02b1 100644
--- a/drivers/gpu/drm/xe/xe_ras.c
+++ b/drivers/gpu/drm/xe/xe_ras.c
@@ -3,7 +3,12 @@
* Copyright © 2026 Intel Corporation
*/
+#include <linux/bitfield.h>
+
+#include "xe_assert.h"
+#include "xe_bo.h"
#include "xe_configfs.h"
+#include "xe_cper.h"
#include "xe_debugfs.h"
#include "xe_device.h"
#include "xe_drm_ras.h"
@@ -16,6 +21,11 @@
#include "xe_sysctrl_event_types.h"
#include "xe_sysctrl_mailbox.h"
#include "xe_sysctrl_mailbox_types.h"
+#include "xe_ttm_vram_mgr.h"
+#include "xe_wedge.h"
+
+/* Any non-null entry marks the page as offlined by firmware */
+#define XE_RAS_PAGE_OFFLINED xa_mk_value(1)
#define CORE_COMPUTE_UNCORR_TYPE GENMASK(26, 25)
/*
@@ -103,7 +113,8 @@ static const char * const gpu_health_states[] = {
};
static_assert(ARRAY_SIZE(gpu_health_states) == XE_RAS_HEALTH_MAX);
-static int get_counter(struct xe_device *xe, struct xe_ras_error_class *counter, u32 *value);
+static int get_counter(struct xe_device *xe, struct xe_ras_error_class *counter,
+ u32 *value, bool emit_cper);
static u8 drm_to_xe_ras_severity(u8 severity)
{
@@ -129,6 +140,20 @@ static u8 xe_to_drm_ras_severity(u8 severity)
}
}
+static u8 ras_sev_to_cper_sev(u8 severity)
+{
+ switch (severity) {
+ case XE_RAS_SEV_CORRECTABLE:
+ return CPER_SEV_CORRECTED;
+ case XE_RAS_SEV_UNCORRECTABLE:
+ return CPER_SEV_RECOVERABLE;
+ case XE_RAS_SEV_INFORMATIONAL:
+ return CPER_SEV_INFORMATIONAL;
+ default:
+ return CPER_SEV_RECOVERABLE;
+ }
+}
+
static u8 drm_to_xe_ras_component(u8 component)
{
switch (component) {
@@ -165,6 +190,24 @@ static u8 xe_to_drm_ras_component(u8 component)
}
}
+static enum xe_sigid ras_comp_to_hw_sigid(u8 component)
+{
+ switch (component) {
+ case XE_RAS_COMP_DEVICE_MEMORY:
+ return XE_SIGID_DEVICE_MEMORY;
+ case XE_RAS_COMP_CORE_COMPUTE:
+ return XE_SIGID_CORE_COMPUTE;
+ case XE_RAS_COMP_PCIE:
+ return XE_SIGID_PCIE;
+ case XE_RAS_COMP_FABRIC:
+ return XE_SIGID_FABRIC;
+ case XE_RAS_COMP_SOC_INTERNAL:
+ return XE_SIGID_SOC_INTERNAL;
+ default:
+ return XE_SIGID_HW;
+ }
+}
+
static int ras_status_to_errno(u32 status)
{
switch (status) {
@@ -185,6 +228,12 @@ static int ras_status_to_errno(u32 status)
}
}
+static u16 ras_comp_to_log_comp(u8 component)
+{
+ return FIELD_PREP(XE_LOG_COMPONENT_CLASS_MASK, XE_LOG_COMPONENT_CLASS_HARDWARE) |
+ FIELD_PREP(XE_LOG_COMPONENT_TYPE_MASK, component);
+}
+
static inline const char *sev_to_str(u8 severity)
{
if (severity >= XE_RAS_SEV_MAX)
@@ -201,6 +250,142 @@ static inline const char *comp_to_str(u8 component)
return xe_ras_components[component];
}
+static int send_page_offline_cmd(struct xe_device *xe, u64 page_address,
+ enum xe_ras_page_action action)
+{
+ struct xe_sysctrl_mailbox_command command = {0};
+ struct xe_ras_page_offline_request request = {0};
+ struct xe_ras_page_offline_response response = {0};
+ struct xe_ras_state *state = &xe->ras.state;
+ size_t rlen;
+ int ret;
+
+ if (!xe->info.has_sysctrl)
+ return 0;
+
+ xe_assert(xe, action < XE_RAS_PAGE_ACTION_MAX);
+
+ request.page_address = page_address;
+ request.action = action;
+
+ if (action == XE_RAS_PAGE_ACTION_OFFLINE)
+ xe_log_err(xe, DEVICE_MEMORY, 0, "Requesting firmware to offline page 0x%llx\n",
+ page_address);
+ else
+ xe_log_err(xe, DEVICE_MEMORY, 0, "Requesting firmware to remove page 0x%llx from queue\n",
+ page_address);
+
+ xe_sysctrl_create_command(&command, XE_SYSCTRL_GROUP_GFSP, XE_SYSCTRL_CMD_PAGE_OFFLINE,
+ &request, sizeof(request), &response, sizeof(response));
+
+ ret = xe_sysctrl_send_command(&xe->sc, &command, &rlen);
+ if (ret) {
+ xe_log_err(xe, SYSCTRL, ret, "failed to send page offline command\n");
+ return ret;
+ }
+
+ if (rlen != sizeof(response)) {
+ xe_log_err(xe, SYSCTRL, -EINVAL,
+ "unexpected page offline response length %zu (expected %zu)\n",
+ rlen, sizeof(response));
+ return -EINVAL;
+ }
+
+ ret = ras_status_to_errno(response.status);
+ if (ret) {
+ xe_log_err(xe, SYSCTRL, ret, "page offline command failed with status %u\n",
+ response.status);
+ return ret;
+ }
+
+ if (action == XE_RAS_PAGE_ACTION_OFFLINE) {
+ ret = xa_err(xa_store(&state->offlined_pages,
+ page_address >> XE_PTE_SHIFT,
+ XE_RAS_PAGE_OFFLINED, GFP_KERNEL));
+ if (ret)
+ xe_err(xe, "Failed to store offlined page 0x%llx: %d\n", page_address,
+ ret);
+ }
+
+ return 0;
+}
+
+static int handle_page_offline(struct xe_device *xe, u64 page_address, bool notify_fw)
+{
+ struct xe_ras_state *state = &xe->ras.state;
+ enum xe_ras_page_action action;
+ u64 addr;
+ int ret;
+
+ if (!IS_ALIGNED(page_address, XE_PAGE_SIZE)) {
+ xe_log_err(xe, SYSCTRL, -EINVAL, "Unaligned physical page address: 0x%llx\n",
+ page_address);
+ return -EINVAL;
+ }
+
+ /* User policy set to disable page offlining */
+ if (xe_ras_get_disable_vram_page_offline(xe)) {
+ action = XE_RAS_PAGE_ACTION_REMOVE;
+ xe_log_err(xe, DEVICE_MEMORY, -EOPNOTSUPP, "User policy set to disable page offlining\n");
+ goto notify;
+ }
+
+ addr = ALIGN_DOWN(page_address, PAGE_SIZE);
+
+ ret = xe_ttm_vram_handle_addr_fault(xe, addr);
+
+ /*
+ * Handle return code from address fault handling function:
+ * 0: Page softofflined, remove from firmware queue
+ * -EIO: Address belongs to a critical BO/stolen area that cannot be offlined
+ * -ENOMEM: Failed to allocate BO for soft-offlining the page
+ * -EEXIST: Address is soft offlined but yet to be offlined by firmware for second
+ * occurrence
+ */
+
+ switch (ret) {
+ case 0:
+ action = XE_RAS_PAGE_ACTION_REMOVE;
+ xe_log_err(xe, DEVICE_MEMORY, ret,
+ "Poison detected at physical address 0x%llx, page soft-offlined\n",
+ page_address);
+ break;
+ case -EIO:
+ xe_log_err(xe, DEVICE_MEMORY, ret,
+ "Poison detected at physical address 0x%llx, page belongs to critical BO and cannot be soft-offlined\n",
+ page_address);
+ return ret;
+ case -EEXIST:
+ if (xa_load(&state->offlined_pages, page_address >> XE_PTE_SHIFT))
+ action = XE_RAS_PAGE_ACTION_REMOVE;
+ else
+ action = XE_RAS_PAGE_ACTION_OFFLINE;
+
+ xe_log_err(xe, DEVICE_MEMORY, ret,
+ "Double-bit ECC error detected at physical address 0x%llx, page soft-offlined\n",
+ page_address);
+ break;
+ case -ENOMEM:
+ xe_log_err(xe, DEVICE_MEMORY, ret,
+ "Poison detected at physical address 0x%llx, failed to soft-offline\n",
+ page_address);
+ return ret;
+ default:
+ xe_log_err(xe, DEVICE_MEMORY, ret, "Failed to handle address fault at physical address 0x%llx\n",
+ page_address);
+ return 0;
+ }
+
+ notify:
+ if (notify_fw) {
+ ret = send_page_offline_cmd(xe, page_address, action);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
static bool ras_counter_is_valid(struct xe_device *xe, struct xe_ras_error_class *counter)
{
u8 severity = counter->common.severity;
@@ -219,6 +404,106 @@ static bool ras_counter_is_valid(struct xe_device *xe, struct xe_ras_error_class
return true;
}
+/*
+ * Fetch and process offline list and queue from firmware via mailbox. @notify_fw controls
+ * whether firmware is notified of the offline/remove action.
+ */
+static void process_offline_pages(struct xe_device *xe, u32 cmd, void *req, size_t req_size,
+ void *resp, size_t resp_size,
+ struct xe_ras_offline_common *common, bool notify_fw)
+{
+ struct xe_sysctrl_mailbox_command command = {0};
+ struct xe_ras_offline_list_request *list_req;
+ struct xe_ras_state *state = &xe->ras.state;
+ unsigned long index;
+ u32 total_pages = 0, count = 0;
+ size_t rlen;
+ int ret, i;
+
+ list_req = req;
+
+ xe_sysctrl_create_command(&command, XE_SYSCTRL_GROUP_GFSP, cmd, req, req_size, resp,
+ resp_size);
+
+ do {
+ memset(resp, 0, resp_size);
+
+ if (list_req)
+ list_req->index = count;
+
+ ret = xe_sysctrl_send_command(&xe->sc, &command, &rlen);
+ if (ret) {
+ xe_log_err(xe, SYSCTRL, ret, "failed to get page offline data, cmd=%#x\n",
+ cmd);
+ return;
+ }
+
+ if (rlen != resp_size) {
+ xe_log_err(xe, SYSCTRL, -EINVAL,
+ "unexpected page offline response length %zu (expected %zu), cmd=%#x\n",
+ rlen, resp_size, cmd);
+ return;
+ }
+
+ /* Populate the maximum number of pages from offline list */
+ if (list_req && !state->max_pages)
+ state->max_pages =
+ ((struct xe_ras_offline_list_response *)resp)->max_entries;
+
+ for (i = 0; i < common->pages_returned && i < XE_RAS_NUM_PAGES; i++) {
+ handle_page_offline(xe, common->page_addresses[i], notify_fw);
+ /* The pages are already offlined by firmware */
+ if (!notify_fw) {
+ index = common->page_addresses[i] >> XE_PTE_SHIFT;
+ ret = xa_err(xa_store(&state->offlined_pages, index,
+ XE_RAS_PAGE_OFFLINED, GFP_KERNEL));
+ if (ret)
+ xe_err(xe, "Failed to store offlined page 0x%llx: %d\n",
+ common->page_addresses[i], ret);
+ }
+ }
+
+ count += common->pages_returned;
+ if (!common->pages_returned)
+ break;
+
+ if (!total_pages)
+ total_pages = common->total_pages;
+
+ if (count > total_pages) {
+ xe_log_err(xe, SYSCTRL, -EINVAL,
+ "Pages returned exceed total pages %u, returned %u, cmd=%#x\n",
+ total_pages, count, cmd);
+ return;
+ }
+ } while (common->additional_data);
+}
+
+/*
+ * Retrieve firmware queued pages from warm-retained SRAM during probe. Handle and soft-offline
+ * each page as required.
+ */
+static void get_offline_queue(struct xe_device *xe)
+{
+ struct xe_ras_offline_common response = {0};
+
+ process_offline_pages(xe, XE_SYSCTRL_CMD_GET_OFFLINE_QUEUE, NULL, 0, &response,
+ sizeof(response), &response, true);
+}
+
+/*
+ * Retrieve permanently offlined pages from firmware's non-volatile storage during probe,
+ * allowing the driver to soft-offline them before kernel allocations begin.
+ */
+static void get_offline_list(struct xe_device *xe)
+{
+ struct xe_ras_offline_list_response response = {0};
+ struct xe_ras_offline_list_request request = {0};
+
+ process_offline_pages(xe, XE_SYSCTRL_CMD_GET_OFFLINE_LIST, &request, sizeof(request),
+ &response, sizeof(response), &response.common, false);
+}
+
static struct pci_dev *find_usp_dev(struct pci_dev *pdev)
{
struct pci_dev *vsp;
@@ -291,7 +576,7 @@ static void ras_send_error_event(struct xe_device *xe, u8 severity, u8 component
counter.common.severity = severity;
counter.common.component = component;
- ret = get_counter(xe, &counter, &value);
+ ret = get_counter(xe, &counter, &value, false);
if (ret)
return;
@@ -301,13 +586,17 @@ static void ras_send_error_event(struct xe_device *xe, u8 severity, u8 component
xe_drm_ras_event(xe, drm_component, drm_severity, value);
}
-static u8 handle_core_compute_errors(struct xe_ras_error_array *arr)
+static u8 handle_core_compute_errors(struct xe_device *xe, struct xe_ras_error_array *arr)
{
struct xe_ras_compute_error *error_info = (void *)arr->details;
+ u8 cper_sev = ras_sev_to_cper_sev(arr->counter.common.severity);
u8 uncorr_type;
uncorr_type = FIELD_GET(CORE_COMPUTE_UNCORR_TYPE, error_info->log_header);
+ xe_log_comp(xe, cper_sev, CORE_COMPUTE, &arr->counter, sizeof(arr->counter),
+ "Uncorrectable error detected, type %u\n", uncorr_type);
+
/* Request a reset if error is global */
if (uncorr_type == GLOBAL_UNCORR_ERROR)
return XE_RAS_RECOVERY_ACTION_RESET;
@@ -321,14 +610,16 @@ static u8 handle_core_compute_errors(struct xe_ras_error_array *arr)
static void punit_error_handler(struct xe_device *xe)
{
- xe_device_declare_wedged(xe, DRM_WEDGE_RECOVERY_COLD_RESET);
+ xe_wedge_declare(xe, DRM_WEDGE_RECOVERY_COLD_RESET);
}
static u8 handle_soc_internal_errors(struct xe_device *xe, struct xe_ras_error_array *arr)
{
struct xe_ras_soc_error *info = (void *)arr->details;
struct xe_ras_soc_error_source *source = &info->source;
- struct xe_ras_error_class *counter = &arr->counter;
+ u32 raw_source;
+
+ BUILD_BUG_ON(sizeof(raw_source) != sizeof(*source));
if (source->csc) {
struct xe_ras_csc_error *csc_error = (void *)info->details;
@@ -342,9 +633,8 @@ static u8 handle_soc_internal_errors(struct xe_device *xe, struct xe_ras_error_a
* is required.
*/
if (csc_error->hec_fw_error) {
- xe_err(xe, "[RAS]: CSC %s detected: 0x%x\n",
- sev_to_str(counter->common.severity),
- csc_error->hec_fw_error);
+ xe_log_comp_fatal(xe, SOC_INTERNAL, &arr->counter, sizeof(arr->counter),
+ "CSC error detected: 0x%08x\n", csc_error->hec_fw_error);
xe_survivability_mode_runtime_enable(xe);
return XE_RAS_RECOVERY_ACTION_DISCONNECT;
}
@@ -352,42 +642,56 @@ static u8 handle_soc_internal_errors(struct xe_device *xe, struct xe_ras_error_a
struct xe_ras_ieh_error *ieh_error = (void *)info->details;
if (ieh_error->global_error_status & XE_RAS_SOC_IEH_PUNIT) {
- xe_err(xe, "[RAS]: PUNIT %s detected: 0x%x\n",
- sev_to_str(counter->common.severity),
- ieh_error->global_error_status);
+ xe_log_comp_fatal(xe, SOC_INTERNAL, &arr->counter, sizeof(arr->counter),
+ "PUNIT error detected: 0x%08x\n",
+ ieh_error->global_error_status);
punit_error_handler(xe);
return XE_RAS_RECOVERY_ACTION_DISCONNECT;
}
}
/* For other SoC internal errors, request a reset as recovery mechanism */
+ memcpy(&raw_source, source, sizeof(raw_source));
+ xe_log_comp(xe, ras_sev_to_cper_sev(arr->counter.common.severity), SOC_INTERNAL,
+ &arr->counter, sizeof(arr->counter),
+ "Unknown error detected: 0x%08x\n", raw_source);
return XE_RAS_RECOVERY_ACTION_RESET;
}
static u8 handle_device_memory_errors(struct xe_device *xe, struct xe_ras_error_array *arr)
{
struct xe_ras_memory_error *info = (void *)arr->details;
+ u8 cper_sev = ras_sev_to_cper_sev(arr->counter.common.severity);
+ int ret;
/*
* For memory errors, the recovery action depends on the error category
*
- * TODO: Double-bit ECC errors: Page offlining
+ * Double-bit ECC errors: Page offlining
* Poison and data parity errors: Log only
* For any other memory errors, request a reset as recovery mechanism
*/
switch (info->category) {
case XE_RAS_MEMORY_POISON:
- xe_info(xe, "[RAS]: Poison error detected\n");
+ xe_log_comp_info(xe, DEVICE_MEMORY, &arr->counter, sizeof(arr->counter),
+ "Poison error detected\n");
break;
case XE_RAS_MEMORY_DATA_PARITY:
- xe_info(xe, "[RAS]: Data parity error detected\n");
+ xe_log_comp_info(xe, DEVICE_MEMORY, &arr->counter, sizeof(arr->counter),
+ "Data parity error detected\n");
break;
case XE_RAS_MEMORY_DB_ECC:
- xe_info(xe, "[RAS]: Double-bit ECC error detected at sw address 0x%llx\n",
- info->sw_address);
- /* TODO: Add page offlining for Double-bit ECC error */
- fallthrough;
+ ret = handle_page_offline(xe, info->sw_address, true);
+ xe_log_comp(xe, ret ? cper_sev : CPER_SEV_INFORMATIONAL,
+ DEVICE_MEMORY, &arr->counter, sizeof(arr->counter),
+ "Double-bit ECC error detected at sw address 0x%llx\n",
+ info->sw_address);
+ if (ret)
+ return XE_RAS_RECOVERY_ACTION_RESET;
+ break;
default:
+ xe_log_comp(xe, cper_sev, DEVICE_MEMORY, &arr->counter, sizeof(arr->counter),
+ "Unknown error detected\n");
return XE_RAS_RECOVERY_ACTION_RESET;
}
@@ -420,8 +724,11 @@ void xe_ras_counter_threshold_crossed(struct xe_device *xe,
if (!ras_counter_is_valid(xe, &errors[id]))
continue;
- xe_warn(xe, "[RAS]: %s %s detected\n",
- comp_to_str(component), sev_to_str(severity));
+ xe_log_from(xe, ras_sev_to_cper_sev(severity),
+ ras_comp_to_hw_sigid(component),
+ ras_comp_to_log_comp(component),
+ &errors[id], sizeof(errors[id]),
+ "error detected\n");
/* Send event once per component */
if (sent & BIT(component))
@@ -432,9 +739,37 @@ void xe_ras_counter_threshold_crossed(struct xe_device *xe,
}
}
-static int get_counter(struct xe_device *xe, struct xe_ras_error_class *counter, u32 *value)
+static int get_counter(struct xe_device *xe, struct xe_ras_error_class *counter,
+ u32 *value, bool emit_cper)
{
struct xe_ras_get_counter_response response = {0};
+ int ret;
+
+ ret = xe_ras_get_counter_response(xe, counter, &response);
+ if (ret)
+ return ret;
+
+ *value = response.value;
+ if (emit_cper)
+ xe_cper_emit_counter(xe, ras_sev_to_cper_sev(counter->common.severity),
+ ras_comp_to_hw_sigid(counter->common.component),
+ counter, &response);
+ return 0;
+}
+
+/**
+ * xe_ras_get_counter_response() - Get error counter record
+ * @xe: Xe device instance
+ * @counter: RAS error class
+ * @response: Counter record retrieved
+ *
+ * Retrieve the counter record of a specific error class from firmware.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int xe_ras_get_counter_response(struct xe_device *xe, const struct xe_ras_error_class *counter,
+ struct xe_ras_get_counter_response *response)
+{
struct xe_ras_get_counter_request request = {0};
struct xe_sysctrl_mailbox_command command = {0};
struct xe_ras_error_common *common;
@@ -444,7 +779,7 @@ static int get_counter(struct xe_device *xe, struct xe_ras_error_class *counter,
request.counter = *counter;
xe_sysctrl_create_command(&command, XE_SYSCTRL_GROUP_GFSP, XE_SYSCTRL_CMD_GET_COUNTER,
- &request, sizeof(request), &response, sizeof(response));
+ &request, sizeof(request), response, sizeof(*response));
ret = xe_sysctrl_send_command(&xe->sc, &command, &rlen);
if (ret) {
@@ -452,20 +787,19 @@ static int get_counter(struct xe_device *xe, struct xe_ras_error_class *counter,
return ret;
}
- if (rlen != sizeof(response)) {
+ if (rlen != sizeof(*response)) {
xe_err(xe, "sysctrl: unexpected get counter response length %zu (expected %zu)\n",
- rlen, sizeof(response));
+ rlen, sizeof(*response));
return -EIO;
}
- if (!ras_counter_is_valid(xe, &response.counter))
+ if (!ras_counter_is_valid(xe, &response->counter))
return -EBADMSG;
- common = &response.counter.common;
- *value = response.value;
+ common = &response->counter.common;
- xe_dbg(xe, "[RAS]: get counter %u for %s %s\n", *value, comp_to_str(common->component),
- sev_to_str(common->severity));
+ xe_dbg(xe, "[RAS]: get counter %u for %s %s\n", response->value,
+ comp_to_str(common->component), sev_to_str(common->severity));
return 0;
}
@@ -547,7 +881,7 @@ enum xe_ras_recovery_action xe_ras_process_errors(struct xe_device *xe)
switch (component) {
case XE_RAS_COMP_CORE_COMPUTE:
- action = handle_core_compute_errors(arr);
+ action = handle_core_compute_errors(xe, arr);
break;
case XE_RAS_COMP_SOC_INTERNAL:
action = handle_soc_internal_errors(xe, arr);
@@ -600,7 +934,7 @@ int xe_ras_get_counter(struct xe_device *xe, u8 severity, u8 component, u32 *val
counter.common.component = drm_to_xe_ras_component(component);
guard(xe_pm_runtime)(xe);
- return get_counter(xe, &counter, value);
+ return get_counter(xe, &counter, value, READ_ONCE(xe->cper.on_query));
}
/**
@@ -905,33 +1239,83 @@ static const struct attribute_group gpu_health_group = {
.attrs = gpu_health_attrs,
};
+static void ras_fini(void *arg)
+{
+ struct xe_device *xe = arg;
+
+ xa_destroy(&xe->ras.state.offlined_pages);
+}
+
+/**
+ * xe_ras_get_disable_vram_page_offline - Get the vram page offline user policy
+ * @xe: xe device instance
+ *
+ * Return: true if the vram page offline policy is disabled, false otherwise.
+ */
+bool xe_ras_get_disable_vram_page_offline(struct xe_device *xe)
+{
+ return xe->ras.state.disable_vram_page_offline;
+}
+
+/**
+ * xe_ras_get_max_pages - Get the maximum number of pages
+ * @xe: xe device instance
+ *
+ * Return: Maximum number of pages stored by the firmware in flash
+ */
+u32 xe_ras_get_max_pages(struct xe_device *xe)
+{
+ return xe->ras.state.max_pages;
+}
+
/**
* xe_ras_init - Initialize Xe RAS
* @xe: xe device instance
*
* Initialize Xe RAS
+ *
+ * Return: 0 on success, negative error code otherwise
*/
-void xe_ras_init(struct xe_device *xe)
+int xe_ras_init(struct xe_device *xe)
{
+ struct xe_ras_state *state = &xe->ras.state;
int ret;
- /*
- * TODO: Replace platform check with xe->info.has_disable_vram_page_offline
- * once the feature flag is plumbed through device info.
- */
- if (xe->info.platform == XE_CRESCENTISLAND)
- xe->ras.disable_vram_page_offline =
- xe_configfs_get_disable_vram_page_offline(to_pci_dev(xe->drm.dev));
-
xe_drm_ras_init(xe);
if (!xe->info.has_sysctrl)
- return;
+ return 0;
+
+ /*
+ * TODO: Replace with xe->info.has_disable_vram_page_offline once the feature flag is
+ * plumbed through device info.
+ */
+ xe->ras.state.disable_vram_page_offline =
+ xe_configfs_get_disable_vram_page_offline(to_pci_dev(xe->drm.dev));
if (IS_ENABLED(CONFIG_PCIEAER))
ras_usp_aer_init(xe);
+ xa_init(&state->offlined_pages);
+
+ ret = devm_add_action_or_reset(xe->drm.dev, ras_fini, xe);
+ if (ret)
+ return ret;
+
+ /* Fetch the offline list before queue to avoid duplication */
+ get_offline_list(xe);
+ get_offline_queue(xe);
+
+ /*
+ * During probe, process and log any errors detected by firmware while the driver was not
+ * loaded. Critical errors such as Punit and CSC are reported through Pcode init failure,
+ * causing the driver to enter survivability mode.
+ */
+ xe_ras_process_errors(xe);
+
ret = devm_device_add_group(xe->drm.dev, &gpu_health_group);
if (ret)
- xe_err(xe, "Failed to create GPU health sysfs, err=%d\n", ret);
+ return ret;
+
+ return xe_cper_sysfs_init(xe);
}
diff --git a/drivers/gpu/drm/xe/xe_survivability_mode.c b/drivers/gpu/drm/xe/xe_survivability_mode.c
index ca031576fecb7..d73cdad0b3a9c 100644
--- a/drivers/gpu/drm/xe/xe_survivability_mode.c
+++ b/drivers/gpu/drm/xe/xe_survivability_mode.c
@@ -20,6 +20,7 @@
#include "xe_pcode_api.h"
#include "xe_printk.h"
#include "xe_vsec.h"
+#include "xe_wedge.h"
/**
* DOC: Survivability Mode
@@ -446,7 +447,7 @@ void xe_survivability_mode_runtime_enable(struct xe_device *xe)
survivability->type = XE_SURVIVABILITY_TYPE_RUNTIME;
xe_log_err(xe, SURVIVABILITY, 0, "Runtime Mode enabled!\n");
- xe_device_declare_wedged(xe, DRM_WEDGE_RECOVERY_VENDOR);
+ xe_wedge_declare(xe, DRM_WEDGE_RECOVERY_VENDOR);
xe_log_err(xe, SURVIVABILITY, 0, "Firmware flash required!\n");
xe_info(xe, "Please refer to the userspace documentation for more details how to flash the firmware on %s!\n",
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 48+ messages in thread* linux-next: manual merge of the drm tree with the drm-fixes tree
@ 2026-09-18 11:14 Mark Brown
0 siblings, 0 replies; 48+ messages in thread
From: Mark Brown @ 2026-09-18 11:14 UTC (permalink / raw)
To: Dave Airlie, DRI
Cc: Alex Deucher, Chenyu Chen, Leo Li, Linux Kernel Mailing List,
Linux Next Mailing List
[-- Attachment #1: Type: text/plain, Size: 1402 bytes --]
Hi all,
Today's linux-next merge of the drm tree got a conflict in:
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c
between commit:
63e19ef3ddab8 ("drm/amd/display: Atomize IRQ register read/modify/write ops")
from the drm-fixes tree and commit:
63e19ef3ddab8 ("drm/amd/display: Atomize IRQ register read/modify/write ops")
from the drm tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
diff --cc drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c
index 74a8735168aaa,f997a50c1293e..0000000000000
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c
@@@ -728,7 -713,12 +731,12 @@@ STATIC_IFN_KUNIT int amdgpu_dm_irq_hand
entry->src_id,
entry->src_data[0]);
+ if (!DAL_VALID_IRQ_SRC_NUM(src)) {
+ DRM_ERROR("DM_IRQ: invalid irq_source:%d!\n", src);
+ return 0;
+ }
+
- dc_interrupt_ack(adev->dm.dc, src);
+ amdgpu_dm_irq_ack(adev, src);
/* Call high irq work immediately */
amdgpu_dm_irq_immediate_work(adev, src);
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 48+ messages in thread* linux-next: manual merge of the drm tree with the drm-fixes tree
@ 2026-09-11 13:21 Mark Brown
0 siblings, 0 replies; 48+ messages in thread
From: Mark Brown @ 2026-09-11 13:21 UTC (permalink / raw)
To: Dave Airlie, DRI
Cc: Linux Kernel Mailing List, Linux Next Mailing List,
Matthew Brost, Rodrigo Vivi, Tejas Upadhyay, Varun Gupta
[-- Attachment #1: Type: text/plain, Size: 2723 bytes --]
Hi all,
Today's linux-next merge of the drm tree got a conflict in:
drivers/gpu/drm/xe/xe_pagefault.c
between commit:
20fce5b34b21a ("drm/xe: Guard page-fault worker with runtime PM check")
from the drm-fixes tree and commit:
f2b4b3f814662 ("drm/xe: Chain page faults via queue-resident cache to avoid fault storms")
from the drm tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
diff --cc drivers/gpu/drm/xe/xe_pagefault.c
index a4986df8328d6,2e415995f067b..0000000000000
--- a/drivers/gpu/drm/xe/xe_pagefault.c
+++ b/drivers/gpu/drm/xe/xe_pagefault.c
@@@ -14,9 -14,9 +14,10 @@@
#include "xe_gt_types.h"
#include "xe_gt_stats.h"
#include "xe_hw_engine.h"
+ #include "xe_log.h"
#include "xe_pagefault.h"
#include "xe_pagefault_types.h"
+#include "xe_pm.h"
#include "xe_svm.h"
#include "xe_trace_bo.h"
#include "xe_vm.h"
@@@ -291,19 -594,16 +595,23 @@@ static void xe_pagefault_save_to_vm(str
static void xe_pagefault_queue_work(struct work_struct *w)
{
- struct xe_pagefault_queue *pf_queue =
- container_of(w, typeof(*pf_queue), worker);
- struct xe_device *xe = pf_queue->xe;
- struct xe_pagefault pf;
+ struct xe_pagefault_work *pf_work =
+ container_of(w, typeof(*pf_work), work);
+ struct xe_device *xe = pf_work->xe;
+ struct xe_pagefault_queue *pf_queue = &xe->usm.pf_queue;
+ struct xe_pagefault *pf;
+ ktime_t start = xe_gt_stats_ktime_get();
unsigned long threshold;
+ u64 cache_start = XE_PAGEFAULT_CACHE_START_INVALID, cache_end = 0;
+ u32 cache_asid = 0;
+ /*
+ * A live VM holds a PM reference, but a torn-down VM does not.
+ * Guard the entire worker loop to safely drain stale faults and
+ * prevent autosuspends from desyncing batched CT flushes.
+ */
+ guard(xe_pm_runtime)(xe);
+
#define USM_QUEUE_MAX_RUNTIME_MS 20
threshold = jiffies + msecs_to_jiffies(USM_QUEUE_MAX_RUNTIME_MS);
@@@ -374,9 -731,7 +739,8 @@@ static int xe_pagefault_queue_init(stru
drm_dbg(&xe->drm, "xe_pagefault_entry_size=%d, total_num_eus=%d, pf_queue->size=%u",
xe_pagefault_entry_size(), total_num_eus, pf_queue->size);
+ pf_queue->xe = xe;
spin_lock_init(&pf_queue->lock);
- INIT_WORK(&pf_queue->worker, xe_pagefault_queue_work);
pf_queue->data = drmm_kzalloc(&xe->drm, pf_queue->size, GFP_KERNEL);
if (!pf_queue->data)
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 48+ messages in thread* linux-next: manual merge of the drm tree with the drm-fixes tree
@ 2026-08-07 14:42 Mark Brown
0 siblings, 0 replies; 48+ messages in thread
From: Mark Brown @ 2026-08-07 14:42 UTC (permalink / raw)
To: Dave Airlie, DRI
Cc: Alex Deucher, Fangzhi Zuo, Linux Kernel Mailing List,
Linux Next Mailing List, Ray Wu
[-- Attachment #1: Type: text/plain, Size: 4509 bytes --]
Hi all,
Today's linux-next merge of the drm tree got a conflict in:
drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c
between commits:
3141e3d61469b ("drm/amd/display: Check for tg ops in dce110_set_avmute")
443290d70b01e ("drm/amd/display: Add AV mute wait frames to dce110_set_avmute")
from the drm-fixes tree and commit:
414da24137ace ("drm/amd/display: Add AV mute wait frames to dce110_set_avmute")
from the drm tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
This is a pretty clear example of the sorts of issues the constant
cherry picking causes.
diff --combined drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c
index f5b3a2eff94a0,f91ce2b4b1bfa..0000000000000
--- a/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c
@@@ -607,11 -607,10 +607,10 @@@ dce110_translate_regamma_to_hw_format(c
}
static bool
- dce110_set_output_transfer_func(struct dc *dc, struct pipe_ctx *pipe_ctx,
- const struct dc_stream_state *stream)
+ dce110_set_output_transfer_func(struct set_output_transfer_func_params *params)
{
- (void)dc;
- struct transform *xfm = pipe_ctx->plane_res.xfm;
+ struct transform *xfm = params->xfm;
+ const struct dc_stream_state *stream = params->stream;
xfm->funcs->opp_power_on_regamma_lut(xfm, true);
xfm->regamma_params.hw_points_num = GAMMA_HW_POINTS_NUM;
@@@ -1348,10 -1347,7 +1347,7 @@@ void dce110_set_avmute(struct pipe_ctx
* process the mute state, especially after link re-establishment
* with HDMI 2.0 scrambling enabled.
*/
- if (enable && pipe_ctx->stream_res.tg &&
- pipe_ctx->stream_res.tg->funcs->is_tg_enabled &&
- pipe_ctx->stream_res.tg->funcs->wait_for_state &&
- pipe_ctx->stream_res.tg->funcs->is_tg_enabled(pipe_ctx->stream_res.tg)) {
+ if (enable && pipe_ctx->stream_res.tg->funcs->is_tg_enabled(pipe_ctx->stream_res.tg)) {
int i;
pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE);
@@@ -2826,23 -2822,26 +2822,26 @@@ static void program_surface_visibility(
}
- static void program_gamut_remap(struct pipe_ctx *pipe_ctx)
+ static void program_gamut_remap(struct program_gamut_remap_params *params)
{
+ struct transform *xfm = params->xfm;
+ const struct dc_stream_state *stream = params->stream;
int i = 0;
struct xfm_grph_csc_adjustment adjust;
+
memset(&adjust, 0, sizeof(adjust));
adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_BYPASS;
- if (pipe_ctx->stream->gamut_remap_matrix.enable_remap == true) {
+ if (stream->gamut_remap_matrix.enable_remap == true) {
adjust.gamut_adjust_type = GRAPHICS_GAMUT_ADJUST_TYPE_SW;
for (i = 0; i < CSC_TEMPERATURE_MATRIX_SIZE; i++)
adjust.temperature_matrix[i] =
- pipe_ctx->stream->gamut_remap_matrix.matrix[i];
+ stream->gamut_remap_matrix.matrix[i];
}
- pipe_ctx->plane_res.xfm->funcs->transform_set_gamut_remap(pipe_ctx->plane_res.xfm, &adjust);
+ xfm->funcs->transform_set_gamut_remap(xfm, &adjust);
}
static void update_plane_addr(const struct dc *dc,
struct pipe_ctx *pipe_ctx)
@@@ -3188,13 -3187,13 +3187,13 @@@ static void dce110_program_front_end_fo
plane_state->rotation);
/* Moved programming gamma from dc to hwss */
- if (pipe_ctx->plane_state->update_flags.bits.full_update ||
- pipe_ctx->plane_state->update_flags.bits.in_transfer_func_change ||
- pipe_ctx->plane_state->update_flags.bits.gamma_change)
+ if (pipe_ctx->plane_state->update_bits.full_update ||
+ pipe_ctx->plane_state->update_bits.in_transfer_func_change ||
+ pipe_ctx->plane_state->update_bits.gamma_change)
hws->funcs.set_input_transfer_func(dc, pipe_ctx, pipe_ctx->plane_state);
- if (pipe_ctx->plane_state->update_flags.bits.full_update)
- hws->funcs.set_output_transfer_func(dc, pipe_ctx, pipe_ctx->stream);
+ if (pipe_ctx->plane_state->update_bits.full_update)
+ hwss_set_output_transfer_func(dc, pipe_ctx);
DC_LOG_SURFACE(
"Pipe:%d %p: addr hi:0x%x, "
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 48+ messages in thread* linux-next: manual merge of the drm tree with the drm-fixes tree
@ 2026-07-31 14:29 Mark Brown
0 siblings, 0 replies; 48+ messages in thread
From: Mark Brown @ 2026-07-31 14:29 UTC (permalink / raw)
To: Dave Airlie, DRI
Cc: Alessandro Rinaldi, Alex Deucher, Alex Hung, Fangzhi Zuo,
George Zhang, Leo Li, Linux Kernel Mailing List,
Linux Next Mailing List, Mario Limonciello, Matthew Schwartz,
Timur Kristóf, WenTao Liang
[-- Attachment #1: Type: text/plain, Size: 318194 bytes --]
Hi all,
Today's linux-next merge of the drm tree got a conflict in:
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
between commits:
8419331e64d92 ("drm/amd/display: Exit idle optimizations before programming")
fbbaca9e20873 ("drm/amd/display: Fix missing DCE check in dm_gpureset_toggle_interrupts()")
f39283eab44fb ("Revert "drm/amd/display: Restore 5s vbl offdelay for NV3x+ DGPUs"")
48ab86360af11 ("drm/amd/display: check GRPH_FLIP status before sending event")
8382cd234981a ("drm/amd/display: consolidate DCN vblank/flip handling onto vupdate_no_lock")
9fa26b9eed619 ("drm/amd/display: set new_stream to NULL after release")
bad177fa75e60 ("drm/amd/display: Force PWM backlight on Lenovo Legion 5 15ARH05")
cea54c52d82dd ("drm/amd/display: Set native cursor mode for disabled CRTCs")
f1b5d8f9cc54a ("drm/amd/display: Fix backlight max_brightness to match exported range")
from the drm-fixes tree and commits:
66517ca0cd8bb ("drm/amd/display: move GPU mem helpers to services")
0b9bd593f0a2c ("drm/amd/display: move watermarks table to pp_smu")
e278e0bf3ed30 ("drm/amd/display: move stutter quirk to quirks file")
a648ed2bfafab ("drm/amd/display: move scaling helper to connector")
459e61b53910b ("drm/amd/display: Fix writeback completion timing")
from the drm tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
diff --combined drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index 1820547b1dde8,f22388e57e989..0000000000000
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@@ -66,6 -66,13 +66,13 @@@
#endif
#include "amdgpu_dm_psr.h"
#include "amdgpu_dm_replay.h"
+ #include "amdgpu_dm_backlight.h"
+ #include "amdgpu_dm_audio.h"
+ #include "amdgpu_dm_dmub.h"
+ #include "amdgpu_dm_connector.h"
+ #include "amdgpu_dm_pp_smu.h"
+ #include "amdgpu_dm_freesync.h"
+ #include "amdgpu_dm_cursor.h"
#include "ivsrcid/ivsrcid_vislands30.h"
@@@ -94,7 -101,6 +101,6 @@@
#include <drm/drm_mode.h>
#include <drm/drm_utils.h>
#include <drm/drm_vblank.h>
- #include <drm/drm_audio_component.h>
#include <drm/drm_colorop.h>
#include <drm/drm_gem_atomic_helper.h>
@@@ -107,60 -113,9 +113,9 @@@
#include "modules/inc/mod_power.h"
#include "modules/power/power_helpers.h"
- static_assert(AMDGPU_DMUB_NOTIFICATION_MAX == DMUB_NOTIFICATION_MAX, "AMDGPU_DMUB_NOTIFICATION_MAX mismatch");
-
- #define FIRMWARE_RENOIR_DMUB "amdgpu/renoir_dmcub.bin"
- MODULE_FIRMWARE(FIRMWARE_RENOIR_DMUB);
- #define FIRMWARE_SIENNA_CICHLID_DMUB "amdgpu/sienna_cichlid_dmcub.bin"
- MODULE_FIRMWARE(FIRMWARE_SIENNA_CICHLID_DMUB);
- #define FIRMWARE_NAVY_FLOUNDER_DMUB "amdgpu/navy_flounder_dmcub.bin"
- MODULE_FIRMWARE(FIRMWARE_NAVY_FLOUNDER_DMUB);
- #define FIRMWARE_GREEN_SARDINE_DMUB "amdgpu/green_sardine_dmcub.bin"
- MODULE_FIRMWARE(FIRMWARE_GREEN_SARDINE_DMUB);
- #define FIRMWARE_VANGOGH_DMUB "amdgpu/vangogh_dmcub.bin"
- MODULE_FIRMWARE(FIRMWARE_VANGOGH_DMUB);
- #define FIRMWARE_DIMGREY_CAVEFISH_DMUB "amdgpu/dimgrey_cavefish_dmcub.bin"
- MODULE_FIRMWARE(FIRMWARE_DIMGREY_CAVEFISH_DMUB);
- #define FIRMWARE_BEIGE_GOBY_DMUB "amdgpu/beige_goby_dmcub.bin"
- MODULE_FIRMWARE(FIRMWARE_BEIGE_GOBY_DMUB);
- #define FIRMWARE_YELLOW_CARP_DMUB "amdgpu/yellow_carp_dmcub.bin"
- MODULE_FIRMWARE(FIRMWARE_YELLOW_CARP_DMUB);
- #define FIRMWARE_DCN_314_DMUB "amdgpu/dcn_3_1_4_dmcub.bin"
- MODULE_FIRMWARE(FIRMWARE_DCN_314_DMUB);
- #define FIRMWARE_DCN_315_DMUB "amdgpu/dcn_3_1_5_dmcub.bin"
- MODULE_FIRMWARE(FIRMWARE_DCN_315_DMUB);
- #define FIRMWARE_DCN316_DMUB "amdgpu/dcn_3_1_6_dmcub.bin"
- MODULE_FIRMWARE(FIRMWARE_DCN316_DMUB);
-
- #define FIRMWARE_DCN_V3_2_0_DMCUB "amdgpu/dcn_3_2_0_dmcub.bin"
- MODULE_FIRMWARE(FIRMWARE_DCN_V3_2_0_DMCUB);
- #define FIRMWARE_DCN_V3_2_1_DMCUB "amdgpu/dcn_3_2_1_dmcub.bin"
- MODULE_FIRMWARE(FIRMWARE_DCN_V3_2_1_DMCUB);
-
- #define FIRMWARE_RAVEN_DMCU "amdgpu/raven_dmcu.bin"
MODULE_FIRMWARE(FIRMWARE_RAVEN_DMCU);
-
- #define FIRMWARE_NAVI12_DMCU "amdgpu/navi12_dmcu.bin"
MODULE_FIRMWARE(FIRMWARE_NAVI12_DMCU);
- #define FIRMWARE_DCN_35_DMUB "amdgpu/dcn_3_5_dmcub.bin"
- MODULE_FIRMWARE(FIRMWARE_DCN_35_DMUB);
-
- #define FIRMWARE_DCN_351_DMUB "amdgpu/dcn_3_5_1_dmcub.bin"
- MODULE_FIRMWARE(FIRMWARE_DCN_351_DMUB);
-
- #define FIRMWARE_DCN_36_DMUB "amdgpu/dcn_3_6_dmcub.bin"
- MODULE_FIRMWARE(FIRMWARE_DCN_36_DMUB);
-
- #define FIRMWARE_DCN_401_DMUB "amdgpu/dcn_4_0_1_dmcub.bin"
- MODULE_FIRMWARE(FIRMWARE_DCN_401_DMUB);
-
- #define FIRMWARE_DCN_42_DMUB "amdgpu/dcn_4_2_dmcub.bin"
- MODULE_FIRMWARE(FIRMWARE_DCN_42_DMUB);
-
- #define FIRMWARE_DCN_42B_DMUB "amdgpu/dcn_4_2_1_dmcub.bin"
- MODULE_FIRMWARE(FIRMWARE_DCN_42B_DMUB);
-
/**
* DOC: overview
*
@@@ -174,46 -129,6 +129,6 @@@
/* basic init/fini API */
static int amdgpu_dm_init(struct amdgpu_device *adev);
static void amdgpu_dm_fini(struct amdgpu_device *adev);
- static bool is_freesync_video_mode(const struct drm_display_mode *mode, struct amdgpu_dm_connector *aconnector);
- static void reset_freesync_config_for_crtc(struct dm_crtc_state *new_crtc_state);
- static struct amdgpu_i2c_adapter *
- create_i2c(struct ddc_service *ddc_service, bool oem);
-
- static enum drm_mode_subconnector get_subconnector_type(struct dc_link *link)
- {
- switch (link->dpcd_caps.dongle_type) {
- case DISPLAY_DONGLE_NONE:
- return DRM_MODE_SUBCONNECTOR_Native;
- case DISPLAY_DONGLE_DP_VGA_CONVERTER:
- return DRM_MODE_SUBCONNECTOR_VGA;
- case DISPLAY_DONGLE_DP_DVI_CONVERTER:
- case DISPLAY_DONGLE_DP_DVI_DONGLE:
- return DRM_MODE_SUBCONNECTOR_DVID;
- case DISPLAY_DONGLE_DP_HDMI_CONVERTER:
- case DISPLAY_DONGLE_DP_HDMI_DONGLE:
- return DRM_MODE_SUBCONNECTOR_HDMIA;
- case DISPLAY_DONGLE_DP_HDMI_MISMATCHED_DONGLE:
- default:
- return DRM_MODE_SUBCONNECTOR_Unknown;
- }
- }
-
- static void update_subconnector_property(struct amdgpu_dm_connector *aconnector)
- {
- struct dc_link *link = aconnector->dc_link;
- struct drm_connector *connector = &aconnector->base;
- enum drm_mode_subconnector subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
-
- if (connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort)
- return;
-
- if (aconnector->dc_sink)
- subconnector = get_subconnector_type(link);
-
- drm_object_property_set_value(&connector->base,
- connector->dev->mode_config.dp_subconnector_property,
- subconnector);
- }
/*
* initializes drm_device display related structures, based on the information
@@@ -226,32 -141,19 +141,19 @@@ static int amdgpu_dm_initialize_drm_dev
/* removes and deallocates the drm structures, created by the above function */
static void amdgpu_dm_destroy_drm_device(struct amdgpu_display_manager *dm);
- static int amdgpu_dm_connector_init(struct amdgpu_display_manager *dm,
- struct amdgpu_dm_connector *amdgpu_dm_connector,
- u32 link_index,
- struct amdgpu_encoder *amdgpu_encoder);
- static int amdgpu_dm_encoder_init(struct drm_device *dev,
- struct amdgpu_encoder *aencoder,
- uint32_t link_index);
-
- static int amdgpu_dm_connector_get_modes(struct drm_connector *connector);
-
static int amdgpu_dm_atomic_setup_commit(struct drm_atomic_commit *state);
static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_commit *state);
+ STATIC_IFN_KUNIT void dm_enable_per_frame_crtc_master_sync(struct dc_state *context);
static int amdgpu_dm_atomic_check(struct drm_device *dev,
struct drm_atomic_commit *state);
- static void handle_hpd_irq_helper(struct amdgpu_dm_connector *aconnector);
- static void handle_hpd_rx_irq(void *param);
+ static inline void amdgpu_dm_exit_ips_for_hw_access(struct dc *dc)
+ {
+ if (dc->ctx->dmub_srv && !dc->ctx->dmub_srv->idle_exit_counter)
+ dc_exit_ips_for_hw_access(dc);
+ }
- static void amdgpu_dm_backlight_set_level(struct amdgpu_display_manager *dm,
- int bl_idx,
- u32 user_brightness);
-
- static bool
- is_timing_unchanged_for_freesync(struct drm_crtc_state *old_crtc_state,
- struct drm_crtc_state *new_crtc_state);
/*
* dm_vblank_get_counter
*
@@@ -265,7 -167,7 +167,7 @@@
* @return
* Counter for vertical blanks
*/
- static u32 dm_vblank_get_counter(struct amdgpu_device *adev, int crtc)
+ STATIC_IFN_KUNIT u32 dm_vblank_get_counter(struct amdgpu_device *adev, int crtc)
{
struct amdgpu_crtc *acrtc = NULL;
@@@ -282,9 -184,10 +184,10 @@@
return dc_stream_get_vblank_counter(acrtc->dm_irq_params.stream);
}
+ EXPORT_IF_KUNIT(dm_vblank_get_counter);
- static int dm_crtc_get_scanoutpos(struct amdgpu_device *adev, int crtc,
- u32 *vbl, u32 *position)
+ STATIC_IFN_KUNIT int dm_crtc_get_scanoutpos(struct amdgpu_device *adev, int crtc,
+ u32 *vbl, u32 *position)
{
u32 v_blank_start = 0, v_blank_end = 0, h_position = 0, v_position = 0;
struct amdgpu_crtc *acrtc = NULL;
@@@ -319,70 -222,35 +222,35 @@@
return 0;
}
+ EXPORT_IF_KUNIT(dm_crtc_get_scanoutpos);
- static bool dm_is_idle(struct amdgpu_ip_block *ip_block)
+ STATIC_IFN_KUNIT bool dm_is_idle(struct amdgpu_ip_block *ip_block)
{
/* XXX todo */
return true;
}
+ EXPORT_IF_KUNIT(dm_is_idle);
- static int dm_wait_for_idle(struct amdgpu_ip_block *ip_block)
+ STATIC_IFN_KUNIT int dm_wait_for_idle(struct amdgpu_ip_block *ip_block)
{
/* XXX todo */
return 0;
}
+ EXPORT_IF_KUNIT(dm_wait_for_idle);
- static bool dm_check_soft_reset(struct amdgpu_ip_block *ip_block)
- {
- return false;
- }
-
- static int dm_soft_reset(struct amdgpu_ip_block *ip_block)
+ STATIC_IFN_KUNIT int dm_soft_reset(struct amdgpu_ip_block *ip_block)
{
/* XXX todo */
return 0;
}
-
- static struct amdgpu_crtc *
- get_crtc_by_otg_inst(struct amdgpu_device *adev,
- int otg_inst)
- {
- struct drm_device *dev = adev_to_drm(adev);
- struct drm_crtc *crtc;
- struct amdgpu_crtc *amdgpu_crtc;
-
- if (WARN_ON(otg_inst == -1))
- return adev->mode_info.crtcs[0];
-
- list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
- amdgpu_crtc = to_amdgpu_crtc(crtc);
-
- if (amdgpu_crtc->otg_inst == otg_inst)
- return amdgpu_crtc;
- }
-
- return NULL;
- }
-
- static inline bool is_dc_timing_adjust_needed(struct dm_crtc_state *old_state,
- struct dm_crtc_state *new_state)
- {
- if (new_state->stream->adjust.timing_adjust_pending)
- return true;
- if (new_state->freesync_config.state == VRR_STATE_ACTIVE_FIXED)
- return true;
- else if (amdgpu_dm_crtc_vrr_active(old_state) != amdgpu_dm_crtc_vrr_active(new_state))
- return true;
- else
- return false;
- }
+ EXPORT_IF_KUNIT(dm_soft_reset);
/*
* DC will program planes with their z-order determined by their ordering
* in the dc_surface_updates array. This comparator is used to sort them
* by descending zpos.
*/
- static int dm_plane_layer_index_cmp(const void *a, const void *b)
+ STATIC_IFN_KUNIT int dm_plane_layer_index_cmp(const void *a, const void *b)
{
const struct dc_surface_update *sa = (struct dc_surface_update *)a;
const struct dc_surface_update *sb = (struct dc_surface_update *)b;
@@@ -390,6 -258,7 +258,7 @@@
/* Sort by descending dc_plane layer_index (i.e. normalized_zpos) */
return sb->surface->layer_index - sa->surface->layer_index;
}
+ EXPORT_IF_KUNIT(dm_plane_layer_index_cmp);
/**
* update_planes_and_stream_adapter() - Send planes to be updated in DC
@@@ -430,1098 -299,24 +299,24 @@@ static inline bool update_planes_and_st
stream_update);
}
- /**
- * dm_pflip_high_irq() - Handle pageflip interrupt
- * @interrupt_params: ignored
- *
- * Handles the pageflip interrupt by notifying all interested parties
- * that the pageflip has been completed.
- */
- static void dm_pflip_high_irq(void *interrupt_params)
- {
- struct amdgpu_crtc *amdgpu_crtc;
- struct common_irq_params *irq_params = interrupt_params;
- struct amdgpu_device *adev = irq_params->adev;
- struct drm_device *dev = adev_to_drm(adev);
- unsigned long flags;
- struct drm_pending_vblank_event *e;
- u32 vpos, hpos, v_blank_start, v_blank_end;
- bool vrr_active;
-
- amdgpu_crtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_PFLIP);
-
- /* IRQ could occur when in initial stage */
- /* TODO work and BO cleanup */
- if (amdgpu_crtc == NULL) {
- drm_dbg_state(dev, "CRTC is null, returning.\n");
- return;
- }
-
- spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags);
-
- if (amdgpu_crtc->pflip_status != AMDGPU_FLIP_SUBMITTED) {
- drm_dbg_state(dev,
- "amdgpu_crtc->pflip_status = %d != AMDGPU_FLIP_SUBMITTED(%d) on crtc:%d[%p]\n",
- amdgpu_crtc->pflip_status, AMDGPU_FLIP_SUBMITTED,
- amdgpu_crtc->crtc_id, amdgpu_crtc);
- spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
- return;
- }
-
- /* page flip completed. */
- e = amdgpu_crtc->event;
- amdgpu_crtc->event = NULL;
-
- WARN_ON(!e);
-
- vrr_active = amdgpu_dm_crtc_vrr_active_irq(amdgpu_crtc);
-
- /* Fixed refresh rate, or VRR scanout position outside front-porch? */
- if (!vrr_active ||
- !dc_stream_get_scanoutpos(amdgpu_crtc->dm_irq_params.stream, &v_blank_start,
- &v_blank_end, &hpos, &vpos) ||
- (vpos < v_blank_start)) {
- /* Update to correct count and vblank timestamp if racing with
- * vblank irq. This also updates to the correct vblank timestamp
- * even in VRR mode, as scanout is past the front-porch atm.
- */
- drm_crtc_accurate_vblank_count(&amdgpu_crtc->base);
-
- /* Wake up userspace by sending the pageflip event with proper
- * count and timestamp of vblank of flip completion.
- */
- if (e) {
- drm_crtc_send_vblank_event(&amdgpu_crtc->base, e);
-
- /* Event sent, so done with vblank for this flip */
- drm_crtc_vblank_put(&amdgpu_crtc->base);
- }
- } else if (e) {
- /* VRR active and inside front-porch: vblank count and
- * timestamp for pageflip event will only be up to date after
- * drm_crtc_handle_vblank() has been executed from late vblank
- * irq handler after start of back-porch (vline 0). We queue the
- * pageflip event for send-out by drm_crtc_handle_vblank() with
- * updated timestamp and count, once it runs after us.
- *
- * We need to open-code this instead of using the helper
- * drm_crtc_arm_vblank_event(), as that helper would
- * call drm_crtc_accurate_vblank_count(), which we must
- * not call in VRR mode while we are in front-porch!
- */
-
- /* sequence will be replaced by real count during send-out. */
- e->sequence = drm_crtc_vblank_count(&amdgpu_crtc->base);
- e->pipe = amdgpu_crtc->crtc_id;
-
- list_add_tail(&e->base.link, &adev_to_drm(adev)->vblank_event_list);
- e = NULL;
- }
-
- /* Keep track of vblank of this flip for flip throttling. We use the
- * cooked hw counter, as that one incremented at start of this vblank
- * of pageflip completion, so last_flip_vblank is the forbidden count
- * for queueing new pageflips if vsync + VRR is enabled.
- */
- amdgpu_crtc->dm_irq_params.last_flip_vblank =
- amdgpu_get_vblank_counter_kms(&amdgpu_crtc->base);
-
- amdgpu_crtc->pflip_status = AMDGPU_FLIP_NONE;
- spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
-
- drm_dbg_state(dev,
- "crtc:%d[%p], pflip_stat:AMDGPU_FLIP_NONE, vrr[%d]-fp %d\n",
- amdgpu_crtc->crtc_id, amdgpu_crtc, vrr_active, (int)!e);
- }
-
- static void dm_handle_vmin_vmax_update(struct work_struct *offload_work)
- {
- struct vupdate_offload_work *work = container_of(offload_work, struct vupdate_offload_work, work);
- struct amdgpu_device *adev = work->adev;
- struct dc_stream_state *stream = work->stream;
- struct dc_crtc_timing_adjust *adjust = work->adjust;
-
- mutex_lock(&adev->dm.dc_lock);
- dc_stream_adjust_vmin_vmax(adev->dm.dc, stream, adjust);
- mutex_unlock(&adev->dm.dc_lock);
-
- dc_stream_release(stream);
- kfree(work->adjust);
- kfree(work);
- }
-
- static void schedule_dc_vmin_vmax(struct amdgpu_device *adev,
- struct dc_stream_state *stream,
- struct dc_crtc_timing_adjust *adjust)
- {
- struct vupdate_offload_work *offload_work = kzalloc_obj(*offload_work,
- GFP_NOWAIT);
- if (!offload_work) {
- drm_dbg_driver(adev_to_drm(adev), "Failed to allocate vupdate_offload_work\n");
- return;
- }
-
- struct dc_crtc_timing_adjust *adjust_copy = kzalloc_obj(*adjust_copy,
- GFP_NOWAIT);
- if (!adjust_copy) {
- drm_dbg_driver(adev_to_drm(adev), "Failed to allocate adjust_copy\n");
- kfree(offload_work);
- return;
- }
-
- dc_stream_retain(stream);
- memcpy(adjust_copy, adjust, sizeof(*adjust_copy));
-
- INIT_WORK(&offload_work->work, dm_handle_vmin_vmax_update);
- offload_work->adev = adev;
- offload_work->stream = stream;
- offload_work->adjust = adjust_copy;
-
- queue_work(system_percpu_wq, &offload_work->work);
- }
-
- /**
- * dm_crtc_high_irq_handler() - Common OTG vblank/flip event handling
- * @adev: amdgpu device
- * @acrtc: the CRTC to service
- *
- * Performs writeback completion, vblank event handling, CRC processing, VRR BTR
- * updates and pageflip completion delivery.
- *
- * On DCN this is driven by VUPDATE_NO_LOCK (the register latch point) from
- * dm_vupdate_high_irq(); on DCE it is driven by VLINE0 at the start of vblank
- * from dm_crtc_high_irq().
- */
- static void dm_crtc_high_irq_handler(struct amdgpu_device *adev,
- struct amdgpu_crtc *acrtc)
- {
- struct drm_writeback_job *job;
- unsigned long flags;
- int vrr_active;
- bool is_dcn = amdgpu_ip_version(adev, DCE_HWIP, 0) != 0;
-
- if (acrtc->wb_conn) {
- spin_lock_irqsave(&acrtc->wb_conn->job_lock, flags);
-
- if (acrtc->wb_pending) {
- job = list_first_entry_or_null(&acrtc->wb_conn->job_queue,
- struct drm_writeback_job,
- list_entry);
- acrtc->wb_pending = false;
- spin_unlock_irqrestore(&acrtc->wb_conn->job_lock, flags);
-
- if (job) {
- unsigned int v_total, refresh_hz;
- struct dc_stream_state *stream = acrtc->dm_irq_params.stream;
-
- v_total = stream->adjust.v_total_max ?
- stream->adjust.v_total_max : stream->timing.v_total;
- refresh_hz = div_u64((uint64_t) stream->timing.pix_clk_100hz *
- 100LL, (v_total * stream->timing.h_total));
- mdelay(1000 / refresh_hz);
-
- drm_writeback_signal_completion(acrtc->wb_conn, 0);
- dc_stream_fc_disable_writeback(adev->dm.dc,
- acrtc->dm_irq_params.stream, 0);
- }
- } else
- spin_unlock_irqrestore(&acrtc->wb_conn->job_lock, flags);
- }
-
- vrr_active = amdgpu_dm_crtc_vrr_active_irq(acrtc);
-
- drm_dbg_vbl(adev_to_drm(adev),
- "crtc:%d, vupdate-vrr:%d, planes:%d\n", acrtc->crtc_id,
- vrr_active, acrtc->dm_irq_params.active_planes);
-
- /**
- * Core vblank handling.
- *
- * On DCN this handler runs at VUPDATE_NO_LOCK, the register latch
- * point, which is the correct place to timestamp both VRR and non-VRR
- * vblanks.
- *
- * On DCE this handler runs at the start of front-porch, where only
- * non-VRR timestamping is valid; VRR vblank is deferred to
- * dm_vupdate_high_irq() after end of front-porch.
- */
- if (is_dcn || !vrr_active)
- amdgpu_dm_crtc_handle_vblank(acrtc);
-
- /**
- * Following stuff must happen at start of vblank, for crc
- * computation and below-the-range btr support in vrr mode.
- */
- amdgpu_dm_crtc_handle_crc_irq(&acrtc->base);
-
- /* BTR updates need to happen before VUPDATE on Vega and above. */
- if (adev->family < AMDGPU_FAMILY_AI)
- return;
-
- spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags);
-
- if (acrtc->dm_irq_params.stream &&
- acrtc->dm_irq_params.vrr_params.supported) {
- bool replay_en = acrtc->dm_irq_params.stream->link->replay_settings.replay_feature_enabled;
- bool psr_en = acrtc->dm_irq_params.stream->link->psr_settings.psr_feature_enabled;
- bool fs_active_var_en = acrtc->dm_irq_params.freesync_config.state == VRR_STATE_ACTIVE_VARIABLE;
-
- mod_freesync_handle_v_update(adev->dm.freesync_module,
- acrtc->dm_irq_params.stream,
- &acrtc->dm_irq_params.vrr_params);
-
- /* update vmin_vmax only if freesync is enabled, or only if PSR and REPLAY are disabled */
- if (fs_active_var_en || (!fs_active_var_en && !replay_en && !psr_en)) {
- schedule_dc_vmin_vmax(adev, acrtc->dm_irq_params.stream,
- &acrtc->dm_irq_params.vrr_params.adjust);
- }
- }
-
- /*
- * Deliver pageflip completion events (DCN only).
- *
- * Since GRPH_PFLIP is not used, VUPDATE_NO_LOCK is the flip latch
- * point. Deliver any pending pageflip completion event from here,
- * once HW has consumed the new address (the OTG no longer reports a
- * pending flip).
- *
- * Also handle the case here where there aren't any active planes and
- * DCN HUBP may be clock-gated, so the flip-pending status may be
- * undefined.
- */
- if (is_dcn && acrtc->pflip_status == AMDGPU_FLIP_SUBMITTED &&
- acrtc->event) {
-
- if (!dc_get_flip_pending_on_otg(adev->dm.dc, acrtc->otg_inst)) {
- drm_crtc_send_vblank_event(&acrtc->base, acrtc->event);
- acrtc->event = NULL;
- drm_crtc_vblank_put(&acrtc->base);
- acrtc->pflip_status = AMDGPU_FLIP_NONE;
- }
- /*
- * If the flip is still pending, leave it armed and
- * retry on the next vupdate.
- */
- } else if (is_dcn && acrtc->pflip_status == AMDGPU_FLIP_SUBMITTED &&
- acrtc->dm_irq_params.active_planes == 0) {
-
- if (acrtc->event) {
- drm_crtc_send_vblank_event(&acrtc->base, acrtc->event);
- acrtc->event = NULL;
- drm_crtc_vblank_put(&acrtc->base);
- }
- acrtc->pflip_status = AMDGPU_FLIP_NONE;
- }
-
- spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
- }
-
- static void dm_vupdate_high_irq(void *interrupt_params)
- {
- struct common_irq_params *irq_params = interrupt_params;
- struct amdgpu_device *adev = irq_params->adev;
- struct amdgpu_crtc *acrtc;
- struct drm_device *drm_dev;
- struct drm_vblank_crtc *vblank;
- ktime_t frame_duration_ns, previous_timestamp;
- unsigned long flags;
- int vrr_active;
-
- acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VUPDATE);
- if (!acrtc)
- return;
-
- vrr_active = amdgpu_dm_crtc_vrr_active_irq(acrtc);
- drm_dev = acrtc->base.dev;
- vblank = drm_crtc_vblank_crtc(&acrtc->base);
- previous_timestamp = atomic64_read(&irq_params->previous_timestamp);
- frame_duration_ns = vblank->time - previous_timestamp;
-
- if (frame_duration_ns > 0) {
- trace_amdgpu_refresh_rate_track(acrtc->base.index,
- frame_duration_ns,
- ktime_divns(NSEC_PER_SEC, frame_duration_ns));
- atomic64_set(&irq_params->previous_timestamp, vblank->time);
- }
-
- drm_dbg_vbl(drm_dev,
- "crtc:%d, vupdate-vrr:%d\n", acrtc->crtc_id,
- vrr_active);
-
- /*
- * On DCN, VUPDATE_NO_LOCK is the single OTG interrupt used to deliver
- * vblank and pageflip completion events; VSTARTUP and GRPH_PFLIP are
- * not used. Run the full handler here.
- */
- if (amdgpu_ip_version(adev, DCE_HWIP, 0) != 0) {
- dm_crtc_high_irq_handler(adev, acrtc);
- return;
- }
-
- /* DCE only below. */
-
- /* Core vblank handling is done here after end of front-porch in
- * vrr mode, as vblank timestamping will give valid results
- * while now done after front-porch. This will also deliver
- * page-flip completion events that have been queued to us
- * if a pageflip happened inside front-porch.
- */
- if (vrr_active && acrtc->dm_irq_params.stream) {
- bool replay_en = acrtc->dm_irq_params.stream->link->replay_settings.replay_feature_enabled;
- bool psr_en = acrtc->dm_irq_params.stream->link->psr_settings.psr_feature_enabled;
- bool fs_active_var_en = acrtc->dm_irq_params.freesync_config.state
- == VRR_STATE_ACTIVE_VARIABLE;
-
- amdgpu_dm_crtc_handle_vblank(acrtc);
-
- /* BTR processing for pre-DCE12 ASICs */
- if (adev->family < AMDGPU_FAMILY_AI) {
- spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags);
- mod_freesync_handle_v_update(
- adev->dm.freesync_module,
- acrtc->dm_irq_params.stream,
- &acrtc->dm_irq_params.vrr_params);
-
- if (fs_active_var_en || (!fs_active_var_en && !replay_en && !psr_en)) {
- schedule_dc_vmin_vmax(adev,
- acrtc->dm_irq_params.stream,
- &acrtc->dm_irq_params.vrr_params.adjust);
- }
- spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
- }
- }
- }
-
- /**
- * dm_crtc_high_irq() - Handles CRTC interrupt
- * @interrupt_params: used for determining the CRTC instance
- *
- * Handles the CRTC/VSYNC interrupt by notifying DRM's VBLANK event handler.
- *
- * Used on DCE (VLINE0, set to vblank start). On DCN the equivalent handling is
- * driven by VUPDATE_NO_LOCK in dm_vupdate_high_irq().
- */
- static void dm_crtc_high_irq(void *interrupt_params)
- {
- struct common_irq_params *irq_params = interrupt_params;
- struct amdgpu_device *adev = irq_params->adev;
- struct amdgpu_crtc *acrtc;
-
- acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VBLANK);
- if (!acrtc)
- return;
-
- dm_crtc_high_irq_handler(adev, acrtc);
- }
-
- #if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
- /**
- * dm_dcn_vertical_interrupt0_high_irq() - Handles OTG Vertical interrupt0 for
- * DCN generation ASICs
- * @interrupt_params: interrupt parameters
- *
- * Used to set crc window/read out crc value at vertical line 0 position
- */
- static void dm_dcn_vertical_interrupt0_high_irq(void *interrupt_params)
- {
- struct common_irq_params *irq_params = interrupt_params;
- struct amdgpu_device *adev = irq_params->adev;
- struct amdgpu_crtc *acrtc;
-
- acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VLINE0);
-
- if (!acrtc)
- return;
-
- amdgpu_dm_crtc_handle_crc_window_irq(&acrtc->base);
- }
- #endif /* CONFIG_DRM_AMD_SECURE_DISPLAY */
-
- /**
- * dmub_aux_setconfig_callback - Callback for AUX or SET_CONFIG command.
- * @adev: amdgpu_device pointer
- * @notify: dmub notification structure
- *
- * Dmub AUX or SET_CONFIG command completion processing callback
- * Copies dmub notification to DM which is to be read by AUX command.
- * issuing thread and also signals the event to wake up the thread.
- */
- static void dmub_aux_setconfig_callback(struct amdgpu_device *adev,
- struct dmub_notification *notify)
- {
- if (adev->dm.dmub_notify)
- memcpy(adev->dm.dmub_notify, notify, sizeof(struct dmub_notification));
- if (notify->type == DMUB_NOTIFICATION_AUX_REPLY)
- complete(&adev->dm.dmub_aux_transfer_done);
- }
-
- static void dmub_aux_fused_io_callback(struct amdgpu_device *adev,
- struct dmub_notification *notify)
- {
- if (!adev || !notify) {
- ASSERT(false);
- return;
- }
-
- const struct dmub_cmd_fused_request *req = ¬ify->fused_request;
- const uint8_t ddc_line = req->u.aux.ddc_line;
-
- if (ddc_line >= ARRAY_SIZE(adev->dm.fused_io)) {
- ASSERT(false);
- return;
- }
-
- struct fused_io_sync *sync = &adev->dm.fused_io[ddc_line];
-
- static_assert(sizeof(*req) <= sizeof(sync->reply_data), "Size mismatch");
- memcpy(sync->reply_data, req, sizeof(*req));
- complete(&sync->replied);
- }
-
- /**
- * dmub_hpd_callback - DMUB HPD interrupt processing callback.
- * @adev: amdgpu_device pointer
- * @notify: dmub notification structure
- *
- * Dmub Hpd interrupt processing callback. Gets displayindex through the
- * ink index and calls helper to do the processing.
- */
- static void dmub_hpd_callback(struct amdgpu_device *adev,
- struct dmub_notification *notify)
- {
- struct amdgpu_dm_connector *aconnector;
- struct amdgpu_dm_connector *hpd_aconnector = NULL;
- struct drm_connector *connector;
- struct drm_connector_list_iter iter;
- struct dc_link *link;
- u8 link_index = 0;
- struct drm_device *dev;
-
- if (adev == NULL)
- return;
-
- if (notify == NULL) {
- drm_err(adev_to_drm(adev), "DMUB HPD callback notification was NULL");
- return;
- }
-
- if (notify->link_index > adev->dm.dc->link_count) {
- drm_err(adev_to_drm(adev), "DMUB HPD index (%u)is abnormal", notify->link_index);
- return;
- }
-
- /* Skip DMUB HPD IRQ in suspend/resume. We will probe them later. */
- if (notify->type == DMUB_NOTIFICATION_HPD && adev->in_suspend) {
- drm_info(adev_to_drm(adev), "Skip DMUB HPD IRQ callback in suspend/resume\n");
- return;
- }
-
- link_index = notify->link_index;
- link = adev->dm.dc->links[link_index];
- dev = adev->dm.ddev;
-
- drm_connector_list_iter_begin(dev, &iter);
- drm_for_each_connector_iter(connector, &iter) {
-
- if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
- continue;
-
- aconnector = to_amdgpu_dm_connector(connector);
- if (link && aconnector->dc_link == link) {
- if (notify->type == DMUB_NOTIFICATION_HPD)
- drm_info(adev_to_drm(adev), "DMUB HPD IRQ callback: link_index=%u\n", link_index);
- else if (notify->type == DMUB_NOTIFICATION_HPD_IRQ)
- drm_info(adev_to_drm(adev), "DMUB HPD RX IRQ callback: link_index=%u\n", link_index);
- else
- drm_warn(adev_to_drm(adev), "DMUB Unknown HPD callback type %d, link_index=%u\n",
- notify->type, link_index);
-
- hpd_aconnector = aconnector;
- break;
- }
- }
- drm_connector_list_iter_end(&iter);
-
- if (hpd_aconnector) {
- if (notify->type == DMUB_NOTIFICATION_HPD) {
- if (hpd_aconnector->dc_link->hpd_status == (notify->hpd_status == DP_HPD_PLUG))
- drm_warn(adev_to_drm(adev), "DMUB reported hpd status unchanged. link_index=%u\n", link_index);
- handle_hpd_irq_helper(hpd_aconnector);
- } else if (notify->type == DMUB_NOTIFICATION_HPD_IRQ) {
- handle_hpd_rx_irq(hpd_aconnector);
- }
- }
- }
-
- /**
- * dmub_hpd_sense_callback - DMUB HPD sense processing callback.
- * @adev: amdgpu_device pointer
- * @notify: dmub notification structure
- *
- * HPD sense changes can occur during low power states and need to be
- * notified from firmware to driver.
- */
- static void dmub_hpd_sense_callback(struct amdgpu_device *adev,
- struct dmub_notification *notify)
- {
- drm_dbg_driver(adev_to_drm(adev), "DMUB HPD SENSE callback.\n");
- }
-
- /**
- * register_dmub_notify_callback - Sets callback for DMUB notify
- * @adev: amdgpu_device pointer
- * @type: Type of dmub notification
- * @callback: Dmub interrupt callback function
- * @dmub_int_thread_offload: offload indicator
- *
- * API to register a dmub callback handler for a dmub notification
- * Also sets indicator whether callback processing to be offloaded.
- * to dmub interrupt handling thread
- * Return: true if successfully registered, false if there is existing registration
- */
- static bool register_dmub_notify_callback(struct amdgpu_device *adev,
- enum dmub_notification_type type,
- dmub_notify_interrupt_callback_t callback,
- bool dmub_int_thread_offload)
- {
- if (callback != NULL && type < ARRAY_SIZE(adev->dm.dmub_thread_offload)) {
- adev->dm.dmub_callback[type] = callback;
- adev->dm.dmub_thread_offload[type] = dmub_int_thread_offload;
- } else
- return false;
-
- return true;
- }
-
- static void dm_handle_hpd_work(struct work_struct *work)
- {
- struct dmub_hpd_work *dmub_hpd_wrk;
-
- dmub_hpd_wrk = container_of(work, struct dmub_hpd_work, handle_hpd_work);
-
- if (!dmub_hpd_wrk->dmub_notify) {
- drm_err(adev_to_drm(dmub_hpd_wrk->adev), "dmub_hpd_wrk dmub_notify is NULL");
- return;
- }
-
- if (dmub_hpd_wrk->dmub_notify->type < ARRAY_SIZE(dmub_hpd_wrk->adev->dm.dmub_callback)) {
- dmub_hpd_wrk->adev->dm.dmub_callback[dmub_hpd_wrk->dmub_notify->type](dmub_hpd_wrk->adev,
- dmub_hpd_wrk->dmub_notify);
- }
-
- kfree(dmub_hpd_wrk->dmub_notify);
- kfree(dmub_hpd_wrk);
-
- }
-
- static const char *dmub_notification_type_str(enum dmub_notification_type e)
- {
- switch (e) {
- case DMUB_NOTIFICATION_NO_DATA:
- return "NO_DATA";
- case DMUB_NOTIFICATION_AUX_REPLY:
- return "AUX_REPLY";
- case DMUB_NOTIFICATION_HPD:
- return "HPD";
- case DMUB_NOTIFICATION_HPD_IRQ:
- return "HPD_IRQ";
- case DMUB_NOTIFICATION_SET_CONFIG_REPLY:
- return "SET_CONFIG_REPLY";
- case DMUB_NOTIFICATION_DPIA_NOTIFICATION:
- return "DPIA_NOTIFICATION";
- case DMUB_NOTIFICATION_HPD_SENSE_NOTIFY:
- return "HPD_SENSE_NOTIFY";
- case DMUB_NOTIFICATION_FUSED_IO:
- return "FUSED_IO";
- default:
- return "<unknown>";
- }
- }
-
- #define DMUB_TRACE_MAX_READ 64
- /**
- * dm_dmub_outbox1_low_irq() - Handles Outbox interrupt
- * @interrupt_params: used for determining the Outbox instance
- *
- * Handles the Outbox Interrupt
- * event handler.
- */
- static void dm_dmub_outbox1_low_irq(void *interrupt_params)
- {
- struct dmub_notification notify = {0};
- struct common_irq_params *irq_params = interrupt_params;
- struct amdgpu_device *adev = irq_params->adev;
- struct amdgpu_display_manager *dm = &adev->dm;
- struct dmcub_trace_buf_entry entry = { 0 };
- u32 count = 0;
- struct dmub_hpd_work *dmub_hpd_wrk;
-
- do {
- if (dc_dmub_srv_get_dmub_outbox0_msg(dm->dc, &entry)) {
- trace_amdgpu_dmub_trace_high_irq(entry.trace_code, entry.tick_count,
- entry.param0, entry.param1);
-
- drm_dbg_driver(adev_to_drm(adev), "trace_code:%u, tick_count:%u, param0:%u, param1:%u\n",
- entry.trace_code, entry.tick_count, entry.param0, entry.param1);
- } else
- break;
-
- count++;
-
- } while (count <= DMUB_TRACE_MAX_READ);
-
- if (count > DMUB_TRACE_MAX_READ)
- drm_dbg_driver(adev_to_drm(adev), "Warning : count > DMUB_TRACE_MAX_READ");
-
- if (dc_enable_dmub_notifications(adev->dm.dc) &&
- irq_params->irq_src == DC_IRQ_SOURCE_DMCUB_OUTBOX) {
-
- do {
- dc_stat_get_dmub_notification(adev->dm.dc, ¬ify);
- if (notify.type >= ARRAY_SIZE(dm->dmub_thread_offload)) {
- drm_err(adev_to_drm(adev), "DM: notify type %d invalid!", notify.type);
- continue;
- }
- if (!dm->dmub_callback[notify.type]) {
- drm_warn(adev_to_drm(adev), "DMUB notification skipped due to no handler: type=%s\n",
- dmub_notification_type_str(notify.type));
- continue;
- }
- if (dm->dmub_thread_offload[notify.type] == true) {
- dmub_hpd_wrk = kzalloc_obj(*dmub_hpd_wrk,
- GFP_ATOMIC);
- if (!dmub_hpd_wrk) {
- drm_err(adev_to_drm(adev), "Failed to allocate dmub_hpd_wrk");
- return;
- }
- dmub_hpd_wrk->dmub_notify = kmemdup(¬ify, sizeof(struct dmub_notification),
- GFP_ATOMIC);
- if (!dmub_hpd_wrk->dmub_notify) {
- kfree(dmub_hpd_wrk);
- drm_err(adev_to_drm(adev), "Failed to allocate dmub_hpd_wrk->dmub_notify");
- return;
- }
- INIT_WORK(&dmub_hpd_wrk->handle_hpd_work, dm_handle_hpd_work);
- dmub_hpd_wrk->adev = adev;
- queue_work(adev->dm.delayed_hpd_wq, &dmub_hpd_wrk->handle_hpd_work);
- } else {
- dm->dmub_callback[notify.type](adev, ¬ify);
- }
- } while (notify.pending_notification);
- }
- }
-
- static int dm_set_clockgating_state(struct amdgpu_ip_block *ip_block,
- enum amd_clockgating_state state)
+ STATIC_IFN_KUNIT int dm_set_clockgating_state(struct amdgpu_ip_block *ip_block,
+ enum amd_clockgating_state state)
{
return 0;
}
+ EXPORT_IF_KUNIT(dm_set_clockgating_state);
- static int dm_set_powergating_state(struct amdgpu_ip_block *ip_block,
- enum amd_powergating_state state)
+ STATIC_IFN_KUNIT int dm_set_powergating_state(struct amdgpu_ip_block *ip_block,
+ enum amd_powergating_state state)
{
return 0;
}
+ EXPORT_IF_KUNIT(dm_set_powergating_state);
/* Prototypes of private functions */
static int dm_early_init(struct amdgpu_ip_block *ip_block);
/* Allocate memory for FBC compressed data */
- static void amdgpu_dm_fbc_init(struct drm_connector *connector)
- {
- struct amdgpu_device *adev = drm_to_adev(connector->dev);
- struct dm_compressor_info *compressor = &adev->dm.compressor;
- struct amdgpu_dm_connector *aconn = to_amdgpu_dm_connector(connector);
- struct drm_display_mode *mode;
- unsigned long max_size = 0;
-
- if (adev->dm.dc->fbc_compressor == NULL)
- return;
-
- if (aconn->dc_link->connector_signal != SIGNAL_TYPE_EDP)
- return;
-
- if (compressor->bo_ptr)
- return;
-
-
- list_for_each_entry(mode, &connector->modes, head) {
- if (max_size < (unsigned long) mode->htotal * mode->vtotal)
- max_size = (unsigned long) mode->htotal * mode->vtotal;
- }
-
- if (max_size) {
- int r = amdgpu_bo_create_kernel(adev, max_size * 4, PAGE_SIZE,
- AMDGPU_GEM_DOMAIN_GTT, &compressor->bo_ptr,
- &compressor->gpu_addr, &compressor->cpu_addr);
-
- if (r)
- drm_err(adev_to_drm(adev), "DM: Failed to initialize FBC\n");
- else {
- adev->dm.dc->ctx->fbc_gpu_addr = compressor->gpu_addr;
- drm_info(adev_to_drm(adev), "DM: FBC alloc %lu\n", max_size*4);
- }
-
- }
-
- }
-
- static int amdgpu_dm_audio_component_get_eld(struct device *kdev, int port,
- int pipe, bool *enabled,
- unsigned char *buf, int max_bytes)
- {
- struct drm_device *dev = dev_get_drvdata(kdev);
- struct amdgpu_device *adev = drm_to_adev(dev);
- struct drm_connector *connector;
- struct drm_connector_list_iter conn_iter;
- struct amdgpu_dm_connector *aconnector;
- int ret = 0;
-
- *enabled = false;
-
- mutex_lock(&adev->dm.audio_lock);
-
- drm_connector_list_iter_begin(dev, &conn_iter);
- drm_for_each_connector_iter(connector, &conn_iter) {
-
- if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
- continue;
-
- aconnector = to_amdgpu_dm_connector(connector);
- if (aconnector->audio_inst != port)
- continue;
-
- *enabled = true;
- mutex_lock(&connector->eld_mutex);
- ret = drm_eld_size(connector->eld);
- memcpy(buf, connector->eld, min(max_bytes, ret));
- mutex_unlock(&connector->eld_mutex);
-
- break;
- }
- drm_connector_list_iter_end(&conn_iter);
-
- mutex_unlock(&adev->dm.audio_lock);
-
- drm_dbg_kms(adev_to_drm(adev), "Get ELD : idx=%d ret=%d en=%d\n", port, ret, *enabled);
-
- return ret;
- }
-
- static const struct drm_audio_component_ops amdgpu_dm_audio_component_ops = {
- .get_eld = amdgpu_dm_audio_component_get_eld,
- };
-
- static int amdgpu_dm_audio_component_bind(struct device *kdev,
- struct device *hda_kdev, void *data)
- {
- struct drm_device *dev = dev_get_drvdata(kdev);
- struct amdgpu_device *adev = drm_to_adev(dev);
- struct drm_audio_component *acomp = data;
-
- acomp->ops = &amdgpu_dm_audio_component_ops;
- acomp->dev = kdev;
- adev->dm.audio_component = acomp;
-
- return 0;
- }
-
- static void amdgpu_dm_audio_component_unbind(struct device *kdev,
- struct device *hda_kdev, void *data)
- {
- struct amdgpu_device *adev = drm_to_adev(dev_get_drvdata(kdev));
- struct drm_audio_component *acomp = data;
-
- acomp->ops = NULL;
- acomp->dev = NULL;
- adev->dm.audio_component = NULL;
- }
-
- static const struct component_ops amdgpu_dm_audio_component_bind_ops = {
- .bind = amdgpu_dm_audio_component_bind,
- .unbind = amdgpu_dm_audio_component_unbind,
- };
-
- static int amdgpu_dm_audio_init(struct amdgpu_device *adev)
- {
- int i, ret;
-
- if (!amdgpu_audio)
- return 0;
-
- adev->mode_info.audio.enabled = true;
-
- adev->mode_info.audio.num_pins = adev->dm.dc->res_pool->audio_count;
-
- for (i = 0; i < adev->mode_info.audio.num_pins; i++) {
- adev->mode_info.audio.pin[i].channels = -1;
- adev->mode_info.audio.pin[i].rate = -1;
- adev->mode_info.audio.pin[i].bits_per_sample = -1;
- adev->mode_info.audio.pin[i].status_bits = 0;
- adev->mode_info.audio.pin[i].category_code = 0;
- adev->mode_info.audio.pin[i].connected = false;
- adev->mode_info.audio.pin[i].id =
- adev->dm.dc->res_pool->audios[i]->inst;
- adev->mode_info.audio.pin[i].offset = 0;
- }
-
- ret = component_add(adev->dev, &amdgpu_dm_audio_component_bind_ops);
- if (ret < 0)
- return ret;
-
- adev->dm.audio_registered = true;
-
- return 0;
- }
-
- static void amdgpu_dm_audio_fini(struct amdgpu_device *adev)
- {
- if (!amdgpu_audio)
- return;
-
- if (!adev->mode_info.audio.enabled)
- return;
-
- if (adev->dm.audio_registered) {
- component_del(adev->dev, &amdgpu_dm_audio_component_bind_ops);
- adev->dm.audio_registered = false;
- }
-
- /* TODO: Disable audio? */
-
- adev->mode_info.audio.enabled = false;
- }
-
- static void amdgpu_dm_audio_eld_notify(struct amdgpu_device *adev, int pin)
- {
- struct drm_audio_component *acomp = adev->dm.audio_component;
-
- if (acomp && acomp->audio_ops && acomp->audio_ops->pin_eld_notify) {
- drm_dbg_kms(adev_to_drm(adev), "Notify ELD: %d\n", pin);
-
- acomp->audio_ops->pin_eld_notify(acomp->audio_ops->audio_ptr,
- pin, -1);
- }
- }
-
- static int dm_dmub_hw_init(struct amdgpu_device *adev)
- {
- const struct dmcub_firmware_header_v1_0 *hdr;
- struct dmub_srv *dmub_srv = adev->dm.dmub_srv;
- struct dmub_srv_fb_info *fb_info = adev->dm.dmub_fb_info;
- const struct firmware *dmub_fw = adev->dm.dmub_fw;
- struct dc *dc = adev->dm.dc;
- struct dmcu *dmcu = adev->dm.dc->res_pool->dmcu;
- struct abm *abm = adev->dm.dc->res_pool->abm;
- struct dc_context *ctx = adev->dm.dc->ctx;
- struct dmub_srv_hw_params hw_params;
- enum dmub_status status;
- const unsigned char *fw_inst_const, *fw_bss_data;
- u32 i, fw_inst_const_size, fw_bss_data_size;
- bool has_hw_support;
-
- if (!dmub_srv)
- /* DMUB isn't supported on the ASIC. */
- return 0;
-
- if (!fb_info) {
- drm_err(adev_to_drm(adev), "No framebuffer info for DMUB service.\n");
- return -EINVAL;
- }
-
- if (!dmub_fw) {
- /* Firmware required for DMUB support. */
- drm_err(adev_to_drm(adev), "No firmware provided for DMUB.\n");
- return -EINVAL;
- }
-
- /* initialize register offsets for ASICs with runtime initialization available */
- if (dmub_srv->hw_funcs.init_reg_offsets)
- dmub_srv->hw_funcs.init_reg_offsets(dmub_srv, ctx);
-
- status = dmub_srv_has_hw_support(dmub_srv, &has_hw_support);
- if (status != DMUB_STATUS_OK) {
- drm_err(adev_to_drm(adev), "Error checking HW support for DMUB: %d\n", status);
- return -EINVAL;
- }
-
- if (!has_hw_support) {
- drm_info(adev_to_drm(adev), "DMUB unsupported on ASIC\n");
- return 0;
- }
-
- /* Reset DMCUB if it was previously running - before we overwrite its memory. */
- status = dmub_srv_hw_reset(dmub_srv);
- if (status != DMUB_STATUS_OK)
- drm_warn(adev_to_drm(adev), "Error resetting DMUB HW: %d\n", status);
-
- hdr = (const struct dmcub_firmware_header_v1_0 *)dmub_fw->data;
-
- fw_inst_const = dmub_fw->data +
- le32_to_cpu(hdr->header.ucode_array_offset_bytes) +
- PSP_HEADER_BYTES_256;
-
- fw_bss_data = dmub_fw->data +
- le32_to_cpu(hdr->header.ucode_array_offset_bytes) +
- le32_to_cpu(hdr->inst_const_bytes);
-
- /* Copy firmware and bios info into FB memory. */
- fw_inst_const_size = adev->dm.fw_inst_size;
-
- fw_bss_data_size = le32_to_cpu(hdr->bss_data_bytes);
-
- /* if adev->firmware.load_type == AMDGPU_FW_LOAD_PSP,
- * amdgpu_ucode_init_single_fw will load dmub firmware
- * fw_inst_const part to cw0; otherwise, the firmware back door load
- * will be done by dm_dmub_hw_init
- */
- if (adev->firmware.load_type != AMDGPU_FW_LOAD_PSP) {
- memcpy(fb_info->fb[DMUB_WINDOW_0_INST_CONST].cpu_addr, fw_inst_const,
- fw_inst_const_size);
- }
-
- if (fw_bss_data_size)
- memcpy(fb_info->fb[DMUB_WINDOW_2_BSS_DATA].cpu_addr,
- fw_bss_data, fw_bss_data_size);
-
- /* Copy firmware bios info into FB memory. */
- memcpy(fb_info->fb[DMUB_WINDOW_3_VBIOS].cpu_addr, adev->bios,
- adev->bios_size);
-
- /* Reset regions that need to be reset. */
- memset(fb_info->fb[DMUB_WINDOW_4_MAILBOX].cpu_addr, 0,
- fb_info->fb[DMUB_WINDOW_4_MAILBOX].size);
-
- memset(fb_info->fb[DMUB_WINDOW_5_TRACEBUFF].cpu_addr, 0,
- fb_info->fb[DMUB_WINDOW_5_TRACEBUFF].size);
-
- memset(fb_info->fb[DMUB_WINDOW_6_FW_STATE].cpu_addr, 0,
- fb_info->fb[DMUB_WINDOW_6_FW_STATE].size);
-
- memset(fb_info->fb[DMUB_WINDOW_SHARED_STATE].cpu_addr, 0,
- fb_info->fb[DMUB_WINDOW_SHARED_STATE].size);
-
- /* Initialize hardware. */
- memset(&hw_params, 0, sizeof(hw_params));
- hw_params.soc_fb_info.fb_base = adev->gmc.fb_start;
- hw_params.soc_fb_info.fb_offset = adev->vm_manager.vram_base_offset;
-
- /* backdoor load firmware and trigger dmub running */
- if (adev->firmware.load_type != AMDGPU_FW_LOAD_PSP)
- hw_params.load_inst_const = true;
-
- if (dmcu)
- hw_params.psp_version = dmcu->psp_version;
-
- for (i = 0; i < fb_info->num_fb; ++i)
- hw_params.fb[i] = &fb_info->fb[i];
-
- /* Enable usb4 dpia in the FW APU */
- if (dc->caps.is_apu &&
- dc->res_pool->usb4_dpia_count != 0 &&
- !dc->debug.dpia_debug.bits.disable_dpia) {
- hw_params.dpia_supported = true;
- hw_params.disable_dpia = dc->debug.dpia_debug.bits.disable_dpia;
- hw_params.dpia_hpd_int_enable_supported = false;
- hw_params.enable_non_transparent_setconfig = dc->config.consolidated_dpia_dp_lt;
- hw_params.disable_dpia_bw_allocation = !dc->config.usb4_bw_alloc_support;
- }
-
- switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
- case IP_VERSION(3, 5, 0):
- case IP_VERSION(3, 5, 1):
- case IP_VERSION(3, 6, 0):
- case IP_VERSION(4, 2, 0):
- case IP_VERSION(4, 2, 1):
- hw_params.ips_sequential_ono = adev->external_rev_id > 0x10;
- hw_params.lower_hbr3_phy_ssc = true;
- break;
- default:
- break;
- }
-
- status = dmub_srv_hw_init(dmub_srv, &hw_params);
- if (status != DMUB_STATUS_OK) {
- drm_err(adev_to_drm(adev), "Error initializing DMUB HW: %d\n", status);
- return -EINVAL;
- }
-
- /* Wait for firmware load to finish. */
- status = dmub_srv_wait_for_auto_load(dmub_srv, 100000);
- if (status != DMUB_STATUS_OK)
- drm_warn(adev_to_drm(adev), "Wait for DMUB auto-load failed: %d\n", status);
-
- /* Init DMCU and ABM if available. */
- if (dmcu && abm) {
- dmcu->funcs->dmcu_init(dmcu);
- abm->dmcu_is_running = dmcu->funcs->is_dmcu_initialized(dmcu);
- }
-
- if (!adev->dm.dc->ctx->dmub_srv)
- adev->dm.dc->ctx->dmub_srv = dc_dmub_srv_create(adev->dm.dc, dmub_srv);
- if (!adev->dm.dc->ctx->dmub_srv) {
- drm_err(adev_to_drm(adev), "Couldn't allocate DC DMUB server!\n");
- return -ENOMEM;
- }
-
- drm_info(adev_to_drm(adev), "DMUB hardware initialized: version=0x%08X\n",
- adev->dm.dmcub_fw_version);
-
- /* Keeping sanity checks off if
- * DCN31 >= 4.0.59.0
- * DCN314 >= 8.0.16.0
- * Otherwise, turn on sanity checks
- */
- switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
- case IP_VERSION(3, 1, 2):
- case IP_VERSION(3, 1, 3):
- if (adev->dm.dmcub_fw_version &&
- adev->dm.dmcub_fw_version >= DMUB_FW_VERSION(4, 0, 0) &&
- adev->dm.dmcub_fw_version < DMUB_FW_VERSION(4, 0, 59))
- adev->dm.dc->debug.sanity_checks = true;
- break;
- case IP_VERSION(3, 1, 4):
- if (adev->dm.dmcub_fw_version &&
- adev->dm.dmcub_fw_version >= DMUB_FW_VERSION(4, 0, 0) &&
- adev->dm.dmcub_fw_version < DMUB_FW_VERSION(8, 0, 16))
- adev->dm.dc->debug.sanity_checks = true;
- break;
- default:
- break;
- }
-
- return 0;
- }
-
- static void dm_dmub_hw_resume(struct amdgpu_device *adev)
- {
- struct dmub_srv *dmub_srv = adev->dm.dmub_srv;
- enum dmub_status status;
- bool init;
- int r;
-
- if (!dmub_srv) {
- /* DMUB isn't supported on the ASIC. */
- return;
- }
-
- status = dmub_srv_is_hw_init(dmub_srv, &init);
- if (status != DMUB_STATUS_OK)
- drm_warn(adev_to_drm(adev), "DMUB hardware init check failed: %d\n", status);
-
- if (status == DMUB_STATUS_OK && init) {
- /* Wait for firmware load to finish. */
- status = dmub_srv_wait_for_auto_load(dmub_srv, 100000);
- if (status != DMUB_STATUS_OK)
- drm_warn(adev_to_drm(adev), "Wait for DMUB auto-load failed: %d\n", status);
- } else {
- /* Perform the full hardware initialization. */
- r = dm_dmub_hw_init(adev);
- if (r)
- drm_err(adev_to_drm(adev), "DMUB interface failed to initialize: status=%d\n", r);
- }
- }
-
static void mmhub_read_system_context(struct amdgpu_device *adev, struct dc_phy_addr_space_config *pa_config)
{
u64 pt_base;
@@@ -1599,349 -394,6 +394,6 @@@
}
- static void force_connector_state(
- struct amdgpu_dm_connector *aconnector,
- enum drm_connector_force force_state)
- {
- struct drm_connector *connector = &aconnector->base;
-
- mutex_lock(&connector->dev->mode_config.mutex);
- aconnector->base.force = force_state;
- mutex_unlock(&connector->dev->mode_config.mutex);
-
- mutex_lock(&aconnector->hpd_lock);
- drm_kms_helper_connector_hotplug_event(connector);
- mutex_unlock(&aconnector->hpd_lock);
- }
-
- static void dm_handle_hpd_rx_offload_work(struct work_struct *work)
- {
- struct hpd_rx_irq_offload_work *offload_work;
- struct amdgpu_dm_connector *aconnector;
- struct dc_link *dc_link;
- struct amdgpu_device *adev;
- enum dc_connection_type new_connection_type = dc_connection_none;
- unsigned long flags;
- union test_response test_response;
-
- memset(&test_response, 0, sizeof(test_response));
-
- offload_work = container_of(work, struct hpd_rx_irq_offload_work, work);
- aconnector = offload_work->offload_wq->aconnector;
- adev = offload_work->adev;
-
- if (!aconnector) {
- drm_err(adev_to_drm(adev), "Can't retrieve aconnector in hpd_rx_irq_offload_work");
- goto skip;
- }
-
- dc_link = aconnector->dc_link;
-
- mutex_lock(&aconnector->hpd_lock);
- if (!dc_link_detect_connection_type(dc_link, &new_connection_type))
- drm_err(adev_to_drm(adev), "KMS: Failed to detect connector\n");
- mutex_unlock(&aconnector->hpd_lock);
-
- if (new_connection_type == dc_connection_none)
- goto skip;
-
- if (amdgpu_in_reset(adev))
- goto skip;
-
- if (offload_work->data.bytes.device_service_irq.bits.UP_REQ_MSG_RDY ||
- offload_work->data.bytes.device_service_irq.bits.DOWN_REP_MSG_RDY) {
- dm_handle_mst_sideband_msg_ready_event(&aconnector->mst_mgr, DOWN_OR_UP_MSG_RDY_EVENT);
- spin_lock_irqsave(&offload_work->offload_wq->offload_lock, flags);
- offload_work->offload_wq->is_handling_mst_msg_rdy_event = false;
- spin_unlock_irqrestore(&offload_work->offload_wq->offload_lock, flags);
- goto skip;
- }
-
- mutex_lock(&adev->dm.dc_lock);
- if (offload_work->data.bytes.device_service_irq.bits.AUTOMATED_TEST) {
- dc_link_dp_handle_automated_test(dc_link);
-
- if (aconnector->timing_changed) {
- /* force connector disconnect and reconnect */
- force_connector_state(aconnector, DRM_FORCE_OFF);
- msleep(100);
- force_connector_state(aconnector, DRM_FORCE_UNSPECIFIED);
- }
-
- test_response.bits.ACK = 1;
-
- core_link_write_dpcd(
- dc_link,
- DP_TEST_RESPONSE,
- &test_response.raw,
- sizeof(test_response));
- } else if ((dc_link->connector_signal != SIGNAL_TYPE_EDP) &&
- dc_link_check_link_loss_status(dc_link, &offload_work->data) &&
- dc_link_dp_allow_hpd_rx_irq(dc_link)) {
- /* offload_work->data is from handle_hpd_rx_irq->
- * schedule_hpd_rx_offload_work.this is defer handle
- * for hpd short pulse. upon here, link status may be
- * changed, need get latest link status from dpcd
- * registers. if link status is good, skip run link
- * training again.
- */
- union hpd_irq_data irq_data;
-
- memset(&irq_data, 0, sizeof(irq_data));
-
- /* before dc_link_dp_handle_link_loss, allow new link lost handle
- * request be added to work queue if link lost at end of dc_link_
- * dp_handle_link_loss
- */
- spin_lock_irqsave(&offload_work->offload_wq->offload_lock, flags);
- offload_work->offload_wq->is_handling_link_loss = false;
- spin_unlock_irqrestore(&offload_work->offload_wq->offload_lock, flags);
-
- if ((dc_link_dp_read_hpd_rx_irq_data(dc_link, &irq_data) == DC_OK) &&
- dc_link_check_link_loss_status(dc_link, &irq_data))
- dc_link_dp_handle_link_loss(dc_link);
- }
- mutex_unlock(&adev->dm.dc_lock);
-
- skip:
- kfree(offload_work);
-
- }
-
- static struct hpd_rx_irq_offload_work_queue *hpd_rx_irq_create_workqueue(struct amdgpu_device *adev)
- {
- struct dc *dc = adev->dm.dc;
- int max_caps = dc->caps.max_links;
- int i = 0;
- struct hpd_rx_irq_offload_work_queue *hpd_rx_offload_wq = NULL;
-
- hpd_rx_offload_wq = kzalloc_objs(*hpd_rx_offload_wq, max_caps);
-
- if (!hpd_rx_offload_wq)
- return NULL;
-
-
- for (i = 0; i < max_caps; i++) {
- hpd_rx_offload_wq[i].wq =
- create_singlethread_workqueue("amdgpu_dm_hpd_rx_offload_wq");
-
- if (hpd_rx_offload_wq[i].wq == NULL) {
- drm_err(adev_to_drm(adev), "create amdgpu_dm_hpd_rx_offload_wq fail!");
- goto out_err;
- }
-
- spin_lock_init(&hpd_rx_offload_wq[i].offload_lock);
- }
-
- return hpd_rx_offload_wq;
-
- out_err:
- for (i = 0; i < max_caps; i++) {
- if (hpd_rx_offload_wq[i].wq)
- destroy_workqueue(hpd_rx_offload_wq[i].wq);
- }
- kfree(hpd_rx_offload_wq);
- return NULL;
- }
-
- struct amdgpu_stutter_quirk {
- u16 chip_vendor;
- u16 chip_device;
- u16 subsys_vendor;
- u16 subsys_device;
- u8 revision;
- };
-
- static const struct amdgpu_stutter_quirk amdgpu_stutter_quirk_list[] = {
- /* https://bugzilla.kernel.org/show_bug.cgi?id=214417 */
- { 0x1002, 0x15dd, 0x1002, 0x15dd, 0xc8 },
- { 0, 0, 0, 0, 0 },
- };
-
- static bool dm_should_disable_stutter(struct pci_dev *pdev)
- {
- const struct amdgpu_stutter_quirk *p = amdgpu_stutter_quirk_list;
-
- while (p && p->chip_device != 0) {
- if (pdev->vendor == p->chip_vendor &&
- pdev->device == p->chip_device &&
- pdev->subsystem_vendor == p->subsys_vendor &&
- pdev->subsystem_device == p->subsys_device &&
- pdev->revision == p->revision) {
- return true;
- }
- ++p;
- }
- return false;
- }
-
-
- void*
- dm_allocate_gpu_mem(
- struct amdgpu_device *adev,
- enum dc_gpu_mem_alloc_type type,
- size_t size,
- long long *addr)
- {
- struct dal_allocation *da;
- u32 domain = (type == DC_MEM_ALLOC_TYPE_GART) ?
- AMDGPU_GEM_DOMAIN_GTT : AMDGPU_GEM_DOMAIN_VRAM;
- int ret;
-
- da = kzalloc_obj(struct dal_allocation);
- if (!da)
- return NULL;
-
- ret = amdgpu_bo_create_kernel(adev, size, PAGE_SIZE,
- domain, &da->bo,
- &da->gpu_addr, &da->cpu_ptr);
-
- *addr = da->gpu_addr;
-
- if (ret) {
- kfree(da);
- return NULL;
- }
-
- /* add da to list in dm */
- list_add(&da->list, &adev->dm.da_list);
-
- return da->cpu_ptr;
- }
-
- void
- dm_free_gpu_mem(
- struct amdgpu_device *adev,
- enum dc_gpu_mem_alloc_type type,
- void *pvMem)
- {
- struct dal_allocation *da;
-
- /* walk the da list in DM */
- list_for_each_entry(da, &adev->dm.da_list, list) {
- if (pvMem == da->cpu_ptr) {
- amdgpu_bo_free_kernel(&da->bo, &da->gpu_addr, &da->cpu_ptr);
- list_del(&da->list);
- kfree(da);
- break;
- }
- }
-
- }
-
- static enum dmub_status
- dm_dmub_send_vbios_gpint_command(struct amdgpu_device *adev,
- enum dmub_gpint_command command_code,
- uint16_t param,
- uint32_t timeout_us)
- {
- union dmub_gpint_data_register reg, test;
- uint32_t i;
-
- /* Assume that VBIOS DMUB is ready to take commands */
-
- reg.bits.status = 1;
- reg.bits.command_code = command_code;
- reg.bits.param = param;
-
- cgs_write_register(adev->dm.cgs_device, 0x34c0 + 0x01f8, reg.all);
-
- for (i = 0; i < timeout_us; ++i) {
- udelay(1);
-
- /* Check if our GPINT got acked */
- reg.bits.status = 0;
- test = (union dmub_gpint_data_register)
- cgs_read_register(adev->dm.cgs_device, 0x34c0 + 0x01f8);
-
- if (test.all == reg.all)
- return DMUB_STATUS_OK;
- }
-
- return DMUB_STATUS_TIMEOUT;
- }
-
- static void *dm_dmub_get_vbios_bounding_box(struct amdgpu_device *adev)
- {
- void *bb;
- long long addr;
- unsigned int bb_size;
- int i = 0;
- uint16_t chunk;
- enum dmub_gpint_command send_addrs[] = {
- DMUB_GPINT__SET_BB_ADDR_WORD0,
- DMUB_GPINT__SET_BB_ADDR_WORD1,
- DMUB_GPINT__SET_BB_ADDR_WORD2,
- DMUB_GPINT__SET_BB_ADDR_WORD3,
- };
- enum dmub_status ret;
-
- switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
- case IP_VERSION(4, 0, 1):
- bb_size = sizeof(struct dml2_soc_bb);
- break;
- case IP_VERSION(4, 2, 0):
- case IP_VERSION(4, 2, 1):
- bb_size = sizeof(struct dml2_soc_bb);
- break;
- default:
- return NULL;
- }
-
- bb = dm_allocate_gpu_mem(adev,
- DC_MEM_ALLOC_TYPE_GART,
- bb_size,
- &addr);
- if (!bb)
- return NULL;
-
- for (i = 0; i < 4; i++) {
- /* Extract 16-bit chunk */
- chunk = ((uint64_t) addr >> (i * 16)) & 0xFFFF;
- /* Send the chunk */
- ret = dm_dmub_send_vbios_gpint_command(adev, send_addrs[i], chunk, 30000);
- if (ret != DMUB_STATUS_OK)
- goto free_bb;
- }
-
- /* Now ask DMUB to copy the bb */
- ret = dm_dmub_send_vbios_gpint_command(adev, DMUB_GPINT__BB_COPY, 1, 200000);
- if (ret != DMUB_STATUS_OK)
- goto free_bb;
-
- return bb;
-
- free_bb:
- dm_free_gpu_mem(adev, DC_MEM_ALLOC_TYPE_GART, (void *) bb);
- return NULL;
-
- }
-
- static enum dmub_ips_disable_type dm_get_default_ips_mode(
- struct amdgpu_device *adev)
- {
- enum dmub_ips_disable_type ret = DMUB_IPS_ENABLE;
-
- switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
- case IP_VERSION(3, 5, 0):
- case IP_VERSION(3, 6, 0):
- case IP_VERSION(3, 5, 1):
- ret = DMUB_IPS_RCG_IN_ACTIVE_IPS2_IN_OFF;
- break;
- case IP_VERSION(4, 2, 0):
- case IP_VERSION(4, 2, 1):
- ret = DMUB_IPS_ENABLE;
- break;
- default:
- /* ASICs older than DCN35 do not have IPSs */
- if (amdgpu_ip_version(adev, DCE_HWIP, 0) < IP_VERSION(3, 5, 0))
- ret = DMUB_IPS_DISABLE_ALL;
- break;
- }
-
- return ret;
- }
-
static int amdgpu_dm_init_power_module(struct amdgpu_display_manager *dm)
{
struct mod_power_init_params init_data[MAX_NUM_EDP];
@@@ -2006,40 -458,6 +458,6 @@@
return 0;
}
- static void hdmi_frl_status_polling_work(struct work_struct *work)
- {
- struct amdgpu_display_manager *dm =
- container_of(to_delayed_work(work), struct amdgpu_display_manager,
- hdmi_frl_status_polling_work);
- struct dc *dc = dm->dc;
- struct dc_link *dc_link;
- bool link_update = false;
-
- for (int i = 0; i < MAX_LINKS; i++) {
- dc_link = dc->links[i];
-
- if (!dc_link || !dc_link->local_sink)
- continue;
-
- if (!dc_is_hdmi_signal(dc_link->connector_signal))
- continue;
-
- if (dc_link->connector_signal != SIGNAL_TYPE_HDMI_FRL)
- continue;
-
- link_update = dc_link_frl_poll_status_flag(dc_link);
- if (link_update) {
- mutex_lock(&dm->dc_lock);
- dc_link_detect(dc_link, DETECT_REASON_RETRAIN);
- mutex_unlock(&dm->dc_lock);
- }
- }
-
- queue_delayed_work(dm->hdmi_frl_status_polling_wq,
- &dm->hdmi_frl_status_polling_work,
- msecs_to_jiffies(dm->hdmi_frl_status_polling_delay_ms));
- }
-
static int amdgpu_dm_init(struct amdgpu_device *adev)
{
struct dc_init_data init_data;
@@@ -2266,7 -684,15 +684,15 @@@
dc_hardware_init(adev->dm.dc);
- adev->dm.hpd_rx_offload_wq = hpd_rx_irq_create_workqueue(adev);
+ /* GOP/vBIOS may leave an OPTC enabled for a display present at power-on
+ * but no longer driven (e.g. an external DP unplugged at boot). Such a
+ * dangling pipe keeps DCN out of idle and blocks s0i3. Power it down
+ * here when nothing needs to be preserved.
+ */
+ if (adev->flags & AMD_IS_APU)
+ dc_disable_dangling_timing_generators(adev->dm.dc);
+
+ adev->dm.hpd_rx_offload_wq = amdgpu_dm_hpd_rx_irq_create_workqueue(adev);
if (!adev->dm.hpd_rx_offload_wq) {
drm_err(adev_to_drm(adev), "failed to create hpd rx offload workqueue.\n");
goto error;
@@@ -2319,8 -745,6 +745,6 @@@
create_singlethread_workqueue("hdmi_frl_status_polling_workqueue");
if (!adev->dm.hdmi_frl_status_polling_wq)
drm_err(adev_to_drm(adev), "failed to initialize hdmi_frl_status_polling_workqueue\n");
- adev->dm.hdmi_frl_status_polling_delay_ms = 200;
- INIT_DELAYED_WORK(&adev->dm.hdmi_frl_status_polling_work, hdmi_frl_status_polling_work);
}
if (dc_is_dmub_outbox_supported(adev->dm.dc)) {
init_completion(&adev->dm.dmub_aux_transfer_done);
@@@ -2337,8 -761,8 +761,8 @@@
}
amdgpu_dm_outbox_init(adev);
- if (!register_dmub_notify_callback(adev, DMUB_NOTIFICATION_AUX_REPLY,
- dmub_aux_setconfig_callback, false)) {
+ if (!dm_register_dmub_notify_callback(adev, DMUB_NOTIFICATION_AUX_REPLY,
+ dm_dmub_aux_setconfig_callback, false)) {
drm_err(adev_to_drm(adev), "fail to register dmub aux callback");
goto error;
}
@@@ -2346,16 -770,17 +770,17 @@@
for (size_t i = 0; i < ARRAY_SIZE(adev->dm.fused_io); i++)
init_completion(&adev->dm.fused_io[i].replied);
- if (!register_dmub_notify_callback(adev, DMUB_NOTIFICATION_FUSED_IO,
- dmub_aux_fused_io_callback, false)) {
+ if (!dm_register_dmub_notify_callback(adev, DMUB_NOTIFICATION_FUSED_IO,
+ dm_dmub_aux_fused_io_callback, false)) {
drm_err(adev_to_drm(adev), "fail to register dmub fused io callback");
goto error;
}
/* Enable outbox notification only after IRQ handlers are registered and DMUB is alive.
* It is expected that DMUB will resend any pending notifications at this point. Note
- * that hpd and hpd_irq handler registration are deferred to register_hpd_handlers() to
- * align legacy interface initialization sequence. Connection status will be proactivly
- * detected once in the amdgpu_dm_initialize_drm_device.
+ * that hpd and hpd_irq handler registration are deferred to
+ * amdgpu_dm_register_hpd_handlers() to align legacy interface initialization
+ * sequence. Connection status will be proactivly detected once in the
+ * amdgpu_dm_initialize_drm_device.
*/
dc_enable_dmub_outbox(adev->dm.dc);
@@@ -2498,14 -923,11 +923,11 @@@ static void amdgpu_dm_fini(struct amdgp
adev->dm.hpd_rx_offload_wq = NULL;
}
+ amdgpu_dm_irq_fini(adev);
+
/* DC Destroy TODO: Replace destroy DAL */
if (adev->dm.dc)
dc_destroy(&adev->dm.dc);
- /*
- * TODO: pageflip, vlank interrupt
- *
- * amdgpu_dm_irq_fini(adev);
- */
if (adev->dm.cgs_device) {
amdgpu_cgs_destroy_device(adev->dm.cgs_device);
@@@ -2635,224 -1057,6 +1057,6 @@@ static int load_dmcu_fw(struct amdgpu_d
return 0;
}
- static uint32_t amdgpu_dm_dmub_reg_read(void *ctx, uint32_t address)
- {
- struct amdgpu_device *adev = ctx;
-
- return dm_read_reg(adev->dm.dc->ctx, address);
- }
-
- static void amdgpu_dm_dmub_reg_write(void *ctx, uint32_t address,
- uint32_t value)
- {
- struct amdgpu_device *adev = ctx;
-
- return dm_write_reg(adev->dm.dc->ctx, address, value);
- }
-
- static int dm_dmub_sw_init(struct amdgpu_device *adev)
- {
- struct dmub_srv_create_params create_params;
- struct dmub_srv_fw_meta_info_params fw_meta_info_params;
- struct dmub_srv_region_params region_params;
- struct dmub_srv_region_info region_info;
- struct dmub_srv_memory_params memory_params;
- struct dmub_fw_meta_info fw_info;
- struct dmub_srv_fb_info *fb_info;
- struct dmub_srv *dmub_srv;
- const struct dmcub_firmware_header_v1_0 *hdr;
- enum dmub_asic dmub_asic;
- enum dmub_status status;
- static enum dmub_window_memory_type window_memory_type[DMUB_WINDOW_TOTAL] = {
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_0_INST_CONST
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_1_STACK
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_2_BSS_DATA
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_3_VBIOS
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_4_MAILBOX
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_5_TRACEBUFF
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_6_FW_STATE
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_7_SCRATCH_MEM
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_IB_MEM
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_SHARED_STATE
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_LSDMA_BUFFER
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_CURSOR_OFFLOAD
- };
- int r;
-
- switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
- case IP_VERSION(2, 1, 0):
- dmub_asic = DMUB_ASIC_DCN21;
- break;
- case IP_VERSION(3, 0, 0):
- dmub_asic = DMUB_ASIC_DCN30;
- break;
- case IP_VERSION(3, 0, 1):
- dmub_asic = DMUB_ASIC_DCN301;
- break;
- case IP_VERSION(3, 0, 2):
- dmub_asic = DMUB_ASIC_DCN302;
- break;
- case IP_VERSION(3, 0, 3):
- dmub_asic = DMUB_ASIC_DCN303;
- break;
- case IP_VERSION(3, 1, 2):
- case IP_VERSION(3, 1, 3):
- dmub_asic = (adev->external_rev_id == YELLOW_CARP_B0) ? DMUB_ASIC_DCN31B : DMUB_ASIC_DCN31;
- break;
- case IP_VERSION(3, 1, 4):
- dmub_asic = DMUB_ASIC_DCN314;
- break;
- case IP_VERSION(3, 1, 5):
- dmub_asic = DMUB_ASIC_DCN315;
- break;
- case IP_VERSION(3, 1, 6):
- dmub_asic = DMUB_ASIC_DCN316;
- break;
- case IP_VERSION(3, 2, 0):
- dmub_asic = DMUB_ASIC_DCN32;
- break;
- case IP_VERSION(3, 2, 1):
- dmub_asic = DMUB_ASIC_DCN321;
- break;
- case IP_VERSION(3, 5, 0):
- case IP_VERSION(3, 5, 1):
- dmub_asic = DMUB_ASIC_DCN35;
- break;
- case IP_VERSION(3, 6, 0):
- dmub_asic = DMUB_ASIC_DCN36;
- break;
- case IP_VERSION(4, 0, 1):
- dmub_asic = DMUB_ASIC_DCN401;
- break;
- case IP_VERSION(4, 2, 0):
- dmub_asic = DMUB_ASIC_DCN42;
- break;
- case IP_VERSION(4, 2, 1):
- dmub_asic = DMUB_ASIC_DCN42B;
- break;
- default:
- /* ASIC doesn't support DMUB. */
- return 0;
- }
-
- hdr = (const struct dmcub_firmware_header_v1_0 *)adev->dm.dmub_fw->data;
- adev->dm.dmcub_fw_version = le32_to_cpu(hdr->header.ucode_version);
-
- if (adev->firmware.load_type == AMDGPU_FW_LOAD_PSP) {
- adev->firmware.ucode[AMDGPU_UCODE_ID_DMCUB].ucode_id =
- AMDGPU_UCODE_ID_DMCUB;
- adev->firmware.ucode[AMDGPU_UCODE_ID_DMCUB].fw =
- adev->dm.dmub_fw;
- adev->firmware.fw_size +=
- ALIGN(le32_to_cpu(hdr->inst_const_bytes), PAGE_SIZE);
-
- drm_info(adev_to_drm(adev), "Loading DMUB firmware via PSP: version=0x%08X\n",
- adev->dm.dmcub_fw_version);
- }
-
-
- adev->dm.dmub_srv = kzalloc_obj(*adev->dm.dmub_srv);
- dmub_srv = adev->dm.dmub_srv;
-
- if (!dmub_srv) {
- drm_err(adev_to_drm(adev), "Failed to allocate DMUB service!\n");
- return -ENOMEM;
- }
-
- memset(&create_params, 0, sizeof(create_params));
- create_params.user_ctx = adev;
- create_params.funcs.reg_read = amdgpu_dm_dmub_reg_read;
- create_params.funcs.reg_write = amdgpu_dm_dmub_reg_write;
- create_params.asic = dmub_asic;
-
- /* Create the DMUB service. */
- status = dmub_srv_create(dmub_srv, &create_params);
- if (status != DMUB_STATUS_OK) {
- drm_err(adev_to_drm(adev), "Error creating DMUB service: %d\n", status);
- return -EINVAL;
- }
-
- /* Extract the FW meta info. */
- memset(&fw_meta_info_params, 0, sizeof(fw_meta_info_params));
-
- fw_meta_info_params.inst_const_size = le32_to_cpu(hdr->inst_const_bytes) -
- PSP_HEADER_BYTES_256;
- fw_meta_info_params.bss_data_size = le32_to_cpu(hdr->bss_data_bytes);
- fw_meta_info_params.fw_inst_const = adev->dm.dmub_fw->data +
- le32_to_cpu(hdr->header.ucode_array_offset_bytes) +
- PSP_HEADER_BYTES_256;
- fw_meta_info_params.fw_bss_data = fw_meta_info_params.bss_data_size ? adev->dm.dmub_fw->data +
- le32_to_cpu(hdr->header.ucode_array_offset_bytes) +
- le32_to_cpu(hdr->inst_const_bytes) : NULL;
- fw_meta_info_params.custom_psp_footer_size = 0;
-
- status = dmub_srv_get_fw_meta_info_from_raw_fw(&fw_meta_info_params, &fw_info);
- if (status != DMUB_STATUS_OK) {
- /* Skip returning early, just log the error. */
- drm_err(adev_to_drm(adev), "Error getting DMUB FW meta info: %d\n", status);
- // return -EINVAL;
- }
-
- /* Calculate the size of all the regions for the DMUB service. */
- memset(®ion_params, 0, sizeof(region_params));
-
- region_params.inst_const_size = fw_meta_info_params.inst_const_size;
- region_params.bss_data_size = fw_meta_info_params.bss_data_size;
- region_params.vbios_size = adev->bios_size;
- region_params.fw_bss_data = fw_meta_info_params.fw_bss_data;
- region_params.fw_inst_const = fw_meta_info_params.fw_inst_const;
- region_params.window_memory_type = window_memory_type;
- region_params.fw_info = (status == DMUB_STATUS_OK) ? &fw_info : NULL;
-
- status = dmub_srv_calc_region_info(dmub_srv, ®ion_params,
- ®ion_info);
-
- if (status != DMUB_STATUS_OK) {
- drm_err(adev_to_drm(adev), "Error calculating DMUB region info: %d\n", status);
- return -EINVAL;
- }
-
- /*
- * Allocate a framebuffer based on the total size of all the regions.
- * TODO: Move this into GART.
- */
- r = amdgpu_bo_create_kernel(adev, region_info.fb_size, PAGE_SIZE,
- AMDGPU_GEM_DOMAIN_VRAM |
- AMDGPU_GEM_DOMAIN_GTT,
- &adev->dm.dmub_bo,
- &adev->dm.dmub_bo_gpu_addr,
- &adev->dm.dmub_bo_cpu_addr);
- if (r)
- return r;
-
- /* Rebase the regions on the framebuffer address. */
- memset(&memory_params, 0, sizeof(memory_params));
- memory_params.cpu_fb_addr = adev->dm.dmub_bo_cpu_addr;
- memory_params.gpu_fb_addr = adev->dm.dmub_bo_gpu_addr;
- memory_params.region_info = ®ion_info;
- memory_params.window_memory_type = window_memory_type;
-
- adev->dm.dmub_fb_info = kzalloc_obj(*adev->dm.dmub_fb_info);
- fb_info = adev->dm.dmub_fb_info;
-
- if (!fb_info) {
- drm_err(adev_to_drm(adev),
- "Failed to allocate framebuffer info for DMUB service!\n");
- return -ENOMEM;
- }
-
- status = dmub_srv_calc_mem_info(dmub_srv, &memory_params, fb_info);
- if (status != DMUB_STATUS_OK) {
- drm_err(adev_to_drm(adev), "Error calculating DMUB FB info: %d\n", status);
- return -EINVAL;
- }
-
- adev->dm.bb_from_dmub = dm_dmub_get_vbios_bounding_box(adev);
- adev->dm.fw_inst_size = fw_meta_info_params.inst_const_size;
-
- return 0;
- }
-
static int dm_sw_init(struct amdgpu_ip_block *ip_block)
{
struct amdgpu_device *adev = ip_block->adev;
@@@ -2906,41 -1110,6 +1110,6 @@@ static int dm_sw_fini(struct amdgpu_ip_
return 0;
}
- static int detect_mst_link_for_all_connectors(struct drm_device *dev)
- {
- struct amdgpu_dm_connector *aconnector;
- struct drm_connector *connector;
- struct drm_connector_list_iter iter;
- int ret = 0;
-
- drm_connector_list_iter_begin(dev, &iter);
- drm_for_each_connector_iter(connector, &iter) {
-
- if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
- continue;
-
- aconnector = to_amdgpu_dm_connector(connector);
- if (aconnector->dc_link->type == dc_connection_mst_branch &&
- aconnector->mst_mgr.aux) {
- drm_dbg_kms(dev, "DM_MST: starting TM on aconnector: %p [id: %d]\n",
- aconnector,
- aconnector->base.base.id);
-
- ret = drm_dp_mst_topology_mgr_set_mst(&aconnector->mst_mgr, true);
- if (ret < 0) {
- drm_err(dev, "DM_MST: Failed to start MST\n");
- aconnector->dc_link->type =
- dc_connection_single;
- ret = dm_helpers_dp_mst_stop_top_mgr(aconnector->dc_link->ctx,
- aconnector->dc_link);
- break;
- }
- }
- }
- drm_connector_list_iter_end(&iter);
-
- return ret;
- }
static void amdgpu_dm_boot_time_crc_init(struct amdgpu_device *adev)
{
@@@ -3038,7 -1207,7 +1207,7 @@@ static int dm_late_init(struct amdgpu_i
}
}
- return detect_mst_link_for_all_connectors(adev_to_drm(adev));
+ return amdgpu_dm_detect_mst_link_for_all_connectors(adev_to_drm(adev));
}
static void resume_mst_branch_status(struct drm_dp_mst_topology_mgr *mgr)
@@@ -3092,48 -1261,6 +1261,6 @@@ out_fail
mutex_unlock(&mgr->lock);
}
- void hdmi_cec_unset_edid(struct amdgpu_dm_connector *aconnector)
- {
- struct cec_notifier *n = aconnector->notifier;
-
- if (!n)
- return;
-
- cec_notifier_phys_addr_invalidate(n);
- }
-
- void hdmi_cec_set_edid(struct amdgpu_dm_connector *aconnector)
- {
- struct drm_connector *connector = &aconnector->base;
- struct cec_notifier *n = aconnector->notifier;
-
- if (!n)
- return;
-
- cec_notifier_set_phys_addr(n,
- connector->display_info.source_physical_address);
- }
-
- static void s3_handle_hdmi_cec(struct drm_device *ddev, bool suspend)
- {
- struct amdgpu_dm_connector *aconnector;
- struct drm_connector *connector;
- struct drm_connector_list_iter conn_iter;
-
- drm_connector_list_iter_begin(ddev, &conn_iter);
- drm_for_each_connector_iter(connector, &conn_iter) {
- if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
- continue;
-
- aconnector = to_amdgpu_dm_connector(connector);
- if (suspend)
- hdmi_cec_unset_edid(aconnector);
- else
- hdmi_cec_set_edid(aconnector);
- }
- drm_connector_list_iter_end(&conn_iter);
- }
-
static void s3_handle_mst(struct drm_device *dev, bool suspend)
{
struct amdgpu_dm_connector *aconnector;
@@@ -3176,57 -1303,6 +1303,6 @@@
drm_connector_list_iter_end(&iter);
}
- static int amdgpu_dm_smu_write_watermarks_table(struct amdgpu_device *adev)
- {
- int ret = 0;
-
- /* This interface is for dGPU Navi1x.Linux dc-pplib interface depends
- * on window driver dc implementation.
- * For Navi1x, clock settings of dcn watermarks are fixed. the settings
- * should be passed to smu during boot up and resume from s3.
- * boot up: dc calculate dcn watermark clock settings within dc_create,
- * dcn20_resource_construct
- * then call pplib functions below to pass the settings to smu:
- * smu_set_watermarks_for_clock_ranges
- * smu_set_watermarks_table
- * navi10_set_watermarks_table
- * smu_write_watermarks_table
- *
- * For Renoir, clock settings of dcn watermark are also fixed values.
- * dc has implemented different flow for window driver:
- * dc_hardware_init / dc_set_power_state
- * dcn10_init_hw
- * notify_wm_ranges
- * set_wm_ranges
- * -- Linux
- * smu_set_watermarks_for_clock_ranges
- * renoir_set_watermarks_table
- * smu_write_watermarks_table
- *
- * For Linux,
- * dc_hardware_init -> amdgpu_dm_init
- * dc_set_power_state --> dm_resume
- *
- * therefore, this function apply to navi10/12/14 but not Renoir
- * *
- */
- switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
- case IP_VERSION(2, 0, 2):
- case IP_VERSION(2, 0, 0):
- break;
- default:
- return 0;
- }
-
- ret = amdgpu_dpm_write_watermarks_table(adev);
- if (ret) {
- drm_err(adev_to_drm(adev), "Failed to update WMTABLE!\n");
- return ret;
- }
-
- return 0;
- }
-
static int dm_oem_i2c_hw_init(struct amdgpu_device *adev)
{
struct amdgpu_display_manager *dm = &adev->dm;
@@@ -3236,7 -1312,7 +1312,7 @@@
oem_ddc_service = dc_get_oem_i2c_device(adev->dm.dc);
if (oem_ddc_service) {
- oem_i2c = create_i2c(oem_ddc_service, true);
+ oem_i2c = amdgpu_dm_create_i2c(oem_ddc_service, true);
if (!oem_i2c) {
drm_info(adev_to_drm(adev), "Failed to create oem i2c adapter data\n");
return -ENOMEM;
@@@ -3279,17 -1355,26 +1355,26 @@@ static int dm_hw_init(struct amdgpu_ip_
struct amdgpu_device *adev = ip_block->adev;
int r;
+ adev->dm.i2c_devres_group = devres_open_group(adev->dev, NULL, GFP_KERNEL);
+ if (!adev->dm.i2c_devres_group)
+ return -ENOMEM;
+
/* Create DAL display manager */
r = amdgpu_dm_init(adev);
if (r)
- return r;
+ goto err_release_i2c;
amdgpu_dm_hpd_init(adev);
r = dm_oem_i2c_hw_init(adev);
if (r)
drm_info(adev_to_drm(adev), "Failed to add OEM i2c bus\n");
+ devres_close_group(adev->dev, adev->dm.i2c_devres_group);
return 0;
+
+ err_release_i2c:
+ devres_release_group(adev->dev, adev->dm.i2c_devres_group);
+ return r;
}
/**
@@@ -3304,9 -1389,11 +1389,11 @@@ static int dm_hw_fini(struct amdgpu_ip_
{
struct amdgpu_device *adev = ip_block->adev;
+ if (adev->dm.i2c_devres_group)
+ devres_release_group(adev->dev, adev->dm.i2c_devres_group);
+
amdgpu_dm_hpd_fini(adev);
- amdgpu_dm_irq_fini(adev);
amdgpu_dm_fini(adev);
return 0;
}
@@@ -3321,7 -1408,7 +1408,7 @@@ static void dm_gpureset_toggle_interrup
int i = 0;
for (i = 0; i < state->stream_count; i++) {
- acrtc = get_crtc_by_otg_inst(
+ acrtc = amdgpu_dm_get_crtc_by_otg_inst(
adev, state->stream_status[i].primary_otg_inst);
if (acrtc && state->stream_status[i].plane_count != 0 &&
@@@ -3406,16 -1493,6 +1493,6 @@@ static enum dc_status amdgpu_dm_commit_
return dc_commit_streams(dc, ¶ms);
}
- static void hpd_rx_irq_work_suspend(struct amdgpu_display_manager *dm)
- {
- int i;
-
- if (dm->hpd_rx_offload_wq) {
- for (i = 0; i < dm->dc->caps.max_links; i++)
- flush_workqueue(dm->hpd_rx_offload_wq[i].wq);
- }
- }
-
static int dm_cache_state(struct amdgpu_device *adev)
{
int r;
@@@ -3448,7 -1525,7 +1525,7 @@@ static void dm_destroy_cached_state(str
for_each_new_crtc_in_state(dm->cached_state, crtc, new_crtc_state, i) {
new_crtc_state->active_changed = true;
dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
- reset_freesync_config_for_crtc(dm_new_crtc_state);
+ amdgpu_dm_reset_freesync_config_for_crtc(dm_new_crtc_state);
}
/*
@@@ -3511,7 -1588,7 +1588,7 @@@ static int dm_suspend(struct amdgpu_ip_
amdgpu_dm_irq_suspend(adev);
- hpd_rx_irq_work_suspend(dm);
+ amdgpu_dm_hpd_rx_irq_work_suspend(dm);
return 0;
}
@@@ -3523,7 -1600,7 +1600,7 @@@
return r;
}
- s3_handle_hdmi_cec(adev_to_drm(adev), true);
+ amdgpu_dm_s3_handle_hdmi_cec(adev_to_drm(adev), true);
s3_handle_mst(adev_to_drm(adev), true);
@@@ -3537,7 -1614,7 +1614,7 @@@
scoped_guard(mutex, &dm->dc_lock)
amdgpu_dm_ism_force_full_power(dm);
- hpd_rx_irq_work_suspend(dm);
+ amdgpu_dm_hpd_rx_irq_work_suspend(dm);
dc_set_power_state(dm->dc, DC_ACPI_CM_POWER_STATE_D3);
@@@ -3549,26 -1626,7 +1626,7 @@@
return 0;
}
- struct drm_connector *
- amdgpu_dm_find_first_crtc_matching_connector(struct drm_atomic_commit *state,
- struct drm_crtc *crtc)
- {
- u32 i;
- struct drm_connector_state *new_con_state;
- struct drm_connector *connector;
- struct drm_crtc *crtc_from_state;
-
- for_each_new_connector_in_state(state, connector, new_con_state, i) {
- crtc_from_state = new_con_state->crtc;
-
- if (crtc_from_state == crtc)
- return connector;
- }
-
- return NULL;
- }
-
- static void emulated_link_detect(struct dc_link *link)
+ void amdgpu_dm_emulated_link_detect(struct dc_link *link)
{
struct dc_sink_init_data sink_init_data = { 0 };
struct display_sink_capability sink_caps = { 0 };
@@@ -3689,8 -1747,8 +1747,8 @@@ static void dm_gpureset_commit_state(st
}
}
- static void apply_delay_after_dpcd_poweroff(struct amdgpu_device *adev,
- struct dc_sink *sink)
+ void amdgpu_dm_apply_delay_after_dpcd_poweroff(struct amdgpu_device *adev,
+ struct dc_sink *sink)
{
struct dc_panel_patch *ppatch = NULL;
@@@ -3706,6 -1764,7 +1764,7 @@@
ppatch->wait_after_dpcd_poweroff_ms / 1000);
}
}
+ EXPORT_IF_KUNIT(amdgpu_dm_apply_delay_after_dpcd_poweroff);
/**
* amdgpu_dm_dump_links_and_sinks - Debug dump of all DC links and their sinks
@@@ -3833,8 -1892,7 +1892,7 @@@ static int dm_resume(struct amdgpu_ip_b
for (i = 0; i < dc_state->stream_count; i++) {
dc_state->streams[i]->mode_changed = true;
for (j = 0; j < dc_state->stream_status[i].plane_count; j++) {
- dc_state->stream_status[i].plane_states[j]->update_flags.raw
- = 0xffffffff;
+ dc_pipe_update_bits_set_full(&dc_state->stream_status[i].plane_states[j]->update_bits);
}
}
@@@ -3897,7 -1955,7 +1955,7 @@@
*/
amdgpu_dm_irq_resume_early(adev);
- s3_handle_hdmi_cec(ddev, false);
+ amdgpu_dm_s3_handle_hdmi_cec(ddev, false);
/* On resume we need to rewrite the MSTM control bits to enable MST*/
s3_handle_mst(ddev, false);
@@@ -3932,14 -1990,14 +1990,14 @@@
drm_err(adev_to_drm(adev), "KMS: Failed to detect connector\n");
if (aconnector->base.force && new_connection_type == dc_connection_none) {
- emulated_link_detect(aconnector->dc_link);
+ amdgpu_dm_emulated_link_detect(aconnector->dc_link);
} else {
guard(mutex)(&dm->dc_lock);
dc_exit_ips_for_hw_access(dm->dc);
ret = dc_link_detect(aconnector->dc_link, DETECT_REASON_RESUMEFROMS3S4);
if (ret) {
/* w/a delay for certain panels */
- apply_delay_after_dpcd_poweroff(adev, aconnector->dc_sink);
+ amdgpu_dm_apply_delay_after_dpcd_poweroff(adev, aconnector->dc_sink);
}
}
@@@ -4017,7 -2075,6 +2075,6 @@@ static const struct amd_ip_funcs amdgpu
.resume = dm_resume,
.is_idle = dm_is_idle,
.wait_for_idle = dm_wait_for_idle,
- .check_soft_reset = dm_check_soft_reset,
.soft_reset = dm_soft_reset,
.set_clockgating_state = dm_set_clockgating_state,
.set_powergating_state = dm_set_powergating_state,
@@@ -4050,1004 -2107,6 +2107,6 @@@ static struct drm_mode_config_helper_fu
.atomic_commit_setup = amdgpu_dm_atomic_setup_commit,
};
- #define DDC_MANUFACTURERNAME_SAMSUNG 0x2D4C
-
- static void dm_set_panel_type(struct amdgpu_dm_connector *aconnector)
- {
- struct drm_connector *connector = &aconnector->base;
- struct drm_display_info *display_info = &connector->display_info;
- struct dc_link *link = aconnector->dc_link;
- struct amdgpu_device *adev;
-
- adev = drm_to_adev(connector->dev);
-
- link->panel_type = PANEL_TYPE_NONE;
-
- switch (display_info->amd_vsdb.panel_type) {
- case AMD_VSDB_PANEL_TYPE_OLED:
- link->panel_type = PANEL_TYPE_OLED;
- break;
- case AMD_VSDB_PANEL_TYPE_MINILED:
- link->panel_type = PANEL_TYPE_MINILED;
- break;
- }
-
- /* If VSDB didn't determine panel type, check DPCD ext caps */
- if (link->panel_type == PANEL_TYPE_NONE) {
- if (link->dpcd_sink_ext_caps.bits.miniled == 1)
- link->panel_type = PANEL_TYPE_MINILED;
- if (link->dpcd_sink_ext_caps.bits.oled == 1)
- link->panel_type = PANEL_TYPE_OLED;
- }
-
- /*
- * TODO: get panel type from DID2 that has device technology field
- * to specify if it's OLED or not. But we need to wait for DID2
- * support in DC and EDID parser to be able to use it here.
- */
-
- if (link->panel_type == PANEL_TYPE_NONE) {
- struct drm_amd_vsdb_info *vsdb = &display_info->amd_vsdb;
- u32 lum1_max = vsdb->luminance_range1.max_luminance;
- u32 lum2_max = vsdb->luminance_range2.max_luminance;
-
- if (vsdb->version && link->local_sink &&
- link->local_sink->edid_caps.manufacturer_id ==
- DDC_MANUFACTURERNAME_SAMSUNG &&
- lum1_max >= ((lum2_max * 3) / 2))
- link->panel_type = PANEL_TYPE_MINILED;
- }
-
- if (link->panel_type == PANEL_TYPE_OLED)
- drm_object_property_set_value(&connector->base,
- adev_to_drm(adev)->mode_config.panel_type_property,
- DRM_MODE_PANEL_TYPE_OLED);
- else
- drm_object_property_set_value(&connector->base,
- adev_to_drm(adev)->mode_config.panel_type_property,
- DRM_MODE_PANEL_TYPE_UNKNOWN);
-
- drm_dbg_kms(aconnector->base.dev, "Panel type: %d\n", link->panel_type);
- }
-
- static void update_connector_ext_caps(struct amdgpu_dm_connector *aconnector)
- {
- const struct drm_panel_backlight_quirk *panel_backlight_quirk;
- struct amdgpu_dm_backlight_caps *caps;
- struct drm_connector *conn_base;
- struct amdgpu_device *adev;
- struct drm_luminance_range_info *luminance_range;
- struct drm_device *drm;
-
- if (aconnector->bl_idx == -1 ||
- aconnector->dc_link->connector_signal != SIGNAL_TYPE_EDP)
- return;
-
- conn_base = &aconnector->base;
- drm = conn_base->dev;
- adev = drm_to_adev(drm);
-
- caps = &adev->dm.backlight_caps[aconnector->bl_idx];
- caps->ext_caps = &aconnector->dc_link->dpcd_sink_ext_caps;
- caps->aux_support = false;
-
- panel_backlight_quirk = drm_get_panel_backlight_quirk(aconnector->drm_edid);
-
- if (caps->ext_caps->bits.oled == 1
- /*
- * ||
- * caps->ext_caps->bits.sdr_aux_backlight_control == 1 ||
- * caps->ext_caps->bits.hdr_aux_backlight_control == 1
- */)
- caps->aux_support = true;
-
- if (amdgpu_backlight == 0)
- caps->aux_support = false;
- else if (amdgpu_backlight == 1)
- caps->aux_support = true;
- else if (!IS_ERR_OR_NULL(panel_backlight_quirk) &&
- panel_backlight_quirk->force_pwm)
- caps->aux_support = false;
- if (caps->aux_support)
- aconnector->dc_link->backlight_control_type = BACKLIGHT_CONTROL_AMD_AUX;
-
- luminance_range = &conn_base->display_info.luminance_range;
-
- if (luminance_range->max_luminance)
- caps->aux_max_input_signal = luminance_range->max_luminance;
- else
- caps->aux_max_input_signal = 512;
-
- if (luminance_range->min_luminance)
- caps->aux_min_input_signal = luminance_range->min_luminance;
- else
- caps->aux_min_input_signal = 1;
-
- if (!IS_ERR_OR_NULL(panel_backlight_quirk)) {
- if (panel_backlight_quirk->min_brightness) {
- caps->min_input_signal =
- panel_backlight_quirk->min_brightness - 1;
- drm_info(drm,
- "Applying panel backlight quirk, min_brightness: %d\n",
- caps->min_input_signal);
- }
- if (panel_backlight_quirk->brightness_mask) {
- drm_info(drm,
- "Applying panel backlight quirk, brightness_mask: 0x%X\n",
- panel_backlight_quirk->brightness_mask);
- caps->brightness_mask =
- panel_backlight_quirk->brightness_mask;
- }
- }
- }
-
- DEFINE_FREE(sink_release, struct dc_sink *, if (_T) dc_sink_release(_T))
-
- void amdgpu_dm_update_connector_after_detect(
- struct amdgpu_dm_connector *aconnector)
- {
- struct drm_connector *connector = &aconnector->base;
- struct dc_sink *sink __free(sink_release) = NULL;
- struct drm_device *dev = connector->dev;
-
- /* MST handled by drm_mst framework */
- if (aconnector->mst_mgr.mst_state == true)
- return;
-
- sink = aconnector->dc_link->local_sink;
- if (sink)
- dc_sink_retain(sink);
-
- /*
- * Edid mgmt connector gets first update only in mode_valid hook and then
- * the connector sink is set to either fake or physical sink depends on link status.
- * Skip if already done during boot.
- */
- if (aconnector->base.force != DRM_FORCE_UNSPECIFIED
- && aconnector->dc_em_sink) {
-
- /*
- * For S3 resume with headless use eml_sink to fake stream
- * because on resume connector->sink is set to NULL
- */
- guard(mutex)(&dev->mode_config.mutex);
-
- if (sink) {
- if (aconnector->dc_sink) {
- amdgpu_dm_update_freesync_caps(connector, NULL, true);
- /*
- * retain and release below are used to
- * bump up refcount for sink because the link doesn't point
- * to it anymore after disconnect, so on next crtc to connector
- * reshuffle by UMD we will get into unwanted dc_sink release
- */
- dc_sink_release(aconnector->dc_sink);
- }
- aconnector->dc_sink = sink;
- dc_sink_retain(aconnector->dc_sink);
- amdgpu_dm_update_freesync_caps(connector,
- aconnector->drm_edid, true);
- } else {
- amdgpu_dm_update_freesync_caps(connector, NULL, true);
- if (!aconnector->dc_sink) {
- aconnector->dc_sink = aconnector->dc_em_sink;
- dc_sink_retain(aconnector->dc_sink);
- }
- }
-
- return;
- }
-
- /*
- * TODO: temporary guard to look for proper fix
- * if this sink is MST sink, we should not do anything
- */
- if (sink && sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT_MST)
- return;
-
- if (aconnector->dc_sink == sink) {
- /*
- * We got a DP short pulse (Link Loss, DP CTS, etc...).
- * Do nothing!!
- */
- drm_dbg_kms(dev, "DCHPD: connector_id=%d: dc_sink didn't change.\n",
- aconnector->connector_id);
- return;
- }
-
- drm_dbg_kms(dev, "DCHPD: connector_id=%d: Old sink=%p New sink=%p\n",
- aconnector->connector_id, aconnector->dc_sink, sink);
-
- /* When polling, DRM has already locked the mutex for us. */
- if (!drm_kms_helper_is_poll_worker())
- mutex_lock(&dev->mode_config.mutex);
-
- /*
- * 1. Update status of the drm connector
- * 2. Send an event and let userspace tell us what to do
- */
- if (sink) {
- /*
- * TODO: check if we still need the S3 mode update workaround.
- * If yes, put it here.
- */
- if (aconnector->dc_sink) {
- amdgpu_dm_update_freesync_caps(connector, NULL, true);
- dc_sink_release(aconnector->dc_sink);
- }
-
- aconnector->dc_sink = sink;
- dc_sink_retain(aconnector->dc_sink);
- drm_edid_free(aconnector->drm_edid);
- aconnector->drm_edid = NULL;
- if (sink->dc_edid.length == 0) {
- hdmi_cec_unset_edid(aconnector);
- if (aconnector->dc_link->aux_mode) {
- drm_dp_cec_unset_edid(&aconnector->dm_dp_aux.aux);
- }
- } else {
- const struct edid *edid = (const struct edid *)sink->dc_edid.raw_edid;
-
- aconnector->drm_edid = drm_edid_alloc(edid, sink->dc_edid.length);
- drm_edid_connector_update(connector, aconnector->drm_edid);
-
- hdmi_cec_set_edid(aconnector);
- if (aconnector->dc_link->aux_mode)
- drm_dp_cec_attach(&aconnector->dm_dp_aux.aux,
- connector->display_info.source_physical_address);
- }
-
- if (!aconnector->timing_requested) {
- aconnector->timing_requested =
- kzalloc_obj(struct dc_crtc_timing);
- if (!aconnector->timing_requested)
- drm_err(dev,
- "failed to create aconnector->requested_timing\n");
- }
-
- amdgpu_dm_update_freesync_caps(connector, aconnector->drm_edid, true);
- update_connector_ext_caps(aconnector);
- dm_set_panel_type(aconnector);
- } else {
- hdmi_cec_unset_edid(aconnector);
- drm_dp_cec_unset_edid(&aconnector->dm_dp_aux.aux);
- amdgpu_dm_update_freesync_caps(connector, NULL, true);
- aconnector->num_modes = 0;
- dc_sink_release(aconnector->dc_sink);
- aconnector->dc_sink = NULL;
- drm_edid_free(aconnector->drm_edid);
- aconnector->drm_edid = NULL;
- kfree(aconnector->timing_requested);
- aconnector->timing_requested = NULL;
- /* Set CP to DESIRED if it was ENABLED, so we can re-enable it again on hotplug */
- if (connector->state->content_protection == DRM_MODE_CONTENT_PROTECTION_ENABLED)
- connector->state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED;
- }
-
- update_subconnector_property(aconnector);
-
- /* When polling, the mutex will be unlocked for us by DRM. */
- if (!drm_kms_helper_is_poll_worker())
- mutex_unlock(&dev->mode_config.mutex);
- }
-
- static bool are_sinks_equal(const struct dc_sink *sink1, const struct dc_sink *sink2)
- {
- if (!sink1 || !sink2)
- return false;
- if (sink1->sink_signal != sink2->sink_signal)
- return false;
-
- if (sink1->dc_edid.length != sink2->dc_edid.length)
- return false;
-
- if (memcmp(sink1->dc_edid.raw_edid, sink2->dc_edid.raw_edid,
- sink1->dc_edid.length) != 0)
- return false;
- return true;
- }
-
-
- /**
- * DOC: hdmi_hpd_debounce_work
- *
- * HDMI HPD debounce delay in milliseconds. When an HDMI display toggles HPD
- * (such as during power save transitions), this delay determines how long to
- * wait before processing the HPD event. This allows distinguishing between a
- * physical unplug (>hdmi_hpd_debounce_delay)
- * and a spontaneous RX HPD toggle (<hdmi_hpd_debounce_delay).
- *
- * If the toggle is less than this delay, the driver compares sink capabilities
- * and permits a hotplug event if they changed.
- *
- * The default value of 1500ms was chosen based on experimental testing with
- * various monitors that exhibit spontaneous HPD toggling behavior.
- */
- static void hdmi_hpd_debounce_work(struct work_struct *work)
- {
- struct amdgpu_dm_connector *aconnector =
- container_of(to_delayed_work(work), struct amdgpu_dm_connector,
- hdmi_hpd_debounce_work);
- struct drm_connector *connector = &aconnector->base;
- struct drm_device *dev = connector->dev;
- struct amdgpu_device *adev = drm_to_adev(dev);
- struct dc *dc = aconnector->dc_link->ctx->dc;
- bool fake_reconnect = false;
- bool reallow_idle = false;
- bool ret = false;
- guard(mutex)(&aconnector->hpd_lock);
-
- /* Re-detect the display */
- scoped_guard(mutex, &adev->dm.dc_lock) {
- if (dc->caps.ips_support && dc->ctx->dmub_srv->idle_allowed) {
- dc_allow_idle_optimizations(dc, false);
- reallow_idle = true;
- }
- ret = dc_link_detect(aconnector->dc_link, DETECT_REASON_HPD);
- }
-
- if (ret) {
- /* Apply workaround delay for certain panels */
- apply_delay_after_dpcd_poweroff(adev, aconnector->dc_sink);
- /* Compare sinks to determine if this was a spontaneous HPD toggle */
- if (are_sinks_equal(aconnector->dc_link->local_sink, aconnector->hdmi_prev_sink)) {
- /*
- * Sinks match - this was a spontaneous HDMI HPD toggle.
- */
- drm_dbg_kms(dev, "HDMI HPD: Sink unchanged after debounce, internal re-enable\n");
- fake_reconnect = true;
- }
-
- /* Update connector state */
- amdgpu_dm_update_connector_after_detect(aconnector);
-
- drm_modeset_lock_all(dev);
- dm_restore_drm_connector_state(dev, connector);
- drm_modeset_unlock_all(dev);
-
- /* Only notify OS if sink actually changed */
- if (!fake_reconnect && aconnector->base.force == DRM_FORCE_UNSPECIFIED)
- drm_kms_helper_hotplug_event(dev);
- }
-
- /* Release the cached sink reference */
- if (aconnector->hdmi_prev_sink) {
- dc_sink_release(aconnector->hdmi_prev_sink);
- aconnector->hdmi_prev_sink = NULL;
- }
-
- scoped_guard(mutex, &adev->dm.dc_lock) {
- if (reallow_idle && dc->caps.ips_support)
- dc_allow_idle_optimizations(dc, true);
- }
- }
-
- static void handle_hpd_irq_helper(struct amdgpu_dm_connector *aconnector)
- {
- struct drm_connector *connector = &aconnector->base;
- struct drm_device *dev = connector->dev;
- enum dc_connection_type new_connection_type = dc_connection_none;
- struct amdgpu_device *adev = drm_to_adev(dev);
- struct dm_connector_state *dm_con_state = to_dm_connector_state(connector->state);
- struct dc *dc = aconnector->dc_link->ctx->dc;
- bool ret = false;
- bool debounce_required = false;
-
- if (adev->dm.disable_hpd_irq)
- return;
-
- /*
- * In case of failure or MST no need to update connector status or notify the OS
- * since (for MST case) MST does this in its own context.
- */
- guard(mutex)(&aconnector->hpd_lock);
-
- if (adev->dm.hdcp_workqueue) {
- hdcp_reset_display(adev->dm.hdcp_workqueue, aconnector->dc_link->link_index);
- dm_con_state->update_hdcp = true;
- }
- if (aconnector->fake_enable)
- aconnector->fake_enable = false;
-
- aconnector->timing_changed = false;
-
- if (!dc_link_detect_connection_type(aconnector->dc_link, &new_connection_type))
- drm_err(adev_to_drm(adev), "KMS: Failed to detect connector\n");
-
- /*
- * Check for HDMI disconnect with debounce enabled.
- */
- debounce_required = (aconnector->hdmi_hpd_debounce_delay_ms > 0 &&
- dc_is_hdmi_signal(aconnector->dc_link->connector_signal) &&
- new_connection_type == dc_connection_none &&
- aconnector->dc_link->local_sink != NULL);
-
- if (aconnector->base.force && new_connection_type == dc_connection_none) {
- emulated_link_detect(aconnector->dc_link);
-
- drm_modeset_lock_all(dev);
- dm_restore_drm_connector_state(dev, connector);
- drm_modeset_unlock_all(dev);
-
- if (aconnector->base.force == DRM_FORCE_UNSPECIFIED)
- drm_kms_helper_connector_hotplug_event(connector);
- } else if (debounce_required) {
- /*
- * HDMI disconnect detected - schedule delayed work instead of
- * processing immediately. This allows us to coalesce spurious
- * HDMI signals from physical unplugs.
- */
- drm_dbg_kms(dev, "HDMI HPD: Disconnect detected, scheduling debounce work (%u ms)\n",
- aconnector->hdmi_hpd_debounce_delay_ms);
-
- /* Cache the current sink for later comparison */
- if (aconnector->hdmi_prev_sink)
- dc_sink_release(aconnector->hdmi_prev_sink);
- aconnector->hdmi_prev_sink = aconnector->dc_link->local_sink;
- if (aconnector->hdmi_prev_sink)
- dc_sink_retain(aconnector->hdmi_prev_sink);
-
- /* Schedule delayed detection. */
- if (mod_delayed_work(system_percpu_wq,
- &aconnector->hdmi_hpd_debounce_work,
- msecs_to_jiffies(aconnector->hdmi_hpd_debounce_delay_ms)))
- drm_dbg_kms(dev, "HDMI HPD: Re-scheduled debounce work\n");
-
- } else {
-
- /* If the aconnector->hdmi_hpd_debounce_work is scheduled, exit early */
- if (delayed_work_pending(&aconnector->hdmi_hpd_debounce_work))
- return;
-
- scoped_guard(mutex, &adev->dm.dc_lock) {
- dc_exit_ips_for_hw_access(dc);
- ret = dc_link_detect(aconnector->dc_link, DETECT_REASON_HPD);
- }
- if (ret) {
- /* w/a delay for certain panels */
- apply_delay_after_dpcd_poweroff(adev, aconnector->dc_sink);
- amdgpu_dm_update_connector_after_detect(aconnector);
-
- drm_modeset_lock_all(dev);
- dm_restore_drm_connector_state(dev, connector);
- drm_modeset_unlock_all(dev);
-
- if (aconnector->base.force == DRM_FORCE_UNSPECIFIED)
- drm_kms_helper_connector_hotplug_event(connector);
- }
- }
- }
-
- static void handle_hpd_irq(void *param)
- {
- struct amdgpu_dm_connector *aconnector = (struct amdgpu_dm_connector *)param;
-
- handle_hpd_irq_helper(aconnector);
-
- }
-
- static void schedule_hpd_rx_offload_work(struct amdgpu_device *adev, struct hpd_rx_irq_offload_work_queue *offload_wq,
- union hpd_irq_data hpd_irq_data)
- {
- struct hpd_rx_irq_offload_work *offload_work = kzalloc_obj(*offload_work);
-
- if (!offload_work) {
- drm_err(adev_to_drm(adev), "Failed to allocate hpd_rx_irq_offload_work.\n");
- return;
- }
-
- INIT_WORK(&offload_work->work, dm_handle_hpd_rx_offload_work);
- offload_work->data = hpd_irq_data;
- offload_work->offload_wq = offload_wq;
- offload_work->adev = adev;
-
- queue_work(offload_wq->wq, &offload_work->work);
- drm_dbg_kms(adev_to_drm(adev), "queue work to handle hpd_rx offload work");
- }
-
- static void handle_hpd_rx_irq(void *param)
- {
- struct amdgpu_dm_connector *aconnector = (struct amdgpu_dm_connector *)param;
- struct drm_connector *connector = &aconnector->base;
- struct drm_device *dev = connector->dev;
- struct dc_link *dc_link = aconnector->dc_link;
- bool is_mst_root_connector = aconnector->mst_mgr.mst_state;
- bool result = false;
- enum dc_connection_type new_connection_type = dc_connection_none;
- struct amdgpu_device *adev = drm_to_adev(dev);
- union hpd_irq_data hpd_irq_data;
- bool link_loss = false;
- bool has_left_work = false;
- int idx = dc_link->link_index;
- struct hpd_rx_irq_offload_work_queue *offload_wq = &adev->dm.hpd_rx_offload_wq[idx];
- struct dc *dc = aconnector->dc_link->ctx->dc;
-
- memset(&hpd_irq_data, 0, sizeof(hpd_irq_data));
-
- if (adev->dm.disable_hpd_irq)
- return;
-
- /*
- * TODO:Temporary add mutex to protect hpd interrupt not have a gpio
- * conflict, after implement i2c helper, this mutex should be
- * retired.
- */
- mutex_lock(&aconnector->hpd_lock);
-
- result = dc_link_handle_hpd_rx_irq(dc_link, &hpd_irq_data,
- &link_loss, true, &has_left_work);
-
- if (!has_left_work)
- goto out;
-
- if (hpd_irq_data.bytes.device_service_irq.bits.AUTOMATED_TEST) {
- schedule_hpd_rx_offload_work(adev, offload_wq, hpd_irq_data);
- goto out;
- }
-
- if (dc_link_dp_allow_hpd_rx_irq(dc_link)) {
- if (hpd_irq_data.bytes.device_service_irq.bits.UP_REQ_MSG_RDY ||
- hpd_irq_data.bytes.device_service_irq.bits.DOWN_REP_MSG_RDY) {
- bool skip = false;
-
- /*
- * DOWN_REP_MSG_RDY is also handled by polling method
- * mgr->cbs->poll_hpd_irq()
- */
- spin_lock(&offload_wq->offload_lock);
- skip = offload_wq->is_handling_mst_msg_rdy_event;
-
- if (!skip)
- offload_wq->is_handling_mst_msg_rdy_event = true;
-
- spin_unlock(&offload_wq->offload_lock);
-
- if (!skip)
- schedule_hpd_rx_offload_work(adev, offload_wq, hpd_irq_data);
-
- goto out;
- }
-
- if (link_loss) {
- bool skip = false;
-
- spin_lock(&offload_wq->offload_lock);
- skip = offload_wq->is_handling_link_loss;
-
- if (!skip)
- offload_wq->is_handling_link_loss = true;
-
- spin_unlock(&offload_wq->offload_lock);
-
- if (!skip)
- schedule_hpd_rx_offload_work(adev, offload_wq, hpd_irq_data);
-
- goto out;
- }
- }
-
- out:
- if (result && !is_mst_root_connector) {
- /* Downstream Port status changed. */
- if (!dc_link_detect_connection_type(dc_link, &new_connection_type))
- drm_err(adev_to_drm(adev), "KMS: Failed to detect connector\n");
-
- if (aconnector->base.force && new_connection_type == dc_connection_none) {
- emulated_link_detect(dc_link);
-
- if (aconnector->fake_enable)
- aconnector->fake_enable = false;
-
- amdgpu_dm_update_connector_after_detect(aconnector);
-
-
- drm_modeset_lock_all(dev);
- dm_restore_drm_connector_state(dev, connector);
- drm_modeset_unlock_all(dev);
-
- drm_kms_helper_connector_hotplug_event(connector);
- } else {
- bool ret = false;
-
- mutex_lock(&adev->dm.dc_lock);
- dc_exit_ips_for_hw_access(dc);
- ret = dc_link_detect(dc_link, DETECT_REASON_HPDRX);
- mutex_unlock(&adev->dm.dc_lock);
-
- if (ret) {
- if (aconnector->fake_enable)
- aconnector->fake_enable = false;
-
- amdgpu_dm_update_connector_after_detect(aconnector);
-
- drm_modeset_lock_all(dev);
- dm_restore_drm_connector_state(dev, connector);
- drm_modeset_unlock_all(dev);
-
- drm_kms_helper_connector_hotplug_event(connector);
- }
- }
- }
- if (hpd_irq_data.bytes.device_service_irq.bits.CP_IRQ) {
- if (adev->dm.hdcp_workqueue)
- hdcp_handle_cpirq(adev->dm.hdcp_workqueue, aconnector->base.index);
- }
-
- if (dc_link->type != dc_connection_mst_branch)
- drm_dp_cec_irq(&aconnector->dm_dp_aux.aux);
-
- mutex_unlock(&aconnector->hpd_lock);
- }
-
- static int register_hpd_handlers(struct amdgpu_device *adev)
- {
- struct drm_device *dev = adev_to_drm(adev);
- struct drm_connector *connector;
- struct amdgpu_dm_connector *aconnector;
- const struct dc_link *dc_link;
- struct dc_interrupt_params int_params = {0};
-
- int_params.requested_polarity = INTERRUPT_POLARITY_DEFAULT;
- int_params.current_polarity = INTERRUPT_POLARITY_DEFAULT;
-
- if (dc_is_dmub_outbox_supported(adev->dm.dc)) {
- if (!register_dmub_notify_callback(adev, DMUB_NOTIFICATION_HPD,
- dmub_hpd_callback, true)) {
- drm_err(adev_to_drm(adev), "fail to register dmub hpd callback");
- return -EINVAL;
- }
-
- if (!register_dmub_notify_callback(adev, DMUB_NOTIFICATION_HPD_IRQ,
- dmub_hpd_callback, true)) {
- drm_err(adev_to_drm(adev), "fail to register dmub hpd callback");
- return -EINVAL;
- }
-
- if (!register_dmub_notify_callback(adev, DMUB_NOTIFICATION_HPD_SENSE_NOTIFY,
- dmub_hpd_sense_callback, true)) {
- drm_err(adev_to_drm(adev), "fail to register dmub hpd sense callback");
- return -EINVAL;
- }
- }
-
- list_for_each_entry(connector,
- &dev->mode_config.connector_list, head) {
-
- if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
- continue;
-
- aconnector = to_amdgpu_dm_connector(connector);
- dc_link = aconnector->dc_link;
-
- if (dc_link->irq_source_hpd != DC_IRQ_SOURCE_INVALID) {
- int_params.int_context = INTERRUPT_LOW_IRQ_CONTEXT;
- int_params.irq_source = dc_link->irq_source_hpd;
-
- if (int_params.irq_source == DC_IRQ_SOURCE_INVALID ||
- int_params.irq_source < DC_IRQ_SOURCE_HPD1 ||
- int_params.irq_source > DC_IRQ_SOURCE_HPD6) {
- drm_err(adev_to_drm(adev), "Failed to register hpd irq!\n");
- return -EINVAL;
- }
-
- if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
- handle_hpd_irq, (void *) aconnector))
- return -ENOMEM;
- }
-
- if (dc_link->irq_source_hpd_rx != DC_IRQ_SOURCE_INVALID) {
-
- /* Also register for DP short pulse (hpd_rx). */
- int_params.int_context = INTERRUPT_LOW_IRQ_CONTEXT;
- int_params.irq_source = dc_link->irq_source_hpd_rx;
-
- if (int_params.irq_source == DC_IRQ_SOURCE_INVALID ||
- int_params.irq_source < DC_IRQ_SOURCE_HPD1RX ||
- int_params.irq_source > DC_IRQ_SOURCE_HPD6RX) {
- drm_err(adev_to_drm(adev), "Failed to register hpd rx irq!\n");
- return -EINVAL;
- }
-
- if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
- handle_hpd_rx_irq, (void *) aconnector))
- return -ENOMEM;
- }
- }
- return 0;
- }
-
- /* Register IRQ sources and initialize IRQ callbacks */
- static int dce110_register_irq_handlers(struct amdgpu_device *adev)
- {
- struct dc *dc = adev->dm.dc;
- struct common_irq_params *c_irq_params;
- struct dc_interrupt_params int_params = {0};
- int r;
- int i;
- unsigned int src_id;
- unsigned int client_id = AMDGPU_IRQ_CLIENTID_LEGACY;
- /* Use different interrupts for VBLANK on DCE 6 vs. newer. */
- const unsigned int vblank_d1 =
- adev->dm.dc->ctx->dce_version >= DCE_VERSION_8_0
- ? VISLANDS30_IV_SRCID_D1_VERTICAL_INTERRUPT0 : 1;
-
- if (adev->family >= AMDGPU_FAMILY_AI)
- client_id = SOC15_IH_CLIENTID_DCE;
-
- int_params.requested_polarity = INTERRUPT_POLARITY_DEFAULT;
- int_params.current_polarity = INTERRUPT_POLARITY_DEFAULT;
-
- /*
- * Actions of amdgpu_irq_add_id():
- * 1. Register a set() function with base driver.
- * Base driver will call set() function to enable/disable an
- * interrupt in DC hardware.
- * 2. Register amdgpu_dm_irq_handler().
- * Base driver will call amdgpu_dm_irq_handler() for ALL interrupts
- * coming from DC hardware.
- * amdgpu_dm_irq_handler() will re-direct the interrupt to DC
- * for acknowledging and handling.
- */
-
- /* Use VBLANK interrupt */
- for (i = 0; i < adev->mode_info.num_crtc; i++) {
- src_id = vblank_d1 + i;
- r = amdgpu_irq_add_id(adev, client_id, src_id, &adev->crtc_irq);
- if (r) {
- drm_err(adev_to_drm(adev), "Failed to add crtc irq id!\n");
- return r;
- }
-
- int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT;
- int_params.irq_source =
- dc_interrupt_to_irq_source(dc, src_id, 0);
-
- if (int_params.irq_source == DC_IRQ_SOURCE_INVALID ||
- int_params.irq_source < DC_IRQ_SOURCE_VBLANK1 ||
- int_params.irq_source > DC_IRQ_SOURCE_VBLANK6) {
- drm_err(adev_to_drm(adev), "Failed to register vblank irq!\n");
- return -EINVAL;
- }
-
- c_irq_params = &adev->dm.vblank_params[int_params.irq_source - DC_IRQ_SOURCE_VBLANK1];
-
- c_irq_params->adev = adev;
- c_irq_params->irq_src = int_params.irq_source;
-
- if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
- dm_crtc_high_irq, c_irq_params))
- return -ENOMEM;
- }
-
- if (dc_supports_vrr(adev->dm.dc->ctx->dce_version)) {
- /* Use VUPDATE interrupt */
- for (i = 0; i < adev->mode_info.num_crtc; i++) {
- src_id = VISLANDS30_IV_SRCID_D1_V_UPDATE_INT + i * 2;
- r = amdgpu_irq_add_id(adev, client_id, src_id, &adev->vupdate_irq);
- if (r) {
- drm_err(adev_to_drm(adev), "Failed to add vupdate irq id!\n");
- return r;
- }
-
- int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT;
- int_params.irq_source =
- dc_interrupt_to_irq_source(dc, src_id, 0);
-
- if (int_params.irq_source == DC_IRQ_SOURCE_INVALID ||
- int_params.irq_source < DC_IRQ_SOURCE_VUPDATE1 ||
- int_params.irq_source > DC_IRQ_SOURCE_VUPDATE6) {
- drm_err(adev_to_drm(adev), "Failed to register vupdate irq!\n");
- return -EINVAL;
- }
-
- c_irq_params = &adev->dm.vupdate_params[
- int_params.irq_source - DC_IRQ_SOURCE_VUPDATE1];
- c_irq_params->adev = adev;
- c_irq_params->irq_src = int_params.irq_source;
-
- if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
- dm_vupdate_high_irq, c_irq_params))
- return -ENOMEM;
- }
- }
-
- /* Use GRPH_PFLIP interrupt */
- for (i = VISLANDS30_IV_SRCID_D1_GRPH_PFLIP;
- i <= VISLANDS30_IV_SRCID_D6_GRPH_PFLIP; i += 2) {
- r = amdgpu_irq_add_id(adev, client_id, i, &adev->pageflip_irq);
- if (r) {
- drm_err(adev_to_drm(adev), "Failed to add page flip irq id!\n");
- return r;
- }
-
- int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT;
- int_params.irq_source =
- dc_interrupt_to_irq_source(dc, i, 0);
-
- if (int_params.irq_source == DC_IRQ_SOURCE_INVALID ||
- int_params.irq_source < DC_IRQ_SOURCE_PFLIP_FIRST ||
- int_params.irq_source > DC_IRQ_SOURCE_PFLIP_LAST) {
- drm_err(adev_to_drm(adev), "Failed to register pflip irq!\n");
- return -EINVAL;
- }
-
- c_irq_params = &adev->dm.pflip_params[int_params.irq_source - DC_IRQ_SOURCE_PFLIP_FIRST];
-
- c_irq_params->adev = adev;
- c_irq_params->irq_src = int_params.irq_source;
-
- if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
- dm_pflip_high_irq, c_irq_params))
- return -ENOMEM;
- }
-
- /* HPD */
- r = amdgpu_irq_add_id(adev, client_id,
- VISLANDS30_IV_SRCID_HOTPLUG_DETECT_A, &adev->hpd_irq);
- if (r) {
- drm_err(adev_to_drm(adev), "Failed to add hpd irq id!\n");
- return r;
- }
-
- r = register_hpd_handlers(adev);
-
- return r;
- }
-
- /* Register IRQ sources and initialize IRQ callbacks */
- static int dcn10_register_irq_handlers(struct amdgpu_device *adev)
- {
- struct dc *dc = adev->dm.dc;
- struct common_irq_params *c_irq_params;
- struct dc_interrupt_params int_params = {0};
- int r;
- int i;
- #if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
- static const unsigned int vrtl_int_srcid[] = {
- DCN_1_0__SRCID__OTG1_VERTICAL_INTERRUPT0_CONTROL,
- DCN_1_0__SRCID__OTG2_VERTICAL_INTERRUPT0_CONTROL,
- DCN_1_0__SRCID__OTG3_VERTICAL_INTERRUPT0_CONTROL,
- DCN_1_0__SRCID__OTG4_VERTICAL_INTERRUPT0_CONTROL,
- DCN_1_0__SRCID__OTG5_VERTICAL_INTERRUPT0_CONTROL,
- DCN_1_0__SRCID__OTG6_VERTICAL_INTERRUPT0_CONTROL
- };
- #endif
-
- int_params.requested_polarity = INTERRUPT_POLARITY_DEFAULT;
- int_params.current_polarity = INTERRUPT_POLARITY_DEFAULT;
-
- /*
- * Actions of amdgpu_irq_add_id():
- * 1. Register a set() function with base driver.
- * Base driver will call set() function to enable/disable an
- * interrupt in DC hardware.
- * 2. Register amdgpu_dm_irq_handler().
- * Base driver will call amdgpu_dm_irq_handler() for ALL interrupts
- * coming from DC hardware.
- * amdgpu_dm_irq_handler() will re-direct the interrupt to DC
- * for acknowledging and handling.
- */
-
- /* Use otg vertical line interrupt */
- #if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
- for (i = 0; i <= adev->mode_info.num_crtc - 1; i++) {
- r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE,
- vrtl_int_srcid[i], &adev->vline0_irq);
-
- if (r) {
- drm_err(adev_to_drm(adev), "Failed to add vline0 irq id!\n");
- return r;
- }
-
- int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT;
- int_params.irq_source =
- dc_interrupt_to_irq_source(dc, vrtl_int_srcid[i], 0);
-
- if (int_params.irq_source == DC_IRQ_SOURCE_INVALID ||
- int_params.irq_source < DC_IRQ_SOURCE_DC1_VLINE0 ||
- int_params.irq_source > DC_IRQ_SOURCE_DC6_VLINE0) {
- drm_err(adev_to_drm(adev), "Failed to register vline0 irq!\n");
- return -EINVAL;
- }
-
- c_irq_params = &adev->dm.vline0_params[int_params.irq_source
- - DC_IRQ_SOURCE_DC1_VLINE0];
-
- c_irq_params->adev = adev;
- c_irq_params->irq_src = int_params.irq_source;
-
- if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
- dm_dcn_vertical_interrupt0_high_irq,
- c_irq_params))
- return -ENOMEM;
- }
- #endif
-
- /* Use VUPDATE_NO_LOCK interrupt on DCN, which seems to correspond to
- * the regular VUPDATE interrupt on DCE. We want DC_IRQ_SOURCE_VUPDATEx
- * to trigger at end of each vblank, regardless of state of the lock,
- * matching DCE behaviour.
- */
- for (i = DCN_1_0__SRCID__OTG0_IHC_V_UPDATE_NO_LOCK_INTERRUPT;
- i <= DCN_1_0__SRCID__OTG0_IHC_V_UPDATE_NO_LOCK_INTERRUPT + adev->mode_info.num_crtc - 1;
- i++) {
- r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, i, &adev->vupdate_irq);
-
- if (r) {
- drm_err(adev_to_drm(adev), "Failed to add vupdate irq id!\n");
- return r;
- }
-
- int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT;
- int_params.irq_source =
- dc_interrupt_to_irq_source(dc, i, 0);
-
- if (int_params.irq_source == DC_IRQ_SOURCE_INVALID ||
- int_params.irq_source < DC_IRQ_SOURCE_VUPDATE1 ||
- int_params.irq_source > DC_IRQ_SOURCE_VUPDATE6) {
- drm_err(adev_to_drm(adev), "Failed to register vupdate irq!\n");
- return -EINVAL;
- }
-
- c_irq_params = &adev->dm.vupdate_params[int_params.irq_source - DC_IRQ_SOURCE_VUPDATE1];
-
- c_irq_params->adev = adev;
- c_irq_params->irq_src = int_params.irq_source;
-
- if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
- dm_vupdate_high_irq, c_irq_params))
- return -ENOMEM;
- }
-
- /* HPD */
- r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, DCN_1_0__SRCID__DC_HPD1_INT,
- &adev->hpd_irq);
- if (r) {
- drm_err(adev_to_drm(adev), "Failed to add hpd irq id!\n");
- return r;
- }
-
- r = register_hpd_handlers(adev);
-
- return r;
- }
- /* Register Outbox IRQ sources and initialize IRQ callbacks */
- static int register_outbox_irq_handlers(struct amdgpu_device *adev)
- {
- struct dc *dc = adev->dm.dc;
- struct common_irq_params *c_irq_params;
- struct dc_interrupt_params int_params = {0};
- int r, i;
-
- int_params.requested_polarity = INTERRUPT_POLARITY_DEFAULT;
- int_params.current_polarity = INTERRUPT_POLARITY_DEFAULT;
-
- r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, DCN_1_0__SRCID__DMCUB_OUTBOX_LOW_PRIORITY_READY_INT,
- &adev->dmub_outbox_irq);
- if (r) {
- drm_err(adev_to_drm(adev), "Failed to add outbox irq id!\n");
- return r;
- }
-
- if (dc->ctx->dmub_srv) {
- i = DCN_1_0__SRCID__DMCUB_OUTBOX_LOW_PRIORITY_READY_INT;
- int_params.int_context = INTERRUPT_LOW_IRQ_CONTEXT;
- int_params.irq_source =
- dc_interrupt_to_irq_source(dc, i, 0);
-
- c_irq_params = &adev->dm.dmub_outbox_params[0];
-
- c_irq_params->adev = adev;
- c_irq_params->irq_src = int_params.irq_source;
-
- if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
- dm_dmub_outbox1_low_irq, c_irq_params))
- return -ENOMEM;
- }
-
- return 0;
- }
-
/*
* Acquires the lock for the atomic state object and returns
* the new atomic state.
@@@ -5074,7 -2133,7 +2133,7 @@@ int dm_atomic_get_state(struct drm_atom
return 0;
}
- static struct dm_atomic_state *
+ STATIC_IFN_KUNIT struct dm_atomic_state *
dm_atomic_get_new_state(struct drm_atomic_commit *state)
{
struct drm_device *dev = state->dev;
@@@ -5091,6 -2150,7 +2150,7 @@@
return NULL;
}
+ EXPORT_IF_KUNIT(dm_atomic_get_new_state);
static struct drm_private_state *
dm_atomic_duplicate_state(struct drm_private_obj *obj)
@@@ -5116,8 -2176,8 +2176,8 @@@
return &new_state->base;
}
- static void dm_atomic_destroy_state(struct drm_private_obj *obj,
- struct drm_private_state *state)
+ STATIC_IFN_KUNIT void dm_atomic_destroy_state(struct drm_private_obj *obj,
+ struct drm_private_state *state)
{
struct dm_atomic_state *dm_state = to_dm_atomic_state(state);
@@@ -5126,6 -2186,7 +2186,7 @@@
kfree(dm_state);
}
+ EXPORT_IF_KUNIT(dm_atomic_destroy_state);
static struct drm_private_state *
dm_atomic_create_state(struct drm_private_obj *obj)
@@@ -5197,420 -2258,6 +2258,6 @@@ static int amdgpu_dm_mode_config_init(s
return 0;
}
- #define AMDGPU_DM_DEFAULT_MIN_BACKLIGHT 12
- #define AMDGPU_DM_DEFAULT_MAX_BACKLIGHT 255
- #define AMDGPU_DM_MIN_SPREAD ((AMDGPU_DM_DEFAULT_MAX_BACKLIGHT - AMDGPU_DM_DEFAULT_MIN_BACKLIGHT) / 2)
- #define AUX_BL_DEFAULT_TRANSITION_TIME_MS 50
-
- void amdgpu_dm_update_backlight_caps(struct amdgpu_display_manager *dm,
- int bl_idx)
- {
- struct amdgpu_dm_backlight_caps *caps = &dm->backlight_caps[bl_idx];
-
- if (caps->caps_valid)
- return;
-
- #if defined(CONFIG_ACPI)
- amdgpu_acpi_get_backlight_caps(caps);
-
- /* validate the firmware value is sane */
- if (caps->caps_valid) {
- int spread = caps->max_input_signal - caps->min_input_signal;
-
- if (caps->max_input_signal > AMDGPU_DM_DEFAULT_MAX_BACKLIGHT ||
- caps->min_input_signal < 0 ||
- spread > AMDGPU_DM_DEFAULT_MAX_BACKLIGHT ||
- spread < AMDGPU_DM_MIN_SPREAD) {
- drm_dbg_kms(adev_to_drm(dm->adev), "DM: Invalid backlight caps: min=%d, max=%d\n",
- caps->min_input_signal, caps->max_input_signal);
- caps->caps_valid = false;
- }
- }
-
- if (!caps->caps_valid) {
- caps->min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT;
- caps->max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
- caps->caps_valid = true;
- }
- #else
- if (caps->aux_support)
- return;
-
- caps->min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT;
- caps->max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
- caps->caps_valid = true;
- #endif
- }
-
- static int get_brightness_range(const struct amdgpu_dm_backlight_caps *caps,
- unsigned int *min, unsigned int *max)
- {
- if (!caps)
- return 0;
-
- if (caps->aux_support) {
- // Firmware limits are in nits, DC API wants millinits.
- *max = 1000 * caps->aux_max_input_signal;
- *min = 1000 * caps->aux_min_input_signal;
- } else {
- // Firmware limits are 8-bit, PWM control is 16-bit.
- *max = 0x101 * caps->max_input_signal;
- *min = 0x101 * caps->min_input_signal;
- }
- return 1;
- }
-
- /* Rescale from [min..max] to [0..AMDGPU_MAX_BL_LEVEL] */
- static inline u32 scale_input_to_fw(int min, int max, u64 input)
- {
- return DIV_ROUND_CLOSEST_ULL(input * AMDGPU_MAX_BL_LEVEL, max - min);
- }
-
- /* Rescale from [0..AMDGPU_MAX_BL_LEVEL] to [min..max] */
- static inline u32 scale_fw_to_input(int min, int max, u64 input)
- {
- return min + DIV_ROUND_CLOSEST_ULL(input * (max - min), AMDGPU_MAX_BL_LEVEL);
- }
-
- static void convert_custom_brightness(const struct amdgpu_dm_backlight_caps *caps,
- unsigned int min, unsigned int max,
- uint32_t *user_brightness)
- {
- u32 brightness = scale_input_to_fw(min, max, *user_brightness);
- u8 lower_signal, upper_signal, upper_lum, lower_lum, lum;
- int left, right;
-
- if (amdgpu_dc_debug_mask & DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE)
- return;
-
- if (!caps->data_points)
- return;
-
- /*
- * Handle the case where brightness is below the first data point
- * Interpolate between (0,0) and (first_signal, first_lum)
- */
- if (brightness < caps->luminance_data[0].input_signal) {
- lum = DIV_ROUND_CLOSEST(caps->luminance_data[0].luminance * brightness,
- caps->luminance_data[0].input_signal);
- goto scale;
- }
-
- left = 0;
- right = caps->data_points - 1;
- while (left <= right) {
- int mid = left + (right - left) / 2;
- u8 signal = caps->luminance_data[mid].input_signal;
-
- /* Exact match found */
- if (signal == brightness) {
- lum = caps->luminance_data[mid].luminance;
- goto scale;
- }
-
- if (signal < brightness)
- left = mid + 1;
- else
- right = mid - 1;
- }
-
- /* verify bound */
- if (left >= caps->data_points)
- left = caps->data_points - 1;
-
- /* At this point, left > right */
- lower_signal = caps->luminance_data[right].input_signal;
- upper_signal = caps->luminance_data[left].input_signal;
- lower_lum = caps->luminance_data[right].luminance;
- upper_lum = caps->luminance_data[left].luminance;
-
- /* interpolate */
- if (right == left || !lower_lum)
- lum = upper_lum;
- else
- lum = lower_lum + DIV_ROUND_CLOSEST((upper_lum - lower_lum) *
- (brightness - lower_signal),
- upper_signal - lower_signal);
- scale:
- *user_brightness = scale_fw_to_input(min, max,
- DIV_ROUND_CLOSEST(lum * brightness, 101));
- }
-
- static u32 convert_brightness_from_user(const struct amdgpu_dm_backlight_caps *caps,
- uint32_t brightness)
- {
- unsigned int min, max;
-
- if (!get_brightness_range(caps, &min, &max))
- return brightness;
-
- convert_custom_brightness(caps, min, max, &brightness);
-
- // Rescale 0..max to min..max
- return min + DIV_ROUND_CLOSEST_ULL((u64)(max - min) * brightness, max);
- }
-
- static u32 convert_brightness_to_user(const struct amdgpu_dm_backlight_caps *caps,
- uint32_t brightness)
- {
- unsigned int min, max;
-
- if (!get_brightness_range(caps, &min, &max))
- return brightness;
-
- if (brightness < min)
- return 0;
- // Rescale min..max to 0..max
- return DIV_ROUND_CLOSEST_ULL((u64)max * (brightness - min),
- max - min);
- }
-
- static struct dc_stream_state *dm_find_stream_with_link(
- struct amdgpu_display_manager *dm,
- struct dc_link *link)
- {
- struct dc_state *cur_dc_state = dm->dc->current_state;
- struct dc_stream_state *stream = NULL;
- int i;
-
- for (i = 0; i < cur_dc_state->stream_count; i++) {
- stream = cur_dc_state->streams[i];
- if (stream->link == link)
- return stream;
- }
-
- return NULL;
- }
-
- static void amdgpu_dm_backlight_set_level(struct amdgpu_display_manager *dm,
- int bl_idx,
- u32 user_brightness)
- {
- struct amdgpu_dm_backlight_caps *caps;
- struct dc_link *link;
- u32 brightness = 0;
- bool rc = false, reallow_idle = false;
- struct drm_connector *connector;
- struct dc_stream_state *stream;
- unsigned int min, max;
-
- list_for_each_entry(connector, &dm->ddev->mode_config.connector_list, head) {
- struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
-
- if (aconnector->bl_idx != bl_idx)
- continue;
-
- /* if connector is off, save the brightness for next time it's on */
- if (!aconnector->base.encoder) {
- dm->brightness[bl_idx] = user_brightness;
- dm->actual_brightness[bl_idx] = 0;
- return;
- }
- }
-
- amdgpu_dm_update_backlight_caps(dm, bl_idx);
- caps = &dm->backlight_caps[bl_idx];
-
- dm->brightness[bl_idx] = user_brightness;
- /* update scratch register */
- if (bl_idx == 0)
- amdgpu_atombios_scratch_regs_set_backlight_level(dm->adev, dm->brightness[bl_idx]);
- brightness = convert_brightness_from_user(caps, dm->brightness[bl_idx]);
- link = (struct dc_link *)dm->backlight_link[bl_idx];
-
- /* Apply brightness quirk */
- if (caps->brightness_mask)
- brightness |= caps->brightness_mask;
-
- if (trace_amdgpu_dm_brightness_enabled()) {
- trace_amdgpu_dm_brightness(__builtin_return_address(0),
- user_brightness,
- brightness,
- caps->aux_support,
- power_supply_is_system_supplied() > 0);
- }
-
- stream = dm_find_stream_with_link(dm, link);
- if (!stream)
- return;
-
- mutex_lock(&dm->dc_lock);
- if (dm->dc->caps.ips_support && dm->dc->ctx->dmub_srv->idle_allowed) {
- dc_allow_idle_optimizations(dm->dc, false);
- reallow_idle = true;
- }
-
- if (caps->aux_support) {
- rc = mod_power_set_backlight_nits(dm->power_module, stream, brightness,
- AUX_BL_DEFAULT_TRANSITION_TIME_MS, false, true);
- } else {
- /* power module uses millipercent */
- get_brightness_range(caps, &min, &max);
- brightness = DIV_ROUND_CLOSEST(brightness * 100, (max - min)) * 1000;
- rc = mod_power_set_backlight_percent(dm->power_module, stream,
- brightness, 0, false);
- }
-
- /*
- * Some kms clients create a ramped backlight transition effect
- * by rapidly changing the backlight. Yet we must wait on dmcub
- * fw to exit psr/replay before programming backlight. To
- * prevent lag, keep disable psr/replay and let the next atomic
- * flip clear the event.
- *
- * ToDo: use ISM to handle rapidly backlight change
- *
- * Rapidly backlight change is similar to rapidly cursor events,
- * which is now handled by ISM. ISM can delay the event until system
- * is really idle, so we may use ISM to handle backlight change as well.
- */
- amdgpu_dm_psr_set_event(dm, stream, true,
- psr_event_hw_programming, true);
- amdgpu_dm_replay_set_event(dm, stream, true,
- replay_event_hw_programming, true);
-
- if (dm->dc->caps.ips_support && reallow_idle)
- dc_allow_idle_optimizations(dm->dc, true);
-
- mutex_unlock(&dm->dc_lock);
-
- if (rc)
- dm->actual_brightness[bl_idx] = user_brightness;
- }
-
- static int amdgpu_dm_backlight_update_status(struct backlight_device *bd)
- {
- struct amdgpu_display_manager *dm = bl_get_data(bd);
- int i;
-
- for (i = 0; i < dm->num_of_edps; i++) {
- if (bd == dm->backlight_dev[i])
- break;
- }
- if (i >= AMDGPU_DM_MAX_NUM_EDP)
- i = 0;
- amdgpu_dm_backlight_set_level(dm, i, bd->props.brightness);
-
- return 0;
- }
-
- static u32 amdgpu_dm_backlight_get_level(struct amdgpu_display_manager *dm,
- int bl_idx)
- {
- int ret;
- struct amdgpu_dm_backlight_caps caps;
- struct dc_link *link = (struct dc_link *)dm->backlight_link[bl_idx];
-
- amdgpu_dm_update_backlight_caps(dm, bl_idx);
- caps = dm->backlight_caps[bl_idx];
-
- if (caps.aux_support) {
- u32 avg, peak;
-
- if (!dc_link_get_backlight_level_nits(link, &avg, &peak))
- return dm->brightness[bl_idx];
- return convert_brightness_to_user(&caps, avg);
- }
-
- ret = dc_link_get_backlight_level(link);
-
- if (ret == DC_ERROR_UNEXPECTED)
- return dm->brightness[bl_idx];
-
- return convert_brightness_to_user(&caps, ret);
- }
-
- static int amdgpu_dm_backlight_get_brightness(struct backlight_device *bd)
- {
- struct amdgpu_display_manager *dm = bl_get_data(bd);
- int i;
-
- for (i = 0; i < dm->num_of_edps; i++) {
- if (bd == dm->backlight_dev[i])
- break;
- }
- if (i >= AMDGPU_DM_MAX_NUM_EDP)
- i = 0;
- return amdgpu_dm_backlight_get_level(dm, i);
- }
-
- static const struct backlight_ops amdgpu_dm_backlight_ops = {
- .options = BL_CORE_SUSPENDRESUME,
- .get_brightness = amdgpu_dm_backlight_get_brightness,
- .update_status = amdgpu_dm_backlight_update_status,
- };
-
- static void
- amdgpu_dm_register_backlight_device(struct amdgpu_dm_connector *aconnector)
- {
- struct drm_device *drm = aconnector->base.dev;
- struct amdgpu_display_manager *dm = &drm_to_adev(drm)->dm;
- struct backlight_properties props = { 0 };
- struct amdgpu_dm_backlight_caps *caps;
- char bl_name[16];
- int min, max;
- int real_brightness;
- int init_brightness;
-
- if (aconnector->bl_idx == -1)
- return;
-
- if (!acpi_video_backlight_use_native()) {
- drm_info(drm, "Skipping amdgpu DM backlight registration\n");
- /* Try registering an ACPI video backlight device instead. */
- acpi_video_register_backlight();
- return;
- }
-
- caps = &dm->backlight_caps[aconnector->bl_idx];
- if (get_brightness_range(caps, &min, &max)) {
- if (power_supply_is_system_supplied() > 0)
- props.brightness = DIV_ROUND_CLOSEST(max * caps->ac_level, 100);
- else
- props.brightness = DIV_ROUND_CLOSEST(max * caps->dc_level, 100);
- /* min is zero, so max needs to be adjusted */
- props.max_brightness = max;
- drm_dbg(drm, "Backlight caps: min: %d, max: %d, ac %d, dc %d\n", min, max,
- caps->ac_level, caps->dc_level);
- } else
- props.brightness = props.max_brightness = MAX_BACKLIGHT_LEVEL;
-
- init_brightness = props.brightness;
-
- if (caps->data_points && !(amdgpu_dc_debug_mask & DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE)) {
- drm_info(drm, "Using custom brightness curve\n");
- props.scale = BACKLIGHT_SCALE_NON_LINEAR;
- } else
- props.scale = BACKLIGHT_SCALE_LINEAR;
- props.type = BACKLIGHT_RAW;
-
- snprintf(bl_name, sizeof(bl_name), "amdgpu_bl%d",
- drm->primary->index + aconnector->bl_idx);
-
- dm->backlight_dev[aconnector->bl_idx] =
- backlight_device_register(bl_name, aconnector->base.kdev, dm,
- &amdgpu_dm_backlight_ops, &props);
- dm->brightness[aconnector->bl_idx] = props.brightness;
-
- if (IS_ERR(dm->backlight_dev[aconnector->bl_idx])) {
- drm_err(drm, "DM: Backlight registration failed!\n");
- dm->backlight_dev[aconnector->bl_idx] = NULL;
- } else {
- /*
- * dm->brightness[x] can be inconsistent just after startup until
- * ops.get_brightness is called.
- */
- real_brightness =
- amdgpu_dm_backlight_ops.get_brightness(dm->backlight_dev[aconnector->bl_idx]);
-
- if (real_brightness != init_brightness) {
- dm->actual_brightness[aconnector->bl_idx] = real_brightness;
- dm->brightness[aconnector->bl_idx] = real_brightness;
- }
- drm_dbg_driver(drm, "DM: Registered Backlight device: %s\n", bl_name);
- }
- }
-
static int initialize_plane(struct amdgpu_display_manager *dm,
struct amdgpu_mode_info *mode_info, int plane_id,
enum drm_plane_type plane_type,
@@@ -5652,42 -2299,6 +2299,6 @@@
}
- static void setup_backlight_device(struct amdgpu_display_manager *dm,
- struct amdgpu_dm_connector *aconnector)
- {
- struct amdgpu_dm_backlight_caps *caps;
- struct dc_link *link = aconnector->dc_link;
- int bl_idx = dm->num_of_edps;
-
- if (!(link->connector_signal & (SIGNAL_TYPE_EDP | SIGNAL_TYPE_LVDS)) ||
- link->type == dc_connection_none)
- return;
-
- if (dm->num_of_edps >= AMDGPU_DM_MAX_NUM_EDP) {
- drm_warn(adev_to_drm(dm->adev), "Too much eDP connections, skipping backlight setup for additional eDPs\n");
- return;
- }
-
- aconnector->bl_idx = bl_idx;
-
- amdgpu_dm_update_backlight_caps(dm, bl_idx);
- dm->backlight_link[bl_idx] = link;
- dm->num_of_edps++;
-
- update_connector_ext_caps(aconnector);
- caps = &dm->backlight_caps[aconnector->bl_idx];
-
- /* Only offer ABM property when non-OLED and user didn't turn off by module parameter */
- if (caps->ext_caps && !caps->ext_caps->bits.oled && amdgpu_dm_abm_level < 0)
- drm_object_attach_property(&aconnector->base.base,
- dm->adev->mode_info.abm_level_property,
- ABM_SYSFS_CONTROL);
- }
-
- static void amdgpu_set_panel_orientation(struct drm_connector *connector);
-
-
-
/*
* In this architecture, the association
* connector -> encoder -> crtc
@@@ -5799,7 -2410,7 +2410,7 @@@ static int amdgpu_dm_initialize_drm_dev
case IP_VERSION(4, 0, 1):
case IP_VERSION(4, 2, 0):
case IP_VERSION(4, 2, 1):
- if (register_outbox_irq_handlers(dm->adev)) {
+ if (amdgpu_dm_register_outbox_irq_handlers(dm->adev)) {
drm_err(adev_to_drm(adev), "DM: Failed to initialize IRQ\n");
goto fail;
}
@@@ -5912,7 -2523,7 +2523,7 @@@
drm_err(adev_to_drm(adev), "KMS: Failed to detect connector\n");
if (aconnector->base.force && new_connection_type == dc_connection_none) {
- emulated_link_detect(link);
+ amdgpu_dm_emulated_link_detect(link);
amdgpu_dm_update_connector_after_detect(aconnector);
} else {
bool ret = false;
@@@ -5924,7 -2535,7 +2535,7 @@@
if (ret) {
amdgpu_dm_update_connector_after_detect(aconnector);
- setup_backlight_device(dm, aconnector);
+ amdgpu_dm_setup_backlight_device(dm, aconnector);
/* Disable PSR if Replay can be enabled */
if (replay_feature_enabled)
@@@ -5976,7 -2587,7 +2587,7 @@@
case CHIP_VEGA10:
case CHIP_VEGA12:
case CHIP_VEGA20:
- if (dce110_register_irq_handlers(dm->adev)) {
+ if (amdgpu_dm_dce110_register_irq_handlers(dm->adev)) {
drm_err(adev_to_drm(adev), "DM: Failed to initialize IRQ\n");
goto fail;
}
@@@ -6006,7 -2617,7 +2617,7 @@@
case IP_VERSION(4, 0, 1):
case IP_VERSION(4, 2, 0):
case IP_VERSION(4, 2, 1):
- if (dcn10_register_irq_handlers(dm->adev)) {
+ if (amdgpu_dm_dcn10_register_irq_handlers(dm->adev)) {
drm_err(adev_to_drm(adev), "DM: Failed to initialize IRQ\n");
goto fail;
}
@@@ -6044,10 -2655,11 +2655,11 @@@ static void amdgpu_dm_destroy_drm_devic
*
* Calculate and program the display watermarks and line buffer allocation.
*/
- static void dm_bandwidth_update(struct amdgpu_device *adev)
+ STATIC_IFN_KUNIT void dm_bandwidth_update(struct amdgpu_device *adev)
{
/* TODO: implement later */
}
+ EXPORT_IF_KUNIT(dm_bandwidth_update);
static const struct amdgpu_display_funcs dm_display_funcs = {
.bandwidth_update = dm_bandwidth_update, /* called unconditionally */
@@@ -6097,78 -2709,6 +2709,6 @@@ DEVICE_ATTR_WO(s3_debug)
#endif
- static int dm_init_microcode(struct amdgpu_device *adev)
- {
- char *fw_name_dmub;
- int r;
-
- switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
- case IP_VERSION(2, 1, 0):
- fw_name_dmub = FIRMWARE_RENOIR_DMUB;
- if (ASICREV_IS_GREEN_SARDINE(adev->external_rev_id))
- fw_name_dmub = FIRMWARE_GREEN_SARDINE_DMUB;
- break;
- case IP_VERSION(3, 0, 0):
- if (amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(10, 3, 0))
- fw_name_dmub = FIRMWARE_SIENNA_CICHLID_DMUB;
- else
- fw_name_dmub = FIRMWARE_NAVY_FLOUNDER_DMUB;
- break;
- case IP_VERSION(3, 0, 1):
- fw_name_dmub = FIRMWARE_VANGOGH_DMUB;
- break;
- case IP_VERSION(3, 0, 2):
- fw_name_dmub = FIRMWARE_DIMGREY_CAVEFISH_DMUB;
- break;
- case IP_VERSION(3, 0, 3):
- fw_name_dmub = FIRMWARE_BEIGE_GOBY_DMUB;
- break;
- case IP_VERSION(3, 1, 2):
- case IP_VERSION(3, 1, 3):
- fw_name_dmub = FIRMWARE_YELLOW_CARP_DMUB;
- break;
- case IP_VERSION(3, 1, 4):
- fw_name_dmub = FIRMWARE_DCN_314_DMUB;
- break;
- case IP_VERSION(3, 1, 5):
- fw_name_dmub = FIRMWARE_DCN_315_DMUB;
- break;
- case IP_VERSION(3, 1, 6):
- fw_name_dmub = FIRMWARE_DCN316_DMUB;
- break;
- case IP_VERSION(3, 2, 0):
- fw_name_dmub = FIRMWARE_DCN_V3_2_0_DMCUB;
- break;
- case IP_VERSION(3, 2, 1):
- fw_name_dmub = FIRMWARE_DCN_V3_2_1_DMCUB;
- break;
- case IP_VERSION(3, 5, 0):
- fw_name_dmub = FIRMWARE_DCN_35_DMUB;
- break;
- case IP_VERSION(3, 5, 1):
- fw_name_dmub = FIRMWARE_DCN_351_DMUB;
- break;
- case IP_VERSION(3, 6, 0):
- fw_name_dmub = FIRMWARE_DCN_36_DMUB;
- break;
- case IP_VERSION(4, 0, 1):
- fw_name_dmub = FIRMWARE_DCN_401_DMUB;
- break;
- case IP_VERSION(4, 2, 0):
- fw_name_dmub = FIRMWARE_DCN_42_DMUB;
- break;
- case IP_VERSION(4, 2, 1):
- fw_name_dmub = FIRMWARE_DCN_42B_DMUB;
- break;
- default:
- /* ASIC doesn't support DMUB. */
- return 0;
- }
- r = amdgpu_ucode_request(adev, &adev->dm.dmub_fw, AMDGPU_UCODE_REQUIRED,
- "%s", fw_name_dmub);
- return r;
- }
-
static int dm_early_init(struct amdgpu_ip_block *ip_block)
{
struct amdgpu_device *adev = ip_block->adev;
@@@ -6319,22 -2859,13 +2859,13 @@@
return dm_init_microcode(adev);
}
- static bool modereset_required(struct drm_crtc_state *crtc_state)
+ STATIC_IFN_KUNIT bool modereset_required(struct drm_crtc_state *crtc_state)
{
return !crtc_state->active && drm_atomic_crtc_needs_modeset(crtc_state);
}
+ EXPORT_IF_KUNIT(modereset_required);
- static void amdgpu_dm_encoder_destroy(struct drm_encoder *encoder)
- {
- drm_encoder_cleanup(encoder);
- kfree(encoder);
- }
-
- static const struct drm_encoder_funcs amdgpu_dm_encoder_funcs = {
- .destroy = amdgpu_dm_encoder_destroy,
- };
-
- static int
+ STATIC_IFN_KUNIT int
fill_plane_color_attributes(const struct drm_plane_state *plane_state,
const enum surface_pixel_format format,
enum dc_color_space *color_space)
@@@ -6381,11 -2912,11 +2912,11 @@@
return 0;
}
+ EXPORT_IF_KUNIT(fill_plane_color_attributes);
static int
fill_dc_plane_info_and_addr(struct amdgpu_device *adev,
const struct drm_plane_state *plane_state,
- const u64 tiling_flags,
struct dc_plane_info *plane_info,
struct dc_plane_address *address,
bool tmz_surface)
@@@ -6483,7 -3014,7 +3014,7 @@@
return ret;
ret = amdgpu_dm_plane_fill_plane_buffer_attributes(adev, afb, plane_info->format,
- plane_info->rotation, tiling_flags,
+ plane_info->rotation,
&plane_info->tiling_info,
&plane_info->plane_size,
&plane_info->dcc, address,
@@@ -6519,7 -3050,6 +3050,6 @@@ static int fill_dc_plane_attributes(str
dc_plane_state->scaling_quality = scaling_info.scaling_quality;
ret = fill_dc_plane_info_and_addr(adev, plane_state,
- afb->tiling_flags,
&plane_info,
&dc_plane_state->address,
afb->tmz_surface);
@@@ -6719,2119 -3249,6 +3249,6 @@@ ffu
&flip_addrs->dirty_rect_count, true);
}
- static void update_stream_scaling_settings(struct drm_device *dev,
- const struct drm_display_mode *mode,
- const struct dm_connector_state *dm_state,
- struct dc_stream_state *stream)
- {
- enum amdgpu_rmx_type rmx_type;
-
- struct rect src = { 0 }; /* viewport in composition space*/
- struct rect dst = { 0 }; /* stream addressable area */
-
- /* no mode. nothing to be done */
- if (!mode)
- return;
-
- /* Full screen scaling by default */
- src.width = mode->hdisplay;
- src.height = mode->vdisplay;
- dst.width = stream->timing.h_addressable;
- dst.height = stream->timing.v_addressable;
-
- if (dm_state) {
- rmx_type = dm_state->scaling;
- if (rmx_type == RMX_ASPECT || rmx_type == RMX_OFF) {
- if (src.width * dst.height <
- src.height * dst.width) {
- /* height needs less upscaling/more downscaling */
- dst.width = src.width *
- dst.height / src.height;
- } else {
- /* width needs less upscaling/more downscaling */
- dst.height = src.height *
- dst.width / src.width;
- }
- } else if (rmx_type == RMX_CENTER) {
- dst = src;
- }
-
- dst.x = (stream->timing.h_addressable - dst.width) / 2;
- dst.y = (stream->timing.v_addressable - dst.height) / 2;
-
- if (dm_state->underscan_enable) {
- dst.x += dm_state->underscan_hborder / 2;
- dst.y += dm_state->underscan_vborder / 2;
- dst.width -= dm_state->underscan_hborder;
- dst.height -= dm_state->underscan_vborder;
- }
- }
-
- stream->src = src;
- stream->dst = dst;
-
- drm_dbg_kms(dev, "Destination Rectangle x:%d y:%d width:%d height:%d\n",
- dst.x, dst.y, dst.width, dst.height);
-
- }
-
- static enum dc_color_depth
- convert_color_depth_from_display_info(const struct drm_connector *connector,
- bool is_y420, int requested_bpc)
- {
- u8 bpc;
-
- if (is_y420) {
- bpc = 8;
-
- /* Cap display bpc based on HDMI 2.0 HF-VSDB */
- if (connector->display_info.hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_48)
- bpc = 16;
- else if (connector->display_info.hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_36)
- bpc = 12;
- else if (connector->display_info.hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30)
- bpc = 10;
- } else {
- bpc = (uint8_t)connector->display_info.bpc;
- /* Assume 8 bpc by default if no bpc is specified. */
- bpc = bpc ? bpc : 8;
- }
-
- if (requested_bpc > 0) {
- /*
- * Cap display bpc based on the user requested value.
- *
- * The value for state->max_bpc may not correctly updated
- * depending on when the connector gets added to the state
- * or if this was called outside of atomic check, so it
- * can't be used directly.
- */
- bpc = min_t(u8, bpc, requested_bpc);
-
- /* Round down to the nearest even number. */
- bpc = bpc - (bpc & 1);
- }
-
- switch (bpc) {
- case 0:
- /*
- * Temporary Work around, DRM doesn't parse color depth for
- * EDID revision before 1.4
- * TODO: Fix edid parsing
- */
- return COLOR_DEPTH_888;
- case 6:
- return COLOR_DEPTH_666;
- case 8:
- return COLOR_DEPTH_888;
- case 10:
- return COLOR_DEPTH_101010;
- case 12:
- return COLOR_DEPTH_121212;
- case 14:
- return COLOR_DEPTH_141414;
- case 16:
- return COLOR_DEPTH_161616;
- default:
- return COLOR_DEPTH_UNDEFINED;
- }
- }
-
- static enum dc_aspect_ratio
- get_aspect_ratio(const struct drm_display_mode *mode_in)
- {
- /* 1-1 mapping, since both enums follow the HDMI spec. */
- return (enum dc_aspect_ratio) mode_in->picture_aspect_ratio;
- }
-
- static enum dc_color_space
- get_output_color_space(const struct dc_crtc_timing *dc_crtc_timing,
- const struct drm_connector_state *connector_state)
- {
- enum dc_color_space color_space = COLOR_SPACE_SRGB;
-
- switch (connector_state->colorspace) {
- case DRM_MODE_COLORIMETRY_BT601_YCC:
- if (dc_crtc_timing->flags.Y_ONLY)
- color_space = COLOR_SPACE_YCBCR601_LIMITED;
- else
- color_space = COLOR_SPACE_YCBCR601;
- break;
- case DRM_MODE_COLORIMETRY_BT709_YCC:
- if (dc_crtc_timing->flags.Y_ONLY)
- color_space = COLOR_SPACE_YCBCR709_LIMITED;
- else
- color_space = COLOR_SPACE_YCBCR709;
- break;
- case DRM_MODE_COLORIMETRY_OPRGB:
- color_space = COLOR_SPACE_ADOBERGB;
- break;
- case DRM_MODE_COLORIMETRY_BT2020_RGB:
- case DRM_MODE_COLORIMETRY_BT2020_YCC:
- if (dc_crtc_timing->pixel_encoding == PIXEL_ENCODING_RGB)
- color_space = COLOR_SPACE_2020_RGB_FULLRANGE;
- else
- color_space = COLOR_SPACE_2020_YCBCR_LIMITED;
- break;
- case DRM_MODE_COLORIMETRY_DEFAULT: // ITU601
- default:
- if (dc_crtc_timing->pixel_encoding == PIXEL_ENCODING_RGB) {
- color_space = COLOR_SPACE_SRGB;
- if (connector_state->hdmi.broadcast_rgb == DRM_HDMI_BROADCAST_RGB_LIMITED)
- color_space = COLOR_SPACE_SRGB_LIMITED;
- /*
- * 27030khz is the separation point between HDTV and SDTV
- * according to HDMI spec, we use YCbCr709 and YCbCr601
- * respectively
- */
- } else if (dc_crtc_timing->pix_clk_100hz > 270300) {
- if (dc_crtc_timing->flags.Y_ONLY)
- color_space =
- COLOR_SPACE_YCBCR709_LIMITED;
- else
- color_space = COLOR_SPACE_YCBCR709;
- } else {
- if (dc_crtc_timing->flags.Y_ONLY)
- color_space =
- COLOR_SPACE_YCBCR601_LIMITED;
- else
- color_space = COLOR_SPACE_YCBCR601;
- }
- break;
- }
-
- return color_space;
- }
-
- static enum display_content_type
- get_output_content_type(const struct drm_connector_state *connector_state)
- {
- switch (connector_state->content_type) {
- default:
- case DRM_MODE_CONTENT_TYPE_NO_DATA:
- return DISPLAY_CONTENT_TYPE_NO_DATA;
- case DRM_MODE_CONTENT_TYPE_GRAPHICS:
- return DISPLAY_CONTENT_TYPE_GRAPHICS;
- case DRM_MODE_CONTENT_TYPE_PHOTO:
- return DISPLAY_CONTENT_TYPE_PHOTO;
- case DRM_MODE_CONTENT_TYPE_CINEMA:
- return DISPLAY_CONTENT_TYPE_CINEMA;
- case DRM_MODE_CONTENT_TYPE_GAME:
- return DISPLAY_CONTENT_TYPE_GAME;
- }
- }
-
- static bool adjust_colour_depth_from_display_info(
- struct dc_crtc_timing *timing_out,
- const struct drm_display_info *info)
- {
- enum dc_color_depth depth = timing_out->display_color_depth;
- int normalized_clk;
-
- do {
- normalized_clk = timing_out->pix_clk_100hz / 10;
- /* YCbCr 4:2:0 requires additional adjustment of 1/2 */
- if (timing_out->pixel_encoding == PIXEL_ENCODING_YCBCR420)
- normalized_clk /= 2;
- /* Adjusting pix clock following on HDMI spec based on colour depth */
- switch (depth) {
- case COLOR_DEPTH_888:
- break;
- case COLOR_DEPTH_101010:
- normalized_clk = (normalized_clk * 30) / 24;
- break;
- case COLOR_DEPTH_121212:
- normalized_clk = (normalized_clk * 36) / 24;
- break;
- case COLOR_DEPTH_161616:
- normalized_clk = (normalized_clk * 48) / 24;
- break;
- default:
- /* The above depths are the only ones valid for HDMI. */
- return false;
- }
- if (normalized_clk <= info->max_tmds_clock) {
- timing_out->display_color_depth = depth;
- return true;
- }
- } while (--depth > COLOR_DEPTH_666);
- return false;
- }
-
- static void fill_stream_properties_from_drm_display_mode(
- struct dc_stream_state *stream,
- const struct drm_display_mode *mode_in,
- const struct drm_connector *connector,
- const struct drm_connector_state *connector_state,
- const struct dc_stream_state *old_stream,
- int requested_bpc)
- {
- struct dc_crtc_timing *timing_out = &stream->timing;
- const struct drm_display_info *info = &connector->display_info;
- struct amdgpu_dm_connector *aconnector = NULL;
- struct hdmi_vendor_infoframe hv_frame;
- struct hdmi_avi_infoframe avi_frame;
- ssize_t err;
-
- if (connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK)
- aconnector = to_amdgpu_dm_connector(connector);
-
- memset(&hv_frame, 0, sizeof(hv_frame));
- memset(&avi_frame, 0, sizeof(avi_frame));
-
- timing_out->h_border_left = 0;
- timing_out->h_border_right = 0;
- timing_out->v_border_top = 0;
- timing_out->v_border_bottom = 0;
- /* TODO: un-hardcode */
- if (drm_mode_is_420_only(info, mode_in)
- && (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A ||
- stream->signal == SIGNAL_TYPE_HDMI_FRL)
- && aconnector
- && aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR420)
- timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420;
- else if (drm_mode_is_420_also(info, mode_in)
- && aconnector
- && (aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR420
- || aconnector->force_yuv420_output))
- timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420;
- else if ((connector->display_info.color_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422))
- && aconnector
- && (aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR422
- || aconnector->force_yuv422_output))
- timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR422;
- else if ((connector->display_info.color_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444))
- && (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A ||
- stream->signal == SIGNAL_TYPE_HDMI_FRL)
- && aconnector
- && aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR444)
- timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR444;
- else
- timing_out->pixel_encoding = PIXEL_ENCODING_RGB;
-
- timing_out->timing_3d_format = TIMING_3D_FORMAT_NONE;
- timing_out->display_color_depth = convert_color_depth_from_display_info(
- connector,
- (timing_out->pixel_encoding == PIXEL_ENCODING_YCBCR420),
- requested_bpc);
- timing_out->scan_type = SCANNING_TYPE_NODATA;
- timing_out->hdmi_vic = 0;
-
- if (old_stream) {
- timing_out->vic = old_stream->timing.vic;
- timing_out->flags.HSYNC_POSITIVE_POLARITY = old_stream->timing.flags.HSYNC_POSITIVE_POLARITY;
- timing_out->flags.VSYNC_POSITIVE_POLARITY = old_stream->timing.flags.VSYNC_POSITIVE_POLARITY;
- } else {
- timing_out->vic = drm_match_cea_mode(mode_in);
- if (mode_in->flags & DRM_MODE_FLAG_PHSYNC)
- timing_out->flags.HSYNC_POSITIVE_POLARITY = 1;
- if (mode_in->flags & DRM_MODE_FLAG_PVSYNC)
- timing_out->flags.VSYNC_POSITIVE_POLARITY = 1;
- }
-
- if (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A ||
- stream->signal == SIGNAL_TYPE_HDMI_FRL) {
- err = drm_hdmi_avi_infoframe_from_display_mode(&avi_frame,
- (struct drm_connector *)connector,
- mode_in);
- if (err < 0)
- drm_warn_once(connector->dev, "Failed to setup avi infoframe on connector %s: %zd\n",
- connector->name, err);
- timing_out->vic = avi_frame.video_code;
- err = drm_hdmi_vendor_infoframe_from_display_mode(&hv_frame,
- (struct drm_connector *)connector,
- mode_in);
- if (err < 0)
- drm_warn_once(connector->dev, "Failed to setup vendor infoframe on connector %s: %zd\n",
- connector->name, err);
- timing_out->hdmi_vic = hv_frame.vic;
- }
-
- if (aconnector && is_freesync_video_mode(mode_in, aconnector)) {
- timing_out->h_addressable = mode_in->hdisplay;
- timing_out->h_total = mode_in->htotal;
- timing_out->h_sync_width = mode_in->hsync_end - mode_in->hsync_start;
- timing_out->h_front_porch = mode_in->hsync_start - mode_in->hdisplay;
- timing_out->v_total = mode_in->vtotal;
- timing_out->v_addressable = mode_in->vdisplay;
- timing_out->v_front_porch = mode_in->vsync_start - mode_in->vdisplay;
- timing_out->v_sync_width = mode_in->vsync_end - mode_in->vsync_start;
- timing_out->pix_clk_100hz = mode_in->clock * 10;
- } else {
- timing_out->h_addressable = mode_in->crtc_hdisplay;
- timing_out->h_total = mode_in->crtc_htotal;
- timing_out->h_sync_width = mode_in->crtc_hsync_end - mode_in->crtc_hsync_start;
- timing_out->h_front_porch = mode_in->crtc_hsync_start - mode_in->crtc_hdisplay;
- timing_out->v_total = mode_in->crtc_vtotal;
- timing_out->v_addressable = mode_in->crtc_vdisplay;
- timing_out->v_front_porch = mode_in->crtc_vsync_start - mode_in->crtc_vdisplay;
- timing_out->v_sync_width = mode_in->crtc_vsync_end - mode_in->crtc_vsync_start;
- timing_out->pix_clk_100hz = mode_in->crtc_clock * 10;
- }
-
- timing_out->aspect_ratio = get_aspect_ratio(mode_in);
-
- stream->out_transfer_func.type = TF_TYPE_PREDEFINED;
- stream->out_transfer_func.tf = TRANSFER_FUNCTION_SRGB;
- if (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A) {
- if (!adjust_colour_depth_from_display_info(timing_out, info) &&
- drm_mode_is_420_also(info, mode_in) &&
- timing_out->pixel_encoding != PIXEL_ENCODING_YCBCR420) {
- timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420;
- adjust_colour_depth_from_display_info(timing_out, info);
- }
- }
-
- stream->output_color_space = get_output_color_space(timing_out, connector_state);
- stream->content_type = get_output_content_type(connector_state);
- }
-
- static void fill_audio_info(struct audio_info *audio_info,
- const struct drm_connector *drm_connector,
- const struct dc_sink *dc_sink)
- {
- int i = 0;
- int cea_revision = 0;
- const struct dc_edid_caps *edid_caps = &dc_sink->edid_caps;
-
- audio_info->manufacture_id = edid_caps->manufacturer_id;
- audio_info->product_id = edid_caps->product_id;
-
- cea_revision = drm_connector->display_info.cea_rev;
-
- strscpy(audio_info->display_name,
- edid_caps->display_name,
- AUDIO_INFO_DISPLAY_NAME_SIZE_IN_CHARS);
-
- if (cea_revision >= 3) {
- audio_info->mode_count = edid_caps->audio_mode_count;
-
- for (i = 0; i < audio_info->mode_count; ++i) {
- audio_info->modes[i].format_code =
- (enum audio_format_code)
- (edid_caps->audio_modes[i].format_code);
- audio_info->modes[i].channel_count =
- edid_caps->audio_modes[i].channel_count;
- audio_info->modes[i].sample_rates.all =
- edid_caps->audio_modes[i].sample_rate;
- audio_info->modes[i].sample_size =
- edid_caps->audio_modes[i].sample_size;
- }
- }
-
- audio_info->flags.all = edid_caps->speaker_flags;
-
- /* TODO: We only check for the progressive mode, check for interlace mode too */
- if (drm_connector->latency_present[0]) {
- audio_info->video_latency = drm_connector->video_latency[0];
- audio_info->audio_latency = drm_connector->audio_latency[0];
- }
-
- /* TODO: For DP, video and audio latency should be calculated from DPCD caps */
-
- }
-
- static void
- copy_crtc_timing_for_drm_display_mode(const struct drm_display_mode *src_mode,
- struct drm_display_mode *dst_mode)
- {
- dst_mode->crtc_hdisplay = src_mode->crtc_hdisplay;
- dst_mode->crtc_vdisplay = src_mode->crtc_vdisplay;
- dst_mode->crtc_clock = src_mode->crtc_clock;
- dst_mode->crtc_hblank_start = src_mode->crtc_hblank_start;
- dst_mode->crtc_hblank_end = src_mode->crtc_hblank_end;
- dst_mode->crtc_hsync_start = src_mode->crtc_hsync_start;
- dst_mode->crtc_hsync_end = src_mode->crtc_hsync_end;
- dst_mode->crtc_htotal = src_mode->crtc_htotal;
- dst_mode->crtc_hskew = src_mode->crtc_hskew;
- dst_mode->crtc_vblank_start = src_mode->crtc_vblank_start;
- dst_mode->crtc_vblank_end = src_mode->crtc_vblank_end;
- dst_mode->crtc_vsync_start = src_mode->crtc_vsync_start;
- dst_mode->crtc_vsync_end = src_mode->crtc_vsync_end;
- dst_mode->crtc_vtotal = src_mode->crtc_vtotal;
- }
-
- static void
- decide_crtc_timing_for_drm_display_mode(struct drm_display_mode *drm_mode,
- const struct drm_display_mode *native_mode,
- bool scale_enabled)
- {
- if (scale_enabled || (
- native_mode->clock == drm_mode->clock &&
- native_mode->htotal == drm_mode->htotal &&
- native_mode->vtotal == drm_mode->vtotal)) {
- if (native_mode->crtc_clock)
- copy_crtc_timing_for_drm_display_mode(native_mode, drm_mode);
- } else {
- /* no scaling nor amdgpu inserted, no need to patch */
- }
- }
-
- static struct dc_sink *
- create_fake_sink(struct drm_device *dev, struct dc_link *link)
- {
- struct dc_sink_init_data sink_init_data = { 0 };
- struct dc_sink *sink = NULL;
-
- sink_init_data.link = link;
- sink_init_data.sink_signal = link->connector_signal;
-
- sink = dc_sink_create(&sink_init_data);
- if (!sink) {
- drm_err(dev, "Failed to create sink!\n");
- return NULL;
- }
- sink->sink_signal = SIGNAL_TYPE_VIRTUAL;
-
- return sink;
- }
-
- static void set_multisync_trigger_params(
- struct dc_stream_state *stream)
- {
- struct dc_stream_state *master = NULL;
-
- if (stream->triggered_crtc_reset.enabled) {
- master = stream->triggered_crtc_reset.event_source;
- stream->triggered_crtc_reset.event =
- master->timing.flags.VSYNC_POSITIVE_POLARITY ?
- CRTC_EVENT_VSYNC_RISING : CRTC_EVENT_VSYNC_FALLING;
- stream->triggered_crtc_reset.delay = TRIGGER_DELAY_NEXT_PIXEL;
- }
- }
-
- static void set_master_stream(struct dc_stream_state *stream_set[],
- int stream_count)
- {
- int j, highest_rfr = 0, master_stream = 0;
-
- for (j = 0; j < stream_count; j++) {
- if (stream_set[j] && stream_set[j]->triggered_crtc_reset.enabled) {
- int refresh_rate = 0;
-
- refresh_rate = (stream_set[j]->timing.pix_clk_100hz*100)/
- (stream_set[j]->timing.h_total*stream_set[j]->timing.v_total);
- if (refresh_rate > highest_rfr) {
- highest_rfr = refresh_rate;
- master_stream = j;
- }
- }
- }
- for (j = 0; j < stream_count; j++) {
- if (stream_set[j])
- stream_set[j]->triggered_crtc_reset.event_source = stream_set[master_stream];
- }
- }
-
- static void dm_enable_per_frame_crtc_master_sync(struct dc_state *context)
- {
- int i = 0;
- struct dc_stream_state *stream;
-
- if (context->stream_count < 2)
- return;
- for (i = 0; i < context->stream_count ; i++) {
- if (!context->streams[i])
- continue;
- /*
- * TODO: add a function to read AMD VSDB bits and set
- * crtc_sync_master.multi_sync_enabled flag
- * For now it's set to false
- */
- }
-
- set_master_stream(context->streams, context->stream_count);
-
- for (i = 0; i < context->stream_count ; i++) {
- stream = context->streams[i];
-
- if (!stream)
- continue;
-
- set_multisync_trigger_params(stream);
- }
- }
-
- /**
- * DOC: FreeSync Video
- *
- * When a userspace application wants to play a video, the content follows a
- * standard format definition that usually specifies the FPS for that format.
- * The below list illustrates some video format and the expected FPS,
- * respectively:
- *
- * - TV/NTSC (23.976 FPS)
- * - Cinema (24 FPS)
- * - TV/PAL (25 FPS)
- * - TV/NTSC (29.97 FPS)
- * - TV/NTSC (30 FPS)
- * - Cinema HFR (48 FPS)
- * - TV/PAL (50 FPS)
- * - Commonly used (60 FPS)
- * - Multiples of 24 (48,72,96 FPS)
- *
- * The list of standards video format is not huge and can be added to the
- * connector modeset list beforehand. With that, userspace can leverage
- * FreeSync to extends the front porch in order to attain the target refresh
- * rate. Such a switch will happen seamlessly, without screen blanking or
- * reprogramming of the output in any other way. If the userspace requests a
- * modesetting change compatible with FreeSync modes that only differ in the
- * refresh rate, DC will skip the full update and avoid blink during the
- * transition. For example, the video player can change the modesetting from
- * 60Hz to 30Hz for playing TV/NTSC content when it goes full screen without
- * causing any display blink. This same concept can be applied to a mode
- * setting change.
- */
- static struct drm_display_mode *
- get_highest_refresh_rate_mode(struct amdgpu_dm_connector *aconnector,
- bool use_probed_modes)
- {
- struct drm_display_mode *m, *m_pref = NULL;
- u16 current_refresh, highest_refresh;
- struct list_head *list_head = use_probed_modes ?
- &aconnector->base.probed_modes :
- &aconnector->base.modes;
-
- if (aconnector->base.connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
- return NULL;
-
- if (aconnector->freesync_vid_base.clock != 0)
- return &aconnector->freesync_vid_base;
-
- /* Find the preferred mode */
- list_for_each_entry(m, list_head, head) {
- if (m->type & DRM_MODE_TYPE_PREFERRED) {
- m_pref = m;
- break;
- }
- }
-
- if (!m_pref) {
- /* Probably an EDID with no preferred mode. Fallback to first entry */
- m_pref = list_first_entry_or_null(
- &aconnector->base.modes, struct drm_display_mode, head);
- if (!m_pref) {
- drm_dbg_driver(aconnector->base.dev, "No preferred mode found in EDID\n");
- return NULL;
- }
- }
-
- highest_refresh = drm_mode_vrefresh(m_pref);
-
- /*
- * Find the mode with highest refresh rate with same resolution.
- * For some monitors, preferred mode is not the mode with highest
- * supported refresh rate.
- */
- list_for_each_entry(m, list_head, head) {
- current_refresh = drm_mode_vrefresh(m);
-
- if (m->hdisplay == m_pref->hdisplay &&
- m->vdisplay == m_pref->vdisplay &&
- highest_refresh < current_refresh) {
- highest_refresh = current_refresh;
- m_pref = m;
- }
- }
-
- drm_mode_copy(&aconnector->freesync_vid_base, m_pref);
- return m_pref;
- }
-
- static bool is_freesync_video_mode(const struct drm_display_mode *mode,
- struct amdgpu_dm_connector *aconnector)
- {
- struct drm_display_mode *high_mode;
- int timing_diff;
-
- high_mode = get_highest_refresh_rate_mode(aconnector, false);
- if (!high_mode || !mode)
- return false;
-
- timing_diff = high_mode->vtotal - mode->vtotal;
-
- if (high_mode->clock == 0 || high_mode->clock != mode->clock ||
- high_mode->hdisplay != mode->hdisplay ||
- high_mode->vdisplay != mode->vdisplay ||
- high_mode->hsync_start != mode->hsync_start ||
- high_mode->hsync_end != mode->hsync_end ||
- high_mode->htotal != mode->htotal ||
- high_mode->hskew != mode->hskew ||
- high_mode->vscan != mode->vscan ||
- high_mode->vsync_start - mode->vsync_start != timing_diff ||
- high_mode->vsync_end - mode->vsync_end != timing_diff)
- return false;
- else
- return true;
- }
-
- #if defined(CONFIG_DRM_AMD_DC_FP)
- static void update_dsc_caps(struct amdgpu_dm_connector *aconnector,
- struct dc_sink *sink, struct dc_stream_state *stream,
- struct dsc_dec_dpcd_caps *dsc_caps)
- {
- stream->timing.flags.DSC = 0;
- dsc_caps->is_dsc_supported = false;
-
- if (aconnector->dc_link && (sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT ||
- sink->sink_signal == SIGNAL_TYPE_EDP)) {
- if (sink->link->dpcd_caps.dongle_type == DISPLAY_DONGLE_NONE)
- dc_dsc_parse_dsc_dpcd(aconnector->dc_link->ctx->dc,
- aconnector->dc_link->dpcd_caps.dsc_caps.dsc_basic_caps.raw,
- aconnector->dc_link->dpcd_caps.dsc_caps.dsc_branch_decoder_caps.raw,
- dsc_caps);
- else if (sink->link->dpcd_caps.dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER) {
- if (aconnector->dc_link->dpcd_caps.dsc_caps.dsc_basic_caps.fields.dsc_support.DSC_PASSTHROUGH_SUPPORT &&
- !aconnector->dsc_settings.dsc_force_disable_passthrough &&
- aconnector->dc_link->dpcd_caps.dongle_caps.dp_hdmi_frl_max_link_bw_in_kbps > 0 &&
- sink->edid_caps.frl_dsc_support &&
- sink->edid_caps.max_frl_rate > 0 &&
- sink->edid_caps.frl_dsc_max_frl_rate > 0)
- dc_dsc_parse_dsc_edid(aconnector->dc_link->ctx->dc, &sink->edid_caps, dsc_caps);
- else
- dc_dsc_parse_dsc_dpcd(aconnector->dc_link->ctx->dc,
- aconnector->dc_link->dpcd_caps.dsc_caps.dsc_basic_caps.raw,
- aconnector->dc_link->dpcd_caps.dsc_caps.dsc_branch_decoder_caps.raw,
- dsc_caps);
- }
- } else if (aconnector->dc_link && sink->sink_signal == SIGNAL_TYPE_HDMI_FRL) {
- if (sink->edid_caps.frl_dsc_support &&
- sink->edid_caps.max_frl_rate > 0 &&
- sink->edid_caps.frl_dsc_max_frl_rate > 0)
- dc_dsc_parse_dsc_edid(aconnector->dc_link->ctx->dc, &sink->edid_caps, dsc_caps);
- }
- }
-
- static void apply_dsc_policy_for_edp(struct amdgpu_dm_connector *aconnector,
- struct dc_sink *sink, struct dc_stream_state *stream,
- struct dsc_dec_dpcd_caps *dsc_caps,
- uint32_t max_dsc_target_bpp_limit_override)
- {
- const struct dc_link_settings *verified_link_cap = NULL;
- u32 link_bw_in_kbps;
- u32 edp_min_bpp_x16, edp_max_bpp_x16;
- struct dc *dc = sink->ctx->dc;
- struct dc_dsc_bw_range bw_range = {0};
- struct dc_dsc_config dsc_cfg = {0};
- struct dc_dsc_config_options dsc_options = {0};
-
- dc_dsc_get_default_config_option(dc, &dsc_options);
- dsc_options.max_target_bpp_limit_override_x16 = max_dsc_target_bpp_limit_override * 16;
-
- verified_link_cap = dc_link_get_link_cap(stream->link);
- link_bw_in_kbps = dc_link_bandwidth_kbps(stream->link, verified_link_cap);
- edp_min_bpp_x16 = 8 * 16;
- edp_max_bpp_x16 = 8 * 16;
-
- if (edp_max_bpp_x16 > dsc_caps->edp_max_bits_per_pixel)
- edp_max_bpp_x16 = dsc_caps->edp_max_bits_per_pixel;
-
- if (edp_max_bpp_x16 < edp_min_bpp_x16)
- edp_min_bpp_x16 = edp_max_bpp_x16;
-
- if (dc_dsc_compute_bandwidth_range(dc->res_pool->dscs[0],
- dc->debug.dsc_min_slice_height_override,
- edp_min_bpp_x16, edp_max_bpp_x16,
- dsc_caps,
- &stream->timing,
- dc_link_get_highest_encoding_format(aconnector->dc_link),
- &bw_range)) {
-
- if (bw_range.max_kbps < link_bw_in_kbps) {
- if (dc_dsc_compute_config(dc->res_pool->dscs[0],
- dsc_caps,
- &dsc_options,
- 0,
- &stream->timing,
- dc_link_get_highest_encoding_format(aconnector->dc_link),
- &dsc_cfg)) {
- stream->timing.dsc_cfg = dsc_cfg;
- stream->timing.flags.DSC = 1;
- stream->timing.dsc_cfg.bits_per_pixel = edp_max_bpp_x16;
- }
- return;
- }
- }
-
- if (dc_dsc_compute_config(dc->res_pool->dscs[0],
- dsc_caps,
- &dsc_options,
- link_bw_in_kbps,
- &stream->timing,
- dc_link_get_highest_encoding_format(aconnector->dc_link),
- &dsc_cfg)) {
- stream->timing.dsc_cfg = dsc_cfg;
- stream->timing.flags.DSC = 1;
- }
- }
-
- static void apply_dsc_policy_for_stream(struct amdgpu_dm_connector *aconnector,
- struct dc_sink *sink, struct dc_stream_state *stream,
- struct dsc_dec_dpcd_caps *dsc_caps)
- {
- struct drm_connector *drm_connector = &aconnector->base;
- u32 link_bandwidth_kbps;
- struct dc *dc = sink->ctx->dc;
- const struct dc_hdmi_frl_link_settings *frl_verified_link_cap = NULL;
- u32 converter_bw_in_kbps;
- u32 sink_bw_in_kbps;
- u32 dsc_sink_bw_in_kbps;
- u32 max_supported_bw_in_kbps, timing_bw_in_kbps;
- u32 dsc_max_supported_bw_in_kbps;
- u32 max_dsc_target_bpp_limit_override =
- drm_connector->display_info.max_dsc_bpp;
- struct dc_dsc_config_options dsc_options = {0};
-
- dc_dsc_get_default_config_option(dc, &dsc_options);
- dsc_options.max_target_bpp_limit_override_x16 = max_dsc_target_bpp_limit_override * 16;
-
- link_bandwidth_kbps = dc_link_bandwidth_kbps(aconnector->dc_link,
- dc_link_get_link_cap(aconnector->dc_link));
-
- /* Set DSC policy according to dsc_clock_en */
- dc_dsc_policy_set_enable_dsc_when_not_needed(
- aconnector->dsc_settings.dsc_force_enable == DSC_CLK_FORCE_ENABLE);
-
- if (sink->sink_signal == SIGNAL_TYPE_EDP &&
- !aconnector->dc_link->panel_config.dsc.disable_dsc_edp &&
- dc->caps.edp_dsc_support && aconnector->dsc_settings.dsc_force_enable != DSC_CLK_FORCE_DISABLE) {
-
- apply_dsc_policy_for_edp(aconnector, sink, stream, dsc_caps, max_dsc_target_bpp_limit_override);
-
- } else if (sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT) {
- if (sink->link->dpcd_caps.dongle_type == DISPLAY_DONGLE_NONE) {
- if (dc_dsc_compute_config(aconnector->dc_link->ctx->dc->res_pool->dscs[0],
- dsc_caps,
- &dsc_options,
- link_bandwidth_kbps,
- &stream->timing,
- dc_link_get_highest_encoding_format(aconnector->dc_link),
- &stream->timing.dsc_cfg)) {
- stream->timing.flags.DSC = 1;
- drm_dbg_driver(drm_connector->dev, "%s: SST_DSC [%s] DSC is selected from SST RX\n",
- __func__, drm_connector->name);
- }
- } else if (sink->link->dpcd_caps.dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER) {
- timing_bw_in_kbps = dc_bandwidth_in_kbps_from_timing(&stream->timing,
- dc_link_get_highest_encoding_format(aconnector->dc_link));
- converter_bw_in_kbps = aconnector->dc_link->dpcd_caps.dongle_caps.dp_hdmi_frl_max_link_bw_in_kbps;
- sink_bw_in_kbps = dc_link_bw_kbps_from_raw_frl_link_rate_data(dc, sink->edid_caps.max_frl_rate);
- dsc_sink_bw_in_kbps = dc_link_bw_kbps_from_raw_frl_link_rate_data(dc, sink->edid_caps.frl_dsc_max_frl_rate);
-
- if (dsc_caps->is_frl) {
- max_supported_bw_in_kbps = min(link_bandwidth_kbps, converter_bw_in_kbps);
- max_supported_bw_in_kbps = min(max_supported_bw_in_kbps, sink_bw_in_kbps);
- dsc_max_supported_bw_in_kbps = min(max_supported_bw_in_kbps, dsc_sink_bw_in_kbps);
- } else {
- max_supported_bw_in_kbps = link_bandwidth_kbps;
- dsc_max_supported_bw_in_kbps = link_bandwidth_kbps;
- }
-
- if (timing_bw_in_kbps > max_supported_bw_in_kbps &&
- max_supported_bw_in_kbps > 0 &&
- dsc_max_supported_bw_in_kbps > 0)
- if (dc_dsc_compute_config(aconnector->dc_link->ctx->dc->res_pool->dscs[0],
- dsc_caps,
- &dsc_options,
- dsc_max_supported_bw_in_kbps,
- &stream->timing,
- dc_link_get_highest_encoding_format(aconnector->dc_link),
- &stream->timing.dsc_cfg)) {
- stream->timing.flags.DSC = 1;
- drm_dbg_driver(drm_connector->dev, "%s: SST_DSC [%s] DSC is selected from %s\n",
- __func__, drm_connector->name,
- (dsc_caps->is_frl == 1) ? "HDMI FRL RX" : "DP-HDMI PCON");
- }
- }
- }
- else if (aconnector->dc_link && sink->sink_signal == SIGNAL_TYPE_HDMI_FRL) {
- struct dc_dsc_policy dsc_policy = {0};
-
- frl_verified_link_cap = dc_link_get_frl_link_cap(stream->link);
- if (frl_verified_link_cap->frl_link_rate != HDMI_FRL_LINK_RATE_DISABLE &&
- aconnector->dc_link->frl_flags.force_frl_dsc) {
- dc_dsc_policy_set_enable_dsc_when_not_needed(true);
- dc_dsc_get_policy_for_timing(&stream->timing, 0, &dsc_policy, dc_link_get_highest_encoding_format(stream->link));
- }
-
- timing_bw_in_kbps = dc_bandwidth_in_kbps_from_timing(&stream->timing, DC_LINK_ENCODING_HDMI_FRL);
- link_bandwidth_kbps = dc_link_frl_bandwidth_kbps(stream->link, frl_verified_link_cap->frl_link_rate);
- dsc_sink_bw_in_kbps = dc_link_bw_kbps_from_raw_frl_link_rate_data(dc, sink->edid_caps.frl_dsc_max_frl_rate);
-
- if ((timing_bw_in_kbps > link_bandwidth_kbps && dsc_sink_bw_in_kbps > 0) ||
- (dsc_policy.enable_dsc_when_not_needed || dsc_options.force_dsc_when_not_needed)) {
- if (dc_dsc_compute_config(aconnector->dc_link->ctx->dc->res_pool->dscs[0],
- dsc_caps,
- &dsc_options,
- dsc_sink_bw_in_kbps,
- &stream->timing,
- dc_link_get_highest_encoding_format(aconnector->dc_link),
- &stream->timing.dsc_cfg)) {
- stream->timing.flags.DSC = 1;
- drm_dbg_driver(drm_connector->dev, "%s: HDMI_FRL_DSC [%s] DSC is selected from HDMI FRL RX\n",
- __func__, drm_connector->name);
- }
- }
- }
-
- /* Overwrite the stream flag if DSC is enabled through debugfs */
- if (aconnector->dsc_settings.dsc_force_enable == DSC_CLK_FORCE_ENABLE)
- stream->timing.flags.DSC = 1;
-
- if (stream->timing.flags.DSC && aconnector->dsc_settings.dsc_num_slices_h)
- stream->timing.dsc_cfg.num_slices_h = aconnector->dsc_settings.dsc_num_slices_h;
-
- if (stream->timing.flags.DSC && aconnector->dsc_settings.dsc_num_slices_v)
- stream->timing.dsc_cfg.num_slices_v = aconnector->dsc_settings.dsc_num_slices_v;
-
- if (stream->timing.flags.DSC && aconnector->dsc_settings.dsc_bits_per_pixel)
- stream->timing.dsc_cfg.bits_per_pixel = aconnector->dsc_settings.dsc_bits_per_pixel;
- }
- #endif
-
- static struct dc_stream_state *
- create_stream_for_sink(struct drm_connector *connector,
- const struct drm_display_mode *drm_mode,
- const struct dm_connector_state *dm_state,
- const struct dc_stream_state *old_stream,
- int requested_bpc)
- {
- struct drm_device *dev = connector->dev;
- struct amdgpu_dm_connector *aconnector = NULL;
- struct drm_display_mode *preferred_mode = NULL;
- const struct drm_connector_state *con_state = &dm_state->base;
- struct dc_stream_state *stream = NULL;
- struct drm_display_mode mode;
- struct drm_display_mode saved_mode;
- struct drm_display_mode *freesync_mode = NULL;
- bool native_mode_found = false;
- bool recalculate_timing = false;
- bool scale = dm_state->scaling != RMX_OFF;
- int mode_refresh;
- int preferred_refresh = 0;
- enum color_transfer_func tf = TRANSFER_FUNC_UNKNOWN;
- #if defined(CONFIG_DRM_AMD_DC_FP)
- struct dsc_dec_dpcd_caps dsc_caps = {0};
- #endif
- struct dc_link *link = NULL;
- struct dc_sink *sink = NULL;
-
- drm_mode_init(&mode, drm_mode);
- memset(&saved_mode, 0, sizeof(saved_mode));
-
- if (connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK) {
- aconnector = NULL;
- aconnector = to_amdgpu_dm_connector(connector);
- link = aconnector->dc_link;
- } else {
- struct drm_writeback_connector *wbcon = NULL;
- struct amdgpu_dm_wb_connector *dm_wbcon = NULL;
-
- wbcon = drm_connector_to_writeback(connector);
- dm_wbcon = to_amdgpu_dm_wb_connector(wbcon);
- link = dm_wbcon->link;
- }
-
- if (!aconnector || !aconnector->dc_sink) {
- sink = create_fake_sink(dev, link);
- if (!sink)
- return stream;
-
- } else {
- sink = aconnector->dc_sink;
- dc_sink_retain(sink);
- }
-
- stream = dc_create_stream_for_sink(sink);
-
- if (stream == NULL) {
- drm_err(dev, "Failed to create stream for sink!\n");
- goto finish;
- }
-
- /* We leave this NULL for writeback connectors */
- stream->dm_stream_context = aconnector;
-
- stream->timing.flags.LTE_340MCSC_SCRAMBLE =
- connector->display_info.hdmi.scdc.scrambling.low_rates;
-
- list_for_each_entry(preferred_mode, &connector->modes, head) {
- /* Search for preferred mode */
- if (preferred_mode->type & DRM_MODE_TYPE_PREFERRED) {
- native_mode_found = true;
- break;
- }
- }
- if (!native_mode_found)
- preferred_mode = list_first_entry_or_null(
- &connector->modes,
- struct drm_display_mode,
- head);
-
- mode_refresh = drm_mode_vrefresh(&mode);
-
- if (preferred_mode == NULL) {
- /*
- * This may not be an error, the use case is when we have no
- * usermode calls to reset and set mode upon hotplug. In this
- * case, we call set mode ourselves to restore the previous mode
- * and the modelist may not be filled in time.
- */
- drm_dbg_driver(dev, "No preferred mode found\n");
- } else if (aconnector) {
- recalculate_timing = amdgpu_freesync_vid_mode &&
- is_freesync_video_mode(&mode, aconnector);
- if (recalculate_timing) {
- freesync_mode = get_highest_refresh_rate_mode(aconnector, false);
- drm_mode_copy(&saved_mode, &mode);
- saved_mode.picture_aspect_ratio = mode.picture_aspect_ratio;
- drm_mode_copy(&mode, freesync_mode);
- mode.picture_aspect_ratio = saved_mode.picture_aspect_ratio;
- } else {
- decide_crtc_timing_for_drm_display_mode(
- &mode, preferred_mode, scale);
-
- preferred_refresh = drm_mode_vrefresh(preferred_mode);
- }
- }
-
- if (recalculate_timing)
- drm_mode_set_crtcinfo(&saved_mode, 0);
-
- /*
- * If scaling is enabled and refresh rate didn't change
- * we copy the vic and polarities of the old timings
- */
- if (!scale || mode_refresh != preferred_refresh)
- fill_stream_properties_from_drm_display_mode(
- stream, &mode, connector, con_state, NULL,
- requested_bpc);
- else
- fill_stream_properties_from_drm_display_mode(
- stream, &mode, connector, con_state, old_stream,
- requested_bpc);
-
- /* The rest isn't needed for writeback connectors */
- if (!aconnector)
- goto finish;
-
- if (aconnector->timing_changed) {
- drm_dbg(aconnector->base.dev,
- "overriding timing for automated test, bpc %d, changing to %d\n",
- stream->timing.display_color_depth,
- aconnector->timing_requested->display_color_depth);
- stream->timing = *aconnector->timing_requested;
- }
-
- #if defined(CONFIG_DRM_AMD_DC_FP)
- /* SST DSC determination policy */
- update_dsc_caps(aconnector, sink, stream, &dsc_caps);
- if (aconnector->dsc_settings.dsc_force_enable != DSC_CLK_FORCE_DISABLE && dsc_caps.is_dsc_supported)
- apply_dsc_policy_for_stream(aconnector, sink, stream, &dsc_caps);
- #endif
-
- update_stream_scaling_settings(dev, &mode, dm_state, stream);
-
- fill_audio_info(
- &stream->audio_info,
- connector,
- sink);
-
- update_stream_signal(stream, sink);
-
- if (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A ||
- stream->signal == SIGNAL_TYPE_HDMI_FRL)
- mod_build_hf_vsif_infopacket(stream, &stream->vsp_infopacket, false, false);
-
- if (stream->signal == SIGNAL_TYPE_DISPLAY_PORT ||
- stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST ||
- stream->signal == SIGNAL_TYPE_EDP) {
- const struct dc_edid_caps *edid_caps;
- unsigned int disable_colorimetry = 0;
-
- if (aconnector->dc_sink) {
- edid_caps = &aconnector->dc_sink->edid_caps;
- disable_colorimetry = edid_caps->panel_patch.disable_colorimetry;
- }
-
- //
- // should decide stream support vsc sdp colorimetry capability
- // before building vsc info packet
- //
- stream->use_vsc_sdp_for_colorimetry = stream->link->dpcd_caps.dpcd_rev.raw >= 0x14 &&
- stream->link->dpcd_caps.dprx_feature.bits.VSC_SDP_COLORIMETRY_SUPPORTED &&
- !disable_colorimetry;
-
- if (stream->out_transfer_func.tf == TRANSFER_FUNCTION_GAMMA22)
- tf = TRANSFER_FUNC_GAMMA_22;
- mod_build_vsc_infopacket(stream, &stream->vsc_infopacket, stream->output_color_space, tf);
- aconnector->sr_skip_count = AMDGPU_DM_PSR_ENTRY_DELAY;
-
- }
- finish:
- dc_sink_release(sink);
-
- return stream;
- }
-
- /**
- * amdgpu_dm_connector_poll - Poll a connector to see if it's connected to a display
- * @aconnector: DM connector to poll (owns @base drm_connector and @dc_link)
- * @force: if true, force polling even when DAC load detection was used
- *
- * Used for connectors that don't support HPD (hotplug detection) to
- * periodically check whether the connector is connected to a display.
- *
- * When connection was determined via DAC load detection, we avoid
- * re-running it on normal polls to prevent visible glitches, unless
- * @force is set.
- *
- * Return: The probed connector status (connected/disconnected/unknown).
- */
- static enum drm_connector_status
- amdgpu_dm_connector_poll(struct amdgpu_dm_connector *aconnector, bool force)
- {
- struct drm_connector *connector = &aconnector->base;
- struct drm_device *dev = connector->dev;
- struct amdgpu_device *adev = drm_to_adev(dev);
- struct dc_link *link = aconnector->dc_link;
- enum dc_connection_type conn_type = dc_connection_none;
- enum drm_connector_status status = connector_status_disconnected;
-
- /* When we determined the connection using DAC load detection,
- * do NOT poll the connector do detect disconnect because
- * that would run DAC load detection again which can cause
- * visible visual glitches.
- *
- * Only allow to poll such a connector again when forcing.
- */
- if (!force && link->local_sink && link->type == dc_connection_analog_load)
- return connector->status;
-
- mutex_lock(&aconnector->hpd_lock);
-
- if (dc_link_detect_connection_type(aconnector->dc_link, &conn_type) &&
- conn_type != dc_connection_none) {
- mutex_lock(&adev->dm.dc_lock);
-
- /* Only call full link detection when a sink isn't created yet,
- * ie. just when the display is plugged in, otherwise we risk flickering.
- */
- if (link->local_sink ||
- dc_link_detect(link, DETECT_REASON_HPD))
- status = connector_status_connected;
-
- mutex_unlock(&adev->dm.dc_lock);
- }
-
- if (connector->status != status) {
- if (status == connector_status_disconnected) {
- if (link->local_sink)
- dc_sink_release(link->local_sink);
-
- link->local_sink = NULL;
- link->dpcd_sink_count = 0;
- link->type = dc_connection_none;
- }
-
- amdgpu_dm_update_connector_after_detect(aconnector);
- }
-
- mutex_unlock(&aconnector->hpd_lock);
- return status;
- }
-
- /**
- * amdgpu_dm_connector_detect() - Detect whether a DRM connector is connected to a display
- *
- * A connector is considered connected when it has a sink that is not NULL.
- * For connectors that support HPD (hotplug detection), the connection is
- * handled in the HPD interrupt.
- * For connectors that may not support HPD, such as analog connectors,
- * DRM will call this function repeatedly to poll them.
- *
- * Notes:
- * 1. This interface is NOT called in context of HPD irq.
- * 2. This interface *is called* in context of user-mode ioctl. Which
- * makes it a bad place for *any* MST-related activity.
- *
- * @connector: The DRM connector we are checking. We convert it to
- * amdgpu_dm_connector so we can read the DC link and state.
- * @force: If true, do a full detect again. This is used even when
- * a lighter check would normally be used to avoid flicker.
- *
- * Return: The connector status (connected, disconnected, or unknown).
- *
- */
- static enum drm_connector_status
- amdgpu_dm_connector_detect(struct drm_connector *connector, bool force)
- {
- struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
-
- update_subconnector_property(aconnector);
-
- if (aconnector->base.force == DRM_FORCE_ON ||
- aconnector->base.force == DRM_FORCE_ON_DIGITAL)
- return connector_status_connected;
- else if (aconnector->base.force == DRM_FORCE_OFF)
- return connector_status_disconnected;
-
- /* Poll analog connectors and only when either
- * disconnected or connected to an analog display.
- */
- if (drm_kms_helper_is_poll_worker() &&
- dc_connector_supports_analog(aconnector->dc_link->link_id.id) &&
- (!aconnector->dc_sink || aconnector->dc_sink->edid_caps.analog))
- return amdgpu_dm_connector_poll(aconnector, force);
-
- return (aconnector->dc_sink ? connector_status_connected :
- connector_status_disconnected);
- }
-
- int amdgpu_dm_connector_atomic_set_property(struct drm_connector *connector,
- struct drm_connector_state *connector_state,
- struct drm_property *property,
- uint64_t val)
- {
- struct drm_device *dev = connector->dev;
- struct amdgpu_device *adev = drm_to_adev(dev);
- struct dm_connector_state *dm_old_state =
- to_dm_connector_state(connector->state);
- struct dm_connector_state *dm_new_state =
- to_dm_connector_state(connector_state);
-
- int ret = -EINVAL;
-
- if (property == dev->mode_config.scaling_mode_property) {
- enum amdgpu_rmx_type rmx_type;
-
- switch (val) {
- case DRM_MODE_SCALE_CENTER:
- rmx_type = RMX_CENTER;
- break;
- case DRM_MODE_SCALE_ASPECT:
- rmx_type = RMX_ASPECT;
- break;
- case DRM_MODE_SCALE_FULLSCREEN:
- rmx_type = RMX_FULL;
- break;
- case DRM_MODE_SCALE_NONE:
- default:
- rmx_type = RMX_OFF;
- break;
- }
-
- if (dm_old_state->scaling == rmx_type)
- return 0;
-
- dm_new_state->scaling = rmx_type;
- ret = 0;
- } else if (property == adev->mode_info.underscan_hborder_property) {
- dm_new_state->underscan_hborder = val;
- ret = 0;
- } else if (property == adev->mode_info.underscan_vborder_property) {
- dm_new_state->underscan_vborder = val;
- ret = 0;
- } else if (property == adev->mode_info.underscan_property) {
- dm_new_state->underscan_enable = val;
- ret = 0;
- } else if (property == adev->mode_info.abm_level_property) {
- switch (val) {
- case ABM_SYSFS_CONTROL:
- dm_new_state->abm_sysfs_forbidden = false;
- break;
- case ABM_LEVEL_OFF:
- dm_new_state->abm_sysfs_forbidden = true;
- dm_new_state->abm_level = ABM_LEVEL_IMMEDIATE_DISABLE;
- break;
- default:
- dm_new_state->abm_sysfs_forbidden = true;
- dm_new_state->abm_level = val;
- }
- ret = 0;
- }
-
- return ret;
- }
-
- int amdgpu_dm_connector_atomic_get_property(struct drm_connector *connector,
- const struct drm_connector_state *state,
- struct drm_property *property,
- uint64_t *val)
- {
- struct drm_device *dev = connector->dev;
- struct amdgpu_device *adev = drm_to_adev(dev);
- struct dm_connector_state *dm_state =
- to_dm_connector_state(state);
- int ret = -EINVAL;
-
- if (property == dev->mode_config.scaling_mode_property) {
- switch (dm_state->scaling) {
- case RMX_CENTER:
- *val = DRM_MODE_SCALE_CENTER;
- break;
- case RMX_ASPECT:
- *val = DRM_MODE_SCALE_ASPECT;
- break;
- case RMX_FULL:
- *val = DRM_MODE_SCALE_FULLSCREEN;
- break;
- case RMX_OFF:
- default:
- *val = DRM_MODE_SCALE_NONE;
- break;
- }
- ret = 0;
- } else if (property == adev->mode_info.underscan_hborder_property) {
- *val = dm_state->underscan_hborder;
- ret = 0;
- } else if (property == adev->mode_info.underscan_vborder_property) {
- *val = dm_state->underscan_vborder;
- ret = 0;
- } else if (property == adev->mode_info.underscan_property) {
- *val = dm_state->underscan_enable;
- ret = 0;
- } else if (property == adev->mode_info.abm_level_property) {
- if (!dm_state->abm_sysfs_forbidden)
- *val = ABM_SYSFS_CONTROL;
- else
- *val = (dm_state->abm_level != ABM_LEVEL_IMMEDIATE_DISABLE) ?
- dm_state->abm_level : 0;
- ret = 0;
- }
-
- return ret;
- }
-
- /**
- * DOC: panel power savings
- *
- * The display manager allows you to set your desired **panel power savings**
- * level (between 0-4, with 0 representing off), e.g. using the following::
- *
- * # echo 3 > /sys/class/drm/card0-eDP-1/amdgpu/panel_power_savings
- *
- * Modifying this value can have implications on color accuracy, so tread
- * carefully.
- */
-
- static ssize_t panel_power_savings_show(struct device *device,
- struct device_attribute *attr,
- char *buf)
- {
- struct drm_connector *connector = dev_get_drvdata(device);
- struct drm_device *dev = connector->dev;
- u8 val;
-
- drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
- val = to_dm_connector_state(connector->state)->abm_level ==
- ABM_LEVEL_IMMEDIATE_DISABLE ? 0 :
- to_dm_connector_state(connector->state)->abm_level;
- drm_modeset_unlock(&dev->mode_config.connection_mutex);
-
- return sysfs_emit(buf, "%u\n", val);
- }
-
- static ssize_t panel_power_savings_store(struct device *device,
- struct device_attribute *attr,
- const char *buf, size_t count)
- {
- struct drm_connector *connector = dev_get_drvdata(device);
- struct drm_device *dev = connector->dev;
- long val;
- int ret;
-
- ret = kstrtol(buf, 0, &val);
-
- if (ret)
- return ret;
-
- if (val < 0 || val > 4)
- return -EINVAL;
-
- drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
- if (to_dm_connector_state(connector->state)->abm_sysfs_forbidden)
- ret = -EBUSY;
- else
- to_dm_connector_state(connector->state)->abm_level = val ?:
- ABM_LEVEL_IMMEDIATE_DISABLE;
- drm_modeset_unlock(&dev->mode_config.connection_mutex);
-
- if (ret)
- return ret;
-
- drm_kms_helper_hotplug_event(dev);
-
- return count;
- }
-
- static DEVICE_ATTR_RW(panel_power_savings);
-
- static struct attribute *amdgpu_attrs[] = {
- &dev_attr_panel_power_savings.attr,
- NULL
- };
-
- static const struct attribute_group amdgpu_group = {
- .name = "amdgpu",
- .attrs = amdgpu_attrs
- };
-
- static bool
- amdgpu_dm_should_create_sysfs(struct amdgpu_dm_connector *amdgpu_dm_connector)
- {
- if (amdgpu_dm_abm_level >= 0)
- return false;
-
- if (amdgpu_dm_connector->base.connector_type != DRM_MODE_CONNECTOR_eDP)
- return false;
-
- /* check for OLED panels */
- if (amdgpu_dm_connector->bl_idx >= 0) {
- struct drm_device *drm = amdgpu_dm_connector->base.dev;
- struct amdgpu_display_manager *dm = &drm_to_adev(drm)->dm;
- struct amdgpu_dm_backlight_caps *caps;
-
- caps = &dm->backlight_caps[amdgpu_dm_connector->bl_idx];
- if (caps->aux_support)
- return false;
- }
-
- return true;
- }
-
- static void amdgpu_dm_connector_unregister(struct drm_connector *connector)
- {
- struct amdgpu_dm_connector *amdgpu_dm_connector = to_amdgpu_dm_connector(connector);
-
- if (amdgpu_dm_should_create_sysfs(amdgpu_dm_connector))
- sysfs_remove_group(&connector->kdev->kobj, &amdgpu_group);
-
- cec_notifier_conn_unregister(amdgpu_dm_connector->notifier);
- drm_dp_aux_unregister(&amdgpu_dm_connector->dm_dp_aux.aux);
- }
-
- static void amdgpu_dm_connector_destroy(struct drm_connector *connector)
- {
- struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
- struct amdgpu_device *adev = drm_to_adev(connector->dev);
- struct amdgpu_display_manager *dm = &adev->dm;
-
- /*
- * Call only if mst_mgr was initialized before since it's not done
- * for all connector types.
- */
- if (aconnector->mst_mgr.dev)
- drm_dp_mst_topology_mgr_destroy(&aconnector->mst_mgr);
-
- /* Cancel and flush any pending HDMI HPD debounce work */
- if (aconnector->hdmi_hpd_debounce_delay_ms) {
- cancel_delayed_work_sync(&aconnector->hdmi_hpd_debounce_work);
- if (aconnector->hdmi_prev_sink) {
- dc_sink_release(aconnector->hdmi_prev_sink);
- aconnector->hdmi_prev_sink = NULL;
- }
- }
-
- if (aconnector->bl_idx != -1) {
- backlight_device_unregister(dm->backlight_dev[aconnector->bl_idx]);
- dm->backlight_dev[aconnector->bl_idx] = NULL;
- }
-
- if (aconnector->dc_em_sink)
- dc_sink_release(aconnector->dc_em_sink);
- aconnector->dc_em_sink = NULL;
- if (aconnector->dc_sink)
- dc_sink_release(aconnector->dc_sink);
- aconnector->dc_sink = NULL;
-
- drm_dp_cec_unregister_connector(&aconnector->dm_dp_aux.aux);
- drm_connector_unregister(connector);
- drm_connector_cleanup(connector);
- kfree(aconnector->dm_dp_aux.aux.name);
-
- kfree(connector);
- }
-
- void amdgpu_dm_connector_funcs_reset(struct drm_connector *connector)
- {
- struct dm_connector_state *state =
- to_dm_connector_state(connector->state);
-
- if (connector->state)
- __drm_atomic_helper_connector_destroy_state(connector->state);
-
- kfree(state);
-
- state = kzalloc_obj(*state);
-
- if (state) {
- state->scaling = RMX_OFF;
- state->underscan_enable = false;
- state->underscan_hborder = 0;
- state->underscan_vborder = 0;
- state->base.max_requested_bpc = 8;
- state->vcpi_slots = 0;
- state->pbn = 0;
-
- if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) {
- if (amdgpu_dm_abm_level <= 0)
- state->abm_level = ABM_LEVEL_IMMEDIATE_DISABLE;
- else
- state->abm_level = amdgpu_dm_abm_level;
- }
-
- __drm_atomic_helper_connector_reset(connector, &state->base);
- }
- }
-
- struct drm_connector_state *
- amdgpu_dm_connector_atomic_duplicate_state(struct drm_connector *connector)
- {
- struct dm_connector_state *state =
- to_dm_connector_state(connector->state);
-
- struct dm_connector_state *new_state =
- kmemdup(state, sizeof(*state), GFP_KERNEL);
-
- if (!new_state)
- return NULL;
-
- __drm_atomic_helper_connector_duplicate_state(connector, &new_state->base);
-
- new_state->freesync_capable = state->freesync_capable;
- new_state->abm_level = state->abm_level;
- new_state->scaling = state->scaling;
- new_state->underscan_enable = state->underscan_enable;
- new_state->underscan_hborder = state->underscan_hborder;
- new_state->underscan_vborder = state->underscan_vborder;
- new_state->vcpi_slots = state->vcpi_slots;
- new_state->pbn = state->pbn;
- return &new_state->base;
- }
-
- static int
- amdgpu_dm_connector_late_register(struct drm_connector *connector)
- {
- struct amdgpu_dm_connector *amdgpu_dm_connector =
- to_amdgpu_dm_connector(connector);
- int r;
-
- if (amdgpu_dm_should_create_sysfs(amdgpu_dm_connector)) {
- r = sysfs_create_group(&connector->kdev->kobj,
- &amdgpu_group);
- if (r)
- return r;
- }
-
- amdgpu_dm_register_backlight_device(amdgpu_dm_connector);
-
- if ((connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort) ||
- (connector->connector_type == DRM_MODE_CONNECTOR_eDP)) {
- amdgpu_dm_connector->dm_dp_aux.aux.dev = connector->kdev;
- r = drm_dp_aux_register(&amdgpu_dm_connector->dm_dp_aux.aux);
- if (r)
- return r;
- }
-
- #if defined(CONFIG_DEBUG_FS)
- connector_debugfs_init(amdgpu_dm_connector);
- #endif
-
- return 0;
- }
-
- static void amdgpu_dm_connector_funcs_force(struct drm_connector *connector)
- {
- struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
- struct dc_link *dc_link = aconnector->dc_link;
- struct dc_sink *dc_em_sink = aconnector->dc_em_sink;
- const struct drm_edid *drm_edid;
- struct i2c_adapter *ddc;
- struct drm_device *dev = connector->dev;
-
- if (dc_link && dc_link->aux_mode)
- ddc = &aconnector->dm_dp_aux.aux.ddc;
- else
- ddc = &aconnector->i2c->base;
-
- drm_edid = drm_edid_read_ddc(connector, ddc);
- drm_edid_connector_update(connector, drm_edid);
- if (!drm_edid) {
- drm_err(dev, "No EDID found on connector: %s.\n", connector->name);
- return;
- }
-
- aconnector->drm_edid = drm_edid;
- /* Update emulated (virtual) sink's EDID */
- if (dc_em_sink && dc_link) {
- // FIXME: Get rid of drm_edid_raw()
- const struct edid *edid = drm_edid_raw(drm_edid);
-
- memset(&dc_em_sink->edid_caps, 0, sizeof(struct dc_edid_caps));
- memmove(dc_em_sink->dc_edid.raw_edid, edid,
- (edid->extensions + 1) * EDID_LENGTH);
- dm_helpers_parse_edid_caps(
- dc_link,
- &dc_em_sink->dc_edid,
- &dc_em_sink->edid_caps);
- }
- }
-
- static const struct drm_connector_funcs amdgpu_dm_connector_funcs = {
- .reset = amdgpu_dm_connector_funcs_reset,
- .detect = amdgpu_dm_connector_detect,
- .fill_modes = drm_helper_probe_single_connector_modes,
- .destroy = amdgpu_dm_connector_destroy,
- .atomic_duplicate_state = amdgpu_dm_connector_atomic_duplicate_state,
- .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
- .atomic_set_property = amdgpu_dm_connector_atomic_set_property,
- .atomic_get_property = amdgpu_dm_connector_atomic_get_property,
- .late_register = amdgpu_dm_connector_late_register,
- .early_unregister = amdgpu_dm_connector_unregister,
- .force = amdgpu_dm_connector_funcs_force
- };
-
- static int get_modes(struct drm_connector *connector)
- {
- return amdgpu_dm_connector_get_modes(connector);
- }
-
- static void create_eml_sink(struct amdgpu_dm_connector *aconnector)
- {
- struct drm_connector *connector = &aconnector->base;
- struct dc_link *dc_link = aconnector->dc_link;
- struct dc_sink_init_data init_params = {
- .link = aconnector->dc_link,
- .sink_signal = SIGNAL_TYPE_VIRTUAL
- };
- const struct drm_edid *drm_edid;
- const struct edid *edid;
- struct i2c_adapter *ddc;
-
- if (dc_link && dc_link->aux_mode)
- ddc = &aconnector->dm_dp_aux.aux.ddc;
- else
- ddc = &aconnector->i2c->base;
-
- drm_edid = drm_edid_read_ddc(connector, ddc);
- drm_edid_connector_update(connector, drm_edid);
- if (!drm_edid) {
- drm_err(connector->dev, "No EDID found on connector: %s.\n", connector->name);
- return;
- }
-
- if (connector->display_info.is_hdmi)
- init_params.sink_signal = SIGNAL_TYPE_HDMI_TYPE_A;
-
- aconnector->drm_edid = drm_edid;
-
- edid = drm_edid_raw(drm_edid); // FIXME: Get rid of drm_edid_raw()
- aconnector->dc_em_sink = dc_link_add_remote_sink(
- aconnector->dc_link,
- (uint8_t *)edid,
- (edid->extensions + 1) * EDID_LENGTH,
- &init_params);
-
- if (aconnector->base.force == DRM_FORCE_ON) {
- aconnector->dc_sink = aconnector->dc_link->local_sink ?
- aconnector->dc_link->local_sink :
- aconnector->dc_em_sink;
- if (aconnector->dc_sink)
- dc_sink_retain(aconnector->dc_sink);
- }
- }
-
- static void handle_edid_mgmt(struct amdgpu_dm_connector *aconnector)
- {
- struct dc_link *link = (struct dc_link *)aconnector->dc_link;
-
- /*
- * In case of headless boot with force on for DP managed connector
- * Those settings have to be != 0 to get initial modeset
- */
- if (link->connector_signal == SIGNAL_TYPE_DISPLAY_PORT) {
- link->verified_link_cap.lane_count = LANE_COUNT_FOUR;
- link->verified_link_cap.link_rate = LINK_RATE_HIGH2;
- }
-
- create_eml_sink(aconnector);
- }
-
- static enum dc_status dm_validate_stream_and_context(struct dc *dc,
- struct dc_stream_state *stream)
- {
- enum dc_status dc_result = DC_ERROR_UNEXPECTED;
- struct dc_plane_state *dc_plane_state = NULL;
- struct dc_state *dc_state = NULL;
-
- if (!stream)
- goto cleanup;
-
- dc_plane_state = dc_create_plane_state(dc);
- if (!dc_plane_state)
- goto cleanup;
-
- dc_state = dc_state_create(dc, NULL);
- if (!dc_state)
- goto cleanup;
-
- /* populate stream to plane */
- dc_plane_state->src_rect.height = stream->src.height;
- dc_plane_state->src_rect.width = stream->src.width;
- dc_plane_state->dst_rect.height = stream->src.height;
- dc_plane_state->dst_rect.width = stream->src.width;
- dc_plane_state->clip_rect.height = stream->src.height;
- dc_plane_state->clip_rect.width = stream->src.width;
- dc_plane_state->plane_size.surface_pitch = ((stream->src.width + 255) / 256) * 256;
- dc_plane_state->plane_size.surface_size.height = stream->src.height;
- dc_plane_state->plane_size.surface_size.width = stream->src.width;
- dc_plane_state->plane_size.chroma_size.height = stream->src.height;
- dc_plane_state->plane_size.chroma_size.width = stream->src.width;
- dc_plane_state->format = SURFACE_PIXEL_FORMAT_GRPH_ARGB8888;
- dc_plane_state->tiling_info.gfx9.swizzle = DC_SW_UNKNOWN;
- dc_plane_state->rotation = ROTATION_ANGLE_0;
- dc_plane_state->is_tiling_rotated = false;
- dc_plane_state->tiling_info.gfx8.array_mode = DC_ARRAY_LINEAR_GENERAL;
-
- dc_result = dc_validate_stream(dc, stream);
- if (dc_result == DC_OK)
- dc_result = dc_validate_plane(dc, dc_plane_state);
-
- if (dc_result == DC_OK)
- dc_result = dc_state_add_stream(dc, dc_state, stream);
-
- if (dc_result == DC_OK && !dc_state_add_plane(
- dc,
- stream,
- dc_plane_state,
- dc_state))
- dc_result = DC_FAIL_ATTACH_SURFACES;
-
- if (dc_result == DC_OK)
- dc_result = dc_validate_global_state(dc, dc_state, DC_VALIDATE_MODE_ONLY);
-
- cleanup:
- if (dc_state)
- dc_state_release(dc_state);
-
- if (dc_plane_state)
- dc_plane_state_release(dc_plane_state);
-
- return dc_result;
- }
-
- struct dc_stream_state *
- create_validate_stream_for_sink(struct drm_connector *connector,
- const struct drm_display_mode *drm_mode,
- const struct dm_connector_state *dm_state,
- const struct dc_stream_state *old_stream)
- {
- struct amdgpu_dm_connector *aconnector = NULL;
- struct amdgpu_device *adev = drm_to_adev(connector->dev);
- struct dc_stream_state *stream;
- const struct drm_connector_state *drm_state = dm_state ? &dm_state->base : NULL;
- int requested_bpc = drm_state ? drm_state->max_requested_bpc : 8;
- enum dc_status dc_result = DC_OK;
- uint8_t bpc_limit = 6;
-
- if (!dm_state)
- return NULL;
-
- if (connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK)
- aconnector = to_amdgpu_dm_connector(connector);
-
- if (aconnector &&
- (aconnector->dc_link->connector_signal == SIGNAL_TYPE_HDMI_TYPE_A ||
- aconnector->dc_link->connector_signal == SIGNAL_TYPE_HDMI_FRL ||
- aconnector->dc_link->dpcd_caps.dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER))
- bpc_limit = 8;
-
- do {
- drm_dbg_kms(connector->dev, "Trying with %d bpc\n", requested_bpc);
- stream = create_stream_for_sink(connector, drm_mode,
- dm_state, old_stream,
- requested_bpc);
- if (stream == NULL) {
- drm_err(adev_to_drm(adev), "Failed to create stream for sink!\n");
- break;
- }
-
- dc_result = dc_validate_stream(adev->dm.dc, stream);
-
- if (!aconnector) /* writeback connector */
- return stream;
-
- if (dc_result == DC_OK && stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST)
- dc_result = dm_dp_mst_is_port_support_mode(aconnector, stream);
-
- if (dc_result == DC_OK)
- dc_result = dm_validate_stream_and_context(adev->dm.dc, stream);
-
- if (dc_result != DC_OK) {
- drm_dbg_kms(connector->dev, "Pruned mode %d x %d (clk %d) %s %s -- %s\n",
- drm_mode->hdisplay,
- drm_mode->vdisplay,
- drm_mode->clock,
- dc_pixel_encoding_to_str(stream->timing.pixel_encoding),
- dc_color_depth_to_str(stream->timing.display_color_depth),
- dc_status_to_str(dc_result));
-
- dc_stream_release(stream);
- stream = NULL;
- requested_bpc -= 2; /* lower bpc to retry validation */
- }
-
- } while (stream == NULL && requested_bpc >= bpc_limit);
-
- switch (dc_result) {
- /*
- * If we failed to validate DP bandwidth stream with the requested RGB color depth,
- * we try to fallback and configure in order:
- * YUV422 (8bpc, 6bpc)
- * YUV420 (8bpc, 6bpc)
- */
- case DC_FAIL_ENC_VALIDATE:
- case DC_EXCEED_DONGLE_CAP:
- case DC_NO_DP_LINK_BANDWIDTH:
- /* recursively entered twice and already tried both YUV422 and YUV420 */
- if (aconnector->force_yuv422_output && aconnector->force_yuv420_output)
- break;
- /* first failure; try YUV422 */
- if (!aconnector->force_yuv422_output) {
- drm_dbg_kms(connector->dev, "%s:%d Validation failed with %d, retrying w/ YUV422\n",
- __func__, __LINE__, dc_result);
- aconnector->force_yuv422_output = true;
- /* recursively entered and YUV422 failed, try YUV420 */
- } else if (!aconnector->force_yuv420_output) {
- drm_dbg_kms(connector->dev, "%s:%d Validation failed with %d, retrying w/ YUV420\n",
- __func__, __LINE__, dc_result);
- aconnector->force_yuv420_output = true;
- }
- stream = create_validate_stream_for_sink(connector, drm_mode,
- dm_state, old_stream);
- aconnector->force_yuv422_output = false;
- aconnector->force_yuv420_output = false;
- break;
- case DC_OK:
- break;
- default:
- drm_dbg_kms(connector->dev, "%s:%d Unhandled validation failure %d\n",
- __func__, __LINE__, dc_result);
- break;
- }
-
- return stream;
- }
-
- enum drm_mode_status amdgpu_dm_connector_mode_valid(struct drm_connector *connector,
- const struct drm_display_mode *mode)
- {
- int result = MODE_ERROR;
- struct dc_sink *dc_sink;
- struct drm_display_mode *test_mode;
- /* TODO: Unhardcode stream count */
- struct dc_stream_state *stream;
- /* we always have an amdgpu_dm_connector here since we got
- * here via the amdgpu_dm_connector_helper_funcs
- */
- struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
-
- if ((mode->flags & DRM_MODE_FLAG_INTERLACE) ||
- (mode->flags & DRM_MODE_FLAG_DBLSCAN))
- return result;
-
- /*
- * Only run this the first time mode_valid is called to initilialize
- * EDID mgmt
- */
- if (aconnector->base.force != DRM_FORCE_UNSPECIFIED &&
- !aconnector->dc_em_sink)
- handle_edid_mgmt(aconnector);
-
- dc_sink = to_amdgpu_dm_connector(connector)->dc_sink;
-
- if (dc_sink == NULL && aconnector->base.force != DRM_FORCE_ON_DIGITAL &&
- aconnector->base.force != DRM_FORCE_ON) {
- drm_err(connector->dev, "dc_sink is NULL!\n");
- goto fail;
- }
-
- test_mode = drm_mode_duplicate(connector->dev, mode);
- if (!test_mode)
- goto fail;
-
- drm_mode_set_crtcinfo(test_mode, 0);
-
- stream = create_validate_stream_for_sink(connector, test_mode,
- to_dm_connector_state(connector->state),
- NULL);
- drm_mode_destroy(connector->dev, test_mode);
- if (stream) {
- dc_stream_release(stream);
- result = MODE_OK;
- }
-
- fail:
- /* TODO: error handling*/
- return result;
- }
-
- static int fill_hdr_info_packet(const struct drm_connector_state *state,
- struct dc_info_packet *out)
- {
- struct hdmi_drm_infoframe frame;
- unsigned char buf[30]; /* 26 + 4 */
- ssize_t len;
- int ret, i;
-
- memset(out, 0, sizeof(*out));
-
- if (!state->hdr_output_metadata)
- return 0;
-
- ret = drm_hdmi_infoframe_set_hdr_metadata(&frame, state);
- if (ret)
- return ret;
-
- len = hdmi_drm_infoframe_pack_only(&frame, buf, sizeof(buf));
- if (len < 0)
- return (int)len;
-
- /* Static metadata is a fixed 26 bytes + 4 byte header. */
- if (len != 30)
- return -EINVAL;
-
- /* Prepare the infopacket for DC. */
- switch (state->connector->connector_type) {
- case DRM_MODE_CONNECTOR_HDMIA:
- out->hb0 = 0x87; /* type */
- out->hb1 = 0x01; /* version */
- out->hb2 = 0x1A; /* length */
- out->sb[0] = buf[3]; /* checksum */
- i = 1;
- break;
-
- case DRM_MODE_CONNECTOR_DisplayPort:
- case DRM_MODE_CONNECTOR_eDP:
- out->hb0 = 0x00; /* sdp id, zero */
- out->hb1 = 0x87; /* type */
- out->hb2 = 0x1D; /* payload len - 1 */
- out->hb3 = (0x13 << 2); /* sdp version */
- out->sb[0] = 0x01; /* version */
- out->sb[1] = 0x1A; /* length */
- i = 2;
- break;
-
- default:
- return -EINVAL;
- }
-
- memcpy(&out->sb[i], &buf[4], 26);
- out->valid = true;
-
- print_hex_dump(KERN_DEBUG, "HDR SB:", DUMP_PREFIX_NONE, 16, 1, out->sb,
- sizeof(out->sb), false);
-
- return 0;
- }
-
- static int
- amdgpu_dm_connector_atomic_check(struct drm_connector *conn,
- struct drm_atomic_commit *state)
- {
- struct drm_connector_state *new_con_state =
- drm_atomic_get_new_connector_state(state, conn);
- struct drm_connector_state *old_con_state =
- drm_atomic_get_old_connector_state(state, conn);
- struct drm_crtc *crtc = new_con_state->crtc;
- struct drm_crtc_state *new_crtc_state;
- struct amdgpu_dm_connector *aconn = to_amdgpu_dm_connector(conn);
- int ret;
-
- if (WARN_ON(unlikely(!old_con_state || !new_con_state)))
- return -EINVAL;
-
- trace_amdgpu_dm_connector_atomic_check(new_con_state);
-
- if (conn->connector_type == DRM_MODE_CONNECTOR_DisplayPort) {
- ret = drm_dp_mst_root_conn_atomic_check(new_con_state, &aconn->mst_mgr);
- if (ret < 0)
- return ret;
- }
-
- if (!crtc)
- return 0;
-
- if (new_con_state->privacy_screen_sw_state != old_con_state->privacy_screen_sw_state) {
- new_crtc_state = drm_atomic_get_crtc_state(state, crtc);
- if (IS_ERR(new_crtc_state))
- return PTR_ERR(new_crtc_state);
-
- new_crtc_state->mode_changed = true;
- }
-
- if (new_con_state->colorspace != old_con_state->colorspace) {
- new_crtc_state = drm_atomic_get_crtc_state(state, crtc);
- if (IS_ERR(new_crtc_state))
- return PTR_ERR(new_crtc_state);
-
- new_crtc_state->mode_changed = true;
- }
-
- if (new_con_state->content_type != old_con_state->content_type) {
- new_crtc_state = drm_atomic_get_crtc_state(state, crtc);
- if (IS_ERR(new_crtc_state))
- return PTR_ERR(new_crtc_state);
-
- new_crtc_state->mode_changed = true;
- }
-
- if (!drm_connector_atomic_hdr_metadata_equal(old_con_state, new_con_state)) {
- struct dc_info_packet hdr_infopacket;
-
- ret = fill_hdr_info_packet(new_con_state, &hdr_infopacket);
- if (ret)
- return ret;
-
- new_crtc_state = drm_atomic_get_crtc_state(state, crtc);
- if (IS_ERR(new_crtc_state))
- return PTR_ERR(new_crtc_state);
-
- /*
- * DC considers the stream backends changed if the
- * static metadata changes. Forcing the modeset also
- * gives a simple way for userspace to switch from
- * 8bpc to 10bpc when setting the metadata to enter
- * or exit HDR.
- *
- * Changing the static metadata after it's been
- * set is permissible, however. So only force a
- * modeset if we're entering or exiting HDR.
- */
- new_crtc_state->mode_changed = new_crtc_state->mode_changed ||
- !old_con_state->hdr_output_metadata ||
- !new_con_state->hdr_output_metadata;
- }
-
- return 0;
- }
-
- static const struct drm_connector_helper_funcs
- amdgpu_dm_connector_helper_funcs = {
- /*
- * If hotplugging a second bigger display in FB Con mode, bigger resolution
- * modes will be filtered by drm_mode_validate_size(), and those modes
- * are missing after user start lightdm. So we need to renew modes list.
- * in get_modes call back, not just return the modes count
- */
- .get_modes = get_modes,
- .mode_valid = amdgpu_dm_connector_mode_valid,
- .atomic_check = amdgpu_dm_connector_atomic_check,
- };
-
- static void dm_encoder_helper_disable(struct drm_encoder *encoder)
- {
-
- }
-
- int convert_dc_color_depth_into_bpc(enum dc_color_depth display_color_depth)
- {
- switch (display_color_depth) {
- case COLOR_DEPTH_666:
- return 6;
- case COLOR_DEPTH_888:
- return 8;
- case COLOR_DEPTH_101010:
- return 10;
- case COLOR_DEPTH_121212:
- return 12;
- case COLOR_DEPTH_141414:
- return 14;
- case COLOR_DEPTH_161616:
- return 16;
- default:
- break;
- }
- return 0;
- }
-
- static int dm_encoder_helper_atomic_check(struct drm_encoder *encoder,
- struct drm_crtc_state *crtc_state,
- struct drm_connector_state *conn_state)
- {
- struct drm_atomic_commit *state = crtc_state->state;
- struct drm_connector *connector = conn_state->connector;
- struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
- struct dm_connector_state *dm_new_connector_state = to_dm_connector_state(conn_state);
- const struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode;
- struct drm_dp_mst_topology_mgr *mst_mgr;
- struct drm_dp_mst_port *mst_port;
- struct drm_dp_mst_topology_state *mst_state;
- enum dc_color_depth color_depth;
- int clock, bpp = 0;
- bool is_y420 = false;
-
- if ((connector->connector_type == DRM_MODE_CONNECTOR_eDP) ||
- (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)) {
- struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
- struct drm_display_mode *native_mode = &amdgpu_encoder->native_mode;
- enum drm_mode_status result;
-
- result = drm_crtc_helper_mode_valid_fixed(encoder->crtc, adjusted_mode, native_mode);
- if (result != MODE_OK && dm_new_connector_state->scaling == RMX_OFF) {
- drm_dbg_driver(encoder->dev,
- "mode %dx%d@%dHz is not native, enabling scaling\n",
- adjusted_mode->hdisplay, adjusted_mode->vdisplay,
- drm_mode_vrefresh(adjusted_mode));
- dm_new_connector_state->scaling = RMX_ASPECT;
- }
- return 0;
- }
-
- if (!aconnector->mst_output_port)
- return 0;
-
- mst_port = aconnector->mst_output_port;
- mst_mgr = &aconnector->mst_root->mst_mgr;
-
- if (!crtc_state->connectors_changed && !crtc_state->mode_changed)
- return 0;
-
- mst_state = drm_atomic_get_mst_topology_state(state, mst_mgr);
- if (IS_ERR(mst_state))
- return PTR_ERR(mst_state);
-
- mst_state->pbn_div.full = dm_mst_get_pbn_divider(aconnector->mst_root->dc_link);
-
- if (!state->duplicated) {
- int max_bpc = conn_state->max_requested_bpc;
-
- is_y420 = drm_mode_is_420_also(&connector->display_info, adjusted_mode) &&
- aconnector->force_yuv420_output;
- color_depth = convert_color_depth_from_display_info(connector,
- is_y420,
- max_bpc);
- bpp = convert_dc_color_depth_into_bpc(color_depth) * 3;
- clock = adjusted_mode->clock;
- dm_new_connector_state->pbn = drm_dp_calc_pbn_mode(clock, bpp << 4);
- }
-
- dm_new_connector_state->vcpi_slots =
- drm_dp_atomic_find_time_slots(state, mst_mgr, mst_port,
- dm_new_connector_state->pbn);
- if (dm_new_connector_state->vcpi_slots < 0) {
- drm_dbg_atomic(connector->dev, "failed finding vcpi slots: %d\n", (int)dm_new_connector_state->vcpi_slots);
- return dm_new_connector_state->vcpi_slots;
- }
- return 0;
- }
-
- const struct drm_encoder_helper_funcs amdgpu_dm_encoder_helper_funcs = {
- .disable = dm_encoder_helper_disable,
- .atomic_check = dm_encoder_helper_atomic_check
- };
-
static int dm_update_mst_vcpi_slots_for_dsc(struct drm_atomic_commit *state,
struct dc_state *dc_state,
struct dsc_mst_fairness_vars *vars)
@@@ -8909,753 -3326,6 +3326,6 @@@
return 0;
}
- static int to_drm_connector_type(enum signal_type st, uint32_t connector_id)
- {
- switch (st) {
- case SIGNAL_TYPE_HDMI_TYPE_A:
- return DRM_MODE_CONNECTOR_HDMIA;
- case SIGNAL_TYPE_EDP:
- return DRM_MODE_CONNECTOR_eDP;
- case SIGNAL_TYPE_LVDS:
- return DRM_MODE_CONNECTOR_LVDS;
- case SIGNAL_TYPE_RGB:
- return DRM_MODE_CONNECTOR_VGA;
- case SIGNAL_TYPE_DISPLAY_PORT:
- case SIGNAL_TYPE_DISPLAY_PORT_MST:
- /* External DP bridges have a different connector type. */
- if (connector_id == CONNECTOR_ID_VGA)
- return DRM_MODE_CONNECTOR_VGA;
- else if (connector_id == CONNECTOR_ID_LVDS)
- return DRM_MODE_CONNECTOR_LVDS;
-
- return DRM_MODE_CONNECTOR_DisplayPort;
- case SIGNAL_TYPE_DVI_DUAL_LINK:
- case SIGNAL_TYPE_DVI_SINGLE_LINK:
- if (connector_id == CONNECTOR_ID_SINGLE_LINK_DVII ||
- connector_id == CONNECTOR_ID_DUAL_LINK_DVII)
- return DRM_MODE_CONNECTOR_DVII;
-
- return DRM_MODE_CONNECTOR_DVID;
- case SIGNAL_TYPE_VIRTUAL:
- return DRM_MODE_CONNECTOR_VIRTUAL;
-
- default:
- return DRM_MODE_CONNECTOR_Unknown;
- }
- }
-
- static struct drm_encoder *amdgpu_dm_connector_to_encoder(struct drm_connector *connector)
- {
- struct drm_encoder *encoder;
-
- /* There is only one encoder per connector */
- drm_connector_for_each_possible_encoder(connector, encoder)
- return encoder;
-
- return NULL;
- }
-
- static void amdgpu_dm_get_native_mode(struct drm_connector *connector)
- {
- struct drm_encoder *encoder;
- struct amdgpu_encoder *amdgpu_encoder;
-
- encoder = amdgpu_dm_connector_to_encoder(connector);
-
- if (encoder == NULL)
- return;
-
- amdgpu_encoder = to_amdgpu_encoder(encoder);
-
- amdgpu_encoder->native_mode.clock = 0;
-
- if (!list_empty(&connector->probed_modes)) {
- struct drm_display_mode *preferred_mode = NULL;
-
- list_for_each_entry(preferred_mode,
- &connector->probed_modes,
- head) {
- if (preferred_mode->type & DRM_MODE_TYPE_PREFERRED)
- amdgpu_encoder->native_mode = *preferred_mode;
-
- break;
- }
-
- }
- }
-
- static struct drm_display_mode *
- amdgpu_dm_create_common_mode(struct drm_encoder *encoder,
- const char *name,
- int hdisplay, int vdisplay)
- {
- struct drm_device *dev = encoder->dev;
- struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
- struct drm_display_mode *mode = NULL;
- struct drm_display_mode *native_mode = &amdgpu_encoder->native_mode;
-
- mode = drm_mode_duplicate(dev, native_mode);
-
- if (mode == NULL)
- return NULL;
-
- mode->hdisplay = hdisplay;
- mode->vdisplay = vdisplay;
- mode->type &= ~DRM_MODE_TYPE_PREFERRED;
- strscpy(mode->name, name, DRM_DISPLAY_MODE_LEN);
-
- return mode;
-
- }
-
- static const struct amdgpu_dm_mode_size {
- char name[DRM_DISPLAY_MODE_LEN];
- int w;
- int h;
- } common_modes[] = {
- { "640x480", 640, 480},
- { "800x600", 800, 600},
- { "1024x768", 1024, 768},
- { "1280x720", 1280, 720},
- { "1280x800", 1280, 800},
- {"1280x1024", 1280, 1024},
- { "1440x900", 1440, 900},
- {"1680x1050", 1680, 1050},
- {"1600x1200", 1600, 1200},
- {"1920x1080", 1920, 1080},
- {"1920x1200", 1920, 1200}
- };
-
- static void amdgpu_dm_connector_add_common_modes(struct drm_encoder *encoder,
- struct drm_connector *connector)
- {
- struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
- struct drm_display_mode *mode = NULL;
- struct drm_display_mode *native_mode = &amdgpu_encoder->native_mode;
- struct amdgpu_dm_connector *amdgpu_dm_connector =
- to_amdgpu_dm_connector(connector);
- int i;
- int n;
-
- if ((connector->connector_type != DRM_MODE_CONNECTOR_eDP) &&
- (connector->connector_type != DRM_MODE_CONNECTOR_LVDS))
- return;
-
- n = ARRAY_SIZE(common_modes);
-
- for (i = 0; i < n; i++) {
- struct drm_display_mode *curmode = NULL;
- bool mode_existed = false;
-
- if (common_modes[i].w > native_mode->hdisplay ||
- common_modes[i].h > native_mode->vdisplay ||
- (common_modes[i].w == native_mode->hdisplay &&
- common_modes[i].h == native_mode->vdisplay))
- continue;
-
- list_for_each_entry(curmode, &connector->probed_modes, head) {
- if (common_modes[i].w == curmode->hdisplay &&
- common_modes[i].h == curmode->vdisplay) {
- mode_existed = true;
- break;
- }
- }
-
- if (mode_existed)
- continue;
-
- mode = amdgpu_dm_create_common_mode(encoder,
- common_modes[i].name, common_modes[i].w,
- common_modes[i].h);
- if (!mode)
- continue;
-
- drm_mode_probed_add(connector, mode);
- amdgpu_dm_connector->num_modes++;
- }
- }
-
- static void amdgpu_set_panel_orientation(struct drm_connector *connector)
- {
- struct drm_encoder *encoder;
- struct amdgpu_encoder *amdgpu_encoder;
- const struct drm_display_mode *native_mode;
-
- if (connector->connector_type != DRM_MODE_CONNECTOR_eDP &&
- connector->connector_type != DRM_MODE_CONNECTOR_LVDS)
- return;
-
- mutex_lock(&connector->dev->mode_config.mutex);
- amdgpu_dm_connector_get_modes(connector);
- mutex_unlock(&connector->dev->mode_config.mutex);
-
- encoder = amdgpu_dm_connector_to_encoder(connector);
- if (!encoder)
- return;
-
- amdgpu_encoder = to_amdgpu_encoder(encoder);
-
- native_mode = &amdgpu_encoder->native_mode;
- if (native_mode->hdisplay == 0 || native_mode->vdisplay == 0)
- return;
-
- drm_connector_set_panel_orientation_with_quirk(connector,
- DRM_MODE_PANEL_ORIENTATION_UNKNOWN,
- native_mode->hdisplay,
- native_mode->vdisplay);
- }
-
- static void amdgpu_dm_connector_ddc_get_modes(struct drm_connector *connector,
- const struct drm_edid *drm_edid)
- {
- struct amdgpu_dm_connector *amdgpu_dm_connector =
- to_amdgpu_dm_connector(connector);
-
- if (drm_edid) {
- /* empty probed_modes */
- INIT_LIST_HEAD(&connector->probed_modes);
- amdgpu_dm_connector->num_modes =
- drm_edid_connector_add_modes(connector);
-
- /* sorting the probed modes before calling function
- * amdgpu_dm_get_native_mode() since EDID can have
- * more than one preferred mode. The modes that are
- * later in the probed mode list could be of higher
- * and preferred resolution. For example, 3840x2160
- * resolution in base EDID preferred timing and 4096x2160
- * preferred resolution in DID extension block later.
- */
- drm_mode_sort(&connector->probed_modes);
- amdgpu_dm_get_native_mode(connector);
-
- /* Freesync capabilities are reset by calling
- * drm_edid_connector_add_modes() and need to be
- * restored here.
- */
- amdgpu_dm_update_freesync_caps(connector, drm_edid, false);
- } else {
- amdgpu_dm_connector->num_modes = 0;
- }
- }
-
- static bool is_duplicate_mode(struct amdgpu_dm_connector *aconnector,
- struct drm_display_mode *mode)
- {
- struct drm_display_mode *m;
-
- list_for_each_entry(m, &aconnector->base.probed_modes, head) {
- if (drm_mode_equal(m, mode))
- return true;
- }
-
- return false;
- }
-
- static uint add_fs_modes(struct amdgpu_dm_connector *aconnector)
- {
- const struct drm_display_mode *m;
- struct drm_display_mode *new_mode;
- uint i;
- u32 new_modes_count = 0;
-
- /* Standard FPS values
- *
- * 23.976 - TV/NTSC
- * 24 - Cinema
- * 25 - TV/PAL
- * 29.97 - TV/NTSC
- * 30 - TV/NTSC
- * 48 - Cinema HFR
- * 50 - TV/PAL
- * 60 - Commonly used
- * 48,72,96,120 - Multiples of 24
- */
- static const u32 common_rates[] = {
- 23976, 24000, 25000, 29970, 30000,
- 48000, 50000, 60000, 72000, 96000, 120000
- };
-
- /*
- * Find mode with highest refresh rate with the same resolution
- * as the preferred mode. Some monitors report a preferred mode
- * with lower resolution than the highest refresh rate supported.
- */
-
- m = get_highest_refresh_rate_mode(aconnector, true);
- if (!m)
- return 0;
-
- for (i = 0; i < ARRAY_SIZE(common_rates); i++) {
- u64 target_vtotal, target_vtotal_diff;
- u64 num, den;
-
- if (drm_mode_vrefresh(m) * 1000 < common_rates[i])
- continue;
-
- if (common_rates[i] < aconnector->min_vfreq * 1000 ||
- common_rates[i] > aconnector->max_vfreq * 1000)
- continue;
-
- num = (unsigned long long)m->clock * 1000 * 1000;
- den = common_rates[i] * (unsigned long long)m->htotal;
- target_vtotal = div_u64(num, den);
- target_vtotal_diff = target_vtotal - m->vtotal;
-
- /* Check for illegal modes */
- if (m->vsync_start + target_vtotal_diff < m->vdisplay ||
- m->vsync_end + target_vtotal_diff < m->vsync_start ||
- m->vtotal + target_vtotal_diff < m->vsync_end)
- continue;
-
- new_mode = drm_mode_duplicate(aconnector->base.dev, m);
- if (!new_mode)
- goto out;
-
- new_mode->vtotal += (u16)target_vtotal_diff;
- new_mode->vsync_start += (u16)target_vtotal_diff;
- new_mode->vsync_end += (u16)target_vtotal_diff;
- new_mode->type &= ~DRM_MODE_TYPE_PREFERRED;
- new_mode->type |= DRM_MODE_TYPE_DRIVER;
-
- if (!is_duplicate_mode(aconnector, new_mode)) {
- drm_mode_probed_add(&aconnector->base, new_mode);
- new_modes_count += 1;
- } else
- drm_mode_destroy(aconnector->base.dev, new_mode);
- }
- out:
- return new_modes_count;
- }
-
- static void amdgpu_dm_connector_add_freesync_modes(struct drm_connector *connector,
- const struct drm_edid *drm_edid)
- {
- struct amdgpu_dm_connector *amdgpu_dm_connector =
- to_amdgpu_dm_connector(connector);
-
- if (!(amdgpu_freesync_vid_mode && drm_edid))
- return;
-
- if (!amdgpu_dm_connector->dc_sink || !amdgpu_dm_connector->dc_link)
- return;
-
- if (!dc_supports_vrr(amdgpu_dm_connector->dc_sink->ctx->dce_version))
- return;
-
- if (dc_connector_supports_analog(amdgpu_dm_connector->dc_link->link_id.id) &&
- amdgpu_dm_connector->dc_sink->edid_caps.analog)
- return;
-
- if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10)
- amdgpu_dm_connector->num_modes +=
- add_fs_modes(amdgpu_dm_connector);
- }
-
- static int amdgpu_dm_connector_get_modes(struct drm_connector *connector)
- {
- struct amdgpu_dm_connector *amdgpu_dm_connector =
- to_amdgpu_dm_connector(connector);
- struct dc_link *dc_link = amdgpu_dm_connector->dc_link;
- struct drm_encoder *encoder;
- const struct drm_edid *drm_edid = amdgpu_dm_connector->drm_edid;
- struct dc_link_settings *verified_link_cap = &dc_link->verified_link_cap;
- const struct dc *dc = dc_link->dc;
-
- encoder = amdgpu_dm_connector_to_encoder(connector);
-
- if (!drm_edid) {
- amdgpu_dm_connector->num_modes =
- drm_add_modes_noedid(connector, 640, 480);
- if (dc->link_srv->dp_get_encoding_format(verified_link_cap) == DP_128b_132b_ENCODING)
- amdgpu_dm_connector->num_modes +=
- drm_add_modes_noedid(connector, 1920, 1080);
-
- if (amdgpu_dm_connector->dc_sink &&
- amdgpu_dm_connector->dc_sink->edid_caps.analog &&
- dc_connector_supports_analog(dc_link->link_id.id)) {
- /* Analog monitor connected by DAC load detection.
- * Add common modes. It will be up to the user to select one that works.
- */
- for (int i = 0; i < ARRAY_SIZE(common_modes); i++)
- amdgpu_dm_connector->num_modes += drm_add_modes_noedid(
- connector, common_modes[i].w, common_modes[i].h);
- }
- } else {
- amdgpu_dm_connector_ddc_get_modes(connector, drm_edid);
- if (encoder)
- amdgpu_dm_connector_add_common_modes(encoder, connector);
- amdgpu_dm_connector_add_freesync_modes(connector, drm_edid);
- }
- amdgpu_dm_fbc_init(connector);
-
- return amdgpu_dm_connector->num_modes;
- }
-
- static const u32 supported_colorspaces =
- BIT(DRM_MODE_COLORIMETRY_BT709_YCC) |
- BIT(DRM_MODE_COLORIMETRY_OPRGB) |
- BIT(DRM_MODE_COLORIMETRY_BT2020_RGB) |
- BIT(DRM_MODE_COLORIMETRY_BT2020_YCC);
-
- void amdgpu_dm_connector_init_helper(struct amdgpu_display_manager *dm,
- struct amdgpu_dm_connector *aconnector,
- int connector_type,
- struct dc_link *link,
- int link_index)
- {
- struct amdgpu_device *adev = drm_to_adev(dm->ddev);
-
- /*
- * Some of the properties below require access to state, like bpc.
- * Allocate some default initial connector state with our reset helper.
- */
- if (aconnector->base.funcs->reset)
- aconnector->base.funcs->reset(&aconnector->base);
-
- aconnector->connector_id = link_index;
- aconnector->bl_idx = -1;
- aconnector->dc_link = link;
- aconnector->base.interlace_allowed = false;
- aconnector->base.doublescan_allowed = false;
- aconnector->base.stereo_allowed = false;
- aconnector->base.dpms = DRM_MODE_DPMS_OFF;
- aconnector->hpd.hpd = AMDGPU_HPD_NONE; /* not used */
- aconnector->audio_inst = -1;
- aconnector->pack_sdp_v1_3 = false;
- aconnector->as_type = ADAPTIVE_SYNC_TYPE_NONE;
- memset(&aconnector->vsdb_info, 0, sizeof(aconnector->vsdb_info));
- mutex_init(&aconnector->hpd_lock);
- mutex_init(&aconnector->handle_mst_msg_ready);
-
- /*
- * If HDMI HPD debounce delay is set, use the minimum between selected
- * value and AMDGPU_DM_MAX_HDMI_HPD_DEBOUNCE_MS
- */
- if (amdgpu_hdmi_hpd_debounce_delay_ms) {
- aconnector->hdmi_hpd_debounce_delay_ms = min(amdgpu_hdmi_hpd_debounce_delay_ms,
- AMDGPU_DM_MAX_HDMI_HPD_DEBOUNCE_MS);
- INIT_DELAYED_WORK(&aconnector->hdmi_hpd_debounce_work, hdmi_hpd_debounce_work);
- aconnector->hdmi_prev_sink = NULL;
- } else {
- aconnector->hdmi_hpd_debounce_delay_ms = 0;
- }
-
- /*
- * configure support HPD hot plug connector_>polled default value is 0
- * which means HPD hot plug not supported
- */
- switch (connector_type) {
- case DRM_MODE_CONNECTOR_HDMIA:
- aconnector->base.polled = DRM_CONNECTOR_POLL_HPD;
- aconnector->base.ycbcr_420_allowed =
- link->link_enc->features.hdmi_ycbcr420_supported ? true : false;
- break;
- case DRM_MODE_CONNECTOR_DisplayPort:
- aconnector->base.polled = DRM_CONNECTOR_POLL_HPD;
- link->link_enc = link_enc_cfg_get_link_enc(link);
- ASSERT(link->link_enc);
- if (link->link_enc)
- aconnector->base.ycbcr_420_allowed =
- link->link_enc->features.dp_ycbcr420_supported ? true : false;
- break;
- case DRM_MODE_CONNECTOR_DVID:
- aconnector->base.polled = DRM_CONNECTOR_POLL_HPD;
- break;
- case DRM_MODE_CONNECTOR_DVII:
- case DRM_MODE_CONNECTOR_VGA:
- aconnector->base.polled =
- DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT;
- break;
- default:
- break;
- }
-
- drm_object_attach_property(&aconnector->base.base,
- dm->ddev->mode_config.scaling_mode_property,
- DRM_MODE_SCALE_NONE);
-
- if (connector_type == DRM_MODE_CONNECTOR_HDMIA
- || (connector_type == DRM_MODE_CONNECTOR_DisplayPort && !aconnector->mst_root))
- drm_connector_attach_broadcast_rgb_property(&aconnector->base);
-
- drm_object_attach_property(&aconnector->base.base,
- adev->mode_info.underscan_property,
- UNDERSCAN_OFF);
- drm_object_attach_property(&aconnector->base.base,
- adev->mode_info.underscan_hborder_property,
- 0);
- drm_object_attach_property(&aconnector->base.base,
- adev->mode_info.underscan_vborder_property,
- 0);
-
- if (!aconnector->mst_root)
- drm_connector_attach_max_bpc_property(&aconnector->base, 8, 16);
-
- aconnector->base.state->max_bpc = 16;
- aconnector->base.state->max_requested_bpc = aconnector->base.state->max_bpc;
-
- if (connector_type == DRM_MODE_CONNECTOR_HDMIA) {
- /* Content Type is currently only implemented for HDMI. */
- drm_connector_attach_content_type_property(&aconnector->base);
- }
-
- if (connector_type == DRM_MODE_CONNECTOR_HDMIA) {
- if (!drm_mode_create_hdmi_colorspace_property(&aconnector->base, supported_colorspaces))
- drm_connector_attach_colorspace_property(&aconnector->base);
- } else if ((connector_type == DRM_MODE_CONNECTOR_DisplayPort && !aconnector->mst_root) ||
- connector_type == DRM_MODE_CONNECTOR_eDP) {
- if (!drm_mode_create_dp_colorspace_property(&aconnector->base, supported_colorspaces))
- drm_connector_attach_colorspace_property(&aconnector->base);
- }
-
- if (connector_type == DRM_MODE_CONNECTOR_HDMIA ||
- connector_type == DRM_MODE_CONNECTOR_DisplayPort ||
- connector_type == DRM_MODE_CONNECTOR_eDP) {
- drm_connector_attach_hdr_output_metadata_property(&aconnector->base);
-
- if (!aconnector->mst_root)
- drm_connector_attach_vrr_capable_property(&aconnector->base);
-
- if (adev->dm.hdcp_workqueue)
- drm_connector_attach_content_protection_property(&aconnector->base, true);
- }
-
- if (connector_type == DRM_MODE_CONNECTOR_eDP) {
- struct drm_privacy_screen *privacy_screen;
-
- drm_connector_attach_panel_type_property(&aconnector->base);
-
- privacy_screen = drm_privacy_screen_get(adev_to_drm(adev)->dev, NULL);
- if (!IS_ERR(privacy_screen)) {
- drm_connector_attach_privacy_screen_provider(&aconnector->base,
- privacy_screen);
- } else if (PTR_ERR(privacy_screen) != -ENODEV) {
- drm_warn(adev_to_drm(adev), "Error getting privacy-screen\n");
- }
- }
- }
-
- static int amdgpu_dm_i2c_xfer(struct i2c_adapter *i2c_adap,
- struct i2c_msg *msgs, int num)
- {
- struct amdgpu_i2c_adapter *i2c = i2c_get_adapdata(i2c_adap);
- struct ddc_service *ddc_service = i2c->ddc_service;
- struct i2c_command cmd;
- int i;
- int result = -EIO;
-
- if (!ddc_service->ddc_pin)
- return result;
-
- cmd.payloads = kzalloc_objs(struct i2c_payload, num);
-
- if (!cmd.payloads)
- return result;
-
- cmd.number_of_payloads = num;
- cmd.engine = I2C_COMMAND_ENGINE_DEFAULT;
- cmd.speed = 100;
-
- for (i = 0; i < num; i++) {
- cmd.payloads[i].write = !(msgs[i].flags & I2C_M_RD);
- cmd.payloads[i].address = msgs[i].addr;
- cmd.payloads[i].length = msgs[i].len;
- cmd.payloads[i].data = msgs[i].buf;
- }
-
- if (i2c->oem) {
- if (dc_submit_i2c_oem(
- ddc_service->ctx->dc,
- &cmd))
- result = num;
- } else {
- if (dc_submit_i2c(
- ddc_service->ctx->dc,
- ddc_service->link->link_index,
- &cmd))
- result = num;
- }
-
- kfree(cmd.payloads);
- return result;
- }
-
- static u32 amdgpu_dm_i2c_func(struct i2c_adapter *adap)
- {
- return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
- }
-
- static const struct i2c_algorithm amdgpu_dm_i2c_algo = {
- .master_xfer = amdgpu_dm_i2c_xfer,
- .functionality = amdgpu_dm_i2c_func,
- };
-
- static struct amdgpu_i2c_adapter *
- create_i2c(struct ddc_service *ddc_service, bool oem)
- {
- struct amdgpu_device *adev = ddc_service->ctx->driver_context;
- struct amdgpu_i2c_adapter *i2c;
-
- i2c = kzalloc_obj(struct amdgpu_i2c_adapter);
- if (!i2c)
- return NULL;
- i2c->base.owner = THIS_MODULE;
- i2c->base.dev.parent = &adev->pdev->dev;
- i2c->base.algo = &amdgpu_dm_i2c_algo;
- if (oem)
- snprintf(i2c->base.name, sizeof(i2c->base.name), "AMDGPU DM i2c OEM bus");
- else
- snprintf(i2c->base.name, sizeof(i2c->base.name), "AMDGPU DM i2c hw bus %d",
- ddc_service->link->link_index);
- i2c_set_adapdata(&i2c->base, i2c);
- i2c->ddc_service = ddc_service;
- i2c->oem = oem;
-
- return i2c;
- }
-
- int amdgpu_dm_initialize_hdmi_connector(struct amdgpu_dm_connector *aconnector)
- {
- struct cec_connector_info conn_info;
- struct drm_device *ddev = aconnector->base.dev;
- struct device *hdmi_dev = ddev->dev;
-
- if (amdgpu_dc_debug_mask & DC_DISABLE_HDMI_CEC) {
- drm_info(ddev, "HDMI-CEC feature masked\n");
- return -EINVAL;
- }
-
- cec_fill_conn_info_from_drm(&conn_info, &aconnector->base);
- aconnector->notifier =
- cec_notifier_conn_register(hdmi_dev, NULL, &conn_info);
- if (!aconnector->notifier) {
- drm_err(ddev, "Failed to create cec notifier\n");
- return -ENOMEM;
- }
-
- return 0;
- }
-
- /*
- * Note: this function assumes that dc_link_detect() was called for the
- * dc_link which will be represented by this aconnector.
- */
- static int amdgpu_dm_connector_init(struct amdgpu_display_manager *dm,
- struct amdgpu_dm_connector *aconnector,
- u32 link_index,
- struct amdgpu_encoder *aencoder)
- {
- int res = 0;
- int connector_type;
- struct dc *dc = dm->dc;
- struct dc_link *link = dc_get_link_at_index(dc, link_index);
- struct amdgpu_i2c_adapter *i2c;
-
- /* Not needed for writeback connector */
- link->priv = aconnector;
-
-
- i2c = create_i2c(link->ddc, false);
- if (!i2c) {
- drm_err(adev_to_drm(dm->adev), "Failed to create i2c adapter data\n");
- return -ENOMEM;
- }
-
- aconnector->i2c = i2c;
- res = devm_i2c_add_adapter(dm->adev->dev, &i2c->base);
-
- if (res) {
- drm_err(adev_to_drm(dm->adev), "Failed to register hw i2c %d\n", link->link_index);
- goto out_free;
- }
-
- connector_type = to_drm_connector_type(link->connector_signal, link->link_id.id);
-
- res = drm_connector_init_with_ddc(
- dm->ddev,
- &aconnector->base,
- &amdgpu_dm_connector_funcs,
- connector_type,
- &i2c->base);
-
- if (res) {
- drm_err(adev_to_drm(dm->adev), "connector_init failed\n");
- aconnector->connector_id = -1;
- goto out_free;
- }
-
- drm_connector_helper_add(
- &aconnector->base,
- &amdgpu_dm_connector_helper_funcs);
-
- amdgpu_dm_connector_init_helper(
- dm,
- aconnector,
- connector_type,
- link,
- link_index);
-
- drm_connector_attach_encoder(
- &aconnector->base, &aencoder->base);
-
- if (connector_type == DRM_MODE_CONNECTOR_HDMIA ||
- connector_type == DRM_MODE_CONNECTOR_HDMIB)
- amdgpu_dm_initialize_hdmi_connector(aconnector);
-
- if (dc_is_dp_signal(link->connector_signal))
- amdgpu_dm_initialize_dp_connector(dm, aconnector, link->link_index);
-
- out_free:
- if (res) {
- kfree(i2c);
- aconnector->i2c = NULL;
- }
- return res;
- }
-
- int amdgpu_dm_get_encoder_crtc_mask(struct amdgpu_device *adev)
- {
- switch (adev->mode_info.num_crtc) {
- case 1:
- return 0x1;
- case 2:
- return 0x3;
- case 3:
- return 0x7;
- case 4:
- return 0xf;
- case 5:
- return 0x1f;
- case 6:
- default:
- return 0x3f;
- }
- }
-
- static int amdgpu_dm_encoder_init(struct drm_device *dev,
- struct amdgpu_encoder *aencoder,
- uint32_t link_index)
- {
- struct amdgpu_device *adev = drm_to_adev(dev);
-
- int res = drm_encoder_init(dev,
- &aencoder->base,
- &amdgpu_dm_encoder_funcs,
- DRM_MODE_ENCODER_TMDS,
- NULL);
-
- aencoder->base.possible_crtcs = amdgpu_dm_get_encoder_crtc_mask(adev);
-
- if (!res)
- aencoder->encoder_id = link_index;
- else
- aencoder->encoder_id = -1;
-
- drm_encoder_helper_add(&aencoder->base, &amdgpu_dm_encoder_helper_funcs);
-
- return res;
- }
-
static void manage_dm_interrupts(struct amdgpu_device *adev,
struct amdgpu_crtc *acrtc,
struct dm_crtc_state *acrtc_state)
@@@ -9755,7 -3425,7 +3425,7 @@@ static void dm_update_pflip_irq_state(s
amdgpu_irq_update(adev, &adev->pageflip_irq, irq_type);
}
- static bool
+ STATIC_IFN_KUNIT bool
is_scaling_state_different(const struct dm_connector_state *dm_state,
const struct dm_connector_state *old_dm_state)
{
@@@ -9772,13 -3442,15 +3442,15 @@@
return true;
return false;
}
+ EXPORT_IF_KUNIT(is_scaling_state_different);
- static bool is_content_protection_different(struct drm_crtc_state *new_crtc_state,
- struct drm_crtc_state *old_crtc_state,
- struct drm_connector_state *new_conn_state,
- struct drm_connector_state *old_conn_state,
- const struct drm_connector *connector,
- struct hdcp_workqueue *hdcp_w)
+ STATIC_IFN_KUNIT bool
+ is_content_protection_different(struct drm_crtc_state *new_crtc_state,
+ struct drm_crtc_state *old_crtc_state,
+ struct drm_connector_state *new_conn_state,
+ struct drm_connector_state *old_conn_state,
+ const struct drm_connector *connector,
+ struct hdcp_workqueue *hdcp_w)
{
struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
struct dm_connector_state *dm_con_state = to_dm_connector_state(connector->state);
@@@ -9892,6 -3564,7 +3564,7 @@@
pr_debug("[HDCP_DM] DESIRED->ENABLED %s :false\n", __func__);
return false;
}
+ EXPORT_IF_KUNIT(is_content_protection_different);
static void remove_stream(struct amdgpu_device *adev,
struct amdgpu_crtc *acrtc,
@@@ -9903,211 -3576,23 +3576,23 @@@
acrtc->enabled = false;
}
- static void update_freesync_state_on_stream(
- struct amdgpu_display_manager *dm,
- struct dm_crtc_state *new_crtc_state,
- struct dc_stream_state *new_stream,
- struct dc_plane_state *surface,
- u32 flip_timestamp_in_us)
+ static void prepare_flip_isr(struct amdgpu_crtc *acrtc)
{
- struct mod_vrr_params vrr_params;
- struct dc_info_packet vrr_infopacket = {0};
- struct amdgpu_device *adev = dm->adev;
- struct amdgpu_crtc *acrtc = to_amdgpu_crtc(new_crtc_state->base.crtc);
- unsigned long flags;
- bool pack_sdp_v1_3 = false;
- struct amdgpu_dm_connector *aconn;
- enum vrr_packet_type packet_type = PACKET_TYPE_VRR;
- if (!new_stream)
- return;
+ assert_spin_locked(&acrtc->base.dev->event_lock);
+ WARN_ON(acrtc->event);
- /*
- * TODO: Determine why min/max totals and vrefresh can be 0 here.
- * For now it's sufficient to just guard against these conditions.
- */
+ acrtc->event = acrtc->base.state->event;
- if (!new_stream->timing.h_total || !new_stream->timing.v_total)
- return;
+ /* Set the flip status */
+ acrtc->pflip_status = AMDGPU_FLIP_SUBMITTED;
- spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags);
- vrr_params = acrtc->dm_irq_params.vrr_params;
+ /* Mark this event as consumed */
+ acrtc->base.state->event = NULL;
- if (surface) {
- mod_freesync_handle_preflip(
- dm->freesync_module,
- surface,
- new_stream,
- flip_timestamp_in_us,
- &vrr_params);
-
- if (adev->family < AMDGPU_FAMILY_AI &&
- amdgpu_dm_crtc_vrr_active(new_crtc_state)) {
- mod_freesync_handle_v_update(dm->freesync_module,
- new_stream, &vrr_params);
-
- /* Need to call this before the frame ends. */
- dc_stream_adjust_vmin_vmax(dm->dc,
- new_crtc_state->stream,
- &vrr_params.adjust);
- }
- }
-
- aconn = (struct amdgpu_dm_connector *)new_stream->dm_stream_context;
-
- if (aconn && (aconn->as_type == FREESYNC_TYPE_PCON_IN_WHITELIST || aconn->vsdb_info.replay_mode)) {
- pack_sdp_v1_3 = aconn->pack_sdp_v1_3;
-
- if (aconn->vsdb_info.amd_vsdb_version == 1)
- packet_type = PACKET_TYPE_FS_V1;
- else if (aconn->vsdb_info.amd_vsdb_version == 2)
- packet_type = PACKET_TYPE_FS_V2;
- else if (aconn->vsdb_info.amd_vsdb_version == 3)
- packet_type = PACKET_TYPE_FS_V3;
-
- mod_build_adaptive_sync_infopacket(new_stream, aconn->as_type, NULL,
- &new_stream->adaptive_sync_infopacket);
- }
-
- mod_freesync_build_vrr_infopacket(
- dm->freesync_module,
- new_stream,
- &vrr_params,
- packet_type,
- TRANSFER_FUNC_UNKNOWN,
- &vrr_infopacket,
- pack_sdp_v1_3);
-
- new_crtc_state->freesync_vrr_info_changed |=
- (memcmp(&new_crtc_state->vrr_infopacket,
- &vrr_infopacket,
- sizeof(vrr_infopacket)) != 0);
-
- acrtc->dm_irq_params.vrr_params = vrr_params;
- new_crtc_state->vrr_infopacket = vrr_infopacket;
-
- new_stream->vrr_infopacket = vrr_infopacket;
- new_stream->allow_freesync = mod_freesync_get_freesync_enabled(&vrr_params);
-
- if (new_crtc_state->freesync_vrr_info_changed)
- drm_dbg_kms(adev_to_drm(adev), "VRR packet update: crtc=%u enabled=%d state=%d",
- new_crtc_state->base.crtc->base.id,
- (int)new_crtc_state->base.vrr_enabled,
- (int)vrr_params.state);
-
- spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
- }
-
- static void update_stream_irq_parameters(
- struct amdgpu_display_manager *dm,
- struct dm_crtc_state *new_crtc_state)
- {
- struct dc_stream_state *new_stream = new_crtc_state->stream;
- struct mod_vrr_params vrr_params;
- struct mod_freesync_config config = new_crtc_state->freesync_config;
- struct amdgpu_device *adev = dm->adev;
- struct amdgpu_crtc *acrtc = to_amdgpu_crtc(new_crtc_state->base.crtc);
- unsigned long flags;
-
- if (!new_stream)
- return;
-
- /*
- * TODO: Determine why min/max totals and vrefresh can be 0 here.
- * For now it's sufficient to just guard against these conditions.
- */
- if (!new_stream->timing.h_total || !new_stream->timing.v_total)
- return;
-
- spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags);
- vrr_params = acrtc->dm_irq_params.vrr_params;
-
- if (new_crtc_state->vrr_supported &&
- config.min_refresh_in_uhz &&
- config.max_refresh_in_uhz) {
- /*
- * if freesync compatible mode was set, config.state will be set
- * in atomic check
- */
- if (config.state == VRR_STATE_ACTIVE_FIXED && config.fixed_refresh_in_uhz &&
- (!drm_atomic_crtc_needs_modeset(&new_crtc_state->base) ||
- new_crtc_state->freesync_config.state == VRR_STATE_ACTIVE_FIXED)) {
- vrr_params.max_refresh_in_uhz = config.max_refresh_in_uhz;
- vrr_params.min_refresh_in_uhz = config.min_refresh_in_uhz;
- vrr_params.fixed_refresh_in_uhz = config.fixed_refresh_in_uhz;
- vrr_params.state = VRR_STATE_ACTIVE_FIXED;
- } else {
- config.state = new_crtc_state->base.vrr_enabled ?
- VRR_STATE_ACTIVE_VARIABLE :
- VRR_STATE_INACTIVE;
- }
- } else {
- config.state = VRR_STATE_UNSUPPORTED;
- }
-
- mod_freesync_build_vrr_params(dm->freesync_module,
- new_stream,
- &config, &vrr_params);
-
- new_crtc_state->freesync_config = config;
- /* Copy state for access from DM IRQ handler */
- acrtc->dm_irq_params.freesync_config = config;
- acrtc->dm_irq_params.active_planes = new_crtc_state->active_planes;
- acrtc->dm_irq_params.vrr_params = vrr_params;
- spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
- }
-
- static void amdgpu_dm_handle_vrr_transition(struct amdgpu_display_manager *dm,
- struct dm_crtc_state *old_state,
- struct dm_crtc_state *new_state)
- {
- struct amdgpu_device *adev = drm_to_adev(new_state->base.crtc->dev);
- bool old_vrr_active = amdgpu_dm_crtc_vrr_active(old_state);
- bool new_vrr_active = amdgpu_dm_crtc_vrr_active(new_state);
-
- /* Only DCE gates vupdate on VRR, keep it enabled for DCN */
- bool vrr_gates_vupdate = amdgpu_ip_version(adev, DCE_HWIP, 0) == 0;
-
- if (!old_vrr_active && new_vrr_active) {
- /* Transition VRR inactive -> active:
- * While VRR is active, we must not disable vblank irq, as a
- * reenable after disable would compute bogus vblank/pflip
- * timestamps if it likely happened inside display front-porch.
- *
- * We also need vupdate irq for the actual core vblank handling
- * at end of vblank.
- */
- if (vrr_gates_vupdate)
- WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, true) != 0);
- WARN_ON(drm_crtc_vblank_get(new_state->base.crtc) != 0);
- drm_dbg_driver(new_state->base.crtc->dev, "%s: crtc=%u VRR off->on: Get vblank ref\n",
- __func__, new_state->base.crtc->base.id);
-
- scoped_guard(mutex, &dm->dc_lock) {
- dc_exit_ips_for_hw_access(dm->dc);
- amdgpu_dm_psr_set_event(dm, new_state->stream, true,
- psr_event_vrr_transition, true);
- amdgpu_dm_replay_set_event(dm, new_state->stream, true,
- replay_event_vrr, true);
- }
- } else if (old_vrr_active && !new_vrr_active) {
- /* Transition VRR active -> inactive:
- * Allow vblank irq disable again for fixed refresh rate.
- */
- if (vrr_gates_vupdate)
- WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, false) != 0);
- drm_crtc_vblank_put(new_state->base.crtc);
- drm_dbg_driver(new_state->base.crtc->dev, "%s: crtc=%u VRR on->off: Drop vblank ref\n",
- __func__, new_state->base.crtc->base.id);
-
- scoped_guard(mutex, &dm->dc_lock) {
- dc_exit_ips_for_hw_access(dm->dc);
- amdgpu_dm_psr_set_event(dm, new_state->stream, false,
- psr_event_vrr_transition, false);
- amdgpu_dm_replay_set_event(dm, new_state->stream, false,
- replay_event_vrr, false);
- }
- }
+ drm_dbg_state(acrtc->base.dev,
+ "crtc:%d, pflip_stat:AMDGPU_FLIP_SUBMITTED\n",
+ acrtc->crtc_id);
}
static void amdgpu_dm_commit_cursors(struct drm_atomic_commit *state)
@@@ -10255,47 -3740,17 +3740,17 @@@ static void dm_arm_vblank_event(struct
return;
if (pflip_update) {
+ drm_crtc_vblank_get(&acrtc->base);
WARN_ON(acrtc->pflip_status != AMDGPU_FLIP_NONE);
- WARN_ON(acrtc->event);
-
- acrtc->pflip_status = AMDGPU_FLIP_SUBMITTED;
- acrtc->event = acrtc->base.state->event;
- acrtc->base.state->event = NULL;
-
- drm_dbg_state(acrtc->base.dev,
- "crtc:%d, pflip_stat:AMDGPU_FLIP_SUBMITTED\n",
- acrtc->crtc_id);
+ /* Arm flip completion handling and event delivery after programming. */
+ prepare_flip_isr(acrtc);
} else if (cursor_update) {
+ drm_crtc_vblank_get(&acrtc->base);
acrtc->event = acrtc->base.state->event;
acrtc->base.state->event = NULL;
}
}
- /**
- * dm_arm_vblank_event_pre_programming - Prepare for programming
- * @acrtc: The amdgpu CRTC to prepare
- * @acrtc_state: The new CRTC state
- * @pflip_update: Whether a page flip is being programmed
- * @cursor_update: Whether a cursor update is being programmed
- *
- * Grab a reference on the vblank counter if a page flip or cursor update is to
- * be programmed. Do this before programming so the HW is not in any
- * idle-optimized state (such as PSR).
- */
- static void dm_arm_vblank_event_pre_programming(struct amdgpu_crtc *acrtc,
- struct dm_crtc_state *acrtc_state,
- bool pflip_update,
- bool cursor_update)
- {
- assert_spin_locked(&acrtc->base.dev->event_lock);
-
- if (!acrtc->base.state->event || acrtc_state->active_planes == 0)
- return;
-
- if (pflip_update || cursor_update)
- drm_crtc_vblank_get(&acrtc->base);
- }
-
static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state,
struct drm_device *dev,
struct amdgpu_display_manager *dm,
@@@ -10396,14 -3851,12 +3851,12 @@@
continue;
bundle->surface_updates[planes_count].surface = dc_plane;
- if (new_pcrtc_state->color_mgmt_changed || new_plane_state->color_mgmt_changed) {
+ if (new_pcrtc_state->color_mgmt_changed) {
bundle->surface_updates[planes_count].gamma = &dc_plane->gamma_correction;
bundle->surface_updates[planes_count].in_transfer_func = &dc_plane->in_transfer_func;
bundle->surface_updates[planes_count].gamut_remap_matrix = &dc_plane->gamut_remap_matrix;
bundle->surface_updates[planes_count].hdr_mult = dc_plane->hdr_mult;
- bundle->surface_updates[planes_count].func_shaper = &dc_plane->in_shaper_func;
- bundle->surface_updates[planes_count].lut3d_func = &dc_plane->lut3d_func;
- bundle->surface_updates[planes_count].blend_tf = &dc_plane->blend_tf;
+ bundle->surface_updates[planes_count].cm = &dc_plane->cm;
}
amdgpu_dm_plane_fill_dc_scaling_info(dm->adev, new_plane_state,
@@@ -10423,7 -3876,6 +3876,6 @@@
fill_dc_plane_info_and_addr(
dm->adev, new_plane_state,
- afb->tiling_flags,
&bundle->plane_infos[planes_count],
&bundle->flip_addrs[planes_count].address,
afb->tmz_surface);
@@@ -10498,7 -3950,7 +3950,7 @@@
}
if (plane == pcrtc->primary)
- update_freesync_state_on_stream(
+ amdgpu_dm_update_freesync_state_on_stream(
dm,
acrtc_state,
acrtc_state->stream,
@@@ -10561,19 -4013,16 +4013,16 @@@
}
}
- scoped_guard(spinlock_irqsave, &pcrtc->dev->event_lock) {
- dm_arm_vblank_event_pre_programming(acrtc_attach, acrtc_state,
- pflip_present,
- cursor_update);
- /*
- * DCE depends on a combination of GRPH_FLIP, VLINE0, and
- * VUPDATE for event delivery. Only GRPH_FLIP handler can send
- * pflip events, and it only fires if HW latched to the flip.
- * Maintain legacy behavior by arming event before programming.
- */
- if (amdgpu_ip_version(dm->adev, DCE_HWIP, 0) == 0) {
+ /*
+ * DCE depends on a combination of GRPH_FLIP, VLINE0, and VUPDATE for
+ * event delivery. Only GRPH_FLIP handler can send pflip events, and it
+ * only fires if HW latched to the flip. Maintain legacy behavior by
+ * arming event before programming.
+ */
+ if (amdgpu_ip_version(dm->adev, DCE_HWIP, 0) == 0) {
+ scoped_guard(spinlock_irqsave, &pcrtc->dev->event_lock) {
dm_arm_vblank_event(acrtc_attach, acrtc_state,
- pflip_present, cursor_update);
+ pflip_present, cursor_update);
}
}
@@@ -10619,7 -4068,7 +4068,7 @@@
* re-adjust the min/max bounds now that DC doesn't handle this
* as part of commit.
*/
- if (is_dc_timing_adjust_needed(dm_old_crtc_state, acrtc_state)) {
+ if (amdgpu_dm_is_dc_timing_adjust_needed(dm_old_crtc_state, acrtc_state)) {
spin_lock_irqsave(&pcrtc->dev->event_lock, flags);
dc_stream_adjust_vmin_vmax(
dm->dc, acrtc_state->stream,
@@@ -10779,87 -4228,6 +4228,6 @@@ cleanup
kfree(bundle);
}
- static void amdgpu_dm_commit_audio(struct drm_device *dev,
- struct drm_atomic_commit *state)
- {
- struct amdgpu_device *adev = drm_to_adev(dev);
- struct amdgpu_dm_connector *aconnector;
- struct drm_connector *connector;
- struct drm_connector_state *old_con_state, *new_con_state;
- struct drm_crtc_state *new_crtc_state;
- struct dm_crtc_state *new_dm_crtc_state;
- const struct dc_stream_status *status;
- int i, inst;
-
- /* Notify device removals. */
- for_each_oldnew_connector_in_state(state, connector, old_con_state, new_con_state, i) {
- if (old_con_state->crtc != new_con_state->crtc) {
- /* CRTC changes require notification. */
- goto notify;
- }
-
- if (!new_con_state->crtc)
- continue;
-
- new_crtc_state = drm_atomic_get_new_crtc_state(
- state, new_con_state->crtc);
-
- if (!new_crtc_state)
- continue;
-
- if (!drm_atomic_crtc_needs_modeset(new_crtc_state))
- continue;
-
- notify:
- if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
- continue;
-
- aconnector = to_amdgpu_dm_connector(connector);
-
- mutex_lock(&adev->dm.audio_lock);
- inst = aconnector->audio_inst;
- aconnector->audio_inst = -1;
- mutex_unlock(&adev->dm.audio_lock);
-
- amdgpu_dm_audio_eld_notify(adev, inst);
- }
-
- /* Notify audio device additions. */
- for_each_new_connector_in_state(state, connector, new_con_state, i) {
- if (!new_con_state->crtc)
- continue;
-
- new_crtc_state = drm_atomic_get_new_crtc_state(
- state, new_con_state->crtc);
-
- if (!new_crtc_state)
- continue;
-
- if (!drm_atomic_crtc_needs_modeset(new_crtc_state))
- continue;
-
- new_dm_crtc_state = to_dm_crtc_state(new_crtc_state);
- if (!new_dm_crtc_state->stream)
- continue;
-
- status = dc_stream_get_status(new_dm_crtc_state->stream);
- if (!status)
- continue;
-
- if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
- continue;
-
- aconnector = to_amdgpu_dm_connector(connector);
-
- mutex_lock(&adev->dm.audio_lock);
- inst = status->audio_inst;
- aconnector->audio_inst = inst;
- mutex_unlock(&adev->dm.audio_lock);
-
- amdgpu_dm_audio_eld_notify(adev, inst);
- }
- }
-
/*
* amdgpu_dm_crtc_copy_transient_flags - copy mirrored flags from DRM to DC
* @crtc_state: the DRM CRTC state
@@@ -10874,10 -4242,57 +4242,57 @@@ static void amdgpu_dm_crtc_copy_transie
stream_state->mode_changed = drm_atomic_crtc_needs_modeset(crtc_state);
}
+ /**
+ * amdgpu_dm_crtc_complete_writeback - finish a pending writeback job
+ * @acrtc: the CRTC whose pending writeback should be completed
+ *
+ * Clears the pending state, signals the writeback out fence and releases the
+ * vblank reference taken in dm_set_writeback() while the writeback was armed.
+ * The pending flag is tested and cleared under the writeback job lock, so this
+ * is safe to call concurrently from the completion vblank IRQ
+ * (dm_crtc_high_irq()) and from the writeback teardown path
+ * (dm_clear_writeback()); only the caller that observes the pending job
+ * performs the completion.
+ *
+ * Return: true if a pending writeback job was completed by this call.
+ */
+ bool amdgpu_dm_crtc_complete_writeback(struct amdgpu_crtc *acrtc)
+ {
+ unsigned long flags;
+ bool pending;
+
+ if (!acrtc->wb_conn)
+ return false;
+
+ spin_lock_irqsave(&acrtc->wb_conn->job_lock, flags);
+ pending = acrtc->wb_pending;
+ acrtc->wb_pending = false;
+ acrtc->wb_frame_done = false;
+ spin_unlock_irqrestore(&acrtc->wb_conn->job_lock, flags);
+
+ if (!pending)
+ return false;
+
+ drm_writeback_signal_completion(acrtc->wb_conn, 0);
+ drm_crtc_vblank_put(&acrtc->base);
+
+ return true;
+ }
+ EXPORT_IF_KUNIT(amdgpu_dm_crtc_complete_writeback);
+
static void dm_clear_writeback(struct amdgpu_display_manager *dm,
+ struct amdgpu_crtc *acrtc,
struct dm_crtc_state *crtc_state)
{
dc_stream_remove_writeback(dm->dc, crtc_state->stream, 0);
+
+ /*
+ * If the writeback is still pending when it is torn down (its
+ * completion vblank IRQ never fired), signal the out fence so a
+ * waiting client does not stall and release the vblank reference
+ * taken in dm_set_writeback().
+ */
+ amdgpu_dm_crtc_complete_writeback(acrtc);
}
/**
@@@ -11030,7 -4445,7 +4445,7 @@@ static void amdgpu_dm_commit_streams(st
dm_old_crtc_state = to_dm_crtc_state(old_crtc_state);
- dm_clear_writeback(dm, dm_old_crtc_state);
+ dm_clear_writeback(dm, acrtc, dm_old_crtc_state);
acrtc->wb_enabled = false;
}
@@@ -11304,9 -4719,25 +4719,25 @@@ static void dm_set_writeback(struct amd
dc_stream_add_writeback(dm->dc, crtc_state->stream, wb_info);
- acrtc->wb_pending = true;
acrtc->wb_conn = wb_conn;
drm_writeback_queue_job(wb_conn, new_con_state);
+
+ /*
+ * Writeback completion is detected in the CRTC vblank IRQ
+ * (dm_crtc_high_irq()). Take a vblank reference so the vblank interrupt
+ * stays enabled while the writeback is pending; otherwise a
+ * writeback-only commit right after drm_crtc_vblank_on() (e.g.
+ * re-enabling a CRTC that was disabled) has no other vblank reference,
+ * the IRQ never fires and the out fence times out. The matching put
+ * happens once completion is signalled in dm_crtc_high_irq(), or when
+ * the writeback is torn down in dm_clear_writeback().
+ *
+ * Arm wb_pending only after the reference is held so the completion IRQ
+ * cannot run its matching vblank_put before this get.
+ */
+ WARN_ON(drm_crtc_vblank_get(&acrtc->base));
+ acrtc->wb_frame_done = false;
+ acrtc->wb_pending = true;
}
static void amdgpu_dm_update_hdcp(struct drm_atomic_commit *state)
@@@ -11457,6 -4888,75 +4888,75 @@@ static int amdgpu_dm_atomic_setup_commi
return 0;
}
+ STATIC_IFN_KUNIT void set_multisync_trigger_params(
+ struct dc_stream_state *stream)
+ {
+ struct dc_stream_state *master = NULL;
+
+ if (stream->triggered_crtc_reset.enabled) {
+ master = stream->triggered_crtc_reset.event_source;
+ stream->triggered_crtc_reset.event =
+ master->timing.flags.VSYNC_POSITIVE_POLARITY ?
+ CRTC_EVENT_VSYNC_RISING : CRTC_EVENT_VSYNC_FALLING;
+ stream->triggered_crtc_reset.delay = TRIGGER_DELAY_NEXT_PIXEL;
+ }
+ }
+ EXPORT_IF_KUNIT(set_multisync_trigger_params);
+
+ STATIC_IFN_KUNIT void set_master_stream(struct dc_stream_state *stream_set[],
+ int stream_count)
+ {
+ int j, highest_rfr = 0, master_stream = 0;
+
+ for (j = 0; j < stream_count; j++) {
+ if (stream_set[j] && stream_set[j]->triggered_crtc_reset.enabled) {
+ int refresh_rate = 0;
+
+ refresh_rate = (stream_set[j]->timing.pix_clk_100hz*100)/
+ (stream_set[j]->timing.h_total*stream_set[j]->timing.v_total);
+ if (refresh_rate > highest_rfr) {
+ highest_rfr = refresh_rate;
+ master_stream = j;
+ }
+ }
+ }
+ for (j = 0; j < stream_count; j++) {
+ if (stream_set[j])
+ stream_set[j]->triggered_crtc_reset.event_source = stream_set[master_stream];
+ }
+ }
+ EXPORT_IF_KUNIT(set_master_stream);
+
+ STATIC_IFN_KUNIT void dm_enable_per_frame_crtc_master_sync(struct dc_state *context)
+ {
+ int i = 0;
+ struct dc_stream_state *stream;
+
+ if (context->stream_count < 2)
+ return;
+ for (i = 0; i < context->stream_count ; i++) {
+ if (!context->streams[i])
+ continue;
+ /*
+ * TODO: add a function to read AMD VSDB bits and set
+ * crtc_sync_master.multi_sync_enabled flag
+ * For now it's set to false
+ */
+ }
+
+ set_master_stream(context->streams, context->stream_count);
+
+ for (i = 0; i < context->stream_count ; i++) {
+ stream = context->streams[i];
+
+ if (!stream)
+ continue;
+
+ set_multisync_trigger_params(stream);
+ }
+ }
+ EXPORT_IF_KUNIT(dm_enable_per_frame_crtc_master_sync);
+
/**
* amdgpu_dm_atomic_commit_tail() - AMDgpu DM's commit tail implementation.
* @state: The atomic state to commit
@@@ -11525,7 -5025,7 +5025,7 @@@ static void amdgpu_dm_atomic_commit_tai
if ((new_con_state->hdmi.broadcast_rgb != old_con_state->hdmi.broadcast_rgb) &&
(dm_old_crtc_state->stream->output_color_space !=
- get_output_color_space(&dm_new_crtc_state->stream->timing, new_con_state)))
+ amdgpu_dm_get_output_color_space(&dm_new_crtc_state->stream->timing, new_con_state)))
output_color_space_changed = true;
abm_changed = dm_new_crtc_state->abm_level !=
@@@ -11539,7 -5039,7 +5039,7 @@@
stream_update.stream = dm_new_crtc_state->stream;
if (scaling_changed) {
- update_stream_scaling_settings(dev, &dm_new_con_state->base.crtc->mode,
+ amdgpu_dm_update_stream_scaling_settings(dev, &dm_new_con_state->base.crtc->mode,
dm_new_con_state, dm_new_crtc_state->stream);
stream_update.src = dm_new_crtc_state->stream->src;
@@@ -11548,7 -5048,7 +5048,7 @@@
if (output_color_space_changed) {
dm_new_crtc_state->stream->output_color_space
- = get_output_color_space(&dm_new_crtc_state->stream->timing, new_con_state);
+ = amdgpu_dm_get_output_color_space(&dm_new_crtc_state->stream->timing, new_con_state);
stream_update.output_color_space = &dm_new_crtc_state->stream->output_color_space;
}
@@@ -11560,7 -5060,7 +5060,7 @@@
}
if (hdr_changed) {
- fill_hdr_info_packet(new_con_state, &hdr_packet);
+ amdgpu_dm_fill_hdr_info_packet(new_con_state, &hdr_packet);
stream_update.hdr_static_metadata = &hdr_packet;
}
@@@ -11619,7 -5119,7 +5119,7 @@@
dm_old_crtc_state = to_dm_crtc_state(old_crtc_state);
/* For freesync config update on crtc state and params for irq */
- update_stream_irq_parameters(dm, dm_new_crtc_state);
+ amdgpu_dm_update_stream_irq_parameters(dm, dm_new_crtc_state);
#ifdef CONFIG_DEBUG_FS
spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags);
@@@ -11768,104 -5268,6 +5268,6 @@@
trace_amdgpu_dm_atomic_commit_tail_finish(state);
}
- static int dm_force_atomic_commit(struct drm_connector *connector)
- {
- int ret = 0;
- struct drm_device *ddev = connector->dev;
- struct drm_atomic_commit *state = drm_atomic_commit_alloc(ddev);
- struct amdgpu_crtc *disconnected_acrtc = to_amdgpu_crtc(connector->encoder->crtc);
- struct drm_plane *plane = disconnected_acrtc->base.primary;
- struct drm_connector_state *conn_state;
- struct drm_crtc_state *crtc_state;
- struct drm_plane_state *plane_state;
-
- if (!state)
- return -ENOMEM;
-
- state->acquire_ctx = ddev->mode_config.acquire_ctx;
-
- /* Construct an atomic state to restore previous display setting */
-
- /*
- * Attach connectors to drm_atomic_commit
- */
- conn_state = drm_atomic_get_connector_state(state, connector);
-
- /* Check for error in getting connector state */
- if (IS_ERR(conn_state)) {
- ret = PTR_ERR(conn_state);
- goto out;
- }
-
- /* Attach crtc to drm_atomic_commit*/
- crtc_state = drm_atomic_get_crtc_state(state, &disconnected_acrtc->base);
-
- /* Check for error in getting crtc state */
- if (IS_ERR(crtc_state)) {
- ret = PTR_ERR(crtc_state);
- goto out;
- }
-
- /* force a restore */
- crtc_state->mode_changed = true;
-
- /* Attach plane to drm_atomic_commit */
- plane_state = drm_atomic_get_plane_state(state, plane);
-
- /* Check for error in getting plane state */
- if (IS_ERR(plane_state)) {
- ret = PTR_ERR(plane_state);
- goto out;
- }
-
- /* Call commit internally with the state we just constructed */
- ret = drm_atomic_commit(state);
-
- out:
- drm_atomic_commit_put(state);
- if (ret)
- drm_err(ddev, "Restoring old state failed with %i\n", ret);
-
- return ret;
- }
-
- /*
- * This function handles all cases when set mode does not come upon hotplug.
- * This includes when a display is unplugged then plugged back into the
- * same port and when running without usermode desktop manager supprot
- */
- void dm_restore_drm_connector_state(struct drm_device *dev,
- struct drm_connector *connector)
- {
- struct amdgpu_dm_connector *aconnector;
- struct amdgpu_crtc *disconnected_acrtc;
- struct dm_crtc_state *acrtc_state;
-
- if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
- return;
-
- aconnector = to_amdgpu_dm_connector(connector);
-
- if (!aconnector->dc_sink || !connector->state || !connector->encoder)
- return;
-
- disconnected_acrtc = to_amdgpu_crtc(connector->encoder->crtc);
- if (!disconnected_acrtc)
- return;
-
- acrtc_state = to_dm_crtc_state(disconnected_acrtc->base.state);
- if (!acrtc_state->stream)
- return;
-
- /*
- * If the previous sink is not released and different from the current,
- * we deduce we are in a state where we can not rely on usermode call
- * to turn on the display, so we do it here
- */
- if (acrtc_state->stream->sink != aconnector->dc_sink)
- dm_force_atomic_commit(&aconnector->base);
- }
-
/*
* Grabs all modesetting locks to serialize against any blocking commits,
* Waits for completion of all non blocking commits.
@@@ -11917,104 -5319,6 +5319,6 @@@ static int do_aquire_global_lock(struc
return ret < 0 ? ret : 0;
}
- static void get_freesync_config_for_crtc(
- struct dm_crtc_state *new_crtc_state,
- struct dm_connector_state *new_con_state)
- {
- struct mod_freesync_config config = {0};
- struct amdgpu_dm_connector *aconnector;
- struct drm_display_mode *mode = &new_crtc_state->base.mode;
- int vrefresh = drm_mode_vrefresh(mode);
- bool fs_vid_mode = false;
-
- if (new_con_state->base.connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
- return;
-
- aconnector = to_amdgpu_dm_connector(new_con_state->base.connector);
-
- new_crtc_state->vrr_supported = new_con_state->freesync_capable &&
- vrefresh >= aconnector->min_vfreq &&
- vrefresh <= aconnector->max_vfreq;
-
- if (new_crtc_state->vrr_supported) {
- new_crtc_state->stream->ignore_msa_timing_param = true;
- fs_vid_mode = new_crtc_state->freesync_config.state == VRR_STATE_ACTIVE_FIXED;
-
- config.min_refresh_in_uhz = aconnector->min_vfreq * 1000000;
- config.max_refresh_in_uhz = aconnector->max_vfreq * 1000000;
- config.vsif_supported = true;
- config.btr = true;
-
- if (fs_vid_mode) {
- config.state = VRR_STATE_ACTIVE_FIXED;
- config.fixed_refresh_in_uhz = new_crtc_state->freesync_config.fixed_refresh_in_uhz;
- goto out;
- } else if (new_crtc_state->base.vrr_enabled) {
- config.state = VRR_STATE_ACTIVE_VARIABLE;
- } else {
- config.state = VRR_STATE_INACTIVE;
- }
- } else {
- config.state = VRR_STATE_UNSUPPORTED;
- }
- out:
- new_crtc_state->freesync_config = config;
- }
-
- static void reset_freesync_config_for_crtc(
- struct dm_crtc_state *new_crtc_state)
- {
- new_crtc_state->vrr_supported = false;
-
- memset(&new_crtc_state->vrr_infopacket, 0,
- sizeof(new_crtc_state->vrr_infopacket));
- }
-
- static bool
- is_timing_unchanged_for_freesync(struct drm_crtc_state *old_crtc_state,
- struct drm_crtc_state *new_crtc_state)
- {
- const struct drm_display_mode *old_mode, *new_mode;
-
- if (!old_crtc_state || !new_crtc_state)
- return false;
-
- old_mode = &old_crtc_state->mode;
- new_mode = &new_crtc_state->mode;
-
- if (old_mode->clock == new_mode->clock &&
- old_mode->hdisplay == new_mode->hdisplay &&
- old_mode->vdisplay == new_mode->vdisplay &&
- old_mode->htotal == new_mode->htotal &&
- old_mode->vtotal != new_mode->vtotal &&
- old_mode->hsync_start == new_mode->hsync_start &&
- old_mode->vsync_start != new_mode->vsync_start &&
- old_mode->hsync_end == new_mode->hsync_end &&
- old_mode->vsync_end != new_mode->vsync_end &&
- old_mode->hskew == new_mode->hskew &&
- old_mode->vscan == new_mode->vscan &&
- (old_mode->vsync_end - old_mode->vsync_start) ==
- (new_mode->vsync_end - new_mode->vsync_start))
- return true;
-
- return false;
- }
-
- static void set_freesync_fixed_config(struct dm_crtc_state *dm_new_crtc_state)
- {
- u64 num, den, res;
- struct drm_crtc_state *new_crtc_state = &dm_new_crtc_state->base;
-
- dm_new_crtc_state->freesync_config.state = VRR_STATE_ACTIVE_FIXED;
-
- num = (unsigned long long)new_crtc_state->mode.clock * 1000 * 1000000;
- den = (unsigned long long)new_crtc_state->mode.htotal *
- (unsigned long long)new_crtc_state->mode.vtotal;
-
- res = div_u64(num, den);
- dm_new_crtc_state->freesync_config.fixed_refresh_in_uhz = res;
- }
-
static int dm_update_crtc_state(struct amdgpu_display_manager *dm,
struct drm_atomic_commit *state,
struct drm_crtc *crtc,
@@@ -12067,7 -5371,7 +5371,7 @@@
if (!drm_atomic_crtc_needs_modeset(new_crtc_state))
goto skip_modeset;
- new_stream = create_validate_stream_for_sink(connector,
+ new_stream = amdgpu_dm_create_validate_stream_for_sink(connector,
&new_crtc_state->mode,
dm_new_conn_state,
dm_old_crtc_state->stream);
@@@ -12095,7 -5399,7 +5399,7 @@@
dm_new_crtc_state->abm_level = dm_new_conn_state->abm_level;
- ret = fill_hdr_info_packet(drm_new_conn_state,
+ ret = amdgpu_dm_fill_hdr_info_packet(drm_new_conn_state,
&new_stream->hdr_static_metadata);
if (ret)
goto fail;
@@@ -12111,7 -5415,7 +5415,7 @@@
*/
if (amdgpu_freesync_vid_mode &&
dm_new_crtc_state->stream &&
- is_timing_unchanged_for_freesync(new_crtc_state, old_crtc_state))
+ amdgpu_dm_is_timing_unchanged_for_freesync(new_crtc_state, old_crtc_state))
goto skip_modeset;
if (dm_new_crtc_state->stream &&
@@@ -12153,24 -5457,24 +5457,24 @@@
if (amdgpu_freesync_vid_mode && dm_new_crtc_state->stream &&
dc_is_stream_unchanged(new_stream, dm_old_crtc_state->stream) &&
dc_is_stream_scaling_unchanged(new_stream, dm_old_crtc_state->stream) &&
- is_timing_unchanged_for_freesync(new_crtc_state,
+ amdgpu_dm_is_timing_unchanged_for_freesync(new_crtc_state,
old_crtc_state)) {
new_crtc_state->mode_changed = false;
drm_dbg_driver(adev_to_drm(adev),
"Mode change not required for front porch change, setting mode_changed to %d",
new_crtc_state->mode_changed);
- set_freesync_fixed_config(dm_new_crtc_state);
+ amdgpu_dm_set_freesync_fixed_config(dm_new_crtc_state);
goto skip_modeset;
} else if (amdgpu_freesync_vid_mode && aconnector &&
- is_freesync_video_mode(&new_crtc_state->mode,
+ amdgpu_dm_is_freesync_video_mode(&new_crtc_state->mode,
aconnector)) {
struct drm_display_mode *high_mode;
- high_mode = get_highest_refresh_rate_mode(aconnector, false);
+ high_mode = amdgpu_dm_get_highest_refresh_rate_mode(aconnector, false);
if (!drm_mode_equal(&new_crtc_state->mode, high_mode))
- set_freesync_fixed_config(dm_new_crtc_state);
+ amdgpu_dm_set_freesync_fixed_config(dm_new_crtc_state);
}
ret = dm_atomic_get_state(state, &dm_state);
@@@ -12192,7 -5496,7 +5496,7 @@@
dc_stream_release(dm_old_crtc_state->stream);
dm_new_crtc_state->stream = NULL;
- reset_freesync_config_for_crtc(dm_new_crtc_state);
+ amdgpu_dm_reset_freesync_config_for_crtc(dm_new_crtc_state);
*lock_and_validation_needed = true;
@@@ -12261,7 -5565,7 +5565,7 @@@ skip_modeset
/* Scaling or underscan settings */
if (is_scaling_state_different(dm_old_conn_state, dm_new_conn_state) ||
drm_atomic_crtc_needs_modeset(new_crtc_state))
- update_stream_scaling_settings(adev_to_drm(adev),
+ amdgpu_dm_update_stream_scaling_settings(adev_to_drm(adev),
&new_crtc_state->mode, dm_new_conn_state, dm_new_crtc_state->stream);
/* ABM settings */
@@@ -12280,7 -5584,7 +5584,7 @@@
}
/* Update Freesync settings. */
- get_freesync_config_for_crtc(dm_new_crtc_state,
+ amdgpu_dm_get_freesync_config_for_crtc(dm_new_crtc_state,
dm_new_conn_state);
return ret;
@@@ -12357,10 -5661,6 +5661,6 @@@ static bool should_reset_plane(struct d
if (new_crtc_state->color_mgmt_changed)
return true;
- /* Plane color pipeline or its colorop changes. */
- if (new_plane_state->color_mgmt_changed)
- return true;
-
/*
* On zpos change, planes need to be reordered by removing and re-adding
* them one by one to the dc state, in order of descending zpos.
@@@ -12448,140 -5748,13 +5748,13 @@@
new_afb = (struct amdgpu_framebuffer *)new_other_state->fb;
/* Tiling and DCC changes also require bandwidth updates. */
- if (old_afb->tiling_flags != new_afb->tiling_flags ||
- old_afb->base.modifier != new_afb->base.modifier)
+ if (old_afb->base.modifier != new_afb->base.modifier)
return true;
}
return false;
}
- static int dm_check_cursor_fb(struct amdgpu_crtc *new_acrtc,
- struct drm_plane_state *new_plane_state,
- struct drm_framebuffer *fb)
- {
- struct amdgpu_device *adev = drm_to_adev(new_acrtc->base.dev);
- struct amdgpu_framebuffer *afb = to_amdgpu_framebuffer(fb);
- unsigned int pitch;
- bool linear;
-
- if (fb->width > new_acrtc->max_cursor_width ||
- fb->height > new_acrtc->max_cursor_height) {
- drm_dbg_atomic(adev_to_drm(adev), "Bad cursor FB size %dx%d\n",
- new_plane_state->fb->width,
- new_plane_state->fb->height);
- return -EINVAL;
- }
- if (new_plane_state->src_w != fb->width << 16 ||
- new_plane_state->src_h != fb->height << 16) {
- drm_dbg_atomic(adev_to_drm(adev), "Cropping not supported for cursor plane\n");
- return -EINVAL;
- }
-
- /* Pitch in pixels */
- pitch = fb->pitches[0] / fb->format->cpp[0];
-
- if (fb->width != pitch) {
- drm_dbg_atomic(adev_to_drm(adev), "Cursor FB width %d doesn't match pitch %d",
- fb->width, pitch);
- return -EINVAL;
- }
-
- switch (pitch) {
- case 64:
- case 128:
- case 256:
- /* FB pitch is supported by cursor plane */
- break;
- default:
- drm_dbg_atomic(adev_to_drm(adev), "Bad cursor FB pitch %d px\n", pitch);
- return -EINVAL;
- }
-
- /* Core DRM takes care of checking FB modifiers, so we only need to
- * check tiling flags when the FB doesn't have a modifier.
- */
- if (!(fb->flags & DRM_MODE_FB_MODIFIERS)) {
- if (adev->family == AMDGPU_FAMILY_GC_12_0_0) {
- linear = AMDGPU_TILING_GET(afb->tiling_flags, GFX12_SWIZZLE_MODE) == 0;
- } else if (adev->family >= AMDGPU_FAMILY_AI) {
- linear = AMDGPU_TILING_GET(afb->tiling_flags, SWIZZLE_MODE) == 0;
- } else {
- linear = AMDGPU_TILING_GET(afb->tiling_flags, ARRAY_MODE) != DC_ARRAY_2D_TILED_THIN1 &&
- AMDGPU_TILING_GET(afb->tiling_flags, ARRAY_MODE) != DC_ARRAY_1D_TILED_THIN1 &&
- AMDGPU_TILING_GET(afb->tiling_flags, MICRO_TILE_MODE) == 0;
- }
- if (!linear) {
- drm_dbg_atomic(adev_to_drm(adev), "Cursor FB not linear");
- return -EINVAL;
- }
- }
-
- return 0;
- }
-
- /*
- * Helper function for checking the cursor in native mode
- */
- static int dm_check_native_cursor_state(struct drm_crtc *new_plane_crtc,
- struct drm_plane *plane,
- struct drm_plane_state *new_plane_state,
- bool enable)
- {
-
- struct amdgpu_crtc *new_acrtc;
- int ret;
-
- if (!enable || !new_plane_crtc ||
- drm_atomic_plane_disabling(plane->state, new_plane_state))
- return 0;
-
- new_acrtc = to_amdgpu_crtc(new_plane_crtc);
-
- if (new_plane_state->src_x != 0 || new_plane_state->src_y != 0) {
- drm_dbg_atomic(new_plane_crtc->dev, "Cropping not supported for cursor plane\n");
- return -EINVAL;
- }
-
- if (new_plane_state->fb) {
- ret = dm_check_cursor_fb(new_acrtc, new_plane_state,
- new_plane_state->fb);
- if (ret)
- return ret;
- }
-
- return 0;
- }
-
- static bool dm_should_update_native_cursor(struct drm_atomic_commit *state,
- struct drm_crtc *old_plane_crtc,
- struct drm_crtc *new_plane_crtc,
- bool enable)
- {
- struct drm_crtc_state *old_crtc_state, *new_crtc_state;
- struct dm_crtc_state *dm_old_crtc_state, *dm_new_crtc_state;
-
- if (!enable) {
- if (old_plane_crtc == NULL)
- return true;
-
- old_crtc_state = drm_atomic_get_old_crtc_state(
- state, old_plane_crtc);
- dm_old_crtc_state = to_dm_crtc_state(old_crtc_state);
-
- return dm_old_crtc_state->cursor_mode == DM_CURSOR_NATIVE_MODE;
- } else {
- if (new_plane_crtc == NULL)
- return true;
-
- new_crtc_state = drm_atomic_get_new_crtc_state(
- state, new_plane_crtc);
- dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
-
- return dm_new_crtc_state->cursor_mode == DM_CURSOR_NATIVE_MODE;
- }
- }
-
static int dm_update_plane_state(struct dc *dc,
struct drm_atomic_commit *state,
struct drm_plane *plane,
@@@ -12606,14 -5779,14 +5779,14 @@@
dm_new_plane_state = to_dm_plane_state(new_plane_state);
dm_old_plane_state = to_dm_plane_state(old_plane_state);
- update_native_cursor = dm_should_update_native_cursor(state,
- old_plane_crtc,
- new_plane_crtc,
- enable);
+ update_native_cursor = amdgpu_dm_should_update_native_cursor(state,
+ old_plane_crtc,
+ new_plane_crtc,
+ enable);
if (plane->type == DRM_PLANE_TYPE_CURSOR && update_native_cursor) {
- ret = dm_check_native_cursor_state(new_plane_crtc, plane,
- new_plane_state, enable);
+ ret = amdgpu_dm_check_native_cursor_state(new_plane_crtc, plane,
+ new_plane_state, enable);
if (ret)
return ret;
@@@ -12735,7 -5908,7 +5908,7 @@@
/* Tell DC to do a full surface update every time there
* is a plane change. Inefficient, but works for now.
*/
- dm_new_plane_state->dc_state->update_flags.bits.full_update = 1;
+ dm_new_plane_state->dc_state->update_bits.full_update = 1;
*lock_and_validation_needed = true;
}
@@@ -12743,8 -5916,8 +5916,8 @@@
out:
/* If enabling cursor overlay failed, attempt fallback to native mode */
if (enable && ret == -EINVAL && plane->type == DRM_PLANE_TYPE_CURSOR) {
- ret = dm_check_native_cursor_state(new_plane_crtc, plane,
- new_plane_state, enable);
+ ret = amdgpu_dm_check_native_cursor_state(new_plane_crtc, plane,
+ new_plane_state, enable);
if (ret)
return ret;
@@@ -12754,35 -5927,6 +5927,6 @@@
return ret;
}
- static void dm_get_oriented_plane_size(struct drm_plane_state *plane_state,
- int *src_w, int *src_h)
- {
- switch (plane_state->rotation & DRM_MODE_ROTATE_MASK) {
- case DRM_MODE_ROTATE_90:
- case DRM_MODE_ROTATE_270:
- *src_w = plane_state->src_h >> 16;
- *src_h = plane_state->src_w >> 16;
- break;
- case DRM_MODE_ROTATE_0:
- case DRM_MODE_ROTATE_180:
- default:
- *src_w = plane_state->src_w >> 16;
- *src_h = plane_state->src_h >> 16;
- break;
- }
- }
-
- static void
- dm_get_plane_scale(struct drm_plane_state *plane_state,
- int *out_plane_scale_w, int *out_plane_scale_h)
- {
- int plane_src_w, plane_src_h;
-
- dm_get_oriented_plane_size(plane_state, &plane_src_w, &plane_src_h);
- *out_plane_scale_w = plane_src_w ? plane_state->crtc_w * 1000 / plane_src_w : 0;
- *out_plane_scale_h = plane_src_h ? plane_state->crtc_h * 1000 / plane_src_h : 0;
- }
-
/*
* The normalized_zpos value cannot be used by this iterator directly. It's only
* calculated for enabled planes, potentially causing normalized_zpos collisions
@@@ -12790,7 -5934,7 +5934,7 @@@
* so that the iterator will not generate the same object twice, or loop
* indefinitely.
*/
- static inline struct __drm_planes_state *__get_next_zpos(
+ struct __drm_planes_state *amdgpu_dm_get_next_zpos(
struct drm_atomic_commit *state,
struct __drm_planes_state *prev)
{
@@@ -12828,19 -5972,6 +5972,6 @@@
return &state->planes[highest_i];
}
- /*
- * Use the uniqueness of the plane's (zpos, drm obj ID) combination to iterate
- * by descending zpos, as read from the new plane state. This is the same
- * ordering as defined by drm_atomic_normalize_zpos().
- */
- #define for_each_oldnew_plane_in_descending_zpos(__state, plane, old_plane_state, new_plane_state) \
- for (struct __drm_planes_state *__i = __get_next_zpos((__state), NULL); \
- __i != NULL; __i = __get_next_zpos((__state), __i)) \
- for_each_if(((plane) = __i->ptr, \
- (void)(plane) /* Only to avoid unused-but-set-variable warning */, \
- (old_plane_state) = __i->old_state, \
- (new_plane_state) = __i->new_state, 1))
-
static int add_affected_mst_dsc_crtcs(struct drm_atomic_commit *state, struct drm_crtc *crtc)
{
struct drm_connector *connector;
@@@ -12871,219 -6002,6 +6002,6 @@@
return drm_dp_mst_add_affected_dsc_crtcs(state, &aconnector->mst_root->mst_mgr);
}
- /**
- * DOC: Cursor Modes - Native vs Overlay
- *
- * In native mode, the cursor uses a integrated cursor pipe within each DCN hw
- * plane. It does not require a dedicated hw plane to enable, but it is
- * subjected to the same z-order and scaling as the hw plane. It also has format
- * restrictions, a RGB cursor in native mode cannot be enabled within a non-RGB
- * hw plane.
- *
- * In overlay mode, the cursor uses a separate DCN hw plane, and thus has its
- * own scaling and z-pos. It also has no blending restrictions. It lends to a
- * cursor behavior more akin to a DRM client's expectations. However, it does
- * occupy an extra DCN plane, and therefore will only be used if a DCN plane is
- * available.
- */
-
- /**
- * dm_plane_color_pipeline_active() - Check if a plane's color pipeline active.
- * @state: DRM atomic state
- * @plane: DRM plane to check
- * @use_old: if true, inspect the old colorop states; otherwise the new ones
- *
- * A color pipeline may be selected (color_pipeline != NULL) but still is
- * inactive if every colorop in the chain is bypassed. Only return
- * true when at least one colorop has bypass == false, meaning the cursor
- * would be subjected to the transformation in native mode.
- *
- * Return: true if the pipeline modifies pixels, false otherwise.
- */
- static bool dm_plane_color_pipeline_active(struct drm_atomic_commit *state,
- struct drm_plane *plane,
- bool use_old)
- {
- struct drm_colorop *colorop;
- struct drm_colorop_state *old_colorop_state, *new_colorop_state;
- int i;
-
- for_each_oldnew_colorop_in_state(state, colorop, old_colorop_state, new_colorop_state, i) {
- struct drm_colorop_state *cstate = use_old ? old_colorop_state : new_colorop_state;
-
- if (cstate->colorop->plane != plane)
- continue;
- if (!cstate->bypass)
- return true;
- }
- return false;
- }
-
- /**
- * dm_crtc_get_cursor_mode() - Determine the required cursor mode on crtc
- * @adev: amdgpu device
- * @state: DRM atomic state
- * @dm_crtc_state: amdgpu state for the CRTC containing the cursor
- * @cursor_mode: Returns the required cursor mode on dm_crtc_state
- *
- * Get whether the cursor should be enabled in native mode, or overlay mode, on
- * the dm_crtc_state.
- *
- * The cursor should be enabled in overlay mode if there exists an underlying
- * plane - on which the cursor may be blended - that is either YUV formatted,
- * scaled differently from the cursor, or has a color pipeline active.
- *
- * Since zpos info is required, drm_atomic_normalize_zpos must be called before
- * calling this function.
- *
- * Return: 0 on success, or an error code if getting the cursor plane state
- * failed.
- */
- static int dm_crtc_get_cursor_mode(struct amdgpu_device *adev,
- struct drm_atomic_commit *state,
- struct dm_crtc_state *dm_crtc_state,
- enum amdgpu_dm_cursor_mode *cursor_mode)
- {
- struct drm_plane_state *old_plane_state, *plane_state, *cursor_state;
- struct drm_crtc_state *crtc_state = &dm_crtc_state->base;
- struct drm_plane *plane;
- bool consider_mode_change = false;
- bool entire_crtc_covered = false;
- bool cursor_changed = false;
- int underlying_scale_w, underlying_scale_h;
- int cursor_scale_w, cursor_scale_h;
- int i;
-
- /* Overlay cursor not supported on HW before DCN
- * DCN401/420 does not have the cursor-on-scaled-plane or cursor-on-yuv-plane restrictions
- * as previous DCN generations, so enable native mode on DCN401/420
- *
- * Always set native cursor mode when the CRTC is disabled,
- * to make sure it doesn't cause atomic commits to fail when
- * they are trying to disable the CRTC.
- */
- if (amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(4, 0, 1) ||
- amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(4, 2, 0) ||
- amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(4, 2, 1) ||
- !dm_crtc_state->base.enable) {
- *cursor_mode = DM_CURSOR_NATIVE_MODE;
- return 0;
- }
-
- /* Init cursor_mode to be the same as current */
- *cursor_mode = dm_crtc_state->cursor_mode;
-
- /*
- * Cursor mode can change if a plane's format changes, scale changes, is
- * enabled/disabled, z-order changes, or color management properties change.
- */
- for_each_oldnew_plane_in_state(state, plane, old_plane_state, plane_state, i) {
- int new_scale_w, new_scale_h, old_scale_w, old_scale_h;
-
- /* Only care about planes on this CRTC */
- if ((drm_plane_mask(plane) & crtc_state->plane_mask) == 0)
- continue;
-
- if (plane->type == DRM_PLANE_TYPE_CURSOR)
- cursor_changed = true;
-
- if (drm_atomic_plane_enabling(old_plane_state, plane_state) ||
- drm_atomic_plane_disabling(old_plane_state, plane_state) ||
- old_plane_state->fb->format != plane_state->fb->format) {
- consider_mode_change = true;
- break;
- }
-
- dm_get_plane_scale(plane_state, &new_scale_w, &new_scale_h);
- dm_get_plane_scale(old_plane_state, &old_scale_w, &old_scale_h);
- if (new_scale_w != old_scale_w || new_scale_h != old_scale_h) {
- consider_mode_change = true;
- break;
- }
-
- if (dm_plane_color_pipeline_active(state, plane, true) !=
- dm_plane_color_pipeline_active(state, plane, false)) {
- consider_mode_change = true;
- break;
- }
- }
-
- if (!consider_mode_change && !crtc_state->zpos_changed)
- return 0;
-
- /*
- * If no cursor change on this CRTC, and not enabled on this CRTC, then
- * no need to set cursor mode. This avoids needlessly locking the cursor
- * state.
- */
- if (!cursor_changed &&
- !(drm_plane_mask(crtc_state->crtc->cursor) & crtc_state->plane_mask)) {
- return 0;
- }
-
- cursor_state = drm_atomic_get_plane_state(state,
- crtc_state->crtc->cursor);
- if (IS_ERR(cursor_state))
- return PTR_ERR(cursor_state);
-
- /* Cursor is disabled */
- if (!cursor_state->fb)
- return 0;
-
- /* For all planes in descending z-order (all of which are below cursor
- * as per zpos definitions), check their scaling and format
- */
- for_each_oldnew_plane_in_descending_zpos(state, plane, old_plane_state, plane_state) {
-
- /* Only care about non-cursor planes on this CRTC */
- if ((drm_plane_mask(plane) & crtc_state->plane_mask) == 0 ||
- plane->type == DRM_PLANE_TYPE_CURSOR)
- continue;
-
- /* Underlying plane is YUV format - use overlay cursor */
- if (amdgpu_dm_plane_is_video_format(plane_state->fb->format->format)) {
- *cursor_mode = DM_CURSOR_OVERLAY_MODE;
- return 0;
- }
-
- /* Underlying plane has an active color pipeline - cursor would be transformed */
- if (dm_plane_color_pipeline_active(state, plane, false)) {
- *cursor_mode = DM_CURSOR_OVERLAY_MODE;
- return 0;
- }
-
- dm_get_plane_scale(plane_state,
- &underlying_scale_w, &underlying_scale_h);
- dm_get_plane_scale(cursor_state,
- &cursor_scale_w, &cursor_scale_h);
-
- /* Underlying plane has different scale - use overlay cursor */
- if (cursor_scale_w != underlying_scale_w &&
- cursor_scale_h != underlying_scale_h) {
- *cursor_mode = DM_CURSOR_OVERLAY_MODE;
- return 0;
- }
-
- /* If this plane covers the whole CRTC, no need to check planes underneath */
- if (plane_state->crtc_x <= 0 && plane_state->crtc_y <= 0 &&
- plane_state->crtc_x + plane_state->crtc_w >= crtc_state->mode.hdisplay &&
- plane_state->crtc_y + plane_state->crtc_h >= crtc_state->mode.vdisplay) {
- entire_crtc_covered = true;
- break;
- }
- }
-
- /* If planes do not cover the entire CRTC, use overlay mode to enable
- * cursor over holes
- */
- if (entire_crtc_covered)
- *cursor_mode = DM_CURSOR_NATIVE_MODE;
- else
- *cursor_mode = DM_CURSOR_OVERLAY_MODE;
-
- return 0;
- }
-
static bool amdgpu_dm_crtc_mem_type_changed(struct drm_device *dev,
struct drm_atomic_commit *state,
struct drm_crtc_state *crtc_state)
@@@ -13156,7 -6074,7 +6074,7 @@@ static int amdgpu_dm_atomic_check(struc
ret = drm_atomic_helper_check_modeset(dev, state);
if (ret) {
- drm_dbg_atomic(dev, "drm_atomic_helper_check_modeset() failed\n");
+ drm_dbg_atomic(dev, "drm_atomic_helper_check_modeset() failed: %pe\n", ERR_PTR(ret));
goto fail;
}
@@@ -13171,7 -6089,7 +6089,7 @@@
new_crtc_state = drm_atomic_get_crtc_state(state, new_con_state->crtc);
if (IS_ERR(new_crtc_state)) {
- drm_dbg_atomic(dev, "drm_atomic_get_crtc_state() failed\n");
+ drm_dbg_atomic(dev, "drm_atomic_get_crtc_state() failed: %pe\n", new_crtc_state);
ret = PTR_ERR(new_crtc_state);
goto fail;
}
@@@ -13191,7 -6109,7 +6109,7 @@@
if (drm_atomic_crtc_needs_modeset(new_crtc_state)) {
ret = add_affected_mst_dsc_crtcs(state, crtc);
if (ret) {
- drm_dbg_atomic(dev, "add_affected_mst_dsc_crtcs() failed\n");
+ drm_dbg_atomic(dev, "add_affected_mst_dsc_crtcs() failed: %pe\n", ERR_PTR(ret));
goto fail;
}
}
@@@ -13208,7 -6126,7 +6126,7 @@@
ret = amdgpu_dm_verify_lut_sizes(new_crtc_state);
if (ret) {
- drm_dbg_atomic(dev, "amdgpu_dm_verify_lut_sizes() failed\n");
+ drm_dbg_atomic(dev, "amdgpu_dm_verify_lut_sizes() failed: %pe\n", ERR_PTR(ret));
goto fail;
}
@@@ -13217,13 -6135,13 +6135,13 @@@
ret = drm_atomic_add_affected_connectors(state, crtc);
if (ret) {
- drm_dbg_atomic(dev, "drm_atomic_add_affected_connectors() failed\n");
+ drm_dbg_atomic(dev, "drm_atomic_add_affected_connectors() failed: %pe\n", ERR_PTR(ret));
goto fail;
}
ret = drm_atomic_add_affected_planes(state, crtc);
if (ret) {
- drm_dbg_atomic(dev, "drm_atomic_add_affected_planes() failed\n");
+ drm_dbg_atomic(dev, "drm_atomic_add_affected_planes() failed: %pe\n", ERR_PTR(ret));
goto fail;
}
@@@ -13262,7 -6180,7 +6180,7 @@@
if (IS_ERR(new_plane_state)) {
ret = PTR_ERR(new_plane_state);
- drm_dbg_atomic(dev, "new_plane_state is BAD\n");
+ drm_dbg_atomic(dev, "new_plane_state is BAD: %pe\n", new_plane_state);
goto fail;
}
}
@@@ -13276,7 -6194,7 +6194,7 @@@
*/
ret = drm_atomic_normalize_zpos(dev, state);
if (ret) {
- drm_dbg(dev, "drm_atomic_normalize_zpos() failed\n");
+ drm_dbg(dev, "drm_atomic_normalize_zpos() failed: %pe\n", ERR_PTR(ret));
goto fail;
}
@@@ -13287,10 -6205,10 +6205,10 @@@
for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
- ret = dm_crtc_get_cursor_mode(adev, state, dm_new_crtc_state,
- &dm_new_crtc_state->cursor_mode);
+ ret = amdgpu_dm_crtc_get_cursor_mode(adev, state, dm_new_crtc_state,
+ &dm_new_crtc_state->cursor_mode);
if (ret) {
- drm_dbg(dev, "Failed to determine cursor mode\n");
+ drm_dbg(dev, "Failed to determine cursor mode: %pe\n", ERR_PTR(ret));
goto fail;
}
@@@ -13322,7 -6240,7 +6240,7 @@@
&lock_and_validation_needed,
&is_top_most_overlay);
if (ret) {
- drm_dbg_atomic(dev, "dm_update_plane_state() failed\n");
+ drm_dbg_atomic(dev, "dm_update_plane_state() failed: %pe\n", ERR_PTR(ret));
goto fail;
}
}
@@@ -13335,7 -6253,7 +6253,7 @@@
false,
&lock_and_validation_needed);
if (ret) {
- drm_dbg_atomic(dev, "DISABLE: dm_update_crtc_state() failed\n");
+ drm_dbg_atomic(dev, "DISABLE: dm_update_crtc_state() failed: %pe\n", ERR_PTR(ret));
goto fail;
}
}
@@@ -13348,7 -6266,7 +6266,7 @@@
true,
&lock_and_validation_needed);
if (ret) {
- drm_dbg_atomic(dev, "ENABLE: dm_update_crtc_state() failed\n");
+ drm_dbg_atomic(dev, "ENABLE: dm_update_crtc_state() failed: %pe\n", ERR_PTR(ret));
goto fail;
}
}
@@@ -13362,7 -6280,7 +6280,7 @@@
&lock_and_validation_needed,
&is_top_most_overlay);
if (ret) {
- drm_dbg_atomic(dev, "dm_update_plane_state() failed\n");
+ drm_dbg_atomic(dev, "dm_update_plane_state() failed: %pe\n", ERR_PTR(ret));
goto fail;
}
}
@@@ -13378,7 -6296,7 +6296,7 @@@
/* Run this here since we want to validate the streams we created */
ret = drm_atomic_helper_check_planes(dev, state);
if (ret) {
- drm_dbg_atomic(dev, "drm_atomic_helper_check_planes() failed\n");
+ drm_dbg_atomic(dev, "drm_atomic_helper_check_planes() failed: %pe\n", ERR_PTR(ret));
goto fail;
}
@@@ -13422,8 -6340,8 +6340,8 @@@
}
/* If HW can only do native cursor, check restrictions again */
- ret = dm_crtc_get_cursor_mode(adev, state, dm_new_crtc_state,
- &required_cursor_mode);
+ ret = amdgpu_dm_crtc_get_cursor_mode(adev, state, dm_new_crtc_state,
+ &required_cursor_mode);
if (ret) {
drm_dbg_driver(crtc->dev,
"[CRTC:%d:%s] Checking cursor mode failed\n",
@@@ -13516,13 -6434,13 +6434,13 @@@
if (lock_and_validation_needed) {
ret = dm_atomic_get_state(state, &dm_state);
if (ret) {
- drm_dbg_atomic(dev, "dm_atomic_get_state() failed\n");
+ drm_dbg_atomic(dev, "dm_atomic_get_state() failed: %pe\n", ERR_PTR(ret));
goto fail;
}
ret = do_aquire_global_lock(dev, state);
if (ret) {
- drm_dbg_atomic(dev, "do_aquire_global_lock() failed\n");
+ drm_dbg_atomic(dev, "do_aquire_global_lock() failed: %pe\n", ERR_PTR(ret));
goto fail;
}
@@@ -13530,7 -6448,7 +6448,7 @@@
if (dc_resource_is_dsc_encoding_supported(dc)) {
ret = compute_mst_dsc_configs_for_state(state, dm_state->context, vars);
if (ret) {
- drm_dbg_atomic(dev, "MST_DSC compute_mst_dsc_configs_for_state() failed\n");
+ drm_dbg_atomic(dev, "MST_DSC compute_mst_dsc_configs_for_state() failed: %pe\n", ERR_PTR(ret));
ret = -EINVAL;
goto fail;
}
@@@ -13539,7 -6457,7 +6457,7 @@@
ret = dm_update_mst_vcpi_slots_for_dsc(state, dm_state->context, vars);
if (ret) {
- drm_dbg_atomic(dev, "dm_update_mst_vcpi_slots_for_dsc() failed\n");
+ drm_dbg_atomic(dev, "dm_update_mst_vcpi_slots_for_dsc() failed: %pe\n", ERR_PTR(ret));
goto fail;
}
@@@ -13551,7 -6469,7 +6469,7 @@@
*/
ret = drm_dp_mst_atomic_check(state);
if (ret) {
- drm_dbg_atomic(dev, "MST drm_dp_mst_atomic_check() failed\n");
+ drm_dbg_atomic(dev, "MST drm_dp_mst_atomic_check() failed: %pe\n", ERR_PTR(ret));
goto fail;
}
status = dc_validate_global_state(dc, dm_state->context, DC_VALIDATE_MODE_ONLY);
@@@ -13640,380 -6558,13 +6558,13 @@@ fail
else if (ret == -EINTR || ret == -EAGAIN || ret == -ERESTARTSYS)
drm_dbg_atomic(dev, "Atomic check stopped due to signal.\n");
else
- drm_dbg_atomic(dev, "Atomic check failed with err: %d\n", ret);
+ drm_dbg_atomic(dev, "Atomic check failed: %pe\n", ERR_PTR(ret));
trace_amdgpu_dm_atomic_check_finish(state, ret);
return ret;
}
- static bool dm_edid_parser_send_cea(struct amdgpu_display_manager *dm,
- unsigned int offset,
- unsigned int total_length,
- u8 *data,
- unsigned int length,
- struct amdgpu_hdmi_vsdb_info *vsdb)
- {
- bool res;
- union dmub_rb_cmd cmd;
- struct dmub_cmd_send_edid_cea *input;
- struct dmub_cmd_edid_cea_output *output;
-
- if (length > DMUB_EDID_CEA_DATA_CHUNK_BYTES)
- return false;
-
- memset(&cmd, 0, sizeof(cmd));
-
- input = &cmd.edid_cea.data.input;
-
- cmd.edid_cea.header.type = DMUB_CMD__EDID_CEA;
- cmd.edid_cea.header.sub_type = 0;
- cmd.edid_cea.header.payload_bytes =
- sizeof(cmd.edid_cea) - sizeof(cmd.edid_cea.header);
- input->offset = offset;
- input->length = length;
- input->cea_total_length = total_length;
- memcpy(input->payload, data, length);
-
- res = dc_wake_and_execute_dmub_cmd(dm->dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT_WITH_REPLY);
- if (!res) {
- drm_err(adev_to_drm(dm->adev), "EDID CEA parser failed\n");
- return false;
- }
-
- output = &cmd.edid_cea.data.output;
-
- if (output->type == DMUB_CMD__EDID_CEA_ACK) {
- if (!output->ack.success) {
- drm_err(adev_to_drm(dm->adev), "EDID CEA ack failed at offset %d\n",
- output->ack.offset);
- }
- } else if (output->type == DMUB_CMD__EDID_CEA_AMD_VSDB) {
- if (!output->amd_vsdb.vsdb_found)
- return false;
-
- vsdb->freesync_supported = output->amd_vsdb.freesync_supported;
- vsdb->amd_vsdb_version = output->amd_vsdb.amd_vsdb_version;
- vsdb->min_refresh_rate_hz = output->amd_vsdb.min_frame_rate;
- vsdb->max_refresh_rate_hz = output->amd_vsdb.max_frame_rate;
- vsdb->freesync_mccs_vcp_code = output->amd_vsdb.freesync_mccs_vcp_code;
- } else {
- drm_warn(adev_to_drm(dm->adev), "Unknown EDID CEA parser results\n");
- return false;
- }
-
- return true;
- }
-
- static bool parse_edid_cea_dmcu(struct amdgpu_display_manager *dm,
- u8 *edid_ext, int len,
- struct amdgpu_hdmi_vsdb_info *vsdb_info)
- {
- int i;
-
- /* send extension block to DMCU for parsing */
- for (i = 0; i < len; i += 8) {
- bool res;
- int offset;
-
- /* send 8 bytes a time */
- if (!dc_edid_parser_send_cea(dm->dc, i, len, &edid_ext[i], 8))
- return false;
-
- if (i+8 == len) {
- /* EDID block sent completed, expect result */
- int version, min_rate, max_rate;
-
- res = dc_edid_parser_recv_amd_vsdb(dm->dc, &version, &min_rate, &max_rate);
- if (res) {
- /* amd vsdb found */
- vsdb_info->freesync_supported = 1;
- vsdb_info->amd_vsdb_version = version;
- vsdb_info->min_refresh_rate_hz = min_rate;
- vsdb_info->max_refresh_rate_hz = max_rate;
- /* Not enabled on DMCU*/
- vsdb_info->freesync_mccs_vcp_code = 0;
- return true;
- }
- /* not amd vsdb */
- return false;
- }
-
- /* check for ack*/
- res = dc_edid_parser_recv_cea_ack(dm->dc, &offset);
- if (!res)
- return false;
- }
-
- return false;
- }
-
- static bool parse_edid_cea_dmub(struct amdgpu_display_manager *dm,
- u8 *edid_ext, int len,
- struct amdgpu_hdmi_vsdb_info *vsdb_info)
- {
- int i;
-
- /* send extension block to DMCU for parsing */
- for (i = 0; i < len; i += 8) {
- /* send 8 bytes a time */
- if (!dm_edid_parser_send_cea(dm, i, len, &edid_ext[i], 8, vsdb_info))
- return false;
- }
-
- return vsdb_info->freesync_supported;
- }
-
- static bool parse_edid_cea(struct amdgpu_dm_connector *aconnector,
- u8 *edid_ext, int len,
- struct amdgpu_hdmi_vsdb_info *vsdb_info)
- {
- struct amdgpu_device *adev = drm_to_adev(aconnector->base.dev);
- bool ret;
-
- mutex_lock(&adev->dm.dc_lock);
- if (adev->dm.dmub_srv)
- ret = parse_edid_cea_dmub(&adev->dm, edid_ext, len, vsdb_info);
- else
- ret = parse_edid_cea_dmcu(&adev->dm, edid_ext, len, vsdb_info);
- mutex_unlock(&adev->dm.dc_lock);
- return ret;
- }
-
- static void parse_edid_displayid_vrr(struct drm_connector *connector,
- const struct edid *edid)
- {
- u8 *edid_ext = NULL;
- int i;
- int j = 0;
- u16 min_vfreq;
- u16 max_vfreq;
-
- if (!edid || !edid->extensions)
- return;
-
- /* Find DisplayID extension */
- for (i = 0; i < edid->extensions; i++) {
- edid_ext = (void *)(edid + (i + 1));
- if (edid_ext[0] == DISPLAYID_EXT)
- break;
- }
-
- if (i == edid->extensions)
- return;
-
- while (j < EDID_LENGTH) {
- /* Get dynamic video timing range from DisplayID if available */
- if (EDID_LENGTH - j > 13 && edid_ext[j] == 0x25 &&
- (edid_ext[j+1] & 0xFE) == 0 && (edid_ext[j+2] == 9)) {
- min_vfreq = edid_ext[j+9];
- if (edid_ext[j+1] & 7)
- max_vfreq = edid_ext[j+10] + ((edid_ext[j+11] & 3) << 8);
- else
- max_vfreq = edid_ext[j+10];
-
- if (max_vfreq && min_vfreq) {
- connector->display_info.monitor_range.max_vfreq = max_vfreq;
- connector->display_info.monitor_range.min_vfreq = min_vfreq;
-
- return;
- }
- }
- j++;
- }
- }
-
- static int get_amd_vsdb(struct amdgpu_dm_connector *aconnector,
- struct amdgpu_hdmi_vsdb_info *vsdb_info)
- {
- struct drm_connector *connector = &aconnector->base;
-
- vsdb_info->replay_mode = connector->display_info.amd_vsdb.replay_mode;
- vsdb_info->amd_vsdb_version = connector->display_info.amd_vsdb.version;
-
- return connector->display_info.amd_vsdb.version != 0;
- }
-
- static int parse_hdmi_amd_vsdb(struct amdgpu_dm_connector *aconnector,
- const struct edid *edid,
- struct amdgpu_hdmi_vsdb_info *vsdb_info)
- {
- u8 *edid_ext = NULL;
- int i;
- bool valid_vsdb_found = false;
-
- /*----- drm_find_cea_extension() -----*/
- /* No EDID or EDID extensions */
- if (edid == NULL || edid->extensions == 0)
- return -ENODEV;
-
- /* Find CEA extension */
- for (i = 0; i < edid->extensions; i++) {
- edid_ext = (uint8_t *)edid + EDID_LENGTH * (i + 1);
- if (edid_ext[0] == CEA_EXT)
- break;
- }
-
- if (i == edid->extensions)
- return -ENODEV;
-
- /*----- cea_db_offsets() -----*/
- if (edid_ext[0] != CEA_EXT)
- return -ENODEV;
-
- valid_vsdb_found = parse_edid_cea(aconnector, edid_ext, EDID_LENGTH, vsdb_info);
-
- return valid_vsdb_found ? i : -ENODEV;
- }
-
- /**
- * amdgpu_dm_update_freesync_caps - Update Freesync capabilities
- *
- * @connector: Connector to query.
- * @drm_edid: DRM EDID from monitor
- * @do_mccs: Controls whether MCCS (Monitor Control Command Set) over
- * DDC (Display Data Channel) transactions are performed. When true,
- * the driver queries the monitor to get or update additional FreeSync
- * capability information. When false, these transactions are skipped.
- *
- * Amdgpu supports Freesync in DP and HDMI displays, and it is required to keep
- * track of some of the display information in the internal data struct used by
- * amdgpu_dm. This function checks which type of connector we need to set the
- * FreeSync parameters.
- */
- void amdgpu_dm_update_freesync_caps(struct drm_connector *connector,
- const struct drm_edid *drm_edid, bool do_mccs)
- {
- int i = 0;
- struct amdgpu_dm_connector *amdgpu_dm_connector =
- to_amdgpu_dm_connector(connector);
- struct dm_connector_state *dm_con_state = NULL;
- struct dc_sink *sink;
- struct amdgpu_device *adev = drm_to_adev(connector->dev);
- struct amdgpu_hdmi_vsdb_info vsdb_info = {0};
- const struct edid *edid;
- bool freesync_capable = false;
- enum adaptive_sync_type as_type = ADAPTIVE_SYNC_TYPE_NONE;
-
- if (!connector->state) {
- drm_err(adev_to_drm(adev), "%s - Connector has no state", __func__);
- goto update;
- }
-
- sink = amdgpu_dm_connector->dc_sink ?
- amdgpu_dm_connector->dc_sink :
- amdgpu_dm_connector->dc_em_sink;
-
- drm_edid_connector_update(connector, drm_edid);
-
- if (!drm_edid || !sink) {
- dm_con_state = to_dm_connector_state(connector->state);
-
- amdgpu_dm_connector->min_vfreq = 0;
- amdgpu_dm_connector->max_vfreq = 0;
- freesync_capable = false;
-
- goto update;
- }
-
- dm_con_state = to_dm_connector_state(connector->state);
-
- if (!adev->dm.freesync_module || !dc_supports_vrr(sink->ctx->dce_version))
- goto update;
-
- edid = drm_edid_raw(drm_edid); // FIXME: Get rid of drm_edid_raw()
-
- /* Some eDP panels only have the refresh rate range info in DisplayID */
- if ((connector->display_info.monitor_range.min_vfreq == 0 ||
- connector->display_info.monitor_range.max_vfreq == 0))
- parse_edid_displayid_vrr(connector, edid);
-
- if (edid && (sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT ||
- sink->sink_signal == SIGNAL_TYPE_EDP)) {
- if (amdgpu_dm_connector->dc_link &&
- amdgpu_dm_connector->dc_link->dpcd_caps.allow_invalid_MSA_timing_param) {
- amdgpu_dm_connector->min_vfreq = connector->display_info.monitor_range.min_vfreq;
- amdgpu_dm_connector->max_vfreq = connector->display_info.monitor_range.max_vfreq;
- if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10)
- freesync_capable = true;
- }
-
- get_amd_vsdb(amdgpu_dm_connector, &vsdb_info);
-
- if (vsdb_info.replay_mode) {
- amdgpu_dm_connector->vsdb_info.replay_mode = vsdb_info.replay_mode;
- amdgpu_dm_connector->vsdb_info.amd_vsdb_version = vsdb_info.amd_vsdb_version;
- amdgpu_dm_connector->as_type = ADAPTIVE_SYNC_TYPE_EDP;
- }
-
- } else if (drm_edid && sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A) {
- i = parse_hdmi_amd_vsdb(amdgpu_dm_connector, edid, &vsdb_info);
- if (i >= 0) {
- amdgpu_dm_connector->vsdb_info = vsdb_info;
- sink->edid_caps.freesync_vcp_code = vsdb_info.freesync_mccs_vcp_code;
-
- if (vsdb_info.freesync_supported) {
- amdgpu_dm_connector->min_vfreq = vsdb_info.min_refresh_rate_hz;
- amdgpu_dm_connector->max_vfreq = vsdb_info.max_refresh_rate_hz;
- if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10)
- freesync_capable = true;
-
- connector->display_info.monitor_range.min_vfreq = vsdb_info.min_refresh_rate_hz;
- connector->display_info.monitor_range.max_vfreq = vsdb_info.max_refresh_rate_hz;
- }
- }
- }
-
- if (amdgpu_dm_connector->dc_link)
- as_type = dm_get_adaptive_sync_support_type(amdgpu_dm_connector->dc_link);
-
- if (as_type == FREESYNC_TYPE_PCON_IN_WHITELIST) {
- i = parse_hdmi_amd_vsdb(amdgpu_dm_connector, edid, &vsdb_info);
- if (i >= 0) {
- amdgpu_dm_connector->vsdb_info = vsdb_info;
- sink->edid_caps.freesync_vcp_code = vsdb_info.freesync_mccs_vcp_code;
-
- if (vsdb_info.freesync_supported && vsdb_info.amd_vsdb_version > 0) {
- amdgpu_dm_connector->pack_sdp_v1_3 = true;
- amdgpu_dm_connector->as_type = as_type;
-
- amdgpu_dm_connector->min_vfreq = vsdb_info.min_refresh_rate_hz;
- amdgpu_dm_connector->max_vfreq = vsdb_info.max_refresh_rate_hz;
- if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10)
- freesync_capable = true;
-
- connector->display_info.monitor_range.min_vfreq = vsdb_info.min_refresh_rate_hz;
- connector->display_info.monitor_range.max_vfreq = vsdb_info.max_refresh_rate_hz;
- }
- }
- }
-
- /* Handle MCCS */
- if (do_mccs) {
- dm_helpers_read_mccs_caps(adev->dm.dc->ctx, amdgpu_dm_connector->dc_link, sink);
-
- if (sink->edid_caps.freesync_vcp_code && !sink->mccs_caps.freesync_supported)
- freesync_capable = false;
-
- if (sink->mccs_caps.freesync_supported && freesync_capable)
- dm_helpers_mccs_vcp_set(adev->dm.dc->ctx, amdgpu_dm_connector->dc_link, sink);
- }
-
- update:
- if (dm_con_state)
- dm_con_state->freesync_capable = freesync_capable;
-
- if (connector->state && amdgpu_dm_connector->dc_link && !freesync_capable &&
- amdgpu_dm_connector->dc_link->replay_settings.config.replay_supported) {
- amdgpu_dm_connector->dc_link->replay_settings.config.replay_supported = false;
- amdgpu_dm_connector->dc_link->replay_settings.replay_feature_enabled = false;
- }
-
- if (connector->vrr_capable_property)
- drm_connector_set_vrr_capable_property(connector,
- freesync_capable);
- }
-
void amdgpu_dm_trigger_timing_sync(struct drm_device *dev)
{
struct amdgpu_device *adev = drm_to_adev(dev);
@@@ -14033,12 -6584,6 +6584,6 @@@
mutex_unlock(&adev->dm.dc_lock);
}
- static inline void amdgpu_dm_exit_ips_for_hw_access(struct dc *dc)
- {
- if (dc->ctx->dmub_srv && !dc->ctx->dmub_srv->idle_exit_counter)
- dc_exit_ips_for_hw_access(dc);
- }
-
void dm_write_reg_func(const struct dc_context *ctx, uint32_t address,
u32 value, const char *func_name)
{
@@@ -14067,13 -6612,6 +6612,6 @@@ uint32_t dm_read_reg_func(const struct
}
#endif
- if (ctx->dmub_srv &&
- ctx->dmub_srv->reg_helper_offload.gather_in_progress &&
- !ctx->dmub_srv->reg_helper_offload.should_burst_write) {
- ASSERT(false);
- return 0;
- }
-
amdgpu_dm_exit_ips_for_hw_access(ctx->dc);
value = cgs_read_register(ctx->cgs_device, address);
@@@ -14083,179 -6621,6 +6621,6 @@@
return value;
}
- int amdgpu_dm_process_dmub_aux_transfer_sync(
- struct dc_context *ctx,
- unsigned int link_index,
- struct aux_payload *payload,
- enum aux_return_code_type *operation_result)
- {
- struct amdgpu_device *adev = ctx->driver_context;
- struct dmub_notification *p_notify = adev->dm.dmub_notify;
- int ret = -1;
-
- mutex_lock(&adev->dm.dpia_aux_lock);
- if (!dc_process_dmub_aux_transfer_async(ctx->dc, link_index, payload)) {
- *operation_result = AUX_RET_ERROR_ENGINE_ACQUIRE;
- goto out;
- }
-
- if (!wait_for_completion_timeout(&adev->dm.dmub_aux_transfer_done, 10 * HZ)) {
- drm_err(adev_to_drm(adev), "wait_for_completion_timeout timeout!");
- *operation_result = AUX_RET_ERROR_TIMEOUT;
- goto out;
- }
-
- if (p_notify->result != AUX_RET_SUCCESS) {
- /*
- * Transient states before tunneling is enabled could
- * lead to this error. We can ignore this for now.
- */
- if (p_notify->result == AUX_RET_ERROR_PROTOCOL_ERROR) {
- drm_warn(adev_to_drm(adev), "DPIA AUX failed on 0x%x(%d), error %d\n",
- payload->address, payload->length,
- p_notify->result);
- }
- *operation_result = p_notify->result;
- goto out;
- }
-
- payload->reply[0] = adev->dm.dmub_notify->aux_reply.command & 0xF;
- if (adev->dm.dmub_notify->aux_reply.command & 0xF0)
- /* The reply is stored in the top nibble of the command. */
- payload->reply[0] = (adev->dm.dmub_notify->aux_reply.command >> 4) & 0xF;
-
- /*write req may receive a byte indicating partially written number as well*/
- if (p_notify->aux_reply.length)
- memcpy(payload->data, p_notify->aux_reply.data,
- p_notify->aux_reply.length);
-
- /* success */
- ret = p_notify->aux_reply.length;
- *operation_result = p_notify->result;
- out:
- reinit_completion(&adev->dm.dmub_aux_transfer_done);
- mutex_unlock(&adev->dm.dpia_aux_lock);
- return ret;
- }
-
- static void abort_fused_io(
- struct dc_context *ctx,
- const struct dmub_cmd_fused_request *request
- )
- {
- union dmub_rb_cmd command = { 0 };
- struct dmub_rb_cmd_fused_io *io = &command.fused_io;
-
- io->header.type = DMUB_CMD__FUSED_IO;
- io->header.sub_type = DMUB_CMD__FUSED_IO_ABORT;
- io->header.payload_bytes = sizeof(*io) - sizeof(io->header);
- io->request = *request;
- dm_execute_dmub_cmd(ctx, &command, DM_DMUB_WAIT_TYPE_NO_WAIT);
- }
-
- static bool execute_fused_io(
- struct amdgpu_device *dev,
- struct dc_context *ctx,
- union dmub_rb_cmd *commands,
- uint8_t count,
- uint32_t timeout_us
- )
- {
- const uint8_t ddc_line = commands[0].fused_io.request.u.aux.ddc_line;
-
- if (ddc_line >= ARRAY_SIZE(dev->dm.fused_io))
- return false;
-
- struct fused_io_sync *sync = &dev->dm.fused_io[ddc_line];
- struct dmub_rb_cmd_fused_io *first = &commands[0].fused_io;
- const bool result = dm_execute_dmub_cmd_list(ctx, count, commands, DM_DMUB_WAIT_TYPE_WAIT_WITH_REPLY)
- && first->header.ret_status
- && first->request.status == FUSED_REQUEST_STATUS_SUCCESS;
-
- if (!result)
- return false;
-
- while (wait_for_completion_timeout(&sync->replied, usecs_to_jiffies(timeout_us))) {
- reinit_completion(&sync->replied);
-
- struct dmub_cmd_fused_request *reply = (struct dmub_cmd_fused_request *) sync->reply_data;
-
- static_assert(sizeof(*reply) <= sizeof(sync->reply_data), "Size mismatch");
-
- if (reply->identifier == first->request.identifier) {
- first->request = *reply;
- return true;
- }
- }
-
- reinit_completion(&sync->replied);
- first->request.status = FUSED_REQUEST_STATUS_TIMEOUT;
- abort_fused_io(ctx, &first->request);
- return false;
- }
-
- bool amdgpu_dm_execute_fused_io(
- struct amdgpu_device *dev,
- struct dc_link *link,
- union dmub_rb_cmd *commands,
- uint8_t count,
- uint32_t timeout_us)
- {
- struct amdgpu_display_manager *dm = &dev->dm;
-
- mutex_lock(&dm->dpia_aux_lock);
-
- const bool result = execute_fused_io(dev, link->ctx, commands, count, timeout_us);
-
- mutex_unlock(&dm->dpia_aux_lock);
- return result;
- }
-
- int amdgpu_dm_process_dmub_set_config_sync(
- struct dc_context *ctx,
- unsigned int link_index,
- struct set_config_cmd_payload *payload,
- enum set_config_status *operation_result)
- {
- struct amdgpu_device *adev = ctx->driver_context;
- bool is_cmd_complete;
- int ret;
-
- mutex_lock(&adev->dm.dpia_aux_lock);
- is_cmd_complete = dc_process_dmub_set_config_async(ctx->dc,
- link_index, payload, adev->dm.dmub_notify);
-
- if (is_cmd_complete || wait_for_completion_timeout(&adev->dm.dmub_aux_transfer_done, 10 * HZ)) {
- ret = 0;
- *operation_result = adev->dm.dmub_notify->sc_status;
- } else {
- drm_err(adev_to_drm(adev), "wait_for_completion_timeout timeout!");
- ret = -1;
- *operation_result = SET_CONFIG_UNKNOWN_ERROR;
- }
-
- if (!is_cmd_complete)
- reinit_completion(&adev->dm.dmub_aux_transfer_done);
- mutex_unlock(&adev->dm.dpia_aux_lock);
- return ret;
- }
-
- bool dm_execute_dmub_cmd(const struct dc_context *ctx, union dmub_rb_cmd *cmd, enum dm_dmub_wait_type wait_type)
- {
- struct amdgpu_device *adev = ctx->driver_context;
-
- guard(spinlock_irqsave)(&adev->dm.dmub_lock);
- return dc_dmub_srv_cmd_run(ctx->dmub_srv, cmd, wait_type);
- }
-
- bool dm_execute_dmub_cmd_list(const struct dc_context *ctx, unsigned int count, union dmub_rb_cmd *cmd, enum dm_dmub_wait_type wait_type)
- {
- struct amdgpu_device *adev = ctx->driver_context;
-
- guard(spinlock_irqsave)(&adev->dm.dmub_lock);
- return dc_dmub_srv_cmd_run_list(ctx->dmub_srv, count, cmd, wait_type);
- }
-
void dm_acpi_process_phy_transition_interlock(
const struct dc_context *ctx,
struct dm_process_phy_transition_init_params process_phy_transition_init_params)
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 48+ messages in thread* linux-next: manual merge of the drm tree with the drm-fixes tree
@ 2026-07-31 14:01 Mark Brown
0 siblings, 0 replies; 48+ messages in thread
From: Mark Brown @ 2026-07-31 14:01 UTC (permalink / raw)
To: Dave Airlie, DRI
Cc: Alex Deucher, Linux Kernel Mailing List, Linux Next Mailing List,
Srinivasan Shanmugam, Yang Wang
[-- Attachment #1: Type: text/plain, Size: 2381 bytes --]
Hi all,
Today's linux-next merge of the drm tree got a conflict in:
drivers/gpu/drm/amd/pm/amdgpu_dpm.c
between commits:
bb493058c35c8 ("drm/amd/pm: fix pptable use-after-free")
a65f5179d3f0c ("drm/amd/pm: hide pp_table sysfs on APUs")
from the drm-fixes tree and commit:
b0ae60ea3f3f1 ("drm/amdgpu: Resolve VM through DRM PASID ownership")
from the drm tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
diff --combined drivers/gpu/drm/amd/pm/amdgpu_dpm.c
index e5e89294958a8,8f59ea12fe0ea..0000000000000
--- a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c
+++ b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c
@@@ -1183,13 -1183,6 +1183,16 @@@ int amdgpu_dpm_dispatch_task(struct amd
return ret;
}
++<<<<<<< HEAD
+static bool amdgpu_dpm_is_pp_table_allowed(struct amdgpu_device *adev)
+{
+ return !amdgpu_sriov_vf(adev) &&
+ !(adev->flags & AMD_IS_APU) &&
+ !adev->scpm_enabled;
+}
+
++=======
++>>>>>>> drm/drm-next
int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char *table,
size_t size)
{
@@@ -1200,8 -1193,7 +1203,8 @@@
if ((!table && size) || (table && !size))
return -EINVAL;
- if (amdgpu_sriov_vf(adev) || !pp_funcs->get_pp_table || adev->scpm_enabled)
+ if (!amdgpu_dpm_is_pp_table_allowed(adev) ||
+ !pp_funcs->get_pp_table)
return -EOPNOTSUPP;
mutex_lock(&adev->pm.mutex);
@@@ -1725,8 -1717,7 +1728,8 @@@ int amdgpu_dpm_set_pp_table(struct amdg
if (!buf || !size)
return -EINVAL;
- if (amdgpu_sriov_vf(adev) || !pp_funcs->set_pp_table || adev->scpm_enabled)
+ if (!amdgpu_dpm_is_pp_table_allowed(adev) ||
+ !pp_funcs->set_pp_table)
return -EOPNOTSUPP;
mutex_lock(&adev->pm.mutex);
@@@ -2133,10 -2124,3 +2136,3 @@@ ssize_t amdgpu_dpm_get_xcp_metrics(stru
return ret;
}
-
- const struct ras_smu_drv *amdgpu_dpm_get_ras_smu_driver(struct amdgpu_device *adev)
- {
- void *pp_handle = adev->powerplay.pp_handle;
-
- return smu_get_ras_smu_driver(pp_handle);
- }
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 48+ messages in thread* linux-next: manual merge of the drm tree with the drm-fixes tree
@ 2026-07-26 21:24 Mark Brown
0 siblings, 0 replies; 48+ messages in thread
From: Mark Brown @ 2026-07-26 21:24 UTC (permalink / raw)
To: Dave Airlie, DRI
Cc: Alex Deucher, Leo Li, Linux Kernel Mailing List,
Linux Next Mailing List, Mario Limonciello, Matthew Schwartz
[-- Attachment #1: Type: text/plain, Size: 5056 bytes --]
Hi all,
Today's linux-next merge of the drm tree got a conflict in:
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
between commits:
8382cd234981a ("drm/amd/display: consolidate DCN vblank/flip handling onto vupdate_no_lock")
82730dba0cf9d ("drm/amd/display: Fix flip-done timeouts on mode1 reset")
from the drm-fixes tree and commit:
c87e6635d2db0 ("drm/amd/display: consolidate DCN vblank/flip handling onto vupdate_no_lock")
from the drm tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
diff --combined drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
index 95d3da3c4199b,d63688b9d93dd..0000000000000
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
@@@ -34,6 -34,7 +34,7 @@@
#include "amdgpu_dm_plane.h"
#include "amdgpu_dm_trace.h"
#include "amdgpu_dm_debugfs.h"
+ #include "amdgpu_dm_kunit_helpers.h"
#include "modules/inc/mod_power.h"
#define HPD_DETECTION_PERIOD_uS 2000000
@@@ -65,6 -66,7 +66,7 @@@ bool amdgpu_dm_crtc_modeset_required(st
{
return crtc_state->active && drm_atomic_crtc_needs_modeset(crtc_state);
}
+ EXPORT_IF_KUNIT(amdgpu_dm_crtc_modeset_required);
bool amdgpu_dm_crtc_vrr_active_irq(struct amdgpu_crtc *acrtc)
@@@ -74,6 -76,7 +76,7 @@@
acrtc->dm_irq_params.freesync_config.state ==
VRR_STATE_ACTIVE_FIXED;
}
+ EXPORT_IF_KUNIT(amdgpu_dm_crtc_vrr_active_irq);
int amdgpu_dm_crtc_set_vupdate_irq(struct drm_crtc *crtc, bool enable)
{
@@@ -93,12 -96,14 +96,14 @@@
acrtc->crtc_id, enable ? "en" : "dis", rc);
return rc;
}
+ EXPORT_IF_KUNIT(amdgpu_dm_crtc_set_vupdate_irq);
bool amdgpu_dm_crtc_vrr_active(const struct dm_crtc_state *dm_state)
{
return dm_state->freesync_config.state == VRR_STATE_ACTIVE_VARIABLE ||
dm_state->freesync_config.state == VRR_STATE_ACTIVE_FIXED;
}
+ EXPORT_IF_KUNIT(amdgpu_dm_crtc_vrr_active);
/**
* amdgpu_dm_crtc_set_static_screen_optimze() - Toggle static screen optimizations.
@@@ -156,6 -161,7 +161,7 @@@ bool amdgpu_dm_is_headless(struct amdgp
drm_connector_list_iter_end(&iter);
return is_headless;
}
+ EXPORT_IF_KUNIT(amdgpu_dm_is_headless);
static void amdgpu_dm_idle_worker(struct work_struct *work)
{
@@@ -207,6 -213,7 +213,7 @@@ struct idle_workqueue *idle_create_work
return idle_work;
}
+ EXPORT_IF_KUNIT(idle_create_workqueue);
static void amdgpu_dm_crtc_vblank_control_worker(struct work_struct *work)
{
@@@ -280,19 -287,10 +287,21 @@@ static inline int amdgpu_dm_crtc_set_vb
* is enabled. On DCE, vupdate is only needed in VRR mode.
*/
if (amdgpu_ip_version(adev, DCE_HWIP, 0) != 0) {
+ rc = amdgpu_dm_crtc_set_vupdate_irq(crtc, enable);
+ } else if (dc_supports_vrr(dm->dc->ctx->dce_version)) {
if (enable) {
- /* vblank irq on -> Only need vupdate irq in vrr mode */
+ rc = amdgpu_irq_get(adev, &adev->vupdate_irq, irq_type);
+ drm_dbg_vbl(crtc->dev, "Get vupdate_irq ret=%d\n", rc);
+ } else {
+ rc = amdgpu_irq_put(adev, &adev->vupdate_irq, irq_type);
+ drm_dbg_vbl(crtc->dev, "Put vupdate_irq ret=%d\n", rc);
+ }
+ } else if (dc_supports_vrr(dm->dc->ctx->dce_version)) {
+ if (enable) {
+ /* vblank irq on -> Only need vupdate irq in vrr mode
+ * Not ref-counted since we need explicit enable/disable
+ * for DCE VRR handling
+ */
if (amdgpu_dm_crtc_vrr_active(acrtc_state))
rc = amdgpu_dm_crtc_set_vupdate_irq(crtc, true);
} else {
@@@ -460,13 -458,13 +469,13 @@@ static void amdgpu_dm_crtc_reset_state(
{
struct dm_crtc_state *state;
+ state = kzalloc_obj(*state);
+ if (!state)
+ return;
+
if (crtc->state)
amdgpu_dm_crtc_destroy_state(crtc, crtc->state);
- state = kzalloc_obj(*state);
- if (WARN_ON(!state))
- return;
-
__drm_atomic_helper_crtc_reset(crtc, &state->base);
}
@@@ -618,12 -616,13 +627,13 @@@ static void amdgpu_dm_crtc_update_crtc_
amdgpu_dm_crtc_count_crtc_active_planes(new_crtc_state);
}
- static bool amdgpu_dm_crtc_helper_mode_fixup(struct drm_crtc *crtc,
- const struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode)
+ STATIC_IFN_KUNIT bool amdgpu_dm_crtc_helper_mode_fixup(struct drm_crtc *crtc,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
{
return true;
}
+ EXPORT_IF_KUNIT(amdgpu_dm_crtc_helper_mode_fixup);
static int amdgpu_dm_crtc_helper_atomic_check(struct drm_crtc *crtc,
struct drm_atomic_commit *state)
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 48+ messages in thread* linux-next: manual merge of the drm tree with the drm-fixes tree
@ 2026-07-17 14:41 Mark Brown
0 siblings, 0 replies; 48+ messages in thread
From: Mark Brown @ 2026-07-17 14:41 UTC (permalink / raw)
To: Dave Airlie, DRI
Cc: Alex Deucher, Ce Sun, Linux Kernel Mailing List,
Linux Next Mailing List, Mario Limonciello, Tvrtko Ursulin,
Yang Wang
[-- Attachment #1: Type: text/plain, Size: 4214 bytes --]
Hi all,
Today's linux-next merge of the drm tree got a conflict in:
drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
between commit:
6e4bc34568e38 ("drm/amd: Create a device link between APU display and XHCI devices")
from the drm-fixes tree and commits:
07c93d7eeb0d9 ("drm/amd: Create a device link between APU display and XHCI devices")
958430a1f068f ("drm/amd/pm: retire legacy smu ras driver framework")
from the drm tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
diff --combined drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
index e3a89e9a9df41,7ea7c4a5279be..0000000000000
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
@@@ -852,8 -852,6 +852,6 @@@ struct pptable_funcs
*/
int (*set_default_dpm_table)(struct smu_context *smu);
- int (*set_power_state)(struct smu_context *smu);
-
/**
* @populate_umd_state_clk: Populate the UMD power state table with
* defaults.
@@@ -906,16 -904,6 +904,6 @@@
struct
pp_clock_levels_with_latency
*clocks);
- /**
- * @get_clock_by_type_with_voltage: Get the speed and voltage of a clock
- * domain.
- */
- int (*get_clock_by_type_with_voltage)(struct smu_context *smu,
- enum amd_pp_clock_type type,
- struct
- pp_clock_levels_with_voltage
- *clocks);
-
/**
* @get_power_profile_mode: Print all power profile modes to
* buffer. Star current mode.
@@@ -1357,11 -1345,6 +1345,6 @@@
*/
int (*register_irq_handler)(struct smu_context *smu);
- /**
- * @set_azalia_d3_pme: Wake the audio decode engine from d3 sleep.
- */
- int (*set_azalia_d3_pme)(struct smu_context *smu);
-
/**
* @get_max_sustainable_clocks_by_dc: Get a copy of the max sustainable
* clock speeds table.
@@@ -1378,18 -1361,6 +1361,6 @@@
*/
int (*get_bamaco_support)(struct smu_context *smu);
- /**
- * @baco_get_state: Get the current BACO state.
- *
- * Return: Current BACO state.
- */
- enum smu_baco_state (*baco_get_state)(struct smu_context *smu);
-
- /**
- * @baco_set_state: Enter/exit BACO.
- */
- int (*baco_set_state)(struct smu_context *smu, enum smu_baco_state state);
-
/**
* @baco_enter: Enter BACO.
*/
@@@ -1651,12 -1622,6 +1622,6 @@@
int (*ras_send_msg)(struct smu_context *smu,
enum smu_message_type msg, uint32_t param, uint32_t *read_arg);
- /**
- * @get_ras_smu_drv: Get RAS smu driver interface
- * Return: ras_smu_drv *
- */
- int (*get_ras_smu_drv)(struct smu_context *smu, const struct ras_smu_drv **ras_smu_drv);
-
/**
* @set_power_dep: Create or destroy a power dependency link
* from an integrated xHCI controller to the GPU so that the GPU is
@@@ -1962,7 -1927,13 +1927,13 @@@ int smu_link_reset(struct smu_context *
extern const struct amd_ip_funcs smu_ip_funcs;
- bool is_support_sw_smu(struct amdgpu_device *adev);
+ void amdgpu_smu_early_init(struct amdgpu_device *adev);
+
+ static inline bool is_support_sw_smu(struct amdgpu_device *adev)
+ {
+ return adev->is_sw_smu;
+ }
+
bool is_support_cclk_dpm(struct amdgpu_device *adev);
int smu_write_watermarks_table(struct smu_context *smu);
@@@ -2005,7 -1976,6 +1976,6 @@@ int smu_set_pm_policy(struct smu_contex
int level);
ssize_t smu_get_pm_policy_info(struct smu_context *smu,
enum pp_pm_policy p_type, char *sysbuf);
- const struct ras_smu_drv *smu_get_ras_smu_driver(void *handle);
int amdgpu_smu_ras_send_msg(struct amdgpu_device *adev, enum smu_message_type msg,
uint32_t param, uint32_t *readarg);
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 48+ messages in thread* linux-next: manual merge of the drm tree with the drm-fixes tree
@ 2026-07-17 14:40 Mark Brown
0 siblings, 0 replies; 48+ messages in thread
From: Mark Brown @ 2026-07-17 14:40 UTC (permalink / raw)
To: Dave Airlie, DRI
Cc: Alex Deucher, Arnd Bergmann, Bhuvanachandra Pinninti,
Charlene Liu, Chenyu Chen, George Zhang, Honglei Huang,
James Lin, Leo Li, Linux Kernel Mailing List,
Linux Next Mailing List, Rafal Ostrowski, Tom Chung
[-- Attachment #1: Type: text/plain, Size: 38293 bytes --]
Hi all,
Today's linux-next merge of the drm tree got a conflict in:
drivers/gpu/drm/amd/display/dc/core/dc.c
between commit:
d73f4e118dc4c ("drm/amd/display: check GRPH_FLIP status before sending event")
from the drm-fixes tree and commits:
f64a9be565368 ("drm/amd/display: check GRPH_FLIP status before sending event")
72b94048020ce ("drm/amd/display: limit reuse dsc capable bootup timing")
98e08a815ef60 ("drm/amd/display: Tear down dangling pipe on boot to fix s0i3")
2a5810782369b ("drm/amd/display: program dither on all OPP heads under ODM combine")
f3403ab74a29f ("drm/amd/display: Add block sequence support for bandwidth programming operations")
991e0516a8072 ("drm/amd/display: use kvzalloc to allocate struct dc")
9e0896fa6f7db ("drm/amd/display: avoid large stack allocation in commit_planes_do_stream_update_sequence")
c1199393ec559 ("drm/amd/display: Refactor surface_update_flags to flat struct with helpers")
b008c67efb36b ("drm/amd/display: Introduce dc_plane_cm and migrate surface update color path")
from the drm tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
diff --combined drivers/gpu/drm/amd/display/dc/core/dc.c
index e25b94b65daca,8d77158229f1b..0000000000000
--- a/drivers/gpu/drm/amd/display/dc/core/dc.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc.c
@@@ -769,22 -769,23 +769,23 @@@ void dc_stream_set_dither_option(struc
{
struct bit_depth_reduction_params params;
struct dc_link *link = stream->link;
- struct pipe_ctx *pipes = NULL;
+ struct resource_context *res_ctx = &link->dc->current_state->res_ctx;
+ struct pipe_ctx *otg_master;
+ struct pipe_ctx *opp_heads[MAX_PIPES];
+ int opp_cnt;
int i;
- for (i = 0; i < MAX_PIPES; i++) {
- if (link->dc->current_state->res_ctx.pipe_ctx[i].stream ==
- stream) {
- pipes = &link->dc->current_state->res_ctx.pipe_ctx[i];
- break;
- }
- }
-
- if (!pipes)
+ otg_master = resource_get_otg_master_for_stream(res_ctx, stream);
+ if (!otg_master)
return;
if (option > DITHER_OPTION_MAX)
return;
+ opp_cnt = resource_get_opp_heads_for_otg_master(otg_master, res_ctx, opp_heads);
+
+ if (opp_cnt == 0)
+ return;
+
dc_exit_ips_for_hw_access(stream->ctx->dc);
stream->dither_option = option;
@@@ -793,16 -794,30 +794,30 @@@
resource_build_bit_depth_reduction_params(stream, ¶ms);
stream->bit_depth_params = params;
- if (pipes->plane_res.xfm &&
- pipes->plane_res.xfm->funcs->transform_set_pixel_storage_depth) {
- pipes->plane_res.xfm->funcs->transform_set_pixel_storage_depth(
- pipes->plane_res.xfm,
- pipes->plane_res.scl_data.lb_params.depth,
- &stream->bit_depth_params);
- }
+ /*
+ * Program bit-depth reduction (dither) on every OPP head of the
+ * stream. Under ODM combine there is more than one OPP head and they
+ * must all be kept in sync, otherwise (e.g. when CRC capture requests
+ * dither off) a secondary ODM segment can keep dither enabled and
+ * produce a different CRC than the primary segment.
+ */
+ for (i = 0; i < opp_cnt; i++) {
+ struct pipe_ctx *opp_head = opp_heads[i];
- pipes->stream_res.opp->funcs->
- opp_program_bit_depth_reduction(pipes->stream_res.opp, ¶ms);
+ if (opp_head->plane_res.xfm &&
+ opp_head->plane_res.xfm->funcs->transform_set_pixel_storage_depth) {
+ opp_head->plane_res.xfm->funcs->transform_set_pixel_storage_depth(
+ opp_head->plane_res.xfm,
+ opp_head->plane_res.scl_data.lb_params.depth,
+ &stream->bit_depth_params);
+ }
+
+ if (opp_head->stream_res.opp &&
+ opp_head->stream_res.opp->funcs->opp_program_bit_depth_reduction) {
+ opp_head->stream_res.opp->funcs->opp_program_bit_depth_reduction(
+ opp_head->stream_res.opp, ¶ms);
+ }
+ }
}
bool dc_stream_set_gamut_remap(struct dc *dc, const struct dc_stream_state *stream)
@@@ -1930,6 -1945,13 +1945,13 @@@ bool dc_validate_boot_timing(const stru
struct display_stream_compressor *dsc = NULL;
struct dcn_dsc_state dsc_state = {0};
+ if (dc->ctx->dce_version < DCN_VERSION_4_2) {
+ /*vbios enabled eDP dsc for one of DCN315 only but it has known issue,
+ since there is no production bios update, block it there*/
+ DC_LOG_DEBUG("boot timing validation failed due to unsupported DSC on this ASIC\n");
+ return false;
+ }
+
/* Find DSC associated with this timing generator */
if (tg_inst < (unsigned int)dc->res_pool->res_cap->num_dsc) {
dsc = dc->res_pool->dscs[tg_inst];
@@@ -2310,7 -2332,7 +2332,7 @@@ static enum dc_status dc_commit_state_n
for (i = 0; i < context->stream_count; i++) {
uint32_t prev_dsc_changed = context->streams[i]->update_flags.bits.dsc_changed;
- context->streams[i]->update_flags.raw = 0xFFFFFFFF;
+ stream_update_flags_set_full(&context->streams[i]->update_flags);
context->streams[i]->update_flags.bits.dsc_changed = prev_dsc_changed;
}
@@@ -2416,7 -2438,7 +2438,7 @@@
/* Clear update flags that were set earlier to avoid redundant programming */
for (i = 0; i < context->stream_count; i++) {
- context->streams[i]->update_flags.raw = 0x0;
+ stream_update_flags_clear(&context->streams[i]->update_flags);
}
old_state = dc->current_state;
@@@ -2764,7 -2786,7 +2786,7 @@@ static bool is_surface_in_context
static struct surface_update_descriptor get_plane_info_update_type(const struct dc_surface_update *u)
{
- union surface_update_flags *update_flags = &u->surface->update_flags;
+ struct pipe_update_bits *update_bits = &u->surface->update_bits;
struct surface_update_descriptor update_type = { UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_NONE };
if (!u->plane_info)
@@@ -2774,37 -2796,37 +2796,37 @@@
elevate_update_type(&update_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM);
if (u->plane_info->color_space != u->surface->color_space) {
- update_flags->bits.color_space_change = 1;
+ update_bits->color_space_change = 1;
elevate_update_type(&update_type, UPDATE_TYPE_MED, LOCK_DESCRIPTOR_STREAM);
}
if (u->plane_info->horizontal_mirror != u->surface->horizontal_mirror) {
- update_flags->bits.horizontal_mirror_change = 1;
+ update_bits->horizontal_mirror_change = 1;
elevate_update_type(&update_type, UPDATE_TYPE_MED, LOCK_DESCRIPTOR_STREAM);
}
if (u->plane_info->rotation != u->surface->rotation) {
- update_flags->bits.rotation_change = 1;
+ update_bits->rotation_change = 1;
elevate_update_type(&update_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
}
if (u->plane_info->format != u->surface->format) {
- update_flags->bits.pixel_format_change = 1;
+ update_bits->pixel_format_change = 1;
elevate_update_type(&update_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
}
if (u->plane_info->stereo_format != u->surface->stereo_format) {
- update_flags->bits.stereo_format_change = 1;
+ update_bits->stereo_format_change = 1;
elevate_update_type(&update_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
}
if (u->plane_info->per_pixel_alpha != u->surface->per_pixel_alpha) {
- update_flags->bits.per_pixel_alpha_change = 1;
+ update_bits->per_pixel_alpha_change = 1;
elevate_update_type(&update_type, UPDATE_TYPE_MED, LOCK_DESCRIPTOR_STREAM);
}
if (u->plane_info->global_alpha_value != u->surface->global_alpha_value) {
- update_flags->bits.global_alpha_change = 1;
+ update_bits->global_alpha_change = 1;
elevate_update_type(&update_type, UPDATE_TYPE_MED, LOCK_DESCRIPTOR_STREAM);
}
@@@ -2816,7 -2838,7 +2838,7 @@@
* stutter period calculation. Triggering a full update will
* recalculate stutter period.
*/
- update_flags->bits.dcc_change = 1;
+ update_bits->dcc_change = 1;
elevate_update_type(&update_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
}
@@@ -2825,25 -2847,25 +2847,25 @@@
/* different bytes per element will require full bandwidth
* and DML calculation
*/
- update_flags->bits.bpp_change = 1;
+ update_bits->bpp_change = 1;
elevate_update_type(&update_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
}
if (u->plane_info->plane_size.surface_pitch != u->surface->plane_size.surface_pitch
|| u->plane_info->plane_size.chroma_pitch != u->surface->plane_size.chroma_pitch) {
- update_flags->bits.plane_size_change = 1;
+ update_bits->plane_size_change = 1;
elevate_update_type(&update_type, UPDATE_TYPE_MED, LOCK_DESCRIPTOR_STREAM);
}
const struct dc_tiling_info *tiling = &u->plane_info->tiling_info;
if (memcmp(tiling, &u->surface->tiling_info, sizeof(*tiling)) != 0) {
- update_flags->bits.swizzle_change = 1;
+ update_bits->swizzle_change = 1;
if (tiling->flags.avoid_full_update_on_tiling_change) {
elevate_update_type(&update_type, UPDATE_TYPE_MED, LOCK_DESCRIPTOR_STREAM);
} else {
- update_flags->bits.bandwidth_change = 1;
+ update_bits->bandwidth_change = 1;
elevate_update_type(&update_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
}
}
@@@ -2853,10 -2875,10 +2875,10 @@@
}
static struct surface_update_descriptor get_scaling_info_update_type(
- const struct dc_check_config *check_config,
- const struct dc_surface_update *u)
+ const struct dc_check_config *check_config,
+ const struct dc_surface_update *u)
{
- union surface_update_flags *update_flags = &u->surface->update_flags;
+ struct pipe_update_bits *update_bits = &u->surface->update_bits;
struct surface_update_descriptor update_type = { UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_NONE };
if (!u->scaling_info)
@@@ -2873,26 -2895,26 +2895,26 @@@
|| u->scaling_info->clip_rect.height != u->surface->clip_rect.height
|| u->scaling_info->scaling_quality.integer_scaling !=
u->surface->scaling_quality.integer_scaling) {
- update_flags->bits.scaling_change = 1;
+ update_bits->scaling_change = 1;
elevate_update_type(&update_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
if (u->scaling_info->src_rect.width > u->surface->src_rect.width
|| u->scaling_info->src_rect.height > u->surface->src_rect.height)
/* Making src rect bigger requires a bandwidth change */
- update_flags->bits.clock_change = 1;
+ update_bits->clock_change = 1;
if ((u->scaling_info->dst_rect.width < u->surface->dst_rect.width
|| u->scaling_info->dst_rect.height < u->surface->dst_rect.height)
&& (u->scaling_info->dst_rect.width < u->surface->src_rect.width
|| u->scaling_info->dst_rect.height < u->surface->src_rect.height))
/* Making dst rect smaller requires a bandwidth change */
- update_flags->bits.bandwidth_change = 1;
+ update_bits->bandwidth_change = 1;
if (u->scaling_info->src_rect.width > (int)check_config->max_optimizable_video_width &&
(u->scaling_info->clip_rect.width > u->surface->clip_rect.width ||
u->scaling_info->clip_rect.height > u->surface->clip_rect.height))
/* Changing clip size of a large surface may result in MPC slice count change */
- update_flags->bits.bandwidth_change = 1;
+ update_bits->bandwidth_change = 1;
}
if (u->scaling_info->src_rect.x != u->surface->src_rect.x
@@@ -2902,7 -2924,7 +2924,7 @@@
|| u->scaling_info->dst_rect.x != u->surface->dst_rect.x
|| u->scaling_info->dst_rect.y != u->surface->dst_rect.y) {
elevate_update_type(&update_type, UPDATE_TYPE_MED, LOCK_DESCRIPTOR_STREAM);
- update_flags->bits.position_change = 1;
+ update_bits->position_change = 1;
}
return update_type;
@@@ -2913,15 -2935,15 +2935,15 @@@ static struct surface_update_descripto
struct dc_surface_update *u)
{
struct surface_update_descriptor overall_type = { UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_NONE };
- union surface_update_flags *update_flags = &u->surface->update_flags;
+ struct pipe_update_bits *update_bits = &u->surface->update_bits;
if (u->surface->force_full_update) {
- update_flags->raw = 0xFFFFFFFF;
+ dc_pipe_update_bits_set_full(update_bits);
elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
return overall_type;
}
- update_flags->raw = 0; // Reset all flags
+ dc_pipe_update_bits_clear(update_bits);
struct surface_update_descriptor inner_type = get_plane_info_update_type(u);
@@@ -2931,87 -2953,112 +2953,112 @@@
elevate_update_type(&overall_type, inner_type.update_type, inner_type.lock_descriptor);
if (u->flip_addr) {
- update_flags->bits.addr_update = 1;
+ update_bits->addr_update = 1;
elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM);
if (u->flip_addr->address.tmz_surface != u->surface->address.tmz_surface) {
- update_flags->bits.tmz_changed = 1;
+ update_bits->tmz_changed = 1;
elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
}
}
if (u->in_transfer_func) {
- update_flags->bits.in_transfer_func_change = 1;
+ update_bits->in_transfer_func_change = 1;
elevate_update_type(&overall_type, UPDATE_TYPE_MED, LOCK_DESCRIPTOR_STREAM);
}
if (u->input_csc_color_matrix) {
- update_flags->bits.input_csc_change = 1;
+ update_bits->input_csc_change = 1;
elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM);
}
if (u->cursor_csc_color_matrix) {
- update_flags->bits.cursor_csc_color_matrix_change = 1;
+ update_bits->cursor_csc_color_matrix_change = 1;
elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM);
}
if (u->coeff_reduction_factor) {
- update_flags->bits.coeff_reduction_change = 1;
+ update_bits->coeff_reduction_change = 1;
elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM);
}
if (u->gamut_remap_matrix) {
- update_flags->bits.gamut_remap_change = 1;
+ update_bits->gamut_remap_change = 1;
elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM);
}
- if (u->blend_tf || (u->gamma && dce_use_lut(u->plane_info ? u->plane_info->format : u->surface->format))) {
- update_flags->bits.gamma_change = 1;
+ if ((u->cm && u->cm->flags.bits.blend_enable) ||
+ (u->gamma && dce_use_lut(u->plane_info ? u->plane_info->format : u->surface->format))) {
+ update_bits->gamma_change = 1;
elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM);
}
- if (u->lut3d_func || u->func_shaper) {
- update_flags->bits.lut_3d = 1;
+ if (u->cm && (u->cm->flags.bits.lut3d_enable || u->cm->flags.bits.shaper_enable)) {
+ update_bits->lut_3d = 1;
elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM);
}
+ if (u->cm && u->cm->flags.bits.lut3d_dma_enable != u->surface->cm.flags.bits.lut3d_dma_enable &&
+ u->cm->flags.bits.lut3d_enable && u->surface->cm.flags.bits.lut3d_enable) {
+ /* Toggling 3DLUT loading between DMA and Host is illegal */
+ BREAK_TO_DEBUGGER();
+ }
+
+ if (u->cm && u->cm->flags.bits.lut3d_enable && !u->cm->flags.bits.lut3d_dma_enable) {
+ /* Host loading 3DLUT requires full update but only stream lock */
+ elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_STREAM);
+ }
+
if (u->hdr_mult.value)
if (u->hdr_mult.value != u->surface->hdr_mult.value) {
// TODO: Should be fast?
- update_flags->bits.hdr_mult = 1;
+ update_bits->hdr_mult = 1;
elevate_update_type(&overall_type, UPDATE_TYPE_MED, LOCK_DESCRIPTOR_STREAM);
}
if (u->sdr_white_level_nits)
if (u->sdr_white_level_nits != u->surface->sdr_white_level_nits) {
// TODO: Should be fast?
- update_flags->bits.sdr_white_level_nits = 1;
+ update_bits->sdr_white_level_nits = 1;
elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
}
if (u->cm_hist_control) {
- update_flags->bits.cm_hist_change = 1;
+ update_bits->cm_hist_change = 1;
elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM);
}
- if (u->cm2_params) {
- if (u->cm2_params->component_settings.shaper_3dlut_setting != u->surface->mcm_shaper_3dlut_setting
- || u->cm2_params->component_settings.lut1d_enable != u->surface->mcm_lut1d_enable
- || u->cm2_params->cm2_luts.lut3d_data.lut3d_src != u->surface->mcm_luts.lut3d_data.lut3d_src) {
- update_flags->bits.mcm_transfer_function_enable_change = 1;
+
+ if (u->cm) {
+ const union dc_plane_cm_flags blend_only_flags = {
+ .bits = {
+ .blend_enable = 1,
+ }
+ };
+
+ if (u->cm->flags.bits.shaper_enable != u->surface->cm.flags.bits.shaper_enable
+ || u->cm->flags.bits.blend_enable != u->surface->cm.flags.bits.blend_enable
+ || u->cm->flags.bits.lut3d_enable != u->surface->cm.flags.bits.lut3d_enable
+ || u->cm->flags.bits.lut3d_dma_enable != u->surface->cm.flags.bits.lut3d_dma_enable) {
+ update_bits->mcm_transfer_function_enable_change = 1;
+ elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
+ }
+
+ if ((u->cm->flags.all != blend_only_flags.all && u->cm->flags.all != 0) ||
+ (u->surface->cm.flags.all != blend_only_flags.all && u->surface->cm.flags.all != 0)) {
elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
}
}
- if (update_flags->bits.lut_3d &&
- u->surface->mcm_luts.lut3d_data.lut3d_src != DC_CM2_TRANSFER_FUNC_SOURCE_VIDMEM) {
+ if (update_bits->lut_3d &&
+ !u->surface->cm.flags.bits.lut3d_dma_enable) {
elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
}
if (check_config->enable_legacy_fast_update &&
- (update_flags->bits.gamma_change ||
- update_flags->bits.gamut_remap_change ||
- update_flags->bits.input_csc_change ||
- update_flags->bits.cm_hist_change ||
- update_flags->bits.coeff_reduction_change)) {
+ (update_bits->gamma_change ||
+ update_bits->gamut_remap_change ||
+ update_bits->input_csc_change ||
+ update_bits->cm_hist_change ||
+ update_bits->coeff_reduction_change)) {
elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
}
return overall_type;
@@@ -3036,7 -3083,7 +3083,7 @@@ static void force_immediate_gsl_plane_f
if (has_flip_immediate_plane && surface_count > 1) {
for (i = 0; i < surface_count; i++) {
if (updates[i].surface->flip_immediate)
- updates[i].surface->update_flags.bits.addr_update = 1;
+ updates[i].surface->update_bits.addr_update = 1;
}
}
}
@@@ -3191,9 -3238,9 +3238,9 @@@ struct surface_update_descriptor dc_che
struct dc_stream_update *stream_update)
{
if (stream_update)
- stream_update->stream->update_flags.raw = 0;
+ stream_update_flags_clear(&stream_update->stream->update_flags);
for (int i = 0; i < surface_count; i++)
- updates[i].surface->update_flags.raw = 0;
+ dc_pipe_update_bits_clear(&updates[i].surface->update_bits);
return check_update_surfaces_for_stream(check_config, updates, surface_count, stream_update);
}
@@@ -3304,24 -3351,55 +3351,55 @@@ static void copy_surface_update_to_plan
sizeof(struct dc_transfer_func_distributed_points));
}
- if (srf_update->cm2_params) {
- surface->mcm_shaper_3dlut_setting = srf_update->cm2_params->component_settings.shaper_3dlut_setting;
- surface->mcm_lut1d_enable = srf_update->cm2_params->component_settings.lut1d_enable;
- surface->mcm_luts = srf_update->cm2_params->cm2_luts;
+ /* Shaper, 3DLUT, 1DLUT */
+ if (srf_update->cm) {
+ struct kref refcount = surface->cm.refcount;
+
+ memcpy(&surface->cm, srf_update->cm, sizeof(surface->cm));
+ surface->cm.refcount = refcount;
+
+ #ifndef TRIM_CM2
+ /* Populate mcm_luts from cm for legacy consumers (dml2, hwseq) */
+ surface->mcm_luts.lut1d_func = &surface->cm.blend_func;
+ surface->mcm_luts.shaper = &surface->cm.shaper_func;
+ if (srf_update->cm->flags.bits.lut3d_dma_enable) {
+ surface->mcm_luts.lut3d_data.lut3d_src = DC_CM2_TRANSFER_FUNC_SOURCE_VIDMEM;
+ surface->mcm_luts.lut3d_data.gpu_mem_params.addr = surface->cm.lut3d_dma.addr;
+ surface->mcm_luts.lut3d_data.gpu_mem_params.layout =
+ (surface->cm.lut3d_dma.swizzle == CM_LUT_3D_SWIZZLE_LINEAR_RGB) ?
+ DC_CM2_GPU_MEM_LAYOUT_3D_SWIZZLE_LINEAR_RGB :
+ (surface->cm.lut3d_dma.swizzle == CM_LUT_3D_SWIZZLE_LINEAR_BGR) ?
+ DC_CM2_GPU_MEM_LAYOUT_3D_SWIZZLE_LINEAR_BGR :
+ DC_CM2_GPU_MEM_LAYOUT_1D_PACKED_LINEAR;
+ surface->mcm_luts.lut3d_data.gpu_mem_params.format_params.format =
+ (surface->cm.lut3d_dma.format == CM_LUT_PIXEL_FORMAT_RGBA16161616_UNORM_12MSB) ?
+ DC_CM2_GPU_MEM_FORMAT_16161616_UNORM_12MSB :
+ (surface->cm.lut3d_dma.format == CM_LUT_PIXEL_FORMAT_RGBA16161616_UNORM_12LSB) ?
+ DC_CM2_GPU_MEM_FORMAT_16161616_UNORM_12LSB :
+ DC_CM2_GPU_MEM_FORMAT_16161616_FLOAT_FP1_5_10;
+ surface->mcm_luts.lut3d_data.gpu_mem_params.format_params.float_params.bias =
+ surface->cm.lut3d_dma.bias;
+ surface->mcm_luts.lut3d_data.gpu_mem_params.format_params.float_params.scale =
+ surface->cm.lut3d_dma.scale;
+ surface->mcm_luts.lut3d_data.gpu_mem_params.component_order =
+ DC_CM2_GPU_MEM_PIXEL_COMPONENT_ORDER_RGBA;
+ surface->mcm_luts.lut3d_data.gpu_mem_params.size = DC_CM2_GPU_MEM_SIZE_TRANSFORMED;
+ surface->mcm_luts.lut3d_data.mpc_3dlut_enable = (srf_update->cm->flags.bits.lut3d_enable != 0);
+ } else {
+ surface->mcm_luts.lut3d_data.lut3d_src = DC_CM2_TRANSFER_FUNC_SOURCE_SYSMEM;
+ surface->mcm_luts.lut3d_data.lut3d_func = &surface->cm.lut3d_func;
+ }
+
+ if (srf_update->cm->flags.bits.shaper_enable &&
+ srf_update->cm->flags.bits.lut3d_enable)
+ surface->mcm_shaper_3dlut_setting = DC_CM2_SHAPER_3DLUT_SETTING_ENABLE_SHAPER_3DLUT;
+ else if (srf_update->cm->flags.bits.shaper_enable)
+ surface->mcm_shaper_3dlut_setting = DC_CM2_SHAPER_3DLUT_SETTING_ENABLE_SHAPER;
+ else
+ surface->mcm_shaper_3dlut_setting = DC_CM2_SHAPER_3DLUT_SETTING_BYPASS_ALL;
+ #endif /* TRIM_CM2 */
}
- if (srf_update->func_shaper) {
- memcpy(&surface->in_shaper_func, srf_update->func_shaper,
- sizeof(surface->in_shaper_func));
-
- if (surface->mcm_shaper_3dlut_setting >= DC_CM2_SHAPER_3DLUT_SETTING_ENABLE_SHAPER)
- surface->mcm_luts.shaper = &surface->in_shaper_func;
- }
-
- if (srf_update->lut3d_func)
- memcpy(&surface->lut3d_func, srf_update->lut3d_func,
- sizeof(surface->lut3d_func));
-
if (srf_update->hdr_mult.value)
surface->hdr_mult =
srf_update->hdr_mult;
@@@ -3330,15 -3408,10 +3408,10 @@@
surface->sdr_white_level_nits =
srf_update->sdr_white_level_nits;
- if (srf_update->blend_tf) {
- memcpy(&surface->blend_tf, srf_update->blend_tf,
- sizeof(surface->blend_tf));
-
- if (surface->mcm_lut1d_enable)
- surface->mcm_luts.lut1d_func = &surface->blend_tf;
- }
-
- if (srf_update->cm2_params || srf_update->blend_tf)
+ if (srf_update->cm &&
+ (srf_update->cm->flags.bits.blend_enable ||
+ srf_update->cm->flags.bits.shaper_enable ||
+ srf_update->cm->flags.bits.lut3d_enable))
surface->lut_bank_a = !surface->lut_bank_a;
if (srf_update->input_csc_color_matrix)
@@@ -3714,11 -3787,11 +3787,11 @@@ static bool update_planes_and_stream_st
if (update_type == UPDATE_TYPE_FULL) {
if (stream_update) {
uint32_t dsc_changed = stream_update->stream->update_flags.bits.dsc_changed;
- stream_update->stream->update_flags.raw = 0xFFFFFFFF;
+ stream_update_flags_set_full(&stream_update->stream->update_flags);
stream_update->stream->update_flags.bits.dsc_changed = dsc_changed;
}
for (i = 0; i < surface_count; i++)
- srf_updates[i].surface->update_flags.raw = 0xFFFFFFFF;
+ dc_pipe_update_bits_set_full(&srf_updates[i].surface->update_bits);
}
if (update_type >= update_surface_trace_level)
@@@ -3767,7 -3840,7 +3840,7 @@@
if (update_type != UPDATE_TYPE_MED)
continue;
- if (surface->update_flags.bits.position_change) {
+ if (surface->update_bits.position_change) {
for (j = 0; j < dc->res_pool->pipe_count; j++) {
struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[j];
@@@ -4205,8 -4278,8 +4278,8 @@@ static void commit_planes_do_stream_upd
hwss_add_dc_set_optimized_required(&seq_state, dc, true);
} else {
- if (get_seamless_boot_stream_count(context) == 0)
- hwss_add_prepare_bandwidth(&seq_state, dc, dc->current_state);
+ if (get_seamless_boot_stream_count(context) == 0 && dc->hwss.prepare_bandwidth_sequence)
+ dc->hwss.prepare_bandwidth_sequence(dc, dc->current_state, &seq_state);
hwss_add_link_set_dpms_on(&seq_state, dc->current_state, dpms_pipe_ctx);
}
} else if (pipe_ctx->stream->link->wa_flags.blank_stream_on_ocs_change && stream_update->output_color_space
@@@ -4550,14 -4623,23 +4623,23 @@@ static void build_dmub_update_dirty_rec
}
}
- static bool check_address_only_update(union surface_update_flags update_flags)
+ /**
+ * dc_check_address_only_update - Check if addr_update is the sole flag set
+ *
+ * @update_bits: The pipe update bits to check
+ *
+ * Determines whether an update contains only an address change with no other
+ * pending updates.
+ *
+ * Return: %true if addr_update is the sole bit set, %false otherwise.
+ */
+ bool dc_check_address_only_update(struct pipe_update_bits update_bits)
{
- union surface_update_flags addr_only_update_flags;
- addr_only_update_flags.raw = 0;
- addr_only_update_flags.bits.addr_update = 1;
+ struct pipe_update_bits check = update_bits; /* 1. Copy all flags from input */
- return update_flags.bits.addr_update &&
- !(update_flags.raw & ~addr_only_update_flags.raw);
+ check.addr_update = 0; /* 2. Zero the addr_update bit in the copy */
+ return update_bits.addr_update && /* 3. Check addr_update was set in original */
+ !dc_pipe_update_bits_is_any_set(&check); /* 4. Check no other bits remain in the copy */
}
/**
@@@ -4617,7 -4699,7 +4699,7 @@@ static void commit_plane_for_stream_off
continue;
/* update pipe context for plane */
- if (pipe_ctx->plane_state->update_flags.bits.addr_update)
+ if (pipe_ctx->plane_state->update_bits.addr_update)
dc->hwss.update_plane_addr(dc, pipe_ctx);
}
}
@@@ -4655,8 -4737,8 +4737,8 @@@ static void commit_planes_for_stream_fa
should_offload_fams2_flip = true;
for (i = 0; i < surface_count; i++) {
if (srf_updates[i].surface &&
- srf_updates[i].surface->update_flags.raw &&
- !check_address_only_update(srf_updates[i].surface->update_flags)) {
+ dc_pipe_update_bits_is_any_set(&srf_updates[i].surface->update_bits) &&
+ !dc_check_address_only_update(srf_updates[i].surface->update_bits)) {
/* more than address update, need to acquire FAMS2 lock */
should_offload_fams2_flip = false;
break;
@@@ -4747,7 -4829,7 +4829,7 @@@
* so no need to clear here.
*/
if (top_pipe_to_program->stream)
- top_pipe_to_program->stream->update_flags.raw = 0;
+ stream_update_flags_clear(&top_pipe_to_program->stream->update_flags);
}
static void commit_planes_for_stream(struct dc *dc,
@@@ -5073,11 -5155,9 +5155,9 @@@
if (!should_update_pipe_for_plane(context, pipe_ctx, plane_state))
continue;
- if (srf_updates[i].cm2_params &&
- srf_updates[i].cm2_params->cm2_luts.lut3d_data.lut3d_src ==
- DC_CM2_TRANSFER_FUNC_SOURCE_VIDMEM &&
- srf_updates[i].cm2_params->component_settings.shaper_3dlut_setting ==
- DC_CM2_SHAPER_3DLUT_SETTING_ENABLE_SHAPER_3DLUT &&
+ if (srf_updates[i].cm &&
+ srf_updates[i].cm->flags.bits.lut3d_enable &&
+ srf_updates[i].cm->flags.bits.lut3d_dma_enable &&
dc->hwss.trigger_3dlut_dma_load)
dc->hwss.trigger_3dlut_dma_load(dc, pipe_ctx);
@@@ -5087,7 -5167,7 +5167,7 @@@
dc->hwss.program_triplebuffer(
dc, pipe_ctx, pipe_ctx->plane_state->triplebuffer_flips);
}
- if (pipe_ctx->plane_state->update_flags.bits.addr_update)
+ if (pipe_ctx->plane_state->update_bits.addr_update)
dc->hwss.update_plane_addr(dc, pipe_ctx);
}
}
@@@ -5178,7 -5258,7 +5258,7 @@@
if (pipe_ctx->bottom_pipe || pipe_ctx->next_odm_pipe ||
!pipe_ctx->stream || !should_update_pipe_for_stream(context, pipe_ctx, stream) ||
- !pipe_ctx->plane_state->update_flags.bits.addr_update ||
+ !pipe_ctx->plane_state->update_bits.addr_update ||
pipe_ctx->plane_state->skip_manual_trigger)
continue;
@@@ -5617,7 -5697,7 +5697,7 @@@ static bool commit_minimal_transition_s
/* force full surface update */
for (i = 0; i < dc->current_state->stream_count; i++) {
for (j = 0; j < (unsigned int)dc->current_state->stream_status[i].plane_count; j++) {
- dc->current_state->stream_status[i].plane_states[j]->update_flags.raw = 0xFFFFFFFF;
+ dc_pipe_update_bits_set_full(&dc->current_state->stream_status[i].plane_states[j]->update_bits);
}
}
@@@ -5792,14 -5872,9 +5872,9 @@@ static bool full_update_required
(srf_updates[i].sdr_white_level_nits &&
srf_updates[i].sdr_white_level_nits != srf_updates->surface->sdr_white_level_nits) ||
srf_updates[i].in_transfer_func ||
- srf_updates[i].func_shaper ||
- srf_updates[i].lut3d_func ||
srf_updates[i].surface->force_full_update ||
(srf_updates[i].flip_addr &&
- srf_updates[i].flip_addr->address.tmz_surface != srf_updates[i].surface->address.tmz_surface) ||
- (srf_updates[i].cm2_params &&
- (srf_updates[i].cm2_params->component_settings.shaper_3dlut_setting != srf_updates[i].surface->mcm_shaper_3dlut_setting ||
- srf_updates[i].cm2_params->component_settings.lut1d_enable != srf_updates[i].surface->mcm_lut1d_enable))))
+ srf_updates[i].flip_addr->address.tmz_surface != srf_updates[i].surface->address.tmz_surface)))
return true;
}
@@@ -6063,17 -6138,17 +6138,17 @@@ static bool update_planes_and_stream_v3
return true;
}
- static void clear_update_flags(struct dc_surface_update *srf_updates,
+ static void clear_update_bits(struct dc_surface_update *srf_updates,
int surface_count, struct dc_stream_state *stream)
{
int i;
if (stream)
- stream->update_flags.raw = 0;
+ stream_update_flags_clear(&stream->update_flags);
for (i = 0; i < surface_count; i++)
if (srf_updates[i].surface)
- srf_updates[i].surface->update_flags.raw = 0;
+ dc_pipe_update_bits_clear(&srf_updates[i].surface->update_bits);
}
bool dc_update_planes_and_stream(struct dc *dc,
@@@ -6125,7 -6200,7 +6200,7 @@@ void dc_commit_updates_for_stream(struc
}
if (ret && dc->ctx->dce_version >= DCN_VERSION_3_2)
- clear_update_flags(srf_updates, surface_count, stream);
+ clear_update_bits(srf_updates, surface_count, stream);
}
uint8_t dc_get_current_stream_count(struct dc *dc)
@@@ -6165,6 -6240,115 +6240,6 @@@ void dc_interrupt_ack(struct dc *dc, en
dal_irq_service_ack(dc->res_pool->irqs, src);
}
-/* Preserve this tg if a physical link is still lighting a present display */
-static bool should_preserve_tg(struct dc *dc, struct timing_generator *tg)
-{
- unsigned int i, j;
-
- /* Check if a physical link is lighting this tg */
- for (i = 0; i < dc->link_count; i++) {
- struct dc_link *link = dc->links[i];
- int fe;
-
- if (!link || link->ep_type != DISPLAY_ENDPOINT_PHY ||
- !link->link_enc ||
- !link->link_enc->funcs->is_dig_enabled ||
- !link->link_enc->funcs->is_dig_enabled(link->link_enc) ||
- !link->link_enc->funcs->get_dig_frontend)
- continue;
-
- /* Get the DIG front-end this link's encoder drives; skip if none */
- fe = link->link_enc->funcs->get_dig_frontend(link->link_enc);
- if (fe == ENGINE_ID_UNKNOWN)
- continue;
-
- /* Find the stream encoder bound to this link's front-end */
- for (j = 0; j < dc->res_pool->stream_enc_count; j++) {
- struct stream_encoder *se = dc->res_pool->stream_enc[j];
-
- /* Skip unless this stream encoder feeds our front-end and drives this tg */
- if (se->id != fe || !se->funcs->dig_source_otg ||
- (int)se->funcs->dig_source_otg(se) != tg->inst)
- continue;
-
- /* This link drives the OTG: keep a seamless-boot eDP, or
- * any external link whose sink is still connected.
- */
- if (link->connector_signal == SIGNAL_TYPE_EDP)
- return true;
- if (link->link_enc->funcs->get_hpd_state &&
- dc->link_srv->get_hpd_state(link))
- return true;
- }
- }
-
- return false;
-}
-
-/*
- * GOP/vBIOS may leave an OPTC enabled for a display present at power-on but no
- * longer driven (e.g. external DP unplugged at boot). Such a dangling pipe keeps
- * DCN out of idle and blocks s0i3. If nothing needs to survive (no committed
- * stream or seamless-boot eDP) and no sink is still connected, power down all hw
- * blocks.
- */
-void dc_disable_dangling_timing_generators(struct dc *dc)
-{
- struct dce_hwseq *hws = dc->hwseq;
- bool any_dangling = false;
- bool any_preserved = false;
- bool any_connected = false;
- unsigned int i;
-
- /* No real hw to touch on a virtual/emulated environment */
- if (dc->ctx->dce_environment == DCE_ENV_VIRTUAL_HW)
- return;
-
- /* Wake hw out of IPS before reading/touching tg state */
- dc_exit_ips_for_hw_access(dc);
-
- /* Classify every enabled tg as either to-preserve or dangling */
- for (i = 0; i < dc->res_pool->timing_generator_count; i++) {
- struct timing_generator *tg = dc->res_pool->timing_generators[i];
-
- if (!tg || !tg->funcs->is_tg_enabled ||
- !tg->funcs->is_tg_enabled(tg))
- continue;
-
- if (should_preserve_tg(dc, tg))
- any_preserved = true;
- else
- any_dangling = true;
- }
-
- /* A physically connected sink (HPD asserted) will be re-lit by a
- * subsequent atomic commit. For that case we don't call the global
- * power_down().
- */
- for (i = 0; i < dc->link_count; i++) {
- struct dc_link *link = dc->links[i];
-
- if (link && link->ep_type == DISPLAY_ENDPOINT_PHY &&
- link->link_enc && link->link_enc->funcs &&
- link->link_enc->funcs->get_hpd_state &&
- dc->link_srv->get_hpd_state(link)) {
- any_connected = true;
- break;
- }
- }
-
- if (!any_dangling)
- return;
-
- if (!any_preserved && !any_connected && hws && hws->funcs.power_down) {
- /* Truly headless / all sinks unplugged: nothing to preserve */
- DC_LOG_DC("%s: powering down dangling hw blocks to allow idle\n",
- __func__);
- hws->funcs.power_down(dc);
- return;
- }
-}
-
/*
* dc_get_flip_pending_on_otg() - Check if a GRPH_FLIP is still pending on OTG
*
@@@ -6183,7 -6367,7 +6258,7 @@@
bool dc_get_flip_pending_on_otg(struct dc *dc, int otg_inst)
{
bool flip_pending = false;
- int i;
+ unsigned int i;
if (!dc || !dc->current_state)
return false;
@@@ -7587,7 -7771,7 +7662,7 @@@ bool dc_capture_register_software_state
struct dc_plane_state *plane_state = pipe_ctx->plane_state;
/* MPCC blending tree and mode control - capture actual blend configuration */
- state->mpc.mpcc_mode[i] = (plane_state->blend_tf.type != TF_TYPE_BYPASS) ? 1 : 0;
+ state->mpc.mpcc_mode[i] = (plane_state->cm.blend_func.type != TF_TYPE_BYPASS) ? 1 : 0;
state->mpc.mpcc_alpha_blend_mode[i] = plane_state->per_pixel_alpha ? 1 : 0;
state->mpc.mpcc_alpha_multiplied_mode[i] = plane_state->pre_multiplied_alpha ? 1 : 0;
state->mpc.mpcc_blnd_active_overlap_only[i] = 0; /* Default - no overlap restriction */
@@@ -7920,7 -8104,7 +7995,7 @@@ struct dc_update_scratch_space
struct dc_stream_state *stream;
struct dc_stream_update *stream_update;
bool update_v3;
- bool do_clear_update_flags;
+ bool do_clear_update_bits;
enum surface_update_type update_type;
struct dc_state *new_context;
enum update_v3_flow flow;
@@@ -7963,8 -8147,8 +8038,8 @@@ static bool update_planes_and_stream_cl
const struct dc_update_scratch_space *scratch
)
{
- if (scratch->do_clear_update_flags)
- clear_update_flags(scratch->surface_updates, scratch->surface_count, scratch->stream);
+ if (scratch->do_clear_update_bits)
+ clear_update_bits(scratch->surface_updates, scratch->surface_count, scratch->stream);
return false;
}
@@@ -8218,8 -8402,8 +8293,8 @@@ static bool update_planes_and_stream_cl
ASSERT(false);
}
- if (scratch->do_clear_update_flags)
- clear_update_flags(scratch->surface_updates, scratch->surface_count, scratch->stream);
+ if (scratch->do_clear_update_bits)
+ clear_update_bits(scratch->surface_updates, scratch->surface_count, scratch->stream);
return false;
}
@@@ -8242,7 -8426,7 +8317,7 @@@ struct dc_update_scratch_space *dc_upda
.stream = stream,
.stream_update = stream_update,
.update_v3 = version >= DCN_VERSION_4_01 || version == DCN_VERSION_3_2 || version == DCN_VERSION_3_21,
- .do_clear_update_flags = version >= DCN_VERSION_1_0,
+ .do_clear_update_bits = version >= DCN_VERSION_1_0,
};
return scratch;
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 48+ messages in thread* linux-next: manual merge of the drm tree with the drm-fixes tree
@ 2026-07-17 14:40 Mark Brown
0 siblings, 0 replies; 48+ messages in thread
From: Mark Brown @ 2026-07-17 14:40 UTC (permalink / raw)
To: Dave Airlie, DRI
Cc: Alex Deucher, Jesse Zhang, Linux Kernel Mailing List,
Linux Next Mailing List
[-- Attachment #1: Type: text/plain, Size: 14782 bytes --]
Hi all,
Today's linux-next merge of the drm tree got a conflict in:
drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
between commits:
614e8a989d462 ("drm/amdgpu: trigger GPU recovery when userq destroy fails to unmap a hung queue")
cee666427f280 ("drm/amdgpu/userq: fix indefinite fence wait during GPU reset")
from the drm-fixes tree and commits:
8396b9de4198a ("drm/amdgpu: trigger GPU recovery when userq destroy fails to unmap a hung queue")
9102b39fa924d ("drm/amdgpu/userq: fix indefinite fence wait during GPU reset")
from the drm tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
diff --combined drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
index 572f2949cb64e,babaabe2d891d..0000000000000
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c
@@@ -33,6 -33,7 +33,7 @@@
#include "amdgpu_userq.h"
#include "amdgpu_hmm.h"
#include "amdgpu_userq_fence.h"
+ #include "amdgpu_trace.h"
u32 amdgpu_userq_get_supported_ip_mask(struct amdgpu_device *adev)
{
@@@ -88,14 -89,7 +89,7 @@@ static void amdgpu_userq_mgr_reset_work
container_of(work, struct amdgpu_userq_mgr,
reset_work);
struct amdgpu_device *adev = uq_mgr->adev;
- const int queue_types[] = {
- AMDGPU_RING_TYPE_COMPUTE,
- AMDGPU_RING_TYPE_GFX,
- AMDGPU_RING_TYPE_SDMA
- };
- const int num_queue_types = ARRAY_SIZE(queue_types);
- bool gpu_reset = false;
- int i, r;
+ struct amdgpu_reset_context reset_context;
if (unlikely(adev->debug_disable_gpu_ring_reset)) {
dev_err(adev->dev, "userq reset disabled by debug mask\n");
@@@ -109,42 -103,15 +103,15 @@@
if (!amdgpu_gpu_recovery)
return;
- /*
- * Iterate through all queue types to detect and reset problematic queues
- * Process each queue type in the defined order
- */
- for (i = 0; i < num_queue_types; i++) {
- int ring_type = queue_types[i];
- const struct amdgpu_userq_funcs *funcs =
- adev->userq_funcs[ring_type];
+ memset(&reset_context, 0, sizeof(reset_context));
- if (!amdgpu_userq_is_reset_type_supported(adev, ring_type,
- AMDGPU_RESET_TYPE_PER_QUEUE))
- continue;
+ reset_context.method = AMD_RESET_METHOD_NONE;
+ reset_context.reset_req_dev = adev;
+ reset_context.src = AMDGPU_RESET_SRC_USERQ;
+ set_bit(AMDGPU_NEED_FULL_RESET, &reset_context.flags);
+ /*set_bit(AMDGPU_SKIP_COREDUMP, &reset_context.flags);*/
- if (atomic_read(&uq_mgr->userq_count[ring_type]) > 0 &&
- funcs && funcs->detect_and_reset) {
- r = funcs->detect_and_reset(adev, ring_type);
- if (r) {
- gpu_reset = true;
- break;
- }
- }
- }
-
- if (gpu_reset) {
- struct amdgpu_reset_context reset_context;
-
- memset(&reset_context, 0, sizeof(reset_context));
-
- reset_context.method = AMD_RESET_METHOD_NONE;
- reset_context.reset_req_dev = adev;
- reset_context.src = AMDGPU_RESET_SRC_USERQ;
- set_bit(AMDGPU_NEED_FULL_RESET, &reset_context.flags);
- /*set_bit(AMDGPU_SKIP_COREDUMP, &reset_context.flags);*/
-
- amdgpu_device_gpu_recover(adev, NULL, &reset_context);
- }
+ amdgpu_device_gpu_recover(adev, NULL, &reset_context);
}
static void amdgpu_userq_hang_detect_work(struct work_struct *work)
@@@ -152,12 -119,45 +119,45 @@@
struct amdgpu_usermode_queue *queue =
container_of(work, struct amdgpu_usermode_queue,
hang_detect_work.work);
+ struct amdgpu_userq_mgr *uq_mgr = queue->userq_mgr;
+ struct amdgpu_device *adev = uq_mgr->adev;
+ const struct amdgpu_userq_funcs *userq_funcs =
+ adev->userq_funcs[queue->queue_type];
+ bool gpu_reset = false;
+
+ if (unlikely(adev->debug_disable_gpu_ring_reset)) {
+ dev_err(adev->dev, "userq reset disabled by debug mask\n");
+ return;
+ }
+
+ /*
+ * If GPU recovery feature is disabled system-wide,
+ * skip all reset detection logic
+ */
+ if (!amdgpu_gpu_recovery)
+ return;
+
+ if (amdgpu_userq_is_reset_type_supported(adev, queue->queue_type,
+ AMDGPU_RESET_TYPE_PER_QUEUE)) {
+ int r;
+
+ if (queue->queue_type == AMDGPU_HW_IP_COMPUTE)
+ r = amdgpu_gfx_reset_mes_compute(adev, NULL, NULL,
+ queue, NULL, NULL);
+ else
+ r = userq_funcs->reset(queue);
+ if (r)
+ gpu_reset = true;
+ } else {
+ gpu_reset = true;
+ }
/*
* Don't schedule the work here! Scheduling or queue work from one reset
* handler to another is illegal if you don't take extra precautions!
*/
- amdgpu_userq_mgr_reset_work(&queue->userq_mgr->reset_work);
+ if (gpu_reset)
+ amdgpu_userq_mgr_reset_work(&queue->userq_mgr->reset_work);
}
/*
@@@ -293,11 -293,15 +293,15 @@@ static int amdgpu_userq_preempt_helper(
int r;
if (queue->state == AMDGPU_USERQ_STATE_MAPPED) {
+ trace_amdgpu_userq_state_start(queue);
+
r = userq_funcs->preempt(queue);
if (r) {
+ trace_amdgpu_userq_state_changed(queue, AMDGPU_USERQ_STATE_HUNG);
queue->state = AMDGPU_USERQ_STATE_HUNG;
return r;
} else {
+ trace_amdgpu_userq_state_changed(queue, AMDGPU_USERQ_STATE_PREEMPTED);
queue->state = AMDGPU_USERQ_STATE_PREEMPTED;
}
}
@@@ -313,10 -317,14 +317,14 @@@ static int amdgpu_userq_restore_helper(
int r = 0;
if (queue->state == AMDGPU_USERQ_STATE_PREEMPTED) {
+ trace_amdgpu_userq_state_start(queue);
+
r = userq_funcs->restore(queue);
if (r) {
+ trace_amdgpu_userq_state_changed(queue, AMDGPU_USERQ_STATE_HUNG);
queue->state = AMDGPU_USERQ_STATE_HUNG;
} else {
+ trace_amdgpu_userq_state_changed(queue, AMDGPU_USERQ_STATE_MAPPED);
queue->state = AMDGPU_USERQ_STATE_MAPPED;
}
}
@@@ -334,12 -342,15 +342,15 @@@ static int amdgpu_userq_unmap_helper(st
if ((queue->state == AMDGPU_USERQ_STATE_MAPPED) ||
(queue->state == AMDGPU_USERQ_STATE_PREEMPTED)) {
+ trace_amdgpu_userq_state_start(queue);
r = userq_funcs->unmap(queue);
if (r) {
+ trace_amdgpu_userq_state_changed(queue, AMDGPU_USERQ_STATE_HUNG);
queue->state = AMDGPU_USERQ_STATE_HUNG;
return r;
} else {
+ trace_amdgpu_userq_state_changed(queue, AMDGPU_USERQ_STATE_UNMAPPED);
queue->state = AMDGPU_USERQ_STATE_UNMAPPED;
}
}
@@@ -356,11 -367,15 +367,15 @@@ static int amdgpu_userq_map_helper(stru
int r;
if (queue->state == AMDGPU_USERQ_STATE_UNMAPPED) {
+ trace_amdgpu_userq_state_start(queue);
+
r = userq_funcs->map(queue);
if (r) {
+ trace_amdgpu_userq_state_changed(queue, AMDGPU_USERQ_STATE_HUNG);
queue->state = AMDGPU_USERQ_STATE_HUNG;
return r;
} else {
+ trace_amdgpu_userq_state_changed(queue, AMDGPU_USERQ_STATE_MAPPED);
queue->state = AMDGPU_USERQ_STATE_MAPPED;
}
}
@@@ -507,6 -522,8 +522,8 @@@ amdgpu_userq_destroy(struct amdgpu_user
const struct amdgpu_userq_funcs *uq_funcs = adev->userq_funcs[queue->queue_type];
int r = 0;
+ trace_amdgpu_userq_destroy_start(queue);
+
cancel_delayed_work_sync(&uq_mgr->resume_work);
/* Cancel any pending hang detection work and cleanup */
@@@ -541,6 -558,7 +558,7 @@@
amdgpu_bo_unreserve(queue->db_obj.obj);
amdgpu_bo_unref(&queue->db_obj.obj);
+ trace_amdgpu_userq_destroy_end(queue, r);
kfree(queue);
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
@@@ -638,6 -656,8 +656,8 @@@ amdgpu_userq_create(struct drm_file *fi
queue->queue_type = args->in.ip_type;
queue->vm = &fpriv->vm;
queue->priority = priority;
+ queue->xcp_id = (fpriv->xcp_id != AMDGPU_XCP_NO_PARTITION) ?
+ fpriv->xcp_id : 0;
queue->userq_mgr = uq_mgr;
INIT_DELAYED_WORK(&queue->hang_detect_work,
amdgpu_userq_hang_detect_work);
@@@ -680,6 -700,8 +700,8 @@@
}
queue->doorbell_index = index;
+ queue->doorbell_offset = (u32)args->in.doorbell_offset;
+ trace_amdgpu_userq_create_start(queue);
r = uq_funcs->mqd_create(queue, &args->in);
if (r) {
drm_file_err(uq_mgr->file, "Failed to create Queue\n");
@@@ -703,6 -725,7 +725,7 @@@
r = amdgpu_userq_map_helper(queue);
if (r) {
drm_file_err(uq_mgr->file, "Failed to map Queue\n");
+ trace_amdgpu_userq_create_end(queue, r);
mutex_unlock(&uq_mgr->userq_mutex);
goto erase_doorbell;
}
@@@ -719,11 -742,13 +742,13 @@@
* This drops the last reference which should take care of
* all cleanup.
*/
+ trace_amdgpu_userq_create_end(queue, r);
amdgpu_userq_put(queue);
return r;
}
amdgpu_debugfs_userq_init(filp, queue, qid);
+ trace_amdgpu_userq_create_end(queue, 0);
args->out.queue_id = qid;
return 0;
@@@ -739,6 -764,7 +764,7 @@@ clean_doorbell_bo
free_fence_drv:
amdgpu_userq_fence_driver_free(queue);
free_queue:
+ trace_amdgpu_userq_create_end(queue, r);
kfree(queue);
err_pm_runtime:
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
@@@ -871,16 -897,10 +897,10 @@@ int amdgpu_userq_ioctl(struct drm_devic
static int
amdgpu_userq_restore_all(struct amdgpu_userq_mgr *uq_mgr)
{
- struct amdgpu_fpriv *fpriv = uq_mgr_to_fpriv(uq_mgr);
- struct amdgpu_vm *vm = &fpriv->vm;
struct amdgpu_usermode_queue *queue;
unsigned long queue_id;
int ret = 0, r;
-
- if (amdgpu_bo_reserve(vm->root.bo, false))
- return false;
-
mutex_lock(&uq_mgr->userq_mutex);
/* Resume all the queues for this process */
xa_for_each(&uq_mgr->userq_xa, queue_id, queue) {
@@@ -888,6 -908,7 +908,7 @@@
if (!amdgpu_userq_buffer_vas_mapped(queue)) {
drm_file_err(uq_mgr->file,
"trying restore queue without va mapping\n");
+ trace_amdgpu_userq_state_changed(queue, AMDGPU_USERQ_STATE_INVALID_VA);
queue->state = AMDGPU_USERQ_STATE_INVALID_VA;
continue;
}
@@@ -895,10 -916,8 +916,8 @@@
r = amdgpu_userq_map_helper(queue);
if (r)
ret = r;
-
}
mutex_unlock(&uq_mgr->userq_mutex);
- amdgpu_bo_unreserve(vm->root.bo);
if (ret)
drm_file_err(uq_mgr->file,
@@@ -932,7 -951,8 +951,8 @@@ amdgpu_userq_bo_validate(struct amdgpu_
spin_unlock(&vm->individual_lock);
bo = bo_va->base.bo;
- ret = drm_exec_prepare_obj(exec, &bo->tbo.base, 2);
+ ret = drm_exec_prepare_obj(exec, &bo->tbo.base,
+ TTM_NUM_MOVE_FENCES + 1);
if (unlikely(ret))
return ret;
@@@ -955,7 -975,7 +975,7 @@@
/* Make sure the whole VM is ready to be used */
static int
- amdgpu_userq_vm_validate(struct amdgpu_userq_mgr *uq_mgr)
+ amdgpu_userq_vm_validate_and_restore_queue(struct amdgpu_userq_mgr *uq_mgr)
{
struct amdgpu_fpriv *fpriv = uq_mgr_to_fpriv(uq_mgr);
bool invalidated = false, new_addition = false;
@@@ -1081,8 -1101,12 +1101,12 @@@ retry_lock
dma_fence_wait(vm->last_update, false);
ret = amdgpu_evf_mgr_rearm(&fpriv->evf_mgr, &exec);
- if (ret)
+ if (ret) {
drm_file_err(uq_mgr->file, "Failed to replace eviction fence\n");
+ goto unlock_all;
+ }
+
+ ret = amdgpu_userq_restore_all(uq_mgr);
unlock_all:
drm_exec_fini(&exec);
@@@ -1108,18 -1132,34 +1132,34 @@@ static void amdgpu_userq_restore_worker
if (!dma_fence_is_signaled(ev_fence))
goto put_fence;
- ret = amdgpu_userq_vm_validate(uq_mgr);
+ ret = amdgpu_userq_vm_validate_and_restore_queue(uq_mgr);
if (ret) {
drm_file_err(uq_mgr->file, "Failed to validate BOs to restore ret=%d\n", ret);
goto put_fence;
}
- amdgpu_userq_restore_all(uq_mgr);
-
put_fence:
dma_fence_put(ev_fence);
}
+ void amdgpu_userq_process_reset_irq(struct amdgpu_device *adev,
+ u32 pasid, u32 doorbell_offset)
+ {
+ struct xarray *xa = &adev->userq_doorbell_xa;
+ struct amdgpu_usermode_queue *queue;
+ unsigned long flags, idx;
+
+ xa_lock_irqsave(xa, flags);
+ xa_for_each(xa, idx, queue) {
+ if (queue->vm && queue->vm->pasid == pasid &&
+ queue->doorbell_offset == doorbell_offset) {
+ amdgpu_userq_start_hang_detect_work(queue);
+ break;
+ }
+ }
+ xa_unlock_irqrestore(xa, flags);
+ }
+
static int
amdgpu_userq_evict_all(struct amdgpu_userq_mgr *uq_mgr)
{
@@@ -1175,6 -1215,7 +1215,7 @@@ int amdgpu_userq_mgr_init(struct amdgpu
xa_init_flags(&userq_mgr->userq_xa, XA_FLAGS_ALLOC);
userq_mgr->adev = adev;
userq_mgr->file = file_priv;
+ mutex_init(&userq_mgr->proc_ctx_lock);
INIT_DELAYED_WORK(&userq_mgr->resume_work, amdgpu_userq_restore_worker);
INIT_WORK(&userq_mgr->reset_work, amdgpu_userq_mgr_reset_work);
@@@ -1228,6 -1269,11 +1269,11 @@@ void amdgpu_userq_mgr_fini(struct amdgp
*/
cancel_work_sync(&userq_mgr->reset_work);
+ amdgpu_bo_free_kernel(&userq_mgr->proc_ctx_obj.obj,
+ &userq_mgr->proc_ctx_obj.gpu_addr,
+ &userq_mgr->proc_ctx_obj.cpu_ptr);
+
+ mutex_destroy(&userq_mgr->proc_ctx_lock);
mutex_destroy(&userq_mgr->userq_mutex);
}
@@@ -1377,12 -1423,14 +1423,14 @@@ void amdgpu_userq_pre_reset(struct amdg
/* TODO: We probably need a new lock for the queue state */
xa_for_each(&adev->userq_doorbell_xa, queue_id, queue) {
if (queue->state == AMDGPU_USERQ_STATE_MAPPED) {
+ trace_amdgpu_userq_state_start(queue);
userq_funcs = adev->userq_funcs[queue->queue_type];
userq_funcs->unmap(queue);
/* just mark all queues as hung at this point.
* if unmap succeeds, we could map again
* in amdgpu_userq_post_reset() if vram is not lost
*/
+ trace_amdgpu_userq_state_changed(queue, AMDGPU_USERQ_STATE_HUNG);
queue->state = AMDGPU_USERQ_STATE_HUNG;
}
/* Force-complete any pending fence regardless of queue state so
@@@ -1406,6 -1454,8 +1454,8 @@@ int amdgpu_userq_post_reset(struct amdg
xa_for_each(&adev->userq_doorbell_xa, queue_id, queue) {
if (queue->state == AMDGPU_USERQ_STATE_HUNG && !vram_lost) {
+ trace_amdgpu_userq_state_start(queue);
+
userq_funcs = adev->userq_funcs[queue->queue_type];
/* Re-map queue */
r = userq_funcs->map(queue);
@@@ -1413,6 -1463,7 +1463,7 @@@
dev_err(adev->dev, "Failed to remap queue %ld\n", queue_id);
continue;
}
+ trace_amdgpu_userq_state_changed(queue, AMDGPU_USERQ_STATE_MAPPED);
queue->state = AMDGPU_USERQ_STATE_MAPPED;
}
}
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 48+ messages in thread* linux-next: manual merge of the drm tree with the drm-fixes tree
@ 2026-07-17 14:40 Mark Brown
0 siblings, 0 replies; 48+ messages in thread
From: Mark Brown @ 2026-07-17 14:40 UTC (permalink / raw)
To: Dave Airlie, DRI
Cc: Alex Deucher, Linux Kernel Mailing List, Linux Next Mailing List,
Mario Limonciello
[-- Attachment #1: Type: text/plain, Size: 16124 bytes --]
Hi all,
Today's linux-next merge of the drm tree got a conflict in:
drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
between commit:
8ad5ab4da7db2 ("drm/amdgpu: Disable PCIe dynamic speed switching on Ryzen Pinnacle Ridge")
from the drm-fixes tree and commits:
9ceb4e034a327 ("drm/amdgpu: Disable PCIe dynamic speed switching on Ryzen Pinnacle Ridge")
a57c68224d739 ("drm/amd: Move dynamic PCIe switching quirks to X86_MATCH_* macros")
from the drm tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
diff --combined drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
index e5f26e5892bac,472e96ae884e5..0000000000000
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
@@@ -72,8 -72,10 +72,10 @@@
#include "amdgpu_xgmi.h"
#include "amdgpu_ras.h"
+ #include "amdgpu_wb.h"
#include "amdgpu_ras_mgr.h"
#include "amdgpu_pmu.h"
+ #include "amdgpu_smu.h"
#include "amdgpu_fru_eeprom.h"
#include "amdgpu_reset.h"
#include "amdgpu_virt.h"
@@@ -1005,109 -1007,6 +1007,6 @@@ int amdgpu_device_pci_reset(struct amdg
return pci_reset_function(adev->pdev);
}
- /*
- * amdgpu_device_wb_*()
- * Writeback is the method by which the GPU updates special pages in memory
- * with the status of certain GPU events (fences, ring pointers,etc.).
- */
-
- /**
- * amdgpu_device_wb_fini - Disable Writeback and free memory
- *
- * @adev: amdgpu_device pointer
- *
- * Disables Writeback and frees the Writeback memory (all asics).
- * Used at driver shutdown.
- */
- static void amdgpu_device_wb_fini(struct amdgpu_device *adev)
- {
- if (adev->wb.wb_obj) {
- amdgpu_bo_free_kernel(&adev->wb.wb_obj,
- &adev->wb.gpu_addr,
- (void **)&adev->wb.wb);
- adev->wb.wb_obj = NULL;
- }
- }
-
- /**
- * amdgpu_device_wb_init - Init Writeback driver info and allocate memory
- *
- * @adev: amdgpu_device pointer
- *
- * Initializes writeback and allocates writeback memory (all asics).
- * Used at driver startup.
- * Returns 0 on success or an -error on failure.
- */
- static int amdgpu_device_wb_init(struct amdgpu_device *adev)
- {
- int r;
-
- if (adev->wb.wb_obj == NULL) {
- /* AMDGPU_MAX_WB * sizeof(uint32_t) * 8 = AMDGPU_MAX_WB 256bit slots */
- r = amdgpu_bo_create_kernel(adev, AMDGPU_MAX_WB * sizeof(uint32_t) * 8,
- PAGE_SIZE, AMDGPU_GEM_DOMAIN_GTT,
- &adev->wb.wb_obj, &adev->wb.gpu_addr,
- (void **)&adev->wb.wb);
- if (r) {
- dev_warn(adev->dev, "(%d) create WB bo failed\n", r);
- return r;
- }
-
- adev->wb.num_wb = AMDGPU_MAX_WB;
- memset(&adev->wb.used, 0, sizeof(adev->wb.used));
-
- /* clear wb memory */
- memset((char *)adev->wb.wb, 0, AMDGPU_MAX_WB * sizeof(uint32_t) * 8);
- }
-
- return 0;
- }
-
- /**
- * amdgpu_device_wb_get - Allocate a wb entry
- *
- * @adev: amdgpu_device pointer
- * @wb: wb index
- *
- * Allocate a wb slot for use by the driver (all asics).
- * Returns 0 on success or -EINVAL on failure.
- */
- int amdgpu_device_wb_get(struct amdgpu_device *adev, u32 *wb)
- {
- unsigned long flags, offset;
-
- spin_lock_irqsave(&adev->wb.lock, flags);
- offset = find_first_zero_bit(adev->wb.used, adev->wb.num_wb);
- if (offset < adev->wb.num_wb) {
- __set_bit(offset, adev->wb.used);
- spin_unlock_irqrestore(&adev->wb.lock, flags);
- *wb = offset << 3; /* convert to dw offset */
- return 0;
- } else {
- spin_unlock_irqrestore(&adev->wb.lock, flags);
- return -EINVAL;
- }
- }
-
- /**
- * amdgpu_device_wb_free - Free a wb entry
- *
- * @adev: amdgpu_device pointer
- * @wb: wb index
- *
- * Free a wb slot allocated for use by the driver (all asics)
- */
- void amdgpu_device_wb_free(struct amdgpu_device *adev, u32 wb)
- {
- unsigned long flags;
-
- wb >>= 3;
- spin_lock_irqsave(&adev->wb.lock, flags);
- if (wb < adev->wb.num_wb)
- __clear_bit(wb, adev->wb.used);
- spin_unlock_irqrestore(&adev->wb.lock, flags);
- }
-
/**
* amdgpu_device_resize_fb_bar - try to resize FB BAR
*
@@@ -1304,37 -1203,44 +1203,44 @@@ bool amdgpu_device_seamless_boot_suppor
return amdgpu_ip_version(adev, DCE_HWIP, 0) >= IP_VERSION(3, 0, 0);
}
- /*
- * Intel hosts such as Rocket Lake, Alder Lake, Raptor Lake and Sapphire Rapids
- * don't support dynamic speed switching. Until we have confirmation from Intel
- * that a specific host supports it, it's safer that we keep it disabled for all.
- *
- * https://edc.intel.com/content/www/us/en/design/products/platforms/details/raptor-lake-s/13th-generation-core-processors-datasheet-volume-1-of-2/005/pci-express-support/
- * https://gitlab.freedesktop.org/drm/amd/-/issues/2663
- */
- static bool amdgpu_device_pcie_dynamic_switching_supported(struct amdgpu_device *adev)
- {
#if IS_ENABLED(CONFIG_X86)
- struct cpuinfo_x86 *c = &cpu_data(0);
-
- /* eGPU change speeds based on USB4 fabric conditions */
- if (dev_is_removable(adev->dev))
- return true;
-
- if (c->x86_vendor == X86_VENDOR_INTEL)
- return false;
-
+ static const struct x86_cpu_id amdgpu_pcie_dynamic_switching_quirks[] = {
+ /*
+ * Intel hosts such as Rocket Lake, Alder Lake, Raptor Lake and Sapphire Rapids
+ * don't support dynamic speed switching. Until we have confirmation from Intel
+ * that a specific host supports it, it's safer that we keep it disabled for all.
+ *
+ * https://edc.intel.com/content/www/us/en/design/products/platforms/details/raptor-lake-s/13th-generation-core-processors-datasheet-volume-1-of-2/005/pci-express-support/
+ * https://gitlab.freedesktop.org/drm/amd/-/issues/2663
+ */
+ X86_MATCH_VENDOR_FAM(INTEL, X86_FAMILY_ANY, NULL),
/*
* AMD Ryzen Pinnacle Ridge (Zen+, family 0x17 model 0x08) CPUs don't
* support PCIe dynamic speed switching.
* https://gitlab.freedesktop.org/drm/amd/-/work_items/5436
*/
- if (c->x86_vendor == X86_VENDOR_AMD && c->x86 == 0x17 &&
- c->x86_model == 0x08)
+ X86_MATCH_VENDOR_FAM_MODEL(AMD, 0x17, 0x08, NULL),
+ {}
+ };
+
+ static bool amdgpu_device_pcie_dynamic_switching_supported(struct amdgpu_device *adev)
+ {
+ /* eGPU change speeds based on USB4 fabric conditions */
+ if (dev_is_removable(adev->dev))
+ return true;
+
+ /* Hosts have problems with dynamic speed switching */
+ if (x86_match_cpu(amdgpu_pcie_dynamic_switching_quirks))
return false;
- #endif
+
return true;
}
+ #else
+ static inline bool amdgpu_device_pcie_dynamic_switching_supported(struct amdgpu_device *adev)
+ {
+ return true;
+ }
+ #endif
static bool amdgpu_device_aspm_support_quirk(struct amdgpu_device *adev)
{
@@@ -2140,6 -2046,8 +2046,8 @@@ static int amdgpu_device_ip_early_init(
adev->cg_flags &= amdgpu_cg_mask;
adev->pg_flags &= amdgpu_pg_mask;
+ amdgpu_smu_early_init(adev);
+
return 0;
}
@@@ -2380,10 -2288,10 +2288,10 @@@ static int amdgpu_device_ip_init(struc
r);
goto init_failed;
}
- r = amdgpu_device_wb_init(adev);
+ r = amdgpu_wb_init(adev);
if (r) {
dev_err(adev->dev,
- "amdgpu_device_wb_init failed %d\n", r);
+ "amdgpu_wb_init failed %d\n", r);
goto init_failed;
}
adev->ip_blocks[i].status.hw = true;
@@@ -2915,7 -2823,7 +2823,7 @@@ static int amdgpu_device_ip_fini(struc
if (adev->ip_blocks[i].version->type == AMD_IP_BLOCK_TYPE_GMC) {
amdgpu_ucode_free_bo(adev);
amdgpu_free_static_csa(&adev->virt.csa_obj);
- amdgpu_device_wb_fini(adev);
+ amdgpu_wb_fini(adev);
amdgpu_device_mem_scratch_fini(adev);
amdgpu_ib_pool_fini(adev);
amdgpu_seq64_fini(adev);
@@@ -3687,6 -3595,10 +3595,10 @@@ static void amdgpu_device_sys_interface
amdgpu_pm_sysfs_fini(adev);
if (adev->ucode_sysfs_en)
amdgpu_ucode_sysfs_fini(adev);
+
+ amdgpu_discovery_sysfs_fini(adev);
+ amdgpu_preempt_mgr_sysfs_fini(adev);
+
amdgpu_device_attr_sysfs_fini(adev);
amdgpu_fru_sysfs_fini(adev);
@@@ -3783,6 -3695,7 +3695,7 @@@ int amdgpu_device_init(struct amdgpu_de
spin_lock_init(&adev->irq.lock);
+ amdgpu_early_init_rlc_reg_funcs(adev);
amdgpu_device_init_apu_flags(adev);
r = amdgpu_device_check_arguments(adev);
@@@ -4218,6 -4131,7 +4131,7 @@@ void amdgpu_device_fini_hw(struct amdgp
if (adev->mman.initialized)
drain_workqueue(adev->mman.bdev.wq);
+
adev->shutdown = true;
unregister_pm_notifier(&adev->pm_nb);
@@@ -4716,161 -4630,6 +4630,6 @@@ exit
return 0;
}
- /**
- * amdgpu_device_ip_check_soft_reset - did soft reset succeed
- *
- * @adev: amdgpu_device pointer
- *
- * The list of all the hardware IPs that make up the asic is walked and
- * the check_soft_reset callbacks are run. check_soft_reset determines
- * if the asic is still hung or not.
- * Returns true if any of the IPs are still in a hung state, false if not.
- */
- static bool amdgpu_device_ip_check_soft_reset(struct amdgpu_device *adev)
- {
- int i;
- bool asic_hang = false;
-
- if (amdgpu_sriov_vf(adev))
- return true;
-
- if (amdgpu_asic_need_full_reset(adev))
- return true;
-
- for (i = 0; i < adev->num_ip_blocks; i++) {
- if (!adev->ip_blocks[i].status.valid)
- continue;
- if (adev->ip_blocks[i].version->funcs->check_soft_reset)
- adev->ip_blocks[i].status.hang =
- adev->ip_blocks[i].version->funcs->check_soft_reset(
- &adev->ip_blocks[i]);
- if (adev->ip_blocks[i].status.hang) {
- dev_info(adev->dev, "IP block:%s is hung!\n", adev->ip_blocks[i].version->funcs->name);
- asic_hang = true;
- }
- }
- return asic_hang;
- }
-
- /**
- * amdgpu_device_ip_pre_soft_reset - prepare for soft reset
- *
- * @adev: amdgpu_device pointer
- *
- * The list of all the hardware IPs that make up the asic is walked and the
- * pre_soft_reset callbacks are run if the block is hung. pre_soft_reset
- * handles any IP specific hardware or software state changes that are
- * necessary for a soft reset to succeed.
- * Returns 0 on success, negative error code on failure.
- */
- static int amdgpu_device_ip_pre_soft_reset(struct amdgpu_device *adev)
- {
- int i, r = 0;
-
- for (i = 0; i < adev->num_ip_blocks; i++) {
- if (!adev->ip_blocks[i].status.valid)
- continue;
- if (adev->ip_blocks[i].status.hang &&
- adev->ip_blocks[i].version->funcs->pre_soft_reset) {
- r = adev->ip_blocks[i].version->funcs->pre_soft_reset(&adev->ip_blocks[i]);
- if (r)
- return r;
- }
- }
-
- return 0;
- }
-
- /**
- * amdgpu_device_ip_need_full_reset - check if a full asic reset is needed
- *
- * @adev: amdgpu_device pointer
- *
- * Some hardware IPs cannot be soft reset. If they are hung, a full gpu
- * reset is necessary to recover.
- * Returns true if a full asic reset is required, false if not.
- */
- static bool amdgpu_device_ip_need_full_reset(struct amdgpu_device *adev)
- {
- int i;
-
- if (amdgpu_asic_need_full_reset(adev))
- return true;
-
- for (i = 0; i < adev->num_ip_blocks; i++) {
- if (!adev->ip_blocks[i].status.valid)
- continue;
- if ((adev->ip_blocks[i].version->type == AMD_IP_BLOCK_TYPE_GMC) ||
- (adev->ip_blocks[i].version->type == AMD_IP_BLOCK_TYPE_SMC) ||
- (adev->ip_blocks[i].version->type == AMD_IP_BLOCK_TYPE_ACP) ||
- (adev->ip_blocks[i].version->type == AMD_IP_BLOCK_TYPE_DCE) ||
- adev->ip_blocks[i].version->type == AMD_IP_BLOCK_TYPE_PSP) {
- if (adev->ip_blocks[i].status.hang) {
- dev_info(adev->dev, "Some block need full reset!\n");
- return true;
- }
- }
- }
- return false;
- }
-
- /**
- * amdgpu_device_ip_soft_reset - do a soft reset
- *
- * @adev: amdgpu_device pointer
- *
- * The list of all the hardware IPs that make up the asic is walked and the
- * soft_reset callbacks are run if the block is hung. soft_reset handles any
- * IP specific hardware or software state changes that are necessary to soft
- * reset the IP.
- * Returns 0 on success, negative error code on failure.
- */
- static int amdgpu_device_ip_soft_reset(struct amdgpu_device *adev)
- {
- int i, r = 0;
-
- for (i = 0; i < adev->num_ip_blocks; i++) {
- if (!adev->ip_blocks[i].status.valid)
- continue;
- if (adev->ip_blocks[i].status.hang &&
- adev->ip_blocks[i].version->funcs->soft_reset) {
- r = adev->ip_blocks[i].version->funcs->soft_reset(&adev->ip_blocks[i]);
- if (r)
- return r;
- }
- }
-
- return 0;
- }
-
- /**
- * amdgpu_device_ip_post_soft_reset - clean up from soft reset
- *
- * @adev: amdgpu_device pointer
- *
- * The list of all the hardware IPs that make up the asic is walked and the
- * post_soft_reset callbacks are run if the asic was hung. post_soft_reset
- * handles any IP specific hardware or software state changes that are
- * necessary after the IP has been soft reset.
- * Returns 0 on success, negative error code on failure.
- */
- static int amdgpu_device_ip_post_soft_reset(struct amdgpu_device *adev)
- {
- int i, r = 0;
-
- for (i = 0; i < adev->num_ip_blocks; i++) {
- if (!adev->ip_blocks[i].status.valid)
- continue;
- if (adev->ip_blocks[i].status.hang &&
- adev->ip_blocks[i].version->funcs->post_soft_reset)
- r = adev->ip_blocks[i].version->funcs->post_soft_reset(&adev->ip_blocks[i]);
- if (r)
- return r;
- }
-
- return 0;
- }
-
/**
* amdgpu_device_reset_sriov - reset ASIC for SR-IOV vf
*
@@@ -5162,20 -4921,7 +4921,7 @@@ int amdgpu_device_pre_asic_reset(struc
/* Don't suspend on bare metal if we are not going to HW reset the ASIC */
if (!amdgpu_sriov_vf(adev)) {
-
- if (!need_full_reset)
- need_full_reset = amdgpu_device_ip_need_full_reset(adev);
-
- if (!need_full_reset && amdgpu_gpu_recovery &&
- amdgpu_device_ip_check_soft_reset(adev)) {
- amdgpu_device_ip_pre_soft_reset(adev);
- r = amdgpu_device_ip_soft_reset(adev);
- amdgpu_device_ip_post_soft_reset(adev);
- if (r || amdgpu_device_ip_check_soft_reset(adev)) {
- dev_info(adev->dev, "soft reset failed, will fallback to full reset!\n");
- need_full_reset = true;
- }
- }
+ need_full_reset = true;
if (!test_bit(AMDGPU_SKIP_COREDUMP, &reset_context->flags)) {
dev_info(tmp_adev->dev, "Dumping IP State\n");
@@@ -5628,8 -5374,7 +5374,7 @@@ static void amdgpu_device_halt_activiti
drm_client_dev_suspend(adev_to_drm(tmp_adev));
/* disable ras on ALL IPs */
- if (!need_emergency_restart && !amdgpu_reset_in_dpc(adev) &&
- amdgpu_device_ip_need_full_reset(tmp_adev))
+ if (!need_emergency_restart && !amdgpu_reset_in_dpc(adev))
amdgpu_ras_suspend(tmp_adev);
amdgpu_userq_pre_reset(tmp_adev);
@@@ -6901,7 -6646,7 +6646,7 @@@ ssize_t amdgpu_get_soft_full_reset_mask
if (unlikely(!ring->adev->debug_disable_soft_recovery) &&
!amdgpu_sriov_vf(ring->adev) && ring->funcs->soft_recovery)
- size |= AMDGPU_RESET_TYPE_SOFT_RESET;
+ size |= AMDGPU_RESET_TYPE_SOFT_RECOVERY;
return size;
}
@@@ -6917,8 -6662,8 +6662,8 @@@ ssize_t amdgpu_show_reset_mask(char *bu
}
- if (supported_reset & AMDGPU_RESET_TYPE_SOFT_RESET)
- size += sysfs_emit_at(buf, size, "soft ");
+ if (supported_reset & AMDGPU_RESET_TYPE_SOFT_RECOVERY)
+ size += sysfs_emit_at(buf, size, "soft_recovery ");
if (supported_reset & AMDGPU_RESET_TYPE_PER_QUEUE)
size += sysfs_emit_at(buf, size, "queue ");
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 48+ messages in thread* linux-next: manual merge of the drm tree with the drm-fixes tree
@ 2026-07-17 14:40 Mark Brown
0 siblings, 0 replies; 48+ messages in thread
From: Mark Brown @ 2026-07-17 14:40 UTC (permalink / raw)
To: Dave Airlie, DRI
Cc: Alessandro Rinaldi, Alex Deucher, Alex Hung, Aurabindo Pillai,
Austin Zheng, Chenyu Chen, Geoffrey McRae, George Zhang,
Harry Wentland, James Lin, Leo Li, Linux Kernel Mailing List,
Linux Next Mailing List, Mario Limonciello, Matthew Schwartz,
Michał Mirosław, Rafal Ostrowski, Thomas Zimmermann,
Timur Kristóf, Tom Chung, WenTao Liang
[-- Attachment #1: Type: text/plain, Size: 277772 bytes --]
Hi all,
Today's linux-next merge of the drm tree got a conflict in:
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
between commits:
d094bc1f17783 ("drm/amd/display: consolidate DCN vblank/flip handling onto vupdate_no_lock")
d73f4e118dc4c ("drm/amd/display: check GRPH_FLIP status before sending event")
c9ab9622f5b27 ("Revert "drm/amd/display: Restore 5s vbl offdelay for NV3x+ DGPUs"")
d094bc1f17783 ("drm/amd/display: consolidate DCN vblank/flip handling onto vupdate_no_lock")
efd29b1cab549 ("drm/amd/display: set new_stream to NULL after release")
aa377fdcb475c ("drm/amd/display: Force PWM backlight on Lenovo Legion 5 15ARH05")
a991960f8bdc4 ("drm/amd/display: Set native cursor mode for disabled CRTCs")
86e2b8644e92d ("drm/amd/display: Fix backlight max_brightness to match exported range")
from the drm-fixes tree and commits:
99f3af19073b3 ("drm/amd/display: set new_stream to NULL after release")
2f79f0130f828 ("drm/amd/display: Set native cursor mode for disabled CRTCs")
ee186ae23ab0f ("Revert "drm/amd/display: Restore 5s vbl offdelay for NV3x+ DGPUs"")
f64a9be565368 ("drm/amd/display: check GRPH_FLIP status before sending event")
c87e6635d2db0 ("drm/amd/display: consolidate DCN vblank/flip handling onto vupdate_no_lock")
0d6453fd6e17a ("drm/amd/display: Fix DM IRQ teardown races")
a5cd2fbe7b45f ("drm/amd/display/amdgpu_dm: show error names")
163b0b32bfbee ("drm/amd/display: don't re-evaluate cursor mode on cursor plane movement")
98e08a815ef60 ("drm/amd/display: Tear down dangling pipe on boot to fix s0i3")
e4ae30a12aa95 ("drm/amd/display: Fix DM I2C teardown race")
acf97c57d091f ("drm/amd/display: Re-evaluate cursor mode on plane position/size change")
9bcf6af12bacb ("drm/amd/display: hold a vblank ref while writeback is pending")
a24019f6480fa ("drm/amd/display: Handle struct drm_plane_state.ignore_damage_clips")
947e46eb2fb9f ("drm/amdgpu: Delete check_soft_reset() from amd_ip_funcs")
13158e5dbd896 ("drm/amd/display: Fix mem_type change detection for async flips")
c26b643aa31b7 ("drm/amd/display: Add KUnit tests for amdgpu_dm")
c1199393ec559 ("drm/amd/display: Refactor surface_update_flags to flat struct with helpers")
0e967e086e751 ("drm/amd/display: Extract connector and encoder code to amdgpu_dm_connector")
0618dec494157 ("drm/amd/display: Move HPD and IRQ handler code to amdgpu_dm_irq")
4734e045f49d9 ("drm/amd/display: Extract DMUB code to amdgpu_dm_dmub")
ee55bf7d6a63f ("drm/amd/display: Extract audio code to amdgpu_dm_audio")
f5165625b8b27 ("drm/amd/display: Extract backlight code to amdgpu_dm_backlight")
b008c67efb36b ("drm/amd/display: Introduce dc_plane_cm and migrate surface update color path")
60597d2cb2199 ("drm/amd/display: Use handle_hpd_irq_helper for HPD RX")
b7e70a466b3d0 ("drm/amd/display: Add detect reason to handle_hpd_irq_helper")
0fde96e06f1cd ("drm/amd/display: Deprecate DMUB register offload functionality")
from the drm tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
diff --combined drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index 9c564cd5edeea,e7080880b2211..0000000000000
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@@ -66,6 -66,11 +66,11 @@@
#endif
#include "amdgpu_dm_psr.h"
#include "amdgpu_dm_replay.h"
+ #include "amdgpu_dm_backlight.h"
+ #include "amdgpu_dm_audio.h"
+ #include "amdgpu_dm_dmub.h"
+ #include "amdgpu_dm_connector.h"
+ #include "amdgpu_dm_kunit_helpers.h"
#include "ivsrcid/ivsrcid_vislands30.h"
@@@ -94,7 -99,6 +99,6 @@@
#include <drm/drm_mode.h>
#include <drm/drm_utils.h>
#include <drm/drm_vblank.h>
- #include <drm/drm_audio_component.h>
#include <drm/drm_colorop.h>
#include <drm/drm_gem_atomic_helper.h>
@@@ -107,60 -111,9 +111,9 @@@
#include "modules/inc/mod_power.h"
#include "modules/power/power_helpers.h"
- static_assert(AMDGPU_DMUB_NOTIFICATION_MAX == DMUB_NOTIFICATION_MAX, "AMDGPU_DMUB_NOTIFICATION_MAX mismatch");
-
- #define FIRMWARE_RENOIR_DMUB "amdgpu/renoir_dmcub.bin"
- MODULE_FIRMWARE(FIRMWARE_RENOIR_DMUB);
- #define FIRMWARE_SIENNA_CICHLID_DMUB "amdgpu/sienna_cichlid_dmcub.bin"
- MODULE_FIRMWARE(FIRMWARE_SIENNA_CICHLID_DMUB);
- #define FIRMWARE_NAVY_FLOUNDER_DMUB "amdgpu/navy_flounder_dmcub.bin"
- MODULE_FIRMWARE(FIRMWARE_NAVY_FLOUNDER_DMUB);
- #define FIRMWARE_GREEN_SARDINE_DMUB "amdgpu/green_sardine_dmcub.bin"
- MODULE_FIRMWARE(FIRMWARE_GREEN_SARDINE_DMUB);
- #define FIRMWARE_VANGOGH_DMUB "amdgpu/vangogh_dmcub.bin"
- MODULE_FIRMWARE(FIRMWARE_VANGOGH_DMUB);
- #define FIRMWARE_DIMGREY_CAVEFISH_DMUB "amdgpu/dimgrey_cavefish_dmcub.bin"
- MODULE_FIRMWARE(FIRMWARE_DIMGREY_CAVEFISH_DMUB);
- #define FIRMWARE_BEIGE_GOBY_DMUB "amdgpu/beige_goby_dmcub.bin"
- MODULE_FIRMWARE(FIRMWARE_BEIGE_GOBY_DMUB);
- #define FIRMWARE_YELLOW_CARP_DMUB "amdgpu/yellow_carp_dmcub.bin"
- MODULE_FIRMWARE(FIRMWARE_YELLOW_CARP_DMUB);
- #define FIRMWARE_DCN_314_DMUB "amdgpu/dcn_3_1_4_dmcub.bin"
- MODULE_FIRMWARE(FIRMWARE_DCN_314_DMUB);
- #define FIRMWARE_DCN_315_DMUB "amdgpu/dcn_3_1_5_dmcub.bin"
- MODULE_FIRMWARE(FIRMWARE_DCN_315_DMUB);
- #define FIRMWARE_DCN316_DMUB "amdgpu/dcn_3_1_6_dmcub.bin"
- MODULE_FIRMWARE(FIRMWARE_DCN316_DMUB);
-
- #define FIRMWARE_DCN_V3_2_0_DMCUB "amdgpu/dcn_3_2_0_dmcub.bin"
- MODULE_FIRMWARE(FIRMWARE_DCN_V3_2_0_DMCUB);
- #define FIRMWARE_DCN_V3_2_1_DMCUB "amdgpu/dcn_3_2_1_dmcub.bin"
- MODULE_FIRMWARE(FIRMWARE_DCN_V3_2_1_DMCUB);
-
- #define FIRMWARE_RAVEN_DMCU "amdgpu/raven_dmcu.bin"
MODULE_FIRMWARE(FIRMWARE_RAVEN_DMCU);
-
- #define FIRMWARE_NAVI12_DMCU "amdgpu/navi12_dmcu.bin"
MODULE_FIRMWARE(FIRMWARE_NAVI12_DMCU);
- #define FIRMWARE_DCN_35_DMUB "amdgpu/dcn_3_5_dmcub.bin"
- MODULE_FIRMWARE(FIRMWARE_DCN_35_DMUB);
-
- #define FIRMWARE_DCN_351_DMUB "amdgpu/dcn_3_5_1_dmcub.bin"
- MODULE_FIRMWARE(FIRMWARE_DCN_351_DMUB);
-
- #define FIRMWARE_DCN_36_DMUB "amdgpu/dcn_3_6_dmcub.bin"
- MODULE_FIRMWARE(FIRMWARE_DCN_36_DMUB);
-
- #define FIRMWARE_DCN_401_DMUB "amdgpu/dcn_4_0_1_dmcub.bin"
- MODULE_FIRMWARE(FIRMWARE_DCN_401_DMUB);
-
- #define FIRMWARE_DCN_42_DMUB "amdgpu/dcn_4_2_dmcub.bin"
- MODULE_FIRMWARE(FIRMWARE_DCN_42_DMUB);
-
- #define FIRMWARE_DCN_42B_DMUB "amdgpu/dcn_4_2_1_dmcub.bin"
- MODULE_FIRMWARE(FIRMWARE_DCN_42B_DMUB);
-
/**
* DOC: overview
*
@@@ -174,46 -127,7 +127,7 @@@
/* basic init/fini API */
static int amdgpu_dm_init(struct amdgpu_device *adev);
static void amdgpu_dm_fini(struct amdgpu_device *adev);
- static bool is_freesync_video_mode(const struct drm_display_mode *mode, struct amdgpu_dm_connector *aconnector);
static void reset_freesync_config_for_crtc(struct dm_crtc_state *new_crtc_state);
- static struct amdgpu_i2c_adapter *
- create_i2c(struct ddc_service *ddc_service, bool oem);
-
- static enum drm_mode_subconnector get_subconnector_type(struct dc_link *link)
- {
- switch (link->dpcd_caps.dongle_type) {
- case DISPLAY_DONGLE_NONE:
- return DRM_MODE_SUBCONNECTOR_Native;
- case DISPLAY_DONGLE_DP_VGA_CONVERTER:
- return DRM_MODE_SUBCONNECTOR_VGA;
- case DISPLAY_DONGLE_DP_DVI_CONVERTER:
- case DISPLAY_DONGLE_DP_DVI_DONGLE:
- return DRM_MODE_SUBCONNECTOR_DVID;
- case DISPLAY_DONGLE_DP_HDMI_CONVERTER:
- case DISPLAY_DONGLE_DP_HDMI_DONGLE:
- return DRM_MODE_SUBCONNECTOR_HDMIA;
- case DISPLAY_DONGLE_DP_HDMI_MISMATCHED_DONGLE:
- default:
- return DRM_MODE_SUBCONNECTOR_Unknown;
- }
- }
-
- static void update_subconnector_property(struct amdgpu_dm_connector *aconnector)
- {
- struct dc_link *link = aconnector->dc_link;
- struct drm_connector *connector = &aconnector->base;
- enum drm_mode_subconnector subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
-
- if (connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort)
- return;
-
- if (aconnector->dc_sink)
- subconnector = get_subconnector_type(link);
-
- drm_object_property_set_value(&connector->base,
- connector->dev->mode_config.dp_subconnector_property,
- subconnector);
- }
/*
* initializes drm_device display related structures, based on the information
@@@ -226,32 -140,23 +140,23 @@@ static int amdgpu_dm_initialize_drm_dev
/* removes and deallocates the drm structures, created by the above function */
static void amdgpu_dm_destroy_drm_device(struct amdgpu_display_manager *dm);
- static int amdgpu_dm_connector_init(struct amdgpu_display_manager *dm,
- struct amdgpu_dm_connector *amdgpu_dm_connector,
- u32 link_index,
- struct amdgpu_encoder *amdgpu_encoder);
- static int amdgpu_dm_encoder_init(struct drm_device *dev,
- struct amdgpu_encoder *aencoder,
- uint32_t link_index);
-
- static int amdgpu_dm_connector_get_modes(struct drm_connector *connector);
-
static int amdgpu_dm_atomic_setup_commit(struct drm_atomic_commit *state);
static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_commit *state);
+ static void dm_enable_per_frame_crtc_master_sync(struct dc_state *context);
static int amdgpu_dm_atomic_check(struct drm_device *dev,
struct drm_atomic_commit *state);
- static void handle_hpd_irq_helper(struct amdgpu_dm_connector *aconnector);
- static void handle_hpd_rx_irq(void *param);
-
- static void amdgpu_dm_backlight_set_level(struct amdgpu_display_manager *dm,
- int bl_idx,
- u32 user_brightness);
-
- static bool
+ STATIC_IFN_KUNIT bool
is_timing_unchanged_for_freesync(struct drm_crtc_state *old_crtc_state,
struct drm_crtc_state *new_crtc_state);
+
+ static inline void amdgpu_dm_exit_ips_for_hw_access(struct dc *dc)
+ {
+ if (dc->ctx->dmub_srv && !dc->ctx->dmub_srv->idle_exit_counter)
+ dc_exit_ips_for_hw_access(dc);
+ }
+
/*
* dm_vblank_get_counter
*
@@@ -332,40 -237,14 +237,14 @@@ static int dm_wait_for_idle(struct amdg
return 0;
}
- static bool dm_check_soft_reset(struct amdgpu_ip_block *ip_block)
- {
- return false;
- }
-
static int dm_soft_reset(struct amdgpu_ip_block *ip_block)
{
/* XXX todo */
return 0;
}
- static struct amdgpu_crtc *
- get_crtc_by_otg_inst(struct amdgpu_device *adev,
- int otg_inst)
- {
- struct drm_device *dev = adev_to_drm(adev);
- struct drm_crtc *crtc;
- struct amdgpu_crtc *amdgpu_crtc;
-
- if (WARN_ON(otg_inst == -1))
- return adev->mode_info.crtcs[0];
-
- list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
- amdgpu_crtc = to_amdgpu_crtc(crtc);
-
- if (amdgpu_crtc->otg_inst == otg_inst)
- return amdgpu_crtc;
- }
-
- return NULL;
- }
-
- static inline bool is_dc_timing_adjust_needed(struct dm_crtc_state *old_state,
- struct dm_crtc_state *new_state)
+ STATIC_IFN_KUNIT bool is_dc_timing_adjust_needed(struct dm_crtc_state *old_state,
+ struct dm_crtc_state *new_state)
{
if (new_state->stream->adjust.timing_adjust_pending)
return true;
@@@ -376,13 -255,14 +255,14 @@@
else
return false;
}
+ EXPORT_IF_KUNIT(is_dc_timing_adjust_needed);
/*
* DC will program planes with their z-order determined by their ordering
* in the dc_surface_updates array. This comparator is used to sort them
* by descending zpos.
*/
- static int dm_plane_layer_index_cmp(const void *a, const void *b)
+ STATIC_IFN_KUNIT int dm_plane_layer_index_cmp(const void *a, const void *b)
{
const struct dc_surface_update *sa = (struct dc_surface_update *)a;
const struct dc_surface_update *sb = (struct dc_surface_update *)b;
@@@ -390,6 -270,7 +270,7 @@@
/* Sort by descending dc_plane layer_index (i.e. normalized_zpos) */
return sb->surface->layer_index - sa->surface->layer_index;
}
+ EXPORT_IF_KUNIT(dm_plane_layer_index_cmp);
/**
* update_planes_and_stream_adapter() - Send planes to be updated in DC
@@@ -430,687 -311,6 +311,6 @@@ static inline bool update_planes_and_st
stream_update);
}
- /**
- * dm_pflip_high_irq() - Handle pageflip interrupt
- * @interrupt_params: ignored
- *
- * Handles the pageflip interrupt by notifying all interested parties
- * that the pageflip has been completed.
- */
- static void dm_pflip_high_irq(void *interrupt_params)
- {
- struct amdgpu_crtc *amdgpu_crtc;
- struct common_irq_params *irq_params = interrupt_params;
- struct amdgpu_device *adev = irq_params->adev;
- struct drm_device *dev = adev_to_drm(adev);
- unsigned long flags;
- struct drm_pending_vblank_event *e;
- u32 vpos, hpos, v_blank_start, v_blank_end;
- bool vrr_active;
-
- amdgpu_crtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_PFLIP);
-
- /* IRQ could occur when in initial stage */
- /* TODO work and BO cleanup */
- if (amdgpu_crtc == NULL) {
- drm_dbg_state(dev, "CRTC is null, returning.\n");
- return;
- }
-
- spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags);
-
- if (amdgpu_crtc->pflip_status != AMDGPU_FLIP_SUBMITTED) {
- drm_dbg_state(dev,
- "amdgpu_crtc->pflip_status = %d != AMDGPU_FLIP_SUBMITTED(%d) on crtc:%d[%p]\n",
- amdgpu_crtc->pflip_status, AMDGPU_FLIP_SUBMITTED,
- amdgpu_crtc->crtc_id, amdgpu_crtc);
- spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
- return;
- }
-
- /* page flip completed. */
- e = amdgpu_crtc->event;
- amdgpu_crtc->event = NULL;
-
- WARN_ON(!e);
-
- vrr_active = amdgpu_dm_crtc_vrr_active_irq(amdgpu_crtc);
-
- /* Fixed refresh rate, or VRR scanout position outside front-porch? */
- if (!vrr_active ||
- !dc_stream_get_scanoutpos(amdgpu_crtc->dm_irq_params.stream, &v_blank_start,
- &v_blank_end, &hpos, &vpos) ||
- (vpos < v_blank_start)) {
- /* Update to correct count and vblank timestamp if racing with
- * vblank irq. This also updates to the correct vblank timestamp
- * even in VRR mode, as scanout is past the front-porch atm.
- */
- drm_crtc_accurate_vblank_count(&amdgpu_crtc->base);
-
- /* Wake up userspace by sending the pageflip event with proper
- * count and timestamp of vblank of flip completion.
- */
- if (e) {
- drm_crtc_send_vblank_event(&amdgpu_crtc->base, e);
-
- /* Event sent, so done with vblank for this flip */
- drm_crtc_vblank_put(&amdgpu_crtc->base);
- }
- } else if (e) {
- /* VRR active and inside front-porch: vblank count and
- * timestamp for pageflip event will only be up to date after
- * drm_crtc_handle_vblank() has been executed from late vblank
- * irq handler after start of back-porch (vline 0). We queue the
- * pageflip event for send-out by drm_crtc_handle_vblank() with
- * updated timestamp and count, once it runs after us.
- *
- * We need to open-code this instead of using the helper
- * drm_crtc_arm_vblank_event(), as that helper would
- * call drm_crtc_accurate_vblank_count(), which we must
- * not call in VRR mode while we are in front-porch!
- */
-
- /* sequence will be replaced by real count during send-out. */
- e->sequence = drm_crtc_vblank_count(&amdgpu_crtc->base);
- e->pipe = amdgpu_crtc->crtc_id;
-
- list_add_tail(&e->base.link, &adev_to_drm(adev)->vblank_event_list);
- e = NULL;
- }
-
- /* Keep track of vblank of this flip for flip throttling. We use the
- * cooked hw counter, as that one incremented at start of this vblank
- * of pageflip completion, so last_flip_vblank is the forbidden count
- * for queueing new pageflips if vsync + VRR is enabled.
- */
- amdgpu_crtc->dm_irq_params.last_flip_vblank =
- amdgpu_get_vblank_counter_kms(&amdgpu_crtc->base);
-
- amdgpu_crtc->pflip_status = AMDGPU_FLIP_NONE;
- spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
-
- drm_dbg_state(dev,
- "crtc:%d[%p], pflip_stat:AMDGPU_FLIP_NONE, vrr[%d]-fp %d\n",
- amdgpu_crtc->crtc_id, amdgpu_crtc, vrr_active, (int)!e);
- }
-
- static void dm_handle_vmin_vmax_update(struct work_struct *offload_work)
- {
- struct vupdate_offload_work *work = container_of(offload_work, struct vupdate_offload_work, work);
- struct amdgpu_device *adev = work->adev;
- struct dc_stream_state *stream = work->stream;
- struct dc_crtc_timing_adjust *adjust = work->adjust;
-
- mutex_lock(&adev->dm.dc_lock);
- dc_stream_adjust_vmin_vmax(adev->dm.dc, stream, adjust);
- mutex_unlock(&adev->dm.dc_lock);
-
- dc_stream_release(stream);
- kfree(work->adjust);
- kfree(work);
- }
-
- static void schedule_dc_vmin_vmax(struct amdgpu_device *adev,
- struct dc_stream_state *stream,
- struct dc_crtc_timing_adjust *adjust)
- {
- struct vupdate_offload_work *offload_work = kzalloc_obj(*offload_work,
- GFP_NOWAIT);
- if (!offload_work) {
- drm_dbg_driver(adev_to_drm(adev), "Failed to allocate vupdate_offload_work\n");
- return;
- }
-
- struct dc_crtc_timing_adjust *adjust_copy = kzalloc_obj(*adjust_copy,
- GFP_NOWAIT);
- if (!adjust_copy) {
- drm_dbg_driver(adev_to_drm(adev), "Failed to allocate adjust_copy\n");
- kfree(offload_work);
- return;
- }
-
- dc_stream_retain(stream);
- memcpy(adjust_copy, adjust, sizeof(*adjust_copy));
-
- INIT_WORK(&offload_work->work, dm_handle_vmin_vmax_update);
- offload_work->adev = adev;
- offload_work->stream = stream;
- offload_work->adjust = adjust_copy;
-
- queue_work(system_percpu_wq, &offload_work->work);
- }
-
- /**
- * dm_crtc_high_irq_handler() - Common OTG vblank/flip event handling
- * @adev: amdgpu device
- * @acrtc: the CRTC to service
- *
- * Performs writeback completion, vblank event handling, CRC processing, VRR BTR
- * updates and pageflip completion delivery.
- *
- * On DCN this is driven by VUPDATE_NO_LOCK (the register latch point) from
- * dm_vupdate_high_irq(); on DCE it is driven by VLINE0 at the start of vblank
- * from dm_crtc_high_irq().
- */
- static void dm_crtc_high_irq_handler(struct amdgpu_device *adev,
- struct amdgpu_crtc *acrtc)
- {
- struct drm_writeback_job *job;
- unsigned long flags;
- int vrr_active;
- bool is_dcn = amdgpu_ip_version(adev, DCE_HWIP, 0) != 0;
-
- if (acrtc->wb_conn) {
- spin_lock_irqsave(&acrtc->wb_conn->job_lock, flags);
-
- if (acrtc->wb_pending) {
- job = list_first_entry_or_null(&acrtc->wb_conn->job_queue,
- struct drm_writeback_job,
- list_entry);
- acrtc->wb_pending = false;
- spin_unlock_irqrestore(&acrtc->wb_conn->job_lock, flags);
-
- if (job) {
- unsigned int v_total, refresh_hz;
- struct dc_stream_state *stream = acrtc->dm_irq_params.stream;
-
- v_total = stream->adjust.v_total_max ?
- stream->adjust.v_total_max : stream->timing.v_total;
- refresh_hz = div_u64((uint64_t) stream->timing.pix_clk_100hz *
- 100LL, (v_total * stream->timing.h_total));
- mdelay(1000 / refresh_hz);
-
- drm_writeback_signal_completion(acrtc->wb_conn, 0);
- dc_stream_fc_disable_writeback(adev->dm.dc,
- acrtc->dm_irq_params.stream, 0);
- }
- } else
- spin_unlock_irqrestore(&acrtc->wb_conn->job_lock, flags);
- }
-
- vrr_active = amdgpu_dm_crtc_vrr_active_irq(acrtc);
-
- drm_dbg_vbl(adev_to_drm(adev),
- "crtc:%d, vupdate-vrr:%d, planes:%d\n", acrtc->crtc_id,
- vrr_active, acrtc->dm_irq_params.active_planes);
-
- /**
- * Core vblank handling.
- *
- * On DCN this handler runs at VUPDATE_NO_LOCK, the register latch
- * point, which is the correct place to timestamp both VRR and non-VRR
- * vblanks.
- *
- * On DCE this handler runs at the start of front-porch, where only
- * non-VRR timestamping is valid; VRR vblank is deferred to
- * dm_vupdate_high_irq() after end of front-porch.
- */
- if (is_dcn || !vrr_active)
- amdgpu_dm_crtc_handle_vblank(acrtc);
-
- /**
- * Following stuff must happen at start of vblank, for crc
- * computation and below-the-range btr support in vrr mode.
- */
- amdgpu_dm_crtc_handle_crc_irq(&acrtc->base);
-
- /* BTR updates need to happen before VUPDATE on Vega and above. */
- if (adev->family < AMDGPU_FAMILY_AI)
- return;
-
- spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags);
-
- if (acrtc->dm_irq_params.stream &&
- acrtc->dm_irq_params.vrr_params.supported) {
- bool replay_en = acrtc->dm_irq_params.stream->link->replay_settings.replay_feature_enabled;
- bool psr_en = acrtc->dm_irq_params.stream->link->psr_settings.psr_feature_enabled;
- bool fs_active_var_en = acrtc->dm_irq_params.freesync_config.state == VRR_STATE_ACTIVE_VARIABLE;
-
- mod_freesync_handle_v_update(adev->dm.freesync_module,
- acrtc->dm_irq_params.stream,
- &acrtc->dm_irq_params.vrr_params);
-
- /* update vmin_vmax only if freesync is enabled, or only if PSR and REPLAY are disabled */
- if (fs_active_var_en || (!fs_active_var_en && !replay_en && !psr_en)) {
- schedule_dc_vmin_vmax(adev, acrtc->dm_irq_params.stream,
- &acrtc->dm_irq_params.vrr_params.adjust);
- }
- }
-
- /*
- * Deliver pageflip completion events (DCN only).
- *
- * Since GRPH_PFLIP is not used, VUPDATE_NO_LOCK is the flip latch
- * point. Deliver any pending pageflip completion event from here,
- * once HW has consumed the new address (the OTG no longer reports a
- * pending flip).
- *
- * Also handle the case here where there aren't any active planes and
- * DCN HUBP may be clock-gated, so the flip-pending status may be
- * undefined.
- */
- if (is_dcn && acrtc->pflip_status == AMDGPU_FLIP_SUBMITTED &&
- acrtc->event) {
-
- if (!dc_get_flip_pending_on_otg(adev->dm.dc, acrtc->otg_inst)) {
- drm_crtc_send_vblank_event(&acrtc->base, acrtc->event);
- acrtc->event = NULL;
- drm_crtc_vblank_put(&acrtc->base);
- acrtc->pflip_status = AMDGPU_FLIP_NONE;
- }
- /*
- * If the flip is still pending, leave it armed and
- * retry on the next vupdate.
- */
- } else if (is_dcn && acrtc->pflip_status == AMDGPU_FLIP_SUBMITTED &&
- acrtc->dm_irq_params.active_planes == 0) {
-
- if (acrtc->event) {
- drm_crtc_send_vblank_event(&acrtc->base, acrtc->event);
- acrtc->event = NULL;
- drm_crtc_vblank_put(&acrtc->base);
- }
- acrtc->pflip_status = AMDGPU_FLIP_NONE;
- }
-
- spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
- }
-
- static void dm_vupdate_high_irq(void *interrupt_params)
- {
- struct common_irq_params *irq_params = interrupt_params;
- struct amdgpu_device *adev = irq_params->adev;
- struct amdgpu_crtc *acrtc;
- struct drm_device *drm_dev;
- struct drm_vblank_crtc *vblank;
- ktime_t frame_duration_ns, previous_timestamp;
- unsigned long flags;
- int vrr_active;
-
- acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VUPDATE);
- if (!acrtc)
- return;
-
- vrr_active = amdgpu_dm_crtc_vrr_active_irq(acrtc);
- drm_dev = acrtc->base.dev;
- vblank = drm_crtc_vblank_crtc(&acrtc->base);
- previous_timestamp = atomic64_read(&irq_params->previous_timestamp);
- frame_duration_ns = vblank->time - previous_timestamp;
-
- if (frame_duration_ns > 0) {
- trace_amdgpu_refresh_rate_track(acrtc->base.index,
- frame_duration_ns,
- ktime_divns(NSEC_PER_SEC, frame_duration_ns));
- atomic64_set(&irq_params->previous_timestamp, vblank->time);
- }
-
- drm_dbg_vbl(drm_dev,
- "crtc:%d, vupdate-vrr:%d\n", acrtc->crtc_id,
- vrr_active);
-
- /*
- * On DCN, VUPDATE_NO_LOCK is the single OTG interrupt used to deliver
- * vblank and pageflip completion events; VSTARTUP and GRPH_PFLIP are
- * not used. Run the full handler here.
- */
- if (amdgpu_ip_version(adev, DCE_HWIP, 0) != 0) {
- dm_crtc_high_irq_handler(adev, acrtc);
- return;
- }
-
- /* DCE only below. */
-
- /* Core vblank handling is done here after end of front-porch in
- * vrr mode, as vblank timestamping will give valid results
- * while now done after front-porch. This will also deliver
- * page-flip completion events that have been queued to us
- * if a pageflip happened inside front-porch.
- */
- if (vrr_active && acrtc->dm_irq_params.stream) {
- bool replay_en = acrtc->dm_irq_params.stream->link->replay_settings.replay_feature_enabled;
- bool psr_en = acrtc->dm_irq_params.stream->link->psr_settings.psr_feature_enabled;
- bool fs_active_var_en = acrtc->dm_irq_params.freesync_config.state
- == VRR_STATE_ACTIVE_VARIABLE;
-
- amdgpu_dm_crtc_handle_vblank(acrtc);
-
- /* BTR processing for pre-DCE12 ASICs */
- if (adev->family < AMDGPU_FAMILY_AI) {
- spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags);
- mod_freesync_handle_v_update(
- adev->dm.freesync_module,
- acrtc->dm_irq_params.stream,
- &acrtc->dm_irq_params.vrr_params);
-
- if (fs_active_var_en || (!fs_active_var_en && !replay_en && !psr_en)) {
- schedule_dc_vmin_vmax(adev,
- acrtc->dm_irq_params.stream,
- &acrtc->dm_irq_params.vrr_params.adjust);
- }
- spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
- }
- }
- }
-
- /**
- * dm_crtc_high_irq() - Handles CRTC interrupt
- * @interrupt_params: used for determining the CRTC instance
- *
- * Handles the CRTC/VSYNC interrupt by notifying DRM's VBLANK event handler.
- *
- * Used on DCE (VLINE0, set to vblank start). On DCN the equivalent handling is
- * driven by VUPDATE_NO_LOCK in dm_vupdate_high_irq().
- */
- static void dm_crtc_high_irq(void *interrupt_params)
- {
- struct common_irq_params *irq_params = interrupt_params;
- struct amdgpu_device *adev = irq_params->adev;
- struct amdgpu_crtc *acrtc;
-
- acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VBLANK);
- if (!acrtc)
- return;
-
- dm_crtc_high_irq_handler(adev, acrtc);
- }
-
- #if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
- /**
- * dm_dcn_vertical_interrupt0_high_irq() - Handles OTG Vertical interrupt0 for
- * DCN generation ASICs
- * @interrupt_params: interrupt parameters
- *
- * Used to set crc window/read out crc value at vertical line 0 position
- */
- static void dm_dcn_vertical_interrupt0_high_irq(void *interrupt_params)
- {
- struct common_irq_params *irq_params = interrupt_params;
- struct amdgpu_device *adev = irq_params->adev;
- struct amdgpu_crtc *acrtc;
-
- acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VLINE0);
-
- if (!acrtc)
- return;
-
- amdgpu_dm_crtc_handle_crc_window_irq(&acrtc->base);
- }
- #endif /* CONFIG_DRM_AMD_SECURE_DISPLAY */
-
- /**
- * dmub_aux_setconfig_callback - Callback for AUX or SET_CONFIG command.
- * @adev: amdgpu_device pointer
- * @notify: dmub notification structure
- *
- * Dmub AUX or SET_CONFIG command completion processing callback
- * Copies dmub notification to DM which is to be read by AUX command.
- * issuing thread and also signals the event to wake up the thread.
- */
- static void dmub_aux_setconfig_callback(struct amdgpu_device *adev,
- struct dmub_notification *notify)
- {
- if (adev->dm.dmub_notify)
- memcpy(adev->dm.dmub_notify, notify, sizeof(struct dmub_notification));
- if (notify->type == DMUB_NOTIFICATION_AUX_REPLY)
- complete(&adev->dm.dmub_aux_transfer_done);
- }
-
- static void dmub_aux_fused_io_callback(struct amdgpu_device *adev,
- struct dmub_notification *notify)
- {
- if (!adev || !notify) {
- ASSERT(false);
- return;
- }
-
- const struct dmub_cmd_fused_request *req = ¬ify->fused_request;
- const uint8_t ddc_line = req->u.aux.ddc_line;
-
- if (ddc_line >= ARRAY_SIZE(adev->dm.fused_io)) {
- ASSERT(false);
- return;
- }
-
- struct fused_io_sync *sync = &adev->dm.fused_io[ddc_line];
-
- static_assert(sizeof(*req) <= sizeof(sync->reply_data), "Size mismatch");
- memcpy(sync->reply_data, req, sizeof(*req));
- complete(&sync->replied);
- }
-
- /**
- * dmub_hpd_callback - DMUB HPD interrupt processing callback.
- * @adev: amdgpu_device pointer
- * @notify: dmub notification structure
- *
- * Dmub Hpd interrupt processing callback. Gets displayindex through the
- * ink index and calls helper to do the processing.
- */
- static void dmub_hpd_callback(struct amdgpu_device *adev,
- struct dmub_notification *notify)
- {
- struct amdgpu_dm_connector *aconnector;
- struct amdgpu_dm_connector *hpd_aconnector = NULL;
- struct drm_connector *connector;
- struct drm_connector_list_iter iter;
- struct dc_link *link;
- u8 link_index = 0;
- struct drm_device *dev;
-
- if (adev == NULL)
- return;
-
- if (notify == NULL) {
- drm_err(adev_to_drm(adev), "DMUB HPD callback notification was NULL");
- return;
- }
-
- if (notify->link_index > adev->dm.dc->link_count) {
- drm_err(adev_to_drm(adev), "DMUB HPD index (%u)is abnormal", notify->link_index);
- return;
- }
-
- /* Skip DMUB HPD IRQ in suspend/resume. We will probe them later. */
- if (notify->type == DMUB_NOTIFICATION_HPD && adev->in_suspend) {
- drm_info(adev_to_drm(adev), "Skip DMUB HPD IRQ callback in suspend/resume\n");
- return;
- }
-
- link_index = notify->link_index;
- link = adev->dm.dc->links[link_index];
- dev = adev->dm.ddev;
-
- drm_connector_list_iter_begin(dev, &iter);
- drm_for_each_connector_iter(connector, &iter) {
-
- if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
- continue;
-
- aconnector = to_amdgpu_dm_connector(connector);
- if (link && aconnector->dc_link == link) {
- if (notify->type == DMUB_NOTIFICATION_HPD)
- drm_info(adev_to_drm(adev), "DMUB HPD IRQ callback: link_index=%u\n", link_index);
- else if (notify->type == DMUB_NOTIFICATION_HPD_IRQ)
- drm_info(adev_to_drm(adev), "DMUB HPD RX IRQ callback: link_index=%u\n", link_index);
- else
- drm_warn(adev_to_drm(adev), "DMUB Unknown HPD callback type %d, link_index=%u\n",
- notify->type, link_index);
-
- hpd_aconnector = aconnector;
- break;
- }
- }
- drm_connector_list_iter_end(&iter);
-
- if (hpd_aconnector) {
- if (notify->type == DMUB_NOTIFICATION_HPD) {
- if (hpd_aconnector->dc_link->hpd_status == (notify->hpd_status == DP_HPD_PLUG))
- drm_warn(adev_to_drm(adev), "DMUB reported hpd status unchanged. link_index=%u\n", link_index);
- handle_hpd_irq_helper(hpd_aconnector);
- } else if (notify->type == DMUB_NOTIFICATION_HPD_IRQ) {
- handle_hpd_rx_irq(hpd_aconnector);
- }
- }
- }
-
- /**
- * dmub_hpd_sense_callback - DMUB HPD sense processing callback.
- * @adev: amdgpu_device pointer
- * @notify: dmub notification structure
- *
- * HPD sense changes can occur during low power states and need to be
- * notified from firmware to driver.
- */
- static void dmub_hpd_sense_callback(struct amdgpu_device *adev,
- struct dmub_notification *notify)
- {
- drm_dbg_driver(adev_to_drm(adev), "DMUB HPD SENSE callback.\n");
- }
-
- /**
- * register_dmub_notify_callback - Sets callback for DMUB notify
- * @adev: amdgpu_device pointer
- * @type: Type of dmub notification
- * @callback: Dmub interrupt callback function
- * @dmub_int_thread_offload: offload indicator
- *
- * API to register a dmub callback handler for a dmub notification
- * Also sets indicator whether callback processing to be offloaded.
- * to dmub interrupt handling thread
- * Return: true if successfully registered, false if there is existing registration
- */
- static bool register_dmub_notify_callback(struct amdgpu_device *adev,
- enum dmub_notification_type type,
- dmub_notify_interrupt_callback_t callback,
- bool dmub_int_thread_offload)
- {
- if (callback != NULL && type < ARRAY_SIZE(adev->dm.dmub_thread_offload)) {
- adev->dm.dmub_callback[type] = callback;
- adev->dm.dmub_thread_offload[type] = dmub_int_thread_offload;
- } else
- return false;
-
- return true;
- }
-
- static void dm_handle_hpd_work(struct work_struct *work)
- {
- struct dmub_hpd_work *dmub_hpd_wrk;
-
- dmub_hpd_wrk = container_of(work, struct dmub_hpd_work, handle_hpd_work);
-
- if (!dmub_hpd_wrk->dmub_notify) {
- drm_err(adev_to_drm(dmub_hpd_wrk->adev), "dmub_hpd_wrk dmub_notify is NULL");
- return;
- }
-
- if (dmub_hpd_wrk->dmub_notify->type < ARRAY_SIZE(dmub_hpd_wrk->adev->dm.dmub_callback)) {
- dmub_hpd_wrk->adev->dm.dmub_callback[dmub_hpd_wrk->dmub_notify->type](dmub_hpd_wrk->adev,
- dmub_hpd_wrk->dmub_notify);
- }
-
- kfree(dmub_hpd_wrk->dmub_notify);
- kfree(dmub_hpd_wrk);
-
- }
-
- static const char *dmub_notification_type_str(enum dmub_notification_type e)
- {
- switch (e) {
- case DMUB_NOTIFICATION_NO_DATA:
- return "NO_DATA";
- case DMUB_NOTIFICATION_AUX_REPLY:
- return "AUX_REPLY";
- case DMUB_NOTIFICATION_HPD:
- return "HPD";
- case DMUB_NOTIFICATION_HPD_IRQ:
- return "HPD_IRQ";
- case DMUB_NOTIFICATION_SET_CONFIG_REPLY:
- return "SET_CONFIG_REPLY";
- case DMUB_NOTIFICATION_DPIA_NOTIFICATION:
- return "DPIA_NOTIFICATION";
- case DMUB_NOTIFICATION_HPD_SENSE_NOTIFY:
- return "HPD_SENSE_NOTIFY";
- case DMUB_NOTIFICATION_FUSED_IO:
- return "FUSED_IO";
- default:
- return "<unknown>";
- }
- }
-
- #define DMUB_TRACE_MAX_READ 64
- /**
- * dm_dmub_outbox1_low_irq() - Handles Outbox interrupt
- * @interrupt_params: used for determining the Outbox instance
- *
- * Handles the Outbox Interrupt
- * event handler.
- */
- static void dm_dmub_outbox1_low_irq(void *interrupt_params)
- {
- struct dmub_notification notify = {0};
- struct common_irq_params *irq_params = interrupt_params;
- struct amdgpu_device *adev = irq_params->adev;
- struct amdgpu_display_manager *dm = &adev->dm;
- struct dmcub_trace_buf_entry entry = { 0 };
- u32 count = 0;
- struct dmub_hpd_work *dmub_hpd_wrk;
-
- do {
- if (dc_dmub_srv_get_dmub_outbox0_msg(dm->dc, &entry)) {
- trace_amdgpu_dmub_trace_high_irq(entry.trace_code, entry.tick_count,
- entry.param0, entry.param1);
-
- drm_dbg_driver(adev_to_drm(adev), "trace_code:%u, tick_count:%u, param0:%u, param1:%u\n",
- entry.trace_code, entry.tick_count, entry.param0, entry.param1);
- } else
- break;
-
- count++;
-
- } while (count <= DMUB_TRACE_MAX_READ);
-
- if (count > DMUB_TRACE_MAX_READ)
- drm_dbg_driver(adev_to_drm(adev), "Warning : count > DMUB_TRACE_MAX_READ");
-
- if (dc_enable_dmub_notifications(adev->dm.dc) &&
- irq_params->irq_src == DC_IRQ_SOURCE_DMCUB_OUTBOX) {
-
- do {
- dc_stat_get_dmub_notification(adev->dm.dc, ¬ify);
- if (notify.type >= ARRAY_SIZE(dm->dmub_thread_offload)) {
- drm_err(adev_to_drm(adev), "DM: notify type %d invalid!", notify.type);
- continue;
- }
- if (!dm->dmub_callback[notify.type]) {
- drm_warn(adev_to_drm(adev), "DMUB notification skipped due to no handler: type=%s\n",
- dmub_notification_type_str(notify.type));
- continue;
- }
- if (dm->dmub_thread_offload[notify.type] == true) {
- dmub_hpd_wrk = kzalloc_obj(*dmub_hpd_wrk,
- GFP_ATOMIC);
- if (!dmub_hpd_wrk) {
- drm_err(adev_to_drm(adev), "Failed to allocate dmub_hpd_wrk");
- return;
- }
- dmub_hpd_wrk->dmub_notify = kmemdup(¬ify, sizeof(struct dmub_notification),
- GFP_ATOMIC);
- if (!dmub_hpd_wrk->dmub_notify) {
- kfree(dmub_hpd_wrk);
- drm_err(adev_to_drm(adev), "Failed to allocate dmub_hpd_wrk->dmub_notify");
- return;
- }
- INIT_WORK(&dmub_hpd_wrk->handle_hpd_work, dm_handle_hpd_work);
- dmub_hpd_wrk->adev = adev;
- queue_work(adev->dm.delayed_hpd_wq, &dmub_hpd_wrk->handle_hpd_work);
- } else {
- dm->dmub_callback[notify.type](adev, ¬ify);
- }
- } while (notify.pending_notification);
- }
- }
-
static int dm_set_clockgating_state(struct amdgpu_ip_block *ip_block,
enum amd_clockgating_state state)
{
@@@ -1127,401 -327,6 +327,6 @@@ static int dm_set_powergating_state(str
static int dm_early_init(struct amdgpu_ip_block *ip_block);
/* Allocate memory for FBC compressed data */
- static void amdgpu_dm_fbc_init(struct drm_connector *connector)
- {
- struct amdgpu_device *adev = drm_to_adev(connector->dev);
- struct dm_compressor_info *compressor = &adev->dm.compressor;
- struct amdgpu_dm_connector *aconn = to_amdgpu_dm_connector(connector);
- struct drm_display_mode *mode;
- unsigned long max_size = 0;
-
- if (adev->dm.dc->fbc_compressor == NULL)
- return;
-
- if (aconn->dc_link->connector_signal != SIGNAL_TYPE_EDP)
- return;
-
- if (compressor->bo_ptr)
- return;
-
-
- list_for_each_entry(mode, &connector->modes, head) {
- if (max_size < (unsigned long) mode->htotal * mode->vtotal)
- max_size = (unsigned long) mode->htotal * mode->vtotal;
- }
-
- if (max_size) {
- int r = amdgpu_bo_create_kernel(adev, max_size * 4, PAGE_SIZE,
- AMDGPU_GEM_DOMAIN_GTT, &compressor->bo_ptr,
- &compressor->gpu_addr, &compressor->cpu_addr);
-
- if (r)
- drm_err(adev_to_drm(adev), "DM: Failed to initialize FBC\n");
- else {
- adev->dm.dc->ctx->fbc_gpu_addr = compressor->gpu_addr;
- drm_info(adev_to_drm(adev), "DM: FBC alloc %lu\n", max_size*4);
- }
-
- }
-
- }
-
- static int amdgpu_dm_audio_component_get_eld(struct device *kdev, int port,
- int pipe, bool *enabled,
- unsigned char *buf, int max_bytes)
- {
- struct drm_device *dev = dev_get_drvdata(kdev);
- struct amdgpu_device *adev = drm_to_adev(dev);
- struct drm_connector *connector;
- struct drm_connector_list_iter conn_iter;
- struct amdgpu_dm_connector *aconnector;
- int ret = 0;
-
- *enabled = false;
-
- mutex_lock(&adev->dm.audio_lock);
-
- drm_connector_list_iter_begin(dev, &conn_iter);
- drm_for_each_connector_iter(connector, &conn_iter) {
-
- if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
- continue;
-
- aconnector = to_amdgpu_dm_connector(connector);
- if (aconnector->audio_inst != port)
- continue;
-
- *enabled = true;
- mutex_lock(&connector->eld_mutex);
- ret = drm_eld_size(connector->eld);
- memcpy(buf, connector->eld, min(max_bytes, ret));
- mutex_unlock(&connector->eld_mutex);
-
- break;
- }
- drm_connector_list_iter_end(&conn_iter);
-
- mutex_unlock(&adev->dm.audio_lock);
-
- drm_dbg_kms(adev_to_drm(adev), "Get ELD : idx=%d ret=%d en=%d\n", port, ret, *enabled);
-
- return ret;
- }
-
- static const struct drm_audio_component_ops amdgpu_dm_audio_component_ops = {
- .get_eld = amdgpu_dm_audio_component_get_eld,
- };
-
- static int amdgpu_dm_audio_component_bind(struct device *kdev,
- struct device *hda_kdev, void *data)
- {
- struct drm_device *dev = dev_get_drvdata(kdev);
- struct amdgpu_device *adev = drm_to_adev(dev);
- struct drm_audio_component *acomp = data;
-
- acomp->ops = &amdgpu_dm_audio_component_ops;
- acomp->dev = kdev;
- adev->dm.audio_component = acomp;
-
- return 0;
- }
-
- static void amdgpu_dm_audio_component_unbind(struct device *kdev,
- struct device *hda_kdev, void *data)
- {
- struct amdgpu_device *adev = drm_to_adev(dev_get_drvdata(kdev));
- struct drm_audio_component *acomp = data;
-
- acomp->ops = NULL;
- acomp->dev = NULL;
- adev->dm.audio_component = NULL;
- }
-
- static const struct component_ops amdgpu_dm_audio_component_bind_ops = {
- .bind = amdgpu_dm_audio_component_bind,
- .unbind = amdgpu_dm_audio_component_unbind,
- };
-
- static int amdgpu_dm_audio_init(struct amdgpu_device *adev)
- {
- int i, ret;
-
- if (!amdgpu_audio)
- return 0;
-
- adev->mode_info.audio.enabled = true;
-
- adev->mode_info.audio.num_pins = adev->dm.dc->res_pool->audio_count;
-
- for (i = 0; i < adev->mode_info.audio.num_pins; i++) {
- adev->mode_info.audio.pin[i].channels = -1;
- adev->mode_info.audio.pin[i].rate = -1;
- adev->mode_info.audio.pin[i].bits_per_sample = -1;
- adev->mode_info.audio.pin[i].status_bits = 0;
- adev->mode_info.audio.pin[i].category_code = 0;
- adev->mode_info.audio.pin[i].connected = false;
- adev->mode_info.audio.pin[i].id =
- adev->dm.dc->res_pool->audios[i]->inst;
- adev->mode_info.audio.pin[i].offset = 0;
- }
-
- ret = component_add(adev->dev, &amdgpu_dm_audio_component_bind_ops);
- if (ret < 0)
- return ret;
-
- adev->dm.audio_registered = true;
-
- return 0;
- }
-
- static void amdgpu_dm_audio_fini(struct amdgpu_device *adev)
- {
- if (!amdgpu_audio)
- return;
-
- if (!adev->mode_info.audio.enabled)
- return;
-
- if (adev->dm.audio_registered) {
- component_del(adev->dev, &amdgpu_dm_audio_component_bind_ops);
- adev->dm.audio_registered = false;
- }
-
- /* TODO: Disable audio? */
-
- adev->mode_info.audio.enabled = false;
- }
-
- static void amdgpu_dm_audio_eld_notify(struct amdgpu_device *adev, int pin)
- {
- struct drm_audio_component *acomp = adev->dm.audio_component;
-
- if (acomp && acomp->audio_ops && acomp->audio_ops->pin_eld_notify) {
- drm_dbg_kms(adev_to_drm(adev), "Notify ELD: %d\n", pin);
-
- acomp->audio_ops->pin_eld_notify(acomp->audio_ops->audio_ptr,
- pin, -1);
- }
- }
-
- static int dm_dmub_hw_init(struct amdgpu_device *adev)
- {
- const struct dmcub_firmware_header_v1_0 *hdr;
- struct dmub_srv *dmub_srv = adev->dm.dmub_srv;
- struct dmub_srv_fb_info *fb_info = adev->dm.dmub_fb_info;
- const struct firmware *dmub_fw = adev->dm.dmub_fw;
- struct dc *dc = adev->dm.dc;
- struct dmcu *dmcu = adev->dm.dc->res_pool->dmcu;
- struct abm *abm = adev->dm.dc->res_pool->abm;
- struct dc_context *ctx = adev->dm.dc->ctx;
- struct dmub_srv_hw_params hw_params;
- enum dmub_status status;
- const unsigned char *fw_inst_const, *fw_bss_data;
- u32 i, fw_inst_const_size, fw_bss_data_size;
- bool has_hw_support;
-
- if (!dmub_srv)
- /* DMUB isn't supported on the ASIC. */
- return 0;
-
- if (!fb_info) {
- drm_err(adev_to_drm(adev), "No framebuffer info for DMUB service.\n");
- return -EINVAL;
- }
-
- if (!dmub_fw) {
- /* Firmware required for DMUB support. */
- drm_err(adev_to_drm(adev), "No firmware provided for DMUB.\n");
- return -EINVAL;
- }
-
- /* initialize register offsets for ASICs with runtime initialization available */
- if (dmub_srv->hw_funcs.init_reg_offsets)
- dmub_srv->hw_funcs.init_reg_offsets(dmub_srv, ctx);
-
- status = dmub_srv_has_hw_support(dmub_srv, &has_hw_support);
- if (status != DMUB_STATUS_OK) {
- drm_err(adev_to_drm(adev), "Error checking HW support for DMUB: %d\n", status);
- return -EINVAL;
- }
-
- if (!has_hw_support) {
- drm_info(adev_to_drm(adev), "DMUB unsupported on ASIC\n");
- return 0;
- }
-
- /* Reset DMCUB if it was previously running - before we overwrite its memory. */
- status = dmub_srv_hw_reset(dmub_srv);
- if (status != DMUB_STATUS_OK)
- drm_warn(adev_to_drm(adev), "Error resetting DMUB HW: %d\n", status);
-
- hdr = (const struct dmcub_firmware_header_v1_0 *)dmub_fw->data;
-
- fw_inst_const = dmub_fw->data +
- le32_to_cpu(hdr->header.ucode_array_offset_bytes) +
- PSP_HEADER_BYTES_256;
-
- fw_bss_data = dmub_fw->data +
- le32_to_cpu(hdr->header.ucode_array_offset_bytes) +
- le32_to_cpu(hdr->inst_const_bytes);
-
- /* Copy firmware and bios info into FB memory. */
- fw_inst_const_size = adev->dm.fw_inst_size;
-
- fw_bss_data_size = le32_to_cpu(hdr->bss_data_bytes);
-
- /* if adev->firmware.load_type == AMDGPU_FW_LOAD_PSP,
- * amdgpu_ucode_init_single_fw will load dmub firmware
- * fw_inst_const part to cw0; otherwise, the firmware back door load
- * will be done by dm_dmub_hw_init
- */
- if (adev->firmware.load_type != AMDGPU_FW_LOAD_PSP) {
- memcpy(fb_info->fb[DMUB_WINDOW_0_INST_CONST].cpu_addr, fw_inst_const,
- fw_inst_const_size);
- }
-
- if (fw_bss_data_size)
- memcpy(fb_info->fb[DMUB_WINDOW_2_BSS_DATA].cpu_addr,
- fw_bss_data, fw_bss_data_size);
-
- /* Copy firmware bios info into FB memory. */
- memcpy(fb_info->fb[DMUB_WINDOW_3_VBIOS].cpu_addr, adev->bios,
- adev->bios_size);
-
- /* Reset regions that need to be reset. */
- memset(fb_info->fb[DMUB_WINDOW_4_MAILBOX].cpu_addr, 0,
- fb_info->fb[DMUB_WINDOW_4_MAILBOX].size);
-
- memset(fb_info->fb[DMUB_WINDOW_5_TRACEBUFF].cpu_addr, 0,
- fb_info->fb[DMUB_WINDOW_5_TRACEBUFF].size);
-
- memset(fb_info->fb[DMUB_WINDOW_6_FW_STATE].cpu_addr, 0,
- fb_info->fb[DMUB_WINDOW_6_FW_STATE].size);
-
- memset(fb_info->fb[DMUB_WINDOW_SHARED_STATE].cpu_addr, 0,
- fb_info->fb[DMUB_WINDOW_SHARED_STATE].size);
-
- /* Initialize hardware. */
- memset(&hw_params, 0, sizeof(hw_params));
- hw_params.soc_fb_info.fb_base = adev->gmc.fb_start;
- hw_params.soc_fb_info.fb_offset = adev->vm_manager.vram_base_offset;
-
- /* backdoor load firmware and trigger dmub running */
- if (adev->firmware.load_type != AMDGPU_FW_LOAD_PSP)
- hw_params.load_inst_const = true;
-
- if (dmcu)
- hw_params.psp_version = dmcu->psp_version;
-
- for (i = 0; i < fb_info->num_fb; ++i)
- hw_params.fb[i] = &fb_info->fb[i];
-
- /* Enable usb4 dpia in the FW APU */
- if (dc->caps.is_apu &&
- dc->res_pool->usb4_dpia_count != 0 &&
- !dc->debug.dpia_debug.bits.disable_dpia) {
- hw_params.dpia_supported = true;
- hw_params.disable_dpia = dc->debug.dpia_debug.bits.disable_dpia;
- hw_params.dpia_hpd_int_enable_supported = false;
- hw_params.enable_non_transparent_setconfig = dc->config.consolidated_dpia_dp_lt;
- hw_params.disable_dpia_bw_allocation = !dc->config.usb4_bw_alloc_support;
- }
-
- switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
- case IP_VERSION(3, 5, 0):
- case IP_VERSION(3, 5, 1):
- case IP_VERSION(3, 6, 0):
- case IP_VERSION(4, 2, 0):
- case IP_VERSION(4, 2, 1):
- hw_params.ips_sequential_ono = adev->external_rev_id > 0x10;
- hw_params.lower_hbr3_phy_ssc = true;
- break;
- default:
- break;
- }
-
- status = dmub_srv_hw_init(dmub_srv, &hw_params);
- if (status != DMUB_STATUS_OK) {
- drm_err(adev_to_drm(adev), "Error initializing DMUB HW: %d\n", status);
- return -EINVAL;
- }
-
- /* Wait for firmware load to finish. */
- status = dmub_srv_wait_for_auto_load(dmub_srv, 100000);
- if (status != DMUB_STATUS_OK)
- drm_warn(adev_to_drm(adev), "Wait for DMUB auto-load failed: %d\n", status);
-
- /* Init DMCU and ABM if available. */
- if (dmcu && abm) {
- dmcu->funcs->dmcu_init(dmcu);
- abm->dmcu_is_running = dmcu->funcs->is_dmcu_initialized(dmcu);
- }
-
- if (!adev->dm.dc->ctx->dmub_srv)
- adev->dm.dc->ctx->dmub_srv = dc_dmub_srv_create(adev->dm.dc, dmub_srv);
- if (!adev->dm.dc->ctx->dmub_srv) {
- drm_err(adev_to_drm(adev), "Couldn't allocate DC DMUB server!\n");
- return -ENOMEM;
- }
-
- drm_info(adev_to_drm(adev), "DMUB hardware initialized: version=0x%08X\n",
- adev->dm.dmcub_fw_version);
-
- /* Keeping sanity checks off if
- * DCN31 >= 4.0.59.0
- * DCN314 >= 8.0.16.0
- * Otherwise, turn on sanity checks
- */
- switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
- case IP_VERSION(3, 1, 2):
- case IP_VERSION(3, 1, 3):
- if (adev->dm.dmcub_fw_version &&
- adev->dm.dmcub_fw_version >= DMUB_FW_VERSION(4, 0, 0) &&
- adev->dm.dmcub_fw_version < DMUB_FW_VERSION(4, 0, 59))
- adev->dm.dc->debug.sanity_checks = true;
- break;
- case IP_VERSION(3, 1, 4):
- if (adev->dm.dmcub_fw_version &&
- adev->dm.dmcub_fw_version >= DMUB_FW_VERSION(4, 0, 0) &&
- adev->dm.dmcub_fw_version < DMUB_FW_VERSION(8, 0, 16))
- adev->dm.dc->debug.sanity_checks = true;
- break;
- default:
- break;
- }
-
- return 0;
- }
-
- static void dm_dmub_hw_resume(struct amdgpu_device *adev)
- {
- struct dmub_srv *dmub_srv = adev->dm.dmub_srv;
- enum dmub_status status;
- bool init;
- int r;
-
- if (!dmub_srv) {
- /* DMUB isn't supported on the ASIC. */
- return;
- }
-
- status = dmub_srv_is_hw_init(dmub_srv, &init);
- if (status != DMUB_STATUS_OK)
- drm_warn(adev_to_drm(adev), "DMUB hardware init check failed: %d\n", status);
-
- if (status == DMUB_STATUS_OK && init) {
- /* Wait for firmware load to finish. */
- status = dmub_srv_wait_for_auto_load(dmub_srv, 100000);
- if (status != DMUB_STATUS_OK)
- drm_warn(adev_to_drm(adev), "Wait for DMUB auto-load failed: %d\n", status);
- } else {
- /* Perform the full hardware initialization. */
- r = dm_dmub_hw_init(adev);
- if (r)
- drm_err(adev_to_drm(adev), "DMUB interface failed to initialize: status=%d\n", r);
- }
- }
-
static void mmhub_read_system_context(struct amdgpu_device *adev, struct dc_phy_addr_space_config *pa_config)
{
u64 pt_base;
@@@ -1599,151 -404,6 +404,6 @@@
}
- static void force_connector_state(
- struct amdgpu_dm_connector *aconnector,
- enum drm_connector_force force_state)
- {
- struct drm_connector *connector = &aconnector->base;
-
- mutex_lock(&connector->dev->mode_config.mutex);
- aconnector->base.force = force_state;
- mutex_unlock(&connector->dev->mode_config.mutex);
-
- mutex_lock(&aconnector->hpd_lock);
- drm_kms_helper_connector_hotplug_event(connector);
- mutex_unlock(&aconnector->hpd_lock);
- }
-
- static void dm_handle_hpd_rx_offload_work(struct work_struct *work)
- {
- struct hpd_rx_irq_offload_work *offload_work;
- struct amdgpu_dm_connector *aconnector;
- struct dc_link *dc_link;
- struct amdgpu_device *adev;
- enum dc_connection_type new_connection_type = dc_connection_none;
- unsigned long flags;
- union test_response test_response;
-
- memset(&test_response, 0, sizeof(test_response));
-
- offload_work = container_of(work, struct hpd_rx_irq_offload_work, work);
- aconnector = offload_work->offload_wq->aconnector;
- adev = offload_work->adev;
-
- if (!aconnector) {
- drm_err(adev_to_drm(adev), "Can't retrieve aconnector in hpd_rx_irq_offload_work");
- goto skip;
- }
-
- dc_link = aconnector->dc_link;
-
- mutex_lock(&aconnector->hpd_lock);
- if (!dc_link_detect_connection_type(dc_link, &new_connection_type))
- drm_err(adev_to_drm(adev), "KMS: Failed to detect connector\n");
- mutex_unlock(&aconnector->hpd_lock);
-
- if (new_connection_type == dc_connection_none)
- goto skip;
-
- if (amdgpu_in_reset(adev))
- goto skip;
-
- if (offload_work->data.bytes.device_service_irq.bits.UP_REQ_MSG_RDY ||
- offload_work->data.bytes.device_service_irq.bits.DOWN_REP_MSG_RDY) {
- dm_handle_mst_sideband_msg_ready_event(&aconnector->mst_mgr, DOWN_OR_UP_MSG_RDY_EVENT);
- spin_lock_irqsave(&offload_work->offload_wq->offload_lock, flags);
- offload_work->offload_wq->is_handling_mst_msg_rdy_event = false;
- spin_unlock_irqrestore(&offload_work->offload_wq->offload_lock, flags);
- goto skip;
- }
-
- mutex_lock(&adev->dm.dc_lock);
- if (offload_work->data.bytes.device_service_irq.bits.AUTOMATED_TEST) {
- dc_link_dp_handle_automated_test(dc_link);
-
- if (aconnector->timing_changed) {
- /* force connector disconnect and reconnect */
- force_connector_state(aconnector, DRM_FORCE_OFF);
- msleep(100);
- force_connector_state(aconnector, DRM_FORCE_UNSPECIFIED);
- }
-
- test_response.bits.ACK = 1;
-
- core_link_write_dpcd(
- dc_link,
- DP_TEST_RESPONSE,
- &test_response.raw,
- sizeof(test_response));
- } else if ((dc_link->connector_signal != SIGNAL_TYPE_EDP) &&
- dc_link_check_link_loss_status(dc_link, &offload_work->data) &&
- dc_link_dp_allow_hpd_rx_irq(dc_link)) {
- /* offload_work->data is from handle_hpd_rx_irq->
- * schedule_hpd_rx_offload_work.this is defer handle
- * for hpd short pulse. upon here, link status may be
- * changed, need get latest link status from dpcd
- * registers. if link status is good, skip run link
- * training again.
- */
- union hpd_irq_data irq_data;
-
- memset(&irq_data, 0, sizeof(irq_data));
-
- /* before dc_link_dp_handle_link_loss, allow new link lost handle
- * request be added to work queue if link lost at end of dc_link_
- * dp_handle_link_loss
- */
- spin_lock_irqsave(&offload_work->offload_wq->offload_lock, flags);
- offload_work->offload_wq->is_handling_link_loss = false;
- spin_unlock_irqrestore(&offload_work->offload_wq->offload_lock, flags);
-
- if ((dc_link_dp_read_hpd_rx_irq_data(dc_link, &irq_data) == DC_OK) &&
- dc_link_check_link_loss_status(dc_link, &irq_data))
- dc_link_dp_handle_link_loss(dc_link);
- }
- mutex_unlock(&adev->dm.dc_lock);
-
- skip:
- kfree(offload_work);
-
- }
-
- static struct hpd_rx_irq_offload_work_queue *hpd_rx_irq_create_workqueue(struct amdgpu_device *adev)
- {
- struct dc *dc = adev->dm.dc;
- int max_caps = dc->caps.max_links;
- int i = 0;
- struct hpd_rx_irq_offload_work_queue *hpd_rx_offload_wq = NULL;
-
- hpd_rx_offload_wq = kzalloc_objs(*hpd_rx_offload_wq, max_caps);
-
- if (!hpd_rx_offload_wq)
- return NULL;
-
-
- for (i = 0; i < max_caps; i++) {
- hpd_rx_offload_wq[i].wq =
- create_singlethread_workqueue("amdgpu_dm_hpd_rx_offload_wq");
-
- if (hpd_rx_offload_wq[i].wq == NULL) {
- drm_err(adev_to_drm(adev), "create amdgpu_dm_hpd_rx_offload_wq fail!");
- goto out_err;
- }
-
- spin_lock_init(&hpd_rx_offload_wq[i].offload_lock);
- }
-
- return hpd_rx_offload_wq;
-
- out_err:
- for (i = 0; i < max_caps; i++) {
- if (hpd_rx_offload_wq[i].wq)
- destroy_workqueue(hpd_rx_offload_wq[i].wq);
- }
- kfree(hpd_rx_offload_wq);
- return NULL;
- }
-
struct amdgpu_stutter_quirk {
u16 chip_vendor;
u16 chip_device;
@@@ -1829,119 -489,6 +489,6 @@@ dm_free_gpu_mem
}
- static enum dmub_status
- dm_dmub_send_vbios_gpint_command(struct amdgpu_device *adev,
- enum dmub_gpint_command command_code,
- uint16_t param,
- uint32_t timeout_us)
- {
- union dmub_gpint_data_register reg, test;
- uint32_t i;
-
- /* Assume that VBIOS DMUB is ready to take commands */
-
- reg.bits.status = 1;
- reg.bits.command_code = command_code;
- reg.bits.param = param;
-
- cgs_write_register(adev->dm.cgs_device, 0x34c0 + 0x01f8, reg.all);
-
- for (i = 0; i < timeout_us; ++i) {
- udelay(1);
-
- /* Check if our GPINT got acked */
- reg.bits.status = 0;
- test = (union dmub_gpint_data_register)
- cgs_read_register(adev->dm.cgs_device, 0x34c0 + 0x01f8);
-
- if (test.all == reg.all)
- return DMUB_STATUS_OK;
- }
-
- return DMUB_STATUS_TIMEOUT;
- }
-
- static void *dm_dmub_get_vbios_bounding_box(struct amdgpu_device *adev)
- {
- void *bb;
- long long addr;
- unsigned int bb_size;
- int i = 0;
- uint16_t chunk;
- enum dmub_gpint_command send_addrs[] = {
- DMUB_GPINT__SET_BB_ADDR_WORD0,
- DMUB_GPINT__SET_BB_ADDR_WORD1,
- DMUB_GPINT__SET_BB_ADDR_WORD2,
- DMUB_GPINT__SET_BB_ADDR_WORD3,
- };
- enum dmub_status ret;
-
- switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
- case IP_VERSION(4, 0, 1):
- bb_size = sizeof(struct dml2_soc_bb);
- break;
- case IP_VERSION(4, 2, 0):
- case IP_VERSION(4, 2, 1):
- bb_size = sizeof(struct dml2_soc_bb);
- break;
- default:
- return NULL;
- }
-
- bb = dm_allocate_gpu_mem(adev,
- DC_MEM_ALLOC_TYPE_GART,
- bb_size,
- &addr);
- if (!bb)
- return NULL;
-
- for (i = 0; i < 4; i++) {
- /* Extract 16-bit chunk */
- chunk = ((uint64_t) addr >> (i * 16)) & 0xFFFF;
- /* Send the chunk */
- ret = dm_dmub_send_vbios_gpint_command(adev, send_addrs[i], chunk, 30000);
- if (ret != DMUB_STATUS_OK)
- goto free_bb;
- }
-
- /* Now ask DMUB to copy the bb */
- ret = dm_dmub_send_vbios_gpint_command(adev, DMUB_GPINT__BB_COPY, 1, 200000);
- if (ret != DMUB_STATUS_OK)
- goto free_bb;
-
- return bb;
-
- free_bb:
- dm_free_gpu_mem(adev, DC_MEM_ALLOC_TYPE_GART, (void *) bb);
- return NULL;
-
- }
-
- static enum dmub_ips_disable_type dm_get_default_ips_mode(
- struct amdgpu_device *adev)
- {
- enum dmub_ips_disable_type ret = DMUB_IPS_ENABLE;
-
- switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
- case IP_VERSION(3, 5, 0):
- case IP_VERSION(3, 6, 0):
- case IP_VERSION(3, 5, 1):
- ret = DMUB_IPS_RCG_IN_ACTIVE_IPS2_IN_OFF;
- break;
- case IP_VERSION(4, 2, 0):
- case IP_VERSION(4, 2, 1):
- ret = DMUB_IPS_ENABLE;
- break;
- default:
- /* ASICs older than DCN35 do not have IPSs */
- if (amdgpu_ip_version(adev, DCE_HWIP, 0) < IP_VERSION(3, 5, 0))
- ret = DMUB_IPS_DISABLE_ALL;
- break;
- }
-
- return ret;
- }
-
static int amdgpu_dm_init_power_module(struct amdgpu_display_manager *dm)
{
struct mod_power_init_params init_data[MAX_NUM_EDP];
@@@ -2006,40 -553,6 +553,6 @@@
return 0;
}
- static void hdmi_frl_status_polling_work(struct work_struct *work)
- {
- struct amdgpu_display_manager *dm =
- container_of(to_delayed_work(work), struct amdgpu_display_manager,
- hdmi_frl_status_polling_work);
- struct dc *dc = dm->dc;
- struct dc_link *dc_link;
- bool link_update = false;
-
- for (int i = 0; i < MAX_LINKS; i++) {
- dc_link = dc->links[i];
-
- if (!dc_link || !dc_link->local_sink)
- continue;
-
- if (!dc_is_hdmi_signal(dc_link->connector_signal))
- continue;
-
- if (dc_link->connector_signal != SIGNAL_TYPE_HDMI_FRL)
- continue;
-
- link_update = dc_link_frl_poll_status_flag(dc_link);
- if (link_update) {
- mutex_lock(&dm->dc_lock);
- dc_link_detect(dc_link, DETECT_REASON_RETRAIN);
- mutex_unlock(&dm->dc_lock);
- }
- }
-
- queue_delayed_work(dm->hdmi_frl_status_polling_wq,
- &dm->hdmi_frl_status_polling_work,
- msecs_to_jiffies(dm->hdmi_frl_status_polling_delay_ms));
- }
-
static int amdgpu_dm_init(struct amdgpu_device *adev)
{
struct dc_init_data init_data;
@@@ -2266,7 -779,15 +779,15 @@@
dc_hardware_init(adev->dm.dc);
- adev->dm.hpd_rx_offload_wq = hpd_rx_irq_create_workqueue(adev);
+ /* GOP/vBIOS may leave an OPTC enabled for a display present at power-on
+ * but no longer driven (e.g. an external DP unplugged at boot). Such a
+ * dangling pipe keeps DCN out of idle and blocks s0i3. Power it down
+ * here when nothing needs to be preserved.
+ */
+ if (adev->flags & AMD_IS_APU)
+ dc_disable_dangling_timing_generators(adev->dm.dc);
+
+ adev->dm.hpd_rx_offload_wq = amdgpu_dm_hpd_rx_irq_create_workqueue(adev);
if (!adev->dm.hpd_rx_offload_wq) {
drm_err(adev_to_drm(adev), "failed to create hpd rx offload workqueue.\n");
goto error;
@@@ -2319,8 -840,6 +840,6 @@@
create_singlethread_workqueue("hdmi_frl_status_polling_workqueue");
if (!adev->dm.hdmi_frl_status_polling_wq)
drm_err(adev_to_drm(adev), "failed to initialize hdmi_frl_status_polling_workqueue\n");
- adev->dm.hdmi_frl_status_polling_delay_ms = 200;
- INIT_DELAYED_WORK(&adev->dm.hdmi_frl_status_polling_work, hdmi_frl_status_polling_work);
}
if (dc_is_dmub_outbox_supported(adev->dm.dc)) {
init_completion(&adev->dm.dmub_aux_transfer_done);
@@@ -2337,8 -856,8 +856,8 @@@
}
amdgpu_dm_outbox_init(adev);
- if (!register_dmub_notify_callback(adev, DMUB_NOTIFICATION_AUX_REPLY,
- dmub_aux_setconfig_callback, false)) {
+ if (!dm_register_dmub_notify_callback(adev, DMUB_NOTIFICATION_AUX_REPLY,
+ dm_dmub_aux_setconfig_callback, false)) {
drm_err(adev_to_drm(adev), "fail to register dmub aux callback");
goto error;
}
@@@ -2346,16 -865,17 +865,17 @@@
for (size_t i = 0; i < ARRAY_SIZE(adev->dm.fused_io); i++)
init_completion(&adev->dm.fused_io[i].replied);
- if (!register_dmub_notify_callback(adev, DMUB_NOTIFICATION_FUSED_IO,
- dmub_aux_fused_io_callback, false)) {
+ if (!dm_register_dmub_notify_callback(adev, DMUB_NOTIFICATION_FUSED_IO,
+ dm_dmub_aux_fused_io_callback, false)) {
drm_err(adev_to_drm(adev), "fail to register dmub fused io callback");
goto error;
}
/* Enable outbox notification only after IRQ handlers are registered and DMUB is alive.
* It is expected that DMUB will resend any pending notifications at this point. Note
- * that hpd and hpd_irq handler registration are deferred to register_hpd_handlers() to
- * align legacy interface initialization sequence. Connection status will be proactivly
- * detected once in the amdgpu_dm_initialize_drm_device.
+ * that hpd and hpd_irq handler registration are deferred to
+ * amdgpu_dm_register_hpd_handlers() to align legacy interface initialization
+ * sequence. Connection status will be proactivly detected once in the
+ * amdgpu_dm_initialize_drm_device.
*/
dc_enable_dmub_outbox(adev->dm.dc);
@@@ -2498,14 -1018,11 +1018,11 @@@ static void amdgpu_dm_fini(struct amdgp
adev->dm.hpd_rx_offload_wq = NULL;
}
+ amdgpu_dm_irq_fini(adev);
+
/* DC Destroy TODO: Replace destroy DAL */
if (adev->dm.dc)
dc_destroy(&adev->dm.dc);
- /*
- * TODO: pageflip, vlank interrupt
- *
- * amdgpu_dm_irq_fini(adev);
- */
if (adev->dm.cgs_device) {
amdgpu_cgs_destroy_device(adev->dm.cgs_device);
@@@ -2635,224 -1152,6 +1152,6 @@@ static int load_dmcu_fw(struct amdgpu_d
return 0;
}
- static uint32_t amdgpu_dm_dmub_reg_read(void *ctx, uint32_t address)
- {
- struct amdgpu_device *adev = ctx;
-
- return dm_read_reg(adev->dm.dc->ctx, address);
- }
-
- static void amdgpu_dm_dmub_reg_write(void *ctx, uint32_t address,
- uint32_t value)
- {
- struct amdgpu_device *adev = ctx;
-
- return dm_write_reg(adev->dm.dc->ctx, address, value);
- }
-
- static int dm_dmub_sw_init(struct amdgpu_device *adev)
- {
- struct dmub_srv_create_params create_params;
- struct dmub_srv_fw_meta_info_params fw_meta_info_params;
- struct dmub_srv_region_params region_params;
- struct dmub_srv_region_info region_info;
- struct dmub_srv_memory_params memory_params;
- struct dmub_fw_meta_info fw_info;
- struct dmub_srv_fb_info *fb_info;
- struct dmub_srv *dmub_srv;
- const struct dmcub_firmware_header_v1_0 *hdr;
- enum dmub_asic dmub_asic;
- enum dmub_status status;
- static enum dmub_window_memory_type window_memory_type[DMUB_WINDOW_TOTAL] = {
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_0_INST_CONST
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_1_STACK
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_2_BSS_DATA
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_3_VBIOS
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_4_MAILBOX
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_5_TRACEBUFF
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_6_FW_STATE
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_7_SCRATCH_MEM
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_IB_MEM
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_SHARED_STATE
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_LSDMA_BUFFER
- DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_CURSOR_OFFLOAD
- };
- int r;
-
- switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
- case IP_VERSION(2, 1, 0):
- dmub_asic = DMUB_ASIC_DCN21;
- break;
- case IP_VERSION(3, 0, 0):
- dmub_asic = DMUB_ASIC_DCN30;
- break;
- case IP_VERSION(3, 0, 1):
- dmub_asic = DMUB_ASIC_DCN301;
- break;
- case IP_VERSION(3, 0, 2):
- dmub_asic = DMUB_ASIC_DCN302;
- break;
- case IP_VERSION(3, 0, 3):
- dmub_asic = DMUB_ASIC_DCN303;
- break;
- case IP_VERSION(3, 1, 2):
- case IP_VERSION(3, 1, 3):
- dmub_asic = (adev->external_rev_id == YELLOW_CARP_B0) ? DMUB_ASIC_DCN31B : DMUB_ASIC_DCN31;
- break;
- case IP_VERSION(3, 1, 4):
- dmub_asic = DMUB_ASIC_DCN314;
- break;
- case IP_VERSION(3, 1, 5):
- dmub_asic = DMUB_ASIC_DCN315;
- break;
- case IP_VERSION(3, 1, 6):
- dmub_asic = DMUB_ASIC_DCN316;
- break;
- case IP_VERSION(3, 2, 0):
- dmub_asic = DMUB_ASIC_DCN32;
- break;
- case IP_VERSION(3, 2, 1):
- dmub_asic = DMUB_ASIC_DCN321;
- break;
- case IP_VERSION(3, 5, 0):
- case IP_VERSION(3, 5, 1):
- dmub_asic = DMUB_ASIC_DCN35;
- break;
- case IP_VERSION(3, 6, 0):
- dmub_asic = DMUB_ASIC_DCN36;
- break;
- case IP_VERSION(4, 0, 1):
- dmub_asic = DMUB_ASIC_DCN401;
- break;
- case IP_VERSION(4, 2, 0):
- dmub_asic = DMUB_ASIC_DCN42;
- break;
- case IP_VERSION(4, 2, 1):
- dmub_asic = DMUB_ASIC_DCN42B;
- break;
- default:
- /* ASIC doesn't support DMUB. */
- return 0;
- }
-
- hdr = (const struct dmcub_firmware_header_v1_0 *)adev->dm.dmub_fw->data;
- adev->dm.dmcub_fw_version = le32_to_cpu(hdr->header.ucode_version);
-
- if (adev->firmware.load_type == AMDGPU_FW_LOAD_PSP) {
- adev->firmware.ucode[AMDGPU_UCODE_ID_DMCUB].ucode_id =
- AMDGPU_UCODE_ID_DMCUB;
- adev->firmware.ucode[AMDGPU_UCODE_ID_DMCUB].fw =
- adev->dm.dmub_fw;
- adev->firmware.fw_size +=
- ALIGN(le32_to_cpu(hdr->inst_const_bytes), PAGE_SIZE);
-
- drm_info(adev_to_drm(adev), "Loading DMUB firmware via PSP: version=0x%08X\n",
- adev->dm.dmcub_fw_version);
- }
-
-
- adev->dm.dmub_srv = kzalloc_obj(*adev->dm.dmub_srv);
- dmub_srv = adev->dm.dmub_srv;
-
- if (!dmub_srv) {
- drm_err(adev_to_drm(adev), "Failed to allocate DMUB service!\n");
- return -ENOMEM;
- }
-
- memset(&create_params, 0, sizeof(create_params));
- create_params.user_ctx = adev;
- create_params.funcs.reg_read = amdgpu_dm_dmub_reg_read;
- create_params.funcs.reg_write = amdgpu_dm_dmub_reg_write;
- create_params.asic = dmub_asic;
-
- /* Create the DMUB service. */
- status = dmub_srv_create(dmub_srv, &create_params);
- if (status != DMUB_STATUS_OK) {
- drm_err(adev_to_drm(adev), "Error creating DMUB service: %d\n", status);
- return -EINVAL;
- }
-
- /* Extract the FW meta info. */
- memset(&fw_meta_info_params, 0, sizeof(fw_meta_info_params));
-
- fw_meta_info_params.inst_const_size = le32_to_cpu(hdr->inst_const_bytes) -
- PSP_HEADER_BYTES_256;
- fw_meta_info_params.bss_data_size = le32_to_cpu(hdr->bss_data_bytes);
- fw_meta_info_params.fw_inst_const = adev->dm.dmub_fw->data +
- le32_to_cpu(hdr->header.ucode_array_offset_bytes) +
- PSP_HEADER_BYTES_256;
- fw_meta_info_params.fw_bss_data = fw_meta_info_params.bss_data_size ? adev->dm.dmub_fw->data +
- le32_to_cpu(hdr->header.ucode_array_offset_bytes) +
- le32_to_cpu(hdr->inst_const_bytes) : NULL;
- fw_meta_info_params.custom_psp_footer_size = 0;
-
- status = dmub_srv_get_fw_meta_info_from_raw_fw(&fw_meta_info_params, &fw_info);
- if (status != DMUB_STATUS_OK) {
- /* Skip returning early, just log the error. */
- drm_err(adev_to_drm(adev), "Error getting DMUB FW meta info: %d\n", status);
- // return -EINVAL;
- }
-
- /* Calculate the size of all the regions for the DMUB service. */
- memset(®ion_params, 0, sizeof(region_params));
-
- region_params.inst_const_size = fw_meta_info_params.inst_const_size;
- region_params.bss_data_size = fw_meta_info_params.bss_data_size;
- region_params.vbios_size = adev->bios_size;
- region_params.fw_bss_data = fw_meta_info_params.fw_bss_data;
- region_params.fw_inst_const = fw_meta_info_params.fw_inst_const;
- region_params.window_memory_type = window_memory_type;
- region_params.fw_info = (status == DMUB_STATUS_OK) ? &fw_info : NULL;
-
- status = dmub_srv_calc_region_info(dmub_srv, ®ion_params,
- ®ion_info);
-
- if (status != DMUB_STATUS_OK) {
- drm_err(adev_to_drm(adev), "Error calculating DMUB region info: %d\n", status);
- return -EINVAL;
- }
-
- /*
- * Allocate a framebuffer based on the total size of all the regions.
- * TODO: Move this into GART.
- */
- r = amdgpu_bo_create_kernel(adev, region_info.fb_size, PAGE_SIZE,
- AMDGPU_GEM_DOMAIN_VRAM |
- AMDGPU_GEM_DOMAIN_GTT,
- &adev->dm.dmub_bo,
- &adev->dm.dmub_bo_gpu_addr,
- &adev->dm.dmub_bo_cpu_addr);
- if (r)
- return r;
-
- /* Rebase the regions on the framebuffer address. */
- memset(&memory_params, 0, sizeof(memory_params));
- memory_params.cpu_fb_addr = adev->dm.dmub_bo_cpu_addr;
- memory_params.gpu_fb_addr = adev->dm.dmub_bo_gpu_addr;
- memory_params.region_info = ®ion_info;
- memory_params.window_memory_type = window_memory_type;
-
- adev->dm.dmub_fb_info = kzalloc_obj(*adev->dm.dmub_fb_info);
- fb_info = adev->dm.dmub_fb_info;
-
- if (!fb_info) {
- drm_err(adev_to_drm(adev),
- "Failed to allocate framebuffer info for DMUB service!\n");
- return -ENOMEM;
- }
-
- status = dmub_srv_calc_mem_info(dmub_srv, &memory_params, fb_info);
- if (status != DMUB_STATUS_OK) {
- drm_err(adev_to_drm(adev), "Error calculating DMUB FB info: %d\n", status);
- return -EINVAL;
- }
-
- adev->dm.bb_from_dmub = dm_dmub_get_vbios_bounding_box(adev);
- adev->dm.fw_inst_size = fw_meta_info_params.inst_const_size;
-
- return 0;
- }
-
static int dm_sw_init(struct amdgpu_ip_block *ip_block)
{
struct amdgpu_device *adev = ip_block->adev;
@@@ -2906,41 -1205,6 +1205,6 @@@ static int dm_sw_fini(struct amdgpu_ip_
return 0;
}
- static int detect_mst_link_for_all_connectors(struct drm_device *dev)
- {
- struct amdgpu_dm_connector *aconnector;
- struct drm_connector *connector;
- struct drm_connector_list_iter iter;
- int ret = 0;
-
- drm_connector_list_iter_begin(dev, &iter);
- drm_for_each_connector_iter(connector, &iter) {
-
- if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
- continue;
-
- aconnector = to_amdgpu_dm_connector(connector);
- if (aconnector->dc_link->type == dc_connection_mst_branch &&
- aconnector->mst_mgr.aux) {
- drm_dbg_kms(dev, "DM_MST: starting TM on aconnector: %p [id: %d]\n",
- aconnector,
- aconnector->base.base.id);
-
- ret = drm_dp_mst_topology_mgr_set_mst(&aconnector->mst_mgr, true);
- if (ret < 0) {
- drm_err(dev, "DM_MST: Failed to start MST\n");
- aconnector->dc_link->type =
- dc_connection_single;
- ret = dm_helpers_dp_mst_stop_top_mgr(aconnector->dc_link->ctx,
- aconnector->dc_link);
- break;
- }
- }
- }
- drm_connector_list_iter_end(&iter);
-
- return ret;
- }
static void amdgpu_dm_boot_time_crc_init(struct amdgpu_device *adev)
{
@@@ -3038,7 -1302,7 +1302,7 @@@ static int dm_late_init(struct amdgpu_i
}
}
- return detect_mst_link_for_all_connectors(adev_to_drm(adev));
+ return amdgpu_dm_detect_mst_link_for_all_connectors(adev_to_drm(adev));
}
static void resume_mst_branch_status(struct drm_dp_mst_topology_mgr *mgr)
@@@ -3092,48 -1356,6 +1356,6 @@@ out_fail
mutex_unlock(&mgr->lock);
}
- void hdmi_cec_unset_edid(struct amdgpu_dm_connector *aconnector)
- {
- struct cec_notifier *n = aconnector->notifier;
-
- if (!n)
- return;
-
- cec_notifier_phys_addr_invalidate(n);
- }
-
- void hdmi_cec_set_edid(struct amdgpu_dm_connector *aconnector)
- {
- struct drm_connector *connector = &aconnector->base;
- struct cec_notifier *n = aconnector->notifier;
-
- if (!n)
- return;
-
- cec_notifier_set_phys_addr(n,
- connector->display_info.source_physical_address);
- }
-
- static void s3_handle_hdmi_cec(struct drm_device *ddev, bool suspend)
- {
- struct amdgpu_dm_connector *aconnector;
- struct drm_connector *connector;
- struct drm_connector_list_iter conn_iter;
-
- drm_connector_list_iter_begin(ddev, &conn_iter);
- drm_for_each_connector_iter(connector, &conn_iter) {
- if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
- continue;
-
- aconnector = to_amdgpu_dm_connector(connector);
- if (suspend)
- hdmi_cec_unset_edid(aconnector);
- else
- hdmi_cec_set_edid(aconnector);
- }
- drm_connector_list_iter_end(&conn_iter);
- }
-
static void s3_handle_mst(struct drm_device *dev, bool suspend)
{
struct amdgpu_dm_connector *aconnector;
@@@ -3236,7 -1458,7 +1458,7 @@@ static int dm_oem_i2c_hw_init(struct am
oem_ddc_service = dc_get_oem_i2c_device(adev->dm.dc);
if (oem_ddc_service) {
- oem_i2c = create_i2c(oem_ddc_service, true);
+ oem_i2c = amdgpu_dm_create_i2c(oem_ddc_service, true);
if (!oem_i2c) {
drm_info(adev_to_drm(adev), "Failed to create oem i2c adapter data\n");
return -ENOMEM;
@@@ -3279,17 -1501,26 +1501,26 @@@ static int dm_hw_init(struct amdgpu_ip_
struct amdgpu_device *adev = ip_block->adev;
int r;
+ adev->dm.i2c_devres_group = devres_open_group(adev->dev, NULL, GFP_KERNEL);
+ if (!adev->dm.i2c_devres_group)
+ return -ENOMEM;
+
/* Create DAL display manager */
r = amdgpu_dm_init(adev);
if (r)
- return r;
+ goto err_release_i2c;
amdgpu_dm_hpd_init(adev);
r = dm_oem_i2c_hw_init(adev);
if (r)
drm_info(adev_to_drm(adev), "Failed to add OEM i2c bus\n");
+ devres_close_group(adev->dev, adev->dm.i2c_devres_group);
return 0;
+
+ err_release_i2c:
+ devres_release_group(adev->dev, adev->dm.i2c_devres_group);
+ return r;
}
/**
@@@ -3304,9 -1535,11 +1535,11 @@@ static int dm_hw_fini(struct amdgpu_ip_
{
struct amdgpu_device *adev = ip_block->adev;
+ if (adev->dm.i2c_devres_group)
+ devres_release_group(adev->dev, adev->dm.i2c_devres_group);
+
amdgpu_dm_hpd_fini(adev);
- amdgpu_dm_irq_fini(adev);
amdgpu_dm_fini(adev);
return 0;
}
@@@ -3321,10 -1554,11 +1554,11 @@@ static void dm_gpureset_toggle_interrup
int i = 0;
for (i = 0; i < state->stream_count; i++) {
- acrtc = get_crtc_by_otg_inst(
+ acrtc = amdgpu_dm_get_crtc_by_otg_inst(
adev, state->stream_status[i].primary_otg_inst);
- if (acrtc && state->stream_status[i].plane_count != 0) {
+ if (acrtc && state->stream_status[i].plane_count != 0 &&
+ amdgpu_ip_version(adev, DCE_HWIP, 0) == 0) {
irq_source = IRQ_TYPE_PFLIP + acrtc->otg_inst;
rc = dc_interrupt_set(adev->dm.dc, irq_source, enable) ? 0 : -EBUSY;
if (rc)
@@@ -3405,16 -1639,6 +1639,6 @@@ static enum dc_status amdgpu_dm_commit_
return dc_commit_streams(dc, ¶ms);
}
- static void hpd_rx_irq_work_suspend(struct amdgpu_display_manager *dm)
- {
- int i;
-
- if (dm->hpd_rx_offload_wq) {
- for (i = 0; i < dm->dc->caps.max_links; i++)
- flush_workqueue(dm->hpd_rx_offload_wq[i].wq);
- }
- }
-
static int dm_cache_state(struct amdgpu_device *adev)
{
int r;
@@@ -3510,7 -1734,7 +1734,7 @@@ static int dm_suspend(struct amdgpu_ip_
amdgpu_dm_irq_suspend(adev);
- hpd_rx_irq_work_suspend(dm);
+ amdgpu_dm_hpd_rx_irq_work_suspend(dm);
return 0;
}
@@@ -3522,7 -1746,7 +1746,7 @@@
return r;
}
- s3_handle_hdmi_cec(adev_to_drm(adev), true);
+ amdgpu_dm_s3_handle_hdmi_cec(adev_to_drm(adev), true);
s3_handle_mst(adev_to_drm(adev), true);
@@@ -3536,7 -1760,7 +1760,7 @@@
scoped_guard(mutex, &dm->dc_lock)
amdgpu_dm_ism_force_full_power(dm);
- hpd_rx_irq_work_suspend(dm);
+ amdgpu_dm_hpd_rx_irq_work_suspend(dm);
dc_set_power_state(dm->dc, DC_ACPI_CM_POWER_STATE_D3);
@@@ -3548,26 -1772,7 +1772,7 @@@
return 0;
}
- struct drm_connector *
- amdgpu_dm_find_first_crtc_matching_connector(struct drm_atomic_commit *state,
- struct drm_crtc *crtc)
- {
- u32 i;
- struct drm_connector_state *new_con_state;
- struct drm_connector *connector;
- struct drm_crtc *crtc_from_state;
-
- for_each_new_connector_in_state(state, connector, new_con_state, i) {
- crtc_from_state = new_con_state->crtc;
-
- if (crtc_from_state == crtc)
- return connector;
- }
-
- return NULL;
- }
-
- static void emulated_link_detect(struct dc_link *link)
+ void amdgpu_dm_emulated_link_detect(struct dc_link *link)
{
struct dc_sink_init_data sink_init_data = { 0 };
struct display_sink_capability sink_caps = { 0 };
@@@ -3688,8 -1893,8 +1893,8 @@@ static void dm_gpureset_commit_state(st
}
}
- static void apply_delay_after_dpcd_poweroff(struct amdgpu_device *adev,
- struct dc_sink *sink)
+ void amdgpu_dm_apply_delay_after_dpcd_poweroff(struct amdgpu_device *adev,
+ struct dc_sink *sink)
{
struct dc_panel_patch *ppatch = NULL;
@@@ -3832,8 -2037,7 +2037,7 @@@ static int dm_resume(struct amdgpu_ip_b
for (i = 0; i < dc_state->stream_count; i++) {
dc_state->streams[i]->mode_changed = true;
for (j = 0; j < dc_state->stream_status[i].plane_count; j++) {
- dc_state->stream_status[i].plane_states[j]->update_flags.raw
- = 0xffffffff;
+ dc_pipe_update_bits_set_full(&dc_state->stream_status[i].plane_states[j]->update_bits);
}
}
@@@ -3896,7 -2100,7 +2100,7 @@@
*/
amdgpu_dm_irq_resume_early(adev);
- s3_handle_hdmi_cec(ddev, false);
+ amdgpu_dm_s3_handle_hdmi_cec(ddev, false);
/* On resume we need to rewrite the MSTM control bits to enable MST*/
s3_handle_mst(ddev, false);
@@@ -3931,14 -2135,14 +2135,14 @@@
drm_err(adev_to_drm(adev), "KMS: Failed to detect connector\n");
if (aconnector->base.force && new_connection_type == dc_connection_none) {
- emulated_link_detect(aconnector->dc_link);
+ amdgpu_dm_emulated_link_detect(aconnector->dc_link);
} else {
guard(mutex)(&dm->dc_lock);
dc_exit_ips_for_hw_access(dm->dc);
ret = dc_link_detect(aconnector->dc_link, DETECT_REASON_RESUMEFROMS3S4);
if (ret) {
/* w/a delay for certain panels */
- apply_delay_after_dpcd_poweroff(adev, aconnector->dc_sink);
+ amdgpu_dm_apply_delay_after_dpcd_poweroff(adev, aconnector->dc_sink);
}
}
@@@ -4016,7 -2220,6 +2220,6 @@@ static const struct amd_ip_funcs amdgpu
.resume = dm_resume,
.is_idle = dm_is_idle,
.wait_for_idle = dm_wait_for_idle,
- .check_soft_reset = dm_check_soft_reset,
.soft_reset = dm_soft_reset,
.set_clockgating_state = dm_set_clockgating_state,
.set_powergating_state = dm_set_powergating_state,
@@@ -4049,1004 -2252,6 +2252,6 @@@ static struct drm_mode_config_helper_fu
.atomic_commit_setup = amdgpu_dm_atomic_setup_commit,
};
- #define DDC_MANUFACTURERNAME_SAMSUNG 0x2D4C
-
- static void dm_set_panel_type(struct amdgpu_dm_connector *aconnector)
- {
- struct drm_connector *connector = &aconnector->base;
- struct drm_display_info *display_info = &connector->display_info;
- struct dc_link *link = aconnector->dc_link;
- struct amdgpu_device *adev;
-
- adev = drm_to_adev(connector->dev);
-
- link->panel_type = PANEL_TYPE_NONE;
-
- switch (display_info->amd_vsdb.panel_type) {
- case AMD_VSDB_PANEL_TYPE_OLED:
- link->panel_type = PANEL_TYPE_OLED;
- break;
- case AMD_VSDB_PANEL_TYPE_MINILED:
- link->panel_type = PANEL_TYPE_MINILED;
- break;
- }
-
- /* If VSDB didn't determine panel type, check DPCD ext caps */
- if (link->panel_type == PANEL_TYPE_NONE) {
- if (link->dpcd_sink_ext_caps.bits.miniled == 1)
- link->panel_type = PANEL_TYPE_MINILED;
- if (link->dpcd_sink_ext_caps.bits.oled == 1)
- link->panel_type = PANEL_TYPE_OLED;
- }
-
- /*
- * TODO: get panel type from DID2 that has device technology field
- * to specify if it's OLED or not. But we need to wait for DID2
- * support in DC and EDID parser to be able to use it here.
- */
-
- if (link->panel_type == PANEL_TYPE_NONE) {
- struct drm_amd_vsdb_info *vsdb = &display_info->amd_vsdb;
- u32 lum1_max = vsdb->luminance_range1.max_luminance;
- u32 lum2_max = vsdb->luminance_range2.max_luminance;
-
- if (vsdb->version && link->local_sink &&
- link->local_sink->edid_caps.manufacturer_id ==
- DDC_MANUFACTURERNAME_SAMSUNG &&
- lum1_max >= ((lum2_max * 3) / 2))
- link->panel_type = PANEL_TYPE_MINILED;
- }
-
- if (link->panel_type == PANEL_TYPE_OLED)
- drm_object_property_set_value(&connector->base,
- adev_to_drm(adev)->mode_config.panel_type_property,
- DRM_MODE_PANEL_TYPE_OLED);
- else
- drm_object_property_set_value(&connector->base,
- adev_to_drm(adev)->mode_config.panel_type_property,
- DRM_MODE_PANEL_TYPE_UNKNOWN);
-
- drm_dbg_kms(aconnector->base.dev, "Panel type: %d\n", link->panel_type);
- }
-
- static void update_connector_ext_caps(struct amdgpu_dm_connector *aconnector)
- {
- const struct drm_panel_backlight_quirk *panel_backlight_quirk;
- struct amdgpu_dm_backlight_caps *caps;
- struct drm_connector *conn_base;
- struct amdgpu_device *adev;
- struct drm_luminance_range_info *luminance_range;
- struct drm_device *drm;
-
- if (aconnector->bl_idx == -1 ||
- aconnector->dc_link->connector_signal != SIGNAL_TYPE_EDP)
- return;
-
- conn_base = &aconnector->base;
- drm = conn_base->dev;
- adev = drm_to_adev(drm);
-
- caps = &adev->dm.backlight_caps[aconnector->bl_idx];
- caps->ext_caps = &aconnector->dc_link->dpcd_sink_ext_caps;
- caps->aux_support = false;
-
- panel_backlight_quirk = drm_get_panel_backlight_quirk(aconnector->drm_edid);
-
- if (caps->ext_caps->bits.oled == 1
- /*
- * ||
- * caps->ext_caps->bits.sdr_aux_backlight_control == 1 ||
- * caps->ext_caps->bits.hdr_aux_backlight_control == 1
- */)
- caps->aux_support = true;
-
- if (amdgpu_backlight == 0)
- caps->aux_support = false;
- else if (amdgpu_backlight == 1)
- caps->aux_support = true;
- else if (!IS_ERR_OR_NULL(panel_backlight_quirk) &&
- panel_backlight_quirk->force_pwm)
- caps->aux_support = false;
- if (caps->aux_support)
- aconnector->dc_link->backlight_control_type = BACKLIGHT_CONTROL_AMD_AUX;
-
- luminance_range = &conn_base->display_info.luminance_range;
-
- if (luminance_range->max_luminance)
- caps->aux_max_input_signal = luminance_range->max_luminance;
- else
- caps->aux_max_input_signal = 512;
-
- if (luminance_range->min_luminance)
- caps->aux_min_input_signal = luminance_range->min_luminance;
- else
- caps->aux_min_input_signal = 1;
-
- if (!IS_ERR_OR_NULL(panel_backlight_quirk)) {
- if (panel_backlight_quirk->min_brightness) {
- caps->min_input_signal =
- panel_backlight_quirk->min_brightness - 1;
- drm_info(drm,
- "Applying panel backlight quirk, min_brightness: %d\n",
- caps->min_input_signal);
- }
- if (panel_backlight_quirk->brightness_mask) {
- drm_info(drm,
- "Applying panel backlight quirk, brightness_mask: 0x%X\n",
- panel_backlight_quirk->brightness_mask);
- caps->brightness_mask =
- panel_backlight_quirk->brightness_mask;
- }
- }
- }
-
- DEFINE_FREE(sink_release, struct dc_sink *, if (_T) dc_sink_release(_T))
-
- void amdgpu_dm_update_connector_after_detect(
- struct amdgpu_dm_connector *aconnector)
- {
- struct drm_connector *connector = &aconnector->base;
- struct dc_sink *sink __free(sink_release) = NULL;
- struct drm_device *dev = connector->dev;
-
- /* MST handled by drm_mst framework */
- if (aconnector->mst_mgr.mst_state == true)
- return;
-
- sink = aconnector->dc_link->local_sink;
- if (sink)
- dc_sink_retain(sink);
-
- /*
- * Edid mgmt connector gets first update only in mode_valid hook and then
- * the connector sink is set to either fake or physical sink depends on link status.
- * Skip if already done during boot.
- */
- if (aconnector->base.force != DRM_FORCE_UNSPECIFIED
- && aconnector->dc_em_sink) {
-
- /*
- * For S3 resume with headless use eml_sink to fake stream
- * because on resume connector->sink is set to NULL
- */
- guard(mutex)(&dev->mode_config.mutex);
-
- if (sink) {
- if (aconnector->dc_sink) {
- amdgpu_dm_update_freesync_caps(connector, NULL, true);
- /*
- * retain and release below are used to
- * bump up refcount for sink because the link doesn't point
- * to it anymore after disconnect, so on next crtc to connector
- * reshuffle by UMD we will get into unwanted dc_sink release
- */
- dc_sink_release(aconnector->dc_sink);
- }
- aconnector->dc_sink = sink;
- dc_sink_retain(aconnector->dc_sink);
- amdgpu_dm_update_freesync_caps(connector,
- aconnector->drm_edid, true);
- } else {
- amdgpu_dm_update_freesync_caps(connector, NULL, true);
- if (!aconnector->dc_sink) {
- aconnector->dc_sink = aconnector->dc_em_sink;
- dc_sink_retain(aconnector->dc_sink);
- }
- }
-
- return;
- }
-
- /*
- * TODO: temporary guard to look for proper fix
- * if this sink is MST sink, we should not do anything
- */
- if (sink && sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT_MST)
- return;
-
- if (aconnector->dc_sink == sink) {
- /*
- * We got a DP short pulse (Link Loss, DP CTS, etc...).
- * Do nothing!!
- */
- drm_dbg_kms(dev, "DCHPD: connector_id=%d: dc_sink didn't change.\n",
- aconnector->connector_id);
- return;
- }
-
- drm_dbg_kms(dev, "DCHPD: connector_id=%d: Old sink=%p New sink=%p\n",
- aconnector->connector_id, aconnector->dc_sink, sink);
-
- /* When polling, DRM has already locked the mutex for us. */
- if (!drm_kms_helper_is_poll_worker())
- mutex_lock(&dev->mode_config.mutex);
-
- /*
- * 1. Update status of the drm connector
- * 2. Send an event and let userspace tell us what to do
- */
- if (sink) {
- /*
- * TODO: check if we still need the S3 mode update workaround.
- * If yes, put it here.
- */
- if (aconnector->dc_sink) {
- amdgpu_dm_update_freesync_caps(connector, NULL, true);
- dc_sink_release(aconnector->dc_sink);
- }
-
- aconnector->dc_sink = sink;
- dc_sink_retain(aconnector->dc_sink);
- drm_edid_free(aconnector->drm_edid);
- aconnector->drm_edid = NULL;
- if (sink->dc_edid.length == 0) {
- hdmi_cec_unset_edid(aconnector);
- if (aconnector->dc_link->aux_mode) {
- drm_dp_cec_unset_edid(&aconnector->dm_dp_aux.aux);
- }
- } else {
- const struct edid *edid = (const struct edid *)sink->dc_edid.raw_edid;
-
- aconnector->drm_edid = drm_edid_alloc(edid, sink->dc_edid.length);
- drm_edid_connector_update(connector, aconnector->drm_edid);
-
- hdmi_cec_set_edid(aconnector);
- if (aconnector->dc_link->aux_mode)
- drm_dp_cec_attach(&aconnector->dm_dp_aux.aux,
- connector->display_info.source_physical_address);
- }
-
- if (!aconnector->timing_requested) {
- aconnector->timing_requested =
- kzalloc_obj(struct dc_crtc_timing);
- if (!aconnector->timing_requested)
- drm_err(dev,
- "failed to create aconnector->requested_timing\n");
- }
-
- amdgpu_dm_update_freesync_caps(connector, aconnector->drm_edid, true);
- update_connector_ext_caps(aconnector);
- dm_set_panel_type(aconnector);
- } else {
- hdmi_cec_unset_edid(aconnector);
- drm_dp_cec_unset_edid(&aconnector->dm_dp_aux.aux);
- amdgpu_dm_update_freesync_caps(connector, NULL, true);
- aconnector->num_modes = 0;
- dc_sink_release(aconnector->dc_sink);
- aconnector->dc_sink = NULL;
- drm_edid_free(aconnector->drm_edid);
- aconnector->drm_edid = NULL;
- kfree(aconnector->timing_requested);
- aconnector->timing_requested = NULL;
- /* Set CP to DESIRED if it was ENABLED, so we can re-enable it again on hotplug */
- if (connector->state->content_protection == DRM_MODE_CONTENT_PROTECTION_ENABLED)
- connector->state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED;
- }
-
- update_subconnector_property(aconnector);
-
- /* When polling, the mutex will be unlocked for us by DRM. */
- if (!drm_kms_helper_is_poll_worker())
- mutex_unlock(&dev->mode_config.mutex);
- }
-
- static bool are_sinks_equal(const struct dc_sink *sink1, const struct dc_sink *sink2)
- {
- if (!sink1 || !sink2)
- return false;
- if (sink1->sink_signal != sink2->sink_signal)
- return false;
-
- if (sink1->dc_edid.length != sink2->dc_edid.length)
- return false;
-
- if (memcmp(sink1->dc_edid.raw_edid, sink2->dc_edid.raw_edid,
- sink1->dc_edid.length) != 0)
- return false;
- return true;
- }
-
-
- /**
- * DOC: hdmi_hpd_debounce_work
- *
- * HDMI HPD debounce delay in milliseconds. When an HDMI display toggles HPD
- * (such as during power save transitions), this delay determines how long to
- * wait before processing the HPD event. This allows distinguishing between a
- * physical unplug (>hdmi_hpd_debounce_delay)
- * and a spontaneous RX HPD toggle (<hdmi_hpd_debounce_delay).
- *
- * If the toggle is less than this delay, the driver compares sink capabilities
- * and permits a hotplug event if they changed.
- *
- * The default value of 1500ms was chosen based on experimental testing with
- * various monitors that exhibit spontaneous HPD toggling behavior.
- */
- static void hdmi_hpd_debounce_work(struct work_struct *work)
- {
- struct amdgpu_dm_connector *aconnector =
- container_of(to_delayed_work(work), struct amdgpu_dm_connector,
- hdmi_hpd_debounce_work);
- struct drm_connector *connector = &aconnector->base;
- struct drm_device *dev = connector->dev;
- struct amdgpu_device *adev = drm_to_adev(dev);
- struct dc *dc = aconnector->dc_link->ctx->dc;
- bool fake_reconnect = false;
- bool reallow_idle = false;
- bool ret = false;
- guard(mutex)(&aconnector->hpd_lock);
-
- /* Re-detect the display */
- scoped_guard(mutex, &adev->dm.dc_lock) {
- if (dc->caps.ips_support && dc->ctx->dmub_srv->idle_allowed) {
- dc_allow_idle_optimizations(dc, false);
- reallow_idle = true;
- }
- ret = dc_link_detect(aconnector->dc_link, DETECT_REASON_HPD);
- }
-
- if (ret) {
- /* Apply workaround delay for certain panels */
- apply_delay_after_dpcd_poweroff(adev, aconnector->dc_sink);
- /* Compare sinks to determine if this was a spontaneous HPD toggle */
- if (are_sinks_equal(aconnector->dc_link->local_sink, aconnector->hdmi_prev_sink)) {
- /*
- * Sinks match - this was a spontaneous HDMI HPD toggle.
- */
- drm_dbg_kms(dev, "HDMI HPD: Sink unchanged after debounce, internal re-enable\n");
- fake_reconnect = true;
- }
-
- /* Update connector state */
- amdgpu_dm_update_connector_after_detect(aconnector);
-
- drm_modeset_lock_all(dev);
- dm_restore_drm_connector_state(dev, connector);
- drm_modeset_unlock_all(dev);
-
- /* Only notify OS if sink actually changed */
- if (!fake_reconnect && aconnector->base.force == DRM_FORCE_UNSPECIFIED)
- drm_kms_helper_hotplug_event(dev);
- }
-
- /* Release the cached sink reference */
- if (aconnector->hdmi_prev_sink) {
- dc_sink_release(aconnector->hdmi_prev_sink);
- aconnector->hdmi_prev_sink = NULL;
- }
-
- scoped_guard(mutex, &adev->dm.dc_lock) {
- if (reallow_idle && dc->caps.ips_support)
- dc_allow_idle_optimizations(dc, true);
- }
- }
-
- static void handle_hpd_irq_helper(struct amdgpu_dm_connector *aconnector)
- {
- struct drm_connector *connector = &aconnector->base;
- struct drm_device *dev = connector->dev;
- enum dc_connection_type new_connection_type = dc_connection_none;
- struct amdgpu_device *adev = drm_to_adev(dev);
- struct dm_connector_state *dm_con_state = to_dm_connector_state(connector->state);
- struct dc *dc = aconnector->dc_link->ctx->dc;
- bool ret = false;
- bool debounce_required = false;
-
- if (adev->dm.disable_hpd_irq)
- return;
-
- /*
- * In case of failure or MST no need to update connector status or notify the OS
- * since (for MST case) MST does this in its own context.
- */
- guard(mutex)(&aconnector->hpd_lock);
-
- if (adev->dm.hdcp_workqueue) {
- hdcp_reset_display(adev->dm.hdcp_workqueue, aconnector->dc_link->link_index);
- dm_con_state->update_hdcp = true;
- }
- if (aconnector->fake_enable)
- aconnector->fake_enable = false;
-
- aconnector->timing_changed = false;
-
- if (!dc_link_detect_connection_type(aconnector->dc_link, &new_connection_type))
- drm_err(adev_to_drm(adev), "KMS: Failed to detect connector\n");
-
- /*
- * Check for HDMI disconnect with debounce enabled.
- */
- debounce_required = (aconnector->hdmi_hpd_debounce_delay_ms > 0 &&
- dc_is_hdmi_signal(aconnector->dc_link->connector_signal) &&
- new_connection_type == dc_connection_none &&
- aconnector->dc_link->local_sink != NULL);
-
- if (aconnector->base.force && new_connection_type == dc_connection_none) {
- emulated_link_detect(aconnector->dc_link);
-
- drm_modeset_lock_all(dev);
- dm_restore_drm_connector_state(dev, connector);
- drm_modeset_unlock_all(dev);
-
- if (aconnector->base.force == DRM_FORCE_UNSPECIFIED)
- drm_kms_helper_connector_hotplug_event(connector);
- } else if (debounce_required) {
- /*
- * HDMI disconnect detected - schedule delayed work instead of
- * processing immediately. This allows us to coalesce spurious
- * HDMI signals from physical unplugs.
- */
- drm_dbg_kms(dev, "HDMI HPD: Disconnect detected, scheduling debounce work (%u ms)\n",
- aconnector->hdmi_hpd_debounce_delay_ms);
-
- /* Cache the current sink for later comparison */
- if (aconnector->hdmi_prev_sink)
- dc_sink_release(aconnector->hdmi_prev_sink);
- aconnector->hdmi_prev_sink = aconnector->dc_link->local_sink;
- if (aconnector->hdmi_prev_sink)
- dc_sink_retain(aconnector->hdmi_prev_sink);
-
- /* Schedule delayed detection. */
- if (mod_delayed_work(system_percpu_wq,
- &aconnector->hdmi_hpd_debounce_work,
- msecs_to_jiffies(aconnector->hdmi_hpd_debounce_delay_ms)))
- drm_dbg_kms(dev, "HDMI HPD: Re-scheduled debounce work\n");
-
- } else {
-
- /* If the aconnector->hdmi_hpd_debounce_work is scheduled, exit early */
- if (delayed_work_pending(&aconnector->hdmi_hpd_debounce_work))
- return;
-
- scoped_guard(mutex, &adev->dm.dc_lock) {
- dc_exit_ips_for_hw_access(dc);
- ret = dc_link_detect(aconnector->dc_link, DETECT_REASON_HPD);
- }
- if (ret) {
- /* w/a delay for certain panels */
- apply_delay_after_dpcd_poweroff(adev, aconnector->dc_sink);
- amdgpu_dm_update_connector_after_detect(aconnector);
-
- drm_modeset_lock_all(dev);
- dm_restore_drm_connector_state(dev, connector);
- drm_modeset_unlock_all(dev);
-
- if (aconnector->base.force == DRM_FORCE_UNSPECIFIED)
- drm_kms_helper_connector_hotplug_event(connector);
- }
- }
- }
-
- static void handle_hpd_irq(void *param)
- {
- struct amdgpu_dm_connector *aconnector = (struct amdgpu_dm_connector *)param;
-
- handle_hpd_irq_helper(aconnector);
-
- }
-
- static void schedule_hpd_rx_offload_work(struct amdgpu_device *adev, struct hpd_rx_irq_offload_work_queue *offload_wq,
- union hpd_irq_data hpd_irq_data)
- {
- struct hpd_rx_irq_offload_work *offload_work = kzalloc_obj(*offload_work);
-
- if (!offload_work) {
- drm_err(adev_to_drm(adev), "Failed to allocate hpd_rx_irq_offload_work.\n");
- return;
- }
-
- INIT_WORK(&offload_work->work, dm_handle_hpd_rx_offload_work);
- offload_work->data = hpd_irq_data;
- offload_work->offload_wq = offload_wq;
- offload_work->adev = adev;
-
- queue_work(offload_wq->wq, &offload_work->work);
- drm_dbg_kms(adev_to_drm(adev), "queue work to handle hpd_rx offload work");
- }
-
- static void handle_hpd_rx_irq(void *param)
- {
- struct amdgpu_dm_connector *aconnector = (struct amdgpu_dm_connector *)param;
- struct drm_connector *connector = &aconnector->base;
- struct drm_device *dev = connector->dev;
- struct dc_link *dc_link = aconnector->dc_link;
- bool is_mst_root_connector = aconnector->mst_mgr.mst_state;
- bool result = false;
- enum dc_connection_type new_connection_type = dc_connection_none;
- struct amdgpu_device *adev = drm_to_adev(dev);
- union hpd_irq_data hpd_irq_data;
- bool link_loss = false;
- bool has_left_work = false;
- int idx = dc_link->link_index;
- struct hpd_rx_irq_offload_work_queue *offload_wq = &adev->dm.hpd_rx_offload_wq[idx];
- struct dc *dc = aconnector->dc_link->ctx->dc;
-
- memset(&hpd_irq_data, 0, sizeof(hpd_irq_data));
-
- if (adev->dm.disable_hpd_irq)
- return;
-
- /*
- * TODO:Temporary add mutex to protect hpd interrupt not have a gpio
- * conflict, after implement i2c helper, this mutex should be
- * retired.
- */
- mutex_lock(&aconnector->hpd_lock);
-
- result = dc_link_handle_hpd_rx_irq(dc_link, &hpd_irq_data,
- &link_loss, true, &has_left_work);
-
- if (!has_left_work)
- goto out;
-
- if (hpd_irq_data.bytes.device_service_irq.bits.AUTOMATED_TEST) {
- schedule_hpd_rx_offload_work(adev, offload_wq, hpd_irq_data);
- goto out;
- }
-
- if (dc_link_dp_allow_hpd_rx_irq(dc_link)) {
- if (hpd_irq_data.bytes.device_service_irq.bits.UP_REQ_MSG_RDY ||
- hpd_irq_data.bytes.device_service_irq.bits.DOWN_REP_MSG_RDY) {
- bool skip = false;
-
- /*
- * DOWN_REP_MSG_RDY is also handled by polling method
- * mgr->cbs->poll_hpd_irq()
- */
- spin_lock(&offload_wq->offload_lock);
- skip = offload_wq->is_handling_mst_msg_rdy_event;
-
- if (!skip)
- offload_wq->is_handling_mst_msg_rdy_event = true;
-
- spin_unlock(&offload_wq->offload_lock);
-
- if (!skip)
- schedule_hpd_rx_offload_work(adev, offload_wq, hpd_irq_data);
-
- goto out;
- }
-
- if (link_loss) {
- bool skip = false;
-
- spin_lock(&offload_wq->offload_lock);
- skip = offload_wq->is_handling_link_loss;
-
- if (!skip)
- offload_wq->is_handling_link_loss = true;
-
- spin_unlock(&offload_wq->offload_lock);
-
- if (!skip)
- schedule_hpd_rx_offload_work(adev, offload_wq, hpd_irq_data);
-
- goto out;
- }
- }
-
- out:
- if (result && !is_mst_root_connector) {
- /* Downstream Port status changed. */
- if (!dc_link_detect_connection_type(dc_link, &new_connection_type))
- drm_err(adev_to_drm(adev), "KMS: Failed to detect connector\n");
-
- if (aconnector->base.force && new_connection_type == dc_connection_none) {
- emulated_link_detect(dc_link);
-
- if (aconnector->fake_enable)
- aconnector->fake_enable = false;
-
- amdgpu_dm_update_connector_after_detect(aconnector);
-
-
- drm_modeset_lock_all(dev);
- dm_restore_drm_connector_state(dev, connector);
- drm_modeset_unlock_all(dev);
-
- drm_kms_helper_connector_hotplug_event(connector);
- } else {
- bool ret = false;
-
- mutex_lock(&adev->dm.dc_lock);
- dc_exit_ips_for_hw_access(dc);
- ret = dc_link_detect(dc_link, DETECT_REASON_HPDRX);
- mutex_unlock(&adev->dm.dc_lock);
-
- if (ret) {
- if (aconnector->fake_enable)
- aconnector->fake_enable = false;
-
- amdgpu_dm_update_connector_after_detect(aconnector);
-
- drm_modeset_lock_all(dev);
- dm_restore_drm_connector_state(dev, connector);
- drm_modeset_unlock_all(dev);
-
- drm_kms_helper_connector_hotplug_event(connector);
- }
- }
- }
- if (hpd_irq_data.bytes.device_service_irq.bits.CP_IRQ) {
- if (adev->dm.hdcp_workqueue)
- hdcp_handle_cpirq(adev->dm.hdcp_workqueue, aconnector->base.index);
- }
-
- if (dc_link->type != dc_connection_mst_branch)
- drm_dp_cec_irq(&aconnector->dm_dp_aux.aux);
-
- mutex_unlock(&aconnector->hpd_lock);
- }
-
- static int register_hpd_handlers(struct amdgpu_device *adev)
- {
- struct drm_device *dev = adev_to_drm(adev);
- struct drm_connector *connector;
- struct amdgpu_dm_connector *aconnector;
- const struct dc_link *dc_link;
- struct dc_interrupt_params int_params = {0};
-
- int_params.requested_polarity = INTERRUPT_POLARITY_DEFAULT;
- int_params.current_polarity = INTERRUPT_POLARITY_DEFAULT;
-
- if (dc_is_dmub_outbox_supported(adev->dm.dc)) {
- if (!register_dmub_notify_callback(adev, DMUB_NOTIFICATION_HPD,
- dmub_hpd_callback, true)) {
- drm_err(adev_to_drm(adev), "fail to register dmub hpd callback");
- return -EINVAL;
- }
-
- if (!register_dmub_notify_callback(adev, DMUB_NOTIFICATION_HPD_IRQ,
- dmub_hpd_callback, true)) {
- drm_err(adev_to_drm(adev), "fail to register dmub hpd callback");
- return -EINVAL;
- }
-
- if (!register_dmub_notify_callback(adev, DMUB_NOTIFICATION_HPD_SENSE_NOTIFY,
- dmub_hpd_sense_callback, true)) {
- drm_err(adev_to_drm(adev), "fail to register dmub hpd sense callback");
- return -EINVAL;
- }
- }
-
- list_for_each_entry(connector,
- &dev->mode_config.connector_list, head) {
-
- if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
- continue;
-
- aconnector = to_amdgpu_dm_connector(connector);
- dc_link = aconnector->dc_link;
-
- if (dc_link->irq_source_hpd != DC_IRQ_SOURCE_INVALID) {
- int_params.int_context = INTERRUPT_LOW_IRQ_CONTEXT;
- int_params.irq_source = dc_link->irq_source_hpd;
-
- if (int_params.irq_source == DC_IRQ_SOURCE_INVALID ||
- int_params.irq_source < DC_IRQ_SOURCE_HPD1 ||
- int_params.irq_source > DC_IRQ_SOURCE_HPD6) {
- drm_err(adev_to_drm(adev), "Failed to register hpd irq!\n");
- return -EINVAL;
- }
-
- if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
- handle_hpd_irq, (void *) aconnector))
- return -ENOMEM;
- }
-
- if (dc_link->irq_source_hpd_rx != DC_IRQ_SOURCE_INVALID) {
-
- /* Also register for DP short pulse (hpd_rx). */
- int_params.int_context = INTERRUPT_LOW_IRQ_CONTEXT;
- int_params.irq_source = dc_link->irq_source_hpd_rx;
-
- if (int_params.irq_source == DC_IRQ_SOURCE_INVALID ||
- int_params.irq_source < DC_IRQ_SOURCE_HPD1RX ||
- int_params.irq_source > DC_IRQ_SOURCE_HPD6RX) {
- drm_err(adev_to_drm(adev), "Failed to register hpd rx irq!\n");
- return -EINVAL;
- }
-
- if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
- handle_hpd_rx_irq, (void *) aconnector))
- return -ENOMEM;
- }
- }
- return 0;
- }
-
- /* Register IRQ sources and initialize IRQ callbacks */
- static int dce110_register_irq_handlers(struct amdgpu_device *adev)
- {
- struct dc *dc = adev->dm.dc;
- struct common_irq_params *c_irq_params;
- struct dc_interrupt_params int_params = {0};
- int r;
- int i;
- unsigned int src_id;
- unsigned int client_id = AMDGPU_IRQ_CLIENTID_LEGACY;
- /* Use different interrupts for VBLANK on DCE 6 vs. newer. */
- const unsigned int vblank_d1 =
- adev->dm.dc->ctx->dce_version >= DCE_VERSION_8_0
- ? VISLANDS30_IV_SRCID_D1_VERTICAL_INTERRUPT0 : 1;
-
- if (adev->family >= AMDGPU_FAMILY_AI)
- client_id = SOC15_IH_CLIENTID_DCE;
-
- int_params.requested_polarity = INTERRUPT_POLARITY_DEFAULT;
- int_params.current_polarity = INTERRUPT_POLARITY_DEFAULT;
-
- /*
- * Actions of amdgpu_irq_add_id():
- * 1. Register a set() function with base driver.
- * Base driver will call set() function to enable/disable an
- * interrupt in DC hardware.
- * 2. Register amdgpu_dm_irq_handler().
- * Base driver will call amdgpu_dm_irq_handler() for ALL interrupts
- * coming from DC hardware.
- * amdgpu_dm_irq_handler() will re-direct the interrupt to DC
- * for acknowledging and handling.
- */
-
- /* Use VBLANK interrupt */
- for (i = 0; i < adev->mode_info.num_crtc; i++) {
- src_id = vblank_d1 + i;
- r = amdgpu_irq_add_id(adev, client_id, src_id, &adev->crtc_irq);
- if (r) {
- drm_err(adev_to_drm(adev), "Failed to add crtc irq id!\n");
- return r;
- }
-
- int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT;
- int_params.irq_source =
- dc_interrupt_to_irq_source(dc, src_id, 0);
-
- if (int_params.irq_source == DC_IRQ_SOURCE_INVALID ||
- int_params.irq_source < DC_IRQ_SOURCE_VBLANK1 ||
- int_params.irq_source > DC_IRQ_SOURCE_VBLANK6) {
- drm_err(adev_to_drm(adev), "Failed to register vblank irq!\n");
- return -EINVAL;
- }
-
- c_irq_params = &adev->dm.vblank_params[int_params.irq_source - DC_IRQ_SOURCE_VBLANK1];
-
- c_irq_params->adev = adev;
- c_irq_params->irq_src = int_params.irq_source;
-
- if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
- dm_crtc_high_irq, c_irq_params))
- return -ENOMEM;
- }
-
- if (dc_supports_vrr(adev->dm.dc->ctx->dce_version)) {
- /* Use VUPDATE interrupt */
- for (i = 0; i < adev->mode_info.num_crtc; i++) {
- src_id = VISLANDS30_IV_SRCID_D1_V_UPDATE_INT + i * 2;
- r = amdgpu_irq_add_id(adev, client_id, src_id, &adev->vupdate_irq);
- if (r) {
- drm_err(adev_to_drm(adev), "Failed to add vupdate irq id!\n");
- return r;
- }
-
- int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT;
- int_params.irq_source =
- dc_interrupt_to_irq_source(dc, src_id, 0);
-
- if (int_params.irq_source == DC_IRQ_SOURCE_INVALID ||
- int_params.irq_source < DC_IRQ_SOURCE_VUPDATE1 ||
- int_params.irq_source > DC_IRQ_SOURCE_VUPDATE6) {
- drm_err(adev_to_drm(adev), "Failed to register vupdate irq!\n");
- return -EINVAL;
- }
-
- c_irq_params = &adev->dm.vupdate_params[
- int_params.irq_source - DC_IRQ_SOURCE_VUPDATE1];
- c_irq_params->adev = adev;
- c_irq_params->irq_src = int_params.irq_source;
-
- if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
- dm_vupdate_high_irq, c_irq_params))
- return -ENOMEM;
- }
- }
-
- /* Use GRPH_PFLIP interrupt */
- for (i = VISLANDS30_IV_SRCID_D1_GRPH_PFLIP;
- i <= VISLANDS30_IV_SRCID_D6_GRPH_PFLIP; i += 2) {
- r = amdgpu_irq_add_id(adev, client_id, i, &adev->pageflip_irq);
- if (r) {
- drm_err(adev_to_drm(adev), "Failed to add page flip irq id!\n");
- return r;
- }
-
- int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT;
- int_params.irq_source =
- dc_interrupt_to_irq_source(dc, i, 0);
-
- if (int_params.irq_source == DC_IRQ_SOURCE_INVALID ||
- int_params.irq_source < DC_IRQ_SOURCE_PFLIP_FIRST ||
- int_params.irq_source > DC_IRQ_SOURCE_PFLIP_LAST) {
- drm_err(adev_to_drm(adev), "Failed to register pflip irq!\n");
- return -EINVAL;
- }
-
- c_irq_params = &adev->dm.pflip_params[int_params.irq_source - DC_IRQ_SOURCE_PFLIP_FIRST];
-
- c_irq_params->adev = adev;
- c_irq_params->irq_src = int_params.irq_source;
-
- if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
- dm_pflip_high_irq, c_irq_params))
- return -ENOMEM;
- }
-
- /* HPD */
- r = amdgpu_irq_add_id(adev, client_id,
- VISLANDS30_IV_SRCID_HOTPLUG_DETECT_A, &adev->hpd_irq);
- if (r) {
- drm_err(adev_to_drm(adev), "Failed to add hpd irq id!\n");
- return r;
- }
-
- r = register_hpd_handlers(adev);
-
- return r;
- }
-
- /* Register IRQ sources and initialize IRQ callbacks */
- static int dcn10_register_irq_handlers(struct amdgpu_device *adev)
- {
- struct dc *dc = adev->dm.dc;
- struct common_irq_params *c_irq_params;
- struct dc_interrupt_params int_params = {0};
- int r;
- int i;
- #if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
- static const unsigned int vrtl_int_srcid[] = {
- DCN_1_0__SRCID__OTG1_VERTICAL_INTERRUPT0_CONTROL,
- DCN_1_0__SRCID__OTG2_VERTICAL_INTERRUPT0_CONTROL,
- DCN_1_0__SRCID__OTG3_VERTICAL_INTERRUPT0_CONTROL,
- DCN_1_0__SRCID__OTG4_VERTICAL_INTERRUPT0_CONTROL,
- DCN_1_0__SRCID__OTG5_VERTICAL_INTERRUPT0_CONTROL,
- DCN_1_0__SRCID__OTG6_VERTICAL_INTERRUPT0_CONTROL
- };
- #endif
-
- int_params.requested_polarity = INTERRUPT_POLARITY_DEFAULT;
- int_params.current_polarity = INTERRUPT_POLARITY_DEFAULT;
-
- /*
- * Actions of amdgpu_irq_add_id():
- * 1. Register a set() function with base driver.
- * Base driver will call set() function to enable/disable an
- * interrupt in DC hardware.
- * 2. Register amdgpu_dm_irq_handler().
- * Base driver will call amdgpu_dm_irq_handler() for ALL interrupts
- * coming from DC hardware.
- * amdgpu_dm_irq_handler() will re-direct the interrupt to DC
- * for acknowledging and handling.
- */
-
- /* Use otg vertical line interrupt */
- #if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
- for (i = 0; i <= adev->mode_info.num_crtc - 1; i++) {
- r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE,
- vrtl_int_srcid[i], &adev->vline0_irq);
-
- if (r) {
- drm_err(adev_to_drm(adev), "Failed to add vline0 irq id!\n");
- return r;
- }
-
- int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT;
- int_params.irq_source =
- dc_interrupt_to_irq_source(dc, vrtl_int_srcid[i], 0);
-
- if (int_params.irq_source == DC_IRQ_SOURCE_INVALID ||
- int_params.irq_source < DC_IRQ_SOURCE_DC1_VLINE0 ||
- int_params.irq_source > DC_IRQ_SOURCE_DC6_VLINE0) {
- drm_err(adev_to_drm(adev), "Failed to register vline0 irq!\n");
- return -EINVAL;
- }
-
- c_irq_params = &adev->dm.vline0_params[int_params.irq_source
- - DC_IRQ_SOURCE_DC1_VLINE0];
-
- c_irq_params->adev = adev;
- c_irq_params->irq_src = int_params.irq_source;
-
- if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
- dm_dcn_vertical_interrupt0_high_irq,
- c_irq_params))
- return -ENOMEM;
- }
- #endif
-
- /* Use VUPDATE_NO_LOCK interrupt on DCN, which seems to correspond to
- * the regular VUPDATE interrupt on DCE. We want DC_IRQ_SOURCE_VUPDATEx
- * to trigger at end of each vblank, regardless of state of the lock,
- * matching DCE behaviour.
- */
- for (i = DCN_1_0__SRCID__OTG0_IHC_V_UPDATE_NO_LOCK_INTERRUPT;
- i <= DCN_1_0__SRCID__OTG0_IHC_V_UPDATE_NO_LOCK_INTERRUPT + adev->mode_info.num_crtc - 1;
- i++) {
- r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, i, &adev->vupdate_irq);
-
- if (r) {
- drm_err(adev_to_drm(adev), "Failed to add vupdate irq id!\n");
- return r;
- }
-
- int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT;
- int_params.irq_source =
- dc_interrupt_to_irq_source(dc, i, 0);
-
- if (int_params.irq_source == DC_IRQ_SOURCE_INVALID ||
- int_params.irq_source < DC_IRQ_SOURCE_VUPDATE1 ||
- int_params.irq_source > DC_IRQ_SOURCE_VUPDATE6) {
- drm_err(adev_to_drm(adev), "Failed to register vupdate irq!\n");
- return -EINVAL;
- }
-
- c_irq_params = &adev->dm.vupdate_params[int_params.irq_source - DC_IRQ_SOURCE_VUPDATE1];
-
- c_irq_params->adev = adev;
- c_irq_params->irq_src = int_params.irq_source;
-
- if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
- dm_vupdate_high_irq, c_irq_params))
- return -ENOMEM;
- }
-
- /* HPD */
- r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, DCN_1_0__SRCID__DC_HPD1_INT,
- &adev->hpd_irq);
- if (r) {
- drm_err(adev_to_drm(adev), "Failed to add hpd irq id!\n");
- return r;
- }
-
- r = register_hpd_handlers(adev);
-
- return r;
- }
- /* Register Outbox IRQ sources and initialize IRQ callbacks */
- static int register_outbox_irq_handlers(struct amdgpu_device *adev)
- {
- struct dc *dc = adev->dm.dc;
- struct common_irq_params *c_irq_params;
- struct dc_interrupt_params int_params = {0};
- int r, i;
-
- int_params.requested_polarity = INTERRUPT_POLARITY_DEFAULT;
- int_params.current_polarity = INTERRUPT_POLARITY_DEFAULT;
-
- r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, DCN_1_0__SRCID__DMCUB_OUTBOX_LOW_PRIORITY_READY_INT,
- &adev->dmub_outbox_irq);
- if (r) {
- drm_err(adev_to_drm(adev), "Failed to add outbox irq id!\n");
- return r;
- }
-
- if (dc->ctx->dmub_srv) {
- i = DCN_1_0__SRCID__DMCUB_OUTBOX_LOW_PRIORITY_READY_INT;
- int_params.int_context = INTERRUPT_LOW_IRQ_CONTEXT;
- int_params.irq_source =
- dc_interrupt_to_irq_source(dc, i, 0);
-
- c_irq_params = &adev->dm.dmub_outbox_params[0];
-
- c_irq_params->adev = adev;
- c_irq_params->irq_src = int_params.irq_source;
-
- if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
- dm_dmub_outbox1_low_irq, c_irq_params))
- return -ENOMEM;
- }
-
- return 0;
- }
-
/*
* Acquires the lock for the atomic state object and returns
* the new atomic state.
@@@ -5196,420 -2401,6 +2401,6 @@@ static int amdgpu_dm_mode_config_init(s
return 0;
}
- #define AMDGPU_DM_DEFAULT_MIN_BACKLIGHT 12
- #define AMDGPU_DM_DEFAULT_MAX_BACKLIGHT 255
- #define AMDGPU_DM_MIN_SPREAD ((AMDGPU_DM_DEFAULT_MAX_BACKLIGHT - AMDGPU_DM_DEFAULT_MIN_BACKLIGHT) / 2)
- #define AUX_BL_DEFAULT_TRANSITION_TIME_MS 50
-
- void amdgpu_dm_update_backlight_caps(struct amdgpu_display_manager *dm,
- int bl_idx)
- {
- struct amdgpu_dm_backlight_caps *caps = &dm->backlight_caps[bl_idx];
-
- if (caps->caps_valid)
- return;
-
- #if defined(CONFIG_ACPI)
- amdgpu_acpi_get_backlight_caps(caps);
-
- /* validate the firmware value is sane */
- if (caps->caps_valid) {
- int spread = caps->max_input_signal - caps->min_input_signal;
-
- if (caps->max_input_signal > AMDGPU_DM_DEFAULT_MAX_BACKLIGHT ||
- caps->min_input_signal < 0 ||
- spread > AMDGPU_DM_DEFAULT_MAX_BACKLIGHT ||
- spread < AMDGPU_DM_MIN_SPREAD) {
- drm_dbg_kms(adev_to_drm(dm->adev), "DM: Invalid backlight caps: min=%d, max=%d\n",
- caps->min_input_signal, caps->max_input_signal);
- caps->caps_valid = false;
- }
- }
-
- if (!caps->caps_valid) {
- caps->min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT;
- caps->max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
- caps->caps_valid = true;
- }
- #else
- if (caps->aux_support)
- return;
-
- caps->min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT;
- caps->max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
- caps->caps_valid = true;
- #endif
- }
-
- static int get_brightness_range(const struct amdgpu_dm_backlight_caps *caps,
- unsigned int *min, unsigned int *max)
- {
- if (!caps)
- return 0;
-
- if (caps->aux_support) {
- // Firmware limits are in nits, DC API wants millinits.
- *max = 1000 * caps->aux_max_input_signal;
- *min = 1000 * caps->aux_min_input_signal;
- } else {
- // Firmware limits are 8-bit, PWM control is 16-bit.
- *max = 0x101 * caps->max_input_signal;
- *min = 0x101 * caps->min_input_signal;
- }
- return 1;
- }
-
- /* Rescale from [min..max] to [0..AMDGPU_MAX_BL_LEVEL] */
- static inline u32 scale_input_to_fw(int min, int max, u64 input)
- {
- return DIV_ROUND_CLOSEST_ULL(input * AMDGPU_MAX_BL_LEVEL, max - min);
- }
-
- /* Rescale from [0..AMDGPU_MAX_BL_LEVEL] to [min..max] */
- static inline u32 scale_fw_to_input(int min, int max, u64 input)
- {
- return min + DIV_ROUND_CLOSEST_ULL(input * (max - min), AMDGPU_MAX_BL_LEVEL);
- }
-
- static void convert_custom_brightness(const struct amdgpu_dm_backlight_caps *caps,
- unsigned int min, unsigned int max,
- uint32_t *user_brightness)
- {
- u32 brightness = scale_input_to_fw(min, max, *user_brightness);
- u8 lower_signal, upper_signal, upper_lum, lower_lum, lum;
- int left, right;
-
- if (amdgpu_dc_debug_mask & DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE)
- return;
-
- if (!caps->data_points)
- return;
-
- /*
- * Handle the case where brightness is below the first data point
- * Interpolate between (0,0) and (first_signal, first_lum)
- */
- if (brightness < caps->luminance_data[0].input_signal) {
- lum = DIV_ROUND_CLOSEST(caps->luminance_data[0].luminance * brightness,
- caps->luminance_data[0].input_signal);
- goto scale;
- }
-
- left = 0;
- right = caps->data_points - 1;
- while (left <= right) {
- int mid = left + (right - left) / 2;
- u8 signal = caps->luminance_data[mid].input_signal;
-
- /* Exact match found */
- if (signal == brightness) {
- lum = caps->luminance_data[mid].luminance;
- goto scale;
- }
-
- if (signal < brightness)
- left = mid + 1;
- else
- right = mid - 1;
- }
-
- /* verify bound */
- if (left >= caps->data_points)
- left = caps->data_points - 1;
-
- /* At this point, left > right */
- lower_signal = caps->luminance_data[right].input_signal;
- upper_signal = caps->luminance_data[left].input_signal;
- lower_lum = caps->luminance_data[right].luminance;
- upper_lum = caps->luminance_data[left].luminance;
-
- /* interpolate */
- if (right == left || !lower_lum)
- lum = upper_lum;
- else
- lum = lower_lum + DIV_ROUND_CLOSEST((upper_lum - lower_lum) *
- (brightness - lower_signal),
- upper_signal - lower_signal);
- scale:
- *user_brightness = scale_fw_to_input(min, max,
- DIV_ROUND_CLOSEST(lum * brightness, 101));
- }
-
- static u32 convert_brightness_from_user(const struct amdgpu_dm_backlight_caps *caps,
- uint32_t brightness)
- {
- unsigned int min, max;
-
- if (!get_brightness_range(caps, &min, &max))
- return brightness;
-
- convert_custom_brightness(caps, min, max, &brightness);
-
- // Rescale 0..max to min..max
- return min + DIV_ROUND_CLOSEST_ULL((u64)(max - min) * brightness, max);
- }
-
- static u32 convert_brightness_to_user(const struct amdgpu_dm_backlight_caps *caps,
- uint32_t brightness)
- {
- unsigned int min, max;
-
- if (!get_brightness_range(caps, &min, &max))
- return brightness;
-
- if (brightness < min)
- return 0;
- // Rescale min..max to 0..max
- return DIV_ROUND_CLOSEST_ULL((u64)max * (brightness - min),
- max - min);
- }
-
- static struct dc_stream_state *dm_find_stream_with_link(
- struct amdgpu_display_manager *dm,
- struct dc_link *link)
- {
- struct dc_state *cur_dc_state = dm->dc->current_state;
- struct dc_stream_state *stream = NULL;
- int i;
-
- for (i = 0; i < cur_dc_state->stream_count; i++) {
- stream = cur_dc_state->streams[i];
- if (stream->link == link)
- return stream;
- }
-
- return NULL;
- }
-
- static void amdgpu_dm_backlight_set_level(struct amdgpu_display_manager *dm,
- int bl_idx,
- u32 user_brightness)
- {
- struct amdgpu_dm_backlight_caps *caps;
- struct dc_link *link;
- u32 brightness = 0;
- bool rc = false, reallow_idle = false;
- struct drm_connector *connector;
- struct dc_stream_state *stream;
- unsigned int min, max;
-
- list_for_each_entry(connector, &dm->ddev->mode_config.connector_list, head) {
- struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
-
- if (aconnector->bl_idx != bl_idx)
- continue;
-
- /* if connector is off, save the brightness for next time it's on */
- if (!aconnector->base.encoder) {
- dm->brightness[bl_idx] = user_brightness;
- dm->actual_brightness[bl_idx] = 0;
- return;
- }
- }
-
- amdgpu_dm_update_backlight_caps(dm, bl_idx);
- caps = &dm->backlight_caps[bl_idx];
-
- dm->brightness[bl_idx] = user_brightness;
- /* update scratch register */
- if (bl_idx == 0)
- amdgpu_atombios_scratch_regs_set_backlight_level(dm->adev, dm->brightness[bl_idx]);
- brightness = convert_brightness_from_user(caps, dm->brightness[bl_idx]);
- link = (struct dc_link *)dm->backlight_link[bl_idx];
-
- /* Apply brightness quirk */
- if (caps->brightness_mask)
- brightness |= caps->brightness_mask;
-
- if (trace_amdgpu_dm_brightness_enabled()) {
- trace_amdgpu_dm_brightness(__builtin_return_address(0),
- user_brightness,
- brightness,
- caps->aux_support,
- power_supply_is_system_supplied() > 0);
- }
-
- stream = dm_find_stream_with_link(dm, link);
- if (!stream)
- return;
-
- mutex_lock(&dm->dc_lock);
- if (dm->dc->caps.ips_support && dm->dc->ctx->dmub_srv->idle_allowed) {
- dc_allow_idle_optimizations(dm->dc, false);
- reallow_idle = true;
- }
-
- if (caps->aux_support) {
- rc = mod_power_set_backlight_nits(dm->power_module, stream, brightness,
- AUX_BL_DEFAULT_TRANSITION_TIME_MS, false, true);
- } else {
- /* power module uses millipercent */
- get_brightness_range(caps, &min, &max);
- brightness = DIV_ROUND_CLOSEST(brightness * 100, (max - min)) * 1000;
- rc = mod_power_set_backlight_percent(dm->power_module, stream,
- brightness, 0, false);
- }
-
- /*
- * Some kms clients create a ramped backlight transition effect
- * by rapidly changing the backlight. Yet we must wait on dmcub
- * fw to exit psr/replay before programming backlight. To
- * prevent lag, keep disable psr/replay and let the next atomic
- * flip clear the event.
- *
- * ToDo: use ISM to handle rapidly backlight change
- *
- * Rapidly backlight change is similar to rapidly cursor events,
- * which is now handled by ISM. ISM can delay the event until system
- * is really idle, so we may use ISM to handle backlight change as well.
- */
- amdgpu_dm_psr_set_event(dm, stream, true,
- psr_event_hw_programming, true);
- amdgpu_dm_replay_set_event(dm, stream, true,
- replay_event_hw_programming, true);
-
- if (dm->dc->caps.ips_support && reallow_idle)
- dc_allow_idle_optimizations(dm->dc, true);
-
- mutex_unlock(&dm->dc_lock);
-
- if (rc)
- dm->actual_brightness[bl_idx] = user_brightness;
- }
-
- static int amdgpu_dm_backlight_update_status(struct backlight_device *bd)
- {
- struct amdgpu_display_manager *dm = bl_get_data(bd);
- int i;
-
- for (i = 0; i < dm->num_of_edps; i++) {
- if (bd == dm->backlight_dev[i])
- break;
- }
- if (i >= AMDGPU_DM_MAX_NUM_EDP)
- i = 0;
- amdgpu_dm_backlight_set_level(dm, i, bd->props.brightness);
-
- return 0;
- }
-
- static u32 amdgpu_dm_backlight_get_level(struct amdgpu_display_manager *dm,
- int bl_idx)
- {
- int ret;
- struct amdgpu_dm_backlight_caps caps;
- struct dc_link *link = (struct dc_link *)dm->backlight_link[bl_idx];
-
- amdgpu_dm_update_backlight_caps(dm, bl_idx);
- caps = dm->backlight_caps[bl_idx];
-
- if (caps.aux_support) {
- u32 avg, peak;
-
- if (!dc_link_get_backlight_level_nits(link, &avg, &peak))
- return dm->brightness[bl_idx];
- return convert_brightness_to_user(&caps, avg);
- }
-
- ret = dc_link_get_backlight_level(link);
-
- if (ret == DC_ERROR_UNEXPECTED)
- return dm->brightness[bl_idx];
-
- return convert_brightness_to_user(&caps, ret);
- }
-
- static int amdgpu_dm_backlight_get_brightness(struct backlight_device *bd)
- {
- struct amdgpu_display_manager *dm = bl_get_data(bd);
- int i;
-
- for (i = 0; i < dm->num_of_edps; i++) {
- if (bd == dm->backlight_dev[i])
- break;
- }
- if (i >= AMDGPU_DM_MAX_NUM_EDP)
- i = 0;
- return amdgpu_dm_backlight_get_level(dm, i);
- }
-
- static const struct backlight_ops amdgpu_dm_backlight_ops = {
- .options = BL_CORE_SUSPENDRESUME,
- .get_brightness = amdgpu_dm_backlight_get_brightness,
- .update_status = amdgpu_dm_backlight_update_status,
- };
-
- static void
- amdgpu_dm_register_backlight_device(struct amdgpu_dm_connector *aconnector)
- {
- struct drm_device *drm = aconnector->base.dev;
- struct amdgpu_display_manager *dm = &drm_to_adev(drm)->dm;
- struct backlight_properties props = { 0 };
- struct amdgpu_dm_backlight_caps *caps;
- char bl_name[16];
- int min, max;
- int real_brightness;
- int init_brightness;
-
- if (aconnector->bl_idx == -1)
- return;
-
- if (!acpi_video_backlight_use_native()) {
- drm_info(drm, "Skipping amdgpu DM backlight registration\n");
- /* Try registering an ACPI video backlight device instead. */
- acpi_video_register_backlight();
- return;
- }
-
- caps = &dm->backlight_caps[aconnector->bl_idx];
- if (get_brightness_range(caps, &min, &max)) {
- if (power_supply_is_system_supplied() > 0)
- props.brightness = DIV_ROUND_CLOSEST(max * caps->ac_level, 100);
- else
- props.brightness = DIV_ROUND_CLOSEST(max * caps->dc_level, 100);
- /* min is zero, so max needs to be adjusted */
- props.max_brightness = max;
- drm_dbg(drm, "Backlight caps: min: %d, max: %d, ac %d, dc %d\n", min, max,
- caps->ac_level, caps->dc_level);
- } else
- props.brightness = props.max_brightness = MAX_BACKLIGHT_LEVEL;
-
- init_brightness = props.brightness;
-
- if (caps->data_points && !(amdgpu_dc_debug_mask & DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE)) {
- drm_info(drm, "Using custom brightness curve\n");
- props.scale = BACKLIGHT_SCALE_NON_LINEAR;
- } else
- props.scale = BACKLIGHT_SCALE_LINEAR;
- props.type = BACKLIGHT_RAW;
-
- snprintf(bl_name, sizeof(bl_name), "amdgpu_bl%d",
- drm->primary->index + aconnector->bl_idx);
-
- dm->backlight_dev[aconnector->bl_idx] =
- backlight_device_register(bl_name, aconnector->base.kdev, dm,
- &amdgpu_dm_backlight_ops, &props);
- dm->brightness[aconnector->bl_idx] = props.brightness;
-
- if (IS_ERR(dm->backlight_dev[aconnector->bl_idx])) {
- drm_err(drm, "DM: Backlight registration failed!\n");
- dm->backlight_dev[aconnector->bl_idx] = NULL;
- } else {
- /*
- * dm->brightness[x] can be inconsistent just after startup until
- * ops.get_brightness is called.
- */
- real_brightness =
- amdgpu_dm_backlight_ops.get_brightness(dm->backlight_dev[aconnector->bl_idx]);
-
- if (real_brightness != init_brightness) {
- dm->actual_brightness[aconnector->bl_idx] = real_brightness;
- dm->brightness[aconnector->bl_idx] = real_brightness;
- }
- drm_dbg_driver(drm, "DM: Registered Backlight device: %s\n", bl_name);
- }
- }
-
static int initialize_plane(struct amdgpu_display_manager *dm,
struct amdgpu_mode_info *mode_info, int plane_id,
enum drm_plane_type plane_type,
@@@ -5651,42 -2442,6 +2442,6 @@@
}
- static void setup_backlight_device(struct amdgpu_display_manager *dm,
- struct amdgpu_dm_connector *aconnector)
- {
- struct amdgpu_dm_backlight_caps *caps;
- struct dc_link *link = aconnector->dc_link;
- int bl_idx = dm->num_of_edps;
-
- if (!(link->connector_signal & (SIGNAL_TYPE_EDP | SIGNAL_TYPE_LVDS)) ||
- link->type == dc_connection_none)
- return;
-
- if (dm->num_of_edps >= AMDGPU_DM_MAX_NUM_EDP) {
- drm_warn(adev_to_drm(dm->adev), "Too much eDP connections, skipping backlight setup for additional eDPs\n");
- return;
- }
-
- aconnector->bl_idx = bl_idx;
-
- amdgpu_dm_update_backlight_caps(dm, bl_idx);
- dm->backlight_link[bl_idx] = link;
- dm->num_of_edps++;
-
- update_connector_ext_caps(aconnector);
- caps = &dm->backlight_caps[aconnector->bl_idx];
-
- /* Only offer ABM property when non-OLED and user didn't turn off by module parameter */
- if (caps->ext_caps && !caps->ext_caps->bits.oled && amdgpu_dm_abm_level < 0)
- drm_object_attach_property(&aconnector->base.base,
- dm->adev->mode_info.abm_level_property,
- ABM_SYSFS_CONTROL);
- }
-
- static void amdgpu_set_panel_orientation(struct drm_connector *connector);
-
-
-
/*
* In this architecture, the association
* connector -> encoder -> crtc
@@@ -5798,7 -2553,7 +2553,7 @@@ static int amdgpu_dm_initialize_drm_dev
case IP_VERSION(4, 0, 1):
case IP_VERSION(4, 2, 0):
case IP_VERSION(4, 2, 1):
- if (register_outbox_irq_handlers(dm->adev)) {
+ if (amdgpu_dm_register_outbox_irq_handlers(dm->adev)) {
drm_err(adev_to_drm(adev), "DM: Failed to initialize IRQ\n");
goto fail;
}
@@@ -5911,7 -2666,7 +2666,7 @@@
drm_err(adev_to_drm(adev), "KMS: Failed to detect connector\n");
if (aconnector->base.force && new_connection_type == dc_connection_none) {
- emulated_link_detect(link);
+ amdgpu_dm_emulated_link_detect(link);
amdgpu_dm_update_connector_after_detect(aconnector);
} else {
bool ret = false;
@@@ -5923,7 -2678,7 +2678,7 @@@
if (ret) {
amdgpu_dm_update_connector_after_detect(aconnector);
- setup_backlight_device(dm, aconnector);
+ amdgpu_dm_setup_backlight_device(dm, aconnector);
/* Disable PSR if Replay can be enabled */
if (replay_feature_enabled)
@@@ -5975,7 -2730,7 +2730,7 @@@
case CHIP_VEGA10:
case CHIP_VEGA12:
case CHIP_VEGA20:
- if (dce110_register_irq_handlers(dm->adev)) {
+ if (amdgpu_dm_dce110_register_irq_handlers(dm->adev)) {
drm_err(adev_to_drm(adev), "DM: Failed to initialize IRQ\n");
goto fail;
}
@@@ -6005,7 -2760,7 +2760,7 @@@
case IP_VERSION(4, 0, 1):
case IP_VERSION(4, 2, 0):
case IP_VERSION(4, 2, 1):
- if (dcn10_register_irq_handlers(dm->adev)) {
+ if (amdgpu_dm_dcn10_register_irq_handlers(dm->adev)) {
drm_err(adev_to_drm(adev), "DM: Failed to initialize IRQ\n");
goto fail;
}
@@@ -6096,78 -2851,6 +2851,6 @@@ DEVICE_ATTR_WO(s3_debug)
#endif
- static int dm_init_microcode(struct amdgpu_device *adev)
- {
- char *fw_name_dmub;
- int r;
-
- switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
- case IP_VERSION(2, 1, 0):
- fw_name_dmub = FIRMWARE_RENOIR_DMUB;
- if (ASICREV_IS_GREEN_SARDINE(adev->external_rev_id))
- fw_name_dmub = FIRMWARE_GREEN_SARDINE_DMUB;
- break;
- case IP_VERSION(3, 0, 0):
- if (amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(10, 3, 0))
- fw_name_dmub = FIRMWARE_SIENNA_CICHLID_DMUB;
- else
- fw_name_dmub = FIRMWARE_NAVY_FLOUNDER_DMUB;
- break;
- case IP_VERSION(3, 0, 1):
- fw_name_dmub = FIRMWARE_VANGOGH_DMUB;
- break;
- case IP_VERSION(3, 0, 2):
- fw_name_dmub = FIRMWARE_DIMGREY_CAVEFISH_DMUB;
- break;
- case IP_VERSION(3, 0, 3):
- fw_name_dmub = FIRMWARE_BEIGE_GOBY_DMUB;
- break;
- case IP_VERSION(3, 1, 2):
- case IP_VERSION(3, 1, 3):
- fw_name_dmub = FIRMWARE_YELLOW_CARP_DMUB;
- break;
- case IP_VERSION(3, 1, 4):
- fw_name_dmub = FIRMWARE_DCN_314_DMUB;
- break;
- case IP_VERSION(3, 1, 5):
- fw_name_dmub = FIRMWARE_DCN_315_DMUB;
- break;
- case IP_VERSION(3, 1, 6):
- fw_name_dmub = FIRMWARE_DCN316_DMUB;
- break;
- case IP_VERSION(3, 2, 0):
- fw_name_dmub = FIRMWARE_DCN_V3_2_0_DMCUB;
- break;
- case IP_VERSION(3, 2, 1):
- fw_name_dmub = FIRMWARE_DCN_V3_2_1_DMCUB;
- break;
- case IP_VERSION(3, 5, 0):
- fw_name_dmub = FIRMWARE_DCN_35_DMUB;
- break;
- case IP_VERSION(3, 5, 1):
- fw_name_dmub = FIRMWARE_DCN_351_DMUB;
- break;
- case IP_VERSION(3, 6, 0):
- fw_name_dmub = FIRMWARE_DCN_36_DMUB;
- break;
- case IP_VERSION(4, 0, 1):
- fw_name_dmub = FIRMWARE_DCN_401_DMUB;
- break;
- case IP_VERSION(4, 2, 0):
- fw_name_dmub = FIRMWARE_DCN_42_DMUB;
- break;
- case IP_VERSION(4, 2, 1):
- fw_name_dmub = FIRMWARE_DCN_42B_DMUB;
- break;
- default:
- /* ASIC doesn't support DMUB. */
- return 0;
- }
- r = amdgpu_ucode_request(adev, &adev->dm.dmub_fw, AMDGPU_UCODE_REQUIRED,
- "%s", fw_name_dmub);
- return r;
- }
-
static int dm_early_init(struct amdgpu_ip_block *ip_block)
{
struct amdgpu_device *adev = ip_block->adev;
@@@ -6318,22 -3001,13 +3001,13 @@@
return dm_init_microcode(adev);
}
- static bool modereset_required(struct drm_crtc_state *crtc_state)
+ STATIC_IFN_KUNIT bool modereset_required(struct drm_crtc_state *crtc_state)
{
return !crtc_state->active && drm_atomic_crtc_needs_modeset(crtc_state);
}
+ EXPORT_IF_KUNIT(modereset_required);
- static void amdgpu_dm_encoder_destroy(struct drm_encoder *encoder)
- {
- drm_encoder_cleanup(encoder);
- kfree(encoder);
- }
-
- static const struct drm_encoder_funcs amdgpu_dm_encoder_funcs = {
- .destroy = amdgpu_dm_encoder_destroy,
- };
-
- static int
+ STATIC_IFN_KUNIT int
fill_plane_color_attributes(const struct drm_plane_state *plane_state,
const enum surface_pixel_format format,
enum dc_color_space *color_space)
@@@ -6380,6 -3054,7 +3054,7 @@@
return 0;
}
+ EXPORT_IF_KUNIT(fill_plane_color_attributes);
static int
fill_dc_plane_info_and_addr(struct amdgpu_device *adev,
@@@ -6718,7 -3393,7 +3393,7 @@@ ffu
&flip_addrs->dirty_rect_count, true);
}
- static void update_stream_scaling_settings(struct drm_device *dev,
+ void amdgpu_dm_update_stream_scaling_settings(struct drm_device *dev,
const struct drm_display_mode *mode,
const struct dm_connector_state *dm_state,
struct dc_stream_state *stream)
@@@ -6774,2063 -3449,6 +3449,6 @@@
}
- static enum dc_color_depth
- convert_color_depth_from_display_info(const struct drm_connector *connector,
- bool is_y420, int requested_bpc)
- {
- u8 bpc;
-
- if (is_y420) {
- bpc = 8;
-
- /* Cap display bpc based on HDMI 2.0 HF-VSDB */
- if (connector->display_info.hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_48)
- bpc = 16;
- else if (connector->display_info.hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_36)
- bpc = 12;
- else if (connector->display_info.hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30)
- bpc = 10;
- } else {
- bpc = (uint8_t)connector->display_info.bpc;
- /* Assume 8 bpc by default if no bpc is specified. */
- bpc = bpc ? bpc : 8;
- }
-
- if (requested_bpc > 0) {
- /*
- * Cap display bpc based on the user requested value.
- *
- * The value for state->max_bpc may not correctly updated
- * depending on when the connector gets added to the state
- * or if this was called outside of atomic check, so it
- * can't be used directly.
- */
- bpc = min_t(u8, bpc, requested_bpc);
-
- /* Round down to the nearest even number. */
- bpc = bpc - (bpc & 1);
- }
-
- switch (bpc) {
- case 0:
- /*
- * Temporary Work around, DRM doesn't parse color depth for
- * EDID revision before 1.4
- * TODO: Fix edid parsing
- */
- return COLOR_DEPTH_888;
- case 6:
- return COLOR_DEPTH_666;
- case 8:
- return COLOR_DEPTH_888;
- case 10:
- return COLOR_DEPTH_101010;
- case 12:
- return COLOR_DEPTH_121212;
- case 14:
- return COLOR_DEPTH_141414;
- case 16:
- return COLOR_DEPTH_161616;
- default:
- return COLOR_DEPTH_UNDEFINED;
- }
- }
-
- static enum dc_aspect_ratio
- get_aspect_ratio(const struct drm_display_mode *mode_in)
- {
- /* 1-1 mapping, since both enums follow the HDMI spec. */
- return (enum dc_aspect_ratio) mode_in->picture_aspect_ratio;
- }
-
- static enum dc_color_space
- get_output_color_space(const struct dc_crtc_timing *dc_crtc_timing,
- const struct drm_connector_state *connector_state)
- {
- enum dc_color_space color_space = COLOR_SPACE_SRGB;
-
- switch (connector_state->colorspace) {
- case DRM_MODE_COLORIMETRY_BT601_YCC:
- if (dc_crtc_timing->flags.Y_ONLY)
- color_space = COLOR_SPACE_YCBCR601_LIMITED;
- else
- color_space = COLOR_SPACE_YCBCR601;
- break;
- case DRM_MODE_COLORIMETRY_BT709_YCC:
- if (dc_crtc_timing->flags.Y_ONLY)
- color_space = COLOR_SPACE_YCBCR709_LIMITED;
- else
- color_space = COLOR_SPACE_YCBCR709;
- break;
- case DRM_MODE_COLORIMETRY_OPRGB:
- color_space = COLOR_SPACE_ADOBERGB;
- break;
- case DRM_MODE_COLORIMETRY_BT2020_RGB:
- case DRM_MODE_COLORIMETRY_BT2020_YCC:
- if (dc_crtc_timing->pixel_encoding == PIXEL_ENCODING_RGB)
- color_space = COLOR_SPACE_2020_RGB_FULLRANGE;
- else
- color_space = COLOR_SPACE_2020_YCBCR_LIMITED;
- break;
- case DRM_MODE_COLORIMETRY_DEFAULT: // ITU601
- default:
- if (dc_crtc_timing->pixel_encoding == PIXEL_ENCODING_RGB) {
- color_space = COLOR_SPACE_SRGB;
- if (connector_state->hdmi.broadcast_rgb == DRM_HDMI_BROADCAST_RGB_LIMITED)
- color_space = COLOR_SPACE_SRGB_LIMITED;
- /*
- * 27030khz is the separation point between HDTV and SDTV
- * according to HDMI spec, we use YCbCr709 and YCbCr601
- * respectively
- */
- } else if (dc_crtc_timing->pix_clk_100hz > 270300) {
- if (dc_crtc_timing->flags.Y_ONLY)
- color_space =
- COLOR_SPACE_YCBCR709_LIMITED;
- else
- color_space = COLOR_SPACE_YCBCR709;
- } else {
- if (dc_crtc_timing->flags.Y_ONLY)
- color_space =
- COLOR_SPACE_YCBCR601_LIMITED;
- else
- color_space = COLOR_SPACE_YCBCR601;
- }
- break;
- }
-
- return color_space;
- }
-
- static enum display_content_type
- get_output_content_type(const struct drm_connector_state *connector_state)
- {
- switch (connector_state->content_type) {
- default:
- case DRM_MODE_CONTENT_TYPE_NO_DATA:
- return DISPLAY_CONTENT_TYPE_NO_DATA;
- case DRM_MODE_CONTENT_TYPE_GRAPHICS:
- return DISPLAY_CONTENT_TYPE_GRAPHICS;
- case DRM_MODE_CONTENT_TYPE_PHOTO:
- return DISPLAY_CONTENT_TYPE_PHOTO;
- case DRM_MODE_CONTENT_TYPE_CINEMA:
- return DISPLAY_CONTENT_TYPE_CINEMA;
- case DRM_MODE_CONTENT_TYPE_GAME:
- return DISPLAY_CONTENT_TYPE_GAME;
- }
- }
-
- static bool adjust_colour_depth_from_display_info(
- struct dc_crtc_timing *timing_out,
- const struct drm_display_info *info)
- {
- enum dc_color_depth depth = timing_out->display_color_depth;
- int normalized_clk;
-
- do {
- normalized_clk = timing_out->pix_clk_100hz / 10;
- /* YCbCr 4:2:0 requires additional adjustment of 1/2 */
- if (timing_out->pixel_encoding == PIXEL_ENCODING_YCBCR420)
- normalized_clk /= 2;
- /* Adjusting pix clock following on HDMI spec based on colour depth */
- switch (depth) {
- case COLOR_DEPTH_888:
- break;
- case COLOR_DEPTH_101010:
- normalized_clk = (normalized_clk * 30) / 24;
- break;
- case COLOR_DEPTH_121212:
- normalized_clk = (normalized_clk * 36) / 24;
- break;
- case COLOR_DEPTH_161616:
- normalized_clk = (normalized_clk * 48) / 24;
- break;
- default:
- /* The above depths are the only ones valid for HDMI. */
- return false;
- }
- if (normalized_clk <= info->max_tmds_clock) {
- timing_out->display_color_depth = depth;
- return true;
- }
- } while (--depth > COLOR_DEPTH_666);
- return false;
- }
-
- static void fill_stream_properties_from_drm_display_mode(
- struct dc_stream_state *stream,
- const struct drm_display_mode *mode_in,
- const struct drm_connector *connector,
- const struct drm_connector_state *connector_state,
- const struct dc_stream_state *old_stream,
- int requested_bpc)
- {
- struct dc_crtc_timing *timing_out = &stream->timing;
- const struct drm_display_info *info = &connector->display_info;
- struct amdgpu_dm_connector *aconnector = NULL;
- struct hdmi_vendor_infoframe hv_frame;
- struct hdmi_avi_infoframe avi_frame;
- ssize_t err;
-
- if (connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK)
- aconnector = to_amdgpu_dm_connector(connector);
-
- memset(&hv_frame, 0, sizeof(hv_frame));
- memset(&avi_frame, 0, sizeof(avi_frame));
-
- timing_out->h_border_left = 0;
- timing_out->h_border_right = 0;
- timing_out->v_border_top = 0;
- timing_out->v_border_bottom = 0;
- /* TODO: un-hardcode */
- if (drm_mode_is_420_only(info, mode_in)
- && (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A ||
- stream->signal == SIGNAL_TYPE_HDMI_FRL)
- && aconnector
- && aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR420)
- timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420;
- else if (drm_mode_is_420_also(info, mode_in)
- && aconnector
- && (aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR420
- || aconnector->force_yuv420_output))
- timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420;
- else if ((connector->display_info.color_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422))
- && aconnector
- && (aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR422
- || aconnector->force_yuv422_output))
- timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR422;
- else if ((connector->display_info.color_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444))
- && (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A ||
- stream->signal == SIGNAL_TYPE_HDMI_FRL)
- && aconnector
- && aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR444)
- timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR444;
- else
- timing_out->pixel_encoding = PIXEL_ENCODING_RGB;
-
- timing_out->timing_3d_format = TIMING_3D_FORMAT_NONE;
- timing_out->display_color_depth = convert_color_depth_from_display_info(
- connector,
- (timing_out->pixel_encoding == PIXEL_ENCODING_YCBCR420),
- requested_bpc);
- timing_out->scan_type = SCANNING_TYPE_NODATA;
- timing_out->hdmi_vic = 0;
-
- if (old_stream) {
- timing_out->vic = old_stream->timing.vic;
- timing_out->flags.HSYNC_POSITIVE_POLARITY = old_stream->timing.flags.HSYNC_POSITIVE_POLARITY;
- timing_out->flags.VSYNC_POSITIVE_POLARITY = old_stream->timing.flags.VSYNC_POSITIVE_POLARITY;
- } else {
- timing_out->vic = drm_match_cea_mode(mode_in);
- if (mode_in->flags & DRM_MODE_FLAG_PHSYNC)
- timing_out->flags.HSYNC_POSITIVE_POLARITY = 1;
- if (mode_in->flags & DRM_MODE_FLAG_PVSYNC)
- timing_out->flags.VSYNC_POSITIVE_POLARITY = 1;
- }
-
- if (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A ||
- stream->signal == SIGNAL_TYPE_HDMI_FRL) {
- err = drm_hdmi_avi_infoframe_from_display_mode(&avi_frame,
- (struct drm_connector *)connector,
- mode_in);
- if (err < 0)
- drm_warn_once(connector->dev, "Failed to setup avi infoframe on connector %s: %zd\n",
- connector->name, err);
- timing_out->vic = avi_frame.video_code;
- err = drm_hdmi_vendor_infoframe_from_display_mode(&hv_frame,
- (struct drm_connector *)connector,
- mode_in);
- if (err < 0)
- drm_warn_once(connector->dev, "Failed to setup vendor infoframe on connector %s: %zd\n",
- connector->name, err);
- timing_out->hdmi_vic = hv_frame.vic;
- }
-
- if (aconnector && is_freesync_video_mode(mode_in, aconnector)) {
- timing_out->h_addressable = mode_in->hdisplay;
- timing_out->h_total = mode_in->htotal;
- timing_out->h_sync_width = mode_in->hsync_end - mode_in->hsync_start;
- timing_out->h_front_porch = mode_in->hsync_start - mode_in->hdisplay;
- timing_out->v_total = mode_in->vtotal;
- timing_out->v_addressable = mode_in->vdisplay;
- timing_out->v_front_porch = mode_in->vsync_start - mode_in->vdisplay;
- timing_out->v_sync_width = mode_in->vsync_end - mode_in->vsync_start;
- timing_out->pix_clk_100hz = mode_in->clock * 10;
- } else {
- timing_out->h_addressable = mode_in->crtc_hdisplay;
- timing_out->h_total = mode_in->crtc_htotal;
- timing_out->h_sync_width = mode_in->crtc_hsync_end - mode_in->crtc_hsync_start;
- timing_out->h_front_porch = mode_in->crtc_hsync_start - mode_in->crtc_hdisplay;
- timing_out->v_total = mode_in->crtc_vtotal;
- timing_out->v_addressable = mode_in->crtc_vdisplay;
- timing_out->v_front_porch = mode_in->crtc_vsync_start - mode_in->crtc_vdisplay;
- timing_out->v_sync_width = mode_in->crtc_vsync_end - mode_in->crtc_vsync_start;
- timing_out->pix_clk_100hz = mode_in->crtc_clock * 10;
- }
-
- timing_out->aspect_ratio = get_aspect_ratio(mode_in);
-
- stream->out_transfer_func.type = TF_TYPE_PREDEFINED;
- stream->out_transfer_func.tf = TRANSFER_FUNCTION_SRGB;
- if (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A) {
- if (!adjust_colour_depth_from_display_info(timing_out, info) &&
- drm_mode_is_420_also(info, mode_in) &&
- timing_out->pixel_encoding != PIXEL_ENCODING_YCBCR420) {
- timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420;
- adjust_colour_depth_from_display_info(timing_out, info);
- }
- }
-
- stream->output_color_space = get_output_color_space(timing_out, connector_state);
- stream->content_type = get_output_content_type(connector_state);
- }
-
- static void fill_audio_info(struct audio_info *audio_info,
- const struct drm_connector *drm_connector,
- const struct dc_sink *dc_sink)
- {
- int i = 0;
- int cea_revision = 0;
- const struct dc_edid_caps *edid_caps = &dc_sink->edid_caps;
-
- audio_info->manufacture_id = edid_caps->manufacturer_id;
- audio_info->product_id = edid_caps->product_id;
-
- cea_revision = drm_connector->display_info.cea_rev;
-
- strscpy(audio_info->display_name,
- edid_caps->display_name,
- AUDIO_INFO_DISPLAY_NAME_SIZE_IN_CHARS);
-
- if (cea_revision >= 3) {
- audio_info->mode_count = edid_caps->audio_mode_count;
-
- for (i = 0; i < audio_info->mode_count; ++i) {
- audio_info->modes[i].format_code =
- (enum audio_format_code)
- (edid_caps->audio_modes[i].format_code);
- audio_info->modes[i].channel_count =
- edid_caps->audio_modes[i].channel_count;
- audio_info->modes[i].sample_rates.all =
- edid_caps->audio_modes[i].sample_rate;
- audio_info->modes[i].sample_size =
- edid_caps->audio_modes[i].sample_size;
- }
- }
-
- audio_info->flags.all = edid_caps->speaker_flags;
-
- /* TODO: We only check for the progressive mode, check for interlace mode too */
- if (drm_connector->latency_present[0]) {
- audio_info->video_latency = drm_connector->video_latency[0];
- audio_info->audio_latency = drm_connector->audio_latency[0];
- }
-
- /* TODO: For DP, video and audio latency should be calculated from DPCD caps */
-
- }
-
- static void
- copy_crtc_timing_for_drm_display_mode(const struct drm_display_mode *src_mode,
- struct drm_display_mode *dst_mode)
- {
- dst_mode->crtc_hdisplay = src_mode->crtc_hdisplay;
- dst_mode->crtc_vdisplay = src_mode->crtc_vdisplay;
- dst_mode->crtc_clock = src_mode->crtc_clock;
- dst_mode->crtc_hblank_start = src_mode->crtc_hblank_start;
- dst_mode->crtc_hblank_end = src_mode->crtc_hblank_end;
- dst_mode->crtc_hsync_start = src_mode->crtc_hsync_start;
- dst_mode->crtc_hsync_end = src_mode->crtc_hsync_end;
- dst_mode->crtc_htotal = src_mode->crtc_htotal;
- dst_mode->crtc_hskew = src_mode->crtc_hskew;
- dst_mode->crtc_vblank_start = src_mode->crtc_vblank_start;
- dst_mode->crtc_vblank_end = src_mode->crtc_vblank_end;
- dst_mode->crtc_vsync_start = src_mode->crtc_vsync_start;
- dst_mode->crtc_vsync_end = src_mode->crtc_vsync_end;
- dst_mode->crtc_vtotal = src_mode->crtc_vtotal;
- }
-
- static void
- decide_crtc_timing_for_drm_display_mode(struct drm_display_mode *drm_mode,
- const struct drm_display_mode *native_mode,
- bool scale_enabled)
- {
- if (scale_enabled || (
- native_mode->clock == drm_mode->clock &&
- native_mode->htotal == drm_mode->htotal &&
- native_mode->vtotal == drm_mode->vtotal)) {
- if (native_mode->crtc_clock)
- copy_crtc_timing_for_drm_display_mode(native_mode, drm_mode);
- } else {
- /* no scaling nor amdgpu inserted, no need to patch */
- }
- }
-
- static struct dc_sink *
- create_fake_sink(struct drm_device *dev, struct dc_link *link)
- {
- struct dc_sink_init_data sink_init_data = { 0 };
- struct dc_sink *sink = NULL;
-
- sink_init_data.link = link;
- sink_init_data.sink_signal = link->connector_signal;
-
- sink = dc_sink_create(&sink_init_data);
- if (!sink) {
- drm_err(dev, "Failed to create sink!\n");
- return NULL;
- }
- sink->sink_signal = SIGNAL_TYPE_VIRTUAL;
-
- return sink;
- }
-
- static void set_multisync_trigger_params(
- struct dc_stream_state *stream)
- {
- struct dc_stream_state *master = NULL;
-
- if (stream->triggered_crtc_reset.enabled) {
- master = stream->triggered_crtc_reset.event_source;
- stream->triggered_crtc_reset.event =
- master->timing.flags.VSYNC_POSITIVE_POLARITY ?
- CRTC_EVENT_VSYNC_RISING : CRTC_EVENT_VSYNC_FALLING;
- stream->triggered_crtc_reset.delay = TRIGGER_DELAY_NEXT_PIXEL;
- }
- }
-
- static void set_master_stream(struct dc_stream_state *stream_set[],
- int stream_count)
- {
- int j, highest_rfr = 0, master_stream = 0;
-
- for (j = 0; j < stream_count; j++) {
- if (stream_set[j] && stream_set[j]->triggered_crtc_reset.enabled) {
- int refresh_rate = 0;
-
- refresh_rate = (stream_set[j]->timing.pix_clk_100hz*100)/
- (stream_set[j]->timing.h_total*stream_set[j]->timing.v_total);
- if (refresh_rate > highest_rfr) {
- highest_rfr = refresh_rate;
- master_stream = j;
- }
- }
- }
- for (j = 0; j < stream_count; j++) {
- if (stream_set[j])
- stream_set[j]->triggered_crtc_reset.event_source = stream_set[master_stream];
- }
- }
-
- static void dm_enable_per_frame_crtc_master_sync(struct dc_state *context)
- {
- int i = 0;
- struct dc_stream_state *stream;
-
- if (context->stream_count < 2)
- return;
- for (i = 0; i < context->stream_count ; i++) {
- if (!context->streams[i])
- continue;
- /*
- * TODO: add a function to read AMD VSDB bits and set
- * crtc_sync_master.multi_sync_enabled flag
- * For now it's set to false
- */
- }
-
- set_master_stream(context->streams, context->stream_count);
-
- for (i = 0; i < context->stream_count ; i++) {
- stream = context->streams[i];
-
- if (!stream)
- continue;
-
- set_multisync_trigger_params(stream);
- }
- }
-
- /**
- * DOC: FreeSync Video
- *
- * When a userspace application wants to play a video, the content follows a
- * standard format definition that usually specifies the FPS for that format.
- * The below list illustrates some video format and the expected FPS,
- * respectively:
- *
- * - TV/NTSC (23.976 FPS)
- * - Cinema (24 FPS)
- * - TV/PAL (25 FPS)
- * - TV/NTSC (29.97 FPS)
- * - TV/NTSC (30 FPS)
- * - Cinema HFR (48 FPS)
- * - TV/PAL (50 FPS)
- * - Commonly used (60 FPS)
- * - Multiples of 24 (48,72,96 FPS)
- *
- * The list of standards video format is not huge and can be added to the
- * connector modeset list beforehand. With that, userspace can leverage
- * FreeSync to extends the front porch in order to attain the target refresh
- * rate. Such a switch will happen seamlessly, without screen blanking or
- * reprogramming of the output in any other way. If the userspace requests a
- * modesetting change compatible with FreeSync modes that only differ in the
- * refresh rate, DC will skip the full update and avoid blink during the
- * transition. For example, the video player can change the modesetting from
- * 60Hz to 30Hz for playing TV/NTSC content when it goes full screen without
- * causing any display blink. This same concept can be applied to a mode
- * setting change.
- */
- static struct drm_display_mode *
- get_highest_refresh_rate_mode(struct amdgpu_dm_connector *aconnector,
- bool use_probed_modes)
- {
- struct drm_display_mode *m, *m_pref = NULL;
- u16 current_refresh, highest_refresh;
- struct list_head *list_head = use_probed_modes ?
- &aconnector->base.probed_modes :
- &aconnector->base.modes;
-
- if (aconnector->base.connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
- return NULL;
-
- if (aconnector->freesync_vid_base.clock != 0)
- return &aconnector->freesync_vid_base;
-
- /* Find the preferred mode */
- list_for_each_entry(m, list_head, head) {
- if (m->type & DRM_MODE_TYPE_PREFERRED) {
- m_pref = m;
- break;
- }
- }
-
- if (!m_pref) {
- /* Probably an EDID with no preferred mode. Fallback to first entry */
- m_pref = list_first_entry_or_null(
- &aconnector->base.modes, struct drm_display_mode, head);
- if (!m_pref) {
- drm_dbg_driver(aconnector->base.dev, "No preferred mode found in EDID\n");
- return NULL;
- }
- }
-
- highest_refresh = drm_mode_vrefresh(m_pref);
-
- /*
- * Find the mode with highest refresh rate with same resolution.
- * For some monitors, preferred mode is not the mode with highest
- * supported refresh rate.
- */
- list_for_each_entry(m, list_head, head) {
- current_refresh = drm_mode_vrefresh(m);
-
- if (m->hdisplay == m_pref->hdisplay &&
- m->vdisplay == m_pref->vdisplay &&
- highest_refresh < current_refresh) {
- highest_refresh = current_refresh;
- m_pref = m;
- }
- }
-
- drm_mode_copy(&aconnector->freesync_vid_base, m_pref);
- return m_pref;
- }
-
- static bool is_freesync_video_mode(const struct drm_display_mode *mode,
- struct amdgpu_dm_connector *aconnector)
- {
- struct drm_display_mode *high_mode;
- int timing_diff;
-
- high_mode = get_highest_refresh_rate_mode(aconnector, false);
- if (!high_mode || !mode)
- return false;
-
- timing_diff = high_mode->vtotal - mode->vtotal;
-
- if (high_mode->clock == 0 || high_mode->clock != mode->clock ||
- high_mode->hdisplay != mode->hdisplay ||
- high_mode->vdisplay != mode->vdisplay ||
- high_mode->hsync_start != mode->hsync_start ||
- high_mode->hsync_end != mode->hsync_end ||
- high_mode->htotal != mode->htotal ||
- high_mode->hskew != mode->hskew ||
- high_mode->vscan != mode->vscan ||
- high_mode->vsync_start - mode->vsync_start != timing_diff ||
- high_mode->vsync_end - mode->vsync_end != timing_diff)
- return false;
- else
- return true;
- }
-
- #if defined(CONFIG_DRM_AMD_DC_FP)
- static void update_dsc_caps(struct amdgpu_dm_connector *aconnector,
- struct dc_sink *sink, struct dc_stream_state *stream,
- struct dsc_dec_dpcd_caps *dsc_caps)
- {
- stream->timing.flags.DSC = 0;
- dsc_caps->is_dsc_supported = false;
-
- if (aconnector->dc_link && (sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT ||
- sink->sink_signal == SIGNAL_TYPE_EDP)) {
- if (sink->link->dpcd_caps.dongle_type == DISPLAY_DONGLE_NONE)
- dc_dsc_parse_dsc_dpcd(aconnector->dc_link->ctx->dc,
- aconnector->dc_link->dpcd_caps.dsc_caps.dsc_basic_caps.raw,
- aconnector->dc_link->dpcd_caps.dsc_caps.dsc_branch_decoder_caps.raw,
- dsc_caps);
- else if (sink->link->dpcd_caps.dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER) {
- if (aconnector->dc_link->dpcd_caps.dsc_caps.dsc_basic_caps.fields.dsc_support.DSC_PASSTHROUGH_SUPPORT &&
- !aconnector->dsc_settings.dsc_force_disable_passthrough &&
- aconnector->dc_link->dpcd_caps.dongle_caps.dp_hdmi_frl_max_link_bw_in_kbps > 0 &&
- sink->edid_caps.frl_dsc_support &&
- sink->edid_caps.max_frl_rate > 0 &&
- sink->edid_caps.frl_dsc_max_frl_rate > 0)
- dc_dsc_parse_dsc_edid(aconnector->dc_link->ctx->dc, &sink->edid_caps, dsc_caps);
- else
- dc_dsc_parse_dsc_dpcd(aconnector->dc_link->ctx->dc,
- aconnector->dc_link->dpcd_caps.dsc_caps.dsc_basic_caps.raw,
- aconnector->dc_link->dpcd_caps.dsc_caps.dsc_branch_decoder_caps.raw,
- dsc_caps);
- }
- } else if (aconnector->dc_link && sink->sink_signal == SIGNAL_TYPE_HDMI_FRL) {
- if (sink->edid_caps.frl_dsc_support &&
- sink->edid_caps.max_frl_rate > 0 &&
- sink->edid_caps.frl_dsc_max_frl_rate > 0)
- dc_dsc_parse_dsc_edid(aconnector->dc_link->ctx->dc, &sink->edid_caps, dsc_caps);
- }
- }
-
- static void apply_dsc_policy_for_edp(struct amdgpu_dm_connector *aconnector,
- struct dc_sink *sink, struct dc_stream_state *stream,
- struct dsc_dec_dpcd_caps *dsc_caps,
- uint32_t max_dsc_target_bpp_limit_override)
- {
- const struct dc_link_settings *verified_link_cap = NULL;
- u32 link_bw_in_kbps;
- u32 edp_min_bpp_x16, edp_max_bpp_x16;
- struct dc *dc = sink->ctx->dc;
- struct dc_dsc_bw_range bw_range = {0};
- struct dc_dsc_config dsc_cfg = {0};
- struct dc_dsc_config_options dsc_options = {0};
-
- dc_dsc_get_default_config_option(dc, &dsc_options);
- dsc_options.max_target_bpp_limit_override_x16 = max_dsc_target_bpp_limit_override * 16;
-
- verified_link_cap = dc_link_get_link_cap(stream->link);
- link_bw_in_kbps = dc_link_bandwidth_kbps(stream->link, verified_link_cap);
- edp_min_bpp_x16 = 8 * 16;
- edp_max_bpp_x16 = 8 * 16;
-
- if (edp_max_bpp_x16 > dsc_caps->edp_max_bits_per_pixel)
- edp_max_bpp_x16 = dsc_caps->edp_max_bits_per_pixel;
-
- if (edp_max_bpp_x16 < edp_min_bpp_x16)
- edp_min_bpp_x16 = edp_max_bpp_x16;
-
- if (dc_dsc_compute_bandwidth_range(dc->res_pool->dscs[0],
- dc->debug.dsc_min_slice_height_override,
- edp_min_bpp_x16, edp_max_bpp_x16,
- dsc_caps,
- &stream->timing,
- dc_link_get_highest_encoding_format(aconnector->dc_link),
- &bw_range)) {
-
- if (bw_range.max_kbps < link_bw_in_kbps) {
- if (dc_dsc_compute_config(dc->res_pool->dscs[0],
- dsc_caps,
- &dsc_options,
- 0,
- &stream->timing,
- dc_link_get_highest_encoding_format(aconnector->dc_link),
- &dsc_cfg)) {
- stream->timing.dsc_cfg = dsc_cfg;
- stream->timing.flags.DSC = 1;
- stream->timing.dsc_cfg.bits_per_pixel = edp_max_bpp_x16;
- }
- return;
- }
- }
-
- if (dc_dsc_compute_config(dc->res_pool->dscs[0],
- dsc_caps,
- &dsc_options,
- link_bw_in_kbps,
- &stream->timing,
- dc_link_get_highest_encoding_format(aconnector->dc_link),
- &dsc_cfg)) {
- stream->timing.dsc_cfg = dsc_cfg;
- stream->timing.flags.DSC = 1;
- }
- }
-
- static void apply_dsc_policy_for_stream(struct amdgpu_dm_connector *aconnector,
- struct dc_sink *sink, struct dc_stream_state *stream,
- struct dsc_dec_dpcd_caps *dsc_caps)
- {
- struct drm_connector *drm_connector = &aconnector->base;
- u32 link_bandwidth_kbps;
- struct dc *dc = sink->ctx->dc;
- const struct dc_hdmi_frl_link_settings *frl_verified_link_cap = NULL;
- u32 converter_bw_in_kbps;
- u32 sink_bw_in_kbps;
- u32 dsc_sink_bw_in_kbps;
- u32 max_supported_bw_in_kbps, timing_bw_in_kbps;
- u32 dsc_max_supported_bw_in_kbps;
- u32 max_dsc_target_bpp_limit_override =
- drm_connector->display_info.max_dsc_bpp;
- struct dc_dsc_config_options dsc_options = {0};
-
- dc_dsc_get_default_config_option(dc, &dsc_options);
- dsc_options.max_target_bpp_limit_override_x16 = max_dsc_target_bpp_limit_override * 16;
-
- link_bandwidth_kbps = dc_link_bandwidth_kbps(aconnector->dc_link,
- dc_link_get_link_cap(aconnector->dc_link));
-
- /* Set DSC policy according to dsc_clock_en */
- dc_dsc_policy_set_enable_dsc_when_not_needed(
- aconnector->dsc_settings.dsc_force_enable == DSC_CLK_FORCE_ENABLE);
-
- if (sink->sink_signal == SIGNAL_TYPE_EDP &&
- !aconnector->dc_link->panel_config.dsc.disable_dsc_edp &&
- dc->caps.edp_dsc_support && aconnector->dsc_settings.dsc_force_enable != DSC_CLK_FORCE_DISABLE) {
-
- apply_dsc_policy_for_edp(aconnector, sink, stream, dsc_caps, max_dsc_target_bpp_limit_override);
-
- } else if (sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT) {
- if (sink->link->dpcd_caps.dongle_type == DISPLAY_DONGLE_NONE) {
- if (dc_dsc_compute_config(aconnector->dc_link->ctx->dc->res_pool->dscs[0],
- dsc_caps,
- &dsc_options,
- link_bandwidth_kbps,
- &stream->timing,
- dc_link_get_highest_encoding_format(aconnector->dc_link),
- &stream->timing.dsc_cfg)) {
- stream->timing.flags.DSC = 1;
- drm_dbg_driver(drm_connector->dev, "%s: SST_DSC [%s] DSC is selected from SST RX\n",
- __func__, drm_connector->name);
- }
- } else if (sink->link->dpcd_caps.dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER) {
- timing_bw_in_kbps = dc_bandwidth_in_kbps_from_timing(&stream->timing,
- dc_link_get_highest_encoding_format(aconnector->dc_link));
- converter_bw_in_kbps = aconnector->dc_link->dpcd_caps.dongle_caps.dp_hdmi_frl_max_link_bw_in_kbps;
- sink_bw_in_kbps = dc_link_bw_kbps_from_raw_frl_link_rate_data(dc, sink->edid_caps.max_frl_rate);
- dsc_sink_bw_in_kbps = dc_link_bw_kbps_from_raw_frl_link_rate_data(dc, sink->edid_caps.frl_dsc_max_frl_rate);
-
- if (dsc_caps->is_frl) {
- max_supported_bw_in_kbps = min(link_bandwidth_kbps, converter_bw_in_kbps);
- max_supported_bw_in_kbps = min(max_supported_bw_in_kbps, sink_bw_in_kbps);
- dsc_max_supported_bw_in_kbps = min(max_supported_bw_in_kbps, dsc_sink_bw_in_kbps);
- } else {
- max_supported_bw_in_kbps = link_bandwidth_kbps;
- dsc_max_supported_bw_in_kbps = link_bandwidth_kbps;
- }
-
- if (timing_bw_in_kbps > max_supported_bw_in_kbps &&
- max_supported_bw_in_kbps > 0 &&
- dsc_max_supported_bw_in_kbps > 0)
- if (dc_dsc_compute_config(aconnector->dc_link->ctx->dc->res_pool->dscs[0],
- dsc_caps,
- &dsc_options,
- dsc_max_supported_bw_in_kbps,
- &stream->timing,
- dc_link_get_highest_encoding_format(aconnector->dc_link),
- &stream->timing.dsc_cfg)) {
- stream->timing.flags.DSC = 1;
- drm_dbg_driver(drm_connector->dev, "%s: SST_DSC [%s] DSC is selected from %s\n",
- __func__, drm_connector->name,
- (dsc_caps->is_frl == 1) ? "HDMI FRL RX" : "DP-HDMI PCON");
- }
- }
- }
- else if (aconnector->dc_link && sink->sink_signal == SIGNAL_TYPE_HDMI_FRL) {
- struct dc_dsc_policy dsc_policy = {0};
-
- frl_verified_link_cap = dc_link_get_frl_link_cap(stream->link);
- if (frl_verified_link_cap->frl_link_rate != HDMI_FRL_LINK_RATE_DISABLE &&
- aconnector->dc_link->frl_flags.force_frl_dsc) {
- dc_dsc_policy_set_enable_dsc_when_not_needed(true);
- dc_dsc_get_policy_for_timing(&stream->timing, 0, &dsc_policy, dc_link_get_highest_encoding_format(stream->link));
- }
-
- timing_bw_in_kbps = dc_bandwidth_in_kbps_from_timing(&stream->timing, DC_LINK_ENCODING_HDMI_FRL);
- link_bandwidth_kbps = dc_link_frl_bandwidth_kbps(stream->link, frl_verified_link_cap->frl_link_rate);
- dsc_sink_bw_in_kbps = dc_link_bw_kbps_from_raw_frl_link_rate_data(dc, sink->edid_caps.frl_dsc_max_frl_rate);
-
- if ((timing_bw_in_kbps > link_bandwidth_kbps && dsc_sink_bw_in_kbps > 0) ||
- (dsc_policy.enable_dsc_when_not_needed || dsc_options.force_dsc_when_not_needed)) {
- if (dc_dsc_compute_config(aconnector->dc_link->ctx->dc->res_pool->dscs[0],
- dsc_caps,
- &dsc_options,
- dsc_sink_bw_in_kbps,
- &stream->timing,
- dc_link_get_highest_encoding_format(aconnector->dc_link),
- &stream->timing.dsc_cfg)) {
- stream->timing.flags.DSC = 1;
- drm_dbg_driver(drm_connector->dev, "%s: HDMI_FRL_DSC [%s] DSC is selected from HDMI FRL RX\n",
- __func__, drm_connector->name);
- }
- }
- }
-
- /* Overwrite the stream flag if DSC is enabled through debugfs */
- if (aconnector->dsc_settings.dsc_force_enable == DSC_CLK_FORCE_ENABLE)
- stream->timing.flags.DSC = 1;
-
- if (stream->timing.flags.DSC && aconnector->dsc_settings.dsc_num_slices_h)
- stream->timing.dsc_cfg.num_slices_h = aconnector->dsc_settings.dsc_num_slices_h;
-
- if (stream->timing.flags.DSC && aconnector->dsc_settings.dsc_num_slices_v)
- stream->timing.dsc_cfg.num_slices_v = aconnector->dsc_settings.dsc_num_slices_v;
-
- if (stream->timing.flags.DSC && aconnector->dsc_settings.dsc_bits_per_pixel)
- stream->timing.dsc_cfg.bits_per_pixel = aconnector->dsc_settings.dsc_bits_per_pixel;
- }
- #endif
-
- static struct dc_stream_state *
- create_stream_for_sink(struct drm_connector *connector,
- const struct drm_display_mode *drm_mode,
- const struct dm_connector_state *dm_state,
- const struct dc_stream_state *old_stream,
- int requested_bpc)
- {
- struct drm_device *dev = connector->dev;
- struct amdgpu_dm_connector *aconnector = NULL;
- struct drm_display_mode *preferred_mode = NULL;
- const struct drm_connector_state *con_state = &dm_state->base;
- struct dc_stream_state *stream = NULL;
- struct drm_display_mode mode;
- struct drm_display_mode saved_mode;
- struct drm_display_mode *freesync_mode = NULL;
- bool native_mode_found = false;
- bool recalculate_timing = false;
- bool scale = dm_state->scaling != RMX_OFF;
- int mode_refresh;
- int preferred_refresh = 0;
- enum color_transfer_func tf = TRANSFER_FUNC_UNKNOWN;
- #if defined(CONFIG_DRM_AMD_DC_FP)
- struct dsc_dec_dpcd_caps dsc_caps = {0};
- #endif
- struct dc_link *link = NULL;
- struct dc_sink *sink = NULL;
-
- drm_mode_init(&mode, drm_mode);
- memset(&saved_mode, 0, sizeof(saved_mode));
-
- if (connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK) {
- aconnector = NULL;
- aconnector = to_amdgpu_dm_connector(connector);
- link = aconnector->dc_link;
- } else {
- struct drm_writeback_connector *wbcon = NULL;
- struct amdgpu_dm_wb_connector *dm_wbcon = NULL;
-
- wbcon = drm_connector_to_writeback(connector);
- dm_wbcon = to_amdgpu_dm_wb_connector(wbcon);
- link = dm_wbcon->link;
- }
-
- if (!aconnector || !aconnector->dc_sink) {
- sink = create_fake_sink(dev, link);
- if (!sink)
- return stream;
-
- } else {
- sink = aconnector->dc_sink;
- dc_sink_retain(sink);
- }
-
- stream = dc_create_stream_for_sink(sink);
-
- if (stream == NULL) {
- drm_err(dev, "Failed to create stream for sink!\n");
- goto finish;
- }
-
- /* We leave this NULL for writeback connectors */
- stream->dm_stream_context = aconnector;
-
- stream->timing.flags.LTE_340MCSC_SCRAMBLE =
- connector->display_info.hdmi.scdc.scrambling.low_rates;
-
- list_for_each_entry(preferred_mode, &connector->modes, head) {
- /* Search for preferred mode */
- if (preferred_mode->type & DRM_MODE_TYPE_PREFERRED) {
- native_mode_found = true;
- break;
- }
- }
- if (!native_mode_found)
- preferred_mode = list_first_entry_or_null(
- &connector->modes,
- struct drm_display_mode,
- head);
-
- mode_refresh = drm_mode_vrefresh(&mode);
-
- if (preferred_mode == NULL) {
- /*
- * This may not be an error, the use case is when we have no
- * usermode calls to reset and set mode upon hotplug. In this
- * case, we call set mode ourselves to restore the previous mode
- * and the modelist may not be filled in time.
- */
- drm_dbg_driver(dev, "No preferred mode found\n");
- } else if (aconnector) {
- recalculate_timing = amdgpu_freesync_vid_mode &&
- is_freesync_video_mode(&mode, aconnector);
- if (recalculate_timing) {
- freesync_mode = get_highest_refresh_rate_mode(aconnector, false);
- drm_mode_copy(&saved_mode, &mode);
- saved_mode.picture_aspect_ratio = mode.picture_aspect_ratio;
- drm_mode_copy(&mode, freesync_mode);
- mode.picture_aspect_ratio = saved_mode.picture_aspect_ratio;
- } else {
- decide_crtc_timing_for_drm_display_mode(
- &mode, preferred_mode, scale);
-
- preferred_refresh = drm_mode_vrefresh(preferred_mode);
- }
- }
-
- if (recalculate_timing)
- drm_mode_set_crtcinfo(&saved_mode, 0);
-
- /*
- * If scaling is enabled and refresh rate didn't change
- * we copy the vic and polarities of the old timings
- */
- if (!scale || mode_refresh != preferred_refresh)
- fill_stream_properties_from_drm_display_mode(
- stream, &mode, connector, con_state, NULL,
- requested_bpc);
- else
- fill_stream_properties_from_drm_display_mode(
- stream, &mode, connector, con_state, old_stream,
- requested_bpc);
-
- /* The rest isn't needed for writeback connectors */
- if (!aconnector)
- goto finish;
-
- if (aconnector->timing_changed) {
- drm_dbg(aconnector->base.dev,
- "overriding timing for automated test, bpc %d, changing to %d\n",
- stream->timing.display_color_depth,
- aconnector->timing_requested->display_color_depth);
- stream->timing = *aconnector->timing_requested;
- }
-
- #if defined(CONFIG_DRM_AMD_DC_FP)
- /* SST DSC determination policy */
- update_dsc_caps(aconnector, sink, stream, &dsc_caps);
- if (aconnector->dsc_settings.dsc_force_enable != DSC_CLK_FORCE_DISABLE && dsc_caps.is_dsc_supported)
- apply_dsc_policy_for_stream(aconnector, sink, stream, &dsc_caps);
- #endif
-
- update_stream_scaling_settings(dev, &mode, dm_state, stream);
-
- fill_audio_info(
- &stream->audio_info,
- connector,
- sink);
-
- update_stream_signal(stream, sink);
-
- if (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A ||
- stream->signal == SIGNAL_TYPE_HDMI_FRL)
- mod_build_hf_vsif_infopacket(stream, &stream->vsp_infopacket, false, false);
-
- if (stream->signal == SIGNAL_TYPE_DISPLAY_PORT ||
- stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST ||
- stream->signal == SIGNAL_TYPE_EDP) {
- const struct dc_edid_caps *edid_caps;
- unsigned int disable_colorimetry = 0;
-
- if (aconnector->dc_sink) {
- edid_caps = &aconnector->dc_sink->edid_caps;
- disable_colorimetry = edid_caps->panel_patch.disable_colorimetry;
- }
-
- //
- // should decide stream support vsc sdp colorimetry capability
- // before building vsc info packet
- //
- stream->use_vsc_sdp_for_colorimetry = stream->link->dpcd_caps.dpcd_rev.raw >= 0x14 &&
- stream->link->dpcd_caps.dprx_feature.bits.VSC_SDP_COLORIMETRY_SUPPORTED &&
- !disable_colorimetry;
-
- if (stream->out_transfer_func.tf == TRANSFER_FUNCTION_GAMMA22)
- tf = TRANSFER_FUNC_GAMMA_22;
- mod_build_vsc_infopacket(stream, &stream->vsc_infopacket, stream->output_color_space, tf);
- aconnector->sr_skip_count = AMDGPU_DM_PSR_ENTRY_DELAY;
-
- }
- finish:
- dc_sink_release(sink);
-
- return stream;
- }
-
- /**
- * amdgpu_dm_connector_poll - Poll a connector to see if it's connected to a display
- * @aconnector: DM connector to poll (owns @base drm_connector and @dc_link)
- * @force: if true, force polling even when DAC load detection was used
- *
- * Used for connectors that don't support HPD (hotplug detection) to
- * periodically check whether the connector is connected to a display.
- *
- * When connection was determined via DAC load detection, we avoid
- * re-running it on normal polls to prevent visible glitches, unless
- * @force is set.
- *
- * Return: The probed connector status (connected/disconnected/unknown).
- */
- static enum drm_connector_status
- amdgpu_dm_connector_poll(struct amdgpu_dm_connector *aconnector, bool force)
- {
- struct drm_connector *connector = &aconnector->base;
- struct drm_device *dev = connector->dev;
- struct amdgpu_device *adev = drm_to_adev(dev);
- struct dc_link *link = aconnector->dc_link;
- enum dc_connection_type conn_type = dc_connection_none;
- enum drm_connector_status status = connector_status_disconnected;
-
- /* When we determined the connection using DAC load detection,
- * do NOT poll the connector do detect disconnect because
- * that would run DAC load detection again which can cause
- * visible visual glitches.
- *
- * Only allow to poll such a connector again when forcing.
- */
- if (!force && link->local_sink && link->type == dc_connection_analog_load)
- return connector->status;
-
- mutex_lock(&aconnector->hpd_lock);
-
- if (dc_link_detect_connection_type(aconnector->dc_link, &conn_type) &&
- conn_type != dc_connection_none) {
- mutex_lock(&adev->dm.dc_lock);
-
- /* Only call full link detection when a sink isn't created yet,
- * ie. just when the display is plugged in, otherwise we risk flickering.
- */
- if (link->local_sink ||
- dc_link_detect(link, DETECT_REASON_HPD))
- status = connector_status_connected;
-
- mutex_unlock(&adev->dm.dc_lock);
- }
-
- if (connector->status != status) {
- if (status == connector_status_disconnected) {
- if (link->local_sink)
- dc_sink_release(link->local_sink);
-
- link->local_sink = NULL;
- link->dpcd_sink_count = 0;
- link->type = dc_connection_none;
- }
-
- amdgpu_dm_update_connector_after_detect(aconnector);
- }
-
- mutex_unlock(&aconnector->hpd_lock);
- return status;
- }
-
- /**
- * amdgpu_dm_connector_detect() - Detect whether a DRM connector is connected to a display
- *
- * A connector is considered connected when it has a sink that is not NULL.
- * For connectors that support HPD (hotplug detection), the connection is
- * handled in the HPD interrupt.
- * For connectors that may not support HPD, such as analog connectors,
- * DRM will call this function repeatedly to poll them.
- *
- * Notes:
- * 1. This interface is NOT called in context of HPD irq.
- * 2. This interface *is called* in context of user-mode ioctl. Which
- * makes it a bad place for *any* MST-related activity.
- *
- * @connector: The DRM connector we are checking. We convert it to
- * amdgpu_dm_connector so we can read the DC link and state.
- * @force: If true, do a full detect again. This is used even when
- * a lighter check would normally be used to avoid flicker.
- *
- * Return: The connector status (connected, disconnected, or unknown).
- *
- */
- static enum drm_connector_status
- amdgpu_dm_connector_detect(struct drm_connector *connector, bool force)
- {
- struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
-
- update_subconnector_property(aconnector);
-
- if (aconnector->base.force == DRM_FORCE_ON ||
- aconnector->base.force == DRM_FORCE_ON_DIGITAL)
- return connector_status_connected;
- else if (aconnector->base.force == DRM_FORCE_OFF)
- return connector_status_disconnected;
-
- /* Poll analog connectors and only when either
- * disconnected or connected to an analog display.
- */
- if (drm_kms_helper_is_poll_worker() &&
- dc_connector_supports_analog(aconnector->dc_link->link_id.id) &&
- (!aconnector->dc_sink || aconnector->dc_sink->edid_caps.analog))
- return amdgpu_dm_connector_poll(aconnector, force);
-
- return (aconnector->dc_sink ? connector_status_connected :
- connector_status_disconnected);
- }
-
- int amdgpu_dm_connector_atomic_set_property(struct drm_connector *connector,
- struct drm_connector_state *connector_state,
- struct drm_property *property,
- uint64_t val)
- {
- struct drm_device *dev = connector->dev;
- struct amdgpu_device *adev = drm_to_adev(dev);
- struct dm_connector_state *dm_old_state =
- to_dm_connector_state(connector->state);
- struct dm_connector_state *dm_new_state =
- to_dm_connector_state(connector_state);
-
- int ret = -EINVAL;
-
- if (property == dev->mode_config.scaling_mode_property) {
- enum amdgpu_rmx_type rmx_type;
-
- switch (val) {
- case DRM_MODE_SCALE_CENTER:
- rmx_type = RMX_CENTER;
- break;
- case DRM_MODE_SCALE_ASPECT:
- rmx_type = RMX_ASPECT;
- break;
- case DRM_MODE_SCALE_FULLSCREEN:
- rmx_type = RMX_FULL;
- break;
- case DRM_MODE_SCALE_NONE:
- default:
- rmx_type = RMX_OFF;
- break;
- }
-
- if (dm_old_state->scaling == rmx_type)
- return 0;
-
- dm_new_state->scaling = rmx_type;
- ret = 0;
- } else if (property == adev->mode_info.underscan_hborder_property) {
- dm_new_state->underscan_hborder = val;
- ret = 0;
- } else if (property == adev->mode_info.underscan_vborder_property) {
- dm_new_state->underscan_vborder = val;
- ret = 0;
- } else if (property == adev->mode_info.underscan_property) {
- dm_new_state->underscan_enable = val;
- ret = 0;
- } else if (property == adev->mode_info.abm_level_property) {
- switch (val) {
- case ABM_SYSFS_CONTROL:
- dm_new_state->abm_sysfs_forbidden = false;
- break;
- case ABM_LEVEL_OFF:
- dm_new_state->abm_sysfs_forbidden = true;
- dm_new_state->abm_level = ABM_LEVEL_IMMEDIATE_DISABLE;
- break;
- default:
- dm_new_state->abm_sysfs_forbidden = true;
- dm_new_state->abm_level = val;
- }
- ret = 0;
- }
-
- return ret;
- }
-
- int amdgpu_dm_connector_atomic_get_property(struct drm_connector *connector,
- const struct drm_connector_state *state,
- struct drm_property *property,
- uint64_t *val)
- {
- struct drm_device *dev = connector->dev;
- struct amdgpu_device *adev = drm_to_adev(dev);
- struct dm_connector_state *dm_state =
- to_dm_connector_state(state);
- int ret = -EINVAL;
-
- if (property == dev->mode_config.scaling_mode_property) {
- switch (dm_state->scaling) {
- case RMX_CENTER:
- *val = DRM_MODE_SCALE_CENTER;
- break;
- case RMX_ASPECT:
- *val = DRM_MODE_SCALE_ASPECT;
- break;
- case RMX_FULL:
- *val = DRM_MODE_SCALE_FULLSCREEN;
- break;
- case RMX_OFF:
- default:
- *val = DRM_MODE_SCALE_NONE;
- break;
- }
- ret = 0;
- } else if (property == adev->mode_info.underscan_hborder_property) {
- *val = dm_state->underscan_hborder;
- ret = 0;
- } else if (property == adev->mode_info.underscan_vborder_property) {
- *val = dm_state->underscan_vborder;
- ret = 0;
- } else if (property == adev->mode_info.underscan_property) {
- *val = dm_state->underscan_enable;
- ret = 0;
- } else if (property == adev->mode_info.abm_level_property) {
- if (!dm_state->abm_sysfs_forbidden)
- *val = ABM_SYSFS_CONTROL;
- else
- *val = (dm_state->abm_level != ABM_LEVEL_IMMEDIATE_DISABLE) ?
- dm_state->abm_level : 0;
- ret = 0;
- }
-
- return ret;
- }
-
- /**
- * DOC: panel power savings
- *
- * The display manager allows you to set your desired **panel power savings**
- * level (between 0-4, with 0 representing off), e.g. using the following::
- *
- * # echo 3 > /sys/class/drm/card0-eDP-1/amdgpu/panel_power_savings
- *
- * Modifying this value can have implications on color accuracy, so tread
- * carefully.
- */
-
- static ssize_t panel_power_savings_show(struct device *device,
- struct device_attribute *attr,
- char *buf)
- {
- struct drm_connector *connector = dev_get_drvdata(device);
- struct drm_device *dev = connector->dev;
- u8 val;
-
- drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
- val = to_dm_connector_state(connector->state)->abm_level ==
- ABM_LEVEL_IMMEDIATE_DISABLE ? 0 :
- to_dm_connector_state(connector->state)->abm_level;
- drm_modeset_unlock(&dev->mode_config.connection_mutex);
-
- return sysfs_emit(buf, "%u\n", val);
- }
-
- static ssize_t panel_power_savings_store(struct device *device,
- struct device_attribute *attr,
- const char *buf, size_t count)
- {
- struct drm_connector *connector = dev_get_drvdata(device);
- struct drm_device *dev = connector->dev;
- long val;
- int ret;
-
- ret = kstrtol(buf, 0, &val);
-
- if (ret)
- return ret;
-
- if (val < 0 || val > 4)
- return -EINVAL;
-
- drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
- if (to_dm_connector_state(connector->state)->abm_sysfs_forbidden)
- ret = -EBUSY;
- else
- to_dm_connector_state(connector->state)->abm_level = val ?:
- ABM_LEVEL_IMMEDIATE_DISABLE;
- drm_modeset_unlock(&dev->mode_config.connection_mutex);
-
- if (ret)
- return ret;
-
- drm_kms_helper_hotplug_event(dev);
-
- return count;
- }
-
- static DEVICE_ATTR_RW(panel_power_savings);
-
- static struct attribute *amdgpu_attrs[] = {
- &dev_attr_panel_power_savings.attr,
- NULL
- };
-
- static const struct attribute_group amdgpu_group = {
- .name = "amdgpu",
- .attrs = amdgpu_attrs
- };
-
- static bool
- amdgpu_dm_should_create_sysfs(struct amdgpu_dm_connector *amdgpu_dm_connector)
- {
- if (amdgpu_dm_abm_level >= 0)
- return false;
-
- if (amdgpu_dm_connector->base.connector_type != DRM_MODE_CONNECTOR_eDP)
- return false;
-
- /* check for OLED panels */
- if (amdgpu_dm_connector->bl_idx >= 0) {
- struct drm_device *drm = amdgpu_dm_connector->base.dev;
- struct amdgpu_display_manager *dm = &drm_to_adev(drm)->dm;
- struct amdgpu_dm_backlight_caps *caps;
-
- caps = &dm->backlight_caps[amdgpu_dm_connector->bl_idx];
- if (caps->aux_support)
- return false;
- }
-
- return true;
- }
-
- static void amdgpu_dm_connector_unregister(struct drm_connector *connector)
- {
- struct amdgpu_dm_connector *amdgpu_dm_connector = to_amdgpu_dm_connector(connector);
-
- if (amdgpu_dm_should_create_sysfs(amdgpu_dm_connector))
- sysfs_remove_group(&connector->kdev->kobj, &amdgpu_group);
-
- cec_notifier_conn_unregister(amdgpu_dm_connector->notifier);
- drm_dp_aux_unregister(&amdgpu_dm_connector->dm_dp_aux.aux);
- }
-
- static void amdgpu_dm_connector_destroy(struct drm_connector *connector)
- {
- struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
- struct amdgpu_device *adev = drm_to_adev(connector->dev);
- struct amdgpu_display_manager *dm = &adev->dm;
-
- /*
- * Call only if mst_mgr was initialized before since it's not done
- * for all connector types.
- */
- if (aconnector->mst_mgr.dev)
- drm_dp_mst_topology_mgr_destroy(&aconnector->mst_mgr);
-
- /* Cancel and flush any pending HDMI HPD debounce work */
- if (aconnector->hdmi_hpd_debounce_delay_ms) {
- cancel_delayed_work_sync(&aconnector->hdmi_hpd_debounce_work);
- if (aconnector->hdmi_prev_sink) {
- dc_sink_release(aconnector->hdmi_prev_sink);
- aconnector->hdmi_prev_sink = NULL;
- }
- }
-
- if (aconnector->bl_idx != -1) {
- backlight_device_unregister(dm->backlight_dev[aconnector->bl_idx]);
- dm->backlight_dev[aconnector->bl_idx] = NULL;
- }
-
- if (aconnector->dc_em_sink)
- dc_sink_release(aconnector->dc_em_sink);
- aconnector->dc_em_sink = NULL;
- if (aconnector->dc_sink)
- dc_sink_release(aconnector->dc_sink);
- aconnector->dc_sink = NULL;
-
- drm_dp_cec_unregister_connector(&aconnector->dm_dp_aux.aux);
- drm_connector_unregister(connector);
- drm_connector_cleanup(connector);
- kfree(aconnector->dm_dp_aux.aux.name);
-
- kfree(connector);
- }
-
- void amdgpu_dm_connector_funcs_reset(struct drm_connector *connector)
- {
- struct dm_connector_state *state =
- to_dm_connector_state(connector->state);
-
- if (connector->state)
- __drm_atomic_helper_connector_destroy_state(connector->state);
-
- kfree(state);
-
- state = kzalloc_obj(*state);
-
- if (state) {
- state->scaling = RMX_OFF;
- state->underscan_enable = false;
- state->underscan_hborder = 0;
- state->underscan_vborder = 0;
- state->base.max_requested_bpc = 8;
- state->vcpi_slots = 0;
- state->pbn = 0;
-
- if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) {
- if (amdgpu_dm_abm_level <= 0)
- state->abm_level = ABM_LEVEL_IMMEDIATE_DISABLE;
- else
- state->abm_level = amdgpu_dm_abm_level;
- }
-
- __drm_atomic_helper_connector_reset(connector, &state->base);
- }
- }
-
- struct drm_connector_state *
- amdgpu_dm_connector_atomic_duplicate_state(struct drm_connector *connector)
- {
- struct dm_connector_state *state =
- to_dm_connector_state(connector->state);
-
- struct dm_connector_state *new_state =
- kmemdup(state, sizeof(*state), GFP_KERNEL);
-
- if (!new_state)
- return NULL;
-
- __drm_atomic_helper_connector_duplicate_state(connector, &new_state->base);
-
- new_state->freesync_capable = state->freesync_capable;
- new_state->abm_level = state->abm_level;
- new_state->scaling = state->scaling;
- new_state->underscan_enable = state->underscan_enable;
- new_state->underscan_hborder = state->underscan_hborder;
- new_state->underscan_vborder = state->underscan_vborder;
- new_state->vcpi_slots = state->vcpi_slots;
- new_state->pbn = state->pbn;
- return &new_state->base;
- }
-
- static int
- amdgpu_dm_connector_late_register(struct drm_connector *connector)
- {
- struct amdgpu_dm_connector *amdgpu_dm_connector =
- to_amdgpu_dm_connector(connector);
- int r;
-
- if (amdgpu_dm_should_create_sysfs(amdgpu_dm_connector)) {
- r = sysfs_create_group(&connector->kdev->kobj,
- &amdgpu_group);
- if (r)
- return r;
- }
-
- amdgpu_dm_register_backlight_device(amdgpu_dm_connector);
-
- if ((connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort) ||
- (connector->connector_type == DRM_MODE_CONNECTOR_eDP)) {
- amdgpu_dm_connector->dm_dp_aux.aux.dev = connector->kdev;
- r = drm_dp_aux_register(&amdgpu_dm_connector->dm_dp_aux.aux);
- if (r)
- return r;
- }
-
- #if defined(CONFIG_DEBUG_FS)
- connector_debugfs_init(amdgpu_dm_connector);
- #endif
-
- return 0;
- }
-
- static void amdgpu_dm_connector_funcs_force(struct drm_connector *connector)
- {
- struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
- struct dc_link *dc_link = aconnector->dc_link;
- struct dc_sink *dc_em_sink = aconnector->dc_em_sink;
- const struct drm_edid *drm_edid;
- struct i2c_adapter *ddc;
- struct drm_device *dev = connector->dev;
-
- if (dc_link && dc_link->aux_mode)
- ddc = &aconnector->dm_dp_aux.aux.ddc;
- else
- ddc = &aconnector->i2c->base;
-
- drm_edid = drm_edid_read_ddc(connector, ddc);
- drm_edid_connector_update(connector, drm_edid);
- if (!drm_edid) {
- drm_err(dev, "No EDID found on connector: %s.\n", connector->name);
- return;
- }
-
- aconnector->drm_edid = drm_edid;
- /* Update emulated (virtual) sink's EDID */
- if (dc_em_sink && dc_link) {
- // FIXME: Get rid of drm_edid_raw()
- const struct edid *edid = drm_edid_raw(drm_edid);
-
- memset(&dc_em_sink->edid_caps, 0, sizeof(struct dc_edid_caps));
- memmove(dc_em_sink->dc_edid.raw_edid, edid,
- (edid->extensions + 1) * EDID_LENGTH);
- dm_helpers_parse_edid_caps(
- dc_link,
- &dc_em_sink->dc_edid,
- &dc_em_sink->edid_caps);
- }
- }
-
- static const struct drm_connector_funcs amdgpu_dm_connector_funcs = {
- .reset = amdgpu_dm_connector_funcs_reset,
- .detect = amdgpu_dm_connector_detect,
- .fill_modes = drm_helper_probe_single_connector_modes,
- .destroy = amdgpu_dm_connector_destroy,
- .atomic_duplicate_state = amdgpu_dm_connector_atomic_duplicate_state,
- .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
- .atomic_set_property = amdgpu_dm_connector_atomic_set_property,
- .atomic_get_property = amdgpu_dm_connector_atomic_get_property,
- .late_register = amdgpu_dm_connector_late_register,
- .early_unregister = amdgpu_dm_connector_unregister,
- .force = amdgpu_dm_connector_funcs_force
- };
-
- static int get_modes(struct drm_connector *connector)
- {
- return amdgpu_dm_connector_get_modes(connector);
- }
-
- static void create_eml_sink(struct amdgpu_dm_connector *aconnector)
- {
- struct drm_connector *connector = &aconnector->base;
- struct dc_link *dc_link = aconnector->dc_link;
- struct dc_sink_init_data init_params = {
- .link = aconnector->dc_link,
- .sink_signal = SIGNAL_TYPE_VIRTUAL
- };
- const struct drm_edid *drm_edid;
- const struct edid *edid;
- struct i2c_adapter *ddc;
-
- if (dc_link && dc_link->aux_mode)
- ddc = &aconnector->dm_dp_aux.aux.ddc;
- else
- ddc = &aconnector->i2c->base;
-
- drm_edid = drm_edid_read_ddc(connector, ddc);
- drm_edid_connector_update(connector, drm_edid);
- if (!drm_edid) {
- drm_err(connector->dev, "No EDID found on connector: %s.\n", connector->name);
- return;
- }
-
- if (connector->display_info.is_hdmi)
- init_params.sink_signal = SIGNAL_TYPE_HDMI_TYPE_A;
-
- aconnector->drm_edid = drm_edid;
-
- edid = drm_edid_raw(drm_edid); // FIXME: Get rid of drm_edid_raw()
- aconnector->dc_em_sink = dc_link_add_remote_sink(
- aconnector->dc_link,
- (uint8_t *)edid,
- (edid->extensions + 1) * EDID_LENGTH,
- &init_params);
-
- if (aconnector->base.force == DRM_FORCE_ON) {
- aconnector->dc_sink = aconnector->dc_link->local_sink ?
- aconnector->dc_link->local_sink :
- aconnector->dc_em_sink;
- if (aconnector->dc_sink)
- dc_sink_retain(aconnector->dc_sink);
- }
- }
-
- static void handle_edid_mgmt(struct amdgpu_dm_connector *aconnector)
- {
- struct dc_link *link = (struct dc_link *)aconnector->dc_link;
-
- /*
- * In case of headless boot with force on for DP managed connector
- * Those settings have to be != 0 to get initial modeset
- */
- if (link->connector_signal == SIGNAL_TYPE_DISPLAY_PORT) {
- link->verified_link_cap.lane_count = LANE_COUNT_FOUR;
- link->verified_link_cap.link_rate = LINK_RATE_HIGH2;
- }
-
- create_eml_sink(aconnector);
- }
-
- static enum dc_status dm_validate_stream_and_context(struct dc *dc,
- struct dc_stream_state *stream)
- {
- enum dc_status dc_result = DC_ERROR_UNEXPECTED;
- struct dc_plane_state *dc_plane_state = NULL;
- struct dc_state *dc_state = NULL;
-
- if (!stream)
- goto cleanup;
-
- dc_plane_state = dc_create_plane_state(dc);
- if (!dc_plane_state)
- goto cleanup;
-
- dc_state = dc_state_create(dc, NULL);
- if (!dc_state)
- goto cleanup;
-
- /* populate stream to plane */
- dc_plane_state->src_rect.height = stream->src.height;
- dc_plane_state->src_rect.width = stream->src.width;
- dc_plane_state->dst_rect.height = stream->src.height;
- dc_plane_state->dst_rect.width = stream->src.width;
- dc_plane_state->clip_rect.height = stream->src.height;
- dc_plane_state->clip_rect.width = stream->src.width;
- dc_plane_state->plane_size.surface_pitch = ((stream->src.width + 255) / 256) * 256;
- dc_plane_state->plane_size.surface_size.height = stream->src.height;
- dc_plane_state->plane_size.surface_size.width = stream->src.width;
- dc_plane_state->plane_size.chroma_size.height = stream->src.height;
- dc_plane_state->plane_size.chroma_size.width = stream->src.width;
- dc_plane_state->format = SURFACE_PIXEL_FORMAT_GRPH_ARGB8888;
- dc_plane_state->tiling_info.gfx9.swizzle = DC_SW_UNKNOWN;
- dc_plane_state->rotation = ROTATION_ANGLE_0;
- dc_plane_state->is_tiling_rotated = false;
- dc_plane_state->tiling_info.gfx8.array_mode = DC_ARRAY_LINEAR_GENERAL;
-
- dc_result = dc_validate_stream(dc, stream);
- if (dc_result == DC_OK)
- dc_result = dc_validate_plane(dc, dc_plane_state);
-
- if (dc_result == DC_OK)
- dc_result = dc_state_add_stream(dc, dc_state, stream);
-
- if (dc_result == DC_OK && !dc_state_add_plane(
- dc,
- stream,
- dc_plane_state,
- dc_state))
- dc_result = DC_FAIL_ATTACH_SURFACES;
-
- if (dc_result == DC_OK)
- dc_result = dc_validate_global_state(dc, dc_state, DC_VALIDATE_MODE_ONLY);
-
- cleanup:
- if (dc_state)
- dc_state_release(dc_state);
-
- if (dc_plane_state)
- dc_plane_state_release(dc_plane_state);
-
- return dc_result;
- }
-
- struct dc_stream_state *
- create_validate_stream_for_sink(struct drm_connector *connector,
- const struct drm_display_mode *drm_mode,
- const struct dm_connector_state *dm_state,
- const struct dc_stream_state *old_stream)
- {
- struct amdgpu_dm_connector *aconnector = NULL;
- struct amdgpu_device *adev = drm_to_adev(connector->dev);
- struct dc_stream_state *stream;
- const struct drm_connector_state *drm_state = dm_state ? &dm_state->base : NULL;
- int requested_bpc = drm_state ? drm_state->max_requested_bpc : 8;
- enum dc_status dc_result = DC_OK;
- uint8_t bpc_limit = 6;
-
- if (!dm_state)
- return NULL;
-
- if (connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK)
- aconnector = to_amdgpu_dm_connector(connector);
-
- if (aconnector &&
- (aconnector->dc_link->connector_signal == SIGNAL_TYPE_HDMI_TYPE_A ||
- aconnector->dc_link->connector_signal == SIGNAL_TYPE_HDMI_FRL ||
- aconnector->dc_link->dpcd_caps.dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER))
- bpc_limit = 8;
-
- do {
- drm_dbg_kms(connector->dev, "Trying with %d bpc\n", requested_bpc);
- stream = create_stream_for_sink(connector, drm_mode,
- dm_state, old_stream,
- requested_bpc);
- if (stream == NULL) {
- drm_err(adev_to_drm(adev), "Failed to create stream for sink!\n");
- break;
- }
-
- dc_result = dc_validate_stream(adev->dm.dc, stream);
-
- if (!aconnector) /* writeback connector */
- return stream;
-
- if (dc_result == DC_OK && stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST)
- dc_result = dm_dp_mst_is_port_support_mode(aconnector, stream);
-
- if (dc_result == DC_OK)
- dc_result = dm_validate_stream_and_context(adev->dm.dc, stream);
-
- if (dc_result != DC_OK) {
- drm_dbg_kms(connector->dev, "Pruned mode %d x %d (clk %d) %s %s -- %s\n",
- drm_mode->hdisplay,
- drm_mode->vdisplay,
- drm_mode->clock,
- dc_pixel_encoding_to_str(stream->timing.pixel_encoding),
- dc_color_depth_to_str(stream->timing.display_color_depth),
- dc_status_to_str(dc_result));
-
- dc_stream_release(stream);
- stream = NULL;
- requested_bpc -= 2; /* lower bpc to retry validation */
- }
-
- } while (stream == NULL && requested_bpc >= bpc_limit);
-
- switch (dc_result) {
- /*
- * If we failed to validate DP bandwidth stream with the requested RGB color depth,
- * we try to fallback and configure in order:
- * YUV422 (8bpc, 6bpc)
- * YUV420 (8bpc, 6bpc)
- */
- case DC_FAIL_ENC_VALIDATE:
- case DC_EXCEED_DONGLE_CAP:
- case DC_NO_DP_LINK_BANDWIDTH:
- /* recursively entered twice and already tried both YUV422 and YUV420 */
- if (aconnector->force_yuv422_output && aconnector->force_yuv420_output)
- break;
- /* first failure; try YUV422 */
- if (!aconnector->force_yuv422_output) {
- drm_dbg_kms(connector->dev, "%s:%d Validation failed with %d, retrying w/ YUV422\n",
- __func__, __LINE__, dc_result);
- aconnector->force_yuv422_output = true;
- /* recursively entered and YUV422 failed, try YUV420 */
- } else if (!aconnector->force_yuv420_output) {
- drm_dbg_kms(connector->dev, "%s:%d Validation failed with %d, retrying w/ YUV420\n",
- __func__, __LINE__, dc_result);
- aconnector->force_yuv420_output = true;
- }
- stream = create_validate_stream_for_sink(connector, drm_mode,
- dm_state, old_stream);
- aconnector->force_yuv422_output = false;
- aconnector->force_yuv420_output = false;
- break;
- case DC_OK:
- break;
- default:
- drm_dbg_kms(connector->dev, "%s:%d Unhandled validation failure %d\n",
- __func__, __LINE__, dc_result);
- break;
- }
-
- return stream;
- }
-
- enum drm_mode_status amdgpu_dm_connector_mode_valid(struct drm_connector *connector,
- const struct drm_display_mode *mode)
- {
- int result = MODE_ERROR;
- struct dc_sink *dc_sink;
- struct drm_display_mode *test_mode;
- /* TODO: Unhardcode stream count */
- struct dc_stream_state *stream;
- /* we always have an amdgpu_dm_connector here since we got
- * here via the amdgpu_dm_connector_helper_funcs
- */
- struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
-
- if ((mode->flags & DRM_MODE_FLAG_INTERLACE) ||
- (mode->flags & DRM_MODE_FLAG_DBLSCAN))
- return result;
-
- /*
- * Only run this the first time mode_valid is called to initilialize
- * EDID mgmt
- */
- if (aconnector->base.force != DRM_FORCE_UNSPECIFIED &&
- !aconnector->dc_em_sink)
- handle_edid_mgmt(aconnector);
-
- dc_sink = to_amdgpu_dm_connector(connector)->dc_sink;
-
- if (dc_sink == NULL && aconnector->base.force != DRM_FORCE_ON_DIGITAL &&
- aconnector->base.force != DRM_FORCE_ON) {
- drm_err(connector->dev, "dc_sink is NULL!\n");
- goto fail;
- }
-
- test_mode = drm_mode_duplicate(connector->dev, mode);
- if (!test_mode)
- goto fail;
-
- drm_mode_set_crtcinfo(test_mode, 0);
-
- stream = create_validate_stream_for_sink(connector, test_mode,
- to_dm_connector_state(connector->state),
- NULL);
- drm_mode_destroy(connector->dev, test_mode);
- if (stream) {
- dc_stream_release(stream);
- result = MODE_OK;
- }
-
- fail:
- /* TODO: error handling*/
- return result;
- }
-
- static int fill_hdr_info_packet(const struct drm_connector_state *state,
- struct dc_info_packet *out)
- {
- struct hdmi_drm_infoframe frame;
- unsigned char buf[30]; /* 26 + 4 */
- ssize_t len;
- int ret, i;
-
- memset(out, 0, sizeof(*out));
-
- if (!state->hdr_output_metadata)
- return 0;
-
- ret = drm_hdmi_infoframe_set_hdr_metadata(&frame, state);
- if (ret)
- return ret;
-
- len = hdmi_drm_infoframe_pack_only(&frame, buf, sizeof(buf));
- if (len < 0)
- return (int)len;
-
- /* Static metadata is a fixed 26 bytes + 4 byte header. */
- if (len != 30)
- return -EINVAL;
-
- /* Prepare the infopacket for DC. */
- switch (state->connector->connector_type) {
- case DRM_MODE_CONNECTOR_HDMIA:
- out->hb0 = 0x87; /* type */
- out->hb1 = 0x01; /* version */
- out->hb2 = 0x1A; /* length */
- out->sb[0] = buf[3]; /* checksum */
- i = 1;
- break;
-
- case DRM_MODE_CONNECTOR_DisplayPort:
- case DRM_MODE_CONNECTOR_eDP:
- out->hb0 = 0x00; /* sdp id, zero */
- out->hb1 = 0x87; /* type */
- out->hb2 = 0x1D; /* payload len - 1 */
- out->hb3 = (0x13 << 2); /* sdp version */
- out->sb[0] = 0x01; /* version */
- out->sb[1] = 0x1A; /* length */
- i = 2;
- break;
-
- default:
- return -EINVAL;
- }
-
- memcpy(&out->sb[i], &buf[4], 26);
- out->valid = true;
-
- print_hex_dump(KERN_DEBUG, "HDR SB:", DUMP_PREFIX_NONE, 16, 1, out->sb,
- sizeof(out->sb), false);
-
- return 0;
- }
-
- static int
- amdgpu_dm_connector_atomic_check(struct drm_connector *conn,
- struct drm_atomic_commit *state)
- {
- struct drm_connector_state *new_con_state =
- drm_atomic_get_new_connector_state(state, conn);
- struct drm_connector_state *old_con_state =
- drm_atomic_get_old_connector_state(state, conn);
- struct drm_crtc *crtc = new_con_state->crtc;
- struct drm_crtc_state *new_crtc_state;
- struct amdgpu_dm_connector *aconn = to_amdgpu_dm_connector(conn);
- int ret;
-
- if (WARN_ON(unlikely(!old_con_state || !new_con_state)))
- return -EINVAL;
-
- trace_amdgpu_dm_connector_atomic_check(new_con_state);
-
- if (conn->connector_type == DRM_MODE_CONNECTOR_DisplayPort) {
- ret = drm_dp_mst_root_conn_atomic_check(new_con_state, &aconn->mst_mgr);
- if (ret < 0)
- return ret;
- }
-
- if (!crtc)
- return 0;
-
- if (new_con_state->privacy_screen_sw_state != old_con_state->privacy_screen_sw_state) {
- new_crtc_state = drm_atomic_get_crtc_state(state, crtc);
- if (IS_ERR(new_crtc_state))
- return PTR_ERR(new_crtc_state);
-
- new_crtc_state->mode_changed = true;
- }
-
- if (new_con_state->colorspace != old_con_state->colorspace) {
- new_crtc_state = drm_atomic_get_crtc_state(state, crtc);
- if (IS_ERR(new_crtc_state))
- return PTR_ERR(new_crtc_state);
-
- new_crtc_state->mode_changed = true;
- }
-
- if (new_con_state->content_type != old_con_state->content_type) {
- new_crtc_state = drm_atomic_get_crtc_state(state, crtc);
- if (IS_ERR(new_crtc_state))
- return PTR_ERR(new_crtc_state);
-
- new_crtc_state->mode_changed = true;
- }
-
- if (!drm_connector_atomic_hdr_metadata_equal(old_con_state, new_con_state)) {
- struct dc_info_packet hdr_infopacket;
-
- ret = fill_hdr_info_packet(new_con_state, &hdr_infopacket);
- if (ret)
- return ret;
-
- new_crtc_state = drm_atomic_get_crtc_state(state, crtc);
- if (IS_ERR(new_crtc_state))
- return PTR_ERR(new_crtc_state);
-
- /*
- * DC considers the stream backends changed if the
- * static metadata changes. Forcing the modeset also
- * gives a simple way for userspace to switch from
- * 8bpc to 10bpc when setting the metadata to enter
- * or exit HDR.
- *
- * Changing the static metadata after it's been
- * set is permissible, however. So only force a
- * modeset if we're entering or exiting HDR.
- */
- new_crtc_state->mode_changed = new_crtc_state->mode_changed ||
- !old_con_state->hdr_output_metadata ||
- !new_con_state->hdr_output_metadata;
- }
-
- return 0;
- }
-
- static const struct drm_connector_helper_funcs
- amdgpu_dm_connector_helper_funcs = {
- /*
- * If hotplugging a second bigger display in FB Con mode, bigger resolution
- * modes will be filtered by drm_mode_validate_size(), and those modes
- * are missing after user start lightdm. So we need to renew modes list.
- * in get_modes call back, not just return the modes count
- */
- .get_modes = get_modes,
- .mode_valid = amdgpu_dm_connector_mode_valid,
- .atomic_check = amdgpu_dm_connector_atomic_check,
- };
-
- static void dm_encoder_helper_disable(struct drm_encoder *encoder)
- {
-
- }
-
- int convert_dc_color_depth_into_bpc(enum dc_color_depth display_color_depth)
- {
- switch (display_color_depth) {
- case COLOR_DEPTH_666:
- return 6;
- case COLOR_DEPTH_888:
- return 8;
- case COLOR_DEPTH_101010:
- return 10;
- case COLOR_DEPTH_121212:
- return 12;
- case COLOR_DEPTH_141414:
- return 14;
- case COLOR_DEPTH_161616:
- return 16;
- default:
- break;
- }
- return 0;
- }
-
- static int dm_encoder_helper_atomic_check(struct drm_encoder *encoder,
- struct drm_crtc_state *crtc_state,
- struct drm_connector_state *conn_state)
- {
- struct drm_atomic_commit *state = crtc_state->state;
- struct drm_connector *connector = conn_state->connector;
- struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
- struct dm_connector_state *dm_new_connector_state = to_dm_connector_state(conn_state);
- const struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode;
- struct drm_dp_mst_topology_mgr *mst_mgr;
- struct drm_dp_mst_port *mst_port;
- struct drm_dp_mst_topology_state *mst_state;
- enum dc_color_depth color_depth;
- int clock, bpp = 0;
- bool is_y420 = false;
-
- if ((connector->connector_type == DRM_MODE_CONNECTOR_eDP) ||
- (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)) {
- struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
- struct drm_display_mode *native_mode = &amdgpu_encoder->native_mode;
- enum drm_mode_status result;
-
- result = drm_crtc_helper_mode_valid_fixed(encoder->crtc, adjusted_mode, native_mode);
- if (result != MODE_OK && dm_new_connector_state->scaling == RMX_OFF) {
- drm_dbg_driver(encoder->dev,
- "mode %dx%d@%dHz is not native, enabling scaling\n",
- adjusted_mode->hdisplay, adjusted_mode->vdisplay,
- drm_mode_vrefresh(adjusted_mode));
- dm_new_connector_state->scaling = RMX_ASPECT;
- }
- return 0;
- }
-
- if (!aconnector->mst_output_port)
- return 0;
-
- mst_port = aconnector->mst_output_port;
- mst_mgr = &aconnector->mst_root->mst_mgr;
-
- if (!crtc_state->connectors_changed && !crtc_state->mode_changed)
- return 0;
-
- mst_state = drm_atomic_get_mst_topology_state(state, mst_mgr);
- if (IS_ERR(mst_state))
- return PTR_ERR(mst_state);
-
- mst_state->pbn_div.full = dm_mst_get_pbn_divider(aconnector->mst_root->dc_link);
-
- if (!state->duplicated) {
- int max_bpc = conn_state->max_requested_bpc;
-
- is_y420 = drm_mode_is_420_also(&connector->display_info, adjusted_mode) &&
- aconnector->force_yuv420_output;
- color_depth = convert_color_depth_from_display_info(connector,
- is_y420,
- max_bpc);
- bpp = convert_dc_color_depth_into_bpc(color_depth) * 3;
- clock = adjusted_mode->clock;
- dm_new_connector_state->pbn = drm_dp_calc_pbn_mode(clock, bpp << 4);
- }
-
- dm_new_connector_state->vcpi_slots =
- drm_dp_atomic_find_time_slots(state, mst_mgr, mst_port,
- dm_new_connector_state->pbn);
- if (dm_new_connector_state->vcpi_slots < 0) {
- drm_dbg_atomic(connector->dev, "failed finding vcpi slots: %d\n", (int)dm_new_connector_state->vcpi_slots);
- return dm_new_connector_state->vcpi_slots;
- }
- return 0;
- }
-
- const struct drm_encoder_helper_funcs amdgpu_dm_encoder_helper_funcs = {
- .disable = dm_encoder_helper_disable,
- .atomic_check = dm_encoder_helper_atomic_check
- };
-
static int dm_update_mst_vcpi_slots_for_dsc(struct drm_atomic_commit *state,
struct dc_state *dc_state,
struct dsc_mst_fairness_vars *vars)
@@@ -8908,753 -3526,6 +3526,6 @@@
return 0;
}
- static int to_drm_connector_type(enum signal_type st, uint32_t connector_id)
- {
- switch (st) {
- case SIGNAL_TYPE_HDMI_TYPE_A:
- return DRM_MODE_CONNECTOR_HDMIA;
- case SIGNAL_TYPE_EDP:
- return DRM_MODE_CONNECTOR_eDP;
- case SIGNAL_TYPE_LVDS:
- return DRM_MODE_CONNECTOR_LVDS;
- case SIGNAL_TYPE_RGB:
- return DRM_MODE_CONNECTOR_VGA;
- case SIGNAL_TYPE_DISPLAY_PORT:
- case SIGNAL_TYPE_DISPLAY_PORT_MST:
- /* External DP bridges have a different connector type. */
- if (connector_id == CONNECTOR_ID_VGA)
- return DRM_MODE_CONNECTOR_VGA;
- else if (connector_id == CONNECTOR_ID_LVDS)
- return DRM_MODE_CONNECTOR_LVDS;
-
- return DRM_MODE_CONNECTOR_DisplayPort;
- case SIGNAL_TYPE_DVI_DUAL_LINK:
- case SIGNAL_TYPE_DVI_SINGLE_LINK:
- if (connector_id == CONNECTOR_ID_SINGLE_LINK_DVII ||
- connector_id == CONNECTOR_ID_DUAL_LINK_DVII)
- return DRM_MODE_CONNECTOR_DVII;
-
- return DRM_MODE_CONNECTOR_DVID;
- case SIGNAL_TYPE_VIRTUAL:
- return DRM_MODE_CONNECTOR_VIRTUAL;
-
- default:
- return DRM_MODE_CONNECTOR_Unknown;
- }
- }
-
- static struct drm_encoder *amdgpu_dm_connector_to_encoder(struct drm_connector *connector)
- {
- struct drm_encoder *encoder;
-
- /* There is only one encoder per connector */
- drm_connector_for_each_possible_encoder(connector, encoder)
- return encoder;
-
- return NULL;
- }
-
- static void amdgpu_dm_get_native_mode(struct drm_connector *connector)
- {
- struct drm_encoder *encoder;
- struct amdgpu_encoder *amdgpu_encoder;
-
- encoder = amdgpu_dm_connector_to_encoder(connector);
-
- if (encoder == NULL)
- return;
-
- amdgpu_encoder = to_amdgpu_encoder(encoder);
-
- amdgpu_encoder->native_mode.clock = 0;
-
- if (!list_empty(&connector->probed_modes)) {
- struct drm_display_mode *preferred_mode = NULL;
-
- list_for_each_entry(preferred_mode,
- &connector->probed_modes,
- head) {
- if (preferred_mode->type & DRM_MODE_TYPE_PREFERRED)
- amdgpu_encoder->native_mode = *preferred_mode;
-
- break;
- }
-
- }
- }
-
- static struct drm_display_mode *
- amdgpu_dm_create_common_mode(struct drm_encoder *encoder,
- const char *name,
- int hdisplay, int vdisplay)
- {
- struct drm_device *dev = encoder->dev;
- struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
- struct drm_display_mode *mode = NULL;
- struct drm_display_mode *native_mode = &amdgpu_encoder->native_mode;
-
- mode = drm_mode_duplicate(dev, native_mode);
-
- if (mode == NULL)
- return NULL;
-
- mode->hdisplay = hdisplay;
- mode->vdisplay = vdisplay;
- mode->type &= ~DRM_MODE_TYPE_PREFERRED;
- strscpy(mode->name, name, DRM_DISPLAY_MODE_LEN);
-
- return mode;
-
- }
-
- static const struct amdgpu_dm_mode_size {
- char name[DRM_DISPLAY_MODE_LEN];
- int w;
- int h;
- } common_modes[] = {
- { "640x480", 640, 480},
- { "800x600", 800, 600},
- { "1024x768", 1024, 768},
- { "1280x720", 1280, 720},
- { "1280x800", 1280, 800},
- {"1280x1024", 1280, 1024},
- { "1440x900", 1440, 900},
- {"1680x1050", 1680, 1050},
- {"1600x1200", 1600, 1200},
- {"1920x1080", 1920, 1080},
- {"1920x1200", 1920, 1200}
- };
-
- static void amdgpu_dm_connector_add_common_modes(struct drm_encoder *encoder,
- struct drm_connector *connector)
- {
- struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);
- struct drm_display_mode *mode = NULL;
- struct drm_display_mode *native_mode = &amdgpu_encoder->native_mode;
- struct amdgpu_dm_connector *amdgpu_dm_connector =
- to_amdgpu_dm_connector(connector);
- int i;
- int n;
-
- if ((connector->connector_type != DRM_MODE_CONNECTOR_eDP) &&
- (connector->connector_type != DRM_MODE_CONNECTOR_LVDS))
- return;
-
- n = ARRAY_SIZE(common_modes);
-
- for (i = 0; i < n; i++) {
- struct drm_display_mode *curmode = NULL;
- bool mode_existed = false;
-
- if (common_modes[i].w > native_mode->hdisplay ||
- common_modes[i].h > native_mode->vdisplay ||
- (common_modes[i].w == native_mode->hdisplay &&
- common_modes[i].h == native_mode->vdisplay))
- continue;
-
- list_for_each_entry(curmode, &connector->probed_modes, head) {
- if (common_modes[i].w == curmode->hdisplay &&
- common_modes[i].h == curmode->vdisplay) {
- mode_existed = true;
- break;
- }
- }
-
- if (mode_existed)
- continue;
-
- mode = amdgpu_dm_create_common_mode(encoder,
- common_modes[i].name, common_modes[i].w,
- common_modes[i].h);
- if (!mode)
- continue;
-
- drm_mode_probed_add(connector, mode);
- amdgpu_dm_connector->num_modes++;
- }
- }
-
- static void amdgpu_set_panel_orientation(struct drm_connector *connector)
- {
- struct drm_encoder *encoder;
- struct amdgpu_encoder *amdgpu_encoder;
- const struct drm_display_mode *native_mode;
-
- if (connector->connector_type != DRM_MODE_CONNECTOR_eDP &&
- connector->connector_type != DRM_MODE_CONNECTOR_LVDS)
- return;
-
- mutex_lock(&connector->dev->mode_config.mutex);
- amdgpu_dm_connector_get_modes(connector);
- mutex_unlock(&connector->dev->mode_config.mutex);
-
- encoder = amdgpu_dm_connector_to_encoder(connector);
- if (!encoder)
- return;
-
- amdgpu_encoder = to_amdgpu_encoder(encoder);
-
- native_mode = &amdgpu_encoder->native_mode;
- if (native_mode->hdisplay == 0 || native_mode->vdisplay == 0)
- return;
-
- drm_connector_set_panel_orientation_with_quirk(connector,
- DRM_MODE_PANEL_ORIENTATION_UNKNOWN,
- native_mode->hdisplay,
- native_mode->vdisplay);
- }
-
- static void amdgpu_dm_connector_ddc_get_modes(struct drm_connector *connector,
- const struct drm_edid *drm_edid)
- {
- struct amdgpu_dm_connector *amdgpu_dm_connector =
- to_amdgpu_dm_connector(connector);
-
- if (drm_edid) {
- /* empty probed_modes */
- INIT_LIST_HEAD(&connector->probed_modes);
- amdgpu_dm_connector->num_modes =
- drm_edid_connector_add_modes(connector);
-
- /* sorting the probed modes before calling function
- * amdgpu_dm_get_native_mode() since EDID can have
- * more than one preferred mode. The modes that are
- * later in the probed mode list could be of higher
- * and preferred resolution. For example, 3840x2160
- * resolution in base EDID preferred timing and 4096x2160
- * preferred resolution in DID extension block later.
- */
- drm_mode_sort(&connector->probed_modes);
- amdgpu_dm_get_native_mode(connector);
-
- /* Freesync capabilities are reset by calling
- * drm_edid_connector_add_modes() and need to be
- * restored here.
- */
- amdgpu_dm_update_freesync_caps(connector, drm_edid, false);
- } else {
- amdgpu_dm_connector->num_modes = 0;
- }
- }
-
- static bool is_duplicate_mode(struct amdgpu_dm_connector *aconnector,
- struct drm_display_mode *mode)
- {
- struct drm_display_mode *m;
-
- list_for_each_entry(m, &aconnector->base.probed_modes, head) {
- if (drm_mode_equal(m, mode))
- return true;
- }
-
- return false;
- }
-
- static uint add_fs_modes(struct amdgpu_dm_connector *aconnector)
- {
- const struct drm_display_mode *m;
- struct drm_display_mode *new_mode;
- uint i;
- u32 new_modes_count = 0;
-
- /* Standard FPS values
- *
- * 23.976 - TV/NTSC
- * 24 - Cinema
- * 25 - TV/PAL
- * 29.97 - TV/NTSC
- * 30 - TV/NTSC
- * 48 - Cinema HFR
- * 50 - TV/PAL
- * 60 - Commonly used
- * 48,72,96,120 - Multiples of 24
- */
- static const u32 common_rates[] = {
- 23976, 24000, 25000, 29970, 30000,
- 48000, 50000, 60000, 72000, 96000, 120000
- };
-
- /*
- * Find mode with highest refresh rate with the same resolution
- * as the preferred mode. Some monitors report a preferred mode
- * with lower resolution than the highest refresh rate supported.
- */
-
- m = get_highest_refresh_rate_mode(aconnector, true);
- if (!m)
- return 0;
-
- for (i = 0; i < ARRAY_SIZE(common_rates); i++) {
- u64 target_vtotal, target_vtotal_diff;
- u64 num, den;
-
- if (drm_mode_vrefresh(m) * 1000 < common_rates[i])
- continue;
-
- if (common_rates[i] < aconnector->min_vfreq * 1000 ||
- common_rates[i] > aconnector->max_vfreq * 1000)
- continue;
-
- num = (unsigned long long)m->clock * 1000 * 1000;
- den = common_rates[i] * (unsigned long long)m->htotal;
- target_vtotal = div_u64(num, den);
- target_vtotal_diff = target_vtotal - m->vtotal;
-
- /* Check for illegal modes */
- if (m->vsync_start + target_vtotal_diff < m->vdisplay ||
- m->vsync_end + target_vtotal_diff < m->vsync_start ||
- m->vtotal + target_vtotal_diff < m->vsync_end)
- continue;
-
- new_mode = drm_mode_duplicate(aconnector->base.dev, m);
- if (!new_mode)
- goto out;
-
- new_mode->vtotal += (u16)target_vtotal_diff;
- new_mode->vsync_start += (u16)target_vtotal_diff;
- new_mode->vsync_end += (u16)target_vtotal_diff;
- new_mode->type &= ~DRM_MODE_TYPE_PREFERRED;
- new_mode->type |= DRM_MODE_TYPE_DRIVER;
-
- if (!is_duplicate_mode(aconnector, new_mode)) {
- drm_mode_probed_add(&aconnector->base, new_mode);
- new_modes_count += 1;
- } else
- drm_mode_destroy(aconnector->base.dev, new_mode);
- }
- out:
- return new_modes_count;
- }
-
- static void amdgpu_dm_connector_add_freesync_modes(struct drm_connector *connector,
- const struct drm_edid *drm_edid)
- {
- struct amdgpu_dm_connector *amdgpu_dm_connector =
- to_amdgpu_dm_connector(connector);
-
- if (!(amdgpu_freesync_vid_mode && drm_edid))
- return;
-
- if (!amdgpu_dm_connector->dc_sink || !amdgpu_dm_connector->dc_link)
- return;
-
- if (!dc_supports_vrr(amdgpu_dm_connector->dc_sink->ctx->dce_version))
- return;
-
- if (dc_connector_supports_analog(amdgpu_dm_connector->dc_link->link_id.id) &&
- amdgpu_dm_connector->dc_sink->edid_caps.analog)
- return;
-
- if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10)
- amdgpu_dm_connector->num_modes +=
- add_fs_modes(amdgpu_dm_connector);
- }
-
- static int amdgpu_dm_connector_get_modes(struct drm_connector *connector)
- {
- struct amdgpu_dm_connector *amdgpu_dm_connector =
- to_amdgpu_dm_connector(connector);
- struct dc_link *dc_link = amdgpu_dm_connector->dc_link;
- struct drm_encoder *encoder;
- const struct drm_edid *drm_edid = amdgpu_dm_connector->drm_edid;
- struct dc_link_settings *verified_link_cap = &dc_link->verified_link_cap;
- const struct dc *dc = dc_link->dc;
-
- encoder = amdgpu_dm_connector_to_encoder(connector);
-
- if (!drm_edid) {
- amdgpu_dm_connector->num_modes =
- drm_add_modes_noedid(connector, 640, 480);
- if (dc->link_srv->dp_get_encoding_format(verified_link_cap) == DP_128b_132b_ENCODING)
- amdgpu_dm_connector->num_modes +=
- drm_add_modes_noedid(connector, 1920, 1080);
-
- if (amdgpu_dm_connector->dc_sink &&
- amdgpu_dm_connector->dc_sink->edid_caps.analog &&
- dc_connector_supports_analog(dc_link->link_id.id)) {
- /* Analog monitor connected by DAC load detection.
- * Add common modes. It will be up to the user to select one that works.
- */
- for (int i = 0; i < ARRAY_SIZE(common_modes); i++)
- amdgpu_dm_connector->num_modes += drm_add_modes_noedid(
- connector, common_modes[i].w, common_modes[i].h);
- }
- } else {
- amdgpu_dm_connector_ddc_get_modes(connector, drm_edid);
- if (encoder)
- amdgpu_dm_connector_add_common_modes(encoder, connector);
- amdgpu_dm_connector_add_freesync_modes(connector, drm_edid);
- }
- amdgpu_dm_fbc_init(connector);
-
- return amdgpu_dm_connector->num_modes;
- }
-
- static const u32 supported_colorspaces =
- BIT(DRM_MODE_COLORIMETRY_BT709_YCC) |
- BIT(DRM_MODE_COLORIMETRY_OPRGB) |
- BIT(DRM_MODE_COLORIMETRY_BT2020_RGB) |
- BIT(DRM_MODE_COLORIMETRY_BT2020_YCC);
-
- void amdgpu_dm_connector_init_helper(struct amdgpu_display_manager *dm,
- struct amdgpu_dm_connector *aconnector,
- int connector_type,
- struct dc_link *link,
- int link_index)
- {
- struct amdgpu_device *adev = drm_to_adev(dm->ddev);
-
- /*
- * Some of the properties below require access to state, like bpc.
- * Allocate some default initial connector state with our reset helper.
- */
- if (aconnector->base.funcs->reset)
- aconnector->base.funcs->reset(&aconnector->base);
-
- aconnector->connector_id = link_index;
- aconnector->bl_idx = -1;
- aconnector->dc_link = link;
- aconnector->base.interlace_allowed = false;
- aconnector->base.doublescan_allowed = false;
- aconnector->base.stereo_allowed = false;
- aconnector->base.dpms = DRM_MODE_DPMS_OFF;
- aconnector->hpd.hpd = AMDGPU_HPD_NONE; /* not used */
- aconnector->audio_inst = -1;
- aconnector->pack_sdp_v1_3 = false;
- aconnector->as_type = ADAPTIVE_SYNC_TYPE_NONE;
- memset(&aconnector->vsdb_info, 0, sizeof(aconnector->vsdb_info));
- mutex_init(&aconnector->hpd_lock);
- mutex_init(&aconnector->handle_mst_msg_ready);
-
- /*
- * If HDMI HPD debounce delay is set, use the minimum between selected
- * value and AMDGPU_DM_MAX_HDMI_HPD_DEBOUNCE_MS
- */
- if (amdgpu_hdmi_hpd_debounce_delay_ms) {
- aconnector->hdmi_hpd_debounce_delay_ms = min(amdgpu_hdmi_hpd_debounce_delay_ms,
- AMDGPU_DM_MAX_HDMI_HPD_DEBOUNCE_MS);
- INIT_DELAYED_WORK(&aconnector->hdmi_hpd_debounce_work, hdmi_hpd_debounce_work);
- aconnector->hdmi_prev_sink = NULL;
- } else {
- aconnector->hdmi_hpd_debounce_delay_ms = 0;
- }
-
- /*
- * configure support HPD hot plug connector_>polled default value is 0
- * which means HPD hot plug not supported
- */
- switch (connector_type) {
- case DRM_MODE_CONNECTOR_HDMIA:
- aconnector->base.polled = DRM_CONNECTOR_POLL_HPD;
- aconnector->base.ycbcr_420_allowed =
- link->link_enc->features.hdmi_ycbcr420_supported ? true : false;
- break;
- case DRM_MODE_CONNECTOR_DisplayPort:
- aconnector->base.polled = DRM_CONNECTOR_POLL_HPD;
- link->link_enc = link_enc_cfg_get_link_enc(link);
- ASSERT(link->link_enc);
- if (link->link_enc)
- aconnector->base.ycbcr_420_allowed =
- link->link_enc->features.dp_ycbcr420_supported ? true : false;
- break;
- case DRM_MODE_CONNECTOR_DVID:
- aconnector->base.polled = DRM_CONNECTOR_POLL_HPD;
- break;
- case DRM_MODE_CONNECTOR_DVII:
- case DRM_MODE_CONNECTOR_VGA:
- aconnector->base.polled =
- DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT;
- break;
- default:
- break;
- }
-
- drm_object_attach_property(&aconnector->base.base,
- dm->ddev->mode_config.scaling_mode_property,
- DRM_MODE_SCALE_NONE);
-
- if (connector_type == DRM_MODE_CONNECTOR_HDMIA
- || (connector_type == DRM_MODE_CONNECTOR_DisplayPort && !aconnector->mst_root))
- drm_connector_attach_broadcast_rgb_property(&aconnector->base);
-
- drm_object_attach_property(&aconnector->base.base,
- adev->mode_info.underscan_property,
- UNDERSCAN_OFF);
- drm_object_attach_property(&aconnector->base.base,
- adev->mode_info.underscan_hborder_property,
- 0);
- drm_object_attach_property(&aconnector->base.base,
- adev->mode_info.underscan_vborder_property,
- 0);
-
- if (!aconnector->mst_root)
- drm_connector_attach_max_bpc_property(&aconnector->base, 8, 16);
-
- aconnector->base.state->max_bpc = 16;
- aconnector->base.state->max_requested_bpc = aconnector->base.state->max_bpc;
-
- if (connector_type == DRM_MODE_CONNECTOR_HDMIA) {
- /* Content Type is currently only implemented for HDMI. */
- drm_connector_attach_content_type_property(&aconnector->base);
- }
-
- if (connector_type == DRM_MODE_CONNECTOR_HDMIA) {
- if (!drm_mode_create_hdmi_colorspace_property(&aconnector->base, supported_colorspaces))
- drm_connector_attach_colorspace_property(&aconnector->base);
- } else if ((connector_type == DRM_MODE_CONNECTOR_DisplayPort && !aconnector->mst_root) ||
- connector_type == DRM_MODE_CONNECTOR_eDP) {
- if (!drm_mode_create_dp_colorspace_property(&aconnector->base, supported_colorspaces))
- drm_connector_attach_colorspace_property(&aconnector->base);
- }
-
- if (connector_type == DRM_MODE_CONNECTOR_HDMIA ||
- connector_type == DRM_MODE_CONNECTOR_DisplayPort ||
- connector_type == DRM_MODE_CONNECTOR_eDP) {
- drm_connector_attach_hdr_output_metadata_property(&aconnector->base);
-
- if (!aconnector->mst_root)
- drm_connector_attach_vrr_capable_property(&aconnector->base);
-
- if (adev->dm.hdcp_workqueue)
- drm_connector_attach_content_protection_property(&aconnector->base, true);
- }
-
- if (connector_type == DRM_MODE_CONNECTOR_eDP) {
- struct drm_privacy_screen *privacy_screen;
-
- drm_connector_attach_panel_type_property(&aconnector->base);
-
- privacy_screen = drm_privacy_screen_get(adev_to_drm(adev)->dev, NULL);
- if (!IS_ERR(privacy_screen)) {
- drm_connector_attach_privacy_screen_provider(&aconnector->base,
- privacy_screen);
- } else if (PTR_ERR(privacy_screen) != -ENODEV) {
- drm_warn(adev_to_drm(adev), "Error getting privacy-screen\n");
- }
- }
- }
-
- static int amdgpu_dm_i2c_xfer(struct i2c_adapter *i2c_adap,
- struct i2c_msg *msgs, int num)
- {
- struct amdgpu_i2c_adapter *i2c = i2c_get_adapdata(i2c_adap);
- struct ddc_service *ddc_service = i2c->ddc_service;
- struct i2c_command cmd;
- int i;
- int result = -EIO;
-
- if (!ddc_service->ddc_pin)
- return result;
-
- cmd.payloads = kzalloc_objs(struct i2c_payload, num);
-
- if (!cmd.payloads)
- return result;
-
- cmd.number_of_payloads = num;
- cmd.engine = I2C_COMMAND_ENGINE_DEFAULT;
- cmd.speed = 100;
-
- for (i = 0; i < num; i++) {
- cmd.payloads[i].write = !(msgs[i].flags & I2C_M_RD);
- cmd.payloads[i].address = msgs[i].addr;
- cmd.payloads[i].length = msgs[i].len;
- cmd.payloads[i].data = msgs[i].buf;
- }
-
- if (i2c->oem) {
- if (dc_submit_i2c_oem(
- ddc_service->ctx->dc,
- &cmd))
- result = num;
- } else {
- if (dc_submit_i2c(
- ddc_service->ctx->dc,
- ddc_service->link->link_index,
- &cmd))
- result = num;
- }
-
- kfree(cmd.payloads);
- return result;
- }
-
- static u32 amdgpu_dm_i2c_func(struct i2c_adapter *adap)
- {
- return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
- }
-
- static const struct i2c_algorithm amdgpu_dm_i2c_algo = {
- .master_xfer = amdgpu_dm_i2c_xfer,
- .functionality = amdgpu_dm_i2c_func,
- };
-
- static struct amdgpu_i2c_adapter *
- create_i2c(struct ddc_service *ddc_service, bool oem)
- {
- struct amdgpu_device *adev = ddc_service->ctx->driver_context;
- struct amdgpu_i2c_adapter *i2c;
-
- i2c = kzalloc_obj(struct amdgpu_i2c_adapter);
- if (!i2c)
- return NULL;
- i2c->base.owner = THIS_MODULE;
- i2c->base.dev.parent = &adev->pdev->dev;
- i2c->base.algo = &amdgpu_dm_i2c_algo;
- if (oem)
- snprintf(i2c->base.name, sizeof(i2c->base.name), "AMDGPU DM i2c OEM bus");
- else
- snprintf(i2c->base.name, sizeof(i2c->base.name), "AMDGPU DM i2c hw bus %d",
- ddc_service->link->link_index);
- i2c_set_adapdata(&i2c->base, i2c);
- i2c->ddc_service = ddc_service;
- i2c->oem = oem;
-
- return i2c;
- }
-
- int amdgpu_dm_initialize_hdmi_connector(struct amdgpu_dm_connector *aconnector)
- {
- struct cec_connector_info conn_info;
- struct drm_device *ddev = aconnector->base.dev;
- struct device *hdmi_dev = ddev->dev;
-
- if (amdgpu_dc_debug_mask & DC_DISABLE_HDMI_CEC) {
- drm_info(ddev, "HDMI-CEC feature masked\n");
- return -EINVAL;
- }
-
- cec_fill_conn_info_from_drm(&conn_info, &aconnector->base);
- aconnector->notifier =
- cec_notifier_conn_register(hdmi_dev, NULL, &conn_info);
- if (!aconnector->notifier) {
- drm_err(ddev, "Failed to create cec notifier\n");
- return -ENOMEM;
- }
-
- return 0;
- }
-
- /*
- * Note: this function assumes that dc_link_detect() was called for the
- * dc_link which will be represented by this aconnector.
- */
- static int amdgpu_dm_connector_init(struct amdgpu_display_manager *dm,
- struct amdgpu_dm_connector *aconnector,
- u32 link_index,
- struct amdgpu_encoder *aencoder)
- {
- int res = 0;
- int connector_type;
- struct dc *dc = dm->dc;
- struct dc_link *link = dc_get_link_at_index(dc, link_index);
- struct amdgpu_i2c_adapter *i2c;
-
- /* Not needed for writeback connector */
- link->priv = aconnector;
-
-
- i2c = create_i2c(link->ddc, false);
- if (!i2c) {
- drm_err(adev_to_drm(dm->adev), "Failed to create i2c adapter data\n");
- return -ENOMEM;
- }
-
- aconnector->i2c = i2c;
- res = devm_i2c_add_adapter(dm->adev->dev, &i2c->base);
-
- if (res) {
- drm_err(adev_to_drm(dm->adev), "Failed to register hw i2c %d\n", link->link_index);
- goto out_free;
- }
-
- connector_type = to_drm_connector_type(link->connector_signal, link->link_id.id);
-
- res = drm_connector_init_with_ddc(
- dm->ddev,
- &aconnector->base,
- &amdgpu_dm_connector_funcs,
- connector_type,
- &i2c->base);
-
- if (res) {
- drm_err(adev_to_drm(dm->adev), "connector_init failed\n");
- aconnector->connector_id = -1;
- goto out_free;
- }
-
- drm_connector_helper_add(
- &aconnector->base,
- &amdgpu_dm_connector_helper_funcs);
-
- amdgpu_dm_connector_init_helper(
- dm,
- aconnector,
- connector_type,
- link,
- link_index);
-
- drm_connector_attach_encoder(
- &aconnector->base, &aencoder->base);
-
- if (connector_type == DRM_MODE_CONNECTOR_HDMIA ||
- connector_type == DRM_MODE_CONNECTOR_HDMIB)
- amdgpu_dm_initialize_hdmi_connector(aconnector);
-
- if (dc_is_dp_signal(link->connector_signal))
- amdgpu_dm_initialize_dp_connector(dm, aconnector, link->link_index);
-
- out_free:
- if (res) {
- kfree(i2c);
- aconnector->i2c = NULL;
- }
- return res;
- }
-
- int amdgpu_dm_get_encoder_crtc_mask(struct amdgpu_device *adev)
- {
- switch (adev->mode_info.num_crtc) {
- case 1:
- return 0x1;
- case 2:
- return 0x3;
- case 3:
- return 0x7;
- case 4:
- return 0xf;
- case 5:
- return 0x1f;
- case 6:
- default:
- return 0x3f;
- }
- }
-
- static int amdgpu_dm_encoder_init(struct drm_device *dev,
- struct amdgpu_encoder *aencoder,
- uint32_t link_index)
- {
- struct amdgpu_device *adev = drm_to_adev(dev);
-
- int res = drm_encoder_init(dev,
- &aencoder->base,
- &amdgpu_dm_encoder_funcs,
- DRM_MODE_ENCODER_TMDS,
- NULL);
-
- aencoder->base.possible_crtcs = amdgpu_dm_get_encoder_crtc_mask(adev);
-
- if (!res)
- aencoder->encoder_id = link_index;
- else
- aencoder->encoder_id = -1;
-
- drm_encoder_helper_add(&aencoder->base, &amdgpu_dm_encoder_helper_funcs);
-
- return res;
- }
-
static void manage_dm_interrupts(struct amdgpu_device *adev,
struct amdgpu_crtc *acrtc,
struct dm_crtc_state *acrtc_state)
@@@ -9754,7 -3625,7 +3625,7 @@@ static void dm_update_pflip_irq_state(s
amdgpu_irq_update(adev, &adev->pageflip_irq, irq_type);
}
- static bool
+ STATIC_IFN_KUNIT bool
is_scaling_state_different(const struct dm_connector_state *dm_state,
const struct dm_connector_state *old_dm_state)
{
@@@ -9771,6 -3642,7 +3642,7 @@@
return true;
return false;
}
+ EXPORT_IF_KUNIT(is_scaling_state_different);
static bool is_content_protection_different(struct drm_crtc_state *new_crtc_state,
struct drm_crtc_state *old_crtc_state,
@@@ -10079,7 -3951,7 +3951,7 @@@ static void amdgpu_dm_handle_vrr_transi
struct dm_crtc_state *old_state,
struct dm_crtc_state *new_state)
{
- struct amdgpu_device *adev = drm_to_adev(new_state->base.crtc->dev);
+ struct amdgpu_device *adev = dm->adev;
bool old_vrr_active = amdgpu_dm_crtc_vrr_active(old_state);
bool new_vrr_active = amdgpu_dm_crtc_vrr_active(new_state);
@@@ -10384,14 -4256,12 +4256,12 @@@ static void amdgpu_dm_commit_planes(str
continue;
bundle->surface_updates[planes_count].surface = dc_plane;
- if (new_pcrtc_state->color_mgmt_changed || new_plane_state->color_mgmt_changed) {
+ if (new_pcrtc_state->color_mgmt_changed) {
bundle->surface_updates[planes_count].gamma = &dc_plane->gamma_correction;
bundle->surface_updates[planes_count].in_transfer_func = &dc_plane->in_transfer_func;
bundle->surface_updates[planes_count].gamut_remap_matrix = &dc_plane->gamut_remap_matrix;
bundle->surface_updates[planes_count].hdr_mult = dc_plane->hdr_mult;
- bundle->surface_updates[planes_count].func_shaper = &dc_plane->in_shaper_func;
- bundle->surface_updates[planes_count].lut3d_func = &dc_plane->lut3d_func;
- bundle->surface_updates[planes_count].blend_tf = &dc_plane->blend_tf;
+ bundle->surface_updates[planes_count].cm = &dc_plane->cm;
}
amdgpu_dm_plane_fill_dc_scaling_info(dm->adev, new_plane_state,
@@@ -10764,87 -4634,6 +4634,6 @@@ cleanup
kfree(bundle);
}
- static void amdgpu_dm_commit_audio(struct drm_device *dev,
- struct drm_atomic_commit *state)
- {
- struct amdgpu_device *adev = drm_to_adev(dev);
- struct amdgpu_dm_connector *aconnector;
- struct drm_connector *connector;
- struct drm_connector_state *old_con_state, *new_con_state;
- struct drm_crtc_state *new_crtc_state;
- struct dm_crtc_state *new_dm_crtc_state;
- const struct dc_stream_status *status;
- int i, inst;
-
- /* Notify device removals. */
- for_each_oldnew_connector_in_state(state, connector, old_con_state, new_con_state, i) {
- if (old_con_state->crtc != new_con_state->crtc) {
- /* CRTC changes require notification. */
- goto notify;
- }
-
- if (!new_con_state->crtc)
- continue;
-
- new_crtc_state = drm_atomic_get_new_crtc_state(
- state, new_con_state->crtc);
-
- if (!new_crtc_state)
- continue;
-
- if (!drm_atomic_crtc_needs_modeset(new_crtc_state))
- continue;
-
- notify:
- if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
- continue;
-
- aconnector = to_amdgpu_dm_connector(connector);
-
- mutex_lock(&adev->dm.audio_lock);
- inst = aconnector->audio_inst;
- aconnector->audio_inst = -1;
- mutex_unlock(&adev->dm.audio_lock);
-
- amdgpu_dm_audio_eld_notify(adev, inst);
- }
-
- /* Notify audio device additions. */
- for_each_new_connector_in_state(state, connector, new_con_state, i) {
- if (!new_con_state->crtc)
- continue;
-
- new_crtc_state = drm_atomic_get_new_crtc_state(
- state, new_con_state->crtc);
-
- if (!new_crtc_state)
- continue;
-
- if (!drm_atomic_crtc_needs_modeset(new_crtc_state))
- continue;
-
- new_dm_crtc_state = to_dm_crtc_state(new_crtc_state);
- if (!new_dm_crtc_state->stream)
- continue;
-
- status = dc_stream_get_status(new_dm_crtc_state->stream);
- if (!status)
- continue;
-
- if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
- continue;
-
- aconnector = to_amdgpu_dm_connector(connector);
-
- mutex_lock(&adev->dm.audio_lock);
- inst = status->audio_inst;
- aconnector->audio_inst = inst;
- mutex_unlock(&adev->dm.audio_lock);
-
- amdgpu_dm_audio_eld_notify(adev, inst);
- }
- }
-
/*
* amdgpu_dm_crtc_copy_transient_flags - copy mirrored flags from DRM to DC
* @crtc_state: the DRM CRTC state
@@@ -10859,10 -4648,55 +4648,55 @@@ static void amdgpu_dm_crtc_copy_transie
stream_state->mode_changed = drm_atomic_crtc_needs_modeset(crtc_state);
}
+ /**
+ * amdgpu_dm_crtc_complete_writeback - finish a pending writeback job
+ * @acrtc: the CRTC whose pending writeback should be completed
+ *
+ * Clears the pending state, signals the writeback out fence and releases the
+ * vblank reference taken in dm_set_writeback() while the writeback was armed.
+ * The pending flag is tested and cleared under the writeback job lock, so this
+ * is safe to call concurrently from the completion vblank IRQ
+ * (dm_crtc_high_irq()) and from the writeback teardown path
+ * (dm_clear_writeback()); only the caller that observes the pending job
+ * performs the completion.
+ *
+ * Return: true if a pending writeback job was completed by this call.
+ */
+ bool amdgpu_dm_crtc_complete_writeback(struct amdgpu_crtc *acrtc)
+ {
+ unsigned long flags;
+ bool pending;
+
+ if (!acrtc->wb_conn)
+ return false;
+
+ spin_lock_irqsave(&acrtc->wb_conn->job_lock, flags);
+ pending = acrtc->wb_pending;
+ acrtc->wb_pending = false;
+ spin_unlock_irqrestore(&acrtc->wb_conn->job_lock, flags);
+
+ if (!pending)
+ return false;
+
+ drm_writeback_signal_completion(acrtc->wb_conn, 0);
+ drm_crtc_vblank_put(&acrtc->base);
+
+ return true;
+ }
+
static void dm_clear_writeback(struct amdgpu_display_manager *dm,
+ struct amdgpu_crtc *acrtc,
struct dm_crtc_state *crtc_state)
{
dc_stream_remove_writeback(dm->dc, crtc_state->stream, 0);
+
+ /*
+ * If the writeback is still pending when it is torn down (its
+ * completion vblank IRQ never fired), signal the out fence so a
+ * waiting client does not stall and release the vblank reference
+ * taken in dm_set_writeback().
+ */
+ amdgpu_dm_crtc_complete_writeback(acrtc);
}
/**
@@@ -11015,7 -4849,7 +4849,7 @@@ static void amdgpu_dm_commit_streams(st
dm_old_crtc_state = to_dm_crtc_state(old_crtc_state);
- dm_clear_writeback(dm, dm_old_crtc_state);
+ dm_clear_writeback(dm, acrtc, dm_old_crtc_state);
acrtc->wb_enabled = false;
}
@@@ -11289,9 -5123,24 +5123,24 @@@ static void dm_set_writeback(struct amd
dc_stream_add_writeback(dm->dc, crtc_state->stream, wb_info);
- acrtc->wb_pending = true;
acrtc->wb_conn = wb_conn;
drm_writeback_queue_job(wb_conn, new_con_state);
+
+ /*
+ * Writeback completion is detected in the CRTC vblank IRQ
+ * (dm_crtc_high_irq()). Take a vblank reference so the vblank interrupt
+ * stays enabled while the writeback is pending; otherwise a
+ * writeback-only commit right after drm_crtc_vblank_on() (e.g.
+ * re-enabling a CRTC that was disabled) has no other vblank reference,
+ * the IRQ never fires and the out fence times out. The matching put
+ * happens once completion is signalled in dm_crtc_high_irq(), or when
+ * the writeback is torn down in dm_clear_writeback().
+ *
+ * Arm wb_pending only after the reference is held so the completion IRQ
+ * cannot run its matching vblank_put before this get.
+ */
+ WARN_ON(drm_crtc_vblank_get(&acrtc->base));
+ acrtc->wb_pending = true;
}
static void amdgpu_dm_update_hdcp(struct drm_atomic_commit *state)
@@@ -11442,6 -5291,74 +5291,74 @@@ static int amdgpu_dm_atomic_setup_commi
return 0;
}
+ STATIC_IFN_KUNIT void set_multisync_trigger_params(
+ struct dc_stream_state *stream)
+ {
+ struct dc_stream_state *master = NULL;
+
+ if (stream->triggered_crtc_reset.enabled) {
+ master = stream->triggered_crtc_reset.event_source;
+ stream->triggered_crtc_reset.event =
+ master->timing.flags.VSYNC_POSITIVE_POLARITY ?
+ CRTC_EVENT_VSYNC_RISING : CRTC_EVENT_VSYNC_FALLING;
+ stream->triggered_crtc_reset.delay = TRIGGER_DELAY_NEXT_PIXEL;
+ }
+ }
+ EXPORT_IF_KUNIT(set_multisync_trigger_params);
+
+ STATIC_IFN_KUNIT void set_master_stream(struct dc_stream_state *stream_set[],
+ int stream_count)
+ {
+ int j, highest_rfr = 0, master_stream = 0;
+
+ for (j = 0; j < stream_count; j++) {
+ if (stream_set[j] && stream_set[j]->triggered_crtc_reset.enabled) {
+ int refresh_rate = 0;
+
+ refresh_rate = (stream_set[j]->timing.pix_clk_100hz*100)/
+ (stream_set[j]->timing.h_total*stream_set[j]->timing.v_total);
+ if (refresh_rate > highest_rfr) {
+ highest_rfr = refresh_rate;
+ master_stream = j;
+ }
+ }
+ }
+ for (j = 0; j < stream_count; j++) {
+ if (stream_set[j])
+ stream_set[j]->triggered_crtc_reset.event_source = stream_set[master_stream];
+ }
+ }
+ EXPORT_IF_KUNIT(set_master_stream);
+
+ static void dm_enable_per_frame_crtc_master_sync(struct dc_state *context)
+ {
+ int i = 0;
+ struct dc_stream_state *stream;
+
+ if (context->stream_count < 2)
+ return;
+ for (i = 0; i < context->stream_count ; i++) {
+ if (!context->streams[i])
+ continue;
+ /*
+ * TODO: add a function to read AMD VSDB bits and set
+ * crtc_sync_master.multi_sync_enabled flag
+ * For now it's set to false
+ */
+ }
+
+ set_master_stream(context->streams, context->stream_count);
+
+ for (i = 0; i < context->stream_count ; i++) {
+ stream = context->streams[i];
+
+ if (!stream)
+ continue;
+
+ set_multisync_trigger_params(stream);
+ }
+ }
+
/**
* amdgpu_dm_atomic_commit_tail() - AMDgpu DM's commit tail implementation.
* @state: The atomic state to commit
@@@ -11510,7 -5427,7 +5427,7 @@@ static void amdgpu_dm_atomic_commit_tai
if ((new_con_state->hdmi.broadcast_rgb != old_con_state->hdmi.broadcast_rgb) &&
(dm_old_crtc_state->stream->output_color_space !=
- get_output_color_space(&dm_new_crtc_state->stream->timing, new_con_state)))
+ amdgpu_dm_get_output_color_space(&dm_new_crtc_state->stream->timing, new_con_state)))
output_color_space_changed = true;
abm_changed = dm_new_crtc_state->abm_level !=
@@@ -11524,7 -5441,7 +5441,7 @@@
stream_update.stream = dm_new_crtc_state->stream;
if (scaling_changed) {
- update_stream_scaling_settings(dev, &dm_new_con_state->base.crtc->mode,
+ amdgpu_dm_update_stream_scaling_settings(dev, &dm_new_con_state->base.crtc->mode,
dm_new_con_state, dm_new_crtc_state->stream);
stream_update.src = dm_new_crtc_state->stream->src;
@@@ -11533,7 -5450,7 +5450,7 @@@
if (output_color_space_changed) {
dm_new_crtc_state->stream->output_color_space
- = get_output_color_space(&dm_new_crtc_state->stream->timing, new_con_state);
+ = amdgpu_dm_get_output_color_space(&dm_new_crtc_state->stream->timing, new_con_state);
stream_update.output_color_space = &dm_new_crtc_state->stream->output_color_space;
}
@@@ -11545,7 -5462,7 +5462,7 @@@
}
if (hdr_changed) {
- fill_hdr_info_packet(new_con_state, &hdr_packet);
+ amdgpu_dm_fill_hdr_info_packet(new_con_state, &hdr_packet);
stream_update.hdr_static_metadata = &hdr_packet;
}
@@@ -11753,104 -5670,6 +5670,6 @@@
trace_amdgpu_dm_atomic_commit_tail_finish(state);
}
- static int dm_force_atomic_commit(struct drm_connector *connector)
- {
- int ret = 0;
- struct drm_device *ddev = connector->dev;
- struct drm_atomic_commit *state = drm_atomic_commit_alloc(ddev);
- struct amdgpu_crtc *disconnected_acrtc = to_amdgpu_crtc(connector->encoder->crtc);
- struct drm_plane *plane = disconnected_acrtc->base.primary;
- struct drm_connector_state *conn_state;
- struct drm_crtc_state *crtc_state;
- struct drm_plane_state *plane_state;
-
- if (!state)
- return -ENOMEM;
-
- state->acquire_ctx = ddev->mode_config.acquire_ctx;
-
- /* Construct an atomic state to restore previous display setting */
-
- /*
- * Attach connectors to drm_atomic_commit
- */
- conn_state = drm_atomic_get_connector_state(state, connector);
-
- /* Check for error in getting connector state */
- if (IS_ERR(conn_state)) {
- ret = PTR_ERR(conn_state);
- goto out;
- }
-
- /* Attach crtc to drm_atomic_commit*/
- crtc_state = drm_atomic_get_crtc_state(state, &disconnected_acrtc->base);
-
- /* Check for error in getting crtc state */
- if (IS_ERR(crtc_state)) {
- ret = PTR_ERR(crtc_state);
- goto out;
- }
-
- /* force a restore */
- crtc_state->mode_changed = true;
-
- /* Attach plane to drm_atomic_commit */
- plane_state = drm_atomic_get_plane_state(state, plane);
-
- /* Check for error in getting plane state */
- if (IS_ERR(plane_state)) {
- ret = PTR_ERR(plane_state);
- goto out;
- }
-
- /* Call commit internally with the state we just constructed */
- ret = drm_atomic_commit(state);
-
- out:
- drm_atomic_commit_put(state);
- if (ret)
- drm_err(ddev, "Restoring old state failed with %i\n", ret);
-
- return ret;
- }
-
- /*
- * This function handles all cases when set mode does not come upon hotplug.
- * This includes when a display is unplugged then plugged back into the
- * same port and when running without usermode desktop manager supprot
- */
- void dm_restore_drm_connector_state(struct drm_device *dev,
- struct drm_connector *connector)
- {
- struct amdgpu_dm_connector *aconnector;
- struct amdgpu_crtc *disconnected_acrtc;
- struct dm_crtc_state *acrtc_state;
-
- if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
- return;
-
- aconnector = to_amdgpu_dm_connector(connector);
-
- if (!aconnector->dc_sink || !connector->state || !connector->encoder)
- return;
-
- disconnected_acrtc = to_amdgpu_crtc(connector->encoder->crtc);
- if (!disconnected_acrtc)
- return;
-
- acrtc_state = to_dm_crtc_state(disconnected_acrtc->base.state);
- if (!acrtc_state->stream)
- return;
-
- /*
- * If the previous sink is not released and different from the current,
- * we deduce we are in a state where we can not rely on usermode call
- * to turn on the display, so we do it here
- */
- if (acrtc_state->stream->sink != aconnector->dc_sink)
- dm_force_atomic_commit(&aconnector->base);
- }
-
/*
* Grabs all modesetting locks to serialize against any blocking commits,
* Waits for completion of all non blocking commits.
@@@ -11955,7 -5774,7 +5774,7 @@@ static void reset_freesync_config_for_c
sizeof(new_crtc_state->vrr_infopacket));
}
- static bool
+ STATIC_IFN_KUNIT bool
is_timing_unchanged_for_freesync(struct drm_crtc_state *old_crtc_state,
struct drm_crtc_state *new_crtc_state)
{
@@@ -11984,8 -5803,9 +5803,9 @@@
return false;
}
+ EXPORT_IF_KUNIT(is_timing_unchanged_for_freesync);
- static void set_freesync_fixed_config(struct dm_crtc_state *dm_new_crtc_state)
+ STATIC_IFN_KUNIT void set_freesync_fixed_config(struct dm_crtc_state *dm_new_crtc_state)
{
u64 num, den, res;
struct drm_crtc_state *new_crtc_state = &dm_new_crtc_state->base;
@@@ -11999,6 -5819,7 +5819,7 @@@
res = div_u64(num, den);
dm_new_crtc_state->freesync_config.fixed_refresh_in_uhz = res;
}
+ EXPORT_IF_KUNIT(set_freesync_fixed_config);
static int dm_update_crtc_state(struct amdgpu_display_manager *dm,
struct drm_atomic_commit *state,
@@@ -12052,7 -5873,7 +5873,7 @@@
if (!drm_atomic_crtc_needs_modeset(new_crtc_state))
goto skip_modeset;
- new_stream = create_validate_stream_for_sink(connector,
+ new_stream = amdgpu_dm_create_validate_stream_for_sink(connector,
&new_crtc_state->mode,
dm_new_conn_state,
dm_old_crtc_state->stream);
@@@ -12080,7 -5901,7 +5901,7 @@@
dm_new_crtc_state->abm_level = dm_new_conn_state->abm_level;
- ret = fill_hdr_info_packet(drm_new_conn_state,
+ ret = amdgpu_dm_fill_hdr_info_packet(drm_new_conn_state,
&new_stream->hdr_static_metadata);
if (ret)
goto fail;
@@@ -12149,11 -5970,11 +5970,11 @@@
goto skip_modeset;
} else if (amdgpu_freesync_vid_mode && aconnector &&
- is_freesync_video_mode(&new_crtc_state->mode,
+ amdgpu_dm_is_freesync_video_mode(&new_crtc_state->mode,
aconnector)) {
struct drm_display_mode *high_mode;
- high_mode = get_highest_refresh_rate_mode(aconnector, false);
+ high_mode = amdgpu_dm_get_highest_refresh_rate_mode(aconnector, false);
if (!drm_mode_equal(&new_crtc_state->mode, high_mode))
set_freesync_fixed_config(dm_new_crtc_state);
}
@@@ -12246,7 -6067,7 +6067,7 @@@ skip_modeset
/* Scaling or underscan settings */
if (is_scaling_state_different(dm_old_conn_state, dm_new_conn_state) ||
drm_atomic_crtc_needs_modeset(new_crtc_state))
- update_stream_scaling_settings(adev_to_drm(adev),
+ amdgpu_dm_update_stream_scaling_settings(adev_to_drm(adev),
&new_crtc_state->mode, dm_new_conn_state, dm_new_crtc_state->stream);
/* ABM settings */
@@@ -12342,10 -6163,6 +6163,6 @@@ static bool should_reset_plane(struct d
if (new_crtc_state->color_mgmt_changed)
return true;
- /* Plane color pipeline or its colorop changes. */
- if (new_plane_state->color_mgmt_changed)
- return true;
-
/*
* On zpos change, planes need to be reordered by removing and re-adding
* them one by one to the dc state, in order of descending zpos.
@@@ -12720,7 -6537,7 +6537,7 @@@ static int dm_update_plane_state(struc
/* Tell DC to do a full surface update every time there
* is a plane change. Inefficient, but works for now.
*/
- dm_new_plane_state->dc_state->update_flags.bits.full_update = 1;
+ dm_new_plane_state->dc_state->update_bits.full_update = 1;
*lock_and_validation_needed = true;
}
@@@ -12739,8 -6556,8 +6556,8 @@@ out
return ret;
}
- static void dm_get_oriented_plane_size(struct drm_plane_state *plane_state,
- int *src_w, int *src_h)
+ STATIC_IFN_KUNIT void dm_get_oriented_plane_size(struct drm_plane_state *plane_state,
+ int *src_w, int *src_h)
{
switch (plane_state->rotation & DRM_MODE_ROTATE_MASK) {
case DRM_MODE_ROTATE_90:
@@@ -12756,8 -6573,9 +6573,9 @@@
break;
}
}
+ EXPORT_IF_KUNIT(dm_get_oriented_plane_size);
- static void
+ STATIC_IFN_KUNIT void
dm_get_plane_scale(struct drm_plane_state *plane_state,
int *out_plane_scale_w, int *out_plane_scale_h)
{
@@@ -12767,6 -6585,7 +6585,7 @@@
*out_plane_scale_w = plane_src_w ? plane_state->crtc_w * 1000 / plane_src_w : 0;
*out_plane_scale_h = plane_src_h ? plane_state->crtc_h * 1000 / plane_src_h : 0;
}
+ EXPORT_IF_KUNIT(dm_get_plane_scale);
/*
* The normalized_zpos value cannot be used by this iterator directly. It's only
@@@ -12986,6 -6805,31 +6805,31 @@@ static int dm_crtc_get_cursor_mode(stru
break;
}
+ /*
+ * A non-cursor plane moving or resizing (without a scale change)
+ * changes how much of the CRTC it covers. This can create or
+ * remove a hole under the cursor and thus flip the required
+ * cursor mode (native vs overlay), so its destination rect must
+ * be re-evaluated too.
+ *
+ * The cursor plane itself is deliberately excluded: the cursor
+ * mode depends on the underlying planes' coverage, not on the
+ * cursor's position (see the entire_crtc_covered logic below).
+ * Triggering on cursor movement would force every legacy cursor
+ * update off its fast path, and in a cursor-only commit - where
+ * the underlying planes are not part of the state - the coverage
+ * loop would see no covering plane and misevaluate the mode as
+ * overlay, regressing flip-vs-cursor-legacy.
+ */
+ if (plane->type != DRM_PLANE_TYPE_CURSOR &&
+ (old_plane_state->crtc_x != plane_state->crtc_x ||
+ old_plane_state->crtc_y != plane_state->crtc_y ||
+ old_plane_state->crtc_w != plane_state->crtc_w ||
+ old_plane_state->crtc_h != plane_state->crtc_h)) {
+ consider_mode_change = true;
+ break;
+ }
+
if (dm_plane_color_pipeline_active(state, plane, true) !=
dm_plane_color_pipeline_active(state, plane, false)) {
consider_mode_change = true;
@@@ -13141,7 -6985,7 +6985,7 @@@ static int amdgpu_dm_atomic_check(struc
ret = drm_atomic_helper_check_modeset(dev, state);
if (ret) {
- drm_dbg_atomic(dev, "drm_atomic_helper_check_modeset() failed\n");
+ drm_dbg_atomic(dev, "drm_atomic_helper_check_modeset() failed: %pe\n", ERR_PTR(ret));
goto fail;
}
@@@ -13156,7 -7000,7 +7000,7 @@@
new_crtc_state = drm_atomic_get_crtc_state(state, new_con_state->crtc);
if (IS_ERR(new_crtc_state)) {
- drm_dbg_atomic(dev, "drm_atomic_get_crtc_state() failed\n");
+ drm_dbg_atomic(dev, "drm_atomic_get_crtc_state() failed: %pe\n", new_crtc_state);
ret = PTR_ERR(new_crtc_state);
goto fail;
}
@@@ -13176,7 -7020,7 +7020,7 @@@
if (drm_atomic_crtc_needs_modeset(new_crtc_state)) {
ret = add_affected_mst_dsc_crtcs(state, crtc);
if (ret) {
- drm_dbg_atomic(dev, "add_affected_mst_dsc_crtcs() failed\n");
+ drm_dbg_atomic(dev, "add_affected_mst_dsc_crtcs() failed: %pe\n", ERR_PTR(ret));
goto fail;
}
}
@@@ -13193,7 -7037,7 +7037,7 @@@
ret = amdgpu_dm_verify_lut_sizes(new_crtc_state);
if (ret) {
- drm_dbg_atomic(dev, "amdgpu_dm_verify_lut_sizes() failed\n");
+ drm_dbg_atomic(dev, "amdgpu_dm_verify_lut_sizes() failed: %pe\n", ERR_PTR(ret));
goto fail;
}
@@@ -13202,13 -7046,13 +7046,13 @@@
ret = drm_atomic_add_affected_connectors(state, crtc);
if (ret) {
- drm_dbg_atomic(dev, "drm_atomic_add_affected_connectors() failed\n");
+ drm_dbg_atomic(dev, "drm_atomic_add_affected_connectors() failed: %pe\n", ERR_PTR(ret));
goto fail;
}
ret = drm_atomic_add_affected_planes(state, crtc);
if (ret) {
- drm_dbg_atomic(dev, "drm_atomic_add_affected_planes() failed\n");
+ drm_dbg_atomic(dev, "drm_atomic_add_affected_planes() failed: %pe\n", ERR_PTR(ret));
goto fail;
}
@@@ -13247,7 -7091,7 +7091,7 @@@
if (IS_ERR(new_plane_state)) {
ret = PTR_ERR(new_plane_state);
- drm_dbg_atomic(dev, "new_plane_state is BAD\n");
+ drm_dbg_atomic(dev, "new_plane_state is BAD: %pe\n", new_plane_state);
goto fail;
}
}
@@@ -13261,7 -7105,7 +7105,7 @@@
*/
ret = drm_atomic_normalize_zpos(dev, state);
if (ret) {
- drm_dbg(dev, "drm_atomic_normalize_zpos() failed\n");
+ drm_dbg(dev, "drm_atomic_normalize_zpos() failed: %pe\n", ERR_PTR(ret));
goto fail;
}
@@@ -13275,7 -7119,7 +7119,7 @@@
ret = dm_crtc_get_cursor_mode(adev, state, dm_new_crtc_state,
&dm_new_crtc_state->cursor_mode);
if (ret) {
- drm_dbg(dev, "Failed to determine cursor mode\n");
+ drm_dbg(dev, "Failed to determine cursor mode: %pe\n", ERR_PTR(ret));
goto fail;
}
@@@ -13307,7 -7151,7 +7151,7 @@@
&lock_and_validation_needed,
&is_top_most_overlay);
if (ret) {
- drm_dbg_atomic(dev, "dm_update_plane_state() failed\n");
+ drm_dbg_atomic(dev, "dm_update_plane_state() failed: %pe\n", ERR_PTR(ret));
goto fail;
}
}
@@@ -13320,7 -7164,7 +7164,7 @@@
false,
&lock_and_validation_needed);
if (ret) {
- drm_dbg_atomic(dev, "DISABLE: dm_update_crtc_state() failed\n");
+ drm_dbg_atomic(dev, "DISABLE: dm_update_crtc_state() failed: %pe\n", ERR_PTR(ret));
goto fail;
}
}
@@@ -13333,7 -7177,7 +7177,7 @@@
true,
&lock_and_validation_needed);
if (ret) {
- drm_dbg_atomic(dev, "ENABLE: dm_update_crtc_state() failed\n");
+ drm_dbg_atomic(dev, "ENABLE: dm_update_crtc_state() failed: %pe\n", ERR_PTR(ret));
goto fail;
}
}
@@@ -13347,7 -7191,7 +7191,7 @@@
&lock_and_validation_needed,
&is_top_most_overlay);
if (ret) {
- drm_dbg_atomic(dev, "dm_update_plane_state() failed\n");
+ drm_dbg_atomic(dev, "dm_update_plane_state() failed: %pe\n", ERR_PTR(ret));
goto fail;
}
}
@@@ -13363,7 -7207,7 +7207,7 @@@
/* Run this here since we want to validate the streams we created */
ret = drm_atomic_helper_check_planes(dev, state);
if (ret) {
- drm_dbg_atomic(dev, "drm_atomic_helper_check_planes() failed\n");
+ drm_dbg_atomic(dev, "drm_atomic_helper_check_planes() failed: %pe\n", ERR_PTR(ret));
goto fail;
}
@@@ -13501,13 -7345,13 +7345,13 @@@
if (lock_and_validation_needed) {
ret = dm_atomic_get_state(state, &dm_state);
if (ret) {
- drm_dbg_atomic(dev, "dm_atomic_get_state() failed\n");
+ drm_dbg_atomic(dev, "dm_atomic_get_state() failed: %pe\n", ERR_PTR(ret));
goto fail;
}
ret = do_aquire_global_lock(dev, state);
if (ret) {
- drm_dbg_atomic(dev, "do_aquire_global_lock() failed\n");
+ drm_dbg_atomic(dev, "do_aquire_global_lock() failed: %pe\n", ERR_PTR(ret));
goto fail;
}
@@@ -13515,7 -7359,7 +7359,7 @@@
if (dc_resource_is_dsc_encoding_supported(dc)) {
ret = compute_mst_dsc_configs_for_state(state, dm_state->context, vars);
if (ret) {
- drm_dbg_atomic(dev, "MST_DSC compute_mst_dsc_configs_for_state() failed\n");
+ drm_dbg_atomic(dev, "MST_DSC compute_mst_dsc_configs_for_state() failed: %pe\n", ERR_PTR(ret));
ret = -EINVAL;
goto fail;
}
@@@ -13524,7 -7368,7 +7368,7 @@@
ret = dm_update_mst_vcpi_slots_for_dsc(state, dm_state->context, vars);
if (ret) {
- drm_dbg_atomic(dev, "dm_update_mst_vcpi_slots_for_dsc() failed\n");
+ drm_dbg_atomic(dev, "dm_update_mst_vcpi_slots_for_dsc() failed: %pe\n", ERR_PTR(ret));
goto fail;
}
@@@ -13536,7 -7380,7 +7380,7 @@@
*/
ret = drm_dp_mst_atomic_check(state);
if (ret) {
- drm_dbg_atomic(dev, "MST drm_dp_mst_atomic_check() failed\n");
+ drm_dbg_atomic(dev, "MST drm_dp_mst_atomic_check() failed: %pe\n", ERR_PTR(ret));
goto fail;
}
status = dc_validate_global_state(dc, dm_state->context, DC_VALIDATE_MODE_ONLY);
@@@ -13625,380 -7469,13 +7469,13 @@@ fail
else if (ret == -EINTR || ret == -EAGAIN || ret == -ERESTARTSYS)
drm_dbg_atomic(dev, "Atomic check stopped due to signal.\n");
else
- drm_dbg_atomic(dev, "Atomic check failed with err: %d\n", ret);
+ drm_dbg_atomic(dev, "Atomic check failed: %pe\n", ERR_PTR(ret));
trace_amdgpu_dm_atomic_check_finish(state, ret);
return ret;
}
- static bool dm_edid_parser_send_cea(struct amdgpu_display_manager *dm,
- unsigned int offset,
- unsigned int total_length,
- u8 *data,
- unsigned int length,
- struct amdgpu_hdmi_vsdb_info *vsdb)
- {
- bool res;
- union dmub_rb_cmd cmd;
- struct dmub_cmd_send_edid_cea *input;
- struct dmub_cmd_edid_cea_output *output;
-
- if (length > DMUB_EDID_CEA_DATA_CHUNK_BYTES)
- return false;
-
- memset(&cmd, 0, sizeof(cmd));
-
- input = &cmd.edid_cea.data.input;
-
- cmd.edid_cea.header.type = DMUB_CMD__EDID_CEA;
- cmd.edid_cea.header.sub_type = 0;
- cmd.edid_cea.header.payload_bytes =
- sizeof(cmd.edid_cea) - sizeof(cmd.edid_cea.header);
- input->offset = offset;
- input->length = length;
- input->cea_total_length = total_length;
- memcpy(input->payload, data, length);
-
- res = dc_wake_and_execute_dmub_cmd(dm->dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT_WITH_REPLY);
- if (!res) {
- drm_err(adev_to_drm(dm->adev), "EDID CEA parser failed\n");
- return false;
- }
-
- output = &cmd.edid_cea.data.output;
-
- if (output->type == DMUB_CMD__EDID_CEA_ACK) {
- if (!output->ack.success) {
- drm_err(adev_to_drm(dm->adev), "EDID CEA ack failed at offset %d\n",
- output->ack.offset);
- }
- } else if (output->type == DMUB_CMD__EDID_CEA_AMD_VSDB) {
- if (!output->amd_vsdb.vsdb_found)
- return false;
-
- vsdb->freesync_supported = output->amd_vsdb.freesync_supported;
- vsdb->amd_vsdb_version = output->amd_vsdb.amd_vsdb_version;
- vsdb->min_refresh_rate_hz = output->amd_vsdb.min_frame_rate;
- vsdb->max_refresh_rate_hz = output->amd_vsdb.max_frame_rate;
- vsdb->freesync_mccs_vcp_code = output->amd_vsdb.freesync_mccs_vcp_code;
- } else {
- drm_warn(adev_to_drm(dm->adev), "Unknown EDID CEA parser results\n");
- return false;
- }
-
- return true;
- }
-
- static bool parse_edid_cea_dmcu(struct amdgpu_display_manager *dm,
- u8 *edid_ext, int len,
- struct amdgpu_hdmi_vsdb_info *vsdb_info)
- {
- int i;
-
- /* send extension block to DMCU for parsing */
- for (i = 0; i < len; i += 8) {
- bool res;
- int offset;
-
- /* send 8 bytes a time */
- if (!dc_edid_parser_send_cea(dm->dc, i, len, &edid_ext[i], 8))
- return false;
-
- if (i+8 == len) {
- /* EDID block sent completed, expect result */
- int version, min_rate, max_rate;
-
- res = dc_edid_parser_recv_amd_vsdb(dm->dc, &version, &min_rate, &max_rate);
- if (res) {
- /* amd vsdb found */
- vsdb_info->freesync_supported = 1;
- vsdb_info->amd_vsdb_version = version;
- vsdb_info->min_refresh_rate_hz = min_rate;
- vsdb_info->max_refresh_rate_hz = max_rate;
- /* Not enabled on DMCU*/
- vsdb_info->freesync_mccs_vcp_code = 0;
- return true;
- }
- /* not amd vsdb */
- return false;
- }
-
- /* check for ack*/
- res = dc_edid_parser_recv_cea_ack(dm->dc, &offset);
- if (!res)
- return false;
- }
-
- return false;
- }
-
- static bool parse_edid_cea_dmub(struct amdgpu_display_manager *dm,
- u8 *edid_ext, int len,
- struct amdgpu_hdmi_vsdb_info *vsdb_info)
- {
- int i;
-
- /* send extension block to DMCU for parsing */
- for (i = 0; i < len; i += 8) {
- /* send 8 bytes a time */
- if (!dm_edid_parser_send_cea(dm, i, len, &edid_ext[i], 8, vsdb_info))
- return false;
- }
-
- return vsdb_info->freesync_supported;
- }
-
- static bool parse_edid_cea(struct amdgpu_dm_connector *aconnector,
- u8 *edid_ext, int len,
- struct amdgpu_hdmi_vsdb_info *vsdb_info)
- {
- struct amdgpu_device *adev = drm_to_adev(aconnector->base.dev);
- bool ret;
-
- mutex_lock(&adev->dm.dc_lock);
- if (adev->dm.dmub_srv)
- ret = parse_edid_cea_dmub(&adev->dm, edid_ext, len, vsdb_info);
- else
- ret = parse_edid_cea_dmcu(&adev->dm, edid_ext, len, vsdb_info);
- mutex_unlock(&adev->dm.dc_lock);
- return ret;
- }
-
- static void parse_edid_displayid_vrr(struct drm_connector *connector,
- const struct edid *edid)
- {
- u8 *edid_ext = NULL;
- int i;
- int j = 0;
- u16 min_vfreq;
- u16 max_vfreq;
-
- if (!edid || !edid->extensions)
- return;
-
- /* Find DisplayID extension */
- for (i = 0; i < edid->extensions; i++) {
- edid_ext = (void *)(edid + (i + 1));
- if (edid_ext[0] == DISPLAYID_EXT)
- break;
- }
-
- if (i == edid->extensions)
- return;
-
- while (j < EDID_LENGTH) {
- /* Get dynamic video timing range from DisplayID if available */
- if (EDID_LENGTH - j > 13 && edid_ext[j] == 0x25 &&
- (edid_ext[j+1] & 0xFE) == 0 && (edid_ext[j+2] == 9)) {
- min_vfreq = edid_ext[j+9];
- if (edid_ext[j+1] & 7)
- max_vfreq = edid_ext[j+10] + ((edid_ext[j+11] & 3) << 8);
- else
- max_vfreq = edid_ext[j+10];
-
- if (max_vfreq && min_vfreq) {
- connector->display_info.monitor_range.max_vfreq = max_vfreq;
- connector->display_info.monitor_range.min_vfreq = min_vfreq;
-
- return;
- }
- }
- j++;
- }
- }
-
- static int get_amd_vsdb(struct amdgpu_dm_connector *aconnector,
- struct amdgpu_hdmi_vsdb_info *vsdb_info)
- {
- struct drm_connector *connector = &aconnector->base;
-
- vsdb_info->replay_mode = connector->display_info.amd_vsdb.replay_mode;
- vsdb_info->amd_vsdb_version = connector->display_info.amd_vsdb.version;
-
- return connector->display_info.amd_vsdb.version != 0;
- }
-
- static int parse_hdmi_amd_vsdb(struct amdgpu_dm_connector *aconnector,
- const struct edid *edid,
- struct amdgpu_hdmi_vsdb_info *vsdb_info)
- {
- u8 *edid_ext = NULL;
- int i;
- bool valid_vsdb_found = false;
-
- /*----- drm_find_cea_extension() -----*/
- /* No EDID or EDID extensions */
- if (edid == NULL || edid->extensions == 0)
- return -ENODEV;
-
- /* Find CEA extension */
- for (i = 0; i < edid->extensions; i++) {
- edid_ext = (uint8_t *)edid + EDID_LENGTH * (i + 1);
- if (edid_ext[0] == CEA_EXT)
- break;
- }
-
- if (i == edid->extensions)
- return -ENODEV;
-
- /*----- cea_db_offsets() -----*/
- if (edid_ext[0] != CEA_EXT)
- return -ENODEV;
-
- valid_vsdb_found = parse_edid_cea(aconnector, edid_ext, EDID_LENGTH, vsdb_info);
-
- return valid_vsdb_found ? i : -ENODEV;
- }
-
- /**
- * amdgpu_dm_update_freesync_caps - Update Freesync capabilities
- *
- * @connector: Connector to query.
- * @drm_edid: DRM EDID from monitor
- * @do_mccs: Controls whether MCCS (Monitor Control Command Set) over
- * DDC (Display Data Channel) transactions are performed. When true,
- * the driver queries the monitor to get or update additional FreeSync
- * capability information. When false, these transactions are skipped.
- *
- * Amdgpu supports Freesync in DP and HDMI displays, and it is required to keep
- * track of some of the display information in the internal data struct used by
- * amdgpu_dm. This function checks which type of connector we need to set the
- * FreeSync parameters.
- */
- void amdgpu_dm_update_freesync_caps(struct drm_connector *connector,
- const struct drm_edid *drm_edid, bool do_mccs)
- {
- int i = 0;
- struct amdgpu_dm_connector *amdgpu_dm_connector =
- to_amdgpu_dm_connector(connector);
- struct dm_connector_state *dm_con_state = NULL;
- struct dc_sink *sink;
- struct amdgpu_device *adev = drm_to_adev(connector->dev);
- struct amdgpu_hdmi_vsdb_info vsdb_info = {0};
- const struct edid *edid;
- bool freesync_capable = false;
- enum adaptive_sync_type as_type = ADAPTIVE_SYNC_TYPE_NONE;
-
- if (!connector->state) {
- drm_err(adev_to_drm(adev), "%s - Connector has no state", __func__);
- goto update;
- }
-
- sink = amdgpu_dm_connector->dc_sink ?
- amdgpu_dm_connector->dc_sink :
- amdgpu_dm_connector->dc_em_sink;
-
- drm_edid_connector_update(connector, drm_edid);
-
- if (!drm_edid || !sink) {
- dm_con_state = to_dm_connector_state(connector->state);
-
- amdgpu_dm_connector->min_vfreq = 0;
- amdgpu_dm_connector->max_vfreq = 0;
- freesync_capable = false;
-
- goto update;
- }
-
- dm_con_state = to_dm_connector_state(connector->state);
-
- if (!adev->dm.freesync_module || !dc_supports_vrr(sink->ctx->dce_version))
- goto update;
-
- edid = drm_edid_raw(drm_edid); // FIXME: Get rid of drm_edid_raw()
-
- /* Some eDP panels only have the refresh rate range info in DisplayID */
- if ((connector->display_info.monitor_range.min_vfreq == 0 ||
- connector->display_info.monitor_range.max_vfreq == 0))
- parse_edid_displayid_vrr(connector, edid);
-
- if (edid && (sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT ||
- sink->sink_signal == SIGNAL_TYPE_EDP)) {
- if (amdgpu_dm_connector->dc_link &&
- amdgpu_dm_connector->dc_link->dpcd_caps.allow_invalid_MSA_timing_param) {
- amdgpu_dm_connector->min_vfreq = connector->display_info.monitor_range.min_vfreq;
- amdgpu_dm_connector->max_vfreq = connector->display_info.monitor_range.max_vfreq;
- if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10)
- freesync_capable = true;
- }
-
- get_amd_vsdb(amdgpu_dm_connector, &vsdb_info);
-
- if (vsdb_info.replay_mode) {
- amdgpu_dm_connector->vsdb_info.replay_mode = vsdb_info.replay_mode;
- amdgpu_dm_connector->vsdb_info.amd_vsdb_version = vsdb_info.amd_vsdb_version;
- amdgpu_dm_connector->as_type = ADAPTIVE_SYNC_TYPE_EDP;
- }
-
- } else if (drm_edid && sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A) {
- i = parse_hdmi_amd_vsdb(amdgpu_dm_connector, edid, &vsdb_info);
- if (i >= 0) {
- amdgpu_dm_connector->vsdb_info = vsdb_info;
- sink->edid_caps.freesync_vcp_code = vsdb_info.freesync_mccs_vcp_code;
-
- if (vsdb_info.freesync_supported) {
- amdgpu_dm_connector->min_vfreq = vsdb_info.min_refresh_rate_hz;
- amdgpu_dm_connector->max_vfreq = vsdb_info.max_refresh_rate_hz;
- if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10)
- freesync_capable = true;
-
- connector->display_info.monitor_range.min_vfreq = vsdb_info.min_refresh_rate_hz;
- connector->display_info.monitor_range.max_vfreq = vsdb_info.max_refresh_rate_hz;
- }
- }
- }
-
- if (amdgpu_dm_connector->dc_link)
- as_type = dm_get_adaptive_sync_support_type(amdgpu_dm_connector->dc_link);
-
- if (as_type == FREESYNC_TYPE_PCON_IN_WHITELIST) {
- i = parse_hdmi_amd_vsdb(amdgpu_dm_connector, edid, &vsdb_info);
- if (i >= 0) {
- amdgpu_dm_connector->vsdb_info = vsdb_info;
- sink->edid_caps.freesync_vcp_code = vsdb_info.freesync_mccs_vcp_code;
-
- if (vsdb_info.freesync_supported && vsdb_info.amd_vsdb_version > 0) {
- amdgpu_dm_connector->pack_sdp_v1_3 = true;
- amdgpu_dm_connector->as_type = as_type;
-
- amdgpu_dm_connector->min_vfreq = vsdb_info.min_refresh_rate_hz;
- amdgpu_dm_connector->max_vfreq = vsdb_info.max_refresh_rate_hz;
- if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10)
- freesync_capable = true;
-
- connector->display_info.monitor_range.min_vfreq = vsdb_info.min_refresh_rate_hz;
- connector->display_info.monitor_range.max_vfreq = vsdb_info.max_refresh_rate_hz;
- }
- }
- }
-
- /* Handle MCCS */
- if (do_mccs) {
- dm_helpers_read_mccs_caps(adev->dm.dc->ctx, amdgpu_dm_connector->dc_link, sink);
-
- if (sink->edid_caps.freesync_vcp_code && !sink->mccs_caps.freesync_supported)
- freesync_capable = false;
-
- if (sink->mccs_caps.freesync_supported && freesync_capable)
- dm_helpers_mccs_vcp_set(adev->dm.dc->ctx, amdgpu_dm_connector->dc_link, sink);
- }
-
- update:
- if (dm_con_state)
- dm_con_state->freesync_capable = freesync_capable;
-
- if (connector->state && amdgpu_dm_connector->dc_link && !freesync_capable &&
- amdgpu_dm_connector->dc_link->replay_settings.config.replay_supported) {
- amdgpu_dm_connector->dc_link->replay_settings.config.replay_supported = false;
- amdgpu_dm_connector->dc_link->replay_settings.replay_feature_enabled = false;
- }
-
- if (connector->vrr_capable_property)
- drm_connector_set_vrr_capable_property(connector,
- freesync_capable);
- }
-
void amdgpu_dm_trigger_timing_sync(struct drm_device *dev)
{
struct amdgpu_device *adev = drm_to_adev(dev);
@@@ -14018,12 -7495,6 +7495,6 @@@
mutex_unlock(&adev->dm.dc_lock);
}
- static inline void amdgpu_dm_exit_ips_for_hw_access(struct dc *dc)
- {
- if (dc->ctx->dmub_srv && !dc->ctx->dmub_srv->idle_exit_counter)
- dc_exit_ips_for_hw_access(dc);
- }
-
void dm_write_reg_func(const struct dc_context *ctx, uint32_t address,
u32 value, const char *func_name)
{
@@@ -14052,13 -7523,6 +7523,6 @@@ uint32_t dm_read_reg_func(const struct
}
#endif
- if (ctx->dmub_srv &&
- ctx->dmub_srv->reg_helper_offload.gather_in_progress &&
- !ctx->dmub_srv->reg_helper_offload.should_burst_write) {
- ASSERT(false);
- return 0;
- }
-
amdgpu_dm_exit_ips_for_hw_access(ctx->dc);
value = cgs_read_register(ctx->cgs_device, address);
@@@ -14068,179 -7532,6 +7532,6 @@@
return value;
}
- int amdgpu_dm_process_dmub_aux_transfer_sync(
- struct dc_context *ctx,
- unsigned int link_index,
- struct aux_payload *payload,
- enum aux_return_code_type *operation_result)
- {
- struct amdgpu_device *adev = ctx->driver_context;
- struct dmub_notification *p_notify = adev->dm.dmub_notify;
- int ret = -1;
-
- mutex_lock(&adev->dm.dpia_aux_lock);
- if (!dc_process_dmub_aux_transfer_async(ctx->dc, link_index, payload)) {
- *operation_result = AUX_RET_ERROR_ENGINE_ACQUIRE;
- goto out;
- }
-
- if (!wait_for_completion_timeout(&adev->dm.dmub_aux_transfer_done, 10 * HZ)) {
- drm_err(adev_to_drm(adev), "wait_for_completion_timeout timeout!");
- *operation_result = AUX_RET_ERROR_TIMEOUT;
- goto out;
- }
-
- if (p_notify->result != AUX_RET_SUCCESS) {
- /*
- * Transient states before tunneling is enabled could
- * lead to this error. We can ignore this for now.
- */
- if (p_notify->result == AUX_RET_ERROR_PROTOCOL_ERROR) {
- drm_warn(adev_to_drm(adev), "DPIA AUX failed on 0x%x(%d), error %d\n",
- payload->address, payload->length,
- p_notify->result);
- }
- *operation_result = p_notify->result;
- goto out;
- }
-
- payload->reply[0] = adev->dm.dmub_notify->aux_reply.command & 0xF;
- if (adev->dm.dmub_notify->aux_reply.command & 0xF0)
- /* The reply is stored in the top nibble of the command. */
- payload->reply[0] = (adev->dm.dmub_notify->aux_reply.command >> 4) & 0xF;
-
- /*write req may receive a byte indicating partially written number as well*/
- if (p_notify->aux_reply.length)
- memcpy(payload->data, p_notify->aux_reply.data,
- p_notify->aux_reply.length);
-
- /* success */
- ret = p_notify->aux_reply.length;
- *operation_result = p_notify->result;
- out:
- reinit_completion(&adev->dm.dmub_aux_transfer_done);
- mutex_unlock(&adev->dm.dpia_aux_lock);
- return ret;
- }
-
- static void abort_fused_io(
- struct dc_context *ctx,
- const struct dmub_cmd_fused_request *request
- )
- {
- union dmub_rb_cmd command = { 0 };
- struct dmub_rb_cmd_fused_io *io = &command.fused_io;
-
- io->header.type = DMUB_CMD__FUSED_IO;
- io->header.sub_type = DMUB_CMD__FUSED_IO_ABORT;
- io->header.payload_bytes = sizeof(*io) - sizeof(io->header);
- io->request = *request;
- dm_execute_dmub_cmd(ctx, &command, DM_DMUB_WAIT_TYPE_NO_WAIT);
- }
-
- static bool execute_fused_io(
- struct amdgpu_device *dev,
- struct dc_context *ctx,
- union dmub_rb_cmd *commands,
- uint8_t count,
- uint32_t timeout_us
- )
- {
- const uint8_t ddc_line = commands[0].fused_io.request.u.aux.ddc_line;
-
- if (ddc_line >= ARRAY_SIZE(dev->dm.fused_io))
- return false;
-
- struct fused_io_sync *sync = &dev->dm.fused_io[ddc_line];
- struct dmub_rb_cmd_fused_io *first = &commands[0].fused_io;
- const bool result = dm_execute_dmub_cmd_list(ctx, count, commands, DM_DMUB_WAIT_TYPE_WAIT_WITH_REPLY)
- && first->header.ret_status
- && first->request.status == FUSED_REQUEST_STATUS_SUCCESS;
-
- if (!result)
- return false;
-
- while (wait_for_completion_timeout(&sync->replied, usecs_to_jiffies(timeout_us))) {
- reinit_completion(&sync->replied);
-
- struct dmub_cmd_fused_request *reply = (struct dmub_cmd_fused_request *) sync->reply_data;
-
- static_assert(sizeof(*reply) <= sizeof(sync->reply_data), "Size mismatch");
-
- if (reply->identifier == first->request.identifier) {
- first->request = *reply;
- return true;
- }
- }
-
- reinit_completion(&sync->replied);
- first->request.status = FUSED_REQUEST_STATUS_TIMEOUT;
- abort_fused_io(ctx, &first->request);
- return false;
- }
-
- bool amdgpu_dm_execute_fused_io(
- struct amdgpu_device *dev,
- struct dc_link *link,
- union dmub_rb_cmd *commands,
- uint8_t count,
- uint32_t timeout_us)
- {
- struct amdgpu_display_manager *dm = &dev->dm;
-
- mutex_lock(&dm->dpia_aux_lock);
-
- const bool result = execute_fused_io(dev, link->ctx, commands, count, timeout_us);
-
- mutex_unlock(&dm->dpia_aux_lock);
- return result;
- }
-
- int amdgpu_dm_process_dmub_set_config_sync(
- struct dc_context *ctx,
- unsigned int link_index,
- struct set_config_cmd_payload *payload,
- enum set_config_status *operation_result)
- {
- struct amdgpu_device *adev = ctx->driver_context;
- bool is_cmd_complete;
- int ret;
-
- mutex_lock(&adev->dm.dpia_aux_lock);
- is_cmd_complete = dc_process_dmub_set_config_async(ctx->dc,
- link_index, payload, adev->dm.dmub_notify);
-
- if (is_cmd_complete || wait_for_completion_timeout(&adev->dm.dmub_aux_transfer_done, 10 * HZ)) {
- ret = 0;
- *operation_result = adev->dm.dmub_notify->sc_status;
- } else {
- drm_err(adev_to_drm(adev), "wait_for_completion_timeout timeout!");
- ret = -1;
- *operation_result = SET_CONFIG_UNKNOWN_ERROR;
- }
-
- if (!is_cmd_complete)
- reinit_completion(&adev->dm.dmub_aux_transfer_done);
- mutex_unlock(&adev->dm.dpia_aux_lock);
- return ret;
- }
-
- bool dm_execute_dmub_cmd(const struct dc_context *ctx, union dmub_rb_cmd *cmd, enum dm_dmub_wait_type wait_type)
- {
- struct amdgpu_device *adev = ctx->driver_context;
-
- guard(spinlock_irqsave)(&adev->dm.dmub_lock);
- return dc_dmub_srv_cmd_run(ctx->dmub_srv, cmd, wait_type);
- }
-
- bool dm_execute_dmub_cmd_list(const struct dc_context *ctx, unsigned int count, union dmub_rb_cmd *cmd, enum dm_dmub_wait_type wait_type)
- {
- struct amdgpu_device *adev = ctx->driver_context;
-
- guard(spinlock_irqsave)(&adev->dm.dmub_lock);
- return dc_dmub_srv_cmd_run_list(ctx->dmub_srv, count, cmd, wait_type);
- }
-
void dm_acpi_process_phy_transition_interlock(
const struct dc_context *ctx,
struct dm_process_phy_transition_init_params process_phy_transition_init_params)
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 48+ messages in thread* linux-next: manual merge of the drm tree with the drm-fixes tree
@ 2026-07-17 14:40 Mark Brown
2026-07-17 15:12 ` Mark Brown
0 siblings, 1 reply; 48+ messages in thread
From: Mark Brown @ 2026-07-17 14:40 UTC (permalink / raw)
To: Dave Airlie, DRI
Cc: Alex Deucher, Chenyu Chen, Dmytro Laktyushkin, Gabe Teeger,
George Zhang, Linux Kernel Mailing List, Linux Next Mailing List,
Matthew Stewart, Peichen Huang, Pengpeng Hou
[-- Attachment #1: Type: text/plain, Size: 6755 bytes --]
Hi all,
Today's linux-next merge of the drm tree got a conflict in:
drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c
between commits:
6351003b1f4ec ("drm/amd/display: wire DCN42B mcache programming callback")
57cf172be2827 ("drm/amd/display: fix dcn42b det allocation order")
c4bc7ffb91586 ("drm/amd/display: Fix DCN42B null registers & register masks")
from the drm-fixes tree and commits:
85453fb4ff726 ("drm/amd/display: wire DCN42B mcache programming callback")
183bbded999a7 ("drm/amd/display: fix dcn42b det allocation order")
fd651b9a3be9e ("drm/amd/display: Add DCN42B VID_CRC_CONTROL and HBLANK_CONTROL registers")
219fb1f4c5b91 ("drm/amd/display: Enable zstate support and fix seamless boot")
b7d69145907cd ("drm/amd/display: Fix DCN42B null registers & register masks")
6cefc59d36667 ("drm/amd/display: Enable IPS support for DCN4 Variant")
329baa1c5f62b ("drm/amd/display: Enable PSR and Replay on DCN4 variant [Part 2]")
e14fcf9e5d2b5 ("drm/amd/display: correct encoder minimal creation")
d2184b1ba1be2 ("drm/amd/display: Enable pstate for DCN4 non-emulation builds")
from the drm tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
diff --combined drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c
index 94e166c0a9b0b,2334bc5b75b8f..0000000000000
--- a/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c
@@@ -134,6 -134,19 +134,6 @@@
#define regHUBP3_HUBPREQ_DEBUG 0x088d
#define regHUBP3_HUBPREQ_DEBUG_BASE_IDX 2
-#define regDP_SYM32_ENC0_DP_SYM32_ENC_VID_CRC_CONTROL 0x3687
-#define regDP_SYM32_ENC0_DP_SYM32_ENC_VID_CRC_CONTROL_BASE_IDX 2
-#define regDP_SYM32_ENC1_DP_SYM32_ENC_VID_CRC_CONTROL 0x375b
-#define regDP_SYM32_ENC1_DP_SYM32_ENC_VID_CRC_CONTROL_BASE_IDX 2
-#define regDP_SYM32_ENC2_DP_SYM32_ENC_VID_CRC_CONTROL 0x382f
-#define regDP_SYM32_ENC2_DP_SYM32_ENC_VID_CRC_CONTROL_BASE_IDX 2
-#define regDP_SYM32_ENC0_DP_SYM32_ENC_HBLANK_CONTROL 0x366b
-#define regDP_SYM32_ENC0_DP_SYM32_ENC_HBLANK_CONTROL_BASE_IDX 2
-#define regDP_SYM32_ENC1_DP_SYM32_ENC_HBLANK_CONTROL 0x373f
-#define regDP_SYM32_ENC1_DP_SYM32_ENC_HBLANK_CONTROL_BASE_IDX 2
-#define regDP_SYM32_ENC2_DP_SYM32_ENC_HBLANK_CONTROL 0x3813
-#define regDP_SYM32_ENC2_DP_SYM32_ENC_HBLANK_CONTROL_BASE_IDX 2
-
enum dcn401_clk_src_array_id {
DCN401_CLK_SRC_PLL0,
DCN401_CLK_SRC_PLL1,
@@@ -253,10 -266,10 +253,10 @@@ static struct bios_registers bios_regs
static struct dce110_clk_src_regs clk_src_regs[5];
static const struct dce110_clk_src_shift cs_shift = {
- CS_COMMON_MASK_SH_LIST_DCN3_2(__SHIFT)
+ CS_COMMON_MASK_SH_LIST_DCN4_0_1(__SHIFT)
};
static const struct dce110_clk_src_mask cs_mask = {
- CS_COMMON_MASK_SH_LIST_DCN3_2(_MASK)
+ CS_COMMON_MASK_SH_LIST_DCN4_0_1(_MASK)
};
#define abm_regs_init(id) \
ABM_DCN42B_REG_LIST_RI(id)
@@@ -351,7 -364,7 +351,7 @@@ static const struct dcn10_link_enc_mas
LINK_ENCODER_MASK_SH_LIST_DCN42B(_MASK)};
#define hpo_dp_stream_encoder_reg_init(id) \
- DCN42B_HPO_DP_STREAM_ENC_REG_LIST_RI(id)
+ DCN42_HPO_DP_STREAM_ENC_REG_LIST_RI(id)
static struct dcn31_hpo_dp_stream_encoder_registers hpo_dp_stream_enc_regs[4];
@@@ -775,7 -788,7 +775,7 @@@ static const struct dc_debug_options de
.underflow_assert_delay_us = 0xFFFFFFFF,
.dwb_fi_phase = -1, // -1 = disable,
.dmub_command_table = true,
- .pstate_enabled = false,
+ .pstate_enabled = true,
.enable_mem_low_power = {
.bits = {
.vga = false,
@@@ -801,7 -814,7 +801,7 @@@
}
},
.seamless_boot_odm_combine = DML_FAIL_SOURCE_PIXEL_FORMAT,
- .enable_z9_disable_interface = false, /* Allow support for the PMFW interface for disable Z9*/
+ .enable_z9_disable_interface = true, /* Allow support for the PMFW interface for disable Z9*/
.minimum_z8_residency_time = 1, /* Always allow when other conditions are met */
.support_eDP1_5 = true,
.use_max_lb = true,
@@@ -823,7 -836,7 +823,7 @@@
.disable_timeout = true,
.min_disp_clk_khz = 50000,
.static_screen_wait_frames = 2,
- .disable_z10 = true,
+ .disable_z10 = false,
.ignore_pg = true,
.disable_stutter_for_wm_program = true,
.min_deep_sleep_dcfclk_khz = 8000,
@@@ -1034,9 -1047,9 +1034,9 @@@ static struct hubp *dcn42b_hubp_create
}
static const struct dc_panel_config dcn42b_panel_config_defaults = {
.psr = {
- .disable_psr = true,
+ .disable_psr = false,
.disallow_psrsu = true,
- .disallow_replay = true,
+ .disallow_replay = false,
},
.ilr = {
.optimize_edp_link_rate = true,
@@@ -1842,7 -1855,7 +1842,7 @@@ static struct link_encoder *dcn42b_link
{
struct dcn20_link_encoder *enc20;
- if ((unsigned int)(eng_id - ENGINE_ID_DIGA) > ctx->dc->res_pool->res_cap->num_dig_link_enc)
+ if ((unsigned int)(eng_id - ENGINE_ID_DIGA) >= ctx->dc->res_pool->res_cap->num_dig_link_enc)
return NULL;
enc20 = kzalloc(sizeof(struct dcn20_link_encoder), GFP_KERNEL);
@@@ -1854,6 -1867,8 +1854,8 @@@
ctx,
&link_enc_feature,
&link_enc_regs[eng_id - ENGINE_ID_DIGA],
+ &le_shift,
+ &le_mask,
eng_id);
return &enc20->enc10.base;
@@@ -2002,11 -2017,10 +2004,10 @@@ static bool dcn42b_resource_construct
dc->caps.dmcub_support = true;
dc->caps.is_apu = true;
dc->caps.seamless_odm = true;
- dc->caps.zstate_support = false;
- dc->caps.ips_support = false;
+ dc->caps.zstate_support = true;
+ dc->caps.ips_support = true;
dc->caps.max_v_total = (1 << 15) - 1;
dc->caps.vtotal_limited_by_fp2 = true;
- dc->config.disable_ips = DMUB_IPS_DISABLE_ALL;
/* Color pipeline capabilities */
dc->caps.color.dpp.dcn_arch = 1;
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 48+ messages in thread* Re: linux-next: manual merge of the drm tree with the drm-fixes tree
2026-07-17 14:40 Mark Brown
@ 2026-07-17 15:12 ` Mark Brown
0 siblings, 0 replies; 48+ messages in thread
From: Mark Brown @ 2026-07-17 15:12 UTC (permalink / raw)
To: Dave Airlie, DRI
Cc: Alex Deucher, Chenyu Chen, Dmytro Laktyushkin, Gabe Teeger,
George Zhang, Linux Kernel Mailing List, Linux Next Mailing List,
Matthew Stewart, Peichen Huang, Pengpeng Hou
[-- Attachment #1: Type: text/plain, Size: 859 bytes --]
On Fri, Jul 17, 2026 at 03:40:07PM +0100, Mark Brown wrote:
> Today's linux-next merge of the drm tree got a conflict in:
>
> drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c
>
> between commits:
>
> 6351003b1f4ec ("drm/amd/display: wire DCN42B mcache programming callback")
> 57cf172be2827 ("drm/amd/display: fix dcn42b det allocation order")
> c4bc7ffb91586 ("drm/amd/display: Fix DCN42B null registers & register masks")
>
> from the drm-fixes tree and commits:
>
> 85453fb4ff726 ("drm/amd/display: wire DCN42B mcache programming callback")
Actually the merge here doesn't build. I'll try to fix it up but
*please* try not to do this constant, routine cherry picking. Sometimes
it's the sensible thing to do but most of the time I'd expect to see the
fixes go up onto the -next branch with a merge.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 48+ messages in thread
* linux-next: manual merge of the drm tree with the drm-fixes tree
@ 2026-07-10 12:11 Mark Brown
0 siblings, 0 replies; 48+ messages in thread
From: Mark Brown @ 2026-07-10 12:11 UTC (permalink / raw)
To: Dave Airlie, DRI
Cc: Alex Deucher, David Francis, Linux Kernel Mailing List,
Linux Next Mailing List
[-- Attachment #1: Type: text/plain, Size: 2289 bytes --]
Hi all,
Today's linux-next merge of the drm tree got conflicts in:
drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h
drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c
between commits:
8a93f77aec655 ("drm/amdkfd: Don't acquire buffers during CRIU queue restore.")
47ea05f246beb ("drm/amdkfd: Check bounds on CRIU restore queue type and mqd size")
from the drm-fixes tree and commits:
f19d8086f6644 ("drm/amdkfd: Check bounds on CRIU restore queue type and mqd size")
1cafa8b29e029 ("drm/amdkfd: Don't acquire buffers during CRIU queue restore.")
from the drm tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
diff --combined drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h
index 641b8ada82a03,c9f9f7a871117..0000000000000
--- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h
@@@ -333,6 -333,8 +333,8 @@@ int debug_refresh_runlist(struct device
bool kfd_dqm_is_queue_in_process(struct device_queue_manager *dqm,
struct qcm_process_device *qpd,
int doorbell_off, u32 *queue_format);
+ int kfd_reset_queue_mes(struct device_queue_manager *dqm, int queue_type,
+ int pipe, int queue, unsigned int db);
size_t mqd_size_from_queue_type(struct device_queue_manager *dqm,
enum kfd_queue_type type);
diff --combined drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c
index 9ccbc6e5b27b3,97e77a5a35e51..0000000000000
--- a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c
@@@ -1055,7 -1055,7 +1055,7 @@@ int kfd_criu_restore_queue(struct kfd_p
ctl_stack = mqd + q_data->mqd_size;
memset(&qp, 0, sizeof(qp));
- set_queue_properties_from_criu(&qp, q_data, NUM_XCC(pdd->dev->adev->gfx.xcc_mask));
+ set_queue_properties_from_criu(&qp, q_data, NUM_XCC(pdd->dev->xcc_mask));
print_queue_properties(&qp);
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 48+ messages in thread* linux-next: manual merge of the drm tree with the drm-fixes tree
@ 2026-05-29 22:00 Mark Brown
0 siblings, 0 replies; 48+ messages in thread
From: Mark Brown @ 2026-05-29 22:00 UTC (permalink / raw)
To: Dave Airlie, DRI
Cc: Alex Deucher, Christian König, Linux Kernel Mailing List,
Linux Next Mailing List
[-- Attachment #1: Type: text/plain, Size: 2218 bytes --]
Hi all,
Today's linux-next merge of the drm tree got a conflict in:
drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
between commit:
1c824497d8acd ("drm/amdgpu: fix calling VM invalidation in amdgpu_hmm_invalidate_gfx")
from the drm-fixes tree and commit:
4cdbba5a16aaf ("drm/amdgpu: restructure VM state machine v4")
from the drm tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
diff --cc drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
index c9f88ecce1a75,a99d01f9dc750..0000000000000
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
@@@ -1629,9 -1598,8 +1598,9 @@@ int amdgpu_vm_handle_moved(struct amdgp
struct amdgpu_vm *vm,
struct ww_acquire_ctx *ticket)
{
- struct amdgpu_bo_va *bo_va;
+ struct amdgpu_bo_va *bo_va, *tmp;
struct dma_resv *resv;
+ struct amdgpu_bo *bo;
bool clear, unlock;
int r;
@@@ -1645,19 -1609,17 +1610,18 @@@
r = amdgpu_vm_bo_update(adev, bo_va, false);
if (r)
return r;
- spin_lock(&vm->status_lock);
}
- while (!list_empty(&vm->invalidated)) {
- bo_va = list_first_entry(&vm->invalidated, struct amdgpu_bo_va,
- base.vm_status);
- bo = bo_va->base.bo;
- resv = bo->tbo.base.resv;
- spin_unlock(&vm->status_lock);
+ spin_lock(&vm->individual_lock);
+ while (!list_empty(&vm->individual.moved)) {
+ bo_va = list_first_entry(&vm->individual.moved,
+ typeof(*bo_va), base.vm_status);
+ resv = bo_va->base.bo->tbo.base.resv;
+ spin_unlock(&vm->individual_lock);
/* Try to reserve the BO to avoid clearing its ptes */
- if (!adev->debug_vm && dma_resv_trylock(resv)) {
+ if (!adev->debug_vm && !amdgpu_ttm_tt_get_usermm(bo->tbo.ttm) &&
+ dma_resv_trylock(resv)) {
clear = false;
unlock = true;
/* The caller is already holding the reservation lock */
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 48+ messages in thread* linux-next: manual merge of the drm tree with the drm-fixes tree
@ 2026-04-03 13:33 Mark Brown
0 siblings, 0 replies; 48+ messages in thread
From: Mark Brown @ 2026-04-03 13:33 UTC (permalink / raw)
To: Dave Airlie, DRI
Cc: Alex Deucher, Lijo Lazar, Linux Kernel Mailing List,
Linux Next Mailing List
[-- Attachment #1: Type: text/plain, Size: 8371 bytes --]
Hi all,
Today's linux-next merge of the drm tree got conflicts in:
drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c
drivers/gpu/drm/amd/pm/swsmu/smu12/smu_v12_0.c
drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c
between commit:
a3ffaa5b397f4 ("drm/amdgpu/pm: drop SMU driver if version not matched messages")
from the drm-fixes tree and commits:
6b0a6116286ef ("drm/amd/pm: Unify version check in SMUv11")
353f200825051 ("drm/amd/pm: Unify version check in SMUv12")
964e532d58378 ("drm/amd/pm: Unify version check in SMUv14")
from the drm tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
diff --combined drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c
index 7ca8fdd23206e,d68ceee16d8f1..0000000000000
--- a/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c
@@@ -192,81 -192,6 +192,6 @@@ int smu_v11_0_check_fw_status(struct sm
return -EIO;
}
- int smu_v11_0_check_fw_version(struct smu_context *smu)
- {
- struct amdgpu_device *adev = smu->adev;
- uint32_t if_version = 0xff, smu_version = 0xff;
- uint8_t smu_program, smu_major, smu_minor, smu_debug;
- int ret = 0;
-
- ret = smu_cmn_get_smc_version(smu, &if_version, &smu_version);
- if (ret)
- return ret;
-
- smu_program = (smu_version >> 24) & 0xff;
- smu_major = (smu_version >> 16) & 0xff;
- smu_minor = (smu_version >> 8) & 0xff;
- smu_debug = (smu_version >> 0) & 0xff;
- if (smu->is_apu)
- adev->pm.fw_version = smu_version;
-
- switch (amdgpu_ip_version(adev, MP1_HWIP, 0)) {
- case IP_VERSION(11, 0, 0):
- smu->smc_driver_if_version = SMU11_DRIVER_IF_VERSION_NV10;
- break;
- case IP_VERSION(11, 0, 9):
- smu->smc_driver_if_version = SMU11_DRIVER_IF_VERSION_NV12;
- break;
- case IP_VERSION(11, 0, 5):
- smu->smc_driver_if_version = SMU11_DRIVER_IF_VERSION_NV14;
- break;
- case IP_VERSION(11, 0, 7):
- smu->smc_driver_if_version = SMU11_DRIVER_IF_VERSION_Sienna_Cichlid;
- break;
- case IP_VERSION(11, 0, 11):
- smu->smc_driver_if_version = SMU11_DRIVER_IF_VERSION_Navy_Flounder;
- break;
- case IP_VERSION(11, 5, 0):
- case IP_VERSION(11, 5, 2):
- smu->smc_driver_if_version = SMU11_DRIVER_IF_VERSION_VANGOGH;
- break;
- case IP_VERSION(11, 0, 12):
- smu->smc_driver_if_version = SMU11_DRIVER_IF_VERSION_Dimgrey_Cavefish;
- break;
- case IP_VERSION(11, 0, 13):
- smu->smc_driver_if_version = SMU11_DRIVER_IF_VERSION_Beige_Goby;
- break;
- case IP_VERSION(11, 0, 8):
- smu->smc_driver_if_version = SMU11_DRIVER_IF_VERSION_Cyan_Skillfish;
- break;
- case IP_VERSION(11, 0, 2):
- smu->smc_driver_if_version = SMU11_DRIVER_IF_VERSION_ARCT;
- break;
- default:
- dev_err(smu->adev->dev, "smu unsupported IP version: 0x%x.\n",
- amdgpu_ip_version(adev, MP1_HWIP, 0));
- smu->smc_driver_if_version = SMU11_DRIVER_IF_VERSION_INV;
- break;
- }
-
- /*
- * 1. if_version mismatch is not critical as our fw is designed
- * to be backward compatible.
- * 2. New fw usually brings some optimizations. But that's visible
- * only on the paired driver.
- * Considering above, we just leave user a verbal message instead
- * of halt driver loading.
- */
- if (if_version != smu->smc_driver_if_version) {
- dev_info(smu->adev->dev, "smu driver if version = 0x%08x, smu fw if version = 0x%08x, "
- "smu fw program = %d, version = 0x%08x (%d.%d.%d)\n",
- smu->smc_driver_if_version, if_version,
- smu_program, smu_version, smu_major, smu_minor, smu_debug);
- }
-
- return ret;
- }
-
static int smu_v11_0_set_pptable_v2_0(struct smu_context *smu, void **table, uint32_t *size)
{
struct amdgpu_device *adev = smu->adev;
diff --combined drivers/gpu/drm/amd/pm/swsmu/smu12/smu_v12_0.c
index ac5e44dff6c97,f09da4d14510b..0000000000000
--- a/drivers/gpu/drm/amd/pm/swsmu/smu12/smu_v12_0.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu12/smu_v12_0.c
@@@ -70,42 -70,6 +70,6 @@@ int smu_v12_0_check_fw_status(struct sm
return -EIO;
}
- int smu_v12_0_check_fw_version(struct smu_context *smu)
- {
- struct amdgpu_device *adev = smu->adev;
- uint32_t if_version = 0xff, smu_version = 0xff;
- uint8_t smu_program, smu_major, smu_minor, smu_debug;
- int ret = 0;
-
- ret = smu_cmn_get_smc_version(smu, &if_version, &smu_version);
- if (ret)
- return ret;
-
- smu_program = (smu_version >> 24) & 0xff;
- smu_major = (smu_version >> 16) & 0xff;
- smu_minor = (smu_version >> 8) & 0xff;
- smu_debug = (smu_version >> 0) & 0xff;
- if (smu->is_apu)
- adev->pm.fw_version = smu_version;
-
- /*
- * 1. if_version mismatch is not critical as our fw is designed
- * to be backward compatible.
- * 2. New fw usually brings some optimizations. But that's visible
- * only on the paired driver.
- * Considering above, we just leave user a verbal message instead
- * of halt driver loading.
- */
- if (if_version != smu->smc_driver_if_version) {
- dev_info(smu->adev->dev, "smu driver if version = 0x%08x, smu fw if version = 0x%08x, "
- "smu fw program = %d, smu fw version = 0x%08x (%d.%d.%d)\n",
- smu->smc_driver_if_version, if_version,
- smu_program, smu_version, smu_major, smu_minor, smu_debug);
- }
-
- return ret;
- }
-
int smu_v12_0_powergate_sdma(struct smu_context *smu, bool gate)
{
if (!smu->is_apu)
diff --combined drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c
index e38354c694c95,d0a8df1aa6b64..0000000000000
--- a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c
@@@ -229,66 -229,6 +229,6 @@@ int smu_v14_0_check_fw_status(struct sm
return -EIO;
}
- int smu_v14_0_check_fw_version(struct smu_context *smu)
- {
- struct amdgpu_device *adev = smu->adev;
- uint32_t if_version = 0xff, smu_version = 0xff;
- uint8_t smu_program, smu_major, smu_minor, smu_debug;
- int ret = 0;
-
- ret = smu_cmn_get_smc_version(smu, &if_version, &smu_version);
- if (ret)
- return ret;
-
- smu_program = (smu_version >> 24) & 0xff;
- smu_major = (smu_version >> 16) & 0xff;
- smu_minor = (smu_version >> 8) & 0xff;
- smu_debug = (smu_version >> 0) & 0xff;
- if (smu->is_apu)
- adev->pm.fw_version = smu_version;
-
- switch (amdgpu_ip_version(adev, MP1_HWIP, 0)) {
- case IP_VERSION(14, 0, 0):
- case IP_VERSION(14, 0, 4):
- case IP_VERSION(14, 0, 5):
- smu->smc_driver_if_version = SMU14_DRIVER_IF_VERSION_SMU_V14_0_0;
- break;
- case IP_VERSION(14, 0, 1):
- smu->smc_driver_if_version = SMU14_DRIVER_IF_VERSION_SMU_V14_0_1;
- break;
- case IP_VERSION(14, 0, 2):
- case IP_VERSION(14, 0, 3):
- smu->smc_driver_if_version = SMU14_DRIVER_IF_VERSION_SMU_V14_0_2;
- break;
- default:
- dev_err(adev->dev, "smu unsupported IP version: 0x%x.\n",
- amdgpu_ip_version(adev, MP1_HWIP, 0));
- smu->smc_driver_if_version = SMU14_DRIVER_IF_VERSION_INV;
- break;
- }
-
- if (adev->pm.fw)
- dev_dbg(smu->adev->dev, "smu fw reported program %d, version = 0x%08x (%d.%d.%d)\n",
- smu_program, smu_version, smu_major, smu_minor, smu_debug);
-
- /*
- * 1. if_version mismatch is not critical as our fw is designed
- * to be backward compatible.
- * 2. New fw usually brings some optimizations. But that's visible
- * only on the paired driver.
- * Considering above, we just leave user a verbal message instead
- * of halt driver loading.
- */
- if (if_version != smu->smc_driver_if_version) {
- dev_info(adev->dev, "smu driver if version = 0x%08x, smu fw if version = 0x%08x, "
- "smu fw program = %d, smu fw version = 0x%08x (%d.%d.%d)\n",
- smu->smc_driver_if_version, if_version,
- smu_program, smu_version, smu_major, smu_minor, smu_debug);
- }
-
- return ret;
- }
-
static int smu_v14_0_set_pptable_v2_0(struct smu_context *smu, void **table, uint32_t *size)
{
struct amdgpu_device *adev = smu->adev;
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 48+ messages in thread* linux-next: manual merge of the drm tree with the drm-fixes tree
@ 2026-03-27 20:52 Mark Brown
0 siblings, 0 replies; 48+ messages in thread
From: Mark Brown @ 2026-03-27 20:52 UTC (permalink / raw)
To: Dave Airlie, DRI
Cc: Linux Kernel Mailing List, Linux Next Mailing List, Matt Roper,
Rodrigo Vivi
[-- Attachment #1: Type: text/plain, Size: 24734 bytes --]
Hi all,
Today's linux-next merge of the drm tree got a conflict in:
drivers/gpu/drm/xe/xe_wa.c
between commit:
56781a4597706c ("drm/xe: Implement recent spec updates to Wa_16025250150")
from the drm-fixes tree and commits:
a31566762d4075 ("drm/xe: Implement recent spec updates to Wa_16025250150")
9f49e185ee31d5 ("drm/xe/wa: Drop redundant entries for Wa_16021867713 & Wa_14019449301")
from the drm tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
diff --combined drivers/gpu/drm/xe/xe_wa.c
index d7e309ad9abaf5,546296f0220b5f..00000000000000
--- a/drivers/gpu/drm/xe/xe_wa.c
+++ b/drivers/gpu/drm/xe/xe_wa.c
@@@ -111,6 -111,17 +111,17 @@@
* difference of how they are maintained in the code. In xe it uses the
* xe_rtp infrastructure so the workarounds can be kept in tables, following
* a more declarative approach rather than procedural.
+ *
+ * .. note::
+ * When a workaround applies to every single known IP version in a range,
+ * the preferred handling is to use a single range-based RTP entry rather
+ * than individual entries for each version, even if some of the intermediate
+ * version numbers are currently unused. If a new intermediate IP version
+ * appears in the future and is enabled in the driver, any existing
+ * range-based entries that contain the new version number will need to be
+ * analyzed to determine whether their workarounds should apply to the new
+ * version, or whether any existing range based entries needs to be split
+ * into two entries that do not include the new intermediate version.
*/
#undef XE_REG_MCR
@@@ -120,6 -131,8 +131,8 @@@ __diag_push()
__diag_ignore_all("-Woverride-init", "Allow field overrides in table");
static const struct xe_rtp_entry_sr gt_was[] = {
+ /* Workarounds applying over a range of IPs */
+
{ XE_RTP_NAME("14011060649"),
XE_RTP_RULES(MEDIA_VERSION_RANGE(1200, 1255),
ENGINE_CLASS(VIDEO_DECODE),
@@@ -135,6 -148,27 +148,27 @@@
XE_RTP_RULES(GRAPHICS_VERSION_RANGE(1200, 1260)),
XE_RTP_ACTIONS(CLR(MISCCPCTL, DOP_CLOCK_GATE_RENDER_ENABLE))
},
+ { XE_RTP_NAME("16021867713"),
+ XE_RTP_RULES(MEDIA_VERSION_RANGE(1300, 3002),
+ ENGINE_CLASS(VIDEO_DECODE)),
+ XE_RTP_ACTIONS(SET(VDBOX_CGCTL3F1C(0), MFXPIPE_CLKGATE_DIS)),
+ XE_RTP_ENTRY_FLAG(FOREACH_ENGINE),
+ },
+ { XE_RTP_NAME("14019449301"),
+ XE_RTP_RULES(MEDIA_VERSION_RANGE(1301, 2000), ENGINE_CLASS(VIDEO_DECODE)),
+ XE_RTP_ACTIONS(SET(VDBOX_CGCTL3F08(0), CG3DDISHRS_CLKGATE_DIS)),
+ XE_RTP_ENTRY_FLAG(FOREACH_ENGINE),
+ },
+ { XE_RTP_NAME("16028005424"),
+ XE_RTP_RULES(GRAPHICS_VERSION_RANGE(3000, 3005), OR,
+ MEDIA_VERSION_RANGE(1301, 3500)),
+ XE_RTP_ACTIONS(SET(GUC_INTR_CHICKEN, DISABLE_SIGNALING_ENGINES))
+ },
+ { XE_RTP_NAME("14026578760"),
+ XE_RTP_RULES(GRAPHICS_VERSION_RANGE(3510, 3511), OR,
+ MEDIA_VERSION(3503)),
+ XE_RTP_ACTIONS(SET(GAMSTLB_CTRL, DIS_PEND_GPA_LINK))
+ },
/* DG1 */
@@@ -191,10 -225,6 +225,6 @@@
/* Xe_LPG */
- { XE_RTP_NAME("14015795083"),
- XE_RTP_RULES(GRAPHICS_VERSION_RANGE(1270, 1271), GRAPHICS_STEP(A0, B0)),
- XE_RTP_ACTIONS(CLR(MISCCPCTL, DOP_CLOCK_GATE_RENDER_ENABLE))
- },
{ XE_RTP_NAME("14018575942"),
XE_RTP_RULES(GRAPHICS_VERSION_RANGE(1270, 1274)),
XE_RTP_ACTIONS(SET(COMP_MOD_CTRL, FORCE_MISS_FTLB))
@@@ -206,12 -236,6 +236,6 @@@
/* Xe_LPM+ */
- { XE_RTP_NAME("16021867713"),
- XE_RTP_RULES(MEDIA_VERSION(1300),
- ENGINE_CLASS(VIDEO_DECODE)),
- XE_RTP_ACTIONS(SET(VDBOX_CGCTL3F1C(0), MFXPIPE_CLKGATE_DIS)),
- XE_RTP_ENTRY_FLAG(FOREACH_ENGINE),
- },
{ XE_RTP_NAME("22016670082"),
XE_RTP_RULES(MEDIA_VERSION(1300)),
XE_RTP_ACTIONS(SET(XELPMP_SQCNT1, ENFORCE_RAR))
@@@ -225,17 -249,6 +249,6 @@@
XE_RTP_ACTIONS(SET(VDBOX_CGCTL3F10(0), IECPUNIT_CLKGATE_DIS)),
XE_RTP_ENTRY_FLAG(FOREACH_ENGINE),
},
- { XE_RTP_NAME("16021867713"),
- XE_RTP_RULES(MEDIA_VERSION(2000),
- ENGINE_CLASS(VIDEO_DECODE)),
- XE_RTP_ACTIONS(SET(VDBOX_CGCTL3F1C(0), MFXPIPE_CLKGATE_DIS)),
- XE_RTP_ENTRY_FLAG(FOREACH_ENGINE),
- },
- { XE_RTP_NAME("14019449301"),
- XE_RTP_RULES(MEDIA_VERSION(2000), ENGINE_CLASS(VIDEO_DECODE)),
- XE_RTP_ACTIONS(SET(VDBOX_CGCTL3F08(0), CG3DDISHRS_CLKGATE_DIS)),
- XE_RTP_ENTRY_FLAG(FOREACH_ENGINE),
- },
/* Xe2_HPG */
@@@ -251,20 -264,6 +264,6 @@@
SET(LSC_CHICKEN_BIT_0_UDW, L3_128B_256B_WRT_DIS))
},
- /* Xe2_HPM */
-
- { XE_RTP_NAME("16021867713"),
- XE_RTP_RULES(MEDIA_VERSION(1301),
- ENGINE_CLASS(VIDEO_DECODE)),
- XE_RTP_ACTIONS(SET(VDBOX_CGCTL3F1C(0), MFXPIPE_CLKGATE_DIS)),
- XE_RTP_ENTRY_FLAG(FOREACH_ENGINE),
- },
- { XE_RTP_NAME("14019449301"),
- XE_RTP_RULES(MEDIA_VERSION(1301), ENGINE_CLASS(VIDEO_DECODE)),
- XE_RTP_ACTIONS(SET(VDBOX_CGCTL3F08(0), CG3DDISHRS_CLKGATE_DIS)),
- XE_RTP_ENTRY_FLAG(FOREACH_ENGINE),
- },
-
/* Xe3_LPG */
{ XE_RTP_NAME("14021871409"),
@@@ -274,43 -273,44 +273,44 @@@
/* Xe3_LPM */
- { XE_RTP_NAME("16021867713"),
- XE_RTP_RULES(MEDIA_VERSION(3000),
- ENGINE_CLASS(VIDEO_DECODE)),
- XE_RTP_ACTIONS(SET(VDBOX_CGCTL3F1C(0), MFXPIPE_CLKGATE_DIS)),
- XE_RTP_ENTRY_FLAG(FOREACH_ENGINE),
- },
{ XE_RTP_NAME("16021865536"),
- XE_RTP_RULES(MEDIA_VERSION(3000),
+ XE_RTP_RULES(MEDIA_VERSION_RANGE(3000, 3002),
ENGINE_CLASS(VIDEO_DECODE)),
XE_RTP_ACTIONS(SET(VDBOX_CGCTL3F10(0), IECPUNIT_CLKGATE_DIS)),
XE_RTP_ENTRY_FLAG(FOREACH_ENGINE),
},
- { XE_RTP_NAME("16021865536"),
- XE_RTP_RULES(MEDIA_VERSION(3002),
- ENGINE_CLASS(VIDEO_DECODE)),
- XE_RTP_ACTIONS(SET(VDBOX_CGCTL3F10(0), IECPUNIT_CLKGATE_DIS)),
- XE_RTP_ENTRY_FLAG(FOREACH_ENGINE),
- },
- { XE_RTP_NAME("16021867713"),
- XE_RTP_RULES(MEDIA_VERSION(3002),
- ENGINE_CLASS(VIDEO_DECODE)),
- XE_RTP_ACTIONS(SET(VDBOX_CGCTL3F1C(0), MFXPIPE_CLKGATE_DIS)),
- XE_RTP_ENTRY_FLAG(FOREACH_ENGINE),
- },
{ XE_RTP_NAME("14021486841"),
XE_RTP_RULES(MEDIA_VERSION(3000), MEDIA_STEP(A0, B0),
ENGINE_CLASS(VIDEO_DECODE)),
XE_RTP_ACTIONS(SET(VDBOX_CGCTL3F10(0), RAMDFTUNIT_CLKGATE_DIS)),
XE_RTP_ENTRY_FLAG(FOREACH_ENGINE),
},
+
+ /* Xe3P_LPG */
+
+ { XE_RTP_NAME("14025160223"),
+ XE_RTP_RULES(GRAPHICS_VERSION(3510), GRAPHICS_STEP(A0, B0)),
+ XE_RTP_ACTIONS(SET(MMIOATSREQLIMIT_GAM_WALK_3D,
+ DIS_ATS_WRONLY_PG))
+ },
+ { XE_RTP_NAME("14026144927, 16029437861"),
+ XE_RTP_RULES(GRAPHICS_VERSION(3510), GRAPHICS_STEP(A0, B0)),
+ XE_RTP_ACTIONS(SET(L3SQCREG2, L3_SQ_DISABLE_COAMA_2WAY_COH |
+ L3_SQ_DISABLE_COAMA))
+ },
+ { XE_RTP_NAME("14025635424"),
+ XE_RTP_RULES(GRAPHICS_VERSION(3510), GRAPHICS_STEP(A0, B0)),
+ XE_RTP_ACTIONS(SET(GAMSTLB_CTRL2, STLB_SINGLE_BANK_MODE))
+ },
{ XE_RTP_NAME("16028005424"),
- XE_RTP_RULES(GRAPHICS_VERSION_RANGE(3000, 3005)),
+ XE_RTP_RULES(GRAPHICS_VERSION(3510), GRAPHICS_STEP(A0, B0)),
XE_RTP_ACTIONS(SET(GUC_INTR_CHICKEN, DISABLE_SIGNALING_ENGINES))
},
};
static const struct xe_rtp_entry_sr engine_was[] = {
+ /* Workarounds applying over a range of IPs */
+
{ XE_RTP_NAME("22010931296, 18011464164, 14010919138"),
XE_RTP_RULES(GRAPHICS_VERSION(1200), ENGINE_CLASS(RENDER)),
XE_RTP_ACTIONS(SET(FF_THREAD_MODE(RENDER_RING_BASE),
@@@ -346,6 -346,54 +346,54 @@@
XE_RTP_ACTIONS(SET(FF_SLICE_CS_CHICKEN1(RENDER_RING_BASE),
FFSC_PERCTX_PREEMPT_CTRL))
},
+ { XE_RTP_NAME("18032247524"),
+ XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, 2004),
+ FUNC(xe_rtp_match_first_render_or_compute)),
+ XE_RTP_ACTIONS(SET(LSC_CHICKEN_BIT_0, SEQUENTIAL_ACCESS_UPGRADE_DISABLE))
+ },
+ { XE_RTP_NAME("16018712365"),
+ XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, 2004),
+ FUNC(xe_rtp_match_first_render_or_compute)),
+ XE_RTP_ACTIONS(SET(LSC_CHICKEN_BIT_0_UDW, XE2_ALLOC_DPA_STARVE_FIX_DIS))
+ },
+ { XE_RTP_NAME("14020338487"),
+ XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, 2004),
+ FUNC(xe_rtp_match_first_render_or_compute)),
+ XE_RTP_ACTIONS(SET(ROW_CHICKEN3, XE2_EUPEND_CHK_FLUSH_DIS))
+ },
+ { XE_RTP_NAME("14018471104"),
+ XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, 2004),
+ FUNC(xe_rtp_match_first_render_or_compute)),
+ XE_RTP_ACTIONS(SET(LSC_CHICKEN_BIT_0_UDW, ENABLE_SMP_LD_RENDER_SURFACE_CONTROL))
+ },
+ /*
+ * Although this workaround isn't required for the RCS, disabling these
+ * reports has no impact for our driver or the GuC, so we go ahead and
+ * apply this to all engines for simplicity.
+ */
+ { XE_RTP_NAME("16021639441"),
+ XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, 2004), OR,
+ MEDIA_VERSION_RANGE(1301, 2000)),
+ XE_RTP_ACTIONS(SET(CSFE_CHICKEN1(0),
+ GHWSP_CSB_REPORT_DIS |
+ PPHWSP_CSB_AND_TIMESTAMP_REPORT_DIS,
+ XE_RTP_ACTION_FLAG(ENGINE_BASE)))
+ },
+ { XE_RTP_NAME("14021402888"),
+ XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, 3005), ENGINE_CLASS(RENDER)),
+ XE_RTP_ACTIONS(SET(HALF_SLICE_CHICKEN7, CLEAR_OPTIMIZATION_DISABLE))
+ },
+ { XE_RTP_NAME("13012615864"),
+ XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, 3005),
+ FUNC(xe_rtp_match_first_render_or_compute)),
+ XE_RTP_ACTIONS(SET(TDL_TSL_CHICKEN, RES_CHK_SPR_DIS))
+ },
+ { XE_RTP_NAME("18041344222"),
+ XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, 3000),
+ FUNC(xe_rtp_match_first_render_or_compute),
+ FUNC(xe_rtp_match_gt_has_discontiguous_dss_groups)),
+ XE_RTP_ACTIONS(SET(TDL_CHICKEN, EUSTALL_PERF_SAMPLING_DISABLE))
+ },
/* TGL */
@@@ -461,11 -509,6 +509,6 @@@
ENGINE_CLASS(COMPUTE)),
XE_RTP_ACTIONS(SET(RING_HWSTAM(RENDER_RING_BASE), ~0))
},
- { XE_RTP_NAME("14014999345"),
- XE_RTP_RULES(PLATFORM(PVC), ENGINE_CLASS(COMPUTE),
- GRAPHICS_STEP(B0, C0)),
- XE_RTP_ACTIONS(SET(CACHE_MODE_SS, DISABLE_ECC))
- },
/* Xe_LPG */
@@@ -488,149 -531,36 +531,36 @@@
/* Xe2_LPG */
- { XE_RTP_NAME("18032247524"),
- XE_RTP_RULES(GRAPHICS_VERSION(2004),
- FUNC(xe_rtp_match_first_render_or_compute)),
- XE_RTP_ACTIONS(SET(LSC_CHICKEN_BIT_0, SEQUENTIAL_ACCESS_UPGRADE_DISABLE))
- },
- { XE_RTP_NAME("16018712365"),
- XE_RTP_RULES(GRAPHICS_VERSION(2004), FUNC(xe_rtp_match_first_render_or_compute)),
- XE_RTP_ACTIONS(SET(LSC_CHICKEN_BIT_0_UDW, XE2_ALLOC_DPA_STARVE_FIX_DIS))
- },
- { XE_RTP_NAME("14020338487"),
- XE_RTP_RULES(GRAPHICS_VERSION(2004), FUNC(xe_rtp_match_first_render_or_compute)),
- XE_RTP_ACTIONS(SET(ROW_CHICKEN3, XE2_EUPEND_CHK_FLUSH_DIS))
- },
{ XE_RTP_NAME("18034896535, 16021540221"), /* 16021540221: GRAPHICS_STEP(A0, B0) */
XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, 2004),
FUNC(xe_rtp_match_first_render_or_compute)),
XE_RTP_ACTIONS(SET(ROW_CHICKEN4, DISABLE_TDL_PUSH))
},
- { XE_RTP_NAME("14018471104"),
- XE_RTP_RULES(GRAPHICS_VERSION(2004), FUNC(xe_rtp_match_first_render_or_compute)),
- XE_RTP_ACTIONS(SET(LSC_CHICKEN_BIT_0_UDW, ENABLE_SMP_LD_RENDER_SURFACE_CONTROL))
- },
- /*
- * These two workarounds are the same, just applying to different
- * engines. Although Wa_18032095049 (for the RCS) isn't required on
- * all steppings, disabling these reports has no impact for our
- * driver or the GuC, so we go ahead and treat it the same as
- * Wa_16021639441 which does apply to all steppings.
- */
- { XE_RTP_NAME("18032095049, 16021639441"),
- XE_RTP_RULES(GRAPHICS_VERSION(2004)),
- XE_RTP_ACTIONS(SET(CSFE_CHICKEN1(0),
- GHWSP_CSB_REPORT_DIS |
- PPHWSP_CSB_AND_TIMESTAMP_REPORT_DIS,
- XE_RTP_ACTION_FLAG(ENGINE_BASE)))
- },
{ XE_RTP_NAME("16018610683"),
XE_RTP_RULES(GRAPHICS_VERSION(2004), FUNC(xe_rtp_match_first_render_or_compute)),
XE_RTP_ACTIONS(SET(TDL_TSL_CHICKEN, SLM_WMTP_RESTORE))
},
- { XE_RTP_NAME("14021402888"),
- XE_RTP_RULES(GRAPHICS_VERSION(2004), ENGINE_CLASS(RENDER)),
- XE_RTP_ACTIONS(SET(HALF_SLICE_CHICKEN7, CLEAR_OPTIMIZATION_DISABLE))
- },
- { XE_RTP_NAME("13012615864"),
- XE_RTP_RULES(GRAPHICS_VERSION(2004),
- FUNC(xe_rtp_match_first_render_or_compute)),
- XE_RTP_ACTIONS(SET(TDL_TSL_CHICKEN, RES_CHK_SPR_DIS))
- },
/* Xe2_HPG */
- { XE_RTP_NAME("16018712365"),
- XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, 2002),
- FUNC(xe_rtp_match_first_render_or_compute)),
- XE_RTP_ACTIONS(SET(LSC_CHICKEN_BIT_0_UDW, XE2_ALLOC_DPA_STARVE_FIX_DIS))
- },
{ XE_RTP_NAME("16018737384"),
- XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, XE_RTP_END_VERSION_UNDEFINED),
+ XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, 2999),
FUNC(xe_rtp_match_first_render_or_compute)),
XE_RTP_ACTIONS(SET(ROW_CHICKEN, EARLY_EOT_DIS))
},
- { XE_RTP_NAME("14020338487"),
- XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, 2002),
- FUNC(xe_rtp_match_first_render_or_compute)),
- XE_RTP_ACTIONS(SET(ROW_CHICKEN3, XE2_EUPEND_CHK_FLUSH_DIS))
- },
- { XE_RTP_NAME("18032247524"),
- XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, 2002),
- FUNC(xe_rtp_match_first_render_or_compute)),
- XE_RTP_ACTIONS(SET(LSC_CHICKEN_BIT_0, SEQUENTIAL_ACCESS_UPGRADE_DISABLE))
- },
- { XE_RTP_NAME("14018471104"),
- XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, 2002),
- FUNC(xe_rtp_match_first_render_or_compute)),
- XE_RTP_ACTIONS(SET(LSC_CHICKEN_BIT_0_UDW, ENABLE_SMP_LD_RENDER_SURFACE_CONTROL))
- },
- /*
- * Although this workaround isn't required for the RCS, disabling these
- * reports has no impact for our driver or the GuC, so we go ahead and
- * apply this to all engines for simplicity.
- */
- { XE_RTP_NAME("16021639441"),
- XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, 2002)),
- XE_RTP_ACTIONS(SET(CSFE_CHICKEN1(0),
- GHWSP_CSB_REPORT_DIS |
- PPHWSP_CSB_AND_TIMESTAMP_REPORT_DIS,
- XE_RTP_ACTION_FLAG(ENGINE_BASE)))
- },
{ XE_RTP_NAME("14019811474"),
XE_RTP_RULES(GRAPHICS_VERSION(2001),
FUNC(xe_rtp_match_first_render_or_compute)),
XE_RTP_ACTIONS(SET(LSC_CHICKEN_BIT_0, WR_REQ_CHAINING_DIS))
},
- { XE_RTP_NAME("14021402888"),
- XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, 2002), ENGINE_CLASS(RENDER)),
- XE_RTP_ACTIONS(SET(HALF_SLICE_CHICKEN7, CLEAR_OPTIMIZATION_DISABLE))
- },
{ XE_RTP_NAME("14021821874, 14022954250"),
XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, 2002),
FUNC(xe_rtp_match_first_render_or_compute)),
XE_RTP_ACTIONS(SET(TDL_TSL_CHICKEN, STK_ID_RESTRICT))
},
- { XE_RTP_NAME("13012615864"),
- XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, 2002),
- FUNC(xe_rtp_match_first_render_or_compute)),
- XE_RTP_ACTIONS(SET(TDL_TSL_CHICKEN, RES_CHK_SPR_DIS))
- },
- { XE_RTP_NAME("18041344222"),
- XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, 2002),
- FUNC(xe_rtp_match_first_render_or_compute),
- FUNC(xe_rtp_match_not_sriov_vf),
- FUNC(xe_rtp_match_gt_has_discontiguous_dss_groups)),
- XE_RTP_ACTIONS(SET(TDL_CHICKEN, EUSTALL_PERF_SAMPLING_DISABLE))
- },
-
- /* Xe2_LPM */
-
- { XE_RTP_NAME("16021639441"),
- XE_RTP_RULES(MEDIA_VERSION(2000)),
- XE_RTP_ACTIONS(SET(CSFE_CHICKEN1(0),
- GHWSP_CSB_REPORT_DIS |
- PPHWSP_CSB_AND_TIMESTAMP_REPORT_DIS,
- XE_RTP_ACTION_FLAG(ENGINE_BASE)))
- },
-
- /* Xe2_HPM */
-
- { XE_RTP_NAME("16021639441"),
- XE_RTP_RULES(MEDIA_VERSION(1301)),
- XE_RTP_ACTIONS(SET(CSFE_CHICKEN1(0),
- GHWSP_CSB_REPORT_DIS |
- PPHWSP_CSB_AND_TIMESTAMP_REPORT_DIS,
- XE_RTP_ACTION_FLAG(ENGINE_BASE)))
- },
/* Xe3_LPG */
- { XE_RTP_NAME("14021402888"),
- XE_RTP_RULES(GRAPHICS_VERSION_RANGE(3000, 3001),
- FUNC(xe_rtp_match_first_render_or_compute)),
- XE_RTP_ACTIONS(SET(HALF_SLICE_CHICKEN7, CLEAR_OPTIMIZATION_DISABLE))
- },
{ XE_RTP_NAME("18034896535"),
XE_RTP_RULES(GRAPHICS_VERSION(3000), GRAPHICS_STEP(A0, B0),
FUNC(xe_rtp_match_first_render_or_compute)),
@@@ -643,34 -573,33 +573,33 @@@
SMP_FORCE_128B_OVERFETCH))
},
{ XE_RTP_NAME("14023061436"),
- XE_RTP_RULES(GRAPHICS_VERSION_RANGE(3000, 3001),
- FUNC(xe_rtp_match_first_render_or_compute), OR,
- GRAPHICS_VERSION_RANGE(3003, 3005),
+ XE_RTP_RULES(GRAPHICS_VERSION_RANGE(3000, 3005),
FUNC(xe_rtp_match_first_render_or_compute)),
XE_RTP_ACTIONS(SET(TDL_CHICKEN, QID_WAIT_FOR_THREAD_NOT_RUN_DISABLE))
},
- { XE_RTP_NAME("13012615864"),
- XE_RTP_RULES(GRAPHICS_VERSION_RANGE(3000, 3001), OR,
- GRAPHICS_VERSION_RANGE(3003, 3005),
- FUNC(xe_rtp_match_first_render_or_compute)),
- XE_RTP_ACTIONS(SET(TDL_TSL_CHICKEN, RES_CHK_SPR_DIS))
- },
{ XE_RTP_NAME("16023105232"),
XE_RTP_RULES(MEDIA_VERSION_RANGE(1301, 3000), OR,
GRAPHICS_VERSION_RANGE(2001, 3001)),
XE_RTP_ACTIONS(SET(RING_PSMI_CTL(0), RC_SEMA_IDLE_MSG_DISABLE,
XE_RTP_ACTION_FLAG(ENGINE_BASE)))
},
- { XE_RTP_NAME("14021402888"),
- XE_RTP_RULES(GRAPHICS_VERSION_RANGE(3003, 3005), FUNC(xe_rtp_match_first_render_or_compute)),
- XE_RTP_ACTIONS(SET(HALF_SLICE_CHICKEN7, CLEAR_OPTIMIZATION_DISABLE))
+
+ /* Xe3p_LPG*/
+
+ { XE_RTP_NAME("22021149932"),
+ XE_RTP_RULES(GRAPHICS_VERSION(3510), GRAPHICS_STEP(A0, B0),
+ FUNC(xe_rtp_match_first_render_or_compute)),
+ XE_RTP_ACTIONS(SET(LSC_CHICKEN_BIT_0_UDW, SAMPLER_LD_LSC_DISABLE))
},
- { XE_RTP_NAME("18041344222"),
- XE_RTP_RULES(GRAPHICS_VERSION(3000),
- FUNC(xe_rtp_match_first_render_or_compute),
- FUNC(xe_rtp_match_not_sriov_vf),
- FUNC(xe_rtp_match_gt_has_discontiguous_dss_groups)),
- XE_RTP_ACTIONS(SET(TDL_CHICKEN, EUSTALL_PERF_SAMPLING_DISABLE))
+ { XE_RTP_NAME("14025676848"),
+ XE_RTP_RULES(GRAPHICS_VERSION(3510), GRAPHICS_STEP(A0, B0),
+ FUNC(xe_rtp_match_first_render_or_compute)),
+ XE_RTP_ACTIONS(SET(LSC_CHICKEN_BIT_0_UDW, LSCFE_SAME_ADDRESS_ATOMICS_COALESCING_DISABLE))
+ },
+ { XE_RTP_NAME("16028951944"),
+ XE_RTP_RULES(GRAPHICS_VERSION(3510), GRAPHICS_STEP(A0, B0),
+ FUNC(xe_rtp_match_first_render_or_compute)),
+ XE_RTP_ACTIONS(SET(ROW_CHICKEN5, CPSS_AWARE_DIS))
},
};
@@@ -708,6 -637,30 +637,30 @@@ static const struct xe_rtp_entry_sr lrc
XE_RTP_RULES(GRAPHICS_VERSION(1200)),
XE_RTP_ACTIONS(SET(COMMON_SLICE_CHICKEN4, DISABLE_TDC_LOAD_BALANCING_CALC))
},
+ { XE_RTP_NAME("14019877138"),
+ XE_RTP_RULES(GRAPHICS_VERSION_RANGE(1255, 2004), ENGINE_CLASS(RENDER)),
+ XE_RTP_ACTIONS(SET(XEHP_PSS_CHICKEN, FD_END_COLLECT))
+ },
+ { XE_RTP_NAME("14019386621"),
+ XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, 2004), ENGINE_CLASS(RENDER)),
+ XE_RTP_ACTIONS(SET(VF_SCRATCHPAD, XE2_VFG_TED_CREDIT_INTERFACE_DISABLE))
+ },
+ { XE_RTP_NAME("14019988906"),
+ XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, 2004), ENGINE_CLASS(RENDER)),
+ XE_RTP_ACTIONS(SET(XEHP_PSS_CHICKEN, FLSH_IGNORES_PSD))
+ },
+ { XE_RTP_NAME("18033852989"),
+ XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, 2004), ENGINE_CLASS(RENDER)),
+ XE_RTP_ACTIONS(SET(COMMON_SLICE_CHICKEN1, DISABLE_BOTTOM_CLIP_RECTANGLE_TEST))
+ },
+ { XE_RTP_NAME("15016589081"),
+ XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, 2004), ENGINE_CLASS(RENDER)),
+ XE_RTP_ACTIONS(SET(CHICKEN_RASTER_1, DIS_CLIP_NEGATIVE_BOUNDING_BOX))
+ },
+ { XE_RTP_NAME("14026781792"),
+ XE_RTP_RULES(GRAPHICS_VERSION_RANGE(3000, 3510), ENGINE_CLASS(RENDER)),
+ XE_RTP_ACTIONS(SET(FF_MODE, DIS_TE_PATCH_CTRL))
+ },
/* DG1 */
@@@ -744,10 -697,6 +697,6 @@@
XE_RTP_RULES(PLATFORM(DG2)),
XE_RTP_ACTIONS(SET(CACHE_MODE_1, MSAA_OPTIMIZATION_REDUC_DISABLE))
},
- { XE_RTP_NAME("14019877138"),
- XE_RTP_RULES(PLATFORM(DG2)),
- XE_RTP_ACTIONS(SET(XEHP_PSS_CHICKEN, FD_END_COLLECT))
- },
/* PVC */
@@@ -765,29 -714,9 +714,9 @@@
XE_RTP_RULES(GRAPHICS_VERSION_RANGE(1270, 1274)),
XE_RTP_ACTIONS(SET(CACHE_MODE_1, MSAA_OPTIMIZATION_REDUC_DISABLE))
},
- { XE_RTP_NAME("14019877138"),
- XE_RTP_RULES(GRAPHICS_VERSION_RANGE(1270, 1274), ENGINE_CLASS(RENDER)),
- XE_RTP_ACTIONS(SET(XEHP_PSS_CHICKEN, FD_END_COLLECT))
- },
/* Xe2_LPG */
- { XE_RTP_NAME("14019386621"),
- XE_RTP_RULES(GRAPHICS_VERSION(2004), ENGINE_CLASS(RENDER)),
- XE_RTP_ACTIONS(SET(VF_SCRATCHPAD, XE2_VFG_TED_CREDIT_INTERFACE_DISABLE))
- },
- { XE_RTP_NAME("14019877138"),
- XE_RTP_RULES(GRAPHICS_VERSION(2004), ENGINE_CLASS(RENDER)),
- XE_RTP_ACTIONS(SET(XEHP_PSS_CHICKEN, FD_END_COLLECT))
- },
- { XE_RTP_NAME("14019988906"),
- XE_RTP_RULES(GRAPHICS_VERSION(2004), ENGINE_CLASS(RENDER)),
- XE_RTP_ACTIONS(SET(XEHP_PSS_CHICKEN, FLSH_IGNORES_PSD))
- },
- { XE_RTP_NAME("18033852989"),
- XE_RTP_RULES(GRAPHICS_VERSION(2004), ENGINE_CLASS(RENDER)),
- XE_RTP_ACTIONS(SET(COMMON_SLICE_CHICKEN1, DISABLE_BOTTOM_CLIP_RECTANGLE_TEST))
- },
{ XE_RTP_NAME("14021567978"),
XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, XE_RTP_END_VERSION_UNDEFINED),
ENGINE_CLASS(RENDER)),
@@@ -807,20 -736,9 +736,9 @@@
DIS_PARTIAL_AUTOSTRIP |
DIS_AUTOSTRIP))
},
- { XE_RTP_NAME("15016589081"),
- XE_RTP_RULES(GRAPHICS_VERSION(2004), ENGINE_CLASS(RENDER)),
- XE_RTP_ACTIONS(SET(CHICKEN_RASTER_1, DIS_CLIP_NEGATIVE_BOUNDING_BOX))
- },
/* Xe2_HPG */
- { XE_RTP_NAME("15010599737"),
- XE_RTP_RULES(GRAPHICS_VERSION(2001), ENGINE_CLASS(RENDER)),
- XE_RTP_ACTIONS(SET(CHICKEN_RASTER_1, DIS_SF_ROUND_NEAREST_EVEN))
- },
- { XE_RTP_NAME("14019386621"),
- XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, 2002), ENGINE_CLASS(RENDER)),
- XE_RTP_ACTIONS(SET(VF_SCRATCHPAD, XE2_VFG_TED_CREDIT_INTERFACE_DISABLE))
- },
+
{ XE_RTP_NAME("14020756599"),
XE_RTP_RULES(GRAPHICS_VERSION(2001), ENGINE_CLASS(RENDER)),
XE_RTP_ACTIONS(SET(WM_CHICKEN3, HIZ_PLANE_COMPRESSION_DIS))
@@@ -842,18 -760,10 +760,10 @@@
DIS_PARTIAL_AUTOSTRIP |
DIS_AUTOSTRIP))
},
- { XE_RTP_NAME("15016589081"),
- XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, 2002), ENGINE_CLASS(RENDER)),
- XE_RTP_ACTIONS(SET(CHICKEN_RASTER_1, DIS_CLIP_NEGATIVE_BOUNDING_BOX))
- },
{ XE_RTP_NAME("22021007897"),
XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, 2002), ENGINE_CLASS(RENDER)),
XE_RTP_ACTIONS(SET(COMMON_SLICE_CHICKEN4, SBE_PUSH_CONSTANT_BEHIND_FIX_ENABLE))
},
- { XE_RTP_NAME("18033852989"),
- XE_RTP_RULES(GRAPHICS_VERSION(2001), ENGINE_CLASS(RENDER)),
- XE_RTP_ACTIONS(SET(COMMON_SLICE_CHICKEN1, DISABLE_BOTTOM_CLIP_RECTANGLE_TEST))
- },
/* Xe3_LPG */
{ XE_RTP_NAME("14021490052"),
@@@ -945,7 -855,8 +855,8 @@@ void xe_wa_process_gt(struct xe_gt *gt
xe_rtp_process_ctx_enable_active_tracking(&ctx, gt->wa_active.gt,
ARRAY_SIZE(gt_was));
- xe_rtp_process_to_sr(&ctx, gt_was, ARRAY_SIZE(gt_was), >->reg_sr);
+ xe_rtp_process_to_sr(&ctx, gt_was, ARRAY_SIZE(gt_was),
+ >->reg_sr, false);
}
EXPORT_SYMBOL_IF_KUNIT(xe_wa_process_gt);
@@@ -963,7 -874,8 +874,8 @@@ void xe_wa_process_engine(struct xe_hw_
xe_rtp_process_ctx_enable_active_tracking(&ctx, hwe->gt->wa_active.engine,
ARRAY_SIZE(engine_was));
- xe_rtp_process_to_sr(&ctx, engine_was, ARRAY_SIZE(engine_was), &hwe->reg_sr);
+ xe_rtp_process_to_sr(&ctx, engine_was, ARRAY_SIZE(engine_was),
+ &hwe->reg_sr, false);
}
/**
@@@ -980,7 -892,8 +892,8 @@@ void xe_wa_process_lrc(struct xe_hw_eng
xe_rtp_process_ctx_enable_active_tracking(&ctx, hwe->gt->wa_active.lrc,
ARRAY_SIZE(lrc_was));
- xe_rtp_process_to_sr(&ctx, lrc_was, ARRAY_SIZE(lrc_was), &hwe->reg_lrc);
+ xe_rtp_process_to_sr(&ctx, lrc_was, ARRAY_SIZE(lrc_was),
+ &hwe->reg_lrc, true);
}
/**
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 48+ messages in thread* linux-next: manual merge of the drm tree with the drm-fixes tree
@ 2026-03-27 20:46 Mark Brown
0 siblings, 0 replies; 48+ messages in thread
From: Mark Brown @ 2026-03-27 20:46 UTC (permalink / raw)
To: Dave Airlie, DRI
Cc: Alex Deucher, Asad Kamal, Linux Kernel Mailing List,
Linux Next Mailing List
[-- Attachment #1: Type: text/plain, Size: 8039 bytes --]
Hi all,
Today's linux-next merge of the drm tree got a conflict in:
drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c
between commit:
cdbc3b62cfc278 ("drm/amd/pm: Skip redundant UCLK restore in smu_v13_0_6")
from the drm-fixes tree and commits:
17f11bbbc76c8e ("drm/amd/pm: Skip redundant UCLK restore in smu_v13_0_6")
5da89a8afca14a ("drm/amd/pm: Add custom fclk setting support")
from the drm tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
diff --combined drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c
index 870bcc86fd7948,47554118978298..00000000000000
--- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c
@@@ -373,6 -373,9 +373,9 @@@ static void smu_v13_0_12_init_caps(stru
} else {
smu_v13_0_12_tables_fini(smu);
}
+
+ if (fw_ver >= 0x04561000)
+ smu_v13_0_6_cap_set(smu, SMU_CAP(TEMP_AID_XCD_HBM));
}
static void smu_v13_0_6_init_caps(struct smu_context *smu)
@@@ -458,6 -461,7 +461,7 @@@
smu_v13_0_6_cap_set(smu, SMU_CAP(SDMA_RESET));
if ((pgm == 0 && fw_ver >= 0x00558200) ||
+ (pgm == 4 && fw_ver >= 0x04557100) ||
(pgm == 7 && fw_ver >= 0x07551400))
smu_v13_0_6_cap_set(smu, SMU_CAP(VCN_RESET));
}
@@@ -478,7 -482,7 +482,7 @@@ static int smu_v13_0_6_check_fw_version
{
int r;
- r = smu_v13_0_check_fw_version(smu);
+ r = smu_cmn_check_fw_version(smu);
/* Initialize caps flags once fw version is fetched */
if (!r)
smu_v13_0_x_init_caps(smu);
@@@ -1196,6 -1200,7 +1200,7 @@@ static int smu_v13_0_6_populate_umd_sta
struct smu_dpm_table *gfx_table = &dpm_context->dpm_tables.gfx_table;
struct smu_dpm_table *mem_table = &dpm_context->dpm_tables.uclk_table;
struct smu_dpm_table *soc_table = &dpm_context->dpm_tables.soc_table;
+ struct smu_dpm_table *fclk_table = &dpm_context->dpm_tables.fclk_table;
struct smu_umd_pstate_table *pstate_table = &smu->pstate_table;
pstate_table->gfxclk_pstate.min = SMU_DPM_TABLE_MIN(gfx_table);
@@@ -1213,6 -1218,12 +1218,12 @@@
pstate_table->socclk_pstate.curr.min = SMU_DPM_TABLE_MIN(soc_table);
pstate_table->socclk_pstate.curr.max = SMU_DPM_TABLE_MAX(soc_table);
+ pstate_table->fclk_pstate.min = SMU_DPM_TABLE_MIN(fclk_table);
+ pstate_table->fclk_pstate.peak = SMU_DPM_TABLE_MAX(fclk_table);
+ pstate_table->fclk_pstate.curr.min = SMU_DPM_TABLE_MIN(fclk_table);
+ pstate_table->fclk_pstate.curr.max = SMU_DPM_TABLE_MAX(fclk_table);
+ pstate_table->fclk_pstate.standard = SMU_DPM_TABLE_MIN(fclk_table);
+
if (gfx_table->count > SMU_13_0_6_UMD_PSTATE_GFXCLK_LEVEL &&
mem_table->count > SMU_13_0_6_UMD_PSTATE_MCLK_LEVEL &&
soc_table->count > SMU_13_0_6_UMD_PSTATE_SOCCLK_LEVEL) {
@@@ -1398,7 -1409,15 +1409,15 @@@ static int smu_v13_0_6_emit_clk_levels(
pstate_table->uclk_pstate.curr.min,
pstate_table->uclk_pstate.curr.max);
break;
+ case SMU_OD_FCLK:
+ if (!smu_cmn_feature_is_enabled(smu, SMU_FEATURE_DPM_FCLK_BIT))
+ return -EOPNOTSUPP;
+ size += sysfs_emit_at(buf, size, "%s:\n", "OD_FCLK");
+ size += sysfs_emit_at(buf, size, "0: %uMhz\n1: %uMhz\n",
+ pstate_table->fclk_pstate.curr.min,
+ pstate_table->fclk_pstate.curr.max);
+ break;
case SMU_SCLK:
case SMU_GFXCLK:
single_dpm_table = &(dpm_context->dpm_tables.gfx_table);
@@@ -2040,7 -2059,7 +2059,7 @@@ static int smu_v13_0_6_set_soft_freq_li
int ret = 0;
if (clk_type != SMU_GFXCLK && clk_type != SMU_SCLK &&
- clk_type != SMU_UCLK)
+ clk_type != SMU_UCLK && clk_type != SMU_FCLK)
return -EINVAL;
if ((smu_dpm->dpm_level != AMD_DPM_FORCED_LEVEL_MANUAL) &&
@@@ -2081,6 -2100,15 +2100,15 @@@
pstate_table->uclk_pstate.curr.max = max;
}
+ if (clk_type == SMU_FCLK) {
+ if (max == pstate_table->fclk_pstate.curr.max)
+ return 0;
+
+ ret = smu_v13_0_set_soft_freq_limited_range(smu, SMU_FCLK, 0, max, false);
+ if (!ret)
+ pstate_table->fclk_pstate.curr.max = max;
+ }
+
return ret;
}
@@@ -2123,6 -2151,7 +2151,7 @@@ static int smu_v13_0_6_usr_edit_dpm_tab
struct smu_dpm_context *smu_dpm = &(smu->smu_dpm);
struct smu_13_0_dpm_context *dpm_context = smu_dpm->dpm_context;
struct smu_dpm_table *uclk_table = &dpm_context->dpm_tables.uclk_table;
+ struct smu_dpm_table *fclk_table = &dpm_context->dpm_tables.fclk_table;
struct smu_umd_pstate_table *pstate_table = &smu->pstate_table;
uint32_t min_clk;
uint32_t max_clk;
@@@ -2203,6 -2232,40 +2232,40 @@@
pstate_table->uclk_pstate.custom.max = input[1];
}
break;
+ case PP_OD_EDIT_FCLK_TABLE:
+ if (size != 2) {
+ dev_err(smu->adev->dev,
+ "Input parameter number not correct\n");
+ return -EINVAL;
+ }
+
+ if (!smu_cmn_feature_is_enabled(smu,
+ SMU_FEATURE_DPM_FCLK_BIT)) {
+ dev_warn(smu->adev->dev,
+ "FCLK limits setting not supported!\n");
+ return -EOPNOTSUPP;
+ }
+
+ max_clk = SMU_DPM_TABLE_MAX(&dpm_context->dpm_tables.fclk_table);
+ if (input[0] == 0) {
+ dev_info(smu->adev->dev,
+ "Setting min FCLK level is not supported\n");
+ return -EOPNOTSUPP;
+ } else if (input[0] == 1) {
+ if (input[1] > max_clk) {
+ dev_warn(smu->adev->dev,
+ "Maximum FCLK (%ld) MHz specified is greater than the maximum allowed (%d) MHz\n",
+ input[1], max_clk);
+ pstate_table->fclk_pstate.custom.max =
+ pstate_table->fclk_pstate.curr.max;
+ return -EINVAL;
+ }
+
+ pstate_table->fclk_pstate.custom.max = input[1];
+ } else {
+ return -EINVAL;
+ }
+ break;
case PP_OD_RESTORE_DEFAULT_TABLE:
if (size != 0) {
@@@ -2232,6 -2295,17 +2295,17 @@@
if (ret)
return ret;
}
+
+ if (SMU_DPM_TABLE_MAX(fclk_table) !=
+ pstate_table->fclk_pstate.curr.max) {
+ max_clk = SMU_DPM_TABLE_MAX(&dpm_context->dpm_tables.fclk_table);
+ min_clk = SMU_DPM_TABLE_MIN(&dpm_context->dpm_tables.fclk_table);
+ ret = smu_v13_0_6_set_soft_freq_limited_range(smu,
+ SMU_FCLK, min_clk,
+ max_clk, false);
+ if (ret)
+ return ret;
+ }
smu_v13_0_reset_custom_level(smu);
}
break;
@@@ -2258,6 -2332,16 +2332,16 @@@
if (ret)
return ret;
+ if (pstate_table->fclk_pstate.custom.max) {
+ min_clk = pstate_table->fclk_pstate.curr.min;
+ max_clk = pstate_table->fclk_pstate.custom.max;
+ ret = smu_v13_0_6_set_soft_freq_limited_range(smu,
+ SMU_FCLK, min_clk,
+ max_clk, false);
+ if (ret)
+ return ret;
+ }
+
if (!pstate_table->uclk_pstate.custom.max)
return 0;
@@@ -3163,14 -3247,25 +3247,25 @@@ static int smu_v13_0_6_reset_vcn(struc
static int smu_v13_0_6_ras_send_msg(struct smu_context *smu, enum smu_message_type msg, uint32_t param, uint32_t *read_arg)
{
+ struct amdgpu_device *adev = smu->adev;
int ret;
+ if (amdgpu_sriov_vf(adev))
+ return -EOPNOTSUPP;
+
switch (msg) {
case SMU_MSG_QueryValidMcaCount:
case SMU_MSG_QueryValidMcaCeCount:
case SMU_MSG_McaBankDumpDW:
case SMU_MSG_McaBankCeDumpDW:
case SMU_MSG_ClearMcaOnRead:
+ case SMU_MSG_GetRASTableVersion:
+ case SMU_MSG_GetBadPageCount:
+ case SMU_MSG_GetBadPageMcaAddr:
+ case SMU_MSG_SetTimestamp:
+ case SMU_MSG_GetTimestamp:
+ case SMU_MSG_GetBadPageIpid:
+ case SMU_MSG_EraseRasTable:
ret = smu_cmn_send_smc_msg_with_param(smu, msg, param, read_arg);
break;
default:
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 48+ messages in thread* linux-next: manual merge of the drm tree with the drm-fixes tree
@ 2026-03-09 14:35 Mark Brown
0 siblings, 0 replies; 48+ messages in thread
From: Mark Brown @ 2026-03-09 14:35 UTC (permalink / raw)
To: Dave Airlie, DRI
Cc: Alex Deucher, Linux Kernel Mailing List, Linux Next Mailing List,
Sunil Khatri
[-- Attachment #1: Type: text/plain, Size: 983 bytes --]
Hi all,
Today's linux-next merge of the drm tree got a conflict in:
drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c
between commit:
65b5c326ce410 ("drm/amdgpu/userq: refcount userqueues to avoid any race conditions")
from the drm-fixes tree and commit:
4952189b284d4 ("drm/amdgpu/userq: refcount userqueues to avoid any race conditions")
from the drm tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
diff --cc drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c
index 5239b06b9ab03,d8ce7b3733e75..0000000000000
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 48+ messages in thread* linux-next: manual merge of the drm tree with the drm-fixes tree
@ 2026-03-06 13:47 Mark Brown
0 siblings, 0 replies; 48+ messages in thread
From: Mark Brown @ 2026-03-06 13:47 UTC (permalink / raw)
To: Dave Airlie, DRI
Cc: Linux Kernel Mailing List, Linux Next Mailing List, Matt Roper,
Rodrigo Vivi
[-- Attachment #1: Type: text/plain, Size: 880 bytes --]
Hi all,
Today's linux-next merge of the drm tree got a conflict in:
drivers/gpu/drm/xe/xe_wa.c
between commit:
89865e6dc8487 ("drm/xe/xe2_hpg: Correct implementation of Wa_16025250150")
from the drm-fixes tree and commit:
941f538b0af81 ("drm/xe: Consolidate workaround entries for Wa_16021867713")
from the drm tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
diff --cc drivers/gpu/drm/xe/xe_wa.c
index 462c2fa712e01,289acac2c3c82..0000000000000
--- a/drivers/gpu/drm/xe/xe_wa.c
+++ b/drivers/gpu/drm/xe/xe_wa.c
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 48+ messages in thread* linux-next: manual merge of the drm tree with the drm-fixes tree
@ 2026-03-06 13:47 Mark Brown
0 siblings, 0 replies; 48+ messages in thread
From: Mark Brown @ 2026-03-06 13:47 UTC (permalink / raw)
To: Dave Airlie, DRI
Cc: Linux Kernel Mailing List, Linux Next Mailing List,
Michal Wajdeczko, Rodrigo Vivi, Tomasz Lis
[-- Attachment #1: Type: text/plain, Size: 979 bytes --]
Hi all,
Today's linux-next merge of the drm tree got a conflict in:
drivers/gpu/drm/xe/xe_exec_queue.c
between commit:
99f9b5343cae8 ("drm/xe/queue: Call fini on exec queue creation fail")
from the drm-fixes tree and commits:
393e5fea6f7d7 ("drm/xe/queue: Call fini on exec queue creation fail")
ec172c7befc4a ("drm/xe: Wrappers for setting and getting LRC references")
from the drm tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
diff --cc drivers/gpu/drm/xe/xe_exec_queue.c
index 8ecdf949f9e4c,2d0e73a6a6eee..0000000000000
--- a/drivers/gpu/drm/xe/xe_exec_queue.c
+++ b/drivers/gpu/drm/xe/xe_exec_queue.c
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 48+ messages in thread* linux-next: manual merge of the drm tree with the drm-fixes tree
@ 2026-01-30 15:37 Mark Brown
0 siblings, 0 replies; 48+ messages in thread
From: Mark Brown @ 2026-01-30 15:37 UTC (permalink / raw)
To: Dave Airlie, DRI
Cc: Brian Nguyen, Linux Kernel Mailing List, Linux Next Mailing List,
Lukasz Laguna, Matthew Brost, Matt Roper, Michal Wajdeczko,
Nitin Gote, Oak Zeng, Tejas Upadhyay, Thomas Hellström
[-- Attachment #1: Type: text/plain, Size: 3823 bytes --]
Hi all,
Today's linux-next merge of the drm tree got conflicts in:
drivers/gpu/drm/xe/xe_pci.c
drivers/gpu/drm/xe/xe_pci_types.h
between commit:
ca8dcfedac480 ("drm/xe: derive mem copy capability from graphics version")
from the drm-fixes tree and commits:
83f4151787c5d ("drm/xe/lnl: Drop pre-production workaround support")
7ef2d25e47736 ("drm/xe: Track pre-production workaround support")
9b1a0e0a15c97 ("drm/xe: Add page reclamation info to device info")
79cb005c71343 ("drm/xe: Add device flag to indicate standalone MERT")
from the drm tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
diff --cc drivers/gpu/drm/xe/xe_pci.c
index 2aa883f5ef797,09189ff3da441..0000000000000
--- a/drivers/gpu/drm/xe/xe_pci.c
+++ b/drivers/gpu/drm/xe/xe_pci.c
@@@ -358,10 -365,15 +364,14 @@@ static const struct xe_device_desc bmg_
.has_fan_control = true,
.has_flat_ccs = 1,
.has_mbx_power_limits = true,
+ .has_mbx_thermal_info = true,
.has_gsc_nvm = 1,
.has_heci_cscfi = 1,
+ .has_i2c = true,
.has_late_bind = true,
+ .has_pre_prod_wa = 1,
+ .has_soc_remapper_telem = true,
.has_sriov = true,
- .has_mem_copy_instr = true,
.max_gt_per_tile = 2,
.needs_scratch = true,
.subplatforms = (const struct xe_subplatform_desc[]) {
@@@ -378,6 -390,9 +388,8 @@@ static const struct xe_device_desc ptl_
.has_display = true,
.has_flat_ccs = 1,
.has_sriov = true,
- .has_mem_copy_instr = true,
+ .has_pre_prod_wa = 1,
+ .has_pxp = true,
.max_gt_per_tile = 2,
.needs_scratch = true,
.needs_shared_vf_gt_wq = true,
@@@ -390,6 -405,8 +402,7 @@@ static const struct xe_device_desc nvls
.dma_mask_size = 46,
.has_display = true,
.has_flat_ccs = 1,
- .has_mem_copy_instr = true,
+ .has_pre_prod_wa = 1,
.max_gt_per_tile = 2,
.require_force_probe = true,
.va_bits = 48,
@@@ -666,11 -692,18 +688,17 @@@ static int xe_info_init_early(struct xe
xe->info.has_gsc_nvm = desc->has_gsc_nvm;
xe->info.has_heci_gscfi = desc->has_heci_gscfi;
xe->info.has_heci_cscfi = desc->has_heci_cscfi;
+ xe->info.has_i2c = desc->has_i2c;
xe->info.has_late_bind = desc->has_late_bind;
xe->info.has_llc = desc->has_llc;
+ xe->info.has_mert = desc->has_mert;
+ xe->info.has_page_reclaim_hw_assist = desc->has_page_reclaim_hw_assist;
+ xe->info.has_pre_prod_wa = desc->has_pre_prod_wa;
xe->info.has_pxp = desc->has_pxp;
+ xe->info.has_soc_remapper_sysctrl = desc->has_soc_remapper_sysctrl;
+ xe->info.has_soc_remapper_telem = desc->has_soc_remapper_telem;
xe->info.has_sriov = xe_configfs_primary_gt_allowed(to_pci_dev(xe->drm.dev)) &&
desc->has_sriov;
- xe->info.has_mem_copy_instr = desc->has_mem_copy_instr;
xe->info.skip_guc_pc = desc->skip_guc_pc;
xe->info.skip_mtcfg = desc->skip_mtcfg;
xe->info.skip_pcode = desc->skip_pcode;
diff --cc drivers/gpu/drm/xe/xe_pci_types.h
index a4451bdc79fb3,20acc5349ee6d..0000000000000
--- a/drivers/gpu/drm/xe/xe_pci_types.h
+++ b/drivers/gpu/drm/xe/xe_pci_types.h
@@@ -46,7 -48,14 +48,13 @@@ struct xe_device_desc
u8 has_late_bind:1;
u8 has_llc:1;
u8 has_mbx_power_limits:1;
+ u8 has_mbx_thermal_info:1;
- u8 has_mem_copy_instr:1;
+ u8 has_mert:1;
+ u8 has_pre_prod_wa:1;
+ u8 has_page_reclaim_hw_assist:1;
u8 has_pxp:1;
+ u8 has_soc_remapper_sysctrl:1;
+ u8 has_soc_remapper_telem:1;
u8 has_sriov:1;
u8 needs_scratch:1;
u8 skip_guc_pc:1;
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 48+ messages in thread* linux-next: manual merge of the drm tree with the drm-fixes tree
@ 2026-01-30 15:28 Mark Brown
0 siblings, 0 replies; 48+ messages in thread
From: Mark Brown @ 2026-01-30 15:28 UTC (permalink / raw)
To: Dave Airlie, DRI
Cc: Alex Deucher, Jack Xiao, Linux Kernel Mailing List,
Linux Next Mailing List
[-- Attachment #1: Type: text/plain, Size: 3625 bytes --]
Hi all,
Today's linux-next merge of the drm tree got conflicts in:
drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c
drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c
between commits:
3eb46fbb601f9 ("drm/amdgpu/gfx11: adjust KGQ reset sequence")
dfd64f6e8cd7b ("drm/amdgpu/gfx12: adjust KGQ reset sequence")
from the drm-fixes tree and commit:
d09c7e266c8cd ("drm/amdgpu/mes: add multi-xcc support")
from the drm tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
diff --cc drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c
index e642236ea2c51,3a4ca104b1612..0000000000000
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c
@@@ -6828,7 -6832,7 +6833,7 @@@ static int gfx_v11_0_reset_kgq(struct a
amdgpu_ring_reset_helper_begin(ring, timedout_fence);
- r = amdgpu_mes_reset_legacy_queue(ring->adev, ring, vmid, use_mmio);
- r = amdgpu_mes_reset_legacy_queue(ring->adev, ring, vmid, false, 0);
++ r = amdgpu_mes_reset_legacy_queue(ring->adev, ring, vmid, use_mmio, 0);
if (r) {
dev_warn(adev->dev, "reset via MES failed and try pipe reset %d\n", r);
@@@ -6837,18 -6841,16 +6842,18 @@@
return r;
}
- r = gfx_v11_0_kgq_init_queue(ring, true);
- if (r) {
- dev_err(adev->dev, "failed to init kgq\n");
- return r;
- }
+ if (use_mmio) {
+ r = gfx_v11_0_kgq_init_queue(ring, true);
+ if (r) {
+ dev_err(adev->dev, "failed to init kgq\n");
+ return r;
+ }
- r = amdgpu_mes_map_legacy_queue(adev, ring);
- r = amdgpu_mes_map_legacy_queue(adev, ring, 0);
- if (r) {
- dev_err(adev->dev, "failed to remap kgq\n");
- return r;
++ r = amdgpu_mes_map_legacy_queue(adev, ring, 0);
+ if (r) {
+ dev_err(adev->dev, "failed to remap kgq\n");
+ return r;
+ }
}
return amdgpu_ring_reset_helper_end(ring, timedout_fence);
diff --cc drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c
index 4aab89a9ab401,40660b05f9794..0000000000000
--- a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c
@@@ -5302,7 -5296,7 +5297,7 @@@ static int gfx_v12_0_reset_kgq(struct a
amdgpu_ring_reset_helper_begin(ring, timedout_fence);
- r = amdgpu_mes_reset_legacy_queue(ring->adev, ring, vmid, use_mmio);
- r = amdgpu_mes_reset_legacy_queue(ring->adev, ring, vmid, false, 0);
++ r = amdgpu_mes_reset_legacy_queue(ring->adev, ring, vmid, use_mmio, 0);
if (r) {
dev_warn(adev->dev, "reset via MES failed and try pipe reset %d\n", r);
r = gfx_v12_reset_gfx_pipe(ring);
@@@ -5310,18 -5304,16 +5305,18 @@@
return r;
}
- r = gfx_v12_0_kgq_init_queue(ring, true);
- if (r) {
- dev_err(adev->dev, "failed to init kgq\n");
- return r;
- }
+ if (use_mmio) {
+ r = gfx_v12_0_kgq_init_queue(ring, true);
+ if (r) {
+ dev_err(adev->dev, "failed to init kgq\n");
+ return r;
+ }
- r = amdgpu_mes_map_legacy_queue(adev, ring);
- r = amdgpu_mes_map_legacy_queue(adev, ring, 0);
- if (r) {
- dev_err(adev->dev, "failed to remap kgq\n");
- return r;
++ r = amdgpu_mes_map_legacy_queue(adev, ring, 0);
+ if (r) {
+ dev_err(adev->dev, "failed to remap kgq\n");
+ return r;
+ }
}
return amdgpu_ring_reset_helper_end(ring, timedout_fence);
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 48+ messages in thread* linux-next: manual merge of the drm tree with the drm-fixes tree
@ 2026-01-23 17:45 Mark Brown
0 siblings, 0 replies; 48+ messages in thread
From: Mark Brown @ 2026-01-23 17:45 UTC (permalink / raw)
To: Dave Airlie, DRI
Cc: Linux Kernel Mailing List, Linux Next Mailing List,
Thomas Hellström
[-- Attachment #1: Type: text/plain, Size: 4640 bytes --]
Hi all,
Today's linux-next merge of the drm tree got a conflict in:
include/drm/drm_pagemap.h
between commits:
754c232384386 ("drm/pagemap, drm/xe: Ensure that the devmem allocation is idle before use")
bdcdf968be314 ("drm, drm/xe: Fix xe userptr in the absence of CONFIG_DEVICE_PRIVATE")
from the drm-fixes tree and commits:
a599b98607dec ("drm/pagemap, drm/xe: Add refcounting to struct drm_pagemap")
75af93b3f5d0a ("drm/pagemap, drm/xe: Support destination migration over interconnect")
77f14f2f2d73f ("drm/pagemap: Add a drm_pagemap cache and shrinker")
from the drm tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
diff --cc include/drm/drm_pagemap.h
index eb29e5309f0ab,46e9c58f09e01..0000000000000
--- a/include/drm/drm_pagemap.h
+++ b/include/drm/drm_pagemap.h
@@@ -209,19 -243,56 +243,69 @@@ struct drm_pagemap_devmem_ops
struct dma_fence *pre_migrate_fence);
};
+#if IS_ENABLED(CONFIG_ZONE_DEVICE)
+
+struct drm_pagemap *drm_pagemap_page_to_dpagemap(struct page *page);
+
+#else
+
+static inline struct drm_pagemap *drm_pagemap_page_to_dpagemap(struct page *page)
+{
+ return NULL;
+}
+
+#endif /* IS_ENABLED(CONFIG_ZONE_DEVICE) */
+
+ int drm_pagemap_init(struct drm_pagemap *dpagemap,
+ struct dev_pagemap *pagemap,
+ struct drm_device *drm,
+ const struct drm_pagemap_ops *ops);
+
+ struct drm_pagemap *drm_pagemap_create(struct drm_device *drm,
+ struct dev_pagemap *pagemap,
+ const struct drm_pagemap_ops *ops);
+
+ #if IS_ENABLED(CONFIG_DRM_GPUSVM)
+
+ void drm_pagemap_put(struct drm_pagemap *dpagemap);
+
+ #else
+
+ static inline void drm_pagemap_put(struct drm_pagemap *dpagemap)
+ {
+ }
+
+ #endif /* IS_ENABLED(CONFIG_DRM_GPUSVM) */
+
+ /**
+ * drm_pagemap_get() - Obtain a reference on a struct drm_pagemap
+ * @dpagemap: Pointer to the struct drm_pagemap, or NULL.
+ *
+ * Return: Pointer to the struct drm_pagemap, or NULL.
+ */
+ static inline struct drm_pagemap *
+ drm_pagemap_get(struct drm_pagemap *dpagemap)
+ {
+ if (likely(dpagemap))
+ kref_get(&dpagemap->ref);
+
+ return dpagemap;
+ }
+
+ /**
+ * drm_pagemap_get_unless_zero() - Obtain a reference on a struct drm_pagemap
+ * unless the current reference count is zero.
+ * @dpagemap: Pointer to the drm_pagemap or NULL.
+ *
+ * Return: A pointer to @dpagemap if the reference count was successfully
+ * incremented. NULL if @dpagemap was NULL, or its refcount was 0.
+ */
+ static inline struct drm_pagemap * __must_check
+ drm_pagemap_get_unless_zero(struct drm_pagemap *dpagemap)
+ {
+ return (dpagemap && kref_get_unless_zero(&dpagemap->ref)) ? dpagemap : NULL;
+ }
+
/**
* struct drm_pagemap_devmem - Structure representing a GPU SVM device memory allocation
*
@@@ -246,8 -317,23 +330,25 @@@ struct drm_pagemap_devmem
struct dma_fence *pre_migrate_fence;
};
+ /**
+ * struct drm_pagemap_migrate_details - Details to govern migration.
+ * @timeslice_ms: The time requested for the migrated pagemap pages to
+ * be present in @mm before being allowed to be migrated back.
+ * @can_migrate_same_pagemap: Whether the copy function as indicated by
+ * the @source_peer_migrates flag, can migrate device pages within a
+ * single drm_pagemap.
+ * @source_peer_migrates: Whether on p2p migration, The source drm_pagemap
+ * should use the copy_to_ram() callback rather than the destination
+ * drm_pagemap should use the copy_to_devmem() callback.
+ */
+ struct drm_pagemap_migrate_details {
+ unsigned long timeslice_ms;
+ u32 can_migrate_same_pagemap : 1;
+ u32 source_peer_migrates : 1;
+ };
+
+#if IS_ENABLED(CONFIG_ZONE_DEVICE)
+
int drm_pagemap_migrate_to_devmem(struct drm_pagemap_devmem *devmem_allocation,
struct mm_struct *mm,
unsigned long start, unsigned long end,
@@@ -269,6 -356,7 +369,10 @@@ int drm_pagemap_populate_mm(struct drm_
struct mm_struct *mm,
unsigned long timeslice_ms);
+#endif /* IS_ENABLED(CONFIG_ZONE_DEVICE) */
+
+ void drm_pagemap_destroy(struct drm_pagemap *dpagemap, bool is_atomic_or_reclaim);
+
+ int drm_pagemap_reinit(struct drm_pagemap *dpagemap);
++
#endif
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 48+ messages in thread* linux-next: manual merge of the drm tree with the drm-fixes tree
@ 2025-06-27 2:58 Stephen Rothwell
0 siblings, 0 replies; 48+ messages in thread
From: Stephen Rothwell @ 2025-06-27 2:58 UTC (permalink / raw)
To: Dave Airlie
Cc: DRI, Linux Kernel Mailing List, Linux Next Mailing List,
Maarten Lankhorst, Michal Wajdeczko, Thomas Hellström
[-- Attachment #1: Type: text/plain, Size: 2103 bytes --]
Hi all,
Today's linux-next merge of the drm tree got a conflict in:
drivers/gpu/drm/xe/xe_ggtt.c
between commit:
af2b588abe00 ("drm/xe: Process deferred GGTT node removals on device unwind")
from the drm-fixes tree and commit:
b2d6fd7ac598 ("drm/xe: Do not rely on GGTT internals in xe_guc_buf kunit tests")
from the drm tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
--
Cheers,
Stephen Rothwell
diff --cc drivers/gpu/drm/xe/xe_ggtt.c
index 2c799958c1e4,7b11fa1356f0..000000000000
--- a/drivers/gpu/drm/xe/xe_ggtt.c
+++ b/drivers/gpu/drm/xe/xe_ggtt.c
@@@ -201,13 -222,22 +222,29 @@@ static const struct xe_ggtt_pt_ops xelp
.ggtt_set_pte = xe_ggtt_set_pte_and_flush,
};
+static void dev_fini_ggtt(void *arg)
+{
+ struct xe_ggtt *ggtt = arg;
+
+ drain_workqueue(ggtt->wq);
+}
+
+ static void __xe_ggtt_init_early(struct xe_ggtt *ggtt, u32 reserved)
+ {
+ drm_mm_init(&ggtt->mm, reserved,
+ ggtt->size - reserved);
+ mutex_init(&ggtt->lock);
+ primelockdep(ggtt);
+ }
+
+ int xe_ggtt_init_kunit(struct xe_ggtt *ggtt, u32 reserved, u32 size)
+ {
+ ggtt->size = size;
+ __xe_ggtt_init_early(ggtt, reserved);
+ return 0;
+ }
+ EXPORT_SYMBOL_IF_KUNIT(xe_ggtt_init_kunit);
+
/**
* xe_ggtt_init_early - Early GGTT initialization
* @ggtt: the &xe_ggtt to be initialized
@@@ -264,12 -290,8 +297,12 @@@ int xe_ggtt_init_early(struct xe_ggtt *
if (err)
return err;
+ err = devm_add_action_or_reset(xe->drm.dev, dev_fini_ggtt, ggtt);
+ if (err)
+ return err;
+
if (IS_SRIOV_VF(xe)) {
- err = xe_gt_sriov_vf_prepare_ggtt(xe_tile_get_gt(ggtt->tile, 0));
+ err = xe_tile_sriov_vf_prepare_ggtt(ggtt->tile);
if (err)
return err;
}
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 48+ messages in thread* linux-next: manual merge of the drm tree with the drm-fixes tree
@ 2024-10-18 1:56 Stephen Rothwell
0 siblings, 0 replies; 48+ messages in thread
From: Stephen Rothwell @ 2024-10-18 1:56 UTC (permalink / raw)
To: Dave Airlie
Cc: Ankit Nautiyal, Imre Deak, Joonas Lahtinen, DRI,
Linux Kernel Mailing List, Linux Next Mailing List
[-- Attachment #1: Type: text/plain, Size: 1814 bytes --]
Hi all,
Today's linux-next merge of the drm tree got a conflict in:
drivers/gpu/drm/i915/display/intel_dp_mst.c
between commit:
69b3d8721267 ("drm/i915/dp_mst: Handle error during DSC BW overhead/slice calculation")
from the drm-fixes tree and commit:
f2e2092a979c ("drm/i915/display: Use joined pipes in dsc helpers for slices, bpp")
from the drm tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
--
Cheers,
Stephen Rothwell
diff --cc drivers/gpu/drm/i915/display/intel_dp_mst.c
index eeaedd979354,4765bda154c1..000000000000
--- a/drivers/gpu/drm/i915/display/intel_dp_mst.c
+++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c
@@@ -147,19 -156,6 +148,19 @@@ static int intel_dp_mst_calc_pbn(int pi
return DIV_ROUND_UP(effective_data_rate * 64, 54 * 1000);
}
+static int intel_dp_mst_dsc_get_slice_count(const struct intel_connector *connector,
+ const struct intel_crtc_state *crtc_state)
+{
+ const struct drm_display_mode *adjusted_mode =
+ &crtc_state->hw.adjusted_mode;
- int num_joined_pipes = crtc_state->joiner_pipes;
++ int num_joined_pipes = intel_crtc_num_joined_pipes(crtc_state);
+
+ return intel_dp_dsc_get_slice_count(connector,
+ adjusted_mode->clock,
+ adjusted_mode->hdisplay,
+ num_joined_pipes);
+}
+
static int intel_dp_mst_find_vcpi_slots_for_bpp(struct intel_encoder *encoder,
struct intel_crtc_state *crtc_state,
int max_bpp,
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 48+ messages in thread* linux-next: manual merge of the drm tree with the drm-fixes tree
@ 2024-06-27 15:08 Mark Brown
0 siblings, 0 replies; 48+ messages in thread
From: Mark Brown @ 2024-06-27 15:08 UTC (permalink / raw)
To: Dave Airlie, DRI
Cc: Alex Deucher, Lijo Lazar, Li Ma, Linux Kernel Mailing List,
Linux Next Mailing List
[-- Attachment #1: Type: text/plain, Size: 1325 bytes --]
Hi all,
Today's linux-next merge of the drm tree got a conflict in:
drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h
between commit:
c223376b3019a ("drm/amd/swsmu: add MALL init support workaround for smu_v14_0_1")
from the drm-fixes tree and commit:
ec41bdd82e9b0 ("drm/amd/pm: Update PMFW messages for SMUv13.0.6")
from the drm tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
diff --cc drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h
index 2e32b085824ae,12a7b0634ed56..0000000000000
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h
@@@ -272,7 -272,8 +272,8 @@@
__SMU_DUMMY_MAP(SetSoftMinVpe), \
__SMU_DUMMY_MAP(GetMetricsVersion), \
__SMU_DUMMY_MAP(EnableUCLKShadow), \
- __SMU_DUMMY_MAP(RmaDueToBadPageThreshold),\
+ __SMU_DUMMY_MAP(RmaDueToBadPageThreshold), \
+ __SMU_DUMMY_MAP(SelectPstatePolicy), \
__SMU_DUMMY_MAP(MALLPowerController), \
__SMU_DUMMY_MAP(MALLPowerState),
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 48+ messages in thread* linux-next: manual merge of the drm tree with the drm-fixes tree
@ 2024-06-27 15:00 Mark Brown
0 siblings, 0 replies; 48+ messages in thread
From: Mark Brown @ 2024-06-27 15:00 UTC (permalink / raw)
To: Dave Airlie, DRI
Cc: Alex Deucher, Frank Min, Linux Kernel Mailing List,
Linux Next Mailing List
[-- Attachment #1: Type: text/plain, Size: 947 bytes --]
Hi all,
Today's linux-next merge of the drm tree got a conflict in:
drivers/gpu/drm/amd/amdgpu/amdgpu_atomfirmware.c
between commit:
f6f49dda49db7 ("drm/amdgpu/atomfirmware: fix parsing of vram_info")
from the drm-fixes tree and commit:
9a55c77978308 ("drm/amdgpu: fix getting vram info for gfx12")
from the drm tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
diff --cc drivers/gpu/drm/amd/amdgpu/amdgpu_atomfirmware.c
index 2e13c7c4b2b41,f932bec6e5345..0000000000000
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_atomfirmware.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_atomfirmware.c
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 48+ messages in thread* linux-next: manual merge of the drm tree with the drm-fixes tree
@ 2024-06-21 13:21 Mark Brown
0 siblings, 0 replies; 48+ messages in thread
From: Mark Brown @ 2024-06-21 13:21 UTC (permalink / raw)
To: Dave Airlie, DRI
Cc: Alex Deucher, Dillon Varone, Linux Kernel Mailing List,
Linux Next Mailing List, Michael Strauss, Nicholas Kazlauskas
[-- Attachment #1: Type: text/plain, Size: 1772 bytes --]
Hi all,
Today's linux-next merge of the drm tree got a conflict in:
drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_init.c
between commit:
c03d770c0b014 ("drm/amd/display: Attempt to avoid empty TUs when endpoint is DPIA")
from the drm-fixes tree and commits:
47745acc5e8dd ("drm/amd/display: Add trigger FIFO resync path for DCN35")
0127f0445f7c1 ("drm/amd/display: Refactor input mode programming for DIG FIFO")
from the drm tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_init.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_init.c
index 199781233fd5f..30e6a63988391 100644
--- a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_init.c
+++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_init.c
@@ -157,8 +160,8 @@ static const struct hwseq_private_funcs dcn35_private_funcs = {
.set_mcm_luts = dcn32_set_mcm_luts,
.setup_hpo_hw_control = dcn35_setup_hpo_hw_control,
.calculate_dccg_k1_k2_values = dcn32_calculate_dccg_k1_k2_values,
- .set_pixels_per_cycle = dcn32_set_pixels_per_cycle,
- .is_dp_dig_pixel_rate_div_policy = dcn35_is_dp_dig_pixel_rate_div_policy,
+ .resync_fifo_dccg_dio = dcn314_resync_fifo_dccg_dio,
+ .is_dp_dig_pixel_rate_div_policy = dcn32_is_dp_dig_pixel_rate_div_policy,
.dsc_pg_control = dcn35_dsc_pg_control,
.dsc_pg_status = dcn32_dsc_pg_status,
.enable_plane = dcn35_enable_plane,
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 48+ messages in thread* linux-next: manual merge of the drm tree with the drm-fixes tree
@ 2023-08-18 1:53 Stephen Rothwell
0 siblings, 0 replies; 48+ messages in thread
From: Stephen Rothwell @ 2023-08-18 1:53 UTC (permalink / raw)
To: Dave Airlie, DRI
Cc: Alex Deucher, Evan Quan, Kenneth Feng, Linux Kernel Mailing List,
Linux Next Mailing List
[-- Attachment #1: Type: text/plain, Size: 1803 bytes --]
Hi all,
Today's linux-next merge of the drm tree got a conflict in:
drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c
between commits:
61319b8e3b58 ("drm/amd/pm: disable the SMU13 OD feature support temporarily")
b6360a5ec31d ("drm/amd/pm: disallow the fan setting if there is no fan on smu 13.0.0")
from the drm-fixes tree and commit:
510d242f498a ("drm/amd/pm: disable the SMU13 OD feature support temporarily")
from the drm tree.
The git automatic resolution got this wrong by adding an extra "#if 0"
above the duplicated "PPTable_t *pptable = smu->smu_table.driver_pptable;"
from b6360a5ec31d.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
--
Cheers,
Stephen Rothwell
diff --cc drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c
index 0fb6be11a0cc,fddcd834bcec..000000000000
--- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c
@@@ -331,9 -331,8 +331,8 @@@ static int smu_v13_0_0_check_powerplay_
struct smu_13_0_0_powerplay_table *powerplay_table =
table_context->power_play_table;
struct smu_baco_context *smu_baco = &smu->smu_baco;
-#if 0
PPTable_t *pptable = smu->smu_table.driver_pptable;
+#if 0
- PPTable_t *pptable = smu->smu_table.driver_pptable;
const OverDriveLimits_t * const overdrive_upperlimits =
&pptable->SkuTable.OverDriveLimitsBasicMax;
const OverDriveLimits_t * const overdrive_lowerlimits =
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 48+ messages in thread* linux-next: manual merge of the drm tree with the drm-fixes tree
@ 2023-02-13 0:23 Stephen Rothwell
0 siblings, 0 replies; 48+ messages in thread
From: Stephen Rothwell @ 2023-02-13 0:23 UTC (permalink / raw)
To: Dave Airlie
Cc: DRI, Alex Deucher, Linux Kernel Mailing List, Linux Next Mailing List
[-- Attachment #1: Type: text/plain, Size: 1571 bytes --]
Hi all,
Today's linux-next merge of the drm tree got a conflict in:
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
between commits:
877f26bf3ca6 ("drm/amd/display: disable S/G display on DCN 2.1.0")
077e9659581a ("drm/amd/display: disable S/G display on DCN 3.1.2/3")
7ece674cd946 ("Revert "drm/amd/display: disable S/G display on DCN 3.1.4"")
9734a75cd99d ("Revert "drm/amd/display: disable S/G display on DCN 3.1.2/3"")
1b7ac7989ad8 ("Revert "drm/amd/display: disable S/G display on DCN 2.1.0"")
e7d636476ba7 ("Revert "drm/amd/display: disable S/G display on DCN 3.1.5"")
from the drm-fixes tree and commits:
2404f9b0ea01 ("drm/amd/display: disable S/G display on DCN 2.1.0")
f081cd4ca265 ("drm/amd/display: disable S/G display on DCN 3.1.2/3")
69ed0c5d44d7 ("Revert "drm/amd/display: disable S/G display on DCN 3.1.4"")
from the drm tree.
Git's automatic merge ignored commit 1b7ac7989ad8, since on the HEAD
side it was cancelled out by commit 877f26bf3ca6. So I have to manually
reapply commit 1b7ac7989ad8.
This is an issue with duplicate patches that you need to keep in mind.
I fixed it up (I used the former changes) and can carry the fix as
necessary. This is now fixed as far as linux-next is concerned, but any
non trivial conflicts should be mentioned to your upstream maintainer
when your tree is submitted for merging. You may also want to consider
cooperating with the maintainer of the conflicting tree to minimise any
particularly complex conflicts.
--
Cheers,
Stephen Rothwell
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 48+ messages in thread* linux-next: manual merge of the drm tree with the drm-fixes tree
@ 2022-09-26 17:49 broonie
0 siblings, 0 replies; 48+ messages in thread
From: broonie @ 2022-09-26 17:49 UTC (permalink / raw)
To: Dave Airlie, DRI
Cc: Alex Deucher, Alvin Lee, Lee, Linux Kernel Mailing List,
Linux Next Mailing List
Hi all,
Today's linux-next merge of the drm tree got a conflict in:
drivers/gpu/drm/amd/display/dc/dcn32/dcn32_resource_helpers.c
between commit:
58d97c99c99f4 ("drm/amd/display: Update MBLK calculation for SubVP")
from the drm-fixes tree and commits:
41c81dcf59991 ("drm/amd/display: Update MBLK calculation for SubVP")
5c1a431aaf52b ("drm/amd/display: Added debug option for forcing subvp num ways")
from the drm tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
diff --cc drivers/gpu/drm/amd/display/dc/dcn32/dcn32_resource_helpers.c
index 1f195c5b3377d,417dfdcf95962..0000000000000
--- a/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_resource_helpers.c
+++ b/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_resource_helpers.c
^ permalink raw reply [flat|nested] 48+ messages in thread* linux-next: manual merge of the drm tree with the drm-fixes tree
@ 2022-02-25 16:37 broonie
0 siblings, 0 replies; 48+ messages in thread
From: broonie @ 2022-02-25 16:37 UTC (permalink / raw)
To: Dave Airlie, DRI
Cc: Alex Deucher, Daniel Vetter, Guchun Chen, Leslie Shi,
Linux Kernel Mailing List, Linux Next Mailing List,
Tomohito Esaki
Hi all,
Today's linux-next merge of the drm tree got a conflict in:
drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
between commit:
e2b993302f40c ("drm/amdgpu: bypass tiling flag check in virtual display case (v2)")
from the drm-fixes tree and commit:
2af104290da5e ("drm: introduce fb_modifiers_not_supported flag in mode_config")
from the drm tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
diff --cc drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
index c4387b38229c2,9e5fc4cdb8ec9..0000000000000
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
@@@ -1141,7 -1148,7 +1148,7 @@@ int amdgpu_display_framebuffer_init(str
if (ret)
return ret;
- if (!dev->mode_config.allow_fb_modifiers && !adev->enable_virtual_display) {
- if (dev->mode_config.fb_modifiers_not_supported) {
++ if (dev->mode_config.fb_modifiers_not_supported && !adev->enable_virtual_display) {
drm_WARN_ONCE(dev, adev->family >= AMDGPU_FAMILY_AI,
"GFX9+ requires FB check based on format modifier\n");
ret = check_tiling_flags_gfx6(rfb);
^ permalink raw reply [flat|nested] 48+ messages in thread* linux-next: manual merge of the drm tree with the drm-fixes tree
@ 2022-02-24 16:33 broonie
0 siblings, 0 replies; 48+ messages in thread
From: broonie @ 2022-02-24 16:33 UTC (permalink / raw)
To: Dave Airlie, DRI
Cc: Alex Deucher, Linux Kernel Mailing List, Linux Next Mailing List,
Nicholas Kazlauskas, Wenjing Liu
Hi all,
Today's linux-next merge of the drm tree got a conflict in:
drivers/gpu/drm/amd/display/dc/core/dc_resource.c
between commit:
3743e7f6fcb93 ("drm/amd/display: Fix stream->link_enc unassigned during stream removal")
from the drm-fixes tree and commits:
6d33f0e820bfb ("drm/amd/display: Fix stream->link_enc unassigned during stream removal")
d9eb8fea6862e ("drm/amd/display: Drop DCN for DP2.x logic")
from the drm tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
diff --cc drivers/gpu/drm/amd/display/dc/core/dc_resource.c
index 18757c1585232,19b56f9acf84e..0000000000000
--- a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c
[Used drm version]
^ permalink raw reply [flat|nested] 48+ messages in thread* linux-next: manual merge of the drm tree with the drm-fixes tree
@ 2021-08-06 12:04 Mark Brown
2021-08-10 11:56 ` Geert Uytterhoeven
0 siblings, 1 reply; 48+ messages in thread
From: Mark Brown @ 2021-08-06 12:04 UTC (permalink / raw)
To: Dave Airlie, DRI
Cc: Alex Deucher, Linux Kernel Mailing List, Linux Next Mailing List,
Chengming Gui, Tao Zhou
Hi all,
Today's linux-next merge of the drm tree got a conflict in:
drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
between commit:
e00f543d3596 ("drm/amdgpu: add DID for beige goby")
from the drm-fixes tree and commit:
a8f706966b92 ("drm/amdgpu: add pci device id for cyan_skillfish")
from the drm tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
diff --cc drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
index 5ed8381ae0f5,d637b0536f84..000000000000
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
@@@ -1213,13 -1212,9 +1212,16 @@@ static const struct pci_device_id pciid
{0x1002, 0x740F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ALDEBARAN|AMD_EXP_HW_SUPPORT},
{0x1002, 0x7410, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ALDEBARAN|AMD_EXP_HW_SUPPORT},
+ /* BEIGE_GOBY */
+ {0x1002, 0x7420, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BEIGE_GOBY},
+ {0x1002, 0x7421, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BEIGE_GOBY},
+ {0x1002, 0x7422, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BEIGE_GOBY},
+ {0x1002, 0x7423, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BEIGE_GOBY},
+ {0x1002, 0x743F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BEIGE_GOBY},
+
+ /* CYAN_SKILLFISH */
+ {0x1002, 0x13FE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYAN_SKILLFISH|AMD_IS_APU},
+
{0, 0, 0}
};
^ permalink raw reply [flat|nested] 48+ messages in thread* Re: linux-next: manual merge of the drm tree with the drm-fixes tree
2021-08-06 12:04 Mark Brown
@ 2021-08-10 11:56 ` Geert Uytterhoeven
2021-08-10 12:53 ` Stephen Rothwell
0 siblings, 1 reply; 48+ messages in thread
From: Geert Uytterhoeven @ 2021-08-10 11:56 UTC (permalink / raw)
To: Mark Brown, Stephen Rothwell
Cc: Dave Airlie, DRI, Alex Deucher, Linux Kernel Mailing List,
Linux Next Mailing List, Chengming Gui, Tao Zhou
Hi Mark, Stephen,
On Fri, Aug 6, 2021 at 6:11 PM Mark Brown <broonie@kernel.org> wrote:
> Today's linux-next merge of the drm tree got a conflict in:
>
> drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
>
> between commit:
>
> e00f543d3596 ("drm/amdgpu: add DID for beige goby")
>
> from the drm-fixes tree and commit:
>
> a8f706966b92 ("drm/amdgpu: add pci device id for cyan_skillfish")
>
> from the drm tree.
>
> I fixed it up (see below) and can carry the fix as necessary. This
> is now fixed as far as linux-next is concerned, but any non trivial
> conflicts should be mentioned to your upstream maintainer when your tree
> is submitted for merging. You may also want to consider cooperating
> with the maintainer of the conflicting tree to minimise any particularly
> complex conflicts.
>
>
> diff --cc drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
> index 5ed8381ae0f5,d637b0536f84..000000000000
> --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
> +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
> @@@ -1213,13 -1212,9 +1212,16 @@@ static const struct pci_device_id pciid
> {0x1002, 0x740F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ALDEBARAN|AMD_EXP_HW_SUPPORT},
> {0x1002, 0x7410, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ALDEBARAN|AMD_EXP_HW_SUPPORT},
>
> + /* BEIGE_GOBY */
> + {0x1002, 0x7420, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BEIGE_GOBY},
> + {0x1002, 0x7421, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BEIGE_GOBY},
> + {0x1002, 0x7422, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BEIGE_GOBY},
> + {0x1002, 0x7423, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BEIGE_GOBY},
> + {0x1002, 0x743F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BEIGE_GOBY},
> +
> + /* CYAN_SKILLFISH */
> + {0x1002, 0x13FE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYAN_SKILLFISH|AMD_IS_APU},
> +
> {0, 0, 0}
> };
next-20210806 and later have a different resolution, duplicating the
BEIGE_GOBY entries before and after the CYAN_SKILLFISH entry.
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply [flat|nested] 48+ messages in thread* Re: linux-next: manual merge of the drm tree with the drm-fixes tree
2021-08-10 11:56 ` Geert Uytterhoeven
@ 2021-08-10 12:53 ` Stephen Rothwell
0 siblings, 0 replies; 48+ messages in thread
From: Stephen Rothwell @ 2021-08-10 12:53 UTC (permalink / raw)
To: Geert Uytterhoeven
Cc: Mark Brown, Dave Airlie, DRI, Alex Deucher,
Linux Kernel Mailing List, Linux Next Mailing List,
Chengming Gui, Tao Zhou
[-- Attachment #1: Type: text/plain, Size: 2102 bytes --]
Hi Geert,
On Tue, 10 Aug 2021 13:56:12 +0200 Geert Uytterhoeven <geert@linux-m68k.org> wrote:
>
> On Fri, Aug 6, 2021 at 6:11 PM Mark Brown <broonie@kernel.org> wrote:
> > Today's linux-next merge of the drm tree got a conflict in:
> >
> > drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
> >
> > between commit:
> >
> > e00f543d3596 ("drm/amdgpu: add DID for beige goby")
> >
> > from the drm-fixes tree and commit:
> >
> > a8f706966b92 ("drm/amdgpu: add pci device id for cyan_skillfish")
> >
> > from the drm tree.
> >
> >
> > diff --cc drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
> > index 5ed8381ae0f5,d637b0536f84..000000000000
> > --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
> > +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
> > @@@ -1213,13 -1212,9 +1212,16 @@@ static const struct pci_device_id pciid
> > {0x1002, 0x740F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ALDEBARAN|AMD_EXP_HW_SUPPORT},
> > {0x1002, 0x7410, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ALDEBARAN|AMD_EXP_HW_SUPPORT},
> >
> > + /* BEIGE_GOBY */
> > + {0x1002, 0x7420, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BEIGE_GOBY},
> > + {0x1002, 0x7421, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BEIGE_GOBY},
> > + {0x1002, 0x7422, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BEIGE_GOBY},
> > + {0x1002, 0x7423, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BEIGE_GOBY},
> > + {0x1002, 0x743F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BEIGE_GOBY},
> > +
> > + /* CYAN_SKILLFISH */
> > + {0x1002, 0x13FE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CYAN_SKILLFISH|AMD_IS_APU},
> > +
> > {0, 0, 0}
> > };
>
> next-20210806 and later have a different resolution, duplicating the
> BEIGE_GOBY entries before and after the CYAN_SKILLFISH entry.
That's because commit e00f543d3596 (which is now in Linus' tree) has
been copied (as a separate commit) into the amdgpu tree (where the
CYAN_SKILLFISH change is present, with the BEIGE_GOBY change after
that).
I have dropped the current rerere resolution and will redo the
resolution tomorrow.
--
Cheers,
Stephen Rothwell
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 48+ messages in thread
* linux-next: manual merge of the drm tree with the drm-fixes tree
@ 2020-07-10 2:28 Stephen Rothwell
0 siblings, 0 replies; 48+ messages in thread
From: Stephen Rothwell @ 2020-07-10 2:28 UTC (permalink / raw)
To: Dave Airlie, DRI
Cc: Linux Next Mailing List, Linux Kernel Mailing List, Huang Rui,
Emily Deng, Alex Deucher
[-- Attachment #1: Type: text/plain, Size: 1754 bytes --]
Hi all,
Today's linux-next merge of the drm tree got a conflict in:
drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c
between commit:
c564b8601ae9 ("drm/amdgpu: add TMR destory function for psp")
from the drm-fixes tree and commit:
3bda8acd974e ("drm/amdgpu/sriov: Add clear vf fw support")
from the drm tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
--
Cheers,
Stephen Rothwell
diff --cc drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c
index ef3269c43d4f,9342a9e8cadf..000000000000
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c
@@@ -1825,6 -1866,9 +1912,7 @@@ static int psp_hw_fini(void *handle
{
struct amdgpu_device *adev = (struct amdgpu_device *)handle;
struct psp_context *psp = &adev->psp;
- void *tmr_buf;
- void **pptr;
+ int ret;
if (psp->adev->psp.ta_fw) {
psp_ras_terminate(psp);
@@@ -1833,10 -1877,16 +1921,15 @@@
}
psp_asd_unload(psp);
+ ret = psp_clear_vf_fw(psp);
+ if (ret) {
+ DRM_ERROR("PSP clear vf fw!\n");
+ return ret;
+ }
+ psp_tmr_terminate(psp);
psp_ring_destroy(psp, PSP_RING_TYPE__KM);
- pptr = amdgpu_sriov_vf(psp->adev) ? &tmr_buf : NULL;
- amdgpu_bo_free_kernel(&psp->tmr_bo, &psp->tmr_mc_addr, pptr);
amdgpu_bo_free_kernel(&psp->fw_pri_bo,
&psp->fw_pri_mc_addr, &psp->fw_pri_buf);
amdgpu_bo_free_kernel(&psp->fence_buf_bo,
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 48+ messages in thread* linux-next: manual merge of the drm tree with the drm-fixes tree
@ 2019-08-23 3:20 Stephen Rothwell
0 siblings, 0 replies; 48+ messages in thread
From: Stephen Rothwell @ 2019-08-23 3:20 UTC (permalink / raw)
To: Dave Airlie, DRI
Cc: Linux Next Mailing List, Linux Kernel Mailing List,
Alexandre Courbot, CK Hu, Daniel Vetter
[-- Attachment #1: Type: text/plain, Size: 2181 bytes --]
Hi all,
Today's linux-next merge of the drm tree got a conflict in:
drivers/gpu/drm/mediatek/mtk_drm_drv.c
between commit:
4c6f3196e6ea ("drm/mediatek: use correct device to import PRIME buffers")
from the drm-fixes tree and commit:
3baeeb21983a ("drm/mtk: Drop drm_gem_prime_export/import")
from the drm tree.
I fixed it up (I think - see below) and can carry the fix as necessary.
This is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
--
Cheers,
Stephen Rothwell
diff --cc drivers/gpu/drm/mediatek/mtk_drm_drv.c
index 7f5408cb2377,2ee809a6f3dc..000000000000
--- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
@@@ -351,21 -314,8 +345,20 @@@ static const struct file_operations mtk
.compat_ioctl = drm_compat_ioctl,
};
+/*
+ * We need to override this because the device used to import the memory is
+ * not dev->dev, as drm_gem_prime_import() expects.
+ */
+struct drm_gem_object *mtk_drm_gem_prime_import(struct drm_device *dev,
+ struct dma_buf *dma_buf)
+{
+ struct mtk_drm_private *private = dev->dev_private;
+
+ return drm_gem_prime_import_dev(dev, dma_buf, private->dma_dev);
+}
+
static struct drm_driver mtk_drm_driver = {
- .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME |
- DRIVER_ATOMIC,
+ .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
.gem_free_object_unlocked = mtk_drm_gem_free_object,
.gem_vm_ops = &drm_gem_cma_vm_ops,
@@@ -373,8 -323,6 +366,7 @@@
.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
- .gem_prime_export = drm_gem_prime_export,
+ .gem_prime_import = mtk_drm_gem_prime_import,
.gem_prime_get_sg_table = mtk_gem_prime_get_sg_table,
.gem_prime_import_sg_table = mtk_gem_prime_import_sg_table,
.gem_prime_mmap = mtk_drm_gem_mmap_buf,
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 48+ messages in thread* linux-next: manual merge of the drm tree with the drm-fixes tree
@ 2019-07-05 2:50 Stephen Rothwell
0 siblings, 0 replies; 48+ messages in thread
From: Stephen Rothwell @ 2019-07-05 2:50 UTC (permalink / raw)
To: Dave Airlie, DRI
Cc: Linux Next Mailing List, Linux Kernel Mailing List,
Trigger Huang, Alex Deucher
[-- Attachment #1: Type: text/plain, Size: 796 bytes --]
Hi all,
Today's linux-next merge of the drm tree got a conflict in:
drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c
between commit:
25f09f858835 ("drm/amdgpu/gfx9: use reset default for PA_SC_FIFO_SIZE")
from the drm-fixes tree and commit:
1bff7f6c679f ("drm/amdgpu: RLC to program regs for Vega10 SR-IOV")
from the drm tree.
I fixed it up (the former removes the code modified by the latter) and
can carry the fix as necessary. This is now fixed as far as linux-next
is concerned, but any non trivial conflicts should be mentioned to your
upstream maintainer when your tree is submitted for merging. You may
also want to consider cooperating with the maintainer of the conflicting
tree to minimise any particularly complex conflicts.
--
Cheers,
Stephen Rothwell
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 48+ messages in thread* linux-next: manual merge of the drm tree with the drm-fixes tree
@ 2019-03-01 2:23 Stephen Rothwell
2019-03-01 23:29 ` Alex Deucher
0 siblings, 1 reply; 48+ messages in thread
From: Stephen Rothwell @ 2019-03-01 2:23 UTC (permalink / raw)
To: Dave Airlie, DRI
Cc: Linux Next Mailing List, Linux Kernel Mailing List,
Mario Kleiner, David Francis, Alex Deucher
[-- Attachment #1: Type: text/plain, Size: 951 bytes --]
Hi all,
Today's linux-next merge of the drm tree got a conflict in:
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
between commit:
d63716658ac1 ("drm/amd/display: Use vrr friendly pageflip throttling in DC.")
from the drm-fixes tree and commits:
8a48b44cd00f ("drm/amd/display: Call into DC once per multiplane flip")
bc7f670ee04c ("drm/amd/display: Perform plane updates only when needed")
from the drm tree.
I had no idea how to resolve this, so I just effectively reverted the
drm-fixes commit.
I fixed it up (see above) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
--
Cheers,
Stephen Rothwell
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 48+ messages in thread* Re: linux-next: manual merge of the drm tree with the drm-fixes tree
2019-03-01 2:23 Stephen Rothwell
@ 2019-03-01 23:29 ` Alex Deucher
2019-03-04 0:54 ` Stephen Rothwell
0 siblings, 1 reply; 48+ messages in thread
From: Alex Deucher @ 2019-03-01 23:29 UTC (permalink / raw)
To: Stephen Rothwell
Cc: Dave Airlie, DRI, Alex Deucher, Linux Next Mailing List,
Linux Kernel Mailing List, David Francis
On Thu, Feb 28, 2019 at 9:24 PM Stephen Rothwell <sfr@canb.auug.org.au> wrote:
>
> Hi all,
>
> Today's linux-next merge of the drm tree got a conflict in:
>
> drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
>
> between commit:
>
> d63716658ac1 ("drm/amd/display: Use vrr friendly pageflip throttling in DC.")
>
> from the drm-fixes tree and commits:
>
> 8a48b44cd00f ("drm/amd/display: Call into DC once per multiplane flip")
> bc7f670ee04c ("drm/amd/display: Perform plane updates only when needed")
>
> from the drm tree.
>
> I had no idea how to resolve this, so I just effectively reverted the
> drm-fixes commit.
>
> I fixed it up (see above) and can carry the fix as necessary. This
> is now fixed as far as linux-next is concerned, but any non trivial
> conflicts should be mentioned to your upstream maintainer when your tree
> is submitted for merging. You may also want to consider cooperating
> with the maintainer of the conflicting tree to minimise any particularly
> complex conflicts.
d63716658ac1 ("drm/amd/display: Use vrr friendly pageflip throttling
in DC.") should be reverted and this should be applied instead for
drm-next:
https://cgit.freedesktop.org/~agd5f/linux/commit/?h=drm-next-5.2-wip&id=5d5f43c235d02e4d8e9d50d012f3b21f380486eb
Alex
>
>
>
> --
> Cheers,
> Stephen Rothwell
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
^ permalink raw reply [flat|nested] 48+ messages in thread* Re: linux-next: manual merge of the drm tree with the drm-fixes tree
2019-03-01 23:29 ` Alex Deucher
@ 2019-03-04 0:54 ` Stephen Rothwell
2019-03-11 12:36 ` Daniel Vetter
0 siblings, 1 reply; 48+ messages in thread
From: Stephen Rothwell @ 2019-03-04 0:54 UTC (permalink / raw)
To: Dave Airlie
Cc: Alex Deucher, DRI, Alex Deucher, Linux Next Mailing List,
Linux Kernel Mailing List, David Francis
[-- Attachment #1: Type: text/plain, Size: 1569 bytes --]
Hi Dave,
On Fri, 1 Mar 2019 18:29:58 -0500 Alex Deucher <alexdeucher@gmail.com> wrote:
>
> On Thu, Feb 28, 2019 at 9:24 PM Stephen Rothwell <sfr@canb.auug.org.au> wrote:
> >
> > Today's linux-next merge of the drm tree got a conflict in:
> >
> > drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> >
> > between commit:
> >
> > d63716658ac1 ("drm/amd/display: Use vrr friendly pageflip throttling in DC.")
> >
> > from the drm-fixes tree and commits:
> >
> > 8a48b44cd00f ("drm/amd/display: Call into DC once per multiplane flip")
> > bc7f670ee04c ("drm/amd/display: Perform plane updates only when needed")
> >
> > from the drm tree.
> >
> > I had no idea how to resolve this, so I just effectively reverted the
> > drm-fixes commit.
> >
> > I fixed it up (see above) and can carry the fix as necessary. This
> > is now fixed as far as linux-next is concerned, but any non trivial
> > conflicts should be mentioned to your upstream maintainer when your tree
> > is submitted for merging. You may also want to consider cooperating
> > with the maintainer of the conflicting tree to minimise any particularly
> > complex conflicts.
>
> d63716658ac1 ("drm/amd/display: Use vrr friendly pageflip throttling
> in DC.") should be reverted and this should be applied instead for
> drm-next:
> https://cgit.freedesktop.org/~agd5f/linux/commit/?h=drm-next-5.2-wip&id=5d5f43c235d02e4d8e9d50d012f3b21f380486eb
Maybe you should merge the drm-fixes tree into the drm tree and apply
the above ...
--
Cheers,
Stephen Rothwell
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 48+ messages in thread* Re: linux-next: manual merge of the drm tree with the drm-fixes tree
2019-03-04 0:54 ` Stephen Rothwell
@ 2019-03-11 12:36 ` Daniel Vetter
0 siblings, 0 replies; 48+ messages in thread
From: Daniel Vetter @ 2019-03-11 12:36 UTC (permalink / raw)
To: Stephen Rothwell
Cc: Dave Airlie, David Francis, Linux Kernel Mailing List, DRI,
Alex Deucher, Linux Next Mailing List
On Mon, Mar 04, 2019 at 11:54:02AM +1100, Stephen Rothwell wrote:
> Hi Dave,
>
> On Fri, 1 Mar 2019 18:29:58 -0500 Alex Deucher <alexdeucher@gmail.com> wrote:
> >
> > On Thu, Feb 28, 2019 at 9:24 PM Stephen Rothwell <sfr@canb.auug.org.au> wrote:
> > >
> > > Today's linux-next merge of the drm tree got a conflict in:
> > >
> > > drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
> > >
> > > between commit:
> > >
> > > d63716658ac1 ("drm/amd/display: Use vrr friendly pageflip throttling in DC.")
> > >
> > > from the drm-fixes tree and commits:
> > >
> > > 8a48b44cd00f ("drm/amd/display: Call into DC once per multiplane flip")
> > > bc7f670ee04c ("drm/amd/display: Perform plane updates only when needed")
> > >
> > > from the drm tree.
> > >
> > > I had no idea how to resolve this, so I just effectively reverted the
> > > drm-fixes commit.
> > >
> > > I fixed it up (see above) and can carry the fix as necessary. This
> > > is now fixed as far as linux-next is concerned, but any non trivial
> > > conflicts should be mentioned to your upstream maintainer when your tree
> > > is submitted for merging. You may also want to consider cooperating
> > > with the maintainer of the conflicting tree to minimise any particularly
> > > complex conflicts.
> >
> > d63716658ac1 ("drm/amd/display: Use vrr friendly pageflip throttling
> > in DC.") should be reverted and this should be applied instead for
> > drm-next:
> > https://cgit.freedesktop.org/~agd5f/linux/commit/?h=drm-next-5.2-wip&id=5d5f43c235d02e4d8e9d50d012f3b21f380486eb
>
> Maybe you should merge the drm-fixes tree into the drm tree and apply
> the above ...
Alex, can you pls get the amdgpu trees added to linux-next? From a quick
look those still seem to be missing.
Thanks, Daniel
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
^ permalink raw reply [flat|nested] 48+ messages in thread
* linux-next: manual merge of the drm tree with the drm-fixes tree
@ 2018-12-14 1:51 Stephen Rothwell
0 siblings, 0 replies; 48+ messages in thread
From: Stephen Rothwell @ 2018-12-14 1:51 UTC (permalink / raw)
To: Dave Airlie, DRI
Cc: Linux Next Mailing List, Linux Kernel Mailing List,
Thomas Hellstrom, Christian König, Alex Deucher
[-- Attachment #1: Type: text/plain, Size: 4099 bytes --]
Hi all,
Today's linux-next merge of the drm tree got a conflict in:
drivers/gpu/drm/vmwgfx/vmwgfx_ttm_glue.c
between commit:
fd567467753f ("drm/vmwgfx: Protect from excessive execbuf kernel memory allocations v3")
from the drm-fixes tree and commit:
a64f784bb14a ("drm/ttm: initialize globals during device init (v2)")
from the drm tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
--
Cheers,
Stephen Rothwell
diff --cc drivers/gpu/drm/vmwgfx/vmwgfx_ttm_glue.c
index f88247046721,154eb09aa91e..000000000000
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_glue.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_glue.c
@@@ -42,93 -42,3 +42,39 @@@ int vmw_mmap(struct file *filp, struct
dev_priv = vmw_priv(file_priv->minor->dev);
return ttm_bo_mmap(filp, vma, &dev_priv->bdev);
}
+
- static int vmw_ttm_mem_global_init(struct drm_global_reference *ref)
- {
- DRM_INFO("global init.\n");
- return ttm_mem_global_init(ref->object);
- }
-
- static void vmw_ttm_mem_global_release(struct drm_global_reference *ref)
- {
- ttm_mem_global_release(ref->object);
- }
-
- int vmw_ttm_global_init(struct vmw_private *dev_priv)
- {
- struct drm_global_reference *global_ref;
- int ret;
-
- global_ref = &dev_priv->mem_global_ref;
- global_ref->global_type = DRM_GLOBAL_TTM_MEM;
- global_ref->size = sizeof(struct ttm_mem_global);
- global_ref->init = &vmw_ttm_mem_global_init;
- global_ref->release = &vmw_ttm_mem_global_release;
-
- ret = drm_global_item_ref(global_ref);
- if (unlikely(ret != 0)) {
- DRM_ERROR("Failed setting up TTM memory accounting.\n");
- return ret;
- }
-
- dev_priv->bo_global_ref.mem_glob =
- dev_priv->mem_global_ref.object;
- global_ref = &dev_priv->bo_global_ref.ref;
- global_ref->global_type = DRM_GLOBAL_TTM_BO;
- global_ref->size = sizeof(struct ttm_bo_global);
- global_ref->init = &ttm_bo_global_init;
- global_ref->release = &ttm_bo_global_release;
- ret = drm_global_item_ref(global_ref);
-
- if (unlikely(ret != 0)) {
- DRM_ERROR("Failed setting up TTM buffer objects.\n");
- goto out_no_bo;
- }
-
- return 0;
- out_no_bo:
- drm_global_item_unref(&dev_priv->mem_global_ref);
- return ret;
- }
-
- void vmw_ttm_global_release(struct vmw_private *dev_priv)
- {
- drm_global_item_unref(&dev_priv->bo_global_ref.ref);
- drm_global_item_unref(&dev_priv->mem_global_ref);
- }
-
+/* struct vmw_validation_mem callback */
+static int vmw_vmt_reserve(struct vmw_validation_mem *m, size_t size)
+{
+ static struct ttm_operation_ctx ctx = {.interruptible = false,
+ .no_wait_gpu = false};
+ struct vmw_private *dev_priv = container_of(m, struct vmw_private, vvm);
+
+ return ttm_mem_global_alloc(vmw_mem_glob(dev_priv), size, &ctx);
+}
+
+/* struct vmw_validation_mem callback */
+static void vmw_vmt_unreserve(struct vmw_validation_mem *m, size_t size)
+{
+ struct vmw_private *dev_priv = container_of(m, struct vmw_private, vvm);
+
+ return ttm_mem_global_free(vmw_mem_glob(dev_priv), size);
+}
+
+/**
+ * vmw_validation_mem_init_ttm - Interface the validation memory tracker
+ * to ttm.
+ * @dev_priv: Pointer to struct vmw_private. The reason we choose a vmw private
+ * rather than a struct vmw_validation_mem is to make sure assumption in the
+ * callbacks that struct vmw_private derives from struct vmw_validation_mem
+ * holds true.
+ * @gran: The recommended allocation granularity
+ */
+void vmw_validation_mem_init_ttm(struct vmw_private *dev_priv, size_t gran)
+{
+ struct vmw_validation_mem *vvm = &dev_priv->vvm;
+
+ vvm->reserve_mem = vmw_vmt_reserve;
+ vvm->unreserve_mem = vmw_vmt_unreserve;
+ vvm->gran = gran;
+}
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 48+ messages in thread* linux-next: manual merge of the drm tree with the drm-fixes tree
@ 2018-12-07 2:38 Stephen Rothwell
0 siblings, 0 replies; 48+ messages in thread
From: Stephen Rothwell @ 2018-12-07 2:38 UTC (permalink / raw)
To: Dave Airlie, DRI
Cc: Linux Next Mailing List, Linux Kernel Mailing List, Roman Li,
Alex Deucher, Dmytro Laktyushkin
[-- Attachment #1: Type: text/plain, Size: 853 bytes --]
Hi all,
Today's linux-next merge of the drm tree got a conflict in:
drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c
between commit:
c6888879fd55 ("drm/amd/display: Fix 6x4K displays light-up on Vega20 (v2)")
from the drm-fixes tree and commit:
24f7dd7ea98d ("drm/amd/display: move pplib/smu notification to dccg block")
from the drm tree.
I fixed it up (I am not sure how to fix this up, so I just dropped the
drm-fixes change) and can carry the fix as necessary. This is now fixed
as far as linux-next is concerned, but any non trivial conflicts should
be mentioned to your upstream maintainer when your tree is submitted for
merging. You may also want to consider cooperating with the maintainer
of the conflicting tree to minimise any particularly complex conflicts.
--
Cheers,
Stephen Rothwell
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 48+ messages in thread* linux-next: manual merge of the drm tree with the drm-fixes tree
@ 2018-12-07 2:31 Stephen Rothwell
0 siblings, 0 replies; 48+ messages in thread
From: Stephen Rothwell @ 2018-12-07 2:31 UTC (permalink / raw)
To: Dave Airlie, DRI
Cc: Linux Next Mailing List, Linux Kernel Mailing List,
David Francis, Alex Deucher, Nicholas Kazlauskas
[-- Attachment #1: Type: text/plain, Size: 1450 bytes --]
Hi all,
Today's linux-next merge of the drm tree got a conflict in:
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
between commit:
49f1c44b581b ("rm/amd/display: Fix unintialized max_bpc state values")
from the drm-fixes tree and commits:
c1ee92f94ce3 ("drm/amd: Add abm level drm property")
bb47de736661 ("drm/amdgpu: Set FreeSync state using drm VRR properties")
from the drm tree.
I fixed it up (see below) and can carry the fix as necessary. This
is now fixed as far as linux-next is concerned, but any non trivial
conflicts should be mentioned to your upstream maintainer when your tree
is submitted for merging. You may also want to consider cooperating
with the maintainer of the conflicting tree to minimise any particularly
complex conflicts.
--
Cheers,
Stephen Rothwell
diff --cc drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index 5a6edf65c9ea,32e791d9b9a8..000000000000
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@@ -3063,8 -3234,7 +3235,8 @@@ amdgpu_dm_connector_atomic_duplicate_st
__drm_atomic_helper_connector_duplicate_state(connector, &new_state->base);
new_state->freesync_capable = state->freesync_capable;
- new_state->freesync_enable = state->freesync_enable;
+ new_state->max_bpc = state->max_bpc;
+ new_state->abm_level = state->abm_level;
return &new_state->base;
}
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 48+ messages in thread
end of thread, other threads:[~2026-11-13 16:07 UTC | newest]
Thread overview: 48+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-13 16:07 linux-next: manual merge of the drm tree with the drm-fixes tree Mark Brown
-- strict thread matches above, loose matches on Subject: below --
2026-09-18 11:14 Mark Brown
2026-09-11 13:21 Mark Brown
2026-08-07 14:42 Mark Brown
2026-07-31 14:29 Mark Brown
2026-07-31 14:01 Mark Brown
2026-07-26 21:24 Mark Brown
2026-07-17 14:41 Mark Brown
2026-07-17 14:40 Mark Brown
2026-07-17 14:40 Mark Brown
2026-07-17 14:40 Mark Brown
2026-07-17 14:40 Mark Brown
2026-07-17 14:40 Mark Brown
2026-07-17 15:12 ` Mark Brown
2026-07-10 12:11 Mark Brown
2026-05-29 22:00 Mark Brown
2026-04-03 13:33 Mark Brown
2026-03-27 20:52 Mark Brown
2026-03-27 20:46 Mark Brown
2026-03-09 14:35 Mark Brown
2026-03-06 13:47 Mark Brown
2026-03-06 13:47 Mark Brown
2026-01-30 15:37 Mark Brown
2026-01-30 15:28 Mark Brown
2026-01-23 17:45 Mark Brown
2025-06-27 2:58 Stephen Rothwell
2024-10-18 1:56 Stephen Rothwell
2024-06-27 15:08 Mark Brown
2024-06-27 15:00 Mark Brown
2024-06-21 13:21 Mark Brown
2023-08-18 1:53 Stephen Rothwell
2023-02-13 0:23 Stephen Rothwell
2022-09-26 17:49 broonie
2022-02-25 16:37 broonie
2022-02-24 16:33 broonie
2021-08-06 12:04 Mark Brown
2021-08-10 11:56 ` Geert Uytterhoeven
2021-08-10 12:53 ` Stephen Rothwell
2020-07-10 2:28 Stephen Rothwell
2019-08-23 3:20 Stephen Rothwell
2019-07-05 2:50 Stephen Rothwell
2019-03-01 2:23 Stephen Rothwell
2019-03-01 23:29 ` Alex Deucher
2019-03-04 0:54 ` Stephen Rothwell
2019-03-11 12:36 ` Daniel Vetter
2018-12-14 1:51 Stephen Rothwell
2018-12-07 2:38 Stephen Rothwell
2018-12-07 2:31 Stephen Rothwell
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®