All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/11] drm/tinydrm: Remove tinydrm_device
@ 2019-01-20 11:43 Noralf Trønnes
  2019-01-20 11:43 ` [PATCH 01/11] drm: Add devm_drm_dev_init/register Noralf Trønnes
                   ` (11 more replies)
  0 siblings, 12 replies; 57+ messages in thread
From: Noralf Trønnes @ 2019-01-20 11:43 UTC (permalink / raw)
  To: dri-devel; +Cc: david

This patchset is part of the effort to remove tinydrm.ko. It removes
struct tinydrm_device and tinydrm.h.

While doing this refactoring I have ensured that device unplug is
working.

The generic fbdev emulation has now been verified that it properly
handles device unplug with open file handles and bound fbcon.

Noralf.

Noralf Trønnes (11):
  drm: Add devm_drm_dev_init/register
  drm/modes: Add DRM_SIMPLE_MODE()
  drm/simple-kms-helper: Add drm_simple_connector_create()
  drm/tinydrm: Remove tinydrm_display_pipe_init()
  drm/tinydrm/mipi-dbi: Add drm_to_mipi_dbi()
  drm/tinydrm: Remove tinydrm_shutdown()
  drm/tinydrm/repaper: Use devm_drm_dev_*()
  drm/tinydrm: Use devm_drm_dev_*()
  drm/tinydrm: Remove tinydrm_device
  drm/tinydrm: Use drm_dev_enter/exit()
  drm/fb-helper: generic: Don't take module ref for fbcon

 Documentation/driver-model/devres.txt         |   4 +
 Documentation/gpu/tinydrm.rst                 |  29 +--
 Documentation/gpu/todo.rst                    |   4 -
 drivers/gpu/drm/drm_drv.c                     | 106 ++++++++++
 drivers/gpu/drm/drm_fb_helper.c               |   6 +-
 drivers/gpu/drm/drm_simple_kms_helper.c       | 122 ++++++++++++
 drivers/gpu/drm/tinydrm/core/Makefile         |   2 +-
 drivers/gpu/drm/tinydrm/core/tinydrm-core.c   | 183 ------------------
 .../gpu/drm/tinydrm/core/tinydrm-helpers.c    |   2 +
 drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c   | 183 ------------------
 drivers/gpu/drm/tinydrm/hx8357d.c             |  44 +++--
 drivers/gpu/drm/tinydrm/ili9225.c             |  67 +++++--
 drivers/gpu/drm/tinydrm/ili9341.c             |  44 +++--
 drivers/gpu/drm/tinydrm/mi0283qt.c            |  52 +++--
 drivers/gpu/drm/tinydrm/mipi-dbi.c            | 133 +++++++++----
 drivers/gpu/drm/tinydrm/repaper.c             | 129 ++++++++----
 drivers/gpu/drm/tinydrm/st7586.c              | 133 +++++++------
 drivers/gpu/drm/tinydrm/st7735r.c             |  44 +++--
 include/drm/drm_drv.h                         |   6 +
 include/drm/drm_modes.h                       |  16 ++
 include/drm/drm_simple_kms_helper.h           |   6 +
 include/drm/tinydrm/mipi-dbi.h                |  27 ++-
 include/drm/tinydrm/tinydrm.h                 |  75 -------
 23 files changed, 715 insertions(+), 702 deletions(-)
 delete mode 100644 drivers/gpu/drm/tinydrm/core/tinydrm-core.c
 delete mode 100644 drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c
 delete mode 100644 include/drm/tinydrm/tinydrm.h

-- 
2.20.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* [PATCH 01/11] drm: Add devm_drm_dev_init/register
  2019-01-20 11:43 [PATCH 00/11] drm/tinydrm: Remove tinydrm_device Noralf Trønnes
@ 2019-01-20 11:43 ` Noralf Trønnes
  2019-01-21  6:11   ` Sam Ravnborg
                     ` (2 more replies)
  2019-01-20 11:43 ` [PATCH 02/11] drm/modes: Add DRM_SIMPLE_MODE() Noralf Trønnes
                   ` (10 subsequent siblings)
  11 siblings, 3 replies; 57+ messages in thread
From: Noralf Trønnes @ 2019-01-20 11:43 UTC (permalink / raw)
  To: dri-devel; +Cc: david

This adds resource managed (devres) versions of drm_dev_init() and
drm_dev_register().

Also added is devm_drm_dev_register_with_fbdev() which sets up generic
fbdev emulation as well.

devm_drm_dev_register() isn't exported since there are no users.

Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
---
 Documentation/driver-model/devres.txt |   4 +
 drivers/gpu/drm/drm_drv.c             | 106 ++++++++++++++++++++++++++
 include/drm/drm_drv.h                 |   6 ++
 3 files changed, 116 insertions(+)

diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt
index b277cafce71e..6eebc28d4c21 100644
--- a/Documentation/driver-model/devres.txt
+++ b/Documentation/driver-model/devres.txt
@@ -254,6 +254,10 @@ DMA
   dmam_pool_create()
   dmam_pool_destroy()
 
+DRM
+  devm_drm_dev_init()
+  devm_drm_dev_register_with_fbdev()
+
 GPIO
   devm_gpiod_get()
   devm_gpiod_get_index()
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 381581b01d48..12129772be45 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -36,6 +36,7 @@
 
 #include <drm/drm_client.h>
 #include <drm/drm_drv.h>
+#include <drm/drm_fb_helper.h>
 #include <drm/drmP.h>
 
 #include "drm_crtc_internal.h"
@@ -871,6 +872,111 @@ void drm_dev_unregister(struct drm_device *dev)
 }
 EXPORT_SYMBOL(drm_dev_unregister);
 
+static void devm_drm_dev_init_release(void *data)
+{
+	drm_dev_put(data);
+}
+
+/**
+ * devm_drm_dev_init - Resource managed drm_dev_init()
+ * @parent: Parent device object
+ * @dev: DRM device
+ * @driver: DRM driver
+ *
+ * Managed drm_dev_init(). The DRM device initialized with this function is
+ * automatically released on driver detach. You must supply a
+ * &drm_driver.release callback to control the finalization explicitly.
+ *
+ * Note: This function must be used together with
+ * devm_drm_dev_register_with_fbdev().
+ *
+ * RETURNS:
+ * 0 on success, or error code on failure.
+ */
+int devm_drm_dev_init(struct device *parent,
+		      struct drm_device *dev,
+		      struct drm_driver *driver)
+{
+	int ret;
+
+	if (WARN_ON(!parent || !driver->release))
+		return -EINVAL;
+
+	ret = drm_dev_init(dev, driver, parent);
+	if (ret)
+		return ret;
+
+	/*
+	 * This is a temporary release action that is used if probing fails
+	 * before devm_drm_dev_register() is called.
+	 */
+	ret = devm_add_action(parent, devm_drm_dev_init_release, dev);
+	if (ret)
+		devm_drm_dev_init_release(dev);
+
+	return ret;
+}
+EXPORT_SYMBOL(devm_drm_dev_init);
+
+static void devm_drm_dev_register_release(void *data)
+{
+	drm_dev_unplug(data);
+}
+
+static int devm_drm_dev_register(struct drm_device *dev)
+{
+	int ret;
+
+	ret = drm_dev_register(dev, 0);
+	if (ret)
+		return ret;
+
+	/*
+	 * This has now served it's purpose, remove it to not mess up ref
+	 * counting.
+	 */
+	devm_remove_action(dev->dev, devm_drm_dev_init_release, dev);
+
+	ret = devm_add_action(dev->dev, devm_drm_dev_register_release, dev);
+	if (ret)
+		devm_drm_dev_register_release(dev);
+
+	return ret;
+}
+
+/**
+ * devm_drm_dev_register_with_fbdev - Resource managed drm_dev_register()
+ *                                    including generic fbdev emulation
+ * @dev: DRM device to register
+ * @fbdev_bpp: Preferred bits per pixel for fbdev (optional)
+ *
+ * Managed drm_dev_register() that also calls drm_fbdev_generic_setup().
+ * The DRM device registered with this function is automatically unregistered on
+ * driver detach using drm_dev_unplug().
+ *
+ * Note: This function must be used together with devm_drm_dev_init().
+ *
+ * For testing driver detach can be triggered manually by writing to the driver
+ * 'unbind' file.
+ *
+ * RETURNS:
+ * 0 on success, negative error code on failure.
+ */
+int devm_drm_dev_register_with_fbdev(struct drm_device *dev,
+				     unsigned int fbdev_bpp)
+{
+	int ret;
+
+	ret = devm_drm_dev_register(dev);
+	if (ret)
+		return ret;
+
+	drm_fbdev_generic_setup(dev, fbdev_bpp);
+
+	return 0;
+}
+EXPORT_SYMBOL(devm_drm_dev_register_with_fbdev);
+
 /**
  * drm_dev_set_unique - Set the unique name of a DRM device
  * @dev: device of which to set the unique name
diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h
index 35af23f5fa0d..c3f0477f2e7f 100644
--- a/include/drm/drm_drv.h
+++ b/include/drm/drm_drv.h
@@ -628,6 +628,12 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver,
 int drm_dev_register(struct drm_device *dev, unsigned long flags);
 void drm_dev_unregister(struct drm_device *dev);
 
+int devm_drm_dev_init(struct device *parent,
+		      struct drm_device *dev,
+		      struct drm_driver *driver);
+int devm_drm_dev_register_with_fbdev(struct drm_device *dev,
+				     unsigned int fbdev_bpp);
+
 void drm_dev_get(struct drm_device *dev);
 void drm_dev_put(struct drm_device *dev);
 void drm_put_dev(struct drm_device *dev);
-- 
2.20.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* [PATCH 02/11] drm/modes: Add DRM_SIMPLE_MODE()
  2019-01-20 11:43 [PATCH 00/11] drm/tinydrm: Remove tinydrm_device Noralf Trønnes
  2019-01-20 11:43 ` [PATCH 01/11] drm: Add devm_drm_dev_init/register Noralf Trønnes
@ 2019-01-20 11:43 ` Noralf Trønnes
  2019-01-20 16:37   ` Ilia Mirkin
  2019-01-20 11:43 ` [PATCH 03/11] drm/simple-kms-helper: Add drm_simple_connector_create() Noralf Trønnes
                   ` (9 subsequent siblings)
  11 siblings, 1 reply; 57+ messages in thread
From: Noralf Trønnes @ 2019-01-20 11:43 UTC (permalink / raw)
  To: dri-devel; +Cc: david

This adds a helper macro to specify modes that only contain info about
resolution.

Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
---
 drivers/gpu/drm/tinydrm/hx8357d.c  |  2 +-
 drivers/gpu/drm/tinydrm/ili9225.c  |  2 +-
 drivers/gpu/drm/tinydrm/ili9341.c  |  2 +-
 drivers/gpu/drm/tinydrm/mi0283qt.c |  2 +-
 drivers/gpu/drm/tinydrm/repaper.c  |  8 ++++----
 drivers/gpu/drm/tinydrm/st7586.c   |  2 +-
 drivers/gpu/drm/tinydrm/st7735r.c  |  2 +-
 include/drm/drm_modes.h            | 16 ++++++++++++++++
 include/drm/tinydrm/tinydrm.h      | 23 -----------------------
 9 files changed, 26 insertions(+), 33 deletions(-)

diff --git a/drivers/gpu/drm/tinydrm/hx8357d.c b/drivers/gpu/drm/tinydrm/hx8357d.c
index 8bbd0beafc6a..5a1ec0451c19 100644
--- a/drivers/gpu/drm/tinydrm/hx8357d.c
+++ b/drivers/gpu/drm/tinydrm/hx8357d.c
@@ -181,7 +181,7 @@ static const struct drm_simple_display_pipe_funcs hx8357d_pipe_funcs = {
 };
 
 static const struct drm_display_mode yx350hv15_mode = {
-	TINYDRM_MODE(320, 480, 60, 75),
+	DRM_SIMPLE_MODE(320, 480, 60, 75),
 };
 
 DEFINE_DRM_GEM_CMA_FOPS(hx8357d_fops);
diff --git a/drivers/gpu/drm/tinydrm/ili9225.c b/drivers/gpu/drm/tinydrm/ili9225.c
index 43a3b68d90a2..d40814d370e2 100644
--- a/drivers/gpu/drm/tinydrm/ili9225.c
+++ b/drivers/gpu/drm/tinydrm/ili9225.c
@@ -332,7 +332,7 @@ static const struct drm_simple_display_pipe_funcs ili9225_pipe_funcs = {
 };
 
 static const struct drm_display_mode ili9225_mode = {
-	TINYDRM_MODE(176, 220, 35, 44),
+	DRM_SIMPLE_MODE(176, 220, 35, 44),
 };
 
 DEFINE_DRM_GEM_CMA_FOPS(ili9225_fops);
diff --git a/drivers/gpu/drm/tinydrm/ili9341.c b/drivers/gpu/drm/tinydrm/ili9341.c
index 713bb2dd7e04..063f4f07f811 100644
--- a/drivers/gpu/drm/tinydrm/ili9341.c
+++ b/drivers/gpu/drm/tinydrm/ili9341.c
@@ -137,7 +137,7 @@ static const struct drm_simple_display_pipe_funcs ili9341_pipe_funcs = {
 };
 
 static const struct drm_display_mode yx240qv29_mode = {
-	TINYDRM_MODE(240, 320, 37, 49),
+	DRM_SIMPLE_MODE(240, 320, 37, 49),
 };
 
 DEFINE_DRM_GEM_CMA_FOPS(ili9341_fops);
diff --git a/drivers/gpu/drm/tinydrm/mi0283qt.c b/drivers/gpu/drm/tinydrm/mi0283qt.c
index 82a92ec9ae3c..3d067c2ba1bc 100644
--- a/drivers/gpu/drm/tinydrm/mi0283qt.c
+++ b/drivers/gpu/drm/tinydrm/mi0283qt.c
@@ -145,7 +145,7 @@ static const struct drm_simple_display_pipe_funcs mi0283qt_pipe_funcs = {
 };
 
 static const struct drm_display_mode mi0283qt_mode = {
-	TINYDRM_MODE(320, 240, 58, 43),
+	DRM_SIMPLE_MODE(320, 240, 58, 43),
 };
 
 DEFINE_DRM_GEM_CMA_FOPS(mi0283qt_fops);
diff --git a/drivers/gpu/drm/tinydrm/repaper.c b/drivers/gpu/drm/tinydrm/repaper.c
index b037c6540cf3..72d30151ecd8 100644
--- a/drivers/gpu/drm/tinydrm/repaper.c
+++ b/drivers/gpu/drm/tinydrm/repaper.c
@@ -860,28 +860,28 @@ static const uint32_t repaper_formats[] = {
 };
 
 static const struct drm_display_mode repaper_e1144cs021_mode = {
-	TINYDRM_MODE(128, 96, 29, 22),
+	DRM_SIMPLE_MODE(128, 96, 29, 22),
 };
 
 static const u8 repaper_e1144cs021_cs[] = { 0x00, 0x00, 0x00, 0x00,
 					    0x00, 0x0f, 0xff, 0x00 };
 
 static const struct drm_display_mode repaper_e1190cs021_mode = {
-	TINYDRM_MODE(144, 128, 36, 32),
+	DRM_SIMPLE_MODE(144, 128, 36, 32),
 };
 
 static const u8 repaper_e1190cs021_cs[] = { 0x00, 0x00, 0x00, 0x03,
 					    0xfc, 0x00, 0x00, 0xff };
 
 static const struct drm_display_mode repaper_e2200cs021_mode = {
-	TINYDRM_MODE(200, 96, 46, 22),
+	DRM_SIMPLE_MODE(200, 96, 46, 22),
 };
 
 static const u8 repaper_e2200cs021_cs[] = { 0x00, 0x00, 0x00, 0x00,
 					    0x01, 0xff, 0xe0, 0x00 };
 
 static const struct drm_display_mode repaper_e2271cs021_mode = {
-	TINYDRM_MODE(264, 176, 57, 38),
+	DRM_SIMPLE_MODE(264, 176, 57, 38),
 };
 
 static const u8 repaper_e2271cs021_cs[] = { 0x00, 0x00, 0x00, 0x7f,
diff --git a/drivers/gpu/drm/tinydrm/st7586.c b/drivers/gpu/drm/tinydrm/st7586.c
index 01a8077954b3..5ee7db561349 100644
--- a/drivers/gpu/drm/tinydrm/st7586.c
+++ b/drivers/gpu/drm/tinydrm/st7586.c
@@ -312,7 +312,7 @@ static const struct drm_simple_display_pipe_funcs st7586_pipe_funcs = {
 };
 
 static const struct drm_display_mode st7586_mode = {
-	TINYDRM_MODE(178, 128, 37, 27),
+	DRM_SIMPLE_MODE(178, 128, 37, 27),
 };
 
 DEFINE_DRM_GEM_CMA_FOPS(st7586_fops);
diff --git a/drivers/gpu/drm/tinydrm/st7735r.c b/drivers/gpu/drm/tinydrm/st7735r.c
index 3bab9a9569a6..6c7904c205f0 100644
--- a/drivers/gpu/drm/tinydrm/st7735r.c
+++ b/drivers/gpu/drm/tinydrm/st7735r.c
@@ -111,7 +111,7 @@ static const struct drm_simple_display_pipe_funcs jd_t18003_t01_pipe_funcs = {
 };
 
 static const struct drm_display_mode jd_t18003_t01_mode = {
-	TINYDRM_MODE(128, 160, 28, 35),
+	DRM_SIMPLE_MODE(128, 160, 28, 35),
 };
 
 DEFINE_DRM_GEM_CMA_FOPS(st7735r_fops);
diff --git a/include/drm/drm_modes.h b/include/drm/drm_modes.h
index be4fed97e727..868a9954a8e4 100644
--- a/include/drm/drm_modes.h
+++ b/include/drm/drm_modes.h
@@ -138,6 +138,22 @@ enum drm_mode_status {
 	.vsync_start = (vss), .vsync_end = (vse), .vtotal = (vt), \
 	.vscan = (vs), .flags = (f)
 
+/**
+ * DRM_SIMPLE_MODE - Simple display mode
+ * @hd: Horizontal resolution, width
+ * @vd: Vertical resolution, height
+ * @hd_mm: Display width in millimeters
+ * @vd_mm: Display height in millimeters
+ *
+ * This macro initializes a &drm_display_mode that only contains info about
+ * resolution and physical size.
+ */
+#define DRM_SIMPLE_MODE(hd, vd, hd_mm, vd_mm) \
+	.type = DRM_MODE_TYPE_DRIVER, .clock = 1 /* pass validation */, \
+	.hdisplay = (hd), .hsync_start = (hd), .hsync_end = (hd), \
+	.htotal = (hd), .vdisplay = (vd), .vsync_start = (vd), \
+	.vsync_end = (vd), .vtotal = (vd)
+
 #define CRTC_INTERLACE_HALVE_V	(1 << 0) /* halve V values for interlacing */
 #define CRTC_STEREO_DOUBLE	(1 << 1) /* adjust timings for stereo modes */
 #define CRTC_NO_DBLSCAN		(1 << 2) /* don't adjust doublescan */
diff --git a/include/drm/tinydrm/tinydrm.h b/include/drm/tinydrm/tinydrm.h
index 5621688edcc0..87e7f9b93a37 100644
--- a/include/drm/tinydrm/tinydrm.h
+++ b/include/drm/tinydrm/tinydrm.h
@@ -35,29 +35,6 @@ pipe_to_tinydrm(struct drm_simple_display_pipe *pipe)
 	return container_of(pipe, struct tinydrm_device, pipe);
 }
 
-/**
- * TINYDRM_MODE - tinydrm display mode
- * @hd: Horizontal resolution, width
- * @vd: Vertical resolution, height
- * @hd_mm: Display width in millimeters
- * @vd_mm: Display height in millimeters
- *
- * This macro creates a &drm_display_mode for use with tinydrm.
- */
-#define TINYDRM_MODE(hd, vd, hd_mm, vd_mm) \
-	.hdisplay = (hd), \
-	.hsync_start = (hd), \
-	.hsync_end = (hd), \
-	.htotal = (hd), \
-	.vdisplay = (vd), \
-	.vsync_start = (vd), \
-	.vsync_end = (vd), \
-	.vtotal = (vd), \
-	.width_mm = (hd_mm), \
-	.height_mm = (vd_mm), \
-	.type = DRM_MODE_TYPE_DRIVER, \
-	.clock = 1 /* pass validation */
-
 int devm_tinydrm_init(struct device *parent, struct tinydrm_device *tdev,
 		      struct drm_driver *driver);
 int devm_tinydrm_register(struct tinydrm_device *tdev);
-- 
2.20.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* [PATCH 03/11] drm/simple-kms-helper: Add drm_simple_connector_create()
  2019-01-20 11:43 [PATCH 00/11] drm/tinydrm: Remove tinydrm_device Noralf Trønnes
  2019-01-20 11:43 ` [PATCH 01/11] drm: Add devm_drm_dev_init/register Noralf Trønnes
  2019-01-20 11:43 ` [PATCH 02/11] drm/modes: Add DRM_SIMPLE_MODE() Noralf Trønnes
@ 2019-01-20 11:43 ` Noralf Trønnes
  2019-01-20 22:14   ` Sam Ravnborg
  2019-01-21  9:22   ` Daniel Vetter
  2019-01-20 11:43 ` [PATCH 04/11] drm/tinydrm: Remove tinydrm_display_pipe_init() Noralf Trønnes
                   ` (8 subsequent siblings)
  11 siblings, 2 replies; 57+ messages in thread
From: Noralf Trønnes @ 2019-01-20 11:43 UTC (permalink / raw)
  To: dri-devel; +Cc: david

This adds a function that creates a simple connector that has only one
static mode. Additionally add a helper to set &drm_mode_config width
and height from the static mode.

Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
---
 drivers/gpu/drm/drm_simple_kms_helper.c | 122 ++++++++++++++++++++++++
 include/drm/drm_simple_kms_helper.h     |   6 ++
 2 files changed, 128 insertions(+)

diff --git a/drivers/gpu/drm/drm_simple_kms_helper.c b/drivers/gpu/drm/drm_simple_kms_helper.c
index 917812448d1b..ca29975afefe 100644
--- a/drivers/gpu/drm/drm_simple_kms_helper.c
+++ b/drivers/gpu/drm/drm_simple_kms_helper.c
@@ -11,6 +11,8 @@
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_device.h>
+#include <drm/drm_modes.h>
 #include <drm/drm_plane_helper.h>
 #include <drm/drm_simple_kms_helper.h>
 #include <linux/slab.h>
@@ -299,4 +301,124 @@ int drm_simple_display_pipe_init(struct drm_device *dev,
 }
 EXPORT_SYMBOL(drm_simple_display_pipe_init);
 
+static const struct drm_connector_helper_funcs drm_simple_connector_hfuncs = {
+	/* dummy for the atomic helper */
+};
+
+static int drm_simple_connector_fill_modes(struct drm_connector *connector,
+					   uint32_t maxX, uint32_t maxY)
+{
+	return 1;
+}
+
+static void drm_simple_connector_destroy(struct drm_connector *connector)
+{
+	drm_connector_cleanup(connector);
+	kfree(connector);
+}
+
+static const struct drm_connector_funcs drm_simple_connector_funcs = {
+	.reset = drm_atomic_helper_connector_reset,
+	.fill_modes = drm_simple_connector_fill_modes,
+	.destroy = drm_simple_connector_destroy,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+/**
+ * drm_simple_connector_create - Create a connector with one static mode
+ * @dev: DRM device
+ * @connector_type: Connector type
+ * @mode: Supported display mode
+ * @rotation: Initial @mode rotation in degrees
+ *
+ * This function creates a &drm_connector that has one fixed &drm_display_mode
+ * which will be rotated according to @rotation.
+ *
+ * Returns:
+ * Pointer to connector on success, or ERR_PTR on failure.
+ */
+struct drm_connector *
+drm_simple_connector_create(struct drm_device *dev, int connector_type,
+			    const struct drm_display_mode *mode,
+			    unsigned int rotation)
+{
+	struct drm_display_mode *mode_dup = NULL;
+	struct drm_connector *connector;
+	int ret;
+
+	connector = kzalloc(sizeof(*connector), GFP_KERNEL);
+	if (!connector)
+		return ERR_PTR(-ENOMEM);
+
+	drm_connector_helper_add(connector, &drm_simple_connector_hfuncs);
+	ret = drm_connector_init(dev, connector, &drm_simple_connector_funcs,
+				 connector_type);
+	if (ret)
+		goto err_free;
+
+	connector->status = connector_status_connected;
+
+	mode_dup = drm_mode_duplicate(dev, mode);
+	if (!mode_dup) {
+		ret = -ENOMEM;
+		goto err_cleanup;
+	}
+
+	if (rotation == 90 || rotation == 270) {
+		swap(mode_dup->hdisplay, mode_dup->vdisplay);
+		swap(mode_dup->hsync_start, mode_dup->vsync_start);
+		swap(mode_dup->hsync_end, mode_dup->vsync_end);
+		swap(mode_dup->htotal, mode_dup->vtotal);
+		swap(mode_dup->width_mm, mode_dup->height_mm);
+	} else if (rotation != 0 && rotation != 180) {
+		DRM_ERROR("Illegal rotation value %u\n", rotation);
+		ret = -EINVAL;
+		goto err_cleanup;
+	}
+
+	mode_dup->type |= DRM_MODE_TYPE_PREFERRED;
+	if (mode_dup->name[0] == '\0')
+		drm_mode_set_name(mode_dup);
+
+	list_add(&mode_dup->head, &connector->modes);
+
+	connector->display_info.width_mm = mode_dup->width_mm;
+	connector->display_info.height_mm = mode_dup->height_mm;
+
+	return connector;
+
+err_cleanup:
+	drm_connector_cleanup(connector);
+	drm_mode_destroy(dev, mode_dup);
+err_free:
+	kfree(connector);
+
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL(drm_simple_connector_create);
+
+/**
+ * drm_simple_connector_set_mode_config - Set &drm_mode_config width and height
+ * @connector: Connector
+ *
+ * This function sets the &drm_mode_config min/max width and height based on the
+ * connector fixed display mode.
+ */
+void drm_simple_connector_set_mode_config(struct drm_connector *connector)
+{
+	struct drm_mode_config *mode_config = &connector->dev->mode_config;
+	struct drm_display_mode *mode;
+
+	mode = list_first_entry(&connector->modes, struct drm_display_mode, head);
+	if (WARN_ON(!mode))
+		return;
+
+	mode_config->min_width = mode->hdisplay;
+	mode_config->max_width = mode->hdisplay;
+	mode_config->min_height = mode->vdisplay;
+	mode_config->max_height = mode->vdisplay;
+}
+EXPORT_SYMBOL(drm_simple_connector_set_mode_config);
+
 MODULE_LICENSE("GPL");
diff --git a/include/drm/drm_simple_kms_helper.h b/include/drm/drm_simple_kms_helper.h
index 451960438a29..ab3d847b7713 100644
--- a/include/drm/drm_simple_kms_helper.h
+++ b/include/drm/drm_simple_kms_helper.h
@@ -182,4 +182,10 @@ int drm_simple_display_pipe_init(struct drm_device *dev,
 			const uint64_t *format_modifiers,
 			struct drm_connector *connector);
 
+struct drm_connector *
+drm_simple_connector_create(struct drm_device *dev, int connector_type,
+			    const struct drm_display_mode *mode,
+			    unsigned int rotation);
+void drm_simple_connector_set_mode_config(struct drm_connector *connector);
+
 #endif /* __LINUX_DRM_SIMPLE_KMS_HELPER_H */
-- 
2.20.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* [PATCH 04/11] drm/tinydrm: Remove tinydrm_display_pipe_init()
  2019-01-20 11:43 [PATCH 00/11] drm/tinydrm: Remove tinydrm_device Noralf Trønnes
                   ` (2 preceding siblings ...)
  2019-01-20 11:43 ` [PATCH 03/11] drm/simple-kms-helper: Add drm_simple_connector_create() Noralf Trønnes
@ 2019-01-20 11:43 ` Noralf Trønnes
  2019-01-21  6:30   ` Sam Ravnborg
  2019-01-21  9:15   ` Daniel Vetter
  2019-01-20 11:43 ` [PATCH 05/11] drm/tinydrm/mipi-dbi: Add drm_to_mipi_dbi() Noralf Trønnes
                   ` (7 subsequent siblings)
  11 siblings, 2 replies; 57+ messages in thread
From: Noralf Trønnes @ 2019-01-20 11:43 UTC (permalink / raw)
  To: dri-devel; +Cc: david

Further strip down tinydrm.ko and switch to drm_simple_connector_create().

Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
---
 Documentation/gpu/tinydrm.rst               |   3 -
 drivers/gpu/drm/tinydrm/core/Makefile       |   2 +-
 drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c | 183 --------------------
 drivers/gpu/drm/tinydrm/mipi-dbi.c          |  24 ++-
 drivers/gpu/drm/tinydrm/repaper.c           |  16 +-
 drivers/gpu/drm/tinydrm/st7586.c            |  19 +-
 include/drm/tinydrm/tinydrm.h               |   9 -
 7 files changed, 43 insertions(+), 213 deletions(-)
 delete mode 100644 drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c

diff --git a/Documentation/gpu/tinydrm.rst b/Documentation/gpu/tinydrm.rst
index a913644bfc19..1ca726474af4 100644
--- a/Documentation/gpu/tinydrm.rst
+++ b/Documentation/gpu/tinydrm.rst
@@ -17,9 +17,6 @@ Core functionality
 .. kernel-doc:: drivers/gpu/drm/tinydrm/core/tinydrm-core.c
    :export:
 
-.. kernel-doc:: drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c
-   :export:
-
 Additional helpers
 ==================
 
diff --git a/drivers/gpu/drm/tinydrm/core/Makefile b/drivers/gpu/drm/tinydrm/core/Makefile
index fb221e6f8885..bf2df7326df7 100644
--- a/drivers/gpu/drm/tinydrm/core/Makefile
+++ b/drivers/gpu/drm/tinydrm/core/Makefile
@@ -1,3 +1,3 @@
-tinydrm-y := tinydrm-core.o tinydrm-pipe.o tinydrm-helpers.o
+tinydrm-y := tinydrm-core.o tinydrm-helpers.o
 
 obj-$(CONFIG_DRM_TINYDRM) += tinydrm.o
diff --git a/drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c b/drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c
deleted file mode 100644
index 323564329535..000000000000
--- a/drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright (C) 2016 Noralf Trønnes
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#include <drm/drm_atomic_helper.h>
-#include <drm/drm_crtc_helper.h>
-#include <drm/drm_drv.h>
-#include <drm/drm_gem_framebuffer_helper.h>
-#include <drm/drm_modes.h>
-#include <drm/drm_print.h>
-#include <drm/tinydrm/tinydrm.h>
-
-struct tinydrm_connector {
-	struct drm_connector base;
-	struct drm_display_mode mode;
-};
-
-static inline struct tinydrm_connector *
-to_tinydrm_connector(struct drm_connector *connector)
-{
-	return container_of(connector, struct tinydrm_connector, base);
-}
-
-static int tinydrm_connector_get_modes(struct drm_connector *connector)
-{
-	struct tinydrm_connector *tconn = to_tinydrm_connector(connector);
-	struct drm_display_mode *mode;
-
-	mode = drm_mode_duplicate(connector->dev, &tconn->mode);
-	if (!mode) {
-		DRM_ERROR("Failed to duplicate mode\n");
-		return 0;
-	}
-
-	if (mode->name[0] == '\0')
-		drm_mode_set_name(mode);
-
-	mode->type |= DRM_MODE_TYPE_PREFERRED;
-	drm_mode_probed_add(connector, mode);
-
-	if (mode->width_mm) {
-		connector->display_info.width_mm = mode->width_mm;
-		connector->display_info.height_mm = mode->height_mm;
-	}
-
-	return 1;
-}
-
-static const struct drm_connector_helper_funcs tinydrm_connector_hfuncs = {
-	.get_modes = tinydrm_connector_get_modes,
-};
-
-static enum drm_connector_status
-tinydrm_connector_detect(struct drm_connector *connector, bool force)
-{
-	if (drm_dev_is_unplugged(connector->dev))
-		return connector_status_disconnected;
-
-	return connector->status;
-}
-
-static void tinydrm_connector_destroy(struct drm_connector *connector)
-{
-	struct tinydrm_connector *tconn = to_tinydrm_connector(connector);
-
-	drm_connector_cleanup(connector);
-	kfree(tconn);
-}
-
-static const struct drm_connector_funcs tinydrm_connector_funcs = {
-	.reset = drm_atomic_helper_connector_reset,
-	.detect = tinydrm_connector_detect,
-	.fill_modes = drm_helper_probe_single_connector_modes,
-	.destroy = tinydrm_connector_destroy,
-	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
-	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
-};
-
-struct drm_connector *
-tinydrm_connector_create(struct drm_device *drm,
-			 const struct drm_display_mode *mode,
-			 int connector_type)
-{
-	struct tinydrm_connector *tconn;
-	struct drm_connector *connector;
-	int ret;
-
-	tconn = kzalloc(sizeof(*tconn), GFP_KERNEL);
-	if (!tconn)
-		return ERR_PTR(-ENOMEM);
-
-	drm_mode_copy(&tconn->mode, mode);
-	connector = &tconn->base;
-
-	drm_connector_helper_add(connector, &tinydrm_connector_hfuncs);
-	ret = drm_connector_init(drm, connector, &tinydrm_connector_funcs,
-				 connector_type);
-	if (ret) {
-		kfree(tconn);
-		return ERR_PTR(ret);
-	}
-
-	connector->status = connector_status_connected;
-
-	return connector;
-}
-
-static int tinydrm_rotate_mode(struct drm_display_mode *mode,
-			       unsigned int rotation)
-{
-	if (rotation == 0 || rotation == 180) {
-		return 0;
-	} else if (rotation == 90 || rotation == 270) {
-		swap(mode->hdisplay, mode->vdisplay);
-		swap(mode->hsync_start, mode->vsync_start);
-		swap(mode->hsync_end, mode->vsync_end);
-		swap(mode->htotal, mode->vtotal);
-		swap(mode->width_mm, mode->height_mm);
-		return 0;
-	} else {
-		return -EINVAL;
-	}
-}
-
-/**
- * tinydrm_display_pipe_init - Initialize display pipe
- * @tdev: tinydrm device
- * @funcs: Display pipe functions
- * @connector_type: Connector type
- * @formats: Array of supported formats (DRM_FORMAT\_\*)
- * @format_count: Number of elements in @formats
- * @mode: Supported mode
- * @rotation: Initial @mode rotation in degrees Counter Clock Wise
- *
- * This function sets up a &drm_simple_display_pipe with a &drm_connector that
- * has one fixed &drm_display_mode which is rotated according to @rotation.
- *
- * Returns:
- * Zero on success, negative error code on failure.
- */
-int
-tinydrm_display_pipe_init(struct tinydrm_device *tdev,
-			  const struct drm_simple_display_pipe_funcs *funcs,
-			  int connector_type,
-			  const uint32_t *formats,
-			  unsigned int format_count,
-			  const struct drm_display_mode *mode,
-			  unsigned int rotation)
-{
-	struct drm_device *drm = tdev->drm;
-	struct drm_display_mode mode_copy;
-	struct drm_connector *connector;
-	int ret;
-	static const uint64_t modifiers[] = {
-		DRM_FORMAT_MOD_LINEAR,
-		DRM_FORMAT_MOD_INVALID
-	};
-
-	drm_mode_copy(&mode_copy, mode);
-	ret = tinydrm_rotate_mode(&mode_copy, rotation);
-	if (ret) {
-		DRM_ERROR("Illegal rotation value %u\n", rotation);
-		return -EINVAL;
-	}
-
-	drm->mode_config.min_width = mode_copy.hdisplay;
-	drm->mode_config.max_width = mode_copy.hdisplay;
-	drm->mode_config.min_height = mode_copy.vdisplay;
-	drm->mode_config.max_height = mode_copy.vdisplay;
-
-	connector = tinydrm_connector_create(drm, &mode_copy, connector_type);
-	if (IS_ERR(connector))
-		return PTR_ERR(connector);
-
-	return drm_simple_display_pipe_init(drm, &tdev->pipe, funcs, formats,
-					    format_count, modifiers, connector);
-}
-EXPORT_SYMBOL(tinydrm_display_pipe_init);
diff --git a/drivers/gpu/drm/tinydrm/mipi-dbi.c b/drivers/gpu/drm/tinydrm/mipi-dbi.c
index 918f77c7de34..d1d546f3a664 100644
--- a/drivers/gpu/drm/tinydrm/mipi-dbi.c
+++ b/drivers/gpu/drm/tinydrm/mipi-dbi.c
@@ -391,7 +391,13 @@ int mipi_dbi_init(struct device *dev, struct mipi_dbi *mipi,
 {
 	size_t bufsize = mode->vdisplay * mode->hdisplay * sizeof(u16);
 	struct tinydrm_device *tdev = &mipi->tinydrm;
+	struct drm_connector *connector;
+	struct drm_device *drm;
 	int ret;
+	static const uint64_t modifiers[] = {
+		DRM_FORMAT_MOD_LINEAR,
+		DRM_FORMAT_MOD_INVALID
+	};
 
 	if (!mipi->command)
 		return -EINVAL;
@@ -406,18 +412,22 @@ int mipi_dbi_init(struct device *dev, struct mipi_dbi *mipi,
 	if (ret)
 		return ret;
 
-	/* TODO: Maybe add DRM_MODE_CONNECTOR_SPI */
-	ret = tinydrm_display_pipe_init(tdev, pipe_funcs,
-					DRM_MODE_CONNECTOR_VIRTUAL,
-					mipi_dbi_formats,
-					ARRAY_SIZE(mipi_dbi_formats), mode,
-					rotation);
+	drm = tdev->drm;
+
+	connector = drm_simple_connector_create(drm, DRM_MODE_CONNECTOR_VIRTUAL, mode, rotation);
+	if (IS_ERR(connector))
+		return PTR_ERR(connector);
+
+	ret = drm_simple_display_pipe_init(drm, &tdev->pipe, pipe_funcs,
+					   mipi_dbi_formats, ARRAY_SIZE(mipi_dbi_formats),
+					   modifiers, connector);
 	if (ret)
 		return ret;
 
 	drm_plane_enable_fb_damage_clips(&tdev->pipe.plane);
 
-	tdev->drm->mode_config.preferred_depth = 16;
+	drm_simple_connector_set_mode_config(connector);
+	drm->mode_config.preferred_depth = 16;
 	mipi->rotation = rotation;
 
 	drm_mode_config_reset(tdev->drm);
diff --git a/drivers/gpu/drm/tinydrm/repaper.c b/drivers/gpu/drm/tinydrm/repaper.c
index 72d30151ecd8..1d551744cc9b 100644
--- a/drivers/gpu/drm/tinydrm/repaper.c
+++ b/drivers/gpu/drm/tinydrm/repaper.c
@@ -924,12 +924,14 @@ static int repaper_probe(struct spi_device *spi)
 	const struct drm_display_mode *mode;
 	const struct spi_device_id *spi_id;
 	const struct of_device_id *match;
+	struct drm_connector *connector;
 	struct device *dev = &spi->dev;
 	struct tinydrm_device *tdev;
 	enum repaper_model model;
 	const char *thermal_zone;
 	struct repaper_epd *epd;
 	size_t line_buffer_size;
+	struct drm_device *drm;
 	int ret;
 
 	match = of_match_device(repaper_of_match, dev);
@@ -1069,13 +1071,19 @@ static int repaper_probe(struct spi_device *spi)
 	if (ret)
 		return ret;
 
-	ret = tinydrm_display_pipe_init(tdev, &repaper_pipe_funcs,
-					DRM_MODE_CONNECTOR_VIRTUAL,
-					repaper_formats,
-					ARRAY_SIZE(repaper_formats), mode, 0);
+	drm = tdev->drm;
+
+	connector = drm_simple_connector_create(drm, DRM_MODE_CONNECTOR_VIRTUAL, mode, 0);
+	if (IS_ERR(connector))
+		return PTR_ERR(connector);
+
+	ret = drm_simple_display_pipe_init(drm, &tdev->pipe, &repaper_pipe_funcs,
+					   repaper_formats, ARRAY_SIZE(repaper_formats),
+					   NULL, connector);
 	if (ret)
 		return ret;
 
+	drm_simple_connector_set_mode_config(connector);
 	drm_mode_config_reset(tdev->drm);
 	spi_set_drvdata(spi, tdev);
 
diff --git a/drivers/gpu/drm/tinydrm/st7586.c b/drivers/gpu/drm/tinydrm/st7586.c
index 5ee7db561349..6cb68dd7e7b6 100644
--- a/drivers/gpu/drm/tinydrm/st7586.c
+++ b/drivers/gpu/drm/tinydrm/st7586.c
@@ -271,6 +271,8 @@ static int st7586_init(struct device *dev, struct mipi_dbi *mipi,
 {
 	size_t bufsize = (mode->vdisplay + 2) / 3 * mode->hdisplay;
 	struct tinydrm_device *tdev = &mipi->tinydrm;
+	struct drm_connector *connector;
+	struct drm_device *drm;
 	int ret;
 
 	mutex_init(&mipi->cmdlock);
@@ -283,17 +285,22 @@ static int st7586_init(struct device *dev, struct mipi_dbi *mipi,
 	if (ret)
 		return ret;
 
-	ret = tinydrm_display_pipe_init(tdev, pipe_funcs,
-					DRM_MODE_CONNECTOR_VIRTUAL,
-					st7586_formats,
-					ARRAY_SIZE(st7586_formats),
-					mode, rotation);
+	drm = tdev->drm;
+
+	connector = drm_simple_connector_create(drm, DRM_MODE_CONNECTOR_VIRTUAL, mode, rotation);
+	if (IS_ERR(connector))
+		return PTR_ERR(connector);
+
+	ret = drm_simple_display_pipe_init(drm, &tdev->pipe, pipe_funcs,
+					   st7586_formats, ARRAY_SIZE(st7586_formats),
+					   NULL, connector);
 	if (ret)
 		return ret;
 
 	drm_plane_enable_fb_damage_clips(&tdev->pipe.plane);
 
-	tdev->drm->mode_config.preferred_depth = 32;
+	drm_simple_connector_set_mode_config(connector);
+	drm->mode_config.preferred_depth = 32;
 	mipi->rotation = rotation;
 
 	drm_mode_config_reset(tdev->drm);
diff --git a/include/drm/tinydrm/tinydrm.h b/include/drm/tinydrm/tinydrm.h
index 87e7f9b93a37..69c4363fd88e 100644
--- a/include/drm/tinydrm/tinydrm.h
+++ b/include/drm/tinydrm/tinydrm.h
@@ -40,13 +40,4 @@ int devm_tinydrm_init(struct device *parent, struct tinydrm_device *tdev,
 int devm_tinydrm_register(struct tinydrm_device *tdev);
 void tinydrm_shutdown(struct tinydrm_device *tdev);
 
-int
-tinydrm_display_pipe_init(struct tinydrm_device *tdev,
-			  const struct drm_simple_display_pipe_funcs *funcs,
-			  int connector_type,
-			  const uint32_t *formats,
-			  unsigned int format_count,
-			  const struct drm_display_mode *mode,
-			  unsigned int rotation);
-
 #endif /* __LINUX_TINYDRM_H */
-- 
2.20.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* [PATCH 05/11] drm/tinydrm/mipi-dbi: Add drm_to_mipi_dbi()
  2019-01-20 11:43 [PATCH 00/11] drm/tinydrm: Remove tinydrm_device Noralf Trønnes
                   ` (3 preceding siblings ...)
  2019-01-20 11:43 ` [PATCH 04/11] drm/tinydrm: Remove tinydrm_display_pipe_init() Noralf Trønnes
@ 2019-01-20 11:43 ` Noralf Trønnes
  2019-01-21  6:34   ` Sam Ravnborg
  2019-01-20 11:43 ` [PATCH 06/11] drm/tinydrm: Remove tinydrm_shutdown() Noralf Trønnes
                   ` (6 subsequent siblings)
  11 siblings, 1 reply; 57+ messages in thread
From: Noralf Trønnes @ 2019-01-20 11:43 UTC (permalink / raw)
  To: dri-devel; +Cc: david

Add a function to derive mipi_dbi from drm_device now that tinydrm_device
is going away.

Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
---
 drivers/gpu/drm/tinydrm/hx8357d.c  |  3 +--
 drivers/gpu/drm/tinydrm/ili9225.c  | 11 ++++-------
 drivers/gpu/drm/tinydrm/ili9341.c  |  3 +--
 drivers/gpu/drm/tinydrm/mi0283qt.c |  3 +--
 drivers/gpu/drm/tinydrm/mipi-dbi.c |  9 +++------
 drivers/gpu/drm/tinydrm/st7586.c   |  9 +++------
 drivers/gpu/drm/tinydrm/st7735r.c  |  3 +--
 include/drm/tinydrm/mipi-dbi.h     |  5 +++--
 8 files changed, 17 insertions(+), 29 deletions(-)

diff --git a/drivers/gpu/drm/tinydrm/hx8357d.c b/drivers/gpu/drm/tinydrm/hx8357d.c
index 5a1ec0451c19..ab604513b865 100644
--- a/drivers/gpu/drm/tinydrm/hx8357d.c
+++ b/drivers/gpu/drm/tinydrm/hx8357d.c
@@ -46,8 +46,7 @@ static void yx240qv29_enable(struct drm_simple_display_pipe *pipe,
 			     struct drm_crtc_state *crtc_state,
 			     struct drm_plane_state *plane_state)
 {
-	struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
-	struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
+	struct mipi_dbi *mipi = drm_to_mipi_dbi(pipe->crtc.dev);
 	u8 addr_mode;
 	int ret;
 
diff --git a/drivers/gpu/drm/tinydrm/ili9225.c b/drivers/gpu/drm/tinydrm/ili9225.c
index d40814d370e2..40e1f98ca393 100644
--- a/drivers/gpu/drm/tinydrm/ili9225.c
+++ b/drivers/gpu/drm/tinydrm/ili9225.c
@@ -81,8 +81,7 @@ static inline int ili9225_command(struct mipi_dbi *mipi, u8 cmd, u16 data)
 static void ili9225_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect)
 {
 	struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
-	struct tinydrm_device *tdev = fb->dev->dev_private;
-	struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
+	struct mipi_dbi *mipi = drm_to_mipi_dbi(fb->dev);
 	unsigned int height = rect->y2 - rect->y1;
 	unsigned int width = rect->x2 - rect->x1;
 	bool swap = mipi->swap_bytes;
@@ -181,10 +180,9 @@ static void ili9225_pipe_enable(struct drm_simple_display_pipe *pipe,
 				struct drm_crtc_state *crtc_state,
 				struct drm_plane_state *plane_state)
 {
-	struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
-	struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
+	struct mipi_dbi *mipi = drm_to_mipi_dbi(pipe->crtc.dev);
 	struct drm_framebuffer *fb = plane_state->fb;
-	struct device *dev = tdev->drm->dev;
+	struct device *dev = pipe->crtc.dev->dev;
 	struct drm_rect rect = {
 		.x1 = 0,
 		.x2 = fb->width,
@@ -284,8 +282,7 @@ static void ili9225_pipe_enable(struct drm_simple_display_pipe *pipe,
 
 static void ili9225_pipe_disable(struct drm_simple_display_pipe *pipe)
 {
-	struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
-	struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
+	struct mipi_dbi *mipi = drm_to_mipi_dbi(pipe->crtc.dev);
 
 	DRM_DEBUG_KMS("\n");
 
diff --git a/drivers/gpu/drm/tinydrm/ili9341.c b/drivers/gpu/drm/tinydrm/ili9341.c
index 063f4f07f811..86f8884036b2 100644
--- a/drivers/gpu/drm/tinydrm/ili9341.c
+++ b/drivers/gpu/drm/tinydrm/ili9341.c
@@ -52,8 +52,7 @@ static void yx240qv29_enable(struct drm_simple_display_pipe *pipe,
 			     struct drm_crtc_state *crtc_state,
 			     struct drm_plane_state *plane_state)
 {
-	struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
-	struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
+	struct mipi_dbi *mipi = drm_to_mipi_dbi(pipe->crtc.dev);
 	u8 addr_mode;
 	int ret;
 
diff --git a/drivers/gpu/drm/tinydrm/mi0283qt.c b/drivers/gpu/drm/tinydrm/mi0283qt.c
index 3d067c2ba1bc..ea14f89cf9af 100644
--- a/drivers/gpu/drm/tinydrm/mi0283qt.c
+++ b/drivers/gpu/drm/tinydrm/mi0283qt.c
@@ -54,8 +54,7 @@ static void mi0283qt_enable(struct drm_simple_display_pipe *pipe,
 			    struct drm_crtc_state *crtc_state,
 			    struct drm_plane_state *plane_state)
 {
-	struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
-	struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
+	struct mipi_dbi *mipi = drm_to_mipi_dbi(pipe->crtc.dev);
 	u8 addr_mode;
 	int ret;
 
diff --git a/drivers/gpu/drm/tinydrm/mipi-dbi.c b/drivers/gpu/drm/tinydrm/mipi-dbi.c
index d1d546f3a664..3c66a844fe56 100644
--- a/drivers/gpu/drm/tinydrm/mipi-dbi.c
+++ b/drivers/gpu/drm/tinydrm/mipi-dbi.c
@@ -216,8 +216,7 @@ EXPORT_SYMBOL(mipi_dbi_buf_copy);
 static void mipi_dbi_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect)
 {
 	struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
-	struct tinydrm_device *tdev = fb->dev->dev_private;
-	struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
+	struct mipi_dbi *mipi = drm_to_mipi_dbi(fb->dev);
 	unsigned int height = rect->y2 - rect->y1;
 	unsigned int width = rect->x2 - rect->x1;
 	bool swap = mipi->swap_bytes;
@@ -342,8 +341,7 @@ static void mipi_dbi_blank(struct mipi_dbi *mipi)
  */
 void mipi_dbi_pipe_disable(struct drm_simple_display_pipe *pipe)
 {
-	struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
-	struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
+	struct mipi_dbi *mipi = drm_to_mipi_dbi(pipe->crtc.dev);
 
 	DRM_DEBUG_KMS("\n");
 
@@ -1098,8 +1096,7 @@ static const struct file_operations mipi_dbi_debugfs_command_fops = {
  */
 int mipi_dbi_debugfs_init(struct drm_minor *minor)
 {
-	struct tinydrm_device *tdev = minor->dev->dev_private;
-	struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
+	struct mipi_dbi *mipi = drm_to_mipi_dbi(minor->dev);
 	umode_t mode = S_IFREG | S_IWUSR;
 
 	if (mipi->read_commands)
diff --git a/drivers/gpu/drm/tinydrm/st7586.c b/drivers/gpu/drm/tinydrm/st7586.c
index 6cb68dd7e7b6..c8df337bf35e 100644
--- a/drivers/gpu/drm/tinydrm/st7586.c
+++ b/drivers/gpu/drm/tinydrm/st7586.c
@@ -116,8 +116,7 @@ static int st7586_buf_copy(void *dst, struct drm_framebuffer *fb,
 
 static void st7586_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect)
 {
-	struct tinydrm_device *tdev = fb->dev->dev_private;
-	struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
+	struct mipi_dbi *mipi = drm_to_mipi_dbi(fb->dev);
 	int start, end;
 	int ret = 0;
 
@@ -175,8 +174,7 @@ static void st7586_pipe_enable(struct drm_simple_display_pipe *pipe,
 			       struct drm_crtc_state *crtc_state,
 			       struct drm_plane_state *plane_state)
 {
-	struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
-	struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
+	struct mipi_dbi *mipi = drm_to_mipi_dbi(pipe->crtc.dev);
 	struct drm_framebuffer *fb = plane_state->fb;
 	struct drm_rect rect = {
 		.x1 = 0,
@@ -248,8 +246,7 @@ static void st7586_pipe_enable(struct drm_simple_display_pipe *pipe,
 
 static void st7586_pipe_disable(struct drm_simple_display_pipe *pipe)
 {
-	struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
-	struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
+	struct mipi_dbi *mipi = drm_to_mipi_dbi(pipe->crtc.dev);
 
 	DRM_DEBUG_KMS("\n");
 
diff --git a/drivers/gpu/drm/tinydrm/st7735r.c b/drivers/gpu/drm/tinydrm/st7735r.c
index 6c7904c205f0..8a13669b77cc 100644
--- a/drivers/gpu/drm/tinydrm/st7735r.c
+++ b/drivers/gpu/drm/tinydrm/st7735r.c
@@ -41,8 +41,7 @@ static void jd_t18003_t01_pipe_enable(struct drm_simple_display_pipe *pipe,
 				      struct drm_crtc_state *crtc_state,
 				      struct drm_plane_state *plane_state)
 {
-	struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
-	struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
+	struct mipi_dbi *mipi = drm_to_mipi_dbi(pipe->crtc.dev);
 	int ret;
 	u8 addr_mode;
 
diff --git a/include/drm/tinydrm/mipi-dbi.h b/include/drm/tinydrm/mipi-dbi.h
index f4ec2834bc22..ad7e6bedab5f 100644
--- a/include/drm/tinydrm/mipi-dbi.h
+++ b/include/drm/tinydrm/mipi-dbi.h
@@ -56,9 +56,10 @@ struct mipi_dbi {
 	struct regulator *regulator;
 };
 
-static inline struct mipi_dbi *
-mipi_dbi_from_tinydrm(struct tinydrm_device *tdev)
+static inline struct mipi_dbi *drm_to_mipi_dbi(struct drm_device *drm)
 {
+	struct tinydrm_device *tdev = drm->dev_private;
+
 	return container_of(tdev, struct mipi_dbi, tinydrm);
 }
 
-- 
2.20.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* [PATCH 06/11] drm/tinydrm: Remove tinydrm_shutdown()
  2019-01-20 11:43 [PATCH 00/11] drm/tinydrm: Remove tinydrm_device Noralf Trønnes
                   ` (4 preceding siblings ...)
  2019-01-20 11:43 ` [PATCH 05/11] drm/tinydrm/mipi-dbi: Add drm_to_mipi_dbi() Noralf Trønnes
@ 2019-01-20 11:43 ` Noralf Trønnes
  2019-01-21  7:12   ` Sam Ravnborg
  2019-01-20 11:43 ` [PATCH 07/11] drm/tinydrm/repaper: Use devm_drm_dev_*() Noralf Trønnes
                   ` (5 subsequent siblings)
  11 siblings, 1 reply; 57+ messages in thread
From: Noralf Trønnes @ 2019-01-20 11:43 UTC (permalink / raw)
  To: dri-devel; +Cc: david

It's just a wrapper around drm_atomic_helper_shutdown() now.
Also store drm_device in the drvdata field, since that's what's used.

Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
---
 drivers/gpu/drm/tinydrm/core/tinydrm-core.c | 14 --------------
 drivers/gpu/drm/tinydrm/hx8357d.c           |  7 +++----
 drivers/gpu/drm/tinydrm/ili9225.c           |  7 +++----
 drivers/gpu/drm/tinydrm/ili9341.c           |  7 +++----
 drivers/gpu/drm/tinydrm/mi0283qt.c          | 15 +++++----------
 drivers/gpu/drm/tinydrm/repaper.c           |  8 ++++----
 drivers/gpu/drm/tinydrm/st7586.c            |  7 +++----
 drivers/gpu/drm/tinydrm/st7735r.c           |  7 +++----
 include/drm/tinydrm/tinydrm.h               |  1 -
 9 files changed, 24 insertions(+), 49 deletions(-)

diff --git a/drivers/gpu/drm/tinydrm/core/tinydrm-core.c b/drivers/gpu/drm/tinydrm/core/tinydrm-core.c
index 614f532ea89f..e4a77feaacd6 100644
--- a/drivers/gpu/drm/tinydrm/core/tinydrm-core.c
+++ b/drivers/gpu/drm/tinydrm/core/tinydrm-core.c
@@ -166,18 +166,4 @@ int devm_tinydrm_register(struct tinydrm_device *tdev)
 }
 EXPORT_SYMBOL(devm_tinydrm_register);
 
-/**
- * tinydrm_shutdown - Shutdown tinydrm
- * @tdev: tinydrm device
- *
- * This function makes sure that the display pipeline is disabled.
- * Used by drivers in their shutdown callback to turn off the display
- * on machine shutdown and reboot.
- */
-void tinydrm_shutdown(struct tinydrm_device *tdev)
-{
-	drm_atomic_helper_shutdown(tdev->drm);
-}
-EXPORT_SYMBOL(tinydrm_shutdown);
-
 MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/tinydrm/hx8357d.c b/drivers/gpu/drm/tinydrm/hx8357d.c
index ab604513b865..84dda622df85 100644
--- a/drivers/gpu/drm/tinydrm/hx8357d.c
+++ b/drivers/gpu/drm/tinydrm/hx8357d.c
@@ -16,6 +16,7 @@
 #include <linux/property.h>
 #include <linux/spi/spi.h>
 
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_drv.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_gem_framebuffer_helper.h>
@@ -242,16 +243,14 @@ static int hx8357d_probe(struct spi_device *spi)
 	if (ret)
 		return ret;
 
-	spi_set_drvdata(spi, mipi);
+	spi_set_drvdata(spi, mipi->tinydrm.drm);
 
 	return devm_tinydrm_register(&mipi->tinydrm);
 }
 
 static void hx8357d_shutdown(struct spi_device *spi)
 {
-	struct mipi_dbi *mipi = spi_get_drvdata(spi);
-
-	tinydrm_shutdown(&mipi->tinydrm);
+	drm_atomic_helper_shutdown(spi_get_drvdata(spi));
 }
 
 static struct spi_driver hx8357d_spi_driver = {
diff --git a/drivers/gpu/drm/tinydrm/ili9225.c b/drivers/gpu/drm/tinydrm/ili9225.c
index 40e1f98ca393..3f59cfbd31ba 100644
--- a/drivers/gpu/drm/tinydrm/ili9225.c
+++ b/drivers/gpu/drm/tinydrm/ili9225.c
@@ -20,6 +20,7 @@
 #include <linux/spi/spi.h>
 #include <video/mipi_display.h>
 
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_damage_helper.h>
 #include <drm/drm_drv.h>
 #include <drm/drm_fb_cma_helper.h>
@@ -396,16 +397,14 @@ static int ili9225_probe(struct spi_device *spi)
 	if (ret)
 		return ret;
 
-	spi_set_drvdata(spi, mipi);
+	spi_set_drvdata(spi, mipi->tinydrm.drm);
 
 	return devm_tinydrm_register(&mipi->tinydrm);
 }
 
 static void ili9225_shutdown(struct spi_device *spi)
 {
-	struct mipi_dbi *mipi = spi_get_drvdata(spi);
-
-	tinydrm_shutdown(&mipi->tinydrm);
+	drm_atomic_helper_shutdown(spi_get_drvdata(spi));
 }
 
 static struct spi_driver ili9225_spi_driver = {
diff --git a/drivers/gpu/drm/tinydrm/ili9341.c b/drivers/gpu/drm/tinydrm/ili9341.c
index 86f8884036b2..3b7565a6311e 100644
--- a/drivers/gpu/drm/tinydrm/ili9341.c
+++ b/drivers/gpu/drm/tinydrm/ili9341.c
@@ -15,6 +15,7 @@
 #include <linux/property.h>
 #include <linux/spi/spi.h>
 
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_drv.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_gem_framebuffer_helper.h>
@@ -204,16 +205,14 @@ static int ili9341_probe(struct spi_device *spi)
 	if (ret)
 		return ret;
 
-	spi_set_drvdata(spi, mipi);
+	spi_set_drvdata(spi, mipi->tinydrm.drm);
 
 	return devm_tinydrm_register(&mipi->tinydrm);
 }
 
 static void ili9341_shutdown(struct spi_device *spi)
 {
-	struct mipi_dbi *mipi = spi_get_drvdata(spi);
-
-	tinydrm_shutdown(&mipi->tinydrm);
+	drm_atomic_helper_shutdown(spi_get_drvdata(spi));
 }
 
 static struct spi_driver ili9341_spi_driver = {
diff --git a/drivers/gpu/drm/tinydrm/mi0283qt.c b/drivers/gpu/drm/tinydrm/mi0283qt.c
index ea14f89cf9af..da326fd1450d 100644
--- a/drivers/gpu/drm/tinydrm/mi0283qt.c
+++ b/drivers/gpu/drm/tinydrm/mi0283qt.c
@@ -17,6 +17,7 @@
 #include <linux/regulator/consumer.h>
 #include <linux/spi/spi.h>
 
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_drv.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_gem_framebuffer_helper.h>
@@ -217,30 +218,24 @@ static int mi0283qt_probe(struct spi_device *spi)
 	if (ret)
 		return ret;
 
-	spi_set_drvdata(spi, mipi);
+	spi_set_drvdata(spi, mipi->tinydrm.drm);
 
 	return devm_tinydrm_register(&mipi->tinydrm);
 }
 
 static void mi0283qt_shutdown(struct spi_device *spi)
 {
-	struct mipi_dbi *mipi = spi_get_drvdata(spi);
-
-	tinydrm_shutdown(&mipi->tinydrm);
+	drm_atomic_helper_shutdown(spi_get_drvdata(spi));
 }
 
 static int __maybe_unused mi0283qt_pm_suspend(struct device *dev)
 {
-	struct mipi_dbi *mipi = dev_get_drvdata(dev);
-
-	return drm_mode_config_helper_suspend(mipi->tinydrm.drm);
+	return drm_mode_config_helper_suspend(dev_get_drvdata(dev));
 }
 
 static int __maybe_unused mi0283qt_pm_resume(struct device *dev)
 {
-	struct mipi_dbi *mipi = dev_get_drvdata(dev);
-
-	drm_mode_config_helper_resume(mipi->tinydrm.drm);
+	drm_mode_config_helper_resume(dev_get_drvdata(dev));
 
 	return 0;
 }
diff --git a/drivers/gpu/drm/tinydrm/repaper.c b/drivers/gpu/drm/tinydrm/repaper.c
index 1d551744cc9b..3141241ca8f0 100644
--- a/drivers/gpu/drm/tinydrm/repaper.c
+++ b/drivers/gpu/drm/tinydrm/repaper.c
@@ -26,6 +26,7 @@
 #include <linux/spi/spi.h>
 #include <linux/thermal.h>
 
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_damage_helper.h>
 #include <drm/drm_drv.h>
 #include <drm/drm_fb_cma_helper.h>
@@ -1085,7 +1086,8 @@ static int repaper_probe(struct spi_device *spi)
 
 	drm_simple_connector_set_mode_config(connector);
 	drm_mode_config_reset(tdev->drm);
-	spi_set_drvdata(spi, tdev);
+
+	spi_set_drvdata(spi, drm);
 
 	DRM_DEBUG_DRIVER("SPI speed: %uMHz\n", spi->max_speed_hz / 1000000);
 
@@ -1094,9 +1096,7 @@ static int repaper_probe(struct spi_device *spi)
 
 static void repaper_shutdown(struct spi_device *spi)
 {
-	struct tinydrm_device *tdev = spi_get_drvdata(spi);
-
-	tinydrm_shutdown(tdev);
+	drm_atomic_helper_shutdown(spi_get_drvdata(spi));
 }
 
 static struct spi_driver repaper_spi_driver = {
diff --git a/drivers/gpu/drm/tinydrm/st7586.c b/drivers/gpu/drm/tinydrm/st7586.c
index c8df337bf35e..99be557cc973 100644
--- a/drivers/gpu/drm/tinydrm/st7586.c
+++ b/drivers/gpu/drm/tinydrm/st7586.c
@@ -17,6 +17,7 @@
 #include <linux/spi/spi.h>
 #include <video/mipi_display.h>
 
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_damage_helper.h>
 #include <drm/drm_drv.h>
 #include <drm/drm_fb_cma_helper.h>
@@ -393,16 +394,14 @@ static int st7586_probe(struct spi_device *spi)
 	if (ret)
 		return ret;
 
-	spi_set_drvdata(spi, mipi);
+	spi_set_drvdata(spi, mipi->tinydrm.drm);
 
 	return devm_tinydrm_register(&mipi->tinydrm);
 }
 
 static void st7586_shutdown(struct spi_device *spi)
 {
-	struct mipi_dbi *mipi = spi_get_drvdata(spi);
-
-	tinydrm_shutdown(&mipi->tinydrm);
+	drm_atomic_helper_shutdown(spi_get_drvdata(spi));
 }
 
 static struct spi_driver st7586_spi_driver = {
diff --git a/drivers/gpu/drm/tinydrm/st7735r.c b/drivers/gpu/drm/tinydrm/st7735r.c
index 8a13669b77cc..bfa7e2221540 100644
--- a/drivers/gpu/drm/tinydrm/st7735r.c
+++ b/drivers/gpu/drm/tinydrm/st7735r.c
@@ -14,6 +14,7 @@
 #include <linux/spi/spi.h>
 #include <video/mipi_display.h>
 
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_drv.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_gem_framebuffer_helper.h>
@@ -182,16 +183,14 @@ static int st7735r_probe(struct spi_device *spi)
 	if (ret)
 		return ret;
 
-	spi_set_drvdata(spi, mipi);
+	spi_set_drvdata(spi, mipi->tinydrm.drm);
 
 	return devm_tinydrm_register(&mipi->tinydrm);
 }
 
 static void st7735r_shutdown(struct spi_device *spi)
 {
-	struct mipi_dbi *mipi = spi_get_drvdata(spi);
-
-	tinydrm_shutdown(&mipi->tinydrm);
+	drm_atomic_helper_shutdown(spi_get_drvdata(spi));
 }
 
 static struct spi_driver st7735r_spi_driver = {
diff --git a/include/drm/tinydrm/tinydrm.h b/include/drm/tinydrm/tinydrm.h
index 69c4363fd88e..ee9b17759391 100644
--- a/include/drm/tinydrm/tinydrm.h
+++ b/include/drm/tinydrm/tinydrm.h
@@ -38,6 +38,5 @@ pipe_to_tinydrm(struct drm_simple_display_pipe *pipe)
 int devm_tinydrm_init(struct device *parent, struct tinydrm_device *tdev,
 		      struct drm_driver *driver);
 int devm_tinydrm_register(struct tinydrm_device *tdev);
-void tinydrm_shutdown(struct tinydrm_device *tdev);
 
 #endif /* __LINUX_TINYDRM_H */
-- 
2.20.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* [PATCH 07/11] drm/tinydrm/repaper: Use devm_drm_dev_*()
  2019-01-20 11:43 [PATCH 00/11] drm/tinydrm: Remove tinydrm_device Noralf Trønnes
                   ` (5 preceding siblings ...)
  2019-01-20 11:43 ` [PATCH 06/11] drm/tinydrm: Remove tinydrm_shutdown() Noralf Trønnes
@ 2019-01-20 11:43 ` Noralf Trønnes
  2019-01-20 22:22   ` Sam Ravnborg
  2019-01-20 11:43 ` [PATCH 08/11] drm/tinydrm: " Noralf Trønnes
                   ` (4 subsequent siblings)
  11 siblings, 1 reply; 57+ messages in thread
From: Noralf Trønnes @ 2019-01-20 11:43 UTC (permalink / raw)
  To: dri-devel; +Cc: david

Use devm_drm_dev_init(), devm_drm_dev_register_with_fbdev() and drop
using tinydrm_device.

Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
---
 drivers/gpu/drm/tinydrm/repaper.c | 63 ++++++++++++++++++++-----------
 1 file changed, 40 insertions(+), 23 deletions(-)

diff --git a/drivers/gpu/drm/tinydrm/repaper.c b/drivers/gpu/drm/tinydrm/repaper.c
index 3141241ca8f0..4c8205565668 100644
--- a/drivers/gpu/drm/tinydrm/repaper.c
+++ b/drivers/gpu/drm/tinydrm/repaper.c
@@ -34,7 +34,7 @@
 #include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_rect.h>
 #include <drm/drm_vblank.h>
-#include <drm/tinydrm/tinydrm.h>
+#include <drm/drm_simple_kms_helper.h>
 #include <drm/tinydrm/tinydrm-helpers.h>
 
 #define REPAPER_RID_G2_COG_ID	0x12
@@ -60,7 +60,8 @@ enum repaper_epd_border_byte {
 };
 
 struct repaper_epd {
-	struct tinydrm_device tinydrm;
+	struct drm_device base;
+	struct drm_simple_display_pipe pipe;
 	struct spi_device *spi;
 
 	struct gpio_desc *panel_on;
@@ -89,10 +90,9 @@ struct repaper_epd {
 	bool partial;
 };
 
-static inline struct repaper_epd *
-epd_from_tinydrm(struct tinydrm_device *tdev)
+static inline struct repaper_epd *drm_to_epd(struct drm_device *drm)
 {
-	return container_of(tdev, struct repaper_epd, tinydrm);
+	return container_of(drm, struct repaper_epd, base);
 }
 
 static int repaper_spi_transfer(struct spi_device *spi, u8 header,
@@ -530,8 +530,7 @@ static int repaper_fb_dirty(struct drm_framebuffer *fb)
 {
 	struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
 	struct dma_buf_attachment *import_attach = cma_obj->base.import_attach;
-	struct tinydrm_device *tdev = fb->dev->dev_private;
-	struct repaper_epd *epd = epd_from_tinydrm(tdev);
+	struct repaper_epd *epd = drm_to_epd(fb->dev);
 	struct drm_rect clip;
 	u8 *buf = NULL;
 	int ret = 0;
@@ -646,8 +645,7 @@ static void repaper_pipe_enable(struct drm_simple_display_pipe *pipe,
 				struct drm_crtc_state *crtc_state,
 				struct drm_plane_state *plane_state)
 {
-	struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
-	struct repaper_epd *epd = epd_from_tinydrm(tdev);
+	struct repaper_epd *epd = drm_to_epd(pipe->crtc.dev);
 	struct spi_device *spi = epd->spi;
 	struct device *dev = &spi->dev;
 	bool dc_ok = false;
@@ -781,8 +779,7 @@ static void repaper_pipe_enable(struct drm_simple_display_pipe *pipe,
 
 static void repaper_pipe_disable(struct drm_simple_display_pipe *pipe)
 {
-	struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
-	struct repaper_epd *epd = epd_from_tinydrm(tdev);
+	struct repaper_epd *epd = drm_to_epd(pipe->crtc.dev);
 	struct spi_device *spi = epd->spi;
 	unsigned int line;
 
@@ -856,6 +853,23 @@ static const struct drm_simple_display_pipe_funcs repaper_pipe_funcs = {
 	.prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb,
 };
 
+static const struct drm_mode_config_funcs repaper_mode_config_funcs = {
+	.fb_create = drm_gem_fb_create_with_dirty,
+	.atomic_check = drm_atomic_helper_check,
+	.atomic_commit = drm_atomic_helper_commit,
+};
+
+static void repaper_release(struct drm_device *drm)
+{
+	struct repaper_epd *epd = drm_to_epd(drm);
+
+	DRM_DEBUG_DRIVER("\n");
+
+	drm_mode_config_cleanup(drm);
+	drm_dev_fini(drm);
+	kfree(epd);
+}
+
 static const uint32_t repaper_formats[] = {
 	DRM_FORMAT_XRGB8888,
 };
@@ -894,6 +908,7 @@ static struct drm_driver repaper_driver = {
 	.driver_features	= DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
 				  DRIVER_ATOMIC,
 	.fops			= &repaper_fops,
+	.release		= repaper_release,
 	DRM_GEM_CMA_VMAP_DRIVER_OPS,
 	.name			= "repaper",
 	.desc			= "Pervasive Displays RePaper e-ink panels",
@@ -927,7 +942,6 @@ static int repaper_probe(struct spi_device *spi)
 	const struct of_device_id *match;
 	struct drm_connector *connector;
 	struct device *dev = &spi->dev;
-	struct tinydrm_device *tdev;
 	enum repaper_model model;
 	const char *thermal_zone;
 	struct repaper_epd *epd;
@@ -952,10 +966,20 @@ static int repaper_probe(struct spi_device *spi)
 		}
 	}
 
-	epd = devm_kzalloc(dev, sizeof(*epd), GFP_KERNEL);
+	epd = kzalloc(sizeof(*epd), GFP_KERNEL);
 	if (!epd)
 		return -ENOMEM;
 
+	drm = &epd->base;
+	ret = devm_drm_dev_init(dev, drm, &repaper_driver);
+	if (ret) {
+		kfree(epd);
+		return ret;
+	}
+
+	drm_mode_config_init(drm);
+	drm->mode_config.funcs = &repaper_mode_config_funcs;
+
 	epd->spi = spi;
 
 	epd->panel_on = devm_gpiod_get(dev, "panel-on", GPIOD_OUT_LOW);
@@ -1066,32 +1090,25 @@ static int repaper_probe(struct spi_device *spi)
 	if (!epd->current_frame)
 		return -ENOMEM;
 
-	tdev = &epd->tinydrm;
-
-	ret = devm_tinydrm_init(dev, tdev, &repaper_driver);
-	if (ret)
-		return ret;
-
-	drm = tdev->drm;
 
 	connector = drm_simple_connector_create(drm, DRM_MODE_CONNECTOR_VIRTUAL, mode, 0);
 	if (IS_ERR(connector))
 		return PTR_ERR(connector);
 
-	ret = drm_simple_display_pipe_init(drm, &tdev->pipe, &repaper_pipe_funcs,
+	ret = drm_simple_display_pipe_init(drm, &epd->pipe, &repaper_pipe_funcs,
 					   repaper_formats, ARRAY_SIZE(repaper_formats),
 					   NULL, connector);
 	if (ret)
 		return ret;
 
 	drm_simple_connector_set_mode_config(connector);
-	drm_mode_config_reset(tdev->drm);
+	drm_mode_config_reset(drm);
 
 	spi_set_drvdata(spi, drm);
 
 	DRM_DEBUG_DRIVER("SPI speed: %uMHz\n", spi->max_speed_hz / 1000000);
 
-	return devm_tinydrm_register(tdev);
+	return devm_drm_dev_register_with_fbdev(drm, 0);
 }
 
 static void repaper_shutdown(struct spi_device *spi)
-- 
2.20.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* [PATCH 08/11] drm/tinydrm: Use devm_drm_dev_*()
  2019-01-20 11:43 [PATCH 00/11] drm/tinydrm: Remove tinydrm_device Noralf Trønnes
                   ` (6 preceding siblings ...)
  2019-01-20 11:43 ` [PATCH 07/11] drm/tinydrm/repaper: Use devm_drm_dev_*() Noralf Trønnes
@ 2019-01-20 11:43 ` Noralf Trønnes
  2019-01-20 11:43 ` [PATCH 09/11] drm/tinydrm: Remove tinydrm_device Noralf Trønnes
                   ` (3 subsequent siblings)
  11 siblings, 0 replies; 57+ messages in thread
From: Noralf Trønnes @ 2019-01-20 11:43 UTC (permalink / raw)
  To: dri-devel; +Cc: david

Use devm_drm_dev_init(), devm_drm_dev_register_with_fbdev() and drop
using tinydrm_device.

Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
---
 drivers/gpu/drm/tinydrm/hx8357d.c  |  25 +++++--
 drivers/gpu/drm/tinydrm/ili9225.c  |  25 +++++--
 drivers/gpu/drm/tinydrm/ili9341.c  |  25 +++++--
 drivers/gpu/drm/tinydrm/mi0283qt.c |  25 +++++--
 drivers/gpu/drm/tinydrm/mipi-dbi.c |  69 +++++++++++---------
 drivers/gpu/drm/tinydrm/st7586.c   | 101 ++++++++++++++---------------
 drivers/gpu/drm/tinydrm/st7735r.c  |  25 +++++--
 include/drm/tinydrm/mipi-dbi.h     |  26 +++++---
 8 files changed, 206 insertions(+), 115 deletions(-)

diff --git a/drivers/gpu/drm/tinydrm/hx8357d.c b/drivers/gpu/drm/tinydrm/hx8357d.c
index 84dda622df85..4648bd6d6282 100644
--- a/drivers/gpu/drm/tinydrm/hx8357d.c
+++ b/drivers/gpu/drm/tinydrm/hx8357d.c
@@ -189,6 +189,7 @@ DEFINE_DRM_GEM_CMA_FOPS(hx8357d_fops);
 static struct drm_driver hx8357d_driver = {
 	.driver_features	= DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC,
 	.fops			= &hx8357d_fops,
+	.release		= mipi_dbi_release,
 	DRM_GEM_CMA_VMAP_DRIVER_OPS,
 	.debugfs_init		= mipi_dbi_debugfs_init,
 	.name			= "hx8357d",
@@ -213,15 +214,25 @@ MODULE_DEVICE_TABLE(spi, hx8357d_id);
 static int hx8357d_probe(struct spi_device *spi)
 {
 	struct device *dev = &spi->dev;
+	struct drm_device *drm;
 	struct mipi_dbi *mipi;
 	struct gpio_desc *dc;
 	u32 rotation = 0;
 	int ret;
 
-	mipi = devm_kzalloc(dev, sizeof(*mipi), GFP_KERNEL);
+	mipi = kzalloc(sizeof(*mipi), GFP_KERNEL);
 	if (!mipi)
 		return -ENOMEM;
 
+	drm = &mipi->base;
+	ret = devm_drm_dev_init(dev, drm, &hx8357d_driver);
+	if (ret) {
+		kfree(mipi);
+		return ret;
+	}
+
+	drm_mode_config_init(drm);
+
 	dc = devm_gpiod_get(dev, "dc", GPIOD_OUT_LOW);
 	if (IS_ERR(dc)) {
 		DRM_DEV_ERROR(dev, "Failed to get gpio 'dc'\n");
@@ -238,14 +249,18 @@ static int hx8357d_probe(struct spi_device *spi)
 	if (ret)
 		return ret;
 
-	ret = mipi_dbi_init(&spi->dev, mipi, &hx8357d_pipe_funcs,
-			    &hx8357d_driver, &yx350hv15_mode, rotation);
+	ret = mipi_dbi_init(mipi, &hx8357d_pipe_funcs, &yx350hv15_mode, rotation);
 	if (ret)
 		return ret;
 
-	spi_set_drvdata(spi, mipi->tinydrm.drm);
+	drm_mode_config_reset(drm);
 
-	return devm_tinydrm_register(&mipi->tinydrm);
+	spi_set_drvdata(spi, drm);
+
+	DRM_DEBUG_DRIVER("SPI speed: %uMHz, rotation = %u\n",
+			 spi->max_speed_hz / 1000000, rotation);
+
+	return devm_drm_dev_register_with_fbdev(drm, 0);
 }
 
 static void hx8357d_shutdown(struct spi_device *spi)
diff --git a/drivers/gpu/drm/tinydrm/ili9225.c b/drivers/gpu/drm/tinydrm/ili9225.c
index 3f59cfbd31ba..20a44a43a64a 100644
--- a/drivers/gpu/drm/tinydrm/ili9225.c
+++ b/drivers/gpu/drm/tinydrm/ili9225.c
@@ -339,6 +339,7 @@ static struct drm_driver ili9225_driver = {
 	.driver_features	= DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
 				  DRIVER_ATOMIC,
 	.fops			= &ili9225_fops,
+	.release		= mipi_dbi_release,
 	DRM_GEM_CMA_VMAP_DRIVER_OPS,
 	.name			= "ili9225",
 	.desc			= "Ilitek ILI9225",
@@ -362,15 +363,25 @@ MODULE_DEVICE_TABLE(spi, ili9225_id);
 static int ili9225_probe(struct spi_device *spi)
 {
 	struct device *dev = &spi->dev;
+	struct drm_device *drm;
 	struct mipi_dbi *mipi;
 	struct gpio_desc *rs;
 	u32 rotation = 0;
 	int ret;
 
-	mipi = devm_kzalloc(dev, sizeof(*mipi), GFP_KERNEL);
+	mipi = kzalloc(sizeof(*mipi), GFP_KERNEL);
 	if (!mipi)
 		return -ENOMEM;
 
+	drm = &mipi->base;
+	ret = devm_drm_dev_init(dev, drm, &ili9225_driver);
+	if (ret) {
+		kfree(mipi);
+		return ret;
+	}
+
+	drm_mode_config_init(drm);
+
 	mipi->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
 	if (IS_ERR(mipi->reset)) {
 		DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n");
@@ -392,14 +403,18 @@ static int ili9225_probe(struct spi_device *spi)
 	/* override the command function set in  mipi_dbi_spi_init() */
 	mipi->command = ili9225_dbi_command;
 
-	ret = mipi_dbi_init(&spi->dev, mipi, &ili9225_pipe_funcs,
-			    &ili9225_driver, &ili9225_mode, rotation);
+	ret = mipi_dbi_init(mipi, &ili9225_pipe_funcs, &ili9225_mode, rotation);
 	if (ret)
 		return ret;
 
-	spi_set_drvdata(spi, mipi->tinydrm.drm);
+	drm_mode_config_reset(drm);
 
-	return devm_tinydrm_register(&mipi->tinydrm);
+	spi_set_drvdata(spi, drm);
+
+	DRM_DEBUG_DRIVER("SPI speed: %uMHz, rotation = %u\n",
+			 spi->max_speed_hz / 1000000, rotation);
+
+	return devm_drm_dev_register_with_fbdev(drm, 0);
 }
 
 static void ili9225_shutdown(struct spi_device *spi)
diff --git a/drivers/gpu/drm/tinydrm/ili9341.c b/drivers/gpu/drm/tinydrm/ili9341.c
index 3b7565a6311e..5ab3267b224a 100644
--- a/drivers/gpu/drm/tinydrm/ili9341.c
+++ b/drivers/gpu/drm/tinydrm/ili9341.c
@@ -145,6 +145,7 @@ DEFINE_DRM_GEM_CMA_FOPS(ili9341_fops);
 static struct drm_driver ili9341_driver = {
 	.driver_features	= DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_ATOMIC,
 	.fops			= &ili9341_fops,
+	.release		= mipi_dbi_release,
 	DRM_GEM_CMA_VMAP_DRIVER_OPS,
 	.debugfs_init		= mipi_dbi_debugfs_init,
 	.name			= "ili9341",
@@ -169,15 +170,25 @@ MODULE_DEVICE_TABLE(spi, ili9341_id);
 static int ili9341_probe(struct spi_device *spi)
 {
 	struct device *dev = &spi->dev;
+	struct drm_device *drm;
 	struct mipi_dbi *mipi;
 	struct gpio_desc *dc;
 	u32 rotation = 0;
 	int ret;
 
-	mipi = devm_kzalloc(dev, sizeof(*mipi), GFP_KERNEL);
+	mipi = kzalloc(sizeof(*mipi), GFP_KERNEL);
 	if (!mipi)
 		return -ENOMEM;
 
+	drm = &mipi->base;
+	ret = devm_drm_dev_init(dev, drm, &ili9341_driver);
+	if (ret) {
+		kfree(mipi);
+		return ret;
+	}
+
+	drm_mode_config_init(drm);
+
 	mipi->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
 	if (IS_ERR(mipi->reset)) {
 		DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n");
@@ -200,14 +211,18 @@ static int ili9341_probe(struct spi_device *spi)
 	if (ret)
 		return ret;
 
-	ret = mipi_dbi_init(&spi->dev, mipi, &ili9341_pipe_funcs,
-			    &ili9341_driver, &yx240qv29_mode, rotation);
+	ret = mipi_dbi_init(mipi, &ili9341_pipe_funcs, &yx240qv29_mode, rotation);
 	if (ret)
 		return ret;
 
-	spi_set_drvdata(spi, mipi->tinydrm.drm);
+	drm_mode_config_reset(drm);
 
-	return devm_tinydrm_register(&mipi->tinydrm);
+	spi_set_drvdata(spi, drm);
+
+	DRM_DEBUG_DRIVER("SPI speed: %uMHz, rotation = %u\n",
+			 spi->max_speed_hz / 1000000, rotation);
+
+	return devm_drm_dev_register_with_fbdev(drm, 0);
 }
 
 static void ili9341_shutdown(struct spi_device *spi)
diff --git a/drivers/gpu/drm/tinydrm/mi0283qt.c b/drivers/gpu/drm/tinydrm/mi0283qt.c
index da326fd1450d..bb0ce4514e60 100644
--- a/drivers/gpu/drm/tinydrm/mi0283qt.c
+++ b/drivers/gpu/drm/tinydrm/mi0283qt.c
@@ -154,6 +154,7 @@ static struct drm_driver mi0283qt_driver = {
 	.driver_features	= DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
 				  DRIVER_ATOMIC,
 	.fops			= &mi0283qt_fops,
+	.release		= mipi_dbi_release,
 	DRM_GEM_CMA_VMAP_DRIVER_OPS,
 	.debugfs_init		= mipi_dbi_debugfs_init,
 	.name			= "mi0283qt",
@@ -178,15 +179,25 @@ MODULE_DEVICE_TABLE(spi, mi0283qt_id);
 static int mi0283qt_probe(struct spi_device *spi)
 {
 	struct device *dev = &spi->dev;
+	struct drm_device *drm;
 	struct mipi_dbi *mipi;
 	struct gpio_desc *dc;
 	u32 rotation = 0;
 	int ret;
 
-	mipi = devm_kzalloc(dev, sizeof(*mipi), GFP_KERNEL);
+	mipi = kzalloc(sizeof(*mipi), GFP_KERNEL);
 	if (!mipi)
 		return -ENOMEM;
 
+	drm = &mipi->base;
+	ret = devm_drm_dev_init(dev, drm, &mi0283qt_driver);
+	if (ret) {
+		kfree(mipi);
+		return ret;
+	}
+
+	drm_mode_config_init(drm);
+
 	mipi->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
 	if (IS_ERR(mipi->reset)) {
 		DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n");
@@ -213,14 +224,18 @@ static int mi0283qt_probe(struct spi_device *spi)
 	if (ret)
 		return ret;
 
-	ret = mipi_dbi_init(&spi->dev, mipi, &mi0283qt_pipe_funcs,
-			    &mi0283qt_driver, &mi0283qt_mode, rotation);
+	ret = mipi_dbi_init(mipi, &mi0283qt_pipe_funcs, &mi0283qt_mode, rotation);
 	if (ret)
 		return ret;
 
-	spi_set_drvdata(spi, mipi->tinydrm.drm);
+	drm_mode_config_reset(drm);
 
-	return devm_tinydrm_register(&mipi->tinydrm);
+	spi_set_drvdata(spi, drm);
+
+	DRM_DEBUG_DRIVER("SPI speed: %uMHz, rotation = %u\n",
+			 spi->max_speed_hz / 1000000, rotation);
+
+	return devm_drm_dev_register_with_fbdev(drm, 0);
 }
 
 static void mi0283qt_shutdown(struct spi_device *spi)
diff --git a/drivers/gpu/drm/tinydrm/mipi-dbi.c b/drivers/gpu/drm/tinydrm/mipi-dbi.c
index 3c66a844fe56..861e04815104 100644
--- a/drivers/gpu/drm/tinydrm/mipi-dbi.c
+++ b/drivers/gpu/drm/tinydrm/mipi-dbi.c
@@ -316,7 +316,7 @@ EXPORT_SYMBOL(mipi_dbi_enable_flush);
 
 static void mipi_dbi_blank(struct mipi_dbi *mipi)
 {
-	struct drm_device *drm = mipi->tinydrm.drm;
+	struct drm_device *drm = &mipi->base;
 	u16 height = drm->mode_config.min_height;
 	u16 width = drm->mode_config.min_width;
 	size_t len = width * height * 2;
@@ -357,6 +357,12 @@ void mipi_dbi_pipe_disable(struct drm_simple_display_pipe *pipe)
 }
 EXPORT_SYMBOL(mipi_dbi_pipe_disable);
 
+static const struct drm_mode_config_funcs mipi_dbi_mode_config_funcs = {
+	.fb_create = drm_gem_fb_create_with_dirty,
+	.atomic_check = drm_atomic_helper_check,
+	.atomic_commit = drm_atomic_helper_commit,
+};
+
 static const uint32_t mipi_dbi_formats[] = {
 	DRM_FORMAT_RGB565,
 	DRM_FORMAT_XRGB8888,
@@ -364,33 +370,28 @@ static const uint32_t mipi_dbi_formats[] = {
 
 /**
  * mipi_dbi_init - MIPI DBI initialization
- * @dev: Parent device
  * @mipi: &mipi_dbi structure to initialize
- * @pipe_funcs: Display pipe functions
- * @driver: DRM driver
+ * @funcs: Display pipe functions
  * @mode: Display mode
  * @rotation: Initial rotation in degrees Counter Clock Wise
  *
- * This function initializes a &mipi_dbi structure and it's underlying
- * @tinydrm_device. It also sets up the display pipeline.
+ * This function sets up a &drm_simple_display_pipe with a &drm_connector that
+ * has one fixed &drm_display_mode which is rotated according to @rotation.
+ * This mode is used to set the mode config min/max width/height properties.
+ * Additionally &mipi_dbi.tx_buf is allocated.
  *
  * Supported formats: Native RGB565 and emulated XRGB8888.
  *
- * Objects created by this function will be automatically freed on driver
- * detach (devres).
- *
  * Returns:
  * Zero on success, negative error code on failure.
  */
-int mipi_dbi_init(struct device *dev, struct mipi_dbi *mipi,
-		  const struct drm_simple_display_pipe_funcs *pipe_funcs,
-		  struct drm_driver *driver,
+int mipi_dbi_init(struct mipi_dbi *mipi,
+		  const struct drm_simple_display_pipe_funcs *funcs,
 		  const struct drm_display_mode *mode, unsigned int rotation)
 {
 	size_t bufsize = mode->vdisplay * mode->hdisplay * sizeof(u16);
-	struct tinydrm_device *tdev = &mipi->tinydrm;
+	struct drm_device *drm = &mipi->base;
 	struct drm_connector *connector;
-	struct drm_device *drm;
 	int ret;
 	static const uint64_t modifiers[] = {
 		DRM_FORMAT_MOD_LINEAR,
@@ -402,41 +403,51 @@ int mipi_dbi_init(struct device *dev, struct mipi_dbi *mipi,
 
 	mutex_init(&mipi->cmdlock);
 
-	mipi->tx_buf = devm_kmalloc(dev, bufsize, GFP_KERNEL);
+	mipi->tx_buf = devm_kmalloc(drm->dev, bufsize, GFP_KERNEL);
 	if (!mipi->tx_buf)
 		return -ENOMEM;
 
-	ret = devm_tinydrm_init(dev, tdev, driver);
-	if (ret)
-		return ret;
-
-	drm = tdev->drm;
-
 	connector = drm_simple_connector_create(drm, DRM_MODE_CONNECTOR_VIRTUAL, mode, rotation);
 	if (IS_ERR(connector))
 		return PTR_ERR(connector);
 
-	ret = drm_simple_display_pipe_init(drm, &tdev->pipe, pipe_funcs,
+	ret = drm_simple_display_pipe_init(drm, &mipi->pipe, funcs,
 					   mipi_dbi_formats, ARRAY_SIZE(mipi_dbi_formats),
 					   modifiers, connector);
 	if (ret)
 		return ret;
 
-	drm_plane_enable_fb_damage_clips(&tdev->pipe.plane);
+	drm_plane_enable_fb_damage_clips(&mipi->pipe.plane);
 
+	drm->mode_config.funcs = &mipi_dbi_mode_config_funcs;
 	drm_simple_connector_set_mode_config(connector);
 	drm->mode_config.preferred_depth = 16;
 	mipi->rotation = rotation;
 
-	drm_mode_config_reset(tdev->drm);
-
-	DRM_DEBUG_KMS("preferred_depth=%u, rotation = %u\n",
-		      tdev->drm->mode_config.preferred_depth, rotation);
-
 	return 0;
 }
 EXPORT_SYMBOL(mipi_dbi_init);
 
+/**
+ * mipi_dbi_release - DRM driver release helper
+ * @drm: DRM device
+ *
+ * This function finalizes and frees &mipi_dbi.
+ *
+ * Drivers can use this as their &drm_driver->release callback.
+ */
+void mipi_dbi_release(struct drm_device *drm)
+{
+	struct mipi_dbi *dbi = drm_to_mipi_dbi(drm);
+
+	DRM_DEBUG_DRIVER("\n");
+
+	drm_mode_config_cleanup(drm);
+	drm_dev_fini(drm);
+	kfree(dbi);
+}
+EXPORT_SYMBOL(mipi_dbi_release);
+
 /**
  * mipi_dbi_hw_reset - Hardware reset of controller
  * @mipi: MIPI DBI structure
@@ -489,7 +500,7 @@ EXPORT_SYMBOL(mipi_dbi_display_is_on);
 
 static int mipi_dbi_poweron_reset_conditional(struct mipi_dbi *mipi, bool cond)
 {
-	struct device *dev = mipi->tinydrm.drm->dev;
+	struct device *dev = mipi->base.dev;
 	int ret;
 
 	if (mipi->regulator) {
diff --git a/drivers/gpu/drm/tinydrm/st7586.c b/drivers/gpu/drm/tinydrm/st7586.c
index 99be557cc973..cae003318b42 100644
--- a/drivers/gpu/drm/tinydrm/st7586.c
+++ b/drivers/gpu/drm/tinydrm/st7586.c
@@ -262,53 +262,6 @@ static const u32 st7586_formats[] = {
 	DRM_FORMAT_XRGB8888,
 };
 
-static int st7586_init(struct device *dev, struct mipi_dbi *mipi,
-		const struct drm_simple_display_pipe_funcs *pipe_funcs,
-		struct drm_driver *driver, const struct drm_display_mode *mode,
-		unsigned int rotation)
-{
-	size_t bufsize = (mode->vdisplay + 2) / 3 * mode->hdisplay;
-	struct tinydrm_device *tdev = &mipi->tinydrm;
-	struct drm_connector *connector;
-	struct drm_device *drm;
-	int ret;
-
-	mutex_init(&mipi->cmdlock);
-
-	mipi->tx_buf = devm_kmalloc(dev, bufsize, GFP_KERNEL);
-	if (!mipi->tx_buf)
-		return -ENOMEM;
-
-	ret = devm_tinydrm_init(dev, tdev, driver);
-	if (ret)
-		return ret;
-
-	drm = tdev->drm;
-
-	connector = drm_simple_connector_create(drm, DRM_MODE_CONNECTOR_VIRTUAL, mode, rotation);
-	if (IS_ERR(connector))
-		return PTR_ERR(connector);
-
-	ret = drm_simple_display_pipe_init(drm, &tdev->pipe, pipe_funcs,
-					   st7586_formats, ARRAY_SIZE(st7586_formats),
-					   NULL, connector);
-	if (ret)
-		return ret;
-
-	drm_plane_enable_fb_damage_clips(&tdev->pipe.plane);
-
-	drm_simple_connector_set_mode_config(connector);
-	drm->mode_config.preferred_depth = 32;
-	mipi->rotation = rotation;
-
-	drm_mode_config_reset(tdev->drm);
-
-	DRM_DEBUG_KMS("preferred_depth=%u, rotation = %u\n",
-		      tdev->drm->mode_config.preferred_depth, rotation);
-
-	return 0;
-}
-
 static const struct drm_simple_display_pipe_funcs st7586_pipe_funcs = {
 	.enable		= st7586_pipe_enable,
 	.disable	= st7586_pipe_disable,
@@ -316,6 +269,12 @@ static const struct drm_simple_display_pipe_funcs st7586_pipe_funcs = {
 	.prepare_fb	= drm_gem_fb_simple_display_pipe_prepare_fb,
 };
 
+static const struct drm_mode_config_funcs st7586_mode_config_funcs = {
+	.fb_create = drm_gem_fb_create_with_dirty,
+	.atomic_check = drm_atomic_helper_check,
+	.atomic_commit = drm_atomic_helper_commit,
+};
+
 static const struct drm_display_mode st7586_mode = {
 	DRM_SIMPLE_MODE(178, 128, 37, 27),
 };
@@ -326,6 +285,7 @@ static struct drm_driver st7586_driver = {
 	.driver_features	= DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
 				  DRIVER_ATOMIC,
 	.fops			= &st7586_fops,
+	.release		= mipi_dbi_release,
 	DRM_GEM_CMA_VMAP_DRIVER_OPS,
 	.debugfs_init		= mipi_dbi_debugfs_init,
 	.name			= "st7586",
@@ -349,16 +309,37 @@ MODULE_DEVICE_TABLE(spi, st7586_id);
 
 static int st7586_probe(struct spi_device *spi)
 {
+	struct drm_connector *connector;
 	struct device *dev = &spi->dev;
+	struct drm_device *drm;
 	struct mipi_dbi *mipi;
 	struct gpio_desc *a0;
 	u32 rotation = 0;
+	size_t bufsize;
 	int ret;
 
-	mipi = devm_kzalloc(dev, sizeof(*mipi), GFP_KERNEL);
+	mipi = kzalloc(sizeof(*mipi), GFP_KERNEL);
 	if (!mipi)
 		return -ENOMEM;
 
+	drm = &mipi->base;
+	ret = devm_drm_dev_init(dev, drm, &st7586_driver);
+	if (ret) {
+		kfree(mipi);
+		return ret;
+	}
+
+	drm_mode_config_init(drm);
+	drm->mode_config.preferred_depth = 32;
+	drm->mode_config.funcs = &st7586_mode_config_funcs;
+
+	mutex_init(&mipi->cmdlock);
+
+	bufsize = (st7586_mode.vdisplay + 2) / 3 * st7586_mode.hdisplay;
+	mipi->tx_buf = devm_kmalloc(dev, bufsize, GFP_KERNEL);
+	if (!mipi->tx_buf)
+		return -ENOMEM;
+
 	mipi->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
 	if (IS_ERR(mipi->reset)) {
 		DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n");
@@ -372,6 +353,7 @@ static int st7586_probe(struct spi_device *spi)
 	}
 
 	device_property_read_u32(dev, "rotation", &rotation);
+	mipi->rotation = rotation;
 
 	ret = mipi_dbi_spi_init(spi, mipi, a0);
 	if (ret)
@@ -389,14 +371,29 @@ static int st7586_probe(struct spi_device *spi)
 	 */
 	mipi->swap_bytes = true;
 
-	ret = st7586_init(&spi->dev, mipi, &st7586_pipe_funcs, &st7586_driver,
-			  &st7586_mode, rotation);
+	connector = drm_simple_connector_create(drm, DRM_MODE_CONNECTOR_VIRTUAL,
+						&st7586_mode, rotation);
+	if (IS_ERR(connector))
+		return PTR_ERR(connector);
+
+	ret = drm_simple_display_pipe_init(drm, &mipi->pipe, &st7586_pipe_funcs,
+					   st7586_formats, ARRAY_SIZE(st7586_formats),
+					   NULL, connector);
 	if (ret)
 		return ret;
 
-	spi_set_drvdata(spi, mipi->tinydrm.drm);
+	drm_plane_enable_fb_damage_clips(&mipi->pipe.plane);
 
-	return devm_tinydrm_register(&mipi->tinydrm);
+	drm_simple_connector_set_mode_config(connector);
+
+	drm_mode_config_reset(drm);
+
+	spi_set_drvdata(spi, drm);
+
+	DRM_DEBUG_DRIVER("SPI speed: %uMHz, rotation = %u\n",
+			 spi->max_speed_hz / 1000000, rotation);
+
+	return devm_drm_dev_register_with_fbdev(drm, 0);
 }
 
 static void st7586_shutdown(struct spi_device *spi)
diff --git a/drivers/gpu/drm/tinydrm/st7735r.c b/drivers/gpu/drm/tinydrm/st7735r.c
index bfa7e2221540..dcd07248884c 100644
--- a/drivers/gpu/drm/tinydrm/st7735r.c
+++ b/drivers/gpu/drm/tinydrm/st7735r.c
@@ -120,6 +120,7 @@ static struct drm_driver st7735r_driver = {
 	.driver_features	= DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
 				  DRIVER_ATOMIC,
 	.fops			= &st7735r_fops,
+	.release		= mipi_dbi_release,
 	DRM_GEM_CMA_VMAP_DRIVER_OPS,
 	.debugfs_init		= mipi_dbi_debugfs_init,
 	.name			= "st7735r",
@@ -144,15 +145,25 @@ MODULE_DEVICE_TABLE(spi, st7735r_id);
 static int st7735r_probe(struct spi_device *spi)
 {
 	struct device *dev = &spi->dev;
+	struct drm_device *drm;
 	struct mipi_dbi *mipi;
 	struct gpio_desc *dc;
 	u32 rotation = 0;
 	int ret;
 
-	mipi = devm_kzalloc(dev, sizeof(*mipi), GFP_KERNEL);
+	mipi = kzalloc(sizeof(*mipi), GFP_KERNEL);
 	if (!mipi)
 		return -ENOMEM;
 
+	drm = &mipi->base;
+	ret = devm_drm_dev_init(dev, drm, &st7735r_driver);
+	if (ret) {
+		kfree(mipi);
+		return ret;
+	}
+
+	drm_mode_config_init(drm);
+
 	mipi->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
 	if (IS_ERR(mipi->reset)) {
 		DRM_DEV_ERROR(dev, "Failed to get gpio 'reset'\n");
@@ -178,14 +189,18 @@ static int st7735r_probe(struct spi_device *spi)
 	/* Cannot read from Adafruit 1.8" display via SPI */
 	mipi->read_commands = NULL;
 
-	ret = mipi_dbi_init(&spi->dev, mipi, &jd_t18003_t01_pipe_funcs,
-			    &st7735r_driver, &jd_t18003_t01_mode, rotation);
+	ret = mipi_dbi_init(mipi, &jd_t18003_t01_pipe_funcs, &jd_t18003_t01_mode, rotation);
 	if (ret)
 		return ret;
 
-	spi_set_drvdata(spi, mipi->tinydrm.drm);
+	drm_mode_config_reset(drm);
 
-	return devm_tinydrm_register(&mipi->tinydrm);
+	spi_set_drvdata(spi, drm);
+
+	DRM_DEBUG_DRIVER("SPI speed: %uMHz, rotation = %u\n",
+			 spi->max_speed_hz / 1000000, rotation);
+
+	return devm_drm_dev_register_with_fbdev(drm, 0);
 }
 
 static void st7735r_shutdown(struct spi_device *spi)
diff --git a/include/drm/tinydrm/mipi-dbi.h b/include/drm/tinydrm/mipi-dbi.h
index ad7e6bedab5f..a678391e4cb1 100644
--- a/include/drm/tinydrm/mipi-dbi.h
+++ b/include/drm/tinydrm/mipi-dbi.h
@@ -12,7 +12,9 @@
 #ifndef __LINUX_MIPI_DBI_H
 #define __LINUX_MIPI_DBI_H
 
-#include <drm/tinydrm/tinydrm.h>
+#include <linux/mutex.h>
+#include <drm/drm_device.h>
+#include <drm/drm_simple_kms_helper.h>
 
 struct drm_rect;
 struct spi_device;
@@ -21,7 +23,6 @@ struct regulator;
 
 /**
  * struct mipi_dbi - MIPI DBI controller
- * @tinydrm: tinydrm base
  * @spi: SPI device
  * @enabled: Pipeline is enabled
  * @cmdlock: Command lock
@@ -39,7 +40,16 @@ struct regulator;
  * @regulator: power regulator (optional)
  */
 struct mipi_dbi {
-	struct tinydrm_device tinydrm;
+	/**
+	 * @base: DRM device
+	 */
+	struct drm_device base;
+
+	/**
+	 * @pipe: Display pipe structure
+	 */
+	struct drm_simple_display_pipe pipe;
+
 	struct spi_device *spi;
 	bool enabled;
 	struct mutex cmdlock;
@@ -58,17 +68,15 @@ struct mipi_dbi {
 
 static inline struct mipi_dbi *drm_to_mipi_dbi(struct drm_device *drm)
 {
-	struct tinydrm_device *tdev = drm->dev_private;
-
-	return container_of(tdev, struct mipi_dbi, tinydrm);
+	return container_of(drm, struct mipi_dbi, base);
 }
 
 int mipi_dbi_spi_init(struct spi_device *spi, struct mipi_dbi *mipi,
 		      struct gpio_desc *dc);
-int mipi_dbi_init(struct device *dev, struct mipi_dbi *mipi,
-		  const struct drm_simple_display_pipe_funcs *pipe_funcs,
-		  struct drm_driver *driver,
+int mipi_dbi_init(struct mipi_dbi *mipi,
+		  const struct drm_simple_display_pipe_funcs *funcs,
 		  const struct drm_display_mode *mode, unsigned int rotation);
+void mipi_dbi_release(struct drm_device *drm);
 void mipi_dbi_pipe_update(struct drm_simple_display_pipe *pipe,
 			  struct drm_plane_state *old_state);
 void mipi_dbi_enable_flush(struct mipi_dbi *mipi,
-- 
2.20.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* [PATCH 09/11] drm/tinydrm: Remove tinydrm_device
  2019-01-20 11:43 [PATCH 00/11] drm/tinydrm: Remove tinydrm_device Noralf Trønnes
                   ` (7 preceding siblings ...)
  2019-01-20 11:43 ` [PATCH 08/11] drm/tinydrm: " Noralf Trønnes
@ 2019-01-20 11:43 ` Noralf Trønnes
  2019-01-21  8:13   ` Sam Ravnborg
  2019-01-21  9:29   ` Daniel Vetter
  2019-01-20 11:43 ` [PATCH 10/11] drm/tinydrm: Use drm_dev_enter/exit() Noralf Trønnes
                   ` (2 subsequent siblings)
  11 siblings, 2 replies; 57+ messages in thread
From: Noralf Trønnes @ 2019-01-20 11:43 UTC (permalink / raw)
  To: dri-devel; +Cc: david

No more users left so it can go alongside its helpers.
Update the tinydrm docs description and remove todo entry.

Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
---
 Documentation/gpu/tinydrm.rst                 |  26 +--
 Documentation/gpu/todo.rst                    |   4 -
 drivers/gpu/drm/tinydrm/core/Makefile         |   2 +-
 drivers/gpu/drm/tinydrm/core/tinydrm-core.c   | 169 ------------------
 .../gpu/drm/tinydrm/core/tinydrm-helpers.c    |   2 +
 include/drm/tinydrm/tinydrm.h                 |  42 -----
 6 files changed, 10 insertions(+), 235 deletions(-)
 delete mode 100644 drivers/gpu/drm/tinydrm/core/tinydrm-core.c
 delete mode 100644 include/drm/tinydrm/tinydrm.h

diff --git a/Documentation/gpu/tinydrm.rst b/Documentation/gpu/tinydrm.rst
index 1ca726474af4..19969b989efb 100644
--- a/Documentation/gpu/tinydrm.rst
+++ b/Documentation/gpu/tinydrm.rst
@@ -1,24 +1,12 @@
-==========================
-drm/tinydrm Driver library
-==========================
+============================
+drm/tinydrm Tiny DRM drivers
+============================
 
-.. kernel-doc:: drivers/gpu/drm/tinydrm/core/tinydrm-core.c
-   :doc: overview
+tinydrm is a collection of DRM drivers that are so small they can fit in a
+single source file.
 
-Core functionality
-==================
-
-.. kernel-doc:: drivers/gpu/drm/tinydrm/core/tinydrm-core.c
-   :doc: core
-
-.. kernel-doc:: include/drm/tinydrm/tinydrm.h
-   :internal:
-
-.. kernel-doc:: drivers/gpu/drm/tinydrm/core/tinydrm-core.c
-   :export:
-
-Additional helpers
-==================
+Helpers
+=======
 
 .. kernel-doc:: include/drm/tinydrm/tinydrm-helpers.h
    :internal:
diff --git a/Documentation/gpu/todo.rst b/Documentation/gpu/todo.rst
index 38360ede1221..3495aec7a8d4 100644
--- a/Documentation/gpu/todo.rst
+++ b/Documentation/gpu/todo.rst
@@ -435,10 +435,6 @@ those drivers as simple as possible, so lots of room for refactoring:
   one of the ideas for having a shared dsi/dbi helper, abstracting away the
   transport details more.
 
-- Quick aside: The unregister devm stuff is kinda getting the lifetimes of
-  a drm_device wrong. Doesn't matter, since everyone else gets it wrong
-  too :-)
-
 Contact: Noralf Trønnes, Daniel Vetter
 
 AMD DC Display Driver
diff --git a/drivers/gpu/drm/tinydrm/core/Makefile b/drivers/gpu/drm/tinydrm/core/Makefile
index bf2df7326df7..f88ea7ad302f 100644
--- a/drivers/gpu/drm/tinydrm/core/Makefile
+++ b/drivers/gpu/drm/tinydrm/core/Makefile
@@ -1,3 +1,3 @@
-tinydrm-y := tinydrm-core.o tinydrm-helpers.o
+tinydrm-y := tinydrm-helpers.o
 
 obj-$(CONFIG_DRM_TINYDRM) += tinydrm.o
diff --git a/drivers/gpu/drm/tinydrm/core/tinydrm-core.c b/drivers/gpu/drm/tinydrm/core/tinydrm-core.c
deleted file mode 100644
index e4a77feaacd6..000000000000
--- a/drivers/gpu/drm/tinydrm/core/tinydrm-core.c
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright (C) 2016 Noralf Trønnes
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#include <drm/drm_atomic.h>
-#include <drm/drm_atomic_helper.h>
-#include <drm/drm_crtc_helper.h>
-#include <drm/drm_drv.h>
-#include <drm/drm_fb_helper.h>
-#include <drm/drm_gem_framebuffer_helper.h>
-#include <drm/drm_print.h>
-#include <drm/tinydrm/tinydrm.h>
-#include <linux/device.h>
-#include <linux/dma-buf.h>
-#include <linux/module.h>
-
-/**
- * DOC: overview
- *
- * This library provides driver helpers for very simple display hardware.
- *
- * It is based on &drm_simple_display_pipe coupled with a &drm_connector which
- * has only one fixed &drm_display_mode. The framebuffers are backed by the
- * cma helper and have support for framebuffer flushing (dirty).
- * fbdev support is also included.
- *
- */
-
-/**
- * DOC: core
- *
- * The driver allocates &tinydrm_device, initializes it using
- * devm_tinydrm_init(), sets up the pipeline using tinydrm_display_pipe_init()
- * and registers the DRM device using devm_tinydrm_register().
- */
-
-static const struct drm_mode_config_funcs tinydrm_mode_config_funcs = {
-	.fb_create = drm_gem_fb_create_with_dirty,
-	.atomic_check = drm_atomic_helper_check,
-	.atomic_commit = drm_atomic_helper_commit,
-};
-
-static int tinydrm_init(struct device *parent, struct tinydrm_device *tdev,
-			struct drm_driver *driver)
-{
-	struct drm_device *drm;
-
-	/*
-	 * We don't embed drm_device, because that prevent us from using
-	 * devm_kzalloc() to allocate tinydrm_device in the driver since
-	 * drm_dev_put() frees the structure. The devm_ functions provide
-	 * for easy error handling.
-	 */
-	drm = drm_dev_alloc(driver, parent);
-	if (IS_ERR(drm))
-		return PTR_ERR(drm);
-
-	tdev->drm = drm;
-	drm->dev_private = tdev;
-	drm_mode_config_init(drm);
-	drm->mode_config.funcs = &tinydrm_mode_config_funcs;
-	drm->mode_config.allow_fb_modifiers = true;
-
-	return 0;
-}
-
-static void tinydrm_fini(struct tinydrm_device *tdev)
-{
-	drm_mode_config_cleanup(tdev->drm);
-	tdev->drm->dev_private = NULL;
-	drm_dev_put(tdev->drm);
-}
-
-static void devm_tinydrm_release(void *data)
-{
-	tinydrm_fini(data);
-}
-
-/**
- * devm_tinydrm_init - Initialize tinydrm device
- * @parent: Parent device object
- * @tdev: tinydrm device
- * @driver: DRM driver
- *
- * This function initializes @tdev, the underlying DRM device and it's
- * mode_config. Resources will be automatically freed on driver detach (devres)
- * using drm_mode_config_cleanup() and drm_dev_put().
- *
- * Returns:
- * Zero on success, negative error code on failure.
- */
-int devm_tinydrm_init(struct device *parent, struct tinydrm_device *tdev,
-		      struct drm_driver *driver)
-{
-	int ret;
-
-	ret = tinydrm_init(parent, tdev, driver);
-	if (ret)
-		return ret;
-
-	ret = devm_add_action(parent, devm_tinydrm_release, tdev);
-	if (ret)
-		tinydrm_fini(tdev);
-
-	return ret;
-}
-EXPORT_SYMBOL(devm_tinydrm_init);
-
-static int tinydrm_register(struct tinydrm_device *tdev)
-{
-	struct drm_device *drm = tdev->drm;
-	int ret;
-
-	ret = drm_dev_register(tdev->drm, 0);
-	if (ret)
-		return ret;
-
-	ret = drm_fbdev_generic_setup(drm, 0);
-	if (ret)
-		DRM_ERROR("Failed to initialize fbdev: %d\n", ret);
-
-	return 0;
-}
-
-static void tinydrm_unregister(struct tinydrm_device *tdev)
-{
-	drm_atomic_helper_shutdown(tdev->drm);
-	drm_dev_unregister(tdev->drm);
-}
-
-static void devm_tinydrm_register_release(void *data)
-{
-	tinydrm_unregister(data);
-}
-
-/**
- * devm_tinydrm_register - Register tinydrm device
- * @tdev: tinydrm device
- *
- * This function registers the underlying DRM device and fbdev.
- * These resources will be automatically unregistered on driver detach (devres)
- * and the display pipeline will be disabled.
- *
- * Returns:
- * Zero on success, negative error code on failure.
- */
-int devm_tinydrm_register(struct tinydrm_device *tdev)
-{
-	struct device *dev = tdev->drm->dev;
-	int ret;
-
-	ret = tinydrm_register(tdev);
-	if (ret)
-		return ret;
-
-	ret = devm_add_action(dev, devm_tinydrm_register_release, tdev);
-	if (ret)
-		tinydrm_unregister(tdev);
-
-	return ret;
-}
-EXPORT_SYMBOL(devm_tinydrm_register);
-
-MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c b/drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c
index 2737b6fdadc8..d7b38dfb6438 100644
--- a/drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c
+++ b/drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c
@@ -365,3 +365,5 @@ int tinydrm_spi_transfer(struct spi_device *spi, u32 speed_hz,
 EXPORT_SYMBOL(tinydrm_spi_transfer);
 
 #endif /* CONFIG_SPI */
+
+MODULE_LICENSE("GPL");
diff --git a/include/drm/tinydrm/tinydrm.h b/include/drm/tinydrm/tinydrm.h
deleted file mode 100644
index ee9b17759391..000000000000
--- a/include/drm/tinydrm/tinydrm.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2016 Noralf Trønnes
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#ifndef __LINUX_TINYDRM_H
-#define __LINUX_TINYDRM_H
-
-#include <drm/drm_simple_kms_helper.h>
-
-struct drm_driver;
-
-/**
- * struct tinydrm_device - tinydrm device
- */
-struct tinydrm_device {
-	/**
-	 * @drm: DRM device
-	 */
-	struct drm_device *drm;
-
-	/**
-	 * @pipe: Display pipe structure
-	 */
-	struct drm_simple_display_pipe pipe;
-};
-
-static inline struct tinydrm_device *
-pipe_to_tinydrm(struct drm_simple_display_pipe *pipe)
-{
-	return container_of(pipe, struct tinydrm_device, pipe);
-}
-
-int devm_tinydrm_init(struct device *parent, struct tinydrm_device *tdev,
-		      struct drm_driver *driver);
-int devm_tinydrm_register(struct tinydrm_device *tdev);
-
-#endif /* __LINUX_TINYDRM_H */
-- 
2.20.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* [PATCH 10/11] drm/tinydrm: Use drm_dev_enter/exit()
  2019-01-20 11:43 [PATCH 00/11] drm/tinydrm: Remove tinydrm_device Noralf Trønnes
                   ` (8 preceding siblings ...)
  2019-01-20 11:43 ` [PATCH 09/11] drm/tinydrm: Remove tinydrm_device Noralf Trønnes
@ 2019-01-20 11:43 ` Noralf Trønnes
  2019-01-20 11:43 ` [PATCH 11/11] drm/fb-helper: generic: Don't take module ref for fbcon Noralf Trønnes
  2019-01-21  8:34 ` [PATCH 00/11] drm/tinydrm: Remove tinydrm_device Sam Ravnborg
  11 siblings, 0 replies; 57+ messages in thread
From: Noralf Trønnes @ 2019-01-20 11:43 UTC (permalink / raw)
  To: dri-devel; +Cc: david

This protects device resources from use after device removal.

There are 3 ways for driver-device unbinding to happen:
- The driver module is unloaded causing the driver to be unregistered.
  This can't happen as long as there are open file handles because a
  reference is taken on the module.
- The device is removed (Device Tree overlay unloading).
  This can happen at any time.
- The driver sysfs unbind file can be used to unbind the driver from the
  device. This can happen any time.

Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
---
 drivers/gpu/drm/tinydrm/hx8357d.c  |  9 +++++--
 drivers/gpu/drm/tinydrm/ili9225.c  | 24 ++++++++++++++---
 drivers/gpu/drm/tinydrm/ili9341.c  |  9 +++++--
 drivers/gpu/drm/tinydrm/mi0283qt.c |  9 +++++--
 drivers/gpu/drm/tinydrm/mipi-dbi.c | 39 +++++++++++++++++++++++----
 drivers/gpu/drm/tinydrm/repaper.c  | 42 +++++++++++++++++++++---------
 drivers/gpu/drm/tinydrm/st7586.c   | 23 +++++++++++++---
 drivers/gpu/drm/tinydrm/st7735r.c  |  9 +++++--
 8 files changed, 131 insertions(+), 33 deletions(-)

diff --git a/drivers/gpu/drm/tinydrm/hx8357d.c b/drivers/gpu/drm/tinydrm/hx8357d.c
index 4648bd6d6282..aac2c4bda779 100644
--- a/drivers/gpu/drm/tinydrm/hx8357d.c
+++ b/drivers/gpu/drm/tinydrm/hx8357d.c
@@ -49,13 +49,16 @@ static void yx240qv29_enable(struct drm_simple_display_pipe *pipe,
 {
 	struct mipi_dbi *mipi = drm_to_mipi_dbi(pipe->crtc.dev);
 	u8 addr_mode;
-	int ret;
+	int ret, idx;
+
+	if (!drm_dev_enter(pipe->crtc.dev, &idx))
+		return;
 
 	DRM_DEBUG_KMS("\n");
 
 	ret = mipi_dbi_poweron_conditional_reset(mipi);
 	if (ret < 0)
-		return;
+		goto out_exit;
 	if (ret == 1)
 		goto out_enable;
 
@@ -171,6 +174,8 @@ static void yx240qv29_enable(struct drm_simple_display_pipe *pipe,
 	}
 	mipi_dbi_command(mipi, MIPI_DCS_SET_ADDRESS_MODE, addr_mode);
 	mipi_dbi_enable_flush(mipi, crtc_state, plane_state);
+out_exit:
+	drm_dev_exit(idx);
 }
 
 static const struct drm_simple_display_pipe_funcs hx8357d_pipe_funcs = {
diff --git a/drivers/gpu/drm/tinydrm/ili9225.c b/drivers/gpu/drm/tinydrm/ili9225.c
index 20a44a43a64a..9dfc472032ae 100644
--- a/drivers/gpu/drm/tinydrm/ili9225.c
+++ b/drivers/gpu/drm/tinydrm/ili9225.c
@@ -88,13 +88,16 @@ static void ili9225_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect)
 	bool swap = mipi->swap_bytes;
 	u16 x_start, y_start;
 	u16 x1, x2, y1, y2;
-	int ret = 0;
+	int idx, ret = 0;
 	bool full;
 	void *tr;
 
 	if (!mipi->enabled)
 		return;
 
+	if (!drm_dev_enter(fb->dev, &idx))
+		return;
+
 	full = width == fb->width && height == fb->height;
 
 	DRM_DEBUG_KMS("Flushing [FB:%d] " DRM_RECT_FMT "\n", fb->base.id, DRM_RECT_ARG(rect));
@@ -157,6 +160,8 @@ static void ili9225_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect)
 err_msg:
 	if (ret)
 		dev_err_once(fb->dev->dev, "Failed to update display %d\n", ret);
+
+	drm_dev_exit(idx);
 }
 
 static void ili9225_pipe_update(struct drm_simple_display_pipe *pipe,
@@ -190,9 +195,12 @@ static void ili9225_pipe_enable(struct drm_simple_display_pipe *pipe,
 		.y1 = 0,
 		.y2 = fb->height,
 	};
-	int ret;
+	int ret, idx;
 	u8 am_id;
 
+	if (!drm_dev_enter(pipe->crtc.dev, &idx))
+		return;
+
 	DRM_DEBUG_KMS("\n");
 
 	mipi_dbi_hw_reset(mipi);
@@ -206,7 +214,7 @@ static void ili9225_pipe_enable(struct drm_simple_display_pipe *pipe,
 	ret = ili9225_command(mipi, ILI9225_POWER_CONTROL_1, 0x0000);
 	if (ret) {
 		DRM_DEV_ERROR(dev, "Error sending command %d\n", ret);
-		return;
+		goto out_exit;
 	}
 	ili9225_command(mipi, ILI9225_POWER_CONTROL_2, 0x0000);
 	ili9225_command(mipi, ILI9225_POWER_CONTROL_3, 0x0000);
@@ -279,16 +287,22 @@ static void ili9225_pipe_enable(struct drm_simple_display_pipe *pipe,
 
 	mipi->enabled = true;
 	ili9225_fb_dirty(fb, &rect);
+out_exit:
+	drm_dev_exit(idx);
 }
 
 static void ili9225_pipe_disable(struct drm_simple_display_pipe *pipe)
 {
 	struct mipi_dbi *mipi = drm_to_mipi_dbi(pipe->crtc.dev);
+	int idx;
+
+	if (!drm_dev_enter(pipe->crtc.dev, &idx))
+		return;
 
 	DRM_DEBUG_KMS("\n");
 
 	if (!mipi->enabled)
-		return;
+		goto out_exit;
 
 	ili9225_command(mipi, ILI9225_DISPLAY_CONTROL_1, 0x0000);
 	msleep(50);
@@ -297,6 +311,8 @@ static void ili9225_pipe_disable(struct drm_simple_display_pipe *pipe)
 	ili9225_command(mipi, ILI9225_POWER_CONTROL_1, 0x0a02);
 
 	mipi->enabled = false;
+out_exit:
+	drm_dev_exit(idx);
 }
 
 static int ili9225_dbi_command(struct mipi_dbi *mipi, u8 cmd, u8 *par,
diff --git a/drivers/gpu/drm/tinydrm/ili9341.c b/drivers/gpu/drm/tinydrm/ili9341.c
index 5ab3267b224a..70b4396f400b 100644
--- a/drivers/gpu/drm/tinydrm/ili9341.c
+++ b/drivers/gpu/drm/tinydrm/ili9341.c
@@ -55,13 +55,16 @@ static void yx240qv29_enable(struct drm_simple_display_pipe *pipe,
 {
 	struct mipi_dbi *mipi = drm_to_mipi_dbi(pipe->crtc.dev);
 	u8 addr_mode;
-	int ret;
+	int ret, idx;
+
+	if (!drm_dev_enter(pipe->crtc.dev, &idx))
+		return;
 
 	DRM_DEBUG_KMS("\n");
 
 	ret = mipi_dbi_poweron_conditional_reset(mipi);
 	if (ret < 0)
-		return;
+		goto out_exit;
 	if (ret == 1)
 		goto out_enable;
 
@@ -127,6 +130,8 @@ static void yx240qv29_enable(struct drm_simple_display_pipe *pipe,
 	addr_mode |= ILI9341_MADCTL_BGR;
 	mipi_dbi_command(mipi, MIPI_DCS_SET_ADDRESS_MODE, addr_mode);
 	mipi_dbi_enable_flush(mipi, crtc_state, plane_state);
+out_exit:
+	drm_dev_exit(idx);
 }
 
 static const struct drm_simple_display_pipe_funcs ili9341_pipe_funcs = {
diff --git a/drivers/gpu/drm/tinydrm/mi0283qt.c b/drivers/gpu/drm/tinydrm/mi0283qt.c
index bb0ce4514e60..7d0bcf5de89f 100644
--- a/drivers/gpu/drm/tinydrm/mi0283qt.c
+++ b/drivers/gpu/drm/tinydrm/mi0283qt.c
@@ -57,13 +57,16 @@ static void mi0283qt_enable(struct drm_simple_display_pipe *pipe,
 {
 	struct mipi_dbi *mipi = drm_to_mipi_dbi(pipe->crtc.dev);
 	u8 addr_mode;
-	int ret;
+	int ret, idx;
+
+	if (!drm_dev_enter(pipe->crtc.dev, &idx))
+		return;
 
 	DRM_DEBUG_KMS("\n");
 
 	ret = mipi_dbi_poweron_conditional_reset(mipi);
 	if (ret < 0)
-		return;
+		goto out_exit;
 	if (ret == 1)
 		goto out_enable;
 
@@ -135,6 +138,8 @@ static void mi0283qt_enable(struct drm_simple_display_pipe *pipe,
 	addr_mode |= ILI9341_MADCTL_BGR;
 	mipi_dbi_command(mipi, MIPI_DCS_SET_ADDRESS_MODE, addr_mode);
 	mipi_dbi_enable_flush(mipi, crtc_state, plane_state);
+out_exit:
+	drm_dev_exit(idx);
 }
 
 static const struct drm_simple_display_pipe_funcs mi0283qt_pipe_funcs = {
diff --git a/drivers/gpu/drm/tinydrm/mipi-dbi.c b/drivers/gpu/drm/tinydrm/mipi-dbi.c
index 861e04815104..df473b31082c 100644
--- a/drivers/gpu/drm/tinydrm/mipi-dbi.c
+++ b/drivers/gpu/drm/tinydrm/mipi-dbi.c
@@ -220,13 +220,16 @@ static void mipi_dbi_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect)
 	unsigned int height = rect->y2 - rect->y1;
 	unsigned int width = rect->x2 - rect->x1;
 	bool swap = mipi->swap_bytes;
-	int ret = 0;
+	int idx, ret = 0;
 	bool full;
 	void *tr;
 
 	if (!mipi->enabled)
 		return;
 
+	if (!drm_dev_enter(fb->dev, &idx))
+		return;
+
 	full = width == fb->width && height == fb->height;
 
 	DRM_DEBUG_KMS("Flushing [FB:%d] " DRM_RECT_FMT "\n", fb->base.id, DRM_RECT_ARG(rect));
@@ -253,6 +256,8 @@ static void mipi_dbi_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect)
 err_msg:
 	if (ret)
 		dev_err_once(fb->dev->dev, "Failed to update display %d\n", ret);
+
+	drm_dev_exit(idx);
 }
 
 /**
@@ -307,10 +312,16 @@ void mipi_dbi_enable_flush(struct mipi_dbi *mipi,
 		.y1 = 0,
 		.y2 = fb->height,
 	};
+	int idx;
+
+	if (!drm_dev_enter(&mipi->base, &idx))
+		return;
 
 	mipi->enabled = true;
 	mipi_dbi_fb_dirty(fb, &rect);
 	backlight_enable(mipi->backlight);
+
+	drm_dev_exit(idx);
 }
 EXPORT_SYMBOL(mipi_dbi_enable_flush);
 
@@ -342,6 +353,10 @@ static void mipi_dbi_blank(struct mipi_dbi *mipi)
 void mipi_dbi_pipe_disable(struct drm_simple_display_pipe *pipe)
 {
 	struct mipi_dbi *mipi = drm_to_mipi_dbi(pipe->crtc.dev);
+	int idx;
+
+	if (!drm_dev_enter(pipe->crtc.dev, &idx))
+		return;
 
 	DRM_DEBUG_KMS("\n");
 
@@ -354,6 +369,8 @@ void mipi_dbi_pipe_disable(struct drm_simple_display_pipe *pipe)
 
 	if (mipi->regulator)
 		regulator_disable(mipi->regulator);
+
+	drm_dev_exit(idx);
 }
 EXPORT_SYMBOL(mipi_dbi_pipe_disable);
 
@@ -995,11 +1012,16 @@ static ssize_t mipi_dbi_debugfs_command_write(struct file *file,
 	u8 val, cmd = 0, parameters[64];
 	char *buf, *pos, *token;
 	unsigned int i;
-	int ret;
+	int ret, idx;
+
+	if (!drm_dev_enter(&mipi->base, &idx))
+		return -ENODEV;
 
 	buf = memdup_user_nul(ubuf, count);
-	if (IS_ERR(buf))
-		return PTR_ERR(buf);
+	if (IS_ERR(buf)) {
+		ret = PTR_ERR(buf);
+		goto err_exit;
+	}
 
 	/* strip trailing whitespace */
 	for (i = count - 1; i > 0; i--)
@@ -1035,6 +1057,8 @@ static ssize_t mipi_dbi_debugfs_command_write(struct file *file,
 
 err_free:
 	kfree(buf);
+err_exit:
+	drm_dev_exit(idx);
 
 	return ret < 0 ? ret : count;
 }
@@ -1043,8 +1067,11 @@ static int mipi_dbi_debugfs_command_show(struct seq_file *m, void *unused)
 {
 	struct mipi_dbi *mipi = m->private;
 	u8 cmd, val[4];
+	int ret, idx;
 	size_t len;
-	int ret;
+
+	if (!drm_dev_enter(&mipi->base, &idx))
+		return -ENODEV;
 
 	for (cmd = 0; cmd < 255; cmd++) {
 		if (!mipi_dbi_command_is_read(mipi, cmd))
@@ -1075,6 +1102,8 @@ static int mipi_dbi_debugfs_command_show(struct seq_file *m, void *unused)
 		seq_printf(m, "%*phN\n", (int)len, val);
 	}
 
+	drm_dev_exit(idx);
+
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/tinydrm/repaper.c b/drivers/gpu/drm/tinydrm/repaper.c
index 4c8205565668..9451583867e4 100644
--- a/drivers/gpu/drm/tinydrm/repaper.c
+++ b/drivers/gpu/drm/tinydrm/repaper.c
@@ -532,8 +532,14 @@ static int repaper_fb_dirty(struct drm_framebuffer *fb)
 	struct dma_buf_attachment *import_attach = cma_obj->base.import_attach;
 	struct repaper_epd *epd = drm_to_epd(fb->dev);
 	struct drm_rect clip;
+	int idx, ret = 0;
 	u8 *buf = NULL;
-	int ret = 0;
+
+	if (!epd->enabled)
+		return 0;
+
+	if (!drm_dev_enter(fb->dev, &idx))
+		return -ENODEV;
 
 	/* repaper can't do partial updates */
 	clip.x1 = 0;
@@ -541,17 +547,16 @@ static int repaper_fb_dirty(struct drm_framebuffer *fb)
 	clip.y1 = 0;
 	clip.y2 = fb->height;
 
-	if (!epd->enabled)
-		return 0;
-
 	repaper_get_temperature(epd);
 
 	DRM_DEBUG("Flushing [FB:%d] st=%ums\n", fb->base.id,
 		  epd->factored_stage_time);
 
 	buf = kmalloc_array(fb->width, fb->height, GFP_KERNEL);
-	if (!buf)
-		return -ENOMEM;
+	if (!buf) {
+		ret = -ENOMEM;
+		goto out_exit;
+	}
 
 	if (import_attach) {
 		ret = dma_buf_begin_cpu_access(import_attach->dmabuf,
@@ -620,6 +625,8 @@ static int repaper_fb_dirty(struct drm_framebuffer *fb)
 
 out_free:
 	kfree(buf);
+out_exit:
+	drm_dev_exit(idx);
 
 	return ret;
 }
@@ -649,7 +656,10 @@ static void repaper_pipe_enable(struct drm_simple_display_pipe *pipe,
 	struct spi_device *spi = epd->spi;
 	struct device *dev = &spi->dev;
 	bool dc_ok = false;
-	int i, ret;
+	int i, ret, idx;
+
+	if (!drm_dev_enter(pipe->crtc.dev, &idx))
+		return;
 
 	DRM_DEBUG_DRIVER("\n");
 
@@ -688,7 +698,7 @@ static void repaper_pipe_enable(struct drm_simple_display_pipe *pipe,
 	if (!i) {
 		DRM_DEV_ERROR(dev, "timeout waiting for panel to become ready.\n");
 		power_off(epd);
-		return;
+		goto out_exit;
 	}
 
 	repaper_read_id(spi);
@@ -699,7 +709,7 @@ static void repaper_pipe_enable(struct drm_simple_display_pipe *pipe,
 		else
 			dev_err(dev, "wrong COG ID 0x%02x\n", ret);
 		power_off(epd);
-		return;
+		goto out_exit;
 	}
 
 	/* Disable OE */
@@ -712,7 +722,7 @@ static void repaper_pipe_enable(struct drm_simple_display_pipe *pipe,
 		else
 			DRM_DEV_ERROR(dev, "panel is reported broken\n");
 		power_off(epd);
-		return;
+		goto out_exit;
 	}
 
 	/* Power saving mode */
@@ -752,7 +762,7 @@ static void repaper_pipe_enable(struct drm_simple_display_pipe *pipe,
 		if (ret < 0) {
 			DRM_DEV_ERROR(dev, "failed to read chip (%d)\n", ret);
 			power_off(epd);
-			return;
+			goto out_exit;
 		}
 
 		if (ret & 0x40) {
@@ -764,7 +774,7 @@ static void repaper_pipe_enable(struct drm_simple_display_pipe *pipe,
 	if (!dc_ok) {
 		DRM_DEV_ERROR(dev, "dc/dc failed\n");
 		power_off(epd);
-		return;
+		goto out_exit;
 	}
 
 	/*
@@ -775,6 +785,8 @@ static void repaper_pipe_enable(struct drm_simple_display_pipe *pipe,
 
 	epd->enabled = true;
 	epd->partial = false;
+out_exit:
+	drm_dev_exit(idx);
 }
 
 static void repaper_pipe_disable(struct drm_simple_display_pipe *pipe)
@@ -782,6 +794,10 @@ static void repaper_pipe_disable(struct drm_simple_display_pipe *pipe)
 	struct repaper_epd *epd = drm_to_epd(pipe->crtc.dev);
 	struct spi_device *spi = epd->spi;
 	unsigned int line;
+	int idx;
+
+	if (!drm_dev_enter(pipe->crtc.dev, &idx))
+		return;
 
 	DRM_DEBUG_DRIVER("\n");
 
@@ -826,6 +842,8 @@ static void repaper_pipe_disable(struct drm_simple_display_pipe *pipe)
 	msleep(50);
 
 	power_off(epd);
+
+	drm_dev_exit(idx);
 }
 
 static void repaper_pipe_update(struct drm_simple_display_pipe *pipe,
diff --git a/drivers/gpu/drm/tinydrm/st7586.c b/drivers/gpu/drm/tinydrm/st7586.c
index cae003318b42..646b55d4fcd0 100644
--- a/drivers/gpu/drm/tinydrm/st7586.c
+++ b/drivers/gpu/drm/tinydrm/st7586.c
@@ -118,12 +118,14 @@ static int st7586_buf_copy(void *dst, struct drm_framebuffer *fb,
 static void st7586_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect)
 {
 	struct mipi_dbi *mipi = drm_to_mipi_dbi(fb->dev);
-	int start, end;
-	int ret = 0;
+	int start, end, idx, ret = 0;
 
 	if (!mipi->enabled)
 		return;
 
+	if (!drm_dev_enter(fb->dev, &idx))
+		return;
+
 	/* 3 pixels per byte, so grow clip to nearest multiple of 3 */
 	rect->x1 = rounddown(rect->x1, 3);
 	rect->x2 = roundup(rect->x2, 3);
@@ -151,6 +153,8 @@ static void st7586_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect)
 err_msg:
 	if (ret)
 		dev_err_once(fb->dev->dev, "Failed to update display %d\n", ret);
+
+	drm_dev_exit(idx);
 }
 
 static void st7586_pipe_update(struct drm_simple_display_pipe *pipe,
@@ -183,14 +187,17 @@ static void st7586_pipe_enable(struct drm_simple_display_pipe *pipe,
 		.y1 = 0,
 		.y2 = fb->height,
 	};
-	int ret;
+	int idx, ret;
 	u8 addr_mode;
 
+	if (!drm_dev_enter(pipe->crtc.dev, &idx))
+		return;
+
 	DRM_DEBUG_KMS("\n");
 
 	ret = mipi_dbi_poweron_reset(mipi);
 	if (ret)
-		return;
+		goto out_exit;
 
 	mipi_dbi_command(mipi, ST7586_AUTO_READ_CTRL, 0x9f);
 	mipi_dbi_command(mipi, ST7586_OTP_RW_CTRL, 0x00);
@@ -243,11 +250,17 @@ static void st7586_pipe_enable(struct drm_simple_display_pipe *pipe,
 	st7586_fb_dirty(fb, &rect);
 
 	mipi_dbi_command(mipi, MIPI_DCS_SET_DISPLAY_ON);
+out_exit:
+	drm_dev_exit(idx);
 }
 
 static void st7586_pipe_disable(struct drm_simple_display_pipe *pipe)
 {
 	struct mipi_dbi *mipi = drm_to_mipi_dbi(pipe->crtc.dev);
+	int idx;
+
+	if (!drm_dev_enter(pipe->crtc.dev, &idx))
+		return;
 
 	DRM_DEBUG_KMS("\n");
 
@@ -256,6 +269,8 @@ static void st7586_pipe_disable(struct drm_simple_display_pipe *pipe)
 
 	mipi_dbi_command(mipi, MIPI_DCS_SET_DISPLAY_OFF);
 	mipi->enabled = false;
+
+	drm_dev_exit(idx);
 }
 
 static const u32 st7586_formats[] = {
diff --git a/drivers/gpu/drm/tinydrm/st7735r.c b/drivers/gpu/drm/tinydrm/st7735r.c
index dcd07248884c..380d26a1b807 100644
--- a/drivers/gpu/drm/tinydrm/st7735r.c
+++ b/drivers/gpu/drm/tinydrm/st7735r.c
@@ -43,14 +43,17 @@ static void jd_t18003_t01_pipe_enable(struct drm_simple_display_pipe *pipe,
 				      struct drm_plane_state *plane_state)
 {
 	struct mipi_dbi *mipi = drm_to_mipi_dbi(pipe->crtc.dev);
-	int ret;
+	int ret, idx;
 	u8 addr_mode;
 
+	if (!drm_dev_enter(pipe->crtc.dev, &idx))
+		return;
+
 	DRM_DEBUG_KMS("\n");
 
 	ret = mipi_dbi_poweron_reset(mipi);
 	if (ret)
-		return;
+		goto out_exit;
 
 	msleep(150);
 
@@ -101,6 +104,8 @@ static void jd_t18003_t01_pipe_enable(struct drm_simple_display_pipe *pipe,
 	msleep(20);
 
 	mipi_dbi_enable_flush(mipi, crtc_state, plane_state);
+out_exit:
+	drm_dev_exit(idx);
 }
 
 static const struct drm_simple_display_pipe_funcs jd_t18003_t01_pipe_funcs = {
-- 
2.20.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* [PATCH 11/11] drm/fb-helper: generic: Don't take module ref for fbcon
  2019-01-20 11:43 [PATCH 00/11] drm/tinydrm: Remove tinydrm_device Noralf Trønnes
                   ` (9 preceding siblings ...)
  2019-01-20 11:43 ` [PATCH 10/11] drm/tinydrm: Use drm_dev_enter/exit() Noralf Trønnes
@ 2019-01-20 11:43 ` Noralf Trønnes
  2019-01-21  9:05   ` Daniel Vetter
  2019-01-21  8:34 ` [PATCH 00/11] drm/tinydrm: Remove tinydrm_device Sam Ravnborg
  11 siblings, 1 reply; 57+ messages in thread
From: Noralf Trønnes @ 2019-01-20 11:43 UTC (permalink / raw)
  To: dri-devel; +Cc: david

It's now safe to let fbcon unbind automatically on fbdev unregister.
The crash problem was fixed in commit 2122b40580dd
("fbdev: fbcon: Fix unregister crash when more than one framebuffer")

Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
---
 drivers/gpu/drm/drm_fb_helper.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 31fcf94bf825..5d0327f603bb 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -2999,7 +2999,8 @@ static int drm_fbdev_fb_open(struct fb_info *info, int user)
 {
 	struct drm_fb_helper *fb_helper = info->par;
 
-	if (!try_module_get(fb_helper->dev->driver->fops->owner))
+	/* No need to take a ref for fbcon because it unbinds on unregister */
+	if (user && !try_module_get(fb_helper->dev->driver->fops->owner))
 		return -ENODEV;
 
 	return 0;
@@ -3009,7 +3010,8 @@ static int drm_fbdev_fb_release(struct fb_info *info, int user)
 {
 	struct drm_fb_helper *fb_helper = info->par;
 
-	module_put(fb_helper->dev->driver->fops->owner);
+	if (user)
+		module_put(fb_helper->dev->driver->fops->owner);
 
 	return 0;
 }
-- 
2.20.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH 02/11] drm/modes: Add DRM_SIMPLE_MODE()
  2019-01-20 11:43 ` [PATCH 02/11] drm/modes: Add DRM_SIMPLE_MODE() Noralf Trønnes
@ 2019-01-20 16:37   ` Ilia Mirkin
  2019-01-20 17:27     ` Noralf Trønnes
  0 siblings, 1 reply; 57+ messages in thread
From: Ilia Mirkin @ 2019-01-20 16:37 UTC (permalink / raw)
  To: Noralf Trønnes; +Cc: david, dri-devel


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

You don't appear to set the mm in the new macro. Not sure if it's on
purpose.

On Sun, Jan 20, 2019, 06:43 Noralf Trønnes <noralf@tronnes.org wrote:

> This adds a helper macro to specify modes that only contain info about
> resolution.
>
> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
> ---
>  drivers/gpu/drm/tinydrm/hx8357d.c  |  2 +-
>  drivers/gpu/drm/tinydrm/ili9225.c  |  2 +-
>  drivers/gpu/drm/tinydrm/ili9341.c  |  2 +-
>  drivers/gpu/drm/tinydrm/mi0283qt.c |  2 +-
>  drivers/gpu/drm/tinydrm/repaper.c  |  8 ++++----
>  drivers/gpu/drm/tinydrm/st7586.c   |  2 +-
>  drivers/gpu/drm/tinydrm/st7735r.c  |  2 +-
>  include/drm/drm_modes.h            | 16 ++++++++++++++++
>  include/drm/tinydrm/tinydrm.h      | 23 -----------------------
>  9 files changed, 26 insertions(+), 33 deletions(-)
>
> diff --git a/drivers/gpu/drm/tinydrm/hx8357d.c
> b/drivers/gpu/drm/tinydrm/hx8357d.c
> index 8bbd0beafc6a..5a1ec0451c19 100644
> --- a/drivers/gpu/drm/tinydrm/hx8357d.c
> +++ b/drivers/gpu/drm/tinydrm/hx8357d.c
> @@ -181,7 +181,7 @@ static const struct drm_simple_display_pipe_funcs
> hx8357d_pipe_funcs = {
>  };
>
>  static const struct drm_display_mode yx350hv15_mode = {
> -       TINYDRM_MODE(320, 480, 60, 75),
> +       DRM_SIMPLE_MODE(320, 480, 60, 75),
>  };
>
>  DEFINE_DRM_GEM_CMA_FOPS(hx8357d_fops);
> diff --git a/drivers/gpu/drm/tinydrm/ili9225.c
> b/drivers/gpu/drm/tinydrm/ili9225.c
> index 43a3b68d90a2..d40814d370e2 100644
> --- a/drivers/gpu/drm/tinydrm/ili9225.c
> +++ b/drivers/gpu/drm/tinydrm/ili9225.c
> @@ -332,7 +332,7 @@ static const struct drm_simple_display_pipe_funcs
> ili9225_pipe_funcs = {
>  };
>
>  static const struct drm_display_mode ili9225_mode = {
> -       TINYDRM_MODE(176, 220, 35, 44),
> +       DRM_SIMPLE_MODE(176, 220, 35, 44),
>  };
>
>  DEFINE_DRM_GEM_CMA_FOPS(ili9225_fops);
> diff --git a/drivers/gpu/drm/tinydrm/ili9341.c
> b/drivers/gpu/drm/tinydrm/ili9341.c
> index 713bb2dd7e04..063f4f07f811 100644
> --- a/drivers/gpu/drm/tinydrm/ili9341.c
> +++ b/drivers/gpu/drm/tinydrm/ili9341.c
> @@ -137,7 +137,7 @@ static const struct drm_simple_display_pipe_funcs
> ili9341_pipe_funcs = {
>  };
>
>  static const struct drm_display_mode yx240qv29_mode = {
> -       TINYDRM_MODE(240, 320, 37, 49),
> +       DRM_SIMPLE_MODE(240, 320, 37, 49),
>  };
>
>  DEFINE_DRM_GEM_CMA_FOPS(ili9341_fops);
> diff --git a/drivers/gpu/drm/tinydrm/mi0283qt.c
> b/drivers/gpu/drm/tinydrm/mi0283qt.c
> index 82a92ec9ae3c..3d067c2ba1bc 100644
> --- a/drivers/gpu/drm/tinydrm/mi0283qt.c
> +++ b/drivers/gpu/drm/tinydrm/mi0283qt.c
> @@ -145,7 +145,7 @@ static const struct drm_simple_display_pipe_funcs
> mi0283qt_pipe_funcs = {
>  };
>
>  static const struct drm_display_mode mi0283qt_mode = {
> -       TINYDRM_MODE(320, 240, 58, 43),
> +       DRM_SIMPLE_MODE(320, 240, 58, 43),
>  };
>
>  DEFINE_DRM_GEM_CMA_FOPS(mi0283qt_fops);
> diff --git a/drivers/gpu/drm/tinydrm/repaper.c
> b/drivers/gpu/drm/tinydrm/repaper.c
> index b037c6540cf3..72d30151ecd8 100644
> --- a/drivers/gpu/drm/tinydrm/repaper.c
> +++ b/drivers/gpu/drm/tinydrm/repaper.c
> @@ -860,28 +860,28 @@ static const uint32_t repaper_formats[] = {
>  };
>
>  static const struct drm_display_mode repaper_e1144cs021_mode = {
> -       TINYDRM_MODE(128, 96, 29, 22),
> +       DRM_SIMPLE_MODE(128, 96, 29, 22),
>  };
>
>  static const u8 repaper_e1144cs021_cs[] = { 0x00, 0x00, 0x00, 0x00,
>                                             0x00, 0x0f, 0xff, 0x00 };
>
>  static const struct drm_display_mode repaper_e1190cs021_mode = {
> -       TINYDRM_MODE(144, 128, 36, 32),
> +       DRM_SIMPLE_MODE(144, 128, 36, 32),
>  };
>
>  static const u8 repaper_e1190cs021_cs[] = { 0x00, 0x00, 0x00, 0x03,
>                                             0xfc, 0x00, 0x00, 0xff };
>
>  static const struct drm_display_mode repaper_e2200cs021_mode = {
> -       TINYDRM_MODE(200, 96, 46, 22),
> +       DRM_SIMPLE_MODE(200, 96, 46, 22),
>  };
>
>  static const u8 repaper_e2200cs021_cs[] = { 0x00, 0x00, 0x00, 0x00,
>                                             0x01, 0xff, 0xe0, 0x00 };
>
>  static const struct drm_display_mode repaper_e2271cs021_mode = {
> -       TINYDRM_MODE(264, 176, 57, 38),
> +       DRM_SIMPLE_MODE(264, 176, 57, 38),
>  };
>
>  static const u8 repaper_e2271cs021_cs[] = { 0x00, 0x00, 0x00, 0x7f,
> diff --git a/drivers/gpu/drm/tinydrm/st7586.c
> b/drivers/gpu/drm/tinydrm/st7586.c
> index 01a8077954b3..5ee7db561349 100644
> --- a/drivers/gpu/drm/tinydrm/st7586.c
> +++ b/drivers/gpu/drm/tinydrm/st7586.c
> @@ -312,7 +312,7 @@ static const struct drm_simple_display_pipe_funcs
> st7586_pipe_funcs = {
>  };
>
>  static const struct drm_display_mode st7586_mode = {
> -       TINYDRM_MODE(178, 128, 37, 27),
> +       DRM_SIMPLE_MODE(178, 128, 37, 27),
>  };
>
>  DEFINE_DRM_GEM_CMA_FOPS(st7586_fops);
> diff --git a/drivers/gpu/drm/tinydrm/st7735r.c
> b/drivers/gpu/drm/tinydrm/st7735r.c
> index 3bab9a9569a6..6c7904c205f0 100644
> --- a/drivers/gpu/drm/tinydrm/st7735r.c
> +++ b/drivers/gpu/drm/tinydrm/st7735r.c
> @@ -111,7 +111,7 @@ static const struct drm_simple_display_pipe_funcs
> jd_t18003_t01_pipe_funcs = {
>  };
>
>  static const struct drm_display_mode jd_t18003_t01_mode = {
> -       TINYDRM_MODE(128, 160, 28, 35),
> +       DRM_SIMPLE_MODE(128, 160, 28, 35),
>  };
>
>  DEFINE_DRM_GEM_CMA_FOPS(st7735r_fops);
> diff --git a/include/drm/drm_modes.h b/include/drm/drm_modes.h
> index be4fed97e727..868a9954a8e4 100644
> --- a/include/drm/drm_modes.h
> +++ b/include/drm/drm_modes.h
> @@ -138,6 +138,22 @@ enum drm_mode_status {
>         .vsync_start = (vss), .vsync_end = (vse), .vtotal = (vt), \
>         .vscan = (vs), .flags = (f)
>
> +/**
> + * DRM_SIMPLE_MODE - Simple display mode
> + * @hd: Horizontal resolution, width
> + * @vd: Vertical resolution, height
> + * @hd_mm: Display width in millimeters
> + * @vd_mm: Display height in millimeters
> + *
> + * This macro initializes a &drm_display_mode that only contains info
> about
> + * resolution and physical size.
> + */
> +#define DRM_SIMPLE_MODE(hd, vd, hd_mm, vd_mm) \
> +       .type = DRM_MODE_TYPE_DRIVER, .clock = 1 /* pass validation */, \
> +       .hdisplay = (hd), .hsync_start = (hd), .hsync_end = (hd), \
> +       .htotal = (hd), .vdisplay = (vd), .vsync_start = (vd), \
> +       .vsync_end = (vd), .vtotal = (vd)
> +
>  #define CRTC_INTERLACE_HALVE_V (1 << 0) /* halve V values for interlacing
> */
>  #define CRTC_STEREO_DOUBLE     (1 << 1) /* adjust timings for stereo
> modes */
>  #define CRTC_NO_DBLSCAN                (1 << 2) /* don't adjust
> doublescan */
> diff --git a/include/drm/tinydrm/tinydrm.h b/include/drm/tinydrm/tinydrm.h
> index 5621688edcc0..87e7f9b93a37 100644
> --- a/include/drm/tinydrm/tinydrm.h
> +++ b/include/drm/tinydrm/tinydrm.h
> @@ -35,29 +35,6 @@ pipe_to_tinydrm(struct drm_simple_display_pipe *pipe)
>         return container_of(pipe, struct tinydrm_device, pipe);
>  }
>
> -/**
> - * TINYDRM_MODE - tinydrm display mode
> - * @hd: Horizontal resolution, width
> - * @vd: Vertical resolution, height
> - * @hd_mm: Display width in millimeters
> - * @vd_mm: Display height in millimeters
> - *
> - * This macro creates a &drm_display_mode for use with tinydrm.
> - */
> -#define TINYDRM_MODE(hd, vd, hd_mm, vd_mm) \
> -       .hdisplay = (hd), \
> -       .hsync_start = (hd), \
> -       .hsync_end = (hd), \
> -       .htotal = (hd), \
> -       .vdisplay = (vd), \
> -       .vsync_start = (vd), \
> -       .vsync_end = (vd), \
> -       .vtotal = (vd), \
> -       .width_mm = (hd_mm), \
> -       .height_mm = (vd_mm), \
> -       .type = DRM_MODE_TYPE_DRIVER, \
> -       .clock = 1 /* pass validation */
> -
>  int devm_tinydrm_init(struct device *parent, struct tinydrm_device *tdev,
>                       struct drm_driver *driver);
>  int devm_tinydrm_register(struct tinydrm_device *tdev);
> --
> 2.20.1
>
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>

[-- Attachment #1.2: Type: text/html, Size: 9514 bytes --]

[-- Attachment #2: Type: text/plain, Size: 160 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH 02/11] drm/modes: Add DRM_SIMPLE_MODE()
  2019-01-20 16:37   ` Ilia Mirkin
@ 2019-01-20 17:27     ` Noralf Trønnes
  0 siblings, 0 replies; 57+ messages in thread
From: Noralf Trønnes @ 2019-01-20 17:27 UTC (permalink / raw)
  To: Ilia Mirkin; +Cc: david, dri-devel



Den 20.01.2019 17.37, skrev Ilia Mirkin:
> You don't appear to set the mm in the new macro. Not sure if it's on
> purpose.
> 

Not intentional, that was a slip. I copied the DRM_MODE macro and forgot
about setting the size.

Thanks,
Noralf.

> On Sun, Jan 20, 2019, 06:43 Noralf Trønnes <noralf@tronnes.org
> <mailto:noralf@tronnes.org> wrote:
> 
>     This adds a helper macro to specify modes that only contain info about
>     resolution.
> 
>     Signed-off-by: Noralf Trønnes <noralf@tronnes.org
>     <mailto:noralf@tronnes.org>>
>     ---
>      drivers/gpu/drm/tinydrm/hx8357d.c  |  2 +-
>      drivers/gpu/drm/tinydrm/ili9225.c  |  2 +-
>      drivers/gpu/drm/tinydrm/ili9341.c  |  2 +-
>      drivers/gpu/drm/tinydrm/mi0283qt.c |  2 +-
>      drivers/gpu/drm/tinydrm/repaper.c  |  8 ++++----
>      drivers/gpu/drm/tinydrm/st7586.c   |  2 +-
>      drivers/gpu/drm/tinydrm/st7735r.c  |  2 +-
>      include/drm/drm_modes.h            | 16 ++++++++++++++++
>      include/drm/tinydrm/tinydrm.h      | 23 -----------------------
>      9 files changed, 26 insertions(+), 33 deletions(-)
> 
>     diff --git a/drivers/gpu/drm/tinydrm/hx8357d.c
>     b/drivers/gpu/drm/tinydrm/hx8357d.c
>     index 8bbd0beafc6a..5a1ec0451c19 100644
>     --- a/drivers/gpu/drm/tinydrm/hx8357d.c
>     +++ b/drivers/gpu/drm/tinydrm/hx8357d.c
>     @@ -181,7 +181,7 @@ static const struct
>     drm_simple_display_pipe_funcs hx8357d_pipe_funcs = {
>      };
> 
>      static const struct drm_display_mode yx350hv15_mode = {
>     -       TINYDRM_MODE(320, 480, 60, 75),
>     +       DRM_SIMPLE_MODE(320, 480, 60, 75),
>      };
> 
>      DEFINE_DRM_GEM_CMA_FOPS(hx8357d_fops);
>     diff --git a/drivers/gpu/drm/tinydrm/ili9225.c
>     b/drivers/gpu/drm/tinydrm/ili9225.c
>     index 43a3b68d90a2..d40814d370e2 100644
>     --- a/drivers/gpu/drm/tinydrm/ili9225.c
>     +++ b/drivers/gpu/drm/tinydrm/ili9225.c
>     @@ -332,7 +332,7 @@ static const struct
>     drm_simple_display_pipe_funcs ili9225_pipe_funcs = {
>      };
> 
>      static const struct drm_display_mode ili9225_mode = {
>     -       TINYDRM_MODE(176, 220, 35, 44),
>     +       DRM_SIMPLE_MODE(176, 220, 35, 44),
>      };
> 
>      DEFINE_DRM_GEM_CMA_FOPS(ili9225_fops);
>     diff --git a/drivers/gpu/drm/tinydrm/ili9341.c
>     b/drivers/gpu/drm/tinydrm/ili9341.c
>     index 713bb2dd7e04..063f4f07f811 100644
>     --- a/drivers/gpu/drm/tinydrm/ili9341.c
>     +++ b/drivers/gpu/drm/tinydrm/ili9341.c
>     @@ -137,7 +137,7 @@ static const struct
>     drm_simple_display_pipe_funcs ili9341_pipe_funcs = {
>      };
> 
>      static const struct drm_display_mode yx240qv29_mode = {
>     -       TINYDRM_MODE(240, 320, 37, 49),
>     +       DRM_SIMPLE_MODE(240, 320, 37, 49),
>      };
> 
>      DEFINE_DRM_GEM_CMA_FOPS(ili9341_fops);
>     diff --git a/drivers/gpu/drm/tinydrm/mi0283qt.c
>     b/drivers/gpu/drm/tinydrm/mi0283qt.c
>     index 82a92ec9ae3c..3d067c2ba1bc 100644
>     --- a/drivers/gpu/drm/tinydrm/mi0283qt.c
>     +++ b/drivers/gpu/drm/tinydrm/mi0283qt.c
>     @@ -145,7 +145,7 @@ static const struct
>     drm_simple_display_pipe_funcs mi0283qt_pipe_funcs = {
>      };
> 
>      static const struct drm_display_mode mi0283qt_mode = {
>     -       TINYDRM_MODE(320, 240, 58, 43),
>     +       DRM_SIMPLE_MODE(320, 240, 58, 43),
>      };
> 
>      DEFINE_DRM_GEM_CMA_FOPS(mi0283qt_fops);
>     diff --git a/drivers/gpu/drm/tinydrm/repaper.c
>     b/drivers/gpu/drm/tinydrm/repaper.c
>     index b037c6540cf3..72d30151ecd8 100644
>     --- a/drivers/gpu/drm/tinydrm/repaper.c
>     +++ b/drivers/gpu/drm/tinydrm/repaper.c
>     @@ -860,28 +860,28 @@ static const uint32_t repaper_formats[] = {
>      };
> 
>      static const struct drm_display_mode repaper_e1144cs021_mode = {
>     -       TINYDRM_MODE(128, 96, 29, 22),
>     +       DRM_SIMPLE_MODE(128, 96, 29, 22),
>      };
> 
>      static const u8 repaper_e1144cs021_cs[] = { 0x00, 0x00, 0x00, 0x00,
>                                                 0x00, 0x0f, 0xff, 0x00 };
> 
>      static const struct drm_display_mode repaper_e1190cs021_mode = {
>     -       TINYDRM_MODE(144, 128, 36, 32),
>     +       DRM_SIMPLE_MODE(144, 128, 36, 32),
>      };
> 
>      static const u8 repaper_e1190cs021_cs[] = { 0x00, 0x00, 0x00, 0x03,
>                                                 0xfc, 0x00, 0x00, 0xff };
> 
>      static const struct drm_display_mode repaper_e2200cs021_mode = {
>     -       TINYDRM_MODE(200, 96, 46, 22),
>     +       DRM_SIMPLE_MODE(200, 96, 46, 22),
>      };
> 
>      static const u8 repaper_e2200cs021_cs[] = { 0x00, 0x00, 0x00, 0x00,
>                                                 0x01, 0xff, 0xe0, 0x00 };
> 
>      static const struct drm_display_mode repaper_e2271cs021_mode = {
>     -       TINYDRM_MODE(264, 176, 57, 38),
>     +       DRM_SIMPLE_MODE(264, 176, 57, 38),
>      };
> 
>      static const u8 repaper_e2271cs021_cs[] = { 0x00, 0x00, 0x00, 0x7f,
>     diff --git a/drivers/gpu/drm/tinydrm/st7586.c
>     b/drivers/gpu/drm/tinydrm/st7586.c
>     index 01a8077954b3..5ee7db561349 100644
>     --- a/drivers/gpu/drm/tinydrm/st7586.c
>     +++ b/drivers/gpu/drm/tinydrm/st7586.c
>     @@ -312,7 +312,7 @@ static const struct
>     drm_simple_display_pipe_funcs st7586_pipe_funcs = {
>      };
> 
>      static const struct drm_display_mode st7586_mode = {
>     -       TINYDRM_MODE(178, 128, 37, 27),
>     +       DRM_SIMPLE_MODE(178, 128, 37, 27),
>      };
> 
>      DEFINE_DRM_GEM_CMA_FOPS(st7586_fops);
>     diff --git a/drivers/gpu/drm/tinydrm/st7735r.c
>     b/drivers/gpu/drm/tinydrm/st7735r.c
>     index 3bab9a9569a6..6c7904c205f0 100644
>     --- a/drivers/gpu/drm/tinydrm/st7735r.c
>     +++ b/drivers/gpu/drm/tinydrm/st7735r.c
>     @@ -111,7 +111,7 @@ static const struct
>     drm_simple_display_pipe_funcs jd_t18003_t01_pipe_funcs = {
>      };
> 
>      static const struct drm_display_mode jd_t18003_t01_mode = {
>     -       TINYDRM_MODE(128, 160, 28, 35),
>     +       DRM_SIMPLE_MODE(128, 160, 28, 35),
>      };
> 
>      DEFINE_DRM_GEM_CMA_FOPS(st7735r_fops);
>     diff --git a/include/drm/drm_modes.h b/include/drm/drm_modes.h
>     index be4fed97e727..868a9954a8e4 100644
>     --- a/include/drm/drm_modes.h
>     +++ b/include/drm/drm_modes.h
>     @@ -138,6 +138,22 @@ enum drm_mode_status {
>             .vsync_start = (vss), .vsync_end = (vse), .vtotal = (vt), \
>             .vscan = (vs), .flags = (f)
> 
>     +/**
>     + * DRM_SIMPLE_MODE - Simple display mode
>     + * @hd: Horizontal resolution, width
>     + * @vd: Vertical resolution, height
>     + * @hd_mm: Display width in millimeters
>     + * @vd_mm: Display height in millimeters
>     + *
>     + * This macro initializes a &drm_display_mode that only contains
>     info about
>     + * resolution and physical size.
>     + */
>     +#define DRM_SIMPLE_MODE(hd, vd, hd_mm, vd_mm) \
>     +       .type = DRM_MODE_TYPE_DRIVER, .clock = 1 /* pass validation
>     */, \
>     +       .hdisplay = (hd), .hsync_start = (hd), .hsync_end = (hd), \
>     +       .htotal = (hd), .vdisplay = (vd), .vsync_start = (vd), \
>     +       .vsync_end = (vd), .vtotal = (vd)
>     +
>      #define CRTC_INTERLACE_HALVE_V (1 << 0) /* halve V values for
>     interlacing */
>      #define CRTC_STEREO_DOUBLE     (1 << 1) /* adjust timings for
>     stereo modes */
>      #define CRTC_NO_DBLSCAN                (1 << 2) /* don't adjust
>     doublescan */
>     diff --git a/include/drm/tinydrm/tinydrm.h
>     b/include/drm/tinydrm/tinydrm.h
>     index 5621688edcc0..87e7f9b93a37 100644
>     --- a/include/drm/tinydrm/tinydrm.h
>     +++ b/include/drm/tinydrm/tinydrm.h
>     @@ -35,29 +35,6 @@ pipe_to_tinydrm(struct drm_simple_display_pipe *pipe)
>             return container_of(pipe, struct tinydrm_device, pipe);
>      }
> 
>     -/**
>     - * TINYDRM_MODE - tinydrm display mode
>     - * @hd: Horizontal resolution, width
>     - * @vd: Vertical resolution, height
>     - * @hd_mm: Display width in millimeters
>     - * @vd_mm: Display height in millimeters
>     - *
>     - * This macro creates a &drm_display_mode for use with tinydrm.
>     - */
>     -#define TINYDRM_MODE(hd, vd, hd_mm, vd_mm) \
>     -       .hdisplay = (hd), \
>     -       .hsync_start = (hd), \
>     -       .hsync_end = (hd), \
>     -       .htotal = (hd), \
>     -       .vdisplay = (vd), \
>     -       .vsync_start = (vd), \
>     -       .vsync_end = (vd), \
>     -       .vtotal = (vd), \
>     -       .width_mm = (hd_mm), \
>     -       .height_mm = (vd_mm), \
>     -       .type = DRM_MODE_TYPE_DRIVER, \
>     -       .clock = 1 /* pass validation */
>     -
>      int devm_tinydrm_init(struct device *parent, struct tinydrm_device
>     *tdev,
>                           struct drm_driver *driver);
>      int devm_tinydrm_register(struct tinydrm_device *tdev);
>     -- 
>     2.20.1
> 
>     _______________________________________________
>     dri-devel mailing list
>     dri-devel@lists.freedesktop.org <mailto:dri-devel@lists.freedesktop.org>
>     https://lists.freedesktop.org/mailman/listinfo/dri-devel
> 
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH 03/11] drm/simple-kms-helper: Add drm_simple_connector_create()
  2019-01-20 11:43 ` [PATCH 03/11] drm/simple-kms-helper: Add drm_simple_connector_create() Noralf Trønnes
@ 2019-01-20 22:14   ` Sam Ravnborg
  2019-01-21  9:22   ` Daniel Vetter
  1 sibling, 0 replies; 57+ messages in thread
From: Sam Ravnborg @ 2019-01-20 22:14 UTC (permalink / raw)
  To: Noralf Trønnes; +Cc: david, dri-devel

Hi Noralf.

On Sun, Jan 20, 2019 at 12:43:10PM +0100, Noralf Trønnes wrote:
> This adds a function that creates a simple connector that has only one
> static mode. Additionally add a helper to set &drm_mode_config width
> and height from the static mode.
> 
> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
> ---
>  drivers/gpu/drm/drm_simple_kms_helper.c | 122 ++++++++++++++++++++++++
>  include/drm/drm_simple_kms_helper.h     |   6 ++
>  2 files changed, 128 insertions(+)
> 
> diff --git a/drivers/gpu/drm/drm_simple_kms_helper.c b/drivers/gpu/drm/drm_simple_kms_helper.c
> index 917812448d1b..ca29975afefe 100644
> --- a/drivers/gpu/drm/drm_simple_kms_helper.c
> +++ b/drivers/gpu/drm/drm_simple_kms_helper.c
> @@ -11,6 +11,8 @@
>  #include <drm/drm_atomic.h>
>  #include <drm/drm_atomic_helper.h>
>  #include <drm/drm_crtc_helper.h>
> +#include <drm/drm_device.h>
> +#include <drm/drm_modes.h>
>  #include <drm/drm_plane_helper.h>
>  #include <drm/drm_simple_kms_helper.h>
>  #include <linux/slab.h>
> @@ -299,4 +301,124 @@ int drm_simple_display_pipe_init(struct drm_device *dev,
>  }
>  EXPORT_SYMBOL(drm_simple_display_pipe_init);
>  
> +static const struct drm_connector_helper_funcs drm_simple_connector_hfuncs = {
> +	/* dummy for the atomic helper */
> +};
> +
> +static int drm_simple_connector_fill_modes(struct drm_connector *connector,
> +					   uint32_t maxX, uint32_t maxY)
> +{
> +	return 1;
> +}
> +
> +static void drm_simple_connector_destroy(struct drm_connector *connector)
> +{
> +	drm_connector_cleanup(connector);
> +	kfree(connector);
> +}
> +
> +static const struct drm_connector_funcs drm_simple_connector_funcs = {
> +	.reset = drm_atomic_helper_connector_reset,
> +	.fill_modes = drm_simple_connector_fill_modes,
> +	.destroy = drm_simple_connector_destroy,
> +	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> +};
> +
> +/**
> + * drm_simple_connector_create - Create a connector with one static mode
> + * @dev: DRM device
> + * @connector_type: Connector type
> + * @mode: Supported display mode
> + * @rotation: Initial @mode rotation in degrees
Maybe extend the documentation to list valid values?
Have you considered to use the DRM_MODE_ROTATE_XXX constants here?
Current tinydrm do not use these and they may not be applicable...

	Sam
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH 07/11] drm/tinydrm/repaper: Use devm_drm_dev_*()
  2019-01-20 11:43 ` [PATCH 07/11] drm/tinydrm/repaper: Use devm_drm_dev_*() Noralf Trønnes
@ 2019-01-20 22:22   ` Sam Ravnborg
  2019-01-20 22:25     ` Sam Ravnborg
  0 siblings, 1 reply; 57+ messages in thread
From: Sam Ravnborg @ 2019-01-20 22:22 UTC (permalink / raw)
  To: Noralf Trønnes; +Cc: david, dri-devel

Hi Noralf.

On Sun, Jan 20, 2019 at 12:43:14PM +0100, Noralf Trønnes wrote:
> Use devm_drm_dev_init(), devm_drm_dev_register_with_fbdev() and drop
> using tinydrm_device.
> 
> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
> ---
>  drivers/gpu/drm/tinydrm/repaper.c | 63 ++++++++++++++++++++-----------
>  1 file changed, 40 insertions(+), 23 deletions(-)
> 
> diff --git a/drivers/gpu/drm/tinydrm/repaper.c b/drivers/gpu/drm/tinydrm/repaper.c
> index 3141241ca8f0..4c8205565668 100644
> --- a/drivers/gpu/drm/tinydrm/repaper.c
> +++ b/drivers/gpu/drm/tinydrm/repaper.c
> @@ -34,7 +34,7 @@
>  #include <drm/drm_gem_framebuffer_helper.h>
>  #include <drm/drm_rect.h>
>  #include <drm/drm_vblank.h>
> -#include <drm/tinydrm/tinydrm.h>
> +#include <drm/drm_simple_kms_helper.h>
>  #include <drm/tinydrm/tinydrm-helpers.h>
>  
>  #define REPAPER_RID_G2_COG_ID	0x12
> @@ -60,7 +60,8 @@ enum repaper_epd_border_byte {
>  };
>  
>  struct repaper_epd {
> -	struct tinydrm_device tinydrm;
> +	struct drm_device base;
> +	struct drm_simple_display_pipe pipe;
>  	struct spi_device *spi;
>  
>  	struct gpio_desc *panel_on;
> @@ -89,10 +90,9 @@ struct repaper_epd {
>  	bool partial;
>  };
>  
> -static inline struct repaper_epd *
> -epd_from_tinydrm(struct tinydrm_device *tdev)
> +static inline struct repaper_epd *drm_to_epd(struct drm_device *drm)
>  {
> -	return container_of(tdev, struct repaper_epd, tinydrm);
> +	return container_of(drm, struct repaper_epd, base);
>  }
>  
>  static int repaper_spi_transfer(struct spi_device *spi, u8 header,
> @@ -530,8 +530,7 @@ static int repaper_fb_dirty(struct drm_framebuffer *fb)
>  {
>  	struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
>  	struct dma_buf_attachment *import_attach = cma_obj->base.import_attach;
> -	struct tinydrm_device *tdev = fb->dev->dev_private;
> -	struct repaper_epd *epd = epd_from_tinydrm(tdev);
> +	struct repaper_epd *epd = drm_to_epd(fb->dev);
>  	struct drm_rect clip;
>  	u8 *buf = NULL;
>  	int ret = 0;
> @@ -646,8 +645,7 @@ static void repaper_pipe_enable(struct drm_simple_display_pipe *pipe,
>  				struct drm_crtc_state *crtc_state,
>  				struct drm_plane_state *plane_state)
>  {
> -	struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
> -	struct repaper_epd *epd = epd_from_tinydrm(tdev);
> +	struct repaper_epd *epd = drm_to_epd(pipe->crtc.dev);
>  	struct spi_device *spi = epd->spi;
>  	struct device *dev = &spi->dev;
>  	bool dc_ok = false;
> @@ -781,8 +779,7 @@ static void repaper_pipe_enable(struct drm_simple_display_pipe *pipe,
>  
>  static void repaper_pipe_disable(struct drm_simple_display_pipe *pipe)
>  {
> -	struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
> -	struct repaper_epd *epd = epd_from_tinydrm(tdev);
> +	struct repaper_epd *epd = drm_to_epd(pipe->crtc.dev);
>  	struct spi_device *spi = epd->spi;
>  	unsigned int line;
>  
> @@ -856,6 +853,23 @@ static const struct drm_simple_display_pipe_funcs repaper_pipe_funcs = {
>  	.prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb,
>  };
>  
> +static const struct drm_mode_config_funcs repaper_mode_config_funcs = {
> +	.fb_create = drm_gem_fb_create_with_dirty,
> +	.atomic_check = drm_atomic_helper_check,
> +	.atomic_commit = drm_atomic_helper_commit,
> +};
> +
> +static void repaper_release(struct drm_device *drm)
> +{
> +	struct repaper_epd *epd = drm_to_epd(drm);
> +
> +	DRM_DEBUG_DRIVER("\n");
> +
> +	drm_mode_config_cleanup(drm);
> +	drm_dev_fini(drm);
> +	kfree(epd);
> +}
> +
>  static const uint32_t repaper_formats[] = {
>  	DRM_FORMAT_XRGB8888,
>  };
> @@ -894,6 +908,7 @@ static struct drm_driver repaper_driver = {
>  	.driver_features	= DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
>  				  DRIVER_ATOMIC,
>  	.fops			= &repaper_fops,
> +	.release		= repaper_release,
>  	DRM_GEM_CMA_VMAP_DRIVER_OPS,
>  	.name			= "repaper",
>  	.desc			= "Pervasive Displays RePaper e-ink panels",
> @@ -927,7 +942,6 @@ static int repaper_probe(struct spi_device *spi)
>  	const struct of_device_id *match;
>  	struct drm_connector *connector;
>  	struct device *dev = &spi->dev;
> -	struct tinydrm_device *tdev;
>  	enum repaper_model model;
>  	const char *thermal_zone;
>  	struct repaper_epd *epd;
> @@ -952,10 +966,20 @@ static int repaper_probe(struct spi_device *spi)
>  		}
>  	}
>  
> -	epd = devm_kzalloc(dev, sizeof(*epd), GFP_KERNEL);
> +	epd = kzalloc(sizeof(*epd), GFP_KERNEL);
>  	if (!epd)
>  		return -ENOMEM;
>  
> +	drm = &epd->base;
> +	ret = devm_drm_dev_init(dev, drm, &repaper_driver);
> +	if (ret) {
> +		kfree(epd);
> +		return ret;
> +	}
Here you go to the trouble and free epd on error.

> +
> +	drm_mode_config_init(drm);
> +	drm->mode_config.funcs = &repaper_mode_config_funcs;
> +
>  	epd->spi = spi;
>  
>  	epd->panel_on = devm_gpiod_get(dev, "panel-on", GPIOD_OUT_LOW);
> @@ -1066,32 +1090,25 @@ static int repaper_probe(struct spi_device *spi)
>  	if (!epd->current_frame)
>  		return -ENOMEM;
>  
> -	tdev = &epd->tinydrm;
> -
> -	ret = devm_tinydrm_init(dev, tdev, &repaper_driver);
> -	if (ret)
> -		return ret;
> -
> -	drm = tdev->drm;
>  
>  	connector = drm_simple_connector_create(drm, DRM_MODE_CONNECTOR_VIRTUAL, mode, 0);
>  	if (IS_ERR(connector))
>  		return PTR_ERR(connector);
>  
> -	ret = drm_simple_display_pipe_init(drm, &tdev->pipe, &repaper_pipe_funcs,
> +	ret = drm_simple_display_pipe_init(drm, &epd->pipe, &repaper_pipe_funcs,
>  					   repaper_formats, ARRAY_SIZE(repaper_formats),
>  					   NULL, connector);
>  	if (ret)
>  		return ret;
But later in the same function epd is not freed.
Looks like a leak?
Nothing new if this is correct but just spotted it.

>  
>  	drm_simple_connector_set_mode_config(connector);
> -	drm_mode_config_reset(tdev->drm);
> +	drm_mode_config_reset(drm);
>  
>  	spi_set_drvdata(spi, drm);
>  
>  	DRM_DEBUG_DRIVER("SPI speed: %uMHz\n", spi->max_speed_hz / 1000000);
>  
> -	return devm_tinydrm_register(tdev);
> +	return devm_drm_dev_register_with_fbdev(drm, 0);
>  }
>  
>  static void repaper_shutdown(struct spi_device *spi)


	Sam
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH 07/11] drm/tinydrm/repaper: Use devm_drm_dev_*()
  2019-01-20 22:22   ` Sam Ravnborg
@ 2019-01-20 22:25     ` Sam Ravnborg
  2019-01-21 13:15       ` Noralf Trønnes
  0 siblings, 1 reply; 57+ messages in thread
From: Sam Ravnborg @ 2019-01-20 22:25 UTC (permalink / raw)
  To: Noralf Trønnes; +Cc: david, dri-devel

On Sun, Jan 20, 2019 at 11:22:36PM +0100, Sam Ravnborg wrote:
> Hi Noralf.
> 
> On Sun, Jan 20, 2019 at 12:43:14PM +0100, Noralf Trønnes wrote:
> > Use devm_drm_dev_init(), devm_drm_dev_register_with_fbdev() and drop
> > using tinydrm_device.
> > 
> > Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
> > ---
> >  drivers/gpu/drm/tinydrm/repaper.c | 63 ++++++++++++++++++++-----------
> >  1 file changed, 40 insertions(+), 23 deletions(-)
> > 
> > diff --git a/drivers/gpu/drm/tinydrm/repaper.c b/drivers/gpu/drm/tinydrm/repaper.c
> > index 3141241ca8f0..4c8205565668 100644
> > --- a/drivers/gpu/drm/tinydrm/repaper.c
> > +++ b/drivers/gpu/drm/tinydrm/repaper.c
> > @@ -34,7 +34,7 @@
> >  #include <drm/drm_gem_framebuffer_helper.h>
> >  #include <drm/drm_rect.h>
> >  #include <drm/drm_vblank.h>
> > -#include <drm/tinydrm/tinydrm.h>
> > +#include <drm/drm_simple_kms_helper.h>
> >  #include <drm/tinydrm/tinydrm-helpers.h>
> >  
> >  #define REPAPER_RID_G2_COG_ID	0x12
> > @@ -60,7 +60,8 @@ enum repaper_epd_border_byte {
> >  };
> >  
> >  struct repaper_epd {
> > -	struct tinydrm_device tinydrm;
> > +	struct drm_device base;
> > +	struct drm_simple_display_pipe pipe;
> >  	struct spi_device *spi;
> >  
> >  	struct gpio_desc *panel_on;
> > @@ -89,10 +90,9 @@ struct repaper_epd {
> >  	bool partial;
> >  };
> >  
> > -static inline struct repaper_epd *
> > -epd_from_tinydrm(struct tinydrm_device *tdev)
> > +static inline struct repaper_epd *drm_to_epd(struct drm_device *drm)
> >  {
> > -	return container_of(tdev, struct repaper_epd, tinydrm);
> > +	return container_of(drm, struct repaper_epd, base);
> >  }
> >  
> >  static int repaper_spi_transfer(struct spi_device *spi, u8 header,
> > @@ -530,8 +530,7 @@ static int repaper_fb_dirty(struct drm_framebuffer *fb)
> >  {
> >  	struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
> >  	struct dma_buf_attachment *import_attach = cma_obj->base.import_attach;
> > -	struct tinydrm_device *tdev = fb->dev->dev_private;
> > -	struct repaper_epd *epd = epd_from_tinydrm(tdev);
> > +	struct repaper_epd *epd = drm_to_epd(fb->dev);
> >  	struct drm_rect clip;
> >  	u8 *buf = NULL;
> >  	int ret = 0;
> > @@ -646,8 +645,7 @@ static void repaper_pipe_enable(struct drm_simple_display_pipe *pipe,
> >  				struct drm_crtc_state *crtc_state,
> >  				struct drm_plane_state *plane_state)
> >  {
> > -	struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
> > -	struct repaper_epd *epd = epd_from_tinydrm(tdev);
> > +	struct repaper_epd *epd = drm_to_epd(pipe->crtc.dev);
> >  	struct spi_device *spi = epd->spi;
> >  	struct device *dev = &spi->dev;
> >  	bool dc_ok = false;
> > @@ -781,8 +779,7 @@ static void repaper_pipe_enable(struct drm_simple_display_pipe *pipe,
> >  
> >  static void repaper_pipe_disable(struct drm_simple_display_pipe *pipe)
> >  {
> > -	struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
> > -	struct repaper_epd *epd = epd_from_tinydrm(tdev);
> > +	struct repaper_epd *epd = drm_to_epd(pipe->crtc.dev);
> >  	struct spi_device *spi = epd->spi;
> >  	unsigned int line;
> >  
> > @@ -856,6 +853,23 @@ static const struct drm_simple_display_pipe_funcs repaper_pipe_funcs = {
> >  	.prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb,
> >  };
> >  
> > +static const struct drm_mode_config_funcs repaper_mode_config_funcs = {
> > +	.fb_create = drm_gem_fb_create_with_dirty,
> > +	.atomic_check = drm_atomic_helper_check,
> > +	.atomic_commit = drm_atomic_helper_commit,
> > +};
> > +
> > +static void repaper_release(struct drm_device *drm)
> > +{
> > +	struct repaper_epd *epd = drm_to_epd(drm);
> > +
> > +	DRM_DEBUG_DRIVER("\n");
> > +
> > +	drm_mode_config_cleanup(drm);
> > +	drm_dev_fini(drm);
> > +	kfree(epd);
> > +}
> > +
> >  static const uint32_t repaper_formats[] = {
> >  	DRM_FORMAT_XRGB8888,
> >  };
> > @@ -894,6 +908,7 @@ static struct drm_driver repaper_driver = {
> >  	.driver_features	= DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
> >  				  DRIVER_ATOMIC,
> >  	.fops			= &repaper_fops,
> > +	.release		= repaper_release,
> >  	DRM_GEM_CMA_VMAP_DRIVER_OPS,
> >  	.name			= "repaper",
> >  	.desc			= "Pervasive Displays RePaper e-ink panels",
> > @@ -927,7 +942,6 @@ static int repaper_probe(struct spi_device *spi)
> >  	const struct of_device_id *match;
> >  	struct drm_connector *connector;
> >  	struct device *dev = &spi->dev;
> > -	struct tinydrm_device *tdev;
> >  	enum repaper_model model;
> >  	const char *thermal_zone;
> >  	struct repaper_epd *epd;
> > @@ -952,10 +966,20 @@ static int repaper_probe(struct spi_device *spi)
> >  		}
> >  	}
> >  
> > -	epd = devm_kzalloc(dev, sizeof(*epd), GFP_KERNEL);
> > +	epd = kzalloc(sizeof(*epd), GFP_KERNEL);
> >  	if (!epd)
> >  		return -ENOMEM;
> >  
> > +	drm = &epd->base;
> > +	ret = devm_drm_dev_init(dev, drm, &repaper_driver);
> > +	if (ret) {
> > +		kfree(epd);
> > +		return ret;
> > +	}
> Here you go to the trouble and free epd on error.
> 
> > +
> > +	drm_mode_config_init(drm);
> > +	drm->mode_config.funcs = &repaper_mode_config_funcs;
> > +
> >  	epd->spi = spi;
> >  
> >  	epd->panel_on = devm_gpiod_get(dev, "panel-on", GPIOD_OUT_LOW);
> > @@ -1066,32 +1090,25 @@ static int repaper_probe(struct spi_device *spi)
> >  	if (!epd->current_frame)
> >  		return -ENOMEM;
> >  
> > -	tdev = &epd->tinydrm;
> > -
> > -	ret = devm_tinydrm_init(dev, tdev, &repaper_driver);
> > -	if (ret)
> > -		return ret;
> > -
> > -	drm = tdev->drm;
> >  
> >  	connector = drm_simple_connector_create(drm, DRM_MODE_CONNECTOR_VIRTUAL, mode, 0);
> >  	if (IS_ERR(connector))
> >  		return PTR_ERR(connector);
> >  
> > -	ret = drm_simple_display_pipe_init(drm, &tdev->pipe, &repaper_pipe_funcs,
> > +	ret = drm_simple_display_pipe_init(drm, &epd->pipe, &repaper_pipe_funcs,
> >  					   repaper_formats, ARRAY_SIZE(repaper_formats),
> >  					   NULL, connector);
> >  	if (ret)
> >  		return ret;
> But later in the same function epd is not freed.
> Looks like a leak?
> Nothing new if this is correct but just spotted it.

The part with "nothing new" I take back. This code converts
devm_kzalloc() to the unmanaged variant.
So the leak is new - and also present in
drivers in next patch (if there is a leak).

Why is the conversion to an unmanaged variant of kzalloc() required?

	Sam
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH 01/11] drm: Add devm_drm_dev_init/register
  2019-01-20 11:43 ` [PATCH 01/11] drm: Add devm_drm_dev_init/register Noralf Trønnes
@ 2019-01-21  6:11   ` Sam Ravnborg
  2019-01-21 13:09     ` Noralf Trønnes
  2019-01-21  9:10   ` Daniel Vetter
  2019-01-22  9:35   ` [PATCH 01/11] drm: Add devm_drm_dev_init/register Daniel Vetter
  2 siblings, 1 reply; 57+ messages in thread
From: Sam Ravnborg @ 2019-01-21  6:11 UTC (permalink / raw)
  To: Noralf Trønnes; +Cc: david, dri-devel

Hi Noralf.

On Sun, Jan 20, 2019 at 12:43:08PM +0100, Noralf Trønnes wrote:
> This adds resource managed (devres) versions of drm_dev_init() and
> drm_dev_register().
> 
> Also added is devm_drm_dev_register_with_fbdev() which sets up generic
> fbdev emulation as well.
> 
> devm_drm_dev_register() isn't exported since there are no users.
Is it relevant to use it outside this patchset - then it should be
documented/exported now to enable use.

> 
> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
> ---
>  Documentation/driver-model/devres.txt |   4 +
>  drivers/gpu/drm/drm_drv.c             | 106 ++++++++++++++++++++++++++
>  include/drm/drm_drv.h                 |   6 ++
>  3 files changed, 116 insertions(+)
> 
> diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt
> index b277cafce71e..6eebc28d4c21 100644
> --- a/Documentation/driver-model/devres.txt
> +++ b/Documentation/driver-model/devres.txt
> @@ -254,6 +254,10 @@ DMA
>    dmam_pool_create()
>    dmam_pool_destroy()
>  
> +DRM
> +  devm_drm_dev_init()
> +  devm_drm_dev_register_with_fbdev()
> +
>  GPIO
>    devm_gpiod_get()
>    devm_gpiod_get_index()
> diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
> index 381581b01d48..12129772be45 100644
> --- a/drivers/gpu/drm/drm_drv.c
> +++ b/drivers/gpu/drm/drm_drv.c
> @@ -36,6 +36,7 @@
>  
>  #include <drm/drm_client.h>
>  #include <drm/drm_drv.h>
> +#include <drm/drm_fb_helper.h>
>  #include <drm/drmP.h>
>  
>  #include "drm_crtc_internal.h"
> @@ -871,6 +872,111 @@ void drm_dev_unregister(struct drm_device *dev)
>  }
>  EXPORT_SYMBOL(drm_dev_unregister);
>  
> +static void devm_drm_dev_init_release(void *data)
> +{
> +	drm_dev_put(data);
> +}
> +
> +/**
> + * devm_drm_dev_init - Resource managed drm_dev_init()
> + * @parent: Parent device object
> + * @dev: DRM device
> + * @driver: DRM driver
> + *
> + * Managed drm_dev_init(). The DRM device initialized with this function is
> + * automatically released on driver detach. You must supply a
> + * &drm_driver.release callback to control the finalization explicitly.
> + *
> + * Note: This function must be used together with
> + * devm_drm_dev_register_with_fbdev().
*must* be used, or can be used?
Reading the code this looks like something drivers that do not offer
fbdev emulation could also benefit from.

With the two comments processed this has:
Reviewed-by: Sam Ravnborg <sam@ravnborg.org>

	Sam
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH 04/11] drm/tinydrm: Remove tinydrm_display_pipe_init()
  2019-01-20 11:43 ` [PATCH 04/11] drm/tinydrm: Remove tinydrm_display_pipe_init() Noralf Trønnes
@ 2019-01-21  6:30   ` Sam Ravnborg
  2019-01-21  9:15   ` Daniel Vetter
  1 sibling, 0 replies; 57+ messages in thread
From: Sam Ravnborg @ 2019-01-21  6:30 UTC (permalink / raw)
  To: Noralf Trønnes; +Cc: david, dri-devel

Hi Noralf.

On Sun, Jan 20, 2019 at 12:43:11PM +0100, Noralf Trønnes wrote:
> Further strip down tinydrm.ko and switch to drm_simple_connector_create().

It is nice how the tinydrm drivers goes from special drivers to small
drivers with a little added to the core.

Two minor comments below. With that addressed/processed it has:
Reviewed-by: Sam Ravnborg <sam@ravnborg.org>

> 
> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
> ---
>  Documentation/gpu/tinydrm.rst               |   3 -
>  drivers/gpu/drm/tinydrm/core/Makefile       |   2 +-
>  drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c | 183 --------------------
>  drivers/gpu/drm/tinydrm/mipi-dbi.c          |  24 ++-
>  drivers/gpu/drm/tinydrm/repaper.c           |  16 +-
>  drivers/gpu/drm/tinydrm/st7586.c            |  19 +-
>  include/drm/tinydrm/tinydrm.h               |   9 -
>  7 files changed, 43 insertions(+), 213 deletions(-)
>  delete mode 100644 drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c
> 
> diff --git a/drivers/gpu/drm/tinydrm/mipi-dbi.c b/drivers/gpu/drm/tinydrm/mipi-dbi.c
> index 918f77c7de34..d1d546f3a664 100644
> --- a/drivers/gpu/drm/tinydrm/mipi-dbi.c
> +++ b/drivers/gpu/drm/tinydrm/mipi-dbi.c
> @@ -391,7 +391,13 @@ int mipi_dbi_init(struct device *dev, struct mipi_dbi *mipi,
>  {
>  	size_t bufsize = mode->vdisplay * mode->hdisplay * sizeof(u16);
>  	struct tinydrm_device *tdev = &mipi->tinydrm;
> +	struct drm_connector *connector;
> +	struct drm_device *drm;
>  	int ret;
> +	static const uint64_t modifiers[] = {
> +		DRM_FORMAT_MOD_LINEAR,
> +		DRM_FORMAT_MOD_INVALID
> +	};
>  
>  	if (!mipi->command)
>  		return -EINVAL;
> @@ -406,18 +412,22 @@ int mipi_dbi_init(struct device *dev, struct mipi_dbi *mipi,
>  	if (ret)
>  		return ret;
>  
> -	/* TODO: Maybe add DRM_MODE_CONNECTOR_SPI */
> -	ret = tinydrm_display_pipe_init(tdev, pipe_funcs,
> -					DRM_MODE_CONNECTOR_VIRTUAL,
> -					mipi_dbi_formats,
> -					ARRAY_SIZE(mipi_dbi_formats), mode,
> -					rotation);
> +	drm = tdev->drm;
> +
> +	connector = drm_simple_connector_create(drm, DRM_MODE_CONNECTOR_VIRTUAL, mode, rotation);
Line length too long.
This goes for all calls to drm_simple_connector_create() in this patch.

We are loosing the comment:
"TODO: Maybe add DRM_MODE_CONNECTOR_SPI"
On purpose?

> +	if (IS_ERR(connector))
> +		return PTR_ERR(connector);
> +
> +	ret = drm_simple_display_pipe_init(drm, &tdev->pipe, pipe_funcs,
> +					   mipi_dbi_formats, ARRAY_SIZE(mipi_dbi_formats),
> +					   modifiers, connector);
>  	if (ret)
>  		return ret;
>  
>  	drm_plane_enable_fb_damage_clips(&tdev->pipe.plane);
>  
> -	tdev->drm->mode_config.preferred_depth = 16;
> +	drm_simple_connector_set_mode_config(connector);
> +	drm->mode_config.preferred_depth = 16;
>  	mipi->rotation = rotation;
>  
>  	drm_mode_config_reset(tdev->drm);
> diff --git a/drivers/gpu/drm/tinydrm/repaper.c b/drivers/gpu/drm/tinydrm/repaper.c
> index 72d30151ecd8..1d551744cc9b 100644
> --- a/drivers/gpu/drm/tinydrm/repaper.c
> +++ b/drivers/gpu/drm/tinydrm/repaper.c
> @@ -924,12 +924,14 @@ static int repaper_probe(struct spi_device *spi)
>  	const struct drm_display_mode *mode;
>  	const struct spi_device_id *spi_id;
>  	const struct of_device_id *match;
> +	struct drm_connector *connector;
>  	struct device *dev = &spi->dev;
>  	struct tinydrm_device *tdev;
>  	enum repaper_model model;
>  	const char *thermal_zone;
>  	struct repaper_epd *epd;
>  	size_t line_buffer_size;
> +	struct drm_device *drm;
>  	int ret;
>  
>  	match = of_match_device(repaper_of_match, dev);
> @@ -1069,13 +1071,19 @@ static int repaper_probe(struct spi_device *spi)
>  	if (ret)
>  		return ret;
>  
> -	ret = tinydrm_display_pipe_init(tdev, &repaper_pipe_funcs,
> -					DRM_MODE_CONNECTOR_VIRTUAL,
> -					repaper_formats,
> -					ARRAY_SIZE(repaper_formats), mode, 0);
> +	drm = tdev->drm;
> +
> +	connector = drm_simple_connector_create(drm, DRM_MODE_CONNECTOR_VIRTUAL, mode, 0);
> +	if (IS_ERR(connector))
> +		return PTR_ERR(connector);
> +
> +	ret = drm_simple_display_pipe_init(drm, &tdev->pipe, &repaper_pipe_funcs,
> +					   repaper_formats, ARRAY_SIZE(repaper_formats),
> +					   NULL, connector);
In mipi-dbi modifiers are specified, like done in tiny-core.
But this, and the other drivers, do not specify any modifiers.

Any specific reason why it is needed in one place but not the others?
Note: I do not really know the purpose, and the comment is alone triggered
because there was a difference in the calls to drm_simple_display_pipe_init()

>  	if (ret)
>  		return ret;
>  
> +	drm_simple_connector_set_mode_config(connector);

No need to set preferred_depth here?
(It is not in the original driver, so it seems to work without it)

>  	drm_mode_config_reset(tdev->drm);
>  	spi_set_drvdata(spi, tdev);
>  
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH 05/11] drm/tinydrm/mipi-dbi: Add drm_to_mipi_dbi()
  2019-01-20 11:43 ` [PATCH 05/11] drm/tinydrm/mipi-dbi: Add drm_to_mipi_dbi() Noralf Trønnes
@ 2019-01-21  6:34   ` Sam Ravnborg
  0 siblings, 0 replies; 57+ messages in thread
From: Sam Ravnborg @ 2019-01-21  6:34 UTC (permalink / raw)
  To: Noralf Trønnes; +Cc: david, dri-devel

On Sun, Jan 20, 2019 at 12:43:12PM +0100, Noralf Trønnes wrote:
> Add a function to derive mipi_dbi from drm_device now that tinydrm_device
> is going away.
> 
> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
Reviewed-by: Sam Ravnborg <sam@ravnborg.org>
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH 06/11] drm/tinydrm: Remove tinydrm_shutdown()
  2019-01-20 11:43 ` [PATCH 06/11] drm/tinydrm: Remove tinydrm_shutdown() Noralf Trønnes
@ 2019-01-21  7:12   ` Sam Ravnborg
  0 siblings, 0 replies; 57+ messages in thread
From: Sam Ravnborg @ 2019-01-21  7:12 UTC (permalink / raw)
  To: Noralf Trønnes; +Cc: david, dri-devel

On Sun, Jan 20, 2019 at 12:43:13PM +0100, Noralf Trønnes wrote:
> It's just a wrapper around drm_atomic_helper_shutdown() now.
> Also store drm_device in the drvdata field, since that's what's used.
> 
> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
Reviewed-by: Sam Ravnborg <sam@ravnborg.org>
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH 09/11] drm/tinydrm: Remove tinydrm_device
  2019-01-20 11:43 ` [PATCH 09/11] drm/tinydrm: Remove tinydrm_device Noralf Trønnes
@ 2019-01-21  8:13   ` Sam Ravnborg
  2019-01-21  9:29   ` Daniel Vetter
  1 sibling, 0 replies; 57+ messages in thread
From: Sam Ravnborg @ 2019-01-21  8:13 UTC (permalink / raw)
  To: Noralf Trønnes; +Cc: david, dri-devel

On Sun, Jan 20, 2019 at 12:43:16PM +0100, Noralf Trønnes wrote:
> No more users left so it can go alongside its helpers.
> Update the tinydrm docs description and remove todo entry.
> 
> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
Reviewed-by: Sam Ravnborg <sam@ravnborg.org>
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH 00/11] drm/tinydrm: Remove tinydrm_device
  2019-01-20 11:43 [PATCH 00/11] drm/tinydrm: Remove tinydrm_device Noralf Trønnes
                   ` (10 preceding siblings ...)
  2019-01-20 11:43 ` [PATCH 11/11] drm/fb-helper: generic: Don't take module ref for fbcon Noralf Trønnes
@ 2019-01-21  8:34 ` Sam Ravnborg
  2019-01-21 13:20   ` Noralf Trønnes
  11 siblings, 1 reply; 57+ messages in thread
From: Sam Ravnborg @ 2019-01-21  8:34 UTC (permalink / raw)
  To: Noralf Trønnes; +Cc: david, dri-devel

Hi Noralf.

On Sun, Jan 20, 2019 at 12:43:07PM +0100, Noralf Trønnes wrote:
> This patchset is part of the effort to remove tinydrm.ko. It removes
> struct tinydrm_device and tinydrm.h.
> 
> While doing this refactoring I have ensured that device unplug is
> working.

Very nice series, it looks good the way the core get a little extra
functionality thus the tinydrm drivers are now more normal drivers.

A few comments for some patches, and those where I felt confident
that I could follow the code got a "Reviewed-by:".

	Sam
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH 11/11] drm/fb-helper: generic: Don't take module ref for fbcon
  2019-01-20 11:43 ` [PATCH 11/11] drm/fb-helper: generic: Don't take module ref for fbcon Noralf Trønnes
@ 2019-01-21  9:05   ` Daniel Vetter
  2019-01-28 14:40     ` Noralf Trønnes
  0 siblings, 1 reply; 57+ messages in thread
From: Daniel Vetter @ 2019-01-21  9:05 UTC (permalink / raw)
  To: Noralf Trønnes; +Cc: david, dri-devel

On Sun, Jan 20, 2019 at 12:43:18PM +0100, Noralf Trønnes wrote:
> It's now safe to let fbcon unbind automatically on fbdev unregister.
> The crash problem was fixed in commit 2122b40580dd
> ("fbdev: fbcon: Fix unregister crash when more than one framebuffer")
> 
> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
> ---
>  drivers/gpu/drm/drm_fb_helper.c | 6 ++++--
>  1 file changed, 4 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
> index 31fcf94bf825..5d0327f603bb 100644
> --- a/drivers/gpu/drm/drm_fb_helper.c
> +++ b/drivers/gpu/drm/drm_fb_helper.c
> @@ -2999,7 +2999,8 @@ static int drm_fbdev_fb_open(struct fb_info *info, int user)
>  {
>  	struct drm_fb_helper *fb_helper = info->par;
>  
> -	if (!try_module_get(fb_helper->dev->driver->fops->owner))
> +	/* No need to take a ref for fbcon because it unbinds on unregister */
> +	if (user && !try_module_get(fb_helper->dev->driver->fops->owner))

Module refcount != driver refcount. You can always unbind a driver through
the sysfs unbind file, no matter whether the module is pinned or not. So I
think pinng the module is still the right thing to do, just to avoid races
and fun stuff.

btw, I looked into making fbdev hotunplug safe (i.e. drm_dev_enter/exit,
but for fbdev) over the holidays. Fixing that properly for fbdev userspace
clients looks like serious amounts of work, and I think it's impossible
for fbcon. Or at least I didn't come up with a workable idea to sprinkle
the dev_enter/exit stuff around. For the userspace side the basic
infrastructure with get_fb_info() and put_fb_info() is there already.

Cheers, Daniel

>  		return -ENODEV;
>  
>  	return 0;
> @@ -3009,7 +3010,8 @@ static int drm_fbdev_fb_release(struct fb_info *info, int user)
>  {
>  	struct drm_fb_helper *fb_helper = info->par;
>  
> -	module_put(fb_helper->dev->driver->fops->owner);
> +	if (user)
> +		module_put(fb_helper->dev->driver->fops->owner);
>  
>  	return 0;
>  }
> -- 
> 2.20.1
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH 01/11] drm: Add devm_drm_dev_init/register
  2019-01-20 11:43 ` [PATCH 01/11] drm: Add devm_drm_dev_init/register Noralf Trønnes
  2019-01-21  6:11   ` Sam Ravnborg
@ 2019-01-21  9:10   ` Daniel Vetter
  2019-01-21  9:55     ` Daniel Vetter
  2019-01-22  9:35   ` [PATCH 01/11] drm: Add devm_drm_dev_init/register Daniel Vetter
  2 siblings, 1 reply; 57+ messages in thread
From: Daniel Vetter @ 2019-01-21  9:10 UTC (permalink / raw)
  To: Noralf Trønnes; +Cc: david, dri-devel

On Sun, Jan 20, 2019 at 12:43:08PM +0100, Noralf Trønnes wrote:
> This adds resource managed (devres) versions of drm_dev_init() and
> drm_dev_register().
> 
> Also added is devm_drm_dev_register_with_fbdev() which sets up generic
> fbdev emulation as well.
> 
> devm_drm_dev_register() isn't exported since there are no users.
> 
> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>

devm_ considered harmful unfortunately. You cannot allocate any structures
which might outlive the lifetime of your device using devm_ on the device.
Since drm_device has it's own lifetime, that structure and _everything_ we
allocate tied to it (i.e. drm_encoder/plane/connector/crtc/whatever)
cannot be allocated using devm_ infrastructure tied to the underlying
parent device.

Yes this means almost all the devm usage in drm drivers is fundamentally
broken. You also generally don't unplug gpus, so no one cares :-/

What could instead is add a struct device to drm_device somehow, use that
struct device to manage the lifetime of the drm_device (would still need
our special open counter maybe), and then use devm to allocate all the drm
bits tied to the drm_device.

This here unfortunataly doesn't work, even if it looks like a really neat
idea.
-Daniel


> ---
>  Documentation/driver-model/devres.txt |   4 +
>  drivers/gpu/drm/drm_drv.c             | 106 ++++++++++++++++++++++++++
>  include/drm/drm_drv.h                 |   6 ++
>  3 files changed, 116 insertions(+)
> 
> diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt
> index b277cafce71e..6eebc28d4c21 100644
> --- a/Documentation/driver-model/devres.txt
> +++ b/Documentation/driver-model/devres.txt
> @@ -254,6 +254,10 @@ DMA
>    dmam_pool_create()
>    dmam_pool_destroy()
>  
> +DRM
> +  devm_drm_dev_init()
> +  devm_drm_dev_register_with_fbdev()
> +
>  GPIO
>    devm_gpiod_get()
>    devm_gpiod_get_index()
> diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
> index 381581b01d48..12129772be45 100644
> --- a/drivers/gpu/drm/drm_drv.c
> +++ b/drivers/gpu/drm/drm_drv.c
> @@ -36,6 +36,7 @@
>  
>  #include <drm/drm_client.h>
>  #include <drm/drm_drv.h>
> +#include <drm/drm_fb_helper.h>
>  #include <drm/drmP.h>
>  
>  #include "drm_crtc_internal.h"
> @@ -871,6 +872,111 @@ void drm_dev_unregister(struct drm_device *dev)
>  }
>  EXPORT_SYMBOL(drm_dev_unregister);
>  
> +static void devm_drm_dev_init_release(void *data)
> +{
> +	drm_dev_put(data);
> +}
> +
> +/**
> + * devm_drm_dev_init - Resource managed drm_dev_init()
> + * @parent: Parent device object
> + * @dev: DRM device
> + * @driver: DRM driver
> + *
> + * Managed drm_dev_init(). The DRM device initialized with this function is
> + * automatically released on driver detach. You must supply a
> + * &drm_driver.release callback to control the finalization explicitly.
> + *
> + * Note: This function must be used together with
> + * devm_drm_dev_register_with_fbdev().
> + *
> + * RETURNS:
> + * 0 on success, or error code on failure.
> + */
> +int devm_drm_dev_init(struct device *parent,
> +		      struct drm_device *dev,
> +		      struct drm_driver *driver)
> +{
> +	int ret;
> +
> +	if (WARN_ON(!parent || !driver->release))
> +		return -EINVAL;
> +
> +	ret = drm_dev_init(dev, driver, parent);
> +	if (ret)
> +		return ret;
> +
> +	/*
> +	 * This is a temporary release action that is used if probing fails
> +	 * before devm_drm_dev_register() is called.
> +	 */
> +	ret = devm_add_action(parent, devm_drm_dev_init_release, dev);
> +	if (ret)
> +		devm_drm_dev_init_release(dev);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL(devm_drm_dev_init);
> +
> +static void devm_drm_dev_register_release(void *data)
> +{
> +	drm_dev_unplug(data);
> +}
> +
> +static int devm_drm_dev_register(struct drm_device *dev)
> +{
> +	int ret;
> +
> +	ret = drm_dev_register(dev, 0);
> +	if (ret)
> +		return ret;
> +
> +	/*
> +	 * This has now served it's purpose, remove it to not mess up ref
> +	 * counting.
> +	 */
> +	devm_remove_action(dev->dev, devm_drm_dev_init_release, dev);
> +
> +	ret = devm_add_action(dev->dev, devm_drm_dev_register_release, dev);
> +	if (ret)
> +		devm_drm_dev_register_release(dev);
> +
> +	return ret;
> +}
> +
> +/**
> + * devm_drm_dev_register_with_fbdev - Resource managed drm_dev_register()
> + *                                    including generic fbdev emulation
> + * @dev: DRM device to register
> + * @fbdev_bpp: Preferred bits per pixel for fbdev (optional)
> + *
> + * Managed drm_dev_register() that also calls drm_fbdev_generic_setup().
> + * The DRM device registered with this function is automatically unregistered on
> + * driver detach using drm_dev_unplug().
> + *
> + * Note: This function must be used together with devm_drm_dev_init().
> + *
> + * For testing driver detach can be triggered manually by writing to the driver
> + * 'unbind' file.
> + *
> + * RETURNS:
> + * 0 on success, negative error code on failure.
> + */
> +int devm_drm_dev_register_with_fbdev(struct drm_device *dev,
> +				     unsigned int fbdev_bpp)
> +{
> +	int ret;
> +
> +	ret = devm_drm_dev_register(dev);
> +	if (ret)
> +		return ret;
> +
> +	drm_fbdev_generic_setup(dev, fbdev_bpp);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(devm_drm_dev_register_with_fbdev);
> +
>  /**
>   * drm_dev_set_unique - Set the unique name of a DRM device
>   * @dev: device of which to set the unique name
> diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h
> index 35af23f5fa0d..c3f0477f2e7f 100644
> --- a/include/drm/drm_drv.h
> +++ b/include/drm/drm_drv.h
> @@ -628,6 +628,12 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver,
>  int drm_dev_register(struct drm_device *dev, unsigned long flags);
>  void drm_dev_unregister(struct drm_device *dev);
>  
> +int devm_drm_dev_init(struct device *parent,
> +		      struct drm_device *dev,
> +		      struct drm_driver *driver);
> +int devm_drm_dev_register_with_fbdev(struct drm_device *dev,
> +				     unsigned int fbdev_bpp);
> +
>  void drm_dev_get(struct drm_device *dev);
>  void drm_dev_put(struct drm_device *dev);
>  void drm_put_dev(struct drm_device *dev);
> -- 
> 2.20.1
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH 04/11] drm/tinydrm: Remove tinydrm_display_pipe_init()
  2019-01-20 11:43 ` [PATCH 04/11] drm/tinydrm: Remove tinydrm_display_pipe_init() Noralf Trønnes
  2019-01-21  6:30   ` Sam Ravnborg
@ 2019-01-21  9:15   ` Daniel Vetter
  2019-01-28 14:46     ` Noralf Trønnes
  1 sibling, 1 reply; 57+ messages in thread
From: Daniel Vetter @ 2019-01-21  9:15 UTC (permalink / raw)
  To: Noralf Trønnes; +Cc: david, dri-devel

On Sun, Jan 20, 2019 at 12:43:11PM +0100, Noralf Trønnes wrote:
> Further strip down tinydrm.ko and switch to drm_simple_connector_create().
> 
> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
> ---
>  Documentation/gpu/tinydrm.rst               |   3 -
>  drivers/gpu/drm/tinydrm/core/Makefile       |   2 +-
>  drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c | 183 --------------------
>  drivers/gpu/drm/tinydrm/mipi-dbi.c          |  24 ++-
>  drivers/gpu/drm/tinydrm/repaper.c           |  16 +-
>  drivers/gpu/drm/tinydrm/st7586.c            |  19 +-
>  include/drm/tinydrm/tinydrm.h               |   9 -
>  7 files changed, 43 insertions(+), 213 deletions(-)
>  delete mode 100644 drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c
> 
> diff --git a/Documentation/gpu/tinydrm.rst b/Documentation/gpu/tinydrm.rst
> index a913644bfc19..1ca726474af4 100644
> --- a/Documentation/gpu/tinydrm.rst
> +++ b/Documentation/gpu/tinydrm.rst
> @@ -17,9 +17,6 @@ Core functionality
>  .. kernel-doc:: drivers/gpu/drm/tinydrm/core/tinydrm-core.c
>     :export:
>  
> -.. kernel-doc:: drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c
> -   :export:
> -
>  Additional helpers
>  ==================
>  
> diff --git a/drivers/gpu/drm/tinydrm/core/Makefile b/drivers/gpu/drm/tinydrm/core/Makefile
> index fb221e6f8885..bf2df7326df7 100644
> --- a/drivers/gpu/drm/tinydrm/core/Makefile
> +++ b/drivers/gpu/drm/tinydrm/core/Makefile
> @@ -1,3 +1,3 @@
> -tinydrm-y := tinydrm-core.o tinydrm-pipe.o tinydrm-helpers.o
> +tinydrm-y := tinydrm-core.o tinydrm-helpers.o
>  
>  obj-$(CONFIG_DRM_TINYDRM) += tinydrm.o
> diff --git a/drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c b/drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c
> deleted file mode 100644
> index 323564329535..000000000000
> --- a/drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c
> +++ /dev/null
> @@ -1,183 +0,0 @@
> -/*
> - * Copyright (C) 2016 Noralf Trønnes
> - *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License as published by
> - * the Free Software Foundation; either version 2 of the License, or
> - * (at your option) any later version.
> - */
> -
> -#include <drm/drm_atomic_helper.h>
> -#include <drm/drm_crtc_helper.h>
> -#include <drm/drm_drv.h>
> -#include <drm/drm_gem_framebuffer_helper.h>
> -#include <drm/drm_modes.h>
> -#include <drm/drm_print.h>
> -#include <drm/tinydrm/tinydrm.h>
> -
> -struct tinydrm_connector {
> -	struct drm_connector base;
> -	struct drm_display_mode mode;
> -};
> -
> -static inline struct tinydrm_connector *
> -to_tinydrm_connector(struct drm_connector *connector)
> -{
> -	return container_of(connector, struct tinydrm_connector, base);
> -}
> -
> -static int tinydrm_connector_get_modes(struct drm_connector *connector)
> -{
> -	struct tinydrm_connector *tconn = to_tinydrm_connector(connector);
> -	struct drm_display_mode *mode;
> -
> -	mode = drm_mode_duplicate(connector->dev, &tconn->mode);
> -	if (!mode) {
> -		DRM_ERROR("Failed to duplicate mode\n");
> -		return 0;
> -	}
> -
> -	if (mode->name[0] == '\0')
> -		drm_mode_set_name(mode);
> -
> -	mode->type |= DRM_MODE_TYPE_PREFERRED;
> -	drm_mode_probed_add(connector, mode);
> -
> -	if (mode->width_mm) {
> -		connector->display_info.width_mm = mode->width_mm;
> -		connector->display_info.height_mm = mode->height_mm;
> -	}
> -
> -	return 1;
> -}
> -
> -static const struct drm_connector_helper_funcs tinydrm_connector_hfuncs = {
> -	.get_modes = tinydrm_connector_get_modes,
> -};
> -
> -static enum drm_connector_status
> -tinydrm_connector_detect(struct drm_connector *connector, bool force)
> -{
> -	if (drm_dev_is_unplugged(connector->dev))
> -		return connector_status_disconnected;
> -
> -	return connector->status;
> -}
> -
> -static void tinydrm_connector_destroy(struct drm_connector *connector)
> -{
> -	struct tinydrm_connector *tconn = to_tinydrm_connector(connector);
> -
> -	drm_connector_cleanup(connector);
> -	kfree(tconn);
> -}
> -
> -static const struct drm_connector_funcs tinydrm_connector_funcs = {
> -	.reset = drm_atomic_helper_connector_reset,
> -	.detect = tinydrm_connector_detect,
> -	.fill_modes = drm_helper_probe_single_connector_modes,
> -	.destroy = tinydrm_connector_destroy,
> -	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> -	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> -};
> -
> -struct drm_connector *
> -tinydrm_connector_create(struct drm_device *drm,
> -			 const struct drm_display_mode *mode,
> -			 int connector_type)
> -{
> -	struct tinydrm_connector *tconn;
> -	struct drm_connector *connector;
> -	int ret;
> -
> -	tconn = kzalloc(sizeof(*tconn), GFP_KERNEL);
> -	if (!tconn)
> -		return ERR_PTR(-ENOMEM);
> -
> -	drm_mode_copy(&tconn->mode, mode);
> -	connector = &tconn->base;
> -
> -	drm_connector_helper_add(connector, &tinydrm_connector_hfuncs);
> -	ret = drm_connector_init(drm, connector, &tinydrm_connector_funcs,
> -				 connector_type);
> -	if (ret) {
> -		kfree(tconn);
> -		return ERR_PTR(ret);
> -	}
> -
> -	connector->status = connector_status_connected;
> -
> -	return connector;
> -}
> -
> -static int tinydrm_rotate_mode(struct drm_display_mode *mode,
> -			       unsigned int rotation)
> -{
> -	if (rotation == 0 || rotation == 180) {
> -		return 0;
> -	} else if (rotation == 90 || rotation == 270) {
> -		swap(mode->hdisplay, mode->vdisplay);
> -		swap(mode->hsync_start, mode->vsync_start);
> -		swap(mode->hsync_end, mode->vsync_end);
> -		swap(mode->htotal, mode->vtotal);
> -		swap(mode->width_mm, mode->height_mm);
> -		return 0;
> -	} else {
> -		return -EINVAL;
> -	}
> -}
> -
> -/**
> - * tinydrm_display_pipe_init - Initialize display pipe
> - * @tdev: tinydrm device
> - * @funcs: Display pipe functions
> - * @connector_type: Connector type
> - * @formats: Array of supported formats (DRM_FORMAT\_\*)
> - * @format_count: Number of elements in @formats
> - * @mode: Supported mode
> - * @rotation: Initial @mode rotation in degrees Counter Clock Wise
> - *
> - * This function sets up a &drm_simple_display_pipe with a &drm_connector that
> - * has one fixed &drm_display_mode which is rotated according to @rotation.
> - *
> - * Returns:
> - * Zero on success, negative error code on failure.
> - */
> -int
> -tinydrm_display_pipe_init(struct tinydrm_device *tdev,
> -			  const struct drm_simple_display_pipe_funcs *funcs,
> -			  int connector_type,
> -			  const uint32_t *formats,
> -			  unsigned int format_count,
> -			  const struct drm_display_mode *mode,
> -			  unsigned int rotation)
> -{
> -	struct drm_device *drm = tdev->drm;
> -	struct drm_display_mode mode_copy;
> -	struct drm_connector *connector;
> -	int ret;
> -	static const uint64_t modifiers[] = {
> -		DRM_FORMAT_MOD_LINEAR,
> -		DRM_FORMAT_MOD_INVALID
> -	};
> -
> -	drm_mode_copy(&mode_copy, mode);
> -	ret = tinydrm_rotate_mode(&mode_copy, rotation);
> -	if (ret) {
> -		DRM_ERROR("Illegal rotation value %u\n", rotation);
> -		return -EINVAL;
> -	}
> -
> -	drm->mode_config.min_width = mode_copy.hdisplay;
> -	drm->mode_config.max_width = mode_copy.hdisplay;
> -	drm->mode_config.min_height = mode_copy.vdisplay;
> -	drm->mode_config.max_height = mode_copy.vdisplay;
> -
> -	connector = tinydrm_connector_create(drm, &mode_copy, connector_type);
> -	if (IS_ERR(connector))
> -		return PTR_ERR(connector);
> -
> -	return drm_simple_display_pipe_init(drm, &tdev->pipe, funcs, formats,
> -					    format_count, modifiers, connector);
> -}
> -EXPORT_SYMBOL(tinydrm_display_pipe_init);
> diff --git a/drivers/gpu/drm/tinydrm/mipi-dbi.c b/drivers/gpu/drm/tinydrm/mipi-dbi.c
> index 918f77c7de34..d1d546f3a664 100644
> --- a/drivers/gpu/drm/tinydrm/mipi-dbi.c
> +++ b/drivers/gpu/drm/tinydrm/mipi-dbi.c
> @@ -391,7 +391,13 @@ int mipi_dbi_init(struct device *dev, struct mipi_dbi *mipi,
>  {
>  	size_t bufsize = mode->vdisplay * mode->hdisplay * sizeof(u16);
>  	struct tinydrm_device *tdev = &mipi->tinydrm;
> +	struct drm_connector *connector;
> +	struct drm_device *drm;
>  	int ret;
> +	static const uint64_t modifiers[] = {
> +		DRM_FORMAT_MOD_LINEAR,
> +		DRM_FORMAT_MOD_INVALID
> +	};
>  
>  	if (!mipi->command)
>  		return -EINVAL;
> @@ -406,18 +412,22 @@ int mipi_dbi_init(struct device *dev, struct mipi_dbi *mipi,
>  	if (ret)
>  		return ret;
>  
> -	/* TODO: Maybe add DRM_MODE_CONNECTOR_SPI */
> -	ret = tinydrm_display_pipe_init(tdev, pipe_funcs,
> -					DRM_MODE_CONNECTOR_VIRTUAL,
> -					mipi_dbi_formats,
> -					ARRAY_SIZE(mipi_dbi_formats), mode,
> -					rotation);
> +	drm = tdev->drm;
> +
> +	connector = drm_simple_connector_create(drm, DRM_MODE_CONNECTOR_VIRTUAL, mode, rotation);

You lost the todo here ... _VIRTUAL is kinda a pretty good lie, since this
is definitely not virtual hw :-)
-Daniel

> +	if (IS_ERR(connector))
> +		return PTR_ERR(connector);
> +
> +	ret = drm_simple_display_pipe_init(drm, &tdev->pipe, pipe_funcs,
> +					   mipi_dbi_formats, ARRAY_SIZE(mipi_dbi_formats),
> +					   modifiers, connector);
>  	if (ret)
>  		return ret;
>  
>  	drm_plane_enable_fb_damage_clips(&tdev->pipe.plane);
>  
> -	tdev->drm->mode_config.preferred_depth = 16;
> +	drm_simple_connector_set_mode_config(connector);
> +	drm->mode_config.preferred_depth = 16;
>  	mipi->rotation = rotation;
>  
>  	drm_mode_config_reset(tdev->drm);
> diff --git a/drivers/gpu/drm/tinydrm/repaper.c b/drivers/gpu/drm/tinydrm/repaper.c
> index 72d30151ecd8..1d551744cc9b 100644
> --- a/drivers/gpu/drm/tinydrm/repaper.c
> +++ b/drivers/gpu/drm/tinydrm/repaper.c
> @@ -924,12 +924,14 @@ static int repaper_probe(struct spi_device *spi)
>  	const struct drm_display_mode *mode;
>  	const struct spi_device_id *spi_id;
>  	const struct of_device_id *match;
> +	struct drm_connector *connector;
>  	struct device *dev = &spi->dev;
>  	struct tinydrm_device *tdev;
>  	enum repaper_model model;
>  	const char *thermal_zone;
>  	struct repaper_epd *epd;
>  	size_t line_buffer_size;
> +	struct drm_device *drm;
>  	int ret;
>  
>  	match = of_match_device(repaper_of_match, dev);
> @@ -1069,13 +1071,19 @@ static int repaper_probe(struct spi_device *spi)
>  	if (ret)
>  		return ret;
>  
> -	ret = tinydrm_display_pipe_init(tdev, &repaper_pipe_funcs,
> -					DRM_MODE_CONNECTOR_VIRTUAL,
> -					repaper_formats,
> -					ARRAY_SIZE(repaper_formats), mode, 0);
> +	drm = tdev->drm;
> +
> +	connector = drm_simple_connector_create(drm, DRM_MODE_CONNECTOR_VIRTUAL, mode, 0);
> +	if (IS_ERR(connector))
> +		return PTR_ERR(connector);
> +
> +	ret = drm_simple_display_pipe_init(drm, &tdev->pipe, &repaper_pipe_funcs,
> +					   repaper_formats, ARRAY_SIZE(repaper_formats),
> +					   NULL, connector);
>  	if (ret)
>  		return ret;
>  
> +	drm_simple_connector_set_mode_config(connector);
>  	drm_mode_config_reset(tdev->drm);
>  	spi_set_drvdata(spi, tdev);
>  
> diff --git a/drivers/gpu/drm/tinydrm/st7586.c b/drivers/gpu/drm/tinydrm/st7586.c
> index 5ee7db561349..6cb68dd7e7b6 100644
> --- a/drivers/gpu/drm/tinydrm/st7586.c
> +++ b/drivers/gpu/drm/tinydrm/st7586.c
> @@ -271,6 +271,8 @@ static int st7586_init(struct device *dev, struct mipi_dbi *mipi,
>  {
>  	size_t bufsize = (mode->vdisplay + 2) / 3 * mode->hdisplay;
>  	struct tinydrm_device *tdev = &mipi->tinydrm;
> +	struct drm_connector *connector;
> +	struct drm_device *drm;
>  	int ret;
>  
>  	mutex_init(&mipi->cmdlock);
> @@ -283,17 +285,22 @@ static int st7586_init(struct device *dev, struct mipi_dbi *mipi,
>  	if (ret)
>  		return ret;
>  
> -	ret = tinydrm_display_pipe_init(tdev, pipe_funcs,
> -					DRM_MODE_CONNECTOR_VIRTUAL,
> -					st7586_formats,
> -					ARRAY_SIZE(st7586_formats),
> -					mode, rotation);
> +	drm = tdev->drm;
> +
> +	connector = drm_simple_connector_create(drm, DRM_MODE_CONNECTOR_VIRTUAL, mode, rotation);
> +	if (IS_ERR(connector))
> +		return PTR_ERR(connector);
> +
> +	ret = drm_simple_display_pipe_init(drm, &tdev->pipe, pipe_funcs,
> +					   st7586_formats, ARRAY_SIZE(st7586_formats),
> +					   NULL, connector);
>  	if (ret)
>  		return ret;
>  
>  	drm_plane_enable_fb_damage_clips(&tdev->pipe.plane);
>  
> -	tdev->drm->mode_config.preferred_depth = 32;
> +	drm_simple_connector_set_mode_config(connector);
> +	drm->mode_config.preferred_depth = 32;
>  	mipi->rotation = rotation;
>  
>  	drm_mode_config_reset(tdev->drm);
> diff --git a/include/drm/tinydrm/tinydrm.h b/include/drm/tinydrm/tinydrm.h
> index 87e7f9b93a37..69c4363fd88e 100644
> --- a/include/drm/tinydrm/tinydrm.h
> +++ b/include/drm/tinydrm/tinydrm.h
> @@ -40,13 +40,4 @@ int devm_tinydrm_init(struct device *parent, struct tinydrm_device *tdev,
>  int devm_tinydrm_register(struct tinydrm_device *tdev);
>  void tinydrm_shutdown(struct tinydrm_device *tdev);
>  
> -int
> -tinydrm_display_pipe_init(struct tinydrm_device *tdev,
> -			  const struct drm_simple_display_pipe_funcs *funcs,
> -			  int connector_type,
> -			  const uint32_t *formats,
> -			  unsigned int format_count,
> -			  const struct drm_display_mode *mode,
> -			  unsigned int rotation);
> -
>  #endif /* __LINUX_TINYDRM_H */
> -- 
> 2.20.1
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH 03/11] drm/simple-kms-helper: Add drm_simple_connector_create()
  2019-01-20 11:43 ` [PATCH 03/11] drm/simple-kms-helper: Add drm_simple_connector_create() Noralf Trønnes
  2019-01-20 22:14   ` Sam Ravnborg
@ 2019-01-21  9:22   ` Daniel Vetter
  2019-01-24 14:38     ` Noralf Trønnes
  1 sibling, 1 reply; 57+ messages in thread
From: Daniel Vetter @ 2019-01-21  9:22 UTC (permalink / raw)
  To: Noralf Trønnes; +Cc: david, dri-devel

On Sun, Jan 20, 2019 at 12:43:10PM +0100, Noralf Trønnes wrote:
> This adds a function that creates a simple connector that has only one
> static mode. Additionally add a helper to set &drm_mode_config width
> and height from the static mode.
> 
> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
> ---
>  drivers/gpu/drm/drm_simple_kms_helper.c | 122 ++++++++++++++++++++++++
>  include/drm/drm_simple_kms_helper.h     |   6 ++
>  2 files changed, 128 insertions(+)
> 
> diff --git a/drivers/gpu/drm/drm_simple_kms_helper.c b/drivers/gpu/drm/drm_simple_kms_helper.c
> index 917812448d1b..ca29975afefe 100644
> --- a/drivers/gpu/drm/drm_simple_kms_helper.c
> +++ b/drivers/gpu/drm/drm_simple_kms_helper.c
> @@ -11,6 +11,8 @@
>  #include <drm/drm_atomic.h>
>  #include <drm/drm_atomic_helper.h>
>  #include <drm/drm_crtc_helper.h>
> +#include <drm/drm_device.h>
> +#include <drm/drm_modes.h>
>  #include <drm/drm_plane_helper.h>
>  #include <drm/drm_simple_kms_helper.h>
>  #include <linux/slab.h>
> @@ -299,4 +301,124 @@ int drm_simple_display_pipe_init(struct drm_device *dev,
>  }
>  EXPORT_SYMBOL(drm_simple_display_pipe_init);
>  
> +static const struct drm_connector_helper_funcs drm_simple_connector_hfuncs = {
> +	/* dummy for the atomic helper */
> +};
> +
> +static int drm_simple_connector_fill_modes(struct drm_connector *connector,
> +					   uint32_t maxX, uint32_t maxY)
> +{
> +	return 1;
> +}
> +
> +static void drm_simple_connector_destroy(struct drm_connector *connector)
> +{
> +	drm_connector_cleanup(connector);
> +	kfree(connector);
> +}
> +
> +static const struct drm_connector_funcs drm_simple_connector_funcs = {
> +	.reset = drm_atomic_helper_connector_reset,
> +	.fill_modes = drm_simple_connector_fill_modes,
> +	.destroy = drm_simple_connector_destroy,
> +	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> +	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> +};
> +
> +/**
> + * drm_simple_connector_create - Create a connector with one static mode
> + * @dev: DRM device
> + * @connector_type: Connector type
> + * @mode: Supported display mode
> + * @rotation: Initial @mode rotation in degrees

We have rotation properties for this, pls don't use degress here.

Also would be good to wire this up with the rotation property Hans added
recently, for pre-rotated screens (see the kerneldoc for "panel
orientation" and drm_connector_init_panel_orientation_property()). So that
userspace knows how to rotate its rendering to make everything look
correct in the end again.


> + *
> + * This function creates a &drm_connector that has one fixed &drm_display_mode
> + * which will be rotated according to @rotation.

From a functionality pov this is very close to a panel wrapped into a
bridge. I think it would be good to differentiate a bit between these two
cases more. After all there was a really long discussion about how the
panel stuff does or does not exactly fit for tinydrm, would be good to
summarize this here and at least point at drm_panel_bridge_add().

Also would be good to explain in the overview DOC comment that this is a
fully independent part of the simple helpers, and drivers can use one or
the other.
-Daniel

> + *
> + * Returns:
> + * Pointer to connector on success, or ERR_PTR on failure.
> + */
> +struct drm_connector *
> +drm_simple_connector_create(struct drm_device *dev, int connector_type,
> +			    const struct drm_display_mode *mode,
> +			    unsigned int rotation)
> +{
> +	struct drm_display_mode *mode_dup = NULL;
> +	struct drm_connector *connector;
> +	int ret;
> +
> +	connector = kzalloc(sizeof(*connector), GFP_KERNEL);
> +	if (!connector)
> +		return ERR_PTR(-ENOMEM);
> +
> +	drm_connector_helper_add(connector, &drm_simple_connector_hfuncs);
> +	ret = drm_connector_init(dev, connector, &drm_simple_connector_funcs,
> +				 connector_type);
> +	if (ret)
> +		goto err_free;
> +
> +	connector->status = connector_status_connected;
> +
> +	mode_dup = drm_mode_duplicate(dev, mode);
> +	if (!mode_dup) {
> +		ret = -ENOMEM;
> +		goto err_cleanup;
> +	}
> +
> +	if (rotation == 90 || rotation == 270) {
> +		swap(mode_dup->hdisplay, mode_dup->vdisplay);
> +		swap(mode_dup->hsync_start, mode_dup->vsync_start);
> +		swap(mode_dup->hsync_end, mode_dup->vsync_end);
> +		swap(mode_dup->htotal, mode_dup->vtotal);
> +		swap(mode_dup->width_mm, mode_dup->height_mm);
> +	} else if (rotation != 0 && rotation != 180) {
> +		DRM_ERROR("Illegal rotation value %u\n", rotation);
> +		ret = -EINVAL;
> +		goto err_cleanup;
> +	}
> +
> +	mode_dup->type |= DRM_MODE_TYPE_PREFERRED;
> +	if (mode_dup->name[0] == '\0')
> +		drm_mode_set_name(mode_dup);
> +
> +	list_add(&mode_dup->head, &connector->modes);
> +
> +	connector->display_info.width_mm = mode_dup->width_mm;
> +	connector->display_info.height_mm = mode_dup->height_mm;
> +
> +	return connector;
> +
> +err_cleanup:
> +	drm_connector_cleanup(connector);
> +	drm_mode_destroy(dev, mode_dup);
> +err_free:
> +	kfree(connector);
> +
> +	return ERR_PTR(ret);
> +}
> +EXPORT_SYMBOL(drm_simple_connector_create);
> +
> +/**
> + * drm_simple_connector_set_mode_config - Set &drm_mode_config width and height
> + * @connector: Connector
> + *
> + * This function sets the &drm_mode_config min/max width and height based on the
> + * connector fixed display mode.
> + */
> +void drm_simple_connector_set_mode_config(struct drm_connector *connector)
> +{
> +	struct drm_mode_config *mode_config = &connector->dev->mode_config;
> +	struct drm_display_mode *mode;
> +
> +	mode = list_first_entry(&connector->modes, struct drm_display_mode, head);
> +	if (WARN_ON(!mode))
> +		return;
> +
> +	mode_config->min_width = mode->hdisplay;
> +	mode_config->max_width = mode->hdisplay;
> +	mode_config->min_height = mode->vdisplay;
> +	mode_config->max_height = mode->vdisplay;
> +}
> +EXPORT_SYMBOL(drm_simple_connector_set_mode_config);
> +
>  MODULE_LICENSE("GPL");
> diff --git a/include/drm/drm_simple_kms_helper.h b/include/drm/drm_simple_kms_helper.h
> index 451960438a29..ab3d847b7713 100644
> --- a/include/drm/drm_simple_kms_helper.h
> +++ b/include/drm/drm_simple_kms_helper.h
> @@ -182,4 +182,10 @@ int drm_simple_display_pipe_init(struct drm_device *dev,
>  			const uint64_t *format_modifiers,
>  			struct drm_connector *connector);
>  
> +struct drm_connector *
> +drm_simple_connector_create(struct drm_device *dev, int connector_type,
> +			    const struct drm_display_mode *mode,
> +			    unsigned int rotation);
> +void drm_simple_connector_set_mode_config(struct drm_connector *connector);
> +
>  #endif /* __LINUX_DRM_SIMPLE_KMS_HELPER_H */
> -- 
> 2.20.1
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH 09/11] drm/tinydrm: Remove tinydrm_device
  2019-01-20 11:43 ` [PATCH 09/11] drm/tinydrm: Remove tinydrm_device Noralf Trønnes
  2019-01-21  8:13   ` Sam Ravnborg
@ 2019-01-21  9:29   ` Daniel Vetter
  2019-01-21 13:30     ` Noralf Trønnes
  1 sibling, 1 reply; 57+ messages in thread
From: Daniel Vetter @ 2019-01-21  9:29 UTC (permalink / raw)
  To: Noralf Trønnes; +Cc: david, dri-devel

On Sun, Jan 20, 2019 at 12:43:16PM +0100, Noralf Trønnes wrote:
> No more users left so it can go alongside its helpers.
> Update the tinydrm docs description and remove todo entry.
> 
> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
> ---
>  Documentation/gpu/tinydrm.rst                 |  26 +--
>  Documentation/gpu/todo.rst                    |   4 -
>  drivers/gpu/drm/tinydrm/core/Makefile         |   2 +-
>  drivers/gpu/drm/tinydrm/core/tinydrm-core.c   | 169 ------------------
>  .../gpu/drm/tinydrm/core/tinydrm-helpers.c    |   2 +
>  include/drm/tinydrm/tinydrm.h                 |  42 -----
>  6 files changed, 10 insertions(+), 235 deletions(-)
>  delete mode 100644 drivers/gpu/drm/tinydrm/core/tinydrm-core.c
>  delete mode 100644 include/drm/tinydrm/tinydrm.h

Looks great. Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
> 
> diff --git a/Documentation/gpu/tinydrm.rst b/Documentation/gpu/tinydrm.rst
> index 1ca726474af4..19969b989efb 100644
> --- a/Documentation/gpu/tinydrm.rst
> +++ b/Documentation/gpu/tinydrm.rst
> @@ -1,24 +1,12 @@
> -==========================
> -drm/tinydrm Driver library
> -==========================
> +============================
> +drm/tinydrm Tiny DRM drivers
> +============================
>  
> -.. kernel-doc:: drivers/gpu/drm/tinydrm/core/tinydrm-core.c
> -   :doc: overview
> +tinydrm is a collection of DRM drivers that are so small they can fit in a
> +single source file.

Since it's now just a collection of tiny drivers I think we should also
rename the directory. Maybe in a follow-up patch, once this has all
settled. I think just /tiny/ or /tinydrivers/ or so would be better.
>  
> -Core functionality
> -==================
> -
> -.. kernel-doc:: drivers/gpu/drm/tinydrm/core/tinydrm-core.c
> -   :doc: core
> -
> -.. kernel-doc:: include/drm/tinydrm/tinydrm.h
> -   :internal:
> -
> -.. kernel-doc:: drivers/gpu/drm/tinydrm/core/tinydrm-core.c
> -   :export:

Yay!

> -
> -Additional helpers
> -==================
> +Helpers
> +=======
>  
>  .. kernel-doc:: include/drm/tinydrm/tinydrm-helpers.h
>     :internal:

Out of curiosity, what's left? From a quick look I think we could move the
memcp/swab helpers into a drm_framebuffer_helper.c file (maybe move the fb
argument first, since that's the main thing for ocd).

And the spi stuff is kinda just spi helpers I guess? Could we move those
into the spi subsystem, or was that idea already nacked?

> diff --git a/Documentation/gpu/todo.rst b/Documentation/gpu/todo.rst
> index 38360ede1221..3495aec7a8d4 100644
> --- a/Documentation/gpu/todo.rst
> +++ b/Documentation/gpu/todo.rst
> @@ -435,10 +435,6 @@ those drivers as simple as possible, so lots of room for refactoring:
>    one of the ideas for having a shared dsi/dbi helper, abstracting away the
>    transport details more.
>  
> -- Quick aside: The unregister devm stuff is kinda getting the lifetimes of
> -  a drm_device wrong. Doesn't matter, since everyone else gets it wrong
> -  too :-)

Hey I even wrote this already :-)

> -
>  Contact: Noralf Trønnes, Daniel Vetter
>  
>  AMD DC Display Driver
> diff --git a/drivers/gpu/drm/tinydrm/core/Makefile b/drivers/gpu/drm/tinydrm/core/Makefile
> index bf2df7326df7..f88ea7ad302f 100644
> --- a/drivers/gpu/drm/tinydrm/core/Makefile
> +++ b/drivers/gpu/drm/tinydrm/core/Makefile
> @@ -1,3 +1,3 @@
> -tinydrm-y := tinydrm-core.o tinydrm-helpers.o
> +tinydrm-y := tinydrm-helpers.o
>  
>  obj-$(CONFIG_DRM_TINYDRM) += tinydrm.o
> diff --git a/drivers/gpu/drm/tinydrm/core/tinydrm-core.c b/drivers/gpu/drm/tinydrm/core/tinydrm-core.c
> deleted file mode 100644
> index e4a77feaacd6..000000000000
> --- a/drivers/gpu/drm/tinydrm/core/tinydrm-core.c
> +++ /dev/null
> @@ -1,169 +0,0 @@
> -/*
> - * Copyright (C) 2016 Noralf Trønnes
> - *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License as published by
> - * the Free Software Foundation; either version 2 of the License, or
> - * (at your option) any later version.
> - */
> -
> -#include <drm/drm_atomic.h>
> -#include <drm/drm_atomic_helper.h>
> -#include <drm/drm_crtc_helper.h>
> -#include <drm/drm_drv.h>
> -#include <drm/drm_fb_helper.h>
> -#include <drm/drm_gem_framebuffer_helper.h>
> -#include <drm/drm_print.h>
> -#include <drm/tinydrm/tinydrm.h>
> -#include <linux/device.h>
> -#include <linux/dma-buf.h>
> -#include <linux/module.h>
> -
> -/**
> - * DOC: overview
> - *
> - * This library provides driver helpers for very simple display hardware.
> - *
> - * It is based on &drm_simple_display_pipe coupled with a &drm_connector which
> - * has only one fixed &drm_display_mode. The framebuffers are backed by the
> - * cma helper and have support for framebuffer flushing (dirty).
> - * fbdev support is also included.
> - *
> - */
> -
> -/**
> - * DOC: core
> - *
> - * The driver allocates &tinydrm_device, initializes it using
> - * devm_tinydrm_init(), sets up the pipeline using tinydrm_display_pipe_init()
> - * and registers the DRM device using devm_tinydrm_register().
> - */
> -
> -static const struct drm_mode_config_funcs tinydrm_mode_config_funcs = {
> -	.fb_create = drm_gem_fb_create_with_dirty,
> -	.atomic_check = drm_atomic_helper_check,
> -	.atomic_commit = drm_atomic_helper_commit,
> -};
> -
> -static int tinydrm_init(struct device *parent, struct tinydrm_device *tdev,
> -			struct drm_driver *driver)
> -{
> -	struct drm_device *drm;
> -
> -	/*
> -	 * We don't embed drm_device, because that prevent us from using
> -	 * devm_kzalloc() to allocate tinydrm_device in the driver since
> -	 * drm_dev_put() frees the structure. The devm_ functions provide
> -	 * for easy error handling.
> -	 */
> -	drm = drm_dev_alloc(driver, parent);
> -	if (IS_ERR(drm))
> -		return PTR_ERR(drm);
> -
> -	tdev->drm = drm;
> -	drm->dev_private = tdev;
> -	drm_mode_config_init(drm);
> -	drm->mode_config.funcs = &tinydrm_mode_config_funcs;
> -	drm->mode_config.allow_fb_modifiers = true;
> -
> -	return 0;
> -}
> -
> -static void tinydrm_fini(struct tinydrm_device *tdev)
> -{
> -	drm_mode_config_cleanup(tdev->drm);
> -	tdev->drm->dev_private = NULL;
> -	drm_dev_put(tdev->drm);
> -}
> -
> -static void devm_tinydrm_release(void *data)
> -{
> -	tinydrm_fini(data);
> -}
> -
> -/**
> - * devm_tinydrm_init - Initialize tinydrm device
> - * @parent: Parent device object
> - * @tdev: tinydrm device
> - * @driver: DRM driver
> - *
> - * This function initializes @tdev, the underlying DRM device and it's
> - * mode_config. Resources will be automatically freed on driver detach (devres)
> - * using drm_mode_config_cleanup() and drm_dev_put().
> - *
> - * Returns:
> - * Zero on success, negative error code on failure.
> - */
> -int devm_tinydrm_init(struct device *parent, struct tinydrm_device *tdev,
> -		      struct drm_driver *driver)
> -{
> -	int ret;
> -
> -	ret = tinydrm_init(parent, tdev, driver);
> -	if (ret)
> -		return ret;
> -
> -	ret = devm_add_action(parent, devm_tinydrm_release, tdev);
> -	if (ret)
> -		tinydrm_fini(tdev);
> -
> -	return ret;
> -}
> -EXPORT_SYMBOL(devm_tinydrm_init);
> -
> -static int tinydrm_register(struct tinydrm_device *tdev)
> -{
> -	struct drm_device *drm = tdev->drm;
> -	int ret;
> -
> -	ret = drm_dev_register(tdev->drm, 0);
> -	if (ret)
> -		return ret;
> -
> -	ret = drm_fbdev_generic_setup(drm, 0);
> -	if (ret)
> -		DRM_ERROR("Failed to initialize fbdev: %d\n", ret);
> -
> -	return 0;
> -}
> -
> -static void tinydrm_unregister(struct tinydrm_device *tdev)
> -{
> -	drm_atomic_helper_shutdown(tdev->drm);
> -	drm_dev_unregister(tdev->drm);
> -}
> -
> -static void devm_tinydrm_register_release(void *data)
> -{
> -	tinydrm_unregister(data);
> -}
> -
> -/**
> - * devm_tinydrm_register - Register tinydrm device
> - * @tdev: tinydrm device
> - *
> - * This function registers the underlying DRM device and fbdev.
> - * These resources will be automatically unregistered on driver detach (devres)
> - * and the display pipeline will be disabled.
> - *
> - * Returns:
> - * Zero on success, negative error code on failure.
> - */
> -int devm_tinydrm_register(struct tinydrm_device *tdev)
> -{
> -	struct device *dev = tdev->drm->dev;
> -	int ret;
> -
> -	ret = tinydrm_register(tdev);
> -	if (ret)
> -		return ret;
> -
> -	ret = devm_add_action(dev, devm_tinydrm_register_release, tdev);
> -	if (ret)
> -		tinydrm_unregister(tdev);
> -
> -	return ret;
> -}
> -EXPORT_SYMBOL(devm_tinydrm_register);
> -
> -MODULE_LICENSE("GPL");
> diff --git a/drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c b/drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c
> index 2737b6fdadc8..d7b38dfb6438 100644
> --- a/drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c
> +++ b/drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c
> @@ -365,3 +365,5 @@ int tinydrm_spi_transfer(struct spi_device *spi, u32 speed_hz,
>  EXPORT_SYMBOL(tinydrm_spi_transfer);
>  
>  #endif /* CONFIG_SPI */
> +
> +MODULE_LICENSE("GPL");
> diff --git a/include/drm/tinydrm/tinydrm.h b/include/drm/tinydrm/tinydrm.h
> deleted file mode 100644
> index ee9b17759391..000000000000
> --- a/include/drm/tinydrm/tinydrm.h
> +++ /dev/null
> @@ -1,42 +0,0 @@
> -/*
> - * Copyright (C) 2016 Noralf Trønnes
> - *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License as published by
> - * the Free Software Foundation; either version 2 of the License, or
> - * (at your option) any later version.
> - */
> -
> -#ifndef __LINUX_TINYDRM_H
> -#define __LINUX_TINYDRM_H
> -
> -#include <drm/drm_simple_kms_helper.h>
> -
> -struct drm_driver;
> -
> -/**
> - * struct tinydrm_device - tinydrm device
> - */
> -struct tinydrm_device {
> -	/**
> -	 * @drm: DRM device
> -	 */
> -	struct drm_device *drm;
> -
> -	/**
> -	 * @pipe: Display pipe structure
> -	 */
> -	struct drm_simple_display_pipe pipe;
> -};
> -
> -static inline struct tinydrm_device *
> -pipe_to_tinydrm(struct drm_simple_display_pipe *pipe)
> -{
> -	return container_of(pipe, struct tinydrm_device, pipe);
> -}
> -
> -int devm_tinydrm_init(struct device *parent, struct tinydrm_device *tdev,
> -		      struct drm_driver *driver);
> -int devm_tinydrm_register(struct tinydrm_device *tdev);
> -
> -#endif /* __LINUX_TINYDRM_H */
> -- 
> 2.20.1
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH 01/11] drm: Add devm_drm_dev_init/register
  2019-01-21  9:10   ` Daniel Vetter
@ 2019-01-21  9:55     ` Daniel Vetter
  2019-01-21 12:21       ` Noralf Trønnes
  0 siblings, 1 reply; 57+ messages in thread
From: Daniel Vetter @ 2019-01-21  9:55 UTC (permalink / raw)
  To: Noralf Trønnes; +Cc: david, dri-devel

On Mon, Jan 21, 2019 at 10:10:14AM +0100, Daniel Vetter wrote:
> On Sun, Jan 20, 2019 at 12:43:08PM +0100, Noralf Trønnes wrote:
> > This adds resource managed (devres) versions of drm_dev_init() and
> > drm_dev_register().
> > 
> > Also added is devm_drm_dev_register_with_fbdev() which sets up generic
> > fbdev emulation as well.
> > 
> > devm_drm_dev_register() isn't exported since there are no users.
> > 
> > Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
> 
> devm_ considered harmful unfortunately. You cannot allocate any structures
> which might outlive the lifetime of your device using devm_ on the device.
> Since drm_device has it's own lifetime, that structure and _everything_ we
> allocate tied to it (i.e. drm_encoder/plane/connector/crtc/whatever)
> cannot be allocated using devm_ infrastructure tied to the underlying
> parent device.
> 
> Yes this means almost all the devm usage in drm drivers is fundamentally
> broken. You also generally don't unplug gpus, so no one cares :-/
> 
> What could instead is add a struct device to drm_device somehow, use that
> struct device to manage the lifetime of the drm_device (would still need
> our special open counter maybe), and then use devm to allocate all the drm
> bits tied to the drm_device.
> 
> This here unfortunataly doesn't work, even if it looks like a really neat
> idea.

Strike all that, because I wasn't awak yet. Sorry for the confusion.

> > ---
> >  Documentation/driver-model/devres.txt |   4 +
> >  drivers/gpu/drm/drm_drv.c             | 106 ++++++++++++++++++++++++++
> >  include/drm/drm_drv.h                 |   6 ++
> >  3 files changed, 116 insertions(+)
> > 
> > diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt
> > index b277cafce71e..6eebc28d4c21 100644
> > --- a/Documentation/driver-model/devres.txt
> > +++ b/Documentation/driver-model/devres.txt
> > @@ -254,6 +254,10 @@ DMA
> >    dmam_pool_create()
> >    dmam_pool_destroy()
> >  
> > +DRM
> > +  devm_drm_dev_init()
> > +  devm_drm_dev_register_with_fbdev()
> > +
> >  GPIO
> >    devm_gpiod_get()
> >    devm_gpiod_get_index()
> > diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
> > index 381581b01d48..12129772be45 100644
> > --- a/drivers/gpu/drm/drm_drv.c
> > +++ b/drivers/gpu/drm/drm_drv.c
> > @@ -36,6 +36,7 @@
> >  
> >  #include <drm/drm_client.h>
> >  #include <drm/drm_drv.h>
> > +#include <drm/drm_fb_helper.h>
> >  #include <drm/drmP.h>
> >  
> >  #include "drm_crtc_internal.h"
> > @@ -871,6 +872,111 @@ void drm_dev_unregister(struct drm_device *dev)
> >  }
> >  EXPORT_SYMBOL(drm_dev_unregister);
> >  
> > +static void devm_drm_dev_init_release(void *data)
> > +{
> > +	drm_dev_put(data);

We need drm_dev_unplug() here, or this isn't safe.

> > +}
> > +
> > +/**
> > + * devm_drm_dev_init - Resource managed drm_dev_init()
> > + * @parent: Parent device object
> > + * @dev: DRM device
> > + * @driver: DRM driver
> > + *
> > + * Managed drm_dev_init(). The DRM device initialized with this function is
> > + * automatically released on driver detach. You must supply a

I think a bit more clarity here would be good:

"... automatically released on driver unbind by callind drm_dev_unplug()."

> > + * &drm_driver.release callback to control the finalization explicitly.

I think a loud warning for these is in order:

"WARNING:

"In generally it is unsafe to use devm functions for drm structures
because the lifetimes of &drm_device and the underlying &device do not
match. This here works because it doesn't immediately free anything, but
only calls drm_dev_unplug(), which internally decrements the &drm_device
refcount through drm_dev_put().

"All other drm structures must still be explicitly released in the
&drm_driver.release callback."

While thinking about this I just realized that with this design we have no
good place to call drm_atomic_helper_shutdown(). Which we need to, or all
kinds of things will leak badly (connectors, fb, ...), but there's no
place to call it:
- unbind is too early, since we haven't yet called drm_dev_unplug, and the
  drm_dev_unregister in there must be called _before_ we start to shut
  down anything.
- drm_driver.release is way too late.

Ofc for a real hotunplug there's no point in shutting down the hw (it's
already gone), but for a driver unload/unbind it would be nice if this
happens automatically and in the right order.

So not sure what to do here really.
-Daniel

> > + *
> > + * Note: This function must be used together with
> > + * devm_drm_dev_register_with_fbdev().
> > + *
> > + * RETURNS:
> > + * 0 on success, or error code on failure.
> > + */
> > +int devm_drm_dev_init(struct device *parent,
> > +		      struct drm_device *dev,
> > +		      struct drm_driver *driver)
> > +{
> > +	int ret;
> > +
> > +	if (WARN_ON(!parent || !driver->release))
> > +		return -EINVAL;
> > +
> > +	ret = drm_dev_init(dev, driver, parent);
> > +	if (ret)
> > +		return ret;
> > +
> > +	/*
> > +	 * This is a temporary release action that is used if probing fails
> > +	 * before devm_drm_dev_register() is called.
> > +	 */
> > +	ret = devm_add_action(parent, devm_drm_dev_init_release, dev);
> > +	if (ret)
> > +		devm_drm_dev_init_release(dev);
> > +
> > +	return ret;
> > +}
> > +EXPORT_SYMBOL(devm_drm_dev_init);
> > +
> > +static void devm_drm_dev_register_release(void *data)
> > +{
> > +	drm_dev_unplug(data);
> > +}
> > +
> > +static int devm_drm_dev_register(struct drm_device *dev)
> > +{
> > +	int ret;
> > +
> > +	ret = drm_dev_register(dev, 0);
> > +	if (ret)
> > +		return ret;
> > +
> > +	/*
> > +	 * This has now served it's purpose, remove it to not mess up ref
> > +	 * counting.
> > +	 */
> > +	devm_remove_action(dev->dev, devm_drm_dev_init_release, dev);
> > +
> > +	ret = devm_add_action(dev->dev, devm_drm_dev_register_release, dev);
> > +	if (ret)
> > +		devm_drm_dev_register_release(dev);
> > +
> > +	return ret;
> > +}
> > +
> > +/**
> > + * devm_drm_dev_register_with_fbdev - Resource managed drm_dev_register()
> > + *                                    including generic fbdev emulation
> > + * @dev: DRM device to register
> > + * @fbdev_bpp: Preferred bits per pixel for fbdev (optional)
> > + *
> > + * Managed drm_dev_register() that also calls drm_fbdev_generic_setup().
> > + * The DRM device registered with this function is automatically unregistered on
> > + * driver detach using drm_dev_unplug().
> > + *
> > + * Note: This function must be used together with devm_drm_dev_init().
> > + *
> > + * For testing driver detach can be triggered manually by writing to the driver
> > + * 'unbind' file.
> > + *
> > + * RETURNS:
> > + * 0 on success, negative error code on failure.
> > + */
> > +int devm_drm_dev_register_with_fbdev(struct drm_device *dev,
> > +				     unsigned int fbdev_bpp)
> > +{
> > +	int ret;
> > +
> > +	ret = devm_drm_dev_register(dev);
> > +	if (ret)
> > +		return ret;
> > +
> > +	drm_fbdev_generic_setup(dev, fbdev_bpp);
> > +
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL(devm_drm_dev_register_with_fbdev);
> > +
> >  /**
> >   * drm_dev_set_unique - Set the unique name of a DRM device
> >   * @dev: device of which to set the unique name
> > diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h
> > index 35af23f5fa0d..c3f0477f2e7f 100644
> > --- a/include/drm/drm_drv.h
> > +++ b/include/drm/drm_drv.h
> > @@ -628,6 +628,12 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver,
> >  int drm_dev_register(struct drm_device *dev, unsigned long flags);
> >  void drm_dev_unregister(struct drm_device *dev);
> >  
> > +int devm_drm_dev_init(struct device *parent,
> > +		      struct drm_device *dev,
> > +		      struct drm_driver *driver);
> > +int devm_drm_dev_register_with_fbdev(struct drm_device *dev,
> > +				     unsigned int fbdev_bpp);
> > +
> >  void drm_dev_get(struct drm_device *dev);
> >  void drm_dev_put(struct drm_device *dev);
> >  void drm_put_dev(struct drm_device *dev);
> > -- 
> > 2.20.1
> > 
> > _______________________________________________
> > dri-devel mailing list
> > dri-devel@lists.freedesktop.org
> > https://lists.freedesktop.org/mailman/listinfo/dri-devel
> 
> -- 
> Daniel Vetter
> Software Engineer, Intel Corporation
> http://blog.ffwll.ch

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH 01/11] drm: Add devm_drm_dev_init/register
  2019-01-21  9:55     ` Daniel Vetter
@ 2019-01-21 12:21       ` Noralf Trønnes
  2019-01-22  9:32         ` Daniel Vetter
  0 siblings, 1 reply; 57+ messages in thread
From: Noralf Trønnes @ 2019-01-21 12:21 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: david, dri-devel



Den 21.01.2019 10.55, skrev Daniel Vetter:
> On Mon, Jan 21, 2019 at 10:10:14AM +0100, Daniel Vetter wrote:
>> On Sun, Jan 20, 2019 at 12:43:08PM +0100, Noralf Trønnes wrote:
>>> This adds resource managed (devres) versions of drm_dev_init() and
>>> drm_dev_register().
>>>
>>> Also added is devm_drm_dev_register_with_fbdev() which sets up generic
>>> fbdev emulation as well.
>>>
>>> devm_drm_dev_register() isn't exported since there are no users.
>>>
>>> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
>>

<snip>

>>> diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
>>> index 381581b01d48..12129772be45 100644
>>> --- a/drivers/gpu/drm/drm_drv.c
>>> +++ b/drivers/gpu/drm/drm_drv.c
>>> @@ -36,6 +36,7 @@
>>>  
>>>  #include <drm/drm_client.h>
>>>  #include <drm/drm_drv.h>
>>> +#include <drm/drm_fb_helper.h>
>>>  #include <drm/drmP.h>
>>>  
>>>  #include "drm_crtc_internal.h"
>>> @@ -871,6 +872,111 @@ void drm_dev_unregister(struct drm_device *dev)
>>>  }
>>>  EXPORT_SYMBOL(drm_dev_unregister);
>>>  
>>> +static void devm_drm_dev_init_release(void *data)
>>> +{
>>> +	drm_dev_put(data);
> 
> We need drm_dev_unplug() here, or this isn't safe.

This function is only used to cover the error path if probe fails before
devm_drm_dev_register() is called. devm_drm_dev_register_release() is
the one that calls unplug. There are comments about this in the functions.

> 
>>> +}
>>> +
>>> +/**
>>> + * devm_drm_dev_init - Resource managed drm_dev_init()
>>> + * @parent: Parent device object
>>> + * @dev: DRM device
>>> + * @driver: DRM driver
>>> + *
>>> + * Managed drm_dev_init(). The DRM device initialized with this function is
>>> + * automatically released on driver detach. You must supply a
> 
> I think a bit more clarity here would be good:
> 
> "... automatically released on driver unbind by callind drm_dev_unplug()."
> 
>>> + * &drm_driver.release callback to control the finalization explicitly.
> 
> I think a loud warning for these is in order:
> 
> "WARNING:
> 
> "In generally it is unsafe to use devm functions for drm structures
> because the lifetimes of &drm_device and the underlying &device do not
> match. This here works because it doesn't immediately free anything, but
> only calls drm_dev_unplug(), which internally decrements the &drm_device
> refcount through drm_dev_put().
> 
> "All other drm structures must still be explicitly released in the
> &drm_driver.release callback."
> 
> While thinking about this I just realized that with this design we have no
> good place to call drm_atomic_helper_shutdown(). Which we need to, or all
> kinds of things will leak badly (connectors, fb, ...), but there's no
> place to call it:
> - unbind is too early, since we haven't yet called drm_dev_unplug, and the
>   drm_dev_unregister in there must be called _before_ we start to shut
>   down anything.
> - drm_driver.release is way too late.
> 
> Ofc for a real hotunplug there's no point in shutting down the hw (it's
> already gone), but for a driver unload/unbind it would be nice if this
> happens automatically and in the right order.
> 
> So not sure what to do here really.

How about this change: (it breaks the rule of pulling helpers into the
core, so maybe we should put the devm_ functions into the simple KMS
helper instead?)

diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 12129772be45..7ed9550baff6 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -34,7 +34,9 @@
 #include <linux/slab.h>
 #include <linux/srcu.h>

+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_client.h>
+#include <drm/drm_crtc_helper.h>
 #include <drm/drm_drv.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drmP.h>
@@ -355,17 +357,7 @@ void drm_dev_exit(int idx)
 }
 EXPORT_SYMBOL(drm_dev_exit);

-/**
- * drm_dev_unplug - unplug a DRM device
- * @dev: DRM device
- *
- * This unplugs a hotpluggable DRM device, which makes it inaccessible to
- * userspace operations. Entry-points can use drm_dev_enter() and
- * drm_dev_exit() to protect device resources in a race free manner. This
- * essentially unregisters the device like drm_dev_unregister(), but can be
- * called while there are still open users of @dev.
- */
-void drm_dev_unplug(struct drm_device *dev)
+static void __drm_dev_unplug(struct drm_device *dev, bool shutdown)
 {
        /*
         * After synchronizing any critical read section is guaranteed
to see
@@ -378,11 +370,32 @@ void drm_dev_unplug(struct drm_device *dev)

        drm_dev_unregister(dev);

+       if (shutdown)
+               drm_kms_helper_poll_fini(dev);
+
        mutex_lock(&drm_global_mutex);
-       if (dev->open_count == 0)
+       if (dev->open_count == 0) {
+               if (shutdown)
+                       drm_atomic_helper_shutdown(dev);
                drm_dev_put(dev);
+       }
        mutex_unlock(&drm_global_mutex);
 }
+
+/**
+ * drm_dev_unplug - unplug a DRM device
+ * @dev: DRM device
+ *
+ * This unplugs a hotpluggable DRM device, which makes it inaccessible to
+ * userspace operations. Entry-points can use drm_dev_enter() and
+ * drm_dev_exit() to protect device resources in a race free manner. This
+ * essentially unregisters the device like drm_dev_unregister(), but can be
+ * called while there are still open users of @dev.
+ */
+void drm_dev_unplug(struct drm_device *dev)
+{
+       __drm_dev_unplug(dev, false);
+}
 EXPORT_SYMBOL(drm_dev_unplug);

 /*
@@ -920,7 +933,7 @@ EXPORT_SYMBOL(devm_drm_dev_init);

 static void devm_drm_dev_register_release(void *data)
 {
-       drm_dev_unplug(data);
+       __drm_dev_unplug(data, true);
 }

 static int devm_drm_dev_register(struct drm_device *dev)


I realised that we should take a ref on the parent device so it can be
accessed by the DRM_DEV_ functions after unplug.


Noralf.


> 
>>> + *
>>> + * Note: This function must be used together with
>>> + * devm_drm_dev_register_with_fbdev().
>>> + *
>>> + * RETURNS:
>>> + * 0 on success, or error code on failure.
>>> + */
>>> +int devm_drm_dev_init(struct device *parent,
>>> +		      struct drm_device *dev,
>>> +		      struct drm_driver *driver)
>>> +{
>>> +	int ret;
>>> +
>>> +	if (WARN_ON(!parent || !driver->release))
>>> +		return -EINVAL;
>>> +
>>> +	ret = drm_dev_init(dev, driver, parent);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	/*
>>> +	 * This is a temporary release action that is used if probing fails
>>> +	 * before devm_drm_dev_register() is called.
>>> +	 */
>>> +	ret = devm_add_action(parent, devm_drm_dev_init_release, dev);
>>> +	if (ret)
>>> +		devm_drm_dev_init_release(dev);
>>> +
>>> +	return ret;
>>> +}
>>> +EXPORT_SYMBOL(devm_drm_dev_init);
>>> +
>>> +static void devm_drm_dev_register_release(void *data)
>>> +{
>>> +	drm_dev_unplug(data);
>>> +}
>>> +
>>> +static int devm_drm_dev_register(struct drm_device *dev)
>>> +{
>>> +	int ret;
>>> +
>>> +	ret = drm_dev_register(dev, 0);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	/*
>>> +	 * This has now served it's purpose, remove it to not mess up ref
>>> +	 * counting.
>>> +	 */
>>> +	devm_remove_action(dev->dev, devm_drm_dev_init_release, dev);
>>> +
>>> +	ret = devm_add_action(dev->dev, devm_drm_dev_register_release, dev);
>>> +	if (ret)
>>> +		devm_drm_dev_register_release(dev);
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +/**
>>> + * devm_drm_dev_register_with_fbdev - Resource managed drm_dev_register()
>>> + *                                    including generic fbdev emulation
>>> + * @dev: DRM device to register
>>> + * @fbdev_bpp: Preferred bits per pixel for fbdev (optional)
>>> + *
>>> + * Managed drm_dev_register() that also calls drm_fbdev_generic_setup().
>>> + * The DRM device registered with this function is automatically unregistered on
>>> + * driver detach using drm_dev_unplug().
>>> + *
>>> + * Note: This function must be used together with devm_drm_dev_init().
>>> + *
>>> + * For testing driver detach can be triggered manually by writing to the driver
>>> + * 'unbind' file.
>>> + *
>>> + * RETURNS:
>>> + * 0 on success, negative error code on failure.
>>> + */
>>> +int devm_drm_dev_register_with_fbdev(struct drm_device *dev,
>>> +				     unsigned int fbdev_bpp)
>>> +{
>>> +	int ret;
>>> +
>>> +	ret = devm_drm_dev_register(dev);
>>> +	if (ret)
>>> +		return ret;
>>> +
>>> +	drm_fbdev_generic_setup(dev, fbdev_bpp);
>>> +
>>> +	return 0;
>>> +}
>>> +EXPORT_SYMBOL(devm_drm_dev_register_with_fbdev);
>>> +
>>>  /**
>>>   * drm_dev_set_unique - Set the unique name of a DRM device
>>>   * @dev: device of which to set the unique name
>>> diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h
>>> index 35af23f5fa0d..c3f0477f2e7f 100644
>>> --- a/include/drm/drm_drv.h
>>> +++ b/include/drm/drm_drv.h
>>> @@ -628,6 +628,12 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver,
>>>  int drm_dev_register(struct drm_device *dev, unsigned long flags);
>>>  void drm_dev_unregister(struct drm_device *dev);
>>>  
>>> +int devm_drm_dev_init(struct device *parent,
>>> +		      struct drm_device *dev,
>>> +		      struct drm_driver *driver);
>>> +int devm_drm_dev_register_with_fbdev(struct drm_device *dev,
>>> +				     unsigned int fbdev_bpp);
>>> +
>>>  void drm_dev_get(struct drm_device *dev);
>>>  void drm_dev_put(struct drm_device *dev);
>>>  void drm_put_dev(struct drm_device *dev);
>>> -- 
>>> 2.20.1
>>>
>>> _______________________________________________
>>> dri-devel mailing list
>>> dri-devel@lists.freedesktop.org
>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>
>> -- 
>> Daniel Vetter
>> Software Engineer, Intel Corporation
>> http://blog.ffwll.ch
> 
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH 01/11] drm: Add devm_drm_dev_init/register
  2019-01-21  6:11   ` Sam Ravnborg
@ 2019-01-21 13:09     ` Noralf Trønnes
  0 siblings, 0 replies; 57+ messages in thread
From: Noralf Trønnes @ 2019-01-21 13:09 UTC (permalink / raw)
  To: Sam Ravnborg; +Cc: david, dri-devel



Den 21.01.2019 07.11, skrev Sam Ravnborg:
> Hi Noralf.
> 
> On Sun, Jan 20, 2019 at 12:43:08PM +0100, Noralf Trønnes wrote:
>> This adds resource managed (devres) versions of drm_dev_init() and
>> drm_dev_register().
>>
>> Also added is devm_drm_dev_register_with_fbdev() which sets up generic
>> fbdev emulation as well.
>>
>> devm_drm_dev_register() isn't exported since there are no users.
> Is it relevant to use it outside this patchset - then it should be
> documented/exported now to enable use.

As a rule we don't export functions that doesn't have any users. It can
be exported when a user shows up. But maybe it would make sense to put
all the documentation in devm_drm_dev_register() instead of duplicating
it all in the two functions when they are both in use. Or it can be
moved to devm_drm_dev_register() when it gets a user. I'm good with
either, don't know what Daniel prefers.

> 
>>
>> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
>> ---
>>  Documentation/driver-model/devres.txt |   4 +
>>  drivers/gpu/drm/drm_drv.c             | 106 ++++++++++++++++++++++++++
>>  include/drm/drm_drv.h                 |   6 ++
>>  3 files changed, 116 insertions(+)
>>
>> diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt
>> index b277cafce71e..6eebc28d4c21 100644
>> --- a/Documentation/driver-model/devres.txt
>> +++ b/Documentation/driver-model/devres.txt
>> @@ -254,6 +254,10 @@ DMA
>>    dmam_pool_create()
>>    dmam_pool_destroy()
>>  
>> +DRM
>> +  devm_drm_dev_init()
>> +  devm_drm_dev_register_with_fbdev()
>> +
>>  GPIO
>>    devm_gpiod_get()
>>    devm_gpiod_get_index()
>> diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
>> index 381581b01d48..12129772be45 100644
>> --- a/drivers/gpu/drm/drm_drv.c
>> +++ b/drivers/gpu/drm/drm_drv.c
>> @@ -36,6 +36,7 @@
>>  
>>  #include <drm/drm_client.h>
>>  #include <drm/drm_drv.h>
>> +#include <drm/drm_fb_helper.h>
>>  #include <drm/drmP.h>
>>  
>>  #include "drm_crtc_internal.h"
>> @@ -871,6 +872,111 @@ void drm_dev_unregister(struct drm_device *dev)
>>  }
>>  EXPORT_SYMBOL(drm_dev_unregister);
>>  
>> +static void devm_drm_dev_init_release(void *data)
>> +{
>> +	drm_dev_put(data);
>> +}
>> +
>> +/**
>> + * devm_drm_dev_init - Resource managed drm_dev_init()
>> + * @parent: Parent device object
>> + * @dev: DRM device
>> + * @driver: DRM driver
>> + *
>> + * Managed drm_dev_init(). The DRM device initialized with this function is
>> + * automatically released on driver detach. You must supply a
>> + * &drm_driver.release callback to control the finalization explicitly.
>> + *
>> + * Note: This function must be used together with
>> + * devm_drm_dev_register_with_fbdev().
> *must* be used, or can be used?
> Reading the code this looks like something drivers that do not offer
> fbdev emulation could also benefit from.

They must be used together to not break ref counting. When
devm_drm_dev_register() gets a user it will read:

 * Note: This function must be used together with
 * devm_drm_dev_register() or devm_drm_dev_register_with_fbdev().

Noralf.

> 
> With the two comments processed this has:
> Reviewed-by: Sam Ravnborg <sam@ravnborg.org>
> 
> 	Sam
> 
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH 07/11] drm/tinydrm/repaper: Use devm_drm_dev_*()
  2019-01-20 22:25     ` Sam Ravnborg
@ 2019-01-21 13:15       ` Noralf Trønnes
  0 siblings, 0 replies; 57+ messages in thread
From: Noralf Trønnes @ 2019-01-21 13:15 UTC (permalink / raw)
  To: Sam Ravnborg; +Cc: david, dri-devel



Den 20.01.2019 23.25, skrev Sam Ravnborg:
> On Sun, Jan 20, 2019 at 11:22:36PM +0100, Sam Ravnborg wrote:
>> Hi Noralf.
>>
>> On Sun, Jan 20, 2019 at 12:43:14PM +0100, Noralf Trønnes wrote:
>>> Use devm_drm_dev_init(), devm_drm_dev_register_with_fbdev() and drop
>>> using tinydrm_device.
>>>
>>> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
>>> ---
>>>  drivers/gpu/drm/tinydrm/repaper.c | 63 ++++++++++++++++++++-----------
>>>  1 file changed, 40 insertions(+), 23 deletions(-)
>>>
>>> diff --git a/drivers/gpu/drm/tinydrm/repaper.c b/drivers/gpu/drm/tinydrm/repaper.c
>>> index 3141241ca8f0..4c8205565668 100644
>>> --- a/drivers/gpu/drm/tinydrm/repaper.c
>>> +++ b/drivers/gpu/drm/tinydrm/repaper.c
>>> @@ -34,7 +34,7 @@
>>>  #include <drm/drm_gem_framebuffer_helper.h>
>>>  #include <drm/drm_rect.h>
>>>  #include <drm/drm_vblank.h>
>>> -#include <drm/tinydrm/tinydrm.h>
>>> +#include <drm/drm_simple_kms_helper.h>
>>>  #include <drm/tinydrm/tinydrm-helpers.h>
>>>  
>>>  #define REPAPER_RID_G2_COG_ID	0x12
>>> @@ -60,7 +60,8 @@ enum repaper_epd_border_byte {
>>>  };
>>>  
>>>  struct repaper_epd {
>>> -	struct tinydrm_device tinydrm;
>>> +	struct drm_device base;
>>> +	struct drm_simple_display_pipe pipe;
>>>  	struct spi_device *spi;
>>>  
>>>  	struct gpio_desc *panel_on;
>>> @@ -89,10 +90,9 @@ struct repaper_epd {
>>>  	bool partial;
>>>  };
>>>  
>>> -static inline struct repaper_epd *
>>> -epd_from_tinydrm(struct tinydrm_device *tdev)
>>> +static inline struct repaper_epd *drm_to_epd(struct drm_device *drm)
>>>  {
>>> -	return container_of(tdev, struct repaper_epd, tinydrm);
>>> +	return container_of(drm, struct repaper_epd, base);
>>>  }
>>>  
>>>  static int repaper_spi_transfer(struct spi_device *spi, u8 header,
>>> @@ -530,8 +530,7 @@ static int repaper_fb_dirty(struct drm_framebuffer *fb)
>>>  {
>>>  	struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
>>>  	struct dma_buf_attachment *import_attach = cma_obj->base.import_attach;
>>> -	struct tinydrm_device *tdev = fb->dev->dev_private;
>>> -	struct repaper_epd *epd = epd_from_tinydrm(tdev);
>>> +	struct repaper_epd *epd = drm_to_epd(fb->dev);
>>>  	struct drm_rect clip;
>>>  	u8 *buf = NULL;
>>>  	int ret = 0;
>>> @@ -646,8 +645,7 @@ static void repaper_pipe_enable(struct drm_simple_display_pipe *pipe,
>>>  				struct drm_crtc_state *crtc_state,
>>>  				struct drm_plane_state *plane_state)
>>>  {
>>> -	struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
>>> -	struct repaper_epd *epd = epd_from_tinydrm(tdev);
>>> +	struct repaper_epd *epd = drm_to_epd(pipe->crtc.dev);
>>>  	struct spi_device *spi = epd->spi;
>>>  	struct device *dev = &spi->dev;
>>>  	bool dc_ok = false;
>>> @@ -781,8 +779,7 @@ static void repaper_pipe_enable(struct drm_simple_display_pipe *pipe,
>>>  
>>>  static void repaper_pipe_disable(struct drm_simple_display_pipe *pipe)
>>>  {
>>> -	struct tinydrm_device *tdev = pipe_to_tinydrm(pipe);
>>> -	struct repaper_epd *epd = epd_from_tinydrm(tdev);
>>> +	struct repaper_epd *epd = drm_to_epd(pipe->crtc.dev);
>>>  	struct spi_device *spi = epd->spi;
>>>  	unsigned int line;
>>>  
>>> @@ -856,6 +853,23 @@ static const struct drm_simple_display_pipe_funcs repaper_pipe_funcs = {
>>>  	.prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb,
>>>  };
>>>  
>>> +static const struct drm_mode_config_funcs repaper_mode_config_funcs = {
>>> +	.fb_create = drm_gem_fb_create_with_dirty,
>>> +	.atomic_check = drm_atomic_helper_check,
>>> +	.atomic_commit = drm_atomic_helper_commit,
>>> +};
>>> +
>>> +static void repaper_release(struct drm_device *drm)
>>> +{
>>> +	struct repaper_epd *epd = drm_to_epd(drm);
>>> +
>>> +	DRM_DEBUG_DRIVER("\n");
>>> +
>>> +	drm_mode_config_cleanup(drm);
>>> +	drm_dev_fini(drm);
>>> +	kfree(epd);
>>> +}
>>> +
>>>  static const uint32_t repaper_formats[] = {
>>>  	DRM_FORMAT_XRGB8888,
>>>  };
>>> @@ -894,6 +908,7 @@ static struct drm_driver repaper_driver = {
>>>  	.driver_features	= DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
>>>  				  DRIVER_ATOMIC,
>>>  	.fops			= &repaper_fops,
>>> +	.release		= repaper_release,
>>>  	DRM_GEM_CMA_VMAP_DRIVER_OPS,
>>>  	.name			= "repaper",
>>>  	.desc			= "Pervasive Displays RePaper e-ink panels",
>>> @@ -927,7 +942,6 @@ static int repaper_probe(struct spi_device *spi)
>>>  	const struct of_device_id *match;
>>>  	struct drm_connector *connector;
>>>  	struct device *dev = &spi->dev;
>>> -	struct tinydrm_device *tdev;
>>>  	enum repaper_model model;
>>>  	const char *thermal_zone;
>>>  	struct repaper_epd *epd;
>>> @@ -952,10 +966,20 @@ static int repaper_probe(struct spi_device *spi)
>>>  		}
>>>  	}
>>>  
>>> -	epd = devm_kzalloc(dev, sizeof(*epd), GFP_KERNEL);
>>> +	epd = kzalloc(sizeof(*epd), GFP_KERNEL);
>>>  	if (!epd)
>>>  		return -ENOMEM;
>>>  
>>> +	drm = &epd->base;
>>> +	ret = devm_drm_dev_init(dev, drm, &repaper_driver);
>>> +	if (ret) {
>>> +		kfree(epd);
>>> +		return ret;
>>> +	}
>> Here you go to the trouble and free epd on error.
>>
>>> +
>>> +	drm_mode_config_init(drm);
>>> +	drm->mode_config.funcs = &repaper_mode_config_funcs;
>>> +
>>>  	epd->spi = spi;
>>>  
>>>  	epd->panel_on = devm_gpiod_get(dev, "panel-on", GPIOD_OUT_LOW);
>>> @@ -1066,32 +1090,25 @@ static int repaper_probe(struct spi_device *spi)
>>>  	if (!epd->current_frame)
>>>  		return -ENOMEM;
>>>  
>>> -	tdev = &epd->tinydrm;
>>> -
>>> -	ret = devm_tinydrm_init(dev, tdev, &repaper_driver);
>>> -	if (ret)
>>> -		return ret;
>>> -
>>> -	drm = tdev->drm;
>>>  
>>>  	connector = drm_simple_connector_create(drm, DRM_MODE_CONNECTOR_VIRTUAL, mode, 0);
>>>  	if (IS_ERR(connector))
>>>  		return PTR_ERR(connector);
>>>  
>>> -	ret = drm_simple_display_pipe_init(drm, &tdev->pipe, &repaper_pipe_funcs,
>>> +	ret = drm_simple_display_pipe_init(drm, &epd->pipe, &repaper_pipe_funcs,
>>>  					   repaper_formats, ARRAY_SIZE(repaper_formats),
>>>  					   NULL, connector);
>>>  	if (ret)
>>>  		return ret;
>> But later in the same function epd is not freed.
>> Looks like a leak?
>> Nothing new if this is correct but just spotted it.
> 
> The part with "nothing new" I take back. This code converts
> devm_kzalloc() to the unmanaged variant.
> So the leak is new - and also present in
> drivers in next patch (if there is a leak).
> 
> Why is the conversion to an unmanaged variant of kzalloc() required?
> 

epd is freed in repaper_release(), but if devm_drm_dev_init() fails it
won't get called so it's freed in that particular error path.

Noralf.
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH 00/11] drm/tinydrm: Remove tinydrm_device
  2019-01-21  8:34 ` [PATCH 00/11] drm/tinydrm: Remove tinydrm_device Sam Ravnborg
@ 2019-01-21 13:20   ` Noralf Trønnes
  0 siblings, 0 replies; 57+ messages in thread
From: Noralf Trønnes @ 2019-01-21 13:20 UTC (permalink / raw)
  To: Sam Ravnborg; +Cc: david, dri-devel



Den 21.01.2019 09.34, skrev Sam Ravnborg:
> Hi Noralf.
> 
> On Sun, Jan 20, 2019 at 12:43:07PM +0100, Noralf Trønnes wrote:
>> This patchset is part of the effort to remove tinydrm.ko. It removes
>> struct tinydrm_device and tinydrm.h.
>>
>> While doing this refactoring I have ensured that device unplug is
>> working.
> 
> Very nice series, it looks good the way the core get a little extra
> functionality thus the tinydrm drivers are now more normal drivers.

Yes, I'm happy to see these drivers act more like normal drivers now.
Daniel wasn't happy about the tinydrm midlayer so I'm glad it's going away.

> 
> A few comments for some patches, and those where I felt confident
> that I could follow the code got a "Reviewed-by:".

Thanks for going through the patches.

Noralf.
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH 09/11] drm/tinydrm: Remove tinydrm_device
  2019-01-21  9:29   ` Daniel Vetter
@ 2019-01-21 13:30     ` Noralf Trønnes
  0 siblings, 0 replies; 57+ messages in thread
From: Noralf Trønnes @ 2019-01-21 13:30 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: david, dri-devel



Den 21.01.2019 10.29, skrev Daniel Vetter:
> On Sun, Jan 20, 2019 at 12:43:16PM +0100, Noralf Trønnes wrote:
>> No more users left so it can go alongside its helpers.
>> Update the tinydrm docs description and remove todo entry.
>>
>> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
>> ---
>>  Documentation/gpu/tinydrm.rst                 |  26 +--
>>  Documentation/gpu/todo.rst                    |   4 -
>>  drivers/gpu/drm/tinydrm/core/Makefile         |   2 +-
>>  drivers/gpu/drm/tinydrm/core/tinydrm-core.c   | 169 ------------------
>>  .../gpu/drm/tinydrm/core/tinydrm-helpers.c    |   2 +
>>  include/drm/tinydrm/tinydrm.h                 |  42 -----
>>  6 files changed, 10 insertions(+), 235 deletions(-)
>>  delete mode 100644 drivers/gpu/drm/tinydrm/core/tinydrm-core.c
>>  delete mode 100644 include/drm/tinydrm/tinydrm.h
> 
> Looks great. Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>

I'm glad that I've finally come to the point where I'm able to get rid
of the midlayer smell that you have talked about :-)

>>
>> diff --git a/Documentation/gpu/tinydrm.rst b/Documentation/gpu/tinydrm.rst
>> index 1ca726474af4..19969b989efb 100644
>> --- a/Documentation/gpu/tinydrm.rst
>> +++ b/Documentation/gpu/tinydrm.rst
>> @@ -1,24 +1,12 @@
>> -==========================
>> -drm/tinydrm Driver library
>> -==========================
>> +============================
>> +drm/tinydrm Tiny DRM drivers
>> +============================
>>  
>> -.. kernel-doc:: drivers/gpu/drm/tinydrm/core/tinydrm-core.c
>> -   :doc: overview
>> +tinydrm is a collection of DRM drivers that are so small they can fit in a
>> +single source file.
> 
> Since it's now just a collection of tiny drivers I think we should also
> rename the directory. Maybe in a follow-up patch, once this has all
> settled. I think just /tiny/ or /tinydrivers/ or so would be better.
>>  
>> -Core functionality
>> -==================
>> -
>> -.. kernel-doc:: drivers/gpu/drm/tinydrm/core/tinydrm-core.c
>> -   :doc: core
>> -
>> -.. kernel-doc:: include/drm/tinydrm/tinydrm.h
>> -   :internal:
>> -
>> -.. kernel-doc:: drivers/gpu/drm/tinydrm/core/tinydrm-core.c
>> -   :export:
> 
> Yay!
> 
>> -
>> -Additional helpers
>> -==================
>> +Helpers
>> +=======
>>  
>>  .. kernel-doc:: include/drm/tinydrm/tinydrm-helpers.h
>>     :internal:
> 
> Out of curiosity, what's left? From a quick look I think we could move the
> memcp/swab helpers into a drm_framebuffer_helper.c file (maybe move the fb
> argument first, since that's the main thing for ocd).

Yep.

> 
> And the spi stuff is kinda just spi helpers I guess? Could we move those
> into the spi subsystem, or was that idea already nacked?

Meghana started on this, but didn't finish. I don't think there was any
blockers, so it's just to pick it up where she left it.

There's also the mipi_dbi helper which should be moved along side
drm_mipi_dsi.

My plan is to do this when I'm done with the modesetting part of drm_client.

Noralf.

> 
>> diff --git a/Documentation/gpu/todo.rst b/Documentation/gpu/todo.rst
>> index 38360ede1221..3495aec7a8d4 100644
>> --- a/Documentation/gpu/todo.rst
>> +++ b/Documentation/gpu/todo.rst
>> @@ -435,10 +435,6 @@ those drivers as simple as possible, so lots of room for refactoring:
>>    one of the ideas for having a shared dsi/dbi helper, abstracting away the
>>    transport details more.
>>  
>> -- Quick aside: The unregister devm stuff is kinda getting the lifetimes of
>> -  a drm_device wrong. Doesn't matter, since everyone else gets it wrong
>> -  too :-)
> 
> Hey I even wrote this already :-)
> 
>> -
>>  Contact: Noralf Trønnes, Daniel Vetter
>>  
>>  AMD DC Display Driver
>> diff --git a/drivers/gpu/drm/tinydrm/core/Makefile b/drivers/gpu/drm/tinydrm/core/Makefile
>> index bf2df7326df7..f88ea7ad302f 100644
>> --- a/drivers/gpu/drm/tinydrm/core/Makefile
>> +++ b/drivers/gpu/drm/tinydrm/core/Makefile
>> @@ -1,3 +1,3 @@
>> -tinydrm-y := tinydrm-core.o tinydrm-helpers.o
>> +tinydrm-y := tinydrm-helpers.o
>>  
>>  obj-$(CONFIG_DRM_TINYDRM) += tinydrm.o
>> diff --git a/drivers/gpu/drm/tinydrm/core/tinydrm-core.c b/drivers/gpu/drm/tinydrm/core/tinydrm-core.c
>> deleted file mode 100644
>> index e4a77feaacd6..000000000000
>> --- a/drivers/gpu/drm/tinydrm/core/tinydrm-core.c
>> +++ /dev/null
>> @@ -1,169 +0,0 @@
>> -/*
>> - * Copyright (C) 2016 Noralf Trønnes
>> - *
>> - * This program is free software; you can redistribute it and/or modify
>> - * it under the terms of the GNU General Public License as published by
>> - * the Free Software Foundation; either version 2 of the License, or
>> - * (at your option) any later version.
>> - */
>> -
>> -#include <drm/drm_atomic.h>
>> -#include <drm/drm_atomic_helper.h>
>> -#include <drm/drm_crtc_helper.h>
>> -#include <drm/drm_drv.h>
>> -#include <drm/drm_fb_helper.h>
>> -#include <drm/drm_gem_framebuffer_helper.h>
>> -#include <drm/drm_print.h>
>> -#include <drm/tinydrm/tinydrm.h>
>> -#include <linux/device.h>
>> -#include <linux/dma-buf.h>
>> -#include <linux/module.h>
>> -
>> -/**
>> - * DOC: overview
>> - *
>> - * This library provides driver helpers for very simple display hardware.
>> - *
>> - * It is based on &drm_simple_display_pipe coupled with a &drm_connector which
>> - * has only one fixed &drm_display_mode. The framebuffers are backed by the
>> - * cma helper and have support for framebuffer flushing (dirty).
>> - * fbdev support is also included.
>> - *
>> - */
>> -
>> -/**
>> - * DOC: core
>> - *
>> - * The driver allocates &tinydrm_device, initializes it using
>> - * devm_tinydrm_init(), sets up the pipeline using tinydrm_display_pipe_init()
>> - * and registers the DRM device using devm_tinydrm_register().
>> - */
>> -
>> -static const struct drm_mode_config_funcs tinydrm_mode_config_funcs = {
>> -	.fb_create = drm_gem_fb_create_with_dirty,
>> -	.atomic_check = drm_atomic_helper_check,
>> -	.atomic_commit = drm_atomic_helper_commit,
>> -};
>> -
>> -static int tinydrm_init(struct device *parent, struct tinydrm_device *tdev,
>> -			struct drm_driver *driver)
>> -{
>> -	struct drm_device *drm;
>> -
>> -	/*
>> -	 * We don't embed drm_device, because that prevent us from using
>> -	 * devm_kzalloc() to allocate tinydrm_device in the driver since
>> -	 * drm_dev_put() frees the structure. The devm_ functions provide
>> -	 * for easy error handling.
>> -	 */
>> -	drm = drm_dev_alloc(driver, parent);
>> -	if (IS_ERR(drm))
>> -		return PTR_ERR(drm);
>> -
>> -	tdev->drm = drm;
>> -	drm->dev_private = tdev;
>> -	drm_mode_config_init(drm);
>> -	drm->mode_config.funcs = &tinydrm_mode_config_funcs;
>> -	drm->mode_config.allow_fb_modifiers = true;
>> -
>> -	return 0;
>> -}
>> -
>> -static void tinydrm_fini(struct tinydrm_device *tdev)
>> -{
>> -	drm_mode_config_cleanup(tdev->drm);
>> -	tdev->drm->dev_private = NULL;
>> -	drm_dev_put(tdev->drm);
>> -}
>> -
>> -static void devm_tinydrm_release(void *data)
>> -{
>> -	tinydrm_fini(data);
>> -}
>> -
>> -/**
>> - * devm_tinydrm_init - Initialize tinydrm device
>> - * @parent: Parent device object
>> - * @tdev: tinydrm device
>> - * @driver: DRM driver
>> - *
>> - * This function initializes @tdev, the underlying DRM device and it's
>> - * mode_config. Resources will be automatically freed on driver detach (devres)
>> - * using drm_mode_config_cleanup() and drm_dev_put().
>> - *
>> - * Returns:
>> - * Zero on success, negative error code on failure.
>> - */
>> -int devm_tinydrm_init(struct device *parent, struct tinydrm_device *tdev,
>> -		      struct drm_driver *driver)
>> -{
>> -	int ret;
>> -
>> -	ret = tinydrm_init(parent, tdev, driver);
>> -	if (ret)
>> -		return ret;
>> -
>> -	ret = devm_add_action(parent, devm_tinydrm_release, tdev);
>> -	if (ret)
>> -		tinydrm_fini(tdev);
>> -
>> -	return ret;
>> -}
>> -EXPORT_SYMBOL(devm_tinydrm_init);
>> -
>> -static int tinydrm_register(struct tinydrm_device *tdev)
>> -{
>> -	struct drm_device *drm = tdev->drm;
>> -	int ret;
>> -
>> -	ret = drm_dev_register(tdev->drm, 0);
>> -	if (ret)
>> -		return ret;
>> -
>> -	ret = drm_fbdev_generic_setup(drm, 0);
>> -	if (ret)
>> -		DRM_ERROR("Failed to initialize fbdev: %d\n", ret);
>> -
>> -	return 0;
>> -}
>> -
>> -static void tinydrm_unregister(struct tinydrm_device *tdev)
>> -{
>> -	drm_atomic_helper_shutdown(tdev->drm);
>> -	drm_dev_unregister(tdev->drm);
>> -}
>> -
>> -static void devm_tinydrm_register_release(void *data)
>> -{
>> -	tinydrm_unregister(data);
>> -}
>> -
>> -/**
>> - * devm_tinydrm_register - Register tinydrm device
>> - * @tdev: tinydrm device
>> - *
>> - * This function registers the underlying DRM device and fbdev.
>> - * These resources will be automatically unregistered on driver detach (devres)
>> - * and the display pipeline will be disabled.
>> - *
>> - * Returns:
>> - * Zero on success, negative error code on failure.
>> - */
>> -int devm_tinydrm_register(struct tinydrm_device *tdev)
>> -{
>> -	struct device *dev = tdev->drm->dev;
>> -	int ret;
>> -
>> -	ret = tinydrm_register(tdev);
>> -	if (ret)
>> -		return ret;
>> -
>> -	ret = devm_add_action(dev, devm_tinydrm_register_release, tdev);
>> -	if (ret)
>> -		tinydrm_unregister(tdev);
>> -
>> -	return ret;
>> -}
>> -EXPORT_SYMBOL(devm_tinydrm_register);
>> -
>> -MODULE_LICENSE("GPL");
>> diff --git a/drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c b/drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c
>> index 2737b6fdadc8..d7b38dfb6438 100644
>> --- a/drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c
>> +++ b/drivers/gpu/drm/tinydrm/core/tinydrm-helpers.c
>> @@ -365,3 +365,5 @@ int tinydrm_spi_transfer(struct spi_device *spi, u32 speed_hz,
>>  EXPORT_SYMBOL(tinydrm_spi_transfer);
>>  
>>  #endif /* CONFIG_SPI */
>> +
>> +MODULE_LICENSE("GPL");
>> diff --git a/include/drm/tinydrm/tinydrm.h b/include/drm/tinydrm/tinydrm.h
>> deleted file mode 100644
>> index ee9b17759391..000000000000
>> --- a/include/drm/tinydrm/tinydrm.h
>> +++ /dev/null
>> @@ -1,42 +0,0 @@
>> -/*
>> - * Copyright (C) 2016 Noralf Trønnes
>> - *
>> - * This program is free software; you can redistribute it and/or modify
>> - * it under the terms of the GNU General Public License as published by
>> - * the Free Software Foundation; either version 2 of the License, or
>> - * (at your option) any later version.
>> - */
>> -
>> -#ifndef __LINUX_TINYDRM_H
>> -#define __LINUX_TINYDRM_H
>> -
>> -#include <drm/drm_simple_kms_helper.h>
>> -
>> -struct drm_driver;
>> -
>> -/**
>> - * struct tinydrm_device - tinydrm device
>> - */
>> -struct tinydrm_device {
>> -	/**
>> -	 * @drm: DRM device
>> -	 */
>> -	struct drm_device *drm;
>> -
>> -	/**
>> -	 * @pipe: Display pipe structure
>> -	 */
>> -	struct drm_simple_display_pipe pipe;
>> -};
>> -
>> -static inline struct tinydrm_device *
>> -pipe_to_tinydrm(struct drm_simple_display_pipe *pipe)
>> -{
>> -	return container_of(pipe, struct tinydrm_device, pipe);
>> -}
>> -
>> -int devm_tinydrm_init(struct device *parent, struct tinydrm_device *tdev,
>> -		      struct drm_driver *driver);
>> -int devm_tinydrm_register(struct tinydrm_device *tdev);
>> -
>> -#endif /* __LINUX_TINYDRM_H */
>> -- 
>> 2.20.1
>>
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel@lists.freedesktop.org
>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
> 
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH 01/11] drm: Add devm_drm_dev_init/register
  2019-01-21 12:21       ` Noralf Trønnes
@ 2019-01-22  9:32         ` Daniel Vetter
  2019-01-22 19:07           ` Noralf Trønnes
  0 siblings, 1 reply; 57+ messages in thread
From: Daniel Vetter @ 2019-01-22  9:32 UTC (permalink / raw)
  To: Noralf Trønnes; +Cc: dri-devel, david

On Mon, Jan 21, 2019 at 01:21:46PM +0100, Noralf Trønnes wrote:
> 
> 
> Den 21.01.2019 10.55, skrev Daniel Vetter:
> > On Mon, Jan 21, 2019 at 10:10:14AM +0100, Daniel Vetter wrote:
> >> On Sun, Jan 20, 2019 at 12:43:08PM +0100, Noralf Trønnes wrote:
> >>> This adds resource managed (devres) versions of drm_dev_init() and
> >>> drm_dev_register().
> >>>
> >>> Also added is devm_drm_dev_register_with_fbdev() which sets up generic
> >>> fbdev emulation as well.
> >>>
> >>> devm_drm_dev_register() isn't exported since there are no users.
> >>>
> >>> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
> >>
> 
> <snip>
> 
> >>> diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
> >>> index 381581b01d48..12129772be45 100644
> >>> --- a/drivers/gpu/drm/drm_drv.c
> >>> +++ b/drivers/gpu/drm/drm_drv.c
> >>> @@ -36,6 +36,7 @@
> >>>  
> >>>  #include <drm/drm_client.h>
> >>>  #include <drm/drm_drv.h>
> >>> +#include <drm/drm_fb_helper.h>
> >>>  #include <drm/drmP.h>
> >>>  
> >>>  #include "drm_crtc_internal.h"
> >>> @@ -871,6 +872,111 @@ void drm_dev_unregister(struct drm_device *dev)
> >>>  }
> >>>  EXPORT_SYMBOL(drm_dev_unregister);
> >>>  
> >>> +static void devm_drm_dev_init_release(void *data)
> >>> +{
> >>> +	drm_dev_put(data);
> > 
> > We need drm_dev_unplug() here, or this isn't safe.
> 
> This function is only used to cover the error path if probe fails before
> devm_drm_dev_register() is called. devm_drm_dev_register_release() is
> the one that calls unplug. There are comments about this in the functions.

I think I get a prize for being ignorant and blind :-/

> 
> > 
> >>> +}
> >>> +
> >>> +/**
> >>> + * devm_drm_dev_init - Resource managed drm_dev_init()
> >>> + * @parent: Parent device object
> >>> + * @dev: DRM device
> >>> + * @driver: DRM driver
> >>> + *
> >>> + * Managed drm_dev_init(). The DRM device initialized with this function is
> >>> + * automatically released on driver detach. You must supply a
> > 
> > I think a bit more clarity here would be good:
> > 
> > "... automatically released on driver unbind by callind drm_dev_unplug()."
> > 
> >>> + * &drm_driver.release callback to control the finalization explicitly.
> > 
> > I think a loud warning for these is in order:
> > 
> > "WARNING:
> > 
> > "In generally it is unsafe to use devm functions for drm structures
> > because the lifetimes of &drm_device and the underlying &device do not
> > match. This here works because it doesn't immediately free anything, but
> > only calls drm_dev_unplug(), which internally decrements the &drm_device
> > refcount through drm_dev_put().
> > 
> > "All other drm structures must still be explicitly released in the
> > &drm_driver.release callback."
> > 
> > While thinking about this I just realized that with this design we have no
> > good place to call drm_atomic_helper_shutdown(). Which we need to, or all
> > kinds of things will leak badly (connectors, fb, ...), but there's no
> > place to call it:
> > - unbind is too early, since we haven't yet called drm_dev_unplug, and the
> >   drm_dev_unregister in there must be called _before_ we start to shut
> >   down anything.
> > - drm_driver.release is way too late.
> > 
> > Ofc for a real hotunplug there's no point in shutting down the hw (it's
> > already gone), but for a driver unload/unbind it would be nice if this
> > happens automatically and in the right order.
> > 
> > So not sure what to do here really.
> 
> How about this change: (it breaks the rule of pulling helpers into the
> core, so maybe we should put the devm_ functions into the simple KMS
> helper instead?)

Yeah smells a bit much like midlayer ... What would work is having a pile
more devm_ helper functions, so that we onion-unwrap everything correctly,
and in the right order. So:

- devm_drm_dev_init (always does a drm_dev_put())

- devm_drm_poll_enable (shuts down the poll helper with a devm action)

- devm_drm_mode_config_reset (does an atomic_helper_shutdown() as it's cleanup action)

- devm_drm_dev_register (grabs an additional drm_dev_get() reference so it
  can call drm_dev_unplug() unconditionally).

We'd need to make sure some of the cleanup actions dtrt when the device is
gone, but I think we can achieve that by liberally sprinkling
drm_dev_enter/exit over them, e.g. the the cleanup action for
drm_mode_config_reset would be:

{
	if (drm_dev_enter())
		return;

	drm_atomic_helper_shutdown();

	drm_dev_exit();
}

This would be analog to your shutdown parameter below.

Essentially I think we can only do the drm_dev_unplug autocleanup in a
given driver from devm_drm_dev_register iff all the cleanup actions have
been devm-ified, and there's nothing left in it's unbind callback. Because
if anything is left in its unbind callback that's a bug, since
drm_dev_unregister() (called through drm_dev_unplug) is the very first (or
at least one of the very first, before we start cleanup up) functions that
need to be called.
-Daniel

> 
> diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
> index 12129772be45..7ed9550baff6 100644
> --- a/drivers/gpu/drm/drm_drv.c
> +++ b/drivers/gpu/drm/drm_drv.c
> @@ -34,7 +34,9 @@
>  #include <linux/slab.h>
>  #include <linux/srcu.h>
> 
> +#include <drm/drm_atomic_helper.h>
>  #include <drm/drm_client.h>
> +#include <drm/drm_crtc_helper.h>
>  #include <drm/drm_drv.h>
>  #include <drm/drm_fb_helper.h>
>  #include <drm/drmP.h>
> @@ -355,17 +357,7 @@ void drm_dev_exit(int idx)
>  }
>  EXPORT_SYMBOL(drm_dev_exit);
> 
> -/**
> - * drm_dev_unplug - unplug a DRM device
> - * @dev: DRM device
> - *
> - * This unplugs a hotpluggable DRM device, which makes it inaccessible to
> - * userspace operations. Entry-points can use drm_dev_enter() and
> - * drm_dev_exit() to protect device resources in a race free manner. This
> - * essentially unregisters the device like drm_dev_unregister(), but can be
> - * called while there are still open users of @dev.
> - */
> -void drm_dev_unplug(struct drm_device *dev)
> +static void __drm_dev_unplug(struct drm_device *dev, bool shutdown)
>  {
>         /*
>          * After synchronizing any critical read section is guaranteed
> to see
> @@ -378,11 +370,32 @@ void drm_dev_unplug(struct drm_device *dev)
> 
>         drm_dev_unregister(dev);
> 
> +       if (shutdown)
> +               drm_kms_helper_poll_fini(dev);
> +
>         mutex_lock(&drm_global_mutex);
> -       if (dev->open_count == 0)
> +       if (dev->open_count == 0) {
> +               if (shutdown)
> +                       drm_atomic_helper_shutdown(dev);
>                 drm_dev_put(dev);
> +       }
>         mutex_unlock(&drm_global_mutex);
>  }
> +
> +/**
> + * drm_dev_unplug - unplug a DRM device
> + * @dev: DRM device
> + *
> + * This unplugs a hotpluggable DRM device, which makes it inaccessible to
> + * userspace operations. Entry-points can use drm_dev_enter() and
> + * drm_dev_exit() to protect device resources in a race free manner. This
> + * essentially unregisters the device like drm_dev_unregister(), but can be
> + * called while there are still open users of @dev.
> + */
> +void drm_dev_unplug(struct drm_device *dev)
> +{
> +       __drm_dev_unplug(dev, false);
> +}
>  EXPORT_SYMBOL(drm_dev_unplug);
> 
>  /*
> @@ -920,7 +933,7 @@ EXPORT_SYMBOL(devm_drm_dev_init);
> 
>  static void devm_drm_dev_register_release(void *data)
>  {
> -       drm_dev_unplug(data);
> +       __drm_dev_unplug(data, true);
>  }
> 
>  static int devm_drm_dev_register(struct drm_device *dev)
> 
> 
> I realised that we should take a ref on the parent device so it can be
> accessed by the DRM_DEV_ functions after unplug.
> 
> 
> Noralf.
> 
> 
> > 
> >>> + *
> >>> + * Note: This function must be used together with
> >>> + * devm_drm_dev_register_with_fbdev().
> >>> + *
> >>> + * RETURNS:
> >>> + * 0 on success, or error code on failure.
> >>> + */
> >>> +int devm_drm_dev_init(struct device *parent,
> >>> +		      struct drm_device *dev,
> >>> +		      struct drm_driver *driver)
> >>> +{
> >>> +	int ret;
> >>> +
> >>> +	if (WARN_ON(!parent || !driver->release))
> >>> +		return -EINVAL;
> >>> +
> >>> +	ret = drm_dev_init(dev, driver, parent);
> >>> +	if (ret)
> >>> +		return ret;
> >>> +
> >>> +	/*
> >>> +	 * This is a temporary release action that is used if probing fails
> >>> +	 * before devm_drm_dev_register() is called.
> >>> +	 */
> >>> +	ret = devm_add_action(parent, devm_drm_dev_init_release, dev);
> >>> +	if (ret)
> >>> +		devm_drm_dev_init_release(dev);
> >>> +
> >>> +	return ret;
> >>> +}
> >>> +EXPORT_SYMBOL(devm_drm_dev_init);
> >>> +
> >>> +static void devm_drm_dev_register_release(void *data)
> >>> +{
> >>> +	drm_dev_unplug(data);
> >>> +}
> >>> +
> >>> +static int devm_drm_dev_register(struct drm_device *dev)
> >>> +{
> >>> +	int ret;
> >>> +
> >>> +	ret = drm_dev_register(dev, 0);
> >>> +	if (ret)
> >>> +		return ret;
> >>> +
> >>> +	/*
> >>> +	 * This has now served it's purpose, remove it to not mess up ref
> >>> +	 * counting.
> >>> +	 */
> >>> +	devm_remove_action(dev->dev, devm_drm_dev_init_release, dev);
> >>> +
> >>> +	ret = devm_add_action(dev->dev, devm_drm_dev_register_release, dev);
> >>> +	if (ret)
> >>> +		devm_drm_dev_register_release(dev);
> >>> +
> >>> +	return ret;
> >>> +}
> >>> +
> >>> +/**
> >>> + * devm_drm_dev_register_with_fbdev - Resource managed drm_dev_register()
> >>> + *                                    including generic fbdev emulation
> >>> + * @dev: DRM device to register
> >>> + * @fbdev_bpp: Preferred bits per pixel for fbdev (optional)
> >>> + *
> >>> + * Managed drm_dev_register() that also calls drm_fbdev_generic_setup().
> >>> + * The DRM device registered with this function is automatically unregistered on
> >>> + * driver detach using drm_dev_unplug().
> >>> + *
> >>> + * Note: This function must be used together with devm_drm_dev_init().
> >>> + *
> >>> + * For testing driver detach can be triggered manually by writing to the driver
> >>> + * 'unbind' file.
> >>> + *
> >>> + * RETURNS:
> >>> + * 0 on success, negative error code on failure.
> >>> + */
> >>> +int devm_drm_dev_register_with_fbdev(struct drm_device *dev,
> >>> +				     unsigned int fbdev_bpp)
> >>> +{
> >>> +	int ret;
> >>> +
> >>> +	ret = devm_drm_dev_register(dev);
> >>> +	if (ret)
> >>> +		return ret;
> >>> +
> >>> +	drm_fbdev_generic_setup(dev, fbdev_bpp);
> >>> +
> >>> +	return 0;
> >>> +}
> >>> +EXPORT_SYMBOL(devm_drm_dev_register_with_fbdev);
> >>> +
> >>>  /**
> >>>   * drm_dev_set_unique - Set the unique name of a DRM device
> >>>   * @dev: device of which to set the unique name
> >>> diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h
> >>> index 35af23f5fa0d..c3f0477f2e7f 100644
> >>> --- a/include/drm/drm_drv.h
> >>> +++ b/include/drm/drm_drv.h
> >>> @@ -628,6 +628,12 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver,
> >>>  int drm_dev_register(struct drm_device *dev, unsigned long flags);
> >>>  void drm_dev_unregister(struct drm_device *dev);
> >>>  
> >>> +int devm_drm_dev_init(struct device *parent,
> >>> +		      struct drm_device *dev,
> >>> +		      struct drm_driver *driver);
> >>> +int devm_drm_dev_register_with_fbdev(struct drm_device *dev,
> >>> +				     unsigned int fbdev_bpp);
> >>> +
> >>>  void drm_dev_get(struct drm_device *dev);
> >>>  void drm_dev_put(struct drm_device *dev);
> >>>  void drm_put_dev(struct drm_device *dev);
> >>> -- 
> >>> 2.20.1
> >>>
> >>> _______________________________________________
> >>> dri-devel mailing list
> >>> dri-devel@lists.freedesktop.org
> >>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
> >>
> >> -- 
> >> Daniel Vetter
> >> Software Engineer, Intel Corporation
> >> http://blog.ffwll.ch
> > 

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH 01/11] drm: Add devm_drm_dev_init/register
  2019-01-20 11:43 ` [PATCH 01/11] drm: Add devm_drm_dev_init/register Noralf Trønnes
  2019-01-21  6:11   ` Sam Ravnborg
  2019-01-21  9:10   ` Daniel Vetter
@ 2019-01-22  9:35   ` Daniel Vetter
  2 siblings, 0 replies; 57+ messages in thread
From: Daniel Vetter @ 2019-01-22  9:35 UTC (permalink / raw)
  To: Noralf Trønnes; +Cc: david, dri-devel

On Sun, Jan 20, 2019 at 12:43:08PM +0100, Noralf Trønnes wrote:
> This adds resource managed (devres) versions of drm_dev_init() and
> drm_dev_register().
> 
> Also added is devm_drm_dev_register_with_fbdev() which sets up generic
> fbdev emulation as well.
> 
> devm_drm_dev_register() isn't exported since there are no users.
> 
> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
> ---
>  Documentation/driver-model/devres.txt |   4 +
>  drivers/gpu/drm/drm_drv.c             | 106 ++++++++++++++++++++++++++
>  include/drm/drm_drv.h                 |   6 ++
>  3 files changed, 116 insertions(+)
> 
> diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt
> index b277cafce71e..6eebc28d4c21 100644
> --- a/Documentation/driver-model/devres.txt
> +++ b/Documentation/driver-model/devres.txt
> @@ -254,6 +254,10 @@ DMA
>    dmam_pool_create()
>    dmam_pool_destroy()
>  
> +DRM
> +  devm_drm_dev_init()
> +  devm_drm_dev_register_with_fbdev()
> +
>  GPIO
>    devm_gpiod_get()
>    devm_gpiod_get_index()
> diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
> index 381581b01d48..12129772be45 100644
> --- a/drivers/gpu/drm/drm_drv.c
> +++ b/drivers/gpu/drm/drm_drv.c
> @@ -36,6 +36,7 @@
>  
>  #include <drm/drm_client.h>
>  #include <drm/drm_drv.h>
> +#include <drm/drm_fb_helper.h>
>  #include <drm/drmP.h>
>  
>  #include "drm_crtc_internal.h"
> @@ -871,6 +872,111 @@ void drm_dev_unregister(struct drm_device *dev)
>  }
>  EXPORT_SYMBOL(drm_dev_unregister);
>  
> +static void devm_drm_dev_init_release(void *data)
> +{
> +	drm_dev_put(data);
> +}
> +
> +/**
> + * devm_drm_dev_init - Resource managed drm_dev_init()
> + * @parent: Parent device object
> + * @dev: DRM device
> + * @driver: DRM driver
> + *
> + * Managed drm_dev_init(). The DRM device initialized with this function is
> + * automatically released on driver detach. You must supply a
> + * &drm_driver.release callback to control the finalization explicitly.
> + *
> + * Note: This function must be used together with
> + * devm_drm_dev_register_with_fbdev().
> + *
> + * RETURNS:
> + * 0 on success, or error code on failure.
> + */
> +int devm_drm_dev_init(struct device *parent,
> +		      struct drm_device *dev,
> +		      struct drm_driver *driver)
> +{
> +	int ret;
> +
> +	if (WARN_ON(!parent || !driver->release))
> +		return -EINVAL;
> +
> +	ret = drm_dev_init(dev, driver, parent);
> +	if (ret)
> +		return ret;
> +
> +	/*
> +	 * This is a temporary release action that is used if probing fails
> +	 * before devm_drm_dev_register() is called.
> +	 */
> +	ret = devm_add_action(parent, devm_drm_dev_init_release, dev);
> +	if (ret)
> +		devm_drm_dev_init_release(dev);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL(devm_drm_dev_init);
> +
> +static void devm_drm_dev_register_release(void *data)
> +{
> +	drm_dev_unplug(data);
> +}
> +
> +static int devm_drm_dev_register(struct drm_device *dev)
> +{
> +	int ret;
> +
> +	ret = drm_dev_register(dev, 0);
> +	if (ret)
> +		return ret;
> +
> +	/*
> +	 * This has now served it's purpose, remove it to not mess up ref
> +	 * counting.
> +	 */
> +	devm_remove_action(dev->dev, devm_drm_dev_init_release, dev);
> +
> +	ret = devm_add_action(dev->dev, devm_drm_dev_register_release, dev);

If this fails I think the cleanup would go wrong. I think simpler if you
never remove the devm action from dev_init, and just grab an additional
drm_dev_get() reference here for drm_dev_unplug. We also need that for
correct onion cleanup in, so that for a normal unbind/driver unload we can
still do:

1. drm_dev_unregister() through drm_dev_unplug()

2. drm_atomic_helper_shutdown() and similar things (needs the drm_device
to still be around)

3. drm_dev_put()

I think that'll give us the cleaner onion unwrapping in the
unload/hotunplug/error case cleanup cases.

Also, this allows drivers to start using devm_drm_dev_init without having
to use devm_drm_dev_register(), which they have to do if they still have
non-devm-ified things in step 2 above in there unbind callback.
-Daniel

> +	if (ret)
> +		devm_drm_dev_register_release(dev);
> +
> +	return ret;
> +}
> +
> +/**
> + * devm_drm_dev_register_with_fbdev - Resource managed drm_dev_register()
> + *                                    including generic fbdev emulation
> + * @dev: DRM device to register
> + * @fbdev_bpp: Preferred bits per pixel for fbdev (optional)
> + *
> + * Managed drm_dev_register() that also calls drm_fbdev_generic_setup().
> + * The DRM device registered with this function is automatically unregistered on
> + * driver detach using drm_dev_unplug().
> + *
> + * Note: This function must be used together with devm_drm_dev_init().
> + *
> + * For testing driver detach can be triggered manually by writing to the driver
> + * 'unbind' file.
> + *
> + * RETURNS:
> + * 0 on success, negative error code on failure.
> + */
> +int devm_drm_dev_register_with_fbdev(struct drm_device *dev,
> +				     unsigned int fbdev_bpp)
> +{
> +	int ret;
> +
> +	ret = devm_drm_dev_register(dev);
> +	if (ret)
> +		return ret;
> +
> +	drm_fbdev_generic_setup(dev, fbdev_bpp);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(devm_drm_dev_register_with_fbdev);
> +
>  /**
>   * drm_dev_set_unique - Set the unique name of a DRM device
>   * @dev: device of which to set the unique name
> diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h
> index 35af23f5fa0d..c3f0477f2e7f 100644
> --- a/include/drm/drm_drv.h
> +++ b/include/drm/drm_drv.h
> @@ -628,6 +628,12 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver,
>  int drm_dev_register(struct drm_device *dev, unsigned long flags);
>  void drm_dev_unregister(struct drm_device *dev);
>  
> +int devm_drm_dev_init(struct device *parent,
> +		      struct drm_device *dev,
> +		      struct drm_driver *driver);
> +int devm_drm_dev_register_with_fbdev(struct drm_device *dev,
> +				     unsigned int fbdev_bpp);
> +
>  void drm_dev_get(struct drm_device *dev);
>  void drm_dev_put(struct drm_device *dev);
>  void drm_put_dev(struct drm_device *dev);
> -- 
> 2.20.1
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH 01/11] drm: Add devm_drm_dev_init/register
  2019-01-22  9:32         ` Daniel Vetter
@ 2019-01-22 19:07           ` Noralf Trønnes
  2019-01-22 19:30             ` Daniel Vetter
  0 siblings, 1 reply; 57+ messages in thread
From: Noralf Trønnes @ 2019-01-22 19:07 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: david, dri-devel



Den 22.01.2019 10.32, skrev Daniel Vetter:
> On Mon, Jan 21, 2019 at 01:21:46PM +0100, Noralf Trønnes wrote:
>>
>>
>> Den 21.01.2019 10.55, skrev Daniel Vetter:
>>> On Mon, Jan 21, 2019 at 10:10:14AM +0100, Daniel Vetter wrote:
>>>> On Sun, Jan 20, 2019 at 12:43:08PM +0100, Noralf Trønnes wrote:
>>>>> This adds resource managed (devres) versions of drm_dev_init() and
>>>>> drm_dev_register().
>>>>>
>>>>> Also added is devm_drm_dev_register_with_fbdev() which sets up generic
>>>>> fbdev emulation as well.
>>>>>
>>>>> devm_drm_dev_register() isn't exported since there are no users.
>>>>>
>>>>> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
>>>>
>>
>> <snip>
>>
>>>>> diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
>>>>> index 381581b01d48..12129772be45 100644
>>>>> --- a/drivers/gpu/drm/drm_drv.c
>>>>> +++ b/drivers/gpu/drm/drm_drv.c
>>>>> @@ -36,6 +36,7 @@
>>>>>  
>>>>>  #include <drm/drm_client.h>
>>>>>  #include <drm/drm_drv.h>
>>>>> +#include <drm/drm_fb_helper.h>
>>>>>  #include <drm/drmP.h>
>>>>>  
>>>>>  #include "drm_crtc_internal.h"
>>>>> @@ -871,6 +872,111 @@ void drm_dev_unregister(struct drm_device *dev)
>>>>>  }
>>>>>  EXPORT_SYMBOL(drm_dev_unregister);
>>>>>  
>>>>> +static void devm_drm_dev_init_release(void *data)
>>>>> +{
>>>>> +	drm_dev_put(data);
>>>
>>> We need drm_dev_unplug() here, or this isn't safe.
>>
>> This function is only used to cover the error path if probe fails before
>> devm_drm_dev_register() is called. devm_drm_dev_register_release() is
>> the one that calls unplug. There are comments about this in the functions.
> 
> I think I get a prize for being ignorant and blind :-/
> 
>>
>>>
>>>>> +}
>>>>> +
>>>>> +/**
>>>>> + * devm_drm_dev_init - Resource managed drm_dev_init()
>>>>> + * @parent: Parent device object
>>>>> + * @dev: DRM device
>>>>> + * @driver: DRM driver
>>>>> + *
>>>>> + * Managed drm_dev_init(). The DRM device initialized with this function is
>>>>> + * automatically released on driver detach. You must supply a
>>>
>>> I think a bit more clarity here would be good:
>>>
>>> "... automatically released on driver unbind by callind drm_dev_unplug()."
>>>
>>>>> + * &drm_driver.release callback to control the finalization explicitly.
>>>
>>> I think a loud warning for these is in order:
>>>
>>> "WARNING:
>>>
>>> "In generally it is unsafe to use devm functions for drm structures
>>> because the lifetimes of &drm_device and the underlying &device do not
>>> match. This here works because it doesn't immediately free anything, but
>>> only calls drm_dev_unplug(), which internally decrements the &drm_device
>>> refcount through drm_dev_put().
>>>
>>> "All other drm structures must still be explicitly released in the
>>> &drm_driver.release callback."
>>>
>>> While thinking about this I just realized that with this design we have no
>>> good place to call drm_atomic_helper_shutdown(). Which we need to, or all
>>> kinds of things will leak badly (connectors, fb, ...), but there's no
>>> place to call it:
>>> - unbind is too early, since we haven't yet called drm_dev_unplug, and the
>>>   drm_dev_unregister in there must be called _before_ we start to shut
>>>   down anything.
>>> - drm_driver.release is way too late.
>>>
>>> Ofc for a real hotunplug there's no point in shutting down the hw (it's
>>> already gone), but for a driver unload/unbind it would be nice if this
>>> happens automatically and in the right order.
>>>
>>> So not sure what to do here really.
>>
>> How about this change: (it breaks the rule of pulling helpers into the
>> core, so maybe we should put the devm_ functions into the simple KMS
>> helper instead?)
> 
> Yeah smells a bit much like midlayer ... What would work is having a pile
> more devm_ helper functions, so that we onion-unwrap everything correctly,
> and in the right order. So:
> 
> - devm_drm_dev_init (always does a drm_dev_put())
> 
> - devm_drm_poll_enable (shuts down the poll helper with a devm action)
> 
> - devm_drm_mode_config_reset (does an atomic_helper_shutdown() as it's cleanup action)
> 
> - devm_drm_dev_register (grabs an additional drm_dev_get() reference so it
>   can call drm_dev_unplug() unconditionally).
> 

Beautiful! I really like this, it's very flexible.

Where should devm_drm_mode_config_reset() live? It will pull in the
atomic helper...

> We'd need to make sure some of the cleanup actions dtrt when the device is
> gone, but I think we can achieve that by liberally sprinkling
> drm_dev_enter/exit over them, e.g. the the cleanup action for
> drm_mode_config_reset would be:
> 
> {
> 	if (drm_dev_enter())
> 		return;
> 
> 	drm_atomic_helper_shutdown();
> 
> 	drm_dev_exit();
> }
> 

drm_dev_enter() can only be used to check whether the drm_device is
registered or not, it doesn't say anything about the state of the parent
device.

All we know is that the device is being unbound from the driver, we
don't know if it's the device that's being removed or if it's the driver
that's unregistered.

I have looked at the various call chains:

driver_unregister ->
    bus_remove_driver ->
        driver_detach ->
            device_release_driver_internal

device_unregister ->
    device_del ->
        bus_remove_device ->
            device_release_driver ->
                device_release_driver_internal

sysfs: unbind_store ->
    device_release_driver ->
        device_release_driver_internal

The only way I've found to differentiate between these in a cleanup
action is that device_del() uses the bus notifier to signal
BUS_NOTIFY_DEL_DEVICE before calling bus_remove_device(). Such a
notifier could be used to set a drm_device->parent_removed flag.

Why is it necessary to call drm_atomic_helper_shutdown() here? Doesn't
everything get disabled when userspace closes? It does in my tinydrm
world :-)

Noralf.


> This would be analog to your shutdown parameter below.
> 
> Essentially I think we can only do the drm_dev_unplug autocleanup in a
> given driver from devm_drm_dev_register iff all the cleanup actions have
> been devm-ified, and there's nothing left in it's unbind callback. Because
> if anything is left in its unbind callback that's a bug, since
> drm_dev_unregister() (called through drm_dev_unplug) is the very first (or
> at least one of the very first, before we start cleanup up) functions that
> need to be called.
> -Daniel
> 
>>
>> diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
>> index 12129772be45..7ed9550baff6 100644
>> --- a/drivers/gpu/drm/drm_drv.c
>> +++ b/drivers/gpu/drm/drm_drv.c
>> @@ -34,7 +34,9 @@
>>  #include <linux/slab.h>
>>  #include <linux/srcu.h>
>>
>> +#include <drm/drm_atomic_helper.h>
>>  #include <drm/drm_client.h>
>> +#include <drm/drm_crtc_helper.h>
>>  #include <drm/drm_drv.h>
>>  #include <drm/drm_fb_helper.h>
>>  #include <drm/drmP.h>
>> @@ -355,17 +357,7 @@ void drm_dev_exit(int idx)
>>  }
>>  EXPORT_SYMBOL(drm_dev_exit);
>>
>> -/**
>> - * drm_dev_unplug - unplug a DRM device
>> - * @dev: DRM device
>> - *
>> - * This unplugs a hotpluggable DRM device, which makes it inaccessible to
>> - * userspace operations. Entry-points can use drm_dev_enter() and
>> - * drm_dev_exit() to protect device resources in a race free manner. This
>> - * essentially unregisters the device like drm_dev_unregister(), but can be
>> - * called while there are still open users of @dev.
>> - */
>> -void drm_dev_unplug(struct drm_device *dev)
>> +static void __drm_dev_unplug(struct drm_device *dev, bool shutdown)
>>  {
>>         /*
>>          * After synchronizing any critical read section is guaranteed
>> to see
>> @@ -378,11 +370,32 @@ void drm_dev_unplug(struct drm_device *dev)
>>
>>         drm_dev_unregister(dev);
>>
>> +       if (shutdown)
>> +               drm_kms_helper_poll_fini(dev);
>> +
>>         mutex_lock(&drm_global_mutex);
>> -       if (dev->open_count == 0)
>> +       if (dev->open_count == 0) {
>> +               if (shutdown)
>> +                       drm_atomic_helper_shutdown(dev);
>>                 drm_dev_put(dev);
>> +       }
>>         mutex_unlock(&drm_global_mutex);
>>  }
>> +
>> +/**
>> + * drm_dev_unplug - unplug a DRM device
>> + * @dev: DRM device
>> + *
>> + * This unplugs a hotpluggable DRM device, which makes it inaccessible to
>> + * userspace operations. Entry-points can use drm_dev_enter() and
>> + * drm_dev_exit() to protect device resources in a race free manner. This
>> + * essentially unregisters the device like drm_dev_unregister(), but can be
>> + * called while there are still open users of @dev.
>> + */
>> +void drm_dev_unplug(struct drm_device *dev)
>> +{
>> +       __drm_dev_unplug(dev, false);
>> +}
>>  EXPORT_SYMBOL(drm_dev_unplug);
>>
>>  /*
>> @@ -920,7 +933,7 @@ EXPORT_SYMBOL(devm_drm_dev_init);
>>
>>  static void devm_drm_dev_register_release(void *data)
>>  {
>> -       drm_dev_unplug(data);
>> +       __drm_dev_unplug(data, true);
>>  }
>>
>>  static int devm_drm_dev_register(struct drm_device *dev)
>>
>>
>> I realised that we should take a ref on the parent device so it can be
>> accessed by the DRM_DEV_ functions after unplug.
>>
>>
>> Noralf.
>>
>>
>>>
>>>>> + *
>>>>> + * Note: This function must be used together with
>>>>> + * devm_drm_dev_register_with_fbdev().
>>>>> + *
>>>>> + * RETURNS:
>>>>> + * 0 on success, or error code on failure.
>>>>> + */
>>>>> +int devm_drm_dev_init(struct device *parent,
>>>>> +		      struct drm_device *dev,
>>>>> +		      struct drm_driver *driver)
>>>>> +{
>>>>> +	int ret;
>>>>> +
>>>>> +	if (WARN_ON(!parent || !driver->release))
>>>>> +		return -EINVAL;
>>>>> +
>>>>> +	ret = drm_dev_init(dev, driver, parent);
>>>>> +	if (ret)
>>>>> +		return ret;
>>>>> +
>>>>> +	/*
>>>>> +	 * This is a temporary release action that is used if probing fails
>>>>> +	 * before devm_drm_dev_register() is called.
>>>>> +	 */
>>>>> +	ret = devm_add_action(parent, devm_drm_dev_init_release, dev);
>>>>> +	if (ret)
>>>>> +		devm_drm_dev_init_release(dev);
>>>>> +
>>>>> +	return ret;
>>>>> +}
>>>>> +EXPORT_SYMBOL(devm_drm_dev_init);
>>>>> +
>>>>> +static void devm_drm_dev_register_release(void *data)
>>>>> +{
>>>>> +	drm_dev_unplug(data);
>>>>> +}
>>>>> +
>>>>> +static int devm_drm_dev_register(struct drm_device *dev)
>>>>> +{
>>>>> +	int ret;
>>>>> +
>>>>> +	ret = drm_dev_register(dev, 0);
>>>>> +	if (ret)
>>>>> +		return ret;
>>>>> +
>>>>> +	/*
>>>>> +	 * This has now served it's purpose, remove it to not mess up ref
>>>>> +	 * counting.
>>>>> +	 */
>>>>> +	devm_remove_action(dev->dev, devm_drm_dev_init_release, dev);
>>>>> +
>>>>> +	ret = devm_add_action(dev->dev, devm_drm_dev_register_release, dev);
>>>>> +	if (ret)
>>>>> +		devm_drm_dev_register_release(dev);
>>>>> +
>>>>> +	return ret;
>>>>> +}
>>>>> +
>>>>> +/**
>>>>> + * devm_drm_dev_register_with_fbdev - Resource managed drm_dev_register()
>>>>> + *                                    including generic fbdev emulation
>>>>> + * @dev: DRM device to register
>>>>> + * @fbdev_bpp: Preferred bits per pixel for fbdev (optional)
>>>>> + *
>>>>> + * Managed drm_dev_register() that also calls drm_fbdev_generic_setup().
>>>>> + * The DRM device registered with this function is automatically unregistered on
>>>>> + * driver detach using drm_dev_unplug().
>>>>> + *
>>>>> + * Note: This function must be used together with devm_drm_dev_init().
>>>>> + *
>>>>> + * For testing driver detach can be triggered manually by writing to the driver
>>>>> + * 'unbind' file.
>>>>> + *
>>>>> + * RETURNS:
>>>>> + * 0 on success, negative error code on failure.
>>>>> + */
>>>>> +int devm_drm_dev_register_with_fbdev(struct drm_device *dev,
>>>>> +				     unsigned int fbdev_bpp)
>>>>> +{
>>>>> +	int ret;
>>>>> +
>>>>> +	ret = devm_drm_dev_register(dev);
>>>>> +	if (ret)
>>>>> +		return ret;
>>>>> +
>>>>> +	drm_fbdev_generic_setup(dev, fbdev_bpp);
>>>>> +
>>>>> +	return 0;
>>>>> +}
>>>>> +EXPORT_SYMBOL(devm_drm_dev_register_with_fbdev);
>>>>> +
>>>>>  /**
>>>>>   * drm_dev_set_unique - Set the unique name of a DRM device
>>>>>   * @dev: device of which to set the unique name
>>>>> diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h
>>>>> index 35af23f5fa0d..c3f0477f2e7f 100644
>>>>> --- a/include/drm/drm_drv.h
>>>>> +++ b/include/drm/drm_drv.h
>>>>> @@ -628,6 +628,12 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver,
>>>>>  int drm_dev_register(struct drm_device *dev, unsigned long flags);
>>>>>  void drm_dev_unregister(struct drm_device *dev);
>>>>>  
>>>>> +int devm_drm_dev_init(struct device *parent,
>>>>> +		      struct drm_device *dev,
>>>>> +		      struct drm_driver *driver);
>>>>> +int devm_drm_dev_register_with_fbdev(struct drm_device *dev,
>>>>> +				     unsigned int fbdev_bpp);
>>>>> +
>>>>>  void drm_dev_get(struct drm_device *dev);
>>>>>  void drm_dev_put(struct drm_device *dev);
>>>>>  void drm_put_dev(struct drm_device *dev);
>>>>> -- 
>>>>> 2.20.1
>>>>>
>>>>> _______________________________________________
>>>>> dri-devel mailing list
>>>>> dri-devel@lists.freedesktop.org
>>>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>>>
>>>> -- 
>>>> Daniel Vetter
>>>> Software Engineer, Intel Corporation
>>>> http://blog.ffwll.ch
>>>
> 
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH 01/11] drm: Add devm_drm_dev_init/register
  2019-01-22 19:07           ` Noralf Trønnes
@ 2019-01-22 19:30             ` Daniel Vetter
  2019-01-23 10:54               ` Noralf Trønnes
  0 siblings, 1 reply; 57+ messages in thread
From: Daniel Vetter @ 2019-01-22 19:30 UTC (permalink / raw)
  To: Noralf Trønnes; +Cc: David Lechner, dri-devel

On Tue, Jan 22, 2019 at 8:07 PM Noralf Trønnes <noralf@tronnes.org> wrote:
>
>
>
> Den 22.01.2019 10.32, skrev Daniel Vetter:
> > On Mon, Jan 21, 2019 at 01:21:46PM +0100, Noralf Trønnes wrote:
> >>
> >>
> >> Den 21.01.2019 10.55, skrev Daniel Vetter:
> >>> On Mon, Jan 21, 2019 at 10:10:14AM +0100, Daniel Vetter wrote:
> >>>> On Sun, Jan 20, 2019 at 12:43:08PM +0100, Noralf Trønnes wrote:
> >>>>> This adds resource managed (devres) versions of drm_dev_init() and
> >>>>> drm_dev_register().
> >>>>>
> >>>>> Also added is devm_drm_dev_register_with_fbdev() which sets up generic
> >>>>> fbdev emulation as well.
> >>>>>
> >>>>> devm_drm_dev_register() isn't exported since there are no users.
> >>>>>
> >>>>> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
> >>>>
> >>
> >> <snip>
> >>
> >>>>> diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
> >>>>> index 381581b01d48..12129772be45 100644
> >>>>> --- a/drivers/gpu/drm/drm_drv.c
> >>>>> +++ b/drivers/gpu/drm/drm_drv.c
> >>>>> @@ -36,6 +36,7 @@
> >>>>>
> >>>>>  #include <drm/drm_client.h>
> >>>>>  #include <drm/drm_drv.h>
> >>>>> +#include <drm/drm_fb_helper.h>
> >>>>>  #include <drm/drmP.h>
> >>>>>
> >>>>>  #include "drm_crtc_internal.h"
> >>>>> @@ -871,6 +872,111 @@ void drm_dev_unregister(struct drm_device *dev)
> >>>>>  }
> >>>>>  EXPORT_SYMBOL(drm_dev_unregister);
> >>>>>
> >>>>> +static void devm_drm_dev_init_release(void *data)
> >>>>> +{
> >>>>> + drm_dev_put(data);
> >>>
> >>> We need drm_dev_unplug() here, or this isn't safe.
> >>
> >> This function is only used to cover the error path if probe fails before
> >> devm_drm_dev_register() is called. devm_drm_dev_register_release() is
> >> the one that calls unplug. There are comments about this in the functions.
> >
> > I think I get a prize for being ignorant and blind :-/
> >
> >>
> >>>
> >>>>> +}
> >>>>> +
> >>>>> +/**
> >>>>> + * devm_drm_dev_init - Resource managed drm_dev_init()
> >>>>> + * @parent: Parent device object
> >>>>> + * @dev: DRM device
> >>>>> + * @driver: DRM driver
> >>>>> + *
> >>>>> + * Managed drm_dev_init(). The DRM device initialized with this function is
> >>>>> + * automatically released on driver detach. You must supply a
> >>>
> >>> I think a bit more clarity here would be good:
> >>>
> >>> "... automatically released on driver unbind by callind drm_dev_unplug()."
> >>>
> >>>>> + * &drm_driver.release callback to control the finalization explicitly.
> >>>
> >>> I think a loud warning for these is in order:
> >>>
> >>> "WARNING:
> >>>
> >>> "In generally it is unsafe to use devm functions for drm structures
> >>> because the lifetimes of &drm_device and the underlying &device do not
> >>> match. This here works because it doesn't immediately free anything, but
> >>> only calls drm_dev_unplug(), which internally decrements the &drm_device
> >>> refcount through drm_dev_put().
> >>>
> >>> "All other drm structures must still be explicitly released in the
> >>> &drm_driver.release callback."
> >>>
> >>> While thinking about this I just realized that with this design we have no
> >>> good place to call drm_atomic_helper_shutdown(). Which we need to, or all
> >>> kinds of things will leak badly (connectors, fb, ...), but there's no
> >>> place to call it:
> >>> - unbind is too early, since we haven't yet called drm_dev_unplug, and the
> >>>   drm_dev_unregister in there must be called _before_ we start to shut
> >>>   down anything.
> >>> - drm_driver.release is way too late.
> >>>
> >>> Ofc for a real hotunplug there's no point in shutting down the hw (it's
> >>> already gone), but for a driver unload/unbind it would be nice if this
> >>> happens automatically and in the right order.
> >>>
> >>> So not sure what to do here really.
> >>
> >> How about this change: (it breaks the rule of pulling helpers into the
> >> core, so maybe we should put the devm_ functions into the simple KMS
> >> helper instead?)
> >
> > Yeah smells a bit much like midlayer ... What would work is having a pile
> > more devm_ helper functions, so that we onion-unwrap everything correctly,
> > and in the right order. So:
> >
> > - devm_drm_dev_init (always does a drm_dev_put())
> >
> > - devm_drm_poll_enable (shuts down the poll helper with a devm action)
> >
> > - devm_drm_mode_config_reset (does an atomic_helper_shutdown() as it's cleanup action)
> >
> > - devm_drm_dev_register (grabs an additional drm_dev_get() reference so it
> >   can call drm_dev_unplug() unconditionally).
> >
>
> Beautiful! I really like this, it's very flexible.
>
> Where should devm_drm_mode_config_reset() live? It will pull in the
> atomic helper...

I think a new drm_devm.c helper would be nice for all this stuff.
Especially since you can't freely mix devm-based setup/cleanup with
normal cleanup I think it'd be good to have it all together in one
place. And perhaps even a code example in the DOC: overview.

> > We'd need to make sure some of the cleanup actions dtrt when the device is
> > gone, but I think we can achieve that by liberally sprinkling
> > drm_dev_enter/exit over them, e.g. the the cleanup action for
> > drm_mode_config_reset would be:
> >
> > {
> >       if (drm_dev_enter())
> >               return;
> >
> >       drm_atomic_helper_shutdown();
> >
> >       drm_dev_exit();
> > }
> >
>
> drm_dev_enter() can only be used to check whether the drm_device is
> registered or not, it doesn't say anything about the state of the parent
> device.
>
> All we know is that the device is being unbound from the driver, we
> don't know if it's the device that's being removed or if it's the driver
> that's unregistered.

You're right, both paths will have called drm_dev_unplug by then.
Silly me. I really liked my idea :-)

> I have looked at the various call chains:
>
> driver_unregister ->
>     bus_remove_driver ->
>         driver_detach ->
>             device_release_driver_internal
>
> device_unregister ->
>     device_del ->
>         bus_remove_device ->
>             device_release_driver ->
>                 device_release_driver_internal
>
> sysfs: unbind_store ->
>     device_release_driver ->
>         device_release_driver_internal
>
> The only way I've found to differentiate between these in a cleanup
> action is that device_del() uses the bus notifier to signal
> BUS_NOTIFY_DEL_DEVICE before calling bus_remove_device(). Such a
> notifier could be used to set a drm_device->parent_removed flag.

Hm, this might upset Greg KH's code taste ... maybe there's a better
way to do this, but best to prototype a patch with this, send it to
him and ask how to :-)

> Why is it necessary to call drm_atomic_helper_shutdown() here? Doesn't
> everything get disabled when userspace closes? It does in my tinydrm
> world :-)

Iirc fbdev/fbcon can result in leaks ... at least we've had patches
where drivers leaked drm_connector and drm_framebuffer objects, and
they've been fixed by calling drm_atomic_helper_shutdown() in the
unload path. Maybe this is cargo-culting, but it goes way back to
pre-atomic, where drivers called drm_helper_force_disable_all().

If you try to move the fbcon to your tinydrm drivers (con2fb is
apparently the cmdline tool you need, never tried it, I only switch
the kernel's console between fbcon and dummycon and back, not what
fbcon drivers itself), then I think you should be able to reproduce.
And maybe you have a better idea how to deal with this all.

Note also that there's been proposals floating around to only close an
drm_framebuffer, not also remove it (like the current RMFB ioctl
does), with that closing userspace would not necessarily lead to a
full cleanup.

Another thing (which doesn't apply to drm_simple_display_pipe drivers)
is if you have the display on, but no planes showing (i.e. all black).
Then all the fbs will be cleaned up, but drm_connector will be
leaking. That's a case where you need drm_atomic_helper_shutdown()
even if fbcon/fbdev isn't even enabled.

Cheers, Daniel


> > This would be analog to your shutdown parameter below.
> >
> > Essentially I think we can only do the drm_dev_unplug autocleanup in a
> > given driver from devm_drm_dev_register iff all the cleanup actions have
> > been devm-ified, and there's nothing left in it's unbind callback. Because
> > if anything is left in its unbind callback that's a bug, since
> > drm_dev_unregister() (called through drm_dev_unplug) is the very first (or
> > at least one of the very first, before we start cleanup up) functions that
> > need to be called.
> > -Daniel
> >
> >>
> >> diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
> >> index 12129772be45..7ed9550baff6 100644
> >> --- a/drivers/gpu/drm/drm_drv.c
> >> +++ b/drivers/gpu/drm/drm_drv.c
> >> @@ -34,7 +34,9 @@
> >>  #include <linux/slab.h>
> >>  #include <linux/srcu.h>
> >>
> >> +#include <drm/drm_atomic_helper.h>
> >>  #include <drm/drm_client.h>
> >> +#include <drm/drm_crtc_helper.h>
> >>  #include <drm/drm_drv.h>
> >>  #include <drm/drm_fb_helper.h>
> >>  #include <drm/drmP.h>
> >> @@ -355,17 +357,7 @@ void drm_dev_exit(int idx)
> >>  }
> >>  EXPORT_SYMBOL(drm_dev_exit);
> >>
> >> -/**
> >> - * drm_dev_unplug - unplug a DRM device
> >> - * @dev: DRM device
> >> - *
> >> - * This unplugs a hotpluggable DRM device, which makes it inaccessible to
> >> - * userspace operations. Entry-points can use drm_dev_enter() and
> >> - * drm_dev_exit() to protect device resources in a race free manner. This
> >> - * essentially unregisters the device like drm_dev_unregister(), but can be
> >> - * called while there are still open users of @dev.
> >> - */
> >> -void drm_dev_unplug(struct drm_device *dev)
> >> +static void __drm_dev_unplug(struct drm_device *dev, bool shutdown)
> >>  {
> >>         /*
> >>          * After synchronizing any critical read section is guaranteed
> >> to see
> >> @@ -378,11 +370,32 @@ void drm_dev_unplug(struct drm_device *dev)
> >>
> >>         drm_dev_unregister(dev);
> >>
> >> +       if (shutdown)
> >> +               drm_kms_helper_poll_fini(dev);
> >> +
> >>         mutex_lock(&drm_global_mutex);
> >> -       if (dev->open_count == 0)
> >> +       if (dev->open_count == 0) {
> >> +               if (shutdown)
> >> +                       drm_atomic_helper_shutdown(dev);
> >>                 drm_dev_put(dev);
> >> +       }
> >>         mutex_unlock(&drm_global_mutex);
> >>  }
> >> +
> >> +/**
> >> + * drm_dev_unplug - unplug a DRM device
> >> + * @dev: DRM device
> >> + *
> >> + * This unplugs a hotpluggable DRM device, which makes it inaccessible to
> >> + * userspace operations. Entry-points can use drm_dev_enter() and
> >> + * drm_dev_exit() to protect device resources in a race free manner. This
> >> + * essentially unregisters the device like drm_dev_unregister(), but can be
> >> + * called while there are still open users of @dev.
> >> + */
> >> +void drm_dev_unplug(struct drm_device *dev)
> >> +{
> >> +       __drm_dev_unplug(dev, false);
> >> +}
> >>  EXPORT_SYMBOL(drm_dev_unplug);
> >>
> >>  /*
> >> @@ -920,7 +933,7 @@ EXPORT_SYMBOL(devm_drm_dev_init);
> >>
> >>  static void devm_drm_dev_register_release(void *data)
> >>  {
> >> -       drm_dev_unplug(data);
> >> +       __drm_dev_unplug(data, true);
> >>  }
> >>
> >>  static int devm_drm_dev_register(struct drm_device *dev)
> >>
> >>
> >> I realised that we should take a ref on the parent device so it can be
> >> accessed by the DRM_DEV_ functions after unplug.
> >>
> >>
> >> Noralf.
> >>
> >>
> >>>
> >>>>> + *
> >>>>> + * Note: This function must be used together with
> >>>>> + * devm_drm_dev_register_with_fbdev().
> >>>>> + *
> >>>>> + * RETURNS:
> >>>>> + * 0 on success, or error code on failure.
> >>>>> + */
> >>>>> +int devm_drm_dev_init(struct device *parent,
> >>>>> +               struct drm_device *dev,
> >>>>> +               struct drm_driver *driver)
> >>>>> +{
> >>>>> + int ret;
> >>>>> +
> >>>>> + if (WARN_ON(!parent || !driver->release))
> >>>>> +         return -EINVAL;
> >>>>> +
> >>>>> + ret = drm_dev_init(dev, driver, parent);
> >>>>> + if (ret)
> >>>>> +         return ret;
> >>>>> +
> >>>>> + /*
> >>>>> +  * This is a temporary release action that is used if probing fails
> >>>>> +  * before devm_drm_dev_register() is called.
> >>>>> +  */
> >>>>> + ret = devm_add_action(parent, devm_drm_dev_init_release, dev);
> >>>>> + if (ret)
> >>>>> +         devm_drm_dev_init_release(dev);
> >>>>> +
> >>>>> + return ret;
> >>>>> +}
> >>>>> +EXPORT_SYMBOL(devm_drm_dev_init);
> >>>>> +
> >>>>> +static void devm_drm_dev_register_release(void *data)
> >>>>> +{
> >>>>> + drm_dev_unplug(data);
> >>>>> +}
> >>>>> +
> >>>>> +static int devm_drm_dev_register(struct drm_device *dev)
> >>>>> +{
> >>>>> + int ret;
> >>>>> +
> >>>>> + ret = drm_dev_register(dev, 0);
> >>>>> + if (ret)
> >>>>> +         return ret;
> >>>>> +
> >>>>> + /*
> >>>>> +  * This has now served it's purpose, remove it to not mess up ref
> >>>>> +  * counting.
> >>>>> +  */
> >>>>> + devm_remove_action(dev->dev, devm_drm_dev_init_release, dev);
> >>>>> +
> >>>>> + ret = devm_add_action(dev->dev, devm_drm_dev_register_release, dev);
> >>>>> + if (ret)
> >>>>> +         devm_drm_dev_register_release(dev);
> >>>>> +
> >>>>> + return ret;
> >>>>> +}
> >>>>> +
> >>>>> +/**
> >>>>> + * devm_drm_dev_register_with_fbdev - Resource managed drm_dev_register()
> >>>>> + *                                    including generic fbdev emulation
> >>>>> + * @dev: DRM device to register
> >>>>> + * @fbdev_bpp: Preferred bits per pixel for fbdev (optional)
> >>>>> + *
> >>>>> + * Managed drm_dev_register() that also calls drm_fbdev_generic_setup().
> >>>>> + * The DRM device registered with this function is automatically unregistered on
> >>>>> + * driver detach using drm_dev_unplug().
> >>>>> + *
> >>>>> + * Note: This function must be used together with devm_drm_dev_init().
> >>>>> + *
> >>>>> + * For testing driver detach can be triggered manually by writing to the driver
> >>>>> + * 'unbind' file.
> >>>>> + *
> >>>>> + * RETURNS:
> >>>>> + * 0 on success, negative error code on failure.
> >>>>> + */
> >>>>> +int devm_drm_dev_register_with_fbdev(struct drm_device *dev,
> >>>>> +                              unsigned int fbdev_bpp)
> >>>>> +{
> >>>>> + int ret;
> >>>>> +
> >>>>> + ret = devm_drm_dev_register(dev);
> >>>>> + if (ret)
> >>>>> +         return ret;
> >>>>> +
> >>>>> + drm_fbdev_generic_setup(dev, fbdev_bpp);
> >>>>> +
> >>>>> + return 0;
> >>>>> +}
> >>>>> +EXPORT_SYMBOL(devm_drm_dev_register_with_fbdev);
> >>>>> +
> >>>>>  /**
> >>>>>   * drm_dev_set_unique - Set the unique name of a DRM device
> >>>>>   * @dev: device of which to set the unique name
> >>>>> diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h
> >>>>> index 35af23f5fa0d..c3f0477f2e7f 100644
> >>>>> --- a/include/drm/drm_drv.h
> >>>>> +++ b/include/drm/drm_drv.h
> >>>>> @@ -628,6 +628,12 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver,
> >>>>>  int drm_dev_register(struct drm_device *dev, unsigned long flags);
> >>>>>  void drm_dev_unregister(struct drm_device *dev);
> >>>>>
> >>>>> +int devm_drm_dev_init(struct device *parent,
> >>>>> +               struct drm_device *dev,
> >>>>> +               struct drm_driver *driver);
> >>>>> +int devm_drm_dev_register_with_fbdev(struct drm_device *dev,
> >>>>> +                              unsigned int fbdev_bpp);
> >>>>> +
> >>>>>  void drm_dev_get(struct drm_device *dev);
> >>>>>  void drm_dev_put(struct drm_device *dev);
> >>>>>  void drm_put_dev(struct drm_device *dev);
> >>>>> --
> >>>>> 2.20.1
> >>>>>
> >>>>> _______________________________________________
> >>>>> dri-devel mailing list
> >>>>> dri-devel@lists.freedesktop.org
> >>>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
> >>>>
> >>>> --
> >>>> Daniel Vetter
> >>>> Software Engineer, Intel Corporation
> >>>> http://blog.ffwll.ch
> >>>
> >



-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH 01/11] drm: Add devm_drm_dev_init/register
  2019-01-22 19:30             ` Daniel Vetter
@ 2019-01-23 10:54               ` Noralf Trønnes
  2019-01-24 10:43                 ` devm actions and hw clenaup (was Re: [PATCH 01/11] drm: Add devm_drm_dev_init/register) Daniel Vetter
  0 siblings, 1 reply; 57+ messages in thread
From: Noralf Trønnes @ 2019-01-23 10:54 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: David Lechner, dri-devel



Den 22.01.2019 20.30, skrev Daniel Vetter:
> On Tue, Jan 22, 2019 at 8:07 PM Noralf Trønnes <noralf@tronnes.org> wrote:
>>
>>
>>
>> Den 22.01.2019 10.32, skrev Daniel Vetter:
>>> On Mon, Jan 21, 2019 at 01:21:46PM +0100, Noralf Trønnes wrote:
>>>>
>>>>
>>>> Den 21.01.2019 10.55, skrev Daniel Vetter:
>>>>> On Mon, Jan 21, 2019 at 10:10:14AM +0100, Daniel Vetter wrote:
>>>>>> On Sun, Jan 20, 2019 at 12:43:08PM +0100, Noralf Trønnes wrote:
>>>>>>> This adds resource managed (devres) versions of drm_dev_init() and
>>>>>>> drm_dev_register().
>>>>>>>
>>>>>>> Also added is devm_drm_dev_register_with_fbdev() which sets up generic
>>>>>>> fbdev emulation as well.
>>>>>>>
>>>>>>> devm_drm_dev_register() isn't exported since there are no users.
>>>>>>>
>>>>>>> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
>>>>>>
>>>>
>>>> <snip>
>>>>
>>>>>>> diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
>>>>>>> index 381581b01d48..12129772be45 100644
>>>>>>> --- a/drivers/gpu/drm/drm_drv.c
>>>>>>> +++ b/drivers/gpu/drm/drm_drv.c
>>>>>>> @@ -36,6 +36,7 @@
>>>>>>>
>>>>>>>  #include <drm/drm_client.h>
>>>>>>>  #include <drm/drm_drv.h>
>>>>>>> +#include <drm/drm_fb_helper.h>
>>>>>>>  #include <drm/drmP.h>
>>>>>>>
>>>>>>>  #include "drm_crtc_internal.h"
>>>>>>> @@ -871,6 +872,111 @@ void drm_dev_unregister(struct drm_device *dev)
>>>>>>>  }
>>>>>>>  EXPORT_SYMBOL(drm_dev_unregister);
>>>>>>>
>>>>>>> +static void devm_drm_dev_init_release(void *data)
>>>>>>> +{
>>>>>>> + drm_dev_put(data);
>>>>>
>>>>> We need drm_dev_unplug() here, or this isn't safe.
>>>>
>>>> This function is only used to cover the error path if probe fails before
>>>> devm_drm_dev_register() is called. devm_drm_dev_register_release() is
>>>> the one that calls unplug. There are comments about this in the functions.
>>>
>>> I think I get a prize for being ignorant and blind :-/
>>>
>>>>
>>>>>
>>>>>>> +}
>>>>>>> +
>>>>>>> +/**
>>>>>>> + * devm_drm_dev_init - Resource managed drm_dev_init()
>>>>>>> + * @parent: Parent device object
>>>>>>> + * @dev: DRM device
>>>>>>> + * @driver: DRM driver
>>>>>>> + *
>>>>>>> + * Managed drm_dev_init(). The DRM device initialized with this function is
>>>>>>> + * automatically released on driver detach. You must supply a
>>>>>
>>>>> I think a bit more clarity here would be good:
>>>>>
>>>>> "... automatically released on driver unbind by callind drm_dev_unplug()."
>>>>>
>>>>>>> + * &drm_driver.release callback to control the finalization explicitly.
>>>>>
>>>>> I think a loud warning for these is in order:
>>>>>
>>>>> "WARNING:
>>>>>
>>>>> "In generally it is unsafe to use devm functions for drm structures
>>>>> because the lifetimes of &drm_device and the underlying &device do not
>>>>> match. This here works because it doesn't immediately free anything, but
>>>>> only calls drm_dev_unplug(), which internally decrements the &drm_device
>>>>> refcount through drm_dev_put().
>>>>>
>>>>> "All other drm structures must still be explicitly released in the
>>>>> &drm_driver.release callback."
>>>>>
>>>>> While thinking about this I just realized that with this design we have no
>>>>> good place to call drm_atomic_helper_shutdown(). Which we need to, or all
>>>>> kinds of things will leak badly (connectors, fb, ...), but there's no
>>>>> place to call it:
>>>>> - unbind is too early, since we haven't yet called drm_dev_unplug, and the
>>>>>   drm_dev_unregister in there must be called _before_ we start to shut
>>>>>   down anything.
>>>>> - drm_driver.release is way too late.
>>>>>
>>>>> Ofc for a real hotunplug there's no point in shutting down the hw (it's
>>>>> already gone), but for a driver unload/unbind it would be nice if this
>>>>> happens automatically and in the right order.
>>>>>
>>>>> So not sure what to do here really.
>>>>
>>>> How about this change: (it breaks the rule of pulling helpers into the
>>>> core, so maybe we should put the devm_ functions into the simple KMS
>>>> helper instead?)
>>>
>>> Yeah smells a bit much like midlayer ... What would work is having a pile
>>> more devm_ helper functions, so that we onion-unwrap everything correctly,
>>> and in the right order. So:
>>>
>>> - devm_drm_dev_init (always does a drm_dev_put())
>>>
>>> - devm_drm_poll_enable (shuts down the poll helper with a devm action)
>>>
>>> - devm_drm_mode_config_reset (does an atomic_helper_shutdown() as it's cleanup action)
>>>
>>> - devm_drm_dev_register (grabs an additional drm_dev_get() reference so it
>>>   can call drm_dev_unplug() unconditionally).
>>>
>>
>> Beautiful! I really like this, it's very flexible.
>>
>> Where should devm_drm_mode_config_reset() live? It will pull in the
>> atomic helper...
> 
> I think a new drm_devm.c helper would be nice for all this stuff.
> Especially since you can't freely mix devm-based setup/cleanup with
> normal cleanup I think it'd be good to have it all together in one
> place. And perhaps even a code example in the DOC: overview.
> 
>>> We'd need to make sure some of the cleanup actions dtrt when the device is
>>> gone, but I think we can achieve that by liberally sprinkling
>>> drm_dev_enter/exit over them, e.g. the the cleanup action for
>>> drm_mode_config_reset would be:
>>>
>>> {
>>>       if (drm_dev_enter())
>>>               return;
>>>
>>>       drm_atomic_helper_shutdown();
>>>
>>>       drm_dev_exit();
>>> }
>>>
>>
>> drm_dev_enter() can only be used to check whether the drm_device is
>> registered or not, it doesn't say anything about the state of the parent
>> device.
>>
>> All we know is that the device is being unbound from the driver, we
>> don't know if it's the device that's being removed or if it's the driver
>> that's unregistered.
> 
> You're right, both paths will have called drm_dev_unplug by then.
> Silly me. I really liked my idea :-)
> 
>> I have looked at the various call chains:
>>
>> driver_unregister ->
>>     bus_remove_driver ->
>>         driver_detach ->
>>             device_release_driver_internal
>>
>> device_unregister ->
>>     device_del ->
>>         bus_remove_device ->
>>             device_release_driver ->
>>                 device_release_driver_internal
>>
>> sysfs: unbind_store ->
>>     device_release_driver ->
>>         device_release_driver_internal
>>
>> The only way I've found to differentiate between these in a cleanup
>> action is that device_del() uses the bus notifier to signal
>> BUS_NOTIFY_DEL_DEVICE before calling bus_remove_device(). Such a
>> notifier could be used to set a drm_device->parent_removed flag.
> 
> Hm, this might upset Greg KH's code taste ... maybe there's a better
> way to do this, but best to prototype a patch with this, send it to
> him and ask how to :-)
> 

I'll leave this to the one that needs it. The tinydrm drivers doesn't
need to touch hw after DRM unregister.

>> Why is it necessary to call drm_atomic_helper_shutdown() here? Doesn't
>> everything get disabled when userspace closes? It does in my tinydrm
>> world :-)
> 
> Iirc fbdev/fbcon can result in leaks ... at least we've had patches
> where drivers leaked drm_connector and drm_framebuffer objects, and
> they've been fixed by calling drm_atomic_helper_shutdown() in the
> unload path. Maybe this is cargo-culting, but it goes way back to
> pre-atomic, where drivers called drm_helper_force_disable_all().
> 
> If you try to move the fbcon to your tinydrm drivers (con2fb is
> apparently the cmdline tool you need, never tried it, I only switch
> the kernel's console between fbcon and dummycon and back, not what
> fbcon drivers itself), then I think you should be able to reproduce.
> And maybe you have a better idea how to deal with this all.
> 
> Note also that there's been proposals floating around to only close an
> drm_framebuffer, not also remove it (like the current RMFB ioctl
> does), with that closing userspace would not necessarily lead to a
> full cleanup.
> 
> Another thing (which doesn't apply to drm_simple_display_pipe drivers)
> is if you have the display on, but no planes showing (i.e. all black).
> Then all the fbs will be cleaned up, but drm_connector will be
> leaking. That's a case where you need drm_atomic_helper_shutdown()
> even if fbcon/fbdev isn't even enabled.
> 

Ok, this means that I don't need to call drm_atomic_helper_shutdown() in
tinydrm. DRM userspace disables the pipe on close and the generic fbdev
emulation also releases everything.
Even so, maybe I should use devm_drm_mode_config_reset() after all to
keep drivers uniform, to avoid confusion: why doesn't he use it?

Noralf.

> Cheers, Daniel
> 
> 
>>> This would be analog to your shutdown parameter below.
>>>
>>> Essentially I think we can only do the drm_dev_unplug autocleanup in a
>>> given driver from devm_drm_dev_register iff all the cleanup actions have
>>> been devm-ified, and there's nothing left in it's unbind callback. Because
>>> if anything is left in its unbind callback that's a bug, since
>>> drm_dev_unregister() (called through drm_dev_unplug) is the very first (or
>>> at least one of the very first, before we start cleanup up) functions that
>>> need to be called.
>>> -Daniel
>>>
>>>>
>>>> diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
>>>> index 12129772be45..7ed9550baff6 100644
>>>> --- a/drivers/gpu/drm/drm_drv.c
>>>> +++ b/drivers/gpu/drm/drm_drv.c
>>>> @@ -34,7 +34,9 @@
>>>>  #include <linux/slab.h>
>>>>  #include <linux/srcu.h>
>>>>
>>>> +#include <drm/drm_atomic_helper.h>
>>>>  #include <drm/drm_client.h>
>>>> +#include <drm/drm_crtc_helper.h>
>>>>  #include <drm/drm_drv.h>
>>>>  #include <drm/drm_fb_helper.h>
>>>>  #include <drm/drmP.h>
>>>> @@ -355,17 +357,7 @@ void drm_dev_exit(int idx)
>>>>  }
>>>>  EXPORT_SYMBOL(drm_dev_exit);
>>>>
>>>> -/**
>>>> - * drm_dev_unplug - unplug a DRM device
>>>> - * @dev: DRM device
>>>> - *
>>>> - * This unplugs a hotpluggable DRM device, which makes it inaccessible to
>>>> - * userspace operations. Entry-points can use drm_dev_enter() and
>>>> - * drm_dev_exit() to protect device resources in a race free manner. This
>>>> - * essentially unregisters the device like drm_dev_unregister(), but can be
>>>> - * called while there are still open users of @dev.
>>>> - */
>>>> -void drm_dev_unplug(struct drm_device *dev)
>>>> +static void __drm_dev_unplug(struct drm_device *dev, bool shutdown)
>>>>  {
>>>>         /*
>>>>          * After synchronizing any critical read section is guaranteed
>>>> to see
>>>> @@ -378,11 +370,32 @@ void drm_dev_unplug(struct drm_device *dev)
>>>>
>>>>         drm_dev_unregister(dev);
>>>>
>>>> +       if (shutdown)
>>>> +               drm_kms_helper_poll_fini(dev);
>>>> +
>>>>         mutex_lock(&drm_global_mutex);
>>>> -       if (dev->open_count == 0)
>>>> +       if (dev->open_count == 0) {
>>>> +               if (shutdown)
>>>> +                       drm_atomic_helper_shutdown(dev);
>>>>                 drm_dev_put(dev);
>>>> +       }
>>>>         mutex_unlock(&drm_global_mutex);
>>>>  }
>>>> +
>>>> +/**
>>>> + * drm_dev_unplug - unplug a DRM device
>>>> + * @dev: DRM device
>>>> + *
>>>> + * This unplugs a hotpluggable DRM device, which makes it inaccessible to
>>>> + * userspace operations. Entry-points can use drm_dev_enter() and
>>>> + * drm_dev_exit() to protect device resources in a race free manner. This
>>>> + * essentially unregisters the device like drm_dev_unregister(), but can be
>>>> + * called while there are still open users of @dev.
>>>> + */
>>>> +void drm_dev_unplug(struct drm_device *dev)
>>>> +{
>>>> +       __drm_dev_unplug(dev, false);
>>>> +}
>>>>  EXPORT_SYMBOL(drm_dev_unplug);
>>>>
>>>>  /*
>>>> @@ -920,7 +933,7 @@ EXPORT_SYMBOL(devm_drm_dev_init);
>>>>
>>>>  static void devm_drm_dev_register_release(void *data)
>>>>  {
>>>> -       drm_dev_unplug(data);
>>>> +       __drm_dev_unplug(data, true);
>>>>  }
>>>>
>>>>  static int devm_drm_dev_register(struct drm_device *dev)
>>>>
>>>>
>>>> I realised that we should take a ref on the parent device so it can be
>>>> accessed by the DRM_DEV_ functions after unplug.
>>>>
>>>>
>>>> Noralf.
>>>>
>>>>
>>>>>
>>>>>>> + *
>>>>>>> + * Note: This function must be used together with
>>>>>>> + * devm_drm_dev_register_with_fbdev().
>>>>>>> + *
>>>>>>> + * RETURNS:
>>>>>>> + * 0 on success, or error code on failure.
>>>>>>> + */
>>>>>>> +int devm_drm_dev_init(struct device *parent,
>>>>>>> +               struct drm_device *dev,
>>>>>>> +               struct drm_driver *driver)
>>>>>>> +{
>>>>>>> + int ret;
>>>>>>> +
>>>>>>> + if (WARN_ON(!parent || !driver->release))
>>>>>>> +         return -EINVAL;
>>>>>>> +
>>>>>>> + ret = drm_dev_init(dev, driver, parent);
>>>>>>> + if (ret)
>>>>>>> +         return ret;
>>>>>>> +
>>>>>>> + /*
>>>>>>> +  * This is a temporary release action that is used if probing fails
>>>>>>> +  * before devm_drm_dev_register() is called.
>>>>>>> +  */
>>>>>>> + ret = devm_add_action(parent, devm_drm_dev_init_release, dev);
>>>>>>> + if (ret)
>>>>>>> +         devm_drm_dev_init_release(dev);
>>>>>>> +
>>>>>>> + return ret;
>>>>>>> +}
>>>>>>> +EXPORT_SYMBOL(devm_drm_dev_init);
>>>>>>> +
>>>>>>> +static void devm_drm_dev_register_release(void *data)
>>>>>>> +{
>>>>>>> + drm_dev_unplug(data);
>>>>>>> +}
>>>>>>> +
>>>>>>> +static int devm_drm_dev_register(struct drm_device *dev)
>>>>>>> +{
>>>>>>> + int ret;
>>>>>>> +
>>>>>>> + ret = drm_dev_register(dev, 0);
>>>>>>> + if (ret)
>>>>>>> +         return ret;
>>>>>>> +
>>>>>>> + /*
>>>>>>> +  * This has now served it's purpose, remove it to not mess up ref
>>>>>>> +  * counting.
>>>>>>> +  */
>>>>>>> + devm_remove_action(dev->dev, devm_drm_dev_init_release, dev);
>>>>>>> +
>>>>>>> + ret = devm_add_action(dev->dev, devm_drm_dev_register_release, dev);
>>>>>>> + if (ret)
>>>>>>> +         devm_drm_dev_register_release(dev);
>>>>>>> +
>>>>>>> + return ret;
>>>>>>> +}
>>>>>>> +
>>>>>>> +/**
>>>>>>> + * devm_drm_dev_register_with_fbdev - Resource managed drm_dev_register()
>>>>>>> + *                                    including generic fbdev emulation
>>>>>>> + * @dev: DRM device to register
>>>>>>> + * @fbdev_bpp: Preferred bits per pixel for fbdev (optional)
>>>>>>> + *
>>>>>>> + * Managed drm_dev_register() that also calls drm_fbdev_generic_setup().
>>>>>>> + * The DRM device registered with this function is automatically unregistered on
>>>>>>> + * driver detach using drm_dev_unplug().
>>>>>>> + *
>>>>>>> + * Note: This function must be used together with devm_drm_dev_init().
>>>>>>> + *
>>>>>>> + * For testing driver detach can be triggered manually by writing to the driver
>>>>>>> + * 'unbind' file.
>>>>>>> + *
>>>>>>> + * RETURNS:
>>>>>>> + * 0 on success, negative error code on failure.
>>>>>>> + */
>>>>>>> +int devm_drm_dev_register_with_fbdev(struct drm_device *dev,
>>>>>>> +                              unsigned int fbdev_bpp)
>>>>>>> +{
>>>>>>> + int ret;
>>>>>>> +
>>>>>>> + ret = devm_drm_dev_register(dev);
>>>>>>> + if (ret)
>>>>>>> +         return ret;
>>>>>>> +
>>>>>>> + drm_fbdev_generic_setup(dev, fbdev_bpp);
>>>>>>> +
>>>>>>> + return 0;
>>>>>>> +}
>>>>>>> +EXPORT_SYMBOL(devm_drm_dev_register_with_fbdev);
>>>>>>> +
>>>>>>>  /**
>>>>>>>   * drm_dev_set_unique - Set the unique name of a DRM device
>>>>>>>   * @dev: device of which to set the unique name
>>>>>>> diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h
>>>>>>> index 35af23f5fa0d..c3f0477f2e7f 100644
>>>>>>> --- a/include/drm/drm_drv.h
>>>>>>> +++ b/include/drm/drm_drv.h
>>>>>>> @@ -628,6 +628,12 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver,
>>>>>>>  int drm_dev_register(struct drm_device *dev, unsigned long flags);
>>>>>>>  void drm_dev_unregister(struct drm_device *dev);
>>>>>>>
>>>>>>> +int devm_drm_dev_init(struct device *parent,
>>>>>>> +               struct drm_device *dev,
>>>>>>> +               struct drm_driver *driver);
>>>>>>> +int devm_drm_dev_register_with_fbdev(struct drm_device *dev,
>>>>>>> +                              unsigned int fbdev_bpp);
>>>>>>> +
>>>>>>>  void drm_dev_get(struct drm_device *dev);
>>>>>>>  void drm_dev_put(struct drm_device *dev);
>>>>>>>  void drm_put_dev(struct drm_device *dev);
>>>>>>> --
>>>>>>> 2.20.1
>>>>>>>
>>>>>>> _______________________________________________
>>>>>>> dri-devel mailing list
>>>>>>> dri-devel@lists.freedesktop.org
>>>>>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>>>>>
>>>>>> --
>>>>>> Daniel Vetter
>>>>>> Software Engineer, Intel Corporation
>>>>>> http://blog.ffwll.ch
>>>>>
>>>
> 
> 
> 
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* devm actions and hw clenaup (was Re: [PATCH 01/11] drm: Add devm_drm_dev_init/register)
  2019-01-23 10:54               ` Noralf Trønnes
@ 2019-01-24 10:43                 ` Daniel Vetter
  2019-01-24 17:46                   ` Greg KH
  0 siblings, 1 reply; 57+ messages in thread
From: Daniel Vetter @ 2019-01-24 10:43 UTC (permalink / raw)
  To: Noralf Trønnes; +Cc: Greg KH, Rafael J. Wysocki, dri-devel, David Lechner

On Wed, Jan 23, 2019 at 11:54:07AM +0100, Noralf Trønnes wrote:
> 
> 
> Den 22.01.2019 20.30, skrev Daniel Vetter:
> > On Tue, Jan 22, 2019 at 8:07 PM Noralf Trønnes <noralf@tronnes.org> wrote:
> >>
> >>
> >>
> >> Den 22.01.2019 10.32, skrev Daniel Vetter:
> >>> On Mon, Jan 21, 2019 at 01:21:46PM +0100, Noralf Trønnes wrote:
> >>>>
> >>>>
> >>>> Den 21.01.2019 10.55, skrev Daniel Vetter:
> >>>>> On Mon, Jan 21, 2019 at 10:10:14AM +0100, Daniel Vetter wrote:
> >>>>>> On Sun, Jan 20, 2019 at 12:43:08PM +0100, Noralf Trønnes wrote:
> >>>>>>> This adds resource managed (devres) versions of drm_dev_init() and
> >>>>>>> drm_dev_register().
> >>>>>>>
> >>>>>>> Also added is devm_drm_dev_register_with_fbdev() which sets up generic
> >>>>>>> fbdev emulation as well.
> >>>>>>>
> >>>>>>> devm_drm_dev_register() isn't exported since there are no users.
> >>>>>>>
> >>>>>>> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
> >>>>>>
> >>>>
> >>>> <snip>
> >>>>
> >>>>>>> diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
> >>>>>>> index 381581b01d48..12129772be45 100644
> >>>>>>> --- a/drivers/gpu/drm/drm_drv.c
> >>>>>>> +++ b/drivers/gpu/drm/drm_drv.c
> >>>>>>> @@ -36,6 +36,7 @@
> >>>>>>>
> >>>>>>>  #include <drm/drm_client.h>
> >>>>>>>  #include <drm/drm_drv.h>
> >>>>>>> +#include <drm/drm_fb_helper.h>
> >>>>>>>  #include <drm/drmP.h>
> >>>>>>>
> >>>>>>>  #include "drm_crtc_internal.h"
> >>>>>>> @@ -871,6 +872,111 @@ void drm_dev_unregister(struct drm_device *dev)
> >>>>>>>  }
> >>>>>>>  EXPORT_SYMBOL(drm_dev_unregister);
> >>>>>>>
> >>>>>>> +static void devm_drm_dev_init_release(void *data)
> >>>>>>> +{
> >>>>>>> + drm_dev_put(data);
> >>>>>
> >>>>> We need drm_dev_unplug() here, or this isn't safe.
> >>>>
> >>>> This function is only used to cover the error path if probe fails before
> >>>> devm_drm_dev_register() is called. devm_drm_dev_register_release() is
> >>>> the one that calls unplug. There are comments about this in the functions.
> >>>
> >>> I think I get a prize for being ignorant and blind :-/
> >>>
> >>>>
> >>>>>
> >>>>>>> +}
> >>>>>>> +
> >>>>>>> +/**
> >>>>>>> + * devm_drm_dev_init - Resource managed drm_dev_init()
> >>>>>>> + * @parent: Parent device object
> >>>>>>> + * @dev: DRM device
> >>>>>>> + * @driver: DRM driver
> >>>>>>> + *
> >>>>>>> + * Managed drm_dev_init(). The DRM device initialized with this function is
> >>>>>>> + * automatically released on driver detach. You must supply a
> >>>>>
> >>>>> I think a bit more clarity here would be good:
> >>>>>
> >>>>> "... automatically released on driver unbind by callind drm_dev_unplug()."
> >>>>>
> >>>>>>> + * &drm_driver.release callback to control the finalization explicitly.
> >>>>>
> >>>>> I think a loud warning for these is in order:
> >>>>>
> >>>>> "WARNING:
> >>>>>
> >>>>> "In generally it is unsafe to use devm functions for drm structures
> >>>>> because the lifetimes of &drm_device and the underlying &device do not
> >>>>> match. This here works because it doesn't immediately free anything, but
> >>>>> only calls drm_dev_unplug(), which internally decrements the &drm_device
> >>>>> refcount through drm_dev_put().
> >>>>>
> >>>>> "All other drm structures must still be explicitly released in the
> >>>>> &drm_driver.release callback."
> >>>>>
> >>>>> While thinking about this I just realized that with this design we have no
> >>>>> good place to call drm_atomic_helper_shutdown(). Which we need to, or all
> >>>>> kinds of things will leak badly (connectors, fb, ...), but there's no
> >>>>> place to call it:
> >>>>> - unbind is too early, since we haven't yet called drm_dev_unplug, and the
> >>>>>   drm_dev_unregister in there must be called _before_ we start to shut
> >>>>>   down anything.
> >>>>> - drm_driver.release is way too late.
> >>>>>
> >>>>> Ofc for a real hotunplug there's no point in shutting down the hw (it's
> >>>>> already gone), but for a driver unload/unbind it would be nice if this
> >>>>> happens automatically and in the right order.
> >>>>>
> >>>>> So not sure what to do here really.
> >>>>
> >>>> How about this change: (it breaks the rule of pulling helpers into the
> >>>> core, so maybe we should put the devm_ functions into the simple KMS
> >>>> helper instead?)
> >>>
> >>> Yeah smells a bit much like midlayer ... What would work is having a pile
> >>> more devm_ helper functions, so that we onion-unwrap everything correctly,
> >>> and in the right order. So:
> >>>
> >>> - devm_drm_dev_init (always does a drm_dev_put())
> >>>
> >>> - devm_drm_poll_enable (shuts down the poll helper with a devm action)
> >>>
> >>> - devm_drm_mode_config_reset (does an atomic_helper_shutdown() as it's cleanup action)
> >>>
> >>> - devm_drm_dev_register (grabs an additional drm_dev_get() reference so it
> >>>   can call drm_dev_unplug() unconditionally).
> >>>
> >>
> >> Beautiful! I really like this, it's very flexible.
> >>
> >> Where should devm_drm_mode_config_reset() live? It will pull in the
> >> atomic helper...
> > 
> > I think a new drm_devm.c helper would be nice for all this stuff.
> > Especially since you can't freely mix devm-based setup/cleanup with
> > normal cleanup I think it'd be good to have it all together in one
> > place. And perhaps even a code example in the DOC: overview.
> > 
> >>> We'd need to make sure some of the cleanup actions dtrt when the device is
> >>> gone, but I think we can achieve that by liberally sprinkling
> >>> drm_dev_enter/exit over them, e.g. the the cleanup action for
> >>> drm_mode_config_reset would be:
> >>>
> >>> {
> >>>       if (drm_dev_enter())
> >>>               return;
> >>>
> >>>       drm_atomic_helper_shutdown();
> >>>
> >>>       drm_dev_exit();
> >>> }
> >>>
> >>
> >> drm_dev_enter() can only be used to check whether the drm_device is
> >> registered or not, it doesn't say anything about the state of the parent
> >> device.
> >>
> >> All we know is that the device is being unbound from the driver, we
> >> don't know if it's the device that's being removed or if it's the driver
> >> that's unregistered.
> > 
> > You're right, both paths will have called drm_dev_unplug by then.
> > Silly me. I really liked my idea :-)
> > 
> >> I have looked at the various call chains:
> >>
> >> driver_unregister ->
> >>     bus_remove_driver ->
> >>         driver_detach ->
> >>             device_release_driver_internal
> >>
> >> device_unregister ->
> >>     device_del ->
> >>         bus_remove_device ->
> >>             device_release_driver ->
> >>                 device_release_driver_internal
> >>
> >> sysfs: unbind_store ->
> >>     device_release_driver ->
> >>         device_release_driver_internal
> >>
> >> The only way I've found to differentiate between these in a cleanup
> >> action is that device_del() uses the bus notifier to signal
> >> BUS_NOTIFY_DEL_DEVICE before calling bus_remove_device(). Such a
> >> notifier could be used to set a drm_device->parent_removed flag.
> > 
> > Hm, this might upset Greg KH's code taste ... maybe there's a better
> > way to do this, but best to prototype a patch with this, send it to
> > him and ask how to :-)
> > 
> 
> I'll leave this to the one that needs it. The tinydrm drivers doesn't
> need to touch hw after DRM unregister.
> 
> >> Why is it necessary to call drm_atomic_helper_shutdown() here? Doesn't
> >> everything get disabled when userspace closes? It does in my tinydrm
> >> world :-)
> > 
> > Iirc fbdev/fbcon can result in leaks ... at least we've had patches
> > where drivers leaked drm_connector and drm_framebuffer objects, and
> > they've been fixed by calling drm_atomic_helper_shutdown() in the
> > unload path. Maybe this is cargo-culting, but it goes way back to
> > pre-atomic, where drivers called drm_helper_force_disable_all().
> > 
> > If you try to move the fbcon to your tinydrm drivers (con2fb is
> > apparently the cmdline tool you need, never tried it, I only switch
> > the kernel's console between fbcon and dummycon and back, not what
> > fbcon drivers itself), then I think you should be able to reproduce.
> > And maybe you have a better idea how to deal with this all.
> > 
> > Note also that there's been proposals floating around to only close an
> > drm_framebuffer, not also remove it (like the current RMFB ioctl
> > does), with that closing userspace would not necessarily lead to a
> > full cleanup.
> > 
> > Another thing (which doesn't apply to drm_simple_display_pipe drivers)
> > is if you have the display on, but no planes showing (i.e. all black).
> > Then all the fbs will be cleaned up, but drm_connector will be
> > leaking. That's a case where you need drm_atomic_helper_shutdown()
> > even if fbcon/fbdev isn't even enabled.
> > 
> 
> Ok, this means that I don't need to call drm_atomic_helper_shutdown() in
> tinydrm. DRM userspace disables the pipe on close and the generic fbdev
> emulation also releases everything.
> Even so, maybe I should use devm_drm_mode_config_reset() after all to
> keep drivers uniform, to avoid confusion: why doesn't he use it?

Hm maybe there is an official way to solve this, pulling in Greg+Rafael.

Super short summary: We want to start using devm actions to clean up drm
drivers. Here's the problem:
- For a driver unload/unbind without hotunplug, we want to properly clean
  up the hardware and shut it all down.

- But if the device is unplugged already, that's probably not the best
  idea, and we only want to clean up the kernel's resources/allocations.

What's the recommendation here? I see a few options:

- Make sure everything can deal with this properly. Hotunplug can happen
  anytime, so there's a race no matter what.

- Check with the device model whether the struct device is disappearing or
  whether we're just dealing with a driver unbind (no idea how to do
  that), and act accordingly.

- Fundamental question: Touching the hw from devm actions, is that ok? If
  not, then the pretty nifty plan laid out in this thread wont work.

- Something completely different?

Thanks, Daniel

> 
> Noralf.
> 
> > Cheers, Daniel
> > 
> > 
> >>> This would be analog to your shutdown parameter below.
> >>>
> >>> Essentially I think we can only do the drm_dev_unplug autocleanup in a
> >>> given driver from devm_drm_dev_register iff all the cleanup actions have
> >>> been devm-ified, and there's nothing left in it's unbind callback. Because
> >>> if anything is left in its unbind callback that's a bug, since
> >>> drm_dev_unregister() (called through drm_dev_unplug) is the very first (or
> >>> at least one of the very first, before we start cleanup up) functions that
> >>> need to be called.
> >>> -Daniel
> >>>
> >>>>
> >>>> diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
> >>>> index 12129772be45..7ed9550baff6 100644
> >>>> --- a/drivers/gpu/drm/drm_drv.c
> >>>> +++ b/drivers/gpu/drm/drm_drv.c
> >>>> @@ -34,7 +34,9 @@
> >>>>  #include <linux/slab.h>
> >>>>  #include <linux/srcu.h>
> >>>>
> >>>> +#include <drm/drm_atomic_helper.h>
> >>>>  #include <drm/drm_client.h>
> >>>> +#include <drm/drm_crtc_helper.h>
> >>>>  #include <drm/drm_drv.h>
> >>>>  #include <drm/drm_fb_helper.h>
> >>>>  #include <drm/drmP.h>
> >>>> @@ -355,17 +357,7 @@ void drm_dev_exit(int idx)
> >>>>  }
> >>>>  EXPORT_SYMBOL(drm_dev_exit);
> >>>>
> >>>> -/**
> >>>> - * drm_dev_unplug - unplug a DRM device
> >>>> - * @dev: DRM device
> >>>> - *
> >>>> - * This unplugs a hotpluggable DRM device, which makes it inaccessible to
> >>>> - * userspace operations. Entry-points can use drm_dev_enter() and
> >>>> - * drm_dev_exit() to protect device resources in a race free manner. This
> >>>> - * essentially unregisters the device like drm_dev_unregister(), but can be
> >>>> - * called while there are still open users of @dev.
> >>>> - */
> >>>> -void drm_dev_unplug(struct drm_device *dev)
> >>>> +static void __drm_dev_unplug(struct drm_device *dev, bool shutdown)
> >>>>  {
> >>>>         /*
> >>>>          * After synchronizing any critical read section is guaranteed
> >>>> to see
> >>>> @@ -378,11 +370,32 @@ void drm_dev_unplug(struct drm_device *dev)
> >>>>
> >>>>         drm_dev_unregister(dev);
> >>>>
> >>>> +       if (shutdown)
> >>>> +               drm_kms_helper_poll_fini(dev);
> >>>> +
> >>>>         mutex_lock(&drm_global_mutex);
> >>>> -       if (dev->open_count == 0)
> >>>> +       if (dev->open_count == 0) {
> >>>> +               if (shutdown)
> >>>> +                       drm_atomic_helper_shutdown(dev);
> >>>>                 drm_dev_put(dev);
> >>>> +       }
> >>>>         mutex_unlock(&drm_global_mutex);
> >>>>  }
> >>>> +
> >>>> +/**
> >>>> + * drm_dev_unplug - unplug a DRM device
> >>>> + * @dev: DRM device
> >>>> + *
> >>>> + * This unplugs a hotpluggable DRM device, which makes it inaccessible to
> >>>> + * userspace operations. Entry-points can use drm_dev_enter() and
> >>>> + * drm_dev_exit() to protect device resources in a race free manner. This
> >>>> + * essentially unregisters the device like drm_dev_unregister(), but can be
> >>>> + * called while there are still open users of @dev.
> >>>> + */
> >>>> +void drm_dev_unplug(struct drm_device *dev)
> >>>> +{
> >>>> +       __drm_dev_unplug(dev, false);
> >>>> +}
> >>>>  EXPORT_SYMBOL(drm_dev_unplug);
> >>>>
> >>>>  /*
> >>>> @@ -920,7 +933,7 @@ EXPORT_SYMBOL(devm_drm_dev_init);
> >>>>
> >>>>  static void devm_drm_dev_register_release(void *data)
> >>>>  {
> >>>> -       drm_dev_unplug(data);
> >>>> +       __drm_dev_unplug(data, true);
> >>>>  }
> >>>>
> >>>>  static int devm_drm_dev_register(struct drm_device *dev)
> >>>>
> >>>>
> >>>> I realised that we should take a ref on the parent device so it can be
> >>>> accessed by the DRM_DEV_ functions after unplug.
> >>>>
> >>>>
> >>>> Noralf.
> >>>>
> >>>>
> >>>>>
> >>>>>>> + *
> >>>>>>> + * Note: This function must be used together with
> >>>>>>> + * devm_drm_dev_register_with_fbdev().
> >>>>>>> + *
> >>>>>>> + * RETURNS:
> >>>>>>> + * 0 on success, or error code on failure.
> >>>>>>> + */
> >>>>>>> +int devm_drm_dev_init(struct device *parent,
> >>>>>>> +               struct drm_device *dev,
> >>>>>>> +               struct drm_driver *driver)
> >>>>>>> +{
> >>>>>>> + int ret;
> >>>>>>> +
> >>>>>>> + if (WARN_ON(!parent || !driver->release))
> >>>>>>> +         return -EINVAL;
> >>>>>>> +
> >>>>>>> + ret = drm_dev_init(dev, driver, parent);
> >>>>>>> + if (ret)
> >>>>>>> +         return ret;
> >>>>>>> +
> >>>>>>> + /*
> >>>>>>> +  * This is a temporary release action that is used if probing fails
> >>>>>>> +  * before devm_drm_dev_register() is called.
> >>>>>>> +  */
> >>>>>>> + ret = devm_add_action(parent, devm_drm_dev_init_release, dev);
> >>>>>>> + if (ret)
> >>>>>>> +         devm_drm_dev_init_release(dev);
> >>>>>>> +
> >>>>>>> + return ret;
> >>>>>>> +}
> >>>>>>> +EXPORT_SYMBOL(devm_drm_dev_init);
> >>>>>>> +
> >>>>>>> +static void devm_drm_dev_register_release(void *data)
> >>>>>>> +{
> >>>>>>> + drm_dev_unplug(data);
> >>>>>>> +}
> >>>>>>> +
> >>>>>>> +static int devm_drm_dev_register(struct drm_device *dev)
> >>>>>>> +{
> >>>>>>> + int ret;
> >>>>>>> +
> >>>>>>> + ret = drm_dev_register(dev, 0);
> >>>>>>> + if (ret)
> >>>>>>> +         return ret;
> >>>>>>> +
> >>>>>>> + /*
> >>>>>>> +  * This has now served it's purpose, remove it to not mess up ref
> >>>>>>> +  * counting.
> >>>>>>> +  */
> >>>>>>> + devm_remove_action(dev->dev, devm_drm_dev_init_release, dev);
> >>>>>>> +
> >>>>>>> + ret = devm_add_action(dev->dev, devm_drm_dev_register_release, dev);
> >>>>>>> + if (ret)
> >>>>>>> +         devm_drm_dev_register_release(dev);
> >>>>>>> +
> >>>>>>> + return ret;
> >>>>>>> +}
> >>>>>>> +
> >>>>>>> +/**
> >>>>>>> + * devm_drm_dev_register_with_fbdev - Resource managed drm_dev_register()
> >>>>>>> + *                                    including generic fbdev emulation
> >>>>>>> + * @dev: DRM device to register
> >>>>>>> + * @fbdev_bpp: Preferred bits per pixel for fbdev (optional)
> >>>>>>> + *
> >>>>>>> + * Managed drm_dev_register() that also calls drm_fbdev_generic_setup().
> >>>>>>> + * The DRM device registered with this function is automatically unregistered on
> >>>>>>> + * driver detach using drm_dev_unplug().
> >>>>>>> + *
> >>>>>>> + * Note: This function must be used together with devm_drm_dev_init().
> >>>>>>> + *
> >>>>>>> + * For testing driver detach can be triggered manually by writing to the driver
> >>>>>>> + * 'unbind' file.
> >>>>>>> + *
> >>>>>>> + * RETURNS:
> >>>>>>> + * 0 on success, negative error code on failure.
> >>>>>>> + */
> >>>>>>> +int devm_drm_dev_register_with_fbdev(struct drm_device *dev,
> >>>>>>> +                              unsigned int fbdev_bpp)
> >>>>>>> +{
> >>>>>>> + int ret;
> >>>>>>> +
> >>>>>>> + ret = devm_drm_dev_register(dev);
> >>>>>>> + if (ret)
> >>>>>>> +         return ret;
> >>>>>>> +
> >>>>>>> + drm_fbdev_generic_setup(dev, fbdev_bpp);
> >>>>>>> +
> >>>>>>> + return 0;
> >>>>>>> +}
> >>>>>>> +EXPORT_SYMBOL(devm_drm_dev_register_with_fbdev);
> >>>>>>> +
> >>>>>>>  /**
> >>>>>>>   * drm_dev_set_unique - Set the unique name of a DRM device
> >>>>>>>   * @dev: device of which to set the unique name
> >>>>>>> diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h
> >>>>>>> index 35af23f5fa0d..c3f0477f2e7f 100644
> >>>>>>> --- a/include/drm/drm_drv.h
> >>>>>>> +++ b/include/drm/drm_drv.h
> >>>>>>> @@ -628,6 +628,12 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver,
> >>>>>>>  int drm_dev_register(struct drm_device *dev, unsigned long flags);
> >>>>>>>  void drm_dev_unregister(struct drm_device *dev);
> >>>>>>>
> >>>>>>> +int devm_drm_dev_init(struct device *parent,
> >>>>>>> +               struct drm_device *dev,
> >>>>>>> +               struct drm_driver *driver);
> >>>>>>> +int devm_drm_dev_register_with_fbdev(struct drm_device *dev,
> >>>>>>> +                              unsigned int fbdev_bpp);
> >>>>>>> +
> >>>>>>>  void drm_dev_get(struct drm_device *dev);
> >>>>>>>  void drm_dev_put(struct drm_device *dev);
> >>>>>>>  void drm_put_dev(struct drm_device *dev);
> >>>>>>> --
> >>>>>>> 2.20.1
> >>>>>>>
> >>>>>>> _______________________________________________
> >>>>>>> dri-devel mailing list
> >>>>>>> dri-devel@lists.freedesktop.org
> >>>>>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
> >>>>>>
> >>>>>> --
> >>>>>> Daniel Vetter
> >>>>>> Software Engineer, Intel Corporation
> >>>>>> http://blog.ffwll.ch
> >>>>>
> >>>
> > 
> > 
> > 

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH 03/11] drm/simple-kms-helper: Add drm_simple_connector_create()
  2019-01-21  9:22   ` Daniel Vetter
@ 2019-01-24 14:38     ` Noralf Trønnes
  2019-01-24 14:53       ` Hans de Goede
  0 siblings, 1 reply; 57+ messages in thread
From: Noralf Trønnes @ 2019-01-24 14:38 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: Hans de Goede, david, dri-devel

[cc:Hans]

Den 21.01.2019 10.22, skrev Daniel Vetter:
> On Sun, Jan 20, 2019 at 12:43:10PM +0100, Noralf Trønnes wrote:
>> This adds a function that creates a simple connector that has only one
>> static mode. Additionally add a helper to set &drm_mode_config width
>> and height from the static mode.
>>
>> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
>> ---
>>  drivers/gpu/drm/drm_simple_kms_helper.c | 122 ++++++++++++++++++++++++
>>  include/drm/drm_simple_kms_helper.h     |   6 ++
>>  2 files changed, 128 insertions(+)
>>
>> diff --git a/drivers/gpu/drm/drm_simple_kms_helper.c b/drivers/gpu/drm/drm_simple_kms_helper.c
>> index 917812448d1b..ca29975afefe 100644
>> --- a/drivers/gpu/drm/drm_simple_kms_helper.c
>> +++ b/drivers/gpu/drm/drm_simple_kms_helper.c
>> @@ -11,6 +11,8 @@
>>  #include <drm/drm_atomic.h>
>>  #include <drm/drm_atomic_helper.h>
>>  #include <drm/drm_crtc_helper.h>
>> +#include <drm/drm_device.h>
>> +#include <drm/drm_modes.h>
>>  #include <drm/drm_plane_helper.h>
>>  #include <drm/drm_simple_kms_helper.h>
>>  #include <linux/slab.h>
>> @@ -299,4 +301,124 @@ int drm_simple_display_pipe_init(struct drm_device *dev,
>>  }
>>  EXPORT_SYMBOL(drm_simple_display_pipe_init);
>>  
>> +static const struct drm_connector_helper_funcs drm_simple_connector_hfuncs = {
>> +	/* dummy for the atomic helper */
>> +};
>> +
>> +static int drm_simple_connector_fill_modes(struct drm_connector *connector,
>> +					   uint32_t maxX, uint32_t maxY)
>> +{
>> +	return 1;
>> +}
>> +
>> +static void drm_simple_connector_destroy(struct drm_connector *connector)
>> +{
>> +	drm_connector_cleanup(connector);
>> +	kfree(connector);
>> +}
>> +
>> +static const struct drm_connector_funcs drm_simple_connector_funcs = {
>> +	.reset = drm_atomic_helper_connector_reset,
>> +	.fill_modes = drm_simple_connector_fill_modes,
>> +	.destroy = drm_simple_connector_destroy,
>> +	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
>> +	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
>> +};
>> +
>> +/**
>> + * drm_simple_connector_create - Create a connector with one static mode
>> + * @dev: DRM device
>> + * @connector_type: Connector type
>> + * @mode: Supported display mode
>> + * @rotation: Initial @mode rotation in degrees
> 
> We have rotation properties for this, pls don't use degress here.
> 

This rotation represents the way the display is mounted in the casing.
It is configured using a DT property:

Documentation/devicetree/bindings/display/panel/panel.txt:
- rotation:	Display rotation in degrees counter clockwise (0,90,180,270)

In the driver I set up a display mode which is rotated to match how it's
mounted:

static const struct drm_display_mode mode = {
	DRM_SIMPLE_MODE(320, 240, 58, 43),
};

	device_property_read_u32(dev, "rotation", &rotation);

	connector = drm_simple_connector_create(drm,
DRM_MODE_CONNECTOR_VIRTUAL, &mode, rotation);


The display controller is configured to scan out according to this rotation.

It's important that the display mode matches the case mounting because
of fbdev. fbdev userspace has very limited support for rotation. Most
utilities have no support for it.

The work Hans did seems to only care about fbcon and it doesn't support
90/270 hw rotation.

Noralf.

> Also would be good to wire this up with the rotation property Hans added
> recently, for pre-rotated screens (see the kerneldoc for "panel
> orientation" and drm_connector_init_panel_orientation_property()). So that
> userspace knows how to rotate its rendering to make everything look
> correct in the end again.
> 
> 
>> + *
>> + * This function creates a &drm_connector that has one fixed &drm_display_mode
>> + * which will be rotated according to @rotation.
> 
> From a functionality pov this is very close to a panel wrapped into a
> bridge. I think it would be good to differentiate a bit between these two
> cases more. After all there was a really long discussion about how the
> panel stuff does or does not exactly fit for tinydrm, would be good to
> summarize this here and at least point at drm_panel_bridge_add().
> 
> Also would be good to explain in the overview DOC comment that this is a
> fully independent part of the simple helpers, and drivers can use one or
> the other.
> -Daniel
> 
>> + *
>> + * Returns:
>> + * Pointer to connector on success, or ERR_PTR on failure.
>> + */
>> +struct drm_connector *
>> +drm_simple_connector_create(struct drm_device *dev, int connector_type,
>> +			    const struct drm_display_mode *mode,
>> +			    unsigned int rotation)
>> +{
>> +	struct drm_display_mode *mode_dup = NULL;
>> +	struct drm_connector *connector;
>> +	int ret;
>> +
>> +	connector = kzalloc(sizeof(*connector), GFP_KERNEL);
>> +	if (!connector)
>> +		return ERR_PTR(-ENOMEM);
>> +
>> +	drm_connector_helper_add(connector, &drm_simple_connector_hfuncs);
>> +	ret = drm_connector_init(dev, connector, &drm_simple_connector_funcs,
>> +				 connector_type);
>> +	if (ret)
>> +		goto err_free;
>> +
>> +	connector->status = connector_status_connected;
>> +
>> +	mode_dup = drm_mode_duplicate(dev, mode);
>> +	if (!mode_dup) {
>> +		ret = -ENOMEM;
>> +		goto err_cleanup;
>> +	}
>> +
>> +	if (rotation == 90 || rotation == 270) {
>> +		swap(mode_dup->hdisplay, mode_dup->vdisplay);
>> +		swap(mode_dup->hsync_start, mode_dup->vsync_start);
>> +		swap(mode_dup->hsync_end, mode_dup->vsync_end);
>> +		swap(mode_dup->htotal, mode_dup->vtotal);
>> +		swap(mode_dup->width_mm, mode_dup->height_mm);
>> +	} else if (rotation != 0 && rotation != 180) {
>> +		DRM_ERROR("Illegal rotation value %u\n", rotation);
>> +		ret = -EINVAL;
>> +		goto err_cleanup;
>> +	}
>> +
>> +	mode_dup->type |= DRM_MODE_TYPE_PREFERRED;
>> +	if (mode_dup->name[0] == '\0')
>> +		drm_mode_set_name(mode_dup);
>> +
>> +	list_add(&mode_dup->head, &connector->modes);
>> +
>> +	connector->display_info.width_mm = mode_dup->width_mm;
>> +	connector->display_info.height_mm = mode_dup->height_mm;
>> +
>> +	return connector;
>> +
>> +err_cleanup:
>> +	drm_connector_cleanup(connector);
>> +	drm_mode_destroy(dev, mode_dup);
>> +err_free:
>> +	kfree(connector);
>> +
>> +	return ERR_PTR(ret);
>> +}
>> +EXPORT_SYMBOL(drm_simple_connector_create);
>> +
>> +/**
>> + * drm_simple_connector_set_mode_config - Set &drm_mode_config width and height
>> + * @connector: Connector
>> + *
>> + * This function sets the &drm_mode_config min/max width and height based on the
>> + * connector fixed display mode.
>> + */
>> +void drm_simple_connector_set_mode_config(struct drm_connector *connector)
>> +{
>> +	struct drm_mode_config *mode_config = &connector->dev->mode_config;
>> +	struct drm_display_mode *mode;
>> +
>> +	mode = list_first_entry(&connector->modes, struct drm_display_mode, head);
>> +	if (WARN_ON(!mode))
>> +		return;
>> +
>> +	mode_config->min_width = mode->hdisplay;
>> +	mode_config->max_width = mode->hdisplay;
>> +	mode_config->min_height = mode->vdisplay;
>> +	mode_config->max_height = mode->vdisplay;
>> +}
>> +EXPORT_SYMBOL(drm_simple_connector_set_mode_config);
>> +
>>  MODULE_LICENSE("GPL");
>> diff --git a/include/drm/drm_simple_kms_helper.h b/include/drm/drm_simple_kms_helper.h
>> index 451960438a29..ab3d847b7713 100644
>> --- a/include/drm/drm_simple_kms_helper.h
>> +++ b/include/drm/drm_simple_kms_helper.h
>> @@ -182,4 +182,10 @@ int drm_simple_display_pipe_init(struct drm_device *dev,
>>  			const uint64_t *format_modifiers,
>>  			struct drm_connector *connector);
>>  
>> +struct drm_connector *
>> +drm_simple_connector_create(struct drm_device *dev, int connector_type,
>> +			    const struct drm_display_mode *mode,
>> +			    unsigned int rotation);
>> +void drm_simple_connector_set_mode_config(struct drm_connector *connector);
>> +
>>  #endif /* __LINUX_DRM_SIMPLE_KMS_HELPER_H */
>> -- 
>> 2.20.1
>>
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel@lists.freedesktop.org
>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
> 
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH 03/11] drm/simple-kms-helper: Add drm_simple_connector_create()
  2019-01-24 14:38     ` Noralf Trønnes
@ 2019-01-24 14:53       ` Hans de Goede
  2019-01-25 12:05         ` Noralf Trønnes
  0 siblings, 1 reply; 57+ messages in thread
From: Hans de Goede @ 2019-01-24 14:53 UTC (permalink / raw)
  To: Noralf Trønnes, Daniel Vetter; +Cc: david, dri-devel

Hi,

On 24-01-19 15:38, Noralf Trønnes wrote:
> [cc:Hans]
> 
> Den 21.01.2019 10.22, skrev Daniel Vetter:
>> On Sun, Jan 20, 2019 at 12:43:10PM +0100, Noralf Trønnes wrote:
>>> This adds a function that creates a simple connector that has only one
>>> static mode. Additionally add a helper to set &drm_mode_config width
>>> and height from the static mode.
>>>
>>> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
>>> ---
>>>   drivers/gpu/drm/drm_simple_kms_helper.c | 122 ++++++++++++++++++++++++
>>>   include/drm/drm_simple_kms_helper.h     |   6 ++
>>>   2 files changed, 128 insertions(+)
>>>
>>> diff --git a/drivers/gpu/drm/drm_simple_kms_helper.c b/drivers/gpu/drm/drm_simple_kms_helper.c
>>> index 917812448d1b..ca29975afefe 100644
>>> --- a/drivers/gpu/drm/drm_simple_kms_helper.c
>>> +++ b/drivers/gpu/drm/drm_simple_kms_helper.c
>>> @@ -11,6 +11,8 @@
>>>   #include <drm/drm_atomic.h>
>>>   #include <drm/drm_atomic_helper.h>
>>>   #include <drm/drm_crtc_helper.h>
>>> +#include <drm/drm_device.h>
>>> +#include <drm/drm_modes.h>
>>>   #include <drm/drm_plane_helper.h>
>>>   #include <drm/drm_simple_kms_helper.h>
>>>   #include <linux/slab.h>
>>> @@ -299,4 +301,124 @@ int drm_simple_display_pipe_init(struct drm_device *dev,
>>>   }
>>>   EXPORT_SYMBOL(drm_simple_display_pipe_init);
>>>   
>>> +static const struct drm_connector_helper_funcs drm_simple_connector_hfuncs = {
>>> +	/* dummy for the atomic helper */
>>> +};
>>> +
>>> +static int drm_simple_connector_fill_modes(struct drm_connector *connector,
>>> +					   uint32_t maxX, uint32_t maxY)
>>> +{
>>> +	return 1;
>>> +}
>>> +
>>> +static void drm_simple_connector_destroy(struct drm_connector *connector)
>>> +{
>>> +	drm_connector_cleanup(connector);
>>> +	kfree(connector);
>>> +}
>>> +
>>> +static const struct drm_connector_funcs drm_simple_connector_funcs = {
>>> +	.reset = drm_atomic_helper_connector_reset,
>>> +	.fill_modes = drm_simple_connector_fill_modes,
>>> +	.destroy = drm_simple_connector_destroy,
>>> +	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
>>> +	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
>>> +};
>>> +
>>> +/**
>>> + * drm_simple_connector_create - Create a connector with one static mode
>>> + * @dev: DRM device
>>> + * @connector_type: Connector type
>>> + * @mode: Supported display mode
>>> + * @rotation: Initial @mode rotation in degrees
>>
>> We have rotation properties for this, pls don't use degress here.
>>
> 
> This rotation represents the way the display is mounted in the casing.
> It is configured using a DT property:
> 
> Documentation/devicetree/bindings/display/panel/panel.txt:
> - rotation:	Display rotation in degrees counter clockwise (0,90,180,270)
> 
> In the driver I set up a display mode which is rotated to match how it's
> mounted:
> 
> static const struct drm_display_mode mode = {
> 	DRM_SIMPLE_MODE(320, 240, 58, 43),
> };
> 
> 	device_property_read_u32(dev, "rotation", &rotation);
> 
> 	connector = drm_simple_connector_create(drm,
> DRM_MODE_CONNECTOR_VIRTUAL, &mode, rotation);
> 
> 
> The display controller is configured to scan out according to this rotation.

That sounds wrong, this sounds like you're trying to hide the fact
that the LCD display is not mounted upright from userspace and
transparently deal with this. This is what I wanted to do at
first too, but in the end it turns out that that has a bunch
of issues.

This was extensively discussed and it was decided that this is not
something which we want to do because it gets us into trouble with
things like overlay planes, etc. Also many devices only support
90 / 270 degrees rotation if the framebuffer is tiled in a specific
way, so if we do the rotation transparently, what do we do then
if userspace attaches an incompatible framebuffer to the CRTC?

Instead the decision was made to provide a property on the drm-connector
which tells userspace that the LCD panel is not mounted upright and then
let userspace deal with this.

> It's important that the display mode matches the case mounting because
> of fbdev. fbdev userspace has very limited support for rotation. Most
> utilities have no support for it.

Right, so maybe it is time for userspace to move to the kms API already?

Note that that will not fix things by itself, but at least it makes
the info that the display is not upright available to userspace.

Note I've already patched both plymouth and mutter to use the
drm-connector property for this.

> The work Hans did seems to only care about fbcon

Since fbcon still uses fbdev, and since it already supported
rendering the console rotated I added some glue code to make
it automatically do the right thing for non-upright LCD-panels.

As mentioned I did also fix the userspace kms apps which most
distros use as their default desktop components.

> and it doesn't support 90/270 hw rotation.

Right, because of the framebuffer tiling thing.

So building on top of my work, how this should work is that
the modeline for the display reflect the actual hardware LCD
modeline, so if it is say a 720x1280 portrait screen mounted
in landscape mode, then the modeline will be 720x1280 as that
is what is actually going over the wire to the panel/display.

And the device-tree rotation property can be passed to
drm_connector_init_panel_orientation_property() to let
fbcon and kms API user know they need to render rotated.

Note that kms API users can use hardware rotation to deal
with this if they so desire.

Regards,

Hans







> 
> Noralf.
> 
>> Also would be good to wire this up with the rotation property Hans added
>> recently, for pre-rotated screens (see the kerneldoc for "panel
>> orientation" and drm_connector_init_panel_orientation_property()). So that
>> userspace knows how to rotate its rendering to make everything look
>> correct in the end again.
>>
>>
>>> + *
>>> + * This function creates a &drm_connector that has one fixed &drm_display_mode
>>> + * which will be rotated according to @rotation.
>>
>>  From a functionality pov this is very close to a panel wrapped into a
>> bridge. I think it would be good to differentiate a bit between these two
>> cases more. After all there was a really long discussion about how the
>> panel stuff does or does not exactly fit for tinydrm, would be good to
>> summarize this here and at least point at drm_panel_bridge_add().
>>
>> Also would be good to explain in the overview DOC comment that this is a
>> fully independent part of the simple helpers, and drivers can use one or
>> the other.
>> -Daniel
>>
>>> + *
>>> + * Returns:
>>> + * Pointer to connector on success, or ERR_PTR on failure.
>>> + */
>>> +struct drm_connector *
>>> +drm_simple_connector_create(struct drm_device *dev, int connector_type,
>>> +			    const struct drm_display_mode *mode,
>>> +			    unsigned int rotation)
>>> +{
>>> +	struct drm_display_mode *mode_dup = NULL;
>>> +	struct drm_connector *connector;
>>> +	int ret;
>>> +
>>> +	connector = kzalloc(sizeof(*connector), GFP_KERNEL);
>>> +	if (!connector)
>>> +		return ERR_PTR(-ENOMEM);
>>> +
>>> +	drm_connector_helper_add(connector, &drm_simple_connector_hfuncs);
>>> +	ret = drm_connector_init(dev, connector, &drm_simple_connector_funcs,
>>> +				 connector_type);
>>> +	if (ret)
>>> +		goto err_free;
>>> +
>>> +	connector->status = connector_status_connected;
>>> +
>>> +	mode_dup = drm_mode_duplicate(dev, mode);
>>> +	if (!mode_dup) {
>>> +		ret = -ENOMEM;
>>> +		goto err_cleanup;
>>> +	}
>>> +
>>> +	if (rotation == 90 || rotation == 270) {
>>> +		swap(mode_dup->hdisplay, mode_dup->vdisplay);
>>> +		swap(mode_dup->hsync_start, mode_dup->vsync_start);
>>> +		swap(mode_dup->hsync_end, mode_dup->vsync_end);
>>> +		swap(mode_dup->htotal, mode_dup->vtotal);
>>> +		swap(mode_dup->width_mm, mode_dup->height_mm);
>>> +	} else if (rotation != 0 && rotation != 180) {
>>> +		DRM_ERROR("Illegal rotation value %u\n", rotation);
>>> +		ret = -EINVAL;
>>> +		goto err_cleanup;
>>> +	}
>>> +
>>> +	mode_dup->type |= DRM_MODE_TYPE_PREFERRED;
>>> +	if (mode_dup->name[0] == '\0')
>>> +		drm_mode_set_name(mode_dup);
>>> +
>>> +	list_add(&mode_dup->head, &connector->modes);
>>> +
>>> +	connector->display_info.width_mm = mode_dup->width_mm;
>>> +	connector->display_info.height_mm = mode_dup->height_mm;
>>> +
>>> +	return connector;
>>> +
>>> +err_cleanup:
>>> +	drm_connector_cleanup(connector);
>>> +	drm_mode_destroy(dev, mode_dup);
>>> +err_free:
>>> +	kfree(connector);
>>> +
>>> +	return ERR_PTR(ret);
>>> +}
>>> +EXPORT_SYMBOL(drm_simple_connector_create);
>>> +
>>> +/**
>>> + * drm_simple_connector_set_mode_config - Set &drm_mode_config width and height
>>> + * @connector: Connector
>>> + *
>>> + * This function sets the &drm_mode_config min/max width and height based on the
>>> + * connector fixed display mode.
>>> + */
>>> +void drm_simple_connector_set_mode_config(struct drm_connector *connector)
>>> +{
>>> +	struct drm_mode_config *mode_config = &connector->dev->mode_config;
>>> +	struct drm_display_mode *mode;
>>> +
>>> +	mode = list_first_entry(&connector->modes, struct drm_display_mode, head);
>>> +	if (WARN_ON(!mode))
>>> +		return;
>>> +
>>> +	mode_config->min_width = mode->hdisplay;
>>> +	mode_config->max_width = mode->hdisplay;
>>> +	mode_config->min_height = mode->vdisplay;
>>> +	mode_config->max_height = mode->vdisplay;
>>> +}
>>> +EXPORT_SYMBOL(drm_simple_connector_set_mode_config);
>>> +
>>>   MODULE_LICENSE("GPL");
>>> diff --git a/include/drm/drm_simple_kms_helper.h b/include/drm/drm_simple_kms_helper.h
>>> index 451960438a29..ab3d847b7713 100644
>>> --- a/include/drm/drm_simple_kms_helper.h
>>> +++ b/include/drm/drm_simple_kms_helper.h
>>> @@ -182,4 +182,10 @@ int drm_simple_display_pipe_init(struct drm_device *dev,
>>>   			const uint64_t *format_modifiers,
>>>   			struct drm_connector *connector);
>>>   
>>> +struct drm_connector *
>>> +drm_simple_connector_create(struct drm_device *dev, int connector_type,
>>> +			    const struct drm_display_mode *mode,
>>> +			    unsigned int rotation);
>>> +void drm_simple_connector_set_mode_config(struct drm_connector *connector);
>>> +
>>>   #endif /* __LINUX_DRM_SIMPLE_KMS_HELPER_H */
>>> -- 
>>> 2.20.1
>>>
>>> _______________________________________________
>>> dri-devel mailing list
>>> dri-devel@lists.freedesktop.org
>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: devm actions and hw clenaup (was Re: [PATCH 01/11] drm: Add devm_drm_dev_init/register)
  2019-01-24 10:43                 ` devm actions and hw clenaup (was Re: [PATCH 01/11] drm: Add devm_drm_dev_init/register) Daniel Vetter
@ 2019-01-24 17:46                   ` Greg KH
  2019-01-24 17:57                     ` Daniel Vetter
  0 siblings, 1 reply; 57+ messages in thread
From: Greg KH @ 2019-01-24 17:46 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: David Lechner, dri-devel, Rafael J. Wysocki

On Thu, Jan 24, 2019 at 11:43:12AM +0100, Daniel Vetter wrote:
> On Wed, Jan 23, 2019 at 11:54:07AM +0100, Noralf Trønnes wrote:
> > 
> > 
> > Den 22.01.2019 20.30, skrev Daniel Vetter:
> > > On Tue, Jan 22, 2019 at 8:07 PM Noralf Trønnes <noralf@tronnes.org> wrote:
> > >>
> > >>
> > >>
> > >> Den 22.01.2019 10.32, skrev Daniel Vetter:
> > >>> On Mon, Jan 21, 2019 at 01:21:46PM +0100, Noralf Trønnes wrote:
> > >>>>
> > >>>>
> > >>>> Den 21.01.2019 10.55, skrev Daniel Vetter:
> > >>>>> On Mon, Jan 21, 2019 at 10:10:14AM +0100, Daniel Vetter wrote:
> > >>>>>> On Sun, Jan 20, 2019 at 12:43:08PM +0100, Noralf Trønnes wrote:
> > >>>>>>> This adds resource managed (devres) versions of drm_dev_init() and
> > >>>>>>> drm_dev_register().
> > >>>>>>>
> > >>>>>>> Also added is devm_drm_dev_register_with_fbdev() which sets up generic
> > >>>>>>> fbdev emulation as well.
> > >>>>>>>
> > >>>>>>> devm_drm_dev_register() isn't exported since there are no users.
> > >>>>>>>
> > >>>>>>> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
> > >>>>>>
> > >>>>
> > >>>> <snip>
> > >>>>
> > >>>>>>> diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
> > >>>>>>> index 381581b01d48..12129772be45 100644
> > >>>>>>> --- a/drivers/gpu/drm/drm_drv.c
> > >>>>>>> +++ b/drivers/gpu/drm/drm_drv.c
> > >>>>>>> @@ -36,6 +36,7 @@
> > >>>>>>>
> > >>>>>>>  #include <drm/drm_client.h>
> > >>>>>>>  #include <drm/drm_drv.h>
> > >>>>>>> +#include <drm/drm_fb_helper.h>
> > >>>>>>>  #include <drm/drmP.h>
> > >>>>>>>
> > >>>>>>>  #include "drm_crtc_internal.h"
> > >>>>>>> @@ -871,6 +872,111 @@ void drm_dev_unregister(struct drm_device *dev)
> > >>>>>>>  }
> > >>>>>>>  EXPORT_SYMBOL(drm_dev_unregister);
> > >>>>>>>
> > >>>>>>> +static void devm_drm_dev_init_release(void *data)
> > >>>>>>> +{
> > >>>>>>> + drm_dev_put(data);
> > >>>>>
> > >>>>> We need drm_dev_unplug() here, or this isn't safe.
> > >>>>
> > >>>> This function is only used to cover the error path if probe fails before
> > >>>> devm_drm_dev_register() is called. devm_drm_dev_register_release() is
> > >>>> the one that calls unplug. There are comments about this in the functions.
> > >>>
> > >>> I think I get a prize for being ignorant and blind :-/
> > >>>
> > >>>>
> > >>>>>
> > >>>>>>> +}
> > >>>>>>> +
> > >>>>>>> +/**
> > >>>>>>> + * devm_drm_dev_init - Resource managed drm_dev_init()
> > >>>>>>> + * @parent: Parent device object
> > >>>>>>> + * @dev: DRM device
> > >>>>>>> + * @driver: DRM driver
> > >>>>>>> + *
> > >>>>>>> + * Managed drm_dev_init(). The DRM device initialized with this function is
> > >>>>>>> + * automatically released on driver detach. You must supply a
> > >>>>>
> > >>>>> I think a bit more clarity here would be good:
> > >>>>>
> > >>>>> "... automatically released on driver unbind by callind drm_dev_unplug()."
> > >>>>>
> > >>>>>>> + * &drm_driver.release callback to control the finalization explicitly.
> > >>>>>
> > >>>>> I think a loud warning for these is in order:
> > >>>>>
> > >>>>> "WARNING:
> > >>>>>
> > >>>>> "In generally it is unsafe to use devm functions for drm structures
> > >>>>> because the lifetimes of &drm_device and the underlying &device do not
> > >>>>> match. This here works because it doesn't immediately free anything, but
> > >>>>> only calls drm_dev_unplug(), which internally decrements the &drm_device
> > >>>>> refcount through drm_dev_put().
> > >>>>>
> > >>>>> "All other drm structures must still be explicitly released in the
> > >>>>> &drm_driver.release callback."
> > >>>>>
> > >>>>> While thinking about this I just realized that with this design we have no
> > >>>>> good place to call drm_atomic_helper_shutdown(). Which we need to, or all
> > >>>>> kinds of things will leak badly (connectors, fb, ...), but there's no
> > >>>>> place to call it:
> > >>>>> - unbind is too early, since we haven't yet called drm_dev_unplug, and the
> > >>>>>   drm_dev_unregister in there must be called _before_ we start to shut
> > >>>>>   down anything.
> > >>>>> - drm_driver.release is way too late.
> > >>>>>
> > >>>>> Ofc for a real hotunplug there's no point in shutting down the hw (it's
> > >>>>> already gone), but for a driver unload/unbind it would be nice if this
> > >>>>> happens automatically and in the right order.
> > >>>>>
> > >>>>> So not sure what to do here really.
> > >>>>
> > >>>> How about this change: (it breaks the rule of pulling helpers into the
> > >>>> core, so maybe we should put the devm_ functions into the simple KMS
> > >>>> helper instead?)
> > >>>
> > >>> Yeah smells a bit much like midlayer ... What would work is having a pile
> > >>> more devm_ helper functions, so that we onion-unwrap everything correctly,
> > >>> and in the right order. So:
> > >>>
> > >>> - devm_drm_dev_init (always does a drm_dev_put())
> > >>>
> > >>> - devm_drm_poll_enable (shuts down the poll helper with a devm action)
> > >>>
> > >>> - devm_drm_mode_config_reset (does an atomic_helper_shutdown() as it's cleanup action)
> > >>>
> > >>> - devm_drm_dev_register (grabs an additional drm_dev_get() reference so it
> > >>>   can call drm_dev_unplug() unconditionally).
> > >>>
> > >>
> > >> Beautiful! I really like this, it's very flexible.
> > >>
> > >> Where should devm_drm_mode_config_reset() live? It will pull in the
> > >> atomic helper...
> > > 
> > > I think a new drm_devm.c helper would be nice for all this stuff.
> > > Especially since you can't freely mix devm-based setup/cleanup with
> > > normal cleanup I think it'd be good to have it all together in one
> > > place. And perhaps even a code example in the DOC: overview.
> > > 
> > >>> We'd need to make sure some of the cleanup actions dtrt when the device is
> > >>> gone, but I think we can achieve that by liberally sprinkling
> > >>> drm_dev_enter/exit over them, e.g. the the cleanup action for
> > >>> drm_mode_config_reset would be:
> > >>>
> > >>> {
> > >>>       if (drm_dev_enter())
> > >>>               return;
> > >>>
> > >>>       drm_atomic_helper_shutdown();
> > >>>
> > >>>       drm_dev_exit();
> > >>> }
> > >>>
> > >>
> > >> drm_dev_enter() can only be used to check whether the drm_device is
> > >> registered or not, it doesn't say anything about the state of the parent
> > >> device.
> > >>
> > >> All we know is that the device is being unbound from the driver, we
> > >> don't know if it's the device that's being removed or if it's the driver
> > >> that's unregistered.
> > > 
> > > You're right, both paths will have called drm_dev_unplug by then.
> > > Silly me. I really liked my idea :-)
> > > 
> > >> I have looked at the various call chains:
> > >>
> > >> driver_unregister ->
> > >>     bus_remove_driver ->
> > >>         driver_detach ->
> > >>             device_release_driver_internal
> > >>
> > >> device_unregister ->
> > >>     device_del ->
> > >>         bus_remove_device ->
> > >>             device_release_driver ->
> > >>                 device_release_driver_internal
> > >>
> > >> sysfs: unbind_store ->
> > >>     device_release_driver ->
> > >>         device_release_driver_internal
> > >>
> > >> The only way I've found to differentiate between these in a cleanup
> > >> action is that device_del() uses the bus notifier to signal
> > >> BUS_NOTIFY_DEL_DEVICE before calling bus_remove_device(). Such a
> > >> notifier could be used to set a drm_device->parent_removed flag.
> > > 
> > > Hm, this might upset Greg KH's code taste ... maybe there's a better
> > > way to do this, but best to prototype a patch with this, send it to
> > > him and ask how to :-)
> > > 
> > 
> > I'll leave this to the one that needs it. The tinydrm drivers doesn't
> > need to touch hw after DRM unregister.
> > 
> > >> Why is it necessary to call drm_atomic_helper_shutdown() here? Doesn't
> > >> everything get disabled when userspace closes? It does in my tinydrm
> > >> world :-)
> > > 
> > > Iirc fbdev/fbcon can result in leaks ... at least we've had patches
> > > where drivers leaked drm_connector and drm_framebuffer objects, and
> > > they've been fixed by calling drm_atomic_helper_shutdown() in the
> > > unload path. Maybe this is cargo-culting, but it goes way back to
> > > pre-atomic, where drivers called drm_helper_force_disable_all().
> > > 
> > > If you try to move the fbcon to your tinydrm drivers (con2fb is
> > > apparently the cmdline tool you need, never tried it, I only switch
> > > the kernel's console between fbcon and dummycon and back, not what
> > > fbcon drivers itself), then I think you should be able to reproduce.
> > > And maybe you have a better idea how to deal with this all.
> > > 
> > > Note also that there's been proposals floating around to only close an
> > > drm_framebuffer, not also remove it (like the current RMFB ioctl
> > > does), with that closing userspace would not necessarily lead to a
> > > full cleanup.
> > > 
> > > Another thing (which doesn't apply to drm_simple_display_pipe drivers)
> > > is if you have the display on, but no planes showing (i.e. all black).
> > > Then all the fbs will be cleaned up, but drm_connector will be
> > > leaking. That's a case where you need drm_atomic_helper_shutdown()
> > > even if fbcon/fbdev isn't even enabled.
> > > 
> > 
> > Ok, this means that I don't need to call drm_atomic_helper_shutdown() in
> > tinydrm. DRM userspace disables the pipe on close and the generic fbdev
> > emulation also releases everything.
> > Even so, maybe I should use devm_drm_mode_config_reset() after all to
> > keep drivers uniform, to avoid confusion: why doesn't he use it?
> 
> Hm maybe there is an official way to solve this, pulling in Greg+Rafael.
> 
> Super short summary: We want to start using devm actions to clean up drm
> drivers. Here's the problem:
> - For a driver unload/unbind without hotunplug, we want to properly clean
>   up the hardware and shut it all down.

Then do it on probe/disconnect.

> - But if the device is unplugged already, that's probably not the best
>   idea, and we only want to clean up the kernel's resources/allocations.

Again, probe/disconnect will be called either way.

But as you note, memory will NOT be freed by the devm stuff if you
manually unbind a driver from a device.

So don't touch hardware there, it's not going to work :)

> What's the recommendation here? I see a few options:
> 
> - Make sure everything can deal with this properly. Hotunplug can happen
>   anytime, so there's a race no matter what.

Yes.

> - Check with the device model whether the struct device is disappearing or
>   whether we're just dealing with a driver unbind (no idea how to do
>   that), and act accordingly.

You don't know that, sorry.  Just do any hardware stuff on disconnect.
Assuming your hardware is still present :)

> - Fundamental question: Touching the hw from devm actions, is that ok? If
>   not, then the pretty nifty plan laid out in this thread wont work.

Nope, that's not going to work, the device could either be long gone, or
you will not be called due to unbind happening from userspace.

But really, unbind from userspace is very very rare, it's a developer
thing mostly.  Oh and a virtual driver thing, but those people are crazy
:)

> - Something completely different?

Do it in disconnect :)

thanks,

greg k-h
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: devm actions and hw clenaup (was Re: [PATCH 01/11] drm: Add devm_drm_dev_init/register)
  2019-01-24 17:46                   ` Greg KH
@ 2019-01-24 17:57                     ` Daniel Vetter
  2019-01-29 14:34                       ` Noralf Trønnes
  0 siblings, 1 reply; 57+ messages in thread
From: Daniel Vetter @ 2019-01-24 17:57 UTC (permalink / raw)
  To: Greg KH; +Cc: David Lechner, dri-devel, Rafael J. Wysocki

On Thu, Jan 24, 2019 at 6:46 PM Greg KH <gregkh@linuxfoundation.org> wrote:
>
> On Thu, Jan 24, 2019 at 11:43:12AM +0100, Daniel Vetter wrote:
> > On Wed, Jan 23, 2019 at 11:54:07AM +0100, Noralf Trønnes wrote:
> > >
> > >
> > > Den 22.01.2019 20.30, skrev Daniel Vetter:
> > > > On Tue, Jan 22, 2019 at 8:07 PM Noralf Trønnes <noralf@tronnes.org> wrote:
> > > >>
> > > >>
> > > >>
> > > >> Den 22.01.2019 10.32, skrev Daniel Vetter:
> > > >>> On Mon, Jan 21, 2019 at 01:21:46PM +0100, Noralf Trønnes wrote:
> > > >>>>
> > > >>>>
> > > >>>> Den 21.01.2019 10.55, skrev Daniel Vetter:
> > > >>>>> On Mon, Jan 21, 2019 at 10:10:14AM +0100, Daniel Vetter wrote:
> > > >>>>>> On Sun, Jan 20, 2019 at 12:43:08PM +0100, Noralf Trønnes wrote:
> > > >>>>>>> This adds resource managed (devres) versions of drm_dev_init() and
> > > >>>>>>> drm_dev_register().
> > > >>>>>>>
> > > >>>>>>> Also added is devm_drm_dev_register_with_fbdev() which sets up generic
> > > >>>>>>> fbdev emulation as well.
> > > >>>>>>>
> > > >>>>>>> devm_drm_dev_register() isn't exported since there are no users.
> > > >>>>>>>
> > > >>>>>>> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
> > > >>>>>>
> > > >>>>
> > > >>>> <snip>
> > > >>>>
> > > >>>>>>> diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
> > > >>>>>>> index 381581b01d48..12129772be45 100644
> > > >>>>>>> --- a/drivers/gpu/drm/drm_drv.c
> > > >>>>>>> +++ b/drivers/gpu/drm/drm_drv.c
> > > >>>>>>> @@ -36,6 +36,7 @@
> > > >>>>>>>
> > > >>>>>>>  #include <drm/drm_client.h>
> > > >>>>>>>  #include <drm/drm_drv.h>
> > > >>>>>>> +#include <drm/drm_fb_helper.h>
> > > >>>>>>>  #include <drm/drmP.h>
> > > >>>>>>>
> > > >>>>>>>  #include "drm_crtc_internal.h"
> > > >>>>>>> @@ -871,6 +872,111 @@ void drm_dev_unregister(struct drm_device *dev)
> > > >>>>>>>  }
> > > >>>>>>>  EXPORT_SYMBOL(drm_dev_unregister);
> > > >>>>>>>
> > > >>>>>>> +static void devm_drm_dev_init_release(void *data)
> > > >>>>>>> +{
> > > >>>>>>> + drm_dev_put(data);
> > > >>>>>
> > > >>>>> We need drm_dev_unplug() here, or this isn't safe.
> > > >>>>
> > > >>>> This function is only used to cover the error path if probe fails before
> > > >>>> devm_drm_dev_register() is called. devm_drm_dev_register_release() is
> > > >>>> the one that calls unplug. There are comments about this in the functions.
> > > >>>
> > > >>> I think I get a prize for being ignorant and blind :-/
> > > >>>
> > > >>>>
> > > >>>>>
> > > >>>>>>> +}
> > > >>>>>>> +
> > > >>>>>>> +/**
> > > >>>>>>> + * devm_drm_dev_init - Resource managed drm_dev_init()
> > > >>>>>>> + * @parent: Parent device object
> > > >>>>>>> + * @dev: DRM device
> > > >>>>>>> + * @driver: DRM driver
> > > >>>>>>> + *
> > > >>>>>>> + * Managed drm_dev_init(). The DRM device initialized with this function is
> > > >>>>>>> + * automatically released on driver detach. You must supply a
> > > >>>>>
> > > >>>>> I think a bit more clarity here would be good:
> > > >>>>>
> > > >>>>> "... automatically released on driver unbind by callind drm_dev_unplug()."
> > > >>>>>
> > > >>>>>>> + * &drm_driver.release callback to control the finalization explicitly.
> > > >>>>>
> > > >>>>> I think a loud warning for these is in order:
> > > >>>>>
> > > >>>>> "WARNING:
> > > >>>>>
> > > >>>>> "In generally it is unsafe to use devm functions for drm structures
> > > >>>>> because the lifetimes of &drm_device and the underlying &device do not
> > > >>>>> match. This here works because it doesn't immediately free anything, but
> > > >>>>> only calls drm_dev_unplug(), which internally decrements the &drm_device
> > > >>>>> refcount through drm_dev_put().
> > > >>>>>
> > > >>>>> "All other drm structures must still be explicitly released in the
> > > >>>>> &drm_driver.release callback."
> > > >>>>>
> > > >>>>> While thinking about this I just realized that with this design we have no
> > > >>>>> good place to call drm_atomic_helper_shutdown(). Which we need to, or all
> > > >>>>> kinds of things will leak badly (connectors, fb, ...), but there's no
> > > >>>>> place to call it:
> > > >>>>> - unbind is too early, since we haven't yet called drm_dev_unplug, and the
> > > >>>>>   drm_dev_unregister in there must be called _before_ we start to shut
> > > >>>>>   down anything.
> > > >>>>> - drm_driver.release is way too late.
> > > >>>>>
> > > >>>>> Ofc for a real hotunplug there's no point in shutting down the hw (it's
> > > >>>>> already gone), but for a driver unload/unbind it would be nice if this
> > > >>>>> happens automatically and in the right order.
> > > >>>>>
> > > >>>>> So not sure what to do here really.
> > > >>>>
> > > >>>> How about this change: (it breaks the rule of pulling helpers into the
> > > >>>> core, so maybe we should put the devm_ functions into the simple KMS
> > > >>>> helper instead?)
> > > >>>
> > > >>> Yeah smells a bit much like midlayer ... What would work is having a pile
> > > >>> more devm_ helper functions, so that we onion-unwrap everything correctly,
> > > >>> and in the right order. So:
> > > >>>
> > > >>> - devm_drm_dev_init (always does a drm_dev_put())
> > > >>>
> > > >>> - devm_drm_poll_enable (shuts down the poll helper with a devm action)
> > > >>>
> > > >>> - devm_drm_mode_config_reset (does an atomic_helper_shutdown() as it's cleanup action)
> > > >>>
> > > >>> - devm_drm_dev_register (grabs an additional drm_dev_get() reference so it
> > > >>>   can call drm_dev_unplug() unconditionally).
> > > >>>
> > > >>
> > > >> Beautiful! I really like this, it's very flexible.
> > > >>
> > > >> Where should devm_drm_mode_config_reset() live? It will pull in the
> > > >> atomic helper...
> > > >
> > > > I think a new drm_devm.c helper would be nice for all this stuff.
> > > > Especially since you can't freely mix devm-based setup/cleanup with
> > > > normal cleanup I think it'd be good to have it all together in one
> > > > place. And perhaps even a code example in the DOC: overview.
> > > >
> > > >>> We'd need to make sure some of the cleanup actions dtrt when the device is
> > > >>> gone, but I think we can achieve that by liberally sprinkling
> > > >>> drm_dev_enter/exit over them, e.g. the the cleanup action for
> > > >>> drm_mode_config_reset would be:
> > > >>>
> > > >>> {
> > > >>>       if (drm_dev_enter())
> > > >>>               return;
> > > >>>
> > > >>>       drm_atomic_helper_shutdown();
> > > >>>
> > > >>>       drm_dev_exit();
> > > >>> }
> > > >>>
> > > >>
> > > >> drm_dev_enter() can only be used to check whether the drm_device is
> > > >> registered or not, it doesn't say anything about the state of the parent
> > > >> device.
> > > >>
> > > >> All we know is that the device is being unbound from the driver, we
> > > >> don't know if it's the device that's being removed or if it's the driver
> > > >> that's unregistered.
> > > >
> > > > You're right, both paths will have called drm_dev_unplug by then.
> > > > Silly me. I really liked my idea :-)
> > > >
> > > >> I have looked at the various call chains:
> > > >>
> > > >> driver_unregister ->
> > > >>     bus_remove_driver ->
> > > >>         driver_detach ->
> > > >>             device_release_driver_internal
> > > >>
> > > >> device_unregister ->
> > > >>     device_del ->
> > > >>         bus_remove_device ->
> > > >>             device_release_driver ->
> > > >>                 device_release_driver_internal
> > > >>
> > > >> sysfs: unbind_store ->
> > > >>     device_release_driver ->
> > > >>         device_release_driver_internal
> > > >>
> > > >> The only way I've found to differentiate between these in a cleanup
> > > >> action is that device_del() uses the bus notifier to signal
> > > >> BUS_NOTIFY_DEL_DEVICE before calling bus_remove_device(). Such a
> > > >> notifier could be used to set a drm_device->parent_removed flag.
> > > >
> > > > Hm, this might upset Greg KH's code taste ... maybe there's a better
> > > > way to do this, but best to prototype a patch with this, send it to
> > > > him and ask how to :-)
> > > >
> > >
> > > I'll leave this to the one that needs it. The tinydrm drivers doesn't
> > > need to touch hw after DRM unregister.
> > >
> > > >> Why is it necessary to call drm_atomic_helper_shutdown() here? Doesn't
> > > >> everything get disabled when userspace closes? It does in my tinydrm
> > > >> world :-)
> > > >
> > > > Iirc fbdev/fbcon can result in leaks ... at least we've had patches
> > > > where drivers leaked drm_connector and drm_framebuffer objects, and
> > > > they've been fixed by calling drm_atomic_helper_shutdown() in the
> > > > unload path. Maybe this is cargo-culting, but it goes way back to
> > > > pre-atomic, where drivers called drm_helper_force_disable_all().
> > > >
> > > > If you try to move the fbcon to your tinydrm drivers (con2fb is
> > > > apparently the cmdline tool you need, never tried it, I only switch
> > > > the kernel's console between fbcon and dummycon and back, not what
> > > > fbcon drivers itself), then I think you should be able to reproduce.
> > > > And maybe you have a better idea how to deal with this all.
> > > >
> > > > Note also that there's been proposals floating around to only close an
> > > > drm_framebuffer, not also remove it (like the current RMFB ioctl
> > > > does), with that closing userspace would not necessarily lead to a
> > > > full cleanup.
> > > >
> > > > Another thing (which doesn't apply to drm_simple_display_pipe drivers)
> > > > is if you have the display on, but no planes showing (i.e. all black).
> > > > Then all the fbs will be cleaned up, but drm_connector will be
> > > > leaking. That's a case where you need drm_atomic_helper_shutdown()
> > > > even if fbcon/fbdev isn't even enabled.
> > > >
> > >
> > > Ok, this means that I don't need to call drm_atomic_helper_shutdown() in
> > > tinydrm. DRM userspace disables the pipe on close and the generic fbdev
> > > emulation also releases everything.
> > > Even so, maybe I should use devm_drm_mode_config_reset() after all to
> > > keep drivers uniform, to avoid confusion: why doesn't he use it?
> >
> > Hm maybe there is an official way to solve this, pulling in Greg+Rafael.
> >
> > Super short summary: We want to start using devm actions to clean up drm
> > drivers. Here's the problem:
> > - For a driver unload/unbind without hotunplug, we want to properly clean
> >   up the hardware and shut it all down.
>
> Then do it on probe/disconnect.
>
> > - But if the device is unplugged already, that's probably not the best
> >   idea, and we only want to clean up the kernel's resources/allocations.
>
> Again, probe/disconnect will be called either way.
>
> But as you note, memory will NOT be freed by the devm stuff if you
> manually unbind a driver from a device.
>
> So don't touch hardware there, it's not going to work :)
>
> > What's the recommendation here? I see a few options:
> >
> > - Make sure everything can deal with this properly. Hotunplug can happen
> >   anytime, so there's a race no matter what.
>
> Yes.
>
> > - Check with the device model whether the struct device is disappearing or
> >   whether we're just dealing with a driver unbind (no idea how to do
> >   that), and act accordingly.
>
> You don't know that, sorry.  Just do any hardware stuff on disconnect.
> Assuming your hardware is still present :)
>
> > - Fundamental question: Touching the hw from devm actions, is that ok? If
> >   not, then the pretty nifty plan laid out in this thread wont work.
>
> Nope, that's not going to work, the device could either be long gone, or
> you will not be called due to unbind happening from userspace.
>
> But really, unbind from userspace is very very rare, it's a developer
> thing mostly.  Oh and a virtual driver thing, but those people are crazy
> :)
>
> > - Something completely different?
>
> Do it in disconnect :)

Ah, I forgot to mention the important constraint :-) disconnect/unbind
should be the following sequence:

1. Unregister all the userspace interfaces (there's a lot of them) and
make sure all the pending ioctls are done so that from now on
userspace sees lots of -EIO (in case it still has fd open, which is
going to be the normal for hotunplug.

2. Shut down hw and all ongoing operations (only relevant for unbind,
but needs to be able to cope with sudden hotunplug on top anyway).

3. Clean up the kernel mess and release everything.

Probe is exactly the other way round, so would perfectly fit into the
devm onion cleanup. See in the commented earlier replies above how
that would match in details, but tldr; if we have to do 2. in
disconnect, then we also have to do 1. in disconnected, and only doing
3. through devm is almost not worth the bother. But if we could do all
three through devm then simple drivers wouldn't even need any
disconnect/unbind callback at all. That's our motivation for trying to
come up with an answer that's not "do it in disconnect". "do it in
disconnect" is how we do it all today already.

Yes we're trying to make tiny drivers even smaller, we have enough
nowadays that this stuff would be worth it :-)

Cheers, Daniel
--
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH 03/11] drm/simple-kms-helper: Add drm_simple_connector_create()
  2019-01-24 14:53       ` Hans de Goede
@ 2019-01-25 12:05         ` Noralf Trønnes
  0 siblings, 0 replies; 57+ messages in thread
From: Noralf Trønnes @ 2019-01-25 12:05 UTC (permalink / raw)
  To: Hans de Goede, Daniel Vetter; +Cc: david, dri-devel



Den 24.01.2019 15.53, skrev Hans de Goede:
> Hi,
> 
> On 24-01-19 15:38, Noralf Trønnes wrote:
>> [cc:Hans]
>>
>> Den 21.01.2019 10.22, skrev Daniel Vetter:
>>> On Sun, Jan 20, 2019 at 12:43:10PM +0100, Noralf Trønnes wrote:
>>>> This adds a function that creates a simple connector that has only one
>>>> static mode. Additionally add a helper to set &drm_mode_config width
>>>> and height from the static mode.
>>>>
>>>> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
>>>> ---
>>>>   drivers/gpu/drm/drm_simple_kms_helper.c | 122
>>>> ++++++++++++++++++++++++
>>>>   include/drm/drm_simple_kms_helper.h     |   6 ++
>>>>   2 files changed, 128 insertions(+)
>>>>
>>>> diff --git a/drivers/gpu/drm/drm_simple_kms_helper.c
>>>> b/drivers/gpu/drm/drm_simple_kms_helper.c
>>>> index 917812448d1b..ca29975afefe 100644
>>>> --- a/drivers/gpu/drm/drm_simple_kms_helper.c
>>>> +++ b/drivers/gpu/drm/drm_simple_kms_helper.c
>>>> @@ -11,6 +11,8 @@
>>>>   #include <drm/drm_atomic.h>
>>>>   #include <drm/drm_atomic_helper.h>
>>>>   #include <drm/drm_crtc_helper.h>
>>>> +#include <drm/drm_device.h>
>>>> +#include <drm/drm_modes.h>
>>>>   #include <drm/drm_plane_helper.h>
>>>>   #include <drm/drm_simple_kms_helper.h>
>>>>   #include <linux/slab.h>
>>>> @@ -299,4 +301,124 @@ int drm_simple_display_pipe_init(struct
>>>> drm_device *dev,
>>>>   }
>>>>   EXPORT_SYMBOL(drm_simple_display_pipe_init);
>>>>   +static const struct drm_connector_helper_funcs
>>>> drm_simple_connector_hfuncs = {
>>>> +    /* dummy for the atomic helper */
>>>> +};
>>>> +
>>>> +static int drm_simple_connector_fill_modes(struct drm_connector
>>>> *connector,
>>>> +                       uint32_t maxX, uint32_t maxY)
>>>> +{
>>>> +    return 1;
>>>> +}
>>>> +
>>>> +static void drm_simple_connector_destroy(struct drm_connector
>>>> *connector)
>>>> +{
>>>> +    drm_connector_cleanup(connector);
>>>> +    kfree(connector);
>>>> +}
>>>> +
>>>> +static const struct drm_connector_funcs drm_simple_connector_funcs = {
>>>> +    .reset = drm_atomic_helper_connector_reset,
>>>> +    .fill_modes = drm_simple_connector_fill_modes,
>>>> +    .destroy = drm_simple_connector_destroy,
>>>> +    .atomic_duplicate_state =
>>>> drm_atomic_helper_connector_duplicate_state,
>>>> +    .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
>>>> +};
>>>> +
>>>> +/**
>>>> + * drm_simple_connector_create - Create a connector with one static
>>>> mode
>>>> + * @dev: DRM device
>>>> + * @connector_type: Connector type
>>>> + * @mode: Supported display mode
>>>> + * @rotation: Initial @mode rotation in degrees
>>>
>>> We have rotation properties for this, pls don't use degress here.
>>>
>>
>> This rotation represents the way the display is mounted in the casing.
>> It is configured using a DT property:
>>
>> Documentation/devicetree/bindings/display/panel/panel.txt:
>> - rotation:    Display rotation in degrees counter clockwise
>> (0,90,180,270)
>>
>> In the driver I set up a display mode which is rotated to match how it's
>> mounted:
>>
>> static const struct drm_display_mode mode = {
>>     DRM_SIMPLE_MODE(320, 240, 58, 43),
>> };
>>
>>     device_property_read_u32(dev, "rotation", &rotation);
>>
>>     connector = drm_simple_connector_create(drm,
>> DRM_MODE_CONNECTOR_VIRTUAL, &mode, rotation);
>>
>>
>> The display controller is configured to scan out according to this
>> rotation.
> 
> That sounds wrong, this sounds like you're trying to hide the fact
> that the LCD display is not mounted upright from userspace and
> transparently deal with this. 

Indeed that's what I'm doing.

>                               This is what I wanted to do at
> first too, but in the end it turns out that that has a bunch
> of issues.
> 
> This was extensively discussed and it was decided that this is not
> something which we want to do because it gets us into trouble with
> things like overlay planes, etc. Also many devices only support
> 90 / 270 degrees rotation if the framebuffer is tiled in a specific
> way, so if we do the rotation transparently, what do we do then
> if userspace attaches an incompatible framebuffer to the CRTC?
> 
> Instead the decision was made to provide a property on the drm-connector
> which tells userspace that the LCD panel is not mounted upright and then
> let userspace deal with this.
> 
>> It's important that the display mode matches the case mounting because
>> of fbdev. fbdev userspace has very limited support for rotation. Most
>> utilities have no support for it.
> 
> Right, so maybe it is time for userspace to move to the kms API already?
> 

These tiny SPI displays are heavily used in the Maker community which is
my main user base. Most are not professionals and fbdev is easy to use,
so I want to avoid crippling fbdev.

> Note that that will not fix things by itself, but at least it makes
> the info that the display is not upright available to userspace.
> 
> Note I've already patched both plymouth and mutter to use the
> drm-connector property for this.
> 
>> The work Hans did seems to only care about fbcon
> 
> Since fbcon still uses fbdev, and since it already supported
> rendering the console rotated I added some glue code to make
> it automatically do the right thing for non-upright LCD-panels.
> 
> As mentioned I did also fix the userspace kms apps which most
> distros use as their default desktop components.
> 
>> and it doesn't support 90/270 hw rotation.
> 
> Right, because of the framebuffer tiling thing.
> 

If we instead apply the rotation after the framebuffer is created, is it
enough to check fb->modifier == DRM_FORMAT_MOD_LINEAR to see if it's ok
to rotate?

My next task after this patchset is to try and move the modesetting code
from drm_fb_helper to drm_client. I see now that I have to take this
rotation into account as well for the future bootsplash client to show
correctly.

Noralf.

> So building on top of my work, how this should work is that
> the modeline for the display reflect the actual hardware LCD
> modeline, so if it is say a 720x1280 portrait screen mounted
> in landscape mode, then the modeline will be 720x1280 as that
> is what is actually going over the wire to the panel/display.
> 
> And the device-tree rotation property can be passed to
> drm_connector_init_panel_orientation_property() to let
> fbcon and kms API user know they need to render rotated.
> 
> Note that kms API users can use hardware rotation to deal
> with this if they so desire.
> 
> Regards,
> 
> Hans
> 
> 
> 
> 
> 
> 
> 
>>
>> Noralf.
>>
>>> Also would be good to wire this up with the rotation property Hans added
>>> recently, for pre-rotated screens (see the kerneldoc for "panel
>>> orientation" and drm_connector_init_panel_orientation_property()). So
>>> that
>>> userspace knows how to rotate its rendering to make everything look
>>> correct in the end again.
>>>
>>>
>>>> + *
>>>> + * This function creates a &drm_connector that has one fixed
>>>> &drm_display_mode
>>>> + * which will be rotated according to @rotation.
>>>
>>>  From a functionality pov this is very close to a panel wrapped into a
>>> bridge. I think it would be good to differentiate a bit between these
>>> two
>>> cases more. After all there was a really long discussion about how the
>>> panel stuff does or does not exactly fit for tinydrm, would be good to
>>> summarize this here and at least point at drm_panel_bridge_add().
>>>
>>> Also would be good to explain in the overview DOC comment that this is a
>>> fully independent part of the simple helpers, and drivers can use one or
>>> the other.
>>> -Daniel
>>>
>>>> + *
>>>> + * Returns:
>>>> + * Pointer to connector on success, or ERR_PTR on failure.
>>>> + */
>>>> +struct drm_connector *
>>>> +drm_simple_connector_create(struct drm_device *dev, int
>>>> connector_type,
>>>> +                const struct drm_display_mode *mode,
>>>> +                unsigned int rotation)
>>>> +{
>>>> +    struct drm_display_mode *mode_dup = NULL;
>>>> +    struct drm_connector *connector;
>>>> +    int ret;
>>>> +
>>>> +    connector = kzalloc(sizeof(*connector), GFP_KERNEL);
>>>> +    if (!connector)
>>>> +        return ERR_PTR(-ENOMEM);
>>>> +
>>>> +    drm_connector_helper_add(connector, &drm_simple_connector_hfuncs);
>>>> +    ret = drm_connector_init(dev, connector,
>>>> &drm_simple_connector_funcs,
>>>> +                 connector_type);
>>>> +    if (ret)
>>>> +        goto err_free;
>>>> +
>>>> +    connector->status = connector_status_connected;
>>>> +
>>>> +    mode_dup = drm_mode_duplicate(dev, mode);
>>>> +    if (!mode_dup) {
>>>> +        ret = -ENOMEM;
>>>> +        goto err_cleanup;
>>>> +    }
>>>> +
>>>> +    if (rotation == 90 || rotation == 270) {
>>>> +        swap(mode_dup->hdisplay, mode_dup->vdisplay);
>>>> +        swap(mode_dup->hsync_start, mode_dup->vsync_start);
>>>> +        swap(mode_dup->hsync_end, mode_dup->vsync_end);
>>>> +        swap(mode_dup->htotal, mode_dup->vtotal);
>>>> +        swap(mode_dup->width_mm, mode_dup->height_mm);
>>>> +    } else if (rotation != 0 && rotation != 180) {
>>>> +        DRM_ERROR("Illegal rotation value %u\n", rotation);
>>>> +        ret = -EINVAL;
>>>> +        goto err_cleanup;
>>>> +    }
>>>> +
>>>> +    mode_dup->type |= DRM_MODE_TYPE_PREFERRED;
>>>> +    if (mode_dup->name[0] == '\0')
>>>> +        drm_mode_set_name(mode_dup);
>>>> +
>>>> +    list_add(&mode_dup->head, &connector->modes);
>>>> +
>>>> +    connector->display_info.width_mm = mode_dup->width_mm;
>>>> +    connector->display_info.height_mm = mode_dup->height_mm;
>>>> +
>>>> +    return connector;
>>>> +
>>>> +err_cleanup:
>>>> +    drm_connector_cleanup(connector);
>>>> +    drm_mode_destroy(dev, mode_dup);
>>>> +err_free:
>>>> +    kfree(connector);
>>>> +
>>>> +    return ERR_PTR(ret);
>>>> +}
>>>> +EXPORT_SYMBOL(drm_simple_connector_create);
>>>> +
>>>> +/**
>>>> + * drm_simple_connector_set_mode_config - Set &drm_mode_config
>>>> width and height
>>>> + * @connector: Connector
>>>> + *
>>>> + * This function sets the &drm_mode_config min/max width and height
>>>> based on the
>>>> + * connector fixed display mode.
>>>> + */
>>>> +void drm_simple_connector_set_mode_config(struct drm_connector
>>>> *connector)
>>>> +{
>>>> +    struct drm_mode_config *mode_config =
>>>> &connector->dev->mode_config;
>>>> +    struct drm_display_mode *mode;
>>>> +
>>>> +    mode = list_first_entry(&connector->modes, struct
>>>> drm_display_mode, head);
>>>> +    if (WARN_ON(!mode))
>>>> +        return;
>>>> +
>>>> +    mode_config->min_width = mode->hdisplay;
>>>> +    mode_config->max_width = mode->hdisplay;
>>>> +    mode_config->min_height = mode->vdisplay;
>>>> +    mode_config->max_height = mode->vdisplay;
>>>> +}
>>>> +EXPORT_SYMBOL(drm_simple_connector_set_mode_config);
>>>> +
>>>>   MODULE_LICENSE("GPL");
>>>> diff --git a/include/drm/drm_simple_kms_helper.h
>>>> b/include/drm/drm_simple_kms_helper.h
>>>> index 451960438a29..ab3d847b7713 100644
>>>> --- a/include/drm/drm_simple_kms_helper.h
>>>> +++ b/include/drm/drm_simple_kms_helper.h
>>>> @@ -182,4 +182,10 @@ int drm_simple_display_pipe_init(struct
>>>> drm_device *dev,
>>>>               const uint64_t *format_modifiers,
>>>>               struct drm_connector *connector);
>>>>   +struct drm_connector *
>>>> +drm_simple_connector_create(struct drm_device *dev, int
>>>> connector_type,
>>>> +                const struct drm_display_mode *mode,
>>>> +                unsigned int rotation);
>>>> +void drm_simple_connector_set_mode_config(struct drm_connector
>>>> *connector);
>>>> +
>>>>   #endif /* __LINUX_DRM_SIMPLE_KMS_HELPER_H */
>>>> -- 
>>>> 2.20.1
>>>>
>>>> _______________________________________________
>>>> dri-devel mailing list
>>>> dri-devel@lists.freedesktop.org
>>>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>>>
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH 11/11] drm/fb-helper: generic: Don't take module ref for fbcon
  2019-01-21  9:05   ` Daniel Vetter
@ 2019-01-28 14:40     ` Noralf Trønnes
  2019-01-29  8:45       ` Daniel Vetter
  0 siblings, 1 reply; 57+ messages in thread
From: Noralf Trønnes @ 2019-01-28 14:40 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: david, dri-devel



Den 21.01.2019 10.05, skrev Daniel Vetter:
> On Sun, Jan 20, 2019 at 12:43:18PM +0100, Noralf Trønnes wrote:
>> It's now safe to let fbcon unbind automatically on fbdev unregister.
>> The crash problem was fixed in commit 2122b40580dd
>> ("fbdev: fbcon: Fix unregister crash when more than one framebuffer")
>>
>> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
>> ---
>>  drivers/gpu/drm/drm_fb_helper.c | 6 ++++--
>>  1 file changed, 4 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
>> index 31fcf94bf825..5d0327f603bb 100644
>> --- a/drivers/gpu/drm/drm_fb_helper.c
>> +++ b/drivers/gpu/drm/drm_fb_helper.c
>> @@ -2999,7 +2999,8 @@ static int drm_fbdev_fb_open(struct fb_info *info, int user)
>>  {
>>  	struct drm_fb_helper *fb_helper = info->par;
>>  
>> -	if (!try_module_get(fb_helper->dev->driver->fops->owner))
>> +	/* No need to take a ref for fbcon because it unbinds on unregister */
>> +	if (user && !try_module_get(fb_helper->dev->driver->fops->owner))
> 
> Module refcount != driver refcount. You can always unbind a driver through
> the sysfs unbind file, no matter whether the module is pinned or not. So I
> think pinng the module is still the right thing to do, just to avoid races
> and fun stuff.
> 
> btw, I looked into making fbdev hotunplug safe (i.e. drm_dev_enter/exit,
> but for fbdev) over the holidays. Fixing that properly for fbdev userspace
> clients looks like serious amounts of work, and I think it's impossible
> for fbcon. Or at least I didn't come up with a workable idea to sprinkle
> the dev_enter/exit stuff around. For the userspace side the basic
> infrastructure with get_fb_info() and put_fb_info() is there already.
> 

fbdev blocks file operations after unregister through this function:

static struct fb_info *file_fb_info(struct file *file)
{
	struct inode *inode = file_inode(file);
	int fbidx = iminor(inode);
	struct fb_info *info = registered_fb[fbidx];

	if (info != file->private_data)
		info = NULL;
	return info;
}

static int do_unregister_framebuffer(struct fb_info *fb_info)
{
...
	registered_fb[fb_info->node] = NULL;
...
}

The only way for fbdev userspace to trigger driver actions is through
mmap and defio. At first I planned to put drm_dev_enter/exit in
drm_fb_helper_dirty_work(), but realised that the driver would need to
do the same in it's dirty function to cover DRM userspace anyways, so I
dropped it. It's the driver's responsibility to protect it's resources.

As for fbcon, yes this is all a spaghetti monster, so better safe than
sorry I guess. And it's just developers that would benefit from this
anyways, not having to unbind fbcon before unlaoding the driver module.

Noralf.

> Cheers, Daniel
> 
>>  		return -ENODEV;
>>  
>>  	return 0;
>> @@ -3009,7 +3010,8 @@ static int drm_fbdev_fb_release(struct fb_info *info, int user)
>>  {
>>  	struct drm_fb_helper *fb_helper = info->par;
>>  
>> -	module_put(fb_helper->dev->driver->fops->owner);
>> +	if (user)
>> +		module_put(fb_helper->dev->driver->fops->owner);
>>  
>>  	return 0;
>>  }
>> -- 
>> 2.20.1
>>
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel@lists.freedesktop.org
>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
> 
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH 04/11] drm/tinydrm: Remove tinydrm_display_pipe_init()
  2019-01-21  9:15   ` Daniel Vetter
@ 2019-01-28 14:46     ` Noralf Trønnes
  0 siblings, 0 replies; 57+ messages in thread
From: Noralf Trønnes @ 2019-01-28 14:46 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: david, dri-devel



Den 21.01.2019 10.15, skrev Daniel Vetter:
> On Sun, Jan 20, 2019 at 12:43:11PM +0100, Noralf Trønnes wrote:
>> Further strip down tinydrm.ko and switch to drm_simple_connector_create().
>>
>> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
>> ---
>>  Documentation/gpu/tinydrm.rst               |   3 -
>>  drivers/gpu/drm/tinydrm/core/Makefile       |   2 +-
>>  drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c | 183 --------------------
>>  drivers/gpu/drm/tinydrm/mipi-dbi.c          |  24 ++-
>>  drivers/gpu/drm/tinydrm/repaper.c           |  16 +-
>>  drivers/gpu/drm/tinydrm/st7586.c            |  19 +-
>>  include/drm/tinydrm/tinydrm.h               |   9 -
>>  7 files changed, 43 insertions(+), 213 deletions(-)
>>  delete mode 100644 drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c
>>
>> diff --git a/Documentation/gpu/tinydrm.rst b/Documentation/gpu/tinydrm.rst
>> index a913644bfc19..1ca726474af4 100644
>> --- a/Documentation/gpu/tinydrm.rst
>> +++ b/Documentation/gpu/tinydrm.rst
>> @@ -17,9 +17,6 @@ Core functionality
>>  .. kernel-doc:: drivers/gpu/drm/tinydrm/core/tinydrm-core.c
>>     :export:
>>  
>> -.. kernel-doc:: drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c
>> -   :export:
>> -
>>  Additional helpers
>>  ==================
>>  
>> diff --git a/drivers/gpu/drm/tinydrm/core/Makefile b/drivers/gpu/drm/tinydrm/core/Makefile
>> index fb221e6f8885..bf2df7326df7 100644
>> --- a/drivers/gpu/drm/tinydrm/core/Makefile
>> +++ b/drivers/gpu/drm/tinydrm/core/Makefile
>> @@ -1,3 +1,3 @@
>> -tinydrm-y := tinydrm-core.o tinydrm-pipe.o tinydrm-helpers.o
>> +tinydrm-y := tinydrm-core.o tinydrm-helpers.o
>>  
>>  obj-$(CONFIG_DRM_TINYDRM) += tinydrm.o
>> diff --git a/drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c b/drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c
>> deleted file mode 100644
>> index 323564329535..000000000000
>> --- a/drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c
>> +++ /dev/null
>> @@ -1,183 +0,0 @@
>> -/*
>> - * Copyright (C) 2016 Noralf Trønnes
>> - *
>> - * This program is free software; you can redistribute it and/or modify
>> - * it under the terms of the GNU General Public License as published by
>> - * the Free Software Foundation; either version 2 of the License, or
>> - * (at your option) any later version.
>> - */
>> -
>> -#include <drm/drm_atomic_helper.h>
>> -#include <drm/drm_crtc_helper.h>
>> -#include <drm/drm_drv.h>
>> -#include <drm/drm_gem_framebuffer_helper.h>
>> -#include <drm/drm_modes.h>
>> -#include <drm/drm_print.h>
>> -#include <drm/tinydrm/tinydrm.h>
>> -
>> -struct tinydrm_connector {
>> -	struct drm_connector base;
>> -	struct drm_display_mode mode;
>> -};
>> -
>> -static inline struct tinydrm_connector *
>> -to_tinydrm_connector(struct drm_connector *connector)
>> -{
>> -	return container_of(connector, struct tinydrm_connector, base);
>> -}
>> -
>> -static int tinydrm_connector_get_modes(struct drm_connector *connector)
>> -{
>> -	struct tinydrm_connector *tconn = to_tinydrm_connector(connector);
>> -	struct drm_display_mode *mode;
>> -
>> -	mode = drm_mode_duplicate(connector->dev, &tconn->mode);
>> -	if (!mode) {
>> -		DRM_ERROR("Failed to duplicate mode\n");
>> -		return 0;
>> -	}
>> -
>> -	if (mode->name[0] == '\0')
>> -		drm_mode_set_name(mode);
>> -
>> -	mode->type |= DRM_MODE_TYPE_PREFERRED;
>> -	drm_mode_probed_add(connector, mode);
>> -
>> -	if (mode->width_mm) {
>> -		connector->display_info.width_mm = mode->width_mm;
>> -		connector->display_info.height_mm = mode->height_mm;
>> -	}
>> -
>> -	return 1;
>> -}
>> -
>> -static const struct drm_connector_helper_funcs tinydrm_connector_hfuncs = {
>> -	.get_modes = tinydrm_connector_get_modes,
>> -};
>> -
>> -static enum drm_connector_status
>> -tinydrm_connector_detect(struct drm_connector *connector, bool force)
>> -{
>> -	if (drm_dev_is_unplugged(connector->dev))
>> -		return connector_status_disconnected;
>> -
>> -	return connector->status;
>> -}
>> -
>> -static void tinydrm_connector_destroy(struct drm_connector *connector)
>> -{
>> -	struct tinydrm_connector *tconn = to_tinydrm_connector(connector);
>> -
>> -	drm_connector_cleanup(connector);
>> -	kfree(tconn);
>> -}
>> -
>> -static const struct drm_connector_funcs tinydrm_connector_funcs = {
>> -	.reset = drm_atomic_helper_connector_reset,
>> -	.detect = tinydrm_connector_detect,
>> -	.fill_modes = drm_helper_probe_single_connector_modes,
>> -	.destroy = tinydrm_connector_destroy,
>> -	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
>> -	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
>> -};
>> -
>> -struct drm_connector *
>> -tinydrm_connector_create(struct drm_device *drm,
>> -			 const struct drm_display_mode *mode,
>> -			 int connector_type)
>> -{
>> -	struct tinydrm_connector *tconn;
>> -	struct drm_connector *connector;
>> -	int ret;
>> -
>> -	tconn = kzalloc(sizeof(*tconn), GFP_KERNEL);
>> -	if (!tconn)
>> -		return ERR_PTR(-ENOMEM);
>> -
>> -	drm_mode_copy(&tconn->mode, mode);
>> -	connector = &tconn->base;
>> -
>> -	drm_connector_helper_add(connector, &tinydrm_connector_hfuncs);
>> -	ret = drm_connector_init(drm, connector, &tinydrm_connector_funcs,
>> -				 connector_type);
>> -	if (ret) {
>> -		kfree(tconn);
>> -		return ERR_PTR(ret);
>> -	}
>> -
>> -	connector->status = connector_status_connected;
>> -
>> -	return connector;
>> -}
>> -
>> -static int tinydrm_rotate_mode(struct drm_display_mode *mode,
>> -			       unsigned int rotation)
>> -{
>> -	if (rotation == 0 || rotation == 180) {
>> -		return 0;
>> -	} else if (rotation == 90 || rotation == 270) {
>> -		swap(mode->hdisplay, mode->vdisplay);
>> -		swap(mode->hsync_start, mode->vsync_start);
>> -		swap(mode->hsync_end, mode->vsync_end);
>> -		swap(mode->htotal, mode->vtotal);
>> -		swap(mode->width_mm, mode->height_mm);
>> -		return 0;
>> -	} else {
>> -		return -EINVAL;
>> -	}
>> -}
>> -
>> -/**
>> - * tinydrm_display_pipe_init - Initialize display pipe
>> - * @tdev: tinydrm device
>> - * @funcs: Display pipe functions
>> - * @connector_type: Connector type
>> - * @formats: Array of supported formats (DRM_FORMAT\_\*)
>> - * @format_count: Number of elements in @formats
>> - * @mode: Supported mode
>> - * @rotation: Initial @mode rotation in degrees Counter Clock Wise
>> - *
>> - * This function sets up a &drm_simple_display_pipe with a &drm_connector that
>> - * has one fixed &drm_display_mode which is rotated according to @rotation.
>> - *
>> - * Returns:
>> - * Zero on success, negative error code on failure.
>> - */
>> -int
>> -tinydrm_display_pipe_init(struct tinydrm_device *tdev,
>> -			  const struct drm_simple_display_pipe_funcs *funcs,
>> -			  int connector_type,
>> -			  const uint32_t *formats,
>> -			  unsigned int format_count,
>> -			  const struct drm_display_mode *mode,
>> -			  unsigned int rotation)
>> -{
>> -	struct drm_device *drm = tdev->drm;
>> -	struct drm_display_mode mode_copy;
>> -	struct drm_connector *connector;
>> -	int ret;
>> -	static const uint64_t modifiers[] = {
>> -		DRM_FORMAT_MOD_LINEAR,
>> -		DRM_FORMAT_MOD_INVALID
>> -	};
>> -
>> -	drm_mode_copy(&mode_copy, mode);
>> -	ret = tinydrm_rotate_mode(&mode_copy, rotation);
>> -	if (ret) {
>> -		DRM_ERROR("Illegal rotation value %u\n", rotation);
>> -		return -EINVAL;
>> -	}
>> -
>> -	drm->mode_config.min_width = mode_copy.hdisplay;
>> -	drm->mode_config.max_width = mode_copy.hdisplay;
>> -	drm->mode_config.min_height = mode_copy.vdisplay;
>> -	drm->mode_config.max_height = mode_copy.vdisplay;
>> -
>> -	connector = tinydrm_connector_create(drm, &mode_copy, connector_type);
>> -	if (IS_ERR(connector))
>> -		return PTR_ERR(connector);
>> -
>> -	return drm_simple_display_pipe_init(drm, &tdev->pipe, funcs, formats,
>> -					    format_count, modifiers, connector);
>> -}
>> -EXPORT_SYMBOL(tinydrm_display_pipe_init);
>> diff --git a/drivers/gpu/drm/tinydrm/mipi-dbi.c b/drivers/gpu/drm/tinydrm/mipi-dbi.c
>> index 918f77c7de34..d1d546f3a664 100644
>> --- a/drivers/gpu/drm/tinydrm/mipi-dbi.c
>> +++ b/drivers/gpu/drm/tinydrm/mipi-dbi.c
>> @@ -391,7 +391,13 @@ int mipi_dbi_init(struct device *dev, struct mipi_dbi *mipi,
>>  {
>>  	size_t bufsize = mode->vdisplay * mode->hdisplay * sizeof(u16);
>>  	struct tinydrm_device *tdev = &mipi->tinydrm;
>> +	struct drm_connector *connector;
>> +	struct drm_device *drm;
>>  	int ret;
>> +	static const uint64_t modifiers[] = {
>> +		DRM_FORMAT_MOD_LINEAR,
>> +		DRM_FORMAT_MOD_INVALID
>> +	};
>>  
>>  	if (!mipi->command)
>>  		return -EINVAL;
>> @@ -406,18 +412,22 @@ int mipi_dbi_init(struct device *dev, struct mipi_dbi *mipi,
>>  	if (ret)
>>  		return ret;
>>  
>> -	/* TODO: Maybe add DRM_MODE_CONNECTOR_SPI */
>> -	ret = tinydrm_display_pipe_init(tdev, pipe_funcs,
>> -					DRM_MODE_CONNECTOR_VIRTUAL,
>> -					mipi_dbi_formats,
>> -					ARRAY_SIZE(mipi_dbi_formats), mode,
>> -					rotation);
>> +	drm = tdev->drm;
>> +
>> +	connector = drm_simple_connector_create(drm, DRM_MODE_CONNECTOR_VIRTUAL, mode, rotation);
> 
> You lost the todo here ... _VIRTUAL is kinda a pretty good lie, since this
> is definitely not virtual hw :-)

I suspect that if I added DRM_MODE_CONNECTOR_SPI, it would mean that I
had to update various userspace libraries as well?
I have zero experience with all that that, hence why I've never tackled
this.

Noralf.

> -Daniel
> 
>> +	if (IS_ERR(connector))
>> +		return PTR_ERR(connector);
>> +
>> +	ret = drm_simple_display_pipe_init(drm, &tdev->pipe, pipe_funcs,
>> +					   mipi_dbi_formats, ARRAY_SIZE(mipi_dbi_formats),
>> +					   modifiers, connector);
>>  	if (ret)
>>  		return ret;
>>  
>>  	drm_plane_enable_fb_damage_clips(&tdev->pipe.plane);
>>  
>> -	tdev->drm->mode_config.preferred_depth = 16;
>> +	drm_simple_connector_set_mode_config(connector);
>> +	drm->mode_config.preferred_depth = 16;
>>  	mipi->rotation = rotation;
>>  
>>  	drm_mode_config_reset(tdev->drm);
>> diff --git a/drivers/gpu/drm/tinydrm/repaper.c b/drivers/gpu/drm/tinydrm/repaper.c
>> index 72d30151ecd8..1d551744cc9b 100644
>> --- a/drivers/gpu/drm/tinydrm/repaper.c
>> +++ b/drivers/gpu/drm/tinydrm/repaper.c
>> @@ -924,12 +924,14 @@ static int repaper_probe(struct spi_device *spi)
>>  	const struct drm_display_mode *mode;
>>  	const struct spi_device_id *spi_id;
>>  	const struct of_device_id *match;
>> +	struct drm_connector *connector;
>>  	struct device *dev = &spi->dev;
>>  	struct tinydrm_device *tdev;
>>  	enum repaper_model model;
>>  	const char *thermal_zone;
>>  	struct repaper_epd *epd;
>>  	size_t line_buffer_size;
>> +	struct drm_device *drm;
>>  	int ret;
>>  
>>  	match = of_match_device(repaper_of_match, dev);
>> @@ -1069,13 +1071,19 @@ static int repaper_probe(struct spi_device *spi)
>>  	if (ret)
>>  		return ret;
>>  
>> -	ret = tinydrm_display_pipe_init(tdev, &repaper_pipe_funcs,
>> -					DRM_MODE_CONNECTOR_VIRTUAL,
>> -					repaper_formats,
>> -					ARRAY_SIZE(repaper_formats), mode, 0);
>> +	drm = tdev->drm;
>> +
>> +	connector = drm_simple_connector_create(drm, DRM_MODE_CONNECTOR_VIRTUAL, mode, 0);
>> +	if (IS_ERR(connector))
>> +		return PTR_ERR(connector);
>> +
>> +	ret = drm_simple_display_pipe_init(drm, &tdev->pipe, &repaper_pipe_funcs,
>> +					   repaper_formats, ARRAY_SIZE(repaper_formats),
>> +					   NULL, connector);
>>  	if (ret)
>>  		return ret;
>>  
>> +	drm_simple_connector_set_mode_config(connector);
>>  	drm_mode_config_reset(tdev->drm);
>>  	spi_set_drvdata(spi, tdev);
>>  
>> diff --git a/drivers/gpu/drm/tinydrm/st7586.c b/drivers/gpu/drm/tinydrm/st7586.c
>> index 5ee7db561349..6cb68dd7e7b6 100644
>> --- a/drivers/gpu/drm/tinydrm/st7586.c
>> +++ b/drivers/gpu/drm/tinydrm/st7586.c
>> @@ -271,6 +271,8 @@ static int st7586_init(struct device *dev, struct mipi_dbi *mipi,
>>  {
>>  	size_t bufsize = (mode->vdisplay + 2) / 3 * mode->hdisplay;
>>  	struct tinydrm_device *tdev = &mipi->tinydrm;
>> +	struct drm_connector *connector;
>> +	struct drm_device *drm;
>>  	int ret;
>>  
>>  	mutex_init(&mipi->cmdlock);
>> @@ -283,17 +285,22 @@ static int st7586_init(struct device *dev, struct mipi_dbi *mipi,
>>  	if (ret)
>>  		return ret;
>>  
>> -	ret = tinydrm_display_pipe_init(tdev, pipe_funcs,
>> -					DRM_MODE_CONNECTOR_VIRTUAL,
>> -					st7586_formats,
>> -					ARRAY_SIZE(st7586_formats),
>> -					mode, rotation);
>> +	drm = tdev->drm;
>> +
>> +	connector = drm_simple_connector_create(drm, DRM_MODE_CONNECTOR_VIRTUAL, mode, rotation);
>> +	if (IS_ERR(connector))
>> +		return PTR_ERR(connector);
>> +
>> +	ret = drm_simple_display_pipe_init(drm, &tdev->pipe, pipe_funcs,
>> +					   st7586_formats, ARRAY_SIZE(st7586_formats),
>> +					   NULL, connector);
>>  	if (ret)
>>  		return ret;
>>  
>>  	drm_plane_enable_fb_damage_clips(&tdev->pipe.plane);
>>  
>> -	tdev->drm->mode_config.preferred_depth = 32;
>> +	drm_simple_connector_set_mode_config(connector);
>> +	drm->mode_config.preferred_depth = 32;
>>  	mipi->rotation = rotation;
>>  
>>  	drm_mode_config_reset(tdev->drm);
>> diff --git a/include/drm/tinydrm/tinydrm.h b/include/drm/tinydrm/tinydrm.h
>> index 87e7f9b93a37..69c4363fd88e 100644
>> --- a/include/drm/tinydrm/tinydrm.h
>> +++ b/include/drm/tinydrm/tinydrm.h
>> @@ -40,13 +40,4 @@ int devm_tinydrm_init(struct device *parent, struct tinydrm_device *tdev,
>>  int devm_tinydrm_register(struct tinydrm_device *tdev);
>>  void tinydrm_shutdown(struct tinydrm_device *tdev);
>>  
>> -int
>> -tinydrm_display_pipe_init(struct tinydrm_device *tdev,
>> -			  const struct drm_simple_display_pipe_funcs *funcs,
>> -			  int connector_type,
>> -			  const uint32_t *formats,
>> -			  unsigned int format_count,
>> -			  const struct drm_display_mode *mode,
>> -			  unsigned int rotation);
>> -
>>  #endif /* __LINUX_TINYDRM_H */
>> -- 
>> 2.20.1
>>
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel@lists.freedesktop.org
>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
> 
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: [PATCH 11/11] drm/fb-helper: generic: Don't take module ref for fbcon
  2019-01-28 14:40     ` Noralf Trønnes
@ 2019-01-29  8:45       ` Daniel Vetter
  0 siblings, 0 replies; 57+ messages in thread
From: Daniel Vetter @ 2019-01-29  8:45 UTC (permalink / raw)
  To: Noralf Trønnes; +Cc: dri-devel, david

On Mon, Jan 28, 2019 at 03:40:47PM +0100, Noralf Trønnes wrote:
> 
> 
> Den 21.01.2019 10.05, skrev Daniel Vetter:
> > On Sun, Jan 20, 2019 at 12:43:18PM +0100, Noralf Trønnes wrote:
> >> It's now safe to let fbcon unbind automatically on fbdev unregister.
> >> The crash problem was fixed in commit 2122b40580dd
> >> ("fbdev: fbcon: Fix unregister crash when more than one framebuffer")
> >>
> >> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
> >> ---
> >>  drivers/gpu/drm/drm_fb_helper.c | 6 ++++--
> >>  1 file changed, 4 insertions(+), 2 deletions(-)
> >>
> >> diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
> >> index 31fcf94bf825..5d0327f603bb 100644
> >> --- a/drivers/gpu/drm/drm_fb_helper.c
> >> +++ b/drivers/gpu/drm/drm_fb_helper.c
> >> @@ -2999,7 +2999,8 @@ static int drm_fbdev_fb_open(struct fb_info *info, int user)
> >>  {
> >>  	struct drm_fb_helper *fb_helper = info->par;
> >>  
> >> -	if (!try_module_get(fb_helper->dev->driver->fops->owner))
> >> +	/* No need to take a ref for fbcon because it unbinds on unregister */
> >> +	if (user && !try_module_get(fb_helper->dev->driver->fops->owner))
> > 
> > Module refcount != driver refcount. You can always unbind a driver through
> > the sysfs unbind file, no matter whether the module is pinned or not. So I
> > think pinng the module is still the right thing to do, just to avoid races
> > and fun stuff.
> > 
> > btw, I looked into making fbdev hotunplug safe (i.e. drm_dev_enter/exit,
> > but for fbdev) over the holidays. Fixing that properly for fbdev userspace
> > clients looks like serious amounts of work, and I think it's impossible
> > for fbcon. Or at least I didn't come up with a workable idea to sprinkle
> > the dev_enter/exit stuff around. For the userspace side the basic
> > infrastructure with get_fb_info() and put_fb_info() is there already.
> > 
> 
> fbdev blocks file operations after unregister through this function:
> 
> static struct fb_info *file_fb_info(struct file *file)
> {
> 	struct inode *inode = file_inode(file);
> 	int fbidx = iminor(inode);
> 	struct fb_info *info = registered_fb[fbidx];
> 
> 	if (info != file->private_data)
> 		info = NULL;
> 	return info;
> }
> 
> static int do_unregister_framebuffer(struct fb_info *fb_info)
> {
> ...
> 	registered_fb[fb_info->node] = NULL;
> ...
> }
> 
> The only way for fbdev userspace to trigger driver actions is through
> mmap and defio. At first I planned to put drm_dev_enter/exit in
> drm_fb_helper_dirty_work(), but realised that the driver would need to
> do the same in it's dirty function to cover DRM userspace anyways, so I
> dropped it. It's the driver's responsibility to protect it's resources.

The above is all about the driver refcounting. Which does exist (but races
badly) for fbdev. The problem is you're changing the module refcount,
which is there to make sure the functions don't disappear. Note that on
the drm side we do the exact same thing, so least for symmetry reasons I
think we should keep it. The refcounting is done by the file open/close
functions since we set fops->owner, not drm code itself, but it's there.
 
> As for fbcon, yes this is all a spaghetti monster, so better safe than
> sorry I guess. And it's just developers that would benefit from this
> anyways, not having to unbind fbcon before unlaoding the driver module.

Hm yeah, make sense. On second thought, on this patch:

Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
-Daniel

> 
> Noralf.
> 
> > Cheers, Daniel
> > 
> >>  		return -ENODEV;
> >>  
> >>  	return 0;
> >> @@ -3009,7 +3010,8 @@ static int drm_fbdev_fb_release(struct fb_info *info, int user)
> >>  {
> >>  	struct drm_fb_helper *fb_helper = info->par;
> >>  
> >> -	module_put(fb_helper->dev->driver->fops->owner);
> >> +	if (user)
> >> +		module_put(fb_helper->dev->driver->fops->owner);
> >>  
> >>  	return 0;
> >>  }
> >> -- 
> >> 2.20.1
> >>
> >> _______________________________________________
> >> dri-devel mailing list
> >> dri-devel@lists.freedesktop.org
> >> https://lists.freedesktop.org/mailman/listinfo/dri-devel
> > 

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: devm actions and hw clenaup (was Re: [PATCH 01/11] drm: Add devm_drm_dev_init/register)
  2019-01-24 17:57                     ` Daniel Vetter
@ 2019-01-29 14:34                       ` Noralf Trønnes
  2019-01-29 15:16                         ` Greg KH
  2019-01-29 16:50                         ` Daniel Vetter
  0 siblings, 2 replies; 57+ messages in thread
From: Noralf Trønnes @ 2019-01-29 14:34 UTC (permalink / raw)
  To: Daniel Vetter, Greg KH; +Cc: David Lechner, dri-devel, Rafael J. Wysocki



Den 24.01.2019 18.57, skrev Daniel Vetter:
> On Thu, Jan 24, 2019 at 6:46 PM Greg KH <gregkh@linuxfoundation.org> wrote:
>>
>> On Thu, Jan 24, 2019 at 11:43:12AM +0100, Daniel Vetter wrote:
>>> On Wed, Jan 23, 2019 at 11:54:07AM +0100, Noralf Trønnes wrote:
>>>>
>>>>
>>>> Den 22.01.2019 20.30, skrev Daniel Vetter:
>>>>> On Tue, Jan 22, 2019 at 8:07 PM Noralf Trønnes <noralf@tronnes.org> wrote:
>>>>>>
>>>>>>
>>>>>>
>>>>>> Den 22.01.2019 10.32, skrev Daniel Vetter:
>>>>>>> On Mon, Jan 21, 2019 at 01:21:46PM +0100, Noralf Trønnes wrote:
>>>>>>>>
>>>>>>>>
>>>>>>>> Den 21.01.2019 10.55, skrev Daniel Vetter:
>>>>>>>>> On Mon, Jan 21, 2019 at 10:10:14AM +0100, Daniel Vetter wrote:
>>>>>>>>>> On Sun, Jan 20, 2019 at 12:43:08PM +0100, Noralf Trønnes wrote:
>>>>>>>>>>> This adds resource managed (devres) versions of drm_dev_init() and
>>>>>>>>>>> drm_dev_register().
>>>>>>>>>>>
>>>>>>>>>>> Also added is devm_drm_dev_register_with_fbdev() which sets up generic
>>>>>>>>>>> fbdev emulation as well.
>>>>>>>>>>>
>>>>>>>>>>> devm_drm_dev_register() isn't exported since there are no users.
>>>>>>>>>>>
>>>>>>>>>>> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
>>>>>>>>>>
>>>>>>>>
>>>>>>>> <snip>
>>>>>>>>
>>>>>>>>>>> diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
>>>>>>>>>>> index 381581b01d48..12129772be45 100644
>>>>>>>>>>> --- a/drivers/gpu/drm/drm_drv.c
>>>>>>>>>>> +++ b/drivers/gpu/drm/drm_drv.c
>>>>>>>>>>> @@ -36,6 +36,7 @@
>>>>>>>>>>>
>>>>>>>>>>>  #include <drm/drm_client.h>
>>>>>>>>>>>  #include <drm/drm_drv.h>
>>>>>>>>>>> +#include <drm/drm_fb_helper.h>
>>>>>>>>>>>  #include <drm/drmP.h>
>>>>>>>>>>>
>>>>>>>>>>>  #include "drm_crtc_internal.h"
>>>>>>>>>>> @@ -871,6 +872,111 @@ void drm_dev_unregister(struct drm_device *dev)
>>>>>>>>>>>  }
>>>>>>>>>>>  EXPORT_SYMBOL(drm_dev_unregister);
>>>>>>>>>>>
>>>>>>>>>>> +static void devm_drm_dev_init_release(void *data)
>>>>>>>>>>> +{
>>>>>>>>>>> + drm_dev_put(data);
>>>>>>>>>
>>>>>>>>> We need drm_dev_unplug() here, or this isn't safe.
>>>>>>>>
>>>>>>>> This function is only used to cover the error path if probe fails before
>>>>>>>> devm_drm_dev_register() is called. devm_drm_dev_register_release() is
>>>>>>>> the one that calls unplug. There are comments about this in the functions.
>>>>>>>
>>>>>>> I think I get a prize for being ignorant and blind :-/
>>>>>>>
>>>>>>>>
>>>>>>>>>
>>>>>>>>>>> +}
>>>>>>>>>>> +
>>>>>>>>>>> +/**
>>>>>>>>>>> + * devm_drm_dev_init - Resource managed drm_dev_init()
>>>>>>>>>>> + * @parent: Parent device object
>>>>>>>>>>> + * @dev: DRM device
>>>>>>>>>>> + * @driver: DRM driver
>>>>>>>>>>> + *
>>>>>>>>>>> + * Managed drm_dev_init(). The DRM device initialized with this function is
>>>>>>>>>>> + * automatically released on driver detach. You must supply a
>>>>>>>>>
>>>>>>>>> I think a bit more clarity here would be good:
>>>>>>>>>
>>>>>>>>> "... automatically released on driver unbind by callind drm_dev_unplug()."
>>>>>>>>>
>>>>>>>>>>> + * &drm_driver.release callback to control the finalization explicitly.
>>>>>>>>>
>>>>>>>>> I think a loud warning for these is in order:
>>>>>>>>>
>>>>>>>>> "WARNING:
>>>>>>>>>
>>>>>>>>> "In generally it is unsafe to use devm functions for drm structures
>>>>>>>>> because the lifetimes of &drm_device and the underlying &device do not
>>>>>>>>> match. This here works because it doesn't immediately free anything, but
>>>>>>>>> only calls drm_dev_unplug(), which internally decrements the &drm_device
>>>>>>>>> refcount through drm_dev_put().
>>>>>>>>>
>>>>>>>>> "All other drm structures must still be explicitly released in the
>>>>>>>>> &drm_driver.release callback."
>>>>>>>>>
>>>>>>>>> While thinking about this I just realized that with this design we have no
>>>>>>>>> good place to call drm_atomic_helper_shutdown(). Which we need to, or all
>>>>>>>>> kinds of things will leak badly (connectors, fb, ...), but there's no
>>>>>>>>> place to call it:
>>>>>>>>> - unbind is too early, since we haven't yet called drm_dev_unplug, and the
>>>>>>>>>   drm_dev_unregister in there must be called _before_ we start to shut
>>>>>>>>>   down anything.
>>>>>>>>> - drm_driver.release is way too late.
>>>>>>>>>
>>>>>>>>> Ofc for a real hotunplug there's no point in shutting down the hw (it's
>>>>>>>>> already gone), but for a driver unload/unbind it would be nice if this
>>>>>>>>> happens automatically and in the right order.
>>>>>>>>>
>>>>>>>>> So not sure what to do here really.
>>>>>>>>
>>>>>>>> How about this change: (it breaks the rule of pulling helpers into the
>>>>>>>> core, so maybe we should put the devm_ functions into the simple KMS
>>>>>>>> helper instead?)
>>>>>>>
>>>>>>> Yeah smells a bit much like midlayer ... What would work is having a pile
>>>>>>> more devm_ helper functions, so that we onion-unwrap everything correctly,
>>>>>>> and in the right order. So:
>>>>>>>
>>>>>>> - devm_drm_dev_init (always does a drm_dev_put())
>>>>>>>
>>>>>>> - devm_drm_poll_enable (shuts down the poll helper with a devm action)
>>>>>>>
>>>>>>> - devm_drm_mode_config_reset (does an atomic_helper_shutdown() as it's cleanup action)
>>>>>>>
>>>>>>> - devm_drm_dev_register (grabs an additional drm_dev_get() reference so it
>>>>>>>   can call drm_dev_unplug() unconditionally).
>>>>>>>
>>>>>>
>>>>>> Beautiful! I really like this, it's very flexible.
>>>>>>
>>>>>> Where should devm_drm_mode_config_reset() live? It will pull in the
>>>>>> atomic helper...
>>>>>
>>>>> I think a new drm_devm.c helper would be nice for all this stuff.
>>>>> Especially since you can't freely mix devm-based setup/cleanup with
>>>>> normal cleanup I think it'd be good to have it all together in one
>>>>> place. And perhaps even a code example in the DOC: overview.
>>>>>
>>>>>>> We'd need to make sure some of the cleanup actions dtrt when the device is
>>>>>>> gone, but I think we can achieve that by liberally sprinkling
>>>>>>> drm_dev_enter/exit over them, e.g. the the cleanup action for
>>>>>>> drm_mode_config_reset would be:
>>>>>>>
>>>>>>> {
>>>>>>>       if (drm_dev_enter())
>>>>>>>               return;
>>>>>>>
>>>>>>>       drm_atomic_helper_shutdown();
>>>>>>>
>>>>>>>       drm_dev_exit();
>>>>>>> }
>>>>>>>
>>>>>>
>>>>>> drm_dev_enter() can only be used to check whether the drm_device is
>>>>>> registered or not, it doesn't say anything about the state of the parent
>>>>>> device.
>>>>>>
>>>>>> All we know is that the device is being unbound from the driver, we
>>>>>> don't know if it's the device that's being removed or if it's the driver
>>>>>> that's unregistered.
>>>>>
>>>>> You're right, both paths will have called drm_dev_unplug by then.
>>>>> Silly me. I really liked my idea :-)
>>>>>
>>>>>> I have looked at the various call chains:
>>>>>>
>>>>>> driver_unregister ->
>>>>>>     bus_remove_driver ->
>>>>>>         driver_detach ->
>>>>>>             device_release_driver_internal
>>>>>>
>>>>>> device_unregister ->
>>>>>>     device_del ->
>>>>>>         bus_remove_device ->
>>>>>>             device_release_driver ->
>>>>>>                 device_release_driver_internal
>>>>>>
>>>>>> sysfs: unbind_store ->
>>>>>>     device_release_driver ->
>>>>>>         device_release_driver_internal
>>>>>>
>>>>>> The only way I've found to differentiate between these in a cleanup
>>>>>> action is that device_del() uses the bus notifier to signal
>>>>>> BUS_NOTIFY_DEL_DEVICE before calling bus_remove_device(). Such a
>>>>>> notifier could be used to set a drm_device->parent_removed flag.
>>>>>
>>>>> Hm, this might upset Greg KH's code taste ... maybe there's a better
>>>>> way to do this, but best to prototype a patch with this, send it to
>>>>> him and ask how to :-)
>>>>>
>>>>
>>>> I'll leave this to the one that needs it. The tinydrm drivers doesn't
>>>> need to touch hw after DRM unregister.
>>>>
>>>>>> Why is it necessary to call drm_atomic_helper_shutdown() here? Doesn't
>>>>>> everything get disabled when userspace closes? It does in my tinydrm
>>>>>> world :-)
>>>>>
>>>>> Iirc fbdev/fbcon can result in leaks ... at least we've had patches
>>>>> where drivers leaked drm_connector and drm_framebuffer objects, and
>>>>> they've been fixed by calling drm_atomic_helper_shutdown() in the
>>>>> unload path. Maybe this is cargo-culting, but it goes way back to
>>>>> pre-atomic, where drivers called drm_helper_force_disable_all().
>>>>>
>>>>> If you try to move the fbcon to your tinydrm drivers (con2fb is
>>>>> apparently the cmdline tool you need, never tried it, I only switch
>>>>> the kernel's console between fbcon and dummycon and back, not what
>>>>> fbcon drivers itself), then I think you should be able to reproduce.
>>>>> And maybe you have a better idea how to deal with this all.
>>>>>
>>>>> Note also that there's been proposals floating around to only close an
>>>>> drm_framebuffer, not also remove it (like the current RMFB ioctl
>>>>> does), with that closing userspace would not necessarily lead to a
>>>>> full cleanup.
>>>>>
>>>>> Another thing (which doesn't apply to drm_simple_display_pipe drivers)
>>>>> is if you have the display on, but no planes showing (i.e. all black).
>>>>> Then all the fbs will be cleaned up, but drm_connector will be
>>>>> leaking. That's a case where you need drm_atomic_helper_shutdown()
>>>>> even if fbcon/fbdev isn't even enabled.
>>>>>
>>>>
>>>> Ok, this means that I don't need to call drm_atomic_helper_shutdown() in
>>>> tinydrm. DRM userspace disables the pipe on close and the generic fbdev
>>>> emulation also releases everything.
>>>> Even so, maybe I should use devm_drm_mode_config_reset() after all to
>>>> keep drivers uniform, to avoid confusion: why doesn't he use it?
>>>
>>> Hm maybe there is an official way to solve this, pulling in Greg+Rafael.
>>>
>>> Super short summary: We want to start using devm actions to clean up drm
>>> drivers. Here's the problem:
>>> - For a driver unload/unbind without hotunplug, we want to properly clean
>>>   up the hardware and shut it all down.
>>
>> Then do it on probe/disconnect.
>>
>>> - But if the device is unplugged already, that's probably not the best
>>>   idea, and we only want to clean up the kernel's resources/allocations.
>>
>> Again, probe/disconnect will be called either way.
>>
>> But as you note, memory will NOT be freed by the devm stuff if you
>> manually unbind a driver from a device.
>>
>> So don't touch hardware there, it's not going to work :)
>>
>>> What's the recommendation here? I see a few options:
>>>
>>> - Make sure everything can deal with this properly. Hotunplug can happen
>>>   anytime, so there's a race no matter what.
>>
>> Yes.
>>
>>> - Check with the device model whether the struct device is disappearing or
>>>   whether we're just dealing with a driver unbind (no idea how to do
>>>   that), and act accordingly.
>>
>> You don't know that, sorry.  Just do any hardware stuff on disconnect.
>> Assuming your hardware is still present :)
>>
>>> - Fundamental question: Touching the hw from devm actions, is that ok? If
>>>   not, then the pretty nifty plan laid out in this thread wont work.
>>
>> Nope, that's not going to work, the device could either be long gone, or
>> you will not be called due to unbind happening from userspace.
>>
>> But really, unbind from userspace is very very rare, it's a developer
>> thing mostly.  Oh and a virtual driver thing, but those people are crazy
>> :)
>>
>>> - Something completely different?
>>
>> Do it in disconnect :)
> 
> Ah, I forgot to mention the important constraint :-) disconnect/unbind
> should be the following sequence:
> 
> 1. Unregister all the userspace interfaces (there's a lot of them) and
> make sure all the pending ioctls are done so that from now on
> userspace sees lots of -EIO (in case it still has fd open, which is
> going to be the normal for hotunplug.
> 
> 2. Shut down hw and all ongoing operations (only relevant for unbind,
> but needs to be able to cope with sudden hotunplug on top anyway).
> 
> 3. Clean up the kernel mess and release everything.
> 
> Probe is exactly the other way round, so would perfectly fit into the
> devm onion cleanup. See in the commented earlier replies above how
> that would match in details, but tldr; if we have to do 2. in
> disconnect, then we also have to do 1. in disconnected, and only doing
> 3. through devm is almost not worth the bother. But if we could do all
> three through devm then simple drivers wouldn't even need any
> disconnect/unbind callback at all. That's our motivation for trying to
> come up with an answer that's not "do it in disconnect". "do it in
> disconnect" is how we do it all today already.
> 
> Yes we're trying to make tiny drivers even smaller, we have enough
> nowadays that this stuff would be worth it :-)
> 

I think a solution is to say that drivers that want to touch hw on
disconnect needs to use device_driver->remove to do that.

This is an example driver that doesn't need to touch hw because it's so
simple that userspace has disabled the pipeline:

static void drm_driver_release(struct drm_device *drm)
{
	drm_mode_config_cleanup(drm);
	drm_dev_fini(drm);
	kfree(drm);
}

static struct drm_driver drm_driver = {
	.release = drm_driver_release,
	/* ... */
};

static int driver_probe(struct device *dev)
{
	struct drm_device *drm;
	int ret;

	drm = kzalloc(sizeof(*drm), GFP_KERNEL);
	if (!drm)
		return -ENOMEM;

	ret = devm_drm_dev_init(dev, drm, &drm_driver);
	if (ret) {
		kfree(drm);
		return ret;
	}

	drm_mode_config_init(drm);

	/* Aquire various resources, all managed by devres */

	drm_mode_config_reset(drm);

	return devm_drm_dev_register(drm);
}

struct device_driver driver = {
	.probe = driver_probe,
};


A driver that wants to touch hardware on disconnect, can look like this:

static void drm_driver_release(struct drm_device *drm)
{
	drm_mode_config_cleanup(drm);
	drm_dev_fini(drm);
	kfree(drm);
}

static struct drm_driver drm_driver = {
	.release = drm_driver_release,
	/* ... */
};

static int driver_probe(struct device *dev)
{
	struct drm_device *drm;
	int ret;

	drm = kzalloc(sizeof(*drm), GFP_KERNEL);
	if (!drm)
		return -ENOMEM;

	ret = devm_drm_dev_init(dev, drm, &drm_driver);
	if (ret) {
		kfree(drm);
		return ret;
	}

	drm_mode_config_init(drm);

	/* Aquire various resources, all managed by devres */

	drm_mode_config_reset(drm);

	ret = drm_dev_register(drm);
	if (ret)
		return ret;

	drm_dev_get(dev); /* If using drm_dev_unplug() */

	dev_set_drvdata(dev, drm);

	return 0;
}

/* This function is called before devres_release_all() */
static int driver_remove(struct device *dev)
{
	struct drm_device *drm = dev_get_drvdata(dev);

	drm_dev_unplug(drm); OR drm_dev_unregister(drm);
	drm_atomic_helper_shutdown(drm)

	return 0;
}

struct device_driver driver = {
	.probe = driver_probe,
	.remove = driver_remove,
};


Noralf.
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: devm actions and hw clenaup (was Re: [PATCH 01/11] drm: Add devm_drm_dev_init/register)
  2019-01-29 14:34                       ` Noralf Trønnes
@ 2019-01-29 15:16                         ` Greg KH
  2019-01-29 16:50                         ` Daniel Vetter
  1 sibling, 0 replies; 57+ messages in thread
From: Greg KH @ 2019-01-29 15:16 UTC (permalink / raw)
  To: Noralf Trønnes; +Cc: Rafael J. Wysocki, dri-devel, David Lechner

On Tue, Jan 29, 2019 at 03:34:46PM +0100, Noralf Trønnes wrote:
> 
> 
> Den 24.01.2019 18.57, skrev Daniel Vetter:
> > On Thu, Jan 24, 2019 at 6:46 PM Greg KH <gregkh@linuxfoundation.org> wrote:
> >>
> >> On Thu, Jan 24, 2019 at 11:43:12AM +0100, Daniel Vetter wrote:
> >>> On Wed, Jan 23, 2019 at 11:54:07AM +0100, Noralf Trønnes wrote:
> >>>>
> >>>>
> >>>> Den 22.01.2019 20.30, skrev Daniel Vetter:
> >>>>> On Tue, Jan 22, 2019 at 8:07 PM Noralf Trønnes <noralf@tronnes.org> wrote:
> >>>>>>
> >>>>>>
> >>>>>>
> >>>>>> Den 22.01.2019 10.32, skrev Daniel Vetter:
> >>>>>>> On Mon, Jan 21, 2019 at 01:21:46PM +0100, Noralf Trønnes wrote:
> >>>>>>>>
> >>>>>>>>
> >>>>>>>> Den 21.01.2019 10.55, skrev Daniel Vetter:
> >>>>>>>>> On Mon, Jan 21, 2019 at 10:10:14AM +0100, Daniel Vetter wrote:
> >>>>>>>>>> On Sun, Jan 20, 2019 at 12:43:08PM +0100, Noralf Trønnes wrote:
> >>>>>>>>>>> This adds resource managed (devres) versions of drm_dev_init() and
> >>>>>>>>>>> drm_dev_register().
> >>>>>>>>>>>
> >>>>>>>>>>> Also added is devm_drm_dev_register_with_fbdev() which sets up generic
> >>>>>>>>>>> fbdev emulation as well.
> >>>>>>>>>>>
> >>>>>>>>>>> devm_drm_dev_register() isn't exported since there are no users.
> >>>>>>>>>>>
> >>>>>>>>>>> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
> >>>>>>>>>>
> >>>>>>>>
> >>>>>>>> <snip>
> >>>>>>>>
> >>>>>>>>>>> diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
> >>>>>>>>>>> index 381581b01d48..12129772be45 100644
> >>>>>>>>>>> --- a/drivers/gpu/drm/drm_drv.c
> >>>>>>>>>>> +++ b/drivers/gpu/drm/drm_drv.c
> >>>>>>>>>>> @@ -36,6 +36,7 @@
> >>>>>>>>>>>
> >>>>>>>>>>>  #include <drm/drm_client.h>
> >>>>>>>>>>>  #include <drm/drm_drv.h>
> >>>>>>>>>>> +#include <drm/drm_fb_helper.h>
> >>>>>>>>>>>  #include <drm/drmP.h>
> >>>>>>>>>>>
> >>>>>>>>>>>  #include "drm_crtc_internal.h"
> >>>>>>>>>>> @@ -871,6 +872,111 @@ void drm_dev_unregister(struct drm_device *dev)
> >>>>>>>>>>>  }
> >>>>>>>>>>>  EXPORT_SYMBOL(drm_dev_unregister);
> >>>>>>>>>>>
> >>>>>>>>>>> +static void devm_drm_dev_init_release(void *data)
> >>>>>>>>>>> +{
> >>>>>>>>>>> + drm_dev_put(data);
> >>>>>>>>>
> >>>>>>>>> We need drm_dev_unplug() here, or this isn't safe.
> >>>>>>>>
> >>>>>>>> This function is only used to cover the error path if probe fails before
> >>>>>>>> devm_drm_dev_register() is called. devm_drm_dev_register_release() is
> >>>>>>>> the one that calls unplug. There are comments about this in the functions.
> >>>>>>>
> >>>>>>> I think I get a prize for being ignorant and blind :-/
> >>>>>>>
> >>>>>>>>
> >>>>>>>>>
> >>>>>>>>>>> +}
> >>>>>>>>>>> +
> >>>>>>>>>>> +/**
> >>>>>>>>>>> + * devm_drm_dev_init - Resource managed drm_dev_init()
> >>>>>>>>>>> + * @parent: Parent device object
> >>>>>>>>>>> + * @dev: DRM device
> >>>>>>>>>>> + * @driver: DRM driver
> >>>>>>>>>>> + *
> >>>>>>>>>>> + * Managed drm_dev_init(). The DRM device initialized with this function is
> >>>>>>>>>>> + * automatically released on driver detach. You must supply a
> >>>>>>>>>
> >>>>>>>>> I think a bit more clarity here would be good:
> >>>>>>>>>
> >>>>>>>>> "... automatically released on driver unbind by callind drm_dev_unplug()."
> >>>>>>>>>
> >>>>>>>>>>> + * &drm_driver.release callback to control the finalization explicitly.
> >>>>>>>>>
> >>>>>>>>> I think a loud warning for these is in order:
> >>>>>>>>>
> >>>>>>>>> "WARNING:
> >>>>>>>>>
> >>>>>>>>> "In generally it is unsafe to use devm functions for drm structures
> >>>>>>>>> because the lifetimes of &drm_device and the underlying &device do not
> >>>>>>>>> match. This here works because it doesn't immediately free anything, but
> >>>>>>>>> only calls drm_dev_unplug(), which internally decrements the &drm_device
> >>>>>>>>> refcount through drm_dev_put().
> >>>>>>>>>
> >>>>>>>>> "All other drm structures must still be explicitly released in the
> >>>>>>>>> &drm_driver.release callback."
> >>>>>>>>>
> >>>>>>>>> While thinking about this I just realized that with this design we have no
> >>>>>>>>> good place to call drm_atomic_helper_shutdown(). Which we need to, or all
> >>>>>>>>> kinds of things will leak badly (connectors, fb, ...), but there's no
> >>>>>>>>> place to call it:
> >>>>>>>>> - unbind is too early, since we haven't yet called drm_dev_unplug, and the
> >>>>>>>>>   drm_dev_unregister in there must be called _before_ we start to shut
> >>>>>>>>>   down anything.
> >>>>>>>>> - drm_driver.release is way too late.
> >>>>>>>>>
> >>>>>>>>> Ofc for a real hotunplug there's no point in shutting down the hw (it's
> >>>>>>>>> already gone), but for a driver unload/unbind it would be nice if this
> >>>>>>>>> happens automatically and in the right order.
> >>>>>>>>>
> >>>>>>>>> So not sure what to do here really.
> >>>>>>>>
> >>>>>>>> How about this change: (it breaks the rule of pulling helpers into the
> >>>>>>>> core, so maybe we should put the devm_ functions into the simple KMS
> >>>>>>>> helper instead?)
> >>>>>>>
> >>>>>>> Yeah smells a bit much like midlayer ... What would work is having a pile
> >>>>>>> more devm_ helper functions, so that we onion-unwrap everything correctly,
> >>>>>>> and in the right order. So:
> >>>>>>>
> >>>>>>> - devm_drm_dev_init (always does a drm_dev_put())
> >>>>>>>
> >>>>>>> - devm_drm_poll_enable (shuts down the poll helper with a devm action)
> >>>>>>>
> >>>>>>> - devm_drm_mode_config_reset (does an atomic_helper_shutdown() as it's cleanup action)
> >>>>>>>
> >>>>>>> - devm_drm_dev_register (grabs an additional drm_dev_get() reference so it
> >>>>>>>   can call drm_dev_unplug() unconditionally).
> >>>>>>>
> >>>>>>
> >>>>>> Beautiful! I really like this, it's very flexible.
> >>>>>>
> >>>>>> Where should devm_drm_mode_config_reset() live? It will pull in the
> >>>>>> atomic helper...
> >>>>>
> >>>>> I think a new drm_devm.c helper would be nice for all this stuff.
> >>>>> Especially since you can't freely mix devm-based setup/cleanup with
> >>>>> normal cleanup I think it'd be good to have it all together in one
> >>>>> place. And perhaps even a code example in the DOC: overview.
> >>>>>
> >>>>>>> We'd need to make sure some of the cleanup actions dtrt when the device is
> >>>>>>> gone, but I think we can achieve that by liberally sprinkling
> >>>>>>> drm_dev_enter/exit over them, e.g. the the cleanup action for
> >>>>>>> drm_mode_config_reset would be:
> >>>>>>>
> >>>>>>> {
> >>>>>>>       if (drm_dev_enter())
> >>>>>>>               return;
> >>>>>>>
> >>>>>>>       drm_atomic_helper_shutdown();
> >>>>>>>
> >>>>>>>       drm_dev_exit();
> >>>>>>> }
> >>>>>>>
> >>>>>>
> >>>>>> drm_dev_enter() can only be used to check whether the drm_device is
> >>>>>> registered or not, it doesn't say anything about the state of the parent
> >>>>>> device.
> >>>>>>
> >>>>>> All we know is that the device is being unbound from the driver, we
> >>>>>> don't know if it's the device that's being removed or if it's the driver
> >>>>>> that's unregistered.
> >>>>>
> >>>>> You're right, both paths will have called drm_dev_unplug by then.
> >>>>> Silly me. I really liked my idea :-)
> >>>>>
> >>>>>> I have looked at the various call chains:
> >>>>>>
> >>>>>> driver_unregister ->
> >>>>>>     bus_remove_driver ->
> >>>>>>         driver_detach ->
> >>>>>>             device_release_driver_internal
> >>>>>>
> >>>>>> device_unregister ->
> >>>>>>     device_del ->
> >>>>>>         bus_remove_device ->
> >>>>>>             device_release_driver ->
> >>>>>>                 device_release_driver_internal
> >>>>>>
> >>>>>> sysfs: unbind_store ->
> >>>>>>     device_release_driver ->
> >>>>>>         device_release_driver_internal
> >>>>>>
> >>>>>> The only way I've found to differentiate between these in a cleanup
> >>>>>> action is that device_del() uses the bus notifier to signal
> >>>>>> BUS_NOTIFY_DEL_DEVICE before calling bus_remove_device(). Such a
> >>>>>> notifier could be used to set a drm_device->parent_removed flag.
> >>>>>
> >>>>> Hm, this might upset Greg KH's code taste ... maybe there's a better
> >>>>> way to do this, but best to prototype a patch with this, send it to
> >>>>> him and ask how to :-)
> >>>>>
> >>>>
> >>>> I'll leave this to the one that needs it. The tinydrm drivers doesn't
> >>>> need to touch hw after DRM unregister.
> >>>>
> >>>>>> Why is it necessary to call drm_atomic_helper_shutdown() here? Doesn't
> >>>>>> everything get disabled when userspace closes? It does in my tinydrm
> >>>>>> world :-)
> >>>>>
> >>>>> Iirc fbdev/fbcon can result in leaks ... at least we've had patches
> >>>>> where drivers leaked drm_connector and drm_framebuffer objects, and
> >>>>> they've been fixed by calling drm_atomic_helper_shutdown() in the
> >>>>> unload path. Maybe this is cargo-culting, but it goes way back to
> >>>>> pre-atomic, where drivers called drm_helper_force_disable_all().
> >>>>>
> >>>>> If you try to move the fbcon to your tinydrm drivers (con2fb is
> >>>>> apparently the cmdline tool you need, never tried it, I only switch
> >>>>> the kernel's console between fbcon and dummycon and back, not what
> >>>>> fbcon drivers itself), then I think you should be able to reproduce.
> >>>>> And maybe you have a better idea how to deal with this all.
> >>>>>
> >>>>> Note also that there's been proposals floating around to only close an
> >>>>> drm_framebuffer, not also remove it (like the current RMFB ioctl
> >>>>> does), with that closing userspace would not necessarily lead to a
> >>>>> full cleanup.
> >>>>>
> >>>>> Another thing (which doesn't apply to drm_simple_display_pipe drivers)
> >>>>> is if you have the display on, but no planes showing (i.e. all black).
> >>>>> Then all the fbs will be cleaned up, but drm_connector will be
> >>>>> leaking. That's a case where you need drm_atomic_helper_shutdown()
> >>>>> even if fbcon/fbdev isn't even enabled.
> >>>>>
> >>>>
> >>>> Ok, this means that I don't need to call drm_atomic_helper_shutdown() in
> >>>> tinydrm. DRM userspace disables the pipe on close and the generic fbdev
> >>>> emulation also releases everything.
> >>>> Even so, maybe I should use devm_drm_mode_config_reset() after all to
> >>>> keep drivers uniform, to avoid confusion: why doesn't he use it?
> >>>
> >>> Hm maybe there is an official way to solve this, pulling in Greg+Rafael.
> >>>
> >>> Super short summary: We want to start using devm actions to clean up drm
> >>> drivers. Here's the problem:
> >>> - For a driver unload/unbind without hotunplug, we want to properly clean
> >>>   up the hardware and shut it all down.
> >>
> >> Then do it on probe/disconnect.
> >>
> >>> - But if the device is unplugged already, that's probably not the best
> >>>   idea, and we only want to clean up the kernel's resources/allocations.
> >>
> >> Again, probe/disconnect will be called either way.
> >>
> >> But as you note, memory will NOT be freed by the devm stuff if you
> >> manually unbind a driver from a device.
> >>
> >> So don't touch hardware there, it's not going to work :)
> >>
> >>> What's the recommendation here? I see a few options:
> >>>
> >>> - Make sure everything can deal with this properly. Hotunplug can happen
> >>>   anytime, so there's a race no matter what.
> >>
> >> Yes.
> >>
> >>> - Check with the device model whether the struct device is disappearing or
> >>>   whether we're just dealing with a driver unbind (no idea how to do
> >>>   that), and act accordingly.
> >>
> >> You don't know that, sorry.  Just do any hardware stuff on disconnect.
> >> Assuming your hardware is still present :)
> >>
> >>> - Fundamental question: Touching the hw from devm actions, is that ok? If
> >>>   not, then the pretty nifty plan laid out in this thread wont work.
> >>
> >> Nope, that's not going to work, the device could either be long gone, or
> >> you will not be called due to unbind happening from userspace.
> >>
> >> But really, unbind from userspace is very very rare, it's a developer
> >> thing mostly.  Oh and a virtual driver thing, but those people are crazy
> >> :)
> >>
> >>> - Something completely different?
> >>
> >> Do it in disconnect :)
> > 
> > Ah, I forgot to mention the important constraint :-) disconnect/unbind
> > should be the following sequence:
> > 
> > 1. Unregister all the userspace interfaces (there's a lot of them) and
> > make sure all the pending ioctls are done so that from now on
> > userspace sees lots of -EIO (in case it still has fd open, which is
> > going to be the normal for hotunplug.
> > 
> > 2. Shut down hw and all ongoing operations (only relevant for unbind,
> > but needs to be able to cope with sudden hotunplug on top anyway).
> > 
> > 3. Clean up the kernel mess and release everything.
> > 
> > Probe is exactly the other way round, so would perfectly fit into the
> > devm onion cleanup. See in the commented earlier replies above how
> > that would match in details, but tldr; if we have to do 2. in
> > disconnect, then we also have to do 1. in disconnected, and only doing
> > 3. through devm is almost not worth the bother. But if we could do all
> > three through devm then simple drivers wouldn't even need any
> > disconnect/unbind callback at all. That's our motivation for trying to
> > come up with an answer that's not "do it in disconnect". "do it in
> > disconnect" is how we do it all today already.
> > 
> > Yes we're trying to make tiny drivers even smaller, we have enough
> > nowadays that this stuff would be worth it :-)
> > 
> 
> I think a solution is to say that drivers that want to touch hw on
> disconnect needs to use device_driver->remove to do that.

That's what I was trying to say above "remove" is what I was trying to
say is "disconnect".

thanks,

greg k-h
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: devm actions and hw clenaup (was Re: [PATCH 01/11] drm: Add devm_drm_dev_init/register)
  2019-01-29 14:34                       ` Noralf Trønnes
  2019-01-29 15:16                         ` Greg KH
@ 2019-01-29 16:50                         ` Daniel Vetter
  2019-01-29 17:26                           ` Noralf Trønnes
  2019-01-29 17:36                           ` Greg KH
  1 sibling, 2 replies; 57+ messages in thread
From: Daniel Vetter @ 2019-01-29 16:50 UTC (permalink / raw)
  To: Noralf Trønnes; +Cc: Greg KH, Rafael J. Wysocki, dri-devel, David Lechner

On Tue, Jan 29, 2019 at 03:34:46PM +0100, Noralf Trønnes wrote:
> 
> 
> Den 24.01.2019 18.57, skrev Daniel Vetter:
> > On Thu, Jan 24, 2019 at 6:46 PM Greg KH <gregkh@linuxfoundation.org> wrote:
> >>
> >> On Thu, Jan 24, 2019 at 11:43:12AM +0100, Daniel Vetter wrote:
> >>> On Wed, Jan 23, 2019 at 11:54:07AM +0100, Noralf Trønnes wrote:
> >>>>
> >>>>
> >>>> Den 22.01.2019 20.30, skrev Daniel Vetter:
> >>>>> On Tue, Jan 22, 2019 at 8:07 PM Noralf Trønnes <noralf@tronnes.org> wrote:
> >>>>>>
> >>>>>>
> >>>>>>
> >>>>>> Den 22.01.2019 10.32, skrev Daniel Vetter:
> >>>>>>> On Mon, Jan 21, 2019 at 01:21:46PM +0100, Noralf Trønnes wrote:
> >>>>>>>>
> >>>>>>>>
> >>>>>>>> Den 21.01.2019 10.55, skrev Daniel Vetter:
> >>>>>>>>> On Mon, Jan 21, 2019 at 10:10:14AM +0100, Daniel Vetter wrote:
> >>>>>>>>>> On Sun, Jan 20, 2019 at 12:43:08PM +0100, Noralf Trønnes wrote:
> >>>>>>>>>>> This adds resource managed (devres) versions of drm_dev_init() and
> >>>>>>>>>>> drm_dev_register().
> >>>>>>>>>>>
> >>>>>>>>>>> Also added is devm_drm_dev_register_with_fbdev() which sets up generic
> >>>>>>>>>>> fbdev emulation as well.
> >>>>>>>>>>>
> >>>>>>>>>>> devm_drm_dev_register() isn't exported since there are no users.
> >>>>>>>>>>>
> >>>>>>>>>>> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
> >>>>>>>>>>
> >>>>>>>>
> >>>>>>>> <snip>
> >>>>>>>>
> >>>>>>>>>>> diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
> >>>>>>>>>>> index 381581b01d48..12129772be45 100644
> >>>>>>>>>>> --- a/drivers/gpu/drm/drm_drv.c
> >>>>>>>>>>> +++ b/drivers/gpu/drm/drm_drv.c
> >>>>>>>>>>> @@ -36,6 +36,7 @@
> >>>>>>>>>>>
> >>>>>>>>>>>  #include <drm/drm_client.h>
> >>>>>>>>>>>  #include <drm/drm_drv.h>
> >>>>>>>>>>> +#include <drm/drm_fb_helper.h>
> >>>>>>>>>>>  #include <drm/drmP.h>
> >>>>>>>>>>>
> >>>>>>>>>>>  #include "drm_crtc_internal.h"
> >>>>>>>>>>> @@ -871,6 +872,111 @@ void drm_dev_unregister(struct drm_device *dev)
> >>>>>>>>>>>  }
> >>>>>>>>>>>  EXPORT_SYMBOL(drm_dev_unregister);
> >>>>>>>>>>>
> >>>>>>>>>>> +static void devm_drm_dev_init_release(void *data)
> >>>>>>>>>>> +{
> >>>>>>>>>>> + drm_dev_put(data);
> >>>>>>>>>
> >>>>>>>>> We need drm_dev_unplug() here, or this isn't safe.
> >>>>>>>>
> >>>>>>>> This function is only used to cover the error path if probe fails before
> >>>>>>>> devm_drm_dev_register() is called. devm_drm_dev_register_release() is
> >>>>>>>> the one that calls unplug. There are comments about this in the functions.
> >>>>>>>
> >>>>>>> I think I get a prize for being ignorant and blind :-/
> >>>>>>>
> >>>>>>>>
> >>>>>>>>>
> >>>>>>>>>>> +}
> >>>>>>>>>>> +
> >>>>>>>>>>> +/**
> >>>>>>>>>>> + * devm_drm_dev_init - Resource managed drm_dev_init()
> >>>>>>>>>>> + * @parent: Parent device object
> >>>>>>>>>>> + * @dev: DRM device
> >>>>>>>>>>> + * @driver: DRM driver
> >>>>>>>>>>> + *
> >>>>>>>>>>> + * Managed drm_dev_init(). The DRM device initialized with this function is
> >>>>>>>>>>> + * automatically released on driver detach. You must supply a
> >>>>>>>>>
> >>>>>>>>> I think a bit more clarity here would be good:
> >>>>>>>>>
> >>>>>>>>> "... automatically released on driver unbind by callind drm_dev_unplug()."
> >>>>>>>>>
> >>>>>>>>>>> + * &drm_driver.release callback to control the finalization explicitly.
> >>>>>>>>>
> >>>>>>>>> I think a loud warning for these is in order:
> >>>>>>>>>
> >>>>>>>>> "WARNING:
> >>>>>>>>>
> >>>>>>>>> "In generally it is unsafe to use devm functions for drm structures
> >>>>>>>>> because the lifetimes of &drm_device and the underlying &device do not
> >>>>>>>>> match. This here works because it doesn't immediately free anything, but
> >>>>>>>>> only calls drm_dev_unplug(), which internally decrements the &drm_device
> >>>>>>>>> refcount through drm_dev_put().
> >>>>>>>>>
> >>>>>>>>> "All other drm structures must still be explicitly released in the
> >>>>>>>>> &drm_driver.release callback."
> >>>>>>>>>
> >>>>>>>>> While thinking about this I just realized that with this design we have no
> >>>>>>>>> good place to call drm_atomic_helper_shutdown(). Which we need to, or all
> >>>>>>>>> kinds of things will leak badly (connectors, fb, ...), but there's no
> >>>>>>>>> place to call it:
> >>>>>>>>> - unbind is too early, since we haven't yet called drm_dev_unplug, and the
> >>>>>>>>>   drm_dev_unregister in there must be called _before_ we start to shut
> >>>>>>>>>   down anything.
> >>>>>>>>> - drm_driver.release is way too late.
> >>>>>>>>>
> >>>>>>>>> Ofc for a real hotunplug there's no point in shutting down the hw (it's
> >>>>>>>>> already gone), but for a driver unload/unbind it would be nice if this
> >>>>>>>>> happens automatically and in the right order.
> >>>>>>>>>
> >>>>>>>>> So not sure what to do here really.
> >>>>>>>>
> >>>>>>>> How about this change: (it breaks the rule of pulling helpers into the
> >>>>>>>> core, so maybe we should put the devm_ functions into the simple KMS
> >>>>>>>> helper instead?)
> >>>>>>>
> >>>>>>> Yeah smells a bit much like midlayer ... What would work is having a pile
> >>>>>>> more devm_ helper functions, so that we onion-unwrap everything correctly,
> >>>>>>> and in the right order. So:
> >>>>>>>
> >>>>>>> - devm_drm_dev_init (always does a drm_dev_put())
> >>>>>>>
> >>>>>>> - devm_drm_poll_enable (shuts down the poll helper with a devm action)
> >>>>>>>
> >>>>>>> - devm_drm_mode_config_reset (does an atomic_helper_shutdown() as it's cleanup action)
> >>>>>>>
> >>>>>>> - devm_drm_dev_register (grabs an additional drm_dev_get() reference so it
> >>>>>>>   can call drm_dev_unplug() unconditionally).
> >>>>>>>
> >>>>>>
> >>>>>> Beautiful! I really like this, it's very flexible.
> >>>>>>
> >>>>>> Where should devm_drm_mode_config_reset() live? It will pull in the
> >>>>>> atomic helper...
> >>>>>
> >>>>> I think a new drm_devm.c helper would be nice for all this stuff.
> >>>>> Especially since you can't freely mix devm-based setup/cleanup with
> >>>>> normal cleanup I think it'd be good to have it all together in one
> >>>>> place. And perhaps even a code example in the DOC: overview.
> >>>>>
> >>>>>>> We'd need to make sure some of the cleanup actions dtrt when the device is
> >>>>>>> gone, but I think we can achieve that by liberally sprinkling
> >>>>>>> drm_dev_enter/exit over them, e.g. the the cleanup action for
> >>>>>>> drm_mode_config_reset would be:
> >>>>>>>
> >>>>>>> {
> >>>>>>>       if (drm_dev_enter())
> >>>>>>>               return;
> >>>>>>>
> >>>>>>>       drm_atomic_helper_shutdown();
> >>>>>>>
> >>>>>>>       drm_dev_exit();
> >>>>>>> }
> >>>>>>>
> >>>>>>
> >>>>>> drm_dev_enter() can only be used to check whether the drm_device is
> >>>>>> registered or not, it doesn't say anything about the state of the parent
> >>>>>> device.
> >>>>>>
> >>>>>> All we know is that the device is being unbound from the driver, we
> >>>>>> don't know if it's the device that's being removed or if it's the driver
> >>>>>> that's unregistered.
> >>>>>
> >>>>> You're right, both paths will have called drm_dev_unplug by then.
> >>>>> Silly me. I really liked my idea :-)
> >>>>>
> >>>>>> I have looked at the various call chains:
> >>>>>>
> >>>>>> driver_unregister ->
> >>>>>>     bus_remove_driver ->
> >>>>>>         driver_detach ->
> >>>>>>             device_release_driver_internal
> >>>>>>
> >>>>>> device_unregister ->
> >>>>>>     device_del ->
> >>>>>>         bus_remove_device ->
> >>>>>>             device_release_driver ->
> >>>>>>                 device_release_driver_internal
> >>>>>>
> >>>>>> sysfs: unbind_store ->
> >>>>>>     device_release_driver ->
> >>>>>>         device_release_driver_internal
> >>>>>>
> >>>>>> The only way I've found to differentiate between these in a cleanup
> >>>>>> action is that device_del() uses the bus notifier to signal
> >>>>>> BUS_NOTIFY_DEL_DEVICE before calling bus_remove_device(). Such a
> >>>>>> notifier could be used to set a drm_device->parent_removed flag.
> >>>>>
> >>>>> Hm, this might upset Greg KH's code taste ... maybe there's a better
> >>>>> way to do this, but best to prototype a patch with this, send it to
> >>>>> him and ask how to :-)
> >>>>>
> >>>>
> >>>> I'll leave this to the one that needs it. The tinydrm drivers doesn't
> >>>> need to touch hw after DRM unregister.
> >>>>
> >>>>>> Why is it necessary to call drm_atomic_helper_shutdown() here? Doesn't
> >>>>>> everything get disabled when userspace closes? It does in my tinydrm
> >>>>>> world :-)
> >>>>>
> >>>>> Iirc fbdev/fbcon can result in leaks ... at least we've had patches
> >>>>> where drivers leaked drm_connector and drm_framebuffer objects, and
> >>>>> they've been fixed by calling drm_atomic_helper_shutdown() in the
> >>>>> unload path. Maybe this is cargo-culting, but it goes way back to
> >>>>> pre-atomic, where drivers called drm_helper_force_disable_all().
> >>>>>
> >>>>> If you try to move the fbcon to your tinydrm drivers (con2fb is
> >>>>> apparently the cmdline tool you need, never tried it, I only switch
> >>>>> the kernel's console between fbcon and dummycon and back, not what
> >>>>> fbcon drivers itself), then I think you should be able to reproduce.
> >>>>> And maybe you have a better idea how to deal with this all.
> >>>>>
> >>>>> Note also that there's been proposals floating around to only close an
> >>>>> drm_framebuffer, not also remove it (like the current RMFB ioctl
> >>>>> does), with that closing userspace would not necessarily lead to a
> >>>>> full cleanup.
> >>>>>
> >>>>> Another thing (which doesn't apply to drm_simple_display_pipe drivers)
> >>>>> is if you have the display on, but no planes showing (i.e. all black).
> >>>>> Then all the fbs will be cleaned up, but drm_connector will be
> >>>>> leaking. That's a case where you need drm_atomic_helper_shutdown()
> >>>>> even if fbcon/fbdev isn't even enabled.
> >>>>>
> >>>>
> >>>> Ok, this means that I don't need to call drm_atomic_helper_shutdown() in
> >>>> tinydrm. DRM userspace disables the pipe on close and the generic fbdev
> >>>> emulation also releases everything.
> >>>> Even so, maybe I should use devm_drm_mode_config_reset() after all to
> >>>> keep drivers uniform, to avoid confusion: why doesn't he use it?
> >>>
> >>> Hm maybe there is an official way to solve this, pulling in Greg+Rafael.
> >>>
> >>> Super short summary: We want to start using devm actions to clean up drm
> >>> drivers. Here's the problem:
> >>> - For a driver unload/unbind without hotunplug, we want to properly clean
> >>>   up the hardware and shut it all down.
> >>
> >> Then do it on probe/disconnect.
> >>
> >>> - But if the device is unplugged already, that's probably not the best
> >>>   idea, and we only want to clean up the kernel's resources/allocations.
> >>
> >> Again, probe/disconnect will be called either way.
> >>
> >> But as you note, memory will NOT be freed by the devm stuff if you
> >> manually unbind a driver from a device.
> >>
> >> So don't touch hardware there, it's not going to work :)
> >>
> >>> What's the recommendation here? I see a few options:
> >>>
> >>> - Make sure everything can deal with this properly. Hotunplug can happen
> >>>   anytime, so there's a race no matter what.
> >>
> >> Yes.
> >>
> >>> - Check with the device model whether the struct device is disappearing or
> >>>   whether we're just dealing with a driver unbind (no idea how to do
> >>>   that), and act accordingly.
> >>
> >> You don't know that, sorry.  Just do any hardware stuff on disconnect.
> >> Assuming your hardware is still present :)
> >>
> >>> - Fundamental question: Touching the hw from devm actions, is that ok? If
> >>>   not, then the pretty nifty plan laid out in this thread wont work.
> >>
> >> Nope, that's not going to work, the device could either be long gone, or
> >> you will not be called due to unbind happening from userspace.
> >>
> >> But really, unbind from userspace is very very rare, it's a developer
> >> thing mostly.  Oh and a virtual driver thing, but those people are crazy
> >> :)
> >>
> >>> - Something completely different?
> >>
> >> Do it in disconnect :)
> > 
> > Ah, I forgot to mention the important constraint :-) disconnect/unbind
> > should be the following sequence:
> > 
> > 1. Unregister all the userspace interfaces (there's a lot of them) and
> > make sure all the pending ioctls are done so that from now on
> > userspace sees lots of -EIO (in case it still has fd open, which is
> > going to be the normal for hotunplug.
> > 
> > 2. Shut down hw and all ongoing operations (only relevant for unbind,
> > but needs to be able to cope with sudden hotunplug on top anyway).
> > 
> > 3. Clean up the kernel mess and release everything.
> > 
> > Probe is exactly the other way round, so would perfectly fit into the
> > devm onion cleanup. See in the commented earlier replies above how
> > that would match in details, but tldr; if we have to do 2. in
> > disconnect, then we also have to do 1. in disconnected, and only doing
> > 3. through devm is almost not worth the bother. But if we could do all
> > three through devm then simple drivers wouldn't even need any
> > disconnect/unbind callback at all. That's our motivation for trying to
> > come up with an answer that's not "do it in disconnect". "do it in
> > disconnect" is how we do it all today already.
> > 
> > Yes we're trying to make tiny drivers even smaller, we have enough
> > nowadays that this stuff would be worth it :-)
> > 
> 
> I think a solution is to say that drivers that want to touch hw on
> disconnect needs to use device_driver->remove to do that.
> 
> This is an example driver that doesn't need to touch hw because it's so
> simple that userspace has disabled the pipeline:
> 
> static void drm_driver_release(struct drm_device *drm)
> {
> 	drm_mode_config_cleanup(drm);
> 	drm_dev_fini(drm);
> 	kfree(drm);
> }
> 
> static struct drm_driver drm_driver = {
> 	.release = drm_driver_release,
> 	/* ... */
> };
> 
> static int driver_probe(struct device *dev)
> {
> 	struct drm_device *drm;
> 	int ret;
> 
> 	drm = kzalloc(sizeof(*drm), GFP_KERNEL);
> 	if (!drm)
> 		return -ENOMEM;
> 
> 	ret = devm_drm_dev_init(dev, drm, &drm_driver);
> 	if (ret) {
> 		kfree(drm);
> 		return ret;
> 	}
> 
> 	drm_mode_config_init(drm);
> 
> 	/* Aquire various resources, all managed by devres */
> 
> 	drm_mode_config_reset(drm);
> 
> 	return devm_drm_dev_register(drm);
> }
> 
> struct device_driver driver = {
> 	.probe = driver_probe,
> };
> 
> 
> A driver that wants to touch hardware on disconnect, can look like this:
> 
> static void drm_driver_release(struct drm_device *drm)
> {
> 	drm_mode_config_cleanup(drm);
> 	drm_dev_fini(drm);
> 	kfree(drm);
> }
> 
> static struct drm_driver drm_driver = {
> 	.release = drm_driver_release,
> 	/* ... */
> };
> 
> static int driver_probe(struct device *dev)
> {
> 	struct drm_device *drm;
> 	int ret;
> 
> 	drm = kzalloc(sizeof(*drm), GFP_KERNEL);
> 	if (!drm)
> 		return -ENOMEM;
> 
> 	ret = devm_drm_dev_init(dev, drm, &drm_driver);
> 	if (ret) {
> 		kfree(drm);
> 		return ret;
> 	}
> 
> 	drm_mode_config_init(drm);
> 
> 	/* Aquire various resources, all managed by devres */
> 
> 	drm_mode_config_reset(drm);
> 
> 	ret = drm_dev_register(drm);
> 	if (ret)
> 		return ret;
> 
> 	drm_dev_get(dev); /* If using drm_dev_unplug() */
> 
> 	dev_set_drvdata(dev, drm);
> 
> 	return 0;
> }
> 
> /* This function is called before devres_release_all() */
> static int driver_remove(struct device *dev)
> {
> 	struct drm_device *drm = dev_get_drvdata(dev);
> 
> 	drm_dev_unplug(drm); OR drm_dev_unregister(drm);
> 	drm_atomic_helper_shutdown(drm)
> 
> 	return 0;
> }
> 
> struct device_driver driver = {
> 	.probe = driver_probe,
> 	.remove = driver_remove,

That's exactly the pattern I'm trying to avoid, because imo your tiny
driver _also_ should do this. Or we realize that all the current drivers
doing drm_atomic_helper_shutdown are misguided, but I'm not really
understanding why.

Having a devm helper which cannot be used for some drivers due to
fundamental design issues is kinda not great, because it means everyone
will still use it, and shrug the bugs off as "not my problem". Which is
what's happening right now with all the devm_kzalloc we have in drm
drivers that all the get lifetim wrong. But because devm_ is convenient
everyone uses it, so the driver unload of most drivers is full of bugs.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: devm actions and hw clenaup (was Re: [PATCH 01/11] drm: Add devm_drm_dev_init/register)
  2019-01-29 16:50                         ` Daniel Vetter
@ 2019-01-29 17:26                           ` Noralf Trønnes
  2019-01-29 17:36                           ` Greg KH
  1 sibling, 0 replies; 57+ messages in thread
From: Noralf Trønnes @ 2019-01-29 17:26 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: Greg KH, David Lechner, dri-devel, Rafael J. Wysocki



Den 29.01.2019 17.50, skrev Daniel Vetter:
> On Tue, Jan 29, 2019 at 03:34:46PM +0100, Noralf Trønnes wrote:
>>
>>
>> Den 24.01.2019 18.57, skrev Daniel Vetter:
>>> On Thu, Jan 24, 2019 at 6:46 PM Greg KH <gregkh@linuxfoundation.org> wrote:
>>>>
>>>> On Thu, Jan 24, 2019 at 11:43:12AM +0100, Daniel Vetter wrote:
>>>>> On Wed, Jan 23, 2019 at 11:54:07AM +0100, Noralf Trønnes wrote:
>>>>>>
>>>>>>
>>>>>> Den 22.01.2019 20.30, skrev Daniel Vetter:
>>>>>>> On Tue, Jan 22, 2019 at 8:07 PM Noralf Trønnes <noralf@tronnes.org> wrote:
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> Den 22.01.2019 10.32, skrev Daniel Vetter:
>>>>>>>>> On Mon, Jan 21, 2019 at 01:21:46PM +0100, Noralf Trønnes wrote:
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> Den 21.01.2019 10.55, skrev Daniel Vetter:
>>>>>>>>>>> On Mon, Jan 21, 2019 at 10:10:14AM +0100, Daniel Vetter wrote:
>>>>>>>>>>>> On Sun, Jan 20, 2019 at 12:43:08PM +0100, Noralf Trønnes wrote:
>>>>>>>>>>>>> This adds resource managed (devres) versions of drm_dev_init() and
>>>>>>>>>>>>> drm_dev_register().
>>>>>>>>>>>>>
>>>>>>>>>>>>> Also added is devm_drm_dev_register_with_fbdev() which sets up generic
>>>>>>>>>>>>> fbdev emulation as well.
>>>>>>>>>>>>>
>>>>>>>>>>>>> devm_drm_dev_register() isn't exported since there are no users.
>>>>>>>>>>>>>
>>>>>>>>>>>>> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
>>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> <snip>
>>>>>>>>>>
>>>>>>>>>>>>> diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
>>>>>>>>>>>>> index 381581b01d48..12129772be45 100644
>>>>>>>>>>>>> --- a/drivers/gpu/drm/drm_drv.c
>>>>>>>>>>>>> +++ b/drivers/gpu/drm/drm_drv.c
>>>>>>>>>>>>> @@ -36,6 +36,7 @@
>>>>>>>>>>>>>
>>>>>>>>>>>>>  #include <drm/drm_client.h>
>>>>>>>>>>>>>  #include <drm/drm_drv.h>
>>>>>>>>>>>>> +#include <drm/drm_fb_helper.h>
>>>>>>>>>>>>>  #include <drm/drmP.h>
>>>>>>>>>>>>>
>>>>>>>>>>>>>  #include "drm_crtc_internal.h"
>>>>>>>>>>>>> @@ -871,6 +872,111 @@ void drm_dev_unregister(struct drm_device *dev)
>>>>>>>>>>>>>  }
>>>>>>>>>>>>>  EXPORT_SYMBOL(drm_dev_unregister);
>>>>>>>>>>>>>
>>>>>>>>>>>>> +static void devm_drm_dev_init_release(void *data)
>>>>>>>>>>>>> +{
>>>>>>>>>>>>> + drm_dev_put(data);
>>>>>>>>>>>
>>>>>>>>>>> We need drm_dev_unplug() here, or this isn't safe.
>>>>>>>>>>
>>>>>>>>>> This function is only used to cover the error path if probe fails before
>>>>>>>>>> devm_drm_dev_register() is called. devm_drm_dev_register_release() is
>>>>>>>>>> the one that calls unplug. There are comments about this in the functions.
>>>>>>>>>
>>>>>>>>> I think I get a prize for being ignorant and blind :-/
>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>>> +}
>>>>>>>>>>>>> +
>>>>>>>>>>>>> +/**
>>>>>>>>>>>>> + * devm_drm_dev_init - Resource managed drm_dev_init()
>>>>>>>>>>>>> + * @parent: Parent device object
>>>>>>>>>>>>> + * @dev: DRM device
>>>>>>>>>>>>> + * @driver: DRM driver
>>>>>>>>>>>>> + *
>>>>>>>>>>>>> + * Managed drm_dev_init(). The DRM device initialized with this function is
>>>>>>>>>>>>> + * automatically released on driver detach. You must supply a
>>>>>>>>>>>
>>>>>>>>>>> I think a bit more clarity here would be good:
>>>>>>>>>>>
>>>>>>>>>>> "... automatically released on driver unbind by callind drm_dev_unplug()."
>>>>>>>>>>>
>>>>>>>>>>>>> + * &drm_driver.release callback to control the finalization explicitly.
>>>>>>>>>>>
>>>>>>>>>>> I think a loud warning for these is in order:
>>>>>>>>>>>
>>>>>>>>>>> "WARNING:
>>>>>>>>>>>
>>>>>>>>>>> "In generally it is unsafe to use devm functions for drm structures
>>>>>>>>>>> because the lifetimes of &drm_device and the underlying &device do not
>>>>>>>>>>> match. This here works because it doesn't immediately free anything, but
>>>>>>>>>>> only calls drm_dev_unplug(), which internally decrements the &drm_device
>>>>>>>>>>> refcount through drm_dev_put().
>>>>>>>>>>>
>>>>>>>>>>> "All other drm structures must still be explicitly released in the
>>>>>>>>>>> &drm_driver.release callback."
>>>>>>>>>>>
>>>>>>>>>>> While thinking about this I just realized that with this design we have no
>>>>>>>>>>> good place to call drm_atomic_helper_shutdown(). Which we need to, or all
>>>>>>>>>>> kinds of things will leak badly (connectors, fb, ...), but there's no
>>>>>>>>>>> place to call it:
>>>>>>>>>>> - unbind is too early, since we haven't yet called drm_dev_unplug, and the
>>>>>>>>>>>   drm_dev_unregister in there must be called _before_ we start to shut
>>>>>>>>>>>   down anything.
>>>>>>>>>>> - drm_driver.release is way too late.
>>>>>>>>>>>
>>>>>>>>>>> Ofc for a real hotunplug there's no point in shutting down the hw (it's
>>>>>>>>>>> already gone), but for a driver unload/unbind it would be nice if this
>>>>>>>>>>> happens automatically and in the right order.
>>>>>>>>>>>
>>>>>>>>>>> So not sure what to do here really.
>>>>>>>>>>
>>>>>>>>>> How about this change: (it breaks the rule of pulling helpers into the
>>>>>>>>>> core, so maybe we should put the devm_ functions into the simple KMS
>>>>>>>>>> helper instead?)
>>>>>>>>>
>>>>>>>>> Yeah smells a bit much like midlayer ... What would work is having a pile
>>>>>>>>> more devm_ helper functions, so that we onion-unwrap everything correctly,
>>>>>>>>> and in the right order. So:
>>>>>>>>>
>>>>>>>>> - devm_drm_dev_init (always does a drm_dev_put())
>>>>>>>>>
>>>>>>>>> - devm_drm_poll_enable (shuts down the poll helper with a devm action)
>>>>>>>>>
>>>>>>>>> - devm_drm_mode_config_reset (does an atomic_helper_shutdown() as it's cleanup action)
>>>>>>>>>
>>>>>>>>> - devm_drm_dev_register (grabs an additional drm_dev_get() reference so it
>>>>>>>>>   can call drm_dev_unplug() unconditionally).
>>>>>>>>>
>>>>>>>>
>>>>>>>> Beautiful! I really like this, it's very flexible.
>>>>>>>>
>>>>>>>> Where should devm_drm_mode_config_reset() live? It will pull in the
>>>>>>>> atomic helper...
>>>>>>>
>>>>>>> I think a new drm_devm.c helper would be nice for all this stuff.
>>>>>>> Especially since you can't freely mix devm-based setup/cleanup with
>>>>>>> normal cleanup I think it'd be good to have it all together in one
>>>>>>> place. And perhaps even a code example in the DOC: overview.
>>>>>>>
>>>>>>>>> We'd need to make sure some of the cleanup actions dtrt when the device is
>>>>>>>>> gone, but I think we can achieve that by liberally sprinkling
>>>>>>>>> drm_dev_enter/exit over them, e.g. the the cleanup action for
>>>>>>>>> drm_mode_config_reset would be:
>>>>>>>>>
>>>>>>>>> {
>>>>>>>>>       if (drm_dev_enter())
>>>>>>>>>               return;
>>>>>>>>>
>>>>>>>>>       drm_atomic_helper_shutdown();
>>>>>>>>>
>>>>>>>>>       drm_dev_exit();
>>>>>>>>> }
>>>>>>>>>
>>>>>>>>
>>>>>>>> drm_dev_enter() can only be used to check whether the drm_device is
>>>>>>>> registered or not, it doesn't say anything about the state of the parent
>>>>>>>> device.
>>>>>>>>
>>>>>>>> All we know is that the device is being unbound from the driver, we
>>>>>>>> don't know if it's the device that's being removed or if it's the driver
>>>>>>>> that's unregistered.
>>>>>>>
>>>>>>> You're right, both paths will have called drm_dev_unplug by then.
>>>>>>> Silly me. I really liked my idea :-)
>>>>>>>
>>>>>>>> I have looked at the various call chains:
>>>>>>>>
>>>>>>>> driver_unregister ->
>>>>>>>>     bus_remove_driver ->
>>>>>>>>         driver_detach ->
>>>>>>>>             device_release_driver_internal
>>>>>>>>
>>>>>>>> device_unregister ->
>>>>>>>>     device_del ->
>>>>>>>>         bus_remove_device ->
>>>>>>>>             device_release_driver ->
>>>>>>>>                 device_release_driver_internal
>>>>>>>>
>>>>>>>> sysfs: unbind_store ->
>>>>>>>>     device_release_driver ->
>>>>>>>>         device_release_driver_internal
>>>>>>>>
>>>>>>>> The only way I've found to differentiate between these in a cleanup
>>>>>>>> action is that device_del() uses the bus notifier to signal
>>>>>>>> BUS_NOTIFY_DEL_DEVICE before calling bus_remove_device(). Such a
>>>>>>>> notifier could be used to set a drm_device->parent_removed flag.
>>>>>>>
>>>>>>> Hm, this might upset Greg KH's code taste ... maybe there's a better
>>>>>>> way to do this, but best to prototype a patch with this, send it to
>>>>>>> him and ask how to :-)
>>>>>>>
>>>>>>
>>>>>> I'll leave this to the one that needs it. The tinydrm drivers doesn't
>>>>>> need to touch hw after DRM unregister.
>>>>>>
>>>>>>>> Why is it necessary to call drm_atomic_helper_shutdown() here? Doesn't
>>>>>>>> everything get disabled when userspace closes? It does in my tinydrm
>>>>>>>> world :-)
>>>>>>>
>>>>>>> Iirc fbdev/fbcon can result in leaks ... at least we've had patches
>>>>>>> where drivers leaked drm_connector and drm_framebuffer objects, and
>>>>>>> they've been fixed by calling drm_atomic_helper_shutdown() in the
>>>>>>> unload path. Maybe this is cargo-culting, but it goes way back to
>>>>>>> pre-atomic, where drivers called drm_helper_force_disable_all().
>>>>>>>
>>>>>>> If you try to move the fbcon to your tinydrm drivers (con2fb is
>>>>>>> apparently the cmdline tool you need, never tried it, I only switch
>>>>>>> the kernel's console between fbcon and dummycon and back, not what
>>>>>>> fbcon drivers itself), then I think you should be able to reproduce.
>>>>>>> And maybe you have a better idea how to deal with this all.
>>>>>>>
>>>>>>> Note also that there's been proposals floating around to only close an
>>>>>>> drm_framebuffer, not also remove it (like the current RMFB ioctl
>>>>>>> does), with that closing userspace would not necessarily lead to a
>>>>>>> full cleanup.
>>>>>>>
>>>>>>> Another thing (which doesn't apply to drm_simple_display_pipe drivers)
>>>>>>> is if you have the display on, but no planes showing (i.e. all black).
>>>>>>> Then all the fbs will be cleaned up, but drm_connector will be
>>>>>>> leaking. That's a case where you need drm_atomic_helper_shutdown()
>>>>>>> even if fbcon/fbdev isn't even enabled.
>>>>>>>
>>>>>>
>>>>>> Ok, this means that I don't need to call drm_atomic_helper_shutdown() in
>>>>>> tinydrm. DRM userspace disables the pipe on close and the generic fbdev
>>>>>> emulation also releases everything.
>>>>>> Even so, maybe I should use devm_drm_mode_config_reset() after all to
>>>>>> keep drivers uniform, to avoid confusion: why doesn't he use it?
>>>>>
>>>>> Hm maybe there is an official way to solve this, pulling in Greg+Rafael.
>>>>>
>>>>> Super short summary: We want to start using devm actions to clean up drm
>>>>> drivers. Here's the problem:
>>>>> - For a driver unload/unbind without hotunplug, we want to properly clean
>>>>>   up the hardware and shut it all down.
>>>>
>>>> Then do it on probe/disconnect.
>>>>
>>>>> - But if the device is unplugged already, that's probably not the best
>>>>>   idea, and we only want to clean up the kernel's resources/allocations.
>>>>
>>>> Again, probe/disconnect will be called either way.
>>>>
>>>> But as you note, memory will NOT be freed by the devm stuff if you
>>>> manually unbind a driver from a device.
>>>>
>>>> So don't touch hardware there, it's not going to work :)
>>>>
>>>>> What's the recommendation here? I see a few options:
>>>>>
>>>>> - Make sure everything can deal with this properly. Hotunplug can happen
>>>>>   anytime, so there's a race no matter what.
>>>>
>>>> Yes.
>>>>
>>>>> - Check with the device model whether the struct device is disappearing or
>>>>>   whether we're just dealing with a driver unbind (no idea how to do
>>>>>   that), and act accordingly.
>>>>
>>>> You don't know that, sorry.  Just do any hardware stuff on disconnect.
>>>> Assuming your hardware is still present :)
>>>>
>>>>> - Fundamental question: Touching the hw from devm actions, is that ok? If
>>>>>   not, then the pretty nifty plan laid out in this thread wont work.
>>>>
>>>> Nope, that's not going to work, the device could either be long gone, or
>>>> you will not be called due to unbind happening from userspace.
>>>>
>>>> But really, unbind from userspace is very very rare, it's a developer
>>>> thing mostly.  Oh and a virtual driver thing, but those people are crazy
>>>> :)
>>>>
>>>>> - Something completely different?
>>>>
>>>> Do it in disconnect :)
>>>
>>> Ah, I forgot to mention the important constraint :-) disconnect/unbind
>>> should be the following sequence:
>>>
>>> 1. Unregister all the userspace interfaces (there's a lot of them) and
>>> make sure all the pending ioctls are done so that from now on
>>> userspace sees lots of -EIO (in case it still has fd open, which is
>>> going to be the normal for hotunplug.
>>>
>>> 2. Shut down hw and all ongoing operations (only relevant for unbind,
>>> but needs to be able to cope with sudden hotunplug on top anyway).
>>>
>>> 3. Clean up the kernel mess and release everything.
>>>
>>> Probe is exactly the other way round, so would perfectly fit into the
>>> devm onion cleanup. See in the commented earlier replies above how
>>> that would match in details, but tldr; if we have to do 2. in
>>> disconnect, then we also have to do 1. in disconnected, and only doing
>>> 3. through devm is almost not worth the bother. But if we could do all
>>> three through devm then simple drivers wouldn't even need any
>>> disconnect/unbind callback at all. That's our motivation for trying to
>>> come up with an answer that's not "do it in disconnect". "do it in
>>> disconnect" is how we do it all today already.
>>>
>>> Yes we're trying to make tiny drivers even smaller, we have enough
>>> nowadays that this stuff would be worth it :-)
>>>
>>
>> I think a solution is to say that drivers that want to touch hw on
>> disconnect needs to use device_driver->remove to do that.
>>
>> This is an example driver that doesn't need to touch hw because it's so
>> simple that userspace has disabled the pipeline:
>>
>> static void drm_driver_release(struct drm_device *drm)
>> {
>> 	drm_mode_config_cleanup(drm);
>> 	drm_dev_fini(drm);
>> 	kfree(drm);
>> }
>>
>> static struct drm_driver drm_driver = {
>> 	.release = drm_driver_release,
>> 	/* ... */
>> };
>>
>> static int driver_probe(struct device *dev)
>> {
>> 	struct drm_device *drm;
>> 	int ret;
>>
>> 	drm = kzalloc(sizeof(*drm), GFP_KERNEL);
>> 	if (!drm)
>> 		return -ENOMEM;
>>
>> 	ret = devm_drm_dev_init(dev, drm, &drm_driver);
>> 	if (ret) {
>> 		kfree(drm);
>> 		return ret;
>> 	}
>>
>> 	drm_mode_config_init(drm);
>>
>> 	/* Aquire various resources, all managed by devres */
>>
>> 	drm_mode_config_reset(drm);
>>
>> 	return devm_drm_dev_register(drm);
>> }
>>
>> struct device_driver driver = {
>> 	.probe = driver_probe,
>> };
>>
>>
>> A driver that wants to touch hardware on disconnect, can look like this:
>>
>> static void drm_driver_release(struct drm_device *drm)
>> {
>> 	drm_mode_config_cleanup(drm);
>> 	drm_dev_fini(drm);
>> 	kfree(drm);
>> }
>>
>> static struct drm_driver drm_driver = {
>> 	.release = drm_driver_release,
>> 	/* ... */
>> };
>>
>> static int driver_probe(struct device *dev)
>> {
>> 	struct drm_device *drm;
>> 	int ret;
>>
>> 	drm = kzalloc(sizeof(*drm), GFP_KERNEL);
>> 	if (!drm)
>> 		return -ENOMEM;
>>
>> 	ret = devm_drm_dev_init(dev, drm, &drm_driver);
>> 	if (ret) {
>> 		kfree(drm);
>> 		return ret;
>> 	}
>>
>> 	drm_mode_config_init(drm);
>>
>> 	/* Aquire various resources, all managed by devres */
>>
>> 	drm_mode_config_reset(drm);
>>
>> 	ret = drm_dev_register(drm);
>> 	if (ret)
>> 		return ret;
>>
>> 	drm_dev_get(dev); /* If using drm_dev_unplug() */
>>
>> 	dev_set_drvdata(dev, drm);
>>
>> 	return 0;
>> }
>>
>> /* This function is called before devres_release_all() */
>> static int driver_remove(struct device *dev)
>> {
>> 	struct drm_device *drm = dev_get_drvdata(dev);
>>
>> 	drm_dev_unplug(drm); OR drm_dev_unregister(drm);
>> 	drm_atomic_helper_shutdown(drm)
>>
>> 	return 0;
>> }
>>
>> struct device_driver driver = {
>> 	.probe = driver_probe,
>> 	.remove = driver_remove,
> 
> That's exactly the pattern I'm trying to avoid, because imo your tiny
> driver _also_ should do this. Or we realize that all the current drivers
> doing drm_atomic_helper_shutdown are misguided, but I'm not really
> understanding why.
> 

I don't know where my head has been, the pipeline on tinydrm isn't
disabled on driver module unload. I've so preoccupied with ensuring that
device removal is working that I must have had some kind of tunnel vision.

The problem is that fbdev is restored on lastclose regardless of it
being in use or not.

I tried to address it in this patch:

drm/fb-helper/generic: Only restore when in use
https://patchwork.freedesktop.org/patch/263271/

> Having a devm helper which cannot be used for some drivers due to
> fundamental design issues is kinda not great, because it means everyone
> will still use it, and shrug the bugs off as "not my problem". Which is
> what's happening right now with all the devm_kzalloc we have in drm
> drivers that all the get lifetim wrong. But because devm_ is convenient
> everyone uses it, so the driver unload of most drivers is full of bugs.

Yes, it's starting to look like this devm idea isn't such a great thing
after all.

And it looks like dropping DRM devm can still give a slim driver:

static int driver_probe(struct device *dev)
{
	struct drm_device *drm;
	int ret;

	drm = kzalloc(sizeof(*drm), GFP_KERNEL);
	if (!drm)
		return -ENOMEM;

	ret = drm_dev_init(dev, drm, &drm_driver);
	if (ret) {
		kfree(drm);
		return ret;
	}

	drm_mode_config_init(drm);

	/*
	 * Aquire various resources, all managed by devres
	 * or being released in drm_driver->release.
	 * goto err_put on failure
	 */

	drm_mode_config_reset(drm);

	ret = drm_dev_register(drm);
	if (ret)
		goto err_put;

	dev_set_drvdata(dev, drm);

	return 0;

err_put:
	drm_dev_put(dev);

	return ret
}

static int driver_remove(struct device *dev)
{
	struct drm_device *drm = dev_get_drvdata(dev);

	drm_atomic_helper_shutdown(drm)
	drm_dev_unplug(drm);

	return 0;
}

Noralf.
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: devm actions and hw clenaup (was Re: [PATCH 01/11] drm: Add devm_drm_dev_init/register)
  2019-01-29 16:50                         ` Daniel Vetter
  2019-01-29 17:26                           ` Noralf Trønnes
@ 2019-01-29 17:36                           ` Greg KH
  2019-01-29 18:10                             ` Daniel Vetter
  1 sibling, 1 reply; 57+ messages in thread
From: Greg KH @ 2019-01-29 17:36 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: David Lechner, dri-devel, Rafael J. Wysocki

On Tue, Jan 29, 2019 at 05:50:05PM +0100, Daniel Vetter wrote:
> On Tue, Jan 29, 2019 at 03:34:46PM +0100, Noralf Trønnes wrote:
> > 
> > 
> > Den 24.01.2019 18.57, skrev Daniel Vetter:
> > > On Thu, Jan 24, 2019 at 6:46 PM Greg KH <gregkh@linuxfoundation.org> wrote:
> > >>
> > >> On Thu, Jan 24, 2019 at 11:43:12AM +0100, Daniel Vetter wrote:
> > >>> On Wed, Jan 23, 2019 at 11:54:07AM +0100, Noralf Trønnes wrote:
> > >>>>
> > >>>>
> > >>>> Den 22.01.2019 20.30, skrev Daniel Vetter:
> > >>>>> On Tue, Jan 22, 2019 at 8:07 PM Noralf Trønnes <noralf@tronnes.org> wrote:
> > >>>>>>
> > >>>>>>
> > >>>>>>
> > >>>>>> Den 22.01.2019 10.32, skrev Daniel Vetter:
> > >>>>>>> On Mon, Jan 21, 2019 at 01:21:46PM +0100, Noralf Trønnes wrote:
> > >>>>>>>>
> > >>>>>>>>
> > >>>>>>>> Den 21.01.2019 10.55, skrev Daniel Vetter:
> > >>>>>>>>> On Mon, Jan 21, 2019 at 10:10:14AM +0100, Daniel Vetter wrote:
> > >>>>>>>>>> On Sun, Jan 20, 2019 at 12:43:08PM +0100, Noralf Trønnes wrote:
> > >>>>>>>>>>> This adds resource managed (devres) versions of drm_dev_init() and
> > >>>>>>>>>>> drm_dev_register().
> > >>>>>>>>>>>
> > >>>>>>>>>>> Also added is devm_drm_dev_register_with_fbdev() which sets up generic
> > >>>>>>>>>>> fbdev emulation as well.
> > >>>>>>>>>>>
> > >>>>>>>>>>> devm_drm_dev_register() isn't exported since there are no users.
> > >>>>>>>>>>>
> > >>>>>>>>>>> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
> > >>>>>>>>>>
> > >>>>>>>>
> > >>>>>>>> <snip>
> > >>>>>>>>
> > >>>>>>>>>>> diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
> > >>>>>>>>>>> index 381581b01d48..12129772be45 100644
> > >>>>>>>>>>> --- a/drivers/gpu/drm/drm_drv.c
> > >>>>>>>>>>> +++ b/drivers/gpu/drm/drm_drv.c
> > >>>>>>>>>>> @@ -36,6 +36,7 @@
> > >>>>>>>>>>>
> > >>>>>>>>>>>  #include <drm/drm_client.h>
> > >>>>>>>>>>>  #include <drm/drm_drv.h>
> > >>>>>>>>>>> +#include <drm/drm_fb_helper.h>
> > >>>>>>>>>>>  #include <drm/drmP.h>
> > >>>>>>>>>>>
> > >>>>>>>>>>>  #include "drm_crtc_internal.h"
> > >>>>>>>>>>> @@ -871,6 +872,111 @@ void drm_dev_unregister(struct drm_device *dev)
> > >>>>>>>>>>>  }
> > >>>>>>>>>>>  EXPORT_SYMBOL(drm_dev_unregister);
> > >>>>>>>>>>>
> > >>>>>>>>>>> +static void devm_drm_dev_init_release(void *data)
> > >>>>>>>>>>> +{
> > >>>>>>>>>>> + drm_dev_put(data);
> > >>>>>>>>>
> > >>>>>>>>> We need drm_dev_unplug() here, or this isn't safe.
> > >>>>>>>>
> > >>>>>>>> This function is only used to cover the error path if probe fails before
> > >>>>>>>> devm_drm_dev_register() is called. devm_drm_dev_register_release() is
> > >>>>>>>> the one that calls unplug. There are comments about this in the functions.
> > >>>>>>>
> > >>>>>>> I think I get a prize for being ignorant and blind :-/
> > >>>>>>>
> > >>>>>>>>
> > >>>>>>>>>
> > >>>>>>>>>>> +}
> > >>>>>>>>>>> +
> > >>>>>>>>>>> +/**
> > >>>>>>>>>>> + * devm_drm_dev_init - Resource managed drm_dev_init()
> > >>>>>>>>>>> + * @parent: Parent device object
> > >>>>>>>>>>> + * @dev: DRM device
> > >>>>>>>>>>> + * @driver: DRM driver
> > >>>>>>>>>>> + *
> > >>>>>>>>>>> + * Managed drm_dev_init(). The DRM device initialized with this function is
> > >>>>>>>>>>> + * automatically released on driver detach. You must supply a
> > >>>>>>>>>
> > >>>>>>>>> I think a bit more clarity here would be good:
> > >>>>>>>>>
> > >>>>>>>>> "... automatically released on driver unbind by callind drm_dev_unplug()."
> > >>>>>>>>>
> > >>>>>>>>>>> + * &drm_driver.release callback to control the finalization explicitly.
> > >>>>>>>>>
> > >>>>>>>>> I think a loud warning for these is in order:
> > >>>>>>>>>
> > >>>>>>>>> "WARNING:
> > >>>>>>>>>
> > >>>>>>>>> "In generally it is unsafe to use devm functions for drm structures
> > >>>>>>>>> because the lifetimes of &drm_device and the underlying &device do not
> > >>>>>>>>> match. This here works because it doesn't immediately free anything, but
> > >>>>>>>>> only calls drm_dev_unplug(), which internally decrements the &drm_device
> > >>>>>>>>> refcount through drm_dev_put().
> > >>>>>>>>>
> > >>>>>>>>> "All other drm structures must still be explicitly released in the
> > >>>>>>>>> &drm_driver.release callback."
> > >>>>>>>>>
> > >>>>>>>>> While thinking about this I just realized that with this design we have no
> > >>>>>>>>> good place to call drm_atomic_helper_shutdown(). Which we need to, or all
> > >>>>>>>>> kinds of things will leak badly (connectors, fb, ...), but there's no
> > >>>>>>>>> place to call it:
> > >>>>>>>>> - unbind is too early, since we haven't yet called drm_dev_unplug, and the
> > >>>>>>>>>   drm_dev_unregister in there must be called _before_ we start to shut
> > >>>>>>>>>   down anything.
> > >>>>>>>>> - drm_driver.release is way too late.
> > >>>>>>>>>
> > >>>>>>>>> Ofc for a real hotunplug there's no point in shutting down the hw (it's
> > >>>>>>>>> already gone), but for a driver unload/unbind it would be nice if this
> > >>>>>>>>> happens automatically and in the right order.
> > >>>>>>>>>
> > >>>>>>>>> So not sure what to do here really.
> > >>>>>>>>
> > >>>>>>>> How about this change: (it breaks the rule of pulling helpers into the
> > >>>>>>>> core, so maybe we should put the devm_ functions into the simple KMS
> > >>>>>>>> helper instead?)
> > >>>>>>>
> > >>>>>>> Yeah smells a bit much like midlayer ... What would work is having a pile
> > >>>>>>> more devm_ helper functions, so that we onion-unwrap everything correctly,
> > >>>>>>> and in the right order. So:
> > >>>>>>>
> > >>>>>>> - devm_drm_dev_init (always does a drm_dev_put())
> > >>>>>>>
> > >>>>>>> - devm_drm_poll_enable (shuts down the poll helper with a devm action)
> > >>>>>>>
> > >>>>>>> - devm_drm_mode_config_reset (does an atomic_helper_shutdown() as it's cleanup action)
> > >>>>>>>
> > >>>>>>> - devm_drm_dev_register (grabs an additional drm_dev_get() reference so it
> > >>>>>>>   can call drm_dev_unplug() unconditionally).
> > >>>>>>>
> > >>>>>>
> > >>>>>> Beautiful! I really like this, it's very flexible.
> > >>>>>>
> > >>>>>> Where should devm_drm_mode_config_reset() live? It will pull in the
> > >>>>>> atomic helper...
> > >>>>>
> > >>>>> I think a new drm_devm.c helper would be nice for all this stuff.
> > >>>>> Especially since you can't freely mix devm-based setup/cleanup with
> > >>>>> normal cleanup I think it'd be good to have it all together in one
> > >>>>> place. And perhaps even a code example in the DOC: overview.
> > >>>>>
> > >>>>>>> We'd need to make sure some of the cleanup actions dtrt when the device is
> > >>>>>>> gone, but I think we can achieve that by liberally sprinkling
> > >>>>>>> drm_dev_enter/exit over them, e.g. the the cleanup action for
> > >>>>>>> drm_mode_config_reset would be:
> > >>>>>>>
> > >>>>>>> {
> > >>>>>>>       if (drm_dev_enter())
> > >>>>>>>               return;
> > >>>>>>>
> > >>>>>>>       drm_atomic_helper_shutdown();
> > >>>>>>>
> > >>>>>>>       drm_dev_exit();
> > >>>>>>> }
> > >>>>>>>
> > >>>>>>
> > >>>>>> drm_dev_enter() can only be used to check whether the drm_device is
> > >>>>>> registered or not, it doesn't say anything about the state of the parent
> > >>>>>> device.
> > >>>>>>
> > >>>>>> All we know is that the device is being unbound from the driver, we
> > >>>>>> don't know if it's the device that's being removed or if it's the driver
> > >>>>>> that's unregistered.
> > >>>>>
> > >>>>> You're right, both paths will have called drm_dev_unplug by then.
> > >>>>> Silly me. I really liked my idea :-)
> > >>>>>
> > >>>>>> I have looked at the various call chains:
> > >>>>>>
> > >>>>>> driver_unregister ->
> > >>>>>>     bus_remove_driver ->
> > >>>>>>         driver_detach ->
> > >>>>>>             device_release_driver_internal
> > >>>>>>
> > >>>>>> device_unregister ->
> > >>>>>>     device_del ->
> > >>>>>>         bus_remove_device ->
> > >>>>>>             device_release_driver ->
> > >>>>>>                 device_release_driver_internal
> > >>>>>>
> > >>>>>> sysfs: unbind_store ->
> > >>>>>>     device_release_driver ->
> > >>>>>>         device_release_driver_internal
> > >>>>>>
> > >>>>>> The only way I've found to differentiate between these in a cleanup
> > >>>>>> action is that device_del() uses the bus notifier to signal
> > >>>>>> BUS_NOTIFY_DEL_DEVICE before calling bus_remove_device(). Such a
> > >>>>>> notifier could be used to set a drm_device->parent_removed flag.
> > >>>>>
> > >>>>> Hm, this might upset Greg KH's code taste ... maybe there's a better
> > >>>>> way to do this, but best to prototype a patch with this, send it to
> > >>>>> him and ask how to :-)
> > >>>>>
> > >>>>
> > >>>> I'll leave this to the one that needs it. The tinydrm drivers doesn't
> > >>>> need to touch hw after DRM unregister.
> > >>>>
> > >>>>>> Why is it necessary to call drm_atomic_helper_shutdown() here? Doesn't
> > >>>>>> everything get disabled when userspace closes? It does in my tinydrm
> > >>>>>> world :-)
> > >>>>>
> > >>>>> Iirc fbdev/fbcon can result in leaks ... at least we've had patches
> > >>>>> where drivers leaked drm_connector and drm_framebuffer objects, and
> > >>>>> they've been fixed by calling drm_atomic_helper_shutdown() in the
> > >>>>> unload path. Maybe this is cargo-culting, but it goes way back to
> > >>>>> pre-atomic, where drivers called drm_helper_force_disable_all().
> > >>>>>
> > >>>>> If you try to move the fbcon to your tinydrm drivers (con2fb is
> > >>>>> apparently the cmdline tool you need, never tried it, I only switch
> > >>>>> the kernel's console between fbcon and dummycon and back, not what
> > >>>>> fbcon drivers itself), then I think you should be able to reproduce.
> > >>>>> And maybe you have a better idea how to deal with this all.
> > >>>>>
> > >>>>> Note also that there's been proposals floating around to only close an
> > >>>>> drm_framebuffer, not also remove it (like the current RMFB ioctl
> > >>>>> does), with that closing userspace would not necessarily lead to a
> > >>>>> full cleanup.
> > >>>>>
> > >>>>> Another thing (which doesn't apply to drm_simple_display_pipe drivers)
> > >>>>> is if you have the display on, but no planes showing (i.e. all black).
> > >>>>> Then all the fbs will be cleaned up, but drm_connector will be
> > >>>>> leaking. That's a case where you need drm_atomic_helper_shutdown()
> > >>>>> even if fbcon/fbdev isn't even enabled.
> > >>>>>
> > >>>>
> > >>>> Ok, this means that I don't need to call drm_atomic_helper_shutdown() in
> > >>>> tinydrm. DRM userspace disables the pipe on close and the generic fbdev
> > >>>> emulation also releases everything.
> > >>>> Even so, maybe I should use devm_drm_mode_config_reset() after all to
> > >>>> keep drivers uniform, to avoid confusion: why doesn't he use it?
> > >>>
> > >>> Hm maybe there is an official way to solve this, pulling in Greg+Rafael.
> > >>>
> > >>> Super short summary: We want to start using devm actions to clean up drm
> > >>> drivers. Here's the problem:
> > >>> - For a driver unload/unbind without hotunplug, we want to properly clean
> > >>>   up the hardware and shut it all down.
> > >>
> > >> Then do it on probe/disconnect.
> > >>
> > >>> - But if the device is unplugged already, that's probably not the best
> > >>>   idea, and we only want to clean up the kernel's resources/allocations.
> > >>
> > >> Again, probe/disconnect will be called either way.
> > >>
> > >> But as you note, memory will NOT be freed by the devm stuff if you
> > >> manually unbind a driver from a device.
> > >>
> > >> So don't touch hardware there, it's not going to work :)
> > >>
> > >>> What's the recommendation here? I see a few options:
> > >>>
> > >>> - Make sure everything can deal with this properly. Hotunplug can happen
> > >>>   anytime, so there's a race no matter what.
> > >>
> > >> Yes.
> > >>
> > >>> - Check with the device model whether the struct device is disappearing or
> > >>>   whether we're just dealing with a driver unbind (no idea how to do
> > >>>   that), and act accordingly.
> > >>
> > >> You don't know that, sorry.  Just do any hardware stuff on disconnect.
> > >> Assuming your hardware is still present :)
> > >>
> > >>> - Fundamental question: Touching the hw from devm actions, is that ok? If
> > >>>   not, then the pretty nifty plan laid out in this thread wont work.
> > >>
> > >> Nope, that's not going to work, the device could either be long gone, or
> > >> you will not be called due to unbind happening from userspace.
> > >>
> > >> But really, unbind from userspace is very very rare, it's a developer
> > >> thing mostly.  Oh and a virtual driver thing, but those people are crazy
> > >> :)
> > >>
> > >>> - Something completely different?
> > >>
> > >> Do it in disconnect :)
> > > 
> > > Ah, I forgot to mention the important constraint :-) disconnect/unbind
> > > should be the following sequence:
> > > 
> > > 1. Unregister all the userspace interfaces (there's a lot of them) and
> > > make sure all the pending ioctls are done so that from now on
> > > userspace sees lots of -EIO (in case it still has fd open, which is
> > > going to be the normal for hotunplug.
> > > 
> > > 2. Shut down hw and all ongoing operations (only relevant for unbind,
> > > but needs to be able to cope with sudden hotunplug on top anyway).
> > > 
> > > 3. Clean up the kernel mess and release everything.
> > > 
> > > Probe is exactly the other way round, so would perfectly fit into the
> > > devm onion cleanup. See in the commented earlier replies above how
> > > that would match in details, but tldr; if we have to do 2. in
> > > disconnect, then we also have to do 1. in disconnected, and only doing
> > > 3. through devm is almost not worth the bother. But if we could do all
> > > three through devm then simple drivers wouldn't even need any
> > > disconnect/unbind callback at all. That's our motivation for trying to
> > > come up with an answer that's not "do it in disconnect". "do it in
> > > disconnect" is how we do it all today already.
> > > 
> > > Yes we're trying to make tiny drivers even smaller, we have enough
> > > nowadays that this stuff would be worth it :-)
> > > 
> > 
> > I think a solution is to say that drivers that want to touch hw on
> > disconnect needs to use device_driver->remove to do that.
> > 
> > This is an example driver that doesn't need to touch hw because it's so
> > simple that userspace has disabled the pipeline:
> > 
> > static void drm_driver_release(struct drm_device *drm)
> > {
> > 	drm_mode_config_cleanup(drm);
> > 	drm_dev_fini(drm);
> > 	kfree(drm);
> > }
> > 
> > static struct drm_driver drm_driver = {
> > 	.release = drm_driver_release,
> > 	/* ... */
> > };
> > 
> > static int driver_probe(struct device *dev)
> > {
> > 	struct drm_device *drm;
> > 	int ret;
> > 
> > 	drm = kzalloc(sizeof(*drm), GFP_KERNEL);
> > 	if (!drm)
> > 		return -ENOMEM;
> > 
> > 	ret = devm_drm_dev_init(dev, drm, &drm_driver);
> > 	if (ret) {
> > 		kfree(drm);
> > 		return ret;
> > 	}
> > 
> > 	drm_mode_config_init(drm);
> > 
> > 	/* Aquire various resources, all managed by devres */
> > 
> > 	drm_mode_config_reset(drm);
> > 
> > 	return devm_drm_dev_register(drm);
> > }
> > 
> > struct device_driver driver = {
> > 	.probe = driver_probe,
> > };
> > 
> > 
> > A driver that wants to touch hardware on disconnect, can look like this:
> > 
> > static void drm_driver_release(struct drm_device *drm)
> > {
> > 	drm_mode_config_cleanup(drm);
> > 	drm_dev_fini(drm);
> > 	kfree(drm);
> > }
> > 
> > static struct drm_driver drm_driver = {
> > 	.release = drm_driver_release,
> > 	/* ... */
> > };
> > 
> > static int driver_probe(struct device *dev)
> > {
> > 	struct drm_device *drm;
> > 	int ret;
> > 
> > 	drm = kzalloc(sizeof(*drm), GFP_KERNEL);
> > 	if (!drm)
> > 		return -ENOMEM;
> > 
> > 	ret = devm_drm_dev_init(dev, drm, &drm_driver);
> > 	if (ret) {
> > 		kfree(drm);
> > 		return ret;
> > 	}
> > 
> > 	drm_mode_config_init(drm);
> > 
> > 	/* Aquire various resources, all managed by devres */
> > 
> > 	drm_mode_config_reset(drm);
> > 
> > 	ret = drm_dev_register(drm);
> > 	if (ret)
> > 		return ret;
> > 
> > 	drm_dev_get(dev); /* If using drm_dev_unplug() */
> > 
> > 	dev_set_drvdata(dev, drm);
> > 
> > 	return 0;
> > }
> > 
> > /* This function is called before devres_release_all() */
> > static int driver_remove(struct device *dev)
> > {
> > 	struct drm_device *drm = dev_get_drvdata(dev);
> > 
> > 	drm_dev_unplug(drm); OR drm_dev_unregister(drm);
> > 	drm_atomic_helper_shutdown(drm)
> > 
> > 	return 0;
> > }
> > 
> > struct device_driver driver = {
> > 	.probe = driver_probe,
> > 	.remove = driver_remove,
> 
> That's exactly the pattern I'm trying to avoid, because imo your tiny
> driver _also_ should do this. Or we realize that all the current drivers
> doing drm_atomic_helper_shutdown are misguided, but I'm not really
> understanding why.
> 
> Having a devm helper which cannot be used for some drivers due to
> fundamental design issues is kinda not great, because it means everyone
> will still use it, and shrug the bugs off as "not my problem". Which is
> what's happening right now with all the devm_kzalloc we have in drm
> drivers that all the get lifetim wrong. But because devm_ is convenient
> everyone uses it, so the driver unload of most drivers is full of bugs.

driver "unload" should not be full of bugs, how would it?  Anything
created with devm_() will just be freed when the device really goes away
in the system, it shouldn't call back into the driver.

And yes, devm_() is a crutch, one that I really don't like, but I
understand the apeal.  And in 95% of the cases, it can work just fine as
no one ever does unbind/bind from userspace, and if they did, they would
never notice the memory issues.

sorry,

greg k-h
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: devm actions and hw clenaup (was Re: [PATCH 01/11] drm: Add devm_drm_dev_init/register)
  2019-01-29 17:36                           ` Greg KH
@ 2019-01-29 18:10                             ` Daniel Vetter
  2019-01-29 19:27                               ` Greg KH
  0 siblings, 1 reply; 57+ messages in thread
From: Daniel Vetter @ 2019-01-29 18:10 UTC (permalink / raw)
  To: Greg KH; +Cc: David Lechner, dri-devel, Rafael J. Wysocki

On Tue, Jan 29, 2019 at 6:36 PM Greg KH <gregkh@linuxfoundation.org> wrote:
>
> On Tue, Jan 29, 2019 at 05:50:05PM +0100, Daniel Vetter wrote:
> > On Tue, Jan 29, 2019 at 03:34:46PM +0100, Noralf Trønnes wrote:
> > >
> > >
> > > Den 24.01.2019 18.57, skrev Daniel Vetter:
> > > > On Thu, Jan 24, 2019 at 6:46 PM Greg KH <gregkh@linuxfoundation.org> wrote:
> > > >>
> > > >> On Thu, Jan 24, 2019 at 11:43:12AM +0100, Daniel Vetter wrote:
> > > >>> On Wed, Jan 23, 2019 at 11:54:07AM +0100, Noralf Trønnes wrote:
> > > >>>>
> > > >>>>
> > > >>>> Den 22.01.2019 20.30, skrev Daniel Vetter:
> > > >>>>> On Tue, Jan 22, 2019 at 8:07 PM Noralf Trønnes <noralf@tronnes.org> wrote:
> > > >>>>>>
> > > >>>>>>
> > > >>>>>>
> > > >>>>>> Den 22.01.2019 10.32, skrev Daniel Vetter:
> > > >>>>>>> On Mon, Jan 21, 2019 at 01:21:46PM +0100, Noralf Trønnes wrote:
> > > >>>>>>>>
> > > >>>>>>>>
> > > >>>>>>>> Den 21.01.2019 10.55, skrev Daniel Vetter:
> > > >>>>>>>>> On Mon, Jan 21, 2019 at 10:10:14AM +0100, Daniel Vetter wrote:
> > > >>>>>>>>>> On Sun, Jan 20, 2019 at 12:43:08PM +0100, Noralf Trønnes wrote:
> > > >>>>>>>>>>> This adds resource managed (devres) versions of drm_dev_init() and
> > > >>>>>>>>>>> drm_dev_register().
> > > >>>>>>>>>>>
> > > >>>>>>>>>>> Also added is devm_drm_dev_register_with_fbdev() which sets up generic
> > > >>>>>>>>>>> fbdev emulation as well.
> > > >>>>>>>>>>>
> > > >>>>>>>>>>> devm_drm_dev_register() isn't exported since there are no users.
> > > >>>>>>>>>>>
> > > >>>>>>>>>>> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
> > > >>>>>>>>>>
> > > >>>>>>>>
> > > >>>>>>>> <snip>
> > > >>>>>>>>
> > > >>>>>>>>>>> diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
> > > >>>>>>>>>>> index 381581b01d48..12129772be45 100644
> > > >>>>>>>>>>> --- a/drivers/gpu/drm/drm_drv.c
> > > >>>>>>>>>>> +++ b/drivers/gpu/drm/drm_drv.c
> > > >>>>>>>>>>> @@ -36,6 +36,7 @@
> > > >>>>>>>>>>>
> > > >>>>>>>>>>>  #include <drm/drm_client.h>
> > > >>>>>>>>>>>  #include <drm/drm_drv.h>
> > > >>>>>>>>>>> +#include <drm/drm_fb_helper.h>
> > > >>>>>>>>>>>  #include <drm/drmP.h>
> > > >>>>>>>>>>>
> > > >>>>>>>>>>>  #include "drm_crtc_internal.h"
> > > >>>>>>>>>>> @@ -871,6 +872,111 @@ void drm_dev_unregister(struct drm_device *dev)
> > > >>>>>>>>>>>  }
> > > >>>>>>>>>>>  EXPORT_SYMBOL(drm_dev_unregister);
> > > >>>>>>>>>>>
> > > >>>>>>>>>>> +static void devm_drm_dev_init_release(void *data)
> > > >>>>>>>>>>> +{
> > > >>>>>>>>>>> + drm_dev_put(data);
> > > >>>>>>>>>
> > > >>>>>>>>> We need drm_dev_unplug() here, or this isn't safe.
> > > >>>>>>>>
> > > >>>>>>>> This function is only used to cover the error path if probe fails before
> > > >>>>>>>> devm_drm_dev_register() is called. devm_drm_dev_register_release() is
> > > >>>>>>>> the one that calls unplug. There are comments about this in the functions.
> > > >>>>>>>
> > > >>>>>>> I think I get a prize for being ignorant and blind :-/
> > > >>>>>>>
> > > >>>>>>>>
> > > >>>>>>>>>
> > > >>>>>>>>>>> +}
> > > >>>>>>>>>>> +
> > > >>>>>>>>>>> +/**
> > > >>>>>>>>>>> + * devm_drm_dev_init - Resource managed drm_dev_init()
> > > >>>>>>>>>>> + * @parent: Parent device object
> > > >>>>>>>>>>> + * @dev: DRM device
> > > >>>>>>>>>>> + * @driver: DRM driver
> > > >>>>>>>>>>> + *
> > > >>>>>>>>>>> + * Managed drm_dev_init(). The DRM device initialized with this function is
> > > >>>>>>>>>>> + * automatically released on driver detach. You must supply a
> > > >>>>>>>>>
> > > >>>>>>>>> I think a bit more clarity here would be good:
> > > >>>>>>>>>
> > > >>>>>>>>> "... automatically released on driver unbind by callind drm_dev_unplug()."
> > > >>>>>>>>>
> > > >>>>>>>>>>> + * &drm_driver.release callback to control the finalization explicitly.
> > > >>>>>>>>>
> > > >>>>>>>>> I think a loud warning for these is in order:
> > > >>>>>>>>>
> > > >>>>>>>>> "WARNING:
> > > >>>>>>>>>
> > > >>>>>>>>> "In generally it is unsafe to use devm functions for drm structures
> > > >>>>>>>>> because the lifetimes of &drm_device and the underlying &device do not
> > > >>>>>>>>> match. This here works because it doesn't immediately free anything, but
> > > >>>>>>>>> only calls drm_dev_unplug(), which internally decrements the &drm_device
> > > >>>>>>>>> refcount through drm_dev_put().
> > > >>>>>>>>>
> > > >>>>>>>>> "All other drm structures must still be explicitly released in the
> > > >>>>>>>>> &drm_driver.release callback."
> > > >>>>>>>>>
> > > >>>>>>>>> While thinking about this I just realized that with this design we have no
> > > >>>>>>>>> good place to call drm_atomic_helper_shutdown(). Which we need to, or all
> > > >>>>>>>>> kinds of things will leak badly (connectors, fb, ...), but there's no
> > > >>>>>>>>> place to call it:
> > > >>>>>>>>> - unbind is too early, since we haven't yet called drm_dev_unplug, and the
> > > >>>>>>>>>   drm_dev_unregister in there must be called _before_ we start to shut
> > > >>>>>>>>>   down anything.
> > > >>>>>>>>> - drm_driver.release is way too late.
> > > >>>>>>>>>
> > > >>>>>>>>> Ofc for a real hotunplug there's no point in shutting down the hw (it's
> > > >>>>>>>>> already gone), but for a driver unload/unbind it would be nice if this
> > > >>>>>>>>> happens automatically and in the right order.
> > > >>>>>>>>>
> > > >>>>>>>>> So not sure what to do here really.
> > > >>>>>>>>
> > > >>>>>>>> How about this change: (it breaks the rule of pulling helpers into the
> > > >>>>>>>> core, so maybe we should put the devm_ functions into the simple KMS
> > > >>>>>>>> helper instead?)
> > > >>>>>>>
> > > >>>>>>> Yeah smells a bit much like midlayer ... What would work is having a pile
> > > >>>>>>> more devm_ helper functions, so that we onion-unwrap everything correctly,
> > > >>>>>>> and in the right order. So:
> > > >>>>>>>
> > > >>>>>>> - devm_drm_dev_init (always does a drm_dev_put())
> > > >>>>>>>
> > > >>>>>>> - devm_drm_poll_enable (shuts down the poll helper with a devm action)
> > > >>>>>>>
> > > >>>>>>> - devm_drm_mode_config_reset (does an atomic_helper_shutdown() as it's cleanup action)
> > > >>>>>>>
> > > >>>>>>> - devm_drm_dev_register (grabs an additional drm_dev_get() reference so it
> > > >>>>>>>   can call drm_dev_unplug() unconditionally).
> > > >>>>>>>
> > > >>>>>>
> > > >>>>>> Beautiful! I really like this, it's very flexible.
> > > >>>>>>
> > > >>>>>> Where should devm_drm_mode_config_reset() live? It will pull in the
> > > >>>>>> atomic helper...
> > > >>>>>
> > > >>>>> I think a new drm_devm.c helper would be nice for all this stuff.
> > > >>>>> Especially since you can't freely mix devm-based setup/cleanup with
> > > >>>>> normal cleanup I think it'd be good to have it all together in one
> > > >>>>> place. And perhaps even a code example in the DOC: overview.
> > > >>>>>
> > > >>>>>>> We'd need to make sure some of the cleanup actions dtrt when the device is
> > > >>>>>>> gone, but I think we can achieve that by liberally sprinkling
> > > >>>>>>> drm_dev_enter/exit over them, e.g. the the cleanup action for
> > > >>>>>>> drm_mode_config_reset would be:
> > > >>>>>>>
> > > >>>>>>> {
> > > >>>>>>>       if (drm_dev_enter())
> > > >>>>>>>               return;
> > > >>>>>>>
> > > >>>>>>>       drm_atomic_helper_shutdown();
> > > >>>>>>>
> > > >>>>>>>       drm_dev_exit();
> > > >>>>>>> }
> > > >>>>>>>
> > > >>>>>>
> > > >>>>>> drm_dev_enter() can only be used to check whether the drm_device is
> > > >>>>>> registered or not, it doesn't say anything about the state of the parent
> > > >>>>>> device.
> > > >>>>>>
> > > >>>>>> All we know is that the device is being unbound from the driver, we
> > > >>>>>> don't know if it's the device that's being removed or if it's the driver
> > > >>>>>> that's unregistered.
> > > >>>>>
> > > >>>>> You're right, both paths will have called drm_dev_unplug by then.
> > > >>>>> Silly me. I really liked my idea :-)
> > > >>>>>
> > > >>>>>> I have looked at the various call chains:
> > > >>>>>>
> > > >>>>>> driver_unregister ->
> > > >>>>>>     bus_remove_driver ->
> > > >>>>>>         driver_detach ->
> > > >>>>>>             device_release_driver_internal
> > > >>>>>>
> > > >>>>>> device_unregister ->
> > > >>>>>>     device_del ->
> > > >>>>>>         bus_remove_device ->
> > > >>>>>>             device_release_driver ->
> > > >>>>>>                 device_release_driver_internal
> > > >>>>>>
> > > >>>>>> sysfs: unbind_store ->
> > > >>>>>>     device_release_driver ->
> > > >>>>>>         device_release_driver_internal
> > > >>>>>>
> > > >>>>>> The only way I've found to differentiate between these in a cleanup
> > > >>>>>> action is that device_del() uses the bus notifier to signal
> > > >>>>>> BUS_NOTIFY_DEL_DEVICE before calling bus_remove_device(). Such a
> > > >>>>>> notifier could be used to set a drm_device->parent_removed flag.
> > > >>>>>
> > > >>>>> Hm, this might upset Greg KH's code taste ... maybe there's a better
> > > >>>>> way to do this, but best to prototype a patch with this, send it to
> > > >>>>> him and ask how to :-)
> > > >>>>>
> > > >>>>
> > > >>>> I'll leave this to the one that needs it. The tinydrm drivers doesn't
> > > >>>> need to touch hw after DRM unregister.
> > > >>>>
> > > >>>>>> Why is it necessary to call drm_atomic_helper_shutdown() here? Doesn't
> > > >>>>>> everything get disabled when userspace closes? It does in my tinydrm
> > > >>>>>> world :-)
> > > >>>>>
> > > >>>>> Iirc fbdev/fbcon can result in leaks ... at least we've had patches
> > > >>>>> where drivers leaked drm_connector and drm_framebuffer objects, and
> > > >>>>> they've been fixed by calling drm_atomic_helper_shutdown() in the
> > > >>>>> unload path. Maybe this is cargo-culting, but it goes way back to
> > > >>>>> pre-atomic, where drivers called drm_helper_force_disable_all().
> > > >>>>>
> > > >>>>> If you try to move the fbcon to your tinydrm drivers (con2fb is
> > > >>>>> apparently the cmdline tool you need, never tried it, I only switch
> > > >>>>> the kernel's console between fbcon and dummycon and back, not what
> > > >>>>> fbcon drivers itself), then I think you should be able to reproduce.
> > > >>>>> And maybe you have a better idea how to deal with this all.
> > > >>>>>
> > > >>>>> Note also that there's been proposals floating around to only close an
> > > >>>>> drm_framebuffer, not also remove it (like the current RMFB ioctl
> > > >>>>> does), with that closing userspace would not necessarily lead to a
> > > >>>>> full cleanup.
> > > >>>>>
> > > >>>>> Another thing (which doesn't apply to drm_simple_display_pipe drivers)
> > > >>>>> is if you have the display on, but no planes showing (i.e. all black).
> > > >>>>> Then all the fbs will be cleaned up, but drm_connector will be
> > > >>>>> leaking. That's a case where you need drm_atomic_helper_shutdown()
> > > >>>>> even if fbcon/fbdev isn't even enabled.
> > > >>>>>
> > > >>>>
> > > >>>> Ok, this means that I don't need to call drm_atomic_helper_shutdown() in
> > > >>>> tinydrm. DRM userspace disables the pipe on close and the generic fbdev
> > > >>>> emulation also releases everything.
> > > >>>> Even so, maybe I should use devm_drm_mode_config_reset() after all to
> > > >>>> keep drivers uniform, to avoid confusion: why doesn't he use it?
> > > >>>
> > > >>> Hm maybe there is an official way to solve this, pulling in Greg+Rafael.
> > > >>>
> > > >>> Super short summary: We want to start using devm actions to clean up drm
> > > >>> drivers. Here's the problem:
> > > >>> - For a driver unload/unbind without hotunplug, we want to properly clean
> > > >>>   up the hardware and shut it all down.
> > > >>
> > > >> Then do it on probe/disconnect.
> > > >>
> > > >>> - But if the device is unplugged already, that's probably not the best
> > > >>>   idea, and we only want to clean up the kernel's resources/allocations.
> > > >>
> > > >> Again, probe/disconnect will be called either way.
> > > >>
> > > >> But as you note, memory will NOT be freed by the devm stuff if you
> > > >> manually unbind a driver from a device.
> > > >>
> > > >> So don't touch hardware there, it's not going to work :)
> > > >>
> > > >>> What's the recommendation here? I see a few options:
> > > >>>
> > > >>> - Make sure everything can deal with this properly. Hotunplug can happen
> > > >>>   anytime, so there's a race no matter what.
> > > >>
> > > >> Yes.
> > > >>
> > > >>> - Check with the device model whether the struct device is disappearing or
> > > >>>   whether we're just dealing with a driver unbind (no idea how to do
> > > >>>   that), and act accordingly.
> > > >>
> > > >> You don't know that, sorry.  Just do any hardware stuff on disconnect.
> > > >> Assuming your hardware is still present :)
> > > >>
> > > >>> - Fundamental question: Touching the hw from devm actions, is that ok? If
> > > >>>   not, then the pretty nifty plan laid out in this thread wont work.
> > > >>
> > > >> Nope, that's not going to work, the device could either be long gone, or
> > > >> you will not be called due to unbind happening from userspace.
> > > >>
> > > >> But really, unbind from userspace is very very rare, it's a developer
> > > >> thing mostly.  Oh and a virtual driver thing, but those people are crazy
> > > >> :)
> > > >>
> > > >>> - Something completely different?
> > > >>
> > > >> Do it in disconnect :)
> > > >
> > > > Ah, I forgot to mention the important constraint :-) disconnect/unbind
> > > > should be the following sequence:
> > > >
> > > > 1. Unregister all the userspace interfaces (there's a lot of them) and
> > > > make sure all the pending ioctls are done so that from now on
> > > > userspace sees lots of -EIO (in case it still has fd open, which is
> > > > going to be the normal for hotunplug.
> > > >
> > > > 2. Shut down hw and all ongoing operations (only relevant for unbind,
> > > > but needs to be able to cope with sudden hotunplug on top anyway).
> > > >
> > > > 3. Clean up the kernel mess and release everything.
> > > >
> > > > Probe is exactly the other way round, so would perfectly fit into the
> > > > devm onion cleanup. See in the commented earlier replies above how
> > > > that would match in details, but tldr; if we have to do 2. in
> > > > disconnect, then we also have to do 1. in disconnected, and only doing
> > > > 3. through devm is almost not worth the bother. But if we could do all
> > > > three through devm then simple drivers wouldn't even need any
> > > > disconnect/unbind callback at all. That's our motivation for trying to
> > > > come up with an answer that's not "do it in disconnect". "do it in
> > > > disconnect" is how we do it all today already.
> > > >
> > > > Yes we're trying to make tiny drivers even smaller, we have enough
> > > > nowadays that this stuff would be worth it :-)
> > > >
> > >
> > > I think a solution is to say that drivers that want to touch hw on
> > > disconnect needs to use device_driver->remove to do that.
> > >
> > > This is an example driver that doesn't need to touch hw because it's so
> > > simple that userspace has disabled the pipeline:
> > >
> > > static void drm_driver_release(struct drm_device *drm)
> > > {
> > >     drm_mode_config_cleanup(drm);
> > >     drm_dev_fini(drm);
> > >     kfree(drm);
> > > }
> > >
> > > static struct drm_driver drm_driver = {
> > >     .release = drm_driver_release,
> > >     /* ... */
> > > };
> > >
> > > static int driver_probe(struct device *dev)
> > > {
> > >     struct drm_device *drm;
> > >     int ret;
> > >
> > >     drm = kzalloc(sizeof(*drm), GFP_KERNEL);
> > >     if (!drm)
> > >             return -ENOMEM;
> > >
> > >     ret = devm_drm_dev_init(dev, drm, &drm_driver);
> > >     if (ret) {
> > >             kfree(drm);
> > >             return ret;
> > >     }
> > >
> > >     drm_mode_config_init(drm);
> > >
> > >     /* Aquire various resources, all managed by devres */
> > >
> > >     drm_mode_config_reset(drm);
> > >
> > >     return devm_drm_dev_register(drm);
> > > }
> > >
> > > struct device_driver driver = {
> > >     .probe = driver_probe,
> > > };
> > >
> > >
> > > A driver that wants to touch hardware on disconnect, can look like this:
> > >
> > > static void drm_driver_release(struct drm_device *drm)
> > > {
> > >     drm_mode_config_cleanup(drm);
> > >     drm_dev_fini(drm);
> > >     kfree(drm);
> > > }
> > >
> > > static struct drm_driver drm_driver = {
> > >     .release = drm_driver_release,
> > >     /* ... */
> > > };
> > >
> > > static int driver_probe(struct device *dev)
> > > {
> > >     struct drm_device *drm;
> > >     int ret;
> > >
> > >     drm = kzalloc(sizeof(*drm), GFP_KERNEL);
> > >     if (!drm)
> > >             return -ENOMEM;
> > >
> > >     ret = devm_drm_dev_init(dev, drm, &drm_driver);
> > >     if (ret) {
> > >             kfree(drm);
> > >             return ret;
> > >     }
> > >
> > >     drm_mode_config_init(drm);
> > >
> > >     /* Aquire various resources, all managed by devres */
> > >
> > >     drm_mode_config_reset(drm);
> > >
> > >     ret = drm_dev_register(drm);
> > >     if (ret)
> > >             return ret;
> > >
> > >     drm_dev_get(dev); /* If using drm_dev_unplug() */
> > >
> > >     dev_set_drvdata(dev, drm);
> > >
> > >     return 0;
> > > }
> > >
> > > /* This function is called before devres_release_all() */
> > > static int driver_remove(struct device *dev)
> > > {
> > >     struct drm_device *drm = dev_get_drvdata(dev);
> > >
> > >     drm_dev_unplug(drm); OR drm_dev_unregister(drm);
> > >     drm_atomic_helper_shutdown(drm)
> > >
> > >     return 0;
> > > }
> > >
> > > struct device_driver driver = {
> > >     .probe = driver_probe,
> > >     .remove = driver_remove,
> >
> > That's exactly the pattern I'm trying to avoid, because imo your tiny
> > driver _also_ should do this. Or we realize that all the current drivers
> > doing drm_atomic_helper_shutdown are misguided, but I'm not really
> > understanding why.
> >
> > Having a devm helper which cannot be used for some drivers due to
> > fundamental design issues is kinda not great, because it means everyone
> > will still use it, and shrug the bugs off as "not my problem". Which is
> > what's happening right now with all the devm_kzalloc we have in drm
> > drivers that all the get lifetim wrong. But because devm_ is convenient
> > everyone uses it, so the driver unload of most drivers is full of bugs.
>
> driver "unload" should not be full of bugs, how would it?  Anything
> created with devm_() will just be freed when the device really goes away
> in the system, it shouldn't call back into the driver.

The problem is when drivers use devm_ not to allocate hw resources and
related things, but structures for objects with other lifetimes. Like
open file descriptors shared with the world.

> And yes, devm_() is a crutch, one that I really don't like, but I
> understand the apeal.  And in 95% of the cases, it can work just fine as
> no one ever does unbind/bind from userspace, and if they did, they would
> never notice the memory issues.

Yeah, additionally people tend to not hotunplug gpus. So even there we
tend to get away with piles of bugs. And I'm kinda ok with piles of
bugs in driver unload code, as long as it's just driver unload code
and mostly gets the job done (i.e. doesn't blow up when a developer
first kills X and then unloads their driver).

But for adding new infrastructure in drm core/helpers that's a
different beast. I think we could pull something off, but not quite
there yet with the solution. Maybe we need something similar to devm,
but for drm uapi objects and stuff, adapted to our needs.

Cheers, Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: devm actions and hw clenaup (was Re: [PATCH 01/11] drm: Add devm_drm_dev_init/register)
  2019-01-29 18:10                             ` Daniel Vetter
@ 2019-01-29 19:27                               ` Greg KH
  2019-01-29 23:14                                 ` Daniel Vetter
  0 siblings, 1 reply; 57+ messages in thread
From: Greg KH @ 2019-01-29 19:27 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: David Lechner, dri-devel, Rafael J. Wysocki

On Tue, Jan 29, 2019 at 07:10:55PM +0100, Daniel Vetter wrote:
> On Tue, Jan 29, 2019 at 6:36 PM Greg KH <gregkh@linuxfoundation.org> wrote:
> >
> > On Tue, Jan 29, 2019 at 05:50:05PM +0100, Daniel Vetter wrote:
> > > On Tue, Jan 29, 2019 at 03:34:46PM +0100, Noralf Trønnes wrote:
> > > >
> > > >
> > > > Den 24.01.2019 18.57, skrev Daniel Vetter:
> > > > > On Thu, Jan 24, 2019 at 6:46 PM Greg KH <gregkh@linuxfoundation.org> wrote:
> > > > >>
> > > > >> On Thu, Jan 24, 2019 at 11:43:12AM +0100, Daniel Vetter wrote:
> > > > >>> On Wed, Jan 23, 2019 at 11:54:07AM +0100, Noralf Trønnes wrote:
> > > > >>>>
> > > > >>>>
> > > > >>>> Den 22.01.2019 20.30, skrev Daniel Vetter:
> > > > >>>>> On Tue, Jan 22, 2019 at 8:07 PM Noralf Trønnes <noralf@tronnes.org> wrote:
> > > > >>>>>>
> > > > >>>>>>
> > > > >>>>>>
> > > > >>>>>> Den 22.01.2019 10.32, skrev Daniel Vetter:
> > > > >>>>>>> On Mon, Jan 21, 2019 at 01:21:46PM +0100, Noralf Trønnes wrote:
> > > > >>>>>>>>
> > > > >>>>>>>>
> > > > >>>>>>>> Den 21.01.2019 10.55, skrev Daniel Vetter:
> > > > >>>>>>>>> On Mon, Jan 21, 2019 at 10:10:14AM +0100, Daniel Vetter wrote:
> > > > >>>>>>>>>> On Sun, Jan 20, 2019 at 12:43:08PM +0100, Noralf Trønnes wrote:
> > > > >>>>>>>>>>> This adds resource managed (devres) versions of drm_dev_init() and
> > > > >>>>>>>>>>> drm_dev_register().
> > > > >>>>>>>>>>>
> > > > >>>>>>>>>>> Also added is devm_drm_dev_register_with_fbdev() which sets up generic
> > > > >>>>>>>>>>> fbdev emulation as well.
> > > > >>>>>>>>>>>
> > > > >>>>>>>>>>> devm_drm_dev_register() isn't exported since there are no users.
> > > > >>>>>>>>>>>
> > > > >>>>>>>>>>> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
> > > > >>>>>>>>>>
> > > > >>>>>>>>
> > > > >>>>>>>> <snip>
> > > > >>>>>>>>
> > > > >>>>>>>>>>> diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
> > > > >>>>>>>>>>> index 381581b01d48..12129772be45 100644
> > > > >>>>>>>>>>> --- a/drivers/gpu/drm/drm_drv.c
> > > > >>>>>>>>>>> +++ b/drivers/gpu/drm/drm_drv.c
> > > > >>>>>>>>>>> @@ -36,6 +36,7 @@
> > > > >>>>>>>>>>>
> > > > >>>>>>>>>>>  #include <drm/drm_client.h>
> > > > >>>>>>>>>>>  #include <drm/drm_drv.h>
> > > > >>>>>>>>>>> +#include <drm/drm_fb_helper.h>
> > > > >>>>>>>>>>>  #include <drm/drmP.h>
> > > > >>>>>>>>>>>
> > > > >>>>>>>>>>>  #include "drm_crtc_internal.h"
> > > > >>>>>>>>>>> @@ -871,6 +872,111 @@ void drm_dev_unregister(struct drm_device *dev)
> > > > >>>>>>>>>>>  }
> > > > >>>>>>>>>>>  EXPORT_SYMBOL(drm_dev_unregister);
> > > > >>>>>>>>>>>
> > > > >>>>>>>>>>> +static void devm_drm_dev_init_release(void *data)
> > > > >>>>>>>>>>> +{
> > > > >>>>>>>>>>> + drm_dev_put(data);
> > > > >>>>>>>>>
> > > > >>>>>>>>> We need drm_dev_unplug() here, or this isn't safe.
> > > > >>>>>>>>
> > > > >>>>>>>> This function is only used to cover the error path if probe fails before
> > > > >>>>>>>> devm_drm_dev_register() is called. devm_drm_dev_register_release() is
> > > > >>>>>>>> the one that calls unplug. There are comments about this in the functions.
> > > > >>>>>>>
> > > > >>>>>>> I think I get a prize for being ignorant and blind :-/
> > > > >>>>>>>
> > > > >>>>>>>>
> > > > >>>>>>>>>
> > > > >>>>>>>>>>> +}
> > > > >>>>>>>>>>> +
> > > > >>>>>>>>>>> +/**
> > > > >>>>>>>>>>> + * devm_drm_dev_init - Resource managed drm_dev_init()
> > > > >>>>>>>>>>> + * @parent: Parent device object
> > > > >>>>>>>>>>> + * @dev: DRM device
> > > > >>>>>>>>>>> + * @driver: DRM driver
> > > > >>>>>>>>>>> + *
> > > > >>>>>>>>>>> + * Managed drm_dev_init(). The DRM device initialized with this function is
> > > > >>>>>>>>>>> + * automatically released on driver detach. You must supply a
> > > > >>>>>>>>>
> > > > >>>>>>>>> I think a bit more clarity here would be good:
> > > > >>>>>>>>>
> > > > >>>>>>>>> "... automatically released on driver unbind by callind drm_dev_unplug()."
> > > > >>>>>>>>>
> > > > >>>>>>>>>>> + * &drm_driver.release callback to control the finalization explicitly.
> > > > >>>>>>>>>
> > > > >>>>>>>>> I think a loud warning for these is in order:
> > > > >>>>>>>>>
> > > > >>>>>>>>> "WARNING:
> > > > >>>>>>>>>
> > > > >>>>>>>>> "In generally it is unsafe to use devm functions for drm structures
> > > > >>>>>>>>> because the lifetimes of &drm_device and the underlying &device do not
> > > > >>>>>>>>> match. This here works because it doesn't immediately free anything, but
> > > > >>>>>>>>> only calls drm_dev_unplug(), which internally decrements the &drm_device
> > > > >>>>>>>>> refcount through drm_dev_put().
> > > > >>>>>>>>>
> > > > >>>>>>>>> "All other drm structures must still be explicitly released in the
> > > > >>>>>>>>> &drm_driver.release callback."
> > > > >>>>>>>>>
> > > > >>>>>>>>> While thinking about this I just realized that with this design we have no
> > > > >>>>>>>>> good place to call drm_atomic_helper_shutdown(). Which we need to, or all
> > > > >>>>>>>>> kinds of things will leak badly (connectors, fb, ...), but there's no
> > > > >>>>>>>>> place to call it:
> > > > >>>>>>>>> - unbind is too early, since we haven't yet called drm_dev_unplug, and the
> > > > >>>>>>>>>   drm_dev_unregister in there must be called _before_ we start to shut
> > > > >>>>>>>>>   down anything.
> > > > >>>>>>>>> - drm_driver.release is way too late.
> > > > >>>>>>>>>
> > > > >>>>>>>>> Ofc for a real hotunplug there's no point in shutting down the hw (it's
> > > > >>>>>>>>> already gone), but for a driver unload/unbind it would be nice if this
> > > > >>>>>>>>> happens automatically and in the right order.
> > > > >>>>>>>>>
> > > > >>>>>>>>> So not sure what to do here really.
> > > > >>>>>>>>
> > > > >>>>>>>> How about this change: (it breaks the rule of pulling helpers into the
> > > > >>>>>>>> core, so maybe we should put the devm_ functions into the simple KMS
> > > > >>>>>>>> helper instead?)
> > > > >>>>>>>
> > > > >>>>>>> Yeah smells a bit much like midlayer ... What would work is having a pile
> > > > >>>>>>> more devm_ helper functions, so that we onion-unwrap everything correctly,
> > > > >>>>>>> and in the right order. So:
> > > > >>>>>>>
> > > > >>>>>>> - devm_drm_dev_init (always does a drm_dev_put())
> > > > >>>>>>>
> > > > >>>>>>> - devm_drm_poll_enable (shuts down the poll helper with a devm action)
> > > > >>>>>>>
> > > > >>>>>>> - devm_drm_mode_config_reset (does an atomic_helper_shutdown() as it's cleanup action)
> > > > >>>>>>>
> > > > >>>>>>> - devm_drm_dev_register (grabs an additional drm_dev_get() reference so it
> > > > >>>>>>>   can call drm_dev_unplug() unconditionally).
> > > > >>>>>>>
> > > > >>>>>>
> > > > >>>>>> Beautiful! I really like this, it's very flexible.
> > > > >>>>>>
> > > > >>>>>> Where should devm_drm_mode_config_reset() live? It will pull in the
> > > > >>>>>> atomic helper...
> > > > >>>>>
> > > > >>>>> I think a new drm_devm.c helper would be nice for all this stuff.
> > > > >>>>> Especially since you can't freely mix devm-based setup/cleanup with
> > > > >>>>> normal cleanup I think it'd be good to have it all together in one
> > > > >>>>> place. And perhaps even a code example in the DOC: overview.
> > > > >>>>>
> > > > >>>>>>> We'd need to make sure some of the cleanup actions dtrt when the device is
> > > > >>>>>>> gone, but I think we can achieve that by liberally sprinkling
> > > > >>>>>>> drm_dev_enter/exit over them, e.g. the the cleanup action for
> > > > >>>>>>> drm_mode_config_reset would be:
> > > > >>>>>>>
> > > > >>>>>>> {
> > > > >>>>>>>       if (drm_dev_enter())
> > > > >>>>>>>               return;
> > > > >>>>>>>
> > > > >>>>>>>       drm_atomic_helper_shutdown();
> > > > >>>>>>>
> > > > >>>>>>>       drm_dev_exit();
> > > > >>>>>>> }
> > > > >>>>>>>
> > > > >>>>>>
> > > > >>>>>> drm_dev_enter() can only be used to check whether the drm_device is
> > > > >>>>>> registered or not, it doesn't say anything about the state of the parent
> > > > >>>>>> device.
> > > > >>>>>>
> > > > >>>>>> All we know is that the device is being unbound from the driver, we
> > > > >>>>>> don't know if it's the device that's being removed or if it's the driver
> > > > >>>>>> that's unregistered.
> > > > >>>>>
> > > > >>>>> You're right, both paths will have called drm_dev_unplug by then.
> > > > >>>>> Silly me. I really liked my idea :-)
> > > > >>>>>
> > > > >>>>>> I have looked at the various call chains:
> > > > >>>>>>
> > > > >>>>>> driver_unregister ->
> > > > >>>>>>     bus_remove_driver ->
> > > > >>>>>>         driver_detach ->
> > > > >>>>>>             device_release_driver_internal
> > > > >>>>>>
> > > > >>>>>> device_unregister ->
> > > > >>>>>>     device_del ->
> > > > >>>>>>         bus_remove_device ->
> > > > >>>>>>             device_release_driver ->
> > > > >>>>>>                 device_release_driver_internal
> > > > >>>>>>
> > > > >>>>>> sysfs: unbind_store ->
> > > > >>>>>>     device_release_driver ->
> > > > >>>>>>         device_release_driver_internal
> > > > >>>>>>
> > > > >>>>>> The only way I've found to differentiate between these in a cleanup
> > > > >>>>>> action is that device_del() uses the bus notifier to signal
> > > > >>>>>> BUS_NOTIFY_DEL_DEVICE before calling bus_remove_device(). Such a
> > > > >>>>>> notifier could be used to set a drm_device->parent_removed flag.
> > > > >>>>>
> > > > >>>>> Hm, this might upset Greg KH's code taste ... maybe there's a better
> > > > >>>>> way to do this, but best to prototype a patch with this, send it to
> > > > >>>>> him and ask how to :-)
> > > > >>>>>
> > > > >>>>
> > > > >>>> I'll leave this to the one that needs it. The tinydrm drivers doesn't
> > > > >>>> need to touch hw after DRM unregister.
> > > > >>>>
> > > > >>>>>> Why is it necessary to call drm_atomic_helper_shutdown() here? Doesn't
> > > > >>>>>> everything get disabled when userspace closes? It does in my tinydrm
> > > > >>>>>> world :-)
> > > > >>>>>
> > > > >>>>> Iirc fbdev/fbcon can result in leaks ... at least we've had patches
> > > > >>>>> where drivers leaked drm_connector and drm_framebuffer objects, and
> > > > >>>>> they've been fixed by calling drm_atomic_helper_shutdown() in the
> > > > >>>>> unload path. Maybe this is cargo-culting, but it goes way back to
> > > > >>>>> pre-atomic, where drivers called drm_helper_force_disable_all().
> > > > >>>>>
> > > > >>>>> If you try to move the fbcon to your tinydrm drivers (con2fb is
> > > > >>>>> apparently the cmdline tool you need, never tried it, I only switch
> > > > >>>>> the kernel's console between fbcon and dummycon and back, not what
> > > > >>>>> fbcon drivers itself), then I think you should be able to reproduce.
> > > > >>>>> And maybe you have a better idea how to deal with this all.
> > > > >>>>>
> > > > >>>>> Note also that there's been proposals floating around to only close an
> > > > >>>>> drm_framebuffer, not also remove it (like the current RMFB ioctl
> > > > >>>>> does), with that closing userspace would not necessarily lead to a
> > > > >>>>> full cleanup.
> > > > >>>>>
> > > > >>>>> Another thing (which doesn't apply to drm_simple_display_pipe drivers)
> > > > >>>>> is if you have the display on, but no planes showing (i.e. all black).
> > > > >>>>> Then all the fbs will be cleaned up, but drm_connector will be
> > > > >>>>> leaking. That's a case where you need drm_atomic_helper_shutdown()
> > > > >>>>> even if fbcon/fbdev isn't even enabled.
> > > > >>>>>
> > > > >>>>
> > > > >>>> Ok, this means that I don't need to call drm_atomic_helper_shutdown() in
> > > > >>>> tinydrm. DRM userspace disables the pipe on close and the generic fbdev
> > > > >>>> emulation also releases everything.
> > > > >>>> Even so, maybe I should use devm_drm_mode_config_reset() after all to
> > > > >>>> keep drivers uniform, to avoid confusion: why doesn't he use it?
> > > > >>>
> > > > >>> Hm maybe there is an official way to solve this, pulling in Greg+Rafael.
> > > > >>>
> > > > >>> Super short summary: We want to start using devm actions to clean up drm
> > > > >>> drivers. Here's the problem:
> > > > >>> - For a driver unload/unbind without hotunplug, we want to properly clean
> > > > >>>   up the hardware and shut it all down.
> > > > >>
> > > > >> Then do it on probe/disconnect.
> > > > >>
> > > > >>> - But if the device is unplugged already, that's probably not the best
> > > > >>>   idea, and we only want to clean up the kernel's resources/allocations.
> > > > >>
> > > > >> Again, probe/disconnect will be called either way.
> > > > >>
> > > > >> But as you note, memory will NOT be freed by the devm stuff if you
> > > > >> manually unbind a driver from a device.
> > > > >>
> > > > >> So don't touch hardware there, it's not going to work :)
> > > > >>
> > > > >>> What's the recommendation here? I see a few options:
> > > > >>>
> > > > >>> - Make sure everything can deal with this properly. Hotunplug can happen
> > > > >>>   anytime, so there's a race no matter what.
> > > > >>
> > > > >> Yes.
> > > > >>
> > > > >>> - Check with the device model whether the struct device is disappearing or
> > > > >>>   whether we're just dealing with a driver unbind (no idea how to do
> > > > >>>   that), and act accordingly.
> > > > >>
> > > > >> You don't know that, sorry.  Just do any hardware stuff on disconnect.
> > > > >> Assuming your hardware is still present :)
> > > > >>
> > > > >>> - Fundamental question: Touching the hw from devm actions, is that ok? If
> > > > >>>   not, then the pretty nifty plan laid out in this thread wont work.
> > > > >>
> > > > >> Nope, that's not going to work, the device could either be long gone, or
> > > > >> you will not be called due to unbind happening from userspace.
> > > > >>
> > > > >> But really, unbind from userspace is very very rare, it's a developer
> > > > >> thing mostly.  Oh and a virtual driver thing, but those people are crazy
> > > > >> :)
> > > > >>
> > > > >>> - Something completely different?
> > > > >>
> > > > >> Do it in disconnect :)
> > > > >
> > > > > Ah, I forgot to mention the important constraint :-) disconnect/unbind
> > > > > should be the following sequence:
> > > > >
> > > > > 1. Unregister all the userspace interfaces (there's a lot of them) and
> > > > > make sure all the pending ioctls are done so that from now on
> > > > > userspace sees lots of -EIO (in case it still has fd open, which is
> > > > > going to be the normal for hotunplug.
> > > > >
> > > > > 2. Shut down hw and all ongoing operations (only relevant for unbind,
> > > > > but needs to be able to cope with sudden hotunplug on top anyway).
> > > > >
> > > > > 3. Clean up the kernel mess and release everything.
> > > > >
> > > > > Probe is exactly the other way round, so would perfectly fit into the
> > > > > devm onion cleanup. See in the commented earlier replies above how
> > > > > that would match in details, but tldr; if we have to do 2. in
> > > > > disconnect, then we also have to do 1. in disconnected, and only doing
> > > > > 3. through devm is almost not worth the bother. But if we could do all
> > > > > three through devm then simple drivers wouldn't even need any
> > > > > disconnect/unbind callback at all. That's our motivation for trying to
> > > > > come up with an answer that's not "do it in disconnect". "do it in
> > > > > disconnect" is how we do it all today already.
> > > > >
> > > > > Yes we're trying to make tiny drivers even smaller, we have enough
> > > > > nowadays that this stuff would be worth it :-)
> > > > >
> > > >
> > > > I think a solution is to say that drivers that want to touch hw on
> > > > disconnect needs to use device_driver->remove to do that.
> > > >
> > > > This is an example driver that doesn't need to touch hw because it's so
> > > > simple that userspace has disabled the pipeline:
> > > >
> > > > static void drm_driver_release(struct drm_device *drm)
> > > > {
> > > >     drm_mode_config_cleanup(drm);
> > > >     drm_dev_fini(drm);
> > > >     kfree(drm);
> > > > }
> > > >
> > > > static struct drm_driver drm_driver = {
> > > >     .release = drm_driver_release,
> > > >     /* ... */
> > > > };
> > > >
> > > > static int driver_probe(struct device *dev)
> > > > {
> > > >     struct drm_device *drm;
> > > >     int ret;
> > > >
> > > >     drm = kzalloc(sizeof(*drm), GFP_KERNEL);
> > > >     if (!drm)
> > > >             return -ENOMEM;
> > > >
> > > >     ret = devm_drm_dev_init(dev, drm, &drm_driver);
> > > >     if (ret) {
> > > >             kfree(drm);
> > > >             return ret;
> > > >     }
> > > >
> > > >     drm_mode_config_init(drm);
> > > >
> > > >     /* Aquire various resources, all managed by devres */
> > > >
> > > >     drm_mode_config_reset(drm);
> > > >
> > > >     return devm_drm_dev_register(drm);
> > > > }
> > > >
> > > > struct device_driver driver = {
> > > >     .probe = driver_probe,
> > > > };
> > > >
> > > >
> > > > A driver that wants to touch hardware on disconnect, can look like this:
> > > >
> > > > static void drm_driver_release(struct drm_device *drm)
> > > > {
> > > >     drm_mode_config_cleanup(drm);
> > > >     drm_dev_fini(drm);
> > > >     kfree(drm);
> > > > }
> > > >
> > > > static struct drm_driver drm_driver = {
> > > >     .release = drm_driver_release,
> > > >     /* ... */
> > > > };
> > > >
> > > > static int driver_probe(struct device *dev)
> > > > {
> > > >     struct drm_device *drm;
> > > >     int ret;
> > > >
> > > >     drm = kzalloc(sizeof(*drm), GFP_KERNEL);
> > > >     if (!drm)
> > > >             return -ENOMEM;
> > > >
> > > >     ret = devm_drm_dev_init(dev, drm, &drm_driver);
> > > >     if (ret) {
> > > >             kfree(drm);
> > > >             return ret;
> > > >     }
> > > >
> > > >     drm_mode_config_init(drm);
> > > >
> > > >     /* Aquire various resources, all managed by devres */
> > > >
> > > >     drm_mode_config_reset(drm);
> > > >
> > > >     ret = drm_dev_register(drm);
> > > >     if (ret)
> > > >             return ret;
> > > >
> > > >     drm_dev_get(dev); /* If using drm_dev_unplug() */
> > > >
> > > >     dev_set_drvdata(dev, drm);
> > > >
> > > >     return 0;
> > > > }
> > > >
> > > > /* This function is called before devres_release_all() */
> > > > static int driver_remove(struct device *dev)
> > > > {
> > > >     struct drm_device *drm = dev_get_drvdata(dev);
> > > >
> > > >     drm_dev_unplug(drm); OR drm_dev_unregister(drm);
> > > >     drm_atomic_helper_shutdown(drm)
> > > >
> > > >     return 0;
> > > > }
> > > >
> > > > struct device_driver driver = {
> > > >     .probe = driver_probe,
> > > >     .remove = driver_remove,
> > >
> > > That's exactly the pattern I'm trying to avoid, because imo your tiny
> > > driver _also_ should do this. Or we realize that all the current drivers
> > > doing drm_atomic_helper_shutdown are misguided, but I'm not really
> > > understanding why.
> > >
> > > Having a devm helper which cannot be used for some drivers due to
> > > fundamental design issues is kinda not great, because it means everyone
> > > will still use it, and shrug the bugs off as "not my problem". Which is
> > > what's happening right now with all the devm_kzalloc we have in drm
> > > drivers that all the get lifetim wrong. But because devm_ is convenient
> > > everyone uses it, so the driver unload of most drivers is full of bugs.
> >
> > driver "unload" should not be full of bugs, how would it?  Anything
> > created with devm_() will just be freed when the device really goes away
> > in the system, it shouldn't call back into the driver.
> 
> The problem is when drivers use devm_ not to allocate hw resources and
> related things, but structures for objects with other lifetimes. Like
> open file descriptors shared with the world.

And irqs, which bites everyone in the end.  You have to be careful here,
never tie a devm allocation to an object with another reference count,
that's just a bug.

greg k-h
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: devm actions and hw clenaup (was Re: [PATCH 01/11] drm: Add devm_drm_dev_init/register)
  2019-01-29 19:27                               ` Greg KH
@ 2019-01-29 23:14                                 ` Daniel Vetter
  2019-01-30  7:14                                   ` Greg KH
  0 siblings, 1 reply; 57+ messages in thread
From: Daniel Vetter @ 2019-01-29 23:14 UTC (permalink / raw)
  To: Greg KH; +Cc: David Lechner, dri-devel, Rafael J. Wysocki

On Tue, Jan 29, 2019 at 8:27 PM Greg KH <gregkh@linuxfoundation.org> wrote:
>
> On Tue, Jan 29, 2019 at 07:10:55PM +0100, Daniel Vetter wrote:
> > On Tue, Jan 29, 2019 at 6:36 PM Greg KH <gregkh@linuxfoundation.org> wrote:
> > >
> > > On Tue, Jan 29, 2019 at 05:50:05PM +0100, Daniel Vetter wrote:
> > > > On Tue, Jan 29, 2019 at 03:34:46PM +0100, Noralf Trønnes wrote:
> > > > >
> > > > >
> > > > > Den 24.01.2019 18.57, skrev Daniel Vetter:
> > > > > > On Thu, Jan 24, 2019 at 6:46 PM Greg KH <gregkh@linuxfoundation.org> wrote:
> > > > > >>
> > > > > >> On Thu, Jan 24, 2019 at 11:43:12AM +0100, Daniel Vetter wrote:
> > > > > >>> On Wed, Jan 23, 2019 at 11:54:07AM +0100, Noralf Trønnes wrote:
> > > > > >>>>
> > > > > >>>>
> > > > > >>>> Den 22.01.2019 20.30, skrev Daniel Vetter:
> > > > > >>>>> On Tue, Jan 22, 2019 at 8:07 PM Noralf Trønnes <noralf@tronnes.org> wrote:
> > > > > >>>>>>
> > > > > >>>>>>
> > > > > >>>>>>
> > > > > >>>>>> Den 22.01.2019 10.32, skrev Daniel Vetter:
> > > > > >>>>>>> On Mon, Jan 21, 2019 at 01:21:46PM +0100, Noralf Trønnes wrote:
> > > > > >>>>>>>>
> > > > > >>>>>>>>
> > > > > >>>>>>>> Den 21.01.2019 10.55, skrev Daniel Vetter:
> > > > > >>>>>>>>> On Mon, Jan 21, 2019 at 10:10:14AM +0100, Daniel Vetter wrote:
> > > > > >>>>>>>>>> On Sun, Jan 20, 2019 at 12:43:08PM +0100, Noralf Trønnes wrote:
> > > > > >>>>>>>>>>> This adds resource managed (devres) versions of drm_dev_init() and
> > > > > >>>>>>>>>>> drm_dev_register().
> > > > > >>>>>>>>>>>
> > > > > >>>>>>>>>>> Also added is devm_drm_dev_register_with_fbdev() which sets up generic
> > > > > >>>>>>>>>>> fbdev emulation as well.
> > > > > >>>>>>>>>>>
> > > > > >>>>>>>>>>> devm_drm_dev_register() isn't exported since there are no users.
> > > > > >>>>>>>>>>>
> > > > > >>>>>>>>>>> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
> > > > > >>>>>>>>>>
> > > > > >>>>>>>>
> > > > > >>>>>>>> <snip>
> > > > > >>>>>>>>
> > > > > >>>>>>>>>>> diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
> > > > > >>>>>>>>>>> index 381581b01d48..12129772be45 100644
> > > > > >>>>>>>>>>> --- a/drivers/gpu/drm/drm_drv.c
> > > > > >>>>>>>>>>> +++ b/drivers/gpu/drm/drm_drv.c
> > > > > >>>>>>>>>>> @@ -36,6 +36,7 @@
> > > > > >>>>>>>>>>>
> > > > > >>>>>>>>>>>  #include <drm/drm_client.h>
> > > > > >>>>>>>>>>>  #include <drm/drm_drv.h>
> > > > > >>>>>>>>>>> +#include <drm/drm_fb_helper.h>
> > > > > >>>>>>>>>>>  #include <drm/drmP.h>
> > > > > >>>>>>>>>>>
> > > > > >>>>>>>>>>>  #include "drm_crtc_internal.h"
> > > > > >>>>>>>>>>> @@ -871,6 +872,111 @@ void drm_dev_unregister(struct drm_device *dev)
> > > > > >>>>>>>>>>>  }
> > > > > >>>>>>>>>>>  EXPORT_SYMBOL(drm_dev_unregister);
> > > > > >>>>>>>>>>>
> > > > > >>>>>>>>>>> +static void devm_drm_dev_init_release(void *data)
> > > > > >>>>>>>>>>> +{
> > > > > >>>>>>>>>>> + drm_dev_put(data);
> > > > > >>>>>>>>>
> > > > > >>>>>>>>> We need drm_dev_unplug() here, or this isn't safe.
> > > > > >>>>>>>>
> > > > > >>>>>>>> This function is only used to cover the error path if probe fails before
> > > > > >>>>>>>> devm_drm_dev_register() is called. devm_drm_dev_register_release() is
> > > > > >>>>>>>> the one that calls unplug. There are comments about this in the functions.
> > > > > >>>>>>>
> > > > > >>>>>>> I think I get a prize for being ignorant and blind :-/
> > > > > >>>>>>>
> > > > > >>>>>>>>
> > > > > >>>>>>>>>
> > > > > >>>>>>>>>>> +}
> > > > > >>>>>>>>>>> +
> > > > > >>>>>>>>>>> +/**
> > > > > >>>>>>>>>>> + * devm_drm_dev_init - Resource managed drm_dev_init()
> > > > > >>>>>>>>>>> + * @parent: Parent device object
> > > > > >>>>>>>>>>> + * @dev: DRM device
> > > > > >>>>>>>>>>> + * @driver: DRM driver
> > > > > >>>>>>>>>>> + *
> > > > > >>>>>>>>>>> + * Managed drm_dev_init(). The DRM device initialized with this function is
> > > > > >>>>>>>>>>> + * automatically released on driver detach. You must supply a
> > > > > >>>>>>>>>
> > > > > >>>>>>>>> I think a bit more clarity here would be good:
> > > > > >>>>>>>>>
> > > > > >>>>>>>>> "... automatically released on driver unbind by callind drm_dev_unplug()."
> > > > > >>>>>>>>>
> > > > > >>>>>>>>>>> + * &drm_driver.release callback to control the finalization explicitly.
> > > > > >>>>>>>>>
> > > > > >>>>>>>>> I think a loud warning for these is in order:
> > > > > >>>>>>>>>
> > > > > >>>>>>>>> "WARNING:
> > > > > >>>>>>>>>
> > > > > >>>>>>>>> "In generally it is unsafe to use devm functions for drm structures
> > > > > >>>>>>>>> because the lifetimes of &drm_device and the underlying &device do not
> > > > > >>>>>>>>> match. This here works because it doesn't immediately free anything, but
> > > > > >>>>>>>>> only calls drm_dev_unplug(), which internally decrements the &drm_device
> > > > > >>>>>>>>> refcount through drm_dev_put().
> > > > > >>>>>>>>>
> > > > > >>>>>>>>> "All other drm structures must still be explicitly released in the
> > > > > >>>>>>>>> &drm_driver.release callback."
> > > > > >>>>>>>>>
> > > > > >>>>>>>>> While thinking about this I just realized that with this design we have no
> > > > > >>>>>>>>> good place to call drm_atomic_helper_shutdown(). Which we need to, or all
> > > > > >>>>>>>>> kinds of things will leak badly (connectors, fb, ...), but there's no
> > > > > >>>>>>>>> place to call it:
> > > > > >>>>>>>>> - unbind is too early, since we haven't yet called drm_dev_unplug, and the
> > > > > >>>>>>>>>   drm_dev_unregister in there must be called _before_ we start to shut
> > > > > >>>>>>>>>   down anything.
> > > > > >>>>>>>>> - drm_driver.release is way too late.
> > > > > >>>>>>>>>
> > > > > >>>>>>>>> Ofc for a real hotunplug there's no point in shutting down the hw (it's
> > > > > >>>>>>>>> already gone), but for a driver unload/unbind it would be nice if this
> > > > > >>>>>>>>> happens automatically and in the right order.
> > > > > >>>>>>>>>
> > > > > >>>>>>>>> So not sure what to do here really.
> > > > > >>>>>>>>
> > > > > >>>>>>>> How about this change: (it breaks the rule of pulling helpers into the
> > > > > >>>>>>>> core, so maybe we should put the devm_ functions into the simple KMS
> > > > > >>>>>>>> helper instead?)
> > > > > >>>>>>>
> > > > > >>>>>>> Yeah smells a bit much like midlayer ... What would work is having a pile
> > > > > >>>>>>> more devm_ helper functions, so that we onion-unwrap everything correctly,
> > > > > >>>>>>> and in the right order. So:
> > > > > >>>>>>>
> > > > > >>>>>>> - devm_drm_dev_init (always does a drm_dev_put())
> > > > > >>>>>>>
> > > > > >>>>>>> - devm_drm_poll_enable (shuts down the poll helper with a devm action)
> > > > > >>>>>>>
> > > > > >>>>>>> - devm_drm_mode_config_reset (does an atomic_helper_shutdown() as it's cleanup action)
> > > > > >>>>>>>
> > > > > >>>>>>> - devm_drm_dev_register (grabs an additional drm_dev_get() reference so it
> > > > > >>>>>>>   can call drm_dev_unplug() unconditionally).
> > > > > >>>>>>>
> > > > > >>>>>>
> > > > > >>>>>> Beautiful! I really like this, it's very flexible.
> > > > > >>>>>>
> > > > > >>>>>> Where should devm_drm_mode_config_reset() live? It will pull in the
> > > > > >>>>>> atomic helper...
> > > > > >>>>>
> > > > > >>>>> I think a new drm_devm.c helper would be nice for all this stuff.
> > > > > >>>>> Especially since you can't freely mix devm-based setup/cleanup with
> > > > > >>>>> normal cleanup I think it'd be good to have it all together in one
> > > > > >>>>> place. And perhaps even a code example in the DOC: overview.
> > > > > >>>>>
> > > > > >>>>>>> We'd need to make sure some of the cleanup actions dtrt when the device is
> > > > > >>>>>>> gone, but I think we can achieve that by liberally sprinkling
> > > > > >>>>>>> drm_dev_enter/exit over them, e.g. the the cleanup action for
> > > > > >>>>>>> drm_mode_config_reset would be:
> > > > > >>>>>>>
> > > > > >>>>>>> {
> > > > > >>>>>>>       if (drm_dev_enter())
> > > > > >>>>>>>               return;
> > > > > >>>>>>>
> > > > > >>>>>>>       drm_atomic_helper_shutdown();
> > > > > >>>>>>>
> > > > > >>>>>>>       drm_dev_exit();
> > > > > >>>>>>> }
> > > > > >>>>>>>
> > > > > >>>>>>
> > > > > >>>>>> drm_dev_enter() can only be used to check whether the drm_device is
> > > > > >>>>>> registered or not, it doesn't say anything about the state of the parent
> > > > > >>>>>> device.
> > > > > >>>>>>
> > > > > >>>>>> All we know is that the device is being unbound from the driver, we
> > > > > >>>>>> don't know if it's the device that's being removed or if it's the driver
> > > > > >>>>>> that's unregistered.
> > > > > >>>>>
> > > > > >>>>> You're right, both paths will have called drm_dev_unplug by then.
> > > > > >>>>> Silly me. I really liked my idea :-)
> > > > > >>>>>
> > > > > >>>>>> I have looked at the various call chains:
> > > > > >>>>>>
> > > > > >>>>>> driver_unregister ->
> > > > > >>>>>>     bus_remove_driver ->
> > > > > >>>>>>         driver_detach ->
> > > > > >>>>>>             device_release_driver_internal
> > > > > >>>>>>
> > > > > >>>>>> device_unregister ->
> > > > > >>>>>>     device_del ->
> > > > > >>>>>>         bus_remove_device ->
> > > > > >>>>>>             device_release_driver ->
> > > > > >>>>>>                 device_release_driver_internal
> > > > > >>>>>>
> > > > > >>>>>> sysfs: unbind_store ->
> > > > > >>>>>>     device_release_driver ->
> > > > > >>>>>>         device_release_driver_internal
> > > > > >>>>>>
> > > > > >>>>>> The only way I've found to differentiate between these in a cleanup
> > > > > >>>>>> action is that device_del() uses the bus notifier to signal
> > > > > >>>>>> BUS_NOTIFY_DEL_DEVICE before calling bus_remove_device(). Such a
> > > > > >>>>>> notifier could be used to set a drm_device->parent_removed flag.
> > > > > >>>>>
> > > > > >>>>> Hm, this might upset Greg KH's code taste ... maybe there's a better
> > > > > >>>>> way to do this, but best to prototype a patch with this, send it to
> > > > > >>>>> him and ask how to :-)
> > > > > >>>>>
> > > > > >>>>
> > > > > >>>> I'll leave this to the one that needs it. The tinydrm drivers doesn't
> > > > > >>>> need to touch hw after DRM unregister.
> > > > > >>>>
> > > > > >>>>>> Why is it necessary to call drm_atomic_helper_shutdown() here? Doesn't
> > > > > >>>>>> everything get disabled when userspace closes? It does in my tinydrm
> > > > > >>>>>> world :-)
> > > > > >>>>>
> > > > > >>>>> Iirc fbdev/fbcon can result in leaks ... at least we've had patches
> > > > > >>>>> where drivers leaked drm_connector and drm_framebuffer objects, and
> > > > > >>>>> they've been fixed by calling drm_atomic_helper_shutdown() in the
> > > > > >>>>> unload path. Maybe this is cargo-culting, but it goes way back to
> > > > > >>>>> pre-atomic, where drivers called drm_helper_force_disable_all().
> > > > > >>>>>
> > > > > >>>>> If you try to move the fbcon to your tinydrm drivers (con2fb is
> > > > > >>>>> apparently the cmdline tool you need, never tried it, I only switch
> > > > > >>>>> the kernel's console between fbcon and dummycon and back, not what
> > > > > >>>>> fbcon drivers itself), then I think you should be able to reproduce.
> > > > > >>>>> And maybe you have a better idea how to deal with this all.
> > > > > >>>>>
> > > > > >>>>> Note also that there's been proposals floating around to only close an
> > > > > >>>>> drm_framebuffer, not also remove it (like the current RMFB ioctl
> > > > > >>>>> does), with that closing userspace would not necessarily lead to a
> > > > > >>>>> full cleanup.
> > > > > >>>>>
> > > > > >>>>> Another thing (which doesn't apply to drm_simple_display_pipe drivers)
> > > > > >>>>> is if you have the display on, but no planes showing (i.e. all black).
> > > > > >>>>> Then all the fbs will be cleaned up, but drm_connector will be
> > > > > >>>>> leaking. That's a case where you need drm_atomic_helper_shutdown()
> > > > > >>>>> even if fbcon/fbdev isn't even enabled.
> > > > > >>>>>
> > > > > >>>>
> > > > > >>>> Ok, this means that I don't need to call drm_atomic_helper_shutdown() in
> > > > > >>>> tinydrm. DRM userspace disables the pipe on close and the generic fbdev
> > > > > >>>> emulation also releases everything.
> > > > > >>>> Even so, maybe I should use devm_drm_mode_config_reset() after all to
> > > > > >>>> keep drivers uniform, to avoid confusion: why doesn't he use it?
> > > > > >>>
> > > > > >>> Hm maybe there is an official way to solve this, pulling in Greg+Rafael.
> > > > > >>>
> > > > > >>> Super short summary: We want to start using devm actions to clean up drm
> > > > > >>> drivers. Here's the problem:
> > > > > >>> - For a driver unload/unbind without hotunplug, we want to properly clean
> > > > > >>>   up the hardware and shut it all down.
> > > > > >>
> > > > > >> Then do it on probe/disconnect.
> > > > > >>
> > > > > >>> - But if the device is unplugged already, that's probably not the best
> > > > > >>>   idea, and we only want to clean up the kernel's resources/allocations.
> > > > > >>
> > > > > >> Again, probe/disconnect will be called either way.
> > > > > >>
> > > > > >> But as you note, memory will NOT be freed by the devm stuff if you
> > > > > >> manually unbind a driver from a device.
> > > > > >>
> > > > > >> So don't touch hardware there, it's not going to work :)
> > > > > >>
> > > > > >>> What's the recommendation here? I see a few options:
> > > > > >>>
> > > > > >>> - Make sure everything can deal with this properly. Hotunplug can happen
> > > > > >>>   anytime, so there's a race no matter what.
> > > > > >>
> > > > > >> Yes.
> > > > > >>
> > > > > >>> - Check with the device model whether the struct device is disappearing or
> > > > > >>>   whether we're just dealing with a driver unbind (no idea how to do
> > > > > >>>   that), and act accordingly.
> > > > > >>
> > > > > >> You don't know that, sorry.  Just do any hardware stuff on disconnect.
> > > > > >> Assuming your hardware is still present :)
> > > > > >>
> > > > > >>> - Fundamental question: Touching the hw from devm actions, is that ok? If
> > > > > >>>   not, then the pretty nifty plan laid out in this thread wont work.
> > > > > >>
> > > > > >> Nope, that's not going to work, the device could either be long gone, or
> > > > > >> you will not be called due to unbind happening from userspace.
> > > > > >>
> > > > > >> But really, unbind from userspace is very very rare, it's a developer
> > > > > >> thing mostly.  Oh and a virtual driver thing, but those people are crazy
> > > > > >> :)
> > > > > >>
> > > > > >>> - Something completely different?
> > > > > >>
> > > > > >> Do it in disconnect :)
> > > > > >
> > > > > > Ah, I forgot to mention the important constraint :-) disconnect/unbind
> > > > > > should be the following sequence:
> > > > > >
> > > > > > 1. Unregister all the userspace interfaces (there's a lot of them) and
> > > > > > make sure all the pending ioctls are done so that from now on
> > > > > > userspace sees lots of -EIO (in case it still has fd open, which is
> > > > > > going to be the normal for hotunplug.
> > > > > >
> > > > > > 2. Shut down hw and all ongoing operations (only relevant for unbind,
> > > > > > but needs to be able to cope with sudden hotunplug on top anyway).
> > > > > >
> > > > > > 3. Clean up the kernel mess and release everything.
> > > > > >
> > > > > > Probe is exactly the other way round, so would perfectly fit into the
> > > > > > devm onion cleanup. See in the commented earlier replies above how
> > > > > > that would match in details, but tldr; if we have to do 2. in
> > > > > > disconnect, then we also have to do 1. in disconnected, and only doing
> > > > > > 3. through devm is almost not worth the bother. But if we could do all
> > > > > > three through devm then simple drivers wouldn't even need any
> > > > > > disconnect/unbind callback at all. That's our motivation for trying to
> > > > > > come up with an answer that's not "do it in disconnect". "do it in
> > > > > > disconnect" is how we do it all today already.
> > > > > >
> > > > > > Yes we're trying to make tiny drivers even smaller, we have enough
> > > > > > nowadays that this stuff would be worth it :-)
> > > > > >
> > > > >
> > > > > I think a solution is to say that drivers that want to touch hw on
> > > > > disconnect needs to use device_driver->remove to do that.
> > > > >
> > > > > This is an example driver that doesn't need to touch hw because it's so
> > > > > simple that userspace has disabled the pipeline:
> > > > >
> > > > > static void drm_driver_release(struct drm_device *drm)
> > > > > {
> > > > >     drm_mode_config_cleanup(drm);
> > > > >     drm_dev_fini(drm);
> > > > >     kfree(drm);
> > > > > }
> > > > >
> > > > > static struct drm_driver drm_driver = {
> > > > >     .release = drm_driver_release,
> > > > >     /* ... */
> > > > > };
> > > > >
> > > > > static int driver_probe(struct device *dev)
> > > > > {
> > > > >     struct drm_device *drm;
> > > > >     int ret;
> > > > >
> > > > >     drm = kzalloc(sizeof(*drm), GFP_KERNEL);
> > > > >     if (!drm)
> > > > >             return -ENOMEM;
> > > > >
> > > > >     ret = devm_drm_dev_init(dev, drm, &drm_driver);
> > > > >     if (ret) {
> > > > >             kfree(drm);
> > > > >             return ret;
> > > > >     }
> > > > >
> > > > >     drm_mode_config_init(drm);
> > > > >
> > > > >     /* Aquire various resources, all managed by devres */
> > > > >
> > > > >     drm_mode_config_reset(drm);
> > > > >
> > > > >     return devm_drm_dev_register(drm);
> > > > > }
> > > > >
> > > > > struct device_driver driver = {
> > > > >     .probe = driver_probe,
> > > > > };
> > > > >
> > > > >
> > > > > A driver that wants to touch hardware on disconnect, can look like this:
> > > > >
> > > > > static void drm_driver_release(struct drm_device *drm)
> > > > > {
> > > > >     drm_mode_config_cleanup(drm);
> > > > >     drm_dev_fini(drm);
> > > > >     kfree(drm);
> > > > > }
> > > > >
> > > > > static struct drm_driver drm_driver = {
> > > > >     .release = drm_driver_release,
> > > > >     /* ... */
> > > > > };
> > > > >
> > > > > static int driver_probe(struct device *dev)
> > > > > {
> > > > >     struct drm_device *drm;
> > > > >     int ret;
> > > > >
> > > > >     drm = kzalloc(sizeof(*drm), GFP_KERNEL);
> > > > >     if (!drm)
> > > > >             return -ENOMEM;
> > > > >
> > > > >     ret = devm_drm_dev_init(dev, drm, &drm_driver);
> > > > >     if (ret) {
> > > > >             kfree(drm);
> > > > >             return ret;
> > > > >     }
> > > > >
> > > > >     drm_mode_config_init(drm);
> > > > >
> > > > >     /* Aquire various resources, all managed by devres */
> > > > >
> > > > >     drm_mode_config_reset(drm);
> > > > >
> > > > >     ret = drm_dev_register(drm);
> > > > >     if (ret)
> > > > >             return ret;
> > > > >
> > > > >     drm_dev_get(dev); /* If using drm_dev_unplug() */
> > > > >
> > > > >     dev_set_drvdata(dev, drm);
> > > > >
> > > > >     return 0;
> > > > > }
> > > > >
> > > > > /* This function is called before devres_release_all() */
> > > > > static int driver_remove(struct device *dev)
> > > > > {
> > > > >     struct drm_device *drm = dev_get_drvdata(dev);
> > > > >
> > > > >     drm_dev_unplug(drm); OR drm_dev_unregister(drm);
> > > > >     drm_atomic_helper_shutdown(drm)
> > > > >
> > > > >     return 0;
> > > > > }
> > > > >
> > > > > struct device_driver driver = {
> > > > >     .probe = driver_probe,
> > > > >     .remove = driver_remove,
> > > >
> > > > That's exactly the pattern I'm trying to avoid, because imo your tiny
> > > > driver _also_ should do this. Or we realize that all the current drivers
> > > > doing drm_atomic_helper_shutdown are misguided, but I'm not really
> > > > understanding why.
> > > >
> > > > Having a devm helper which cannot be used for some drivers due to
> > > > fundamental design issues is kinda not great, because it means everyone
> > > > will still use it, and shrug the bugs off as "not my problem". Which is
> > > > what's happening right now with all the devm_kzalloc we have in drm
> > > > drivers that all the get lifetim wrong. But because devm_ is convenient
> > > > everyone uses it, so the driver unload of most drivers is full of bugs.
> > >
> > > driver "unload" should not be full of bugs, how would it?  Anything
> > > created with devm_() will just be freed when the device really goes away
> > > in the system, it shouldn't call back into the driver.
> >
> > The problem is when drivers use devm_ not to allocate hw resources and
> > related things, but structures for objects with other lifetimes. Like
> > open file descriptors shared with the world.
>
> And irqs, which bites everyone in the end.  You have to be careful here,
> never tie a devm allocation to an object with another reference count,
> that's just a bug.

The classic "I forgot to shut down the interrupt before releasing
driver structures and now the irq handler oopsed" or something more
sinister? gpus definitely needs lots of interrupt stuff, so if that is
fundamentally broken then our devm ideas won't work out at all. And
yes the idea is to register devm actions which call drm_dev_put() and
stuff like that, so people stop freeing stuff to early through the
wrong devm functions because devm_kmalloc is less typing :-)
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

* Re: devm actions and hw clenaup (was Re: [PATCH 01/11] drm: Add devm_drm_dev_init/register)
  2019-01-29 23:14                                 ` Daniel Vetter
@ 2019-01-30  7:14                                   ` Greg KH
  0 siblings, 0 replies; 57+ messages in thread
From: Greg KH @ 2019-01-30  7:14 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: David Lechner, dri-devel, Rafael J. Wysocki

On Wed, Jan 30, 2019 at 12:14:46AM +0100, Daniel Vetter wrote:
> On Tue, Jan 29, 2019 at 8:27 PM Greg KH <gregkh@linuxfoundation.org> wrote:
> > On Tue, Jan 29, 2019 at 07:10:55PM +0100, Daniel Vetter wrote:
> > > The problem is when drivers use devm_ not to allocate hw resources and
> > > related things, but structures for objects with other lifetimes. Like
> > > open file descriptors shared with the world.
> >
> > And irqs, which bites everyone in the end.  You have to be careful here,
> > never tie a devm allocation to an object with another reference count,
> > that's just a bug.
> 
> The classic "I forgot to shut down the interrupt before releasing
> driver structures and now the irq handler oopsed" or something more
> sinister?

The classic is always the best, and most common :)

> gpus definitely needs lots of interrupt stuff, so if that is
> fundamentally broken then our devm ideas won't work out at all.

You better not be using devm for your irqs right now then.

good luck!

greg k-h
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

end of thread, other threads:[~2019-01-30  7:14 UTC | newest]

Thread overview: 57+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-01-20 11:43 [PATCH 00/11] drm/tinydrm: Remove tinydrm_device Noralf Trønnes
2019-01-20 11:43 ` [PATCH 01/11] drm: Add devm_drm_dev_init/register Noralf Trønnes
2019-01-21  6:11   ` Sam Ravnborg
2019-01-21 13:09     ` Noralf Trønnes
2019-01-21  9:10   ` Daniel Vetter
2019-01-21  9:55     ` Daniel Vetter
2019-01-21 12:21       ` Noralf Trønnes
2019-01-22  9:32         ` Daniel Vetter
2019-01-22 19:07           ` Noralf Trønnes
2019-01-22 19:30             ` Daniel Vetter
2019-01-23 10:54               ` Noralf Trønnes
2019-01-24 10:43                 ` devm actions and hw clenaup (was Re: [PATCH 01/11] drm: Add devm_drm_dev_init/register) Daniel Vetter
2019-01-24 17:46                   ` Greg KH
2019-01-24 17:57                     ` Daniel Vetter
2019-01-29 14:34                       ` Noralf Trønnes
2019-01-29 15:16                         ` Greg KH
2019-01-29 16:50                         ` Daniel Vetter
2019-01-29 17:26                           ` Noralf Trønnes
2019-01-29 17:36                           ` Greg KH
2019-01-29 18:10                             ` Daniel Vetter
2019-01-29 19:27                               ` Greg KH
2019-01-29 23:14                                 ` Daniel Vetter
2019-01-30  7:14                                   ` Greg KH
2019-01-22  9:35   ` [PATCH 01/11] drm: Add devm_drm_dev_init/register Daniel Vetter
2019-01-20 11:43 ` [PATCH 02/11] drm/modes: Add DRM_SIMPLE_MODE() Noralf Trønnes
2019-01-20 16:37   ` Ilia Mirkin
2019-01-20 17:27     ` Noralf Trønnes
2019-01-20 11:43 ` [PATCH 03/11] drm/simple-kms-helper: Add drm_simple_connector_create() Noralf Trønnes
2019-01-20 22:14   ` Sam Ravnborg
2019-01-21  9:22   ` Daniel Vetter
2019-01-24 14:38     ` Noralf Trønnes
2019-01-24 14:53       ` Hans de Goede
2019-01-25 12:05         ` Noralf Trønnes
2019-01-20 11:43 ` [PATCH 04/11] drm/tinydrm: Remove tinydrm_display_pipe_init() Noralf Trønnes
2019-01-21  6:30   ` Sam Ravnborg
2019-01-21  9:15   ` Daniel Vetter
2019-01-28 14:46     ` Noralf Trønnes
2019-01-20 11:43 ` [PATCH 05/11] drm/tinydrm/mipi-dbi: Add drm_to_mipi_dbi() Noralf Trønnes
2019-01-21  6:34   ` Sam Ravnborg
2019-01-20 11:43 ` [PATCH 06/11] drm/tinydrm: Remove tinydrm_shutdown() Noralf Trønnes
2019-01-21  7:12   ` Sam Ravnborg
2019-01-20 11:43 ` [PATCH 07/11] drm/tinydrm/repaper: Use devm_drm_dev_*() Noralf Trønnes
2019-01-20 22:22   ` Sam Ravnborg
2019-01-20 22:25     ` Sam Ravnborg
2019-01-21 13:15       ` Noralf Trønnes
2019-01-20 11:43 ` [PATCH 08/11] drm/tinydrm: " Noralf Trønnes
2019-01-20 11:43 ` [PATCH 09/11] drm/tinydrm: Remove tinydrm_device Noralf Trønnes
2019-01-21  8:13   ` Sam Ravnborg
2019-01-21  9:29   ` Daniel Vetter
2019-01-21 13:30     ` Noralf Trønnes
2019-01-20 11:43 ` [PATCH 10/11] drm/tinydrm: Use drm_dev_enter/exit() Noralf Trønnes
2019-01-20 11:43 ` [PATCH 11/11] drm/fb-helper: generic: Don't take module ref for fbcon Noralf Trønnes
2019-01-21  9:05   ` Daniel Vetter
2019-01-28 14:40     ` Noralf Trønnes
2019-01-29  8:45       ` Daniel Vetter
2019-01-21  8:34 ` [PATCH 00/11] drm/tinydrm: Remove tinydrm_device Sam Ravnborg
2019-01-21 13:20   ` Noralf Trønnes

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.