All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4
@ 2022-06-22 14:31 Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 01/68] drm/mipi-dsi: Detach devices when removing the host Maxime Ripard
                   ` (67 more replies)
  0 siblings, 68 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  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

---

Changes from v1:
  - Rebased on top on next-20220622
  - Consolidated the new drmm init helpers with their alloc counterparts
  - Dropped the drmm writeback and simple encoder helpers
  - Clarified the documentation of drm_connector_unregister
  - Removed the drm_connector_unregister usage
  - Fixed a vblank timeout when unbinding
  - Renamed the kref functions in vc4_dsi
  - Collected the tags

Maxime Ripard (68):
  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: Clarify when drm_connector_unregister is needed
  drm/connector: Introduce drmm_connector_init
  drm/connector: Introduce drmm_connector_init_with_ddc
  drm/bridge: panel: Introduce drmm_panel_bridge_add
  drm/bridge: panel: Introduce drmm_of_get_bridge
  drm/vc4: drv: Call component_unbind_all()
  drm/vc4: drv: Use drm_dev_unplug
  drm/vc4: crtc: Create vblank reporting function
  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: crtc: Remove manual plane removal on error
  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: Remove call to drm_connector_unregister()
  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: Remove call to drm_connector_unregister()
  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: Remove call to drm_connector_unregister()
  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   | 196 +++++++--
 drivers/gpu/drm/drm_crtc.c        |  75 +++-
 drivers/gpu/drm/drm_encoder.c     |  57 ++-
 drivers/gpu/drm/drm_mipi_dsi.c    |   1 +
 drivers/gpu/drm/vc4/vc4_bo.c      |  33 +-
 drivers/gpu/drm/vc4/vc4_crtc.c    |  90 ++--
 drivers/gpu/drm/vc4/vc4_debugfs.c |  71 ++--
 drivers/gpu/drm/vc4/vc4_dpi.c     | 131 +++---
 drivers/gpu/drm/vc4/vc4_drv.c     |  21 +-
 drivers/gpu/drm/vc4/vc4_drv.h     |  47 ++-
 drivers/gpu/drm/vc4/vc4_dsi.c     | 133 ++++--
 drivers/gpu/drm/vc4/vc4_gem.c     |  10 +-
 drivers/gpu/drm/vc4/vc4_hdmi.c    | 659 ++++++++++++++++++++++--------
 drivers/gpu/drm/vc4/vc4_hdmi.h    |   3 +-
 drivers/gpu/drm/vc4/vc4_hvs.c     | 138 ++++++-
 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     |  55 ++-
 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 +
 26 files changed, 1514 insertions(+), 624 deletions(-)

-- 
2.36.1


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

* [PATCH v2 01/68] drm/mipi-dsi: Detach devices when removing the host
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 02/68] drm/crtc: Introduce drmm_crtc_init_with_planes Maxime Ripard
                   ` (66 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  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] 82+ messages in thread

* [PATCH v2 02/68] drm/crtc: Introduce drmm_crtc_init_with_planes
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 01/68] drm/mipi-dsi: Detach devices when removing the host Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 18:37   ` kernel test robot
  2022-06-28  8:54   ` Thomas Zimmermann
  2022-06-22 14:31 ` [PATCH v2 03/68] drm/encoder: Introduce drmm_encoder_init Maxime Ripard
                   ` (65 subsequent siblings)
  67 siblings, 2 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  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 | 75 ++++++++++++++++++++++++++++++++------
 include/drm/drm_crtc.h     |  6 +++
 2 files changed, 69 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 26a77a735905..6c1fb42ecf19 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,14 +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_alloc_with_planes_cleanup(struct drm_device *dev,
-						void *ptr)
+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);
+
 void *__drmm_crtc_alloc_with_planes(struct drm_device *dev,
 				    size_t size, size_t offset,
 				    struct drm_plane *primary,
@@ -398,17 +454,12 @@ void *__drmm_crtc_alloc_with_planes(struct drm_device *dev,
 	crtc = container + offset;
 
 	va_start(ap, name);
-	ret = __drm_crtc_init_with_planes(dev, crtc, primary, cursor, funcs,
-					  name, ap);
+	ret = drmm_crtc_init_with_planes(dev, crtc, primary, cursor, funcs,
+					 name, ap);
 	va_end(ap);
 	if (ret)
 		return ERR_PTR(ret);
 
-	ret = drmm_add_action_or_reset(dev, drmm_crtc_alloc_with_planes_cleanup,
-				       crtc);
-	if (ret)
-		return ERR_PTR(ret);
-
 	return container;
 }
 EXPORT_SYMBOL(__drmm_crtc_alloc_with_planes);
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] 82+ messages in thread

* [PATCH v2 03/68] drm/encoder: Introduce drmm_encoder_init
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 01/68] drm/mipi-dsi: Detach devices when removing the host Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 02/68] drm/crtc: Introduce drmm_crtc_init_with_planes Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 19:38   ` kernel test robot
  2022-06-28  8:55   ` Thomas Zimmermann
  2022-06-22 14:31 ` [PATCH v2 04/68] drm/connector: Reorder headers Maxime Ripard
                   ` (64 subsequent siblings)
  67 siblings, 2 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  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 | 57 ++++++++++++++++++++++++++++-------
 include/drm/drm_encoder.h     |  5 +++
 2 files changed, 51 insertions(+), 11 deletions(-)

diff --git a/drivers/gpu/drm/drm_encoder.c b/drivers/gpu/drm/drm_encoder.c
index a940024c8087..b29f2f07744f 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.
@@ -221,9 +221,6 @@ void *__drmm_encoder_alloc(struct drm_device *dev, size_t size, size_t offset,
 	va_list ap;
 	int ret;
 
-	if (WARN_ON(funcs && funcs->destroy))
-		return ERR_PTR(-EINVAL);
-
 	container = drmm_kzalloc(dev, size, GFP_KERNEL);
 	if (!container)
 		return ERR_PTR(-ENOMEM);
@@ -231,19 +228,57 @@ void *__drmm_encoder_alloc(struct drm_device *dev, size_t size, size_t offset,
 	encoder = container + offset;
 
 	va_start(ap, name);
-	ret = __drm_encoder_init(dev, encoder, funcs, encoder_type, name, ap);
+	ret = drmm_encoder_init(dev, encoder, funcs, encoder_type, name, ap);
 	va_end(ap);
 	if (ret)
 		return ERR_PTR(ret);
 
-	ret = drmm_add_action_or_reset(dev, drmm_encoder_alloc_release, encoder);
-	if (ret)
-		return ERR_PTR(ret);
-
 	return container;
 }
 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] 82+ messages in thread

* [PATCH v2 04/68] drm/connector: Reorder headers
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (2 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 03/68] drm/encoder: Introduce drmm_encoder_init Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-24 19:21   ` Sam Ravnborg
  2022-06-22 14:31 ` [PATCH v2 05/68] drm/connector: Mention the cleanup after drm_connector_init Maxime Ripard
                   ` (63 subsequent siblings)
  67 siblings, 1 reply; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  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] 82+ messages in thread

* [PATCH v2 05/68] drm/connector: Mention the cleanup after drm_connector_init
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (3 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 04/68] drm/connector: Reorder headers Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-24 19:25   ` Sam Ravnborg
  2022-06-22 14:31 ` [PATCH v2 06/68] drm/connector: Clarify when drm_connector_unregister is needed Maxime Ripard
                   ` (62 subsequent siblings)
  67 siblings, 1 reply; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  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 | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index 353d83ae09d3..f0c4665caf38 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -222,6 +222,10 @@ 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_cleanup() and free the connector structure.
+ * The connector structure should not be allocated with devm_kzalloc().
+ *
  * Returns:
  * Zero on success, error code on failure.
  */
@@ -345,6 +349,10 @@ 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_cleanup() and free 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] 82+ messages in thread

* [PATCH v2 06/68] drm/connector: Clarify when drm_connector_unregister is needed
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (4 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 05/68] drm/connector: Mention the cleanup after drm_connector_init Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-24 19:25   ` Sam Ravnborg
  2022-06-22 14:31 ` [PATCH v2 07/68] drm/connector: Introduce drmm_connector_init Maxime Ripard
                   ` (61 subsequent siblings)
  67 siblings, 1 reply; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  Cc: dri-devel

The current documentation for drm_connector_unregister() mentions that
it's needed for connectors that have been registered through
drm_dev_register().

However, this was a typo and was meant to be drm_connector_register(),
which only applies to connectors registered after drm_dev_register() has
been called.

In addition, it was also mentioning that connectors are unregistered
automatically when drm_dev_unregister() is called. This part is a bit
misleading, since it might make it appear that
drm_connector_unregister() applies either to all connectors, or none of
them.

After discussing it with Daniel, it appears that we always need to call
drm_connector_unregister() on connectors that have been registered with
drm_connector_register(), but only those.

drm_connector_init() already mentions that it only needs
drm_connector_cleanup(), so let's clarify the drm_connector_register()
and drm_connector_unregister() documentation to point at each other, and
remove the misleading part about drm_dev_unregister().

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

diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index f0c4665caf38..b9f22cc4ee67 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -524,6 +524,9 @@ EXPORT_SYMBOL(drm_connector_cleanup);
  * e.g. DP MST connectors. All other connectors will be registered automatically
  * when calling drm_dev_register().
  *
+ * When the connector is no longer available, callers must call
+ * drm_connector_unregister().
+ *
  * Returns:
  * Zero on success, error code on failure.
  */
@@ -580,9 +583,8 @@ EXPORT_SYMBOL(drm_connector_register);
  * @connector: the connector to unregister
  *
  * Unregister userspace interfaces for a connector. Only call this for
- * connectors which have registered explicitly by calling drm_dev_register(),
- * since connectors are unregistered automatically when drm_dev_unregister() is
- * called.
+ * connectors which have been registered explicitly by calling
+ * drm_connector_register().
  */
 void drm_connector_unregister(struct drm_connector *connector)
 {
-- 
2.36.1


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

* [PATCH v2 07/68] drm/connector: Introduce drmm_connector_init
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (5 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 06/68] drm/connector: Clarify when drm_connector_unregister is needed Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-24 19:26   ` Sam Ravnborg
  2022-06-22 14:31 ` [PATCH v2 08/68] drm/connector: Introduce drmm_connector_init_with_ddc Maxime Ripard
                   ` (60 subsequent siblings)
  67 siblings, 1 reply; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  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.

Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/drm_connector.c | 108 +++++++++++++++++++++++++-------
 include/drm/drm_connector.h     |   4 ++
 2 files changed, 91 insertions(+), 21 deletions(-)

diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index b9f22cc4ee67..0fec2d87178f 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,27 +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_cleanup() and free 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;
@@ -336,6 +320,38 @@ 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_cleanup() and free 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);
 
 /**
@@ -377,6 +393,56 @@ 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_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 with a call to
+ * drm_connector_cleanup() in a DRM-managed 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] 82+ messages in thread

* [PATCH v2 08/68] drm/connector: Introduce drmm_connector_init_with_ddc
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (6 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 07/68] drm/connector: Introduce drmm_connector_init Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-24 19:26   ` Sam Ravnborg
  2022-06-28  9:00   ` Thomas Zimmermann
  2022-06-22 14:31 ` [PATCH v2 09/68] drm/bridge: panel: Introduce drmm_panel_bridge_add Maxime Ripard
                   ` (59 subsequent siblings)
  67 siblings, 2 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  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 | 74 ++++++++++++++++++++++++++++-----
 include/drm/drm_connector.h     |  5 +++
 2 files changed, 69 insertions(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index 0fec2d87178f..076ca247c6d0 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -354,6 +354,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
@@ -371,6 +395,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.
  */
@@ -380,16 +408,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,39 @@ 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 with a call to
+ * drm_connector_cleanup() in a DRM-managed 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] 82+ messages in thread

* [PATCH v2 09/68] drm/bridge: panel: Introduce drmm_panel_bridge_add
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (7 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 08/68] drm/connector: Introduce drmm_connector_init_with_ddc Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-24 19:27   ` Sam Ravnborg
  2022-06-22 14:31 ` [PATCH v2 10/68] drm/bridge: panel: Introduce drmm_of_get_bridge Maxime Ripard
                   ` (58 subsequent siblings)
  67 siblings, 1 reply; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  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] 82+ messages in thread

* [PATCH v2 10/68] drm/bridge: panel: Introduce drmm_of_get_bridge
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (8 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 09/68] drm/bridge: panel: Introduce drmm_panel_bridge_add Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-24 19:31   ` Sam Ravnborg
  2022-06-22 14:31 ` [PATCH v2 11/68] drm/vc4: drv: Call component_unbind_all() Maxime Ripard
                   ` (57 subsequent siblings)
  67 siblings, 1 reply; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  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] 82+ messages in thread

