dri-devel.lists.freedesktop.org archive mirror
 help / color / mirror / Atom feed
From: "Noralf Trønnes" <noralf@tronnes.org>
To: dri-devel@lists.freedesktop.org
Subject: [RFC 7/7] drm/tinydrm: Switch from CMA to shmem buffers
Date: Wed, 12 Jul 2017 15:46:05 +0200	[thread overview]
Message-ID: <1499867165-60925-8-git-send-email-noralf@tronnes.org> (raw)
In-Reply-To: <1499867165-60925-1-git-send-email-noralf@tronnes.org>

This move makes tinydrm useful for more drivers. tinydrm doesn't
need continous memory, but at the time it was convinient to use
the CMA library. The spi core can do dma on vmalloc addresses
making this possible.

Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
---
 drivers/gpu/drm/tinydrm/Kconfig             |   2 +-
 drivers/gpu/drm/tinydrm/core/tinydrm-core.c | 147 ++++++++++++----------------
 drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c |   4 +-
 drivers/gpu/drm/tinydrm/mi0283qt.c          |   2 +-
 drivers/gpu/drm/tinydrm/mipi-dbi.c          |  41 +++-----
 include/drm/tinydrm/tinydrm.h               |  32 ++----
 6 files changed, 92 insertions(+), 136 deletions(-)

diff --git a/drivers/gpu/drm/tinydrm/Kconfig b/drivers/gpu/drm/tinydrm/Kconfig
index 3504c53..da9b8b8 100644
--- a/drivers/gpu/drm/tinydrm/Kconfig
+++ b/drivers/gpu/drm/tinydrm/Kconfig
@@ -2,7 +2,7 @@ menuconfig DRM_TINYDRM
 	tristate "Support for simple displays"
 	depends on DRM
 	select DRM_KMS_HELPER
-	select DRM_KMS_CMA_HELPER
+	select DRM_KMS_SHMEM_HELPER
 	select BACKLIGHT_LCD_SUPPORT
 	select BACKLIGHT_CLASS_DEVICE
 	help
diff --git a/drivers/gpu/drm/tinydrm/core/tinydrm-core.c b/drivers/gpu/drm/tinydrm/core/tinydrm-core.c
index 551709e..fb2cc1f 100644
--- a/drivers/gpu/drm/tinydrm/core/tinydrm-core.c
+++ b/drivers/gpu/drm/tinydrm/core/tinydrm-core.c
@@ -10,6 +10,7 @@
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_helper.h>
 #include <drm/tinydrm/tinydrm.h>
 #include <linux/device.h>
 #include <linux/dma-buf.h>
@@ -21,7 +22,7 @@
  *
  * 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).
+ * shmem helper and have support for framebuffer flushing (dirty).
  * fbdev support is also included.
  *
  */
@@ -47,80 +48,33 @@ void tinydrm_lastclose(struct drm_device *drm)
 	struct tinydrm_device *tdev = drm->dev_private;
 
 	DRM_DEBUG_KMS("\n");
-	drm_fbdev_cma_restore_mode(tdev->fbdev_cma);
+	if (tdev->fbdev)
+		drm_fb_helper_restore_fbdev_mode_unlocked(tdev->fbdev);
 }
 EXPORT_SYMBOL(tinydrm_lastclose);
 
 /**
- * tinydrm_gem_cma_prime_import_sg_table - Produce a CMA GEM object from
- *     another driver's scatter/gather table of pinned pages
- * @drm: DRM device to import into
- * @attach: DMA-BUF attachment
- * @sgt: Scatter/gather table of pinned pages
- *
- * This function imports a scatter/gather table exported via DMA-BUF by
- * another driver using drm_gem_cma_prime_import_sg_table(). It sets the
- * kernel virtual address on the CMA object. Drivers should use this as their
- * &drm_driver->gem_prime_import_sg_table callback if they need the virtual
- * address. tinydrm_gem_cma_free_object() should be used in combination with
- * this function.
+ * tinydrm_gem_create_object - Create shmem GEM object
+ * @drm: DRM device
+ * @size: Size
  *
- * Returns:
- * A pointer to a newly created GEM object or an ERR_PTR-encoded negative
- * error code on failure.
+ * This function set cache mode to cached. Drivers should use this as their
+ * &drm_driver->gem_create_object callback.
  */
