All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/64] drm/vc4: Fix hotplug for vc4
@ 2022-06-10  9:28 Maxime Ripard
  2022-06-10  9:28 ` [PATCH 01/64] drm/mipi-dsi: Detach devices when removing the host Maxime Ripard
                   ` (63 more replies)
  0 siblings, 64 replies; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:28 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

Hi,

Here is a series that address multiple issues when trying to unbind/rebind
vc4-related devices to their drivers.

Most of these issues involve either use-after-free, improper resource
liberation or similar.

It has been tested on the Pi3 and Pi4, with X and glxgears running and KASAN
enabled to properly validate our memory accesses.

Pi3 isn't functional after a rebind though, with vblank timeouts occuring. I'm
not quite sure why at this point, but at least the kernel doesn't completely
crash now.

Let me know what you think,
Maxime

Maxime Ripard (64):
  drm/mipi-dsi: Detach devices when removing the host
  drm/crtc: Introduce drmm_crtc_init_with_planes
  drm/encoder: Introduce drmm_encoder_init
  drm/connector: Reorder headers
  drm/connector: Mention the cleanup after drm_connector_init
  drm/connector: Introduce drmm_connector_init
  drm/connector: Introduce drmm_connector_init_with_ddc
  drm/writeback: Introduce drmm_writeback_connector_init
  drm/simple: Introduce drmm_simple_encoder_init
  drm/bridge: panel: Introduce drmm_panel_bridge_add
  drm/bridge: panel: Introduce drmm_of_get_bridge
  drm/vc4: Call component_unbind_all()
  drm/vc4: hvs: Protect device resources after removal
  drm/vc4: hvs: Remove planes currently allocated before taking down
  drm/vc4: plane: Take possible_crtcs as an argument
  drm/vc4: plane: Switch to drmm_universal_plane_alloc()
  drm/vc4: crtc: Move debugfs_name to crtc_data
  drm/vc4: crtc: Switch to drmm_kzalloc
  drm/vc4: crtc: Switch to DRM-managed CRTC initialization
  drm/vc4: dpi: Remove vc4_dev dpi pointer
  drm/vc4: dpi: Embed DRM structures into the private structure
  drm/vc4: dpi: Switch to drmm_kzalloc
  drm/vc4: dpi: Return an error if we can't enable our clock
  drm/vc4: dpi: Remove unnecessary drm_of_panel_bridge_remove call
  drm/vc4: dpi: Add action to disable the clock
  drm/vc4: dpi: Switch to DRM-managed encoder initialization
  drm/vc4: dpi: Switch to drmm_of_get_bridge
  drm/vc4: dpi: Protect device resources
  drm/vc4: dsi: Embed DRM structures into the private structure
  drm/vc4: dsi: Switch to DRM-managed encoder initialization
  drm/vc4: dsi: Switch to drmm_of_get_bridge
  drm/vc4: dsi: Fix the driver structure lifetime
  drm/vc4: dsi: Switch to devm_pm_runtime_enable
  drm/vc4: hdmi: Switch to drmm_kzalloc
  drm/vc4: hdmi: Switch to DRM-managed encoder initialization
  drm/vc4: hdmi: Switch to DRM-managed connector initialization
  drm/vc4: hdmi: Switch to device-managed ALSA initialization
  drm/vc4: hdmi: Switch to device-managed CEC initialization
  drm/vc4: hdmi: Use a device-managed action for DDC
  drm/vc4: hdmi: Switch to DRM-managed kfree to build regsets
  drm/vc4: hdmi: Use devm to register hotplug interrupts
  drm/vc4: hdmi: Move audio structure offset checks
  drm/vc4: hdmi: Protect device resources after removal
  drm/vc4: hdmi: Switch to devm_pm_runtime_enable
  drm/vc4: txp: Remove vc4_dev txp pointer
  drm/vc4: txp: Remove duplicate regset
  drm/vc4: txp: Switch to drmm_kzalloc
  drm/vc4: txp: Switch to DRM-managed writeback initialization
  drm/vc4: txp: Protect device resources
  drm/vc4: vec: Remove vc4_dev vec pointer
  drm/vc4: vec: Embed DRM structures into the private structure
  drm/vc4: vec: Switch to drmm_kzalloc
  drm/vc4: vec: Switch to DRM-managed encoder initialization
  drm/vc4: vec: Switch to DRM-managed connector initialization
  drm/vc4: vec: Protect device resources after removal
  drm/vc4: vec: Switch to devm_pm_runtime_enable
  drm/vc4: debugfs: Protect device resources
  drm/vc4: debugfs: Return an error on failure
  drm/vc4: debugfs: Simplify debugfs registration
  drm/vc4: Switch to drmm_mutex_init
  drm/vc4: perfmon: Add missing mutex_destroy
  drm/vc4: v3d: Stop disabling interrupts
  drm/vc4: v3d: Rework the runtime_pm setup
  drm/vc4: v3d: Switch to devm_pm_runtime_enable

 drivers/gpu/drm/bridge/panel.c          |  74 +++
 drivers/gpu/drm/drm_connector.c         | 186 +++++--
 drivers/gpu/drm/drm_crtc.c              |  70 ++-
 drivers/gpu/drm/drm_encoder.c           |  48 +-
 drivers/gpu/drm/drm_mipi_dsi.c          |   1 +
 drivers/gpu/drm/drm_simple_kms_helper.c |  46 +-
 drivers/gpu/drm/drm_writeback.c         | 136 +++--
 drivers/gpu/drm/vc4/vc4_bo.c            |  33 +-
 drivers/gpu/drm/vc4/vc4_crtc.c          |  69 ++-
 drivers/gpu/drm/vc4/vc4_debugfs.c       |  71 ++-
 drivers/gpu/drm/vc4/vc4_dpi.c           | 131 ++---
 drivers/gpu/drm/vc4/vc4_drv.c           |  18 +-
 drivers/gpu/drm/vc4/vc4_drv.h           |  47 +-
 drivers/gpu/drm/vc4/vc4_dsi.c           | 120 +++--
 drivers/gpu/drm/vc4/vc4_gem.c           |  10 +-
 drivers/gpu/drm/vc4/vc4_hdmi.c          | 637 +++++++++++++++++-------
 drivers/gpu/drm/vc4/vc4_hdmi.h          |   3 +-
 drivers/gpu/drm/vc4/vc4_hvs.c           | 145 +++++-
 drivers/gpu/drm/vc4/vc4_irq.c           |   2 +-
 drivers/gpu/drm/vc4/vc4_perfmon.c       |   1 +
 drivers/gpu/drm/vc4/vc4_plane.c         |  36 +-
 drivers/gpu/drm/vc4/vc4_txp.c           |  69 +--
 drivers/gpu/drm/vc4/vc4_v3d.c           |  65 ++-
 drivers/gpu/drm/vc4/vc4_vec.c           | 216 ++++----
 include/drm/drm_bridge.h                |   4 +
 include/drm/drm_connector.h             |   9 +
 include/drm/drm_crtc.h                  |   6 +
 include/drm/drm_encoder.h               |   5 +
 include/drm/drm_simple_kms_helper.h     |   3 +
 include/drm/drm_writeback.h             |   5 +
 30 files changed, 1621 insertions(+), 645 deletions(-)

-- 
2.36.1


^ permalink raw reply	[flat|nested] 124+ messages in thread

* [PATCH 01/64] drm/mipi-dsi: Detach devices when removing the host
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
@ 2022-06-10  9:28 ` Maxime Ripard
  2022-06-10  9:28 ` [PATCH 02/64] drm/crtc: Introduce drmm_crtc_init_with_planes Maxime Ripard
                   ` (62 subsequent siblings)
  63 siblings, 0 replies; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:28 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

Whenever the MIPI-DSI host is unregistered, the code of
mipi_dsi_host_unregister() loops over every device currently found on that
bus and will unregister it.

However, it doesn't detach it from the bus first, which leads to all kind
of resource leaks if the host wants to perform some clean up whenever a
device is detached.

Fixes: 068a00233969 ("drm: Add MIPI DSI bus support")
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/drm_mipi_dsi.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/gpu/drm/drm_mipi_dsi.c b/drivers/gpu/drm/drm_mipi_dsi.c
index c40bde96cfdf..c317ee9fa445 100644
--- a/drivers/gpu/drm/drm_mipi_dsi.c
+++ b/drivers/gpu/drm/drm_mipi_dsi.c
@@ -346,6 +346,7 @@ static int mipi_dsi_remove_device_fn(struct device *dev, void *priv)
 {
 	struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev);
 
+	mipi_dsi_detach(dsi);
 	mipi_dsi_device_unregister(dsi);
 
 	return 0;
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 02/64] drm/crtc: Introduce drmm_crtc_init_with_planes
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
  2022-06-10  9:28 ` [PATCH 01/64] drm/mipi-dsi: Detach devices when removing the host Maxime Ripard
@ 2022-06-10  9:28 ` Maxime Ripard
  2022-06-13 11:23   ` Thomas Zimmermann
  2022-06-10  9:28 ` [PATCH 03/64] drm/encoder: Introduce drmm_encoder_init Maxime Ripard
                   ` (61 subsequent siblings)
  63 siblings, 1 reply; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:28 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

The DRM-managed function to register a CRTC is
drmm_crtc_alloc_with_planes(), which will allocate the underlying
structure and initialisation the CRTC.

However, we might want to separate the structure creation and the CRTC
initialisation, for example if the structure is shared across multiple
DRM entities, for example an encoder and a connector.

Let's create an helper to only initialise a CRTC that would be passed as
an argument.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/drm_crtc.c | 70 ++++++++++++++++++++++++++++++++++++--
 include/drm/drm_crtc.h     |  6 ++++
 2 files changed, 73 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 26a77a735905..fd986a7dd4ad 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -341,9 +341,10 @@ static int __drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *
  * The @primary and @cursor planes are only relevant for legacy uAPI, see
  * &drm_crtc.primary and &drm_crtc.cursor.
  *
- * Note: consider using drmm_crtc_alloc_with_planes() instead of
- * drm_crtc_init_with_planes() to let the DRM managed resource infrastructure
- * take care of cleanup and deallocation.
+ * Note: consider using drmm_crtc_alloc_with_planes() or
+ * drmm_crtc_init_with_planes() instead of drm_crtc_init_with_planes()
+ * to let the DRM managed resource infrastructure take care of cleanup
+ * and deallocation.
  *
  * Returns:
  * Zero on success, error code on failure.
@@ -368,6 +369,69 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
 }
 EXPORT_SYMBOL(drm_crtc_init_with_planes);
 
+static void drmm_crtc_init_with_planes_cleanup(struct drm_device *dev,
+					       void *ptr)
+{
+	struct drm_crtc *crtc = ptr;
+
+	drm_crtc_cleanup(crtc);
+}
+
+/**
+ * drmm_crtc_init_with_planes - Initialise a new CRTC object with
+ *    specified primary and cursor planes.
+ * @dev: DRM device
+ * @crtc: CRTC object to init
+ * @primary: Primary plane for CRTC
+ * @cursor: Cursor plane for CRTC
+ * @funcs: callbacks for the new CRTC
+ * @name: printf style format string for the CRTC name, or NULL for default name
+ *
+ * Inits a new object created as base part of a driver crtc object. Drivers
+ * should use this function instead of drm_crtc_init(), which is only provided
+ * for backwards compatibility with drivers which do not yet support universal
+ * planes). For really simple hardware which has only 1 plane look at
+ * drm_simple_display_pipe_init() instead.
+ *
+ * Cleanup is automatically handled through registering
+ * drmm_crtc_cleanup() with drmm_add_action(). The crtc structure should
+ * be allocated with drmm_kzalloc().
+ *
+ * The @drm_crtc_funcs.destroy hook must be NULL.
+ *
+ * The @primary and @cursor planes are only relevant for legacy uAPI, see
+ * &drm_crtc.primary and &drm_crtc.cursor.
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
+int drmm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
+			       struct drm_plane *primary,
+			       struct drm_plane *cursor,
+			       const struct drm_crtc_funcs *funcs,
+			       const char *name, ...)
+{
+	va_list ap;
+	int ret;
+
+	WARN_ON(funcs && funcs->destroy);
+
+	va_start(ap, name);
+	ret = __drm_crtc_init_with_planes(dev, crtc, primary, cursor, funcs,
+					  name, ap);
+	va_end(ap);
+	if (ret)
+		return ret;
+
+	ret = drmm_add_action_or_reset(dev, drmm_crtc_init_with_planes_cleanup,
+				       crtc);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+EXPORT_SYMBOL(drmm_crtc_init_with_planes);
+
 static void drmm_crtc_alloc_with_planes_cleanup(struct drm_device *dev,
 						void *ptr)
 {
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index a70baea0636c..2babd5cffbf3 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -1229,6 +1229,12 @@ int drm_crtc_init_with_planes(struct drm_device *dev,
 			      struct drm_plane *cursor,
 			      const struct drm_crtc_funcs *funcs,
 			      const char *name, ...);
+int drmm_crtc_init_with_planes(struct drm_device *dev,
+			       struct drm_crtc *crtc,
+			       struct drm_plane *primary,
+			       struct drm_plane *cursor,
+			       const struct drm_crtc_funcs *funcs,
+			       const char *name, ...);
 void drm_crtc_cleanup(struct drm_crtc *crtc);
 
 __printf(7, 8)
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 03/64] drm/encoder: Introduce drmm_encoder_init
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
  2022-06-10  9:28 ` [PATCH 01/64] drm/mipi-dsi: Detach devices when removing the host Maxime Ripard
  2022-06-10  9:28 ` [PATCH 02/64] drm/crtc: Introduce drmm_crtc_init_with_planes Maxime Ripard
@ 2022-06-10  9:28 ` Maxime Ripard
  2022-06-10  9:28 ` [PATCH 04/64] drm/connector: Reorder headers Maxime Ripard
                   ` (60 subsequent siblings)
  63 siblings, 0 replies; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:28 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

The DRM-managed function to register an encoder is
drmm_encoder_alloc() and its variants, which will allocate the underlying
structure and initialisation the encoder.

However, we might want to separate the structure creation and the encoder
initialisation, for example if the structure is shared across multiple DRM
entities, for example an encoder and a connector.

Let's create an helper to only initialise an encoder that would be passed
as an argument.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/drm_encoder.c | 48 ++++++++++++++++++++++++++++++++---
 include/drm/drm_encoder.h     |  5 ++++
 2 files changed, 50 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/drm_encoder.c b/drivers/gpu/drm/drm_encoder.c
index a940024c8087..4b7b82f8c6fa 100644
--- a/drivers/gpu/drm/drm_encoder.c
+++ b/drivers/gpu/drm/drm_encoder.c
@@ -148,9 +148,9 @@ static int __drm_encoder_init(struct drm_device *dev,
  * the encoder structure. The encoder structure should not be allocated with
  * devm_kzalloc().
  *
- * Note: consider using drmm_encoder_alloc() instead of drm_encoder_init() to
- * let the DRM managed resource infrastructure take care of cleanup and
- * deallocation.
+ * Note: consider using drmm_encoder_alloc() or drmm_encoder_init()
+ * instead of drm_encoder_init() to let the DRM managed resource
+ * infrastructure take care of cleanup and deallocation.
  *
  * Returns:
  * Zero on success, error code on failure.
@@ -244,6 +244,48 @@ void *__drmm_encoder_alloc(struct drm_device *dev, size_t size, size_t offset,
 }
 EXPORT_SYMBOL(__drmm_encoder_alloc);
 
+/**
+ * drmm_encoder_init - Initialize a preallocated encoder
+ * @dev: drm device
+ * @encoder: the encoder to init
+ * @funcs: callbacks for this encoder (optional)
+ * @encoder_type: user visible type of the encoder
+ * @name: printf style format string for the encoder name, or NULL for default name
+ *
+ * Initializes a preallocated encoder. Encoder should be subclassed as
+ * part of driver encoder objects. Cleanup is automatically handled
+ * through registering drm_encoder_cleanup() with drmm_add_action(). The
+ * encoder structure should be allocated with drmm_kzalloc().
+ *
+ * The @drm_encoder_funcs.destroy hook must be NULL.
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
+int drmm_encoder_init(struct drm_device *dev, struct drm_encoder *encoder,
+		      const struct drm_encoder_funcs *funcs,
+		      int encoder_type, const char *name, ...)
+{
+	va_list ap;
+	int ret;
+
+	if (WARN_ON(funcs && funcs->destroy))
+		return -EINVAL;
+
+	va_start(ap, name);
+	ret = __drm_encoder_init(dev, encoder, funcs, encoder_type, name, ap);
+	va_end(ap);
+	if (ret)
+		return ret;
+
+	ret = drmm_add_action_or_reset(dev, drmm_encoder_alloc_release, encoder);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+EXPORT_SYMBOL(drmm_encoder_init);
+
 static struct drm_crtc *drm_encoder_get_crtc(struct drm_encoder *encoder)
 {
 	struct drm_connector *connector;
diff --git a/include/drm/drm_encoder.h b/include/drm/drm_encoder.h
index 6e91a0280f31..6713fe1804e9 100644
--- a/include/drm/drm_encoder.h
+++ b/include/drm/drm_encoder.h
@@ -194,6 +194,11 @@ int drm_encoder_init(struct drm_device *dev,
 		     const struct drm_encoder_funcs *funcs,
 		     int encoder_type, const char *name, ...);
 
+int drmm_encoder_init(struct drm_device *dev,
+		      struct drm_encoder *encoder,
+		      const struct drm_encoder_funcs *funcs,
+		      int encoder_type, const char *name, ...);
+
 __printf(6, 7)
 void *__drmm_encoder_alloc(struct drm_device *dev,
 			   size_t size, size_t offset,
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 04/64] drm/connector: Reorder headers
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (2 preceding siblings ...)
  2022-06-10  9:28 ` [PATCH 03/64] drm/encoder: Introduce drmm_encoder_init Maxime Ripard
@ 2022-06-10  9:28 ` Maxime Ripard
  2022-06-10  9:28 ` [PATCH 05/64] drm/connector: Mention the cleanup after drm_connector_init Maxime Ripard
                   ` (59 subsequent siblings)
  63 siblings, 0 replies; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:28 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

Unlike most of the other files in DRM, and Linux in general, the headers in
drm_connector.c aren't sorted alphabetically. Let's fix that.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/drm_connector.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index 1c48d162c77e..353d83ae09d3 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -22,14 +22,14 @@
 
 #include <drm/drm_auth.h>
 #include <drm/drm_connector.h>
+#include <drm/drm_drv.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_encoder.h>
-#include <drm/drm_utils.h>
-#include <drm/drm_print.h>
-#include <drm/drm_drv.h>
 #include <drm/drm_file.h>
+#include <drm/drm_print.h>
 #include <drm/drm_privacy_screen_consumer.h>
 #include <drm/drm_sysfs.h>
+#include <drm/drm_utils.h>
 
 #include <linux/uaccess.h>
 
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 05/64] drm/connector: Mention the cleanup after drm_connector_init
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (3 preceding siblings ...)
  2022-06-10  9:28 ` [PATCH 04/64] drm/connector: Reorder headers Maxime Ripard
@ 2022-06-10  9:28 ` Maxime Ripard
  2022-06-20 10:21   ` Thomas Zimmermann
  2022-06-10  9:28 ` [PATCH 06/64] drm/connector: Introduce drmm_connector_init Maxime Ripard
                   ` (58 subsequent siblings)
  63 siblings, 1 reply; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:28 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

Unlike encoders and CRTCs, the drm_connector_init() and
drm_connector_init_with_ddc() don't mention how the cleanup is supposed to
be done. Let's add it.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/drm_connector.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index 353d83ae09d3..2a78a23836d8 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -222,6 +222,11 @@ void drm_connector_free_work_fn(struct work_struct *work)
  * Initialises a preallocated connector. Connectors should be
  * subclassed as part of driver connector objects.
  *
+ * At driver unload time the driver's &drm_connector_funcs.destroy hook
+ * should call drm_connector_unregister(), drm_connector_cleanup() and
+ * kfree() the connector structure. The connector structure should not
+ * be allocated with devm_kzalloc().
+ *
  * Returns:
  * Zero on success, error code on failure.
  */
@@ -345,6 +350,11 @@ EXPORT_SYMBOL(drm_connector_init);
  * Initialises a preallocated connector. Connectors should be
  * subclassed as part of driver connector objects.
  *
+ * At driver unload time the driver's &drm_connector_funcs.destroy hook
+ * should call drm_connector_unregister(), drm_connector_cleanup() and
+ * kfree() the connector structure. The connector structure should not
+ * be allocated with devm_kzalloc().
+ *
  * Ensures that the ddc field of the connector is correctly set.
  *
  * Returns:
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 06/64] drm/connector: Introduce drmm_connector_init
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (4 preceding siblings ...)
  2022-06-10  9:28 ` [PATCH 05/64] drm/connector: Mention the cleanup after drm_connector_init Maxime Ripard
@ 2022-06-10  9:28 ` Maxime Ripard
  2022-06-20 10:23   ` Thomas Zimmermann
  2022-06-10  9:28 ` [PATCH 07/64] drm/connector: Introduce drmm_connector_init_with_ddc Maxime Ripard
                   ` (57 subsequent siblings)
  63 siblings, 1 reply; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:28 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

Unlike other DRM entities, there's no helper to create a DRM-managed
initialisation of a connector.

Let's create an helper to initialise a connector that would be passed as an
argument, and handle the cleanup through a DRM-managed action.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/drm_connector.c | 108 +++++++++++++++++++++++++-------
 include/drm/drm_connector.h     |   4 ++
 2 files changed, 90 insertions(+), 22 deletions(-)

diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index 2a78a23836d8..f150270b519f 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -26,6 +26,7 @@
 #include <drm/drm_edid.h>
 #include <drm/drm_encoder.h>
 #include <drm/drm_file.h>
+#include <drm/drm_managed.h>
 #include <drm/drm_print.h>
 #include <drm/drm_privacy_screen_consumer.h>
 #include <drm/drm_sysfs.h>
@@ -212,28 +213,10 @@ void drm_connector_free_work_fn(struct work_struct *work)
 	}
 }
 
-/**
- * drm_connector_init - Init a preallocated connector
- * @dev: DRM device
- * @connector: the connector to init
- * @funcs: callbacks for this connector
- * @connector_type: user visible type of the connector
- *
- * Initialises a preallocated connector. Connectors should be
- * subclassed as part of driver connector objects.
- *
- * At driver unload time the driver's &drm_connector_funcs.destroy hook
- * should call drm_connector_unregister(), drm_connector_cleanup() and
- * kfree() the connector structure. The connector structure should not
- * be allocated with devm_kzalloc().
- *
- * Returns:
- * Zero on success, error code on failure.
- */
-int drm_connector_init(struct drm_device *dev,
-		       struct drm_connector *connector,
-		       const struct drm_connector_funcs *funcs,
-		       int connector_type)
+static int __drm_connector_init(struct drm_device *dev,
+				struct drm_connector *connector,
+				const struct drm_connector_funcs *funcs,
+				int connector_type)
 {
 	struct drm_mode_config *config = &dev->mode_config;
 	int ret;
@@ -337,6 +320,39 @@ int drm_connector_init(struct drm_device *dev,
 
 	return ret;
 }
+
+/**
+ * drm_connector_init - Init a preallocated connector
+ * @dev: DRM device
+ * @connector: the connector to init
+ * @funcs: callbacks for this connector
+ * @connector_type: user visible type of the connector
+ *
+ * Initialises a preallocated connector. Connectors should be
+ * subclassed as part of driver connector objects.
+ *
+ * At driver unload time the driver's &drm_connector_funcs.destroy hook
+ * should call drm_connector_unregister(), drm_connector_cleanup() and
+ * kfree() the connector structure. The connector structure should not
+ * be allocated with devm_kzalloc().
+ *
+ * Note: consider using drmm_connector_init() instead of
+ * drm_connector_init() to let the DRM managed resource infrastructure
+ * take care of cleanup and deallocation.
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
+int drm_connector_init(struct drm_device *dev,
+		       struct drm_connector *connector,
+		       const struct drm_connector_funcs *funcs,
+		       int connector_type)
+{
+	if (WARN_ON(!(funcs && funcs->destroy)))
+		return -EINVAL;
+
+	return __drm_connector_init(dev, connector, funcs, connector_type);
+}
 EXPORT_SYMBOL(drm_connector_init);
 
 /**
@@ -379,6 +395,54 @@ int drm_connector_init_with_ddc(struct drm_device *dev,
 }
 EXPORT_SYMBOL(drm_connector_init_with_ddc);
 
+static void drm_connector_cleanup_action(struct drm_device *dev,
+					 void *ptr)
+{
+	struct drm_connector *connector = ptr;
+
+	drm_connector_unregister(connector);
+	drm_connector_cleanup(connector);
+}
+
+/**
+ * drmm_connector_init - Init a preallocated connector
+ * @dev: DRM device
+ * @connector: the connector to init
+ * @funcs: callbacks for this connector
+ * @connector_type: user visible type of the connector
+ *
+ * Initialises a preallocated connector. Connectors should be
+ * subclassed as part of driver connector objects. Cleanup is
+ * automatically handled through registering drm_connector_unregister()
+ * and drm_connector_cleanup() with drm_add_action(). The connector
+ * structure should be allocated with drmm_kzalloc().
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
+int drmm_connector_init(struct drm_device *dev,
+			struct drm_connector *connector,
+			const struct drm_connector_funcs *funcs,
+			int connector_type)
+{
+	int ret;
+
+	if (WARN_ON(funcs && funcs->destroy))
+		return -EINVAL;
+
+	ret = __drm_connector_init(dev, connector, funcs, connector_type);
+	if (ret)
+		return ret;
+
+	ret = drmm_add_action_or_reset(dev, drm_connector_cleanup_action,
+				       connector);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+EXPORT_SYMBOL(drmm_connector_init);
+
 /**
  * drm_connector_attach_edid_property - attach edid property.
  * @connector: the connector
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index 3ac4bf87f257..35a6b6e944b7 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -1672,6 +1672,10 @@ int drm_connector_init_with_ddc(struct drm_device *dev,
 				const struct drm_connector_funcs *funcs,
 				int connector_type,
 				struct i2c_adapter *ddc);
+int drmm_connector_init(struct drm_device *dev,
+			struct drm_connector *connector,
+			const struct drm_connector_funcs *funcs,
+			int connector_type);
 void drm_connector_attach_edid_property(struct drm_connector *connector);
 int drm_connector_register(struct drm_connector *connector);
 void drm_connector_unregister(struct drm_connector *connector);
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 07/64] drm/connector: Introduce drmm_connector_init_with_ddc
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (5 preceding siblings ...)
  2022-06-10  9:28 ` [PATCH 06/64] drm/connector: Introduce drmm_connector_init Maxime Ripard
@ 2022-06-10  9:28 ` Maxime Ripard
  2022-06-10  9:28 ` [PATCH 08/64] drm/writeback: Introduce drmm_writeback_connector_init Maxime Ripard
                   ` (56 subsequent siblings)
  63 siblings, 0 replies; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:28 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

Let's create a DRM-managed variant of drm_connector_init_with_ddc that will
take care of an action of the connector cleanup.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/drm_connector.c | 72 ++++++++++++++++++++++++++++-----
 include/drm/drm_connector.h     |  5 +++
 2 files changed, 67 insertions(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index f150270b519f..f577e5a739f1 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -355,6 +355,30 @@ int drm_connector_init(struct drm_device *dev,
 }
 EXPORT_SYMBOL(drm_connector_init);
 
+typedef int (*connector_init_t)(struct drm_device *dev,
+				struct drm_connector *connector,
+				const struct drm_connector_funcs *funcs,
+				int connector_type);
+
+static int __drm_connector_init_with_ddc(struct drm_device *dev,
+					 struct drm_connector *connector,
+					 connector_init_t init_func,
+					 const struct drm_connector_funcs *funcs,
+					 int connector_type,
+					 struct i2c_adapter *ddc)
+{
+	int ret;
+
+	ret = init_func(dev, connector, funcs, connector_type);
+	if (ret)
+		return ret;
+
+	/* provide ddc symlink in sysfs */
+	connector->ddc = ddc;
+
+	return ret;
+}
+
 /**
  * drm_connector_init_with_ddc - Init a preallocated connector
  * @dev: DRM device
@@ -373,6 +397,10 @@ EXPORT_SYMBOL(drm_connector_init);
  *
  * Ensures that the ddc field of the connector is correctly set.
  *
+ * Note: consider using drmm_connector_init_with_ddc() instead of
+ * drm_connector_init_with_ddc() to let the DRM managed resource
+ * infrastructure take care of cleanup and deallocation.
+ *
  * Returns:
  * Zero on success, error code on failure.
  */
@@ -382,16 +410,9 @@ int drm_connector_init_with_ddc(struct drm_device *dev,
 				int connector_type,
 				struct i2c_adapter *ddc)
 {
-	int ret;
-
-	ret = drm_connector_init(dev, connector, funcs, connector_type);
-	if (ret)
-		return ret;
-
-	/* provide ddc symlink in sysfs */
-	connector->ddc = ddc;
-
-	return ret;
+	return __drm_connector_init_with_ddc(dev, connector,
+					     drm_connector_init,
+					     funcs, connector_type, ddc);
 }
 EXPORT_SYMBOL(drm_connector_init_with_ddc);
 
@@ -443,6 +464,37 @@ int drmm_connector_init(struct drm_device *dev,
 }
 EXPORT_SYMBOL(drmm_connector_init);
 
+/**
+ * drmm_connector_init_with_ddc - Init a preallocated connector
+ * @dev: DRM device
+ * @connector: the connector to init
+ * @funcs: callbacks for this connector
+ * @connector_type: user visible type of the connector
+ * @ddc: pointer to the associated ddc adapter
+ *
+ * Initialises a preallocated connector. Connectors should be
+ * subclassed as part of driver connector objects. Cleanup is
+ * automatically handled through registering drm_connector_unregister()
+ * and drm_connector_cleanup() with drm_add_action(). The connector
+ * structure should be allocated with drmm_kzalloc().
+ *
+ * Ensures that the ddc field of the connector is correctly set.
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
+int drmm_connector_init_with_ddc(struct drm_device *dev,
+				 struct drm_connector *connector,
+				 const struct drm_connector_funcs *funcs,
+				 int connector_type,
+				 struct i2c_adapter *ddc)
+{
+	return __drm_connector_init_with_ddc(dev, connector,
+					     drmm_connector_init,
+					     funcs, connector_type, ddc);
+}
+EXPORT_SYMBOL(drmm_connector_init_with_ddc);
+
 /**
  * drm_connector_attach_edid_property - attach edid property.
  * @connector: the connector
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index 35a6b6e944b7..2565541f2c10 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -1676,6 +1676,11 @@ int drmm_connector_init(struct drm_device *dev,
 			struct drm_connector *connector,
 			const struct drm_connector_funcs *funcs,
 			int connector_type);
+int drmm_connector_init_with_ddc(struct drm_device *dev,
+				 struct drm_connector *connector,
+				 const struct drm_connector_funcs *funcs,
+				 int connector_type,
+				 struct i2c_adapter *ddc);
 void drm_connector_attach_edid_property(struct drm_connector *connector);
 int drm_connector_register(struct drm_connector *connector);
 void drm_connector_unregister(struct drm_connector *connector);
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 08/64] drm/writeback: Introduce drmm_writeback_connector_init
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (6 preceding siblings ...)
  2022-06-10  9:28 ` [PATCH 07/64] drm/connector: Introduce drmm_connector_init_with_ddc Maxime Ripard
@ 2022-06-10  9:28 ` Maxime Ripard
  2022-06-20 10:33   ` Thomas Zimmermann
  2022-06-10  9:28 ` [PATCH 09/64] drm/simple: Introduce drmm_simple_encoder_init Maxime Ripard
                   ` (55 subsequent siblings)
  63 siblings, 1 reply; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:28 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

Let's create a DRM-managed variant of drmm_writeback_connector_init that
will take care of an action of the encoder and connector cleanup.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/drm_writeback.c | 136 ++++++++++++++++++++++++--------
 include/drm/drm_writeback.h     |   5 ++
 2 files changed, 108 insertions(+), 33 deletions(-)

diff --git a/drivers/gpu/drm/drm_writeback.c b/drivers/gpu/drm/drm_writeback.c
index dccf4504f1bb..0b00921f3379 100644
--- a/drivers/gpu/drm/drm_writeback.c
+++ b/drivers/gpu/drm/drm_writeback.c
@@ -149,32 +149,27 @@ static const struct drm_encoder_funcs drm_writeback_encoder_funcs = {
 	.destroy = drm_encoder_cleanup,
 };
 
-/**
- * drm_writeback_connector_init - Initialize a writeback connector and its properties
- * @dev: DRM device
- * @wb_connector: Writeback connector to initialize
- * @con_funcs: Connector funcs vtable
- * @enc_helper_funcs: Encoder helper funcs vtable to be used by the internal encoder
- * @formats: Array of supported pixel formats for the writeback engine
- * @n_formats: Length of the formats array
- *
- * This function creates the writeback-connector-specific properties if they
- * have not been already created, initializes the connector as
- * type DRM_MODE_CONNECTOR_WRITEBACK, and correctly initializes the property
- * values. It will also create an internal encoder associated with the
- * drm_writeback_connector and set it to use the @enc_helper_funcs vtable for
- * the encoder helper.
- *
- * Drivers should always use this function instead of drm_connector_init() to
- * set up writeback connectors.
- *
- * Returns: 0 on success, or a negative error code
- */
-int drm_writeback_connector_init(struct drm_device *dev,
-				 struct drm_writeback_connector *wb_connector,
-				 const struct drm_connector_funcs *con_funcs,
-				 const struct drm_encoder_helper_funcs *enc_helper_funcs,
-				 const u32 *formats, int n_formats)
+typedef int (*encoder_init_t)(struct drm_device *dev,
+			      struct drm_encoder *encoder,
+			      const struct drm_encoder_funcs *funcs,
+			      int encoder_type,
+			      const char *name, ...);
+
+typedef int (*connector_init_t)(struct drm_device *dev,
+				struct drm_connector *connector,
+				const struct drm_connector_funcs *funcs,
+				int connector_type);
+
+static int writeback_init(struct drm_device *dev,
+			  struct drm_writeback_connector *wb_connector,
+			  connector_init_t conn_init,
+			  void (*conn_destroy)(struct drm_connector *connector),
+			  encoder_init_t enc_init,
+			  void (*enc_destroy)(struct drm_encoder *encoder),
+			  const struct drm_connector_funcs *con_funcs,
+			  const struct drm_encoder_funcs *enc_funcs,
+			  const struct drm_encoder_helper_funcs *enc_helper_funcs,
+			  const u32 *formats, int n_formats)
 {
 	struct drm_property_blob *blob;
 	struct drm_connector *connector = &wb_connector->base;
@@ -190,16 +185,16 @@ int drm_writeback_connector_init(struct drm_device *dev,
 		return PTR_ERR(blob);
 
 	drm_encoder_helper_add(&wb_connector->encoder, enc_helper_funcs);
-	ret = drm_encoder_init(dev, &wb_connector->encoder,
-			       &drm_writeback_encoder_funcs,
-			       DRM_MODE_ENCODER_VIRTUAL, NULL);
+	ret = enc_init(dev, &wb_connector->encoder,
+		       enc_funcs,
+		       DRM_MODE_ENCODER_VIRTUAL, NULL);
 	if (ret)
 		goto fail;
 
 	connector->interlace_allowed = 0;
 
-	ret = drm_connector_init(dev, connector, con_funcs,
-				 DRM_MODE_CONNECTOR_WRITEBACK);
+	ret = conn_init(dev, connector, con_funcs,
+			DRM_MODE_CONNECTOR_WRITEBACK);
 	if (ret)
 		goto connector_fail;
 
@@ -231,15 +226,90 @@ int drm_writeback_connector_init(struct drm_device *dev,
 	return 0;
 
 attach_fail:
-	drm_connector_cleanup(connector);
+	if (conn_destroy)
+		conn_destroy(connector);
 connector_fail:
-	drm_encoder_cleanup(&wb_connector->encoder);
+	if (enc_destroy)
+		enc_destroy(&wb_connector->encoder);
 fail:
 	drm_property_blob_put(blob);
 	return ret;
 }
+
+/**
+ * drm_writeback_connector_init - Initialize a writeback connector and its properties
+ * @dev: DRM device
+ * @wb_connector: Writeback connector to initialize
+ * @con_funcs: Connector funcs vtable
+ * @enc_helper_funcs: Encoder helper funcs vtable to be used by the internal encoder
+ * @formats: Array of supported pixel formats for the writeback engine
+ * @n_formats: Length of the formats array
+ *
+ * This function creates the writeback-connector-specific properties if they
+ * have not been already created, initializes the connector as
+ * type DRM_MODE_CONNECTOR_WRITEBACK, and correctly initializes the property
+ * values. It will also create an internal encoder associated with the
+ * drm_writeback_connector and set it to use the @enc_helper_funcs vtable for
+ * the encoder helper.
+ *
+ * Drivers should always use this function instead of drm_connector_init() to
+ * set up writeback connectors.
+ *
+ * Returns: 0 on success, or a negative error code
+ */
+int drm_writeback_connector_init(struct drm_device *dev,
+				 struct drm_writeback_connector *wb_connector,
+				 const struct drm_connector_funcs *con_funcs,
+				 const struct drm_encoder_helper_funcs *enc_helper_funcs,
+				 const u32 *formats, int n_formats)
+{
+	return writeback_init(dev, wb_connector,
+			      drm_connector_init, drm_connector_cleanup,
+			      drm_encoder_init, drm_encoder_cleanup,
+			      con_funcs,
+			      &drm_writeback_encoder_funcs, enc_helper_funcs,
+			      formats, n_formats);
+}
 EXPORT_SYMBOL(drm_writeback_connector_init);
 
+/**
+ * drmm_writeback_connector_init - Initialize a writeback connector and its properties
+ * @dev: DRM device
+ * @wb_connector: Writeback connector to initialize
+ * @con_funcs: Connector funcs vtable
+ * @enc_helper_funcs: Encoder helper funcs vtable to be used by the internal encoder
+ * @formats: Array of supported pixel formats for the writeback engine
+ * @n_formats: Length of the formats array
+ *
+ * This function creates the writeback-connector-specific properties if they
+ * have not been already created, initializes the connector as
+ * type DRM_MODE_CONNECTOR_WRITEBACK, and correctly initializes the property
+ * values. It will also create an internal encoder associated with the
+ * drm_writeback_connector and set it to use the @enc_helper_funcs vtable for
+ * the encoder helper.
+ *
+ * The writeback connector is DRM-managed and won't need any cleanup.
+ *
+ * Drivers should always use this function instead of drm_connector_init() to
+ * set up writeback connectors.
+ *
+ * Returns: 0 on success, or a negative error code
+ */
+int drmm_writeback_connector_init(struct drm_device *dev,
+				 struct drm_writeback_connector *wb_connector,
+				 const struct drm_connector_funcs *con_funcs,
+				 const struct drm_encoder_helper_funcs *enc_helper_funcs,
+				 const u32 *formats, int n_formats)
+{
+	return writeback_init(dev, wb_connector,
+			      drmm_connector_init, NULL,
+			      drmm_encoder_init, NULL,
+			      con_funcs,
+			      NULL, enc_helper_funcs,
+			      formats, n_formats);
+}
+EXPORT_SYMBOL(drmm_writeback_connector_init);
+
 int drm_writeback_set_fb(struct drm_connector_state *conn_state,
 			 struct drm_framebuffer *fb)
 {
diff --git a/include/drm/drm_writeback.h b/include/drm/drm_writeback.h
index 9697d2714d2a..cc259ae44734 100644
--- a/include/drm/drm_writeback.h
+++ b/include/drm/drm_writeback.h
@@ -151,6 +151,11 @@ int drm_writeback_connector_init(struct drm_device *dev,
 				 const struct drm_connector_funcs *con_funcs,
 				 const struct drm_encoder_helper_funcs *enc_helper_funcs,
 				 const u32 *formats, int n_formats);
+int drmm_writeback_connector_init(struct drm_device *dev,
+				  struct drm_writeback_connector *wb_connector,
+				  const struct drm_connector_funcs *con_funcs,
+				  const struct drm_encoder_helper_funcs *enc_helper_funcs,
+				  const u32 *formats, int n_formats);
 
 int drm_writeback_set_fb(struct drm_connector_state *conn_state,
 			 struct drm_framebuffer *fb);
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 09/64] drm/simple: Introduce drmm_simple_encoder_init
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (7 preceding siblings ...)
  2022-06-10  9:28 ` [PATCH 08/64] drm/writeback: Introduce drmm_writeback_connector_init Maxime Ripard
@ 2022-06-10  9:28 ` Maxime Ripard
  2022-06-20 10:44   ` Thomas Zimmermann
  2022-06-10  9:28 ` [PATCH 10/64] drm/bridge: panel: Introduce drmm_panel_bridge_add Maxime Ripard
                   ` (54 subsequent siblings)
  63 siblings, 1 reply; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:28 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

The DRM-managed function to register an encoder is
drmm_simple_encoder_alloc() and its variants, which will allocate the
underlying structure and initialisation the encoder.

However, we might want to separate the structure creation and the encoder
initialisation, for example if the structure is shared across multiple DRM
entities, for example an encoder and a connector.

Let's create an helper to only initialise an encoder that would be passed
as an argument.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/drm_simple_kms_helper.c | 46 +++++++++++++++++++++++--
 include/drm/drm_simple_kms_helper.h     |  3 ++
 2 files changed, 46 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/drm_simple_kms_helper.c b/drivers/gpu/drm/drm_simple_kms_helper.c
index 72989ed1baba..876870dd98e5 100644
--- a/drivers/gpu/drm/drm_simple_kms_helper.c
+++ b/drivers/gpu/drm/drm_simple_kms_helper.c
@@ -58,9 +58,10 @@ static const struct drm_encoder_funcs drm_simple_encoder_funcs_cleanup = {
  * stored in the device structure. Free the encoder's memory as part of
  * the device release function.
  *
- * Note: consider using drmm_simple_encoder_alloc() instead of
- * drm_simple_encoder_init() to let the DRM managed resource infrastructure
- * take care of cleanup and deallocation.
+ * Note: consider using drmm_simple_encoder_alloc() or
+ * drmm_simple_encoder_init() instead of drm_simple_encoder_init() to
+ * let the DRM managed resource infrastructure take care of cleanup and
+ * deallocation.
  *
  * Returns:
  * Zero on success, error code on failure.
@@ -75,6 +76,45 @@ int drm_simple_encoder_init(struct drm_device *dev,
 }
 EXPORT_SYMBOL(drm_simple_encoder_init);
 
+static void drmm_simple_encoder_cleanup(struct drm_device *dev, void *ptr)
+{
+	struct drm_encoder *encoder = ptr;
+
+	drm_encoder_cleanup(encoder);
+}
+
+/**
+ * drmm_simple_encoder_init - Initialize a preallocated encoder with
+ *                            basic functionality.
+ * @dev: drm device
+ * @encoder: the encoder to initialize
+ * @encoder_type: user visible type of the encoder
+ *
+ * Initialises a preallocated encoder that has no further functionality.
+ * Settings for possible CRTC and clones are left to their initial
+ * values. The encoder will be cleaned up automatically using a
+ * DRM-managed action.
+ *
+ * The structure containing the encoder's memory should be allocated
+ * using drmm_kzalloc().
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ */
+int drmm_simple_encoder_init(struct drm_device *dev,
+			     struct drm_encoder *encoder,
+			     int encoder_type)
+{
+	int ret;
+
+	ret = drm_encoder_init(dev, encoder, NULL, encoder_type, NULL);
+	if (ret)
+		return ret;
+
+	return drmm_add_action_or_reset(dev, drmm_simple_encoder_cleanup, encoder);
+}
+EXPORT_SYMBOL(drmm_simple_encoder_init);
+
 void *__drmm_simple_encoder_alloc(struct drm_device *dev, size_t size,
 				  size_t offset, int encoder_type)
 {
diff --git a/include/drm/drm_simple_kms_helper.h b/include/drm/drm_simple_kms_helper.h
index 0b3647e614dd..20456f4712f0 100644
--- a/include/drm/drm_simple_kms_helper.h
+++ b/include/drm/drm_simple_kms_helper.h
@@ -241,6 +241,9 @@ int drm_simple_display_pipe_init(struct drm_device *dev,
 int drm_simple_encoder_init(struct drm_device *dev,
 			    struct drm_encoder *encoder,
 			    int encoder_type);
+int drmm_simple_encoder_init(struct drm_device *dev,
+			     struct drm_encoder *encoder,
+			     int encoder_type);
 
 void *__drmm_simple_encoder_alloc(struct drm_device *dev, size_t size,
 				  size_t offset, int encoder_type);
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 10/64] drm/bridge: panel: Introduce drmm_panel_bridge_add
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (8 preceding siblings ...)
  2022-06-10  9:28 ` [PATCH 09/64] drm/simple: Introduce drmm_simple_encoder_init Maxime Ripard
@ 2022-06-10  9:28 ` Maxime Ripard
  2022-06-10  9:28 ` [PATCH 11/64] drm/bridge: panel: Introduce drmm_of_get_bridge Maxime Ripard
                   ` (53 subsequent siblings)
  63 siblings, 0 replies; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:28 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

Unlike what can be found for other entities, there's no DRM-managed
function to create a panel_bridge instance from a panel.

Let's introduce one.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/bridge/panel.c | 39 ++++++++++++++++++++++++++++++++++
 include/drm/drm_bridge.h       |  2 ++
 2 files changed, 41 insertions(+)

diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c
index 0ee563eb2b6f..07d720aa38c6 100644
--- a/drivers/gpu/drm/bridge/panel.c
+++ b/drivers/gpu/drm/bridge/panel.c
@@ -8,6 +8,7 @@
 #include <drm/drm_bridge.h>
 #include <drm/drm_connector.h>
 #include <drm/drm_encoder.h>
+#include <drm/drm_managed.h>
 #include <drm/drm_modeset_helper_vtables.h>
 #include <drm/drm_of.h>
 #include <drm/drm_panel.h>
@@ -333,6 +334,44 @@ struct drm_bridge *devm_drm_panel_bridge_add_typed(struct device *dev,
 }
 EXPORT_SYMBOL(devm_drm_panel_bridge_add_typed);
 
+static void drmm_drm_panel_bridge_release(struct drm_device *drm, void *ptr)
+{
+	struct drm_bridge *bridge = ptr;
+
+	drm_panel_bridge_remove(bridge);
+}
+
+/**
+ * drmm_panel_bridge_add - Creates a DRM-managed &drm_bridge and
+ *                         &drm_connector that just calls the
+ *                         appropriate functions from &drm_panel.
+ *
+ * @dev: DRM device to tie the bridge lifetime to
+ * @panel: The drm_panel being wrapped.  Must be non-NULL.
+ *
+ * This is the DRM-managed version of drm_panel_bridge_add() which
+ * automatically calls drm_panel_bridge_remove() when @dev is cleaned
+ * up.
+ */
+struct drm_bridge *drmm_panel_bridge_add(struct drm_device *drm,
+					 struct drm_panel *panel)
+{
+	struct drm_bridge *bridge;
+	int ret;
+
+	bridge = drm_panel_bridge_add_typed(panel, panel->connector_type);
+	if (IS_ERR(bridge))
+		return bridge;
+
+	ret = drmm_add_action_or_reset(drm, drmm_drm_panel_bridge_release,
+				       bridge);
+	if (ret)
+		return ERR_PTR(ret);
+
+	return bridge;
+}
+EXPORT_SYMBOL(drmm_panel_bridge_add);
+
 /**
  * drm_panel_bridge_connector - return the connector for the panel bridge
  * @bridge: The drm_bridge.
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index 42aec8612f37..8100a15dd9c2 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -927,6 +927,8 @@ struct drm_bridge *devm_drm_panel_bridge_add(struct device *dev,
 struct drm_bridge *devm_drm_panel_bridge_add_typed(struct device *dev,
 						   struct drm_panel *panel,
 						   u32 connector_type);
+struct drm_bridge *drmm_panel_bridge_add(struct drm_device *drm,
+					     struct drm_panel *panel);
 struct drm_connector *drm_panel_bridge_connector(struct drm_bridge *bridge);
 #endif
 
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 11/64] drm/bridge: panel: Introduce drmm_of_get_bridge
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (9 preceding siblings ...)
  2022-06-10  9:28 ` [PATCH 10/64] drm/bridge: panel: Introduce drmm_panel_bridge_add Maxime Ripard
@ 2022-06-10  9:28 ` Maxime Ripard
  2022-06-10  9:28 ` [PATCH 12/64] drm/vc4: Call component_unbind_all() Maxime Ripard
                   ` (52 subsequent siblings)
  63 siblings, 0 replies; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:28 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

Unlike what can be found for other DRM entities, we don't have a
DRM-managed function equivalent to devm_drm_of_get_bridge().

Let's create it.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/bridge/panel.c | 35 ++++++++++++++++++++++++++++++++++
 include/drm/drm_bridge.h       |  2 ++
 2 files changed, 37 insertions(+)

diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c
index 07d720aa38c6..0bf824ca1f25 100644
--- a/drivers/gpu/drm/bridge/panel.c
+++ b/drivers/gpu/drm/bridge/panel.c
@@ -425,4 +425,39 @@ struct drm_bridge *devm_drm_of_get_bridge(struct device *dev,
 	return bridge;
 }
 EXPORT_SYMBOL(devm_drm_of_get_bridge);
+
+/**
+ * drmm_of_get_bridge - Return next bridge in the chain
+ * @dev: device to tie the bridge lifetime to
+ * @np: device tree node containing encoder output ports
+ * @port: port in the device tree node
+ * @endpoint: endpoint in the device tree node
+ *
+ * Given a DT node's port and endpoint number, finds the connected node
+ * and returns the associated bridge if any, or creates and returns a
+ * drm panel bridge instance if a panel is connected.
+ *
+ * Returns a pointer to the bridge if successful, or an error pointer
+ * otherwise.
+ */
+struct drm_bridge *drmm_of_get_bridge(struct drm_device *drm,
+					  struct device_node *np,
+					  u32 port, u32 endpoint)
+{
+	struct drm_bridge *bridge;
+	struct drm_panel *panel;
+	int ret;
+
+	ret = drm_of_find_panel_or_bridge(np, port, endpoint,
+					  &panel, &bridge);
+	if (ret)
+		return ERR_PTR(ret);
+
+	if (panel)
+		bridge = drmm_panel_bridge_add(drm, panel);
+
+	return bridge;
+}
+EXPORT_SYMBOL(drmm_of_get_bridge);
+
 #endif
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index 8100a15dd9c2..ddb92e745b2e 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -935,6 +935,8 @@ struct drm_connector *drm_panel_bridge_connector(struct drm_bridge *bridge);
 #if defined(CONFIG_OF) && defined(CONFIG_DRM_PANEL_BRIDGE)
 struct drm_bridge *devm_drm_of_get_bridge(struct device *dev, struct device_node *node,
 					  u32 port, u32 endpoint);
+struct drm_bridge *drmm_of_get_bridge(struct drm_device *drm, struct device_node *node,
+					  u32 port, u32 endpoint);
 #else
 static inline struct drm_bridge *devm_drm_of_get_bridge(struct device *dev,
 							struct device_node *node,
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 12/64] drm/vc4: Call component_unbind_all()
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (10 preceding siblings ...)
  2022-06-10  9:28 ` [PATCH 11/64] drm/bridge: panel: Introduce drmm_of_get_bridge Maxime Ripard
@ 2022-06-10  9:28 ` Maxime Ripard
  2022-06-14 15:02   ` Dave Stevenson
  2022-06-10  9:28 ` [PATCH 13/64] drm/vc4: hvs: Protect device resources after removal Maxime Ripard
                   ` (51 subsequent siblings)
  63 siblings, 1 reply; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:28 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

While we were using the component framework to deal with all the DRM
subdevices, we were not calling component_unbind_all().

This leads to none of the subdevices freeing up their resources as part of
their unbind() or device managed hooks.

Fixes: c8b75bca92cb ("drm/vc4: Add KMS support for Raspberry Pi.")
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_drv.c | 14 ++++++++++++--
 drivers/gpu/drm/vc4/vc4_drv.h |  1 +
 2 files changed, 13 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c
index 162bc18e7497..031f2cdd658d 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.c
+++ b/drivers/gpu/drm/vc4/vc4_drv.c
@@ -209,6 +209,13 @@ static void vc4_match_add_drivers(struct device *dev,
 	}
 }
 
+static void vc4_component_unbind_all(void *ptr)
+{
+	struct vc4_dev *vc4 = ptr;
+
+	component_unbind_all(vc4->dev, &vc4->base);
+}
+
 static int vc4_drm_bind(struct device *dev)
 {
 	struct platform_device *pdev = to_platform_device(dev);
@@ -230,6 +237,7 @@ static int vc4_drm_bind(struct device *dev)
 	vc4 = devm_drm_dev_alloc(dev, &vc4_drm_driver, struct vc4_dev, base);
 	if (IS_ERR(vc4))
 		return PTR_ERR(vc4);
+	vc4->dev = dev;
 
 	drm = &vc4->base;
 	platform_set_drvdata(pdev, drm);
@@ -276,6 +284,10 @@ static int vc4_drm_bind(struct device *dev)
 	if (ret)
 		return ret;
 
+	ret = devm_add_action_or_reset(dev, vc4_component_unbind_all, vc4);
+	if (ret)
+		return ret;
+
 	ret = vc4_plane_create_additional_planes(drm);
 	if (ret)
 		goto unbind_all;
@@ -296,8 +308,6 @@ static int vc4_drm_bind(struct device *dev)
 	return 0;
 
 unbind_all:
-	component_unbind_all(dev, drm);
-
 	return ret;
 }
 
diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h
index 15e0c2ac3940..aa4c5910ea05 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.h
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
@@ -73,6 +73,7 @@ struct vc4_perfmon {
 
 struct vc4_dev {
 	struct drm_device base;
+	struct device *dev;
 
 	unsigned int irq;
 
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 13/64] drm/vc4: hvs: Protect device resources after removal
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (11 preceding siblings ...)
  2022-06-10  9:28 ` [PATCH 12/64] drm/vc4: Call component_unbind_all() Maxime Ripard
@ 2022-06-10  9:28 ` Maxime Ripard
  2022-06-14 15:11   ` Dave Stevenson
  2022-06-20 10:48   ` Thomas Zimmermann
  2022-06-10  9:28 ` [PATCH 14/64] drm/vc4: hvs: Remove planes currently allocated before taking down Maxime Ripard
                   ` (50 subsequent siblings)
  63 siblings, 2 replies; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:28 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

Whenever the device and driver are unbound, the main device and all the
subdevices will be removed by calling their unbind() method.

However, the DRM device itself will only be freed when the last user will
have closed it.

It means that there is a time window where the device and its resources
aren't there anymore, but the userspace can still call into our driver.

Fortunately, the DRM framework provides the drm_dev_enter() and
drm_dev_exit() functions to make sure our underlying device is still there
for the section protected by those calls. Let's add them to the HVS driver.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_drv.h |   1 +
 drivers/gpu/drm/vc4/vc4_hvs.c | 106 +++++++++++++++++++++++++++++++---
 2 files changed, 99 insertions(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h
index aa4c5910ea05..080deae55f64 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.h
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
@@ -317,6 +317,7 @@ struct vc4_v3d {
 };
 
 struct vc4_hvs {
+	struct drm_device *dev;
 	struct platform_device *pdev;
 	void __iomem *regs;
 	u32 __iomem *dlist;
diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c
index 2a58fc421cf6..483053e7b14f 100644
--- a/drivers/gpu/drm/vc4/vc4_hvs.c
+++ b/drivers/gpu/drm/vc4/vc4_hvs.c
@@ -25,6 +25,7 @@
 #include <linux/platform_device.h>
 
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_drv.h>
 #include <drm/drm_vblank.h>
 
 #include "vc4_drv.h"
@@ -66,11 +67,15 @@ static const struct debugfs_reg32 hvs_regs[] = {
 
 void vc4_hvs_dump_state(struct vc4_hvs *hvs)
 {
+	struct drm_device *drm = hvs->dev;
 	struct drm_printer p = drm_info_printer(&hvs->pdev->dev);
-	int i;
+	int idx, i;
 
 	drm_print_regset32(&p, &hvs->regset);
 
+	if (!drm_dev_enter(drm, &idx))
+		return;
+
 	DRM_INFO("HVS ctx:\n");
 	for (i = 0; i < 64; i += 4) {
 		DRM_INFO("0x%08x (%s): 0x%08x 0x%08x 0x%08x 0x%08x\n",
@@ -80,6 +85,8 @@ void vc4_hvs_dump_state(struct vc4_hvs *hvs)
 			 readl((u32 __iomem *)hvs->dlist + i + 2),
 			 readl((u32 __iomem *)hvs->dlist + i + 3));
 	}
+
+	drm_dev_exit(idx);
 }
 
 static int vc4_hvs_debugfs_underrun(struct seq_file *m, void *data)
@@ -132,14 +139,18 @@ static int vc4_hvs_upload_linear_kernel(struct vc4_hvs *hvs,
 					struct drm_mm_node *space,
 					const u32 *kernel)
 {
-	int ret, i;
+	struct drm_device *drm = hvs->dev;
+	int idx, ret, i;
 	u32 __iomem *dst_kernel;
 
+	if (!drm_dev_enter(drm, &idx))
+		return -ENODEV;
+
 	ret = drm_mm_insert_node(&hvs->dlist_mm, space, VC4_KERNEL_DWORDS);
 	if (ret) {
 		DRM_ERROR("Failed to allocate space for filter kernel: %d\n",
 			  ret);
-		return ret;
+		goto err_dev_exit;
 	}
 
 	dst_kernel = hvs->dlist + space->start;
@@ -153,16 +164,26 @@ static int vc4_hvs_upload_linear_kernel(struct vc4_hvs *hvs,
 		}
 	}
 
+	drm_dev_exit(idx);
 	return 0;
+
+err_dev_exit:
+	drm_dev_exit(idx);
+	return ret;
 }
 
 static void vc4_hvs_lut_load(struct vc4_hvs *hvs,
 			     struct vc4_crtc *vc4_crtc)
 {
+	struct drm_device *drm = hvs->dev;
 	struct drm_crtc *crtc = &vc4_crtc->base;
 	struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state);
+	int idx;
 	u32 i;
 
+	if (!drm_dev_enter(drm, &idx))
+		return;
+
 	/* The LUT memory is laid out with each HVS channel in order,
 	 * each of which takes 256 writes for R, 256 for G, then 256
 	 * for B.
@@ -177,6 +198,8 @@ static void vc4_hvs_lut_load(struct vc4_hvs *hvs,
 		HVS_WRITE(SCALER_GAMDATA, vc4_crtc->lut_g[i]);
 	for (i = 0; i < crtc->gamma_size; i++)
 		HVS_WRITE(SCALER_GAMDATA, vc4_crtc->lut_b[i]);
+
+	drm_dev_exit(idx);
 }
 
 static void vc4_hvs_update_gamma_lut(struct vc4_hvs *hvs,
@@ -198,7 +221,12 @@ static void vc4_hvs_update_gamma_lut(struct vc4_hvs *hvs,
 
 u8 vc4_hvs_get_fifo_frame_count(struct vc4_hvs *hvs, unsigned int fifo)
 {
+	struct drm_device *drm = hvs->dev;
 	u8 field = 0;
+	int idx;
+
+	if (!drm_dev_enter(drm, &idx))
+		return 0;
 
 	switch (fifo) {
 	case 0:
@@ -215,6 +243,7 @@ u8 vc4_hvs_get_fifo_frame_count(struct vc4_hvs *hvs, unsigned int fifo)
 		break;
 	}
 
+	drm_dev_exit(idx);
 	return field;
 }
 
@@ -226,6 +255,12 @@ int vc4_hvs_get_fifo_from_output(struct vc4_hvs *hvs, unsigned int output)
 	if (!hvs->hvs5)
 		return output;
 
+	/*
+	 * NOTE: We should probably use drm_dev_enter()/drm_dev_exit()
+	 * here, but this function is only used during the DRM device
+	 * initialization, so we should be fine.
+	 */
+
 	switch (output) {
 	case 0:
 		return 0;
@@ -273,12 +308,17 @@ int vc4_hvs_get_fifo_from_output(struct vc4_hvs *hvs, unsigned int output)
 static int vc4_hvs_init_channel(struct vc4_hvs *hvs, struct drm_crtc *crtc,
 				struct drm_display_mode *mode, bool oneshot)
 {
+	struct drm_device *drm = hvs->dev;
 	struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
 	struct vc4_crtc_state *vc4_crtc_state = to_vc4_crtc_state(crtc->state);
 	unsigned int chan = vc4_crtc_state->assigned_channel;
 	bool interlace = mode->flags & DRM_MODE_FLAG_INTERLACE;
 	u32 dispbkgndx;
 	u32 dispctrl;
+	int idx;
+
+	if (!drm_dev_enter(drm, &idx))
+		return -ENODEV;
 
 	HVS_WRITE(SCALER_DISPCTRLX(chan), 0);
 	HVS_WRITE(SCALER_DISPCTRLX(chan), SCALER_DISPCTRLX_RESET);
@@ -320,13 +360,21 @@ static int vc4_hvs_init_channel(struct vc4_hvs *hvs, struct drm_crtc *crtc,
 	 */
 	vc4_hvs_lut_load(hvs, vc4_crtc);
 
+	drm_dev_exit(idx);
+
 	return 0;
 }
 
 void vc4_hvs_stop_channel(struct vc4_hvs *hvs, unsigned int chan)
 {
+	struct drm_device *drm = hvs->dev;
+	int idx;
+
+	if (!drm_dev_enter(drm, &idx))
+		return;
+
 	if (HVS_READ(SCALER_DISPCTRLX(chan)) & SCALER_DISPCTRLX_ENABLE)
-		return;
+		goto out;
 
 	HVS_WRITE(SCALER_DISPCTRLX(chan),
 		  HVS_READ(SCALER_DISPCTRLX(chan)) | SCALER_DISPCTRLX_RESET);
@@ -343,6 +391,9 @@ void vc4_hvs_stop_channel(struct vc4_hvs *hvs, unsigned int chan)
 	WARN_ON_ONCE((HVS_READ(SCALER_DISPSTATX(chan)) &
 		      (SCALER_DISPSTATX_FULL | SCALER_DISPSTATX_EMPTY)) !=
 		     SCALER_DISPSTATX_EMPTY);
+
+out:
+	drm_dev_exit(idx);
 }
 
 int vc4_hvs_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state)
@@ -384,9 +435,15 @@ static void vc4_hvs_install_dlist(struct drm_crtc *crtc)
 	struct vc4_dev *vc4 = to_vc4_dev(dev);
 	struct vc4_hvs *hvs = vc4->hvs;
 	struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state);
+	int idx;
+
+	if (!drm_dev_enter(dev, &idx))
+		return;
 
 	HVS_WRITE(SCALER_DISPLISTX(vc4_state->assigned_channel),
 		  vc4_state->mm.start);
+
+	drm_dev_exit(idx);
 }
 
 static void vc4_hvs_update_dlist(struct drm_crtc *crtc)
@@ -471,6 +528,10 @@ void vc4_hvs_atomic_flush(struct drm_crtc *crtc,
 	bool enable_bg_fill = false;
 	u32 __iomem *dlist_start = vc4->hvs->dlist + vc4_state->mm.start;
 	u32 __iomem *dlist_next = dlist_start;
+	int idx;
+
+	if (!drm_dev_enter(dev, &idx))
+		return;
 
 	if (debug_dump_regs) {
 		DRM_INFO("CRTC %d HVS before:\n", drm_crtc_index(crtc));
@@ -541,26 +602,44 @@ void vc4_hvs_atomic_flush(struct drm_crtc *crtc,
 		DRM_INFO("CRTC %d HVS after:\n", drm_crtc_index(crtc));
 		vc4_hvs_dump_state(hvs);
 	}
+
+	drm_dev_exit(idx);
 }
 
 void vc4_hvs_mask_underrun(struct vc4_hvs *hvs, int channel)
 {
-	u32 dispctrl = HVS_READ(SCALER_DISPCTRL);
+	struct drm_device *drm = hvs->dev;
+	u32 dispctrl;
+	int idx;
 
+	if (!drm_dev_enter(drm, &idx))
+		return;
+
+	dispctrl = HVS_READ(SCALER_DISPCTRL);
 	dispctrl &= ~SCALER_DISPCTRL_DSPEISLUR(channel);
 
 	HVS_WRITE(SCALER_DISPCTRL, dispctrl);
+
+	drm_dev_exit(idx);
 }
 
 void vc4_hvs_unmask_underrun(struct vc4_hvs *hvs, int channel)
 {
-	u32 dispctrl = HVS_READ(SCALER_DISPCTRL);
+	struct drm_device *drm = hvs->dev;
+	u32 dispctrl;
+	int idx;
 
+	if (!drm_dev_enter(drm, &idx))
+		return;
+
+	dispctrl = HVS_READ(SCALER_DISPCTRL);
 	dispctrl |= SCALER_DISPCTRL_DSPEISLUR(channel);
 
 	HVS_WRITE(SCALER_DISPSTAT,
 		  SCALER_DISPSTAT_EUFLOW(channel));
 	HVS_WRITE(SCALER_DISPCTRL, dispctrl);
+
+	drm_dev_exit(idx);
 }
 
 static void vc4_hvs_report_underrun(struct drm_device *dev)
@@ -581,6 +660,17 @@ static irqreturn_t vc4_hvs_irq_handler(int irq, void *data)
 	u32 control;
 	u32 status;
 
+	/*
+	 * NOTE: We don't need to protect the register access using
+	 * drm_dev_enter() there because the interrupt handler lifetime
+	 * is tied to the device itself, and not to the DRM device.
+	 *
+	 * So when the device will be gone, one of the first thing we
+	 * will be doing will be to unregister the interrupt handler,
+	 * and then unregister the DRM device. drm_dev_enter() would
+	 * thus always succeed if we are here.
+	 */
+
 	status = HVS_READ(SCALER_DISPSTAT);
 	control = HVS_READ(SCALER_DISPCTRL);
 
@@ -613,10 +703,10 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)
 	u32 dispctrl;
 	u32 reg;
 
-	hvs = devm_kzalloc(&pdev->dev, sizeof(*hvs), GFP_KERNEL);
+	hvs = drmm_kzalloc(drm, sizeof(*hvs), GFP_KERNEL);
 	if (!hvs)
 		return -ENOMEM;
-
+	hvs->dev = drm;
 	hvs->pdev = pdev;
 
 	if (of_device_is_compatible(pdev->dev.of_node, "brcm,bcm2711-hvs"))
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 14/64] drm/vc4: hvs: Remove planes currently allocated before taking down
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (12 preceding siblings ...)
  2022-06-10  9:28 ` [PATCH 13/64] drm/vc4: hvs: Protect device resources after removal Maxime Ripard
@ 2022-06-10  9:28 ` Maxime Ripard
  2022-06-14 14:51   ` Dave Stevenson
  2022-06-10  9:28 ` [PATCH 15/64] drm/vc4: plane: Take possible_crtcs as an argument Maxime Ripard
                   ` (49 subsequent siblings)
  63 siblings, 1 reply; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:28 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

When the HVS driver is unbound, a lot of memory allocations in the LBM and
DLIST RAM are still assigned to planes that are still allocated.

Thus, we hit a warning when calling drm_mm_takedown() since the memory pool
is not completely free of allocations.

Let's free all the currently live entries before calling drm_mm_takedown().

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_hvs.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c
index 483053e7b14f..b0906bb96c32 100644
--- a/drivers/gpu/drm/vc4/vc4_hvs.c
+++ b/drivers/gpu/drm/vc4/vc4_hvs.c
@@ -834,11 +834,18 @@ static void vc4_hvs_unbind(struct device *dev, struct device *master,
 	struct drm_device *drm = dev_get_drvdata(master);
 	struct vc4_dev *vc4 = to_vc4_dev(drm);
 	struct vc4_hvs *hvs = vc4->hvs;
+	struct drm_mm_node *node, *next;
 
 	if (drm_mm_node_allocated(&vc4->hvs->mitchell_netravali_filter))
 		drm_mm_remove_node(&vc4->hvs->mitchell_netravali_filter);
 
+	drm_mm_for_each_node_safe(node, next, &vc4->hvs->dlist_mm)
+		drm_mm_remove_node(node);
+
 	drm_mm_takedown(&vc4->hvs->dlist_mm);
+
+	drm_mm_for_each_node_safe(node, next, &vc4->hvs->lbm_mm)
+		drm_mm_remove_node(node);
 	drm_mm_takedown(&vc4->hvs->lbm_mm);
 
 	clk_disable_unprepare(hvs->core_clk);
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 15/64] drm/vc4: plane: Take possible_crtcs as an argument
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (13 preceding siblings ...)
  2022-06-10  9:28 ` [PATCH 14/64] drm/vc4: hvs: Remove planes currently allocated before taking down Maxime Ripard
@ 2022-06-10  9:28 ` Maxime Ripard
  2022-06-14 15:27   ` Dave Stevenson
  2022-06-10  9:28 ` [PATCH 16/64] drm/vc4: plane: Switch to drmm_universal_plane_alloc() Maxime Ripard
                   ` (48 subsequent siblings)
  63 siblings, 1 reply; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:28 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

vc4_plane_init() currently initialises the plane with no possible CRTCs,
and will expect the caller to set it up by itself.

Let's change that logic a bit to follow the syntax of
drm_universal_plane_init() and pass the possible CRTCs bitmask as an
argument to the function instead.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_crtc.c  |  2 +-
 drivers/gpu/drm/vc4/vc4_drv.h   |  3 ++-
 drivers/gpu/drm/vc4/vc4_plane.c | 15 +++++++--------
 3 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c
index 59b20c8f132b..840a93484bb1 100644
--- a/drivers/gpu/drm/vc4/vc4_crtc.c
+++ b/drivers/gpu/drm/vc4/vc4_crtc.c
@@ -1138,7 +1138,7 @@ int vc4_crtc_init(struct drm_device *drm, struct vc4_crtc *vc4_crtc,
 	 * requirement of the plane configuration, and reject ones
 	 * that will take too much.
 	 */
-	primary_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_PRIMARY);
+	primary_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_PRIMARY, 0);
 	if (IS_ERR(primary_plane)) {
 		dev_err(drm->dev, "failed to construct primary plane\n");
 		return PTR_ERR(primary_plane);
diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h
index 080deae55f64..5125ca1a8158 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.h
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
@@ -952,7 +952,8 @@ int vc4_kms_load(struct drm_device *dev);
 
 /* vc4_plane.c */
 struct drm_plane *vc4_plane_init(struct drm_device *dev,
-				 enum drm_plane_type type);
+				 enum drm_plane_type type,
+				 unsigned int possible_crtcs);
 int vc4_plane_create_additional_planes(struct drm_device *dev);
 u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist);
 u32 vc4_plane_dlist_size(const struct drm_plane_state *state);
diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c
index b3438f4a81ce..17dab470ecdf 100644
--- a/drivers/gpu/drm/vc4/vc4_plane.c
+++ b/drivers/gpu/drm/vc4/vc4_plane.c
@@ -1451,7 +1451,8 @@ static const struct drm_plane_funcs vc4_plane_funcs = {
 };
 
 struct drm_plane *vc4_plane_init(struct drm_device *dev,
-				 enum drm_plane_type type)
+				 enum drm_plane_type type,
+				 unsigned int possible_crtcs)
 {
 	struct drm_plane *plane = NULL;
 	struct vc4_plane *vc4_plane;
@@ -1483,7 +1484,7 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev,
 	}
 
 	plane = &vc4_plane->base;
-	ret = drm_universal_plane_init(dev, plane, 0,
+	ret = drm_universal_plane_init(dev, plane, possible_crtcs,
 				       &vc4_plane_funcs,
 				       formats, num_formats,
 				       modifiers, type, NULL);
@@ -1528,13 +1529,11 @@ int vc4_plane_create_additional_planes(struct drm_device *drm)
 	 */
 	for (i = 0; i < 16; i++) {
 		struct drm_plane *plane =
-			vc4_plane_init(drm, DRM_PLANE_TYPE_OVERLAY);
+			vc4_plane_init(drm, DRM_PLANE_TYPE_OVERLAY,
+				       GENMASK(drm->mode_config.num_crtc - 1, 0));
 
 		if (IS_ERR(plane))
 			continue;
-
-		plane->possible_crtcs =
-			GENMASK(drm->mode_config.num_crtc - 1, 0);
 	}
 
 	drm_for_each_crtc(crtc, drm) {
@@ -1542,9 +1541,9 @@ int vc4_plane_create_additional_planes(struct drm_device *drm)
 		 * since we overlay planes on the CRTC in the order they were
 		 * initialized.
 		 */
-		cursor_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_CURSOR);
+		cursor_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_CURSOR,
+					      drm_crtc_mask(crtc));
 		if (!IS_ERR(cursor_plane)) {
-			cursor_plane->possible_crtcs = drm_crtc_mask(crtc);
 			crtc->cursor = cursor_plane;
 		}
 	}
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 16/64] drm/vc4: plane: Switch to drmm_universal_plane_alloc()
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (14 preceding siblings ...)
  2022-06-10  9:28 ` [PATCH 15/64] drm/vc4: plane: Take possible_crtcs as an argument Maxime Ripard
@ 2022-06-10  9:28 ` Maxime Ripard
  2022-06-14 15:42   ` Dave Stevenson
  2022-06-10  9:28 ` [PATCH 17/64] drm/vc4: crtc: Move debugfs_name to crtc_data Maxime Ripard
                   ` (47 subsequent siblings)
  63 siblings, 1 reply; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:28 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

Let's switch to drmm_universal_plane_alloc() for our plane allocation and
initialisation to make the driver a bit simpler.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_crtc.c  | 12 +-----------
 drivers/gpu/drm/vc4/vc4_plane.c | 23 ++++++++---------------
 2 files changed, 9 insertions(+), 26 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c
index 840a93484bb1..7163f924b48b 100644
--- a/drivers/gpu/drm/vc4/vc4_crtc.c
+++ b/drivers/gpu/drm/vc4/vc4_crtc.c
@@ -1176,7 +1176,6 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data)
 	const struct vc4_pv_data *pv_data;
 	struct vc4_crtc *vc4_crtc;
 	struct drm_crtc *crtc;
-	struct drm_plane *destroy_plane, *temp;
 	int ret;
 
 	vc4_crtc = devm_kzalloc(dev, sizeof(*vc4_crtc), GFP_KERNEL);
@@ -1211,7 +1210,7 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data)
 			       IRQF_SHARED,
 			       "vc4 crtc", vc4_crtc);
 	if (ret)
-		goto err_destroy_planes;
+		return ret;
 
 	platform_set_drvdata(pdev, vc4_crtc);
 
@@ -1219,15 +1218,6 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data)
 				 &vc4_crtc->regset);
 
 	return 0;
-
-err_destroy_planes:
-	list_for_each_entry_safe(destroy_plane, temp,
-				 &drm->mode_config.plane_list, head) {
-		if (destroy_plane->possible_crtcs == drm_crtc_mask(crtc))
-		    destroy_plane->funcs->destroy(destroy_plane);
-	}
-
-	return ret;
 }
 
 static void vc4_crtc_unbind(struct device *dev, struct device *master,
diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c
index 17dab470ecdf..673c963f5c5a 100644
--- a/drivers/gpu/drm/vc4/vc4_plane.c
+++ b/drivers/gpu/drm/vc4/vc4_plane.c
@@ -1442,8 +1442,6 @@ static bool vc4_format_mod_supported(struct drm_plane *plane,
 static const struct drm_plane_funcs vc4_plane_funcs = {
 	.update_plane = drm_atomic_helper_update_plane,
 	.disable_plane = drm_atomic_helper_disable_plane,
-	.destroy = drm_plane_cleanup,
-	.set_property = NULL,
 	.reset = vc4_plane_reset,
 	.atomic_duplicate_state = vc4_plane_duplicate_state,
 	.atomic_destroy_state = vc4_plane_destroy_state,
@@ -1454,11 +1452,10 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev,
 				 enum drm_plane_type type,
 				 unsigned int possible_crtcs)
 {
-	struct drm_plane *plane = NULL;
+	struct drm_plane *plane;
 	struct vc4_plane *vc4_plane;
 	u32 formats[ARRAY_SIZE(hvs_formats)];
 	int num_formats = 0;
-	int ret = 0;
 	unsigned i;
 	bool hvs5 = of_device_is_compatible(dev->dev->of_node,
 					    "brcm,bcm2711-vc5");
@@ -1471,11 +1468,6 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev,
 		DRM_FORMAT_MOD_INVALID
 	};
 
-	vc4_plane = devm_kzalloc(dev->dev, sizeof(*vc4_plane),
-				 GFP_KERNEL);
-	if (!vc4_plane)
-		return ERR_PTR(-ENOMEM);
-
 	for (i = 0; i < ARRAY_SIZE(hvs_formats); i++) {
 		if (!hvs_formats[i].hvs5_only || hvs5) {
 			formats[num_formats] = hvs_formats[i].drm;
@@ -1483,13 +1475,14 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev,
 		}
 	}
 
+	vc4_plane = drmm_universal_plane_alloc(dev, struct vc4_plane, base,
+					       possible_crtcs,
+					       &vc4_plane_funcs,
+					       formats, num_formats,
+					       modifiers, type, NULL);
+	if (IS_ERR(vc4_plane))
+		return ERR_CAST(vc4_plane);
 	plane = &vc4_plane->base;
-	ret = drm_universal_plane_init(dev, plane, possible_crtcs,
-				       &vc4_plane_funcs,
-				       formats, num_formats,
-				       modifiers, type, NULL);
-	if (ret)
-		return ERR_PTR(ret);
 
 	drm_plane_helper_add(plane, &vc4_plane_helper_funcs);
 
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 17/64] drm/vc4: crtc: Move debugfs_name to crtc_data
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (15 preceding siblings ...)
  2022-06-10  9:28 ` [PATCH 16/64] drm/vc4: plane: Switch to drmm_universal_plane_alloc() Maxime Ripard
@ 2022-06-10  9:28 ` Maxime Ripard
  2022-06-14 15:57   ` Dave Stevenson
  2022-06-10  9:28 ` [PATCH 18/64] drm/vc4: crtc: Switch to drmm_kzalloc Maxime Ripard
                   ` (46 subsequent siblings)
  63 siblings, 1 reply; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:28 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

All the CRTCs, including the TXP, have a debugfs file and name so we can
consolidate it into vc4_crtc_data.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_crtc.c | 18 +++++++++---------
 drivers/gpu/drm/vc4/vc4_drv.h  |  4 ++--
 drivers/gpu/drm/vc4/vc4_txp.c  |  1 +
 3 files changed, 12 insertions(+), 11 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c
index 7163f924b48b..1f7e987e68aa 100644
--- a/drivers/gpu/drm/vc4/vc4_crtc.c
+++ b/drivers/gpu/drm/vc4/vc4_crtc.c
@@ -978,10 +978,10 @@ static const struct drm_crtc_helper_funcs vc4_crtc_helper_funcs = {
 
 static const struct vc4_pv_data bcm2835_pv0_data = {
 	.base = {
+		.debugfs_name = "crtc0_regs",
 		.hvs_available_channels = BIT(0),
 		.hvs_output = 0,
 	},
-	.debugfs_name = "crtc0_regs",
 	.fifo_depth = 64,
 	.pixels_per_clock = 1,
 	.encoder_types = {
@@ -992,10 +992,10 @@ static const struct vc4_pv_data bcm2835_pv0_data = {
 
 static const struct vc4_pv_data bcm2835_pv1_data = {
 	.base = {
+		.debugfs_name = "crtc1_regs",
 		.hvs_available_channels = BIT(2),
 		.hvs_output = 2,
 	},
-	.debugfs_name = "crtc1_regs",
 	.fifo_depth = 64,
 	.pixels_per_clock = 1,
 	.encoder_types = {
@@ -1006,10 +1006,10 @@ static const struct vc4_pv_data bcm2835_pv1_data = {
 
 static const struct vc4_pv_data bcm2835_pv2_data = {
 	.base = {
+		.debugfs_name = "crtc2_regs",
 		.hvs_available_channels = BIT(1),
 		.hvs_output = 1,
 	},
-	.debugfs_name = "crtc2_regs",
 	.fifo_depth = 64,
 	.pixels_per_clock = 1,
 	.encoder_types = {
@@ -1020,10 +1020,10 @@ static const struct vc4_pv_data bcm2835_pv2_data = {
 
 static const struct vc4_pv_data bcm2711_pv0_data = {
 	.base = {
+		.debugfs_name = "crtc0_regs",
 		.hvs_available_channels = BIT(0),
 		.hvs_output = 0,
 	},
-	.debugfs_name = "crtc0_regs",
 	.fifo_depth = 64,
 	.pixels_per_clock = 1,
 	.encoder_types = {
@@ -1034,10 +1034,10 @@ static const struct vc4_pv_data bcm2711_pv0_data = {
 
 static const struct vc4_pv_data bcm2711_pv1_data = {
 	.base = {
+		.debugfs_name = "crtc1_regs",
 		.hvs_available_channels = BIT(0) | BIT(1) | BIT(2),
 		.hvs_output = 3,
 	},
-	.debugfs_name = "crtc1_regs",
 	.fifo_depth = 64,
 	.pixels_per_clock = 1,
 	.encoder_types = {
@@ -1048,10 +1048,10 @@ static const struct vc4_pv_data bcm2711_pv1_data = {
 
 static const struct vc4_pv_data bcm2711_pv2_data = {
 	.base = {
+		.debugfs_name = "crtc2_regs",
 		.hvs_available_channels = BIT(0) | BIT(1) | BIT(2),
 		.hvs_output = 4,
 	},
-	.debugfs_name = "crtc2_regs",
 	.fifo_depth = 256,
 	.pixels_per_clock = 2,
 	.encoder_types = {
@@ -1061,10 +1061,10 @@ static const struct vc4_pv_data bcm2711_pv2_data = {
 
 static const struct vc4_pv_data bcm2711_pv3_data = {
 	.base = {
+		.debugfs_name = "crtc3_regs",
 		.hvs_available_channels = BIT(1),
 		.hvs_output = 1,
 	},
-	.debugfs_name = "crtc3_regs",
 	.fifo_depth = 64,
 	.pixels_per_clock = 1,
 	.encoder_types = {
@@ -1074,10 +1074,10 @@ static const struct vc4_pv_data bcm2711_pv3_data = {
 
 static const struct vc4_pv_data bcm2711_pv4_data = {
 	.base = {
+		.debugfs_name = "crtc4_regs",
 		.hvs_available_channels = BIT(0) | BIT(1) | BIT(2),
 		.hvs_output = 5,
 	},
-	.debugfs_name = "crtc4_regs",
 	.fifo_depth = 64,
 	.pixels_per_clock = 2,
 	.encoder_types = {
@@ -1214,7 +1214,7 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data)
 
 	platform_set_drvdata(pdev, vc4_crtc);
 
-	vc4_debugfs_add_regset32(drm, pv_data->debugfs_name,
+	vc4_debugfs_add_regset32(drm, pv_data->base.debugfs_name,
 				 &vc4_crtc->regset);
 
 	return 0;
diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h
index 5125ca1a8158..9a53ace85d95 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.h
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
@@ -457,6 +457,8 @@ to_vc4_encoder(struct drm_encoder *encoder)
 }
 
 struct vc4_crtc_data {
+	const char *debugfs_name;
+
 	/* Bitmask of channels (FIFOs) of the HVS that the output can source from */
 	unsigned int hvs_available_channels;
 
@@ -474,8 +476,6 @@ struct vc4_pv_data {
 	u8 pixels_per_clock;
 
 	enum vc4_encoder_type encoder_types[4];
-	const char *debugfs_name;
-
 };
 
 struct vc4_crtc {
diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c
index 82beb8c159f2..e983ff7c5e13 100644
--- a/drivers/gpu/drm/vc4/vc4_txp.c
+++ b/drivers/gpu/drm/vc4/vc4_txp.c
@@ -460,6 +460,7 @@ static irqreturn_t vc4_txp_interrupt(int irq, void *data)
 }
 
 static const struct vc4_crtc_data vc4_txp_crtc_data = {
+	.debugfs_name = "txp_regs",
 	.hvs_available_channels = BIT(2),
 	.hvs_output = 2,
 };
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 18/64] drm/vc4: crtc: Switch to drmm_kzalloc
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (16 preceding siblings ...)
  2022-06-10  9:28 ` [PATCH 17/64] drm/vc4: crtc: Move debugfs_name to crtc_data Maxime Ripard
@ 2022-06-10  9:28 ` Maxime Ripard
  2022-06-14 15:43   ` Dave Stevenson
  2022-06-10  9:28 ` [PATCH 19/64] drm/vc4: crtc: Switch to DRM-managed CRTC initialization Maxime Ripard
                   ` (45 subsequent siblings)
  63 siblings, 1 reply; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:28 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

Our internal structure that stores the DRM entities structure is allocated
through a device-managed kzalloc.

This means that this will eventually be freed whenever the device is
removed. In our case, the most like source of removal is that the main
device is going to be unbound, and component_unbind_all() is being run.

However, it occurs while the DRM device is still registered, which will
create dangling pointers, eventually resulting in use-after-free.

Switch to a DRM-managed allocation to keep our structure until the DRM
driver doesn't need it anymore.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_crtc.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c
index 1f7e987e68aa..c74fa3d07561 100644
--- a/drivers/gpu/drm/vc4/vc4_crtc.c
+++ b/drivers/gpu/drm/vc4/vc4_crtc.c
@@ -1178,7 +1178,7 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data)
 	struct drm_crtc *crtc;
 	int ret;
 
-	vc4_crtc = devm_kzalloc(dev, sizeof(*vc4_crtc), GFP_KERNEL);
+	vc4_crtc = drmm_kzalloc(drm, sizeof(*vc4_crtc), GFP_KERNEL);
 	if (!vc4_crtc)
 		return -ENOMEM;
 	crtc = &vc4_crtc->base;
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 19/64] drm/vc4: crtc: Switch to DRM-managed CRTC initialization
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (17 preceding siblings ...)
  2022-06-10  9:28 ` [PATCH 18/64] drm/vc4: crtc: Switch to drmm_kzalloc Maxime Ripard
@ 2022-06-10  9:28 ` Maxime Ripard
  2022-06-14 16:13   ` Dave Stevenson
  2022-06-10  9:28 ` [PATCH 20/64] drm/vc4: dpi: Remove vc4_dev dpi pointer Maxime Ripard
                   ` (44 subsequent siblings)
  63 siblings, 1 reply; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:28 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

The current code will call drm_crtc_cleanup() when the device is
unbound. However, by then, there might still be some references held to
that CRTC, including by the userspace that might still have the DRM
device open.

Let's switch to a DRM-managed initialization to clean up after ourselves
only once the DRM device has been last closed.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_crtc.c | 18 +++++++-----------
 drivers/gpu/drm/vc4/vc4_drv.h  |  1 -
 drivers/gpu/drm/vc4/vc4_txp.c  |  1 -
 3 files changed, 7 insertions(+), 13 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c
index c74fa3d07561..24de4706b61a 100644
--- a/drivers/gpu/drm/vc4/vc4_crtc.c
+++ b/drivers/gpu/drm/vc4/vc4_crtc.c
@@ -205,11 +205,6 @@ static bool vc4_crtc_get_scanout_position(struct drm_crtc *crtc,
 	return ret;
 }
 
-void vc4_crtc_destroy(struct drm_crtc *crtc)
-{
-	drm_crtc_cleanup(crtc);
-}
-
 static u32 vc4_get_fifo_full_level(struct vc4_crtc *vc4_crtc, u32 format)
 {
 	const struct vc4_crtc_data *crtc_data = vc4_crtc_to_vc4_crtc_data(vc4_crtc);
@@ -953,7 +948,6 @@ void vc4_crtc_reset(struct drm_crtc *crtc)
 
 static const struct drm_crtc_funcs vc4_crtc_funcs = {
 	.set_config = drm_atomic_helper_set_config,
-	.destroy = vc4_crtc_destroy,
 	.page_flip = vc4_page_flip,
 	.set_property = NULL,
 	.cursor_set = NULL, /* handled by drm_mode_cursor_universal */
@@ -1131,6 +1125,7 @@ int vc4_crtc_init(struct drm_device *drm, struct vc4_crtc *vc4_crtc,
 	struct drm_crtc *crtc = &vc4_crtc->base;
 	struct drm_plane *primary_plane;
 	unsigned int i;
+	int ret;
 
 	/* For now, we create just the primary and the legacy cursor
 	 * planes.  We should be able to stack more planes on easily,
@@ -1144,10 +1139,13 @@ int vc4_crtc_init(struct drm_device *drm, struct vc4_crtc *vc4_crtc,
 		return PTR_ERR(primary_plane);
 	}
 
-	spin_lock_init(&vc4_crtc->irq_lock);
-	drm_crtc_init_with_planes(drm, crtc, primary_plane, NULL,
-				  crtc_funcs, NULL);
+	ret = drmm_crtc_init_with_planes(drm, crtc, primary_plane, NULL,
+					 crtc_funcs, NULL);
+	if (ret)
+		return ret;
+
 	drm_crtc_helper_add(crtc, crtc_helper_funcs);
+	spin_lock_init(&vc4_crtc->irq_lock);
 
 	if (!vc4->hvs->hvs5) {
 		drm_mode_crtc_set_gamma_size(crtc, ARRAY_SIZE(vc4_crtc->lut_r));
@@ -1226,8 +1224,6 @@ static void vc4_crtc_unbind(struct device *dev, struct device *master,
 	struct platform_device *pdev = to_platform_device(dev);
 	struct vc4_crtc *vc4_crtc = dev_get_drvdata(dev);
 
-	vc4_crtc_destroy(&vc4_crtc->base);
-
 	CRTC_WRITE(PV_INTEN, 0);
 
 	platform_set_drvdata(pdev, NULL);
diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h
index 9a53ace85d95..fff3772be2d4 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.h
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
@@ -845,7 +845,6 @@ int vc4_crtc_disable_at_boot(struct drm_crtc *crtc);
 int vc4_crtc_init(struct drm_device *drm, struct vc4_crtc *vc4_crtc,
 		  const struct drm_crtc_funcs *crtc_funcs,
 		  const struct drm_crtc_helper_funcs *crtc_helper_funcs);
-void vc4_crtc_destroy(struct drm_crtc *crtc);
 int vc4_page_flip(struct drm_crtc *crtc,
 		  struct drm_framebuffer *fb,
 		  struct drm_pending_vblank_event *event,
diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c
index e983ff7c5e13..f306e05ac5b2 100644
--- a/drivers/gpu/drm/vc4/vc4_txp.c
+++ b/drivers/gpu/drm/vc4/vc4_txp.c
@@ -383,7 +383,6 @@ static void vc4_txp_disable_vblank(struct drm_crtc *crtc) {}
 
 static const struct drm_crtc_funcs vc4_txp_crtc_funcs = {
 	.set_config		= drm_atomic_helper_set_config,
-	.destroy		= vc4_crtc_destroy,
 	.page_flip		= vc4_page_flip,
 	.reset			= vc4_crtc_reset,
 	.atomic_duplicate_state	= vc4_crtc_duplicate_state,
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 20/64] drm/vc4: dpi: Remove vc4_dev dpi pointer
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (18 preceding siblings ...)
  2022-06-10  9:28 ` [PATCH 19/64] drm/vc4: crtc: Switch to DRM-managed CRTC initialization Maxime Ripard
@ 2022-06-10  9:28 ` Maxime Ripard
  2022-06-14 16:00   ` Dave Stevenson
  2022-06-10  9:28 ` [PATCH 21/64] drm/vc4: dpi: Embed DRM structures into the private structure Maxime Ripard
                   ` (43 subsequent siblings)
  63 siblings, 1 reply; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:28 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

There's no user for that pointer so let's just get rid of it.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_dpi.c | 7 -------
 drivers/gpu/drm/vc4/vc4_drv.h | 1 -
 2 files changed, 8 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c
index c180eb60bee8..f2b46c524919 100644
--- a/drivers/gpu/drm/vc4/vc4_dpi.c
+++ b/drivers/gpu/drm/vc4/vc4_dpi.c
@@ -249,7 +249,6 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data)
 {
 	struct platform_device *pdev = to_platform_device(dev);
 	struct drm_device *drm = dev_get_drvdata(master);
-	struct vc4_dev *vc4 = to_vc4_dev(drm);
 	struct vc4_dpi *dpi;
 	struct vc4_dpi_encoder *vc4_dpi_encoder;
 	int ret;
@@ -308,8 +307,6 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data)
 
 	dev_set_drvdata(dev, dpi);
 
-	vc4->dpi = dpi;
-
 	vc4_debugfs_add_regset32(drm, "dpi_regs", &dpi->regset);
 
 	return 0;
@@ -323,8 +320,6 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data)
 static void vc4_dpi_unbind(struct device *dev, struct device *master,
 			   void *data)
 {
-	struct drm_device *drm = dev_get_drvdata(master);
-	struct vc4_dev *vc4 = to_vc4_dev(drm);
 	struct vc4_dpi *dpi = dev_get_drvdata(dev);
 
 	drm_of_panel_bridge_remove(dev->of_node, 0, 0);
@@ -332,8 +327,6 @@ static void vc4_dpi_unbind(struct device *dev, struct device *master,
 	drm_encoder_cleanup(dpi->encoder);
 
 	clk_disable_unprepare(dpi->core_clock);
-
-	vc4->dpi = NULL;
 }
 
 static const struct component_ops vc4_dpi_ops = {
diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h
index fff3772be2d4..846f3cda179a 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.h
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
@@ -79,7 +79,6 @@ struct vc4_dev {
 
 	struct vc4_hvs *hvs;
 	struct vc4_v3d *v3d;
-	struct vc4_dpi *dpi;
 	struct vc4_vec *vec;
 	struct vc4_txp *txp;
 
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 21/64] drm/vc4: dpi: Embed DRM structures into the private structure
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (19 preceding siblings ...)
  2022-06-10  9:28 ` [PATCH 20/64] drm/vc4: dpi: Remove vc4_dev dpi pointer Maxime Ripard
@ 2022-06-10  9:28 ` Maxime Ripard
  2022-06-14 16:07   ` Dave Stevenson
  2022-06-10  9:28 ` [PATCH 22/64] drm/vc4: dpi: Switch to drmm_kzalloc Maxime Ripard
                   ` (42 subsequent siblings)
  63 siblings, 1 reply; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:28 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

The VC4 DPI driver private structure contains only a pointer to the
encoder it implements. This makes the overall structure somewhat
inconsistent with the rest of the driver, and complicates its
initialisation without any apparent gain.

Let's embed the drm_encoder structure (through the vc4_encoder one) into
struct vc4_dpi to fix both issues.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_dpi.c | 49 ++++++++++++-----------------------
 1 file changed, 16 insertions(+), 33 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c
index f2b46c524919..c88e8e397730 100644
--- a/drivers/gpu/drm/vc4/vc4_dpi.c
+++ b/drivers/gpu/drm/vc4/vc4_dpi.c
@@ -83,10 +83,10 @@
 
 /* General DPI hardware state. */
 struct vc4_dpi {
+	struct vc4_encoder encoder;
+
 	struct platform_device *pdev;
 
-	struct drm_encoder *encoder;
-
 	void __iomem *regs;
 
 	struct clk *pixel_clock;
@@ -95,21 +95,15 @@ struct vc4_dpi {
 	struct debugfs_regset32 regset;
 };
 
+static inline struct vc4_dpi *
+to_vc4_dpi(struct drm_encoder *encoder)
+{
+	return container_of(encoder, struct vc4_dpi, encoder.base);
+}
+
 #define DPI_READ(offset) readl(dpi->regs + (offset))
 #define DPI_WRITE(offset, val) writel(val, dpi->regs + (offset))
 
-/* VC4 DPI encoder KMS struct */
-struct vc4_dpi_encoder {
-	struct vc4_encoder base;
-	struct vc4_dpi *dpi;
-};
-
-static inline struct vc4_dpi_encoder *
-to_vc4_dpi_encoder(struct drm_encoder *encoder)
-{
-	return container_of(encoder, struct vc4_dpi_encoder, base.base);
-}
-
 static const struct debugfs_reg32 dpi_regs[] = {
 	VC4_REG32(DPI_C),
 	VC4_REG32(DPI_ID),
@@ -117,8 +111,7 @@ static const struct debugfs_reg32 dpi_regs[] = {
 
 static void vc4_dpi_encoder_disable(struct drm_encoder *encoder)
 {
-	struct vc4_dpi_encoder *vc4_encoder = to_vc4_dpi_encoder(encoder);
-	struct vc4_dpi *dpi = vc4_encoder->dpi;
+	struct vc4_dpi *dpi = to_vc4_dpi(encoder);
 
 	clk_disable_unprepare(dpi->pixel_clock);
 }
@@ -127,8 +120,7 @@ static void vc4_dpi_encoder_enable(struct drm_encoder *encoder)
 {
 	struct drm_device *dev = encoder->dev;
 	struct drm_display_mode *mode = &encoder->crtc->mode;
-	struct vc4_dpi_encoder *vc4_encoder = to_vc4_dpi_encoder(encoder);
-	struct vc4_dpi *dpi = vc4_encoder->dpi;
+	struct vc4_dpi *dpi = to_vc4_dpi(encoder);
 	struct drm_connector_list_iter conn_iter;
 	struct drm_connector *connector = NULL, *connector_scan;
 	u32 dpi_c = DPI_ENABLE | DPI_OUTPUT_ENABLE_MODE;
@@ -242,7 +234,7 @@ static int vc4_dpi_init_bridge(struct vc4_dpi *dpi)
 			return PTR_ERR(bridge);
 	}
 
-	return drm_bridge_attach(dpi->encoder, bridge, NULL, 0);
+	return drm_bridge_attach(&dpi->encoder.base, bridge, NULL, 0);
 }
 
 static int vc4_dpi_bind(struct device *dev, struct device *master, void *data)
@@ -250,21 +242,12 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data)
 	struct platform_device *pdev = to_platform_device(dev);
 	struct drm_device *drm = dev_get_drvdata(master);
 	struct vc4_dpi *dpi;
-	struct vc4_dpi_encoder *vc4_dpi_encoder;
 	int ret;
 
 	dpi = devm_kzalloc(dev, sizeof(*dpi), GFP_KERNEL);
 	if (!dpi)
 		return -ENOMEM;
-
-	vc4_dpi_encoder = devm_kzalloc(dev, sizeof(*vc4_dpi_encoder),
-				       GFP_KERNEL);
-	if (!vc4_dpi_encoder)
-		return -ENOMEM;
-	vc4_dpi_encoder->base.type = VC4_ENCODER_TYPE_DPI;
-	vc4_dpi_encoder->dpi = dpi;
-	dpi->encoder = &vc4_dpi_encoder->base.base;
-
+	dpi->encoder.type = VC4_ENCODER_TYPE_DPI;
 	dpi->pdev = pdev;
 	dpi->regs = vc4_ioremap_regs(pdev, 0);
 	if (IS_ERR(dpi->regs))
@@ -298,8 +281,8 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data)
 	if (ret)
 		DRM_ERROR("Failed to turn on core clock: %d\n", ret);
 
-	drm_simple_encoder_init(drm, dpi->encoder, DRM_MODE_ENCODER_DPI);
-	drm_encoder_helper_add(dpi->encoder, &vc4_dpi_encoder_helper_funcs);
+	drm_simple_encoder_init(drm, &dpi->encoder.base, DRM_MODE_ENCODER_DPI);
+	drm_encoder_helper_add(&dpi->encoder.base, &vc4_dpi_encoder_helper_funcs);
 
 	ret = vc4_dpi_init_bridge(dpi);
 	if (ret)
@@ -312,7 +295,7 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data)
 	return 0;
 
 err_destroy_encoder:
-	drm_encoder_cleanup(dpi->encoder);
+	drm_encoder_cleanup(&dpi->encoder.base);
 	clk_disable_unprepare(dpi->core_clock);
 	return ret;
 }
@@ -324,7 +307,7 @@ static void vc4_dpi_unbind(struct device *dev, struct device *master,
 
 	drm_of_panel_bridge_remove(dev->of_node, 0, 0);
 
-	drm_encoder_cleanup(dpi->encoder);
+	drm_encoder_cleanup(&dpi->encoder.base);
 
 	clk_disable_unprepare(dpi->core_clock);
 }
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 22/64] drm/vc4: dpi: Switch to drmm_kzalloc
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (20 preceding siblings ...)
  2022-06-10  9:28 ` [PATCH 21/64] drm/vc4: dpi: Embed DRM structures into the private structure Maxime Ripard
@ 2022-06-10  9:28 ` Maxime Ripard
  2022-06-14 16:11   ` Dave Stevenson
  2022-06-10  9:28 ` [PATCH 23/64] drm/vc4: dpi: Return an error if we can't enable our clock Maxime Ripard
                   ` (41 subsequent siblings)
  63 siblings, 1 reply; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:28 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

Our internal structure that stores the DRM entities structure is allocated
through a device-managed kzalloc.

This means that this will eventually be freed whenever the device is
removed. In our case, the most like source of removal is that the main
device is going to be unbound, and component_unbind_all() is being run.

However, it occurs while the DRM device is still registered, which will
create dangling pointers, eventually resulting in use-after-free.

Switch to a DRM-managed allocation to keep our structure until the DRM
driver doesn't need it anymore.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_dpi.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c
index c88e8e397730..d1eaafb43bd1 100644
--- a/drivers/gpu/drm/vc4/vc4_dpi.c
+++ b/drivers/gpu/drm/vc4/vc4_dpi.c
@@ -244,9 +244,10 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data)
 	struct vc4_dpi *dpi;
 	int ret;
 
-	dpi = devm_kzalloc(dev, sizeof(*dpi), GFP_KERNEL);
+	dpi = drmm_kzalloc(drm, sizeof(*dpi), GFP_KERNEL);
 	if (!dpi)
 		return -ENOMEM;
+
 	dpi->encoder.type = VC4_ENCODER_TYPE_DPI;
 	dpi->pdev = pdev;
 	dpi->regs = vc4_ioremap_regs(pdev, 0);
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 23/64] drm/vc4: dpi: Return an error if we can't enable our clock
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (21 preceding siblings ...)
  2022-06-10  9:28 ` [PATCH 22/64] drm/vc4: dpi: Switch to drmm_kzalloc Maxime Ripard
@ 2022-06-10  9:28 ` Maxime Ripard
  2022-06-14 16:14   ` Dave Stevenson
  2022-06-10  9:28 ` [PATCH 24/64] drm/vc4: dpi: Remove unnecessary drm_of_panel_bridge_remove call Maxime Ripard
                   ` (40 subsequent siblings)
  63 siblings, 1 reply; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:28 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

If we fail to enable the DPI clock, we just ignore the error and moves
forward. Let's return an error instead.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_dpi.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c
index d1eaafb43bd1..658e0aa9e2e1 100644
--- a/drivers/gpu/drm/vc4/vc4_dpi.c
+++ b/drivers/gpu/drm/vc4/vc4_dpi.c
@@ -270,6 +270,7 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data)
 			DRM_ERROR("Failed to get core clock: %d\n", ret);
 		return ret;
 	}
+
 	dpi->pixel_clock = devm_clk_get(dev, "pixel");
 	if (IS_ERR(dpi->pixel_clock)) {
 		ret = PTR_ERR(dpi->pixel_clock);
@@ -279,8 +280,10 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data)
 	}
 
 	ret = clk_prepare_enable(dpi->core_clock);
-	if (ret)
+	if (ret) {
 		DRM_ERROR("Failed to turn on core clock: %d\n", ret);
+		return ret;
+	}
 
 	drm_simple_encoder_init(drm, &dpi->encoder.base, DRM_MODE_ENCODER_DPI);
 	drm_encoder_helper_add(&dpi->encoder.base, &vc4_dpi_encoder_helper_funcs);
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 24/64] drm/vc4: dpi: Remove unnecessary drm_of_panel_bridge_remove call
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (22 preceding siblings ...)
  2022-06-10  9:28 ` [PATCH 23/64] drm/vc4: dpi: Return an error if we can't enable our clock Maxime Ripard
@ 2022-06-10  9:28 ` Maxime Ripard
  2022-06-14 16:37   ` Dave Stevenson
  2022-06-10  9:28 ` [PATCH 25/64] drm/vc4: dpi: Add action to disable the clock Maxime Ripard
                   ` (39 subsequent siblings)
  63 siblings, 1 reply; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:28 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

Since we have a managed call to create our panel_bridge instance, the call
to drm_of_panel_bridge_remove() at unbind is both redundant and dangerous
since it might lead to a use-after-free.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_dpi.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c
index 658e0aa9e2e1..5a6cdea7bf7b 100644
--- a/drivers/gpu/drm/vc4/vc4_dpi.c
+++ b/drivers/gpu/drm/vc4/vc4_dpi.c
@@ -309,8 +309,6 @@ static void vc4_dpi_unbind(struct device *dev, struct device *master,
 {
 	struct vc4_dpi *dpi = dev_get_drvdata(dev);
 
-	drm_of_panel_bridge_remove(dev->of_node, 0, 0);
-
 	drm_encoder_cleanup(&dpi->encoder.base);
 
 	clk_disable_unprepare(dpi->core_clock);
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 25/64] drm/vc4: dpi: Add action to disable the clock
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (23 preceding siblings ...)
  2022-06-10  9:28 ` [PATCH 24/64] drm/vc4: dpi: Remove unnecessary drm_of_panel_bridge_remove call Maxime Ripard
@ 2022-06-10  9:28 ` Maxime Ripard
  2022-06-14 16:47   ` Dave Stevenson
  2022-06-10  9:28 ` [PATCH 26/64] drm/vc4: dpi: Switch to DRM-managed encoder initialization Maxime Ripard
                   ` (38 subsequent siblings)
  63 siblings, 1 reply; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:28 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

Adding a device-managed action will make the error path easier, so let's
create one to disable our clock.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_dpi.c | 14 +++++++++++---
 1 file changed, 11 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c
index 5a6cdea7bf7b..4e24dbad77f2 100644
--- a/drivers/gpu/drm/vc4/vc4_dpi.c
+++ b/drivers/gpu/drm/vc4/vc4_dpi.c
@@ -237,6 +237,13 @@ static int vc4_dpi_init_bridge(struct vc4_dpi *dpi)
 	return drm_bridge_attach(&dpi->encoder.base, bridge, NULL, 0);
 }
 
+static void vc4_dpi_disable_clock(void *ptr)
+{
+	struct vc4_dpi *dpi = ptr;
+
+	clk_disable_unprepare(dpi->core_clock);
+}
+
 static int vc4_dpi_bind(struct device *dev, struct device *master, void *data)
 {
 	struct platform_device *pdev = to_platform_device(dev);
@@ -285,6 +292,10 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data)
 		return ret;
 	}
 
+	ret = devm_add_action_or_reset(dev, vc4_dpi_disable_clock, dpi);
+	if (ret)
+		return ret;
+
 	drm_simple_encoder_init(drm, &dpi->encoder.base, DRM_MODE_ENCODER_DPI);
 	drm_encoder_helper_add(&dpi->encoder.base, &vc4_dpi_encoder_helper_funcs);
 
@@ -300,7 +311,6 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data)
 
 err_destroy_encoder:
 	drm_encoder_cleanup(&dpi->encoder.base);
-	clk_disable_unprepare(dpi->core_clock);
 	return ret;
 }
 
@@ -310,8 +320,6 @@ static void vc4_dpi_unbind(struct device *dev, struct device *master,
 	struct vc4_dpi *dpi = dev_get_drvdata(dev);
 
 	drm_encoder_cleanup(&dpi->encoder.base);
-
-	clk_disable_unprepare(dpi->core_clock);
 }
 
 static const struct component_ops vc4_dpi_ops = {
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 26/64] drm/vc4: dpi: Switch to DRM-managed encoder initialization
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (24 preceding siblings ...)
  2022-06-10  9:28 ` [PATCH 25/64] drm/vc4: dpi: Add action to disable the clock Maxime Ripard
@ 2022-06-10  9:28 ` Maxime Ripard
  2022-06-14 16:51   ` Dave Stevenson
  2022-06-10  9:28 ` [PATCH 27/64] drm/vc4: dpi: Switch to drmm_of_get_bridge Maxime Ripard
                   ` (37 subsequent siblings)
  63 siblings, 1 reply; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:28 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

The current code will call drm_encoder_cleanup() when the device is
unbound. However, by then, there might still be some references held to
that encoder, including by the userspace that might still have the DRM
device open.

Let's switch to a DRM-managed initialization to clean up after ourselves
only once the DRM device has been last closed.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_dpi.c | 20 +++++---------------
 1 file changed, 5 insertions(+), 15 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c
index 4e24dbad77f2..8a50de2c40d9 100644
--- a/drivers/gpu/drm/vc4/vc4_dpi.c
+++ b/drivers/gpu/drm/vc4/vc4_dpi.c
@@ -296,35 +296,25 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data)
 	if (ret)
 		return ret;
 
-	drm_simple_encoder_init(drm, &dpi->encoder.base, DRM_MODE_ENCODER_DPI);
+	ret = drmm_simple_encoder_init(drm, &dpi->encoder.base, DRM_MODE_ENCODER_DPI);
+	if (ret)
+		return ret;
+
 	drm_encoder_helper_add(&dpi->encoder.base, &vc4_dpi_encoder_helper_funcs);
 
 	ret = vc4_dpi_init_bridge(dpi);
 	if (ret)
-		goto err_destroy_encoder;
+		return ret;
 
 	dev_set_drvdata(dev, dpi);
 
 	vc4_debugfs_add_regset32(drm, "dpi_regs", &dpi->regset);
 
 	return 0;
-
-err_destroy_encoder:
-	drm_encoder_cleanup(&dpi->encoder.base);
-	return ret;
-}
-
-static void vc4_dpi_unbind(struct device *dev, struct device *master,
-			   void *data)
-{
-	struct vc4_dpi *dpi = dev_get_drvdata(dev);
-
-	drm_encoder_cleanup(&dpi->encoder.base);
 }
 
 static const struct component_ops vc4_dpi_ops = {
 	.bind   = vc4_dpi_bind,
-	.unbind = vc4_dpi_unbind,
 };
 
 static int vc4_dpi_dev_probe(struct platform_device *pdev)
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 27/64] drm/vc4: dpi: Switch to drmm_of_get_bridge
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (25 preceding siblings ...)
  2022-06-10  9:28 ` [PATCH 26/64] drm/vc4: dpi: Switch to DRM-managed encoder initialization Maxime Ripard
@ 2022-06-10  9:28 ` Maxime Ripard
  2022-06-14 16:52   ` Dave Stevenson
  2022-06-10  9:28 ` [PATCH 28/64] drm/vc4: dpi: Protect device resources Maxime Ripard
                   ` (36 subsequent siblings)
  63 siblings, 1 reply; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:28 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

The current code uses a device-managed function to retrieve the next bridge
downstream.

However, that means that it will be removed at unbind time, where the DRM
device is still very much live and might still have some applications that
still have it open.

Switch to a DRM-managed variant to clean everything up once the DRM device
has been last closed.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_dpi.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c
index 8a50de2c40d9..9950761449cf 100644
--- a/drivers/gpu/drm/vc4/vc4_dpi.c
+++ b/drivers/gpu/drm/vc4/vc4_dpi.c
@@ -220,10 +220,11 @@ static const struct of_device_id vc4_dpi_dt_match[] = {
  */
 static int vc4_dpi_init_bridge(struct vc4_dpi *dpi)
 {
+	struct drm_device *drm = dpi->encoder.base.dev;
 	struct device *dev = &dpi->pdev->dev;
 	struct drm_bridge *bridge;
 
-	bridge = devm_drm_of_get_bridge(dev, dev->of_node, 0, 0);
+	bridge = drmm_of_get_bridge(drm, dev->of_node, 0, 0);
 	if (IS_ERR(bridge)) {
 		/* If nothing was connected in the DT, that's not an
 		 * error.
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 28/64] drm/vc4: dpi: Protect device resources
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (26 preceding siblings ...)
  2022-06-10  9:28 ` [PATCH 27/64] drm/vc4: dpi: Switch to drmm_of_get_bridge Maxime Ripard
@ 2022-06-10  9:28 ` Maxime Ripard
  2022-06-14 16:55   ` Dave Stevenson
  2022-06-10  9:28 ` [PATCH 29/64] drm/vc4: dsi: Embed DRM structures into the private structure Maxime Ripard
                   ` (35 subsequent siblings)
  63 siblings, 1 reply; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:28 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

Our current code now mixes some resources whose lifetime are tied to the
device (clocks, IO mappings, etc.) and some that are tied to the DRM device
(encoder, bridge).

The device one will be freed at unbind time, but the DRM one will only be
freed when the last user of the DRM device closes its file handle.

So we end up with a time window during which we can call the encoder hooks,
but we don't have access to the underlying resources and device.

Let's protect all those sections with drm_dev_enter() and drm_dev_exit() so
that we bail out if we are during that window.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_dpi.c | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c
index 9950761449cf..ea3d20651f43 100644
--- a/drivers/gpu/drm/vc4/vc4_dpi.c
+++ b/drivers/gpu/drm/vc4/vc4_dpi.c
@@ -13,6 +13,7 @@
 
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_bridge.h>
+#include <drm/drm_drv.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_of.h>
 #include <drm/drm_panel.h>
@@ -111,9 +112,16 @@ static const struct debugfs_reg32 dpi_regs[] = {
 
 static void vc4_dpi_encoder_disable(struct drm_encoder *encoder)
 {
+	struct drm_device *dev = encoder->dev;
 	struct vc4_dpi *dpi = to_vc4_dpi(encoder);
+	int idx;
+
+	if (!drm_dev_enter(dev, &idx))
+		return;
 
 	clk_disable_unprepare(dpi->pixel_clock);
+
+	drm_dev_exit(idx);
 }
 
 static void vc4_dpi_encoder_enable(struct drm_encoder *encoder)
@@ -124,6 +132,7 @@ static void vc4_dpi_encoder_enable(struct drm_encoder *encoder)
 	struct drm_connector_list_iter conn_iter;
 	struct drm_connector *connector = NULL, *connector_scan;
 	u32 dpi_c = DPI_ENABLE | DPI_OUTPUT_ENABLE_MODE;
+	int idx;
 	int ret;
 
 	/* Look up the connector attached to DPI so we can get the
@@ -184,6 +193,9 @@ static void vc4_dpi_encoder_enable(struct drm_encoder *encoder)
 	else if (!(mode->flags & DRM_MODE_FLAG_PVSYNC))
 		dpi_c |= DPI_VSYNC_DISABLE;
 
+	if (!drm_dev_enter(dev, &idx))
+		return;
+
 	DPI_WRITE(DPI_C, dpi_c);
 
 	ret = clk_set_rate(dpi->pixel_clock, mode->clock * 1000);
@@ -193,6 +205,8 @@ static void vc4_dpi_encoder_enable(struct drm_encoder *encoder)
 	ret = clk_prepare_enable(dpi->pixel_clock);
 	if (ret)
 		DRM_ERROR("Failed to set clock rate: %d\n", ret);
+
+	drm_dev_exit(idx);
 }
 
 static enum drm_mode_status vc4_dpi_encoder_mode_valid(struct drm_encoder *encoder,
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 29/64] drm/vc4: dsi: Embed DRM structures into the private structure
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (27 preceding siblings ...)
  2022-06-10  9:28 ` [PATCH 28/64] drm/vc4: dpi: Protect device resources Maxime Ripard
@ 2022-06-10  9:28 ` Maxime Ripard
  2022-06-10  9:28 ` [PATCH 30/64] drm/vc4: dsi: Switch to DRM-managed encoder initialization Maxime Ripard
                   ` (34 subsequent siblings)
  63 siblings, 0 replies; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:28 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

The VC4 DSI driver private structure contains only a pointer to the
encoder it implements. This makes the overall structure somewhat
inconsistent with the rest of the driver, and complicates its
initialisation without any apparent gain.

Let's embed the drm_encoder structure (through the vc4_encoder one) into
struct vc4_dsi to fix both issues.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_dsi.c | 58 +++++++++++++----------------------
 1 file changed, 22 insertions(+), 36 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
index 98308a17e4ed..dbb3f6fb39b4 100644
--- a/drivers/gpu/drm/vc4/vc4_dsi.c
+++ b/drivers/gpu/drm/vc4/vc4_dsi.c
@@ -507,10 +507,11 @@ struct vc4_dsi_variant {
 
 /* General DSI hardware state. */
 struct vc4_dsi {
-	struct platform_device *pdev;
-
+	struct vc4_encoder encoder;
 	struct mipi_dsi_host dsi_host;
-	struct drm_encoder *encoder;
+
+	struct platform_device *pdev;
+
 	struct drm_bridge *bridge;
 	struct list_head bridge_chain;
 
@@ -558,6 +559,12 @@ struct vc4_dsi {
 
 #define host_to_dsi(host) container_of(host, struct vc4_dsi, dsi_host)
 
+static inline struct vc4_dsi *
+to_vc4_dsi(struct drm_encoder *encoder)
+{
+	return container_of(encoder, struct vc4_dsi, encoder.base);
+}
+
 static inline void
 dsi_dma_workaround_write(struct vc4_dsi *dsi, u32 offset, u32 val)
 {
@@ -602,18 +609,6 @@ dsi_dma_workaround_write(struct vc4_dsi *dsi, u32 offset, u32 val)
 	DSI_WRITE(dsi->variant->port ? DSI1_##offset : DSI0_##offset, val)
 #define DSI_PORT_BIT(bit) (dsi->variant->port ? DSI1_##bit : DSI0_##bit)
 
-/* VC4 DSI encoder KMS struct */
-struct vc4_dsi_encoder {
-	struct vc4_encoder base;
-	struct vc4_dsi *dsi;
-};
-
-static inline struct vc4_dsi_encoder *
-to_vc4_dsi_encoder(struct drm_encoder *encoder)
-{
-	return container_of(encoder, struct vc4_dsi_encoder, base.base);
-}
-
 static const struct debugfs_reg32 dsi0_regs[] = {
 	VC4_REG32(DSI0_CTRL),
 	VC4_REG32(DSI0_STAT),
@@ -753,8 +748,7 @@ dsi_esc_timing(u32 ns)
 
 static void vc4_dsi_encoder_disable(struct drm_encoder *encoder)
 {
-	struct vc4_dsi_encoder *vc4_encoder = to_vc4_dsi_encoder(encoder);
-	struct vc4_dsi *dsi = vc4_encoder->dsi;
+	struct vc4_dsi *dsi = to_vc4_dsi(encoder);
 	struct device *dev = &dsi->pdev->dev;
 	struct drm_bridge *iter;
 
@@ -794,8 +788,7 @@ static bool vc4_dsi_encoder_mode_fixup(struct drm_encoder *encoder,
 				       const struct drm_display_mode *mode,
 				       struct drm_display_mode *adjusted_mode)
 {
-	struct vc4_dsi_encoder *vc4_encoder = to_vc4_dsi_encoder(encoder);
-	struct vc4_dsi *dsi = vc4_encoder->dsi;
+	struct vc4_dsi *dsi = to_vc4_dsi(encoder);
 	struct clk *phy_parent = clk_get_parent(dsi->pll_phy_clock);
 	unsigned long parent_rate = clk_get_rate(phy_parent);
 	unsigned long pixel_clock_hz = mode->clock * 1000;
@@ -832,8 +825,7 @@ static bool vc4_dsi_encoder_mode_fixup(struct drm_encoder *encoder,
 static void vc4_dsi_encoder_enable(struct drm_encoder *encoder)
 {
 	struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
-	struct vc4_dsi_encoder *vc4_encoder = to_vc4_dsi_encoder(encoder);
-	struct vc4_dsi *dsi = vc4_encoder->dsi;
+	struct vc4_dsi *dsi = to_vc4_dsi(encoder);
 	struct device *dev = &dsi->pdev->dev;
 	bool debug_dump_regs = false;
 	struct drm_bridge *iter;
@@ -1492,21 +1484,14 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
 	struct platform_device *pdev = to_platform_device(dev);
 	struct drm_device *drm = dev_get_drvdata(master);
 	struct vc4_dsi *dsi = dev_get_drvdata(dev);
-	struct vc4_dsi_encoder *vc4_dsi_encoder;
+	struct drm_encoder *encoder = &dsi->encoder.base;
 	dma_cap_mask_t dma_mask;
 	int ret;
 
 	dsi->variant = of_device_get_match_data(dev);
 
-	vc4_dsi_encoder = devm_kzalloc(dev, sizeof(*vc4_dsi_encoder),
-				       GFP_KERNEL);
-	if (!vc4_dsi_encoder)
-		return -ENOMEM;
-
 	INIT_LIST_HEAD(&dsi->bridge_chain);
-	vc4_dsi_encoder->base.type = VC4_ENCODER_TYPE_DSI1;
-	vc4_dsi_encoder->dsi = dsi;
-	dsi->encoder = &vc4_dsi_encoder->base.base;
+	dsi->encoder.type = VC4_ENCODER_TYPE_DSI1;
 
 	dsi->regs = vc4_ioremap_regs(pdev, 0);
 	if (IS_ERR(dsi->regs))
@@ -1614,10 +1599,10 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
 	if (ret)
 		return ret;
 
-	drm_simple_encoder_init(drm, dsi->encoder, DRM_MODE_ENCODER_DSI);
-	drm_encoder_helper_add(dsi->encoder, &vc4_dsi_encoder_helper_funcs);
+	drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_DSI);
+	drm_encoder_helper_add(encoder, &vc4_dsi_encoder_helper_funcs);
 
-	ret = drm_bridge_attach(dsi->encoder, dsi->bridge, NULL, 0);
+	ret = drm_bridge_attach(encoder, dsi->bridge, NULL, 0);
 	if (ret)
 		return ret;
 	/* Disable the atomic helper calls into the bridge.  We
@@ -1625,7 +1610,7 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
 	 * from our driver, since we need to sequence them within the
 	 * encoder's enable/disable paths.
 	 */
-	list_splice_init(&dsi->encoder->bridge_chain, &dsi->bridge_chain);
+	list_splice_init(&encoder->bridge_chain, &dsi->bridge_chain);
 
 	vc4_debugfs_add_regset32(drm, dsi->variant->debugfs_name, &dsi->regset);
 
@@ -1638,6 +1623,7 @@ static void vc4_dsi_unbind(struct device *dev, struct device *master,
 			   void *data)
 {
 	struct vc4_dsi *dsi = dev_get_drvdata(dev);
+	struct drm_encoder *encoder = &dsi->encoder.base;
 
 	pm_runtime_disable(dev);
 
@@ -1645,8 +1631,8 @@ static void vc4_dsi_unbind(struct device *dev, struct device *master,
 	 * Restore the bridge_chain so the bridge detach procedure can happen
 	 * normally.
 	 */
-	list_splice_init(&dsi->bridge_chain, &dsi->encoder->bridge_chain);
-	drm_encoder_cleanup(dsi->encoder);
+	list_splice_init(&dsi->bridge_chain, &encoder->bridge_chain);
+	drm_encoder_cleanup(encoder);
 }
 
 static const struct component_ops vc4_dsi_ops = {
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 30/64] drm/vc4: dsi: Switch to DRM-managed encoder initialization
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (28 preceding siblings ...)
  2022-06-10  9:28 ` [PATCH 29/64] drm/vc4: dsi: Embed DRM structures into the private structure Maxime Ripard
@ 2022-06-10  9:28 ` Maxime Ripard
  2022-06-14 17:04   ` Dave Stevenson
  2022-06-10  9:28 ` [PATCH 31/64] drm/vc4: dsi: Switch to drmm_of_get_bridge Maxime Ripard
                   ` (33 subsequent siblings)
  63 siblings, 1 reply; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:28 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

The current code will call drm_encoder_cleanup() when the device is
unbound. However, by then, there might still be some references held to
that encoder, including by the userspace that might still have the DRM
device open.

Let's switch to a DRM-managed initialization to clean up after ourselves
only once the DRM device has been last closed.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_dsi.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
index dbb3f6fb39b4..bcaf87b43cbd 100644
--- a/drivers/gpu/drm/vc4/vc4_dsi.c
+++ b/drivers/gpu/drm/vc4/vc4_dsi.c
@@ -1599,7 +1599,10 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
 	if (ret)
 		return ret;
 
-	drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_DSI);
+	ret = drmm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_DSI);
+	if (ret)
+		return ret;
+
 	drm_encoder_helper_add(encoder, &vc4_dsi_encoder_helper_funcs);
 
 	ret = drm_bridge_attach(encoder, dsi->bridge, NULL, 0);
@@ -1632,7 +1635,6 @@ static void vc4_dsi_unbind(struct device *dev, struct device *master,
 	 * normally.
 	 */
 	list_splice_init(&dsi->bridge_chain, &encoder->bridge_chain);
-	drm_encoder_cleanup(encoder);
 }
 
 static const struct component_ops vc4_dsi_ops = {
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 31/64] drm/vc4: dsi: Switch to drmm_of_get_bridge
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (29 preceding siblings ...)
  2022-06-10  9:28 ` [PATCH 30/64] drm/vc4: dsi: Switch to DRM-managed encoder initialization Maxime Ripard
@ 2022-06-10  9:28 ` Maxime Ripard
  2022-06-14 17:05   ` Dave Stevenson
  2022-06-10  9:28 ` [PATCH 32/64] drm/vc4: dsi: Fix the driver structure lifetime Maxime Ripard
                   ` (32 subsequent siblings)
  63 siblings, 1 reply; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:28 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

The current code uses a device-managed function to retrieve the next bridge
downstream.

However, that means that it will be removed at unbind time, where the DRM
device is still very much live and might still have some applications that
still have it open.

Switch to a DRM-managed variant to clean everything up once the DRM device
has been last closed.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_dsi.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
index bcaf87b43cbd..10533a2a41b3 100644
--- a/drivers/gpu/drm/vc4/vc4_dsi.c
+++ b/drivers/gpu/drm/vc4/vc4_dsi.c
@@ -1584,7 +1584,7 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
 		return ret;
 	}
 
-	dsi->bridge = devm_drm_of_get_bridge(dev, dev->of_node, 0, 0);
+	dsi->bridge = drmm_of_get_bridge(drm, dev->of_node, 0, 0);
 	if (IS_ERR(dsi->bridge))
 		return PTR_ERR(dsi->bridge);
 
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 32/64] drm/vc4: dsi: Fix the driver structure lifetime
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (30 preceding siblings ...)
  2022-06-10  9:28 ` [PATCH 31/64] drm/vc4: dsi: Switch to drmm_of_get_bridge Maxime Ripard
@ 2022-06-10  9:28 ` Maxime Ripard
  2022-06-20 10:55   ` Thomas Zimmermann
  2022-06-10  9:28 ` [PATCH 33/64] drm/vc4: dsi: Switch to devm_pm_runtime_enable Maxime Ripard
                   ` (31 subsequent siblings)
  63 siblings, 1 reply; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:28 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

The vc4_dsi structure is currently allocated through a device-managed
allocation. This can lead to use-after-free issues however in the unbinding
path since the DRM entities will stick around, but the underlying structure
has been freed.

However, we can't just fix it by using a DRM-managed allocation like we did
for the other drivers since the DSI case is a bit more intricate.

Indeed, the structure will be allocated at probe time, when we don't have a
DRM device yet, to be able to register the DSI bus driver. We will then
reuse it at bind time to register our KMS entities in the framework.

In order to work around both constraints, we can use a kref to track the
users of the structure (DSI host, and KMS), and then put our structure when
the DSI host will have been unregistered, and through a DRM-managed action
that will execute once we won't need the KMS entities anymore.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_dsi.c | 29 ++++++++++++++++++++++++++++-
 1 file changed, 28 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
index 10533a2a41b3..282537f27b8e 100644
--- a/drivers/gpu/drm/vc4/vc4_dsi.c
+++ b/drivers/gpu/drm/vc4/vc4_dsi.c
@@ -510,6 +510,8 @@ struct vc4_dsi {
 	struct vc4_encoder encoder;
 	struct mipi_dsi_host dsi_host;
 
+	struct kref kref;
+
 	struct platform_device *pdev;
 
 	struct drm_bridge *bridge;
@@ -1479,6 +1481,15 @@ vc4_dsi_init_phy_clocks(struct vc4_dsi *dsi)
 				      dsi->clk_onecell);
 }
 
+static void vc4_dsi_release(struct kref *kref);
+
+static void vc4_dsi_put(struct drm_device *drm, void *ptr)
+{
+	struct vc4_dsi *dsi = ptr;
+
+	kref_put(&dsi->kref, &vc4_dsi_release);
+}
+
 static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
 {
 	struct platform_device *pdev = to_platform_device(dev);
@@ -1488,6 +1499,12 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
 	dma_cap_mask_t dma_mask;
 	int ret;
 
+	kref_get(&dsi->kref);
+
+	ret = drmm_add_action_or_reset(drm, vc4_dsi_put, dsi);
+	if (ret)
+		return ret;
+
 	dsi->variant = of_device_get_match_data(dev);
 
 	INIT_LIST_HEAD(&dsi->bridge_chain);
@@ -1642,16 +1659,25 @@ static const struct component_ops vc4_dsi_ops = {
 	.unbind = vc4_dsi_unbind,
 };
 
+static void vc4_dsi_release(struct kref *kref)
+{
+	struct vc4_dsi *dsi =
+		container_of(kref, struct vc4_dsi, kref);
+
+	kfree(dsi);
+}
+
 static int vc4_dsi_dev_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
 	struct vc4_dsi *dsi;
 
-	dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
+	dsi = kzalloc(sizeof(*dsi), GFP_KERNEL);
 	if (!dsi)
 		return -ENOMEM;
 	dev_set_drvdata(dev, dsi);
 
+	kref_init(&dsi->kref);
 	dsi->pdev = pdev;
 	dsi->dsi_host.ops = &vc4_dsi_host_ops;
 	dsi->dsi_host.dev = dev;
@@ -1666,6 +1692,7 @@ static int vc4_dsi_dev_remove(struct platform_device *pdev)
 	struct vc4_dsi *dsi = dev_get_drvdata(dev);
 
 	mipi_dsi_host_unregister(&dsi->dsi_host);
+	kref_put(&dsi->kref, &vc4_dsi_release);
 	return 0;
 }
 
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 33/64] drm/vc4: dsi: Switch to devm_pm_runtime_enable
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (31 preceding siblings ...)
  2022-06-10  9:28 ` [PATCH 32/64] drm/vc4: dsi: Fix the driver structure lifetime Maxime Ripard
@ 2022-06-10  9:28 ` Maxime Ripard
  2022-06-10  9:28 ` [PATCH 34/64] drm/vc4: hdmi: Switch to drmm_kzalloc Maxime Ripard
                   ` (30 subsequent siblings)
  63 siblings, 0 replies; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:28 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

devm_pm_runtime_enable() simplifies the driver a bit since it will call
pm_runtime_disable() automatically through a device-managed action.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_dsi.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
index 282537f27b8e..741db2dce8ab 100644
--- a/drivers/gpu/drm/vc4/vc4_dsi.c
+++ b/drivers/gpu/drm/vc4/vc4_dsi.c
@@ -1622,6 +1622,10 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
 
 	drm_encoder_helper_add(encoder, &vc4_dsi_encoder_helper_funcs);
 
+	ret = devm_pm_runtime_enable(dev);
+	if (ret)
+		return ret;
+
 	ret = drm_bridge_attach(encoder, dsi->bridge, NULL, 0);
 	if (ret)
 		return ret;
@@ -1634,8 +1638,6 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
 
 	vc4_debugfs_add_regset32(drm, dsi->variant->debugfs_name, &dsi->regset);
 
-	pm_runtime_enable(dev);
-
 	return 0;
 }
 
@@ -1645,8 +1647,6 @@ static void vc4_dsi_unbind(struct device *dev, struct device *master,
 	struct vc4_dsi *dsi = dev_get_drvdata(dev);
 	struct drm_encoder *encoder = &dsi->encoder.base;
 
-	pm_runtime_disable(dev);
-
 	/*
 	 * Restore the bridge_chain so the bridge detach procedure can happen
 	 * normally.
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 34/64] drm/vc4: hdmi: Switch to drmm_kzalloc
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (32 preceding siblings ...)
  2022-06-10  9:28 ` [PATCH 33/64] drm/vc4: dsi: Switch to devm_pm_runtime_enable Maxime Ripard
@ 2022-06-10  9:28 ` Maxime Ripard
  2022-06-20 11:01   ` Thomas Zimmermann
  2022-06-10  9:28 ` [PATCH 35/64] drm/vc4: hdmi: Switch to DRM-managed encoder initialization Maxime Ripard
                   ` (29 subsequent siblings)
  63 siblings, 1 reply; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:28 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

Our internal structure that stores the DRM entities structure is allocated
through a device-managed kzalloc.

This means that this will eventually be freed whenever the device is
removed. In our case, the most like source of removal is that the main
device is going to be unbound, and component_unbind_all() is being run.

However, it occurs while the DRM device is still registered, which will
create dangling pointers, eventually resulting in use-after-free.

Switch to a DRM-managed allocation to keep our structure until the DRM
driver doesn't need it anymore.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_hdmi.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index 6aadb65eb640..eb8ff7b258d1 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -2833,9 +2833,10 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
 	struct device_node *ddc_node;
 	int ret;
 
-	vc4_hdmi = devm_kzalloc(dev, sizeof(*vc4_hdmi), GFP_KERNEL);
+	vc4_hdmi = drmm_kzalloc(drm, sizeof(*vc4_hdmi), GFP_KERNEL);
 	if (!vc4_hdmi)
 		return -ENOMEM;
+
 	mutex_init(&vc4_hdmi->mutex);
 	spin_lock_init(&vc4_hdmi->hw_lock);
 	INIT_DELAYED_WORK(&vc4_hdmi->scrambling_work, vc4_hdmi_scrambling_wq);
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 35/64] drm/vc4: hdmi: Switch to DRM-managed encoder initialization
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (33 preceding siblings ...)
  2022-06-10  9:28 ` [PATCH 34/64] drm/vc4: hdmi: Switch to drmm_kzalloc Maxime Ripard
@ 2022-06-10  9:28 ` Maxime Ripard
  2022-06-10  9:28 ` [PATCH 36/64] drm/vc4: hdmi: Switch to DRM-managed connector initialization Maxime Ripard
                   ` (28 subsequent siblings)
  63 siblings, 0 replies; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:28 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

The current code will call drm_encoder_cleanup() when the device is
unbound. However, by then, there might still be some references held to
that encoder, including by the userspace that might still have the DRM
device open.

Let's switch to a DRM-managed initialization to clean up after ourselves
only once the DRM device has been last closed.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_hdmi.c | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index eb8ff7b258d1..e5b6e35f57f6 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -2921,12 +2921,15 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
 		clk_prepare_enable(vc4_hdmi->pixel_bvb_clock);
 	}
 
-	drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS);
+	ret = drmm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS);
+	if (ret)
+		goto err_put_runtime_pm;
+
 	drm_encoder_helper_add(encoder, &vc4_hdmi_encoder_helper_funcs);
 
 	ret = vc4_hdmi_connector_init(drm, vc4_hdmi);
 	if (ret)
-		goto err_destroy_encoder;
+		goto err_put_runtime_pm;
 
 	ret = vc4_hdmi_hotplug_init(vc4_hdmi);
 	if (ret)
@@ -2954,8 +2957,7 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
 	vc4_hdmi_hotplug_exit(vc4_hdmi);
 err_destroy_conn:
 	vc4_hdmi_connector_destroy(&vc4_hdmi->connector);
-err_destroy_encoder:
-	drm_encoder_cleanup(encoder);
+err_put_runtime_pm:
 	pm_runtime_put_sync(dev);
 	pm_runtime_disable(dev);
 err_put_ddc:
@@ -2997,7 +2999,6 @@ static void vc4_hdmi_unbind(struct device *dev, struct device *master,
 	vc4_hdmi_cec_exit(vc4_hdmi);
 	vc4_hdmi_hotplug_exit(vc4_hdmi);
 	vc4_hdmi_connector_destroy(&vc4_hdmi->connector);
-	drm_encoder_cleanup(&vc4_hdmi->encoder.base);
 
 	pm_runtime_disable(dev);
 
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 36/64] drm/vc4: hdmi: Switch to DRM-managed connector initialization
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (34 preceding siblings ...)
  2022-06-10  9:28 ` [PATCH 35/64] drm/vc4: hdmi: Switch to DRM-managed encoder initialization Maxime Ripard
@ 2022-06-10  9:28 ` Maxime Ripard
  2022-06-10  9:28 ` [PATCH 37/64] drm/vc4: hdmi: Switch to device-managed ALSA initialization Maxime Ripard
                   ` (27 subsequent siblings)
  63 siblings, 0 replies; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:28 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

The current code will call drm_connector_unregister() and
drm_connector_cleanup() when the device is unbound. However, by then, there
might still be some references held to that connector, including by the
userspace that might still have the DRM device open.

Let's switch to a DRM-managed initialization to clean up after ourselves
only once the DRM device has been last closed.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_hdmi.c | 23 ++++++++---------------
 1 file changed, 8 insertions(+), 15 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index e5b6e35f57f6..63d9a91f5038 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -255,12 +255,6 @@ vc4_hdmi_connector_detect(struct drm_connector *connector, bool force)
 	return connector_status_disconnected;
 }
 
-static void vc4_hdmi_connector_destroy(struct drm_connector *connector)
-{
-	drm_connector_unregister(connector);
-	drm_connector_cleanup(connector);
-}
-
 static int vc4_hdmi_connector_get_modes(struct drm_connector *connector)
 {
 	struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector);
@@ -368,7 +362,6 @@ vc4_hdmi_connector_duplicate_state(struct drm_connector *connector)
 static const struct drm_connector_funcs vc4_hdmi_connector_funcs = {
 	.detect = vc4_hdmi_connector_detect,
 	.fill_modes = drm_helper_probe_single_connector_modes,
-	.destroy = vc4_hdmi_connector_destroy,
 	.reset = vc4_hdmi_connector_reset,
 	.atomic_duplicate_state = vc4_hdmi_connector_duplicate_state,
 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
@@ -386,10 +379,13 @@ static int vc4_hdmi_connector_init(struct drm_device *dev,
 	struct drm_encoder *encoder = &vc4_hdmi->encoder.base;
 	int ret;
 
-	drm_connector_init_with_ddc(dev, connector,
-				    &vc4_hdmi_connector_funcs,
-				    DRM_MODE_CONNECTOR_HDMIA,
-				    vc4_hdmi->ddc);
+	ret = drmm_connector_init_with_ddc(dev, connector,
+					   &vc4_hdmi_connector_funcs,
+					   DRM_MODE_CONNECTOR_HDMIA,
+					   vc4_hdmi->ddc);
+	if (ret)
+		return ret;
+
 	drm_connector_helper_add(connector, &vc4_hdmi_connector_helper_funcs);
 
 	/*
@@ -2933,7 +2929,7 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
 
 	ret = vc4_hdmi_hotplug_init(vc4_hdmi);
 	if (ret)
-		goto err_destroy_conn;
+		goto err_put_runtime_pm;
 
 	ret = vc4_hdmi_cec_init(vc4_hdmi);
 	if (ret)
@@ -2955,8 +2951,6 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
 	vc4_hdmi_cec_exit(vc4_hdmi);
 err_free_hotplug:
 	vc4_hdmi_hotplug_exit(vc4_hdmi);
-err_destroy_conn:
-	vc4_hdmi_connector_destroy(&vc4_hdmi->connector);
 err_put_runtime_pm:
 	pm_runtime_put_sync(dev);
 	pm_runtime_disable(dev);
@@ -2998,7 +2992,6 @@ static void vc4_hdmi_unbind(struct device *dev, struct device *master,
 	vc4_hdmi_audio_exit(vc4_hdmi);
 	vc4_hdmi_cec_exit(vc4_hdmi);
 	vc4_hdmi_hotplug_exit(vc4_hdmi);
-	vc4_hdmi_connector_destroy(&vc4_hdmi->connector);
 
 	pm_runtime_disable(dev);
 
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 37/64] drm/vc4: hdmi: Switch to device-managed ALSA initialization
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (35 preceding siblings ...)
  2022-06-10  9:28 ` [PATCH 36/64] drm/vc4: hdmi: Switch to DRM-managed connector initialization Maxime Ripard
@ 2022-06-10  9:28 ` Maxime Ripard
  2022-06-10  9:28 ` [PATCH 38/64] drm/vc4: hdmi: Switch to device-managed CEC initialization Maxime Ripard
                   ` (26 subsequent siblings)
  63 siblings, 0 replies; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:28 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

The current code to unregister our ALSA device needs to be undone manually
when we remove the HDMI driver.

Since ALSA doesn't seem to support any mechanism to defer freeing something
until the last user of the ALSA device is gone, we can either use a
device-managed or a DRM-managed action.

The consistent way would be to use a DRM-managed one, just like pretty much
any framework-facing structure should be doing. However, ALSA does a lot of
allocation and registration using device-managed calls. Thus, if we're
going that way, by the time the DRM-managed action would run all of those
allocation would have been freed and we would end up with a use-after-free.

Thus, let's do a device-managed action. It's been tested with KASAN enabled
and doesn't seem to trigger any issue, so it's as good as anything.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_hdmi.c | 43 ++++++++++++++++++++++++++++------
 1 file changed, 36 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index 63d9a91f5038..8f71f5a5e4ce 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -2021,6 +2021,14 @@ static struct hdmi_codec_pdata vc4_hdmi_codec_pdata = {
 	.i2s = 1,
 };
 
+static void vc4_hdmi_audio_codec_release(void *ptr)
+{
+	struct vc4_hdmi *vc4_hdmi = ptr;
+
+	platform_device_unregister(vc4_hdmi->audio.codec_pdev);
+	vc4_hdmi->audio.codec_pdev = NULL;
+}
+
 static int vc4_hdmi_audio_init(struct vc4_hdmi *vc4_hdmi)
 {
 	const struct vc4_hdmi_register *mai_data =
@@ -2062,6 +2070,30 @@ static int vc4_hdmi_audio_init(struct vc4_hdmi *vc4_hdmi)
 	vc4_hdmi->audio.dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
 	vc4_hdmi->audio.dma_data.maxburst = 2;
 
+	/*
+	 * NOTE: Strictly speaking, we should probably use a DRM-managed
+	 * registration there to avoid removing all the audio components
+	 * by the time the driver doesn't have any user anymore.
+	 *
+	 * However, the ASoC core uses a number of devm_kzalloc calls
+	 * when registering, even when using non-device-managed
+	 * functions (such as in snd_soc_register_component()).
+	 *
+	 * If we call snd_soc_unregister_component() in a DRM-managed
+	 * action, the device-managed actions have already been executed
+	 * and thus we would access memory that has been freed.
+	 *
+	 * Using device-managed hooks here probably leaves us open to a
+	 * bunch of issues if userspace still has a handle on the ALSA
+	 * device when the device is removed. However, this is mitigated
+	 * by the use of drm_dev_enter()/drm_dev_exit() in the audio
+	 * path to prevent the access to the device resources if it
+	 * isn't there anymore.
+	 *
+	 * Then, the vc4_hdmi structure is DRM-managed and thus only
+	 * freed whenever the last user has closed the DRM device file.
+	 * It should thus outlive ALSA in most situations.
+	 */
 	ret = devm_snd_dmaengine_pcm_register(dev, &pcm_conf, 0);
 	if (ret) {
 		dev_err(dev, "Could not register PCM component: %d\n", ret);
@@ -2085,6 +2117,10 @@ static int vc4_hdmi_audio_init(struct vc4_hdmi *vc4_hdmi)
 	}
 	vc4_hdmi->audio.codec_pdev = codec_pdev;
 
+	ret = devm_add_action_or_reset(dev, vc4_hdmi_audio_codec_release, vc4_hdmi);
+	if (ret)
+		return ret;
+
 	dai_link->cpus		= &vc4_hdmi->audio.cpu;
 	dai_link->codecs	= &vc4_hdmi->audio.codec;
 	dai_link->platforms	= &vc4_hdmi->audio.platform;
@@ -2123,12 +2159,6 @@ static int vc4_hdmi_audio_init(struct vc4_hdmi *vc4_hdmi)
 
 }
 
-static void vc4_hdmi_audio_exit(struct vc4_hdmi *vc4_hdmi)
-{
-	platform_device_unregister(vc4_hdmi->audio.codec_pdev);
-	vc4_hdmi->audio.codec_pdev = NULL;
-}
-
 static irqreturn_t vc4_hdmi_hpd_irq_thread(int irq, void *priv)
 {
 	struct vc4_hdmi *vc4_hdmi = priv;
@@ -2989,7 +3019,6 @@ static void vc4_hdmi_unbind(struct device *dev, struct device *master,
 	kfree(vc4_hdmi->hdmi_regset.regs);
 	kfree(vc4_hdmi->hd_regset.regs);
 
-	vc4_hdmi_audio_exit(vc4_hdmi);
 	vc4_hdmi_cec_exit(vc4_hdmi);
 	vc4_hdmi_hotplug_exit(vc4_hdmi);
 
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 38/64] drm/vc4: hdmi: Switch to device-managed CEC initialization
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (36 preceding siblings ...)
  2022-06-10  9:28 ` [PATCH 37/64] drm/vc4: hdmi: Switch to device-managed ALSA initialization Maxime Ripard
@ 2022-06-10  9:28 ` Maxime Ripard
  2022-06-10  9:28 ` [PATCH 39/64] drm/vc4: hdmi: Use a device-managed action for DDC Maxime Ripard
                   ` (25 subsequent siblings)
  63 siblings, 0 replies; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:28 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

The current code to unregister our CEC device needs to be undone manually
when we remove the HDMI driver.

Since the CEC framework will allocate its main structure, and will defer
its deallocation to when the last user will have closed it, we don't really
need to take any particular measure to prevent any use-after-free and can
thus use any managed action.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_hdmi.c | 95 ++++++++++++++++++----------------
 1 file changed, 50 insertions(+), 45 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index 8f71f5a5e4ce..402bfde3b5fe 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -2541,6 +2541,14 @@ static const struct cec_adap_ops vc4_hdmi_cec_adap_ops = {
 	.adap_transmit = vc4_hdmi_cec_adap_transmit,
 };
 
+static void vc4_hdmi_cec_release(void *ptr)
+{
+	struct vc4_hdmi *vc4_hdmi = ptr;
+
+	cec_unregister_adapter(vc4_hdmi->cec_adap);
+	vc4_hdmi->cec_adap = NULL;
+}
+
 static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi)
 {
 	struct cec_connector_info conn_info;
@@ -2576,75 +2584,75 @@ static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi)
 	vc4_hdmi_cec_update_clk_div(vc4_hdmi);
 
 	if (vc4_hdmi->variant->external_irq_controller) {
-		ret = request_threaded_irq(platform_get_irq_byname(pdev, "cec-rx"),
-					   vc4_cec_irq_handler_rx_bare,
-					   vc4_cec_irq_handler_rx_thread, 0,
-					   "vc4 hdmi cec rx", vc4_hdmi);
+		ret = devm_request_threaded_irq(dev, platform_get_irq_byname(pdev, "cec-rx"),
+						vc4_cec_irq_handler_rx_bare,
+						vc4_cec_irq_handler_rx_thread, 0,
+						"vc4 hdmi cec rx", vc4_hdmi);
 		if (ret)
 			goto err_delete_cec_adap;
 
-		ret = request_threaded_irq(platform_get_irq_byname(pdev, "cec-tx"),
-					   vc4_cec_irq_handler_tx_bare,
-					   vc4_cec_irq_handler_tx_thread, 0,
-					   "vc4 hdmi cec tx", vc4_hdmi);
+		ret = devm_request_threaded_irq(dev, platform_get_irq_byname(pdev, "cec-tx"),
+						vc4_cec_irq_handler_tx_bare,
+						vc4_cec_irq_handler_tx_thread, 0,
+						"vc4 hdmi cec tx", vc4_hdmi);
 		if (ret)
-			goto err_remove_cec_rx_handler;
+			goto err_delete_cec_adap;
 	} else {
 		spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
 		HDMI_WRITE(HDMI_CEC_CPU_MASK_SET, 0xffffffff);
 		spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
 
-		ret = request_threaded_irq(platform_get_irq(pdev, 0),
-					   vc4_cec_irq_handler,
-					   vc4_cec_irq_handler_thread, 0,
-					   "vc4 hdmi cec", vc4_hdmi);
+		ret = devm_request_threaded_irq(dev, platform_get_irq(pdev, 0),
+						vc4_cec_irq_handler,
+						vc4_cec_irq_handler_thread, 0,
+						"vc4 hdmi cec", vc4_hdmi);
 		if (ret)
 			goto err_delete_cec_adap;
 	}
 
 	ret = cec_register_adapter(vc4_hdmi->cec_adap, &pdev->dev);
 	if (ret < 0)
-		goto err_remove_handlers;
+		goto err_delete_cec_adap;
+
+	/*
+	 * NOTE: Strictly speaking, we should probably use a DRM-managed
+	 * registration there to avoid removing the CEC adapter by the
+	 * time the DRM driver doesn't have any user anymore.
+	 *
+	 * However, the CEC framework already cleans up the CEC adapter
+	 * only when the last user has closed its file descriptor, so we
+	 * don't need to handle it in DRM.
+	 *
+	 * By the time the device-managed hook is executed, we will give
+	 * up our reference to the CEC adapter and therefore don't
+	 * really care when it's actually freed.
+	 *
+	 * There's still a problematic sequence: if we unregister our
+	 * CEC adapter, but the userspace keeps a handle on the CEC
+	 * adapter but not the DRM device for some reason. In such a
+	 * case, our vc4_hdmi structure will be freed, but the
+	 * cec_adapter structure will have a dangling pointer to what
+	 * used to be our HDMI controller. If we get a CEC call at that
+	 * moment, we could end up with a use-after-free. Fortunately,
+	 * the CEC framework already handles this too, by calling
+	 * cec_is_registered() in cec_ioctl() and cec_poll().
+	 */
+	ret = devm_add_action_or_reset(dev, vc4_hdmi_cec_release, vc4_hdmi);
+	if (ret)
+		return ret;
 
 	return 0;
 
-err_remove_handlers:
-	if (vc4_hdmi->variant->external_irq_controller)
-		free_irq(platform_get_irq_byname(pdev, "cec-tx"), vc4_hdmi);
-	else
-		free_irq(platform_get_irq(pdev, 0), vc4_hdmi);
-
-err_remove_cec_rx_handler:
-	if (vc4_hdmi->variant->external_irq_controller)
-		free_irq(platform_get_irq_byname(pdev, "cec-rx"), vc4_hdmi);
-
 err_delete_cec_adap:
 	cec_delete_adapter(vc4_hdmi->cec_adap);
 
 	return ret;
 }
-
-static void vc4_hdmi_cec_exit(struct vc4_hdmi *vc4_hdmi)
-{
-	struct platform_device *pdev = vc4_hdmi->pdev;
-
-	if (vc4_hdmi->variant->external_irq_controller) {
-		free_irq(platform_get_irq_byname(pdev, "cec-rx"), vc4_hdmi);
-		free_irq(platform_get_irq_byname(pdev, "cec-tx"), vc4_hdmi);
-	} else {
-		free_irq(platform_get_irq(pdev, 0), vc4_hdmi);
-	}
-
-	cec_unregister_adapter(vc4_hdmi->cec_adap);
-}
 #else
 static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi)
 {
 	return 0;
 }
-
-static void vc4_hdmi_cec_exit(struct vc4_hdmi *vc4_hdmi) {};
-
 #endif
 
 static int vc4_hdmi_build_regset(struct vc4_hdmi *vc4_hdmi,
@@ -2967,7 +2975,7 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
 
 	ret = vc4_hdmi_audio_init(vc4_hdmi);
 	if (ret)
-		goto err_free_cec;
+		goto err_free_hotplug;
 
 	vc4_debugfs_add_file(drm, variant->debugfs_name,
 			     vc4_hdmi_debugfs_regs,
@@ -2977,8 +2985,6 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
 
 	return 0;
 
-err_free_cec:
-	vc4_hdmi_cec_exit(vc4_hdmi);
 err_free_hotplug:
 	vc4_hdmi_hotplug_exit(vc4_hdmi);
 err_put_runtime_pm:
@@ -3019,7 +3025,6 @@ static void vc4_hdmi_unbind(struct device *dev, struct device *master,
 	kfree(vc4_hdmi->hdmi_regset.regs);
 	kfree(vc4_hdmi->hd_regset.regs);
 
-	vc4_hdmi_cec_exit(vc4_hdmi);
 	vc4_hdmi_hotplug_exit(vc4_hdmi);
 
 	pm_runtime_disable(dev);
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 39/64] drm/vc4: hdmi: Use a device-managed action for DDC
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (37 preceding siblings ...)
  2022-06-10  9:28 ` [PATCH 38/64] drm/vc4: hdmi: Switch to device-managed CEC initialization Maxime Ripard
@ 2022-06-10  9:28 ` Maxime Ripard
  2022-06-10  9:29 ` [PATCH 40/64] drm/vc4: hdmi: Switch to DRM-managed kfree to build regsets Maxime Ripard
                   ` (24 subsequent siblings)
  63 siblings, 0 replies; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:28 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

The reference to the DDC controller device needs to be put back when we're
done with it. Let's use a device-managed action to simplify the driver.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_hdmi.c | 20 +++++++++++++-------
 1 file changed, 13 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index 402bfde3b5fe..bb9bd0c701be 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -2857,6 +2857,13 @@ static int vc4_hdmi_runtime_resume(struct device *dev)
 	return 0;
 }
 
+static void vc4_hdmi_put_ddc_device(void *ptr)
+{
+	struct vc4_hdmi *vc4_hdmi = ptr;
+
+	put_device(&vc4_hdmi->ddc->dev);
+}
+
 static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
 {
 	const struct vc4_hdmi_variant *variant = of_device_get_match_data(dev);
@@ -2912,13 +2919,16 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
 		return -EPROBE_DEFER;
 	}
 
+	ret = devm_add_action_or_reset(dev, vc4_hdmi_put_ddc_device, vc4_hdmi);
+	if (ret)
+		return ret;
+
 	/* Only use the GPIO HPD pin if present in the DT, otherwise
 	 * we'll use the HDMI core's register.
 	 */
 	vc4_hdmi->hpd_gpio = devm_gpiod_get_optional(dev, "hpd", GPIOD_IN);
 	if (IS_ERR(vc4_hdmi->hpd_gpio)) {
-		ret = PTR_ERR(vc4_hdmi->hpd_gpio);
-		goto err_put_ddc;
+		return PTR_ERR(vc4_hdmi->hpd_gpio);
 	}
 
 	vc4_hdmi->disable_wifi_frequencies =
@@ -2938,7 +2948,7 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
 	 */
 	ret = vc4_hdmi_runtime_resume(dev);
 	if (ret)
-		goto err_put_ddc;
+		return ret;
 
 	pm_runtime_get_noresume(dev);
 	pm_runtime_set_active(dev);
@@ -2990,8 +3000,6 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
 err_put_runtime_pm:
 	pm_runtime_put_sync(dev);
 	pm_runtime_disable(dev);
-err_put_ddc:
-	put_device(&vc4_hdmi->ddc->dev);
 
 	return ret;
 }
@@ -3028,8 +3036,6 @@ static void vc4_hdmi_unbind(struct device *dev, struct device *master,
 	vc4_hdmi_hotplug_exit(vc4_hdmi);
 
 	pm_runtime_disable(dev);
-
-	put_device(&vc4_hdmi->ddc->dev);
 }
 
 static const struct component_ops vc4_hdmi_ops = {
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 40/64] drm/vc4: hdmi: Switch to DRM-managed kfree to build regsets
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (38 preceding siblings ...)
  2022-06-10  9:28 ` [PATCH 39/64] drm/vc4: hdmi: Use a device-managed action for DDC Maxime Ripard
@ 2022-06-10  9:29 ` Maxime Ripard
  2022-06-10  9:29 ` [PATCH 41/64] drm/vc4: hdmi: Use devm to register hotplug interrupts Maxime Ripard
                   ` (23 subsequent siblings)
  63 siblings, 0 replies; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:29 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

The current code to build the registers set later exposed in debugfs for
the HDMI controller relies on traditional allocations, that are later
free'd as part of the driver unbind hook.

Since krealloc doesn't have a DRM-managed equivalent, let's add an action
to free the buffer later on.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_hdmi.c | 16 +++++++++++++---
 1 file changed, 13 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index bb9bd0c701be..ecc898684c4b 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -2655,14 +2655,23 @@ static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi)
 }
 #endif
 
+static void vc4_hdmi_free_regset(struct drm_device *drm, void *ptr)
+{
+	struct debugfs_reg32 *regs = ptr;
+
+	kfree(regs);
+}
+
 static int vc4_hdmi_build_regset(struct vc4_hdmi *vc4_hdmi,
 				 struct debugfs_regset32 *regset,
 				 enum vc4_hdmi_regs reg)
 {
+	struct drm_device *drm = vc4_hdmi->connector.dev;
 	const struct vc4_hdmi_variant *variant = vc4_hdmi->variant;
 	struct debugfs_reg32 *regs, *new_regs;
 	unsigned int count = 0;
 	unsigned int i;
+	int ret;
 
 	regs = kcalloc(variant->num_registers, sizeof(*regs),
 		       GFP_KERNEL);
@@ -2688,6 +2697,10 @@ static int vc4_hdmi_build_regset(struct vc4_hdmi *vc4_hdmi,
 	regset->regs = new_regs;
 	regset->nregs = count;
 
+	ret = drmm_add_action_or_reset(drm, vc4_hdmi_free_regset, new_regs);
+	if (ret)
+		return ret;
+
 	return 0;
 }
 
@@ -3030,9 +3043,6 @@ static void vc4_hdmi_unbind(struct device *dev, struct device *master,
 	BUILD_BUG_ON(offsetof(struct vc4_hdmi, audio) != 0);
 	vc4_hdmi = dev_get_drvdata(dev);
 
-	kfree(vc4_hdmi->hdmi_regset.regs);
-	kfree(vc4_hdmi->hd_regset.regs);
-
 	vc4_hdmi_hotplug_exit(vc4_hdmi);
 
 	pm_runtime_disable(dev);
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 41/64] drm/vc4: hdmi: Use devm to register hotplug interrupts
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (39 preceding siblings ...)
  2022-06-10  9:29 ` [PATCH 40/64] drm/vc4: hdmi: Switch to DRM-managed kfree to build regsets Maxime Ripard
@ 2022-06-10  9:29 ` Maxime Ripard
  2022-06-10  9:29 ` [PATCH 42/64] drm/vc4: hdmi: Move audio structure offset checks Maxime Ripard
                   ` (22 subsequent siblings)
  63 siblings, 0 replies; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:29 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

Commit 776efe800fed ("drm/vc4: hdmi: Drop devm interrupt handler for
hotplug interrupts") dropped the device-managed interrupt registration
because it was creating bugs and races whenever an interrupt was coming in
while the device was removed.

However, our latest patches to the HDMI controller driver fix this as well,
so we can use device-managed interrupt handlers again.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_hdmi.c | 41 +++++++++-------------------------
 1 file changed, 11 insertions(+), 30 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index ecc898684c4b..ca0bc8be3e6a 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -2181,21 +2181,19 @@ static int vc4_hdmi_hotplug_init(struct vc4_hdmi *vc4_hdmi)
 		unsigned int hpd_con = platform_get_irq_byname(pdev, "hpd-connected");
 		unsigned int hpd_rm = platform_get_irq_byname(pdev, "hpd-removed");
 
-		ret = request_threaded_irq(hpd_con,
-					   NULL,
-					   vc4_hdmi_hpd_irq_thread, IRQF_ONESHOT,
-					   "vc4 hdmi hpd connected", vc4_hdmi);
+		ret = devm_request_threaded_irq(&pdev->dev, hpd_con,
+						NULL,
+						vc4_hdmi_hpd_irq_thread, IRQF_ONESHOT,
+						"vc4 hdmi hpd connected", vc4_hdmi);
 		if (ret)
 			return ret;
 
-		ret = request_threaded_irq(hpd_rm,
-					   NULL,
-					   vc4_hdmi_hpd_irq_thread, IRQF_ONESHOT,
-					   "vc4 hdmi hpd disconnected", vc4_hdmi);
-		if (ret) {
-			free_irq(hpd_con, vc4_hdmi);
+		ret = devm_request_threaded_irq(&pdev->dev, hpd_rm,
+						NULL,
+						vc4_hdmi_hpd_irq_thread, IRQF_ONESHOT,
+						"vc4 hdmi hpd disconnected", vc4_hdmi);
+		if (ret)
 			return ret;
-		}
 
 		connector->polled = DRM_CONNECTOR_POLL_HPD;
 	}
@@ -2203,16 +2201,6 @@ static int vc4_hdmi_hotplug_init(struct vc4_hdmi *vc4_hdmi)
 	return 0;
 }
 
-static void vc4_hdmi_hotplug_exit(struct vc4_hdmi *vc4_hdmi)
-{
-	struct platform_device *pdev = vc4_hdmi->pdev;
-
-	if (vc4_hdmi->variant->external_irq_controller) {
-		free_irq(platform_get_irq_byname(pdev, "hpd-connected"), vc4_hdmi);
-		free_irq(platform_get_irq_byname(pdev, "hpd-removed"), vc4_hdmi);
-	}
-}
-
 #ifdef CONFIG_DRM_VC4_HDMI_CEC
 static irqreturn_t vc4_cec_irq_handler_rx_thread(int irq, void *priv)
 {
@@ -2994,11 +2982,11 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
 
 	ret = vc4_hdmi_cec_init(vc4_hdmi);
 	if (ret)
-		goto err_free_hotplug;
+		goto err_put_runtime_pm;
 
 	ret = vc4_hdmi_audio_init(vc4_hdmi);
 	if (ret)
-		goto err_free_hotplug;
+		goto err_put_runtime_pm;
 
 	vc4_debugfs_add_file(drm, variant->debugfs_name,
 			     vc4_hdmi_debugfs_regs,
@@ -3008,8 +2996,6 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
 
 	return 0;
 
-err_free_hotplug:
-	vc4_hdmi_hotplug_exit(vc4_hdmi);
 err_put_runtime_pm:
 	pm_runtime_put_sync(dev);
 	pm_runtime_disable(dev);
@@ -3020,8 +3006,6 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
 static void vc4_hdmi_unbind(struct device *dev, struct device *master,
 			    void *data)
 {
-	struct vc4_hdmi *vc4_hdmi;
-
 	/*
 	 * ASoC makes it a bit hard to retrieve a pointer to the
 	 * vc4_hdmi structure. Registering the card will overwrite our
@@ -3041,9 +3025,6 @@ static void vc4_hdmi_unbind(struct device *dev, struct device *master,
 	 */
 	BUILD_BUG_ON(offsetof(struct vc4_hdmi_audio, card) != 0);
 	BUILD_BUG_ON(offsetof(struct vc4_hdmi, audio) != 0);
-	vc4_hdmi = dev_get_drvdata(dev);
-
-	vc4_hdmi_hotplug_exit(vc4_hdmi);
 
 	pm_runtime_disable(dev);
 }
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 42/64] drm/vc4: hdmi: Move audio structure offset checks
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (40 preceding siblings ...)
  2022-06-10  9:29 ` [PATCH 41/64] drm/vc4: hdmi: Use devm to register hotplug interrupts Maxime Ripard
@ 2022-06-10  9:29 ` Maxime Ripard
  2022-06-10  9:29 ` [PATCH 43/64] drm/vc4: hdmi: Protect device resources after removal Maxime Ripard
                   ` (21 subsequent siblings)
  63 siblings, 0 replies; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:29 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

The HDMI driver unbind hook doesn't have any ALSA-related code anymore, so
let's move the ALSA sanity checks and comments we have to some other part
of the driver dedicated to ALSA.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_hdmi.c | 40 +++++++++++++++++-----------------
 1 file changed, 20 insertions(+), 20 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index ca0bc8be3e6a..814517c1fdaa 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -2041,6 +2041,26 @@ static int vc4_hdmi_audio_init(struct vc4_hdmi *vc4_hdmi)
 	int index;
 	int ret;
 
+	/*
+	 * ASoC makes it a bit hard to retrieve a pointer to the
+	 * vc4_hdmi structure. Registering the card will overwrite our
+	 * device drvdata with a pointer to the snd_soc_card structure,
+	 * which can then be used to retrieve whatever drvdata we want
+	 * to associate.
+	 *
+	 * However, that doesn't fly in the case where we wouldn't
+	 * register an ASoC card (because of an old DT that is missing
+	 * the dmas properties for example), then the card isn't
+	 * registered and the device drvdata wouldn't be set.
+	 *
+	 * We can deal with both cases by making sure a snd_soc_card
+	 * pointer and a vc4_hdmi structure are pointing to the same
+	 * memory address, so we can treat them indistinctly without any
+	 * issue.
+	 */
+	BUILD_BUG_ON(offsetof(struct vc4_hdmi_audio, card) != 0);
+	BUILD_BUG_ON(offsetof(struct vc4_hdmi, audio) != 0);
+
 	if (!of_find_property(dev->of_node, "dmas", NULL)) {
 		dev_warn(dev,
 			 "'dmas' DT property is missing, no HDMI audio\n");
@@ -3006,26 +3026,6 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
 static void vc4_hdmi_unbind(struct device *dev, struct device *master,
 			    void *data)
 {
-	/*
-	 * ASoC makes it a bit hard to retrieve a pointer to the
-	 * vc4_hdmi structure. Registering the card will overwrite our
-	 * device drvdata with a pointer to the snd_soc_card structure,
-	 * which can then be used to retrieve whatever drvdata we want
-	 * to associate.
-	 *
-	 * However, that doesn't fly in the case where we wouldn't
-	 * register an ASoC card (because of an old DT that is missing
-	 * the dmas properties for example), then the card isn't
-	 * registered and the device drvdata wouldn't be set.
-	 *
-	 * We can deal with both cases by making sure a snd_soc_card
-	 * pointer and a vc4_hdmi structure are pointing to the same
-	 * memory address, so we can treat them indistinctly without any
-	 * issue.
-	 */
-	BUILD_BUG_ON(offsetof(struct vc4_hdmi_audio, card) != 0);
-	BUILD_BUG_ON(offsetof(struct vc4_hdmi, audio) != 0);
-
 	pm_runtime_disable(dev);
 }
 
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 43/64] drm/vc4: hdmi: Protect device resources after removal
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (41 preceding siblings ...)
  2022-06-10  9:29 ` [PATCH 42/64] drm/vc4: hdmi: Move audio structure offset checks Maxime Ripard
@ 2022-06-10  9:29 ` Maxime Ripard
  2022-06-20 11:12   ` Thomas Zimmermann
  2022-06-10  9:29 ` [PATCH 44/64] drm/vc4: hdmi: Switch to devm_pm_runtime_enable Maxime Ripard
                   ` (20 subsequent siblings)
  63 siblings, 1 reply; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:29 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

Whenever the device and driver are unbound, the main device and all the
subdevices will be removed by calling their unbind() method.

However, the DRM device itself will only be freed when the last user will
have closed it.

It means that there is a time window where the device and its resources
aren't there anymore, but the userspace can still call into our driver.

Fortunately, the DRM framework provides the drm_dev_enter() and
drm_dev_exit() functions to make sure our underlying device is still there
for the section protected by those calls. Let's add them to the HDMI driver.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_hdmi.c | 286 +++++++++++++++++++++++++++++++--
 1 file changed, 269 insertions(+), 17 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index 814517c1fdaa..b4fd581861ea 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -34,6 +34,7 @@
 #include <drm/display/drm_hdmi_helper.h>
 #include <drm/display/drm_scdc_helper.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_drv.h>
 #include <drm/drm_probe_helper.h>
 #include <drm/drm_simple_kms_helper.h>
 #include <linux/clk.h>
@@ -140,17 +141,33 @@ static int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused)
 {
 	struct drm_info_node *node = (struct drm_info_node *)m->private;
 	struct vc4_hdmi *vc4_hdmi = node->info_ent->data;
+	struct drm_device *drm = vc4_hdmi->connector.dev;
 	struct drm_printer p = drm_seq_file_printer(m);
+	int idx;
+
+	if (!drm_dev_enter(drm, &idx))
+		return -ENODEV;
 
 	drm_print_regset32(&p, &vc4_hdmi->hdmi_regset);
 	drm_print_regset32(&p, &vc4_hdmi->hd_regset);
 
+	drm_dev_exit(idx);
+
 	return 0;
 }
 
 static void vc4_hdmi_reset(struct vc4_hdmi *vc4_hdmi)
 {
+	struct drm_device *drm = vc4_hdmi->connector.dev;
 	unsigned long flags;
+	int idx;
+
+	/*
+	 * We can be called by our bind callback, when the
+	 * connector->dev pointer might not be initialised yet.
+	 */
+	if (drm && !drm_dev_enter(drm, &idx))
+		return;
 
 	spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
 
@@ -167,11 +184,23 @@ static void vc4_hdmi_reset(struct vc4_hdmi *vc4_hdmi)
 	HDMI_WRITE(HDMI_SW_RESET_CONTROL, 0);
 
 	spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
+
+	if (drm)
+		drm_dev_exit(idx);
 }
 
 static void vc5_hdmi_reset(struct vc4_hdmi *vc4_hdmi)
 {
+	struct drm_device *drm = vc4_hdmi->connector.dev;
 	unsigned long flags;
+	int idx;
+
+	/*
+	 * We can be called by our bind callback, when the
+	 * connector->dev pointer might not be initialised yet.
+	 */
+	if (drm && !drm_dev_enter(drm, &idx))
+		return;
 
 	reset_control_reset(vc4_hdmi->reset);
 
@@ -183,15 +212,25 @@ static void vc5_hdmi_reset(struct vc4_hdmi *vc4_hdmi)
 		   HDMI_READ(HDMI_CLOCK_STOP) | VC4_DVP_HT_CLOCK_STOP_PIXEL);
 
 	spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
+
+	if (drm)
+		drm_dev_exit(idx);
 }
 
 #ifdef CONFIG_DRM_VC4_HDMI_CEC
 static void vc4_hdmi_cec_update_clk_div(struct vc4_hdmi *vc4_hdmi)
 {
-	unsigned long cec_rate = clk_get_rate(vc4_hdmi->cec_clock);
+	struct drm_device *drm = vc4_hdmi->connector.dev;
+	unsigned long cec_rate;
 	unsigned long flags;
 	u16 clk_cnt;
 	u32 value;
+	int idx;
+
+	if (!drm_dev_enter(drm, &idx))
+		return;
+
+	cec_rate = clk_get_rate(vc4_hdmi->cec_clock);
 
 	spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
 
@@ -207,6 +246,8 @@ static void vc4_hdmi_cec_update_clk_div(struct vc4_hdmi *vc4_hdmi)
 	HDMI_WRITE(HDMI_CEC_CNTRL_1, value);
 
 	spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
+
+	drm_dev_exit(idx);
 }
 #else
 static void vc4_hdmi_cec_update_clk_div(struct vc4_hdmi *vc4_hdmi) {}
@@ -427,25 +468,34 @@ static int vc4_hdmi_stop_packet(struct drm_encoder *encoder,
 				bool poll)
 {
 	struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
+	struct drm_device *drm = vc4_hdmi->connector.dev;
 	u32 packet_id = type - 0x80;
 	unsigned long flags;
+	int ret = 0;
+	int idx;
+
+	if (!drm_dev_enter(drm, &idx))
+		return -ENODEV;
 
 	spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
 	HDMI_WRITE(HDMI_RAM_PACKET_CONFIG,
 		   HDMI_READ(HDMI_RAM_PACKET_CONFIG) & ~BIT(packet_id));
 	spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
 
-	if (!poll)
-		return 0;
+	if (poll) {
+		ret = wait_for(!(HDMI_READ(HDMI_RAM_PACKET_STATUS) &
+				 BIT(packet_id)), 100);
+	}
 
-	return wait_for(!(HDMI_READ(HDMI_RAM_PACKET_STATUS) &
-			  BIT(packet_id)), 100);
+	drm_dev_exit(idx);
+	return ret;
 }
 
 static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder,
 				     union hdmi_infoframe *frame)
 {
 	struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
+	struct drm_device *drm = vc4_hdmi->connector.dev;
 	u32 packet_id = frame->any.type - 0x80;
 	const struct vc4_hdmi_register *ram_packet_start =
 		&vc4_hdmi->variant->registers[HDMI_RAM_PACKET_START];
@@ -456,6 +506,10 @@ static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder,
 	unsigned long flags;
 	ssize_t len, i;
 	int ret;
+	int idx;
+
+	if (!drm_dev_enter(drm, &idx))
+		return;
 
 	WARN_ONCE(!(HDMI_READ(HDMI_RAM_PACKET_CONFIG) &
 		    VC4_HDMI_RAM_PACKET_ENABLE),
@@ -463,12 +517,12 @@ static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder,
 
 	len = hdmi_infoframe_pack(frame, buffer, sizeof(buffer));
 	if (len < 0)
-		return;
+		goto out;
 
 	ret = vc4_hdmi_stop_packet(encoder, frame->any.type, true);
 	if (ret) {
 		DRM_ERROR("Failed to wait for infoframe to go idle: %d\n", ret);
-		return;
+		goto out;
 	}
 
 	spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
@@ -497,6 +551,9 @@ static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder,
 			BIT(packet_id)), 100);
 	if (ret)
 		DRM_ERROR("Failed to wait for infoframe to start: %d\n", ret);
+
+out:
+	drm_dev_exit(idx);
 }
 
 static void vc4_hdmi_avi_infoframe_colorspace(struct hdmi_avi_infoframe *frame,
@@ -644,8 +701,10 @@ static bool vc4_hdmi_supports_scrambling(struct drm_encoder *encoder,
 static void vc4_hdmi_enable_scrambling(struct drm_encoder *encoder)
 {
 	struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
+	struct drm_device *drm = vc4_hdmi->connector.dev;
 	struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode;
 	unsigned long flags;
+	int idx;
 
 	lockdep_assert_held(&vc4_hdmi->mutex);
 
@@ -657,6 +716,9 @@ static void vc4_hdmi_enable_scrambling(struct drm_encoder *encoder)
 					    vc4_hdmi->output_format))
 		return;
 
+	if (!drm_dev_enter(drm, &idx))
+		return;
+
 	drm_scdc_set_high_tmds_clock_ratio(vc4_hdmi->ddc, true);
 	drm_scdc_set_scrambling(vc4_hdmi->ddc, true);
 
@@ -665,6 +727,8 @@ static void vc4_hdmi_enable_scrambling(struct drm_encoder *encoder)
 		   VC5_HDMI_SCRAMBLER_CTL_ENABLE);
 	spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
 
+	drm_dev_exit(idx);
+
 	vc4_hdmi->scdc_enabled = true;
 
 	queue_delayed_work(system_wq, &vc4_hdmi->scrambling_work,
@@ -674,7 +738,9 @@ static void vc4_hdmi_enable_scrambling(struct drm_encoder *encoder)
 static void vc4_hdmi_disable_scrambling(struct drm_encoder *encoder)
 {
 	struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
+	struct drm_device *drm = vc4_hdmi->connector.dev;
 	unsigned long flags;
+	int idx;
 
 	lockdep_assert_held(&vc4_hdmi->mutex);
 
@@ -686,6 +752,9 @@ static void vc4_hdmi_disable_scrambling(struct drm_encoder *encoder)
 	if (delayed_work_pending(&vc4_hdmi->scrambling_work))
 		cancel_delayed_work_sync(&vc4_hdmi->scrambling_work);
 
+	if (!drm_dev_enter(drm, &idx))
+		return;
+
 	spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
 	HDMI_WRITE(HDMI_SCRAMBLER_CTL, HDMI_READ(HDMI_SCRAMBLER_CTL) &
 		   ~VC5_HDMI_SCRAMBLER_CTL_ENABLE);
@@ -693,6 +762,8 @@ static void vc4_hdmi_disable_scrambling(struct drm_encoder *encoder)
 
 	drm_scdc_set_scrambling(vc4_hdmi->ddc, false);
 	drm_scdc_set_high_tmds_clock_ratio(vc4_hdmi->ddc, false);
+
+	drm_dev_exit(idx);
 }
 
 static void vc4_hdmi_scrambling_wq(struct work_struct *work)
@@ -715,10 +786,15 @@ static void vc4_hdmi_encoder_post_crtc_disable(struct drm_encoder *encoder,
 					       struct drm_atomic_state *state)
 {
 	struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
+	struct drm_device *drm = vc4_hdmi->connector.dev;
 	unsigned long flags;
+	int idx;
 
 	mutex_lock(&vc4_hdmi->mutex);
 
+	if (!drm_dev_enter(drm, &idx))
+		goto out;
+
 	spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
 
 	HDMI_WRITE(HDMI_RAM_PACKET_CONFIG, 0);
@@ -736,6 +812,9 @@ static void vc4_hdmi_encoder_post_crtc_disable(struct drm_encoder *encoder,
 
 	vc4_hdmi_disable_scrambling(encoder);
 
+	drm_dev_exit(idx);
+
+out:
 	mutex_unlock(&vc4_hdmi->mutex);
 }
 
@@ -743,11 +822,16 @@ static void vc4_hdmi_encoder_post_crtc_powerdown(struct drm_encoder *encoder,
 						 struct drm_atomic_state *state)
 {
 	struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
+	struct drm_device *drm = vc4_hdmi->connector.dev;
 	unsigned long flags;
 	int ret;
+	int idx;
 
 	mutex_lock(&vc4_hdmi->mutex);
 
+	if (!drm_dev_enter(drm, &idx))
+		goto out;
+
 	spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
 	HDMI_WRITE(HDMI_VID_CTL,
 		   HDMI_READ(HDMI_VID_CTL) | VC4_HD_VID_CTL_BLANKPIX);
@@ -763,6 +847,9 @@ static void vc4_hdmi_encoder_post_crtc_powerdown(struct drm_encoder *encoder,
 	if (ret < 0)
 		DRM_ERROR("Failed to release power domain: %d\n", ret);
 
+	drm_dev_exit(idx);
+
+out:
 	mutex_unlock(&vc4_hdmi->mutex);
 }
 
@@ -779,8 +866,13 @@ static void vc4_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi,
 			       struct drm_connector_state *state,
 			       const struct drm_display_mode *mode)
 {
+	struct drm_device *drm = vc4_hdmi->connector.dev;
 	unsigned long flags;
 	u32 csc_ctl;
+	int idx;
+
+	if (!drm_dev_enter(drm, &idx))
+		return;
 
 	spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
 
@@ -815,6 +907,8 @@ static void vc4_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi,
 	HDMI_WRITE(HDMI_CSC_CTL, csc_ctl);
 
 	spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
+
+	drm_dev_exit(idx);
 }
 
 /*
@@ -899,6 +993,7 @@ static void vc5_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi,
 			       struct drm_connector_state *state,
 			       const struct drm_display_mode *mode)
 {
+	struct drm_device *drm = vc4_hdmi->connector.dev;
 	struct vc4_hdmi_connector_state *vc4_state =
 		conn_state_to_vc4_hdmi_conn_state(state);
 	unsigned long flags;
@@ -907,6 +1002,10 @@ static void vc5_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi,
 	u32 csc_chan_ctl = 0;
 	u32 csc_ctl = VC5_MT_CP_CSC_CTL_ENABLE | VC4_SET_FIELD(VC4_HD_CSC_CTL_MODE_CUSTOM,
 							       VC5_MT_CP_CSC_CTL_MODE);
+	int idx;
+
+	if (!drm_dev_enter(drm, &idx))
+		return;
 
 	spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
 
@@ -949,12 +1048,15 @@ static void vc5_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi,
 	HDMI_WRITE(HDMI_CSC_CTL, csc_ctl);
 
 	spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
+
+	drm_dev_exit(idx);
 }
 
 static void vc4_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi,
 				 struct drm_connector_state *state,
 				 struct drm_display_mode *mode)
 {
+	struct drm_device *drm = vc4_hdmi->connector.dev;
 	bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC;
 	bool vsync_pos = mode->flags & DRM_MODE_FLAG_PVSYNC;
 	bool interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE;
@@ -973,6 +1075,10 @@ static void vc4_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi,
 					interlaced,
 					VC4_HDMI_VERTB_VBP));
 	unsigned long flags;
+	int idx;
+
+	if (!drm_dev_enter(drm, &idx))
+		return;
 
 	spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
 
@@ -1000,12 +1106,15 @@ static void vc4_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi,
 	HDMI_WRITE(HDMI_VERTB1, vertb);
 
 	spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
+
+	drm_dev_exit(idx);
 }
 
 static void vc5_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi,
 				 struct drm_connector_state *state,
 				 struct drm_display_mode *mode)
 {
+	struct drm_device *drm = vc4_hdmi->connector.dev;
 	const struct vc4_hdmi_connector_state *vc4_state =
 		conn_state_to_vc4_hdmi_conn_state(state);
 	bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC;
@@ -1029,6 +1138,10 @@ static void vc5_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi,
 	unsigned char gcp;
 	bool gcp_en;
 	u32 reg;
+	int idx;
+
+	if (!drm_dev_enter(drm, &idx))
+		return;
 
 	spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
 
@@ -1100,13 +1213,20 @@ static void vc5_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi,
 	HDMI_WRITE(HDMI_CLOCK_STOP, 0);
 
 	spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
+
+	drm_dev_exit(idx);
 }
 
 static void vc4_hdmi_recenter_fifo(struct vc4_hdmi *vc4_hdmi)
 {
+	struct drm_device *drm = vc4_hdmi->connector.dev;
 	unsigned long flags;
 	u32 drift;
 	int ret;
+	int idx;
+
+	if (!drm_dev_enter(drm, &idx))
+		return;
 
 	spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
 
@@ -1135,12 +1255,15 @@ static void vc4_hdmi_recenter_fifo(struct vc4_hdmi *vc4_hdmi)
 		       VC4_HDMI_FIFO_CTL_RECENTER_DONE, 1);
 	WARN_ONCE(ret, "Timeout waiting for "
 		  "VC4_HDMI_FIFO_CTL_RECENTER_DONE");
+
+	drm_dev_exit(idx);
 }
 
 static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder,
 						struct drm_atomic_state *state)
 {
 	struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
+	struct drm_device *drm = vc4_hdmi->connector.dev;
 	struct drm_connector *connector = &vc4_hdmi->connector;
 	struct drm_connector_state *conn_state =
 		drm_atomic_get_new_connector_state(state, connector);
@@ -1151,9 +1274,13 @@ static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder,
 	unsigned long bvb_rate, hsm_rate;
 	unsigned long flags;
 	int ret;
+	int idx;
 
 	mutex_lock(&vc4_hdmi->mutex);
 
+	if (!drm_dev_enter(drm, &idx))
+		goto out;
+
 	/*
 	 * As stated in RPi's vc4 firmware "HDMI state machine (HSM) clock must
 	 * be faster than pixel clock, infinitesimally faster, tested in
@@ -1174,13 +1301,13 @@ static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder,
 	ret = clk_set_min_rate(vc4_hdmi->hsm_clock, hsm_rate);
 	if (ret) {
 		DRM_ERROR("Failed to set HSM clock rate: %d\n", ret);
-		goto out;
+		goto err_dev_exit;
 	}
 
 	ret = pm_runtime_resume_and_get(&vc4_hdmi->pdev->dev);
 	if (ret < 0) {
 		DRM_ERROR("Failed to retain power domain: %d\n", ret);
-		goto out;
+		goto err_dev_exit;
 	}
 
 	ret = clk_set_rate(vc4_hdmi->pixel_clock, tmds_char_rate);
@@ -1232,6 +1359,8 @@ static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder,
 	if (vc4_hdmi->variant->set_timings)
 		vc4_hdmi->variant->set_timings(vc4_hdmi, conn_state, mode);
 
+	drm_dev_exit(idx);
+
 	mutex_unlock(&vc4_hdmi->mutex);
 
 	return;
@@ -1240,6 +1369,8 @@ static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder,
 	clk_disable_unprepare(vc4_hdmi->pixel_clock);
 err_put_runtime_pm:
 	pm_runtime_put(&vc4_hdmi->pdev->dev);
+err_dev_exit:
+	drm_dev_exit(idx);
 out:
 	mutex_unlock(&vc4_hdmi->mutex);
 	return;
@@ -1249,14 +1380,19 @@ static void vc4_hdmi_encoder_pre_crtc_enable(struct drm_encoder *encoder,
 					     struct drm_atomic_state *state)
 {
 	struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
+	struct drm_device *drm = vc4_hdmi->connector.dev;
 	struct drm_connector *connector = &vc4_hdmi->connector;
 	struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode;
 	struct drm_connector_state *conn_state =
 		drm_atomic_get_new_connector_state(state, connector);
 	unsigned long flags;
+	int idx;
 
 	mutex_lock(&vc4_hdmi->mutex);
 
+	if (!drm_dev_enter(drm, &idx))
+		return;
+
 	if (vc4_hdmi->variant->csc_setup)
 		vc4_hdmi->variant->csc_setup(vc4_hdmi, conn_state, mode);
 
@@ -1264,6 +1400,8 @@ static void vc4_hdmi_encoder_pre_crtc_enable(struct drm_encoder *encoder,
 	HDMI_WRITE(HDMI_FIFO_CTL, VC4_HDMI_FIFO_CTL_MASTER_SLAVE_N);
 	spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
 
+	drm_dev_exit(idx);
+
 	mutex_unlock(&vc4_hdmi->mutex);
 }
 
@@ -1271,15 +1409,20 @@ static void vc4_hdmi_encoder_post_crtc_enable(struct drm_encoder *encoder,
 					      struct drm_atomic_state *state)
 {
 	struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
+	struct drm_device *drm = vc4_hdmi->connector.dev;
 	struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode;
 	struct drm_display_info *display = &vc4_hdmi->connector.display_info;
 	bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC;
 	bool vsync_pos = mode->flags & DRM_MODE_FLAG_PVSYNC;
 	unsigned long flags;
 	int ret;
+	int idx;
 
 	mutex_lock(&vc4_hdmi->mutex);
 
+	if (!drm_dev_enter(drm, &idx))
+		return;
+
 	spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
 
 	HDMI_WRITE(HDMI_VID_CTL,
@@ -1340,6 +1483,7 @@ static void vc4_hdmi_encoder_post_crtc_enable(struct drm_encoder *encoder,
 	vc4_hdmi_recenter_fifo(vc4_hdmi);
 	vc4_hdmi_enable_scrambling(encoder);
 
+	drm_dev_exit(idx);
 	mutex_unlock(&vc4_hdmi->mutex);
 }
 
@@ -1675,13 +1819,20 @@ static u32 vc5_hdmi_channel_map(struct vc4_hdmi *vc4_hdmi, u32 channel_mask)
 
 static bool vc5_hdmi_hp_detect(struct vc4_hdmi *vc4_hdmi)
 {
+	struct drm_device *drm = vc4_hdmi->connector.dev;
 	unsigned long flags;
 	u32 hotplug;
+	int idx;
+
+	if (!drm_dev_enter(drm, &idx))
+		return false;
 
 	spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
 	hotplug = HDMI_READ(HDMI_HOTPLUG);
 	spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
 
+	drm_dev_exit(idx);
+
 	return !!(hotplug & VC4_HDMI_HOTPLUG_CONNECTED);
 }
 
@@ -1689,10 +1840,16 @@ static bool vc5_hdmi_hp_detect(struct vc4_hdmi *vc4_hdmi)
 static void vc4_hdmi_audio_set_mai_clock(struct vc4_hdmi *vc4_hdmi,
 					 unsigned int samplerate)
 {
-	u32 hsm_clock = clk_get_rate(vc4_hdmi->audio_clock);
+	struct drm_device *drm = vc4_hdmi->connector.dev;
+	u32 hsm_clock;
 	unsigned long flags;
 	unsigned long n, m;
+	int idx;
 
+	if (!drm_dev_enter(drm, &idx))
+		return;
+
+	hsm_clock = clk_get_rate(vc4_hdmi->audio_clock);
 	rational_best_approximation(hsm_clock, samplerate,
 				    VC4_HD_MAI_SMP_N_MASK >>
 				    VC4_HD_MAI_SMP_N_SHIFT,
@@ -1705,6 +1862,8 @@ static void vc4_hdmi_audio_set_mai_clock(struct vc4_hdmi *vc4_hdmi,
 		   VC4_SET_FIELD(n, VC4_HD_MAI_SMP_N) |
 		   VC4_SET_FIELD(m - 1, VC4_HD_MAI_SMP_M));
 	spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
+
+	drm_dev_exit(idx);
 }
 
 static void vc4_hdmi_set_n_cts(struct vc4_hdmi *vc4_hdmi, unsigned int samplerate)
@@ -1764,13 +1923,21 @@ static bool vc4_hdmi_audio_can_stream(struct vc4_hdmi *vc4_hdmi)
 static int vc4_hdmi_audio_startup(struct device *dev, void *data)
 {
 	struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev);
+	struct drm_device *drm = vc4_hdmi->connector.dev;
 	unsigned long flags;
+	int ret = 0;
+	int idx;
 
 	mutex_lock(&vc4_hdmi->mutex);
 
+	if (!drm_dev_enter(drm, &idx)) {
+		ret = -ENODEV;
+		goto out;
+	}
+
 	if (!vc4_hdmi_audio_can_stream(vc4_hdmi)) {
-		mutex_unlock(&vc4_hdmi->mutex);
-		return -ENODEV;
+		ret = -ENODEV;
+		goto out_dev_exit;
 	}
 
 	vc4_hdmi->audio.streaming = true;
@@ -1787,9 +1954,12 @@ static int vc4_hdmi_audio_startup(struct device *dev, void *data)
 	if (vc4_hdmi->variant->phy_rng_enable)
 		vc4_hdmi->variant->phy_rng_enable(vc4_hdmi);
 
+out_dev_exit:
+	drm_dev_exit(idx);
+out:
 	mutex_unlock(&vc4_hdmi->mutex);
 
-	return 0;
+	return ret;
 }
 
 static void vc4_hdmi_audio_reset(struct vc4_hdmi *vc4_hdmi)
@@ -1818,10 +1988,15 @@ static void vc4_hdmi_audio_reset(struct vc4_hdmi *vc4_hdmi)
 static void vc4_hdmi_audio_shutdown(struct device *dev, void *data)
 {
 	struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev);
+	struct drm_device *drm = vc4_hdmi->connector.dev;
 	unsigned long flags;
+	int idx;
 
 	mutex_lock(&vc4_hdmi->mutex);
 
+	if (!drm_dev_enter(drm, &idx))
+		goto out;
+
 	spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
 
 	HDMI_WRITE(HDMI_MAI_CTL,
@@ -1837,6 +2012,9 @@ static void vc4_hdmi_audio_shutdown(struct device *dev, void *data)
 	vc4_hdmi->audio.streaming = false;
 	vc4_hdmi_audio_reset(vc4_hdmi);
 
+	drm_dev_exit(idx);
+
+out:
 	mutex_unlock(&vc4_hdmi->mutex);
 }
 
@@ -1884,6 +2062,7 @@ static int vc4_hdmi_audio_prepare(struct device *dev, void *data,
 				  struct hdmi_codec_params *params)
 {
 	struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev);
+	struct drm_device *drm = vc4_hdmi->connector.dev;
 	struct drm_encoder *encoder = &vc4_hdmi->encoder.base;
 	unsigned int sample_rate = params->sample_rate;
 	unsigned int channels = params->channels;
@@ -1892,15 +2071,22 @@ static int vc4_hdmi_audio_prepare(struct device *dev, void *data,
 	u32 channel_map;
 	u32 mai_audio_format;
 	u32 mai_sample_rate;
+	int ret = 0;
+	int idx;
 
 	dev_dbg(dev, "%s: %u Hz, %d bit, %d channels\n", __func__,
 		sample_rate, params->sample_width, channels);
 
 	mutex_lock(&vc4_hdmi->mutex);
 
+	if (!drm_dev_enter(drm, &idx)) {
+		ret = -ENODEV;
+		goto out;
+	}
+
 	if (!vc4_hdmi_audio_can_stream(vc4_hdmi)) {
-		mutex_unlock(&vc4_hdmi->mutex);
-		return -EINVAL;
+		ret = -EINVAL;
+		goto out_dev_exit;
 	}
 
 	vc4_hdmi_audio_set_mai_clock(vc4_hdmi, sample_rate);
@@ -1957,9 +2143,12 @@ static int vc4_hdmi_audio_prepare(struct device *dev, void *data,
 	memcpy(&vc4_hdmi->audio.infoframe, &params->cea, sizeof(params->cea));
 	vc4_hdmi_set_audio_infoframe(encoder);
 
+out_dev_exit:
+	drm_dev_exit(idx);
+out:
 	mutex_unlock(&vc4_hdmi->mutex);
 
-	return 0;
+	return ret;
 }
 
 static const struct snd_soc_component_driver vc4_hdmi_audio_cpu_dai_comp = {
@@ -2294,6 +2483,17 @@ static irqreturn_t vc4_cec_irq_handler_tx_bare_locked(struct vc4_hdmi *vc4_hdmi)
 {
 	u32 cntrl1;
 
+	/*
+	 * We don't need to protect the register access using
+	 * drm_dev_enter() there because the interrupt handler lifetime
+	 * is tied to the device itself, and not to the DRM device.
+	 *
+	 * So when the device will be gone, one of the first thing we
+	 * will be doing will be to unregister the interrupt handler,
+	 * and then unregister the DRM device. drm_dev_enter() would
+	 * thus always succeed if we are here.
+	 */
+
 	lockdep_assert_held(&vc4_hdmi->hw_lock);
 
 	cntrl1 = HDMI_READ(HDMI_CEC_CNTRL_1);
@@ -2322,6 +2522,17 @@ static irqreturn_t vc4_cec_irq_handler_rx_bare_locked(struct vc4_hdmi *vc4_hdmi)
 
 	lockdep_assert_held(&vc4_hdmi->hw_lock);
 
+	/*
+	 * We don't need to protect the register access using
+	 * drm_dev_enter() there because the interrupt handler lifetime
+	 * is tied to the device itself, and not to the DRM device.
+	 *
+	 * So when the device will be gone, one of the first thing we
+	 * will be doing will be to unregister the interrupt handler,
+	 * and then unregister the DRM device. drm_dev_enter() would
+	 * thus always succeed if we are here.
+	 */
+
 	vc4_hdmi->cec_rx_msg.len = 0;
 	cntrl1 = HDMI_READ(HDMI_CEC_CNTRL_1);
 	vc4_cec_read_msg(vc4_hdmi, cntrl1);
@@ -2353,6 +2564,17 @@ static irqreturn_t vc4_cec_irq_handler(int irq, void *priv)
 	irqreturn_t ret;
 	u32 cntrl5;
 
+	/*
+	 * We don't need to protect the register access using
+	 * drm_dev_enter() there because the interrupt handler lifetime
+	 * is tied to the device itself, and not to the DRM device.
+	 *
+	 * So when the device will be gone, one of the first thing we
+	 * will be doing will be to unregister the interrupt handler,
+	 * and then unregister the DRM device. drm_dev_enter() would
+	 * thus always succeed if we are here.
+	 */
+
 	if (!(stat & VC4_HDMI_CPU_CEC))
 		return IRQ_NONE;
 
@@ -2373,11 +2595,13 @@ static irqreturn_t vc4_cec_irq_handler(int irq, void *priv)
 static int vc4_hdmi_cec_enable(struct cec_adapter *adap)
 {
 	struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap);
+	struct drm_device *drm = vc4_hdmi->connector.dev;
 	/* clock period in microseconds */
 	const u32 usecs = 1000000 / CEC_CLOCK_FREQ;
 	unsigned long flags;
 	u32 val;
 	int ret;
+	int idx;
 
 	/*
 	 * NOTE: This function should really take vc4_hdmi->mutex, but doing so
@@ -2390,9 +2614,14 @@ static int vc4_hdmi_cec_enable(struct cec_adapter *adap)
 	 * keep it in mind if we were to change that assumption.
 	 */
 
+	if (!drm_dev_enter(drm, &idx))
+		return -ENODEV;
+
 	ret = pm_runtime_resume_and_get(&vc4_hdmi->pdev->dev);
-	if (ret)
+	if (ret) {
+		drm_dev_exit(idx);
 		return ret;
+	}
 
 	spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
 
@@ -2428,13 +2657,20 @@ static int vc4_hdmi_cec_enable(struct cec_adapter *adap)
 
 	spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
 
+	drm_dev_exit(idx);
+
 	return 0;
 }
 
 static int vc4_hdmi_cec_disable(struct cec_adapter *adap)
 {
 	struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap);
+	struct drm_device *drm = vc4_hdmi->connector.dev;
 	unsigned long flags;
+	int idx;
+
+	if (!drm_dev_enter(drm, &idx))
+		return -ENODEV;
 
 	/*
 	 * NOTE: This function should really take vc4_hdmi->mutex, but doing so
@@ -2459,6 +2695,8 @@ static int vc4_hdmi_cec_disable(struct cec_adapter *adap)
 
 	pm_runtime_put(&vc4_hdmi->pdev->dev);
 
+	drm_dev_exit(idx);
+
 	return 0;
 }
 
@@ -2473,7 +2711,9 @@ static int vc4_hdmi_cec_adap_enable(struct cec_adapter *adap, bool enable)
 static int vc4_hdmi_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr)
 {
 	struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap);
+	struct drm_device *drm = vc4_hdmi->connector.dev;
 	unsigned long flags;
+	int idx;
 
 	/*
 	 * NOTE: This function should really take vc4_hdmi->mutex, but doing so
@@ -2486,12 +2726,17 @@ static int vc4_hdmi_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr)
 	 * keep it in mind if we were to change that assumption.
 	 */
 
+	if (!drm_dev_enter(drm, &idx))
+		return -ENODEV;
+
 	spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
 	HDMI_WRITE(HDMI_CEC_CNTRL_1,
 		   (HDMI_READ(HDMI_CEC_CNTRL_1) & ~VC4_HDMI_CEC_ADDR_MASK) |
 		   (log_addr & 0xf) << VC4_HDMI_CEC_ADDR_SHIFT);
 	spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
 
+	drm_dev_exit(idx);
+
 	return 0;
 }
 
@@ -2503,6 +2748,7 @@ static int vc4_hdmi_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
 	unsigned long flags;
 	u32 val;
 	unsigned int i;
+	int idx;
 
 	/*
 	 * NOTE: This function should really take vc4_hdmi->mutex, but doing so
@@ -2515,8 +2761,12 @@ static int vc4_hdmi_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
 	 * keep it in mind if we were to change that assumption.
 	 */
 
+	if (!drm_dev_enter(dev, &idx))
+		return -ENODEV;
+
 	if (msg->len > 16) {
 		drm_err(dev, "Attempting to transmit too much data (%d)\n", msg->len);
+		drm_dev_exit(idx);
 		return -ENOMEM;
 	}
 
@@ -2540,6 +2790,8 @@ static int vc4_hdmi_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
 
 	spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
 
+	drm_dev_exit(idx);
+
 	return 0;
 }
 
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 44/64] drm/vc4: hdmi: Switch to devm_pm_runtime_enable
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (42 preceding siblings ...)
  2022-06-10  9:29 ` [PATCH 43/64] drm/vc4: hdmi: Protect device resources after removal Maxime Ripard
@ 2022-06-10  9:29 ` Maxime Ripard
  2022-06-10  9:29 ` [PATCH 45/64] drm/vc4: txp: Remove vc4_dev txp pointer Maxime Ripard
                   ` (19 subsequent siblings)
  63 siblings, 0 replies; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:29 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

devm_pm_runtime_enable() simplifies the driver a bit since it will call
pm_runtime_disable() automatically through a device-managed action.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_hdmi.c | 15 ++++++---------
 1 file changed, 6 insertions(+), 9 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index b4fd581861ea..b31487547070 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -3225,7 +3225,12 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
 
 	pm_runtime_get_noresume(dev);
 	pm_runtime_set_active(dev);
-	pm_runtime_enable(dev);
+
+	ret = devm_pm_runtime_enable(dev);
+	if (ret) {
+		vc4_hdmi_runtime_suspend(dev);
+		return ret;
+	}
 
 	if (vc4_hdmi->variant->reset)
 		vc4_hdmi->variant->reset(vc4_hdmi);
@@ -3270,20 +3275,12 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
 
 err_put_runtime_pm:
 	pm_runtime_put_sync(dev);
-	pm_runtime_disable(dev);
 
 	return ret;
 }
 
-static void vc4_hdmi_unbind(struct device *dev, struct device *master,
-			    void *data)
-{
-	pm_runtime_disable(dev);
-}
-
 static const struct component_ops vc4_hdmi_ops = {
 	.bind   = vc4_hdmi_bind,
-	.unbind = vc4_hdmi_unbind,
 };
 
 static int vc4_hdmi_dev_probe(struct platform_device *pdev)
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 45/64] drm/vc4: txp: Remove vc4_dev txp pointer
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (43 preceding siblings ...)
  2022-06-10  9:29 ` [PATCH 44/64] drm/vc4: hdmi: Switch to devm_pm_runtime_enable Maxime Ripard
@ 2022-06-10  9:29 ` Maxime Ripard
  2022-06-20 11:16   ` Thomas Zimmermann
  2022-06-10  9:29 ` [PATCH 46/64] drm/vc4: txp: Remove duplicate regset Maxime Ripard
                   ` (18 subsequent siblings)
  63 siblings, 1 reply; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:29 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

There's no user for that pointer so let's just get rid of it.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_drv.h | 1 -
 drivers/gpu/drm/vc4/vc4_txp.c | 6 ------
 2 files changed, 7 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h
index 846f3cda179a..5f2d7640a09d 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.h
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
@@ -80,7 +80,6 @@ struct vc4_dev {
 	struct vc4_hvs *hvs;
 	struct vc4_v3d *v3d;
 	struct vc4_vec *vec;
-	struct vc4_txp *txp;
 
 	struct vc4_hang_state *hang_state;
 
diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c
index f306e05ac5b2..87c896f482fb 100644
--- a/drivers/gpu/drm/vc4/vc4_txp.c
+++ b/drivers/gpu/drm/vc4/vc4_txp.c
@@ -468,7 +468,6 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data)
 {
 	struct platform_device *pdev = to_platform_device(dev);
 	struct drm_device *drm = dev_get_drvdata(master);
-	struct vc4_dev *vc4 = to_vc4_dev(drm);
 	struct vc4_crtc *vc4_crtc;
 	struct vc4_txp *txp;
 	struct drm_crtc *crtc;
@@ -521,7 +520,6 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data)
 		return ret;
 
 	dev_set_drvdata(dev, txp);
-	vc4->txp = txp;
 
 	vc4_debugfs_add_regset32(drm, "txp_regs", &txp->regset);
 
@@ -531,13 +529,9 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data)
 static void vc4_txp_unbind(struct device *dev, struct device *master,
 			   void *data)
 {
-	struct drm_device *drm = dev_get_drvdata(master);
-	struct vc4_dev *vc4 = to_vc4_dev(drm);
 	struct vc4_txp *txp = dev_get_drvdata(dev);
 
 	vc4_txp_connector_destroy(&txp->connector.base);
-
-	vc4->txp = NULL;
 }
 
 static const struct component_ops vc4_txp_ops = {
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 46/64] drm/vc4: txp: Remove duplicate regset
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (44 preceding siblings ...)
  2022-06-10  9:29 ` [PATCH 45/64] drm/vc4: txp: Remove vc4_dev txp pointer Maxime Ripard
@ 2022-06-10  9:29 ` Maxime Ripard
  2022-06-20 11:16   ` Thomas Zimmermann
  2022-06-10  9:29 ` [PATCH 47/64] drm/vc4: txp: Switch to drmm_kzalloc Maxime Ripard
                   ` (17 subsequent siblings)
  63 siblings, 1 reply; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:29 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

There's already a regset in the vc4_crtc structure so there's no need to
duplicate it in vc4_txp.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_txp.c | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c
index 87c896f482fb..51ac01838093 100644
--- a/drivers/gpu/drm/vc4/vc4_txp.c
+++ b/drivers/gpu/drm/vc4/vc4_txp.c
@@ -154,7 +154,6 @@ struct vc4_txp {
 	struct drm_writeback_connector connector;
 
 	void __iomem *regs;
-	struct debugfs_regset32 regset;
 };
 
 static inline struct vc4_txp *encoder_to_vc4_txp(struct drm_encoder *encoder)
@@ -493,9 +492,9 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data)
 	txp->regs = vc4_ioremap_regs(pdev, 0);
 	if (IS_ERR(txp->regs))
 		return PTR_ERR(txp->regs);
-	txp->regset.base = txp->regs;
-	txp->regset.regs = txp_regs;
-	txp->regset.nregs = ARRAY_SIZE(txp_regs);
+	vc4_crtc->regset.base = txp->regs;
+	vc4_crtc->regset.regs = txp_regs;
+	vc4_crtc->regset.nregs = ARRAY_SIZE(txp_regs);
 
 	drm_connector_helper_add(&txp->connector.base,
 				 &vc4_txp_connector_helper_funcs);
@@ -521,7 +520,7 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data)
 
 	dev_set_drvdata(dev, txp);
 
-	vc4_debugfs_add_regset32(drm, "txp_regs", &txp->regset);
+	vc4_debugfs_add_regset32(drm, "txp_regs", &vc4_crtc->regset);
 
 	return 0;
 }
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 47/64] drm/vc4: txp: Switch to drmm_kzalloc
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (45 preceding siblings ...)
  2022-06-10  9:29 ` [PATCH 46/64] drm/vc4: txp: Remove duplicate regset Maxime Ripard
@ 2022-06-10  9:29 ` Maxime Ripard
  2022-06-20 11:17   ` Thomas Zimmermann
  2022-06-10  9:29 ` [PATCH 48/64] drm/vc4: txp: Switch to DRM-managed writeback initialization Maxime Ripard
                   ` (16 subsequent siblings)
  63 siblings, 1 reply; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:29 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

Our internal structure that stores the DRM entities structure is allocated
through a device-managed kzalloc.

This means that this will eventually be freed whenever the device is
removed. In our case, the most like source of removal is that the main
device is going to be unbound, and component_unbind_all() is being run.

However, it occurs while the DRM device is still registered, which will
create dangling pointers, eventually resulting in use-after-free.

Switch to a DRM-managed allocation to keep our structure until the DRM
driver doesn't need it anymore.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_txp.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c
index 51ac01838093..6a16b2798724 100644
--- a/drivers/gpu/drm/vc4/vc4_txp.c
+++ b/drivers/gpu/drm/vc4/vc4_txp.c
@@ -477,7 +477,7 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data)
 	if (irq < 0)
 		return irq;
 
-	txp = devm_kzalloc(dev, sizeof(*txp), GFP_KERNEL);
+	txp = drmm_kzalloc(drm, sizeof(*txp), GFP_KERNEL);
 	if (!txp)
 		return -ENOMEM;
 	vc4_crtc = &txp->base;
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 48/64] drm/vc4: txp: Switch to DRM-managed writeback initialization
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (46 preceding siblings ...)
  2022-06-10  9:29 ` [PATCH 47/64] drm/vc4: txp: Switch to drmm_kzalloc Maxime Ripard
@ 2022-06-10  9:29 ` Maxime Ripard
  2022-06-10  9:29 ` [PATCH 49/64] drm/vc4: txp: Protect device resources Maxime Ripard
                   ` (15 subsequent siblings)
  63 siblings, 0 replies; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:29 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

The current code will call drm_connector_unregister() and
drm_connector_cleanup() when the device is unbound. However, by then, there
might still be some references held to that connector, including by the
userspace that might still have the DRM device open.

Let's switch to a DRM-managed initialization to clean up after ourselves
only once the DRM device has been last closed.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_txp.c | 24 ++++--------------------
 1 file changed, 4 insertions(+), 20 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c
index 6a16b2798724..3f214b702c47 100644
--- a/drivers/gpu/drm/vc4/vc4_txp.c
+++ b/drivers/gpu/drm/vc4/vc4_txp.c
@@ -335,16 +335,9 @@ vc4_txp_connector_detect(struct drm_connector *connector, bool force)
 	return connector_status_connected;
 }
 
-static void vc4_txp_connector_destroy(struct drm_connector *connector)
-{
-	drm_connector_unregister(connector);
-	drm_connector_cleanup(connector);
-}
-
 static const struct drm_connector_funcs vc4_txp_connector_funcs = {
 	.detect = vc4_txp_connector_detect,
 	.fill_modes = drm_helper_probe_single_connector_modes,
-	.destroy = vc4_txp_connector_destroy,
 	.reset = drm_atomic_helper_connector_reset,
 	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
@@ -498,10 +491,10 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data)
 
 	drm_connector_helper_add(&txp->connector.base,
 				 &vc4_txp_connector_helper_funcs);
-	ret = drm_writeback_connector_init(drm, &txp->connector,
-					   &vc4_txp_connector_funcs,
-					   &vc4_txp_encoder_helper_funcs,
-					   drm_fmts, ARRAY_SIZE(drm_fmts));
+	ret = drmm_writeback_connector_init(drm, &txp->connector,
+					    &vc4_txp_connector_funcs,
+					    &vc4_txp_encoder_helper_funcs,
+					    drm_fmts, ARRAY_SIZE(drm_fmts));
 	if (ret)
 		return ret;
 
@@ -525,17 +518,8 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data)
 	return 0;
 }
 
-static void vc4_txp_unbind(struct device *dev, struct device *master,
-			   void *data)
-{
-	struct vc4_txp *txp = dev_get_drvdata(dev);
-
-	vc4_txp_connector_destroy(&txp->connector.base);
-}
-
 static const struct component_ops vc4_txp_ops = {
 	.bind   = vc4_txp_bind,
-	.unbind = vc4_txp_unbind,
 };
 
 static int vc4_txp_probe(struct platform_device *pdev)
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 49/64] drm/vc4: txp: Protect device resources
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (47 preceding siblings ...)
  2022-06-10  9:29 ` [PATCH 48/64] drm/vc4: txp: Switch to DRM-managed writeback initialization Maxime Ripard
@ 2022-06-10  9:29 ` Maxime Ripard
  2022-06-10  9:29 ` [PATCH 50/64] drm/vc4: vec: Remove vc4_dev vec pointer Maxime Ripard
                   ` (14 subsequent siblings)
  63 siblings, 0 replies; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:29 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

Our current code now mixes some resources whose lifetime are tied to the
device (clocks, IO mappings, etc.) and some that are tied to the DRM device
(encoder, bridge).

The device one will be freed at unbind time, but the DRM one will only be
freed when the last user of the DRM device closes its file handle.

So we end up with a time window during which we can call the encoder hooks,
but we don't have access to the underlying resources and device.

Let's protect all those sections with drm_dev_enter() and drm_dev_exit() so
that we bail out if we are during that window.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_txp.c | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c
index 3f214b702c47..fee00b7003ab 100644
--- a/drivers/gpu/drm/vc4/vc4_txp.c
+++ b/drivers/gpu/drm/vc4/vc4_txp.c
@@ -15,6 +15,7 @@
 
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_drv.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_fourcc.h>
@@ -274,6 +275,7 @@ static int vc4_txp_connector_atomic_check(struct drm_connector *conn,
 static void vc4_txp_connector_atomic_commit(struct drm_connector *conn,
 					struct drm_atomic_state *state)
 {
+	struct drm_device *drm = conn->dev;
 	struct drm_connector_state *conn_state = drm_atomic_get_new_connector_state(state,
 										    conn);
 	struct vc4_txp *txp = connector_to_vc4_txp(conn);
@@ -281,6 +283,7 @@ static void vc4_txp_connector_atomic_commit(struct drm_connector *conn,
 	struct drm_display_mode *mode;
 	struct drm_framebuffer *fb;
 	u32 ctrl;
+	int idx;
 	int i;
 
 	if (WARN_ON(!conn_state->writeback_job))
@@ -310,6 +313,9 @@ static void vc4_txp_connector_atomic_commit(struct drm_connector *conn,
 		 */
 		ctrl |= TXP_ALPHA_INVERT;
 
+	if (!drm_dev_enter(drm, &idx))
+		return;
+
 	gem = drm_fb_cma_get_gem_obj(fb, 0);
 	TXP_WRITE(TXP_DST_PTR, gem->paddr + fb->offsets[0]);
 	TXP_WRITE(TXP_DST_PITCH, fb->pitches[0]);
@@ -320,6 +326,8 @@ static void vc4_txp_connector_atomic_commit(struct drm_connector *conn,
 	TXP_WRITE(TXP_DST_CTRL, ctrl);
 
 	drm_writeback_queue_job(&txp->connector, conn_state);
+
+	drm_dev_exit(idx);
 }
 
 static const struct drm_connector_helper_funcs vc4_txp_connector_helper_funcs = {
@@ -345,7 +353,12 @@ static const struct drm_connector_funcs vc4_txp_connector_funcs = {
 
 static void vc4_txp_encoder_disable(struct drm_encoder *encoder)
 {
+	struct drm_device *drm = encoder->dev;
 	struct vc4_txp *txp = encoder_to_vc4_txp(encoder);
+	int idx;
+
+	if (!drm_dev_enter(drm, &idx))
+		return;
 
 	if (TXP_READ(TXP_DST_CTRL) & TXP_BUSY) {
 		unsigned long timeout = jiffies + msecs_to_jiffies(1000);
@@ -360,6 +373,8 @@ static void vc4_txp_encoder_disable(struct drm_encoder *encoder)
 	}
 
 	TXP_WRITE(TXP_DST_CTRL, TXP_POWERDOWN);
+
+	drm_dev_exit(idx);
 }
 
 static const struct drm_encoder_helper_funcs vc4_txp_encoder_helper_funcs = {
@@ -443,6 +458,16 @@ static irqreturn_t vc4_txp_interrupt(int irq, void *data)
 	struct vc4_txp *txp = data;
 	struct vc4_crtc *vc4_crtc = &txp->base;
 
+	/*
+	 * We don't need to protect the register access using
+	 * drm_dev_enter() there because the interrupt handler lifetime
+	 * is tied to the device itself, and not to the DRM device.
+	 *
+	 * So when the device will be gone, one of the first thing we
+	 * will be doing will be to unregister the interrupt handler,
+	 * and then unregister the DRM device. drm_dev_enter() would
+	 * thus always succeed if we are here.
+	 */
 	TXP_WRITE(TXP_DST_CTRL, TXP_READ(TXP_DST_CTRL) & ~TXP_EI);
 	vc4_crtc_handle_vblank(vc4_crtc);
 	drm_writeback_signal_completion(&txp->connector, 0);
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 50/64] drm/vc4: vec: Remove vc4_dev vec pointer
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (48 preceding siblings ...)
  2022-06-10  9:29 ` [PATCH 49/64] drm/vc4: txp: Protect device resources Maxime Ripard
@ 2022-06-10  9:29 ` Maxime Ripard
  2022-06-10  9:29 ` [PATCH 51/64] drm/vc4: vec: Embed DRM structures into the private structure Maxime Ripard
                   ` (13 subsequent siblings)
  63 siblings, 0 replies; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:29 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

There's no user for that pointer so let's just get rid of it.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_drv.h | 1 -
 drivers/gpu/drm/vc4/vc4_vec.c | 7 -------
 2 files changed, 8 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h
index 5f2d7640a09d..12ab6df30629 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.h
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
@@ -79,7 +79,6 @@ struct vc4_dev {
 
 	struct vc4_hvs *hvs;
 	struct vc4_v3d *v3d;
-	struct vc4_vec *vec;
 
 	struct vc4_hang_state *hang_state;
 
diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c
index 11fc3d6f66b1..99fe40c8cf81 100644
--- a/drivers/gpu/drm/vc4/vc4_vec.c
+++ b/drivers/gpu/drm/vc4/vc4_vec.c
@@ -532,7 +532,6 @@ static int vc4_vec_bind(struct device *dev, struct device *master, void *data)
 {
 	struct platform_device *pdev = to_platform_device(dev);
 	struct drm_device *drm = dev_get_drvdata(master);
-	struct vc4_dev *vc4 = to_vc4_dev(drm);
 	struct vc4_vec *vec;
 	struct vc4_vec_encoder *vc4_vec_encoder;
 	int ret;
@@ -585,8 +584,6 @@ static int vc4_vec_bind(struct device *dev, struct device *master, void *data)
 
 	dev_set_drvdata(dev, vec);
 
-	vc4->vec = vec;
-
 	vc4_debugfs_add_regset32(drm, "vec_regs", &vec->regset);
 
 	return 0;
@@ -601,15 +598,11 @@ static int vc4_vec_bind(struct device *dev, struct device *master, void *data)
 static void vc4_vec_unbind(struct device *dev, struct device *master,
 			   void *data)
 {
-	struct drm_device *drm = dev_get_drvdata(master);
-	struct vc4_dev *vc4 = to_vc4_dev(drm);
 	struct vc4_vec *vec = dev_get_drvdata(dev);
 
 	vc4_vec_connector_destroy(vec->connector);
 	drm_encoder_cleanup(vec->encoder);
 	pm_runtime_disable(dev);
-
-	vc4->vec = NULL;
 }
 
 static const struct component_ops vc4_vec_ops = {
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 51/64] drm/vc4: vec: Embed DRM structures into the private structure
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (49 preceding siblings ...)
  2022-06-10  9:29 ` [PATCH 50/64] drm/vc4: vec: Remove vc4_dev vec pointer Maxime Ripard
@ 2022-06-10  9:29 ` Maxime Ripard
  2022-06-10  9:29 ` [PATCH 52/64] drm/vc4: vec: Switch to drmm_kzalloc Maxime Ripard
                   ` (12 subsequent siblings)
  63 siblings, 0 replies; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:29 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

The VC4 VEC driver private structure contains only a pointer to the
encoder and connector it implements. This makes the overall structure
somewhat inconsistent with the rest of the driver, and complicates its
initialisation without any apparent gain.

Let's embed the drm_encoder structure (through the vc4_encoder one) and
drm_connector into struct vc4_vec to fix both issues.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_vec.c | 83 +++++++++--------------------------
 1 file changed, 21 insertions(+), 62 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c
index 99fe40c8cf81..2c96d5adcf49 100644
--- a/drivers/gpu/drm/vc4/vc4_vec.c
+++ b/drivers/gpu/drm/vc4/vc4_vec.c
@@ -160,12 +160,12 @@ struct vc4_vec_variant {
 
 /* General VEC hardware state. */
 struct vc4_vec {
+	struct vc4_encoder encoder;
+	struct drm_connector connector;
+
 	struct platform_device *pdev;
 	const struct vc4_vec_variant *variant;
 
-	struct drm_encoder *encoder;
-	struct drm_connector *connector;
-
 	void __iomem *regs;
 
 	struct clk *clock;
@@ -178,30 +178,12 @@ struct vc4_vec {
 #define VEC_READ(offset) readl(vec->regs + (offset))
 #define VEC_WRITE(offset, val) writel(val, vec->regs + (offset))
 
-/* VC4 VEC encoder KMS struct */
-struct vc4_vec_encoder {
-	struct vc4_encoder base;
-	struct vc4_vec *vec;
-};
-
-static inline struct vc4_vec_encoder *
-to_vc4_vec_encoder(struct drm_encoder *encoder)
+static inline struct vc4_vec *
+encoder_to_vc4_vec(struct drm_encoder *encoder)
 {
-	return container_of(encoder, struct vc4_vec_encoder, base.base);
+	return container_of(encoder, struct vc4_vec, encoder.base);
 }
 
-/* VC4 VEC connector KMS struct */
-struct vc4_vec_connector {
-	struct drm_connector base;
-	struct vc4_vec *vec;
-
-	/* Since the connector is attached to just the one encoder,
-	 * this is the reference to it so we can do the best_encoder()
-	 * hook.
-	 */
-	struct drm_encoder *encoder;
-};
-
 enum vc4_vec_tv_mode_id {
 	VC4_VEC_TV_MODE_NTSC,
 	VC4_VEC_TV_MODE_NTSC_J,
@@ -343,22 +325,12 @@ static const struct drm_connector_helper_funcs vc4_vec_connector_helper_funcs =
 	.get_modes = vc4_vec_connector_get_modes,
 };
 
-static struct drm_connector *vc4_vec_connector_init(struct drm_device *dev,
-						    struct vc4_vec *vec)
+static int vc4_vec_connector_init(struct drm_device *dev, struct vc4_vec *vec)
 {
-	struct drm_connector *connector = NULL;
-	struct vc4_vec_connector *vec_connector;
+	struct drm_connector *connector = &vec->connector;
 
-	vec_connector = devm_kzalloc(dev->dev, sizeof(*vec_connector),
-				     GFP_KERNEL);
-	if (!vec_connector)
-		return ERR_PTR(-ENOMEM);
-
-	connector = &vec_connector->base;
 	connector->interlace_allowed = true;
 
-	vec_connector->encoder = vec->encoder;
-	vec_connector->vec = vec;
 
 	drm_connector_init(dev, connector, &vc4_vec_connector_funcs,
 			   DRM_MODE_CONNECTOR_Composite);
@@ -369,15 +341,14 @@ static struct drm_connector *vc4_vec_connector_init(struct drm_device *dev,
 				   VC4_VEC_TV_MODE_NTSC);
 	vec->tv_mode = &vc4_vec_tv_modes[VC4_VEC_TV_MODE_NTSC];
 
-	drm_connector_attach_encoder(connector, vec->encoder);
+	drm_connector_attach_encoder(connector, &vec->encoder.base);
 
-	return connector;
+	return 0;
 }
 
 static void vc4_vec_encoder_disable(struct drm_encoder *encoder)
 {
-	struct vc4_vec_encoder *vc4_vec_encoder = to_vc4_vec_encoder(encoder);
-	struct vc4_vec *vec = vc4_vec_encoder->vec;
+	struct vc4_vec *vec = encoder_to_vc4_vec(encoder);
 	int ret;
 
 	VEC_WRITE(VEC_CFG, 0);
@@ -398,8 +369,7 @@ static void vc4_vec_encoder_disable(struct drm_encoder *encoder)
 
 static void vc4_vec_encoder_enable(struct drm_encoder *encoder)
 {
-	struct vc4_vec_encoder *vc4_vec_encoder = to_vc4_vec_encoder(encoder);
-	struct vc4_vec *vec = vc4_vec_encoder->vec;
+	struct vc4_vec *vec = encoder_to_vc4_vec(encoder);
 	int ret;
 
 	ret = pm_runtime_get_sync(&vec->pdev->dev);
@@ -474,8 +444,7 @@ static void vc4_vec_encoder_atomic_mode_set(struct drm_encoder *encoder,
 					struct drm_crtc_state *crtc_state,
 					struct drm_connector_state *conn_state)
 {
-	struct vc4_vec_encoder *vc4_vec_encoder = to_vc4_vec_encoder(encoder);
-	struct vc4_vec *vec = vc4_vec_encoder->vec;
+	struct vc4_vec *vec = encoder_to_vc4_vec(encoder);
 
 	vec->tv_mode = &vc4_vec_tv_modes[conn_state->tv.mode];
 }
@@ -533,7 +502,6 @@ static int vc4_vec_bind(struct device *dev, struct device *master, void *data)
 	struct platform_device *pdev = to_platform_device(dev);
 	struct drm_device *drm = dev_get_drvdata(master);
 	struct vc4_vec *vec;
-	struct vc4_vec_encoder *vc4_vec_encoder;
 	int ret;
 
 	ret = drm_mode_create_tv_properties(drm, ARRAY_SIZE(tv_mode_names),
@@ -545,14 +513,7 @@ static int vc4_vec_bind(struct device *dev, struct device *master, void *data)
 	if (!vec)
 		return -ENOMEM;
 
-	vc4_vec_encoder = devm_kzalloc(dev, sizeof(*vc4_vec_encoder),
-				       GFP_KERNEL);
-	if (!vc4_vec_encoder)
-		return -ENOMEM;
-	vc4_vec_encoder->base.type = VC4_ENCODER_TYPE_VEC;
-	vc4_vec_encoder->vec = vec;
-	vec->encoder = &vc4_vec_encoder->base.base;
-
+	vec->encoder.type = VC4_ENCODER_TYPE_VEC;
 	vec->pdev = pdev;
 	vec->variant = (const struct vc4_vec_variant *)
 		of_device_get_match_data(dev);
@@ -573,14 +534,12 @@ static int vc4_vec_bind(struct device *dev, struct device *master, void *data)
 
 	pm_runtime_enable(dev);
 
-	drm_simple_encoder_init(drm, vec->encoder, DRM_MODE_ENCODER_TVDAC);
-	drm_encoder_helper_add(vec->encoder, &vc4_vec_encoder_helper_funcs);
+	drm_simple_encoder_init(drm, &vec->encoder.base, DRM_MODE_ENCODER_TVDAC);
+	drm_encoder_helper_add(&vec->encoder.base, &vc4_vec_encoder_helper_funcs);
 
-	vec->connector = vc4_vec_connector_init(drm, vec);
-	if (IS_ERR(vec->connector)) {
-		ret = PTR_ERR(vec->connector);
+	ret = vc4_vec_connector_init(drm, vec);
+	if (ret)
 		goto err_destroy_encoder;
-	}
 
 	dev_set_drvdata(dev, vec);
 
@@ -589,7 +548,7 @@ static int vc4_vec_bind(struct device *dev, struct device *master, void *data)
 	return 0;
 
 err_destroy_encoder:
-	drm_encoder_cleanup(vec->encoder);
+	drm_encoder_cleanup(&vec->encoder.base);
 	pm_runtime_disable(dev);
 
 	return ret;
@@ -600,8 +559,8 @@ static void vc4_vec_unbind(struct device *dev, struct device *master,
 {
 	struct vc4_vec *vec = dev_get_drvdata(dev);
 
-	vc4_vec_connector_destroy(vec->connector);
-	drm_encoder_cleanup(vec->encoder);
+	vc4_vec_connector_destroy(&vec->connector);
+	drm_encoder_cleanup(&vec->encoder.base);
 	pm_runtime_disable(dev);
 }
 
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 52/64] drm/vc4: vec: Switch to drmm_kzalloc
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (50 preceding siblings ...)
  2022-06-10  9:29 ` [PATCH 51/64] drm/vc4: vec: Embed DRM structures into the private structure Maxime Ripard
@ 2022-06-10  9:29 ` Maxime Ripard
  2022-06-10  9:29 ` [PATCH 53/64] drm/vc4: vec: Switch to DRM-managed encoder initialization Maxime Ripard
                   ` (11 subsequent siblings)
  63 siblings, 0 replies; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:29 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

Our internal structure that stores the DRM entities structure is allocated
through a device-managed kzalloc.

This means that this will eventually be freed whenever the device is
removed. In our case, the most like source of removal is that the main
device is going to be unbound, and component_unbind_all() is being run.

However, it occurs while the DRM device is still registered, which will
create dangling pointers, eventually resulting in use-after-free.

Switch to a DRM-managed allocation to keep our structure until the DRM
driver doesn't need it anymore.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_vec.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c
index 2c96d5adcf49..a051b25337c0 100644
--- a/drivers/gpu/drm/vc4/vc4_vec.c
+++ b/drivers/gpu/drm/vc4/vc4_vec.c
@@ -509,7 +509,7 @@ static int vc4_vec_bind(struct device *dev, struct device *master, void *data)
 	if (ret)
 		return ret;
 
-	vec = devm_kzalloc(dev, sizeof(*vec), GFP_KERNEL);
+	vec = drmm_kzalloc(drm, sizeof(*vec), GFP_KERNEL);
 	if (!vec)
 		return -ENOMEM;
 
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 53/64] drm/vc4: vec: Switch to DRM-managed encoder initialization
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (51 preceding siblings ...)
  2022-06-10  9:29 ` [PATCH 52/64] drm/vc4: vec: Switch to drmm_kzalloc Maxime Ripard
@ 2022-06-10  9:29 ` Maxime Ripard
  2022-06-10  9:29 ` [PATCH 54/64] drm/vc4: vec: Switch to DRM-managed connector initialization Maxime Ripard
                   ` (10 subsequent siblings)
  63 siblings, 0 replies; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:29 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

The current code will call drm_encoder_cleanup() when the device is
unbound. However, by then, there might still be some references held to
that encoder, including by the userspace that might still have the DRM
device open.

Let's switch to a DRM-managed initialization to clean up after ourselves
only once the DRM device has been last closed.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_vec.c | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c
index a051b25337c0..3ccbe34b22ea 100644
--- a/drivers/gpu/drm/vc4/vc4_vec.c
+++ b/drivers/gpu/drm/vc4/vc4_vec.c
@@ -534,12 +534,15 @@ static int vc4_vec_bind(struct device *dev, struct device *master, void *data)
 
 	pm_runtime_enable(dev);
 
-	drm_simple_encoder_init(drm, &vec->encoder.base, DRM_MODE_ENCODER_TVDAC);
+	ret = drmm_simple_encoder_init(drm, &vec->encoder.base, DRM_MODE_ENCODER_TVDAC);
+	if (ret)
+		goto err_put_runtime_pm;
+
 	drm_encoder_helper_add(&vec->encoder.base, &vc4_vec_encoder_helper_funcs);
 
 	ret = vc4_vec_connector_init(drm, vec);
 	if (ret)
-		goto err_destroy_encoder;
+		goto err_put_runtime_pm;
 
 	dev_set_drvdata(dev, vec);
 
@@ -547,8 +550,7 @@ static int vc4_vec_bind(struct device *dev, struct device *master, void *data)
 
 	return 0;
 
-err_destroy_encoder:
-	drm_encoder_cleanup(&vec->encoder.base);
+err_put_runtime_pm:
 	pm_runtime_disable(dev);
 
 	return ret;
@@ -560,7 +562,6 @@ static void vc4_vec_unbind(struct device *dev, struct device *master,
 	struct vc4_vec *vec = dev_get_drvdata(dev);
 
 	vc4_vec_connector_destroy(&vec->connector);
-	drm_encoder_cleanup(&vec->encoder.base);
 	pm_runtime_disable(dev);
 }
 
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 54/64] drm/vc4: vec: Switch to DRM-managed connector initialization
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (52 preceding siblings ...)
  2022-06-10  9:29 ` [PATCH 53/64] drm/vc4: vec: Switch to DRM-managed encoder initialization Maxime Ripard
@ 2022-06-10  9:29 ` Maxime Ripard
  2022-06-10  9:29 ` [PATCH 55/64] drm/vc4: vec: Protect device resources after removal Maxime Ripard
                   ` (9 subsequent siblings)
  63 siblings, 0 replies; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:29 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

The current code will call drm_connector_unregister() and
drm_connector_cleanup() when the device is unbound. However, by then, there
might still be some references held to that connector, including by the
userspace that might still have the DRM device open.

Let's switch to a DRM-managed initialization to clean up after ourselves
only once the DRM device has been last closed.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_vec.c | 17 +++++------------
 1 file changed, 5 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c
index 3ccbe34b22ea..c63701503141 100644
--- a/drivers/gpu/drm/vc4/vc4_vec.c
+++ b/drivers/gpu/drm/vc4/vc4_vec.c
@@ -289,12 +289,6 @@ vc4_vec_connector_detect(struct drm_connector *connector, bool force)
 	return connector_status_unknown;
 }
 
-static void vc4_vec_connector_destroy(struct drm_connector *connector)
-{
-	drm_connector_unregister(connector);
-	drm_connector_cleanup(connector);
-}
-
 static int vc4_vec_connector_get_modes(struct drm_connector *connector)
 {
 	struct drm_connector_state *state = connector->state;
@@ -315,7 +309,6 @@ static int vc4_vec_connector_get_modes(struct drm_connector *connector)
 static const struct drm_connector_funcs vc4_vec_connector_funcs = {
 	.detect = vc4_vec_connector_detect,
 	.fill_modes = drm_helper_probe_single_connector_modes,
-	.destroy = vc4_vec_connector_destroy,
 	.reset = drm_atomic_helper_connector_reset,
 	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
@@ -328,12 +321,15 @@ static const struct drm_connector_helper_funcs vc4_vec_connector_helper_funcs =
 static int vc4_vec_connector_init(struct drm_device *dev, struct vc4_vec *vec)
 {
 	struct drm_connector *connector = &vec->connector;
+	int ret;
 
 	connector->interlace_allowed = true;
 
+	ret = drmm_connector_init(dev, connector, &vc4_vec_connector_funcs,
+				 DRM_MODE_CONNECTOR_Composite);
+	if (ret)
+		return ret;
 
-	drm_connector_init(dev, connector, &vc4_vec_connector_funcs,
-			   DRM_MODE_CONNECTOR_Composite);
 	drm_connector_helper_add(connector, &vc4_vec_connector_helper_funcs);
 
 	drm_object_attach_property(&connector->base,
@@ -559,9 +555,6 @@ static int vc4_vec_bind(struct device *dev, struct device *master, void *data)
 static void vc4_vec_unbind(struct device *dev, struct device *master,
 			   void *data)
 {
-	struct vc4_vec *vec = dev_get_drvdata(dev);
-
-	vc4_vec_connector_destroy(&vec->connector);
 	pm_runtime_disable(dev);
 }
 
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 55/64] drm/vc4: vec: Protect device resources after removal
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (53 preceding siblings ...)
  2022-06-10  9:29 ` [PATCH 54/64] drm/vc4: vec: Switch to DRM-managed connector initialization Maxime Ripard
@ 2022-06-10  9:29 ` Maxime Ripard
  2022-06-10  9:29 ` [PATCH 56/64] drm/vc4: vec: Switch to devm_pm_runtime_enable Maxime Ripard
                   ` (8 subsequent siblings)
  63 siblings, 0 replies; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:29 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

Whenever the device and driver are unbound, the main device and all the
subdevices will be removed by calling their unbind() method.

However, the DRM device itself will only be freed when the last user will
have closed it.

It means that there is a time window where the device and its resources
aren't there anymore, but the userspace can still call into our driver.

Fortunately, the DRM framework provides the drm_dev_enter() and
drm_dev_exit() functions to make sure our underlying device is still there
for the section protected by those calls. Let's add them to the VEC driver.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_vec.c | 67 +++++++++++++++++++++++++++++++----
 1 file changed, 61 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c
index c63701503141..2a72644d99c5 100644
--- a/drivers/gpu/drm/vc4/vc4_vec.c
+++ b/drivers/gpu/drm/vc4/vc4_vec.c
@@ -14,6 +14,7 @@
  */
 
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_drv.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_panel.h>
 #include <drm/drm_probe_helper.h>
@@ -225,14 +226,30 @@ static const struct debugfs_reg32 vec_regs[] = {
 
 static void vc4_vec_ntsc_mode_set(struct vc4_vec *vec)
 {
+	struct drm_device *drm = vec->connector.dev;
+	int idx;
+
+	if (!drm_dev_enter(drm, &idx))
+		return;
+
 	VEC_WRITE(VEC_CONFIG0, VEC_CONFIG0_NTSC_STD | VEC_CONFIG0_PDEN);
 	VEC_WRITE(VEC_CONFIG1, VEC_CONFIG1_C_CVBS_CVBS);
+
+	drm_dev_exit(idx);
 }
 
 static void vc4_vec_ntsc_j_mode_set(struct vc4_vec *vec)
 {
+	struct drm_device *drm = vec->connector.dev;
+	int idx;
+
+	if (!drm_dev_enter(drm, &idx))
+		return;
+
 	VEC_WRITE(VEC_CONFIG0, VEC_CONFIG0_NTSC_STD);
 	VEC_WRITE(VEC_CONFIG1, VEC_CONFIG1_C_CVBS_CVBS);
+
+	drm_dev_exit(idx);
 }
 
 static const struct drm_display_mode ntsc_mode = {
@@ -244,17 +261,33 @@ static const struct drm_display_mode ntsc_mode = {
 
 static void vc4_vec_pal_mode_set(struct vc4_vec *vec)
 {
+	struct drm_device *drm = vec->connector.dev;
+	int idx;
+
+	if (!drm_dev_enter(drm, &idx))
+		return;
+
 	VEC_WRITE(VEC_CONFIG0, VEC_CONFIG0_PAL_BDGHI_STD);
 	VEC_WRITE(VEC_CONFIG1, VEC_CONFIG1_C_CVBS_CVBS);
+
+	drm_dev_exit(idx);
 }
 
 static void vc4_vec_pal_m_mode_set(struct vc4_vec *vec)
 {
+	struct drm_device *drm = vec->connector.dev;
+	int idx;
+
+	if (!drm_dev_enter(drm, &idx))
+		return;
+
 	VEC_WRITE(VEC_CONFIG0, VEC_CONFIG0_PAL_BDGHI_STD);
 	VEC_WRITE(VEC_CONFIG1,
 		  VEC_CONFIG1_C_CVBS_CVBS | VEC_CONFIG1_CUSTOM_FREQ);
 	VEC_WRITE(VEC_FREQ3_2, 0x223b);
 	VEC_WRITE(VEC_FREQ1_0, 0x61d1);
+
+	drm_dev_exit(idx);
 }
 
 static const struct drm_display_mode pal_mode = {
@@ -344,8 +377,12 @@ static int vc4_vec_connector_init(struct drm_device *dev, struct vc4_vec *vec)
 
 static void vc4_vec_encoder_disable(struct drm_encoder *encoder)
 {
+	struct drm_device *drm = encoder->dev;
 	struct vc4_vec *vec = encoder_to_vc4_vec(encoder);
-	int ret;
+	int idx, ret;
+
+	if (!drm_dev_enter(drm, &idx))
+		return;
 
 	VEC_WRITE(VEC_CFG, 0);
 	VEC_WRITE(VEC_DAC_MISC,
@@ -359,19 +396,29 @@ static void vc4_vec_encoder_disable(struct drm_encoder *encoder)
 	ret = pm_runtime_put(&vec->pdev->dev);
 	if (ret < 0) {
 		DRM_ERROR("Failed to release power domain: %d\n", ret);
-		return;
+		goto err_dev_exit;
 	}
+
+	drm_dev_exit(idx);
+	return;
+
+err_dev_exit:
+	drm_dev_exit(idx);
 }
 
 static void vc4_vec_encoder_enable(struct drm_encoder *encoder)
 {
+	struct drm_device *drm = encoder->dev;
 	struct vc4_vec *vec = encoder_to_vc4_vec(encoder);
-	int ret;
+	int idx, ret;
+
+	if (!drm_dev_enter(drm, &idx))
+		return;
 
 	ret = pm_runtime_get_sync(&vec->pdev->dev);
 	if (ret < 0) {
 		DRM_ERROR("Failed to retain power domain: %d\n", ret);
-		return;
+		goto err_dev_exit;
 	}
 
 	/*
@@ -384,13 +431,13 @@ static void vc4_vec_encoder_enable(struct drm_encoder *encoder)
 	ret = clk_set_rate(vec->clock, 108000000);
 	if (ret) {
 		DRM_ERROR("Failed to set clock rate: %d\n", ret);
-		return;
+		goto err_put_runtime_pm;
 	}
 
 	ret = clk_prepare_enable(vec->clock);
 	if (ret) {
 		DRM_ERROR("Failed to turn on core clock: %d\n", ret);
-		return;
+		goto err_put_runtime_pm;
 	}
 
 	/* Reset the different blocks */
@@ -426,6 +473,14 @@ static void vc4_vec_encoder_enable(struct drm_encoder *encoder)
 	VEC_WRITE(VEC_DAC_MISC,
 		  VEC_DAC_MISC_VID_ACT | VEC_DAC_MISC_DAC_RST_N);
 	VEC_WRITE(VEC_CFG, VEC_CFG_VEC_EN);
+
+	drm_dev_exit(idx);
+	return;
+
+err_put_runtime_pm:
+	pm_runtime_put(&vec->pdev->dev);
+err_dev_exit:
+	drm_dev_exit(idx);
 }
 
 
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 56/64] drm/vc4: vec: Switch to devm_pm_runtime_enable
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (54 preceding siblings ...)
  2022-06-10  9:29 ` [PATCH 55/64] drm/vc4: vec: Protect device resources after removal Maxime Ripard
@ 2022-06-10  9:29 ` Maxime Ripard
  2022-06-10  9:29 ` [PATCH 57/64] drm/vc4: debugfs: Protect device resources Maxime Ripard
                   ` (7 subsequent siblings)
  63 siblings, 0 replies; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:29 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

devm_pm_runtime_enable() simplifies the driver a bit since it will call
pm_runtime_disable() automatically through a device-managed action.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_vec.c | 20 +++++---------------
 1 file changed, 5 insertions(+), 15 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c
index 2a72644d99c5..a75b82de3796 100644
--- a/drivers/gpu/drm/vc4/vc4_vec.c
+++ b/drivers/gpu/drm/vc4/vc4_vec.c
@@ -583,39 +583,29 @@ static int vc4_vec_bind(struct device *dev, struct device *master, void *data)
 		return ret;
 	}
 
-	pm_runtime_enable(dev);
+	ret = devm_pm_runtime_enable(dev);
+	if (ret)
+		return ret;
 
 	ret = drmm_simple_encoder_init(drm, &vec->encoder.base, DRM_MODE_ENCODER_TVDAC);
 	if (ret)
-		goto err_put_runtime_pm;
+		return ret;
 
 	drm_encoder_helper_add(&vec->encoder.base, &vc4_vec_encoder_helper_funcs);
 
 	ret = vc4_vec_connector_init(drm, vec);
 	if (ret)
-		goto err_put_runtime_pm;
+		return ret;
 
 	dev_set_drvdata(dev, vec);
 
 	vc4_debugfs_add_regset32(drm, "vec_regs", &vec->regset);
 
 	return 0;
-
-err_put_runtime_pm:
-	pm_runtime_disable(dev);
-
-	return ret;
-}
-
-static void vc4_vec_unbind(struct device *dev, struct device *master,
-			   void *data)
-{
-	pm_runtime_disable(dev);
 }
 
 static const struct component_ops vc4_vec_ops = {
 	.bind   = vc4_vec_bind,
-	.unbind = vc4_vec_unbind,
 };
 
 static int vc4_vec_dev_probe(struct platform_device *pdev)
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 57/64] drm/vc4: debugfs: Protect device resources
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (55 preceding siblings ...)
  2022-06-10  9:29 ` [PATCH 56/64] drm/vc4: vec: Switch to devm_pm_runtime_enable Maxime Ripard
@ 2022-06-10  9:29 ` Maxime Ripard
  2022-06-10  9:29 ` [PATCH 58/64] drm/vc4: debugfs: Return an error on failure Maxime Ripard
                   ` (6 subsequent siblings)
  63 siblings, 0 replies; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:29 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

Our current code now mixes some resources whose lifetime are tied to the
device (clocks, IO mappings, etc.) and some that are tied to the DRM device
(encoder, bridge).

The device one will be freed at unbind time, but the DRM one will only be
freed when the last user of the DRM device closes its file handle.

So we end up with a time window during which we can call the encoder hooks,
but we don't have access to the underlying resources and device.

Let's protect all those sections with drm_dev_enter() and drm_dev_exit() so
that we bail out if we are during that window.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_debugfs.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/drivers/gpu/drm/vc4/vc4_debugfs.c b/drivers/gpu/drm/vc4/vc4_debugfs.c
index ba2d8ea562af..d6350a8ca048 100644
--- a/drivers/gpu/drm/vc4/vc4_debugfs.c
+++ b/drivers/gpu/drm/vc4/vc4_debugfs.c
@@ -3,6 +3,8 @@
  *  Copyright © 2014 Broadcom
  */
 
+#include <drm/drm_drv.h>
+
 #include <linux/seq_file.h>
 #include <linux/circ_buf.h>
 #include <linux/ctype.h>
@@ -41,11 +43,18 @@ vc4_debugfs_init(struct drm_minor *minor)
 static int vc4_debugfs_regset32(struct seq_file *m, void *unused)
 {
 	struct drm_info_node *node = (struct drm_info_node *)m->private;
+	struct drm_device *drm = node->minor->dev;
 	struct debugfs_regset32 *regset = node->info_ent->data;
 	struct drm_printer p = drm_seq_file_printer(m);
+	int idx;
+
+	if (!drm_dev_enter(drm, &idx))
+		return -ENODEV;
 
 	drm_print_regset32(&p, regset);
 
+	drm_dev_exit(idx);
+
 	return 0;
 }
 
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 58/64] drm/vc4: debugfs: Return an error on failure
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (56 preceding siblings ...)
  2022-06-10  9:29 ` [PATCH 57/64] drm/vc4: debugfs: Protect device resources Maxime Ripard
@ 2022-06-10  9:29 ` Maxime Ripard
  2022-06-10  9:29 ` [PATCH 59/64] drm/vc4: debugfs: Simplify debugfs registration Maxime Ripard
                   ` (5 subsequent siblings)
  63 siblings, 0 replies; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:29 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

vc4_debugfs_add_file() can fail, so let's propagate its error code instead
of silencing it.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_debugfs.c | 20 +++++++++++---------
 drivers/gpu/drm/vc4/vc4_drv.h     | 30 ++++++++++++++++--------------
 2 files changed, 27 insertions(+), 23 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_debugfs.c b/drivers/gpu/drm/vc4/vc4_debugfs.c
index d6350a8ca048..b857fb9c94bc 100644
--- a/drivers/gpu/drm/vc4/vc4_debugfs.c
+++ b/drivers/gpu/drm/vc4/vc4_debugfs.c
@@ -67,10 +67,10 @@ static int vc4_debugfs_regset32(struct seq_file *m, void *unused)
  * track the request and delay it to be called on each minor during
  * vc4_debugfs_init().
  */
-void vc4_debugfs_add_file(struct drm_device *dev,
-			  const char *name,
-			  int (*show)(struct seq_file*, void*),
-			  void *data)
+int vc4_debugfs_add_file(struct drm_device *dev,
+			 const char *name,
+			 int (*show)(struct seq_file*, void*),
+			 void *data)
 {
 	struct vc4_dev *vc4 = to_vc4_dev(dev);
 
@@ -78,18 +78,20 @@ void vc4_debugfs_add_file(struct drm_device *dev,
 		devm_kzalloc(dev->dev, sizeof(*entry), GFP_KERNEL);
 
 	if (!entry)
-		return;
+		return -ENOMEM;
 
 	entry->info.name = name;
 	entry->info.show = show;
 	entry->info.data = data;
 
 	list_add(&entry->link, &vc4->debugfs_list);
+
+	return 0;
 }
 
-void vc4_debugfs_add_regset32(struct drm_device *drm,
-			      const char *name,
-			      struct debugfs_regset32 *regset)
+int vc4_debugfs_add_regset32(struct drm_device *drm,
+			     const char *name,
+			     struct debugfs_regset32 *regset)
 {
-	vc4_debugfs_add_file(drm, name, vc4_debugfs_regset32, regset);
+	return vc4_debugfs_add_file(drm, name, vc4_debugfs_regset32, regset);
 }
diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h
index 12ab6df30629..3d1482f414c8 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.h
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
@@ -859,25 +859,27 @@ void vc4_crtc_get_margins(struct drm_crtc_state *state,
 /* vc4_debugfs.c */
 void vc4_debugfs_init(struct drm_minor *minor);
 #ifdef CONFIG_DEBUG_FS
-void vc4_debugfs_add_file(struct drm_device *drm,
-			  const char *filename,
-			  int (*show)(struct seq_file*, void*),
-			  void *data);
-void vc4_debugfs_add_regset32(struct drm_device *drm,
-			      const char *filename,
-			      struct debugfs_regset32 *regset);
+int vc4_debugfs_add_file(struct drm_device *drm,
+			 const char *filename,
+			 int (*show)(struct seq_file*, void*),
+			 void *data);
+int vc4_debugfs_add_regset32(struct drm_device *drm,
+			     const char *filename,
+			     struct debugfs_regset32 *regset);
 #else
-static inline void vc4_debugfs_add_file(struct drm_device *drm,
-					const char *filename,
-					int (*show)(struct seq_file*, void*),
-					void *data)
+static inline int vc4_debugfs_add_file(struct drm_device *drm,
+				       const char *filename,
+				       int (*show)(struct seq_file*, void*),
+				       void *data)
 {
+	return 0;
 }
 
-static inline void vc4_debugfs_add_regset32(struct drm_device *drm,
-					    const char *filename,
-					    struct debugfs_regset32 *regset)
+static inline int vc4_debugfs_add_regset32(struct drm_device *drm,
+					   const char *filename,
+					   struct debugfs_regset32 *regset)
 {
+	return 0;
 }
 #endif
 
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 59/64] drm/vc4: debugfs: Simplify debugfs registration
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (57 preceding siblings ...)
  2022-06-10  9:29 ` [PATCH 58/64] drm/vc4: debugfs: Return an error on failure Maxime Ripard
@ 2022-06-10  9:29 ` Maxime Ripard
  2022-06-10  9:29 ` [PATCH 60/64] drm/vc4: Switch to drmm_mutex_init Maxime Ripard
                   ` (4 subsequent siblings)
  63 siblings, 0 replies; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:29 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

The vc4 has a custom API to allow components to register a debugfs file
before the DRM driver has been registered and the debugfs_init hook has
been called.

However, the .late_register hook allows to have the debugfs file creation
deferred after that time already.

Let's remove our custom code to only register later our debugfs entries as
part of either debugfs_init or after it.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_bo.c      | 20 +++++++++++--
 drivers/gpu/drm/vc4/vc4_crtc.c    | 19 ++++++++++--
 drivers/gpu/drm/vc4/vc4_debugfs.c | 50 ++++++++++---------------------
 drivers/gpu/drm/vc4/vc4_dpi.c     | 24 +++++++++++++--
 drivers/gpu/drm/vc4/vc4_drv.h     | 12 +++++---
 drivers/gpu/drm/vc4/vc4_dsi.c     | 23 ++++++++++++--
 drivers/gpu/drm/vc4/vc4_hdmi.c    | 45 ++++++++++++++++++++--------
 drivers/gpu/drm/vc4/vc4_hdmi.h    |  3 +-
 drivers/gpu/drm/vc4/vc4_hvs.c     | 32 +++++++++++++++++---
 drivers/gpu/drm/vc4/vc4_txp.c     |  3 +-
 drivers/gpu/drm/vc4/vc4_v3d.c     | 25 ++++++++++++++--
 drivers/gpu/drm/vc4/vc4_vec.c     | 25 ++++++++++++++--
 12 files changed, 206 insertions(+), 75 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c
index 49c0f2ac868b..68fe9903947d 100644
--- a/drivers/gpu/drm/vc4/vc4_bo.c
+++ b/drivers/gpu/drm/vc4/vc4_bo.c
@@ -942,6 +942,23 @@ int vc4_get_tiling_ioctl(struct drm_device *dev, void *data,
 	return 0;
 }
 
+int vc4_bo_debugfs_init(struct drm_minor *minor)
+{
+	struct drm_device *drm = minor->dev;
+	struct vc4_dev *vc4 = to_vc4_dev(drm);
+	int ret;
+
+	if (!vc4->v3d)
+		return -ENODEV;
+
+	ret = vc4_debugfs_add_file(minor, "bo_stats",
+				   vc4_bo_stats_debugfs, NULL);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
 static void vc4_bo_cache_destroy(struct drm_device *dev, void *unused);
 int vc4_bo_cache_init(struct drm_device *dev)
 {
@@ -963,9 +980,6 @@ int vc4_bo_cache_init(struct drm_device *dev)
 		vc4->bo_labels[i].name = bo_type_names[i];
 
 	mutex_init(&vc4->bo_lock);
-
-	vc4_debugfs_add_file(dev, "bo_stats", vc4_bo_stats_debugfs, NULL);
-
 	INIT_LIST_HEAD(&vc4->bo_cache.time_list);
 
 	INIT_WORK(&vc4->bo_cache.time_work, vc4_bo_cache_time_work);
diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c
index 24de4706b61a..4a9f68362137 100644
--- a/drivers/gpu/drm/vc4/vc4_crtc.c
+++ b/drivers/gpu/drm/vc4/vc4_crtc.c
@@ -946,6 +946,21 @@ void vc4_crtc_reset(struct drm_crtc *crtc)
 	__drm_atomic_helper_crtc_reset(crtc, &vc4_crtc_state->base);
 }
 
+int vc4_crtc_late_register(struct drm_crtc *crtc)
+{
+	struct drm_device *drm = crtc->dev;
+	struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+	const struct vc4_crtc_data *crtc_data = vc4_crtc_to_vc4_crtc_data(vc4_crtc);
+	int ret;
+
+	ret = vc4_debugfs_add_regset32(drm->primary, crtc_data->debugfs_name,
+				       &vc4_crtc->regset);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
 static const struct drm_crtc_funcs vc4_crtc_funcs = {
 	.set_config = drm_atomic_helper_set_config,
 	.page_flip = vc4_page_flip,
@@ -958,6 +973,7 @@ static const struct drm_crtc_funcs vc4_crtc_funcs = {
 	.enable_vblank = vc4_enable_vblank,
 	.disable_vblank = vc4_disable_vblank,
 	.get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp,
+	.late_register = vc4_crtc_late_register,
 };
 
 static const struct drm_crtc_helper_funcs vc4_crtc_helper_funcs = {
@@ -1212,9 +1228,6 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data)
 
 	platform_set_drvdata(pdev, vc4_crtc);
 
-	vc4_debugfs_add_regset32(drm, pv_data->base.debugfs_name,
-				 &vc4_crtc->regset);
-
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/vc4/vc4_debugfs.c b/drivers/gpu/drm/vc4/vc4_debugfs.c
index b857fb9c94bc..b40d6a44b16b 100644
--- a/drivers/gpu/drm/vc4/vc4_debugfs.c
+++ b/drivers/gpu/drm/vc4/vc4_debugfs.c
@@ -14,11 +14,6 @@
 #include "vc4_drv.h"
 #include "vc4_regs.h"
 
-struct vc4_debugfs_info_entry {
-	struct list_head link;
-	struct drm_info_list info;
-};
-
 /*
  * Called at drm_dev_register() time on each of the minors registered
  * by the DRM device, to attach the debugfs files.
@@ -27,16 +22,12 @@ void
 vc4_debugfs_init(struct drm_minor *minor)
 {
 	struct vc4_dev *vc4 = to_vc4_dev(minor->dev);
-	struct vc4_debugfs_info_entry *entry;
 
-	if (!of_device_is_compatible(vc4->hvs->pdev->dev.of_node,
-				     "brcm,bcm2711-vc5"))
-		debugfs_create_bool("hvs_load_tracker", S_IRUGO | S_IWUSR,
-				    minor->debugfs_root, &vc4->load_tracker_enabled);
+	WARN_ON(vc4_hvs_debugfs_init(minor));
 
-	list_for_each_entry(entry, &vc4->debugfs_list, link) {
-		drm_debugfs_create_files(&entry->info, 1,
-					 minor->debugfs_root, minor);
+	if (vc4->v3d) {
+		WARN_ON(vc4_bo_debugfs_init(minor));
+		WARN_ON(vc4_v3d_debugfs_init(minor));
 	}
 }
 
@@ -58,40 +49,31 @@ static int vc4_debugfs_regset32(struct seq_file *m, void *unused)
 	return 0;
 }
 
-/*
- * Registers a debugfs file with a callback function for a vc4 component.
- *
- * This is like drm_debugfs_create_files(), but that can only be
- * called a given DRM minor, while the various VC4 components want to
- * register their debugfs files during the component bind process.  We
- * track the request and delay it to be called on each minor during
- * vc4_debugfs_init().
- */
-int vc4_debugfs_add_file(struct drm_device *dev,
+int vc4_debugfs_add_file(struct drm_minor *minor,
 			 const char *name,
 			 int (*show)(struct seq_file*, void*),
 			 void *data)
 {
-	struct vc4_dev *vc4 = to_vc4_dev(dev);
+	struct drm_device *dev = minor->dev;
+	struct dentry *root = minor->debugfs_root;
+	struct drm_info_list *file;
 
-	struct vc4_debugfs_info_entry *entry =
-		devm_kzalloc(dev->dev, sizeof(*entry), GFP_KERNEL);
-
-	if (!entry)
+	file = drmm_kzalloc(dev, sizeof(*file), GFP_KERNEL);
+	if (!file)
 		return -ENOMEM;
 
-	entry->info.name = name;
-	entry->info.show = show;
-	entry->info.data = data;
+	file->name = name;
+	file->show = show;
+	file->data = data;
 
-	list_add(&entry->link, &vc4->debugfs_list);
+	drm_debugfs_create_files(file, 1, root, minor);
 
 	return 0;
 }
 
-int vc4_debugfs_add_regset32(struct drm_device *drm,
+int vc4_debugfs_add_regset32(struct drm_minor *minor,
 			     const char *name,
 			     struct debugfs_regset32 *regset)
 {
-	return vc4_debugfs_add_file(drm, name, vc4_debugfs_regset32, regset);
+	return vc4_debugfs_add_file(minor, name, vc4_debugfs_regset32, regset);
 }
diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c
index ea3d20651f43..0445357597f0 100644
--- a/drivers/gpu/drm/vc4/vc4_dpi.c
+++ b/drivers/gpu/drm/vc4/vc4_dpi.c
@@ -224,6 +224,23 @@ static const struct drm_encoder_helper_funcs vc4_dpi_encoder_helper_funcs = {
 	.mode_valid = vc4_dpi_encoder_mode_valid,
 };
 
+static int vc4_dpi_late_register(struct drm_encoder *encoder)
+{
+	struct drm_device *drm = encoder->dev;
+	struct vc4_dpi *dpi = to_vc4_dpi(encoder);
+	int ret;
+
+	ret = vc4_debugfs_add_regset32(drm->primary, "dpi_regs", &dpi->regset);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static const struct drm_encoder_funcs vc4_dpi_encoder_funcs = {
+	.late_register = vc4_dpi_late_register,
+};
+
 static const struct of_device_id vc4_dpi_dt_match[] = {
 	{ .compatible = "brcm,bcm2835-dpi", .data = NULL },
 	{}
@@ -311,7 +328,10 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data)
 	if (ret)
 		return ret;
 
-	ret = drmm_simple_encoder_init(drm, &dpi->encoder.base, DRM_MODE_ENCODER_DPI);
+	ret = drmm_encoder_init(drm, &dpi->encoder.base,
+				&vc4_dpi_encoder_funcs,
+				DRM_MODE_ENCODER_DPI,
+				NULL);
 	if (ret)
 		return ret;
 
@@ -323,8 +343,6 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data)
 
 	dev_set_drvdata(dev, dpi);
 
-	vc4_debugfs_add_regset32(drm, "dpi_regs", &dpi->regset);
-
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h
index 3d1482f414c8..ccde56be3a9c 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.h
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
@@ -835,6 +835,7 @@ int vc4_bo_inc_usecnt(struct vc4_bo *bo);
 void vc4_bo_dec_usecnt(struct vc4_bo *bo);
 void vc4_bo_add_to_purgeable_pool(struct vc4_bo *bo);
 void vc4_bo_remove_from_purgeable_pool(struct vc4_bo *bo);
+int vc4_bo_debugfs_init(struct drm_minor *minor);
 
 /* vc4_crtc.c */
 extern struct platform_driver vc4_crtc_driver;
@@ -852,6 +853,7 @@ void vc4_crtc_destroy_state(struct drm_crtc *crtc,
 			    struct drm_crtc_state *state);
 void vc4_crtc_reset(struct drm_crtc *crtc);
 void vc4_crtc_handle_vblank(struct vc4_crtc *crtc);
+int vc4_crtc_late_register(struct drm_crtc *crtc);
 void vc4_crtc_get_margins(struct drm_crtc_state *state,
 			  unsigned int *left, unsigned int *right,
 			  unsigned int *top, unsigned int *bottom);
@@ -859,15 +861,15 @@ void vc4_crtc_get_margins(struct drm_crtc_state *state,
 /* vc4_debugfs.c */
 void vc4_debugfs_init(struct drm_minor *minor);
 #ifdef CONFIG_DEBUG_FS
-int vc4_debugfs_add_file(struct drm_device *drm,
+int vc4_debugfs_add_file(struct drm_minor *minor,
 			 const char *filename,
 			 int (*show)(struct seq_file*, void*),
 			 void *data);
-int vc4_debugfs_add_regset32(struct drm_device *drm,
+int vc4_debugfs_add_regset32(struct drm_minor *minor,
 			     const char *filename,
 			     struct debugfs_regset32 *regset);
 #else
-static inline int vc4_debugfs_add_file(struct drm_device *drm,
+static inline int vc4_debugfs_add_file(struct drm_minor *minor,
 				       const char *filename,
 				       int (*show)(struct seq_file*, void*),
 				       void *data)
@@ -875,7 +877,7 @@ static inline int vc4_debugfs_add_file(struct drm_device *drm,
 	return 0;
 }
 
-static inline int vc4_debugfs_add_regset32(struct drm_device *drm,
+static inline int vc4_debugfs_add_regset32(struct drm_minor *minor,
 					   const char *filename,
 					   struct debugfs_regset32 *regset)
 {
@@ -944,6 +946,7 @@ void vc4_hvs_atomic_flush(struct drm_crtc *crtc, struct drm_atomic_state *state)
 void vc4_hvs_dump_state(struct vc4_hvs *hvs);
 void vc4_hvs_unmask_underrun(struct vc4_hvs *hvs, int channel);
 void vc4_hvs_mask_underrun(struct vc4_hvs *hvs, int channel);
+int vc4_hvs_debugfs_init(struct drm_minor *minor);
 
 /* vc4_kms.c */
 int vc4_kms_load(struct drm_device *dev);
@@ -966,6 +969,7 @@ int vc4_v3d_bin_bo_get(struct vc4_dev *vc4, bool *used);
 void vc4_v3d_bin_bo_put(struct vc4_dev *vc4);
 int vc4_v3d_pm_get(struct vc4_dev *vc4);
 void vc4_v3d_pm_put(struct vc4_dev *vc4);
+int vc4_v3d_debugfs_init(struct drm_minor *minor);
 
 /* vc4_validate.c */
 int
diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
index 741db2dce8ab..77afbdfac897 100644
--- a/drivers/gpu/drm/vc4/vc4_dsi.c
+++ b/drivers/gpu/drm/vc4/vc4_dsi.c
@@ -1314,6 +1314,24 @@ static const struct drm_encoder_helper_funcs vc4_dsi_encoder_helper_funcs = {
 	.mode_fixup = vc4_dsi_encoder_mode_fixup,
 };
 
+static int vc4_dsi_late_register(struct drm_encoder *encoder)
+{
+	struct drm_device *drm = encoder->dev;
+	struct vc4_dsi *dsi = to_vc4_dsi(encoder);
+	int ret;
+
+	ret = vc4_debugfs_add_regset32(drm->primary, dsi->variant->debugfs_name,
+				       &dsi->regset);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static const struct drm_encoder_funcs vc4_dsi_encoder_funcs = {
+	.late_register = vc4_dsi_late_register,
+};
+
 static const struct vc4_dsi_variant bcm2711_dsi1_variant = {
 	.port			= 1,
 	.debugfs_name		= "dsi1_regs",
@@ -1616,7 +1634,8 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
 	if (ret)
 		return ret;
 
-	ret = drmm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_DSI);
+	ret = drmm_encoder_init(drm, encoder, &vc4_dsi_encoder_funcs,
+				DRM_MODE_ENCODER_DSI, NULL);
 	if (ret)
 		return ret;
 
@@ -1636,8 +1655,6 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
 	 */
 	list_splice_init(&encoder->bridge_chain, &dsi->bridge_chain);
 
-	vc4_debugfs_add_regset32(drm, dsi->variant->debugfs_name, &dsi->regset);
-
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index b31487547070..2092231001c8 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -1793,6 +1793,26 @@ static const struct drm_encoder_helper_funcs vc4_hdmi_encoder_helper_funcs = {
 	.enable = vc4_hdmi_encoder_enable,
 };
 
+static int vc4_hdmi_late_register(struct drm_encoder *encoder)
+{
+	struct drm_device *drm = encoder->dev;
+	struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
+	const struct vc4_hdmi_variant *variant = vc4_hdmi->variant;
+	int ret;
+
+	ret = vc4_debugfs_add_file(drm->primary, variant->debugfs_name,
+				   vc4_hdmi_debugfs_regs,
+				   vc4_hdmi);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static const struct drm_encoder_funcs vc4_hdmi_encoder_funcs = {
+	.late_register = vc4_hdmi_late_register,
+};
+
 static u32 vc4_hdmi_channel_map(struct vc4_hdmi *vc4_hdmi, u32 channel_mask)
 {
 	int i;
@@ -2922,11 +2942,11 @@ static void vc4_hdmi_free_regset(struct drm_device *drm, void *ptr)
 	kfree(regs);
 }
 
-static int vc4_hdmi_build_regset(struct vc4_hdmi *vc4_hdmi,
+static int vc4_hdmi_build_regset(struct drm_device *drm,
+				 struct vc4_hdmi *vc4_hdmi,
 				 struct debugfs_regset32 *regset,
 				 enum vc4_hdmi_regs reg)
 {
-	struct drm_device *drm = vc4_hdmi->connector.dev;
 	const struct vc4_hdmi_variant *variant = vc4_hdmi->variant;
 	struct debugfs_reg32 *regs, *new_regs;
 	unsigned int count = 0;
@@ -2964,7 +2984,8 @@ static int vc4_hdmi_build_regset(struct vc4_hdmi *vc4_hdmi,
 	return 0;
 }
 
-static int vc4_hdmi_init_resources(struct vc4_hdmi *vc4_hdmi)
+static int vc4_hdmi_init_resources(struct drm_device *drm,
+				   struct vc4_hdmi *vc4_hdmi)
 {
 	struct platform_device *pdev = vc4_hdmi->pdev;
 	struct device *dev = &pdev->dev;
@@ -2978,11 +2999,11 @@ static int vc4_hdmi_init_resources(struct vc4_hdmi *vc4_hdmi)
 	if (IS_ERR(vc4_hdmi->hd_regs))
 		return PTR_ERR(vc4_hdmi->hd_regs);
 
-	ret = vc4_hdmi_build_regset(vc4_hdmi, &vc4_hdmi->hd_regset, VC4_HD);
+	ret = vc4_hdmi_build_regset(drm, vc4_hdmi, &vc4_hdmi->hd_regset, VC4_HD);
 	if (ret)
 		return ret;
 
-	ret = vc4_hdmi_build_regset(vc4_hdmi, &vc4_hdmi->hdmi_regset, VC4_HDMI);
+	ret = vc4_hdmi_build_regset(drm, vc4_hdmi, &vc4_hdmi->hdmi_regset, VC4_HDMI);
 	if (ret)
 		return ret;
 
@@ -3005,7 +3026,8 @@ static int vc4_hdmi_init_resources(struct vc4_hdmi *vc4_hdmi)
 	return 0;
 }
 
-static int vc5_hdmi_init_resources(struct vc4_hdmi *vc4_hdmi)
+static int vc5_hdmi_init_resources(struct drm_device *drm,
+				   struct vc4_hdmi *vc4_hdmi)
 {
 	struct platform_device *pdev = vc4_hdmi->pdev;
 	struct device *dev = &pdev->dev;
@@ -3175,7 +3197,7 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
 	if (variant->max_pixel_clock > HDMI_14_MAX_TMDS_CLK)
 		vc4_hdmi->scdc_enabled = true;
 
-	ret = variant->init_resources(vc4_hdmi);
+	ret = variant->init_resources(drm, vc4_hdmi);
 	if (ret)
 		return ret;
 
@@ -3243,7 +3265,10 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
 		clk_prepare_enable(vc4_hdmi->pixel_bvb_clock);
 	}
 
-	ret = drmm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS);
+	ret = drmm_encoder_init(drm, encoder,
+				&vc4_hdmi_encoder_funcs,
+				DRM_MODE_ENCODER_TMDS,
+				NULL);
 	if (ret)
 		goto err_put_runtime_pm;
 
@@ -3265,10 +3290,6 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
 	if (ret)
 		goto err_put_runtime_pm;
 
-	vc4_debugfs_add_file(drm, variant->debugfs_name,
-			     vc4_hdmi_debugfs_regs,
-			     vc4_hdmi);
-
 	pm_runtime_put_sync(dev);
 
 	return 0;
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.h b/drivers/gpu/drm/vc4/vc4_hdmi.h
index 51b27dcdcd9b..f6be92f33383 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.h
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
@@ -58,7 +58,8 @@ struct vc4_hdmi_variant {
 	/* Callback to get the resources (memory region, interrupts,
 	 * clocks, etc) for that variant.
 	 */
-	int (*init_resources)(struct vc4_hdmi *vc4_hdmi);
+	int (*init_resources)(struct drm_device *drm,
+			      struct vc4_hdmi *vc4_hdmi);
 
 	/* Callback to reset the HDMI block */
 	void (*reset)(struct vc4_hdmi *vc4_hdmi);
diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c
index b0906bb96c32..f336ada6c84c 100644
--- a/drivers/gpu/drm/vc4/vc4_hvs.c
+++ b/drivers/gpu/drm/vc4/vc4_hvs.c
@@ -693,6 +693,34 @@ static irqreturn_t vc4_hvs_irq_handler(int irq, void *data)
 	return irqret;
 }
 
+int vc4_hvs_debugfs_init(struct drm_minor *minor)
+{
+	struct drm_device *drm = minor->dev;
+	struct vc4_dev *vc4 = to_vc4_dev(drm);
+	struct vc4_hvs *hvs = vc4->hvs;
+	int ret;
+
+	if (!vc4->hvs)
+		return -ENODEV;
+
+	if (!hvs->hvs5)
+		debugfs_create_bool("hvs_load_tracker", S_IRUGO | S_IWUSR,
+				    minor->debugfs_root,
+				    &vc4->load_tracker_enabled);
+
+	ret = vc4_debugfs_add_file(minor, "hvs_underrun",
+				   vc4_hvs_debugfs_underrun, NULL);
+	if (ret)
+		return ret;
+
+	ret = vc4_debugfs_add_regset32(minor, "hvs_regs",
+				       &hvs->regset);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
 static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)
 {
 	struct platform_device *pdev = to_platform_device(dev);
@@ -821,10 +849,6 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)
 	if (ret)
 		return ret;
 
-	vc4_debugfs_add_regset32(drm, "hvs_regs", &hvs->regset);
-	vc4_debugfs_add_file(drm, "hvs_underrun", vc4_hvs_debugfs_underrun,
-			     NULL);
-
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c
index fee00b7003ab..19d3cfac40f2 100644
--- a/drivers/gpu/drm/vc4/vc4_txp.c
+++ b/drivers/gpu/drm/vc4/vc4_txp.c
@@ -396,6 +396,7 @@ static const struct drm_crtc_funcs vc4_txp_crtc_funcs = {
 	.atomic_destroy_state	= vc4_crtc_destroy_state,
 	.enable_vblank		= vc4_txp_enable_vblank,
 	.disable_vblank		= vc4_txp_disable_vblank,
+	.late_register		= vc4_crtc_late_register,
 };
 
 static int vc4_txp_atomic_check(struct drm_crtc *crtc,
@@ -538,8 +539,6 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data)
 
 	dev_set_drvdata(dev, txp);
 
-	vc4_debugfs_add_regset32(drm, "txp_regs", &vc4_crtc->regset);
-
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/vc4/vc4_v3d.c b/drivers/gpu/drm/vc4/vc4_v3d.c
index 7bb3067f8425..8fd16ece5b5c 100644
--- a/drivers/gpu/drm/vc4/vc4_v3d.c
+++ b/drivers/gpu/drm/vc4/vc4_v3d.c
@@ -386,6 +386,28 @@ static int vc4_v3d_runtime_resume(struct device *dev)
 }
 #endif
 
+int vc4_v3d_debugfs_init(struct drm_minor *minor)
+{
+	struct drm_device *drm = minor->dev;
+	struct vc4_dev *vc4 = to_vc4_dev(drm);
+	struct vc4_v3d *v3d = vc4->v3d;
+	int ret;
+
+	if (!vc4->v3d)
+		return -ENODEV;
+
+	ret = vc4_debugfs_add_file(minor, "v3d_ident",
+				   vc4_v3d_debugfs_ident, NULL);
+	if (ret)
+		return ret;
+
+	ret = vc4_debugfs_add_regset32(minor, "v3d_regs", &v3d->regset);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
 static int vc4_v3d_bind(struct device *dev, struct device *master, void *data)
 {
 	struct platform_device *pdev = to_platform_device(dev);
@@ -462,9 +484,6 @@ static int vc4_v3d_bind(struct device *dev, struct device *master, void *data)
 	pm_runtime_set_autosuspend_delay(dev, 40); /* a little over 2 frames. */
 	pm_runtime_enable(dev);
 
-	vc4_debugfs_add_file(drm, "v3d_ident", vc4_v3d_debugfs_ident, NULL);
-	vc4_debugfs_add_regset32(drm, "v3d_regs", &v3d->regset);
-
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c
index a75b82de3796..4502a83ad16f 100644
--- a/drivers/gpu/drm/vc4/vc4_vec.c
+++ b/drivers/gpu/drm/vc4/vc4_vec.c
@@ -523,6 +523,24 @@ static const struct drm_encoder_helper_funcs vc4_vec_encoder_helper_funcs = {
 	.atomic_mode_set = vc4_vec_encoder_atomic_mode_set,
 };
 
+static int vc4_vec_late_register(struct drm_encoder *encoder)
+{
+	struct drm_device *drm = encoder->dev;
+	struct vc4_vec *vec = encoder_to_vc4_vec(encoder);
+	int ret;
+
+	ret = vc4_debugfs_add_regset32(drm->primary, "vec_regs",
+				       &vec->regset);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static const struct drm_encoder_funcs vc4_vec_encoder_funcs = {
+	.late_register = vc4_vec_late_register,
+};
+
 static const struct vc4_vec_variant bcm2835_vec_variant = {
 	.dac_config = VEC_DAC_CONFIG_DAC_CTRL(0xc) |
 		      VEC_DAC_CONFIG_DRIVER_CTRL(0xc) |
@@ -587,7 +605,10 @@ static int vc4_vec_bind(struct device *dev, struct device *master, void *data)
 	if (ret)
 		return ret;
 
-	ret = drmm_simple_encoder_init(drm, &vec->encoder.base, DRM_MODE_ENCODER_TVDAC);
+	ret = drmm_encoder_init(drm, &vec->encoder.base,
+				&vc4_vec_encoder_funcs,
+				DRM_MODE_ENCODER_TVDAC,
+				NULL);
 	if (ret)
 		return ret;
 
@@ -599,8 +620,6 @@ static int vc4_vec_bind(struct device *dev, struct device *master, void *data)
 
 	dev_set_drvdata(dev, vec);
 
-	vc4_debugfs_add_regset32(drm, "vec_regs", &vec->regset);
-
 	return 0;
 }
 
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 60/64] drm/vc4: Switch to drmm_mutex_init
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (58 preceding siblings ...)
  2022-06-10  9:29 ` [PATCH 59/64] drm/vc4: debugfs: Simplify debugfs registration Maxime Ripard
@ 2022-06-10  9:29 ` Maxime Ripard
  2022-06-10  9:29 ` [PATCH 61/64] drm/vc4: perfmon: Add missing mutex_destroy Maxime Ripard
                   ` (3 subsequent siblings)
  63 siblings, 0 replies; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:29 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

mutex_init is supposed to be balanced by a call to mutex_destroy that we
were never doing in the vc4 driver.

Since a DRM-managed mutex_init variant has been introduced, let's just
switch to it.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_bo.c   | 15 +++++++++++++--
 drivers/gpu/drm/vc4/vc4_drv.c  |  4 +++-
 drivers/gpu/drm/vc4/vc4_gem.c  | 10 ++++++++--
 drivers/gpu/drm/vc4/vc4_hdmi.c |  5 ++++-
 4 files changed, 28 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c
index 68fe9903947d..f52c2cd85650 100644
--- a/drivers/gpu/drm/vc4/vc4_bo.c
+++ b/drivers/gpu/drm/vc4/vc4_bo.c
@@ -386,6 +386,7 @@ struct drm_gem_object *vc4_create_object(struct drm_device *dev, size_t size)
 {
 	struct vc4_dev *vc4 = to_vc4_dev(dev);
 	struct vc4_bo *bo;
+	int ret;
 
 	bo = kzalloc(sizeof(*bo), GFP_KERNEL);
 	if (!bo)
@@ -393,7 +394,11 @@ struct drm_gem_object *vc4_create_object(struct drm_device *dev, size_t size)
 
 	bo->madv = VC4_MADV_WILLNEED;
 	refcount_set(&bo->usecnt, 0);
-	mutex_init(&bo->madv_lock);
+
+	ret = drmm_mutex_init(dev, &bo->madv_lock);
+	if (ret)
+		return ERR_PTR(ret);
+
 	mutex_lock(&vc4->bo_lock);
 	bo->label = VC4_BO_TYPE_KERNEL;
 	vc4->bo_labels[VC4_BO_TYPE_KERNEL].num_allocated++;
@@ -963,6 +968,7 @@ static void vc4_bo_cache_destroy(struct drm_device *dev, void *unused);
 int vc4_bo_cache_init(struct drm_device *dev)
 {
 	struct vc4_dev *vc4 = to_vc4_dev(dev);
+	int ret;
 	int i;
 
 	/* Create the initial set of BO labels that the kernel will
@@ -979,7 +985,12 @@ int vc4_bo_cache_init(struct drm_device *dev)
 	for (i = 0; i < VC4_BO_TYPE_COUNT; i++)
 		vc4->bo_labels[i].name = bo_type_names[i];
 
-	mutex_init(&vc4->bo_lock);
+	ret = drmm_mutex_init(dev, &vc4->bo_lock);
+	if (ret) {
+		kfree(vc4->bo_labels);
+		return ret;
+	}
+
 	INIT_LIST_HEAD(&vc4->bo_cache.time_list);
 
 	INIT_WORK(&vc4->bo_cache.time_work, vc4_bo_cache_time_work);
diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c
index 031f2cdd658d..df3b92d06bd0 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.c
+++ b/drivers/gpu/drm/vc4/vc4_drv.c
@@ -243,7 +243,9 @@ static int vc4_drm_bind(struct device *dev)
 	platform_set_drvdata(pdev, drm);
 	INIT_LIST_HEAD(&vc4->debugfs_list);
 
-	mutex_init(&vc4->bin_bo_lock);
+	ret = drmm_mutex_init(drm, &vc4->bin_bo_lock);
+	if (ret)
+		return ret;
 
 	ret = vc4_bo_cache_init(drm);
 	if (ret)
diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c
index 9eaf304fc20d..45f96409a72e 100644
--- a/drivers/gpu/drm/vc4/vc4_gem.c
+++ b/drivers/gpu/drm/vc4/vc4_gem.c
@@ -1275,6 +1275,7 @@ static void vc4_gem_destroy(struct drm_device *dev, void *unused);
 int vc4_gem_init(struct drm_device *dev)
 {
 	struct vc4_dev *vc4 = to_vc4_dev(dev);
+	int ret;
 
 	vc4->dma_fence_context = dma_fence_context_alloc(1);
 
@@ -1289,10 +1290,15 @@ int vc4_gem_init(struct drm_device *dev)
 
 	INIT_WORK(&vc4->job_done_work, vc4_job_done_work);
 
-	mutex_init(&vc4->power_lock);
+	ret = drmm_mutex_init(dev, &vc4->power_lock);
+	if (ret)
+		return ret;
 
 	INIT_LIST_HEAD(&vc4->purgeable.list);
-	mutex_init(&vc4->purgeable.lock);
+
+	ret = drmm_mutex_init(dev, &vc4->purgeable.lock);
+	if (ret)
+		return ret;
 
 	return drmm_add_action_or_reset(dev, vc4_gem_destroy, NULL);
 }
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index 2092231001c8..6672542811c9 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -3173,7 +3173,10 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
 	if (!vc4_hdmi)
 		return -ENOMEM;
 
-	mutex_init(&vc4_hdmi->mutex);
+	ret = drmm_mutex_init(drm, &vc4_hdmi->mutex);
+	if (ret)
+		return ret;
+
 	spin_lock_init(&vc4_hdmi->hw_lock);
 	INIT_DELAYED_WORK(&vc4_hdmi->scrambling_work, vc4_hdmi_scrambling_wq);
 
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 61/64] drm/vc4: perfmon: Add missing mutex_destroy
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (59 preceding siblings ...)
  2022-06-10  9:29 ` [PATCH 60/64] drm/vc4: Switch to drmm_mutex_init Maxime Ripard
@ 2022-06-10  9:29 ` Maxime Ripard
  2022-06-10  9:29 ` [PATCH 62/64] drm/vc4: v3d: Stop disabling interrupts Maxime Ripard
                   ` (2 subsequent siblings)
  63 siblings, 0 replies; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:29 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

vc4_perfmon_open_file() will instantiate a mutex for that file instance,
but we never call mutex_destroy () in vc4_perfmon_close_file().

Let's add that missing call.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_perfmon.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/gpu/drm/vc4/vc4_perfmon.c b/drivers/gpu/drm/vc4/vc4_perfmon.c
index 18abc06335c1..531b0e7ba035 100644
--- a/drivers/gpu/drm/vc4/vc4_perfmon.c
+++ b/drivers/gpu/drm/vc4/vc4_perfmon.c
@@ -95,6 +95,7 @@ void vc4_perfmon_close_file(struct vc4_file *vc4file)
 	idr_for_each(&vc4file->perfmon.idr, vc4_perfmon_idr_del, NULL);
 	idr_destroy(&vc4file->perfmon.idr);
 	mutex_unlock(&vc4file->perfmon.lock);
+	mutex_destroy(&vc4file->perfmon.lock);
 }
 
 int vc4_perfmon_create_ioctl(struct drm_device *dev, void *data,
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 62/64] drm/vc4: v3d: Stop disabling interrupts
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (60 preceding siblings ...)
  2022-06-10  9:29 ` [PATCH 61/64] drm/vc4: perfmon: Add missing mutex_destroy Maxime Ripard
@ 2022-06-10  9:29 ` Maxime Ripard
  2022-06-10  9:29 ` [PATCH 63/64] drm/vc4: v3d: Rework the runtime_pm setup Maxime Ripard
  2022-06-10  9:29 ` [PATCH 64/64] drm/vc4: v3d: Switch to devm_pm_runtime_enable Maxime Ripard
  63 siblings, 0 replies; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:29 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

The vc4_irq_disable(), among other things, will call disable_irq() to
complete any in-flight interrupts.

This requires its counterpart, vc4_irq_enable(), to call enable_irq() which
causes issues addressed in a later patch.

However, vc4_irq_disable() is called by two callees: vc4_irq_uninstall()
and vc4_v3d_runtime_suspend().

vc4_irq_uninstall() also calls free_irq() which already disables the
interrupt line. We thus don't require an explicit disable_irq() for that
call site.

vc4_v3d_runtime_suspend() doesn't have any other code. However, the rest of
vc4_irq_disable() masks the interrupts coming from the v3d, so explictly
disabling the interrupt line is also redundant.

The only thing we really care about is thus to make sure we don't have any
handler in-flight, as suggested by the comment. We can thus replace
disable_irq() by synchronize_irq().

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_irq.c | 2 +-
 drivers/gpu/drm/vc4/vc4_v3d.c | 2 --
 2 files changed, 1 insertion(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_irq.c b/drivers/gpu/drm/vc4/vc4_irq.c
index 4342fb43e8c1..1af0560ed16c 100644
--- a/drivers/gpu/drm/vc4/vc4_irq.c
+++ b/drivers/gpu/drm/vc4/vc4_irq.c
@@ -289,7 +289,7 @@ vc4_irq_disable(struct drm_device *dev)
 	V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS);
 
 	/* Finish any interrupt handler still in flight. */
-	disable_irq(vc4->irq);
+	synchronize_irq(vc4->irq);
 
 	cancel_work_sync(&vc4->overflow_mem_work);
 }
diff --git a/drivers/gpu/drm/vc4/vc4_v3d.c b/drivers/gpu/drm/vc4/vc4_v3d.c
index 8fd16ece5b5c..ad0dac62deb2 100644
--- a/drivers/gpu/drm/vc4/vc4_v3d.c
+++ b/drivers/gpu/drm/vc4/vc4_v3d.c
@@ -378,8 +378,6 @@ static int vc4_v3d_runtime_resume(struct device *dev)
 
 	vc4_v3d_init_hw(&vc4->base);
 
-	/* We disabled the IRQ as part of vc4_irq_uninstall in suspend. */
-	enable_irq(vc4->irq);
 	vc4_irq_enable(&vc4->base);
 
 	return 0;
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 63/64] drm/vc4: v3d: Rework the runtime_pm setup
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (61 preceding siblings ...)
  2022-06-10  9:29 ` [PATCH 62/64] drm/vc4: v3d: Stop disabling interrupts Maxime Ripard
@ 2022-06-10  9:29 ` Maxime Ripard
  2022-06-10  9:29 ` [PATCH 64/64] drm/vc4: v3d: Switch to devm_pm_runtime_enable Maxime Ripard
  63 siblings, 0 replies; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:29 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

At bind time, vc4_v3d_bind() will read a register to retrieve the v3d
version and make sure it's a version we're compatible with.

However, the v3d has an optional clock that is enabled only after the
register read-out and a power domain that wasn't enabled at all in the bind
implementation. This was working fine at boot because both were enabled,
but resulted in the version check failing if we were unbinding and
rebinding the driver because the unbinding would have turned them off.

The fix isn't as easy as calling pm_runtime_resume_and_get() prior to the
register access to power up the power domain though.

Indeed, the runtime_resume implementation will enable the clock mentioned
above, call vc4_v3d_init_hw() and then vc4_irq_enable().

Prior to the previous patch, vc4_irq_enable() needed to occur after our
call to platform_get_irq() and vc4_irq_install(), since vc4_irq_enable()
used to call enable_irq() and vc4_irq_install() will call request_irq().

vc4_irq_install() will also do some register access, so needs the power
domain to be on. So we ended up in a situation where
vc4_v3d_runtime_resume() needed vc4_irq_install() to have been called
before, and vc4_irq_install() needed vc4_v3d_runtime_resume().

The previous patch removed the enable_irq() call in vc4_irq_enable() and
thus removed the dependency of vc4_v3d_runtime_resume() on
vc4_irq_install().

Thus, we can now rework our bind implementation to call
pm_runtime_resume_and_get() before our register access to make sure the
power domain is on. vc4_v3d_runtime_resume() also takes care of turning the
clock on and calling vc4_v3d_init_hw() so we can remove them from bind.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_v3d.c | 37 +++++++++++++++++++++--------------
 1 file changed, 22 insertions(+), 15 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_v3d.c b/drivers/gpu/drm/vc4/vc4_v3d.c
index ad0dac62deb2..a3fcabf5e6ab 100644
--- a/drivers/gpu/drm/vc4/vc4_v3d.c
+++ b/drivers/gpu/drm/vc4/vc4_v3d.c
@@ -448,41 +448,48 @@ static int vc4_v3d_bind(struct device *dev, struct device *master, void *data)
 		}
 	}
 
+	ret = platform_get_irq(pdev, 0);
+	if (ret < 0)
+		return ret;
+	vc4->irq = ret;
+
+	pm_runtime_enable(dev);
+
+	ret = pm_runtime_resume_and_get(dev);
+	if (ret)
+		goto err_disable_runtime_pm;
+
 	if (V3D_READ(V3D_IDENT0) != V3D_EXPECTED_IDENT0) {
 		DRM_ERROR("V3D_IDENT0 read 0x%08x instead of 0x%08x\n",
 			  V3D_READ(V3D_IDENT0), V3D_EXPECTED_IDENT0);
-		return -EINVAL;
+		ret = -EINVAL;
+		goto err_put_runtime_pm;
 	}
 
-	ret = clk_prepare_enable(v3d->clk);
-	if (ret != 0)
-		return ret;
-
 	/* Reset the binner overflow address/size at setup, to be sure
 	 * we don't reuse an old one.
 	 */
 	V3D_WRITE(V3D_BPOA, 0);
 	V3D_WRITE(V3D_BPOS, 0);
 
-	vc4_v3d_init_hw(drm);
-
-	ret = platform_get_irq(pdev, 0);
-	if (ret < 0)
-		return ret;
-	vc4->irq = ret;
-
 	ret = vc4_irq_install(drm, vc4->irq);
 	if (ret) {
 		DRM_ERROR("Failed to install IRQ handler\n");
-		return ret;
+		goto err_put_runtime_pm;
 	}
 
-	pm_runtime_set_active(dev);
 	pm_runtime_use_autosuspend(dev);
 	pm_runtime_set_autosuspend_delay(dev, 40); /* a little over 2 frames. */
-	pm_runtime_enable(dev);
 
 	return 0;
+
+err_put_runtime_pm:
+	pm_runtime_put(dev);
+
+err_disable_runtime_pm:
+	pm_runtime_disable(dev);
+
+	return ret;
 }
 
 static void vc4_v3d_unbind(struct device *dev, struct device *master,
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* [PATCH 64/64] drm/vc4: v3d: Switch to devm_pm_runtime_enable
  2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (62 preceding siblings ...)
  2022-06-10  9:29 ` [PATCH 63/64] drm/vc4: v3d: Rework the runtime_pm setup Maxime Ripard
@ 2022-06-10  9:29 ` Maxime Ripard
  63 siblings, 0 replies; 124+ messages in thread
From: Maxime Ripard @ 2022-06-10  9:29 UTC (permalink / raw)
  To: Daniel Vetter, David Airlie, Maarten Lankhorst,
	Thomas Zimmermann, Maxime Ripard
  Cc: dri-devel

devm_pm_runtime_enable() simplifies the driver a bit since it will call
pm_runtime_disable() automatically through a device-managed action.

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_v3d.c | 11 ++++-------
 1 file changed, 4 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_v3d.c b/drivers/gpu/drm/vc4/vc4_v3d.c
index a3fcabf5e6ab..2d63124e2ac0 100644
--- a/drivers/gpu/drm/vc4/vc4_v3d.c
+++ b/drivers/gpu/drm/vc4/vc4_v3d.c
@@ -453,11 +453,13 @@ static int vc4_v3d_bind(struct device *dev, struct device *master, void *data)
 		return ret;
 	vc4->irq = ret;
 
-	pm_runtime_enable(dev);
+	ret = devm_pm_runtime_enable(dev);
+	if (ret)
+		return ret;
 
 	ret = pm_runtime_resume_and_get(dev);
 	if (ret)
-		goto err_disable_runtime_pm;
+		return ret;
 
 	if (V3D_READ(V3D_IDENT0) != V3D_EXPECTED_IDENT0) {
 		DRM_ERROR("V3D_IDENT0 read 0x%08x instead of 0x%08x\n",
@@ -486,9 +488,6 @@ static int vc4_v3d_bind(struct device *dev, struct device *master, void *data)
 err_put_runtime_pm:
 	pm_runtime_put(dev);
 
-err_disable_runtime_pm:
-	pm_runtime_disable(dev);
-
 	return ret;
 }
 
@@ -498,8 +497,6 @@ static void vc4_v3d_unbind(struct device *dev, struct device *master,
 	struct drm_device *drm = dev_get_drvdata(master);
 	struct vc4_dev *vc4 = to_vc4_dev(drm);
 
-	pm_runtime_disable(dev);
-
 	vc4_irq_uninstall(drm);
 
 	/* Disable the binner's overflow memory address, so the next
-- 
2.36.1


^ permalink raw reply related	[flat|nested] 124+ messages in thread

* Re: [PATCH 02/64] drm/crtc: Introduce drmm_crtc_init_with_planes
  2022-06-10  9:28 ` [PATCH 02/64] drm/crtc: Introduce drmm_crtc_init_with_planes Maxime Ripard
@ 2022-06-13 11:23   ` Thomas Zimmermann
  2022-06-14  7:37     ` Maxime Ripard
  0 siblings, 1 reply; 124+ messages in thread
From: Thomas Zimmermann @ 2022-06-13 11:23 UTC (permalink / raw)
  To: Maxime Ripard, Daniel Vetter, David Airlie, Maarten Lankhorst; +Cc: dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 5518 bytes --]

Hi Maxime

Am 10.06.22 um 11:28 schrieb Maxime Ripard:
> The DRM-managed function to register a CRTC is
> drmm_crtc_alloc_with_planes(), which will allocate the underlying
> structure and initialisation the CRTC.
> 
> However, we might want to separate the structure creation and the CRTC
> initialisation, for example if the structure is shared across multiple
> DRM entities, for example an encoder and a connector.
> 
> Let's create an helper to only initialise a CRTC that would be passed as
> an argument.

Before I review all of thes patches, one question. it's yet not clear to 
me why drm_crtc_init_with_planes() wouldn't work. (I know we discussed 
this on IRC.)

If you're calling drmm_mode_config_init(), it will clean up all the 
CRTC, encoder connector, etc. data structures for you. For CRTC 
instances in kmalloced memory, wouldn't it be simpler to put the 
corresponding kfree into vc4_crtc_destroy()?

It seems only useful if you need it strictly ordered with drmm_kzalloc()?

Best regards
Thomas

> 
> Signed-off-by: Maxime Ripard <maxime@cerno.tech>
> ---
>   drivers/gpu/drm/drm_crtc.c | 70 ++++++++++++++++++++++++++++++++++++--
>   include/drm/drm_crtc.h     |  6 ++++
>   2 files changed, 73 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
> index 26a77a735905..fd986a7dd4ad 100644
> --- a/drivers/gpu/drm/drm_crtc.c
> +++ b/drivers/gpu/drm/drm_crtc.c
> @@ -341,9 +341,10 @@ static int __drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *
>    * The @primary and @cursor planes are only relevant for legacy uAPI, see
>    * &drm_crtc.primary and &drm_crtc.cursor.
>    *
> - * Note: consider using drmm_crtc_alloc_with_planes() instead of
> - * drm_crtc_init_with_planes() to let the DRM managed resource infrastructure
> - * take care of cleanup and deallocation.
> + * Note: consider using drmm_crtc_alloc_with_planes() or
> + * drmm_crtc_init_with_planes() instead of drm_crtc_init_with_planes()
> + * to let the DRM managed resource infrastructure take care of cleanup
> + * and deallocation.
>    *
>    * Returns:
>    * Zero on success, error code on failure.
> @@ -368,6 +369,69 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
>   }
>   EXPORT_SYMBOL(drm_crtc_init_with_planes);
>   
> +static void drmm_crtc_init_with_planes_cleanup(struct drm_device *dev,
> +					       void *ptr)
> +{
> +	struct drm_crtc *crtc = ptr;
> +
> +	drm_crtc_cleanup(crtc);
> +}
> +
> +/**
> + * drmm_crtc_init_with_planes - Initialise a new CRTC object with
> + *    specified primary and cursor planes.
> + * @dev: DRM device
> + * @crtc: CRTC object to init
> + * @primary: Primary plane for CRTC
> + * @cursor: Cursor plane for CRTC
> + * @funcs: callbacks for the new CRTC
> + * @name: printf style format string for the CRTC name, or NULL for default name
> + *
> + * Inits a new object created as base part of a driver crtc object. Drivers
> + * should use this function instead of drm_crtc_init(), which is only provided
> + * for backwards compatibility with drivers which do not yet support universal
> + * planes). For really simple hardware which has only 1 plane look at
> + * drm_simple_display_pipe_init() instead.
> + *
> + * Cleanup is automatically handled through registering
> + * drmm_crtc_cleanup() with drmm_add_action(). The crtc structure should
> + * be allocated with drmm_kzalloc().
> + *
> + * The @drm_crtc_funcs.destroy hook must be NULL.
> + *
> + * The @primary and @cursor planes are only relevant for legacy uAPI, see
> + * &drm_crtc.primary and &drm_crtc.cursor.
> + *
> + * Returns:
> + * Zero on success, error code on failure.
> + */
> +int drmm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
> +			       struct drm_plane *primary,
> +			       struct drm_plane *cursor,
> +			       const struct drm_crtc_funcs *funcs,
> +			       const char *name, ...)
> +{
> +	va_list ap;
> +	int ret;
> +
> +	WARN_ON(funcs && funcs->destroy);
> +
> +	va_start(ap, name);
> +	ret = __drm_crtc_init_with_planes(dev, crtc, primary, cursor, funcs,
> +					  name, ap);
> +	va_end(ap);
> +	if (ret)
> +		return ret;
> +
> +	ret = drmm_add_action_or_reset(dev, drmm_crtc_init_with_planes_cleanup,
> +				       crtc);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(drmm_crtc_init_with_planes);
> +
>   static void drmm_crtc_alloc_with_planes_cleanup(struct drm_device *dev,
>   						void *ptr)
>   {
> diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
> index a70baea0636c..2babd5cffbf3 100644
> --- a/include/drm/drm_crtc.h
> +++ b/include/drm/drm_crtc.h
> @@ -1229,6 +1229,12 @@ int drm_crtc_init_with_planes(struct drm_device *dev,
>   			      struct drm_plane *cursor,
>   			      const struct drm_crtc_funcs *funcs,
>   			      const char *name, ...);
> +int drmm_crtc_init_with_planes(struct drm_device *dev,
> +			       struct drm_crtc *crtc,
> +			       struct drm_plane *primary,
> +			       struct drm_plane *cursor,
> +			       const struct drm_crtc_funcs *funcs,
> +			       const char *name, ...);
>   void drm_crtc_cleanup(struct drm_crtc *crtc);
>   
>   __printf(7, 8)

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Ivo Totev

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 02/64] drm/crtc: Introduce drmm_crtc_init_with_planes
  2022-06-13 11:23   ` Thomas Zimmermann
@ 2022-06-14  7:37     ` Maxime Ripard
  2022-06-14  8:29       ` Thomas Zimmermann
  0 siblings, 1 reply; 124+ messages in thread
From: Maxime Ripard @ 2022-06-14  7:37 UTC (permalink / raw)
  To: Thomas Zimmermann; +Cc: David Airlie, Daniel Vetter, dri-devel

Hi Thomas,

On Mon, Jun 13, 2022 at 01:23:54PM +0200, Thomas Zimmermann wrote:
> Am 10.06.22 um 11:28 schrieb Maxime Ripard:
> > The DRM-managed function to register a CRTC is
> > drmm_crtc_alloc_with_planes(), which will allocate the underlying
> > structure and initialisation the CRTC.
> > 
> > However, we might want to separate the structure creation and the CRTC
> > initialisation, for example if the structure is shared across multiple
> > DRM entities, for example an encoder and a connector.
> > 
> > Let's create an helper to only initialise a CRTC that would be passed as
> > an argument.
> 
> Before I review all of thes patches, one question. it's yet not clear to me
> why drm_crtc_init_with_planes() wouldn't work. (I know we discussed this on
> IRC.)
> 
> If you're calling drmm_mode_config_init(), it will clean up all the CRTC,
> encoder connector, etc. data structures for you. For CRTC instances in
> kmalloced memory, wouldn't it be simpler to put the corresponding kfree into
> vc4_crtc_destroy()?

My intent was to remove as much of the lifetime handling and
memory-management from drivers as possible.

My feeling is that while the solution you suggest would definitely work,
it relies on drivers authors to know about this, and make the effort to
convert the drivers themselves. And then we would have to review that,
which we will probably miss (collectively), because it's a bit obscure.

While with either the drmm_alloc_* functions, or the new functions I
introduce there, we can just deprecate the old ones, create a TODO entry
and a coccinelle script and trust that it works properly.

Maxime

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 02/64] drm/crtc: Introduce drmm_crtc_init_with_planes
  2022-06-14  7:37     ` Maxime Ripard
@ 2022-06-14  8:29       ` Thomas Zimmermann
  2022-06-14  9:04         ` Maxime Ripard
  0 siblings, 1 reply; 124+ messages in thread
From: Thomas Zimmermann @ 2022-06-14  8:29 UTC (permalink / raw)
  To: Maxime Ripard; +Cc: David Airlie, Daniel Vetter, dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 2189 bytes --]

Hi

Am 14.06.22 um 09:37 schrieb Maxime Ripard:
> Hi Thomas,
> 
> On Mon, Jun 13, 2022 at 01:23:54PM +0200, Thomas Zimmermann wrote:
>> Am 10.06.22 um 11:28 schrieb Maxime Ripard:
>>> The DRM-managed function to register a CRTC is
>>> drmm_crtc_alloc_with_planes(), which will allocate the underlying
>>> structure and initialisation the CRTC.
>>>
>>> However, we might want to separate the structure creation and the CRTC
>>> initialisation, for example if the structure is shared across multiple
>>> DRM entities, for example an encoder and a connector.
>>>
>>> Let's create an helper to only initialise a CRTC that would be passed as
>>> an argument.
>>
>> Before I review all of thes patches, one question. it's yet not clear to me
>> why drm_crtc_init_with_planes() wouldn't work. (I know we discussed this on
>> IRC.)
>>
>> If you're calling drmm_mode_config_init(), it will clean up all the CRTC,
>> encoder connector, etc. data structures for you. For CRTC instances in
>> kmalloced memory, wouldn't it be simpler to put the corresponding kfree into
>> vc4_crtc_destroy()?
> 
> My intent was to remove as much of the lifetime handling and
> memory-management from drivers as possible.
> 
> My feeling is that while the solution you suggest would definitely work,
> it relies on drivers authors to know about this, and make the effort to
> convert the drivers themselves. And then we would have to review that,
> which we will probably miss (collectively), because it's a bit obscure.
> 
> While with either the drmm_alloc_* functions, or the new functions I
> introduce there, we can just deprecate the old ones, create a TODO entry
> and a coccinelle script and trust that it works properly.

Thanks for explaining the motivation.

I would not want to deprecate any of the existing functions, as they 
work for many drivers. The drmm_ functions add additional overhead that 
not everyone is willing to pay.

Best regards
Thomas

> 
> Maxime

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Ivo Totev

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 02/64] drm/crtc: Introduce drmm_crtc_init_with_planes
  2022-06-14  8:29       ` Thomas Zimmermann
@ 2022-06-14  9:04         ` Maxime Ripard
  2022-06-14 11:47           ` Thomas Zimmermann
  0 siblings, 1 reply; 124+ messages in thread
From: Maxime Ripard @ 2022-06-14  9:04 UTC (permalink / raw)
  To: Thomas Zimmermann; +Cc: David Airlie, Daniel Vetter, dri-devel

[-- Attachment #1: Type: text/plain, Size: 2762 bytes --]

On Tue, Jun 14, 2022 at 10:29:20AM +0200, Thomas Zimmermann wrote:
> Hi
> 
> Am 14.06.22 um 09:37 schrieb Maxime Ripard:
> > Hi Thomas,
> > 
> > On Mon, Jun 13, 2022 at 01:23:54PM +0200, Thomas Zimmermann wrote:
> > > Am 10.06.22 um 11:28 schrieb Maxime Ripard:
> > > > The DRM-managed function to register a CRTC is
> > > > drmm_crtc_alloc_with_planes(), which will allocate the underlying
> > > > structure and initialisation the CRTC.
> > > > 
> > > > However, we might want to separate the structure creation and the CRTC
> > > > initialisation, for example if the structure is shared across multiple
> > > > DRM entities, for example an encoder and a connector.
> > > > 
> > > > Let's create an helper to only initialise a CRTC that would be passed as
> > > > an argument.
> > > 
> > > Before I review all of thes patches, one question. it's yet not clear to me
> > > why drm_crtc_init_with_planes() wouldn't work. (I know we discussed this on
> > > IRC.)
> > > 
> > > If you're calling drmm_mode_config_init(), it will clean up all the CRTC,
> > > encoder connector, etc. data structures for you. For CRTC instances in
> > > kmalloced memory, wouldn't it be simpler to put the corresponding kfree into
> > > vc4_crtc_destroy()?
> > 
> > My intent was to remove as much of the lifetime handling and
> > memory-management from drivers as possible.
> > 
> > My feeling is that while the solution you suggest would definitely work,
> > it relies on drivers authors to know about this, and make the effort to
> > convert the drivers themselves. And then we would have to review that,
> > which we will probably miss (collectively), because it's a bit obscure.
> > 
> > While with either the drmm_alloc_* functions, or the new functions I
> > introduce there, we can just deprecate the old ones, create a TODO entry
> > and a coccinelle script and trust that it works properly.
> 
> Thanks for explaining the motivation.
> 
> I would not want to deprecate any of the existing functions, as they work
> for many drivers. The drmm_ functions add additional overhead that not
> everyone is willing to pay.

I'm a bit confused. drm_crtc_init_with_planes() at the moment has:

* Note: consider using drmm_crtc_alloc_with_planes() instead of
* drm_crtc_init_with_planes() to let the DRM managed resource infrastructure
* take care of cleanup and deallocation.

Just like drm_encoder_init(), drm_simple_encoder_init() and
drm_universal_plane_init(), so all the functions we have a drmm_* helper
for.

And we have a TODO-list item that heavily hints at using them:
https://dri.freedesktop.org/docs/drm/gpu/todo.html#object-lifetime-fixes

So it looks like we're already well on the deprecation path?

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 02/64] drm/crtc: Introduce drmm_crtc_init_with_planes
  2022-06-14  9:04         ` Maxime Ripard
@ 2022-06-14 11:47           ` Thomas Zimmermann
  2022-06-14 12:09             ` Maxime Ripard
  0 siblings, 1 reply; 124+ messages in thread
From: Thomas Zimmermann @ 2022-06-14 11:47 UTC (permalink / raw)
  To: Maxime Ripard; +Cc: David Airlie, Daniel Vetter, dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 5152 bytes --]

Hi Maxime

Am 14.06.22 um 11:04 schrieb Maxime Ripard:
> On Tue, Jun 14, 2022 at 10:29:20AM +0200, Thomas Zimmermann wrote:
>> Hi
>>
>> Am 14.06.22 um 09:37 schrieb Maxime Ripard:
>>> Hi Thomas,
>>>
>>> On Mon, Jun 13, 2022 at 01:23:54PM +0200, Thomas Zimmermann wrote:
>>>> Am 10.06.22 um 11:28 schrieb Maxime Ripard:
>>>>> The DRM-managed function to register a CRTC is
>>>>> drmm_crtc_alloc_with_planes(), which will allocate the underlying
>>>>> structure and initialisation the CRTC.
>>>>>
>>>>> However, we might want to separate the structure creation and the CRTC
>>>>> initialisation, for example if the structure is shared across multiple
>>>>> DRM entities, for example an encoder and a connector.
>>>>>
>>>>> Let's create an helper to only initialise a CRTC that would be passed as
>>>>> an argument.
>>>>
>>>> Before I review all of thes patches, one question. it's yet not clear to me
>>>> why drm_crtc_init_with_planes() wouldn't work. (I know we discussed this on
>>>> IRC.)
>>>>
>>>> If you're calling drmm_mode_config_init(), it will clean up all the CRTC,
>>>> encoder connector, etc. data structures for you. For CRTC instances in
>>>> kmalloced memory, wouldn't it be simpler to put the corresponding kfree into
>>>> vc4_crtc_destroy()?
>>>
>>> My intent was to remove as much of the lifetime handling and
>>> memory-management from drivers as possible.
>>>
>>> My feeling is that while the solution you suggest would definitely work,
>>> it relies on drivers authors to know about this, and make the effort to
>>> convert the drivers themselves. And then we would have to review that,
>>> which we will probably miss (collectively), because it's a bit obscure.
>>>
>>> While with either the drmm_alloc_* functions, or the new functions I
>>> introduce there, we can just deprecate the old ones, create a TODO entry
>>> and a coccinelle script and trust that it works properly.
>>
>> Thanks for explaining the motivation.
>>
>> I would not want to deprecate any of the existing functions, as they work
>> for many drivers. The drmm_ functions add additional overhead that not
>> everyone is willing to pay.
> 
> I'm a bit confused. drm_crtc_init_with_planes() at the moment has:
> 
> * Note: consider using drmm_crtc_alloc_with_planes() instead of
> * drm_crtc_init_with_planes() to let the DRM managed resource infrastructure
> * take care of cleanup and deallocation.
> 
> Just like drm_encoder_init(), drm_simple_encoder_init() and
> drm_universal_plane_init(), so all the functions we have a drmm_* helper
> for.
> 
> And we have a TODO-list item that heavily hints at using them:
> https://dri.freedesktop.org/docs/drm/gpu/todo.html#object-lifetime-fixes
> 
> So it looks like we're already well on the deprecation path?

AFAIU that TODO item is about replacing devm_kzalloc() with 
drmm_kzalloc(). It's not about the plain init functions, such as 
drm_crtc_init_with_planes() or drm_universal_plane_init(). Many simple 
drivers allocate their modesetting pipeline's components first and then 
build the pipeline with the drm_ functions. I don't think we can take 
that away from them.

The concern I have is that we're adding lots of helpers for all kind of 
scenarios and end up with a lot of duplication (and fragmentation among 
drivers). For example, drmm_crtc_alloc_with_planes() really isn't much 
used by anything. [1] Same for drmm_universal_plane_alloc(). [2]

Instead of adding new helpers, it would be better to build upon 
drmm_crtc_alloc_with_planes(), drmm_univeral_plane_alloc(), etc.

For example, a good starting point would be vc4_plane_init(). It could 
alloc with drmm_univeral_plane_alloc(), which would replace 
devm_kzalloc() [3] and drm_univeral_plane_alloc() [4] in one step. From 
what I understand, that's what your patchset wants to do. But it looks 
like you're effectively open-coding drmm_universl_plane_alloc().

With vc4_plane_init() correctly managed, the next candidate could be 
vc4_crtc_init(). You probably want to pull vc4_plane_init() [5] into 
callers. to get it out of the way. If you move calls to devm_kzalloc() 
[6] and drm_crtc_init_with_planes() [7] closer together, you can replace 
them with drmm_crtc_alloc_with_planes().

Best regards
Thomas


[1] 
https://elixir.bootlin.com/linux/latest/C/ident/drmm_crtc_alloc_with_planes
[2] 
https://elixir.bootlin.com/linux/latest/A/ident/drmm_universal_plane_alloc
[3] 
https://elixir.bootlin.com/linux/v5.18.3/source/drivers/gpu/drm/vc4/vc4_plane.c#L1478
[4] 
https://elixir.bootlin.com/linux/v5.18.3/source/drivers/gpu/drm/vc4/vc4_plane.c#L1491
[5] 
https://elixir.bootlin.com/linux/latest/source/drivers/gpu/drm/vc4/vc4_crtc.c#L1135
[6] 
https://elixir.bootlin.com/linux/v5.18.3/source/drivers/gpu/drm/vc4/vc4_crtc.c#L1176
[7] 
https://elixir.bootlin.com/linux/v5.18.3/source/drivers/gpu/drm/vc4/vc4_crtc.c#L1142



> 
> Maxime

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Ivo Totev

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 02/64] drm/crtc: Introduce drmm_crtc_init_with_planes
  2022-06-14 11:47           ` Thomas Zimmermann
@ 2022-06-14 12:09             ` Maxime Ripard
  2022-06-15  7:22               ` Thomas Zimmermann
  0 siblings, 1 reply; 124+ messages in thread
From: Maxime Ripard @ 2022-06-14 12:09 UTC (permalink / raw)
  To: Thomas Zimmermann; +Cc: David Airlie, Daniel Vetter, dri-devel

[-- Attachment #1: Type: text/plain, Size: 6117 bytes --]

On Tue, Jun 14, 2022 at 01:47:28PM +0200, Thomas Zimmermann wrote:
> Am 14.06.22 um 11:04 schrieb Maxime Ripard:
> > On Tue, Jun 14, 2022 at 10:29:20AM +0200, Thomas Zimmermann wrote:
> > > Am 14.06.22 um 09:37 schrieb Maxime Ripard:
> > > > Hi Thomas,
> > > > 
> > > > On Mon, Jun 13, 2022 at 01:23:54PM +0200, Thomas Zimmermann wrote:
> > > > > Am 10.06.22 um 11:28 schrieb Maxime Ripard:
> > > > > > The DRM-managed function to register a CRTC is
> > > > > > drmm_crtc_alloc_with_planes(), which will allocate the underlying
> > > > > > structure and initialisation the CRTC.
> > > > > > 
> > > > > > However, we might want to separate the structure creation and the CRTC
> > > > > > initialisation, for example if the structure is shared across multiple
> > > > > > DRM entities, for example an encoder and a connector.
> > > > > > 
> > > > > > Let's create an helper to only initialise a CRTC that would be passed as
> > > > > > an argument.
> > > > > 
> > > > > Before I review all of thes patches, one question. it's yet not clear to me
> > > > > why drm_crtc_init_with_planes() wouldn't work. (I know we discussed this on
> > > > > IRC.)
> > > > > 
> > > > > If you're calling drmm_mode_config_init(), it will clean up all the CRTC,
> > > > > encoder connector, etc. data structures for you. For CRTC instances in
> > > > > kmalloced memory, wouldn't it be simpler to put the corresponding kfree into
> > > > > vc4_crtc_destroy()?
> > > > 
> > > > My intent was to remove as much of the lifetime handling and
> > > > memory-management from drivers as possible.
> > > > 
> > > > My feeling is that while the solution you suggest would definitely work,
> > > > it relies on drivers authors to know about this, and make the effort to
> > > > convert the drivers themselves. And then we would have to review that,
> > > > which we will probably miss (collectively), because it's a bit obscure.
> > > > 
> > > > While with either the drmm_alloc_* functions, or the new functions I
> > > > introduce there, we can just deprecate the old ones, create a TODO entry
> > > > and a coccinelle script and trust that it works properly.
> > > 
> > > Thanks for explaining the motivation.
> > > 
> > > I would not want to deprecate any of the existing functions, as they work
> > > for many drivers. The drmm_ functions add additional overhead that not
> > > everyone is willing to pay.
> > 
> > I'm a bit confused. drm_crtc_init_with_planes() at the moment has:
> > 
> > * Note: consider using drmm_crtc_alloc_with_planes() instead of
> > * drm_crtc_init_with_planes() to let the DRM managed resource infrastructure
> > * take care of cleanup and deallocation.
> > 
> > Just like drm_encoder_init(), drm_simple_encoder_init() and
> > drm_universal_plane_init(), so all the functions we have a drmm_* helper
> > for.
> > 
> > And we have a TODO-list item that heavily hints at using them:
> > https://dri.freedesktop.org/docs/drm/gpu/todo.html#object-lifetime-fixes
> > 
> > So it looks like we're already well on the deprecation path?
> 
> AFAIU that TODO item is about replacing devm_kzalloc() with drmm_kzalloc().
> It's not about the plain init functions, such as drm_crtc_init_with_planes()
> or drm_universal_plane_init(). Many simple drivers allocate their
> modesetting pipeline's components first and then build the pipeline with the
> drm_ functions. I don't think we can take that away from them.

Sure, that's exactly what those first patches are about? It allows to
use a DRM managed initialization without disrupting the drivers
structure too much?

> The concern I have is that we're adding lots of helpers for all kind of
> scenarios and end up with a lot of duplication (and fragmentation among
> drivers).

I can see two: whether you want to allocate / init, or just init?
We're not going to have more than that.

> For example, drmm_crtc_alloc_with_planes() really isn't much used
> by anything. [1]

Not that I disagree here, but it might be that it isn't the most helpful
helper?

In vc4 case, we just can't use it easily.

Our CRTC driver is shared between the "regular" CRTCs in the display
path, and another instance dedicated to the writeback connector.

The shared stuff is initialized through vc4_crtc_init():
https://elixir.bootlin.com/linux/latest/source/drivers/gpu/drm/vc4/vc4_crtc.c#L1120

It initializes the structure, set up the planes, etc. Basically
everything that our CRTC controller will be doing, and would be shared
by both cases.

However, since the writeback and regular CRTC structures are different,
we can't really make that function allocate the backing structure
either.

We could do some compiler magic to pass the total size and the offset to
that function, just like what drmm_crtc_alloc_with_planes is doing, but
then we would have the same issue with the writeback stuff that needs to
initialize the encoder and connector.

So we would need a drmm_encoder_init anyway.

> Same for drmm_universal_plane_alloc(). [2]
> 
> Instead of adding new helpers, it would be better to build upon
> drmm_crtc_alloc_with_planes(), drmm_univeral_plane_alloc(), etc.
> 
> For example, a good starting point would be vc4_plane_init(). It could alloc
> with drmm_univeral_plane_alloc(), which would replace devm_kzalloc() [3] and
> drm_univeral_plane_alloc() [4] in one step. From what I understand, that's
> what your patchset wants to do. But it looks like you're effectively
> open-coding drmm_universl_plane_alloc().

Where I could use the alloc helper, I did. See the following patch that
does exactly what you described:
https://lore.kernel.org/dri-devel/20220610092924.754942-17-maxime@cerno.tech/

> With vc4_plane_init() correctly managed, the next candidate could be
> vc4_crtc_init(). You probably want to pull vc4_plane_init() [5] into
> callers. to get it out of the way. If you move calls to devm_kzalloc() [6]
> and drm_crtc_init_with_planes() [7] closer together, you can replace them
> with drmm_crtc_alloc_with_planes().

See above

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 14/64] drm/vc4: hvs: Remove planes currently allocated before taking down
  2022-06-10  9:28 ` [PATCH 14/64] drm/vc4: hvs: Remove planes currently allocated before taking down Maxime Ripard
@ 2022-06-14 14:51   ` Dave Stevenson
  0 siblings, 0 replies; 124+ messages in thread
From: Dave Stevenson @ 2022-06-14 14:51 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: David Airlie, Daniel Vetter, DRI Development, Thomas Zimmermann

On Fri, 10 Jun 2022 at 10:30, Maxime Ripard <maxime@cerno.tech> wrote:
>
> When the HVS driver is unbound, a lot of memory allocations in the LBM and
> DLIST RAM are still assigned to planes that are still allocated.
>
> Thus, we hit a warning when calling drm_mm_takedown() since the memory pool
> is not completely free of allocations.
>
> Let's free all the currently live entries before calling drm_mm_takedown().
>
> Signed-off-by: Maxime Ripard <maxime@cerno.tech>

Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>

> ---
>  drivers/gpu/drm/vc4/vc4_hvs.c | 7 +++++++
>  1 file changed, 7 insertions(+)
>
> diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c
> index 483053e7b14f..b0906bb96c32 100644
> --- a/drivers/gpu/drm/vc4/vc4_hvs.c
> +++ b/drivers/gpu/drm/vc4/vc4_hvs.c
> @@ -834,11 +834,18 @@ static void vc4_hvs_unbind(struct device *dev, struct device *master,
>         struct drm_device *drm = dev_get_drvdata(master);
>         struct vc4_dev *vc4 = to_vc4_dev(drm);
>         struct vc4_hvs *hvs = vc4->hvs;
> +       struct drm_mm_node *node, *next;
>
>         if (drm_mm_node_allocated(&vc4->hvs->mitchell_netravali_filter))
>                 drm_mm_remove_node(&vc4->hvs->mitchell_netravali_filter);
>
> +       drm_mm_for_each_node_safe(node, next, &vc4->hvs->dlist_mm)
> +               drm_mm_remove_node(node);
> +
>         drm_mm_takedown(&vc4->hvs->dlist_mm);
> +
> +       drm_mm_for_each_node_safe(node, next, &vc4->hvs->lbm_mm)
> +               drm_mm_remove_node(node);
>         drm_mm_takedown(&vc4->hvs->lbm_mm);
>
>         clk_disable_unprepare(hvs->core_clk);
> --
> 2.36.1
>

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 12/64] drm/vc4: Call component_unbind_all()
  2022-06-10  9:28 ` [PATCH 12/64] drm/vc4: Call component_unbind_all() Maxime Ripard
@ 2022-06-14 15:02   ` Dave Stevenson
  0 siblings, 0 replies; 124+ messages in thread
From: Dave Stevenson @ 2022-06-14 15:02 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: David Airlie, Daniel Vetter, DRI Development, Thomas Zimmermann

On Fri, 10 Jun 2022 at 10:30, Maxime Ripard <maxime@cerno.tech> wrote:
>
> While we were using the component framework to deal with all the DRM
> subdevices, we were not calling component_unbind_all().
>
> This leads to none of the subdevices freeing up their resources as part of
> their unbind() or device managed hooks.
>
> Fixes: c8b75bca92cb ("drm/vc4: Add KMS support for Raspberry Pi.")
> Signed-off-by: Maxime Ripard <maxime@cerno.tech>

Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>

> ---
>  drivers/gpu/drm/vc4/vc4_drv.c | 14 ++++++++++++--
>  drivers/gpu/drm/vc4/vc4_drv.h |  1 +
>  2 files changed, 13 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c
> index 162bc18e7497..031f2cdd658d 100644
> --- a/drivers/gpu/drm/vc4/vc4_drv.c
> +++ b/drivers/gpu/drm/vc4/vc4_drv.c
> @@ -209,6 +209,13 @@ static void vc4_match_add_drivers(struct device *dev,
>         }
>  }
>
> +static void vc4_component_unbind_all(void *ptr)
> +{
> +       struct vc4_dev *vc4 = ptr;
> +
> +       component_unbind_all(vc4->dev, &vc4->base);
> +}
> +
>  static int vc4_drm_bind(struct device *dev)
>  {
>         struct platform_device *pdev = to_platform_device(dev);
> @@ -230,6 +237,7 @@ static int vc4_drm_bind(struct device *dev)
>         vc4 = devm_drm_dev_alloc(dev, &vc4_drm_driver, struct vc4_dev, base);
>         if (IS_ERR(vc4))
>                 return PTR_ERR(vc4);
> +       vc4->dev = dev;
>
>         drm = &vc4->base;
>         platform_set_drvdata(pdev, drm);
> @@ -276,6 +284,10 @@ static int vc4_drm_bind(struct device *dev)
>         if (ret)
>                 return ret;
>
> +       ret = devm_add_action_or_reset(dev, vc4_component_unbind_all, vc4);
> +       if (ret)
> +               return ret;
> +
>         ret = vc4_plane_create_additional_planes(drm);
>         if (ret)
>                 goto unbind_all;
> @@ -296,8 +308,6 @@ static int vc4_drm_bind(struct device *dev)
>         return 0;
>
>  unbind_all:
> -       component_unbind_all(dev, drm);
> -
>         return ret;
>  }
>
> diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h
> index 15e0c2ac3940..aa4c5910ea05 100644
> --- a/drivers/gpu/drm/vc4/vc4_drv.h
> +++ b/drivers/gpu/drm/vc4/vc4_drv.h
> @@ -73,6 +73,7 @@ struct vc4_perfmon {
>
>  struct vc4_dev {
>         struct drm_device base;
> +       struct device *dev;
>
>         unsigned int irq;
>
> --
> 2.36.1
>

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 13/64] drm/vc4: hvs: Protect device resources after removal
  2022-06-10  9:28 ` [PATCH 13/64] drm/vc4: hvs: Protect device resources after removal Maxime Ripard
@ 2022-06-14 15:11   ` Dave Stevenson
  2022-06-14 16:59     ` Dave Stevenson
  2022-06-20 13:20     ` Maxime Ripard
  2022-06-20 10:48   ` Thomas Zimmermann
  1 sibling, 2 replies; 124+ messages in thread
From: Dave Stevenson @ 2022-06-14 15:11 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: David Airlie, Daniel Vetter, DRI Development, Thomas Zimmermann

Hi Maxime

On Fri, 10 Jun 2022 at 10:30, Maxime Ripard <maxime@cerno.tech> wrote:
>
> Whenever the device and driver are unbound, the main device and all the
> subdevices will be removed by calling their unbind() method.
>
> However, the DRM device itself will only be freed when the last user will
> have closed it.
>
> It means that there is a time window where the device and its resources
> aren't there anymore, but the userspace can still call into our driver.
>
> Fortunately, the DRM framework provides the drm_dev_enter() and
> drm_dev_exit() functions to make sure our underlying device is still there
> for the section protected by those calls. Let's add them to the HVS driver.

The framework appears to rely on the remove function calling
drm_dev_unplug instead of drm_dev_unregister, but I haven't seen a
patch that makes that change in the vc4 driver.
Have I missed it, or is there some other route to set the unplugged
flag that drm_dev_enter/exit are relying on?

  Dave

> Signed-off-by: Maxime Ripard <maxime@cerno.tech>
> ---
>  drivers/gpu/drm/vc4/vc4_drv.h |   1 +
>  drivers/gpu/drm/vc4/vc4_hvs.c | 106 +++++++++++++++++++++++++++++++---
>  2 files changed, 99 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h
> index aa4c5910ea05..080deae55f64 100644
> --- a/drivers/gpu/drm/vc4/vc4_drv.h
> +++ b/drivers/gpu/drm/vc4/vc4_drv.h
> @@ -317,6 +317,7 @@ struct vc4_v3d {
>  };
>
>  struct vc4_hvs {
> +       struct drm_device *dev;
>         struct platform_device *pdev;
>         void __iomem *regs;
>         u32 __iomem *dlist;
> diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c
> index 2a58fc421cf6..483053e7b14f 100644
> --- a/drivers/gpu/drm/vc4/vc4_hvs.c
> +++ b/drivers/gpu/drm/vc4/vc4_hvs.c
> @@ -25,6 +25,7 @@
>  #include <linux/platform_device.h>
>
>  #include <drm/drm_atomic_helper.h>
> +#include <drm/drm_drv.h>
>  #include <drm/drm_vblank.h>
>
>  #include "vc4_drv.h"
> @@ -66,11 +67,15 @@ static const struct debugfs_reg32 hvs_regs[] = {
>
>  void vc4_hvs_dump_state(struct vc4_hvs *hvs)
>  {
> +       struct drm_device *drm = hvs->dev;
>         struct drm_printer p = drm_info_printer(&hvs->pdev->dev);
> -       int i;
> +       int idx, i;
>
>         drm_print_regset32(&p, &hvs->regset);
>
> +       if (!drm_dev_enter(drm, &idx))
> +               return;
> +
>         DRM_INFO("HVS ctx:\n");
>         for (i = 0; i < 64; i += 4) {
>                 DRM_INFO("0x%08x (%s): 0x%08x 0x%08x 0x%08x 0x%08x\n",
> @@ -80,6 +85,8 @@ void vc4_hvs_dump_state(struct vc4_hvs *hvs)
>                          readl((u32 __iomem *)hvs->dlist + i + 2),
>                          readl((u32 __iomem *)hvs->dlist + i + 3));
>         }
> +
> +       drm_dev_exit(idx);
>  }
>
>  static int vc4_hvs_debugfs_underrun(struct seq_file *m, void *data)
> @@ -132,14 +139,18 @@ static int vc4_hvs_upload_linear_kernel(struct vc4_hvs *hvs,
>                                         struct drm_mm_node *space,
>                                         const u32 *kernel)
>  {
> -       int ret, i;
> +       struct drm_device *drm = hvs->dev;
> +       int idx, ret, i;
>         u32 __iomem *dst_kernel;
>
> +       if (!drm_dev_enter(drm, &idx))
> +               return -ENODEV;
> +
>         ret = drm_mm_insert_node(&hvs->dlist_mm, space, VC4_KERNEL_DWORDS);
>         if (ret) {
>                 DRM_ERROR("Failed to allocate space for filter kernel: %d\n",
>                           ret);
> -               return ret;
> +               goto err_dev_exit;
>         }
>
>         dst_kernel = hvs->dlist + space->start;
> @@ -153,16 +164,26 @@ static int vc4_hvs_upload_linear_kernel(struct vc4_hvs *hvs,
>                 }
>         }
>
> +       drm_dev_exit(idx);
>         return 0;
> +
> +err_dev_exit:
> +       drm_dev_exit(idx);
> +       return ret;
>  }
>
>  static void vc4_hvs_lut_load(struct vc4_hvs *hvs,
>                              struct vc4_crtc *vc4_crtc)
>  {
> +       struct drm_device *drm = hvs->dev;
>         struct drm_crtc *crtc = &vc4_crtc->base;
>         struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state);
> +       int idx;
>         u32 i;
>
> +       if (!drm_dev_enter(drm, &idx))
> +               return;
> +
>         /* The LUT memory is laid out with each HVS channel in order,
>          * each of which takes 256 writes for R, 256 for G, then 256
>          * for B.
> @@ -177,6 +198,8 @@ static void vc4_hvs_lut_load(struct vc4_hvs *hvs,
>                 HVS_WRITE(SCALER_GAMDATA, vc4_crtc->lut_g[i]);
>         for (i = 0; i < crtc->gamma_size; i++)
>                 HVS_WRITE(SCALER_GAMDATA, vc4_crtc->lut_b[i]);
> +
> +       drm_dev_exit(idx);
>  }
>
>  static void vc4_hvs_update_gamma_lut(struct vc4_hvs *hvs,
> @@ -198,7 +221,12 @@ static void vc4_hvs_update_gamma_lut(struct vc4_hvs *hvs,
>
>  u8 vc4_hvs_get_fifo_frame_count(struct vc4_hvs *hvs, unsigned int fifo)
>  {
> +       struct drm_device *drm = hvs->dev;
>         u8 field = 0;
> +       int idx;
> +
> +       if (!drm_dev_enter(drm, &idx))
> +               return 0;
>
>         switch (fifo) {
>         case 0:
> @@ -215,6 +243,7 @@ u8 vc4_hvs_get_fifo_frame_count(struct vc4_hvs *hvs, unsigned int fifo)
>                 break;
>         }
>
> +       drm_dev_exit(idx);
>         return field;
>  }
>
> @@ -226,6 +255,12 @@ int vc4_hvs_get_fifo_from_output(struct vc4_hvs *hvs, unsigned int output)
>         if (!hvs->hvs5)
>                 return output;
>
> +       /*
> +        * NOTE: We should probably use drm_dev_enter()/drm_dev_exit()
> +        * here, but this function is only used during the DRM device
> +        * initialization, so we should be fine.
> +        */
> +
>         switch (output) {
>         case 0:
>                 return 0;
> @@ -273,12 +308,17 @@ int vc4_hvs_get_fifo_from_output(struct vc4_hvs *hvs, unsigned int output)
>  static int vc4_hvs_init_channel(struct vc4_hvs *hvs, struct drm_crtc *crtc,
>                                 struct drm_display_mode *mode, bool oneshot)
>  {
> +       struct drm_device *drm = hvs->dev;
>         struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
>         struct vc4_crtc_state *vc4_crtc_state = to_vc4_crtc_state(crtc->state);
>         unsigned int chan = vc4_crtc_state->assigned_channel;
>         bool interlace = mode->flags & DRM_MODE_FLAG_INTERLACE;
>         u32 dispbkgndx;
>         u32 dispctrl;
> +       int idx;
> +
> +       if (!drm_dev_enter(drm, &idx))
> +               return -ENODEV;
>
>         HVS_WRITE(SCALER_DISPCTRLX(chan), 0);
>         HVS_WRITE(SCALER_DISPCTRLX(chan), SCALER_DISPCTRLX_RESET);
> @@ -320,13 +360,21 @@ static int vc4_hvs_init_channel(struct vc4_hvs *hvs, struct drm_crtc *crtc,
>          */
>         vc4_hvs_lut_load(hvs, vc4_crtc);
>
> +       drm_dev_exit(idx);
> +
>         return 0;
>  }
>
>  void vc4_hvs_stop_channel(struct vc4_hvs *hvs, unsigned int chan)
>  {
> +       struct drm_device *drm = hvs->dev;
> +       int idx;
> +
> +       if (!drm_dev_enter(drm, &idx))
> +               return;
> +
>         if (HVS_READ(SCALER_DISPCTRLX(chan)) & SCALER_DISPCTRLX_ENABLE)
> -               return;
> +               goto out;
>
>         HVS_WRITE(SCALER_DISPCTRLX(chan),
>                   HVS_READ(SCALER_DISPCTRLX(chan)) | SCALER_DISPCTRLX_RESET);
> @@ -343,6 +391,9 @@ void vc4_hvs_stop_channel(struct vc4_hvs *hvs, unsigned int chan)
>         WARN_ON_ONCE((HVS_READ(SCALER_DISPSTATX(chan)) &
>                       (SCALER_DISPSTATX_FULL | SCALER_DISPSTATX_EMPTY)) !=
>                      SCALER_DISPSTATX_EMPTY);
> +
> +out:
> +       drm_dev_exit(idx);
>  }
>
>  int vc4_hvs_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state)
> @@ -384,9 +435,15 @@ static void vc4_hvs_install_dlist(struct drm_crtc *crtc)
>         struct vc4_dev *vc4 = to_vc4_dev(dev);
>         struct vc4_hvs *hvs = vc4->hvs;
>         struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state);
> +       int idx;
> +
> +       if (!drm_dev_enter(dev, &idx))
> +               return;
>
>         HVS_WRITE(SCALER_DISPLISTX(vc4_state->assigned_channel),
>                   vc4_state->mm.start);
> +
> +       drm_dev_exit(idx);
>  }
>
>  static void vc4_hvs_update_dlist(struct drm_crtc *crtc)
> @@ -471,6 +528,10 @@ void vc4_hvs_atomic_flush(struct drm_crtc *crtc,
>         bool enable_bg_fill = false;
>         u32 __iomem *dlist_start = vc4->hvs->dlist + vc4_state->mm.start;
>         u32 __iomem *dlist_next = dlist_start;
> +       int idx;
> +
> +       if (!drm_dev_enter(dev, &idx))
> +               return;
>
>         if (debug_dump_regs) {
>                 DRM_INFO("CRTC %d HVS before:\n", drm_crtc_index(crtc));
> @@ -541,26 +602,44 @@ void vc4_hvs_atomic_flush(struct drm_crtc *crtc,
>                 DRM_INFO("CRTC %d HVS after:\n", drm_crtc_index(crtc));
>                 vc4_hvs_dump_state(hvs);
>         }
> +
> +       drm_dev_exit(idx);
>  }
>
>  void vc4_hvs_mask_underrun(struct vc4_hvs *hvs, int channel)
>  {
> -       u32 dispctrl = HVS_READ(SCALER_DISPCTRL);
> +       struct drm_device *drm = hvs->dev;
> +       u32 dispctrl;
> +       int idx;
>
> +       if (!drm_dev_enter(drm, &idx))
> +               return;
> +
> +       dispctrl = HVS_READ(SCALER_DISPCTRL);
>         dispctrl &= ~SCALER_DISPCTRL_DSPEISLUR(channel);
>
>         HVS_WRITE(SCALER_DISPCTRL, dispctrl);
> +
> +       drm_dev_exit(idx);
>  }
>
>  void vc4_hvs_unmask_underrun(struct vc4_hvs *hvs, int channel)
>  {
> -       u32 dispctrl = HVS_READ(SCALER_DISPCTRL);
> +       struct drm_device *drm = hvs->dev;
> +       u32 dispctrl;
> +       int idx;
>
> +       if (!drm_dev_enter(drm, &idx))
> +               return;
> +
> +       dispctrl = HVS_READ(SCALER_DISPCTRL);
>         dispctrl |= SCALER_DISPCTRL_DSPEISLUR(channel);
>
>         HVS_WRITE(SCALER_DISPSTAT,
>                   SCALER_DISPSTAT_EUFLOW(channel));
>         HVS_WRITE(SCALER_DISPCTRL, dispctrl);
> +
> +       drm_dev_exit(idx);
>  }
>
>  static void vc4_hvs_report_underrun(struct drm_device *dev)
> @@ -581,6 +660,17 @@ static irqreturn_t vc4_hvs_irq_handler(int irq, void *data)
>         u32 control;
>         u32 status;
>
> +       /*
> +        * NOTE: We don't need to protect the register access using
> +        * drm_dev_enter() there because the interrupt handler lifetime
> +        * is tied to the device itself, and not to the DRM device.
> +        *
> +        * So when the device will be gone, one of the first thing we
> +        * will be doing will be to unregister the interrupt handler,
> +        * and then unregister the DRM device. drm_dev_enter() would
> +        * thus always succeed if we are here.
> +        */
> +
>         status = HVS_READ(SCALER_DISPSTAT);
>         control = HVS_READ(SCALER_DISPCTRL);
>
> @@ -613,10 +703,10 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)
>         u32 dispctrl;
>         u32 reg;
>
> -       hvs = devm_kzalloc(&pdev->dev, sizeof(*hvs), GFP_KERNEL);
> +       hvs = drmm_kzalloc(drm, sizeof(*hvs), GFP_KERNEL);
>         if (!hvs)
>                 return -ENOMEM;
> -
> +       hvs->dev = drm;
>         hvs->pdev = pdev;
>
>         if (of_device_is_compatible(pdev->dev.of_node, "brcm,bcm2711-hvs"))
> --
> 2.36.1
>

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 15/64] drm/vc4: plane: Take possible_crtcs as an argument
  2022-06-10  9:28 ` [PATCH 15/64] drm/vc4: plane: Take possible_crtcs as an argument Maxime Ripard
@ 2022-06-14 15:27   ` Dave Stevenson
  0 siblings, 0 replies; 124+ messages in thread
From: Dave Stevenson @ 2022-06-14 15:27 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: David Airlie, Daniel Vetter, DRI Development, Thomas Zimmermann

On Fri, 10 Jun 2022 at 10:30, Maxime Ripard <maxime@cerno.tech> wrote:
>
> vc4_plane_init() currently initialises the plane with no possible CRTCs,
> and will expect the caller to set it up by itself.
>
> Let's change that logic a bit to follow the syntax of
> drm_universal_plane_init() and pass the possible CRTCs bitmask as an
> argument to the function instead.
>
> Signed-off-by: Maxime Ripard <maxime@cerno.tech>
> ---
>  drivers/gpu/drm/vc4/vc4_crtc.c  |  2 +-
>  drivers/gpu/drm/vc4/vc4_drv.h   |  3 ++-
>  drivers/gpu/drm/vc4/vc4_plane.c | 15 +++++++--------
>  3 files changed, 10 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c
> index 59b20c8f132b..840a93484bb1 100644
> --- a/drivers/gpu/drm/vc4/vc4_crtc.c
> +++ b/drivers/gpu/drm/vc4/vc4_crtc.c
> @@ -1138,7 +1138,7 @@ int vc4_crtc_init(struct drm_device *drm, struct vc4_crtc *vc4_crtc,
>          * requirement of the plane configuration, and reject ones
>          * that will take too much.
>          */
> -       primary_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_PRIMARY);
> +       primary_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_PRIMARY, 0);
>         if (IS_ERR(primary_plane)) {
>                 dev_err(drm->dev, "failed to construct primary plane\n");
>                 return PTR_ERR(primary_plane);
> diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h
> index 080deae55f64..5125ca1a8158 100644
> --- a/drivers/gpu/drm/vc4/vc4_drv.h
> +++ b/drivers/gpu/drm/vc4/vc4_drv.h
> @@ -952,7 +952,8 @@ int vc4_kms_load(struct drm_device *dev);
>
>  /* vc4_plane.c */
>  struct drm_plane *vc4_plane_init(struct drm_device *dev,
> -                                enum drm_plane_type type);
> +                                enum drm_plane_type type,
> +                                unsigned int possible_crtcs);

A nit pick.
possible_crtcs in struct drm_plane is a uint32_t , not an unsigned int.
It would never matter on a Pi as unsigned int will never be 16bit, but
avoids the oddity.

Otherwise:
Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>

>  int vc4_plane_create_additional_planes(struct drm_device *dev);
>  u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist);
>  u32 vc4_plane_dlist_size(const struct drm_plane_state *state);
> diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c
> index b3438f4a81ce..17dab470ecdf 100644
> --- a/drivers/gpu/drm/vc4/vc4_plane.c
> +++ b/drivers/gpu/drm/vc4/vc4_plane.c
> @@ -1451,7 +1451,8 @@ static const struct drm_plane_funcs vc4_plane_funcs = {
>  };
>
>  struct drm_plane *vc4_plane_init(struct drm_device *dev,
> -                                enum drm_plane_type type)
> +                                enum drm_plane_type type,
> +                                unsigned int possible_crtcs)
>  {
>         struct drm_plane *plane = NULL;
>         struct vc4_plane *vc4_plane;
> @@ -1483,7 +1484,7 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev,
>         }
>
>         plane = &vc4_plane->base;
> -       ret = drm_universal_plane_init(dev, plane, 0,
> +       ret = drm_universal_plane_init(dev, plane, possible_crtcs,
>                                        &vc4_plane_funcs,
>                                        formats, num_formats,
>                                        modifiers, type, NULL);
> @@ -1528,13 +1529,11 @@ int vc4_plane_create_additional_planes(struct drm_device *drm)
>          */
>         for (i = 0; i < 16; i++) {
>                 struct drm_plane *plane =
> -                       vc4_plane_init(drm, DRM_PLANE_TYPE_OVERLAY);
> +                       vc4_plane_init(drm, DRM_PLANE_TYPE_OVERLAY,
> +                                      GENMASK(drm->mode_config.num_crtc - 1, 0));
>
>                 if (IS_ERR(plane))
>                         continue;
> -
> -               plane->possible_crtcs =
> -                       GENMASK(drm->mode_config.num_crtc - 1, 0);
>         }
>
>         drm_for_each_crtc(crtc, drm) {
> @@ -1542,9 +1541,9 @@ int vc4_plane_create_additional_planes(struct drm_device *drm)
>                  * since we overlay planes on the CRTC in the order they were
>                  * initialized.
>                  */
> -               cursor_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_CURSOR);
> +               cursor_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_CURSOR,
> +                                             drm_crtc_mask(crtc));
>                 if (!IS_ERR(cursor_plane)) {
> -                       cursor_plane->possible_crtcs = drm_crtc_mask(crtc);
>                         crtc->cursor = cursor_plane;
>                 }
>         }
> --
> 2.36.1
>

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 16/64] drm/vc4: plane: Switch to drmm_universal_plane_alloc()
  2022-06-10  9:28 ` [PATCH 16/64] drm/vc4: plane: Switch to drmm_universal_plane_alloc() Maxime Ripard
@ 2022-06-14 15:42   ` Dave Stevenson
  0 siblings, 0 replies; 124+ messages in thread
From: Dave Stevenson @ 2022-06-14 15:42 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: David Airlie, Daniel Vetter, DRI Development, Thomas Zimmermann

On Fri, 10 Jun 2022 at 10:30, Maxime Ripard <maxime@cerno.tech> wrote:
>
> Let's switch to drmm_universal_plane_alloc() for our plane allocation and
> initialisation to make the driver a bit simpler.
>
> Signed-off-by: Maxime Ripard <maxime@cerno.tech>

Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>

> ---
>  drivers/gpu/drm/vc4/vc4_crtc.c  | 12 +-----------
>  drivers/gpu/drm/vc4/vc4_plane.c | 23 ++++++++---------------
>  2 files changed, 9 insertions(+), 26 deletions(-)
>
> diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c
> index 840a93484bb1..7163f924b48b 100644
> --- a/drivers/gpu/drm/vc4/vc4_crtc.c
> +++ b/drivers/gpu/drm/vc4/vc4_crtc.c
> @@ -1176,7 +1176,6 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data)
>         const struct vc4_pv_data *pv_data;
>         struct vc4_crtc *vc4_crtc;
>         struct drm_crtc *crtc;
> -       struct drm_plane *destroy_plane, *temp;
>         int ret;
>
>         vc4_crtc = devm_kzalloc(dev, sizeof(*vc4_crtc), GFP_KERNEL);
> @@ -1211,7 +1210,7 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data)
>                                IRQF_SHARED,
>                                "vc4 crtc", vc4_crtc);
>         if (ret)
> -               goto err_destroy_planes;
> +               return ret;
>
>         platform_set_drvdata(pdev, vc4_crtc);
>
> @@ -1219,15 +1218,6 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data)
>                                  &vc4_crtc->regset);
>
>         return 0;
> -
> -err_destroy_planes:
> -       list_for_each_entry_safe(destroy_plane, temp,
> -                                &drm->mode_config.plane_list, head) {
> -               if (destroy_plane->possible_crtcs == drm_crtc_mask(crtc))
> -                   destroy_plane->funcs->destroy(destroy_plane);
> -       }
> -
> -       return ret;
>  }
>
>  static void vc4_crtc_unbind(struct device *dev, struct device *master,
> diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c
> index 17dab470ecdf..673c963f5c5a 100644
> --- a/drivers/gpu/drm/vc4/vc4_plane.c
> +++ b/drivers/gpu/drm/vc4/vc4_plane.c
> @@ -1442,8 +1442,6 @@ static bool vc4_format_mod_supported(struct drm_plane *plane,
>  static const struct drm_plane_funcs vc4_plane_funcs = {
>         .update_plane = drm_atomic_helper_update_plane,
>         .disable_plane = drm_atomic_helper_disable_plane,
> -       .destroy = drm_plane_cleanup,
> -       .set_property = NULL,
>         .reset = vc4_plane_reset,
>         .atomic_duplicate_state = vc4_plane_duplicate_state,
>         .atomic_destroy_state = vc4_plane_destroy_state,
> @@ -1454,11 +1452,10 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev,
>                                  enum drm_plane_type type,
>                                  unsigned int possible_crtcs)
>  {
> -       struct drm_plane *plane = NULL;
> +       struct drm_plane *plane;
>         struct vc4_plane *vc4_plane;
>         u32 formats[ARRAY_SIZE(hvs_formats)];
>         int num_formats = 0;
> -       int ret = 0;
>         unsigned i;
>         bool hvs5 = of_device_is_compatible(dev->dev->of_node,
>                                             "brcm,bcm2711-vc5");
> @@ -1471,11 +1468,6 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev,
>                 DRM_FORMAT_MOD_INVALID
>         };
>
> -       vc4_plane = devm_kzalloc(dev->dev, sizeof(*vc4_plane),
> -                                GFP_KERNEL);
> -       if (!vc4_plane)
> -               return ERR_PTR(-ENOMEM);
> -
>         for (i = 0; i < ARRAY_SIZE(hvs_formats); i++) {
>                 if (!hvs_formats[i].hvs5_only || hvs5) {
>                         formats[num_formats] = hvs_formats[i].drm;
> @@ -1483,13 +1475,14 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev,
>                 }
>         }
>
> +       vc4_plane = drmm_universal_plane_alloc(dev, struct vc4_plane, base,
> +                                              possible_crtcs,
> +                                              &vc4_plane_funcs,
> +                                              formats, num_formats,
> +                                              modifiers, type, NULL);
> +       if (IS_ERR(vc4_plane))
> +               return ERR_CAST(vc4_plane);
>         plane = &vc4_plane->base;
> -       ret = drm_universal_plane_init(dev, plane, possible_crtcs,
> -                                      &vc4_plane_funcs,
> -                                      formats, num_formats,
> -                                      modifiers, type, NULL);
> -       if (ret)
> -               return ERR_PTR(ret);
>
>         drm_plane_helper_add(plane, &vc4_plane_helper_funcs);
>
> --
> 2.36.1
>

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 18/64] drm/vc4: crtc: Switch to drmm_kzalloc
  2022-06-10  9:28 ` [PATCH 18/64] drm/vc4: crtc: Switch to drmm_kzalloc Maxime Ripard
@ 2022-06-14 15:43   ` Dave Stevenson
  0 siblings, 0 replies; 124+ messages in thread
From: Dave Stevenson @ 2022-06-14 15:43 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: David Airlie, Daniel Vetter, DRI Development, Thomas Zimmermann

On Fri, 10 Jun 2022 at 10:30, Maxime Ripard <maxime@cerno.tech> wrote:
>
> Our internal structure that stores the DRM entities structure is allocated
> through a device-managed kzalloc.
>
> This means that this will eventually be freed whenever the device is
> removed. In our case, the most like source of removal is that the main
> device is going to be unbound, and component_unbind_all() is being run.
>
> However, it occurs while the DRM device is still registered, which will
> create dangling pointers, eventually resulting in use-after-free.
>
> Switch to a DRM-managed allocation to keep our structure until the DRM
> driver doesn't need it anymore.
>
> Signed-off-by: Maxime Ripard <maxime@cerno.tech>

Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>

> ---
>  drivers/gpu/drm/vc4/vc4_crtc.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c
> index 1f7e987e68aa..c74fa3d07561 100644
> --- a/drivers/gpu/drm/vc4/vc4_crtc.c
> +++ b/drivers/gpu/drm/vc4/vc4_crtc.c
> @@ -1178,7 +1178,7 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data)
>         struct drm_crtc *crtc;
>         int ret;
>
> -       vc4_crtc = devm_kzalloc(dev, sizeof(*vc4_crtc), GFP_KERNEL);
> +       vc4_crtc = drmm_kzalloc(drm, sizeof(*vc4_crtc), GFP_KERNEL);
>         if (!vc4_crtc)
>                 return -ENOMEM;
>         crtc = &vc4_crtc->base;
> --
> 2.36.1
>

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 17/64] drm/vc4: crtc: Move debugfs_name to crtc_data
  2022-06-10  9:28 ` [PATCH 17/64] drm/vc4: crtc: Move debugfs_name to crtc_data Maxime Ripard
@ 2022-06-14 15:57   ` Dave Stevenson
  2022-06-16  9:41     ` Maxime Ripard
  0 siblings, 1 reply; 124+ messages in thread
From: Dave Stevenson @ 2022-06-14 15:57 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: David Airlie, Daniel Vetter, DRI Development, Thomas Zimmermann

On Fri, 10 Jun 2022 at 10:30, Maxime Ripard <maxime@cerno.tech> wrote:
>
> All the CRTCs, including the TXP, have a debugfs file and name so we can
> consolidate it into vc4_crtc_data.
>
> Signed-off-by: Maxime Ripard <maxime@cerno.tech>

Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>

I was sort of expecting the vc4_debugfs_add_regset32 call to move to
vc4_crtc_init so that it would be a common call for crtcs and txp, but
that doesn't appear to happen in this set. The debugfs_name for the
txp is therefore actually unused.

> ---
>  drivers/gpu/drm/vc4/vc4_crtc.c | 18 +++++++++---------
>  drivers/gpu/drm/vc4/vc4_drv.h  |  4 ++--
>  drivers/gpu/drm/vc4/vc4_txp.c  |  1 +
>  3 files changed, 12 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c
> index 7163f924b48b..1f7e987e68aa 100644
> --- a/drivers/gpu/drm/vc4/vc4_crtc.c
> +++ b/drivers/gpu/drm/vc4/vc4_crtc.c
> @@ -978,10 +978,10 @@ static const struct drm_crtc_helper_funcs vc4_crtc_helper_funcs = {
>
>  static const struct vc4_pv_data bcm2835_pv0_data = {
>         .base = {
> +               .debugfs_name = "crtc0_regs",
>                 .hvs_available_channels = BIT(0),
>                 .hvs_output = 0,
>         },
> -       .debugfs_name = "crtc0_regs",
>         .fifo_depth = 64,
>         .pixels_per_clock = 1,
>         .encoder_types = {
> @@ -992,10 +992,10 @@ static const struct vc4_pv_data bcm2835_pv0_data = {
>
>  static const struct vc4_pv_data bcm2835_pv1_data = {
>         .base = {
> +               .debugfs_name = "crtc1_regs",
>                 .hvs_available_channels = BIT(2),
>                 .hvs_output = 2,
>         },
> -       .debugfs_name = "crtc1_regs",
>         .fifo_depth = 64,
>         .pixels_per_clock = 1,
>         .encoder_types = {
> @@ -1006,10 +1006,10 @@ static const struct vc4_pv_data bcm2835_pv1_data = {
>
>  static const struct vc4_pv_data bcm2835_pv2_data = {
>         .base = {
> +               .debugfs_name = "crtc2_regs",
>                 .hvs_available_channels = BIT(1),
>                 .hvs_output = 1,
>         },
> -       .debugfs_name = "crtc2_regs",
>         .fifo_depth = 64,
>         .pixels_per_clock = 1,
>         .encoder_types = {
> @@ -1020,10 +1020,10 @@ static const struct vc4_pv_data bcm2835_pv2_data = {
>
>  static const struct vc4_pv_data bcm2711_pv0_data = {
>         .base = {
> +               .debugfs_name = "crtc0_regs",
>                 .hvs_available_channels = BIT(0),
>                 .hvs_output = 0,
>         },
> -       .debugfs_name = "crtc0_regs",
>         .fifo_depth = 64,
>         .pixels_per_clock = 1,
>         .encoder_types = {
> @@ -1034,10 +1034,10 @@ static const struct vc4_pv_data bcm2711_pv0_data = {
>
>  static const struct vc4_pv_data bcm2711_pv1_data = {
>         .base = {
> +               .debugfs_name = "crtc1_regs",
>                 .hvs_available_channels = BIT(0) | BIT(1) | BIT(2),
>                 .hvs_output = 3,
>         },
> -       .debugfs_name = "crtc1_regs",
>         .fifo_depth = 64,
>         .pixels_per_clock = 1,
>         .encoder_types = {
> @@ -1048,10 +1048,10 @@ static const struct vc4_pv_data bcm2711_pv1_data = {
>
>  static const struct vc4_pv_data bcm2711_pv2_data = {
>         .base = {
> +               .debugfs_name = "crtc2_regs",
>                 .hvs_available_channels = BIT(0) | BIT(1) | BIT(2),
>                 .hvs_output = 4,
>         },
> -       .debugfs_name = "crtc2_regs",
>         .fifo_depth = 256,
>         .pixels_per_clock = 2,
>         .encoder_types = {
> @@ -1061,10 +1061,10 @@ static const struct vc4_pv_data bcm2711_pv2_data = {
>
>  static const struct vc4_pv_data bcm2711_pv3_data = {
>         .base = {
> +               .debugfs_name = "crtc3_regs",
>                 .hvs_available_channels = BIT(1),
>                 .hvs_output = 1,
>         },
> -       .debugfs_name = "crtc3_regs",
>         .fifo_depth = 64,
>         .pixels_per_clock = 1,
>         .encoder_types = {
> @@ -1074,10 +1074,10 @@ static const struct vc4_pv_data bcm2711_pv3_data = {
>
>  static const struct vc4_pv_data bcm2711_pv4_data = {
>         .base = {
> +               .debugfs_name = "crtc4_regs",
>                 .hvs_available_channels = BIT(0) | BIT(1) | BIT(2),
>                 .hvs_output = 5,
>         },
> -       .debugfs_name = "crtc4_regs",
>         .fifo_depth = 64,
>         .pixels_per_clock = 2,
>         .encoder_types = {
> @@ -1214,7 +1214,7 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data)
>
>         platform_set_drvdata(pdev, vc4_crtc);
>
> -       vc4_debugfs_add_regset32(drm, pv_data->debugfs_name,
> +       vc4_debugfs_add_regset32(drm, pv_data->base.debugfs_name,
>                                  &vc4_crtc->regset);
>
>         return 0;
> diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h
> index 5125ca1a8158..9a53ace85d95 100644
> --- a/drivers/gpu/drm/vc4/vc4_drv.h
> +++ b/drivers/gpu/drm/vc4/vc4_drv.h
> @@ -457,6 +457,8 @@ to_vc4_encoder(struct drm_encoder *encoder)
>  }
>
>  struct vc4_crtc_data {
> +       const char *debugfs_name;
> +
>         /* Bitmask of channels (FIFOs) of the HVS that the output can source from */
>         unsigned int hvs_available_channels;
>
> @@ -474,8 +476,6 @@ struct vc4_pv_data {
>         u8 pixels_per_clock;
>
>         enum vc4_encoder_type encoder_types[4];
> -       const char *debugfs_name;
> -
>  };
>
>  struct vc4_crtc {
> diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c
> index 82beb8c159f2..e983ff7c5e13 100644
> --- a/drivers/gpu/drm/vc4/vc4_txp.c
> +++ b/drivers/gpu/drm/vc4/vc4_txp.c
> @@ -460,6 +460,7 @@ static irqreturn_t vc4_txp_interrupt(int irq, void *data)
>  }
>
>  static const struct vc4_crtc_data vc4_txp_crtc_data = {
> +       .debugfs_name = "txp_regs",
>         .hvs_available_channels = BIT(2),
>         .hvs_output = 2,
>  };
> --
> 2.36.1
>

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 20/64] drm/vc4: dpi: Remove vc4_dev dpi pointer
  2022-06-10  9:28 ` [PATCH 20/64] drm/vc4: dpi: Remove vc4_dev dpi pointer Maxime Ripard
@ 2022-06-14 16:00   ` Dave Stevenson
  0 siblings, 0 replies; 124+ messages in thread
From: Dave Stevenson @ 2022-06-14 16:00 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: David Airlie, Daniel Vetter, DRI Development, Thomas Zimmermann

On Fri, 10 Jun 2022 at 10:30, Maxime Ripard <maxime@cerno.tech> wrote:
>
> There's no user for that pointer so let's just get rid of it.
>
> Signed-off-by: Maxime Ripard <maxime@cerno.tech>

Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>

> ---
>  drivers/gpu/drm/vc4/vc4_dpi.c | 7 -------
>  drivers/gpu/drm/vc4/vc4_drv.h | 1 -
>  2 files changed, 8 deletions(-)
>
> diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c
> index c180eb60bee8..f2b46c524919 100644
> --- a/drivers/gpu/drm/vc4/vc4_dpi.c
> +++ b/drivers/gpu/drm/vc4/vc4_dpi.c
> @@ -249,7 +249,6 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data)
>  {
>         struct platform_device *pdev = to_platform_device(dev);
>         struct drm_device *drm = dev_get_drvdata(master);
> -       struct vc4_dev *vc4 = to_vc4_dev(drm);
>         struct vc4_dpi *dpi;
>         struct vc4_dpi_encoder *vc4_dpi_encoder;
>         int ret;
> @@ -308,8 +307,6 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data)
>
>         dev_set_drvdata(dev, dpi);
>
> -       vc4->dpi = dpi;
> -
>         vc4_debugfs_add_regset32(drm, "dpi_regs", &dpi->regset);
>
>         return 0;
> @@ -323,8 +320,6 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data)
>  static void vc4_dpi_unbind(struct device *dev, struct device *master,
>                            void *data)
>  {
> -       struct drm_device *drm = dev_get_drvdata(master);
> -       struct vc4_dev *vc4 = to_vc4_dev(drm);
>         struct vc4_dpi *dpi = dev_get_drvdata(dev);
>
>         drm_of_panel_bridge_remove(dev->of_node, 0, 0);
> @@ -332,8 +327,6 @@ static void vc4_dpi_unbind(struct device *dev, struct device *master,
>         drm_encoder_cleanup(dpi->encoder);
>
>         clk_disable_unprepare(dpi->core_clock);
> -
> -       vc4->dpi = NULL;
>  }
>
>  static const struct component_ops vc4_dpi_ops = {
> diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h
> index fff3772be2d4..846f3cda179a 100644
> --- a/drivers/gpu/drm/vc4/vc4_drv.h
> +++ b/drivers/gpu/drm/vc4/vc4_drv.h
> @@ -79,7 +79,6 @@ struct vc4_dev {
>
>         struct vc4_hvs *hvs;
>         struct vc4_v3d *v3d;
> -       struct vc4_dpi *dpi;
>         struct vc4_vec *vec;
>         struct vc4_txp *txp;
>
> --
> 2.36.1
>

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 21/64] drm/vc4: dpi: Embed DRM structures into the private structure
  2022-06-10  9:28 ` [PATCH 21/64] drm/vc4: dpi: Embed DRM structures into the private structure Maxime Ripard
@ 2022-06-14 16:07   ` Dave Stevenson
  0 siblings, 0 replies; 124+ messages in thread
From: Dave Stevenson @ 2022-06-14 16:07 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: David Airlie, Daniel Vetter, DRI Development, Thomas Zimmermann

On Fri, 10 Jun 2022 at 10:30, Maxime Ripard <maxime@cerno.tech> wrote:
>
> The VC4 DPI driver private structure contains only a pointer to the
> encoder it implements. This makes the overall structure somewhat
> inconsistent with the rest of the driver, and complicates its
> initialisation without any apparent gain.
>
> Let's embed the drm_encoder structure (through the vc4_encoder one) into
> struct vc4_dpi to fix both issues.
>
> Signed-off-by: Maxime Ripard <maxime@cerno.tech>

Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>

> ---
>  drivers/gpu/drm/vc4/vc4_dpi.c | 49 ++++++++++++-----------------------
>  1 file changed, 16 insertions(+), 33 deletions(-)
>
> diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c
> index f2b46c524919..c88e8e397730 100644
> --- a/drivers/gpu/drm/vc4/vc4_dpi.c
> +++ b/drivers/gpu/drm/vc4/vc4_dpi.c
> @@ -83,10 +83,10 @@
>
>  /* General DPI hardware state. */
>  struct vc4_dpi {
> +       struct vc4_encoder encoder;
> +
>         struct platform_device *pdev;
>
> -       struct drm_encoder *encoder;
> -
>         void __iomem *regs;
>
>         struct clk *pixel_clock;
> @@ -95,21 +95,15 @@ struct vc4_dpi {
>         struct debugfs_regset32 regset;
>  };
>
> +static inline struct vc4_dpi *
> +to_vc4_dpi(struct drm_encoder *encoder)
> +{
> +       return container_of(encoder, struct vc4_dpi, encoder.base);
> +}
> +
>  #define DPI_READ(offset) readl(dpi->regs + (offset))
>  #define DPI_WRITE(offset, val) writel(val, dpi->regs + (offset))
>
> -/* VC4 DPI encoder KMS struct */
> -struct vc4_dpi_encoder {
> -       struct vc4_encoder base;
> -       struct vc4_dpi *dpi;
> -};
> -
> -static inline struct vc4_dpi_encoder *
> -to_vc4_dpi_encoder(struct drm_encoder *encoder)
> -{
> -       return container_of(encoder, struct vc4_dpi_encoder, base.base);
> -}
> -
>  static const struct debugfs_reg32 dpi_regs[] = {
>         VC4_REG32(DPI_C),
>         VC4_REG32(DPI_ID),
> @@ -117,8 +111,7 @@ static const struct debugfs_reg32 dpi_regs[] = {
>
>  static void vc4_dpi_encoder_disable(struct drm_encoder *encoder)
>  {
> -       struct vc4_dpi_encoder *vc4_encoder = to_vc4_dpi_encoder(encoder);
> -       struct vc4_dpi *dpi = vc4_encoder->dpi;
> +       struct vc4_dpi *dpi = to_vc4_dpi(encoder);
>
>         clk_disable_unprepare(dpi->pixel_clock);
>  }
> @@ -127,8 +120,7 @@ static void vc4_dpi_encoder_enable(struct drm_encoder *encoder)
>  {
>         struct drm_device *dev = encoder->dev;
>         struct drm_display_mode *mode = &encoder->crtc->mode;
> -       struct vc4_dpi_encoder *vc4_encoder = to_vc4_dpi_encoder(encoder);
> -       struct vc4_dpi *dpi = vc4_encoder->dpi;
> +       struct vc4_dpi *dpi = to_vc4_dpi(encoder);
>         struct drm_connector_list_iter conn_iter;
>         struct drm_connector *connector = NULL, *connector_scan;
>         u32 dpi_c = DPI_ENABLE | DPI_OUTPUT_ENABLE_MODE;
> @@ -242,7 +234,7 @@ static int vc4_dpi_init_bridge(struct vc4_dpi *dpi)
>                         return PTR_ERR(bridge);
>         }
>
> -       return drm_bridge_attach(dpi->encoder, bridge, NULL, 0);
> +       return drm_bridge_attach(&dpi->encoder.base, bridge, NULL, 0);
>  }
>
>  static int vc4_dpi_bind(struct device *dev, struct device *master, void *data)
> @@ -250,21 +242,12 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data)
>         struct platform_device *pdev = to_platform_device(dev);
>         struct drm_device *drm = dev_get_drvdata(master);
>         struct vc4_dpi *dpi;
> -       struct vc4_dpi_encoder *vc4_dpi_encoder;
>         int ret;
>
>         dpi = devm_kzalloc(dev, sizeof(*dpi), GFP_KERNEL);
>         if (!dpi)
>                 return -ENOMEM;
> -
> -       vc4_dpi_encoder = devm_kzalloc(dev, sizeof(*vc4_dpi_encoder),
> -                                      GFP_KERNEL);
> -       if (!vc4_dpi_encoder)
> -               return -ENOMEM;
> -       vc4_dpi_encoder->base.type = VC4_ENCODER_TYPE_DPI;
> -       vc4_dpi_encoder->dpi = dpi;
> -       dpi->encoder = &vc4_dpi_encoder->base.base;
> -
> +       dpi->encoder.type = VC4_ENCODER_TYPE_DPI;
>         dpi->pdev = pdev;
>         dpi->regs = vc4_ioremap_regs(pdev, 0);
>         if (IS_ERR(dpi->regs))
> @@ -298,8 +281,8 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data)
>         if (ret)
>                 DRM_ERROR("Failed to turn on core clock: %d\n", ret);
>
> -       drm_simple_encoder_init(drm, dpi->encoder, DRM_MODE_ENCODER_DPI);
> -       drm_encoder_helper_add(dpi->encoder, &vc4_dpi_encoder_helper_funcs);
> +       drm_simple_encoder_init(drm, &dpi->encoder.base, DRM_MODE_ENCODER_DPI);
> +       drm_encoder_helper_add(&dpi->encoder.base, &vc4_dpi_encoder_helper_funcs);
>
>         ret = vc4_dpi_init_bridge(dpi);
>         if (ret)
> @@ -312,7 +295,7 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data)
>         return 0;
>
>  err_destroy_encoder:
> -       drm_encoder_cleanup(dpi->encoder);
> +       drm_encoder_cleanup(&dpi->encoder.base);
>         clk_disable_unprepare(dpi->core_clock);
>         return ret;
>  }
> @@ -324,7 +307,7 @@ static void vc4_dpi_unbind(struct device *dev, struct device *master,
>
>         drm_of_panel_bridge_remove(dev->of_node, 0, 0);
>
> -       drm_encoder_cleanup(dpi->encoder);
> +       drm_encoder_cleanup(&dpi->encoder.base);
>
>         clk_disable_unprepare(dpi->core_clock);
>  }
> --
> 2.36.1
>

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 22/64] drm/vc4: dpi: Switch to drmm_kzalloc
  2022-06-10  9:28 ` [PATCH 22/64] drm/vc4: dpi: Switch to drmm_kzalloc Maxime Ripard
@ 2022-06-14 16:11   ` Dave Stevenson
  0 siblings, 0 replies; 124+ messages in thread
From: Dave Stevenson @ 2022-06-14 16:11 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: David Airlie, Daniel Vetter, DRI Development, Thomas Zimmermann

On Fri, 10 Jun 2022 at 10:30, Maxime Ripard <maxime@cerno.tech> wrote:
>
> Our internal structure that stores the DRM entities structure is allocated
> through a device-managed kzalloc.
>
> This means that this will eventually be freed whenever the device is
> removed. In our case, the most like source of removal is that the main
> device is going to be unbound, and component_unbind_all() is being run.
>
> However, it occurs while the DRM device is still registered, which will
> create dangling pointers, eventually resulting in use-after-free.
>
> Switch to a DRM-managed allocation to keep our structure until the DRM
> driver doesn't need it anymore.
>
> Signed-off-by: Maxime Ripard <maxime@cerno.tech>

Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>

> ---
>  drivers/gpu/drm/vc4/vc4_dpi.c | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c
> index c88e8e397730..d1eaafb43bd1 100644
> --- a/drivers/gpu/drm/vc4/vc4_dpi.c
> +++ b/drivers/gpu/drm/vc4/vc4_dpi.c
> @@ -244,9 +244,10 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data)
>         struct vc4_dpi *dpi;
>         int ret;
>
> -       dpi = devm_kzalloc(dev, sizeof(*dpi), GFP_KERNEL);
> +       dpi = drmm_kzalloc(drm, sizeof(*dpi), GFP_KERNEL);
>         if (!dpi)
>                 return -ENOMEM;
> +
>         dpi->encoder.type = VC4_ENCODER_TYPE_DPI;
>         dpi->pdev = pdev;
>         dpi->regs = vc4_ioremap_regs(pdev, 0);
> --
> 2.36.1
>

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 19/64] drm/vc4: crtc: Switch to DRM-managed CRTC initialization
  2022-06-10  9:28 ` [PATCH 19/64] drm/vc4: crtc: Switch to DRM-managed CRTC initialization Maxime Ripard
@ 2022-06-14 16:13   ` Dave Stevenson
  0 siblings, 0 replies; 124+ messages in thread
From: Dave Stevenson @ 2022-06-14 16:13 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: David Airlie, Daniel Vetter, DRI Development, Thomas Zimmermann

On Fri, 10 Jun 2022 at 10:30, Maxime Ripard <maxime@cerno.tech> wrote:
>
> The current code will call drm_crtc_cleanup() when the device is
> unbound. However, by then, there might still be some references held to
> that CRTC, including by the userspace that might still have the DRM
> device open.
>
> Let's switch to a DRM-managed initialization to clean up after ourselves
> only once the DRM device has been last closed.
>
> Signed-off-by: Maxime Ripard <maxime@cerno.tech>
> ---
>  drivers/gpu/drm/vc4/vc4_crtc.c | 18 +++++++-----------
>  drivers/gpu/drm/vc4/vc4_drv.h  |  1 -
>  drivers/gpu/drm/vc4/vc4_txp.c  |  1 -
>  3 files changed, 7 insertions(+), 13 deletions(-)
>
> diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c
> index c74fa3d07561..24de4706b61a 100644
> --- a/drivers/gpu/drm/vc4/vc4_crtc.c
> +++ b/drivers/gpu/drm/vc4/vc4_crtc.c
> @@ -205,11 +205,6 @@ static bool vc4_crtc_get_scanout_position(struct drm_crtc *crtc,
>         return ret;
>  }
>
> -void vc4_crtc_destroy(struct drm_crtc *crtc)
> -{
> -       drm_crtc_cleanup(crtc);
> -}
> -
>  static u32 vc4_get_fifo_full_level(struct vc4_crtc *vc4_crtc, u32 format)
>  {
>         const struct vc4_crtc_data *crtc_data = vc4_crtc_to_vc4_crtc_data(vc4_crtc);
> @@ -953,7 +948,6 @@ void vc4_crtc_reset(struct drm_crtc *crtc)
>
>  static const struct drm_crtc_funcs vc4_crtc_funcs = {
>         .set_config = drm_atomic_helper_set_config,
> -       .destroy = vc4_crtc_destroy,
>         .page_flip = vc4_page_flip,
>         .set_property = NULL,
>         .cursor_set = NULL, /* handled by drm_mode_cursor_universal */
> @@ -1131,6 +1125,7 @@ int vc4_crtc_init(struct drm_device *drm, struct vc4_crtc *vc4_crtc,
>         struct drm_crtc *crtc = &vc4_crtc->base;
>         struct drm_plane *primary_plane;
>         unsigned int i;
> +       int ret;
>
>         /* For now, we create just the primary and the legacy cursor
>          * planes.  We should be able to stack more planes on easily,
> @@ -1144,10 +1139,13 @@ int vc4_crtc_init(struct drm_device *drm, struct vc4_crtc *vc4_crtc,
>                 return PTR_ERR(primary_plane);
>         }
>
> -       spin_lock_init(&vc4_crtc->irq_lock);
> -       drm_crtc_init_with_planes(drm, crtc, primary_plane, NULL,
> -                                 crtc_funcs, NULL);
> +       ret = drmm_crtc_init_with_planes(drm, crtc, primary_plane, NULL,
> +                                        crtc_funcs, NULL);
> +       if (ret)
> +               return ret;
> +
>         drm_crtc_helper_add(crtc, crtc_helper_funcs);
> +       spin_lock_init(&vc4_crtc->irq_lock);

Moving the spin_lock_init appears to be cosmetic and unrelated, but otherwise:

Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>

>
>         if (!vc4->hvs->hvs5) {
>                 drm_mode_crtc_set_gamma_size(crtc, ARRAY_SIZE(vc4_crtc->lut_r));
> @@ -1226,8 +1224,6 @@ static void vc4_crtc_unbind(struct device *dev, struct device *master,
>         struct platform_device *pdev = to_platform_device(dev);
>         struct vc4_crtc *vc4_crtc = dev_get_drvdata(dev);
>
> -       vc4_crtc_destroy(&vc4_crtc->base);
> -
>         CRTC_WRITE(PV_INTEN, 0);
>
>         platform_set_drvdata(pdev, NULL);
> diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h
> index 9a53ace85d95..fff3772be2d4 100644
> --- a/drivers/gpu/drm/vc4/vc4_drv.h
> +++ b/drivers/gpu/drm/vc4/vc4_drv.h
> @@ -845,7 +845,6 @@ int vc4_crtc_disable_at_boot(struct drm_crtc *crtc);
>  int vc4_crtc_init(struct drm_device *drm, struct vc4_crtc *vc4_crtc,
>                   const struct drm_crtc_funcs *crtc_funcs,
>                   const struct drm_crtc_helper_funcs *crtc_helper_funcs);
> -void vc4_crtc_destroy(struct drm_crtc *crtc);
>  int vc4_page_flip(struct drm_crtc *crtc,
>                   struct drm_framebuffer *fb,
>                   struct drm_pending_vblank_event *event,
> diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c
> index e983ff7c5e13..f306e05ac5b2 100644
> --- a/drivers/gpu/drm/vc4/vc4_txp.c
> +++ b/drivers/gpu/drm/vc4/vc4_txp.c
> @@ -383,7 +383,6 @@ static void vc4_txp_disable_vblank(struct drm_crtc *crtc) {}
>
>  static const struct drm_crtc_funcs vc4_txp_crtc_funcs = {
>         .set_config             = drm_atomic_helper_set_config,
> -       .destroy                = vc4_crtc_destroy,
>         .page_flip              = vc4_page_flip,
>         .reset                  = vc4_crtc_reset,
>         .atomic_duplicate_state = vc4_crtc_duplicate_state,
> --
> 2.36.1
>

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 23/64] drm/vc4: dpi: Return an error if we can't enable our clock
  2022-06-10  9:28 ` [PATCH 23/64] drm/vc4: dpi: Return an error if we can't enable our clock Maxime Ripard
@ 2022-06-14 16:14   ` Dave Stevenson
  0 siblings, 0 replies; 124+ messages in thread
From: Dave Stevenson @ 2022-06-14 16:14 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: David Airlie, Daniel Vetter, DRI Development, Thomas Zimmermann

On Fri, 10 Jun 2022 at 10:30, Maxime Ripard <maxime@cerno.tech> wrote:
>
> If we fail to enable the DPI clock, we just ignore the error and moves
> forward. Let's return an error instead.
>
> Signed-off-by: Maxime Ripard <maxime@cerno.tech>

Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>

> ---
>  drivers/gpu/drm/vc4/vc4_dpi.c | 5 ++++-
>  1 file changed, 4 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c
> index d1eaafb43bd1..658e0aa9e2e1 100644
> --- a/drivers/gpu/drm/vc4/vc4_dpi.c
> +++ b/drivers/gpu/drm/vc4/vc4_dpi.c
> @@ -270,6 +270,7 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data)
>                         DRM_ERROR("Failed to get core clock: %d\n", ret);
>                 return ret;
>         }
> +
>         dpi->pixel_clock = devm_clk_get(dev, "pixel");
>         if (IS_ERR(dpi->pixel_clock)) {
>                 ret = PTR_ERR(dpi->pixel_clock);
> @@ -279,8 +280,10 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data)
>         }
>
>         ret = clk_prepare_enable(dpi->core_clock);
> -       if (ret)
> +       if (ret) {
>                 DRM_ERROR("Failed to turn on core clock: %d\n", ret);
> +               return ret;
> +       }
>
>         drm_simple_encoder_init(drm, &dpi->encoder.base, DRM_MODE_ENCODER_DPI);
>         drm_encoder_helper_add(&dpi->encoder.base, &vc4_dpi_encoder_helper_funcs);
> --
> 2.36.1
>

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 24/64] drm/vc4: dpi: Remove unnecessary drm_of_panel_bridge_remove call
  2022-06-10  9:28 ` [PATCH 24/64] drm/vc4: dpi: Remove unnecessary drm_of_panel_bridge_remove call Maxime Ripard
@ 2022-06-14 16:37   ` Dave Stevenson
  0 siblings, 0 replies; 124+ messages in thread
From: Dave Stevenson @ 2022-06-14 16:37 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: David Airlie, Daniel Vetter, DRI Development, Thomas Zimmermann

On Fri, 10 Jun 2022 at 10:30, Maxime Ripard <maxime@cerno.tech> wrote:
>
> Since we have a managed call to create our panel_bridge instance, the call
> to drm_of_panel_bridge_remove() at unbind is both redundant and dangerous
> since it might lead to a use-after-free.
>
> Signed-off-by: Maxime Ripard <maxime@cerno.tech>

Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>

> ---
>  drivers/gpu/drm/vc4/vc4_dpi.c | 2 --
>  1 file changed, 2 deletions(-)
>
> diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c
> index 658e0aa9e2e1..5a6cdea7bf7b 100644
> --- a/drivers/gpu/drm/vc4/vc4_dpi.c
> +++ b/drivers/gpu/drm/vc4/vc4_dpi.c
> @@ -309,8 +309,6 @@ static void vc4_dpi_unbind(struct device *dev, struct device *master,
>  {
>         struct vc4_dpi *dpi = dev_get_drvdata(dev);
>
> -       drm_of_panel_bridge_remove(dev->of_node, 0, 0);
> -
>         drm_encoder_cleanup(&dpi->encoder.base);
>
>         clk_disable_unprepare(dpi->core_clock);
> --
> 2.36.1
>

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 25/64] drm/vc4: dpi: Add action to disable the clock
  2022-06-10  9:28 ` [PATCH 25/64] drm/vc4: dpi: Add action to disable the clock Maxime Ripard
@ 2022-06-14 16:47   ` Dave Stevenson
  2022-06-16  8:38     ` Maxime Ripard
  0 siblings, 1 reply; 124+ messages in thread
From: Dave Stevenson @ 2022-06-14 16:47 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: David Airlie, Daniel Vetter, DRI Development, Thomas Zimmermann

Hi Maxime.

On Fri, 10 Jun 2022 at 10:30, Maxime Ripard <maxime@cerno.tech> wrote:
>
> Adding a device-managed action will make the error path easier, so let's
> create one to disable our clock.

The DPI block has two clocks (core and pixel), and this is only
affecting one of them (the core clock).
(I'm actually puzzling over what it's wanting to do with the core
clock here as it's only enabling it rather than setting a rate. I
think it may be redundant).

With that text amended:
Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>

  Dave

> Signed-off-by: Maxime Ripard <maxime@cerno.tech>
> ---
>  drivers/gpu/drm/vc4/vc4_dpi.c | 14 +++++++++++---
>  1 file changed, 11 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c
> index 5a6cdea7bf7b..4e24dbad77f2 100644
> --- a/drivers/gpu/drm/vc4/vc4_dpi.c
> +++ b/drivers/gpu/drm/vc4/vc4_dpi.c
> @@ -237,6 +237,13 @@ static int vc4_dpi_init_bridge(struct vc4_dpi *dpi)
>         return drm_bridge_attach(&dpi->encoder.base, bridge, NULL, 0);
>  }
>
> +static void vc4_dpi_disable_clock(void *ptr)
> +{
> +       struct vc4_dpi *dpi = ptr;
> +
> +       clk_disable_unprepare(dpi->core_clock);
> +}
> +
>  static int vc4_dpi_bind(struct device *dev, struct device *master, void *data)
>  {
>         struct platform_device *pdev = to_platform_device(dev);
> @@ -285,6 +292,10 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data)
>                 return ret;
>         }
>
> +       ret = devm_add_action_or_reset(dev, vc4_dpi_disable_clock, dpi);
> +       if (ret)
> +               return ret;
> +
>         drm_simple_encoder_init(drm, &dpi->encoder.base, DRM_MODE_ENCODER_DPI);
>         drm_encoder_helper_add(&dpi->encoder.base, &vc4_dpi_encoder_helper_funcs);
>
> @@ -300,7 +311,6 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data)
>
>  err_destroy_encoder:
>         drm_encoder_cleanup(&dpi->encoder.base);
> -       clk_disable_unprepare(dpi->core_clock);
>         return ret;
>  }
>
> @@ -310,8 +320,6 @@ static void vc4_dpi_unbind(struct device *dev, struct device *master,
>         struct vc4_dpi *dpi = dev_get_drvdata(dev);
>
>         drm_encoder_cleanup(&dpi->encoder.base);
> -
> -       clk_disable_unprepare(dpi->core_clock);
>  }
>
>  static const struct component_ops vc4_dpi_ops = {
> --
> 2.36.1
>

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 26/64] drm/vc4: dpi: Switch to DRM-managed encoder initialization
  2022-06-10  9:28 ` [PATCH 26/64] drm/vc4: dpi: Switch to DRM-managed encoder initialization Maxime Ripard
@ 2022-06-14 16:51   ` Dave Stevenson
  0 siblings, 0 replies; 124+ messages in thread
From: Dave Stevenson @ 2022-06-14 16:51 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: David Airlie, Daniel Vetter, DRI Development, Thomas Zimmermann

On Fri, 10 Jun 2022 at 10:30, Maxime Ripard <maxime@cerno.tech> wrote:
>
> The current code will call drm_encoder_cleanup() when the device is
> unbound. However, by then, there might still be some references held to
> that encoder, including by the userspace that might still have the DRM
> device open.
>
> Let's switch to a DRM-managed initialization to clean up after ourselves
> only once the DRM device has been last closed.
>
> Signed-off-by: Maxime Ripard <maxime@cerno.tech>

Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>

> ---
>  drivers/gpu/drm/vc4/vc4_dpi.c | 20 +++++---------------
>  1 file changed, 5 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c
> index 4e24dbad77f2..8a50de2c40d9 100644
> --- a/drivers/gpu/drm/vc4/vc4_dpi.c
> +++ b/drivers/gpu/drm/vc4/vc4_dpi.c
> @@ -296,35 +296,25 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data)
>         if (ret)
>                 return ret;
>
> -       drm_simple_encoder_init(drm, &dpi->encoder.base, DRM_MODE_ENCODER_DPI);
> +       ret = drmm_simple_encoder_init(drm, &dpi->encoder.base, DRM_MODE_ENCODER_DPI);
> +       if (ret)
> +               return ret;
> +
>         drm_encoder_helper_add(&dpi->encoder.base, &vc4_dpi_encoder_helper_funcs);
>
>         ret = vc4_dpi_init_bridge(dpi);
>         if (ret)
> -               goto err_destroy_encoder;
> +               return ret;
>
>         dev_set_drvdata(dev, dpi);
>
>         vc4_debugfs_add_regset32(drm, "dpi_regs", &dpi->regset);
>
>         return 0;
> -
> -err_destroy_encoder:
> -       drm_encoder_cleanup(&dpi->encoder.base);
> -       return ret;
> -}
> -
> -static void vc4_dpi_unbind(struct device *dev, struct device *master,
> -                          void *data)
> -{
> -       struct vc4_dpi *dpi = dev_get_drvdata(dev);
> -
> -       drm_encoder_cleanup(&dpi->encoder.base);
>  }
>
>  static const struct component_ops vc4_dpi_ops = {
>         .bind   = vc4_dpi_bind,
> -       .unbind = vc4_dpi_unbind,
>  };
>
>  static int vc4_dpi_dev_probe(struct platform_device *pdev)
> --
> 2.36.1
>

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 27/64] drm/vc4: dpi: Switch to drmm_of_get_bridge
  2022-06-10  9:28 ` [PATCH 27/64] drm/vc4: dpi: Switch to drmm_of_get_bridge Maxime Ripard
@ 2022-06-14 16:52   ` Dave Stevenson
  0 siblings, 0 replies; 124+ messages in thread
From: Dave Stevenson @ 2022-06-14 16:52 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: David Airlie, Daniel Vetter, DRI Development, Thomas Zimmermann

On Fri, 10 Jun 2022 at 10:30, Maxime Ripard <maxime@cerno.tech> wrote:
>
> The current code uses a device-managed function to retrieve the next bridge
> downstream.
>
> However, that means that it will be removed at unbind time, where the DRM
> device is still very much live and might still have some applications that
> still have it open.
>
> Switch to a DRM-managed variant to clean everything up once the DRM device
> has been last closed.
>
> Signed-off-by: Maxime Ripard <maxime@cerno.tech>

Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>

> ---
>  drivers/gpu/drm/vc4/vc4_dpi.c | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c
> index 8a50de2c40d9..9950761449cf 100644
> --- a/drivers/gpu/drm/vc4/vc4_dpi.c
> +++ b/drivers/gpu/drm/vc4/vc4_dpi.c
> @@ -220,10 +220,11 @@ static const struct of_device_id vc4_dpi_dt_match[] = {
>   */
>  static int vc4_dpi_init_bridge(struct vc4_dpi *dpi)
>  {
> +       struct drm_device *drm = dpi->encoder.base.dev;
>         struct device *dev = &dpi->pdev->dev;
>         struct drm_bridge *bridge;
>
> -       bridge = devm_drm_of_get_bridge(dev, dev->of_node, 0, 0);
> +       bridge = drmm_of_get_bridge(drm, dev->of_node, 0, 0);
>         if (IS_ERR(bridge)) {
>                 /* If nothing was connected in the DT, that's not an
>                  * error.
> --
> 2.36.1
>

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 28/64] drm/vc4: dpi: Protect device resources
  2022-06-10  9:28 ` [PATCH 28/64] drm/vc4: dpi: Protect device resources Maxime Ripard
@ 2022-06-14 16:55   ` Dave Stevenson
  0 siblings, 0 replies; 124+ messages in thread
From: Dave Stevenson @ 2022-06-14 16:55 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: David Airlie, Daniel Vetter, DRI Development, Thomas Zimmermann

On Fri, 10 Jun 2022 at 10:30, Maxime Ripard <maxime@cerno.tech> wrote:
>
> Our current code now mixes some resources whose lifetime are tied to the
> device (clocks, IO mappings, etc.) and some that are tied to the DRM device
> (encoder, bridge).
>
> The device one will be freed at unbind time, but the DRM one will only be
> freed when the last user of the DRM device closes its file handle.
>
> So we end up with a time window during which we can call the encoder hooks,
> but we don't have access to the underlying resources and device.
>
> Let's protect all those sections with drm_dev_enter() and drm_dev_exit() so
> that we bail out if we are during that window.
>
> Signed-off-by: Maxime Ripard <maxime@cerno.tech>

Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>

> ---
>  drivers/gpu/drm/vc4/vc4_dpi.c | 14 ++++++++++++++
>  1 file changed, 14 insertions(+)
>
> diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c
> index 9950761449cf..ea3d20651f43 100644
> --- a/drivers/gpu/drm/vc4/vc4_dpi.c
> +++ b/drivers/gpu/drm/vc4/vc4_dpi.c
> @@ -13,6 +13,7 @@
>
>  #include <drm/drm_atomic_helper.h>
>  #include <drm/drm_bridge.h>
> +#include <drm/drm_drv.h>
>  #include <drm/drm_edid.h>
>  #include <drm/drm_of.h>
>  #include <drm/drm_panel.h>
> @@ -111,9 +112,16 @@ static const struct debugfs_reg32 dpi_regs[] = {
>
>  static void vc4_dpi_encoder_disable(struct drm_encoder *encoder)
>  {
> +       struct drm_device *dev = encoder->dev;
>         struct vc4_dpi *dpi = to_vc4_dpi(encoder);
> +       int idx;
> +
> +       if (!drm_dev_enter(dev, &idx))
> +               return;
>
>         clk_disable_unprepare(dpi->pixel_clock);
> +
> +       drm_dev_exit(idx);
>  }
>
>  static void vc4_dpi_encoder_enable(struct drm_encoder *encoder)
> @@ -124,6 +132,7 @@ static void vc4_dpi_encoder_enable(struct drm_encoder *encoder)
>         struct drm_connector_list_iter conn_iter;
>         struct drm_connector *connector = NULL, *connector_scan;
>         u32 dpi_c = DPI_ENABLE | DPI_OUTPUT_ENABLE_MODE;
> +       int idx;
>         int ret;
>
>         /* Look up the connector attached to DPI so we can get the
> @@ -184,6 +193,9 @@ static void vc4_dpi_encoder_enable(struct drm_encoder *encoder)
>         else if (!(mode->flags & DRM_MODE_FLAG_PVSYNC))
>                 dpi_c |= DPI_VSYNC_DISABLE;
>
> +       if (!drm_dev_enter(dev, &idx))
> +               return;
> +
>         DPI_WRITE(DPI_C, dpi_c);
>
>         ret = clk_set_rate(dpi->pixel_clock, mode->clock * 1000);
> @@ -193,6 +205,8 @@ static void vc4_dpi_encoder_enable(struct drm_encoder *encoder)
>         ret = clk_prepare_enable(dpi->pixel_clock);
>         if (ret)
>                 DRM_ERROR("Failed to set clock rate: %d\n", ret);
> +
> +       drm_dev_exit(idx);
>  }
>
>  static enum drm_mode_status vc4_dpi_encoder_mode_valid(struct drm_encoder *encoder,
> --
> 2.36.1
>

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 13/64] drm/vc4: hvs: Protect device resources after removal
  2022-06-14 15:11   ` Dave Stevenson
@ 2022-06-14 16:59     ` Dave Stevenson
  2022-06-20 13:23       ` Maxime Ripard
  2022-06-20 13:20     ` Maxime Ripard
  1 sibling, 1 reply; 124+ messages in thread
From: Dave Stevenson @ 2022-06-14 16:59 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: David Airlie, Daniel Vetter, DRI Development, Thomas Zimmermann

On Tue, 14 Jun 2022 at 16:11, Dave Stevenson
<dave.stevenson@raspberrypi.com> wrote:
>
> Hi Maxime
>
> On Fri, 10 Jun 2022 at 10:30, Maxime Ripard <maxime@cerno.tech> wrote:
> >
> > Whenever the device and driver are unbound, the main device and all the
> > subdevices will be removed by calling their unbind() method.
> >
> > However, the DRM device itself will only be freed when the last user will
> > have closed it.
> >
> > It means that there is a time window where the device and its resources
> > aren't there anymore, but the userspace can still call into our driver.
> >
> > Fortunately, the DRM framework provides the drm_dev_enter() and
> > drm_dev_exit() functions to make sure our underlying device is still there
> > for the section protected by those calls. Let's add them to the HVS driver.
>
> The framework appears to rely on the remove function calling
> drm_dev_unplug instead of drm_dev_unregister, but I haven't seen a
> patch that makes that change in the vc4 driver.
> Have I missed it, or is there some other route to set the unplugged
> flag that drm_dev_enter/exit are relying on?
>
>   Dave
>
> > Signed-off-by: Maxime Ripard <maxime@cerno.tech>
> > ---
> >  drivers/gpu/drm/vc4/vc4_drv.h |   1 +
> >  drivers/gpu/drm/vc4/vc4_hvs.c | 106 +++++++++++++++++++++++++++++++---
> >  2 files changed, 99 insertions(+), 8 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h
> > index aa4c5910ea05..080deae55f64 100644
> > --- a/drivers/gpu/drm/vc4/vc4_drv.h
> > +++ b/drivers/gpu/drm/vc4/vc4_drv.h
> > @@ -317,6 +317,7 @@ struct vc4_v3d {
> >  };
> >
> >  struct vc4_hvs {
> > +       struct drm_device *dev;
> >         struct platform_device *pdev;
> >         void __iomem *regs;
> >         u32 __iomem *dlist;
> > diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c
> > index 2a58fc421cf6..483053e7b14f 100644
> > --- a/drivers/gpu/drm/vc4/vc4_hvs.c
> > +++ b/drivers/gpu/drm/vc4/vc4_hvs.c
> > @@ -25,6 +25,7 @@
> >  #include <linux/platform_device.h>
> >
> >  #include <drm/drm_atomic_helper.h>
> > +#include <drm/drm_drv.h>
> >  #include <drm/drm_vblank.h>
> >
> >  #include "vc4_drv.h"
> > @@ -66,11 +67,15 @@ static const struct debugfs_reg32 hvs_regs[] = {
> >
> >  void vc4_hvs_dump_state(struct vc4_hvs *hvs)
> >  {
> > +       struct drm_device *drm = hvs->dev;
> >         struct drm_printer p = drm_info_printer(&hvs->pdev->dev);
> > -       int i;
> > +       int idx, i;
> >
> >         drm_print_regset32(&p, &hvs->regset);
> >
> > +       if (!drm_dev_enter(drm, &idx))
> > +               return;
> > +
> >         DRM_INFO("HVS ctx:\n");
> >         for (i = 0; i < 64; i += 4) {
> >                 DRM_INFO("0x%08x (%s): 0x%08x 0x%08x 0x%08x 0x%08x\n",
> > @@ -80,6 +85,8 @@ void vc4_hvs_dump_state(struct vc4_hvs *hvs)
> >                          readl((u32 __iomem *)hvs->dlist + i + 2),
> >                          readl((u32 __iomem *)hvs->dlist + i + 3));
> >         }
> > +
> > +       drm_dev_exit(idx);
> >  }
> >
> >  static int vc4_hvs_debugfs_underrun(struct seq_file *m, void *data)
> > @@ -132,14 +139,18 @@ static int vc4_hvs_upload_linear_kernel(struct vc4_hvs *hvs,
> >                                         struct drm_mm_node *space,
> >                                         const u32 *kernel)
> >  {
> > -       int ret, i;
> > +       struct drm_device *drm = hvs->dev;
> > +       int idx, ret, i;
> >         u32 __iomem *dst_kernel;
> >
> > +       if (!drm_dev_enter(drm, &idx))
> > +               return -ENODEV;
> > +

vc4_hvs_upload_linear_kernel is only called from vc4_hvs_bind, so
unless bind and unbind calls can be concurrent, then there's no need
for protection here.

> >         ret = drm_mm_insert_node(&hvs->dlist_mm, space, VC4_KERNEL_DWORDS);
> >         if (ret) {
> >                 DRM_ERROR("Failed to allocate space for filter kernel: %d\n",
> >                           ret);
> > -               return ret;
> > +               goto err_dev_exit;
> >         }
> >
> >         dst_kernel = hvs->dlist + space->start;
> > @@ -153,16 +164,26 @@ static int vc4_hvs_upload_linear_kernel(struct vc4_hvs *hvs,
> >                 }
> >         }
> >
> > +       drm_dev_exit(idx);
> >         return 0;
> > +
> > +err_dev_exit:
> > +       drm_dev_exit(idx);
> > +       return ret;
> >  }
> >
> >  static void vc4_hvs_lut_load(struct vc4_hvs *hvs,
> >                              struct vc4_crtc *vc4_crtc)
> >  {
> > +       struct drm_device *drm = hvs->dev;
> >         struct drm_crtc *crtc = &vc4_crtc->base;
> >         struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state);
> > +       int idx;
> >         u32 i;
> >
> > +       if (!drm_dev_enter(drm, &idx))
> > +               return;
> > +
> >         /* The LUT memory is laid out with each HVS channel in order,
> >          * each of which takes 256 writes for R, 256 for G, then 256
> >          * for B.
> > @@ -177,6 +198,8 @@ static void vc4_hvs_lut_load(struct vc4_hvs *hvs,
> >                 HVS_WRITE(SCALER_GAMDATA, vc4_crtc->lut_g[i]);
> >         for (i = 0; i < crtc->gamma_size; i++)
> >                 HVS_WRITE(SCALER_GAMDATA, vc4_crtc->lut_b[i]);
> > +
> > +       drm_dev_exit(idx);
> >  }
> >
> >  static void vc4_hvs_update_gamma_lut(struct vc4_hvs *hvs,
> > @@ -198,7 +221,12 @@ static void vc4_hvs_update_gamma_lut(struct vc4_hvs *hvs,
> >
> >  u8 vc4_hvs_get_fifo_frame_count(struct vc4_hvs *hvs, unsigned int fifo)
> >  {
> > +       struct drm_device *drm = hvs->dev;
> >         u8 field = 0;
> > +       int idx;
> > +
> > +       if (!drm_dev_enter(drm, &idx))
> > +               return 0;
> >
> >         switch (fifo) {
> >         case 0:
> > @@ -215,6 +243,7 @@ u8 vc4_hvs_get_fifo_frame_count(struct vc4_hvs *hvs, unsigned int fifo)
> >                 break;
> >         }
> >
> > +       drm_dev_exit(idx);
> >         return field;
> >  }
> >
> > @@ -226,6 +255,12 @@ int vc4_hvs_get_fifo_from_output(struct vc4_hvs *hvs, unsigned int output)
> >         if (!hvs->hvs5)
> >                 return output;
> >
> > +       /*
> > +        * NOTE: We should probably use drm_dev_enter()/drm_dev_exit()
> > +        * here, but this function is only used during the DRM device
> > +        * initialization, so we should be fine.
> > +        */
> > +
> >         switch (output) {
> >         case 0:
> >                 return 0;
> > @@ -273,12 +308,17 @@ int vc4_hvs_get_fifo_from_output(struct vc4_hvs *hvs, unsigned int output)
> >  static int vc4_hvs_init_channel(struct vc4_hvs *hvs, struct drm_crtc *crtc,
> >                                 struct drm_display_mode *mode, bool oneshot)
> >  {
> > +       struct drm_device *drm = hvs->dev;
> >         struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
> >         struct vc4_crtc_state *vc4_crtc_state = to_vc4_crtc_state(crtc->state);
> >         unsigned int chan = vc4_crtc_state->assigned_channel;
> >         bool interlace = mode->flags & DRM_MODE_FLAG_INTERLACE;
> >         u32 dispbkgndx;
> >         u32 dispctrl;
> > +       int idx;
> > +
> > +       if (!drm_dev_enter(drm, &idx))
> > +               return -ENODEV;
> >
> >         HVS_WRITE(SCALER_DISPCTRLX(chan), 0);
> >         HVS_WRITE(SCALER_DISPCTRLX(chan), SCALER_DISPCTRLX_RESET);
> > @@ -320,13 +360,21 @@ static int vc4_hvs_init_channel(struct vc4_hvs *hvs, struct drm_crtc *crtc,
> >          */
> >         vc4_hvs_lut_load(hvs, vc4_crtc);
> >
> > +       drm_dev_exit(idx);
> > +
> >         return 0;
> >  }
> >
> >  void vc4_hvs_stop_channel(struct vc4_hvs *hvs, unsigned int chan)
> >  {
> > +       struct drm_device *drm = hvs->dev;
> > +       int idx;
> > +
> > +       if (!drm_dev_enter(drm, &idx))
> > +               return;
> > +
> >         if (HVS_READ(SCALER_DISPCTRLX(chan)) & SCALER_DISPCTRLX_ENABLE)
> > -               return;
> > +               goto out;
> >
> >         HVS_WRITE(SCALER_DISPCTRLX(chan),
> >                   HVS_READ(SCALER_DISPCTRLX(chan)) | SCALER_DISPCTRLX_RESET);
> > @@ -343,6 +391,9 @@ void vc4_hvs_stop_channel(struct vc4_hvs *hvs, unsigned int chan)
> >         WARN_ON_ONCE((HVS_READ(SCALER_DISPSTATX(chan)) &
> >                       (SCALER_DISPSTATX_FULL | SCALER_DISPSTATX_EMPTY)) !=
> >                      SCALER_DISPSTATX_EMPTY);
> > +
> > +out:
> > +       drm_dev_exit(idx);
> >  }
> >
> >  int vc4_hvs_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state)
> > @@ -384,9 +435,15 @@ static void vc4_hvs_install_dlist(struct drm_crtc *crtc)
> >         struct vc4_dev *vc4 = to_vc4_dev(dev);
> >         struct vc4_hvs *hvs = vc4->hvs;
> >         struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state);
> > +       int idx;
> > +
> > +       if (!drm_dev_enter(dev, &idx))
> > +               return;
> >
> >         HVS_WRITE(SCALER_DISPLISTX(vc4_state->assigned_channel),
> >                   vc4_state->mm.start);
> > +
> > +       drm_dev_exit(idx);
> >  }
> >
> >  static void vc4_hvs_update_dlist(struct drm_crtc *crtc)
> > @@ -471,6 +528,10 @@ void vc4_hvs_atomic_flush(struct drm_crtc *crtc,
> >         bool enable_bg_fill = false;
> >         u32 __iomem *dlist_start = vc4->hvs->dlist + vc4_state->mm.start;
> >         u32 __iomem *dlist_next = dlist_start;
> > +       int idx;
> > +
> > +       if (!drm_dev_enter(dev, &idx))
> > +               return;
> >
> >         if (debug_dump_regs) {
> >                 DRM_INFO("CRTC %d HVS before:\n", drm_crtc_index(crtc));
> > @@ -541,26 +602,44 @@ void vc4_hvs_atomic_flush(struct drm_crtc *crtc,
> >                 DRM_INFO("CRTC %d HVS after:\n", drm_crtc_index(crtc));
> >                 vc4_hvs_dump_state(hvs);
> >         }
> > +
> > +       drm_dev_exit(idx);
> >  }
> >
> >  void vc4_hvs_mask_underrun(struct vc4_hvs *hvs, int channel)
> >  {
> > -       u32 dispctrl = HVS_READ(SCALER_DISPCTRL);
> > +       struct drm_device *drm = hvs->dev;
> > +       u32 dispctrl;
> > +       int idx;
> >
> > +       if (!drm_dev_enter(drm, &idx))
> > +               return;
> > +
> > +       dispctrl = HVS_READ(SCALER_DISPCTRL);
> >         dispctrl &= ~SCALER_DISPCTRL_DSPEISLUR(channel);
> >
> >         HVS_WRITE(SCALER_DISPCTRL, dispctrl);
> > +
> > +       drm_dev_exit(idx);
> >  }
> >
> >  void vc4_hvs_unmask_underrun(struct vc4_hvs *hvs, int channel)
> >  {
> > -       u32 dispctrl = HVS_READ(SCALER_DISPCTRL);
> > +       struct drm_device *drm = hvs->dev;
> > +       u32 dispctrl;
> > +       int idx;
> >
> > +       if (!drm_dev_enter(drm, &idx))
> > +               return;
> > +
> > +       dispctrl = HVS_READ(SCALER_DISPCTRL);
> >         dispctrl |= SCALER_DISPCTRL_DSPEISLUR(channel);
> >
> >         HVS_WRITE(SCALER_DISPSTAT,
> >                   SCALER_DISPSTAT_EUFLOW(channel));
> >         HVS_WRITE(SCALER_DISPCTRL, dispctrl);
> > +
> > +       drm_dev_exit(idx);
> >  }
> >
> >  static void vc4_hvs_report_underrun(struct drm_device *dev)
> > @@ -581,6 +660,17 @@ static irqreturn_t vc4_hvs_irq_handler(int irq, void *data)
> >         u32 control;
> >         u32 status;
> >
> > +       /*
> > +        * NOTE: We don't need to protect the register access using
> > +        * drm_dev_enter() there because the interrupt handler lifetime
> > +        * is tied to the device itself, and not to the DRM device.
> > +        *
> > +        * So when the device will be gone, one of the first thing we
> > +        * will be doing will be to unregister the interrupt handler,
> > +        * and then unregister the DRM device. drm_dev_enter() would
> > +        * thus always succeed if we are here.
> > +        */
> > +
> >         status = HVS_READ(SCALER_DISPSTAT);
> >         control = HVS_READ(SCALER_DISPCTRL);
> >
> > @@ -613,10 +703,10 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)
> >         u32 dispctrl;
> >         u32 reg;
> >
> > -       hvs = devm_kzalloc(&pdev->dev, sizeof(*hvs), GFP_KERNEL);
> > +       hvs = drmm_kzalloc(drm, sizeof(*hvs), GFP_KERNEL);
> >         if (!hvs)
> >                 return -ENOMEM;
> > -
> > +       hvs->dev = drm;
> >         hvs->pdev = pdev;
> >
> >         if (of_device_is_compatible(pdev->dev.of_node, "brcm,bcm2711-hvs"))
> > --
> > 2.36.1
> >

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 30/64] drm/vc4: dsi: Switch to DRM-managed encoder initialization
  2022-06-10  9:28 ` [PATCH 30/64] drm/vc4: dsi: Switch to DRM-managed encoder initialization Maxime Ripard
@ 2022-06-14 17:04   ` Dave Stevenson
  0 siblings, 0 replies; 124+ messages in thread
From: Dave Stevenson @ 2022-06-14 17:04 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: David Airlie, Daniel Vetter, DRI Development, Thomas Zimmermann

On Fri, 10 Jun 2022 at 10:30, Maxime Ripard <maxime@cerno.tech> wrote:
>
> The current code will call drm_encoder_cleanup() when the device is
> unbound. However, by then, there might still be some references held to
> that encoder, including by the userspace that might still have the DRM
> device open.
>
> Let's switch to a DRM-managed initialization to clean up after ourselves
> only once the DRM device has been last closed.
>
> Signed-off-by: Maxime Ripard <maxime@cerno.tech>

Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>

> ---
>  drivers/gpu/drm/vc4/vc4_dsi.c | 6 ++++--
>  1 file changed, 4 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
> index dbb3f6fb39b4..bcaf87b43cbd 100644
> --- a/drivers/gpu/drm/vc4/vc4_dsi.c
> +++ b/drivers/gpu/drm/vc4/vc4_dsi.c
> @@ -1599,7 +1599,10 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
>         if (ret)
>                 return ret;
>
> -       drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_DSI);
> +       ret = drmm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_DSI);
> +       if (ret)
> +               return ret;
> +
>         drm_encoder_helper_add(encoder, &vc4_dsi_encoder_helper_funcs);
>
>         ret = drm_bridge_attach(encoder, dsi->bridge, NULL, 0);
> @@ -1632,7 +1635,6 @@ static void vc4_dsi_unbind(struct device *dev, struct device *master,
>          * normally.
>          */
>         list_splice_init(&dsi->bridge_chain, &encoder->bridge_chain);
> -       drm_encoder_cleanup(encoder);
>  }
>
>  static const struct component_ops vc4_dsi_ops = {
> --
> 2.36.1
>

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 31/64] drm/vc4: dsi: Switch to drmm_of_get_bridge
  2022-06-10  9:28 ` [PATCH 31/64] drm/vc4: dsi: Switch to drmm_of_get_bridge Maxime Ripard
@ 2022-06-14 17:05   ` Dave Stevenson
  0 siblings, 0 replies; 124+ messages in thread
From: Dave Stevenson @ 2022-06-14 17:05 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: David Airlie, Daniel Vetter, DRI Development, Thomas Zimmermann

On Fri, 10 Jun 2022 at 10:30, Maxime Ripard <maxime@cerno.tech> wrote:
>
> The current code uses a device-managed function to retrieve the next bridge
> downstream.
>
> However, that means that it will be removed at unbind time, where the DRM
> device is still very much live and might still have some applications that
> still have it open.
>
> Switch to a DRM-managed variant to clean everything up once the DRM device
> has been last closed.
>
> Signed-off-by: Maxime Ripard <maxime@cerno.tech>

Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>

> ---
>  drivers/gpu/drm/vc4/vc4_dsi.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
> index bcaf87b43cbd..10533a2a41b3 100644
> --- a/drivers/gpu/drm/vc4/vc4_dsi.c
> +++ b/drivers/gpu/drm/vc4/vc4_dsi.c
> @@ -1584,7 +1584,7 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
>                 return ret;
>         }
>
> -       dsi->bridge = devm_drm_of_get_bridge(dev, dev->of_node, 0, 0);
> +       dsi->bridge = drmm_of_get_bridge(drm, dev->of_node, 0, 0);
>         if (IS_ERR(dsi->bridge))
>                 return PTR_ERR(dsi->bridge);
>
> --
> 2.36.1
>

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 02/64] drm/crtc: Introduce drmm_crtc_init_with_planes
  2022-06-14 12:09             ` Maxime Ripard
@ 2022-06-15  7:22               ` Thomas Zimmermann
  2022-06-15  8:32                 ` Maxime Ripard
  0 siblings, 1 reply; 124+ messages in thread
From: Thomas Zimmermann @ 2022-06-15  7:22 UTC (permalink / raw)
  To: Maxime Ripard; +Cc: David Airlie, Daniel Vetter, dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 7223 bytes --]

Hi

Am 14.06.22 um 14:09 schrieb Maxime Ripard:
> On Tue, Jun 14, 2022 at 01:47:28PM +0200, Thomas Zimmermann wrote:
>> Am 14.06.22 um 11:04 schrieb Maxime Ripard:
>>> On Tue, Jun 14, 2022 at 10:29:20AM +0200, Thomas Zimmermann wrote:
>>>> Am 14.06.22 um 09:37 schrieb Maxime Ripard:
>>>>> Hi Thomas,
>>>>>
>>>>> On Mon, Jun 13, 2022 at 01:23:54PM +0200, Thomas Zimmermann wrote:
>>>>>> Am 10.06.22 um 11:28 schrieb Maxime Ripard:
>>>>>>> The DRM-managed function to register a CRTC is
>>>>>>> drmm_crtc_alloc_with_planes(), which will allocate the underlying
>>>>>>> structure and initialisation the CRTC.
>>>>>>>
>>>>>>> However, we might want to separate the structure creation and the CRTC
>>>>>>> initialisation, for example if the structure is shared across multiple
>>>>>>> DRM entities, for example an encoder and a connector.
>>>>>>>
>>>>>>> Let's create an helper to only initialise a CRTC that would be passed as
>>>>>>> an argument.
>>>>>>
>>>>>> Before I review all of thes patches, one question. it's yet not clear to me
>>>>>> why drm_crtc_init_with_planes() wouldn't work. (I know we discussed this on
>>>>>> IRC.)
>>>>>>
>>>>>> If you're calling drmm_mode_config_init(), it will clean up all the CRTC,
>>>>>> encoder connector, etc. data structures for you. For CRTC instances in
>>>>>> kmalloced memory, wouldn't it be simpler to put the corresponding kfree into
>>>>>> vc4_crtc_destroy()?
>>>>>
>>>>> My intent was to remove as much of the lifetime handling and
>>>>> memory-management from drivers as possible.
>>>>>
>>>>> My feeling is that while the solution you suggest would definitely work,
>>>>> it relies on drivers authors to know about this, and make the effort to
>>>>> convert the drivers themselves. And then we would have to review that,
>>>>> which we will probably miss (collectively), because it's a bit obscure.
>>>>>
>>>>> While with either the drmm_alloc_* functions, or the new functions I
>>>>> introduce there, we can just deprecate the old ones, create a TODO entry
>>>>> and a coccinelle script and trust that it works properly.
>>>>
>>>> Thanks for explaining the motivation.
>>>>
>>>> I would not want to deprecate any of the existing functions, as they work
>>>> for many drivers. The drmm_ functions add additional overhead that not
>>>> everyone is willing to pay.
>>>
>>> I'm a bit confused. drm_crtc_init_with_planes() at the moment has:
>>>
>>> * Note: consider using drmm_crtc_alloc_with_planes() instead of
>>> * drm_crtc_init_with_planes() to let the DRM managed resource infrastructure
>>> * take care of cleanup and deallocation.
>>>
>>> Just like drm_encoder_init(), drm_simple_encoder_init() and
>>> drm_universal_plane_init(), so all the functions we have a drmm_* helper
>>> for.
>>>
>>> And we have a TODO-list item that heavily hints at using them:
>>> https://dri.freedesktop.org/docs/drm/gpu/todo.html#object-lifetime-fixes
>>>
>>> So it looks like we're already well on the deprecation path?
>>
>> AFAIU that TODO item is about replacing devm_kzalloc() with drmm_kzalloc().
>> It's not about the plain init functions, such as drm_crtc_init_with_planes()
>> or drm_universal_plane_init(). Many simple drivers allocate their
>> modesetting pipeline's components first and then build the pipeline with the
>> drm_ functions. I don't think we can take that away from them.
> 
> Sure, that's exactly what those first patches are about? It allows to
> use a DRM managed initialization without disrupting the drivers
> structure too much?
> 
>> The concern I have is that we're adding lots of helpers for all kind of
>> scenarios and end up with a lot of duplication (and fragmentation among
>> drivers).
> 
> I can see two: whether you want to allocate / init, or just init?
> We're not going to have more than that.
> 
>> For example, drmm_crtc_alloc_with_planes() really isn't much used
>> by anything. [1]
> 
> Not that I disagree here, but it might be that it isn't the most helpful
> helper?
> 
> In vc4 case, we just can't use it easily.
> 
> Our CRTC driver is shared between the "regular" CRTCs in the display
> path, and another instance dedicated to the writeback connector.
> 
> The shared stuff is initialized through vc4_crtc_init():
> https://elixir.bootlin.com/linux/latest/source/drivers/gpu/drm/vc4/vc4_crtc.c#L1120
> 
> It initializes the structure, set up the planes, etc. Basically
> everything that our CRTC controller will be doing, and would be shared
> by both cases.
> 
> However, since the writeback and regular CRTC structures are different,
> we can't really make that function allocate the backing structure
> either.

It appears to me that it's a problem with how vc4 organizes its 
pipeline. That's why I suggested to move some of vc4's init code around 
to make such allocations more flexible.

> 
> We could do some compiler magic to pass the total size and the offset to
> that function, just like what drmm_crtc_alloc_with_planes is doing, but
> then we would have the same issue with the writeback stuff that needs to
> initialize the encoder and connector.

In vc4_crtc.c it should be possible to use 
drmm_crtc_alloc_with_planes(). In vc4_txp.c, the code apparently 
initializes struct vc4_txp, so it would be better to use a managed 
cleanup of struct vc4_txp.


See, helpers should be useful to many drivers. If we add them, we also 
add a resources and maintenance overhead to our libraries. And right 
now, these new functions appear to work around the design of the vc4 
driver's data structures.  If you want to keep them, maybe let's first 
merge them into vc4 (something like vc4_crtc_init_with_planes(), etc). 
If another driver with a use case comes along, we can still move them 
out easily.

Best regards
Thomas

> 
> So we would need a drmm_encoder_init anyway.
> 
>> Same for drmm_universal_plane_alloc(). [2]
>>
>> Instead of adding new helpers, it would be better to build upon
>> drmm_crtc_alloc_with_planes(), drmm_univeral_plane_alloc(), etc.
>>
>> For example, a good starting point would be vc4_plane_init(). It could alloc
>> with drmm_univeral_plane_alloc(), which would replace devm_kzalloc() [3] and
>> drm_univeral_plane_alloc() [4] in one step. From what I understand, that's
>> what your patchset wants to do. But it looks like you're effectively
>> open-coding drmm_universl_plane_alloc().
> 
> Where I could use the alloc helper, I did. See the following patch that
> does exactly what you described:
> https://lore.kernel.org/dri-devel/20220610092924.754942-17-maxime@cerno.tech/
> 
>> With vc4_plane_init() correctly managed, the next candidate could be
>> vc4_crtc_init(). You probably want to pull vc4_plane_init() [5] into
>> callers. to get it out of the way. If you move calls to devm_kzalloc() [6]
>> and drm_crtc_init_with_planes() [7] closer together, you can replace them
>> with drmm_crtc_alloc_with_planes().
> 
> See above
> 
> Maxime

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Ivo Totev

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 02/64] drm/crtc: Introduce drmm_crtc_init_with_planes
  2022-06-15  7:22               ` Thomas Zimmermann
@ 2022-06-15  8:32                 ` Maxime Ripard
  2022-06-15 10:34                   ` Thomas Zimmermann
  0 siblings, 1 reply; 124+ messages in thread
From: Maxime Ripard @ 2022-06-15  8:32 UTC (permalink / raw)
  To: Thomas Zimmermann; +Cc: David Airlie, Daniel Vetter, dri-devel

On Wed, Jun 15, 2022 at 09:22:55AM +0200, Thomas Zimmermann wrote:
> Hi
> 
> Am 14.06.22 um 14:09 schrieb Maxime Ripard:
> > On Tue, Jun 14, 2022 at 01:47:28PM +0200, Thomas Zimmermann wrote:
> > > Am 14.06.22 um 11:04 schrieb Maxime Ripard:
> > > > On Tue, Jun 14, 2022 at 10:29:20AM +0200, Thomas Zimmermann wrote:
> > > > > Am 14.06.22 um 09:37 schrieb Maxime Ripard:
> > > > > > Hi Thomas,
> > > > > > 
> > > > > > On Mon, Jun 13, 2022 at 01:23:54PM +0200, Thomas Zimmermann wrote:
> > > > > > > Am 10.06.22 um 11:28 schrieb Maxime Ripard:
> > > > > > > > The DRM-managed function to register a CRTC is
> > > > > > > > drmm_crtc_alloc_with_planes(), which will allocate the underlying
> > > > > > > > structure and initialisation the CRTC.
> > > > > > > > 
> > > > > > > > However, we might want to separate the structure creation and the CRTC
> > > > > > > > initialisation, for example if the structure is shared across multiple
> > > > > > > > DRM entities, for example an encoder and a connector.
> > > > > > > > 
> > > > > > > > Let's create an helper to only initialise a CRTC that would be passed as
> > > > > > > > an argument.
> > > > > > > 
> > > > > > > Before I review all of thes patches, one question. it's yet not clear to me
> > > > > > > why drm_crtc_init_with_planes() wouldn't work. (I know we discussed this on
> > > > > > > IRC.)
> > > > > > > 
> > > > > > > If you're calling drmm_mode_config_init(), it will clean up all the CRTC,
> > > > > > > encoder connector, etc. data structures for you. For CRTC instances in
> > > > > > > kmalloced memory, wouldn't it be simpler to put the corresponding kfree into
> > > > > > > vc4_crtc_destroy()?
> > > > > > 
> > > > > > My intent was to remove as much of the lifetime handling and
> > > > > > memory-management from drivers as possible.
> > > > > > 
> > > > > > My feeling is that while the solution you suggest would definitely work,
> > > > > > it relies on drivers authors to know about this, and make the effort to
> > > > > > convert the drivers themselves. And then we would have to review that,
> > > > > > which we will probably miss (collectively), because it's a bit obscure.
> > > > > > 
> > > > > > While with either the drmm_alloc_* functions, or the new functions I
> > > > > > introduce there, we can just deprecate the old ones, create a TODO entry
> > > > > > and a coccinelle script and trust that it works properly.
> > > > > 
> > > > > Thanks for explaining the motivation.
> > > > > 
> > > > > I would not want to deprecate any of the existing functions, as they work
> > > > > for many drivers. The drmm_ functions add additional overhead that not
> > > > > everyone is willing to pay.
> > > > 
> > > > I'm a bit confused. drm_crtc_init_with_planes() at the moment has:
> > > > 
> > > > * Note: consider using drmm_crtc_alloc_with_planes() instead of
> > > > * drm_crtc_init_with_planes() to let the DRM managed resource infrastructure
> > > > * take care of cleanup and deallocation.
> > > > 
> > > > Just like drm_encoder_init(), drm_simple_encoder_init() and
> > > > drm_universal_plane_init(), so all the functions we have a drmm_* helper
> > > > for.
> > > > 
> > > > And we have a TODO-list item that heavily hints at using them:
> > > > https://dri.freedesktop.org/docs/drm/gpu/todo.html#object-lifetime-fixes
> > > > 
> > > > So it looks like we're already well on the deprecation path?
> > > 
> > > AFAIU that TODO item is about replacing devm_kzalloc() with drmm_kzalloc().
> > > It's not about the plain init functions, such as drm_crtc_init_with_planes()
> > > or drm_universal_plane_init(). Many simple drivers allocate their
> > > modesetting pipeline's components first and then build the pipeline with the
> > > drm_ functions. I don't think we can take that away from them.
> > 
> > Sure, that's exactly what those first patches are about? It allows to
> > use a DRM managed initialization without disrupting the drivers
> > structure too much?
> > 
> > > The concern I have is that we're adding lots of helpers for all kind of
> > > scenarios and end up with a lot of duplication (and fragmentation among
> > > drivers).
> > 
> > I can see two: whether you want to allocate / init, or just init?
> > We're not going to have more than that.
> > 
> > > For example, drmm_crtc_alloc_with_planes() really isn't much used
> > > by anything. [1]
> > 
> > Not that I disagree here, but it might be that it isn't the most helpful
> > helper?
> > 
> > In vc4 case, we just can't use it easily.
> > 
> > Our CRTC driver is shared between the "regular" CRTCs in the display
> > path, and another instance dedicated to the writeback connector.
> > 
> > The shared stuff is initialized through vc4_crtc_init():
> > https://elixir.bootlin.com/linux/latest/source/drivers/gpu/drm/vc4/vc4_crtc.c#L1120
> > 
> > It initializes the structure, set up the planes, etc. Basically
> > everything that our CRTC controller will be doing, and would be shared
> > by both cases.
> > 
> > However, since the writeback and regular CRTC structures are different,
> > we can't really make that function allocate the backing structure
> > either.
> 
> It appears to me that it's a problem with how vc4 organizes its pipeline.
> That's why I suggested to move some of vc4's init code around to make such
> allocations more flexible.

I mean, it's only a problem because the helpers aren't flexible enough.
Reworking the code to allocate the CRTC in vc4_crtc_init() would create
much more churn in the TXP driver. So sure, the core would be nice and
tidy, but aren't helpers supposed to simplify drivers?

> > We could do some compiler magic to pass the total size and the offset to
> > that function, just like what drmm_crtc_alloc_with_planes is doing, but
> > then we would have the same issue with the writeback stuff that needs to
> > initialize the encoder and connector.
> 
> In vc4_crtc.c it should be possible to use drmm_crtc_alloc_with_planes(). In
> vc4_txp.c, the code apparently initializes struct vc4_txp, so it would be
> better to use a managed cleanup of struct vc4_txp.

This only pushes the problem later on. We fixed the CRTC issue, but we
have now the exact same situation with the encoder and connector.

> See, helpers should be useful to many drivers. If we add them, we also add a
> resources and maintenance overhead to our libraries. And right now, these
> new functions appear to work around the design of the vc4 driver's data
> structures.  If you want to keep them, maybe let's first merge them into vc4
> (something like vc4_crtc_init_with_planes(), etc). If another driver with a
> use case comes along, we can still move them out easily.

Not that I disagree, but there's also the fact that people will start
using helpers because they are available.

You mentioned drmm_crtc_alloc_with_planes(). It was introduced in 5.12
with a single user (ipuv3-crtc.c). And then, because it was available,
in 5.17 was merged the Unisoc driver that was the second user of that
function.

drmm_simple_encoder_alloc() and drmm_universal_plane_alloc() are in the
same situation and we wouldn't have had that discussion if it was kept
in the imx driver.

The helper being there allows driver authors to discover them easily,
pointing out an issue that possibly wasn't obvious to the author, and we
can also point during review that the helpers are there to be used.

None of that would be possible if we were to keep them in a driver,
because no one but the author would know about it.

My feeling is that the rule you mention works great when you know that
some deviation is going to happen. But we're replacing an init function
that has been proved good enough here, so it's not rocket science
really.

drmm_mutex_init() is a great example of that actually. You merged it
recently with two users. We could have used the exact same argument that
it belonged in those drivers because it wasn't generic enough or
something. But it's trivial, so it was a good decision to merge it as a
helper. And because you did so, I later found out that mutex_destroy()
was supposed to be called in the first place, I converted vc4 to
drmm_mutex_init(), and now that bug is fixed.

It wouldn't have been the case if you kept it inside the drivers.

Maxime

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 02/64] drm/crtc: Introduce drmm_crtc_init_with_planes
  2022-06-15  8:32                 ` Maxime Ripard
@ 2022-06-15 10:34                   ` Thomas Zimmermann
  2022-06-16  9:32                     ` Maxime Ripard
  0 siblings, 1 reply; 124+ messages in thread
From: Thomas Zimmermann @ 2022-06-15 10:34 UTC (permalink / raw)
  To: Maxime Ripard; +Cc: David Airlie, Daniel Vetter, dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 3252 bytes --]

Hi

Am 15.06.22 um 10:32 schrieb Maxime Ripard:
[...]
>> See, helpers should be useful to many drivers. If we add them, we also add a
>> resources and maintenance overhead to our libraries. And right now, these
>> new functions appear to work around the design of the vc4 driver's data
>> structures.  If you want to keep them, maybe let's first merge them into vc4
>> (something like vc4_crtc_init_with_planes(), etc). If another driver with a
>> use case comes along, we can still move them out easily.
> 
> Not that I disagree, but there's also the fact that people will start
> using helpers because they are available.
> 
> You mentioned drmm_crtc_alloc_with_planes(). It was introduced in 5.12
> with a single user (ipuv3-crtc.c). And then, because it was available,
> in 5.17 was merged the Unisoc driver that was the second user of that
> function.

OTOH, it actually took 5 releases to find another user. Maybe we need to 
look harder for possible reuse of helpers, but I wouldn't count 5 
releases as a good track record.

> 
> drmm_simple_encoder_alloc() and drmm_universal_plane_alloc() are in the
> same situation and we wouldn't have had that discussion if it was kept
> in the imx driver.
> 
> The helper being there allows driver authors to discover them easily,
> pointing out an issue that possibly wasn't obvious to the author, and we
> can also point during review that the helpers are there to be used.
> 
> None of that would be possible if we were to keep them in a driver,
> because no one but the author would know about it.
> 
> My feeling is that the rule you mention works great when you know that
> some deviation is going to happen. But we're replacing an init function
> that has been proved good enough here, so it's not rocket science
> really.
> 
> drmm_mutex_init() is a great example of that actually. You merged it
> recently with two users. We could have used the exact same argument that
> it belonged in those drivers because it wasn't generic enough or
> something. But it's trivial, so it was a good decision to merge it as a
> helper. And because you did so, I later found out that mutex_destroy()
> was supposed to be called in the first place, I converted vc4 to
> drmm_mutex_init(), and now that bug is fixed.

But when I added it, there actually were two users. I would not have 
added drmm_mutex_init() if it was only useful for a single driver.

In other cases, we tend to push single-user helpers into the drivers. 
That happened several times with TTM. Code was moved into vmwgfx, 
because there where no other users.

Anyway, as you insist on using this helper, go for it. But please, at 
least reimplement drm_crtc_alloc_with_planes() on top of a shared 
internal implementation. AFAICT drm_crtc_alloc_with_planes() is 
drmm_kzalloc + drmm_crtc_init_with_planes(). Same for other related 
helpers in the other patches, if there are any.

Best regards
Thomas

> 
> It wouldn't have been the case if you kept it inside the drivers.
> 
> Maxime

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Ivo Totev

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 25/64] drm/vc4: dpi: Add action to disable the clock
  2022-06-14 16:47   ` Dave Stevenson
@ 2022-06-16  8:38     ` Maxime Ripard
  2022-06-16  9:47       ` Dave Stevenson
  0 siblings, 1 reply; 124+ messages in thread
From: Maxime Ripard @ 2022-06-16  8:38 UTC (permalink / raw)
  To: Dave Stevenson
  Cc: David Airlie, Daniel Vetter, DRI Development, Thomas Zimmermann

[-- Attachment #1: Type: text/plain, Size: 727 bytes --]

Hi Dave,

On Tue, Jun 14, 2022 at 05:47:28PM +0100, Dave Stevenson wrote:
> Hi Maxime.
> 
> On Fri, 10 Jun 2022 at 10:30, Maxime Ripard <maxime@cerno.tech> wrote:
> >
> > Adding a device-managed action will make the error path easier, so let's
> > create one to disable our clock.
> 
> The DPI block has two clocks (core and pixel), and this is only
> affecting one of them (the core clock).

Thanks for the suggestion, I've amended the commit message.

> (I'm actually puzzling over what it's wanting to do with the core
> clock here as it's only enabling it rather than setting a rate. I
> think it may be redundant).

Could it be that it a "bus" clock that we need it to access the
registers?

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 02/64] drm/crtc: Introduce drmm_crtc_init_with_planes
  2022-06-15 10:34                   ` Thomas Zimmermann
@ 2022-06-16  9:32                     ` Maxime Ripard
  0 siblings, 0 replies; 124+ messages in thread
From: Maxime Ripard @ 2022-06-16  9:32 UTC (permalink / raw)
  To: Thomas Zimmermann; +Cc: David Airlie, Daniel Vetter, dri-devel

[-- Attachment #1: Type: text/plain, Size: 4107 bytes --]

On Wed, Jun 15, 2022 at 12:34:46PM +0200, Thomas Zimmermann wrote:
> Hi
> 
> Am 15.06.22 um 10:32 schrieb Maxime Ripard:
> [...]
> > > See, helpers should be useful to many drivers. If we add them, we also add a
> > > resources and maintenance overhead to our libraries. And right now, these
> > > new functions appear to work around the design of the vc4 driver's data
> > > structures.  If you want to keep them, maybe let's first merge them into vc4
> > > (something like vc4_crtc_init_with_planes(), etc). If another driver with a
> > > use case comes along, we can still move them out easily.
> > 
> > Not that I disagree, but there's also the fact that people will start
> > using helpers because they are available.
> > 
> > You mentioned drmm_crtc_alloc_with_planes(). It was introduced in 5.12
> > with a single user (ipuv3-crtc.c). And then, because it was available,
> > in 5.17 was merged the Unisoc driver that was the second user of that
> > function.
> 
> OTOH, it actually took 5 releases to find another user. Maybe we need to
> look harder for possible reuse of helpers, but I wouldn't count 5 releases
> as a good track record.

Indeed, but I'm not sure it's due to the helper itself. I'm fairly sure
nobody really cared or knows about the lifetime issues solved by the
drm-managed functions, and so nobody feels an urge to convert.

And one can't ask during review to use it if they're not aware of the
helpers existence. Between 5.12 and 5.17, only hyperv and sprd were
merged. 50% of the new drivers using it is not too bad.

> > drmm_simple_encoder_alloc() and drmm_universal_plane_alloc() are in the
> > same situation and we wouldn't have had that discussion if it was kept
> > in the imx driver.
> > 
> > The helper being there allows driver authors to discover them easily,
> > pointing out an issue that possibly wasn't obvious to the author, and we
> > can also point during review that the helpers are there to be used.
> > 
> > None of that would be possible if we were to keep them in a driver,
> > because no one but the author would know about it.
> > 
> > My feeling is that the rule you mention works great when you know that
> > some deviation is going to happen. But we're replacing an init function
> > that has been proved good enough here, so it's not rocket science
> > really.
> > 
> > drmm_mutex_init() is a great example of that actually. You merged it
> > recently with two users. We could have used the exact same argument that
> > it belonged in those drivers because it wasn't generic enough or
> > something. But it's trivial, so it was a good decision to merge it as a
> > helper. And because you did so, I later found out that mutex_destroy()
> > was supposed to be called in the first place, I converted vc4 to
> > drmm_mutex_init(), and now that bug is fixed.
> 
> But when I added it, there actually were two users. I would not have added
> drmm_mutex_init() if it was only useful for a single driver.
> 
> In other cases, we tend to push single-user helpers into the drivers. That
> happened several times with TTM. Code was moved into vmwgfx, because there
> where no other users.

Yeah, and I introduced some in that series too. It makes sense to have
that restriction for stuff that we're not really sure about. But for
strict equivalents to functions that already exists with the same API
I'm not sure that restriction makes sense.

In fact, we also merged recently devm_drm_bridge_add with a single user
and that was fine too, because that function has been there for a while
and we know it's not going to change much.

> Anyway, as you insist on using this helper, go for it. But please, at least
> reimplement drm_crtc_alloc_with_planes() on top of a shared internal
> implementation. AFAICT drm_crtc_alloc_with_planes() is drmm_kzalloc +
> drmm_crtc_init_with_planes().

Ack

> Same for other related helpers in the other patches, if there are any.

drmm_encoder_alloc() and drmm_simple_encoder_alloc() are in the same
situation, I'll fix those too.

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 17/64] drm/vc4: crtc: Move debugfs_name to crtc_data
  2022-06-14 15:57   ` Dave Stevenson
@ 2022-06-16  9:41     ` Maxime Ripard
  2022-06-16  9:44       ` Dave Stevenson
  0 siblings, 1 reply; 124+ messages in thread
From: Maxime Ripard @ 2022-06-16  9:41 UTC (permalink / raw)
  To: Dave Stevenson
  Cc: David Airlie, Daniel Vetter, DRI Development, Thomas Zimmermann

[-- Attachment #1: Type: text/plain, Size: 829 bytes --]

Hi Dave,

On Tue, Jun 14, 2022 at 04:57:45PM +0100, Dave Stevenson wrote:
> On Fri, 10 Jun 2022 at 10:30, Maxime Ripard <maxime@cerno.tech> wrote:
> >
> > All the CRTCs, including the TXP, have a debugfs file and name so we can
> > consolidate it into vc4_crtc_data.
> >
> > Signed-off-by: Maxime Ripard <maxime@cerno.tech>
> 
> Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
> 
> I was sort of expecting the vc4_debugfs_add_regset32 call to move to
> vc4_crtc_init so that it would be a common call for crtcs and txp, but
> that doesn't appear to happen in this set. The debugfs_name for the
> txp is therefore actually unused.

As of this patch, you're right

This is later changed by some other patch though:
https://lore.kernel.org/all/20220610092924.754942-60-maxime@cerno.tech/

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 17/64] drm/vc4: crtc: Move debugfs_name to crtc_data
  2022-06-16  9:41     ` Maxime Ripard
@ 2022-06-16  9:44       ` Dave Stevenson
  0 siblings, 0 replies; 124+ messages in thread
From: Dave Stevenson @ 2022-06-16  9:44 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: David Airlie, Daniel Vetter, DRI Development, Thomas Zimmermann

On Thu, 16 Jun 2022 at 10:41, Maxime Ripard <maxime@cerno.tech> wrote:
>
> Hi Dave,
>
> On Tue, Jun 14, 2022 at 04:57:45PM +0100, Dave Stevenson wrote:
> > On Fri, 10 Jun 2022 at 10:30, Maxime Ripard <maxime@cerno.tech> wrote:
> > >
> > > All the CRTCs, including the TXP, have a debugfs file and name so we can
> > > consolidate it into vc4_crtc_data.
> > >
> > > Signed-off-by: Maxime Ripard <maxime@cerno.tech>
> >
> > Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
> >
> > I was sort of expecting the vc4_debugfs_add_regset32 call to move to
> > vc4_crtc_init so that it would be a common call for crtcs and txp, but
> > that doesn't appear to happen in this set. The debugfs_name for the
> > txp is therefore actually unused.
>
> As of this patch, you're right
>
> This is later changed by some other patch though:
> https://lore.kernel.org/all/20220610092924.754942-60-maxime@cerno.tech/

Indeed, and that cleans it all up. Perfect.

  Dave

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 25/64] drm/vc4: dpi: Add action to disable the clock
  2022-06-16  8:38     ` Maxime Ripard
@ 2022-06-16  9:47       ` Dave Stevenson
  2022-06-20 12:21         ` Maxime Ripard
  0 siblings, 1 reply; 124+ messages in thread
From: Dave Stevenson @ 2022-06-16  9:47 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: David Airlie, Daniel Vetter, DRI Development, Thomas Zimmermann

On Thu, 16 Jun 2022 at 09:38, Maxime Ripard <maxime@cerno.tech> wrote:
>
> Hi Dave,
>
> On Tue, Jun 14, 2022 at 05:47:28PM +0100, Dave Stevenson wrote:
> > Hi Maxime.
> >
> > On Fri, 10 Jun 2022 at 10:30, Maxime Ripard <maxime@cerno.tech> wrote:
> > >
> > > Adding a device-managed action will make the error path easier, so let's
> > > create one to disable our clock.
> >
> > The DPI block has two clocks (core and pixel), and this is only
> > affecting one of them (the core clock).
>
> Thanks for the suggestion, I've amended the commit message.
>
> > (I'm actually puzzling over what it's wanting to do with the core
> > clock here as it's only enabling it rather than setting a rate. I
> > think it may be redundant).
>
> Could it be that it a "bus" clock that we need it to access the
> registers?

No idea. Normally it's the power domain that is required to access registers.
AFAIK the core clock is never turned off, so the request for it is
just a little odd.
It is what it is though, so fine to leave it alone.

  Dave

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 05/64] drm/connector: Mention the cleanup after drm_connector_init
  2022-06-10  9:28 ` [PATCH 05/64] drm/connector: Mention the cleanup after drm_connector_init Maxime Ripard
@ 2022-06-20 10:21   ` Thomas Zimmermann
  2022-06-20 12:18     ` Maxime Ripard
  0 siblings, 1 reply; 124+ messages in thread
From: Thomas Zimmermann @ 2022-06-20 10:21 UTC (permalink / raw)
  To: Maxime Ripard, Daniel Vetter, David Airlie, Maarten Lankhorst; +Cc: dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 2005 bytes --]

Hi

Am 10.06.22 um 11:28 schrieb Maxime Ripard:
> Unlike encoders and CRTCs, the drm_connector_init() and
> drm_connector_init_with_ddc() don't mention how the cleanup is supposed to
> be done. Let's add it.
> 
> Signed-off-by: Maxime Ripard <maxime@cerno.tech>
> ---
>   drivers/gpu/drm/drm_connector.c | 10 ++++++++++
>   1 file changed, 10 insertions(+)
> 
> diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
> index 353d83ae09d3..2a78a23836d8 100644
> --- a/drivers/gpu/drm/drm_connector.c
> +++ b/drivers/gpu/drm/drm_connector.c
> @@ -222,6 +222,11 @@ void drm_connector_free_work_fn(struct work_struct *work)
>    * Initialises a preallocated connector. Connectors should be
>    * subclassed as part of driver connector objects.
>    *
> + * At driver unload time the driver's &drm_connector_funcs.destroy hook
> + * should call drm_connector_unregister(), drm_connector_cleanup() and
> + * kfree() the connector structure. The connector structure should not

This sounds odd. Maybe

'... to release... the connector structure' ?

> + * be allocated with devm_kzalloc().
> + *
>    * Returns:
>    * Zero on success, error code on failure.
>    */
> @@ -345,6 +350,11 @@ EXPORT_SYMBOL(drm_connector_init);
>    * Initialises a preallocated connector. Connectors should be
>    * subclassed as part of driver connector objects.
>    *
> + * At driver unload time the driver's &drm_connector_funcs.destroy hook
> + * should call drm_connector_unregister(), drm_connector_cleanup() and
> + * kfree() the connector structure. The connector structure should not
> + * be allocated with devm_kzalloc().
> + *

Same here.

Best regards
Thomas

>    * Ensures that the ddc field of the connector is correctly set.
>    *
>    * Returns:

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Ivo Totev

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 06/64] drm/connector: Introduce drmm_connector_init
  2022-06-10  9:28 ` [PATCH 06/64] drm/connector: Introduce drmm_connector_init Maxime Ripard
@ 2022-06-20 10:23   ` Thomas Zimmermann
  0 siblings, 0 replies; 124+ messages in thread
From: Thomas Zimmermann @ 2022-06-20 10:23 UTC (permalink / raw)
  To: Maxime Ripard, Daniel Vetter, David Airlie, Maarten Lankhorst; +Cc: dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 6419 bytes --]



Am 10.06.22 um 11:28 schrieb Maxime Ripard:
> Unlike other DRM entities, there's no helper to create a DRM-managed
> initialisation of a connector.
> 
> Let's create an helper to initialise a connector that would be passed as an
> argument, and handle the cleanup through a DRM-managed action.
> 
> Signed-off-by: Maxime Ripard <maxime@cerno.tech>

Acked-by: Thomas Zimmermann <tzimmermann@suse.de>

> ---
>   drivers/gpu/drm/drm_connector.c | 108 +++++++++++++++++++++++++-------
>   include/drm/drm_connector.h     |   4 ++
>   2 files changed, 90 insertions(+), 22 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
> index 2a78a23836d8..f150270b519f 100644
> --- a/drivers/gpu/drm/drm_connector.c
> +++ b/drivers/gpu/drm/drm_connector.c
> @@ -26,6 +26,7 @@
>   #include <drm/drm_edid.h>
>   #include <drm/drm_encoder.h>
>   #include <drm/drm_file.h>
> +#include <drm/drm_managed.h>
>   #include <drm/drm_print.h>
>   #include <drm/drm_privacy_screen_consumer.h>
>   #include <drm/drm_sysfs.h>
> @@ -212,28 +213,10 @@ void drm_connector_free_work_fn(struct work_struct *work)
>   	}
>   }
>   
> -/**
> - * drm_connector_init - Init a preallocated connector
> - * @dev: DRM device
> - * @connector: the connector to init
> - * @funcs: callbacks for this connector
> - * @connector_type: user visible type of the connector
> - *
> - * Initialises a preallocated connector. Connectors should be
> - * subclassed as part of driver connector objects.
> - *
> - * At driver unload time the driver's &drm_connector_funcs.destroy hook
> - * should call drm_connector_unregister(), drm_connector_cleanup() and
> - * kfree() the connector structure. The connector structure should not
> - * be allocated with devm_kzalloc().
> - *
> - * Returns:
> - * Zero on success, error code on failure.
> - */
> -int drm_connector_init(struct drm_device *dev,
> -		       struct drm_connector *connector,
> -		       const struct drm_connector_funcs *funcs,
> -		       int connector_type)
> +static int __drm_connector_init(struct drm_device *dev,
> +				struct drm_connector *connector,
> +				const struct drm_connector_funcs *funcs,
> +				int connector_type)
>   {
>   	struct drm_mode_config *config = &dev->mode_config;
>   	int ret;
> @@ -337,6 +320,39 @@ int drm_connector_init(struct drm_device *dev,
>   
>   	return ret;
>   }
> +
> +/**
> + * drm_connector_init - Init a preallocated connector
> + * @dev: DRM device
> + * @connector: the connector to init
> + * @funcs: callbacks for this connector
> + * @connector_type: user visible type of the connector
> + *
> + * Initialises a preallocated connector. Connectors should be
> + * subclassed as part of driver connector objects.
> + *
> + * At driver unload time the driver's &drm_connector_funcs.destroy hook
> + * should call drm_connector_unregister(), drm_connector_cleanup() and
> + * kfree() the connector structure. The connector structure should not
> + * be allocated with devm_kzalloc().
> + *
> + * Note: consider using drmm_connector_init() instead of
> + * drm_connector_init() to let the DRM managed resource infrastructure
> + * take care of cleanup and deallocation.
> + *
> + * Returns:
> + * Zero on success, error code on failure.
> + */
> +int drm_connector_init(struct drm_device *dev,
> +		       struct drm_connector *connector,
> +		       const struct drm_connector_funcs *funcs,
> +		       int connector_type)
> +{
> +	if (WARN_ON(!(funcs && funcs->destroy)))
> +		return -EINVAL;
> +
> +	return __drm_connector_init(dev, connector, funcs, connector_type);
> +}
>   EXPORT_SYMBOL(drm_connector_init);
>   
>   /**
> @@ -379,6 +395,54 @@ int drm_connector_init_with_ddc(struct drm_device *dev,
>   }
>   EXPORT_SYMBOL(drm_connector_init_with_ddc);
>   
> +static void drm_connector_cleanup_action(struct drm_device *dev,
> +					 void *ptr)
> +{
> +	struct drm_connector *connector = ptr;
> +
> +	drm_connector_unregister(connector);
> +	drm_connector_cleanup(connector);
> +}
> +
> +/**
> + * drmm_connector_init - Init a preallocated connector
> + * @dev: DRM device
> + * @connector: the connector to init
> + * @funcs: callbacks for this connector
> + * @connector_type: user visible type of the connector
> + *
> + * Initialises a preallocated connector. Connectors should be
> + * subclassed as part of driver connector objects. Cleanup is
> + * automatically handled through registering drm_connector_unregister()
> + * and drm_connector_cleanup() with drm_add_action(). The connector
> + * structure should be allocated with drmm_kzalloc().
> + *
> + * Returns:
> + * Zero on success, error code on failure.
> + */
> +int drmm_connector_init(struct drm_device *dev,
> +			struct drm_connector *connector,
> +			const struct drm_connector_funcs *funcs,
> +			int connector_type)
> +{
> +	int ret;
> +
> +	if (WARN_ON(funcs && funcs->destroy))
> +		return -EINVAL;
> +
> +	ret = __drm_connector_init(dev, connector, funcs, connector_type);
> +	if (ret)
> +		return ret;
> +
> +	ret = drmm_add_action_or_reset(dev, drm_connector_cleanup_action,
> +				       connector);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(drmm_connector_init);
> +
>   /**
>    * drm_connector_attach_edid_property - attach edid property.
>    * @connector: the connector
> diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
> index 3ac4bf87f257..35a6b6e944b7 100644
> --- a/include/drm/drm_connector.h
> +++ b/include/drm/drm_connector.h
> @@ -1672,6 +1672,10 @@ int drm_connector_init_with_ddc(struct drm_device *dev,
>   				const struct drm_connector_funcs *funcs,
>   				int connector_type,
>   				struct i2c_adapter *ddc);
> +int drmm_connector_init(struct drm_device *dev,
> +			struct drm_connector *connector,
> +			const struct drm_connector_funcs *funcs,
> +			int connector_type);
>   void drm_connector_attach_edid_property(struct drm_connector *connector);
>   int drm_connector_register(struct drm_connector *connector);
>   void drm_connector_unregister(struct drm_connector *connector);

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Ivo Totev

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 08/64] drm/writeback: Introduce drmm_writeback_connector_init
  2022-06-10  9:28 ` [PATCH 08/64] drm/writeback: Introduce drmm_writeback_connector_init Maxime Ripard
@ 2022-06-20 10:33   ` Thomas Zimmermann
  0 siblings, 0 replies; 124+ messages in thread
From: Thomas Zimmermann @ 2022-06-20 10:33 UTC (permalink / raw)
  To: Maxime Ripard, Daniel Vetter, David Airlie, Maarten Lankhorst; +Cc: dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 9253 bytes --]

Hi

Am 10.06.22 um 11:28 schrieb Maxime Ripard:
> Let's create a DRM-managed variant of drmm_writeback_connector_init that
> will take care of an action of the encoder and connector cleanup.
> 
> Signed-off-by: Maxime Ripard <maxime@cerno.tech>
> ---
>   drivers/gpu/drm/drm_writeback.c | 136 ++++++++++++++++++++++++--------
>   include/drm/drm_writeback.h     |   5 ++
>   2 files changed, 108 insertions(+), 33 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_writeback.c b/drivers/gpu/drm/drm_writeback.c
> index dccf4504f1bb..0b00921f3379 100644
> --- a/drivers/gpu/drm/drm_writeback.c
> +++ b/drivers/gpu/drm/drm_writeback.c
> @@ -149,32 +149,27 @@ static const struct drm_encoder_funcs drm_writeback_encoder_funcs = {
>   	.destroy = drm_encoder_cleanup,
>   };
>   
> -/**
> - * drm_writeback_connector_init - Initialize a writeback connector and its properties
> - * @dev: DRM device
> - * @wb_connector: Writeback connector to initialize
> - * @con_funcs: Connector funcs vtable
> - * @enc_helper_funcs: Encoder helper funcs vtable to be used by the internal encoder
> - * @formats: Array of supported pixel formats for the writeback engine
> - * @n_formats: Length of the formats array
> - *
> - * This function creates the writeback-connector-specific properties if they
> - * have not been already created, initializes the connector as
> - * type DRM_MODE_CONNECTOR_WRITEBACK, and correctly initializes the property
> - * values. It will also create an internal encoder associated with the
> - * drm_writeback_connector and set it to use the @enc_helper_funcs vtable for
> - * the encoder helper.
> - *
> - * Drivers should always use this function instead of drm_connector_init() to
> - * set up writeback connectors.
> - *
> - * Returns: 0 on success, or a negative error code
> - */
> -int drm_writeback_connector_init(struct drm_device *dev,
> -				 struct drm_writeback_connector *wb_connector,
> -				 const struct drm_connector_funcs *con_funcs,
> -				 const struct drm_encoder_helper_funcs *enc_helper_funcs,
> -				 const u32 *formats, int n_formats)
> +typedef int (*encoder_init_t)(struct drm_device *dev,
> +			      struct drm_encoder *encoder,
> +			      const struct drm_encoder_funcs *funcs,
> +			      int encoder_type,
> +			      const char *name, ...);
> +
> +typedef int (*connector_init_t)(struct drm_device *dev,
> +				struct drm_connector *connector,
> +				const struct drm_connector_funcs *funcs,
> +				int connector_type);

This code has menawhile changed in drm-tip, which makes it harder to 
give a good review for such refactoring.

But generally, I'd do the connector and encoder initialization in 
drmm_writeback_connector_init() and give the initialized encoders to an 
internal helper that does the common init steps. That avoids such 
indirections with functions pointers.

Best regards
Thomas

> +
> +static int writeback_init(struct drm_device *dev,
> +			  struct drm_writeback_connector *wb_connector,
> +			  connector_init_t conn_init,
> +			  void (*conn_destroy)(struct drm_connector *connector),
> +			  encoder_init_t enc_init,
> +			  void (*enc_destroy)(struct drm_encoder *encoder),
> +			  const struct drm_connector_funcs *con_funcs,
> +			  const struct drm_encoder_funcs *enc_funcs,
> +			  const struct drm_encoder_helper_funcs *enc_helper_funcs,
> +			  const u32 *formats, int n_formats)
>   {
>   	struct drm_property_blob *blob;
>   	struct drm_connector *connector = &wb_connector->base;
> @@ -190,16 +185,16 @@ int drm_writeback_connector_init(struct drm_device *dev,
>   		return PTR_ERR(blob);
>   
>   	drm_encoder_helper_add(&wb_connector->encoder, enc_helper_funcs);
> -	ret = drm_encoder_init(dev, &wb_connector->encoder,
> -			       &drm_writeback_encoder_funcs,
> -			       DRM_MODE_ENCODER_VIRTUAL, NULL);
> +	ret = enc_init(dev, &wb_connector->encoder,
> +		       enc_funcs,
> +		       DRM_MODE_ENCODER_VIRTUAL, NULL);
>   	if (ret)
>   		goto fail;
>   
>   	connector->interlace_allowed = 0;
>   
> -	ret = drm_connector_init(dev, connector, con_funcs,
> -				 DRM_MODE_CONNECTOR_WRITEBACK);
> +	ret = conn_init(dev, connector, con_funcs,
> +			DRM_MODE_CONNECTOR_WRITEBACK);
>   	if (ret)
>   		goto connector_fail;
>   
> @@ -231,15 +226,90 @@ int drm_writeback_connector_init(struct drm_device *dev,
>   	return 0;
>   
>   attach_fail:
> -	drm_connector_cleanup(connector);
> +	if (conn_destroy)
> +		conn_destroy(connector);
>   connector_fail:
> -	drm_encoder_cleanup(&wb_connector->encoder);
> +	if (enc_destroy)
> +		enc_destroy(&wb_connector->encoder);
>   fail:
>   	drm_property_blob_put(blob);
>   	return ret;
>   }
> +
> +/**
> + * drm_writeback_connector_init - Initialize a writeback connector and its properties
> + * @dev: DRM device
> + * @wb_connector: Writeback connector to initialize
> + * @con_funcs: Connector funcs vtable
> + * @enc_helper_funcs: Encoder helper funcs vtable to be used by the internal encoder
> + * @formats: Array of supported pixel formats for the writeback engine
> + * @n_formats: Length of the formats array
> + *
> + * This function creates the writeback-connector-specific properties if they
> + * have not been already created, initializes the connector as
> + * type DRM_MODE_CONNECTOR_WRITEBACK, and correctly initializes the property
> + * values. It will also create an internal encoder associated with the
> + * drm_writeback_connector and set it to use the @enc_helper_funcs vtable for
> + * the encoder helper.
> + *
> + * Drivers should always use this function instead of drm_connector_init() to
> + * set up writeback connectors.
> + *
> + * Returns: 0 on success, or a negative error code
> + */
> +int drm_writeback_connector_init(struct drm_device *dev,
> +				 struct drm_writeback_connector *wb_connector,
> +				 const struct drm_connector_funcs *con_funcs,
> +				 const struct drm_encoder_helper_funcs *enc_helper_funcs,
> +				 const u32 *formats, int n_formats)
> +{
> +	return writeback_init(dev, wb_connector,
> +			      drm_connector_init, drm_connector_cleanup,
> +			      drm_encoder_init, drm_encoder_cleanup,
> +			      con_funcs,
> +			      &drm_writeback_encoder_funcs, enc_helper_funcs,
> +			      formats, n_formats);
> +}
>   EXPORT_SYMBOL(drm_writeback_connector_init);
>   
> +/**
> + * drmm_writeback_connector_init - Initialize a writeback connector and its properties
> + * @dev: DRM device
> + * @wb_connector: Writeback connector to initialize
> + * @con_funcs: Connector funcs vtable
> + * @enc_helper_funcs: Encoder helper funcs vtable to be used by the internal encoder
> + * @formats: Array of supported pixel formats for the writeback engine
> + * @n_formats: Length of the formats array
> + *
> + * This function creates the writeback-connector-specific properties if they
> + * have not been already created, initializes the connector as
> + * type DRM_MODE_CONNECTOR_WRITEBACK, and correctly initializes the property
> + * values. It will also create an internal encoder associated with the
> + * drm_writeback_connector and set it to use the @enc_helper_funcs vtable for
> + * the encoder helper.
> + *
> + * The writeback connector is DRM-managed and won't need any cleanup.
> + *
> + * Drivers should always use this function instead of drm_connector_init() to
> + * set up writeback connectors.
> + *
> + * Returns: 0 on success, or a negative error code
> + */
> +int drmm_writeback_connector_init(struct drm_device *dev,
> +				 struct drm_writeback_connector *wb_connector,
> +				 const struct drm_connector_funcs *con_funcs,
> +				 const struct drm_encoder_helper_funcs *enc_helper_funcs,
> +				 const u32 *formats, int n_formats)
> +{
> +	return writeback_init(dev, wb_connector,
> +			      drmm_connector_init, NULL,
> +			      drmm_encoder_init, NULL,
> +			      con_funcs,
> +			      NULL, enc_helper_funcs,
> +			      formats, n_formats);
> +}
> +EXPORT_SYMBOL(drmm_writeback_connector_init);
> +
>   int drm_writeback_set_fb(struct drm_connector_state *conn_state,
>   			 struct drm_framebuffer *fb)
>   {
> diff --git a/include/drm/drm_writeback.h b/include/drm/drm_writeback.h
> index 9697d2714d2a..cc259ae44734 100644
> --- a/include/drm/drm_writeback.h
> +++ b/include/drm/drm_writeback.h
> @@ -151,6 +151,11 @@ int drm_writeback_connector_init(struct drm_device *dev,
>   				 const struct drm_connector_funcs *con_funcs,
>   				 const struct drm_encoder_helper_funcs *enc_helper_funcs,
>   				 const u32 *formats, int n_formats);
> +int drmm_writeback_connector_init(struct drm_device *dev,
> +				  struct drm_writeback_connector *wb_connector,
> +				  const struct drm_connector_funcs *con_funcs,
> +				  const struct drm_encoder_helper_funcs *enc_helper_funcs,
> +				  const u32 *formats, int n_formats);
>   
>   int drm_writeback_set_fb(struct drm_connector_state *conn_state,
>   			 struct drm_framebuffer *fb);

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Ivo Totev

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 09/64] drm/simple: Introduce drmm_simple_encoder_init
  2022-06-10  9:28 ` [PATCH 09/64] drm/simple: Introduce drmm_simple_encoder_init Maxime Ripard
@ 2022-06-20 10:44   ` Thomas Zimmermann
  2022-06-20 13:48     ` Maxime Ripard
  0 siblings, 1 reply; 124+ messages in thread
From: Thomas Zimmermann @ 2022-06-20 10:44 UTC (permalink / raw)
  To: Maxime Ripard, Daniel Vetter, David Airlie, Maarten Lankhorst; +Cc: dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 4563 bytes --]

Hi

Am 10.06.22 um 11:28 schrieb Maxime Ripard:
> The DRM-managed function to register an encoder is
> drmm_simple_encoder_alloc() and its variants, which will allocate the
> underlying structure and initialisation the encoder.
> 
> However, we might want to separate the structure creation and the encoder
> initialisation, for example if the structure is shared across multiple DRM
> entities, for example an encoder and a connector.
> 
> Let's create an helper to only initialise an encoder that would be passed
> as an argument.
> 

There's nothing wrong with this patch, but I have to admit that adding 
drm_simple_encoder_init() et al was a mistake. It would have been better 
to add an initializer macro like

#define DRM_STATIC_ENCODER_DEFAULT_FUNCS \
   .destroy = drm_encoder_cleanup

It's way more flexible and similarly easy to use. Anyway...

> Signed-off-by: Maxime Ripard <maxime@cerno.tech>

Acked-by: Thomas Zimmermann <tzimmermann@suse.de>

Best regards
Thomas

> ---
>   drivers/gpu/drm/drm_simple_kms_helper.c | 46 +++++++++++++++++++++++--
>   include/drm/drm_simple_kms_helper.h     |  3 ++
>   2 files changed, 46 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_simple_kms_helper.c b/drivers/gpu/drm/drm_simple_kms_helper.c
> index 72989ed1baba..876870dd98e5 100644
> --- a/drivers/gpu/drm/drm_simple_kms_helper.c
> +++ b/drivers/gpu/drm/drm_simple_kms_helper.c
> @@ -58,9 +58,10 @@ static const struct drm_encoder_funcs drm_simple_encoder_funcs_cleanup = {
>    * stored in the device structure. Free the encoder's memory as part of
>    * the device release function.
>    *
> - * Note: consider using drmm_simple_encoder_alloc() instead of
> - * drm_simple_encoder_init() to let the DRM managed resource infrastructure
> - * take care of cleanup and deallocation.
> + * Note: consider using drmm_simple_encoder_alloc() or
> + * drmm_simple_encoder_init() instead of drm_simple_encoder_init() to
> + * let the DRM managed resource infrastructure take care of cleanup and
> + * deallocation.
>    *
>    * Returns:
>    * Zero on success, error code on failure.
> @@ -75,6 +76,45 @@ int drm_simple_encoder_init(struct drm_device *dev,
>   }
>   EXPORT_SYMBOL(drm_simple_encoder_init);
>   
> +static void drmm_simple_encoder_cleanup(struct drm_device *dev, void *ptr)
> +{
> +	struct drm_encoder *encoder = ptr;
> +
> +	drm_encoder_cleanup(encoder);
> +}
> +
> +/**
> + * drmm_simple_encoder_init - Initialize a preallocated encoder with
> + *                            basic functionality.
> + * @dev: drm device
> + * @encoder: the encoder to initialize
> + * @encoder_type: user visible type of the encoder
> + *
> + * Initialises a preallocated encoder that has no further functionality.
> + * Settings for possible CRTC and clones are left to their initial
> + * values. The encoder will be cleaned up automatically using a
> + * DRM-managed action.
> + *
> + * The structure containing the encoder's memory should be allocated
> + * using drmm_kzalloc().
> + *
> + * Returns:
> + * Zero on success, error code on failure.
> + */
> +int drmm_simple_encoder_init(struct drm_device *dev,
> +			     struct drm_encoder *encoder,
> +			     int encoder_type)
> +{
> +	int ret;
> +
> +	ret = drm_encoder_init(dev, encoder, NULL, encoder_type, NULL);
> +	if (ret)
> +		return ret;
> +
> +	return drmm_add_action_or_reset(dev, drmm_simple_encoder_cleanup, encoder);
> +}
> +EXPORT_SYMBOL(drmm_simple_encoder_init);
> +
>   void *__drmm_simple_encoder_alloc(struct drm_device *dev, size_t size,
>   				  size_t offset, int encoder_type)
>   {
> diff --git a/include/drm/drm_simple_kms_helper.h b/include/drm/drm_simple_kms_helper.h
> index 0b3647e614dd..20456f4712f0 100644
> --- a/include/drm/drm_simple_kms_helper.h
> +++ b/include/drm/drm_simple_kms_helper.h
> @@ -241,6 +241,9 @@ int drm_simple_display_pipe_init(struct drm_device *dev,
>   int drm_simple_encoder_init(struct drm_device *dev,
>   			    struct drm_encoder *encoder,
>   			    int encoder_type);
> +int drmm_simple_encoder_init(struct drm_device *dev,
> +			     struct drm_encoder *encoder,
> +			     int encoder_type);
>   
>   void *__drmm_simple_encoder_alloc(struct drm_device *dev, size_t size,
>   				  size_t offset, int encoder_type);

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Ivo Totev

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 13/64] drm/vc4: hvs: Protect device resources after removal
  2022-06-10  9:28 ` [PATCH 13/64] drm/vc4: hvs: Protect device resources after removal Maxime Ripard
  2022-06-14 15:11   ` Dave Stevenson
@ 2022-06-20 10:48   ` Thomas Zimmermann
  1 sibling, 0 replies; 124+ messages in thread
From: Thomas Zimmermann @ 2022-06-20 10:48 UTC (permalink / raw)
  To: Maxime Ripard, Daniel Vetter, David Airlie, Maarten Lankhorst; +Cc: dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 10404 bytes --]



Am 10.06.22 um 11:28 schrieb Maxime Ripard:
> Whenever the device and driver are unbound, the main device and all the
> subdevices will be removed by calling their unbind() method.
> 
> However, the DRM device itself will only be freed when the last user will
> have closed it.
> 
> It means that there is a time window where the device and its resources
> aren't there anymore, but the userspace can still call into our driver.
> 
> Fortunately, the DRM framework provides the drm_dev_enter() and
> drm_dev_exit() functions to make sure our underlying device is still there
> for the section protected by those calls. Let's add them to the HVS driver.
> 
> Signed-off-by: Maxime Ripard <maxime@cerno.tech>

Reviewed-by: Thomas Zimmermann <tzimmermann@suse.de>

> ---
>   drivers/gpu/drm/vc4/vc4_drv.h |   1 +
>   drivers/gpu/drm/vc4/vc4_hvs.c | 106 +++++++++++++++++++++++++++++++---
>   2 files changed, 99 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h
> index aa4c5910ea05..080deae55f64 100644
> --- a/drivers/gpu/drm/vc4/vc4_drv.h
> +++ b/drivers/gpu/drm/vc4/vc4_drv.h
> @@ -317,6 +317,7 @@ struct vc4_v3d {
>   };
>   
>   struct vc4_hvs {
> +	struct drm_device *dev;
>   	struct platform_device *pdev;
>   	void __iomem *regs;
>   	u32 __iomem *dlist;
> diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c
> index 2a58fc421cf6..483053e7b14f 100644
> --- a/drivers/gpu/drm/vc4/vc4_hvs.c
> +++ b/drivers/gpu/drm/vc4/vc4_hvs.c
> @@ -25,6 +25,7 @@
>   #include <linux/platform_device.h>
>   
>   #include <drm/drm_atomic_helper.h>
> +#include <drm/drm_drv.h>
>   #include <drm/drm_vblank.h>
>   
>   #include "vc4_drv.h"
> @@ -66,11 +67,15 @@ static const struct debugfs_reg32 hvs_regs[] = {
>   
>   void vc4_hvs_dump_state(struct vc4_hvs *hvs)
>   {
> +	struct drm_device *drm = hvs->dev;
>   	struct drm_printer p = drm_info_printer(&hvs->pdev->dev);
> -	int i;
> +	int idx, i;
>   
>   	drm_print_regset32(&p, &hvs->regset);
>   
> +	if (!drm_dev_enter(drm, &idx))
> +		return;
> +
>   	DRM_INFO("HVS ctx:\n");
>   	for (i = 0; i < 64; i += 4) {
>   		DRM_INFO("0x%08x (%s): 0x%08x 0x%08x 0x%08x 0x%08x\n",
> @@ -80,6 +85,8 @@ void vc4_hvs_dump_state(struct vc4_hvs *hvs)
>   			 readl((u32 __iomem *)hvs->dlist + i + 2),
>   			 readl((u32 __iomem *)hvs->dlist + i + 3));
>   	}
> +
> +	drm_dev_exit(idx);
>   }
>   
>   static int vc4_hvs_debugfs_underrun(struct seq_file *m, void *data)
> @@ -132,14 +139,18 @@ static int vc4_hvs_upload_linear_kernel(struct vc4_hvs *hvs,
>   					struct drm_mm_node *space,
>   					const u32 *kernel)
>   {
> -	int ret, i;
> +	struct drm_device *drm = hvs->dev;
> +	int idx, ret, i;
>   	u32 __iomem *dst_kernel;
>   
> +	if (!drm_dev_enter(drm, &idx))
> +		return -ENODEV;
> +
>   	ret = drm_mm_insert_node(&hvs->dlist_mm, space, VC4_KERNEL_DWORDS);
>   	if (ret) {
>   		DRM_ERROR("Failed to allocate space for filter kernel: %d\n",
>   			  ret);
> -		return ret;
> +		goto err_dev_exit;
>   	}
>   
>   	dst_kernel = hvs->dlist + space->start;
> @@ -153,16 +164,26 @@ static int vc4_hvs_upload_linear_kernel(struct vc4_hvs *hvs,
>   		}
>   	}
>   
> +	drm_dev_exit(idx);
>   	return 0;
> +
> +err_dev_exit:
> +	drm_dev_exit(idx);
> +	return ret;
>   }
>   
>   static void vc4_hvs_lut_load(struct vc4_hvs *hvs,
>   			     struct vc4_crtc *vc4_crtc)
>   {
> +	struct drm_device *drm = hvs->dev;
>   	struct drm_crtc *crtc = &vc4_crtc->base;
>   	struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state);
> +	int idx;
>   	u32 i;
>   
> +	if (!drm_dev_enter(drm, &idx))
> +		return;
> +
>   	/* The LUT memory is laid out with each HVS channel in order,
>   	 * each of which takes 256 writes for R, 256 for G, then 256
>   	 * for B.
> @@ -177,6 +198,8 @@ static void vc4_hvs_lut_load(struct vc4_hvs *hvs,
>   		HVS_WRITE(SCALER_GAMDATA, vc4_crtc->lut_g[i]);
>   	for (i = 0; i < crtc->gamma_size; i++)
>   		HVS_WRITE(SCALER_GAMDATA, vc4_crtc->lut_b[i]);
> +
> +	drm_dev_exit(idx);
>   }
>   
>   static void vc4_hvs_update_gamma_lut(struct vc4_hvs *hvs,
> @@ -198,7 +221,12 @@ static void vc4_hvs_update_gamma_lut(struct vc4_hvs *hvs,
>   
>   u8 vc4_hvs_get_fifo_frame_count(struct vc4_hvs *hvs, unsigned int fifo)
>   {
> +	struct drm_device *drm = hvs->dev;
>   	u8 field = 0;
> +	int idx;
> +
> +	if (!drm_dev_enter(drm, &idx))
> +		return 0;
>   
>   	switch (fifo) {
>   	case 0:
> @@ -215,6 +243,7 @@ u8 vc4_hvs_get_fifo_frame_count(struct vc4_hvs *hvs, unsigned int fifo)
>   		break;
>   	}
>   
> +	drm_dev_exit(idx);
>   	return field;
>   }
>   
> @@ -226,6 +255,12 @@ int vc4_hvs_get_fifo_from_output(struct vc4_hvs *hvs, unsigned int output)
>   	if (!hvs->hvs5)
>   		return output;
>   
> +	/*
> +	 * NOTE: We should probably use drm_dev_enter()/drm_dev_exit()
> +	 * here, but this function is only used during the DRM device
> +	 * initialization, so we should be fine.
> +	 */
> +
>   	switch (output) {
>   	case 0:
>   		return 0;
> @@ -273,12 +308,17 @@ int vc4_hvs_get_fifo_from_output(struct vc4_hvs *hvs, unsigned int output)
>   static int vc4_hvs_init_channel(struct vc4_hvs *hvs, struct drm_crtc *crtc,
>   				struct drm_display_mode *mode, bool oneshot)
>   {
> +	struct drm_device *drm = hvs->dev;
>   	struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
>   	struct vc4_crtc_state *vc4_crtc_state = to_vc4_crtc_state(crtc->state);
>   	unsigned int chan = vc4_crtc_state->assigned_channel;
>   	bool interlace = mode->flags & DRM_MODE_FLAG_INTERLACE;
>   	u32 dispbkgndx;
>   	u32 dispctrl;
> +	int idx;
> +
> +	if (!drm_dev_enter(drm, &idx))
> +		return -ENODEV;
>   
>   	HVS_WRITE(SCALER_DISPCTRLX(chan), 0);
>   	HVS_WRITE(SCALER_DISPCTRLX(chan), SCALER_DISPCTRLX_RESET);
> @@ -320,13 +360,21 @@ static int vc4_hvs_init_channel(struct vc4_hvs *hvs, struct drm_crtc *crtc,
>   	 */
>   	vc4_hvs_lut_load(hvs, vc4_crtc);
>   
> +	drm_dev_exit(idx);
> +
>   	return 0;
>   }
>   
>   void vc4_hvs_stop_channel(struct vc4_hvs *hvs, unsigned int chan)
>   {
> +	struct drm_device *drm = hvs->dev;
> +	int idx;
> +
> +	if (!drm_dev_enter(drm, &idx))
> +		return;
> +
>   	if (HVS_READ(SCALER_DISPCTRLX(chan)) & SCALER_DISPCTRLX_ENABLE)
> -		return;
> +		goto out;
>   
>   	HVS_WRITE(SCALER_DISPCTRLX(chan),
>   		  HVS_READ(SCALER_DISPCTRLX(chan)) | SCALER_DISPCTRLX_RESET);
> @@ -343,6 +391,9 @@ void vc4_hvs_stop_channel(struct vc4_hvs *hvs, unsigned int chan)
>   	WARN_ON_ONCE((HVS_READ(SCALER_DISPSTATX(chan)) &
>   		      (SCALER_DISPSTATX_FULL | SCALER_DISPSTATX_EMPTY)) !=
>   		     SCALER_DISPSTATX_EMPTY);
> +
> +out:
> +	drm_dev_exit(idx);
>   }
>   
>   int vc4_hvs_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state)
> @@ -384,9 +435,15 @@ static void vc4_hvs_install_dlist(struct drm_crtc *crtc)
>   	struct vc4_dev *vc4 = to_vc4_dev(dev);
>   	struct vc4_hvs *hvs = vc4->hvs;
>   	struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state);
> +	int idx;
> +
> +	if (!drm_dev_enter(dev, &idx))
> +		return;
>   
>   	HVS_WRITE(SCALER_DISPLISTX(vc4_state->assigned_channel),
>   		  vc4_state->mm.start);
> +
> +	drm_dev_exit(idx);
>   }
>   
>   static void vc4_hvs_update_dlist(struct drm_crtc *crtc)
> @@ -471,6 +528,10 @@ void vc4_hvs_atomic_flush(struct drm_crtc *crtc,
>   	bool enable_bg_fill = false;
>   	u32 __iomem *dlist_start = vc4->hvs->dlist + vc4_state->mm.start;
>   	u32 __iomem *dlist_next = dlist_start;
> +	int idx;
> +
> +	if (!drm_dev_enter(dev, &idx))
> +		return;
>   
>   	if (debug_dump_regs) {
>   		DRM_INFO("CRTC %d HVS before:\n", drm_crtc_index(crtc));
> @@ -541,26 +602,44 @@ void vc4_hvs_atomic_flush(struct drm_crtc *crtc,
>   		DRM_INFO("CRTC %d HVS after:\n", drm_crtc_index(crtc));
>   		vc4_hvs_dump_state(hvs);
>   	}
> +
> +	drm_dev_exit(idx);
>   }
>   
>   void vc4_hvs_mask_underrun(struct vc4_hvs *hvs, int channel)
>   {
> -	u32 dispctrl = HVS_READ(SCALER_DISPCTRL);
> +	struct drm_device *drm = hvs->dev;
> +	u32 dispctrl;
> +	int idx;
>   
> +	if (!drm_dev_enter(drm, &idx))
> +		return;
> +
> +	dispctrl = HVS_READ(SCALER_DISPCTRL);
>   	dispctrl &= ~SCALER_DISPCTRL_DSPEISLUR(channel);
>   
>   	HVS_WRITE(SCALER_DISPCTRL, dispctrl);
> +
> +	drm_dev_exit(idx);
>   }
>   
>   void vc4_hvs_unmask_underrun(struct vc4_hvs *hvs, int channel)
>   {
> -	u32 dispctrl = HVS_READ(SCALER_DISPCTRL);
> +	struct drm_device *drm = hvs->dev;
> +	u32 dispctrl;
> +	int idx;
>   
> +	if (!drm_dev_enter(drm, &idx))
> +		return;
> +
> +	dispctrl = HVS_READ(SCALER_DISPCTRL);
>   	dispctrl |= SCALER_DISPCTRL_DSPEISLUR(channel);
>   
>   	HVS_WRITE(SCALER_DISPSTAT,
>   		  SCALER_DISPSTAT_EUFLOW(channel));
>   	HVS_WRITE(SCALER_DISPCTRL, dispctrl);
> +
> +	drm_dev_exit(idx);
>   }
>   
>   static void vc4_hvs_report_underrun(struct drm_device *dev)
> @@ -581,6 +660,17 @@ static irqreturn_t vc4_hvs_irq_handler(int irq, void *data)
>   	u32 control;
>   	u32 status;
>   
> +	/*
> +	 * NOTE: We don't need to protect the register access using
> +	 * drm_dev_enter() there because the interrupt handler lifetime
> +	 * is tied to the device itself, and not to the DRM device.
> +	 *
> +	 * So when the device will be gone, one of the first thing we
> +	 * will be doing will be to unregister the interrupt handler,
> +	 * and then unregister the DRM device. drm_dev_enter() would
> +	 * thus always succeed if we are here.
> +	 */
> +
>   	status = HVS_READ(SCALER_DISPSTAT);
>   	control = HVS_READ(SCALER_DISPCTRL);
>   
> @@ -613,10 +703,10 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)
>   	u32 dispctrl;
>   	u32 reg;
>   
> -	hvs = devm_kzalloc(&pdev->dev, sizeof(*hvs), GFP_KERNEL);
> +	hvs = drmm_kzalloc(drm, sizeof(*hvs), GFP_KERNEL);
>   	if (!hvs)
>   		return -ENOMEM;
> -
> +	hvs->dev = drm;
>   	hvs->pdev = pdev;
>   
>   	if (of_device_is_compatible(pdev->dev.of_node, "brcm,bcm2711-hvs"))

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Ivo Totev

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 32/64] drm/vc4: dsi: Fix the driver structure lifetime
  2022-06-10  9:28 ` [PATCH 32/64] drm/vc4: dsi: Fix the driver structure lifetime Maxime Ripard
@ 2022-06-20 10:55   ` Thomas Zimmermann
  2022-06-20 10:59     ` Thomas Zimmermann
  0 siblings, 1 reply; 124+ messages in thread
From: Thomas Zimmermann @ 2022-06-20 10:55 UTC (permalink / raw)
  To: Maxime Ripard, Daniel Vetter, David Airlie, Maarten Lankhorst; +Cc: dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 3687 bytes --]



Am 10.06.22 um 11:28 schrieb Maxime Ripard:
> The vc4_dsi structure is currently allocated through a device-managed
> allocation. This can lead to use-after-free issues however in the unbinding
> path since the DRM entities will stick around, but the underlying structure
> has been freed.
> 
> However, we can't just fix it by using a DRM-managed allocation like we did
> for the other drivers since the DSI case is a bit more intricate.
> 
> Indeed, the structure will be allocated at probe time, when we don't have a
> DRM device yet, to be able to register the DSI bus driver. We will then
> reuse it at bind time to register our KMS entities in the framework.
> 
> In order to work around both constraints, we can use a kref to track the
> users of the structure (DSI host, and KMS), and then put our structure when
> the DSI host will have been unregistered, and through a DRM-managed action
> that will execute once we won't need the KMS entities anymore.
> 
> Signed-off-by: Maxime Ripard <maxime@cerno.tech>
> ---
>   drivers/gpu/drm/vc4/vc4_dsi.c | 29 ++++++++++++++++++++++++++++-
>   1 file changed, 28 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
> index 10533a2a41b3..282537f27b8e 100644
> --- a/drivers/gpu/drm/vc4/vc4_dsi.c
> +++ b/drivers/gpu/drm/vc4/vc4_dsi.c
> @@ -510,6 +510,8 @@ struct vc4_dsi {
>   	struct vc4_encoder encoder;
>   	struct mipi_dsi_host dsi_host;
>   
> +	struct kref kref;
> +
>   	struct platform_device *pdev;
>   
>   	struct drm_bridge *bridge;
> @@ -1479,6 +1481,15 @@ vc4_dsi_init_phy_clocks(struct vc4_dsi *dsi)
>   				      dsi->clk_onecell);
>   }
>   
> +static void vc4_dsi_release(struct kref *kref);
> +
> +static void vc4_dsi_put(struct drm_device *drm, void *ptr)
> +{
> +	struct vc4_dsi *dsi = ptr;
> +
> +	kref_put(&dsi->kref, &vc4_dsi_release);
> +}
> +
>   static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
>   {
>   	struct platform_device *pdev = to_platform_device(dev);
> @@ -1488,6 +1499,12 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
>   	dma_cap_mask_t dma_mask;
>   	int ret;
>   
> +	kref_get(&dsi->kref);
> +
> +	ret = drmm_add_action_or_reset(drm, vc4_dsi_put, dsi);
> +	if (ret)
> +		return ret;
> +
>   	dsi->variant = of_device_get_match_data(dev);
>   
>   	INIT_LIST_HEAD(&dsi->bridge_chain);
> @@ -1642,16 +1659,25 @@ static const struct component_ops vc4_dsi_ops = {
>   	.unbind = vc4_dsi_unbind,
>   };
>   
> +static void vc4_dsi_release(struct kref *kref)
> +{
> +	struct vc4_dsi *dsi =
> +		container_of(kref, struct vc4_dsi, kref);
> +
> +	kfree(dsi);
> +}
> +
>   static int vc4_dsi_dev_probe(struct platform_device *pdev)
>   {
>   	struct device *dev = &pdev->dev;
>   	struct vc4_dsi *dsi;
>   
> -	dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
> +	dsi = kzalloc(sizeof(*dsi), GFP_KERNEL);
>   	if (!dsi)
>   		return -ENOMEM;
>   	dev_set_drvdata(dev, dsi);
>   
> +	kref_init(&dsi->kref);
>   	dsi->pdev = pdev;
>   	dsi->dsi_host.ops = &vc4_dsi_host_ops;
>   	dsi->dsi_host.dev = dev;
> @@ -1666,6 +1692,7 @@ static int vc4_dsi_dev_remove(struct platform_device *pdev)
>   	struct vc4_dsi *dsi = dev_get_drvdata(dev);
>   
>   	mipi_dsi_host_unregister(&dsi->dsi_host);
> +	kref_put(&dsi->kref, &vc4_dsi_release);

Maybe vc4_dsi_put() ?

>   	return 0;
>   }
>   

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Ivo Totev

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 32/64] drm/vc4: dsi: Fix the driver structure lifetime
  2022-06-20 10:55   ` Thomas Zimmermann
@ 2022-06-20 10:59     ` Thomas Zimmermann
  0 siblings, 0 replies; 124+ messages in thread
From: Thomas Zimmermann @ 2022-06-20 10:59 UTC (permalink / raw)
  To: Maxime Ripard, Daniel Vetter, David Airlie, Maarten Lankhorst; +Cc: dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 4442 bytes --]



Am 20.06.22 um 12:55 schrieb Thomas Zimmermann:
> 
> 
> Am 10.06.22 um 11:28 schrieb Maxime Ripard:
>> The vc4_dsi structure is currently allocated through a device-managed
>> allocation. This can lead to use-after-free issues however in the 
>> unbinding
>> path since the DRM entities will stick around, but the underlying 
>> structure
>> has been freed.
>>
>> However, we can't just fix it by using a DRM-managed allocation like 
>> we did
>> for the other drivers since the DSI case is a bit more intricate.
>>
>> Indeed, the structure will be allocated at probe time, when we don't 
>> have a
>> DRM device yet, to be able to register the DSI bus driver. We will then
>> reuse it at bind time to register our KMS entities in the framework.
>>
>> In order to work around both constraints, we can use a kref to track the
>> users of the structure (DSI host, and KMS), and then put our structure 
>> when
>> the DSI host will have been unregistered, and through a DRM-managed 
>> action
>> that will execute once we won't need the KMS entities anymore.
>>
>> Signed-off-by: Maxime Ripard <maxime@cerno.tech>
>> ---
>>   drivers/gpu/drm/vc4/vc4_dsi.c | 29 ++++++++++++++++++++++++++++-
>>   1 file changed, 28 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c 
>> b/drivers/gpu/drm/vc4/vc4_dsi.c
>> index 10533a2a41b3..282537f27b8e 100644
>> --- a/drivers/gpu/drm/vc4/vc4_dsi.c
>> +++ b/drivers/gpu/drm/vc4/vc4_dsi.c
>> @@ -510,6 +510,8 @@ struct vc4_dsi {
>>       struct vc4_encoder encoder;
>>       struct mipi_dsi_host dsi_host;
>> +    struct kref kref;
>> +
>>       struct platform_device *pdev;
>>       struct drm_bridge *bridge;
>> @@ -1479,6 +1481,15 @@ vc4_dsi_init_phy_clocks(struct vc4_dsi *dsi)
>>                         dsi->clk_onecell);
>>   }
>> +static void vc4_dsi_release(struct kref *kref);
>> +
>> +static void vc4_dsi_put(struct drm_device *drm, void *ptr)
>> +{
>> +    struct vc4_dsi *dsi = ptr;
>> +
>> +    kref_put(&dsi->kref, &vc4_dsi_release);
>> +}
>> +
>>   static int vc4_dsi_bind(struct device *dev, struct device *master, 
>> void *data)
>>   {
>>       struct platform_device *pdev = to_platform_device(dev);
>> @@ -1488,6 +1499,12 @@ static int vc4_dsi_bind(struct device *dev, 
>> struct device *master, void *data)
>>       dma_cap_mask_t dma_mask;
>>       int ret;
>> +    kref_get(&dsi->kref);
>> +
>> +    ret = drmm_add_action_or_reset(drm, vc4_dsi_put, dsi);
>> +    if (ret)
>> +        return ret;
>> +
>>       dsi->variant = of_device_get_match_data(dev);
>>       INIT_LIST_HEAD(&dsi->bridge_chain);
>> @@ -1642,16 +1659,25 @@ static const struct component_ops vc4_dsi_ops = {
>>       .unbind = vc4_dsi_unbind,
>>   };
>> +static void vc4_dsi_release(struct kref *kref)
>> +{
>> +    struct vc4_dsi *dsi =
>> +        container_of(kref, struct vc4_dsi, kref);
>> +
>> +    kfree(dsi);
>> +}
>> +
>>   static int vc4_dsi_dev_probe(struct platform_device *pdev)
>>   {
>>       struct device *dev = &pdev->dev;
>>       struct vc4_dsi *dsi;
>> -    dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
>> +    dsi = kzalloc(sizeof(*dsi), GFP_KERNEL);
>>       if (!dsi)
>>           return -ENOMEM;
>>       dev_set_drvdata(dev, dsi);
>> +    kref_init(&dsi->kref);
>>       dsi->pdev = pdev;
>>       dsi->dsi_host.ops = &vc4_dsi_host_ops;
>>       dsi->dsi_host.dev = dev;
>> @@ -1666,6 +1692,7 @@ static int vc4_dsi_dev_remove(struct 
>> platform_device *pdev)
>>       struct vc4_dsi *dsi = dev_get_drvdata(dev);
>>       mipi_dsi_host_unregister(&dsi->dsi_host);
>> +    kref_put(&dsi->kref, &vc4_dsi_release);
> 
> Maybe vc4_dsi_put() ?

No, wait. That's the release function.

It's confusing. I'd rename vc4_dsi_put() to vc4_dsi_release_action() and 
wrap those kref_get/kref_put calls in small helpers named 
vc4_dsi_get/vc4_dsi_put. That's more aligned to the usual naming 
conventions, I'd say.

Best regards
Thomas

> 
>>       return 0;
>>   }
> 

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Ivo Totev

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 34/64] drm/vc4: hdmi: Switch to drmm_kzalloc
  2022-06-10  9:28 ` [PATCH 34/64] drm/vc4: hdmi: Switch to drmm_kzalloc Maxime Ripard
@ 2022-06-20 11:01   ` Thomas Zimmermann
  0 siblings, 0 replies; 124+ messages in thread
From: Thomas Zimmermann @ 2022-06-20 11:01 UTC (permalink / raw)
  To: Maxime Ripard, Daniel Vetter, David Airlie, Maarten Lankhorst; +Cc: dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 1730 bytes --]



Am 10.06.22 um 11:28 schrieb Maxime Ripard:
> Our internal structure that stores the DRM entities structure is allocated
> through a device-managed kzalloc.
> 
> This means that this will eventually be freed whenever the device is
> removed. In our case, the most like source of removal is that the main

'most likely source'

> device is going to be unbound, and component_unbind_all() is being run.
> 
> However, it occurs while the DRM device is still registered, which will
> create dangling pointers, eventually resulting in use-after-free.
> 
> Switch to a DRM-managed allocation to keep our structure until the DRM
> driver doesn't need it anymore.
> 
> Signed-off-by: Maxime Ripard <maxime@cerno.tech>
> ---
>   drivers/gpu/drm/vc4/vc4_hdmi.c | 3 ++-
>   1 file changed, 2 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
> index 6aadb65eb640..eb8ff7b258d1 100644
> --- a/drivers/gpu/drm/vc4/vc4_hdmi.c
> +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
> @@ -2833,9 +2833,10 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
>   	struct device_node *ddc_node;
>   	int ret;
>   
> -	vc4_hdmi = devm_kzalloc(dev, sizeof(*vc4_hdmi), GFP_KERNEL);
> +	vc4_hdmi = drmm_kzalloc(drm, sizeof(*vc4_hdmi), GFP_KERNEL);
>   	if (!vc4_hdmi)
>   		return -ENOMEM;
> +
>   	mutex_init(&vc4_hdmi->mutex);
>   	spin_lock_init(&vc4_hdmi->hw_lock);
>   	INIT_DELAYED_WORK(&vc4_hdmi->scrambling_work, vc4_hdmi_scrambling_wq);

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Ivo Totev

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 43/64] drm/vc4: hdmi: Protect device resources after removal
  2022-06-10  9:29 ` [PATCH 43/64] drm/vc4: hdmi: Protect device resources after removal Maxime Ripard
@ 2022-06-20 11:12   ` Thomas Zimmermann
  0 siblings, 0 replies; 124+ messages in thread
From: Thomas Zimmermann @ 2022-06-20 11:12 UTC (permalink / raw)
  To: Maxime Ripard, Daniel Vetter, David Airlie, Maarten Lankhorst; +Cc: dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 29819 bytes --]



Am 10.06.22 um 11:29 schrieb Maxime Ripard:
> Whenever the device and driver are unbound, the main device and all the
> subdevices will be removed by calling their unbind() method.
> 
> However, the DRM device itself will only be freed when the last user will
> have closed it.
> 
> It means that there is a time window where the device and its resources
> aren't there anymore, but the userspace can still call into our driver.
> 
> Fortunately, the DRM framework provides the drm_dev_enter() and
> drm_dev_exit() functions to make sure our underlying device is still there
> for the section protected by those calls. Let's add them to the HDMI driver.
> 
> Signed-off-by: Maxime Ripard <maxime@cerno.tech>

Acked-by: Thomas Zimmermann <tzimmermann@suse.de>

> ---
>   drivers/gpu/drm/vc4/vc4_hdmi.c | 286 +++++++++++++++++++++++++++++++--
>   1 file changed, 269 insertions(+), 17 deletions(-)
> 
> diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
> index 814517c1fdaa..b4fd581861ea 100644
> --- a/drivers/gpu/drm/vc4/vc4_hdmi.c
> +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
> @@ -34,6 +34,7 @@
>   #include <drm/display/drm_hdmi_helper.h>
>   #include <drm/display/drm_scdc_helper.h>
>   #include <drm/drm_atomic_helper.h>
> +#include <drm/drm_drv.h>
>   #include <drm/drm_probe_helper.h>
>   #include <drm/drm_simple_kms_helper.h>
>   #include <linux/clk.h>
> @@ -140,17 +141,33 @@ static int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused)
>   {
>   	struct drm_info_node *node = (struct drm_info_node *)m->private;
>   	struct vc4_hdmi *vc4_hdmi = node->info_ent->data;
> +	struct drm_device *drm = vc4_hdmi->connector.dev;
>   	struct drm_printer p = drm_seq_file_printer(m);
> +	int idx;
> +
> +	if (!drm_dev_enter(drm, &idx))
> +		return -ENODEV;
>   
>   	drm_print_regset32(&p, &vc4_hdmi->hdmi_regset);
>   	drm_print_regset32(&p, &vc4_hdmi->hd_regset);
>   
> +	drm_dev_exit(idx);
> +
>   	return 0;
>   }
>   
>   static void vc4_hdmi_reset(struct vc4_hdmi *vc4_hdmi)
>   {
> +	struct drm_device *drm = vc4_hdmi->connector.dev;
>   	unsigned long flags;
> +	int idx;
> +
> +	/*
> +	 * We can be called by our bind callback, when the
> +	 * connector->dev pointer might not be initialised yet.
> +	 */
> +	if (drm && !drm_dev_enter(drm, &idx))
> +		return;
>   
>   	spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
>   
> @@ -167,11 +184,23 @@ static void vc4_hdmi_reset(struct vc4_hdmi *vc4_hdmi)
>   	HDMI_WRITE(HDMI_SW_RESET_CONTROL, 0);
>   
>   	spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
> +
> +	if (drm)
> +		drm_dev_exit(idx);
>   }
>   
>   static void vc5_hdmi_reset(struct vc4_hdmi *vc4_hdmi)
>   {
> +	struct drm_device *drm = vc4_hdmi->connector.dev;
>   	unsigned long flags;
> +	int idx;
> +
> +	/*
> +	 * We can be called by our bind callback, when the
> +	 * connector->dev pointer might not be initialised yet.
> +	 */
> +	if (drm && !drm_dev_enter(drm, &idx))
> +		return;
>   
>   	reset_control_reset(vc4_hdmi->reset);
>   
> @@ -183,15 +212,25 @@ static void vc5_hdmi_reset(struct vc4_hdmi *vc4_hdmi)
>   		   HDMI_READ(HDMI_CLOCK_STOP) | VC4_DVP_HT_CLOCK_STOP_PIXEL);
>   
>   	spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
> +
> +	if (drm)
> +		drm_dev_exit(idx);
>   }
>   
>   #ifdef CONFIG_DRM_VC4_HDMI_CEC
>   static void vc4_hdmi_cec_update_clk_div(struct vc4_hdmi *vc4_hdmi)
>   {
> -	unsigned long cec_rate = clk_get_rate(vc4_hdmi->cec_clock);
> +	struct drm_device *drm = vc4_hdmi->connector.dev;
> +	unsigned long cec_rate;
>   	unsigned long flags;
>   	u16 clk_cnt;
>   	u32 value;
> +	int idx;
> +
> +	if (!drm_dev_enter(drm, &idx))
> +		return;
> +
> +	cec_rate = clk_get_rate(vc4_hdmi->cec_clock);
>   
>   	spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
>   
> @@ -207,6 +246,8 @@ static void vc4_hdmi_cec_update_clk_div(struct vc4_hdmi *vc4_hdmi)
>   	HDMI_WRITE(HDMI_CEC_CNTRL_1, value);
>   
>   	spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
> +
> +	drm_dev_exit(idx);
>   }
>   #else
>   static void vc4_hdmi_cec_update_clk_div(struct vc4_hdmi *vc4_hdmi) {}
> @@ -427,25 +468,34 @@ static int vc4_hdmi_stop_packet(struct drm_encoder *encoder,
>   				bool poll)
>   {
>   	struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
> +	struct drm_device *drm = vc4_hdmi->connector.dev;
>   	u32 packet_id = type - 0x80;
>   	unsigned long flags;
> +	int ret = 0;
> +	int idx;
> +
> +	if (!drm_dev_enter(drm, &idx))
> +		return -ENODEV;
>   
>   	spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
>   	HDMI_WRITE(HDMI_RAM_PACKET_CONFIG,
>   		   HDMI_READ(HDMI_RAM_PACKET_CONFIG) & ~BIT(packet_id));
>   	spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
>   
> -	if (!poll)
> -		return 0;
> +	if (poll) {
> +		ret = wait_for(!(HDMI_READ(HDMI_RAM_PACKET_STATUS) &
> +				 BIT(packet_id)), 100);
> +	}
>   
> -	return wait_for(!(HDMI_READ(HDMI_RAM_PACKET_STATUS) &
> -			  BIT(packet_id)), 100);
> +	drm_dev_exit(idx);
> +	return ret;
>   }
>   
>   static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder,
>   				     union hdmi_infoframe *frame)
>   {
>   	struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
> +	struct drm_device *drm = vc4_hdmi->connector.dev;
>   	u32 packet_id = frame->any.type - 0x80;
>   	const struct vc4_hdmi_register *ram_packet_start =
>   		&vc4_hdmi->variant->registers[HDMI_RAM_PACKET_START];
> @@ -456,6 +506,10 @@ static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder,
>   	unsigned long flags;
>   	ssize_t len, i;
>   	int ret;
> +	int idx;
> +
> +	if (!drm_dev_enter(drm, &idx))
> +		return;
>   
>   	WARN_ONCE(!(HDMI_READ(HDMI_RAM_PACKET_CONFIG) &
>   		    VC4_HDMI_RAM_PACKET_ENABLE),
> @@ -463,12 +517,12 @@ static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder,
>   
>   	len = hdmi_infoframe_pack(frame, buffer, sizeof(buffer));
>   	if (len < 0)
> -		return;
> +		goto out;
>   
>   	ret = vc4_hdmi_stop_packet(encoder, frame->any.type, true);
>   	if (ret) {
>   		DRM_ERROR("Failed to wait for infoframe to go idle: %d\n", ret);
> -		return;
> +		goto out;
>   	}
>   
>   	spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
> @@ -497,6 +551,9 @@ static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder,
>   			BIT(packet_id)), 100);
>   	if (ret)
>   		DRM_ERROR("Failed to wait for infoframe to start: %d\n", ret);
> +
> +out:
> +	drm_dev_exit(idx);
>   }
>   
>   static void vc4_hdmi_avi_infoframe_colorspace(struct hdmi_avi_infoframe *frame,
> @@ -644,8 +701,10 @@ static bool vc4_hdmi_supports_scrambling(struct drm_encoder *encoder,
>   static void vc4_hdmi_enable_scrambling(struct drm_encoder *encoder)
>   {
>   	struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
> +	struct drm_device *drm = vc4_hdmi->connector.dev;
>   	struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode;
>   	unsigned long flags;
> +	int idx;
>   
>   	lockdep_assert_held(&vc4_hdmi->mutex);
>   
> @@ -657,6 +716,9 @@ static void vc4_hdmi_enable_scrambling(struct drm_encoder *encoder)
>   					    vc4_hdmi->output_format))
>   		return;
>   
> +	if (!drm_dev_enter(drm, &idx))
> +		return;
> +
>   	drm_scdc_set_high_tmds_clock_ratio(vc4_hdmi->ddc, true);
>   	drm_scdc_set_scrambling(vc4_hdmi->ddc, true);
>   
> @@ -665,6 +727,8 @@ static void vc4_hdmi_enable_scrambling(struct drm_encoder *encoder)
>   		   VC5_HDMI_SCRAMBLER_CTL_ENABLE);
>   	spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
>   
> +	drm_dev_exit(idx);
> +
>   	vc4_hdmi->scdc_enabled = true;
>   
>   	queue_delayed_work(system_wq, &vc4_hdmi->scrambling_work,
> @@ -674,7 +738,9 @@ static void vc4_hdmi_enable_scrambling(struct drm_encoder *encoder)
>   static void vc4_hdmi_disable_scrambling(struct drm_encoder *encoder)
>   {
>   	struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
> +	struct drm_device *drm = vc4_hdmi->connector.dev;
>   	unsigned long flags;
> +	int idx;
>   
>   	lockdep_assert_held(&vc4_hdmi->mutex);
>   
> @@ -686,6 +752,9 @@ static void vc4_hdmi_disable_scrambling(struct drm_encoder *encoder)
>   	if (delayed_work_pending(&vc4_hdmi->scrambling_work))
>   		cancel_delayed_work_sync(&vc4_hdmi->scrambling_work);
>   
> +	if (!drm_dev_enter(drm, &idx))
> +		return;
> +
>   	spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
>   	HDMI_WRITE(HDMI_SCRAMBLER_CTL, HDMI_READ(HDMI_SCRAMBLER_CTL) &
>   		   ~VC5_HDMI_SCRAMBLER_CTL_ENABLE);
> @@ -693,6 +762,8 @@ static void vc4_hdmi_disable_scrambling(struct drm_encoder *encoder)
>   
>   	drm_scdc_set_scrambling(vc4_hdmi->ddc, false);
>   	drm_scdc_set_high_tmds_clock_ratio(vc4_hdmi->ddc, false);
> +
> +	drm_dev_exit(idx);
>   }
>   
>   static void vc4_hdmi_scrambling_wq(struct work_struct *work)
> @@ -715,10 +786,15 @@ static void vc4_hdmi_encoder_post_crtc_disable(struct drm_encoder *encoder,
>   					       struct drm_atomic_state *state)
>   {
>   	struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
> +	struct drm_device *drm = vc4_hdmi->connector.dev;
>   	unsigned long flags;
> +	int idx;
>   
>   	mutex_lock(&vc4_hdmi->mutex);
>   
> +	if (!drm_dev_enter(drm, &idx))
> +		goto out;
> +
>   	spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
>   
>   	HDMI_WRITE(HDMI_RAM_PACKET_CONFIG, 0);
> @@ -736,6 +812,9 @@ static void vc4_hdmi_encoder_post_crtc_disable(struct drm_encoder *encoder,
>   
>   	vc4_hdmi_disable_scrambling(encoder);
>   
> +	drm_dev_exit(idx);
> +
> +out:
>   	mutex_unlock(&vc4_hdmi->mutex);
>   }
>   
> @@ -743,11 +822,16 @@ static void vc4_hdmi_encoder_post_crtc_powerdown(struct drm_encoder *encoder,
>   						 struct drm_atomic_state *state)
>   {
>   	struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
> +	struct drm_device *drm = vc4_hdmi->connector.dev;
>   	unsigned long flags;
>   	int ret;
> +	int idx;
>   
>   	mutex_lock(&vc4_hdmi->mutex);
>   
> +	if (!drm_dev_enter(drm, &idx))
> +		goto out;
> +
>   	spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
>   	HDMI_WRITE(HDMI_VID_CTL,
>   		   HDMI_READ(HDMI_VID_CTL) | VC4_HD_VID_CTL_BLANKPIX);
> @@ -763,6 +847,9 @@ static void vc4_hdmi_encoder_post_crtc_powerdown(struct drm_encoder *encoder,
>   	if (ret < 0)
>   		DRM_ERROR("Failed to release power domain: %d\n", ret);
>   
> +	drm_dev_exit(idx);
> +
> +out:
>   	mutex_unlock(&vc4_hdmi->mutex);
>   }
>   
> @@ -779,8 +866,13 @@ static void vc4_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi,
>   			       struct drm_connector_state *state,
>   			       const struct drm_display_mode *mode)
>   {
> +	struct drm_device *drm = vc4_hdmi->connector.dev;
>   	unsigned long flags;
>   	u32 csc_ctl;
> +	int idx;
> +
> +	if (!drm_dev_enter(drm, &idx))
> +		return;
>   
>   	spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
>   
> @@ -815,6 +907,8 @@ static void vc4_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi,
>   	HDMI_WRITE(HDMI_CSC_CTL, csc_ctl);
>   
>   	spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
> +
> +	drm_dev_exit(idx);
>   }
>   
>   /*
> @@ -899,6 +993,7 @@ static void vc5_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi,
>   			       struct drm_connector_state *state,
>   			       const struct drm_display_mode *mode)
>   {
> +	struct drm_device *drm = vc4_hdmi->connector.dev;
>   	struct vc4_hdmi_connector_state *vc4_state =
>   		conn_state_to_vc4_hdmi_conn_state(state);
>   	unsigned long flags;
> @@ -907,6 +1002,10 @@ static void vc5_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi,
>   	u32 csc_chan_ctl = 0;
>   	u32 csc_ctl = VC5_MT_CP_CSC_CTL_ENABLE | VC4_SET_FIELD(VC4_HD_CSC_CTL_MODE_CUSTOM,
>   							       VC5_MT_CP_CSC_CTL_MODE);
> +	int idx;
> +
> +	if (!drm_dev_enter(drm, &idx))
> +		return;
>   
>   	spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
>   
> @@ -949,12 +1048,15 @@ static void vc5_hdmi_csc_setup(struct vc4_hdmi *vc4_hdmi,
>   	HDMI_WRITE(HDMI_CSC_CTL, csc_ctl);
>   
>   	spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
> +
> +	drm_dev_exit(idx);
>   }
>   
>   static void vc4_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi,
>   				 struct drm_connector_state *state,
>   				 struct drm_display_mode *mode)
>   {
> +	struct drm_device *drm = vc4_hdmi->connector.dev;
>   	bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC;
>   	bool vsync_pos = mode->flags & DRM_MODE_FLAG_PVSYNC;
>   	bool interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE;
> @@ -973,6 +1075,10 @@ static void vc4_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi,
>   					interlaced,
>   					VC4_HDMI_VERTB_VBP));
>   	unsigned long flags;
> +	int idx;
> +
> +	if (!drm_dev_enter(drm, &idx))
> +		return;
>   
>   	spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
>   
> @@ -1000,12 +1106,15 @@ static void vc4_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi,
>   	HDMI_WRITE(HDMI_VERTB1, vertb);
>   
>   	spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
> +
> +	drm_dev_exit(idx);
>   }
>   
>   static void vc5_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi,
>   				 struct drm_connector_state *state,
>   				 struct drm_display_mode *mode)
>   {
> +	struct drm_device *drm = vc4_hdmi->connector.dev;
>   	const struct vc4_hdmi_connector_state *vc4_state =
>   		conn_state_to_vc4_hdmi_conn_state(state);
>   	bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC;
> @@ -1029,6 +1138,10 @@ static void vc5_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi,
>   	unsigned char gcp;
>   	bool gcp_en;
>   	u32 reg;
> +	int idx;
> +
> +	if (!drm_dev_enter(drm, &idx))
> +		return;
>   
>   	spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
>   
> @@ -1100,13 +1213,20 @@ static void vc5_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi,
>   	HDMI_WRITE(HDMI_CLOCK_STOP, 0);
>   
>   	spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
> +
> +	drm_dev_exit(idx);
>   }
>   
>   static void vc4_hdmi_recenter_fifo(struct vc4_hdmi *vc4_hdmi)
>   {
> +	struct drm_device *drm = vc4_hdmi->connector.dev;
>   	unsigned long flags;
>   	u32 drift;
>   	int ret;
> +	int idx;
> +
> +	if (!drm_dev_enter(drm, &idx))
> +		return;
>   
>   	spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
>   
> @@ -1135,12 +1255,15 @@ static void vc4_hdmi_recenter_fifo(struct vc4_hdmi *vc4_hdmi)
>   		       VC4_HDMI_FIFO_CTL_RECENTER_DONE, 1);
>   	WARN_ONCE(ret, "Timeout waiting for "
>   		  "VC4_HDMI_FIFO_CTL_RECENTER_DONE");
> +
> +	drm_dev_exit(idx);
>   }
>   
>   static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder,
>   						struct drm_atomic_state *state)
>   {
>   	struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
> +	struct drm_device *drm = vc4_hdmi->connector.dev;
>   	struct drm_connector *connector = &vc4_hdmi->connector;
>   	struct drm_connector_state *conn_state =
>   		drm_atomic_get_new_connector_state(state, connector);
> @@ -1151,9 +1274,13 @@ static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder,
>   	unsigned long bvb_rate, hsm_rate;
>   	unsigned long flags;
>   	int ret;
> +	int idx;
>   
>   	mutex_lock(&vc4_hdmi->mutex);
>   
> +	if (!drm_dev_enter(drm, &idx))
> +		goto out;
> +
>   	/*
>   	 * As stated in RPi's vc4 firmware "HDMI state machine (HSM) clock must
>   	 * be faster than pixel clock, infinitesimally faster, tested in
> @@ -1174,13 +1301,13 @@ static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder,
>   	ret = clk_set_min_rate(vc4_hdmi->hsm_clock, hsm_rate);
>   	if (ret) {
>   		DRM_ERROR("Failed to set HSM clock rate: %d\n", ret);
> -		goto out;
> +		goto err_dev_exit;
>   	}
>   
>   	ret = pm_runtime_resume_and_get(&vc4_hdmi->pdev->dev);
>   	if (ret < 0) {
>   		DRM_ERROR("Failed to retain power domain: %d\n", ret);
> -		goto out;
> +		goto err_dev_exit;
>   	}
>   
>   	ret = clk_set_rate(vc4_hdmi->pixel_clock, tmds_char_rate);
> @@ -1232,6 +1359,8 @@ static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder,
>   	if (vc4_hdmi->variant->set_timings)
>   		vc4_hdmi->variant->set_timings(vc4_hdmi, conn_state, mode);
>   
> +	drm_dev_exit(idx);
> +
>   	mutex_unlock(&vc4_hdmi->mutex);
>   
>   	return;
> @@ -1240,6 +1369,8 @@ static void vc4_hdmi_encoder_pre_crtc_configure(struct drm_encoder *encoder,
>   	clk_disable_unprepare(vc4_hdmi->pixel_clock);
>   err_put_runtime_pm:
>   	pm_runtime_put(&vc4_hdmi->pdev->dev);
> +err_dev_exit:
> +	drm_dev_exit(idx);
>   out:
>   	mutex_unlock(&vc4_hdmi->mutex);
>   	return;
> @@ -1249,14 +1380,19 @@ static void vc4_hdmi_encoder_pre_crtc_enable(struct drm_encoder *encoder,
>   					     struct drm_atomic_state *state)
>   {
>   	struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
> +	struct drm_device *drm = vc4_hdmi->connector.dev;
>   	struct drm_connector *connector = &vc4_hdmi->connector;
>   	struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode;
>   	struct drm_connector_state *conn_state =
>   		drm_atomic_get_new_connector_state(state, connector);
>   	unsigned long flags;
> +	int idx;
>   
>   	mutex_lock(&vc4_hdmi->mutex);
>   
> +	if (!drm_dev_enter(drm, &idx))
> +		return;
> +
>   	if (vc4_hdmi->variant->csc_setup)
>   		vc4_hdmi->variant->csc_setup(vc4_hdmi, conn_state, mode);
>   
> @@ -1264,6 +1400,8 @@ static void vc4_hdmi_encoder_pre_crtc_enable(struct drm_encoder *encoder,
>   	HDMI_WRITE(HDMI_FIFO_CTL, VC4_HDMI_FIFO_CTL_MASTER_SLAVE_N);
>   	spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
>   
> +	drm_dev_exit(idx);
> +
>   	mutex_unlock(&vc4_hdmi->mutex);
>   }
>   
> @@ -1271,15 +1409,20 @@ static void vc4_hdmi_encoder_post_crtc_enable(struct drm_encoder *encoder,
>   					      struct drm_atomic_state *state)
>   {
>   	struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
> +	struct drm_device *drm = vc4_hdmi->connector.dev;
>   	struct drm_display_mode *mode = &vc4_hdmi->saved_adjusted_mode;
>   	struct drm_display_info *display = &vc4_hdmi->connector.display_info;
>   	bool hsync_pos = mode->flags & DRM_MODE_FLAG_PHSYNC;
>   	bool vsync_pos = mode->flags & DRM_MODE_FLAG_PVSYNC;
>   	unsigned long flags;
>   	int ret;
> +	int idx;
>   
>   	mutex_lock(&vc4_hdmi->mutex);
>   
> +	if (!drm_dev_enter(drm, &idx))
> +		return;
> +
>   	spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
>   
>   	HDMI_WRITE(HDMI_VID_CTL,
> @@ -1340,6 +1483,7 @@ static void vc4_hdmi_encoder_post_crtc_enable(struct drm_encoder *encoder,
>   	vc4_hdmi_recenter_fifo(vc4_hdmi);
>   	vc4_hdmi_enable_scrambling(encoder);
>   
> +	drm_dev_exit(idx);
>   	mutex_unlock(&vc4_hdmi->mutex);
>   }
>   
> @@ -1675,13 +1819,20 @@ static u32 vc5_hdmi_channel_map(struct vc4_hdmi *vc4_hdmi, u32 channel_mask)
>   
>   static bool vc5_hdmi_hp_detect(struct vc4_hdmi *vc4_hdmi)
>   {
> +	struct drm_device *drm = vc4_hdmi->connector.dev;
>   	unsigned long flags;
>   	u32 hotplug;
> +	int idx;
> +
> +	if (!drm_dev_enter(drm, &idx))
> +		return false;
>   
>   	spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
>   	hotplug = HDMI_READ(HDMI_HOTPLUG);
>   	spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
>   
> +	drm_dev_exit(idx);
> +
>   	return !!(hotplug & VC4_HDMI_HOTPLUG_CONNECTED);
>   }
>   
> @@ -1689,10 +1840,16 @@ static bool vc5_hdmi_hp_detect(struct vc4_hdmi *vc4_hdmi)
>   static void vc4_hdmi_audio_set_mai_clock(struct vc4_hdmi *vc4_hdmi,
>   					 unsigned int samplerate)
>   {
> -	u32 hsm_clock = clk_get_rate(vc4_hdmi->audio_clock);
> +	struct drm_device *drm = vc4_hdmi->connector.dev;
> +	u32 hsm_clock;
>   	unsigned long flags;
>   	unsigned long n, m;
> +	int idx;
>   
> +	if (!drm_dev_enter(drm, &idx))
> +		return;
> +
> +	hsm_clock = clk_get_rate(vc4_hdmi->audio_clock);
>   	rational_best_approximation(hsm_clock, samplerate,
>   				    VC4_HD_MAI_SMP_N_MASK >>
>   				    VC4_HD_MAI_SMP_N_SHIFT,
> @@ -1705,6 +1862,8 @@ static void vc4_hdmi_audio_set_mai_clock(struct vc4_hdmi *vc4_hdmi,
>   		   VC4_SET_FIELD(n, VC4_HD_MAI_SMP_N) |
>   		   VC4_SET_FIELD(m - 1, VC4_HD_MAI_SMP_M));
>   	spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
> +
> +	drm_dev_exit(idx);
>   }
>   
>   static void vc4_hdmi_set_n_cts(struct vc4_hdmi *vc4_hdmi, unsigned int samplerate)
> @@ -1764,13 +1923,21 @@ static bool vc4_hdmi_audio_can_stream(struct vc4_hdmi *vc4_hdmi)
>   static int vc4_hdmi_audio_startup(struct device *dev, void *data)
>   {
>   	struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev);
> +	struct drm_device *drm = vc4_hdmi->connector.dev;
>   	unsigned long flags;
> +	int ret = 0;
> +	int idx;
>   
>   	mutex_lock(&vc4_hdmi->mutex);
>   
> +	if (!drm_dev_enter(drm, &idx)) {
> +		ret = -ENODEV;
> +		goto out;
> +	}
> +
>   	if (!vc4_hdmi_audio_can_stream(vc4_hdmi)) {
> -		mutex_unlock(&vc4_hdmi->mutex);
> -		return -ENODEV;
> +		ret = -ENODEV;
> +		goto out_dev_exit;
>   	}
>   
>   	vc4_hdmi->audio.streaming = true;
> @@ -1787,9 +1954,12 @@ static int vc4_hdmi_audio_startup(struct device *dev, void *data)
>   	if (vc4_hdmi->variant->phy_rng_enable)
>   		vc4_hdmi->variant->phy_rng_enable(vc4_hdmi);
>   
> +out_dev_exit:
> +	drm_dev_exit(idx);
> +out:
>   	mutex_unlock(&vc4_hdmi->mutex);
>   
> -	return 0;
> +	return ret;
>   }
>   
>   static void vc4_hdmi_audio_reset(struct vc4_hdmi *vc4_hdmi)
> @@ -1818,10 +1988,15 @@ static void vc4_hdmi_audio_reset(struct vc4_hdmi *vc4_hdmi)
>   static void vc4_hdmi_audio_shutdown(struct device *dev, void *data)
>   {
>   	struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev);
> +	struct drm_device *drm = vc4_hdmi->connector.dev;
>   	unsigned long flags;
> +	int idx;
>   
>   	mutex_lock(&vc4_hdmi->mutex);
>   
> +	if (!drm_dev_enter(drm, &idx))
> +		goto out;
> +
>   	spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
>   
>   	HDMI_WRITE(HDMI_MAI_CTL,
> @@ -1837,6 +2012,9 @@ static void vc4_hdmi_audio_shutdown(struct device *dev, void *data)
>   	vc4_hdmi->audio.streaming = false;
>   	vc4_hdmi_audio_reset(vc4_hdmi);
>   
> +	drm_dev_exit(idx);
> +
> +out:
>   	mutex_unlock(&vc4_hdmi->mutex);
>   }
>   
> @@ -1884,6 +2062,7 @@ static int vc4_hdmi_audio_prepare(struct device *dev, void *data,
>   				  struct hdmi_codec_params *params)
>   {
>   	struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev);
> +	struct drm_device *drm = vc4_hdmi->connector.dev;
>   	struct drm_encoder *encoder = &vc4_hdmi->encoder.base;
>   	unsigned int sample_rate = params->sample_rate;
>   	unsigned int channels = params->channels;
> @@ -1892,15 +2071,22 @@ static int vc4_hdmi_audio_prepare(struct device *dev, void *data,
>   	u32 channel_map;
>   	u32 mai_audio_format;
>   	u32 mai_sample_rate;
> +	int ret = 0;
> +	int idx;
>   
>   	dev_dbg(dev, "%s: %u Hz, %d bit, %d channels\n", __func__,
>   		sample_rate, params->sample_width, channels);
>   
>   	mutex_lock(&vc4_hdmi->mutex);
>   
> +	if (!drm_dev_enter(drm, &idx)) {
> +		ret = -ENODEV;
> +		goto out;
> +	}
> +
>   	if (!vc4_hdmi_audio_can_stream(vc4_hdmi)) {
> -		mutex_unlock(&vc4_hdmi->mutex);
> -		return -EINVAL;
> +		ret = -EINVAL;
> +		goto out_dev_exit;
>   	}
>   
>   	vc4_hdmi_audio_set_mai_clock(vc4_hdmi, sample_rate);
> @@ -1957,9 +2143,12 @@ static int vc4_hdmi_audio_prepare(struct device *dev, void *data,
>   	memcpy(&vc4_hdmi->audio.infoframe, &params->cea, sizeof(params->cea));
>   	vc4_hdmi_set_audio_infoframe(encoder);
>   
> +out_dev_exit:
> +	drm_dev_exit(idx);
> +out:
>   	mutex_unlock(&vc4_hdmi->mutex);
>   
> -	return 0;
> +	return ret;
>   }
>   
>   static const struct snd_soc_component_driver vc4_hdmi_audio_cpu_dai_comp = {
> @@ -2294,6 +2483,17 @@ static irqreturn_t vc4_cec_irq_handler_tx_bare_locked(struct vc4_hdmi *vc4_hdmi)
>   {
>   	u32 cntrl1;
>   
> +	/*
> +	 * We don't need to protect the register access using
> +	 * drm_dev_enter() there because the interrupt handler lifetime
> +	 * is tied to the device itself, and not to the DRM device.
> +	 *
> +	 * So when the device will be gone, one of the first thing we
> +	 * will be doing will be to unregister the interrupt handler,
> +	 * and then unregister the DRM device. drm_dev_enter() would
> +	 * thus always succeed if we are here.
> +	 */
> +
>   	lockdep_assert_held(&vc4_hdmi->hw_lock);
>   
>   	cntrl1 = HDMI_READ(HDMI_CEC_CNTRL_1);
> @@ -2322,6 +2522,17 @@ static irqreturn_t vc4_cec_irq_handler_rx_bare_locked(struct vc4_hdmi *vc4_hdmi)
>   
>   	lockdep_assert_held(&vc4_hdmi->hw_lock);
>   
> +	/*
> +	 * We don't need to protect the register access using
> +	 * drm_dev_enter() there because the interrupt handler lifetime
> +	 * is tied to the device itself, and not to the DRM device.
> +	 *
> +	 * So when the device will be gone, one of the first thing we
> +	 * will be doing will be to unregister the interrupt handler,
> +	 * and then unregister the DRM device. drm_dev_enter() would
> +	 * thus always succeed if we are here.
> +	 */
> +
>   	vc4_hdmi->cec_rx_msg.len = 0;
>   	cntrl1 = HDMI_READ(HDMI_CEC_CNTRL_1);
>   	vc4_cec_read_msg(vc4_hdmi, cntrl1);
> @@ -2353,6 +2564,17 @@ static irqreturn_t vc4_cec_irq_handler(int irq, void *priv)
>   	irqreturn_t ret;
>   	u32 cntrl5;
>   
> +	/*
> +	 * We don't need to protect the register access using
> +	 * drm_dev_enter() there because the interrupt handler lifetime
> +	 * is tied to the device itself, and not to the DRM device.
> +	 *
> +	 * So when the device will be gone, one of the first thing we
> +	 * will be doing will be to unregister the interrupt handler,
> +	 * and then unregister the DRM device. drm_dev_enter() would
> +	 * thus always succeed if we are here.
> +	 */
> +
>   	if (!(stat & VC4_HDMI_CPU_CEC))
>   		return IRQ_NONE;
>   
> @@ -2373,11 +2595,13 @@ static irqreturn_t vc4_cec_irq_handler(int irq, void *priv)
>   static int vc4_hdmi_cec_enable(struct cec_adapter *adap)
>   {
>   	struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap);
> +	struct drm_device *drm = vc4_hdmi->connector.dev;
>   	/* clock period in microseconds */
>   	const u32 usecs = 1000000 / CEC_CLOCK_FREQ;
>   	unsigned long flags;
>   	u32 val;
>   	int ret;
> +	int idx;
>   
>   	/*
>   	 * NOTE: This function should really take vc4_hdmi->mutex, but doing so
> @@ -2390,9 +2614,14 @@ static int vc4_hdmi_cec_enable(struct cec_adapter *adap)
>   	 * keep it in mind if we were to change that assumption.
>   	 */
>   
> +	if (!drm_dev_enter(drm, &idx))
> +		return -ENODEV;
> +
>   	ret = pm_runtime_resume_and_get(&vc4_hdmi->pdev->dev);
> -	if (ret)
> +	if (ret) {
> +		drm_dev_exit(idx);
>   		return ret;
> +	}
>   
>   	spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
>   
> @@ -2428,13 +2657,20 @@ static int vc4_hdmi_cec_enable(struct cec_adapter *adap)
>   
>   	spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
>   
> +	drm_dev_exit(idx);
> +
>   	return 0;
>   }
>   
>   static int vc4_hdmi_cec_disable(struct cec_adapter *adap)
>   {
>   	struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap);
> +	struct drm_device *drm = vc4_hdmi->connector.dev;
>   	unsigned long flags;
> +	int idx;
> +
> +	if (!drm_dev_enter(drm, &idx))
> +		return -ENODEV;
>   
>   	/*
>   	 * NOTE: This function should really take vc4_hdmi->mutex, but doing so
> @@ -2459,6 +2695,8 @@ static int vc4_hdmi_cec_disable(struct cec_adapter *adap)
>   
>   	pm_runtime_put(&vc4_hdmi->pdev->dev);
>   
> +	drm_dev_exit(idx);
> +
>   	return 0;
>   }
>   
> @@ -2473,7 +2711,9 @@ static int vc4_hdmi_cec_adap_enable(struct cec_adapter *adap, bool enable)
>   static int vc4_hdmi_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr)
>   {
>   	struct vc4_hdmi *vc4_hdmi = cec_get_drvdata(adap);
> +	struct drm_device *drm = vc4_hdmi->connector.dev;
>   	unsigned long flags;
> +	int idx;
>   
>   	/*
>   	 * NOTE: This function should really take vc4_hdmi->mutex, but doing so
> @@ -2486,12 +2726,17 @@ static int vc4_hdmi_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr)
>   	 * keep it in mind if we were to change that assumption.
>   	 */
>   
> +	if (!drm_dev_enter(drm, &idx))
> +		return -ENODEV;
> +
>   	spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
>   	HDMI_WRITE(HDMI_CEC_CNTRL_1,
>   		   (HDMI_READ(HDMI_CEC_CNTRL_1) & ~VC4_HDMI_CEC_ADDR_MASK) |
>   		   (log_addr & 0xf) << VC4_HDMI_CEC_ADDR_SHIFT);
>   	spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
>   
> +	drm_dev_exit(idx);
> +
>   	return 0;
>   }
>   
> @@ -2503,6 +2748,7 @@ static int vc4_hdmi_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
>   	unsigned long flags;
>   	u32 val;
>   	unsigned int i;
> +	int idx;
>   
>   	/*
>   	 * NOTE: This function should really take vc4_hdmi->mutex, but doing so
> @@ -2515,8 +2761,12 @@ static int vc4_hdmi_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
>   	 * keep it in mind if we were to change that assumption.
>   	 */
>   
> +	if (!drm_dev_enter(dev, &idx))
> +		return -ENODEV;
> +
>   	if (msg->len > 16) {
>   		drm_err(dev, "Attempting to transmit too much data (%d)\n", msg->len);
> +		drm_dev_exit(idx);
>   		return -ENOMEM;
>   	}
>   
> @@ -2540,6 +2790,8 @@ static int vc4_hdmi_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
>   
>   	spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
>   
> +	drm_dev_exit(idx);
> +
>   	return 0;
>   }
>   

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Ivo Totev

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 45/64] drm/vc4: txp: Remove vc4_dev txp pointer
  2022-06-10  9:29 ` [PATCH 45/64] drm/vc4: txp: Remove vc4_dev txp pointer Maxime Ripard
@ 2022-06-20 11:16   ` Thomas Zimmermann
  0 siblings, 0 replies; 124+ messages in thread
From: Thomas Zimmermann @ 2022-06-20 11:16 UTC (permalink / raw)
  To: Maxime Ripard, Daniel Vetter, David Airlie, Maarten Lankhorst; +Cc: dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 2274 bytes --]



Am 10.06.22 um 11:29 schrieb Maxime Ripard:
> There's no user for that pointer so let's just get rid of it.
> 
> Signed-off-by: Maxime Ripard <maxime@cerno.tech>

Acked-by: Thomas Zimmermann <tzimmermann@suse.de>

> ---
>   drivers/gpu/drm/vc4/vc4_drv.h | 1 -
>   drivers/gpu/drm/vc4/vc4_txp.c | 6 ------
>   2 files changed, 7 deletions(-)
> 
> diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h
> index 846f3cda179a..5f2d7640a09d 100644
> --- a/drivers/gpu/drm/vc4/vc4_drv.h
> +++ b/drivers/gpu/drm/vc4/vc4_drv.h
> @@ -80,7 +80,6 @@ struct vc4_dev {
>   	struct vc4_hvs *hvs;
>   	struct vc4_v3d *v3d;
>   	struct vc4_vec *vec;
> -	struct vc4_txp *txp;
>   
>   	struct vc4_hang_state *hang_state;
>   
> diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c
> index f306e05ac5b2..87c896f482fb 100644
> --- a/drivers/gpu/drm/vc4/vc4_txp.c
> +++ b/drivers/gpu/drm/vc4/vc4_txp.c
> @@ -468,7 +468,6 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data)
>   {
>   	struct platform_device *pdev = to_platform_device(dev);
>   	struct drm_device *drm = dev_get_drvdata(master);
> -	struct vc4_dev *vc4 = to_vc4_dev(drm);
>   	struct vc4_crtc *vc4_crtc;
>   	struct vc4_txp *txp;
>   	struct drm_crtc *crtc;
> @@ -521,7 +520,6 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data)
>   		return ret;
>   
>   	dev_set_drvdata(dev, txp);
> -	vc4->txp = txp;
>   
>   	vc4_debugfs_add_regset32(drm, "txp_regs", &txp->regset);
>   
> @@ -531,13 +529,9 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data)
>   static void vc4_txp_unbind(struct device *dev, struct device *master,
>   			   void *data)
>   {
> -	struct drm_device *drm = dev_get_drvdata(master);
> -	struct vc4_dev *vc4 = to_vc4_dev(drm);
>   	struct vc4_txp *txp = dev_get_drvdata(dev);
>   
>   	vc4_txp_connector_destroy(&txp->connector.base);
> -
> -	vc4->txp = NULL;
>   }
>   
>   static const struct component_ops vc4_txp_ops = {

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Ivo Totev

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 46/64] drm/vc4: txp: Remove duplicate regset
  2022-06-10  9:29 ` [PATCH 46/64] drm/vc4: txp: Remove duplicate regset Maxime Ripard
@ 2022-06-20 11:16   ` Thomas Zimmermann
  0 siblings, 0 replies; 124+ messages in thread
From: Thomas Zimmermann @ 2022-06-20 11:16 UTC (permalink / raw)
  To: Maxime Ripard, Daniel Vetter, David Airlie, Maarten Lankhorst; +Cc: dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 1884 bytes --]



Am 10.06.22 um 11:29 schrieb Maxime Ripard:
> There's already a regset in the vc4_crtc structure so there's no need to
> duplicate it in vc4_txp.
> 
> Signed-off-by: Maxime Ripard <maxime@cerno.tech>

Acked-by: Thomas Zimmermann <tzimmermann@suse.de>

> ---
>   drivers/gpu/drm/vc4/vc4_txp.c | 9 ++++-----
>   1 file changed, 4 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c
> index 87c896f482fb..51ac01838093 100644
> --- a/drivers/gpu/drm/vc4/vc4_txp.c
> +++ b/drivers/gpu/drm/vc4/vc4_txp.c
> @@ -154,7 +154,6 @@ struct vc4_txp {
>   	struct drm_writeback_connector connector;
>   
>   	void __iomem *regs;
> -	struct debugfs_regset32 regset;
>   };
>   
>   static inline struct vc4_txp *encoder_to_vc4_txp(struct drm_encoder *encoder)
> @@ -493,9 +492,9 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data)
>   	txp->regs = vc4_ioremap_regs(pdev, 0);
>   	if (IS_ERR(txp->regs))
>   		return PTR_ERR(txp->regs);
> -	txp->regset.base = txp->regs;
> -	txp->regset.regs = txp_regs;
> -	txp->regset.nregs = ARRAY_SIZE(txp_regs);
> +	vc4_crtc->regset.base = txp->regs;
> +	vc4_crtc->regset.regs = txp_regs;
> +	vc4_crtc->regset.nregs = ARRAY_SIZE(txp_regs);
>   
>   	drm_connector_helper_add(&txp->connector.base,
>   				 &vc4_txp_connector_helper_funcs);
> @@ -521,7 +520,7 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data)
>   
>   	dev_set_drvdata(dev, txp);
>   
> -	vc4_debugfs_add_regset32(drm, "txp_regs", &txp->regset);
> +	vc4_debugfs_add_regset32(drm, "txp_regs", &vc4_crtc->regset);
>   
>   	return 0;
>   }

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Ivo Totev

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 47/64] drm/vc4: txp: Switch to drmm_kzalloc
  2022-06-10  9:29 ` [PATCH 47/64] drm/vc4: txp: Switch to drmm_kzalloc Maxime Ripard
@ 2022-06-20 11:17   ` Thomas Zimmermann
  0 siblings, 0 replies; 124+ messages in thread
From: Thomas Zimmermann @ 2022-06-20 11:17 UTC (permalink / raw)
  To: Maxime Ripard, Daniel Vetter, David Airlie, Maarten Lankhorst; +Cc: dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 1579 bytes --]



Am 10.06.22 um 11:29 schrieb Maxime Ripard:
> Our internal structure that stores the DRM entities structure is allocated
> through a device-managed kzalloc.
> 
> This means that this will eventually be freed whenever the device is
> removed. In our case, the most like source of removal is that the main
> device is going to be unbound, and component_unbind_all() is being run.
> 
> However, it occurs while the DRM device is still registered, which will
> create dangling pointers, eventually resulting in use-after-free.
> 
> Switch to a DRM-managed allocation to keep our structure until the DRM
> driver doesn't need it anymore.
> 
> Signed-off-by: Maxime Ripard <maxime@cerno.tech>

Acked-by: Thomas Zimmermann <tzimmermann@suse.de>

> ---
>   drivers/gpu/drm/vc4/vc4_txp.c | 2 +-
>   1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c
> index 51ac01838093..6a16b2798724 100644
> --- a/drivers/gpu/drm/vc4/vc4_txp.c
> +++ b/drivers/gpu/drm/vc4/vc4_txp.c
> @@ -477,7 +477,7 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data)
>   	if (irq < 0)
>   		return irq;
>   
> -	txp = devm_kzalloc(dev, sizeof(*txp), GFP_KERNEL);
> +	txp = drmm_kzalloc(drm, sizeof(*txp), GFP_KERNEL);
>   	if (!txp)
>   		return -ENOMEM;
>   	vc4_crtc = &txp->base;

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Ivo Totev

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 05/64] drm/connector: Mention the cleanup after drm_connector_init
  2022-06-20 10:21   ` Thomas Zimmermann
@ 2022-06-20 12:18     ` Maxime Ripard
  2022-06-20 14:19       ` Thomas Zimmermann
  0 siblings, 1 reply; 124+ messages in thread
From: Maxime Ripard @ 2022-06-20 12:18 UTC (permalink / raw)
  To: Thomas Zimmermann; +Cc: David Airlie, Daniel Vetter, dri-devel

[-- Attachment #1: Type: text/plain, Size: 1543 bytes --]

Hi Thomas,

Thanks for your review

On Mon, Jun 20, 2022 at 12:21:33PM +0200, Thomas Zimmermann wrote:
> Am 10.06.22 um 11:28 schrieb Maxime Ripard:
> > Unlike encoders and CRTCs, the drm_connector_init() and
> > drm_connector_init_with_ddc() don't mention how the cleanup is supposed to
> > be done. Let's add it.
> > 
> > Signed-off-by: Maxime Ripard <maxime@cerno.tech>
> > ---
> >   drivers/gpu/drm/drm_connector.c | 10 ++++++++++
> >   1 file changed, 10 insertions(+)
> > 
> > diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
> > index 353d83ae09d3..2a78a23836d8 100644
> > --- a/drivers/gpu/drm/drm_connector.c
> > +++ b/drivers/gpu/drm/drm_connector.c
> > @@ -222,6 +222,11 @@ void drm_connector_free_work_fn(struct work_struct *work)
> >    * Initialises a preallocated connector. Connectors should be
> >    * subclassed as part of driver connector objects.
> >    *
> > + * At driver unload time the driver's &drm_connector_funcs.destroy hook
> > + * should call drm_connector_unregister(), drm_connector_cleanup() and
> > + * kfree() the connector structure. The connector structure should not
> 
> This sounds odd. Maybe
> 
> '... to release... the connector structure' ?

I didn't really get what your suggestion was exactly

Is it

> At driver unload time the driver's &drm_connector_funcs.destroy hook
> should call drm_connector_unregister(), drm_connector_cleanup() and
> kfree() to release the connector structure.

Or something else?

Thanks!
Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 25/64] drm/vc4: dpi: Add action to disable the clock
  2022-06-16  9:47       ` Dave Stevenson
@ 2022-06-20 12:21         ` Maxime Ripard
  0 siblings, 0 replies; 124+ messages in thread
From: Maxime Ripard @ 2022-06-20 12:21 UTC (permalink / raw)
  To: Dave Stevenson
  Cc: David Airlie, Daniel Vetter, DRI Development, Thomas Zimmermann

[-- Attachment #1: Type: text/plain, Size: 1575 bytes --]

On Thu, Jun 16, 2022 at 10:47:56AM +0100, Dave Stevenson wrote:
> On Thu, 16 Jun 2022 at 09:38, Maxime Ripard <maxime@cerno.tech> wrote:
> >
> > Hi Dave,
> >
> > On Tue, Jun 14, 2022 at 05:47:28PM +0100, Dave Stevenson wrote:
> > > Hi Maxime.
> > >
> > > On Fri, 10 Jun 2022 at 10:30, Maxime Ripard <maxime@cerno.tech> wrote:
> > > >
> > > > Adding a device-managed action will make the error path easier, so let's
> > > > create one to disable our clock.
> > >
> > > The DPI block has two clocks (core and pixel), and this is only
> > > affecting one of them (the core clock).
> >
> > Thanks for the suggestion, I've amended the commit message.
> >
> > > (I'm actually puzzling over what it's wanting to do with the core
> > > clock here as it's only enabling it rather than setting a rate. I
> > > think it may be redundant).
> >
> > Could it be that it a "bus" clock that we need it to access the
> > registers?
> 
> No idea. Normally it's the power domain that is required to access
> registers.

For HDMI at least, the power domain being off will make a read return
some bogus value, but the core clock being off will just make the CPU
stall. I can only assume it would be the same for the DPI controller?

> AFAIK the core clock is never turned off, so the request for it is
> just a little odd.

Even if the clock driver never shuts it off, I think it still has value
to follow the Clock Framework conventions. We might have a different
clock policy in the future, and it would throw people reading the DPI
driver off.

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 13/64] drm/vc4: hvs: Protect device resources after removal
  2022-06-14 15:11   ` Dave Stevenson
  2022-06-14 16:59     ` Dave Stevenson
@ 2022-06-20 13:20     ` Maxime Ripard
  1 sibling, 0 replies; 124+ messages in thread
From: Maxime Ripard @ 2022-06-20 13:20 UTC (permalink / raw)
  To: Dave Stevenson
  Cc: David Airlie, Daniel Vetter, DRI Development, Thomas Zimmermann

[-- Attachment #1: Type: text/plain, Size: 1214 bytes --]

Hi Dave,

On Tue, Jun 14, 2022 at 04:11:20PM +0100, Dave Stevenson wrote:
> On Fri, 10 Jun 2022 at 10:30, Maxime Ripard <maxime@cerno.tech> wrote:
> >
> > Whenever the device and driver are unbound, the main device and all the
> > subdevices will be removed by calling their unbind() method.
> >
> > However, the DRM device itself will only be freed when the last user will
> > have closed it.
> >
> > It means that there is a time window where the device and its resources
> > aren't there anymore, but the userspace can still call into our driver.
> >
> > Fortunately, the DRM framework provides the drm_dev_enter() and
> > drm_dev_exit() functions to make sure our underlying device is still there
> > for the section protected by those calls. Let's add them to the HVS driver.
> 
> The framework appears to rely on the remove function calling
> drm_dev_unplug instead of drm_dev_unregister, but I haven't seen a
> patch that makes that change in the vc4 driver.
> Have I missed it, or is there some other route to set the unplugged
> flag that drm_dev_enter/exit are relying on?

You're right, we should have called drm_dev_unplug. I fixed it up (and
the fallouts)

Thanks!
Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 13/64] drm/vc4: hvs: Protect device resources after removal
  2022-06-14 16:59     ` Dave Stevenson
@ 2022-06-20 13:23       ` Maxime Ripard
  0 siblings, 0 replies; 124+ messages in thread
From: Maxime Ripard @ 2022-06-20 13:23 UTC (permalink / raw)
  To: Dave Stevenson
  Cc: David Airlie, Daniel Vetter, DRI Development, Thomas Zimmermann

[-- Attachment #1: Type: text/plain, Size: 808 bytes --]

Hi,

On Tue, Jun 14, 2022 at 05:59:15PM +0100, Dave Stevenson wrote:
> > > @@ -132,14 +139,18 @@ static int vc4_hvs_upload_linear_kernel(struct vc4_hvs *hvs,
> > >                                         struct drm_mm_node *space,
> > >                                         const u32 *kernel)
> > >  {
> > > -       int ret, i;
> > > +       struct drm_device *drm = hvs->dev;
> > > +       int idx, ret, i;
> > >         u32 __iomem *dst_kernel;
> > >
> > > +       if (!drm_dev_enter(drm, &idx))
> > > +               return -ENODEV;
> > > +
> 
> vc4_hvs_upload_linear_kernel is only called from vc4_hvs_bind, so
> unless bind and unbind calls can be concurrent, then there's no need
> for protection here.

Indeed. I've removed those changes and added a comment

Thanks!
Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 09/64] drm/simple: Introduce drmm_simple_encoder_init
  2022-06-20 10:44   ` Thomas Zimmermann
@ 2022-06-20 13:48     ` Maxime Ripard
  2022-06-20 14:25       ` Thomas Zimmermann
  0 siblings, 1 reply; 124+ messages in thread
From: Maxime Ripard @ 2022-06-20 13:48 UTC (permalink / raw)
  To: Thomas Zimmermann; +Cc: David Airlie, Daniel Vetter, dri-devel

[-- Attachment #1: Type: text/plain, Size: 1225 bytes --]

Hi,

On Mon, Jun 20, 2022 at 12:44:24PM +0200, Thomas Zimmermann wrote:
> Am 10.06.22 um 11:28 schrieb Maxime Ripard:
> > The DRM-managed function to register an encoder is
> > drmm_simple_encoder_alloc() and its variants, which will allocate the
> > underlying structure and initialisation the encoder.
> > 
> > However, we might want to separate the structure creation and the encoder
> > initialisation, for example if the structure is shared across multiple DRM
> > entities, for example an encoder and a connector.
> > 
> > Let's create an helper to only initialise an encoder that would be passed
> > as an argument.
> > 
> 
> There's nothing wrong with this patch, but I have to admit that adding
> drm_simple_encoder_init() et al was a mistake.

Why do you think it was a mistake?

> It would have been better to add an initializer macro like
> 
> #define DRM_STATIC_ENCODER_DEFAULT_FUNCS \
>   .destroy = drm_encoder_cleanup
> 
> It's way more flexible and similarly easy to use. Anyway...

We can still have this. It would simplify this series so I could
definitely squeeze that patch in and add a TODO item / deprecation
notice for simple encoders if you think it's needed

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 05/64] drm/connector: Mention the cleanup after drm_connector_init
  2022-06-20 12:18     ` Maxime Ripard
@ 2022-06-20 14:19       ` Thomas Zimmermann
  2022-06-20 14:40         ` Maxime Ripard
  0 siblings, 1 reply; 124+ messages in thread
From: Thomas Zimmermann @ 2022-06-20 14:19 UTC (permalink / raw)
  To: Maxime Ripard; +Cc: David Airlie, Daniel Vetter, dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 460 bytes --]

Hi

Am 20.06.22 um 14:18 schrieb Maxime Ripard:
> + * At driver unload time the driver's &drm_connector_funcs.destroy hook
> + * should call drm_connector_unregister(), drm_connector_cleanup() and
> + * kfree() the connector structure.

This sentence sounds odd.

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Ivo Totev

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 09/64] drm/simple: Introduce drmm_simple_encoder_init
  2022-06-20 13:48     ` Maxime Ripard
@ 2022-06-20 14:25       ` Thomas Zimmermann
  2022-06-20 14:39         ` Maxime Ripard
  0 siblings, 1 reply; 124+ messages in thread
From: Thomas Zimmermann @ 2022-06-20 14:25 UTC (permalink / raw)
  To: Maxime Ripard; +Cc: David Airlie, Daniel Vetter, dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 2093 bytes --]

Hi

Am 20.06.22 um 15:48 schrieb Maxime Ripard:
> Hi,
> 
> On Mon, Jun 20, 2022 at 12:44:24PM +0200, Thomas Zimmermann wrote:
>> Am 10.06.22 um 11:28 schrieb Maxime Ripard:
>>> The DRM-managed function to register an encoder is
>>> drmm_simple_encoder_alloc() and its variants, which will allocate the
>>> underlying structure and initialisation the encoder.
>>>
>>> However, we might want to separate the structure creation and the encoder
>>> initialisation, for example if the structure is shared across multiple DRM
>>> entities, for example an encoder and a connector.
>>>
>>> Let's create an helper to only initialise an encoder that would be passed
>>> as an argument.
>>>
>>
>> There's nothing wrong with this patch, but I have to admit that adding
>> drm_simple_encoder_init() et al was a mistake.
> 
> Why do you think it was a mistake?

The simple-encoder stuff is an interface that no one really needs. 
Compared to open-coding the function, it's barely an improvement in 
LOCs, but nothing else.

Back when I added drm_simple_encoder_init(), I wanted to simplify the 
many drivers that initialized the encoder with a cleanup callback and 
nothing else.  IIRC it was an improvement back then.  But now we already 
have a related drmm_ helper and a drmm_alloc_ helper. If I had only 
added the macro back then, we could use the regular encoder helpers.

> 
>> It would have been better to add an initializer macro like
>>
>> #define DRM_STATIC_ENCODER_DEFAULT_FUNCS \
>>    .destroy = drm_encoder_cleanup
>>
>> It's way more flexible and similarly easy to use. Anyway...
> 
> We can still have this. It would simplify this series so I could
> definitely squeeze that patch in and add a TODO item / deprecation
> notice for simple encoders if you think it's needed

Not necessary. It's not super important.

Best regards
Thomas

> 
> Maxime

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Ivo Totev

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 09/64] drm/simple: Introduce drmm_simple_encoder_init
  2022-06-20 14:25       ` Thomas Zimmermann
@ 2022-06-20 14:39         ` Maxime Ripard
  2022-06-20 14:45           ` Thomas Zimmermann
  0 siblings, 1 reply; 124+ messages in thread
From: Maxime Ripard @ 2022-06-20 14:39 UTC (permalink / raw)
  To: Thomas Zimmermann; +Cc: David Airlie, Daniel Vetter, dri-devel

[-- Attachment #1: Type: text/plain, Size: 2434 bytes --]

On Mon, Jun 20, 2022 at 04:25:38PM +0200, Thomas Zimmermann wrote:
> Hi
> 
> Am 20.06.22 um 15:48 schrieb Maxime Ripard:
> > Hi,
> > 
> > On Mon, Jun 20, 2022 at 12:44:24PM +0200, Thomas Zimmermann wrote:
> > > Am 10.06.22 um 11:28 schrieb Maxime Ripard:
> > > > The DRM-managed function to register an encoder is
> > > > drmm_simple_encoder_alloc() and its variants, which will allocate the
> > > > underlying structure and initialisation the encoder.
> > > > 
> > > > However, we might want to separate the structure creation and the encoder
> > > > initialisation, for example if the structure is shared across multiple DRM
> > > > entities, for example an encoder and a connector.
> > > > 
> > > > Let's create an helper to only initialise an encoder that would be passed
> > > > as an argument.
> > > > 
> > > 
> > > There's nothing wrong with this patch, but I have to admit that adding
> > > drm_simple_encoder_init() et al was a mistake.
> > 
> > Why do you think it was a mistake?
> 
> The simple-encoder stuff is an interface that no one really needs. Compared
> to open-coding the function, it's barely an improvement in LOCs, but nothing
> else.
> 
> Back when I added drm_simple_encoder_init(), I wanted to simplify the many
> drivers that initialized the encoder with a cleanup callback and nothing
> else.  IIRC it was an improvement back then.  But now we already have a
> related drmm_ helper and a drmm_alloc_ helper. If I had only added the macro
> back then, we could use the regular encoder helpers.
> 
> > 
> > > It would have been better to add an initializer macro like
> > > 
> > > #define DRM_STATIC_ENCODER_DEFAULT_FUNCS \
> > >    .destroy = drm_encoder_cleanup
> > > 
> > > It's way more flexible and similarly easy to use. Anyway...
> > 
> > We can still have this. It would simplify this series so I could
> > definitely squeeze that patch in and add a TODO item / deprecation
> > notice for simple encoders if you think it's needed
> 
> Not necessary. It's not super important.

The corollary is though :)

If I understand you right, it means that you'd rather have a destroy
callback everywhere instead of calling the _cleanup function through a
drm-managed callback, and let drm_dev_unregister do its job?

If so, it means that we shouldn't be following the drmm_.*_alloc
functions and should drop all the new ones from this series.

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 05/64] drm/connector: Mention the cleanup after drm_connector_init
  2022-06-20 14:19       ` Thomas Zimmermann
@ 2022-06-20 14:40         ` Maxime Ripard
  2022-06-20 14:43           ` Thomas Zimmermann
  0 siblings, 1 reply; 124+ messages in thread
From: Maxime Ripard @ 2022-06-20 14:40 UTC (permalink / raw)
  To: Thomas Zimmermann; +Cc: David Airlie, Daniel Vetter, dri-devel

[-- Attachment #1: Type: text/plain, Size: 623 bytes --]

On Mon, Jun 20, 2022 at 04:19:43PM +0200, Thomas Zimmermann wrote:
> Hi
> 
> Am 20.06.22 um 14:18 schrieb Maxime Ripard:
> > + * At driver unload time the driver's &drm_connector_funcs.destroy hook
> > + * should call drm_connector_unregister(), drm_connector_cleanup() and
> > + * kfree() the connector structure.
> 
> This sentence sounds odd.

Yeah, I was using kfree as a verb which is probably where the weirdness comes from.

Would that be better:

At driver unload time the driver's &drm_connector_funcs.destroy hook should call
drm_connector_unregister() and free the connector structure.

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 05/64] drm/connector: Mention the cleanup after drm_connector_init
  2022-06-20 14:40         ` Maxime Ripard
@ 2022-06-20 14:43           ` Thomas Zimmermann
  0 siblings, 0 replies; 124+ messages in thread
From: Thomas Zimmermann @ 2022-06-20 14:43 UTC (permalink / raw)
  To: Maxime Ripard; +Cc: David Airlie, Daniel Vetter, dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 935 bytes --]

Hi

Am 20.06.22 um 16:40 schrieb Maxime Ripard:
> On Mon, Jun 20, 2022 at 04:19:43PM +0200, Thomas Zimmermann wrote:
>> Hi
>>
>> Am 20.06.22 um 14:18 schrieb Maxime Ripard:
>>> + * At driver unload time the driver's &drm_connector_funcs.destroy hook
>>> + * should call drm_connector_unregister(), drm_connector_cleanup() and
>>> + * kfree() the connector structure.
>>
>> This sentence sounds odd.
> 
> Yeah, I was using kfree as a verb which is probably where the weirdness comes from.
> 
> Would that be better:
> 
> At driver unload time the driver's &drm_connector_funcs.destroy hook should call
> drm_connector_unregister() and free the connector structure.

Yes. That's better.

Best regards
Thomas

> 
> Maxime

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Ivo Totev

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 09/64] drm/simple: Introduce drmm_simple_encoder_init
  2022-06-20 14:39         ` Maxime Ripard
@ 2022-06-20 14:45           ` Thomas Zimmermann
  2022-06-20 19:04             ` Thomas Zimmermann
  0 siblings, 1 reply; 124+ messages in thread
From: Thomas Zimmermann @ 2022-06-20 14:45 UTC (permalink / raw)
  To: Maxime Ripard; +Cc: David Airlie, Daniel Vetter, dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 2859 bytes --]

Hi

Am 20.06.22 um 16:39 schrieb Maxime Ripard:
> On Mon, Jun 20, 2022 at 04:25:38PM +0200, Thomas Zimmermann wrote:
>> Hi
>>
>> Am 20.06.22 um 15:48 schrieb Maxime Ripard:
>>> Hi,
>>>
>>> On Mon, Jun 20, 2022 at 12:44:24PM +0200, Thomas Zimmermann wrote:
>>>> Am 10.06.22 um 11:28 schrieb Maxime Ripard:
>>>>> The DRM-managed function to register an encoder is
>>>>> drmm_simple_encoder_alloc() and its variants, which will allocate the
>>>>> underlying structure and initialisation the encoder.
>>>>>
>>>>> However, we might want to separate the structure creation and the encoder
>>>>> initialisation, for example if the structure is shared across multiple DRM
>>>>> entities, for example an encoder and a connector.
>>>>>
>>>>> Let's create an helper to only initialise an encoder that would be passed
>>>>> as an argument.
>>>>>
>>>>
>>>> There's nothing wrong with this patch, but I have to admit that adding
>>>> drm_simple_encoder_init() et al was a mistake.
>>>
>>> Why do you think it was a mistake?
>>
>> The simple-encoder stuff is an interface that no one really needs. Compared
>> to open-coding the function, it's barely an improvement in LOCs, but nothing
>> else.
>>
>> Back when I added drm_simple_encoder_init(), I wanted to simplify the many
>> drivers that initialized the encoder with a cleanup callback and nothing
>> else.  IIRC it was an improvement back then.  But now we already have a
>> related drmm_ helper and a drmm_alloc_ helper. If I had only added the macro
>> back then, we could use the regular encoder helpers.
>>
>>>
>>>> It would have been better to add an initializer macro like
>>>>
>>>> #define DRM_STATIC_ENCODER_DEFAULT_FUNCS \
>>>>     .destroy = drm_encoder_cleanup
>>>>
>>>> It's way more flexible and similarly easy to use. Anyway...
>>>
>>> We can still have this. It would simplify this series so I could
>>> definitely squeeze that patch in and add a TODO item / deprecation
>>> notice for simple encoders if you think it's needed
>>
>> Not necessary. It's not super important.
> 
> The corollary is though :)
> 
> If I understand you right, it means that you'd rather have a destroy
> callback everywhere instead of calling the _cleanup function through a
> drm-managed callback, and let drm_dev_unregister do its job?
> 
> If so, it means that we shouldn't be following the drmm_.*_alloc
> functions and should drop all the new ones from this series.

No, no. What I'm saying is that simple-encoder is a pointless mid-layer. 
There's nothing that couldn't easily be achieved with the regular 
encoder functions.

Best regards
Thomas

> 
> Maxime

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Ivo Totev

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 09/64] drm/simple: Introduce drmm_simple_encoder_init
  2022-06-20 14:45           ` Thomas Zimmermann
@ 2022-06-20 19:04             ` Thomas Zimmermann
  2022-06-22 12:36               ` Maxime Ripard
  0 siblings, 1 reply; 124+ messages in thread
From: Thomas Zimmermann @ 2022-06-20 19:04 UTC (permalink / raw)
  To: Maxime Ripard; +Cc: David Airlie, Daniel Vetter, dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 3264 bytes --]

Hi

Am 20.06.22 um 16:45 schrieb Thomas Zimmermann:
> Hi
> 
> Am 20.06.22 um 16:39 schrieb Maxime Ripard:
>> On Mon, Jun 20, 2022 at 04:25:38PM +0200, Thomas Zimmermann wrote:
>>> Hi
>>>
>>> Am 20.06.22 um 15:48 schrieb Maxime Ripard:
>>>> Hi,
>>>>
>>>> On Mon, Jun 20, 2022 at 12:44:24PM +0200, Thomas Zimmermann wrote:
>>>>> Am 10.06.22 um 11:28 schrieb Maxime Ripard:
>>>>>> The DRM-managed function to register an encoder is
>>>>>> drmm_simple_encoder_alloc() and its variants, which will allocate the
>>>>>> underlying structure and initialisation the encoder.
>>>>>>
>>>>>> However, we might want to separate the structure creation and the 
>>>>>> encoder
>>>>>> initialisation, for example if the structure is shared across 
>>>>>> multiple DRM
>>>>>> entities, for example an encoder and a connector.
>>>>>>
>>>>>> Let's create an helper to only initialise an encoder that would be 
>>>>>> passed
>>>>>> as an argument.
>>>>>>
>>>>>
>>>>> There's nothing wrong with this patch, but I have to admit that adding
>>>>> drm_simple_encoder_init() et al was a mistake.
>>>>
>>>> Why do you think it was a mistake?
>>>
>>> The simple-encoder stuff is an interface that no one really needs. 
>>> Compared
>>> to open-coding the function, it's barely an improvement in LOCs, but 
>>> nothing
>>> else.
>>>
>>> Back when I added drm_simple_encoder_init(), I wanted to simplify the 
>>> many
>>> drivers that initialized the encoder with a cleanup callback and nothing
>>> else.  IIRC it was an improvement back then.  But now we already have a
>>> related drmm_ helper and a drmm_alloc_ helper. If I had only added 
>>> the macro
>>> back then, we could use the regular encoder helpers.
>>>
>>>>
>>>>> It would have been better to add an initializer macro like
>>>>>
>>>>> #define DRM_STATIC_ENCODER_DEFAULT_FUNCS \
>>>>>     .destroy = drm_encoder_cleanup
>>>>>
>>>>> It's way more flexible and similarly easy to use. Anyway...
>>>>
>>>> We can still have this. It would simplify this series so I could
>>>> definitely squeeze that patch in and add a TODO item / deprecation
>>>> notice for simple encoders if you think it's needed
>>>
>>> Not necessary. It's not super important.
>>
>> The corollary is though :)
>>
>> If I understand you right, it means that you'd rather have a destroy
>> callback everywhere instead of calling the _cleanup function through a
>> drm-managed callback, and let drm_dev_unregister do its job?
>>
>> If so, it means that we shouldn't be following the drmm_.*_alloc
>> functions and should drop all the new ones from this series.
> 
> No, no. What I'm saying is that simple-encoder is a pointless mid-layer. 
> There's nothing that couldn't easily be achieved with the regular 
> encoder functions.

I guess that if you want to change something here, you could add 
drmm_encoder_init() instead and convert vc4. That function might be more 
useful for other drivers in the long run.

Best regards
Thomas

> 
> Best regards
> Thomas
> 
>>
>> Maxime
> 

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Ivo Totev

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 124+ messages in thread

* Re: [PATCH 09/64] drm/simple: Introduce drmm_simple_encoder_init
  2022-06-20 19:04             ` Thomas Zimmermann
@ 2022-06-22 12:36               ` Maxime Ripard
  0 siblings, 0 replies; 124+ messages in thread
From: Maxime Ripard @ 2022-06-22 12:36 UTC (permalink / raw)
  To: Thomas Zimmermann; +Cc: David Airlie, Daniel Vetter, dri-devel

[-- Attachment #1: Type: text/plain, Size: 3542 bytes --]

On Mon, Jun 20, 2022 at 09:04:11PM +0200, Thomas Zimmermann wrote:
> Hi
> 
> Am 20.06.22 um 16:45 schrieb Thomas Zimmermann:
> > Hi
> > 
> > Am 20.06.22 um 16:39 schrieb Maxime Ripard:
> > > On Mon, Jun 20, 2022 at 04:25:38PM +0200, Thomas Zimmermann wrote:
> > > > Hi
> > > > 
> > > > Am 20.06.22 um 15:48 schrieb Maxime Ripard:
> > > > > Hi,
> > > > > 
> > > > > On Mon, Jun 20, 2022 at 12:44:24PM +0200, Thomas Zimmermann wrote:
> > > > > > Am 10.06.22 um 11:28 schrieb Maxime Ripard:
> > > > > > > The DRM-managed function to register an encoder is
> > > > > > > drmm_simple_encoder_alloc() and its variants, which will allocate the
> > > > > > > underlying structure and initialisation the encoder.
> > > > > > > 
> > > > > > > However, we might want to separate the structure
> > > > > > > creation and the encoder
> > > > > > > initialisation, for example if the structure is
> > > > > > > shared across multiple DRM
> > > > > > > entities, for example an encoder and a connector.
> > > > > > > 
> > > > > > > Let's create an helper to only initialise an encoder
> > > > > > > that would be passed
> > > > > > > as an argument.
> > > > > > > 
> > > > > > 
> > > > > > There's nothing wrong with this patch, but I have to admit that adding
> > > > > > drm_simple_encoder_init() et al was a mistake.
> > > > > 
> > > > > Why do you think it was a mistake?
> > > > 
> > > > The simple-encoder stuff is an interface that no one really
> > > > needs. Compared
> > > > to open-coding the function, it's barely an improvement in LOCs,
> > > > but nothing
> > > > else.
> > > > 
> > > > Back when I added drm_simple_encoder_init(), I wanted to
> > > > simplify the many
> > > > drivers that initialized the encoder with a cleanup callback and nothing
> > > > else.  IIRC it was an improvement back then.  But now we already have a
> > > > related drmm_ helper and a drmm_alloc_ helper. If I had only
> > > > added the macro
> > > > back then, we could use the regular encoder helpers.
> > > > 
> > > > > 
> > > > > > It would have been better to add an initializer macro like
> > > > > > 
> > > > > > #define DRM_STATIC_ENCODER_DEFAULT_FUNCS \
> > > > > >     .destroy = drm_encoder_cleanup
> > > > > > 
> > > > > > It's way more flexible and similarly easy to use. Anyway...
> > > > > 
> > > > > We can still have this. It would simplify this series so I could
> > > > > definitely squeeze that patch in and add a TODO item / deprecation
> > > > > notice for simple encoders if you think it's needed
> > > > 
> > > > Not necessary. It's not super important.
> > > 
> > > The corollary is though :)
> > > 
> > > If I understand you right, it means that you'd rather have a destroy
> > > callback everywhere instead of calling the _cleanup function through a
> > > drm-managed callback, and let drm_dev_unregister do its job?
> > > 
> > > If so, it means that we shouldn't be following the drmm_.*_alloc
> > > functions and should drop all the new ones from this series.
> > 
> > No, no. What I'm saying is that simple-encoder is a pointless mid-layer.
> > There's nothing that couldn't easily be achieved with the regular
> > encoder functions.
> 
> I guess that if you want to change something here, you could add
> drmm_encoder_init() instead and convert vc4. That function might be more
> useful for other drivers in the long run.

I already had that patch in the series. I've dropped this one and
converted everyone to a !simple encoder.

Thanks!
maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 124+ messages in thread

end of thread, other threads:[~2022-06-22 12:37 UTC | newest]

Thread overview: 124+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-06-10  9:28 [PATCH 00/64] drm/vc4: Fix hotplug for vc4 Maxime Ripard
2022-06-10  9:28 ` [PATCH 01/64] drm/mipi-dsi: Detach devices when removing the host Maxime Ripard
2022-06-10  9:28 ` [PATCH 02/64] drm/crtc: Introduce drmm_crtc_init_with_planes Maxime Ripard
2022-06-13 11:23   ` Thomas Zimmermann
2022-06-14  7:37     ` Maxime Ripard
2022-06-14  8:29       ` Thomas Zimmermann
2022-06-14  9:04         ` Maxime Ripard
2022-06-14 11:47           ` Thomas Zimmermann
2022-06-14 12:09             ` Maxime Ripard
2022-06-15  7:22               ` Thomas Zimmermann
2022-06-15  8:32                 ` Maxime Ripard
2022-06-15 10:34                   ` Thomas Zimmermann
2022-06-16  9:32                     ` Maxime Ripard
2022-06-10  9:28 ` [PATCH 03/64] drm/encoder: Introduce drmm_encoder_init Maxime Ripard
2022-06-10  9:28 ` [PATCH 04/64] drm/connector: Reorder headers Maxime Ripard
2022-06-10  9:28 ` [PATCH 05/64] drm/connector: Mention the cleanup after drm_connector_init Maxime Ripard
2022-06-20 10:21   ` Thomas Zimmermann
2022-06-20 12:18     ` Maxime Ripard
2022-06-20 14:19       ` Thomas Zimmermann
2022-06-20 14:40         ` Maxime Ripard
2022-06-20 14:43           ` Thomas Zimmermann
2022-06-10  9:28 ` [PATCH 06/64] drm/connector: Introduce drmm_connector_init Maxime Ripard
2022-06-20 10:23   ` Thomas Zimmermann
2022-06-10  9:28 ` [PATCH 07/64] drm/connector: Introduce drmm_connector_init_with_ddc Maxime Ripard
2022-06-10  9:28 ` [PATCH 08/64] drm/writeback: Introduce drmm_writeback_connector_init Maxime Ripard
2022-06-20 10:33   ` Thomas Zimmermann
2022-06-10  9:28 ` [PATCH 09/64] drm/simple: Introduce drmm_simple_encoder_init Maxime Ripard
2022-06-20 10:44   ` Thomas Zimmermann
2022-06-20 13:48     ` Maxime Ripard
2022-06-20 14:25       ` Thomas Zimmermann
2022-06-20 14:39         ` Maxime Ripard
2022-06-20 14:45           ` Thomas Zimmermann
2022-06-20 19:04             ` Thomas Zimmermann
2022-06-22 12:36               ` Maxime Ripard
2022-06-10  9:28 ` [PATCH 10/64] drm/bridge: panel: Introduce drmm_panel_bridge_add Maxime Ripard
2022-06-10  9:28 ` [PATCH 11/64] drm/bridge: panel: Introduce drmm_of_get_bridge Maxime Ripard
2022-06-10  9:28 ` [PATCH 12/64] drm/vc4: Call component_unbind_all() Maxime Ripard
2022-06-14 15:02   ` Dave Stevenson
2022-06-10  9:28 ` [PATCH 13/64] drm/vc4: hvs: Protect device resources after removal Maxime Ripard
2022-06-14 15:11   ` Dave Stevenson
2022-06-14 16:59     ` Dave Stevenson
2022-06-20 13:23       ` Maxime Ripard
2022-06-20 13:20     ` Maxime Ripard
2022-06-20 10:48   ` Thomas Zimmermann
2022-06-10  9:28 ` [PATCH 14/64] drm/vc4: hvs: Remove planes currently allocated before taking down Maxime Ripard
2022-06-14 14:51   ` Dave Stevenson
2022-06-10  9:28 ` [PATCH 15/64] drm/vc4: plane: Take possible_crtcs as an argument Maxime Ripard
2022-06-14 15:27   ` Dave Stevenson
2022-06-10  9:28 ` [PATCH 16/64] drm/vc4: plane: Switch to drmm_universal_plane_alloc() Maxime Ripard
2022-06-14 15:42   ` Dave Stevenson
2022-06-10  9:28 ` [PATCH 17/64] drm/vc4: crtc: Move debugfs_name to crtc_data Maxime Ripard
2022-06-14 15:57   ` Dave Stevenson
2022-06-16  9:41     ` Maxime Ripard
2022-06-16  9:44       ` Dave Stevenson
2022-06-10  9:28 ` [PATCH 18/64] drm/vc4: crtc: Switch to drmm_kzalloc Maxime Ripard
2022-06-14 15:43   ` Dave Stevenson
2022-06-10  9:28 ` [PATCH 19/64] drm/vc4: crtc: Switch to DRM-managed CRTC initialization Maxime Ripard
2022-06-14 16:13   ` Dave Stevenson
2022-06-10  9:28 ` [PATCH 20/64] drm/vc4: dpi: Remove vc4_dev dpi pointer Maxime Ripard
2022-06-14 16:00   ` Dave Stevenson
2022-06-10  9:28 ` [PATCH 21/64] drm/vc4: dpi: Embed DRM structures into the private structure Maxime Ripard
2022-06-14 16:07   ` Dave Stevenson
2022-06-10  9:28 ` [PATCH 22/64] drm/vc4: dpi: Switch to drmm_kzalloc Maxime Ripard
2022-06-14 16:11   ` Dave Stevenson
2022-06-10  9:28 ` [PATCH 23/64] drm/vc4: dpi: Return an error if we can't enable our clock Maxime Ripard
2022-06-14 16:14   ` Dave Stevenson
2022-06-10  9:28 ` [PATCH 24/64] drm/vc4: dpi: Remove unnecessary drm_of_panel_bridge_remove call Maxime Ripard
2022-06-14 16:37   ` Dave Stevenson
2022-06-10  9:28 ` [PATCH 25/64] drm/vc4: dpi: Add action to disable the clock Maxime Ripard
2022-06-14 16:47   ` Dave Stevenson
2022-06-16  8:38     ` Maxime Ripard
2022-06-16  9:47       ` Dave Stevenson
2022-06-20 12:21         ` Maxime Ripard
2022-06-10  9:28 ` [PATCH 26/64] drm/vc4: dpi: Switch to DRM-managed encoder initialization Maxime Ripard
2022-06-14 16:51   ` Dave Stevenson
2022-06-10  9:28 ` [PATCH 27/64] drm/vc4: dpi: Switch to drmm_of_get_bridge Maxime Ripard
2022-06-14 16:52   ` Dave Stevenson
2022-06-10  9:28 ` [PATCH 28/64] drm/vc4: dpi: Protect device resources Maxime Ripard
2022-06-14 16:55   ` Dave Stevenson
2022-06-10  9:28 ` [PATCH 29/64] drm/vc4: dsi: Embed DRM structures into the private structure Maxime Ripard
2022-06-10  9:28 ` [PATCH 30/64] drm/vc4: dsi: Switch to DRM-managed encoder initialization Maxime Ripard
2022-06-14 17:04   ` Dave Stevenson
2022-06-10  9:28 ` [PATCH 31/64] drm/vc4: dsi: Switch to drmm_of_get_bridge Maxime Ripard
2022-06-14 17:05   ` Dave Stevenson
2022-06-10  9:28 ` [PATCH 32/64] drm/vc4: dsi: Fix the driver structure lifetime Maxime Ripard
2022-06-20 10:55   ` Thomas Zimmermann
2022-06-20 10:59     ` Thomas Zimmermann
2022-06-10  9:28 ` [PATCH 33/64] drm/vc4: dsi: Switch to devm_pm_runtime_enable Maxime Ripard
2022-06-10  9:28 ` [PATCH 34/64] drm/vc4: hdmi: Switch to drmm_kzalloc Maxime Ripard
2022-06-20 11:01   ` Thomas Zimmermann
2022-06-10  9:28 ` [PATCH 35/64] drm/vc4: hdmi: Switch to DRM-managed encoder initialization Maxime Ripard
2022-06-10  9:28 ` [PATCH 36/64] drm/vc4: hdmi: Switch to DRM-managed connector initialization Maxime Ripard
2022-06-10  9:28 ` [PATCH 37/64] drm/vc4: hdmi: Switch to device-managed ALSA initialization Maxime Ripard
2022-06-10  9:28 ` [PATCH 38/64] drm/vc4: hdmi: Switch to device-managed CEC initialization Maxime Ripard
2022-06-10  9:28 ` [PATCH 39/64] drm/vc4: hdmi: Use a device-managed action for DDC Maxime Ripard
2022-06-10  9:29 ` [PATCH 40/64] drm/vc4: hdmi: Switch to DRM-managed kfree to build regsets Maxime Ripard
2022-06-10  9:29 ` [PATCH 41/64] drm/vc4: hdmi: Use devm to register hotplug interrupts Maxime Ripard
2022-06-10  9:29 ` [PATCH 42/64] drm/vc4: hdmi: Move audio structure offset checks Maxime Ripard
2022-06-10  9:29 ` [PATCH 43/64] drm/vc4: hdmi: Protect device resources after removal Maxime Ripard
2022-06-20 11:12   ` Thomas Zimmermann
2022-06-10  9:29 ` [PATCH 44/64] drm/vc4: hdmi: Switch to devm_pm_runtime_enable Maxime Ripard
2022-06-10  9:29 ` [PATCH 45/64] drm/vc4: txp: Remove vc4_dev txp pointer Maxime Ripard
2022-06-20 11:16   ` Thomas Zimmermann
2022-06-10  9:29 ` [PATCH 46/64] drm/vc4: txp: Remove duplicate regset Maxime Ripard
2022-06-20 11:16   ` Thomas Zimmermann
2022-06-10  9:29 ` [PATCH 47/64] drm/vc4: txp: Switch to drmm_kzalloc Maxime Ripard
2022-06-20 11:17   ` Thomas Zimmermann
2022-06-10  9:29 ` [PATCH 48/64] drm/vc4: txp: Switch to DRM-managed writeback initialization Maxime Ripard
2022-06-10  9:29 ` [PATCH 49/64] drm/vc4: txp: Protect device resources Maxime Ripard
2022-06-10  9:29 ` [PATCH 50/64] drm/vc4: vec: Remove vc4_dev vec pointer Maxime Ripard
2022-06-10  9:29 ` [PATCH 51/64] drm/vc4: vec: Embed DRM structures into the private structure Maxime Ripard
2022-06-10  9:29 ` [PATCH 52/64] drm/vc4: vec: Switch to drmm_kzalloc Maxime Ripard
2022-06-10  9:29 ` [PATCH 53/64] drm/vc4: vec: Switch to DRM-managed encoder initialization Maxime Ripard
2022-06-10  9:29 ` [PATCH 54/64] drm/vc4: vec: Switch to DRM-managed connector initialization Maxime Ripard
2022-06-10  9:29 ` [PATCH 55/64] drm/vc4: vec: Protect device resources after removal Maxime Ripard
2022-06-10  9:29 ` [PATCH 56/64] drm/vc4: vec: Switch to devm_pm_runtime_enable Maxime Ripard
2022-06-10  9:29 ` [PATCH 57/64] drm/vc4: debugfs: Protect device resources Maxime Ripard
2022-06-10  9:29 ` [PATCH 58/64] drm/vc4: debugfs: Return an error on failure Maxime Ripard
2022-06-10  9:29 ` [PATCH 59/64] drm/vc4: debugfs: Simplify debugfs registration Maxime Ripard
2022-06-10  9:29 ` [PATCH 60/64] drm/vc4: Switch to drmm_mutex_init Maxime Ripard
2022-06-10  9:29 ` [PATCH 61/64] drm/vc4: perfmon: Add missing mutex_destroy Maxime Ripard
2022-06-10  9:29 ` [PATCH 62/64] drm/vc4: v3d: Stop disabling interrupts Maxime Ripard
2022-06-10  9:29 ` [PATCH 63/64] drm/vc4: v3d: Rework the runtime_pm setup Maxime Ripard
2022-06-10  9:29 ` [PATCH 64/64] drm/vc4: v3d: Switch to devm_pm_runtime_enable Maxime Ripard

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.