* [PATCH v2 11/68] drm/vc4: drv: Call component_unbind_all()
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (9 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 10/68] drm/bridge: panel: Introduce drmm_of_get_bridge Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 12/68] drm/vc4: drv: Use drm_dev_unplug Maxime Ripard
                   ` (56 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  Cc: dri-devel, Dave Stevenson

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.")
Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
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 0f0f0263e744..90575171824d 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.c
+++ b/drivers/gpu/drm/vc4/vc4_drv.c
@@ -267,6 +267,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);
@@ -291,6 +298,7 @@ static int vc4_drm_bind(struct device *dev)
 	if (IS_ERR(vc4))
 		return PTR_ERR(vc4);
 	vc4->is_vc5 = is_vc5;
+	vc4->dev = dev;
 
 	drm = &vc4->base;
 	platform_set_drvdata(pdev, drm);
@@ -341,6 +349,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;
@@ -361,8 +373,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 93fd55b9e99e..c48a73914200 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.h
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
@@ -75,6 +75,7 @@ struct vc4_perfmon {
 
 struct vc4_dev {
 	struct drm_device base;
+	struct device *dev;
 
 	bool is_vc5;
 
-- 
2.36.1


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

* [PATCH v2 12/68] drm/vc4: drv: Use drm_dev_unplug
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (10 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 11/68] drm/vc4: drv: Call component_unbind_all() Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 13/68] drm/vc4: crtc: Create vblank reporting function Maxime Ripard
                   ` (55 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  Cc: dri-devel

When our KMS driver is unbound, the device is no longer there but we might
still have users with an opened fd to the KMS device.

To avoid any issue in such a situation, every device access needs to be
protected by calls to drm_dev_enter() and drm_dev_exit(), and the driver
needs to call drm_dev_unplug().

We'll add calls to drm_dev_enter()/drm_dev_exit() in subsequent patches
changing the relevant drivers, but let's start by calling drm_dev_unplug().

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

diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c
index 90575171824d..06d605a12633 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.c
+++ b/drivers/gpu/drm/vc4/vc4_drv.c
@@ -380,8 +380,7 @@ static void vc4_drm_unbind(struct device *dev)
 {
 	struct drm_device *drm = dev_get_drvdata(dev);
 
-	drm_dev_unregister(drm);
-
+	drm_dev_unplug(drm);
 	drm_atomic_helper_shutdown(drm);
 }
 
-- 
2.36.1


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

* [PATCH v2 13/68] drm/vc4: crtc: Create vblank reporting function
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (11 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 12/68] drm/vc4: drv: Use drm_dev_unplug Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 14/68] drm/vc4: hvs: Protect device resources after removal Maxime Ripard
                   ` (54 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  Cc: dri-devel

We'll need that code in the HVS driver, so let's create a shared function
to reuse it.

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

diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c
index 9355213dc883..49ff0506e551 100644
--- a/drivers/gpu/drm/vc4/vc4_crtc.c
+++ b/drivers/gpu/drm/vc4/vc4_crtc.c
@@ -539,6 +539,20 @@ int vc4_crtc_disable_at_boot(struct drm_crtc *crtc)
 	return 0;
 }
 
+void vc4_crtc_send_vblank(struct drm_crtc *crtc)
+{
+	struct drm_device *dev = crtc->dev;
+	unsigned long flags;
+
+	if (!crtc->state || !crtc->state->event)
+		return;
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+	drm_crtc_send_vblank_event(crtc, crtc->state->event);
+	crtc->state->event = NULL;
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
 static void vc4_crtc_atomic_disable(struct drm_crtc *crtc,
 				    struct drm_atomic_state *state)
 {
@@ -562,14 +576,7 @@ static void vc4_crtc_atomic_disable(struct drm_crtc *crtc,
 	 * Make sure we issue a vblank event after disabling the CRTC if
 	 * someone was waiting it.
 	 */
-	if (crtc->state->event) {
-		unsigned long flags;
-
-		spin_lock_irqsave(&dev->event_lock, flags);
-		drm_crtc_send_vblank_event(crtc, crtc->state->event);
-		crtc->state->event = NULL;
-		spin_unlock_irqrestore(&dev->event_lock, flags);
-	}
+	vc4_crtc_send_vblank(crtc);
 }
 
 static void vc4_crtc_atomic_enable(struct drm_crtc *crtc,
diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h
index c48a73914200..da52d1e0c7b5 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.h
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
@@ -861,6 +861,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);
+void vc4_crtc_send_vblank(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);
-- 
2.36.1


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

* [PATCH v2 14/68] drm/vc4: hvs: Protect device resources after removal
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (12 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 13/68] drm/vc4: crtc: Create vblank reporting function Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 15/68] drm/vc4: hvs: Remove planes currently allocated before taking down Maxime Ripard
                   ` (53 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  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.

Reviewed-by: Thomas Zimmermann <tzimmermann@suse.de>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_hvs.c | 99 ++++++++++++++++++++++++++++++++---
 1 file changed, 93 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c
index ba2c8e5a9b64..c02243bcb61f 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->vc4->base;
 	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)
@@ -135,6 +142,11 @@ static int vc4_hvs_upload_linear_kernel(struct vc4_hvs *hvs,
 	int ret, i;
 	u32 __iomem *dst_kernel;
 
+	/*
+	 * NOTE: We don't need a call to drm_dev_enter()/drm_dev_exit()
+	 * here since that function is only called from vc4_hvs_bind().
+	 */
+
 	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",
@@ -159,10 +171,15 @@ static int vc4_hvs_upload_linear_kernel(struct vc4_hvs *hvs,
 static void vc4_hvs_lut_load(struct vc4_hvs *hvs,
 			     struct vc4_crtc *vc4_crtc)
 {
+	struct drm_device *drm = &hvs->vc4->base;
 	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 +194,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 +217,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->vc4->base;
 	u8 field = 0;
+	int idx;
+
+	if (!drm_dev_enter(drm, &idx))
+		return 0;
 
 	switch (fifo) {
 	case 0:
@@ -215,6 +239,7 @@ u8 vc4_hvs_get_fifo_frame_count(struct vc4_hvs *hvs, unsigned int fifo)
 		break;
 	}
 
+	drm_dev_exit(idx);
 	return field;
 }
 
@@ -227,6 +252,12 @@ int vc4_hvs_get_fifo_from_output(struct vc4_hvs *hvs, unsigned int output)
 	if (!vc4->is_vc5)
 		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;
@@ -275,12 +306,17 @@ static int vc4_hvs_init_channel(struct vc4_hvs *hvs, struct drm_crtc *crtc,
 				struct drm_display_mode *mode, bool oneshot)
 {
 	struct vc4_dev *vc4 = hvs->vc4;
+	struct drm_device *drm = &vc4->base;
 	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);
@@ -322,13 +358,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->vc4->base;
+	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);
@@ -345,6 +389,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)
@@ -386,9 +433,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)
@@ -473,6 +526,12 @@ 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)) {
+		vc4_crtc_send_vblank(crtc);
+		return;
+	}
 
 	if (debug_dump_regs) {
 		DRM_INFO("CRTC %d HVS before:\n", drm_crtc_index(crtc));
@@ -543,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->vc4->base;
+	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->vc4->base;
+	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)
@@ -583,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);
 
@@ -615,10 +703,9 @@ 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->vc4 = vc4;
 	hvs->pdev = pdev;
 
-- 
2.36.1


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

* [PATCH v2 15/68] drm/vc4: hvs: Remove planes currently allocated before taking down
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (13 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 14/68] drm/vc4: hvs: Protect device resources after removal Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 16/68] drm/vc4: plane: Take possible_crtcs as an argument Maxime Ripard
                   ` (52 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  Cc: dri-devel, Dave Stevenson

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().

Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
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 c02243bcb61f..1994136a377e 100644
--- a/drivers/gpu/drm/vc4/vc4_hvs.c
+++ b/drivers/gpu/drm/vc4/vc4_hvs.c
@@ -831,11 +831,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] 82+ messages in thread

* [PATCH v2 16/68] drm/vc4: plane: Take possible_crtcs as an argument
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (14 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 15/68] drm/vc4: hvs: Remove planes currently allocated before taking down Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 17/68] drm/vc4: crtc: Remove manual plane removal on error Maxime Ripard
                   ` (51 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  Cc: dri-devel, Dave Stevenson

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.

Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
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 49ff0506e551..32c0dd6a7348 100644
--- a/drivers/gpu/drm/vc4/vc4_crtc.c
+++ b/drivers/gpu/drm/vc4/vc4_crtc.c
@@ -1239,7 +1239,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 da52d1e0c7b5..91983fbd4d83 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.h
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
@@ -959,7 +959,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,
+				 uint32_t 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 1e866dc00ac3..bc1f1c4172e7 100644
--- a/drivers/gpu/drm/vc4/vc4_plane.c
+++ b/drivers/gpu/drm/vc4/vc4_plane.c
@@ -1462,7 +1462,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,
+				 uint32_t possible_crtcs)
 {
 	struct vc4_dev *vc4 = to_vc4_dev(dev);
 	struct drm_plane *plane = NULL;
@@ -1493,7 +1494,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);
@@ -1541,13 +1542,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) {
@@ -1555,9 +1554,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] 82+ messages in thread

* [PATCH v2 17/68] drm/vc4: crtc: Remove manual plane removal on error
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (15 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 16/68] drm/vc4: plane: Take possible_crtcs as an argument Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 18/68] drm/vc4: plane: Switch to drmm_universal_plane_alloc() Maxime Ripard
                   ` (50 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  Cc: dri-devel

When vc4_crtc_bind() fails after vc4_crtc_init() has been called, we have
a loop undoing the plane creation and calling destroy on each plane
registered and matching the possible_crtcs mask.

However, this is redundant with what drm_mode_config_cleanup() is doing, so
let's remove it.

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

diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c
index 32c0dd6a7348..e559fdb217a5 100644
--- a/drivers/gpu/drm/vc4/vc4_crtc.c
+++ b/drivers/gpu/drm/vc4/vc4_crtc.c
@@ -1312,7 +1312,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);
 
@@ -1320,15 +1320,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,
-- 
2.36.1


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

* [PATCH v2 18/68] drm/vc4: plane: Switch to drmm_universal_plane_alloc()
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (16 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 17/68] drm/vc4: crtc: Remove manual plane removal on error Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 19/68] drm/vc4: crtc: Move debugfs_name to crtc_data Maxime Ripard
                   ` (49 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  Cc: dri-devel, Dave Stevenson

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

Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_crtc.c  |  1 -
 drivers/gpu/drm/vc4/vc4_plane.c | 23 ++++++++---------------
 2 files changed, 8 insertions(+), 16 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c
index e559fdb217a5..84fd25e71c41 100644
--- a/drivers/gpu/drm/vc4/vc4_crtc.c
+++ b/drivers/gpu/drm/vc4/vc4_crtc.c
@@ -1277,7 +1277,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);
diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c
index bc1f1c4172e7..813ed4c05b29 100644
--- a/drivers/gpu/drm/vc4/vc4_plane.c
+++ b/drivers/gpu/drm/vc4/vc4_plane.c
@@ -1453,8 +1453,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,
@@ -1466,11 +1464,10 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev,
 				 uint32_t possible_crtcs)
 {
 	struct vc4_dev *vc4 = to_vc4_dev(dev);
-	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;
 	static const uint64_t modifiers[] = {
 		DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED,
@@ -1481,11 +1478,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 || vc4->is_vc5) {
 			formats[num_formats] = hvs_formats[i].drm;
@@ -1493,13 +1485,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);
 
 	if (vc4->is_vc5)
 		drm_plane_helper_add(plane, &vc5_plane_helper_funcs);
-- 
2.36.1


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

* [PATCH v2 19/68] drm/vc4: crtc: Move debugfs_name to crtc_data
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (17 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 18/68] drm/vc4: plane: Switch to drmm_universal_plane_alloc() Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 20/68] drm/vc4: crtc: Switch to drmm_kzalloc Maxime Ripard
                   ` (48 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  Cc: dri-devel, Dave Stevenson

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

Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
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 84fd25e71c41..188cf25125f0 100644
--- a/drivers/gpu/drm/vc4/vc4_crtc.c
+++ b/drivers/gpu/drm/vc4/vc4_crtc.c
@@ -1079,10 +1079,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 = {
@@ -1093,10 +1093,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 = {
@@ -1107,10 +1107,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 = {
@@ -1121,10 +1121,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 = {
@@ -1135,10 +1135,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 = {
@@ -1149,10 +1149,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 = {
@@ -1162,10 +1162,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 = {
@@ -1175,10 +1175,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 = {
@@ -1315,7 +1315,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 91983fbd4d83..df4ff6747eeb 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.h
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
@@ -458,6 +458,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;
 
@@ -475,8 +477,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 3579d487402e..4c2f0862fc88 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] 82+ messages in thread

* [PATCH v2 20/68] drm/vc4: crtc: Switch to drmm_kzalloc
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (18 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 19/68] drm/vc4: crtc: Move debugfs_name to crtc_data Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 21/68] drm/vc4: crtc: Switch to DRM-managed CRTC initialization Maxime Ripard
                   ` (47 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  Cc: dri-devel, Dave Stevenson

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 likely 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.

Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
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 188cf25125f0..dadf962b0bb5 100644
--- a/drivers/gpu/drm/vc4/vc4_crtc.c
+++ b/drivers/gpu/drm/vc4/vc4_crtc.c
@@ -1279,7 +1279,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] 82+ messages in thread