-struct drm_gem_object *
-tinydrm_gem_cma_prime_import_sg_table(struct drm_device *drm,
-				      struct dma_buf_attachment *attach,
-				      struct sg_table *sgt)
+struct drm_gem_object *tinydrm_gem_create_object(struct drm_device *drm,
+						 size_t size)
 {
-	struct drm_gem_cma_object *cma_obj;
-	struct drm_gem_object *obj;
-	void *vaddr;
+	struct drm_gem_shmem_object *obj;
 
-	vaddr = dma_buf_vmap(attach->dmabuf);
-	if (!vaddr) {
-		DRM_ERROR("Failed to vmap PRIME buffer\n");
+	obj = kzalloc(sizeof(*obj), GFP_KERNEL);
+	if (!obj)
 		return ERR_PTR(-ENOMEM);
-	}
-
-	obj = drm_gem_cma_prime_import_sg_table(drm, attach, sgt);
-	if (IS_ERR(obj)) {
-		dma_buf_vunmap(attach->dmabuf, vaddr);
-		return obj;
-	}
 
-	cma_obj = to_drm_gem_cma_obj(obj);
-	cma_obj->vaddr = vaddr;
+	obj->cache_mode = DRM_GEM_SHMEM_BO_CACHED;
 
-	return obj;
+	return &obj->base;
 }
