304 lines
12 KiB
Diff
304 lines
12 KiB
Diff
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
|
|
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
|
|
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
|
|
@@ -3345,7 +3345,7 @@ static void dm_gpureset_toggle_interrupts(struct amdgpu_device *adev,
|
|
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;
|
|
+ rc = amdgpu_dm_irq_set(adev, irq_source, enable) ? 0 : -EBUSY;
|
|
if (rc)
|
|
drm_warn(adev_to_drm(adev), "Failed to %s pflip interrupts\n",
|
|
enable ? "enable" : "disable");
|
|
@@ -3369,7 +3369,7 @@ static void dm_gpureset_toggle_interrupts(struct amdgpu_device *adev,
|
|
/* During gpu-reset we disable and then enable vblank irq, so
|
|
* don't use amdgpu_irq_get/put() to avoid refcount change.
|
|
*/
|
|
- if (!dc_interrupt_set(adev->dm.dc, irq_source, enable))
|
|
+ if (!amdgpu_dm_irq_set(adev, irq_source, enable))
|
|
drm_warn(adev_to_drm(adev), "Failed to %sable vblank interrupt\n", enable ? "en" : "dis");
|
|
|
|
} else if (acrtc && state->stream_status[i].plane_count != 0) {
|
|
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
|
|
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
|
|
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
|
|
@@ -550,6 +550,18 @@ struct amdgpu_display_manager {
|
|
struct common_irq_params
|
|
vupdate_params[DC_IRQ_SOURCE_VUPDATE6 - DC_IRQ_SOURCE_VUPDATE1 + 1];
|
|
|
|
+ /**
|
|
+ * @irq_reg_lock:
|
|
+ *
|
|
+ * Serializes the read-modify-writes of the HW interrupt control
|
|
+ * registers. Several interrupt sources share one register - e.g. the
|
|
+ * enable and clear bits of both VSTARTUP (vblank) and VUPDATE_NO_LOCK
|
|
+ * live in OTG_GLOBAL_SYNC_STATUS. Therefore, enabling one source must
|
|
+ * not race with acking another. Held only across amdgpu_dm_irq_set()
|
|
+ * and amdgpu_dm_irq_ack().
|
|
+ */
|
|
+ spinlock_t irq_reg_lock;
|
|
+
|
|
/**
|
|
* @dmub_trace_params:
|
|
*
|
|
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
|
|
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
|
|
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c
|
|
@@ -31,6 +31,7 @@
|
|
#include "amdgpu_dm_psr.h"
|
|
#include "amdgpu_dm_replay.h"
|
|
#include "amdgpu_dm_crtc.h"
|
|
+#include "amdgpu_dm_irq.h"
|
|
#include "amdgpu_dm_plane.h"
|
|
#include "amdgpu_dm_trace.h"
|
|
#include "amdgpu_dm_debugfs.h"
|
|
@@ -87,7 +88,7 @@ int amdgpu_dm_crtc_set_vupdate_irq(struct drm_crtc *crtc, bool enable)
|
|
|
|
irq_source = IRQ_TYPE_VUPDATE + acrtc->otg_inst;
|
|
|
|
- rc = dc_interrupt_set(adev->dm.dc, irq_source, enable) ? 0 : -EBUSY;
|
|
+ rc = amdgpu_dm_irq_set(adev, irq_source, enable) ? 0 : -EBUSY;
|
|
|
|
DRM_DEBUG_VBL("crtc %d - vupdate irq %sabling: r=%d\n",
|
|
acrtc->crtc_id, enable ? "en" : "dis", rc);
|
|
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
|
|
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
|
|
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
|
|
@@ -1378,12 +1378,13 @@ void dm_helpers_free_gpu_mem(
|
|
|
|
bool dm_helpers_dmub_outbox_interrupt_control(struct dc_context *ctx, bool enable)
|
|
{
|
|
+ struct amdgpu_device *adev = ctx->driver_context;
|
|
enum dc_irq_source irq_source;
|
|
bool ret;
|
|
|
|
irq_source = DC_IRQ_SOURCE_DMCUB_OUTBOX;
|
|
|
|
- ret = dc_interrupt_set(ctx->dc, irq_source, enable);
|
|
+ ret = amdgpu_dm_irq_set(adev, irq_source, enable);
|
|
|
|
DRM_DEBUG_DRIVER("Dmub trace irq %sabling: r=%d\n",
|
|
enable ? "en" : "dis", ret);
|
|
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c
|
|
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c
|
|
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c
|
|
@@ -424,6 +424,7 @@ int amdgpu_dm_irq_init(struct amdgpu_device *adev)
|
|
DRM_DEBUG_KMS("DM_IRQ\n");
|
|
|
|
spin_lock_init(&adev->dm.irq_handler_list_table_lock);
|
|
+ spin_lock_init(&adev->dm.irq_reg_lock);
|
|
|
|
for (src = 0; src < DAL_IRQ_SOURCES_NUMBER; src++) {
|
|
/* low context handler list init */
|
|
@@ -496,7 +497,7 @@ void amdgpu_dm_irq_suspend(struct amdgpu_device *adev)
|
|
hnd_list_l = &adev->dm.irq_handler_list_low_tab[src];
|
|
hnd_list_h = &adev->dm.irq_handler_list_high_tab[src];
|
|
if (!list_empty(hnd_list_l) || !list_empty(hnd_list_h))
|
|
- dc_interrupt_set(adev->dm.dc, src, false);
|
|
+ amdgpu_dm_irq_set(adev, src, false);
|
|
|
|
DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags);
|
|
|
|
@@ -533,7 +534,7 @@ void amdgpu_dm_irq_resume_early(struct amdgpu_device *adev)
|
|
hnd_list_l = &adev->dm.irq_handler_list_low_tab[src];
|
|
hnd_list_h = &adev->dm.irq_handler_list_high_tab[src];
|
|
if (!list_empty(hnd_list_l) || !list_empty(hnd_list_h))
|
|
- dc_interrupt_set(adev->dm.dc, src, true);
|
|
+ amdgpu_dm_irq_set(adev, src, true);
|
|
}
|
|
|
|
DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags);
|
|
@@ -558,7 +559,7 @@ void amdgpu_dm_irq_resume_late(struct amdgpu_device *adev)
|
|
hnd_list_l = &adev->dm.irq_handler_list_low_tab[src];
|
|
hnd_list_h = &adev->dm.irq_handler_list_high_tab[src];
|
|
if (!list_empty(hnd_list_l) || !list_empty(hnd_list_h))
|
|
- dc_interrupt_set(adev->dm.dc, src, true);
|
|
+ amdgpu_dm_irq_set(adev, src, true);
|
|
}
|
|
|
|
DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags);
|
|
@@ -645,6 +646,21 @@ static void amdgpu_dm_irq_immediate_work(struct amdgpu_device *adev,
|
|
DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags);
|
|
}
|
|
|
|
+bool amdgpu_dm_irq_set(struct amdgpu_device *adev, enum dc_irq_source src,
|
|
+ bool enable)
|
|
+{
|
|
+ guard(spinlock_irqsave)(&adev->dm.irq_reg_lock);
|
|
+
|
|
+ return dc_interrupt_set(adev->dm.dc, src, enable);
|
|
+}
|
|
+
|
|
+void amdgpu_dm_irq_ack(struct amdgpu_device *adev, enum dc_irq_source src)
|
|
+{
|
|
+ guard(spinlock_irqsave)(&adev->dm.irq_reg_lock);
|
|
+
|
|
+ dc_interrupt_ack(adev->dm.dc, src);
|
|
+}
|
|
+
|
|
/**
|
|
* amdgpu_dm_irq_handler - Generic DM IRQ handler
|
|
* @adev: amdgpu base driver device containing the DM device
|
|
@@ -665,7 +681,7 @@ static int amdgpu_dm_irq_handler(struct amdgpu_device *adev,
|
|
entry->src_id,
|
|
entry->src_data[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);
|
|
@@ -703,7 +719,7 @@ static int amdgpu_dm_set_hpd_irq_state(struct amdgpu_device *adev,
|
|
enum dc_irq_source src = amdgpu_dm_hpd_to_dal_irq_source(type);
|
|
bool st = (state == AMDGPU_IRQ_STATE_ENABLE);
|
|
|
|
- dc_interrupt_set(adev->dm.dc, src, st);
|
|
+ amdgpu_dm_irq_set(adev, src, st);
|
|
return 0;
|
|
}
|
|
|
|
@@ -737,7 +753,7 @@ static inline int dm_irq_state(struct amdgpu_device *adev,
|
|
if (dc && dc->caps.ips_support && dc->idle_optimizations_allowed)
|
|
dc_allow_idle_optimizations(dc, false);
|
|
|
|
- dc_interrupt_set(adev->dm.dc, irq_source, st);
|
|
+ amdgpu_dm_irq_set(adev, irq_source, st);
|
|
return 0;
|
|
}
|
|
|
|
@@ -791,7 +807,7 @@ static int amdgpu_dm_set_dmub_outbox_irq_state(struct amdgpu_device *adev,
|
|
enum dc_irq_source irq_source = DC_IRQ_SOURCE_DMCUB_OUTBOX;
|
|
bool st = (state == AMDGPU_IRQ_STATE_ENABLE);
|
|
|
|
- dc_interrupt_set(adev->dm.dc, irq_source, st);
|
|
+ amdgpu_dm_irq_set(adev, irq_source, st);
|
|
return 0;
|
|
}
|
|
|
|
@@ -817,7 +833,7 @@ static int amdgpu_dm_set_dmub_trace_irq_state(struct amdgpu_device *adev,
|
|
enum dc_irq_source irq_source = DC_IRQ_SOURCE_DMCUB_OUTBOX0;
|
|
bool st = (state == AMDGPU_IRQ_STATE_ENABLE);
|
|
|
|
- dc_interrupt_set(adev->dm.dc, irq_source, st);
|
|
+ amdgpu_dm_irq_set(adev, irq_source, st);
|
|
return 0;
|
|
}
|
|
|
|
@@ -881,9 +897,7 @@ void amdgpu_dm_set_irq_funcs(struct amdgpu_device *adev)
|
|
}
|
|
void amdgpu_dm_outbox_init(struct amdgpu_device *adev)
|
|
{
|
|
- dc_interrupt_set(adev->dm.dc,
|
|
- DC_IRQ_SOURCE_DMCUB_OUTBOX,
|
|
- true);
|
|
+ amdgpu_dm_irq_set(adev, DC_IRQ_SOURCE_DMCUB_OUTBOX, true);
|
|
}
|
|
|
|
/**
|
|
@@ -905,7 +919,7 @@ void amdgpu_dm_hpd_init(struct amdgpu_device *adev)
|
|
|
|
/* First, clear all hpd and hpdrx interrupts */
|
|
for (i = DC_IRQ_SOURCE_HPD1; i <= DC_IRQ_SOURCE_HPD6RX; i++) {
|
|
- if (!dc_interrupt_set(adev->dm.dc, i, false))
|
|
+ if (!amdgpu_dm_irq_set(adev, i, false))
|
|
drm_err(dev, "Failed to clear hpd(rx) source=%d on init\n",
|
|
i);
|
|
}
|
|
@@ -934,7 +948,7 @@ void amdgpu_dm_hpd_init(struct amdgpu_device *adev)
|
|
* of dm. Note that only hpd interrupt types are registered with
|
|
* base driver; hpd_rx types aren't. IOW, amdgpu_irq_get/put on
|
|
* hpd_rx isn't available. DM currently controls hpd_rx
|
|
- * explicitly with dc_interrupt_set()
|
|
+ * explicitly with amdgpu_dm_irq_set()
|
|
*/
|
|
if (dc_link->irq_source_hpd != DC_IRQ_SOURCE_INVALID) {
|
|
irq_type = dc_link->irq_source_hpd - DC_IRQ_SOURCE_HPD1;
|
|
@@ -943,23 +957,21 @@ void amdgpu_dm_hpd_init(struct amdgpu_device *adev)
|
|
* and what bios reports as the # of connectors with hpd
|
|
* sources. Since the # of hpd source types registered
|
|
* with base driver == mode_info.num_hpd, we have to
|
|
- * fallback to dc_interrupt_set for the remaining types.
|
|
+ * fallback to amdgpu_dm_irq_set for the remaining types.
|
|
*/
|
|
if (irq_type < adev->mode_info.num_hpd) {
|
|
if (amdgpu_irq_get(adev, &adev->hpd_irq, irq_type))
|
|
drm_err(dev, "DM_IRQ: Failed get HPD for source=%d)!\n",
|
|
dc_link->irq_source_hpd);
|
|
} else {
|
|
- dc_interrupt_set(adev->dm.dc,
|
|
- dc_link->irq_source_hpd,
|
|
- true);
|
|
+ amdgpu_dm_irq_set(adev, dc_link->irq_source_hpd,
|
|
+ true);
|
|
}
|
|
}
|
|
|
|
if (dc_link->irq_source_hpd_rx != DC_IRQ_SOURCE_INVALID) {
|
|
- dc_interrupt_set(adev->dm.dc,
|
|
- dc_link->irq_source_hpd_rx,
|
|
- true);
|
|
+ amdgpu_dm_irq_set(adev, dc_link->irq_source_hpd_rx,
|
|
+ true);
|
|
}
|
|
}
|
|
drm_connector_list_iter_end(&iter);
|
|
@@ -1003,16 +1015,14 @@ void amdgpu_dm_hpd_fini(struct amdgpu_device *adev)
|
|
drm_err(dev, "DM_IRQ: Failed put HPD for source=%d!\n",
|
|
dc_link->irq_source_hpd);
|
|
} else {
|
|
- dc_interrupt_set(adev->dm.dc,
|
|
- dc_link->irq_source_hpd,
|
|
- false);
|
|
+ amdgpu_dm_irq_set(adev, dc_link->irq_source_hpd,
|
|
+ false);
|
|
}
|
|
}
|
|
|
|
if (dc_link->irq_source_hpd_rx != DC_IRQ_SOURCE_INVALID) {
|
|
- dc_interrupt_set(adev->dm.dc,
|
|
- dc_link->irq_source_hpd_rx,
|
|
- false);
|
|
+ amdgpu_dm_irq_set(adev, dc_link->irq_source_hpd_rx,
|
|
+ false);
|
|
}
|
|
}
|
|
drm_connector_list_iter_end(&iter);
|
|
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.h
|
|
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.h
|
|
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.h
|
|
@@ -81,6 +81,34 @@ void amdgpu_dm_irq_unregister_interrupt(struct amdgpu_device *adev,
|
|
enum dc_irq_source irq_source,
|
|
void *ih_index);
|
|
|
|
+/**
|
|
+ * amdgpu_dm_irq_set - enable or disable a DC interrupt source.
|
|
+ *
|
|
+ * @adev: AMD DRM device
|
|
+ * @src: DC interrupt source to toggle
|
|
+ * @enable: true to enable the source, false to disable it
|
|
+ *
|
|
+ * DM-wide replacement for dc_interrupt_set(). As locking is DM's
|
|
+ * responsibility, this is a thin wrapper serializes the underlying
|
|
+ * read-modify-write against the other interrupt sources sharing HW control
|
|
+ * registers with @src, so DM must never call dc_interrupt_set() directly.
|
|
+ *
|
|
+ * Returns: true if the source was toggled.
|
|
+ */
|
|
+bool amdgpu_dm_irq_set(struct amdgpu_device *adev, enum dc_irq_source src,
|
|
+ bool enable);
|
|
+
|
|
+/**
|
|
+ * amdgpu_dm_irq_ack - acknowledge a DC interrupt source.
|
|
+ *
|
|
+ * @adev: AMD DRM device
|
|
+ * @src: DC interrupt source to acknowledge
|
|
+ *
|
|
+ * DM-wide replacement for dc_interrupt_ack(), serialized the same way as
|
|
+ * amdgpu_dm_irq_set().
|
|
+ */
|
|
+void amdgpu_dm_irq_ack(struct amdgpu_device *adev, enum dc_irq_source src);
|
|
+
|
|
void amdgpu_dm_set_irq_funcs(struct amdgpu_device *adev);
|
|
|
|
void amdgpu_dm_outbox_init(struct amdgpu_device *adev);
|