* [PATCH v2 21/68] drm/vc4: crtc: Switch to DRM-managed CRTC initialization
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (19 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 20/68] drm/vc4: crtc: Switch to drmm_kzalloc Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 22/68] drm/vc4: dpi: Remove vc4_dev dpi pointer Maxime Ripard
                   ` (46 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  Cc: dri-devel, Dave Stevenson

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.

Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_crtc.c | 16 ++++++----------
 drivers/gpu/drm/vc4/vc4_drv.h  |  1 -
 drivers/gpu/drm/vc4/vc4_txp.c  |  1 -
 3 files changed, 6 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c
index dadf962b0bb5..851e4a470939 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);
@@ -1054,7 +1049,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 */
@@ -1232,6 +1226,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,
@@ -1246,8 +1241,11 @@ int vc4_crtc_init(struct drm_device *drm, struct vc4_crtc *vc4_crtc,
 	}
 
 	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);
 
 	if (!vc4->is_vc5) {
@@ -1327,8 +1325,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 df4ff6747eeb..6afa873f0def 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.h
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
@@ -850,7 +850,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 4c2f0862fc88..949db41c50fb 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] 82+ messages in thread

* [PATCH v2 22/68] drm/vc4: dpi: Remove vc4_dev dpi pointer
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (20 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 21/68] drm/vc4: crtc: Switch to DRM-managed CRTC initialization Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 23/68] drm/vc4: dpi: Embed DRM structures into the private structure Maxime Ripard
                   ` (45 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  Cc: dri-devel, Dave Stevenson

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

Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
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 6afa873f0def..db51dd3e20b8 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.h
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
@@ -83,7 +83,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] 82+ messages in thread

* [PATCH v2 23/68] drm/vc4: dpi: Embed DRM structures into the private structure
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (21 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 22/68] drm/vc4: dpi: Remove vc4_dev dpi pointer Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 24/68] drm/vc4: dpi: Switch to drmm_kzalloc Maxime Ripard
                   ` (44 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  Cc: dri-devel, Dave Stevenson

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.

Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
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] 82+ messages in thread

* [PATCH v2 24/68] drm/vc4: dpi: Switch to drmm_kzalloc
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (22 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 23/68] drm/vc4: dpi: Embed DRM structures into the private structure Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 25/68] drm/vc4: dpi: Return an error if we can't enable our clock Maxime Ripard
                   ` (43 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  Cc: dri-devel, Dave Stevenson

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 likely 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.

Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
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] 82+ messages in thread

* [PATCH v2 25/68] drm/vc4: dpi: Return an error if we can't enable our clock
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (23 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 24/68] drm/vc4: dpi: Switch to drmm_kzalloc Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 26/68] drm/vc4: dpi: Remove unnecessary drm_of_panel_bridge_remove call Maxime Ripard
                   ` (42 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  Cc: dri-devel, Dave Stevenson

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

Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
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] 82+ messages in thread

* [PATCH v2 26/68] drm/vc4: dpi: Remove unnecessary drm_of_panel_bridge_remove call
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (24 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 25/68] drm/vc4: dpi: Return an error if we can't enable our clock Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 27/68] drm/vc4: dpi: Add action to disable the clock Maxime Ripard
                   ` (41 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  Cc: dri-devel, Dave Stevenson

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.

Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
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] 82+ messages in thread

* [PATCH v2 27/68] drm/vc4: dpi: Add action to disable the clock
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (25 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 26/68] drm/vc4: dpi: Remove unnecessary drm_of_panel_bridge_remove call Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 28/68] drm/vc4: dpi: Switch to DRM-managed encoder initialization Maxime Ripard
                   ` (40 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  Cc: dri-devel, Dave Stevenson

The DPI controller has two clocks called core and pixel, the core clock
being enabled at bind time.

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

Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
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] 82+ messages in thread

* [PATCH v2 28/68] drm/vc4: dpi: Switch to DRM-managed encoder initialization
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (26 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 27/68] drm/vc4: dpi: Add action to disable the clock Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 29/68] drm/vc4: dpi: Switch to drmm_of_get_bridge Maxime Ripard
                   ` (39 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  Cc: dri-devel, Dave Stevenson

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.

Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_dpi.c | 23 ++++++++---------------
 1 file changed, 8 insertions(+), 15 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c
index 4e24dbad77f2..233bc5d3b02f 100644
--- a/drivers/gpu/drm/vc4/vc4_dpi.c
+++ b/drivers/gpu/drm/vc4/vc4_dpi.c
@@ -296,35 +296,28 @@ 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_encoder_init(drm, &dpi->encoder.base,
+				NULL,
+				DRM_MODE_ENCODER_DPI,
+				NULL);
+	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] 82+ messages in thread

* [PATCH v2 29/68] drm/vc4: dpi: Switch to drmm_of_get_bridge
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (27 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 28/68] drm/vc4: dpi: Switch to DRM-managed encoder initialization Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 30/68] drm/vc4: dpi: Protect device resources Maxime Ripard
                   ` (38 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  Cc: dri-devel, Dave Stevenson

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.

Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
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 233bc5d3b02f..71bb51e1d703 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] 82+ messages in thread

* [PATCH v2 30/68] drm/vc4: dpi: Protect device resources
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (28 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 29/68] drm/vc4: dpi: Switch to drmm_of_get_bridge Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 31/68] drm/vc4: dsi: Embed DRM structures into the private structure Maxime Ripard
                   ` (37 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  Cc: dri-devel, Dave Stevenson

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.

Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
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 71bb51e1d703..75b4ea3ab7e3 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] 82+ messages in thread

* [PATCH v2 31/68] drm/vc4: dsi: Embed DRM structures into the private structure
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (29 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 30/68] drm/vc4: dpi: Protect device resources Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 32/68] drm/vc4: dsi: Switch to DRM-managed encoder initialization Maxime Ripard
                   ` (36 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  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] 82+ messages in thread

* [PATCH v2 32/68] drm/vc4: dsi: Switch to DRM-managed encoder initialization
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (30 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 31/68] drm/vc4: dsi: Embed DRM structures into the private structure Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 33/68] drm/vc4: dsi: Switch to drmm_of_get_bridge Maxime Ripard
                   ` (35 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  Cc: dri-devel, Dave Stevenson

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.

Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_dsi.c | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
index dbb3f6fb39b4..f3214675f8b3 100644
--- a/drivers/gpu/drm/vc4/vc4_dsi.c
+++ b/drivers/gpu/drm/vc4/vc4_dsi.c
@@ -1599,7 +1599,13 @@ 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_encoder_init(drm, encoder,
+				NULL,
+				DRM_MODE_ENCODER_DSI,
+				NULL);
+	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 +1638,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] 82+ messages in thread

* [PATCH v2 33/68] drm/vc4: dsi: Switch to drmm_of_get_bridge
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (31 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 32/68] drm/vc4: dsi: Switch to DRM-managed encoder initialization Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 34/68] drm/vc4: dsi: Fix the driver structure lifetime Maxime Ripard
                   ` (34 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  Cc: dri-devel, Dave Stevenson

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.

Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
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 f3214675f8b3..b3c97aa11e7e 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] 82+ messages in thread

* [PATCH v2 34/68] drm/vc4: dsi: Fix the driver structure lifetime
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (32 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 33/68] drm/vc4: dsi: Switch to drmm_of_get_bridge Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 35/68] drm/vc4: dsi: Switch to devm_pm_runtime_enable Maxime Ripard
                   ` (33 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  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 | 40 ++++++++++++++++++++++++++++++++++-
 1 file changed, 39 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
index b3c97aa11e7e..86d53d50ad9d 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,25 @@ vc4_dsi_init_phy_clocks(struct vc4_dsi *dsi)
 				      dsi->clk_onecell);
 }
 
+static void vc4_dsi_get(struct vc4_dsi *dsi)
+{
+	kref_get(&dsi->kref);
+}
+
+static void vc4_dsi_release(struct kref *kref);
+
+static void vc4_dsi_put(struct vc4_dsi *dsi)
+{
+	kref_put(&dsi->kref, &vc4_dsi_release);
+}
+
+static void vc4_dsi_release_action(struct drm_device *drm, void *ptr)
+{
+	struct vc4_dsi *dsi = ptr;
+
+	vc4_dsi_put(dsi);
+}
+
 static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
 {
 	struct platform_device *pdev = to_platform_device(dev);
@@ -1488,6 +1509,12 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
 	dma_cap_mask_t dma_mask;
 	int ret;
 
+	vc4_dsi_get(dsi);
+
+	ret = drmm_add_action_or_reset(drm, vc4_dsi_release_action, dsi);
+	if (ret)
+		return ret;
+
 	dsi->variant = of_device_get_match_data(dev);
 
 	INIT_LIST_HEAD(&dsi->bridge_chain);
@@ -1645,16 +1672,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;
@@ -1669,6 +1705,8 @@ 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);
+	vc4_dsi_put(dsi);
+
 	return 0;
 }
 
-- 
2.36.1


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

* [PATCH v2 35/68] drm/vc4: dsi: Switch to devm_pm_runtime_enable
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (33 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 34/68] drm/vc4: dsi: Fix the driver structure lifetime Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 36/68] drm/vc4: hdmi: Switch to drmm_kzalloc Maxime Ripard
                   ` (32 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  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 86d53d50ad9d..1157bf549633 100644
--- a/drivers/gpu/drm/vc4/vc4_dsi.c
+++ b/drivers/gpu/drm/vc4/vc4_dsi.c
@@ -1635,6 +1635,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;
@@ -1647,8 +1651,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;
 }
 
@@ -1658,8 +1660,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] 82+ messages in thread

* [PATCH v2 36/68] drm/vc4: hdmi: Switch to drmm_kzalloc
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (34 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 35/68] drm/vc4: dsi: Switch to devm_pm_runtime_enable Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 37/68] drm/vc4: hdmi: Remove call to drm_connector_unregister() Maxime Ripard
                   ` (31 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  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 likely 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 823d812f4982..fd7bfcd1696f 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -2834,9 +2834,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] 82+ messages in thread