-EXPORT_SYMBOL(tinydrm_gem_cma_prime_import_sg_table);
-
-/**
- * tinydrm_gem_cma_free_object - Free resources associated with a CMA GEM
- *                               object
- * @gem_obj: GEM object to free
- *
- * This function frees the backing memory of the CMA GEM object, cleans up the
- * GEM object state and frees the memory used to store the object itself using
- * drm_gem_cma_free_object(). It also handles PRIME buffers which has the kernel
- * virtual address set by tinydrm_gem_cma_prime_import_sg_table(). Drivers
- * can use this as their &drm_driver->gem_free_object callback.
- */
-void tinydrm_gem_cma_free_object(struct drm_gem_object *gem_obj)
-{
-	if (gem_obj->import_attach) {
-		struct drm_gem_cma_object *cma_obj;
-
-		cma_obj = to_drm_gem_cma_obj(gem_obj);
-		dma_buf_vunmap(gem_obj->import_attach->dmabuf, cma_obj->vaddr);
-		cma_obj->vaddr = NULL;
-	}
-
-	drm_gem_cma_free_object(gem_obj);
-}
-EXPORT_SYMBOL_GPL(tinydrm_gem_cma_free_object);
+EXPORT_SYMBOL(tinydrm_gem_create_object);
 
 static struct drm_framebuffer *
 tinydrm_fb_create(struct drm_device *drm, struct drm_file *file_priv,
@@ -128,7 +82,7 @@ tinydrm_fb_create(struct drm_device *drm, struct drm_file *file_priv,
 {
 	struct tinydrm_device *tdev = drm->dev_private;
 
-	return drm_fb_cma_create_with_funcs(drm, file_priv, mode_cmd,
+	return drm_fb_gem_create_with_funcs(drm, file_priv, mode_cmd,
 					    tdev->fb_funcs);
 }
 
@@ -210,38 +164,64 @@ int devm_tinydrm_init(struct device *parent, struct tinydrm_device *tdev,
 }
 EXPORT_SYMBOL(devm_tinydrm_init);
 
-static int tinydrm_register(struct tinydrm_device *tdev)
+static int tinydrm_fbdev_probe(struct drm_fb_helper *helper,
+			       struct drm_fb_helper_surface_size *sizes)
+{
+	struct tinydrm_device *tdev = helper->dev->dev_private;
+
+	return drm_fb_shmem_fbdev_probe(helper, sizes, tdev->fb_funcs);
+}
+
+static const struct drm_fb_helper_funcs tinydrm_fb_helper_funcs = {
+	.fb_probe = tinydrm_fbdev_probe,
+};
+
+static int tinydrm_fbdev_init(struct tinydrm_device *tdev)
 {
 	struct drm_device *drm = tdev->drm;
 	int bpp = drm->mode_config.preferred_depth;
-	struct drm_fbdev_cma *fbdev;
+	int ret;
+
+	tdev->fbdev = kzalloc(sizeof(*tdev->fbdev), GFP_KERNEL);
+	if (!tdev->fbdev)
+		return -ENOMEM;
+
+	ret = drm_fb_helper_simple_init(drm, tdev->fbdev, bpp ? bpp : 32,
+					drm->mode_config.num_connector,
+					&tinydrm_fb_helper_funcs);
+	if (ret) {
+		kfree(tdev->fbdev);
+		tdev->fbdev = NULL;
+		return ret;
+	}
+
+	return 0;
+}
+
+static int tinydrm_register(struct tinydrm_device *tdev)
+{
 	int ret;
 
 	ret = drm_dev_register(tdev->drm, 0);
 	if (ret)
 		return ret;
 
-	fbdev = drm_fbdev_cma_init_with_funcs(drm, bpp ? bpp : 32,
-					      drm->mode_config.num_connector,
-					      tdev->fb_funcs);
-	if (IS_ERR(fbdev))
-		DRM_ERROR("Failed to initialize fbdev: %ld\n", PTR_ERR(fbdev));
-	else
-		tdev->fbdev_cma = fbdev;
+	if (tinydrm_fbdev_init(tdev))
+		DRM_WARN("Failed to initialize fbdev\n");
 
-	return 0;
+	return ret;
 }
 
 static void tinydrm_unregister(struct tinydrm_device *tdev)
 {
-	struct drm_fbdev_cma *fbdev_cma = tdev->fbdev_cma;
+	struct drm_fb_helper *fbdev = tdev->fbdev;
 
-	drm_atomic_helper_shutdown(tdev->drm);
 	/* don't restore fbdev in lastclose, keep pipeline disabled */
-	tdev->fbdev_cma = NULL;
+	tdev->fbdev = NULL;
+	drm_atomic_helper_shutdown(tdev->drm);
+	drm_fb_helper_simple_fini(fbdev);
 	drm_dev_unregister(tdev->drm);
-	if (fbdev_cma)
-		drm_fbdev_cma_fini(fbdev_cma);
+	kfree(fbdev);
 }
 
 static void devm_tinydrm_register_release(void *data)
@@ -311,10 +291,12 @@ int tinydrm_suspend(struct tinydrm_device *tdev)
 		return -EINVAL;
 	}
 
-	drm_fbdev_cma_set_suspend_unlocked(tdev->fbdev_cma, 1);
+	if (tdev->fbdev)
+		drm_fb_helper_set_suspend_unlocked(tdev->fbdev, 1);
 	state = drm_atomic_helper_suspend(tdev->drm);
 	if (IS_ERR(state)) {
-		drm_fbdev_cma_set_suspend_unlocked(tdev->fbdev_cma, 0);
+		if (tdev->fbdev)
+			drm_fb_helper_set_suspend_unlocked(tdev->fbdev, 0);
 		return PTR_ERR(state);
 	}
 
@@ -352,7 +334,8 @@ int tinydrm_resume(struct tinydrm_device *tdev)
 		return ret;
 	}
 
-	drm_fbdev_cma_set_suspend_unlocked(tdev->fbdev_cma, 0);
+	if (tdev->fbdev)
+		drm_fb_helper_set_suspend_unlocked(tdev->fbdev, 0);
 
 	return 0;
 }
diff --git a/drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c b/drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c
index ec43fb7..a30518c 100644
--- a/drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c
+++ b/drivers/gpu/drm/tinydrm/core/tinydrm-pipe.c
@@ -145,7 +145,7 @@ EXPORT_SYMBOL(tinydrm_display_pipe_update);
  * @pipe: Simple display pipe
  * @plane_state: Plane state
  *