* [PATCH v2 37/68] drm/vc4: hdmi: Remove call to drm_connector_unregister()
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (35 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 36/68] drm/vc4: hdmi: Switch to drmm_kzalloc Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 38/68] drm/vc4: hdmi: Switch to DRM-managed encoder initialization Maxime Ripard
                   ` (30 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  Cc: dri-devel

drm_connector_unregister() is only to be used for connectors that have been
registered through drm_connector_register() after drm_dev_register() has
been called. This is our case here so let's remove the call.

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

diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index fd7bfcd1696f..7d08be14b075 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -256,12 +256,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);
@@ -369,7 +363,7 @@ 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,
+	.destroy = drm_connector_cleanup,
 	.reset = vc4_hdmi_connector_reset,
 	.atomic_duplicate_state = vc4_hdmi_connector_duplicate_state,
 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
@@ -2954,7 +2948,7 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
 err_free_hotplug:
 	vc4_hdmi_hotplug_exit(vc4_hdmi);
 err_destroy_conn:
-	vc4_hdmi_connector_destroy(&vc4_hdmi->connector);
+	drm_connector_cleanup(&vc4_hdmi->connector);
 err_destroy_encoder:
 	drm_encoder_cleanup(encoder);
 	pm_runtime_put_sync(dev);
@@ -2997,7 +2991,7 @@ 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);
+	drm_connector_cleanup(&vc4_hdmi->connector);
 	drm_encoder_cleanup(&vc4_hdmi->encoder.base);
 
 	pm_runtime_disable(dev);
-- 
2.36.1


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

* [PATCH v2 38/68] drm/vc4: hdmi: Switch to DRM-managed encoder initialization
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (36 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 37/68] drm/vc4: hdmi: Remove call to drm_connector_unregister() Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 39/68] drm/vc4: hdmi: Switch to DRM-managed connector initialization Maxime Ripard
                   ` (29 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  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 | 14 +++++++++-----
 1 file changed, 9 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index 7d08be14b075..592f85993a80 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -2916,12 +2916,18 @@ 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_encoder_init(drm, encoder,
+				NULL,
+				DRM_MODE_ENCODER_TMDS,
+				NULL);
+	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)
@@ -2949,8 +2955,7 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
 	vc4_hdmi_hotplug_exit(vc4_hdmi);
 err_destroy_conn:
 	drm_connector_cleanup(&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:
@@ -2992,7 +2997,6 @@ static void vc4_hdmi_unbind(struct device *dev, struct device *master,
 	vc4_hdmi_cec_exit(vc4_hdmi);
 	vc4_hdmi_hotplug_exit(vc4_hdmi);
 	drm_connector_cleanup(&vc4_hdmi->connector);
-	drm_encoder_cleanup(&vc4_hdmi->encoder.base);
 
 	pm_runtime_disable(dev);
 
-- 
2.36.1


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

* [PATCH v2 39/68] drm/vc4: hdmi: Switch to DRM-managed connector initialization
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (37 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 38/68] drm/vc4: hdmi: Switch to DRM-managed encoder initialization Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 40/68] drm/vc4: hdmi: Switch to device-managed ALSA initialization Maxime Ripard
                   ` (28 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  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 | 17 ++++++++---------
 1 file changed, 8 insertions(+), 9 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index 592f85993a80..2d1f87f89994 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -363,7 +363,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 = drm_connector_cleanup,
 	.reset = vc4_hdmi_connector_reset,
 	.atomic_duplicate_state = vc4_hdmi_connector_duplicate_state,
 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
@@ -381,10 +380,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);
 
 	/*
@@ -2931,7 +2933,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)
@@ -2953,8 +2955,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:
-	drm_connector_cleanup(&vc4_hdmi->connector);
 err_put_runtime_pm:
 	pm_runtime_put_sync(dev);
 	pm_runtime_disable(dev);
@@ -2996,7 +2996,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);
-	drm_connector_cleanup(&vc4_hdmi->connector);
 
 	pm_runtime_disable(dev);
 
-- 
2.36.1


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

* [PATCH v2 40/68] drm/vc4: hdmi: Switch to device-managed ALSA initialization
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (38 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 39/68] drm/vc4: hdmi: Switch to DRM-managed connector initialization Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 41/68] drm/vc4: hdmi: Switch to device-managed CEC initialization Maxime Ripard
                   ` (27 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  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 2d1f87f89994..2de7532b6c78 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -2022,6 +2022,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 =
@@ -2063,6 +2071,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);
@@ -2086,6 +2118,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;
@@ -2124,12 +2160,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;
@@ -2993,7 +3023,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] 82+ messages in thread

* [PATCH v2 41/68] drm/vc4: hdmi: Switch to device-managed CEC initialization
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (39 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 40/68] drm/vc4: hdmi: Switch to device-managed ALSA initialization Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 42/68] drm/vc4: hdmi: Use a device-managed action for DDC Maxime Ripard
                   ` (26 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  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 2de7532b6c78..36ded0f56548 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -2542,6 +2542,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;
@@ -2577,75 +2585,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,
@@ -2971,7 +2979,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,
@@ -2981,8 +2989,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:
@@ -3023,7 +3029,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] 82+ messages in thread

* [PATCH v2 42/68] drm/vc4: hdmi: Use a device-managed action for DDC
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (40 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 41/68] drm/vc4: hdmi: Switch to device-managed CEC initialization Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 43/68] drm/vc4: hdmi: Switch to DRM-managed kfree to build regsets Maxime Ripard
                   ` (25 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  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 36ded0f56548..350220786bef 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -2858,6 +2858,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);
@@ -2913,13 +2920,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 =
@@ -2939,7 +2949,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);
@@ -2994,8 +3004,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;
 }
@@ -3032,8 +3040,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] 82+ messages in thread

* [PATCH v2 43/68] drm/vc4: hdmi: Switch to DRM-managed kfree to build regsets
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (41 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 42/68] drm/vc4: hdmi: Use a device-managed action for DDC Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 44/68] drm/vc4: hdmi: Use devm to register hotplug interrupts Maxime Ripard
                   ` (24 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  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 | 30 +++++++++++++++++++++---------
 drivers/gpu/drm/vc4/vc4_hdmi.h |  3 ++-
 2 files changed, 23 insertions(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index 350220786bef..1febab3b52c6 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -2656,7 +2656,15 @@ static int vc4_hdmi_cec_init(struct vc4_hdmi *vc4_hdmi)
 }
 #endif
 
-static int vc4_hdmi_build_regset(struct vc4_hdmi *vc4_hdmi,
+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 drm_device *drm,
+				 struct vc4_hdmi *vc4_hdmi,
 				 struct debugfs_regset32 *regset,
 				 enum vc4_hdmi_regs reg)
 {
@@ -2664,6 +2672,7 @@ static int vc4_hdmi_build_regset(struct vc4_hdmi *vc4_hdmi,
 	struct debugfs_reg32 *regs, *new_regs;
 	unsigned int count = 0;
 	unsigned int i;
+	int ret;
 
 	regs = kcalloc(variant->num_registers, sizeof(*regs),
 		       GFP_KERNEL);
@@ -2689,10 +2698,15 @@ 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;
 }
 
-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;
@@ -2706,11 +2720,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;
 
@@ -2733,7 +2747,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;
@@ -2903,7 +2918,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;
 
@@ -3034,9 +3049,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);
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);
-- 
2.36.1


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

* [PATCH v2 44/68] drm/vc4: hdmi: Use devm to register hotplug interrupts
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (42 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 43/68] drm/vc4: hdmi: Switch to DRM-managed kfree to build regsets Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 45/68] drm/vc4: hdmi: Move audio structure offset checks Maxime Ripard
                   ` (23 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  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 1febab3b52c6..cba51827b621 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -2182,21 +2182,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;
 	}
@@ -2204,16 +2202,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)
 {
@@ -3000,11 +2988,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,
@@ -3014,8 +3002,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);
@@ -3026,8 +3012,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
@@ -3047,9 +3031,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] 82+ messages in thread

* [PATCH v2 45/68] drm/vc4: hdmi: Move audio structure offset checks
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (43 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 44/68] drm/vc4: hdmi: Use devm to register hotplug interrupts Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 46/68] drm/vc4: hdmi: Protect device resources after removal Maxime Ripard
                   ` (22 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  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 cba51827b621..1729da01f8b1 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -2042,6 +2042,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");
@@ -3012,26 +3032,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] 82+ messages in thread

* [PATCH v2 46/68] drm/vc4: hdmi: Protect device resources after removal
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (44 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 45/68] drm/vc4: hdmi: Move audio structure offset checks Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 47/68] drm/vc4: hdmi: Switch to devm_pm_runtime_enable Maxime Ripard
                   ` (21 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  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.

Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
---
 drivers/gpu/drm/vc4/vc4_hdmi.c | 308 +++++++++++++++++++++++++++++++--
 1 file changed, 291 insertions(+), 17 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index 1729da01f8b1..e6678a668ff8 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>
@@ -141,17 +142,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);
 
@@ -168,11 +185,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);
 
@@ -184,15 +213,31 @@ 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;
+
+	/*
+	 * This function is called by our runtime_resume implementation
+	 * and thus at bind time, when we haven't registered our
+	 * connector yet and thus don't have a pointer to the DRM
+	 * device.
+	 */
+	if (drm && !drm_dev_enter(drm, &idx))
+		return;
+
+	cec_rate = clk_get_rate(vc4_hdmi->cec_clock);
 
 	spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
 
@@ -208,6 +253,9 @@ 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);
+
+	if (drm)
+		drm_dev_exit(idx);
 }
 #else
 static void vc4_hdmi_cec_update_clk_div(struct vc4_hdmi *vc4_hdmi) {}
@@ -428,25 +476,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];
@@ -457,6 +514,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),
@@ -464,12 +525,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);
@@ -498,6 +559,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,
@@ -645,8 +709,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);
 
@@ -658,6 +724,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);
 
@@ -666,6 +735,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,
@@ -675,7 +746,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);
 
@@ -687,6 +760,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);
@@ -694,6 +770,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)
@@ -716,10 +794,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);
@@ -737,6 +820,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);
 }
 
@@ -744,11 +830,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);
@@ -764,6 +855,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);
 }
 
@@ -780,8 +874,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);
 
@@ -816,6 +915,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);
 }
 
 /*
@@ -900,6 +1001,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;
@@ -908,6 +1010,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);
 
@@ -950,12 +1056,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;
@@ -974,6 +1083,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);
 
@@ -1001,12 +1114,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;
@@ -1030,6 +1146,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);
 
@@ -1101,13 +1221,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);
 
@@ -1136,12 +1263,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);
@@ -1152,9 +1282,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
@@ -1175,13 +1309,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);
@@ -1233,6 +1367,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;
@@ -1241,6 +1377,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;
@@ -1250,14 +1388,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);
 
@@ -1265,6 +1408,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);
 }
 
@@ -1272,15 +1417,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,
@@ -1341,6 +1491,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);
 }
 
@@ -1676,13 +1827,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);
 }
 
@@ -1690,10 +1848,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,
@@ -1706,6 +1870,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)
@@ -1765,13 +1931,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;
@@ -1788,9 +1962,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)
@@ -1819,10 +1996,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,
@@ -1838,6 +2020,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);
 }
 
@@ -1885,6 +2070,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;
@@ -1893,15 +2079,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);
@@ -1958,9 +2151,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 = {
@@ -2295,6 +2491,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);
@@ -2323,6 +2530,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);
@@ -2354,6 +2572,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;
 
@@ -2374,11 +2603,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
@@ -2391,9 +2622,19 @@ 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))
+		/*
+		 * We can't return an error code, because the CEC
+		 * framework will emit WARN_ON messages at unbind
+		 * otherwise.
+		 */
+		return 0;
+
 	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);
 
@@ -2429,13 +2670,25 @@ 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))
+		/*
+		 * We can't return an error code, because the CEC
+		 * framework will emit WARN_ON messages at unbind
+		 * otherwise.
+		 */
+		return 0;
 
 	/*
 	 * NOTE: This function should really take vc4_hdmi->mutex, but doing so
@@ -2460,6 +2713,8 @@ static int vc4_hdmi_cec_disable(struct cec_adapter *adap)
 
 	pm_runtime_put(&vc4_hdmi->pdev->dev);
 
+	drm_dev_exit(idx);
+
 	return 0;
 }
 
@@ -2474,7 +2729,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
@@ -2487,12 +2744,22 @@ 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))
+		/*
+		 * We can't return an error code, because the CEC
+		 * framework will emit WARN_ON messages at unbind
+		 * otherwise.
+		 */
+		return 0;
+
 	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;
 }
 
@@ -2504,6 +2771,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
@@ -2516,8 +2784,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;
 	}
 
@@ -2541,6 +2813,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] 82+ messages in thread

* [PATCH v2 47/68] drm/vc4: hdmi: Switch to devm_pm_runtime_enable
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (45 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 46/68] drm/vc4: hdmi: Protect device resources after removal Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 48/68] drm/vc4: txp: Remove vc4_dev txp pointer Maxime Ripard
                   ` (20 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  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 e6678a668ff8..85686a8eb49e 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -3250,7 +3250,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);
@@ -3298,20 +3303,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] 82+ messages in thread

* [PATCH v2 48/68] drm/vc4: txp: Remove vc4_dev txp pointer
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (46 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 47/68] drm/vc4: hdmi: Switch to devm_pm_runtime_enable Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 49/68] drm/vc4: txp: Remove duplicate regset Maxime Ripard
                   ` (19 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  Cc: dri-devel

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

Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
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 db51dd3e20b8..9c5b31fa4b9c 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.h
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
@@ -84,7 +84,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 949db41c50fb..0e868aebc25f 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;
@@ -522,7 +521,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);
 
@@ -532,13 +530,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] 82+ messages in thread

* [PATCH v2 49/68] drm/vc4: txp: Remove duplicate regset
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (47 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 48/68] drm/vc4: txp: Remove vc4_dev txp pointer Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 50/68] drm/vc4: txp: Switch to drmm_kzalloc Maxime Ripard
                   ` (18 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  Cc: dri-devel

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

Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
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 0e868aebc25f..10862a850d6e 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);
@@ -522,7 +521,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] 82+ messages in thread

* [PATCH v2 50/68] drm/vc4: txp: Switch to drmm_kzalloc
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (48 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 49/68] drm/vc4: txp: Remove duplicate regset Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 51/68] drm/vc4: txp: Remove call to drm_connector_unregister() Maxime Ripard
                   ` (17 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  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 likely 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.

Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
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 10862a850d6e..93576b2d9ba6 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] 82+ messages in thread

* [PATCH v2 51/68] drm/vc4: txp: Remove call to drm_connector_unregister()
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (49 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 50/68] drm/vc4: txp: Switch to drmm_kzalloc Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 52/68] drm/vc4: txp: Protect device resources Maxime Ripard
                   ` (16 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  Cc: dri-devel

drm_connector_unregister() is only to be used for connectors that have been
registered through drm_connector_register() after drm_dev_register() has
been called. This is our case here so let's remove the call.

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

diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c
index 93576b2d9ba6..0976d29adc7f 100644
--- a/drivers/gpu/drm/vc4/vc4_txp.c
+++ b/drivers/gpu/drm/vc4/vc4_txp.c
@@ -335,16 +335,10 @@ 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,
+	.destroy = drm_connector_cleanup,
 	.reset = drm_atomic_helper_connector_reset,
 	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
@@ -531,7 +525,7 @@ static void vc4_txp_unbind(struct device *dev, struct device *master,
 {
 	struct vc4_txp *txp = dev_get_drvdata(dev);
 
-	vc4_txp_connector_destroy(&txp->connector.base);
+	drm_connector_cleanup(&txp->connector.base);
 }
 
 static const struct component_ops vc4_txp_ops = {
-- 
2.36.1


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

* [PATCH v2 52/68] drm/vc4: txp: Protect device resources
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (50 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 51/68] drm/vc4: txp: Remove call to drm_connector_unregister() Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 53/68] drm/vc4: vec: Remove vc4_dev vec pointer Maxime Ripard
                   ` (15 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  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 0976d29adc7f..0748e3f6d40f 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 = {
@@ -346,7 +354,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);
@@ -361,6 +374,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 = {
@@ -444,6 +459,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] 82+ messages in thread

* [PATCH v2 53/68] drm/vc4: vec: Remove vc4_dev vec pointer
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (51 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 52/68] drm/vc4: txp: Protect device resources Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 54/68] drm/vc4: vec: Embed DRM structures into the private structure Maxime Ripard
                   ` (14 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  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 9c5b31fa4b9c..ce12d7ec9c6e 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.h
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
@@ -83,7 +83,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] 82+ messages in thread

* [PATCH v2 54/68] drm/vc4: vec: Embed DRM structures into the private structure
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (52 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 53/68] drm/vc4: vec: Remove vc4_dev vec pointer Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 55/68] drm/vc4: vec: Switch to drmm_kzalloc Maxime Ripard
                   ` (13 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  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] 82+ messages in thread

* [PATCH v2 55/68] drm/vc4: vec: Switch to drmm_kzalloc
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (53 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 54/68] drm/vc4: vec: Embed DRM structures into the private structure Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 56/68] drm/vc4: vec: Remove call to drm_connector_unregister() Maxime Ripard
                   ` (12 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  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 likely 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] 82+ messages in thread

* [PATCH v2 56/68] drm/vc4: vec: Remove call to drm_connector_unregister()
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (54 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 55/68] drm/vc4: vec: Switch to drmm_kzalloc Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 57/68] drm/vc4: vec: Switch to DRM-managed encoder initialization Maxime Ripard
                   ` (11 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  Cc: dri-devel

drm_connector_unregister() is only to be used for connectors that have been
registered through drm_connector_register() after drm_dev_register() has
been called. This is our case here so let's remove the call.

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

diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c
index a051b25337c0..2e9dc09fc6f0 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,7 @@ 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,
+	.destroy = drm_connector_cleanup,
 	.reset = drm_atomic_helper_connector_reset,
 	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
@@ -559,7 +553,7 @@ 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_connector_cleanup(&vec->connector);
 	drm_encoder_cleanup(&vec->encoder.base);
 	pm_runtime_disable(dev);
 }
-- 
2.36.1


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

* [PATCH v2 57/68] drm/vc4: vec: Switch to DRM-managed encoder initialization
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (55 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 56/68] drm/vc4: vec: Remove call to drm_connector_unregister() Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:31 ` [PATCH v2 58/68] drm/vc4: vec: Switch to DRM-managed connector initialization Maxime Ripard
                   ` (10 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  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 | 14 +++++++++-----
 1 file changed, 9 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c
index 2e9dc09fc6f0..518b66bcc0b5 100644
--- a/drivers/gpu/drm/vc4/vc4_vec.c
+++ b/drivers/gpu/drm/vc4/vc4_vec.c
@@ -528,12 +528,18 @@ 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_encoder_init(drm, &vec->encoder.base,
+				NULL,
+				DRM_MODE_ENCODER_TVDAC,
+				NULL);
+	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);
 
@@ -541,8 +547,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;
@@ -554,7 +559,6 @@ static void vc4_vec_unbind(struct device *dev, struct device *master,
 	struct vc4_vec *vec = dev_get_drvdata(dev);
 
 	drm_connector_cleanup(&vec->connector);
-	drm_encoder_cleanup(&vec->encoder.base);
 	pm_runtime_disable(dev);
 }
 
-- 
2.36.1


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

* [PATCH v2 58/68] drm/vc4: vec: Switch to DRM-managed connector initialization
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (56 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 57/68] drm/vc4: vec: Switch to DRM-managed encoder initialization Maxime Ripard
@ 2022-06-22 14:31 ` Maxime Ripard
  2022-06-22 14:32 ` [PATCH v2 59/68] drm/vc4: vec: Protect device resources after removal Maxime Ripard
                   ` (9 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:31 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  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 | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c
index 518b66bcc0b5..f6858e40ddbc 100644
--- a/drivers/gpu/drm/vc4/vc4_vec.c
+++ b/drivers/gpu/drm/vc4/vc4_vec.c
@@ -309,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 = drm_connector_cleanup,
 	.reset = drm_atomic_helper_connector_reset,
 	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
@@ -322,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,
@@ -556,9 +558,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);
-
-	drm_connector_cleanup(&vec->connector);
 	pm_runtime_disable(dev);
 }
 
-- 
2.36.1


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

* [PATCH v2 59/68] drm/vc4: vec: Protect device resources after removal
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (57 preceding siblings ...)
  2022-06-22 14:31 ` [PATCH v2 58/68] drm/vc4: vec: Switch to DRM-managed connector initialization Maxime Ripard
@ 2022-06-22 14:32 ` Maxime Ripard
  2022-06-22 14:32 ` [PATCH v2 60/68] drm/vc4: vec: Switch to devm_pm_runtime_enable Maxime Ripard
                   ` (8 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:32 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  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 f6858e40ddbc..b2a15c39939e 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] 82+ messages in thread

* [PATCH v2 60/68] drm/vc4: vec: Switch to devm_pm_runtime_enable
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (58 preceding siblings ...)
  2022-06-22 14:32 ` [PATCH v2 59/68] drm/vc4: vec: Protect device resources after removal Maxime Ripard
@ 2022-06-22 14:32 ` Maxime Ripard
  2022-06-22 14:32 ` [PATCH v2 61/68] drm/vc4: debugfs: Protect device resources Maxime Ripard
                   ` (7 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:32 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  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 b2a15c39939e..7e02fbad3208 100644
--- a/drivers/gpu/drm/vc4/vc4_vec.c
+++ b/drivers/gpu/drm/vc4/vc4_vec.c
@@ -583,42 +583,32 @@ 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_encoder_init(drm, &vec->encoder.base,
 				NULL,
 				DRM_MODE_ENCODER_TVDAC,
 				NULL);
 	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] 82+ messages in thread

* [PATCH v2 61/68] drm/vc4: debugfs: Protect device resources
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (59 preceding siblings ...)
  2022-06-22 14:32 ` [PATCH v2 60/68] drm/vc4: vec: Switch to devm_pm_runtime_enable Maxime Ripard
@ 2022-06-22 14:32 ` Maxime Ripard
  2022-06-22 14:32 ` [PATCH v2 62/68] drm/vc4: debugfs: Return an error on failure Maxime Ripard
                   ` (6 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:32 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  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] 82+ messages in thread

* [PATCH v2 62/68] drm/vc4: debugfs: Return an error on failure
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (60 preceding siblings ...)
  2022-06-22 14:32 ` [PATCH v2 61/68] drm/vc4: debugfs: Protect device resources Maxime Ripard
@ 2022-06-22 14:32 ` Maxime Ripard
  2022-06-22 14:32 ` [PATCH v2 63/68] drm/vc4: debugfs: Simplify debugfs registration Maxime Ripard
                   ` (5 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:32 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  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 ce12d7ec9c6e..e029dbd4567b 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.h
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
@@ -865,25 +865,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] 82+ messages in thread

* [PATCH v2 63/68] drm/vc4: debugfs: Simplify debugfs registration
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (61 preceding siblings ...)
  2022-06-22 14:32 ` [PATCH v2 62/68] drm/vc4: debugfs: Return an error on failure Maxime Ripard
@ 2022-06-22 14:32 ` Maxime Ripard
  2022-06-22 14:32 ` [PATCH v2 64/68] drm/vc4: Switch to drmm_mutex_init Maxime Ripard
                   ` (4 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:32 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  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     | 21 +++++++++++--
 drivers/gpu/drm/vc4/vc4_drv.h     | 12 +++++---
 drivers/gpu/drm/vc4/vc4_dsi.c     | 22 ++++++++++++--
 drivers/gpu/drm/vc4/vc4_hdmi.c    | 26 ++++++++++++----
 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     | 22 ++++++++++++--
 11 files changed, 185 insertions(+), 67 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c
index b8d856312846..19807e4e0c55 100644
--- a/drivers/gpu/drm/vc4/vc4_bo.c
+++ b/drivers/gpu/drm/vc4/vc4_bo.c
@@ -982,6 +982,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)
 {
@@ -1006,9 +1023,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 851e4a470939..59bdb308252a 100644
--- a/drivers/gpu/drm/vc4/vc4_crtc.c
+++ b/drivers/gpu/drm/vc4/vc4_crtc.c
@@ -1047,6 +1047,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,
@@ -1059,6 +1074,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 = {
@@ -1313,9 +1329,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 75b4ea3ab7e3..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 },
 	{}
@@ -312,7 +329,7 @@ static int vc4_dpi_bind(struct device *dev, struct device *master, void *data)
 		return ret;
 
 	ret = drmm_encoder_init(drm, &dpi->encoder.base,
-				NULL,
+				&vc4_dpi_encoder_funcs,
 				DRM_MODE_ENCODER_DPI,
 				NULL);
 	if (ret)
@@ -326,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 e029dbd4567b..b7e3821100b5 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.h
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
@@ -840,6 +840,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;
@@ -858,6 +859,7 @@ void vc4_crtc_destroy_state(struct drm_crtc *crtc,
 void vc4_crtc_reset(struct drm_crtc *crtc);
 void vc4_crtc_handle_vblank(struct vc4_crtc *crtc);
 void vc4_crtc_send_vblank(struct drm_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);
@@ -865,15 +867,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)
@@ -881,7 +883,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)
 {
@@ -951,6 +953,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);
@@ -973,6 +976,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 1157bf549633..7aa9b47b2eb2 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",
@@ -1627,7 +1645,7 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
 		return ret;
 
 	ret = drmm_encoder_init(drm, encoder,
-				NULL,
+				&vc4_dsi_encoder_funcs,
 				DRM_MODE_ENCODER_DSI,
 				NULL);
 	if (ret)
@@ -1649,8 +1667,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 85686a8eb49e..ef46dc34316a 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -1801,6 +1801,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;
@@ -3269,7 +3289,7 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
 	}
 
 	ret = drmm_encoder_init(drm, encoder,
-				NULL,
+				&vc4_hdmi_encoder_funcs,
 				DRM_MODE_ENCODER_TMDS,
 				NULL);
 	if (ret)
@@ -3293,10 +3313,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_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c
index 1994136a377e..4454009ef09f 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 (!vc4->is_vc5)
+		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);
@@ -818,10 +846,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 0748e3f6d40f..7a4f0d8c1549 100644
--- a/drivers/gpu/drm/vc4/vc4_txp.c
+++ b/drivers/gpu/drm/vc4/vc4_txp.c
@@ -397,6 +397,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,
@@ -540,8 +541,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 cc714dcfe1f2..e7990bed0a96 100644
--- a/drivers/gpu/drm/vc4/vc4_v3d.c
+++ b/drivers/gpu/drm/vc4/vc4_v3d.c
@@ -401,6 +401,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);
@@ -477,9 +499,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 7e02fbad3208..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) |
@@ -588,7 +606,7 @@ static int vc4_vec_bind(struct device *dev, struct device *master, void *data)
 		return ret;
 
 	ret = drmm_encoder_init(drm, &vec->encoder.base,
-				NULL,
+				&vc4_vec_encoder_funcs,
 				DRM_MODE_ENCODER_TVDAC,
 				NULL);
 	if (ret)
@@ -602,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] 82+ messages in thread

* [PATCH v2 64/68] drm/vc4: Switch to drmm_mutex_init
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (62 preceding siblings ...)
  2022-06-22 14:32 ` [PATCH v2 63/68] drm/vc4: debugfs: Simplify debugfs registration Maxime Ripard