- * This function uses drm_fb_cma_prepare_fb() to check if the plane FB has an
+ * This function uses drm_fb_shmem_prepare_fb() to check if the plane FB has a
  * dma-buf attached, extracts the exclusive fence and attaches it to plane
  * state for the atomic helper to wait on. Drivers can use this as their
  * &drm_simple_display_pipe_funcs->prepare_fb callback.
@@ -153,7 +153,7 @@ EXPORT_SYMBOL(tinydrm_display_pipe_update);
 int tinydrm_display_pipe_prepare_fb(struct drm_simple_display_pipe *pipe,
 				    struct drm_plane_state *plane_state)
 {
-	return drm_fb_cma_prepare_fb(&pipe->plane, plane_state);
+	return drm_fb_gem_prepare_fb(&pipe->plane, plane_state);
 }
 EXPORT_SYMBOL(tinydrm_display_pipe_prepare_fb);
 
diff --git a/drivers/gpu/drm/tinydrm/mi0283qt.c b/drivers/gpu/drm/tinydrm/mi0283qt.c
index 482ff1c3..8d11cd0 100644
--- a/drivers/gpu/drm/tinydrm/mi0283qt.c
+++ b/drivers/gpu/drm/tinydrm/mi0283qt.c
@@ -132,7 +132,7 @@ static const struct drm_display_mode mi0283qt_mode = {
 	TINYDRM_MODE(320, 240, 58, 43),
 };
 