@ 2022-06-22 14:32 ` Maxime Ripard
  2022-06-22 14:32 ` [PATCH v2 65/68] drm/vc4: perfmon: Add missing mutex_destroy Maxime Ripard
                   ` (3 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:32 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  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 19807e4e0c55..312a1a936caf 100644
--- a/drivers/gpu/drm/vc4/vc4_bo.c
+++ b/drivers/gpu/drm/vc4/vc4_bo.c
@@ -392,6 +392,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;
 
 	if (WARN_ON_ONCE(vc4->is_vc5))
 		return ERR_PTR(-ENODEV);
@@ -402,7 +403,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++;
@@ -1003,6 +1008,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;
 
 	if (WARN_ON_ONCE(vc4->is_vc5))
@@ -1022,7 +1028,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 06d605a12633..c85f86a87bda 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.c
+++ b/drivers/gpu/drm/vc4/vc4_drv.c
@@ -305,7 +305,9 @@ static int vc4_drm_bind(struct device *dev)
 	INIT_LIST_HEAD(&vc4->debugfs_list);
 
 	if (!is_vc5) {
-		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 fe10d9c3fff8..7acb43972e69 100644
--- a/drivers/gpu/drm/vc4/vc4_gem.c
+++ b/drivers/gpu/drm/vc4/vc4_gem.c
@@ -1308,6 +1308,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;
 
 	if (WARN_ON_ONCE(vc4->is_vc5))
 		return -ENODEV;
@@ -1325,10 +1326,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 ef46dc34316a..8f6531e2a500 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -3196,7 +3196,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] 82+ messages in thread

* [PATCH v2 65/68] drm/vc4: perfmon: Add missing mutex_destroy
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (63 preceding siblings ...)
  2022-06-22 14:32 ` [PATCH v2 64/68] drm/vc4: Switch to drmm_mutex_init Maxime Ripard
@ 2022-06-22 14:32 ` Maxime Ripard
  2022-06-22 14:32 ` [PATCH v2 66/68] drm/vc4: v3d: Stop disabling interrupts Maxime Ripard
                   ` (2 subsequent siblings)
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:32 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  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 c7f5adb6bcf8..da2d2cee5da2 100644
--- a/drivers/gpu/drm/vc4/vc4_perfmon.c
+++ b/drivers/gpu/drm/vc4/vc4_perfmon.c
@@ -130,6 +130,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] 82+ messages in thread

* [PATCH v2 66/68] drm/vc4: v3d: Stop disabling interrupts
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (64 preceding siblings ...)
  2022-06-22 14:32 ` [PATCH v2 65/68] drm/vc4: perfmon: Add missing mutex_destroy Maxime Ripard
@ 2022-06-22 14:32 ` Maxime Ripard
  2022-06-22 14:32 ` [PATCH v2 67/68] drm/vc4: v3d: Rework the runtime_pm setup Maxime Ripard
  2022-06-22 14:32 ` [PATCH v2 68/68] drm/vc4: v3d: Switch to devm_pm_runtime_enable Maxime Ripard
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:32 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  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 2eacfb6773d2..a9fc63d9a7f0 100644
--- a/drivers/gpu/drm/vc4/vc4_irq.c
+++ b/drivers/gpu/drm/vc4/vc4_irq.c
@@ -295,7 +295,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 e7990bed0a96..a2da0db73a5c 100644
--- a/drivers/gpu/drm/vc4/vc4_v3d.c
+++ b/drivers/gpu/drm/vc4/vc4_v3d.c
@@ -393,8 +393,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] 82+ messages in thread

* [PATCH v2 67/68] drm/vc4: v3d: Rework the runtime_pm setup
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (65 preceding siblings ...)
  2022-06-22 14:32 ` [PATCH v2 66/68] drm/vc4: v3d: Stop disabling interrupts Maxime Ripard
@ 2022-06-22 14:32 ` Maxime Ripard
  2022-06-22 14:32 ` [PATCH v2 68/68] drm/vc4: v3d: Switch to devm_pm_runtime_enable Maxime Ripard
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:32 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  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 a2da0db73a5c..d82c86865079 100644
--- a/drivers/gpu/drm/vc4/vc4_v3d.c
+++ b/drivers/gpu/drm/vc4/vc4_v3d.c
@@ -463,41 +463,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] 82+ messages in thread

* [PATCH v2 68/68] drm/vc4: v3d: Switch to devm_pm_runtime_enable
  2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
                   ` (66 preceding siblings ...)
  2022-06-22 14:32 ` [PATCH v2 67/68] drm/vc4: v3d: Rework the runtime_pm setup Maxime Ripard
@ 2022-06-22 14:32 ` Maxime Ripard
  67 siblings, 0 replies; 82+ messages in thread
From: Maxime Ripard @ 2022-06-22 14:32 UTC (permalink / raw)
  To: Maarten Lankhorst, Thomas Zimmermann, Maxime Ripard,
	Daniel Vetter, David Airlie
  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 d82c86865079..40f04157ea39 100644
--- a/drivers/gpu/drm/vc4/vc4_v3d.c
+++ b/drivers/gpu/drm/vc4/vc4_v3d.c
@@ -468,11 +468,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",
@@ -501,9 +503,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;
 }
 
@@ -513,8 +512,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] 82+ messages in thread

* Re: [PATCH v2 02/68] drm/crtc: Introduce drmm_crtc_init_with_planes
  2022-06-22 14:31 ` [PATCH v2 02/68] drm/crtc: Introduce drmm_crtc_init_with_planes Maxime Ripard
@ 2022-06-22 18:37   ` kernel test robot
  2022-06-28  8:54   ` Thomas Zimmermann
  1 sibling, 0 replies; 82+ messages in thread
From: kernel test robot @ 2022-06-22 18:37 UTC (permalink / raw)
  To: Maxime Ripard, Maarten Lankhorst, Thomas Zimmermann,
	Daniel Vetter, David Airlie
  Cc: kbuild-all, dri-devel

Hi Maxime,

I love your patch! Perhaps something to improve:

[auto build test WARNING on next-20220622]
[also build test WARNING on v5.19-rc3]
[cannot apply to drm-misc/drm-misc-next drm-intel/for-linux-next drm-tip/drm-tip linus/master anholt/for-next v5.19-rc3 v5.19-rc2 v5.19-rc1]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/intel-lab-lkp/linux/commits/Maxime-Ripard/drm-vc4-Fix-hotplug-for-vc4/20220622-223842
base:    ac0ba5454ca85162c08dc429fef1999e077ca976
config: riscv-rv32_defconfig (https://download.01.org/0day-ci/archive/20220623/202206230238.D3tMKlmQ-lkp@intel.com/config)
compiler: riscv32-linux-gcc (GCC) 11.3.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/46edccc8b6046ecee2de71b23c941dc23514f522
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Maxime-Ripard/drm-vc4-Fix-hotplug-for-vc4/20220622-223842
        git checkout 46edccc8b6046ecee2de71b23c941dc23514f522
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.3.0 make.cross W=1 O=build_dir ARCH=riscv SHELL=/bin/bash drivers/gpu/drm/

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

   drivers/gpu/drm/drm_crtc.c: In function 'drmm_crtc_init_with_planes':
>> drivers/gpu/drm/drm_crtc.c:421:43: warning: function 'drmm_crtc_init_with_planes' might be a candidate for 'gnu_printf' format attribute [-Wsuggest-attribute=format]
     421 |                                           name, ap);
         |                                           ^~~~


vim +421 drivers/gpu/drm/drm_crtc.c

   379	
   380	/**
   381	 * drmm_crtc_init_with_planes - Initialise a new CRTC object with
   382	 *    specified primary and cursor planes.
   383	 * @dev: DRM device
   384	 * @crtc: CRTC object to init
   385	 * @primary: Primary plane for CRTC
   386	 * @cursor: Cursor plane for CRTC
   387	 * @funcs: callbacks for the new CRTC
   388	 * @name: printf style format string for the CRTC name, or NULL for default name
   389	 *
   390	 * Inits a new object created as base part of a driver crtc object. Drivers
   391	 * should use this function instead of drm_crtc_init(), which is only provided
   392	 * for backwards compatibility with drivers which do not yet support universal
   393	 * planes). For really simple hardware which has only 1 plane look at
   394	 * drm_simple_display_pipe_init() instead.
   395	 *
   396	 * Cleanup is automatically handled through registering
   397	 * drmm_crtc_cleanup() with drmm_add_action(). The crtc structure should
   398	 * be allocated with drmm_kzalloc().
   399	 *
   400	 * The @drm_crtc_funcs.destroy hook must be NULL.
   401	 *
   402	 * The @primary and @cursor planes are only relevant for legacy uAPI, see
   403	 * &drm_crtc.primary and &drm_crtc.cursor.
   404	 *
   405	 * Returns:
   406	 * Zero on success, error code on failure.
   407	 */
   408	int drmm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
   409				       struct drm_plane *primary,
   410				       struct drm_plane *cursor,
   411				       const struct drm_crtc_funcs *funcs,
   412				       const char *name, ...)
   413	{
   414		va_list ap;
   415		int ret;
   416	
   417		WARN_ON(funcs && funcs->destroy);
   418	
   419		va_start(ap, name);
   420		ret = __drm_crtc_init_with_planes(dev, crtc, primary, cursor, funcs,
 > 421						  name, ap);
   422		va_end(ap);
   423		if (ret)
   424			return ret;
   425	
   426		ret = drmm_add_action_or_reset(dev, drmm_crtc_init_with_planes_cleanup,
   427					       crtc);
   428		if (ret)
   429			return ret;
   430	
   431		return 0;
   432	}
   433	EXPORT_SYMBOL(drmm_crtc_init_with_planes);
   434	

-- 
0-DAY CI Kernel Test Service
https://01.org/lkp

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

* Re: [PATCH v2 03/68] drm/encoder: Introduce drmm_encoder_init
  2022-06-22 14:31 ` [PATCH v2 03/68] drm/encoder: Introduce drmm_encoder_init Maxime Ripard
@ 2022-06-22 19:38   ` kernel test robot
  2022-06-28  8:55   ` Thomas Zimmermann
  1 sibling, 0 replies; 82+ messages in thread
From: kernel test robot @ 2022-06-22 19:38 UTC (permalink / raw)
  To: Maxime Ripard, Maarten Lankhorst, Thomas Zimmermann,
	Daniel Vetter, David Airlie
  Cc: kbuild-all, dri-devel

Hi Maxime,

I love your patch! Perhaps something to improve:

[auto build test WARNING on next-20220622]
[also build test WARNING on v5.19-rc3]
[cannot apply to drm-misc/drm-misc-next drm-intel/for-linux-next drm-tip/drm-tip linus/master anholt/for-next v5.19-rc3 v5.19-rc2 v5.19-rc1]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/intel-lab-lkp/linux/commits/Maxime-Ripard/drm-vc4-Fix-hotplug-for-vc4/20220622-223842
base:    ac0ba5454ca85162c08dc429fef1999e077ca976
config: riscv-rv32_defconfig (https://download.01.org/0day-ci/archive/20220623/202206230352.n3jM0UCD-lkp@intel.com/config)
compiler: riscv32-linux-gcc (GCC) 11.3.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/241f292ab7ccd70b2f6259d1155de8d1bfdd5c9c
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Maxime-Ripard/drm-vc4-Fix-hotplug-for-vc4/20220622-223842
        git checkout 241f292ab7ccd70b2f6259d1155de8d1bfdd5c9c
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.3.0 make.cross W=1 O=build_dir ARCH=riscv SHELL=/bin/bash drivers/gpu/drm/

If you fix the issue, kindly add following tag where applicable
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

   drivers/gpu/drm/drm_encoder.c: In function 'drmm_encoder_init':
>> drivers/gpu/drm/drm_encoder.c:269:9: warning: function 'drmm_encoder_init' might be a candidate for 'gnu_printf' format attribute [-Wsuggest-attribute=format]
     269 |         ret = __drm_encoder_init(dev, encoder, funcs, encoder_type, name, ap);
         |         ^~~


vim +269 drivers/gpu/drm/drm_encoder.c

   239	
   240	/**
   241	 * drmm_encoder_init - Initialize a preallocated encoder
   242	 * @dev: drm device
   243	 * @encoder: the encoder to init
   244	 * @funcs: callbacks for this encoder (optional)
   245	 * @encoder_type: user visible type of the encoder
   246	 * @name: printf style format string for the encoder name, or NULL for default name
   247	 *
   248	 * Initializes a preallocated encoder. Encoder should be subclassed as
   249	 * part of driver encoder objects. Cleanup is automatically handled
   250	 * through registering drm_encoder_cleanup() with drmm_add_action(). The
   251	 * encoder structure should be allocated with drmm_kzalloc().
   252	 *
   253	 * The @drm_encoder_funcs.destroy hook must be NULL.
   254	 *
   255	 * Returns:
   256	 * Zero on success, error code on failure.
   257	 */
   258	int drmm_encoder_init(struct drm_device *dev, struct drm_encoder *encoder,
   259			      const struct drm_encoder_funcs *funcs,
   260			      int encoder_type, const char *name, ...)
   261	{
   262		va_list ap;
   263		int ret;
   264	
   265		if (WARN_ON(funcs && funcs->destroy))
   266			return -EINVAL;
   267	
   268		va_start(ap, name);
 > 269		ret = __drm_encoder_init(dev, encoder, funcs, encoder_type, name, ap);
   270		va_end(ap);
   271		if (ret)
   272			return ret;
   273	
   274		ret = drmm_add_action_or_reset(dev, drmm_encoder_alloc_release, encoder);
   275		if (ret)
   276			return ret;
   277	
   278		return 0;
   279	}
   280	EXPORT_SYMBOL(drmm_encoder_init);
   281	

-- 
0-DAY CI Kernel Test Service
https://01.org/lkp

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

* Re: [PATCH v2 04/68] drm/connector: Reorder headers
  2022-06-22 14:31 ` [PATCH v2 04/68] drm/connector: Reorder headers Maxime Ripard
@ 2022-06-24 19:21   ` Sam Ravnborg
  0 siblings, 0 replies; 82+ messages in thread
From: Sam Ravnborg @ 2022-06-24 19:21 UTC (permalink / raw)
  To: Maxime Ripard; +Cc: David Airlie, Daniel Vetter, dri-devel, Thomas Zimmermann

On Wed, Jun 22, 2022 at 04:31:05PM +0200, Maxime Ripard wrote:
> 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>
Acked-by: Sam Ravnborg <sam@ravnborg.org>
> ---
>  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	[flat|nested] 82+ messages in thread

* Re: [PATCH v2 05/68] drm/connector: Mention the cleanup after drm_connector_init
  2022-06-22 14:31 ` [PATCH v2 05/68] drm/connector: Mention the cleanup after drm_connector_init Maxime Ripard
@ 2022-06-24 19:25   ` Sam Ravnborg
  0 siblings, 0 replies; 82+ messages in thread
From: Sam Ravnborg @ 2022-06-24 19:25 UTC (permalink / raw)
  To: Maxime Ripard; +Cc: David Airlie, Daniel Vetter, dri-devel, Thomas Zimmermann

On Wed, Jun 22, 2022 at 04:31:06PM +0200, Maxime Ripard wrote:
> 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>
Looks sensible,
Acked-by: Sam Ravnborg <sam@ravnborg.org>
> ---
>  drivers/gpu/drm/drm_connector.c | 8 ++++++++
>  1 file changed, 8 insertions(+)
> 
> diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
> index 353d83ae09d3..f0c4665caf38 100644
> --- a/drivers/gpu/drm/drm_connector.c
> +++ b/drivers/gpu/drm/drm_connector.c
> @@ -222,6 +222,10 @@ 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_cleanup() and free the connector structure.
> + * The connector structure should not be allocated with devm_kzalloc().
> + *
>   * Returns:
>   * Zero on success, error code on failure.
>   */
> @@ -345,6 +349,10 @@ 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_cleanup() and free 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	[flat|nested] 82+ messages in thread

* Re: [PATCH v2 06/68] drm/connector: Clarify when drm_connector_unregister is needed
  2022-06-22 14:31 ` [PATCH v2 06/68] drm/connector: Clarify when drm_connector_unregister is needed Maxime Ripard
@ 2022-06-24 19:25   ` Sam Ravnborg
  0 siblings, 0 replies; 82+ messages in thread
From: Sam Ravnborg @ 2022-06-24 19:25 UTC (permalink / raw)
  To: Maxime Ripard; +Cc: David Airlie, Daniel Vetter, dri-devel, Thomas Zimmermann

On Wed, Jun 22, 2022 at 04:31:07PM +0200, Maxime Ripard wrote:
> The current documentation for drm_connector_unregister() mentions that
> it's needed for connectors that have been registered through
> drm_dev_register().
> 
> However, this was a typo and was meant to be drm_connector_register(),
> which only applies to connectors registered after drm_dev_register() has
> been called.
> 
> In addition, it was also mentioning that connectors are unregistered
> automatically when drm_dev_unregister() is called. This part is a bit
> misleading, since it might make it appear that
> drm_connector_unregister() applies either to all connectors, or none of
> them.
> 
> After discussing it with Daniel, it appears that we always need to call
> drm_connector_unregister() on connectors that have been registered with
> drm_connector_register(), but only those.
> 
> drm_connector_init() already mentions that it only needs
> drm_connector_cleanup(), so let's clarify the drm_connector_register()
> and drm_connector_unregister() documentation to point at each other, and
> remove the misleading part about drm_dev_unregister().
> 
> Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Sam Ravnborg <sam@ravnborg.org>

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

* Re: [PATCH v2 07/68] drm/connector: Introduce drmm_connector_init
  2022-06-22 14:31 ` [PATCH v2 07/68] drm/connector: Introduce drmm_connector_init Maxime Ripard
@ 2022-06-24 19:26   ` Sam Ravnborg
  0 siblings, 0 replies; 82+ messages in thread
From: Sam Ravnborg @ 2022-06-24 19:26 UTC (permalink / raw)
  To: Maxime Ripard; +Cc: David Airlie, Daniel Vetter, dri-devel, Thomas Zimmermann

On Wed, Jun 22, 2022 at 04:31:08PM +0200, Maxime Ripard wrote:
> 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.
> 
> Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
> Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Sam Ravnborg <sam@ravnborg.org>

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

* Re: [PATCH v2 08/68] drm/connector: Introduce drmm_connector_init_with_ddc
  2022-06-22 14:31 ` [PATCH v2 08/68] drm/connector: Introduce drmm_connector_init_with_ddc Maxime Ripard
@ 2022-06-24 19:26   ` Sam Ravnborg
  2022-06-28  9:00   ` Thomas Zimmermann
  1 sibling, 0 replies; 82+ messages in thread
From: Sam Ravnborg @ 2022-06-24 19:26 UTC (permalink / raw)
  To: Maxime Ripard; +Cc: David Airlie, Daniel Vetter, dri-devel, Thomas Zimmermann

On Wed, Jun 22, 2022 at 04:31:09PM +0200, Maxime Ripard wrote:
> 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>
Acked-by: Sam Ravnborg <sam@ravnborg.org>

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

* Re: [PATCH v2 09/68] drm/bridge: panel: Introduce drmm_panel_bridge_add
  2022-06-22 14:31 ` [PATCH v2 09/68] drm/bridge: panel: Introduce drmm_panel_bridge_add Maxime Ripard
@ 2022-06-24 19:27   ` Sam Ravnborg
  0 siblings, 0 replies; 82+ messages in thread
From: Sam Ravnborg @ 2022-06-24 19:27 UTC (permalink / raw)
  To: Maxime Ripard; +Cc: David Airlie, Daniel Vetter, dri-devel, Thomas Zimmermann

On Wed, Jun 22, 2022 at 04:31:10PM +0200, Maxime Ripard wrote:
> 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>
Acked-by: Sam Ravnborg <sam@ravnborg.org>

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

* Re: [PATCH v2 10/68] drm/bridge: panel: Introduce drmm_of_get_bridge
  2022-06-22 14:31 ` [PATCH v2 10/68] drm/bridge: panel: Introduce drmm_of_get_bridge Maxime Ripard
@ 2022-06-24 19:31   ` Sam Ravnborg
  0 siblings, 0 replies; 82+ messages in thread
From: Sam Ravnborg @ 2022-06-24 19:31 UTC (permalink / raw)
  To: Maxime Ripard; +Cc: David Airlie, Daniel Vetter, dri-devel, Thomas Zimmermann

On Wed, Jun 22, 2022 at 04:31:11PM +0200, Maxime Ripard wrote:
> 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.
Maybe extend this text to:

	Returns a drmm managed pointer to the bridge if successful, or an error
	pointer otherwise.

To tell the reader that there is no need for any cleanup.

With this or something similar added:
Acked-by: Sam Ravnborg <sam@ravnborg.org>

	Sam

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

* Re: [PATCH v2 02/68] drm/crtc: Introduce drmm_crtc_init_with_planes
  2022-06-22 14:31 ` [PATCH v2 02/68] drm/crtc: Introduce drmm_crtc_init_with_planes Maxime Ripard
  2022-06-22 18:37   ` kernel test robot
@ 2022-06-28  8:54   ` Thomas Zimmermann
  1 sibling, 0 replies; 82+ messages in thread
From: Thomas Zimmermann @ 2022-06-28  8:54 UTC (permalink / raw)
  To: Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie; +Cc: dri-devel


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

Hi

Am 22.06.22 um 16:31 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.
> 
> Signed-off-by: Maxime Ripard <maxime@cerno.tech>
> ---
>   drivers/gpu/drm/drm_crtc.c | 75 ++++++++++++++++++++++++++++++++------
>   include/drm/drm_crtc.h     |  6 +++
>   2 files changed, 69 insertions(+), 12 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
> index 26a77a735905..6c1fb42ecf19 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,14 +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_alloc_with_planes_cleanup(struct drm_device *dev,
> -						void *ptr)
> +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);
> +
>   void *__drmm_crtc_alloc_with_planes(struct drm_device *dev,
>   				    size_t size, size_t offset,
>   				    struct drm_plane *primary,
> @@ -398,17 +454,12 @@ void *__drmm_crtc_alloc_with_planes(struct drm_device *dev,
>   	crtc = container + offset;
>   
>   	va_start(ap, name);
> -	ret = __drm_crtc_init_with_planes(dev, crtc, primary, cursor, funcs,
> -					  name, ap);
> +	ret = drmm_crtc_init_with_planes(dev, crtc, primary, cursor, funcs,
> +					 name, ap);

You cannot pass ap as final argument here. There needs to be an internal 
helper, say __drmm_crtc_init_with_planes(), that specifically takes the 
va pointer.

The rest of the code looks good.

Best regards
Thomas

>   	va_end(ap);
>   	if (ret)
>   		return ERR_PTR(ret);
>   
> -	ret = drmm_add_action_or_reset(dev, drmm_crtc_alloc_with_planes_cleanup,
> -				       crtc);
> -	if (ret)
> -		return ERR_PTR(ret);
> -
>   	return container;
>   }
>   EXPORT_SYMBOL(__drmm_crtc_alloc_with_planes);
> 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] 82+ messages in thread

* Re: [PATCH v2 03/68] drm/encoder: Introduce drmm_encoder_init
  2022-06-22 14:31 ` [PATCH v2 03/68] drm/encoder: Introduce drmm_encoder_init Maxime Ripard
  2022-06-22 19:38   ` kernel test robot
@ 2022-06-28  8:55   ` Thomas Zimmermann
  1 sibling, 0 replies; 82+ messages in thread
From: Thomas Zimmermann @ 2022-06-28  8:55 UTC (permalink / raw)
  To: Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie; +Cc: dri-devel


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

Hi