-DEFINE_DRM_GEM_CMA_FOPS(mi0283qt_fops);
+DEFINE_DRM_GEM_SHMEM_FOPS(mi0283qt_fops);
 
 static struct drm_driver mi0283qt_driver = {
 	.driver_features	= DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME |
diff --git a/drivers/gpu/drm/tinydrm/mipi-dbi.c b/drivers/gpu/drm/tinydrm/mipi-dbi.c
index c83eeb7..a4d8c93 100644
--- a/drivers/gpu/drm/tinydrm/mipi-dbi.c
+++ b/drivers/gpu/drm/tinydrm/mipi-dbi.c
@@ -156,10 +156,11 @@ EXPORT_SYMBOL(mipi_dbi_command_buf);
 static int mipi_dbi_buf_copy(void *dst, struct drm_framebuffer *fb,
 				struct drm_clip_rect *clip, bool swap)
 {
-	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 drm_gem_object *gem_obj = drm_fb_gem_get_obj(fb, 0);
+	struct drm_gem_shmem_object *obj = to_drm_gem_shmem_obj(gem_obj);
+	struct dma_buf_attachment *import_attach = gem_obj->import_attach;
 	struct drm_format_name_buf format_name;
-	void *src = cma_obj->vaddr;
+	void *src = obj->vaddr;
 	int ret = 0;
 
 	if (import_attach) {
@@ -198,7 +199,8 @@ static int mipi_dbi_fb_dirty(struct drm_framebuffer *fb,
 			     struct drm_clip_rect *clips,
 			     unsigned int num_clips)
 {
-	struct drm_gem_cma_object *cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
+	struct drm_gem_object *gem_obj = drm_fb_gem_get_obj(fb, 0);
+	struct drm_gem_shmem_object *obj = to_drm_gem_shmem_obj(gem_obj);
 	struct tinydrm_device *tdev = fb->dev->dev_private;
 	struct mipi_dbi *mipi = mipi_dbi_from_tinydrm(tdev);
 	bool swap = mipi->swap_bytes;
@@ -216,6 +218,10 @@ static int mipi_dbi_fb_dirty(struct drm_framebuffer *fb,
 	if (tdev->pipe.plane.fb != fb)
 		goto out_unlock;
 
+	ret = drm_gem_shmem_vmap(obj);
+	if (ret)
+		goto out_unlock;
+
 	full = tinydrm_merge_clips(&clip, clips, num_clips, flags,
 				   fb->width, fb->height);
 
@@ -229,7 +235,7 @@ static int mipi_dbi_fb_dirty(struct drm_framebuffer *fb,
 		if (ret)
 			goto out_unlock;
 	} else {
-		tr = cma_obj->vaddr;
+		tr = obj->vaddr;
 	}
 
 	mipi_dbi_command(mipi, MIPI_DCS_SET_COLUMN_ADDRESS,
@@ -253,8 +259,8 @@ static int mipi_dbi_fb_dirty(struct drm_framebuffer *fb,
 }
 
 static const struct drm_framebuffer_funcs mipi_dbi_fb_funcs = {
-	.destroy	= drm_fb_cma_destroy,
-	.create_handle	= drm_fb_cma_create_handle,
+	.destroy	= drm_fb_gem_destroy,
+	.create_handle	= drm_fb_gem_create_handle,
 	.dirty		= mipi_dbi_fb_dirty,
 };
 
@@ -807,31 +813,12 @@ int mipi_dbi_spi_init(struct spi_device *spi, struct mipi_dbi *mipi,
 {
 	size_t tx_size = tinydrm_spi_max_transfer_size(spi, 0);
 	struct device *dev = &spi->dev;
-	int ret;
 
 	if (tx_size < 16) {
 		DRM_ERROR("SPI transmit buffer too small: %zu\n", tx_size);
 		return -EINVAL;
 	}
 
-	/*
-	 * Even though it's not the SPI device that does DMA (the master does),
-	 * the dma mask is necessary for the dma_alloc_wc() in
-	 * drm_gem_cma_create(). The dma_addr returned will be a physical
-	 * adddress which might be different from the bus address, but this is
-	 * not a problem since the address will not be used.
-	 * The virtual address is used in the transfer and the SPI core
-	 * re-maps it on the SPI master device using the DMA streaming API
-	 * (spi_map_buf()).
-	 */
-	if (!dev->coherent_dma_mask) {
-		ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32));
-		if (ret) {
-			dev_warn(dev, "Failed to set dma mask %d\n", ret);
-			return ret;
-		}
-	}
-
 	mipi->spi = spi;
 	mipi->read_commands = mipi_dbi_dcs_read_commands;
 
@@ -966,7 +953,7 @@ static const struct file_operations mipi_dbi_debugfs_command_fops = {
 };
 
 static const struct drm_info_list mipi_dbi_debugfs_list[] = {
-	{ "fb",   drm_fb_cma_debugfs_show, 0 },
+	{ "fb",   drm_fb_shmem_debugfs_show, 0 },
 };
 
 /**
diff --git a/include/drm/tinydrm/tinydrm.h b/include/drm/tinydrm/tinydrm.h
index 00b800d..93d7a03 100644
--- a/include/drm/tinydrm/tinydrm.h
+++ b/include/drm/tinydrm/tinydrm.h
@@ -10,8 +10,9 @@
 #ifndef __LINUX_TINYDRM_H
 #define __LINUX_TINYDRM_H
 
-#include <drm/drm_gem_cma_helper.h>
-#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_gem_shmem_helper.h>
+#include <drm/drm_fb_gem_helper.h>
+#include <drm/drm_fb_shmem_helper.h>
 #include <drm/drm_simple_kms_helper.h>
 
 /**
@@ -19,7 +20,7 @@
  * @drm: DRM device
  * @pipe: Display pipe structure
  * @dirty_lock: Serializes framebuffer flushing
- * @fbdev_cma: CMA fbdev structure
+ * @fbdev: fbdev helper
  * @suspend_state: Atomic state when suspended
  * @fb_funcs: Framebuffer functions used when creating framebuffers
  */
@@ -27,7 +28,7 @@ struct tinydrm_device {
 	struct drm_device *drm;
 	struct drm_simple_display_pipe pipe;
 	struct mutex dirty_lock;
-	struct drm_fbdev_cma *fbdev_cma;
+	struct drm_fb_helper *fbdev;
 	struct drm_atomic_state *suspend_state;
 	const struct drm_framebuffer_funcs *fb_funcs;
 };
@@ -45,20 +46,8 @@ pipe_to_tinydrm(struct drm_simple_display_pipe *pipe)
  * the &drm_driver structure.
  */
 #define TINYDRM_GEM_DRIVER_OPS \
-	.gem_free_object	= tinydrm_gem_cma_free_object, \
-	.gem_vm_ops		= &drm_gem_cma_vm_ops, \
-	.prime_handle_to_fd	= drm_gem_prime_handle_to_fd, \
-	.prime_fd_to_handle	= drm_gem_prime_fd_to_handle, \
-	.gem_prime_import	= drm_gem_prime_import, \
-	.gem_prime_export	= drm_gem_prime_export, \
-	.gem_prime_get_sg_table	= drm_gem_cma_prime_get_sg_table, \
-	.gem_prime_import_sg_table = tinydrm_gem_cma_prime_import_sg_table, \
-	.gem_prime_vmap		= drm_gem_cma_prime_vmap, \
-	.gem_prime_vunmap	= drm_gem_cma_prime_vunmap, \
-	.gem_prime_mmap		= drm_gem_cma_prime_mmap, \
-	.dumb_create		= drm_gem_cma_dumb_create, \
-	.dumb_map_offset	= drm_gem_cma_dumb_map_offset, \
-	.dumb_destroy		= drm_gem_dumb_destroy
+	.gem_create_object = tinydrm_gem_create_object, \
+	DRM_GEM_SHMEM_DRIVER_OPS
 
 /**
  * TINYDRM_MODE - tinydrm display mode
@@ -84,11 +73,8 @@ pipe_to_tinydrm(struct drm_simple_display_pipe *pipe)
 	.clock = 1 /* pass validation */
 
 void tinydrm_lastclose(struct drm_device *drm);
-void tinydrm_gem_cma_free_object(struct drm_gem_object *gem_obj);
-struct drm_gem_object *
-tinydrm_gem_cma_prime_import_sg_table(struct drm_device *drm,
-				      struct dma_buf_attachment *attach,
-				      struct sg_table *sgt);
+struct drm_gem_object *tinydrm_gem_create_object(struct drm_device *drm,
+						 size_t size);
 int devm_tinydrm_init(struct device *parent, struct tinydrm_device *tdev,
 		      const struct drm_framebuffer_funcs *fb_funcs,
 		      struct drm_driver *driver);
-- 
2.7.4

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

      parent reply	other threads:[~2017-07-12 13:46 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-07-12 13:45 [RFC 0/7] drm: Add shmem GEM library Noralf Trønnes
2017-07-12 13:45 ` [RFC 1/7] drm/gem: Add drm_gem_dumb_map_offset() Noralf Trønnes
2017-07-18 21:06   ` Noralf Trønnes
2017-07-20  8:04     ` Daniel Vetter
2017-07-19 21:01   ` Eric Anholt
2017-07-19 22:13     ` Noralf Trønnes
2017-07-20  8:00       ` Daniel Vetter
2017-07-21 18:41         ` Noralf Trønnes
2017-07-24  8:28           ` Daniel Vetter
2017-07-24 19:41             ` Noralf Trønnes
2017-07-12 13:46 ` [RFC 2/7] drm: Add GEM backed framebuffer library Noralf Trønnes
2017-07-18 15:42   ` Noralf Trønnes
2017-07-19 20:59     ` Eric Anholt
2017-07-20  8:10     ` Daniel Vetter
2017-07-21 18:39       ` Noralf Trønnes
2017-07-25  7:00         ` Daniel Vetter
2017-07-12 13:46 ` [RFC 3/7] drm/fb-helper: Support shadow buffer with deferred io Noralf Trønnes
2017-07-12 13:46 ` [RFC 4/7] drm/fb-helper: Add simple init/fini functions Noralf Trønnes
2017-07-12 13:46 ` [RFC 5/7] drm: Add library for shmem backed GEM objects Noralf Trønnes
2017-07-12 13:46 ` [RFC 6/7] drm: Add kms library for shmem backed GEM Noralf Trønnes
2017-07-12 13:46 ` Noralf Trønnes [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1499867165-60925-8-git-send-email-noralf@tronnes.org \
    --to=noralf@tronnes.org \
    --cc=dri-devel@lists.freedesktop.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).