Am 22.06.22 um 16:31 schrieb Maxime Ripard:
> 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 | 57 ++++++++++++++++++++++++++++-------
>   include/drm/drm_encoder.h     |  5 +++
>   2 files changed, 51 insertions(+), 11 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_encoder.c b/drivers/gpu/drm/drm_encoder.c
> index a940024c8087..b29f2f07744f 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.
> @@ -221,9 +221,6 @@ void *__drmm_encoder_alloc(struct drm_device *dev, size_t size, size_t offset,
>   	va_list ap;
>   	int ret;
>   
> -	if (WARN_ON(funcs && funcs->destroy))
> -		return ERR_PTR(-EINVAL);
> -
>   	container = drmm_kzalloc(dev, size, GFP_KERNEL);
>   	if (!container)
>   		return ERR_PTR(-ENOMEM);
> @@ -231,19 +228,57 @@ void *__drmm_encoder_alloc(struct drm_device *dev, size_t size, size_t offset,
>   	encoder = container + offset;
>   
>   	va_start(ap, name);
> -	ret = __drm_encoder_init(dev, encoder, funcs, encoder_type, name, ap);
> +	ret = drmm_encoder_init(dev, encoder, funcs, encoder_type, name, ap);

That's the same problem with ap as in the CRTC case. I guess other 
patches are also affected.

Best regards
Thomas

>   	va_end(ap);
>   	if (ret)
>   		return ERR_PTR(ret);
>   
> -	ret = drmm_add_action_or_reset(dev, drmm_encoder_alloc_release, encoder);
> -	if (ret)
> -		return ERR_PTR(ret);
> -
>   	return container;
>   }
>   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,

-- 
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] 82+ messages in thread

* Re: [PATCH v2 08/68] drm/connector: Introduce drmm_connector_init_with_ddc
  2022-06-22 14:31 ` [PATCH v2 08/68] drm/connector: Introduce drmm_connector_init_with_ddc Maxime Ripard
  2022-06-24 19:26   ` Sam Ravnborg
@ 2022-06-28  9:00   ` Thomas Zimmermann
  2022-06-28 14:05     ` Jani Nikula
  1 sibling, 1 reply; 82+ messages in thread
From: Thomas Zimmermann @ 2022-06-28  9:00 UTC (permalink / raw)
  To: Maxime Ripard, Maarten Lankhorst, Daniel Vetter, David Airlie; +Cc: dri-devel


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

Hi

Am 22.06.22 um 16:31 schrieb Maxime Ripard:
> 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 | 74 ++++++++++++++++++++++++++++-----
>   include/drm/drm_connector.h     |  5 +++
>   2 files changed, 69 insertions(+), 10 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
> index 0fec2d87178f..076ca247c6d0 100644
> --- a/drivers/gpu/drm/drm_connector.c
> +++ b/drivers/gpu/drm/drm_connector.c
> @@ -354,6 +354,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)

Back in the days when drm_connector_init_with_ddc() was added, there was 
a small controversy about whether we should simply extend the regular 
drm_connector_init() to support the extra ddc argument. That would have 
meant a lot of churn, but the idea was probably sound.

Maybe it would be better to provide a single function 
drmm_connector_init() that receives a ddc argument. If the argument is 
NULL, no DDC channel would be set. This would make 
drmm_connector_init_with_ddc() unnnecessary.

Best regards
Thomas

> +{
> +	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
> @@ -371,6 +395,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.
>    */
> @@ -380,16 +408,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,39 @@ 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 with a call to
> + * drm_connector_cleanup() in a DRM-managed 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);

-- 
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] 82+ messages in thread

* Re: [PATCH v2 08/68] drm/connector: Introduce drmm_connector_init_with_ddc
  2022-06-28  9:00   ` Thomas Zimmermann
@ 2022-06-28 14:05     ` Jani Nikula
  0 siblings, 0 replies; 82+ messages in thread
From: Jani Nikula @ 2022-06-28 14:05 UTC (permalink / raw)
  To: Thomas Zimmermann, Maxime Ripard, Maarten Lankhorst,
	Daniel Vetter, David Airlie
  Cc: dri-devel

On Tue, 28 Jun 2022, Thomas Zimmermann <tzimmermann@suse.de> wrote:
> Hi
>
> Am 22.06.22 um 16:31 schrieb Maxime Ripard:
>> 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 | 74 ++++++++++++++++++++++++++++-----
>>   include/drm/drm_connector.h     |  5 +++
>>   2 files changed, 69 insertions(+), 10 deletions(-)
>> 
>> diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
>> index 0fec2d87178f..076ca247c6d0 100644
>> --- a/drivers/gpu/drm/drm_connector.c
>> +++ b/drivers/gpu/drm/drm_connector.c
>> @@ -354,6 +354,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)
>
> Back in the days when drm_connector_init_with_ddc() was added, there was 
> a small controversy about whether we should simply extend the regular 
> drm_connector_init() to support the extra ddc argument. That would have 
> meant a lot of churn, but the idea was probably sound.
>
> Maybe it would be better to provide a single function 
> drmm_connector_init() that receives a ddc argument. If the argument is 
> NULL, no DDC channel would be set. This would make 
> drmm_connector_init_with_ddc() unnnecessary.

FWIW I really dislike the "_with_ddc" variant as a function name. Like,
what if you add another thing you'd need to pass in at init. Add
functions "_with_ddc_and_foo" and "_with_foo", and so on.

I'd take the churn to convert to drm_connector_init() and
drmm_connector_init() only.

BR,
Jani.


>
> Best regards
> Thomas
>
>> +{
>> +	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
>> @@ -371,6 +395,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.
>>    */
>> @@ -380,16 +408,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,39 @@ 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 with a call to
>> + * drm_connector_cleanup() in a DRM-managed 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);

-- 
Jani Nikula, Intel Open Source Graphics Center

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

end of thread, other threads:[~2022-06-28 14:05 UTC | newest]

Thread overview: 82+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-06-22 14:31 [PATCH v2 00/68] drm/vc4: Fix hotplug for vc4 Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 01/68] drm/mipi-dsi: Detach devices when removing the host Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 02/68] drm/crtc: Introduce drmm_crtc_init_with_planes Maxime Ripard
2022-06-22 18:37   ` kernel test robot
2022-06-28  8:54   ` Thomas Zimmermann
2022-06-22 14:31 ` [PATCH v2 03/68] drm/encoder: Introduce drmm_encoder_init Maxime Ripard
2022-06-22 19:38   ` kernel test robot
2022-06-28  8:55   ` Thomas Zimmermann
2022-06-22 14:31 ` [PATCH v2 04/68] drm/connector: Reorder headers Maxime Ripard
2022-06-24 19:21   ` Sam Ravnborg
2022-06-22 14:31 ` [PATCH v2 05/68] drm/connector: Mention the cleanup after drm_connector_init Maxime Ripard
2022-06-24 19:25   ` Sam Ravnborg
2022-06-22 14:31 ` [PATCH v2 06/68] drm/connector: Clarify when drm_connector_unregister is needed Maxime Ripard
2022-06-24 19:25   ` Sam Ravnborg
2022-06-22 14:31 ` [PATCH v2 07/68] drm/connector: Introduce drmm_connector_init Maxime Ripard
2022-06-24 19:26   ` Sam Ravnborg
2022-06-22 14:31 ` [PATCH v2 08/68] drm/connector: Introduce drmm_connector_init_with_ddc Maxime Ripard
2022-06-24 19:26   ` Sam Ravnborg
2022-06-28  9:00   ` Thomas Zimmermann
2022-06-28 14:05     ` Jani Nikula
2022-06-22 14:31 ` [PATCH v2 09/68] drm/bridge: panel: Introduce drmm_panel_bridge_add Maxime Ripard
2022-06-24 19:27   ` Sam Ravnborg
2022-06-22 14:31 ` [PATCH v2 10/68] drm/bridge: panel: Introduce drmm_of_get_bridge Maxime Ripard
2022-06-24 19:31   ` Sam Ravnborg
2022-06-22 14:31 ` [PATCH v2 11/68] drm/vc4: drv: Call component_unbind_all() Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 12/68] drm/vc4: drv: Use drm_dev_unplug Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 13/68] drm/vc4: crtc: Create vblank reporting function Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 14/68] drm/vc4: hvs: Protect device resources after removal Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 15/68] drm/vc4: hvs: Remove planes currently allocated before taking down Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 16/68] drm/vc4: plane: Take possible_crtcs as an argument Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 17/68] drm/vc4: crtc: Remove manual plane removal on error Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 18/68] drm/vc4: plane: Switch to drmm_universal_plane_alloc() Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 19/68] drm/vc4: crtc: Move debugfs_name to crtc_data Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 20/68] drm/vc4: crtc: Switch to drmm_kzalloc Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 21/68] drm/vc4: crtc: Switch to DRM-managed CRTC initialization Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 22/68] drm/vc4: dpi: Remove vc4_dev dpi pointer Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 23/68] drm/vc4: dpi: Embed DRM structures into the private structure Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 24/68] drm/vc4: dpi: Switch to drmm_kzalloc Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 25/68] drm/vc4: dpi: Return an error if we can't enable our clock Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 26/68] drm/vc4: dpi: Remove unnecessary drm_of_panel_bridge_remove call Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 27/68] drm/vc4: dpi: Add action to disable the clock Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 28/68] drm/vc4: dpi: Switch to DRM-managed encoder initialization Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 29/68] drm/vc4: dpi: Switch to drmm_of_get_bridge Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 30/68] drm/vc4: dpi: Protect device resources Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 31/68] drm/vc4: dsi: Embed DRM structures into the private structure Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 32/68] drm/vc4: dsi: Switch to DRM-managed encoder initialization Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 33/68] drm/vc4: dsi: Switch to drmm_of_get_bridge Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 34/68] drm/vc4: dsi: Fix the driver structure lifetime Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 35/68] drm/vc4: dsi: Switch to devm_pm_runtime_enable Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 36/68] drm/vc4: hdmi: Switch to drmm_kzalloc Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 37/68] drm/vc4: hdmi: Remove call to drm_connector_unregister() Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 38/68] drm/vc4: hdmi: Switch to DRM-managed encoder initialization Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 39/68] drm/vc4: hdmi: Switch to DRM-managed connector initialization Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 40/68] drm/vc4: hdmi: Switch to device-managed ALSA initialization Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 41/68] drm/vc4: hdmi: Switch to device-managed CEC initialization Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 42/68] drm/vc4: hdmi: Use a device-managed action for DDC Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 43/68] drm/vc4: hdmi: Switch to DRM-managed kfree to build regsets Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 44/68] drm/vc4: hdmi: Use devm to register hotplug interrupts Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 45/68] drm/vc4: hdmi: Move audio structure offset checks Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 46/68] drm/vc4: hdmi: Protect device resources after removal Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 47/68] drm/vc4: hdmi: Switch to devm_pm_runtime_enable Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 48/68] drm/vc4: txp: Remove vc4_dev txp pointer Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 49/68] drm/vc4: txp: Remove duplicate regset Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 50/68] drm/vc4: txp: Switch to drmm_kzalloc Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 51/68] drm/vc4: txp: Remove call to drm_connector_unregister() Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 52/68] drm/vc4: txp: Protect device resources Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 53/68] drm/vc4: vec: Remove vc4_dev vec pointer Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 54/68] drm/vc4: vec: Embed DRM structures into the private structure Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 55/68] drm/vc4: vec: Switch to drmm_kzalloc Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 56/68] drm/vc4: vec: Remove call to drm_connector_unregister() Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 57/68] drm/vc4: vec: Switch to DRM-managed encoder initialization Maxime Ripard
2022-06-22 14:31 ` [PATCH v2 58/68] drm/vc4: vec: Switch to DRM-managed connector initialization Maxime Ripard
2022-06-22 14:32 ` [PATCH v2 59/68] drm/vc4: vec: Protect device resources after removal Maxime Ripard
2022-06-22 14:32 ` [PATCH v2 60/68] drm/vc4: vec: Switch to devm_pm_runtime_enable Maxime Ripard
2022-06-22 14:32 ` [PATCH v2 61/68] drm/vc4: debugfs: Protect device resources Maxime Ripard
2022-06-22 14:32 ` [PATCH v2 62/68] drm/vc4: debugfs: Return an error on failure Maxime Ripard
2022-06-22 14:32 ` [PATCH v2 63/68] drm/vc4: debugfs: Simplify debugfs registration Maxime Ripard
2022-06-22 14:32 ` [PATCH v2 64/68] drm/vc4: Switch to drmm_mutex_init Maxime Ripard
2022-06-22 14:32 ` [PATCH v2 65/68] drm/vc4: perfmon: Add missing mutex_destroy Maxime Ripard
2022-06-22 14:32 ` [PATCH v2 66/68] drm/vc4: v3d: Stop disabling interrupts Maxime Ripard
2022-06-22 14:32 ` [PATCH v2 67/68] drm/vc4: v3d: Rework the runtime_pm setup Maxime Ripard
2022-06-22 14:32 ` [PATCH v2 68/68] 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.