All of lore.kernel.org
 help / color / mirror / Atom feed
From: Daniel Vetter <daniel@ffwll.ch>
To: Rob Clark <robdclark@gmail.com>
Cc: dri-devel@lists.freedesktop.org
Subject: Re: [PATCH 13/13] RFC: drm: Atomic modeset ioctl
Date: Wed, 17 Dec 2014 16:04:15 +0100	[thread overview]
Message-ID: <20141217150415.GS2711@phenom.ffwll.local> (raw)
In-Reply-To: <1418771141-16954-14-git-send-email-robdclark@gmail.com>

On Tue, Dec 16, 2014 at 06:05:41PM -0500, Rob Clark wrote:
> The atomic modeset ioctl can be used to push any number of new values
> for object properties. The driver can then check the full device
> configuration as single unit, and try to apply the changes atomically.
> 
> The ioctl simply takes a list of object IDs and property IDs and their
> values.
> 
> Originally based on a patch from Ville Syrjälä, although it has mutated
> (mutilated?) enough since then that you probably shouldn't blame it on
> him ;-)
> 
> TODO:
>  * hide behind moduleparam initially

Yeah we need this, and I think we really need this (plus
file_priv->atomic_kms to allow new userspace to enable atomic, similar to
file_priv->universal_planes) even for patches 6, 10&11.

>  * either bring back blob property support, or add ioctls for create/
>    destroy blob properties so they can be passed in by id in this
>    ioctl.  I'm leaning towards the latter approach as (a) it is more
>    in line with how blob properties are read, and (b) the atomic
>    ioctl function is big enough already.

The blob approach is a bit more work, since we need to copypaste all the
logic we have for per-filpriv framebuffers: cleanup lists & refcounting.
Not sure whether we should just go with lots more properties, they're
cheap ;-) But I'm ok with this blob approach, too. Adding blobs to the
atomic ioctl itself feels a bit wrong.

One thing I have to consider with my intel hat on is the bridge we need to
adf. Everyting plain 64bit props would be a bit easier to marshal through
adf into atomic interfaces. But only slightly, we can just add an in-line
set of blobs at the end and write them directly, too. So either approach
is fine really.

Below a few comments all over, but looks good overall.

Cheers, Daniel

> 
> Signed-off-by: Rob Clark <robdclark@gmail.com>
> ---
>  drivers/gpu/drm/drm_atomic.c | 308 +++++++++++++++++++++++++++++++++++++++++++
>  drivers/gpu/drm/drm_crtc.c   |   4 +-
>  drivers/gpu/drm/drm_ioctl.c  |   1 +
>  include/drm/drm_crtc.h       |   6 +
>  include/uapi/drm/drm.h       |   1 +
>  include/uapi/drm/drm_mode.h  |  21 +++
>  6 files changed, 339 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
> index 71b48a0..e7a45ec 100644
> --- a/drivers/gpu/drm/drm_atomic.c
> +++ b/drivers/gpu/drm/drm_atomic.c
> @@ -960,3 +960,311 @@ int drm_atomic_async_commit(struct drm_atomic_state *state)
>  	return config->funcs->atomic_commit(state->dev, state, true);
>  }
>  EXPORT_SYMBOL(drm_atomic_async_commit);
> +
> +/*
> + * The big monstor ioctl
> + */
> +
> +static struct drm_pending_vblank_event *create_vblank_event(
> +		struct drm_device *dev, struct drm_file *file_priv, uint64_t user_data)
> +{
> +	struct drm_pending_vblank_event *e = NULL;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&dev->event_lock, flags);
> +	if (file_priv->event_space < sizeof e->event) {
> +		spin_unlock_irqrestore(&dev->event_lock, flags);
> +		goto out;
> +	}
> +	file_priv->event_space -= sizeof e->event;
> +	spin_unlock_irqrestore(&dev->event_lock, flags);
> +
> +	e = kzalloc(sizeof *e, GFP_KERNEL);
> +	if (e == NULL) {
> +		spin_lock_irqsave(&dev->event_lock, flags);
> +		file_priv->event_space += sizeof e->event;
> +		spin_unlock_irqrestore(&dev->event_lock, flags);
> +		goto out;
> +	}
> +
> +	e->event.base.type = DRM_EVENT_FLIP_COMPLETE;
> +	e->event.base.length = sizeof e->event;
> +	e->event.user_data = user_data;
> +	e->base.event = &e->event.base;
> +	e->base.file_priv = file_priv;
> +	e->base.destroy = (void (*) (struct drm_pending_event *)) kfree;
> +
> +out:
> +	return e;
> +}
> +
> +static void destroy_vblank_event(struct drm_device *dev,
> +		struct drm_file *file_priv, struct drm_pending_vblank_event *e)
> +{
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&dev->event_lock, flags);
> +	file_priv->event_space += sizeof e->event;
> +	spin_unlock_irqrestore(&dev->event_lock, flags);
> +	kfree(e);
> +}

I guess these two are the skeleton functions for the refactoring you've
started? Just an aside really.

> +int drm_mode_atomic_ioctl(struct drm_device *dev,
> +			  void *data, struct drm_file *file_priv)
> +{
> +	struct drm_mode_atomic *arg = data;
> +	uint32_t __user *objs_ptr = (uint32_t __user *)(unsigned long)(arg->objs_ptr);
> +	uint32_t __user *count_props_ptr = (uint32_t __user *)(unsigned long)(arg->count_props_ptr);
> +	uint32_t __user *props_ptr = (uint32_t __user *)(unsigned long)(arg->props_ptr);
> +	uint64_t __user *prop_values_ptr = (uint64_t __user *)(unsigned long)(arg->prop_values_ptr);
> +	unsigned int copied_objs, copied_props;
> +	struct drm_atomic_state *state;
> +	struct drm_modeset_acquire_ctx ctx;
> +	struct drm_plane *plane;
> +	unsigned plane_mask = 0;
> +	int ret = 0;
> +	unsigned int i, j;
> +
> +	if (arg->flags & ~DRM_MODE_ATOMIC_FLAGS)
> +		return -EINVAL;
> +
> +	/* can't test and expect an event at the same time. */
> +	if ((arg->flags & DRM_MODE_ATOMIC_TEST_ONLY) &&
> +			(arg->flags & DRM_MODE_PAGE_FLIP_EVENT))
> +		return -EINVAL;

Maybe check for async implies nonblock, too?

And I think we need to write the flags from userspace into
drm_atomic_state. At least for most hw async flips have fairly strict
requirements, a lot stricter than normal flips at least.

Or we just disallow async updates for now through atomic.

> +
> +	drm_modeset_acquire_init(&ctx, 0);
> +
> +	state = drm_atomic_state_alloc(dev);
> +	if (!state)
> +		return -ENOMEM;
> +
> +	state->acquire_ctx = &ctx;
> +
> +retry:
> +	copied_objs = 0;
> +	copied_props = 0;
> +
> +	for (i = 0; i < arg->count_objs; i++) {
> +		uint32_t obj_id, count_props;
> +		struct drm_mode_object *obj;
> +
> +		if (get_user(obj_id, objs_ptr + copied_objs)) {
> +			ret = -EFAULT;
> +			goto fail;
> +		}
> +
> +		obj = drm_mode_object_find(dev, obj_id, DRM_MODE_OBJECT_ANY);
> +		if (!obj || !obj->properties) {
> +			ret = -ENOENT;
> +			goto fail;
> +		}
> +
> +		if (obj->type == DRM_MODE_OBJECT_PLANE) {
> +			plane = obj_to_plane(obj);
> +			plane_mask |= (1 << drm_plane_index(plane));
> +			plane->old_fb = plane->fb;
> +		}
> +
> +		if (get_user(count_props, count_props_ptr + copied_objs)) {
> +			ret = -EFAULT;
> +			goto fail;
> +		}
> +
> +		copied_objs++;
> +
> +		for (j = 0; j < count_props; j++) {

If it's not too horrible I think extracting the inner properties loop
would be really nice to tame the monster.

> +			uint32_t prop_id;
> +			uint64_t prop_value;
> +			struct drm_property *prop;
> +			struct drm_mode_object *ref;
> +
> +			if (get_user(prop_id, props_ptr + copied_props)) {
> +				ret = -EFAULT;
> +				goto fail;
> +			}
> +
> +			prop = drm_property_find(dev, prop_id);
> +			if (!prop) {
> +				ret = -ENOENT;
> +				goto fail;
> +			}
> +
> +			if (get_user(prop_value, prop_values_ptr + copied_props)) {
> +				ret = -EFAULT;
> +				goto fail;
> +			}
> +
> +			if (!drm_property_change_valid_get(prop, prop_value, &ref)) {
> +				ret = -EINVAL;
> +				goto fail;
> +			}
> +
> +			switch (obj->type) {
> +			case DRM_MODE_OBJECT_CONNECTOR: {
> +				struct drm_connector *connector = obj_to_connector(obj);
> +				struct drm_connector_state *connector_state;
> +
> +				connector_state = drm_atomic_get_connector_state(state, connector);
> +				if (IS_ERR(connector_state)) {
> +					ret = PTR_ERR(connector_state);
> +					break;
> +				}
> +
> +				ret = drm_atomic_connector_set_property(connector,
> +						connector_state, prop, prop_value);
> +				break;
> +			}
> +			case DRM_MODE_OBJECT_CRTC: {
> +				struct drm_crtc *crtc = obj_to_crtc(obj);
> +				struct drm_crtc_state *crtc_state;
> +
> +				crtc_state = drm_atomic_get_crtc_state(state, crtc);
> +				if (IS_ERR(crtc_state)) {
> +					ret = PTR_ERR(crtc_state);
> +					break;
> +				}
> +
> +				ret = drm_atomic_crtc_set_property(crtc,
> +						crtc_state, prop, prop_value);
> +				break;
> +			}
> +			case DRM_MODE_OBJECT_PLANE: {
> +				struct drm_plane *plane = obj_to_plane(obj);
> +				struct drm_plane_state *plane_state;
> +
> +				plane_state = drm_atomic_get_plane_state(state, plane);
> +				if (IS_ERR(plane_state)) {
> +					ret = PTR_ERR(plane_state);
> +					break;
> +				}
> +
> +				ret = drm_atomic_plane_set_property(plane,
> +						plane_state, prop, prop_value);
> +				break;
> +			}
> +			default:
> +				ret = -EINVAL;
> +				break;
> +			}
> +
> +			copied_props++;
> +
> +			drm_property_change_valid_put(prop, ref);
> +
> +			if (ret)
> +				goto fail;
> +		}
> +	}
> +
> +	if (arg->flags & DRM_MODE_PAGE_FLIP_EVENT) {
> +		int ncrtcs = dev->mode_config.num_crtc;
> +
> +		for (i = 0; i < ncrtcs; i++) {
> +			struct drm_crtc_state *crtc_state = state->crtc_states[i];
> +			struct drm_pending_vblank_event *e;
> +
> +			if (!crtc_state)
> +				continue;
> +
> +			e = create_vblank_event(dev, file_priv, arg->user_data);
> +			if (!e) {
> +				ret = -ENOMEM;
> +				goto fail;
> +			}
> +
> +			crtc_state->event = e;
> +		}
> +	}
> +
> +	/* sanity check incoming state, because danvet wanted this
> +	 * to be an even bigger monster ioctl and wanted to be able
> +	 * to WARN_ON() in these cases in the helpers:
> +	 */
> +	drm_for_each_plane_mask(plane, dev, plane_mask) {
> +		struct drm_plane_state *plane_state =
> +			drm_atomic_get_plane_state(state, plane);
> +
> +		if (IS_ERR(plane_state)) {
> +			ret = PTR_ERR(plane_state);
> +			goto fail;
> +		}
> +
> +		if (WARN_ON(plane_state->crtc && !plane_state->fb)) {
> +			DRM_DEBUG_KMS("CRTC set but no FB\n");
> +			ret = -EINVAL;
> +			goto fail;
> +		} else if (WARN_ON(plane_state->fb && !plane_state->crtc)) {
> +			DRM_DEBUG_KMS("FB set but no CRTC\n");
> +			ret = -EINVAL;
> +			goto fail;
> +		}
> +	}

With my suggestion to move the checks from heleprs to check_only we could
remove the above hunk as redundant.

> +
> +	if (arg->flags & DRM_MODE_ATOMIC_TEST_ONLY) {
> +		ret = drm_atomic_check_only(state);
> +		/* _check_only() does not free state, unlike _commit() */
> +		drm_atomic_state_free(state);
> +	} else if (arg->flags & DRM_MODE_ATOMIC_NONBLOCK) {
> +		ret = drm_atomic_async_commit(state);
> +	} else {
> +		ret = drm_atomic_commit(state);
> +	}
> +
> +	/* if succeeded, fixup legacy plane crtc/fb ptrs before dropping
> +	 * locks (ie. while it is still safe to deref plane->state).  We
> +	 * need to do this here because the driver entry points cannot
> +	 * distinguish between legacy and atomic ioctls.
> +	 */
> +	drm_for_each_plane_mask(plane, dev, plane_mask) {
> +		if (ret == 0) {
> +			struct drm_framebuffer *new_fb = plane->state->fb;
> +			if (new_fb)
> +				drm_framebuffer_reference(new_fb);
> +			plane->fb = new_fb;
> +			plane->crtc = plane->state->crtc;
> +		} else {
> +			plane->old_fb = NULL;
> +		}
> +		if (plane->old_fb) {
> +			drm_framebuffer_unreference(plane->old_fb);
> +			plane->old_fb = NULL;
> +		}
> +	}
> +
> +	drm_modeset_drop_locks(&ctx);
> +	drm_modeset_acquire_fini(&ctx);
> +
> +	return ret;
> +
> +fail:
> +	if (ret == -EDEADLK)
> +		goto backoff;
> +
> +	if (arg->flags & DRM_MODE_PAGE_FLIP_EVENT) {
> +		int ncrtcs = dev->mode_config.num_crtc;
> +
> +		for (i = 0; i < ncrtcs; i++) {
> +			struct drm_crtc_state *crtc_state = state->crtc_states[i];
> +
> +			if (!crtc_state)
> +				continue;
> +
> +			destroy_vblank_event(dev, file_priv, crtc_state->event);
> +			crtc_state->event = NULL;
> +		}
> +	}
> +
> +	drm_atomic_state_free(state);
> +
> +	drm_modeset_drop_locks(&ctx);
> +	drm_modeset_acquire_fini(&ctx);
> +
> +	return ret;
> +
> +backoff:
> +	drm_atomic_state_clear(state);
> +	drm_modeset_backoff(&ctx);
> +
> +	goto retry;
> +}
> diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
> index 39c3e06..948d04d 100644
> --- a/drivers/gpu/drm/drm_crtc.c
> +++ b/drivers/gpu/drm/drm_crtc.c
> @@ -4299,7 +4299,7 @@ EXPORT_SYMBOL(drm_mode_connector_update_edid_property);
>   * object to which the property is attached has a chance to take it's own
>   * reference).
>   */
> -static bool drm_property_change_valid_get(struct drm_property *property,
> +bool drm_property_change_valid_get(struct drm_property *property,
>  					 uint64_t value, struct drm_mode_object **ref)
>  {
>  	if (property->flags & DRM_MODE_PROP_IMMUTABLE)
> @@ -4353,7 +4353,7 @@ static bool drm_property_change_valid_get(struct drm_property *property,
>  	}
>  }
>  
> -static void drm_property_change_valid_put(struct drm_property *property,
> +void drm_property_change_valid_put(struct drm_property *property,
>  		struct drm_mode_object *ref)
>  {
>  	if (!ref)
> diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
> index 00587a1..fe76815 100644
> --- a/drivers/gpu/drm/drm_ioctl.c
> +++ b/drivers/gpu/drm/drm_ioctl.c
> @@ -620,6 +620,7 @@ static const struct drm_ioctl_desc drm_ioctls[] = {
>  	DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_GETPROPERTIES, drm_mode_obj_get_properties_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
>  	DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_SETPROPERTY, drm_mode_obj_set_property_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
>  	DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR2, drm_mode_cursor2_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
> +	DRM_IOCTL_DEF(DRM_IOCTL_MODE_ATOMIC, drm_mode_atomic_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
>  };
>  
>  #define DRM_CORE_IOCTL_COUNT	ARRAY_SIZE( drm_ioctls )
> diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
> index 48fcd98..8330676 100644
> --- a/include/drm/drm_crtc.h
> +++ b/include/drm/drm_crtc.h
> @@ -1332,6 +1332,10 @@ extern int drm_mode_create_scaling_mode_property(struct drm_device *dev);
>  extern int drm_mode_create_aspect_ratio_property(struct drm_device *dev);
>  extern int drm_mode_create_dirty_info_property(struct drm_device *dev);
>  extern int drm_mode_create_suggested_offset_properties(struct drm_device *dev);
> +extern bool drm_property_change_valid_get(struct drm_property *property,
> +					 uint64_t value, struct drm_mode_object **ref);
> +extern void drm_property_change_valid_put(struct drm_property *property,
> +		struct drm_mode_object *ref);
>  
>  extern int drm_mode_connector_attach_encoder(struct drm_connector *connector,
>  					     struct drm_encoder *encoder);
> @@ -1423,6 +1427,8 @@ extern int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data,
>  extern int drm_mode_plane_set_obj_prop(struct drm_plane *plane,
>  				       struct drm_property *property,
>  				       uint64_t value);
> +extern int drm_mode_atomic_ioctl(struct drm_device *dev,
> +				 void *data, struct drm_file *file_priv);
>  
>  extern void drm_fb_get_bpp_depth(uint32_t format, unsigned int *depth,
>  				 int *bpp);
> diff --git a/include/uapi/drm/drm.h b/include/uapi/drm/drm.h
> index b0b8556..9cea966 100644
> --- a/include/uapi/drm/drm.h
> +++ b/include/uapi/drm/drm.h
> @@ -777,6 +777,7 @@ struct drm_prime_handle {
>  #define DRM_IOCTL_MODE_OBJ_GETPROPERTIES	DRM_IOWR(0xB9, struct drm_mode_obj_get_properties)
>  #define DRM_IOCTL_MODE_OBJ_SETPROPERTY	DRM_IOWR(0xBA, struct drm_mode_obj_set_property)
>  #define DRM_IOCTL_MODE_CURSOR2		DRM_IOWR(0xBB, struct drm_mode_cursor2)
> +#define DRM_IOCTL_MODE_ATOMIC		DRM_IOWR(0xBC, struct drm_mode_atomic)
>  
>  /**
>   * Device specific ioctls should only be in their respective headers
> diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h
> index 86574b0..3459778 100644
> --- a/include/uapi/drm/drm_mode.h
> +++ b/include/uapi/drm/drm_mode.h
> @@ -519,4 +519,25 @@ struct drm_mode_destroy_dumb {
>  	uint32_t handle;
>  };
>  
> +/* page-flip flags are valid, plus: */
> +#define DRM_MODE_ATOMIC_TEST_ONLY 0x0100
> +#define DRM_MODE_ATOMIC_NONBLOCK  0x0200
> +
> +#define DRM_MODE_ATOMIC_FLAGS (\
> +		DRM_MODE_PAGE_FLIP_EVENT |\
> +		DRM_MODE_PAGE_FLIP_ASYNC |\
> +		DRM_MODE_ATOMIC_TEST_ONLY |\
> +		DRM_MODE_ATOMIC_NONBLOCK)

Just for future-proofing: Should we add an ATOMIC_ALLOW_MODESET right away
(and add checks in the helpers for that case, should just be a check for
crtc_state->modeset_changed at the very end of
atomic_helper_check_modeset?

I really think we need to clarify this before we make the abi stable,
otherwise there will be surprise compositors doing a full modeset once per
frame. Which is totally uncool ofc. But doesn't need to be done right
away, but a small patch on top would be great (since it's so simple with
helpers at least).

> +
> +struct drm_mode_atomic {
> +	__u32 flags;
> +	__u32 count_objs;
> +	__u64 objs_ptr;
> +	__u64 count_props_ptr;
> +	__u64 props_ptr;
> +	__u64 prop_values_ptr;
> +	__u64 blob_values_ptr;  /* remove? */
> +	__u64 user_data;
> +};
> +
>  #endif
> -- 
> 2.1.0
> 

-- 
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
http://lists.freedesktop.org/mailman/listinfo/dri-devel

  parent reply	other threads:[~2014-12-17 15:03 UTC|newest]

Thread overview: 41+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-12-16 23:05 [PATCH 00/13] Atomic Properties Rob Clark
2014-12-16 23:05 ` [PATCH 01/13] drm: allow property validation for refcnted props Rob Clark
2014-12-17 12:31   ` Daniel Vetter
2014-12-16 23:05 ` [PATCH 02/13] drm: store property instead of id in obj attachment Rob Clark
2014-12-17 13:29   ` Daniel Vetter
2014-12-16 23:05 ` [PATCH 03/13] drm: get rid of direct property value access Rob Clark
2014-12-17 13:37   ` Daniel Vetter
2014-12-16 23:05 ` [PATCH 04/13] drm: add atomic_set_property wrappers Rob Clark
2014-12-17 12:49   ` Sean Paul
2014-12-17 13:43   ` Daniel Vetter
2014-12-16 23:05 ` [PATCH 05/13] drm: add atomic_get_property Rob Clark
2014-12-17 12:52   ` Sean Paul
2014-12-17 13:54   ` Daniel Vetter
2014-12-16 23:05 ` [PATCH 06/13] drm: add atomic hook to read property plus helper Rob Clark
2014-12-17 14:02   ` Daniel Vetter
2014-12-17 14:06     ` Daniel Vetter
2014-12-17 14:15       ` Rob Clark
2014-12-17 14:29         ` Daniel Vetter
2014-12-17 14:12     ` Rob Clark
2014-12-17 14:32       ` Daniel Vetter
2014-12-16 23:05 ` [PATCH 07/13] drm: small property creation cleanup Rob Clark
2014-12-17 14:13   ` Daniel Vetter
2014-12-16 23:05 ` [PATCH 08/13] drm: tweak getconnector locking Rob Clark
2014-12-17 14:14   ` Daniel Vetter
2014-12-16 23:05 ` [PATCH 09/13] drm/atomic: atomic_check functions Rob Clark
2014-12-17 14:25   ` Daniel Vetter
2014-12-16 23:05 ` [PATCH 10/13] drm/atomic: atomic plane properties Rob Clark
2014-12-17 14:41   ` Daniel Vetter
2014-12-16 23:05 ` [PATCH 11/13] drm/atomic: atomic connector properties Rob Clark
2014-12-17 14:48   ` Daniel Vetter
2014-12-16 23:05 ` [PATCH 12/13] drm/msm: atomic property support Rob Clark
2014-12-16 23:05 ` [PATCH 13/13] RFC: drm: Atomic modeset ioctl Rob Clark
2014-12-17  2:48   ` Michel Dänzer
2014-12-17  7:20     ` Pekka Paalanen
2014-12-17  9:31       ` Michel Dänzer
2014-12-17 11:18         ` Daniel Vetter
2014-12-19  3:29           ` Michel Dänzer
2014-12-19  7:55             ` Daniel Vetter
2014-12-17 14:04         ` Rob Clark
2014-12-17 15:04   ` Daniel Vetter [this message]
2014-12-17 13:08 ` [PATCH 00/13] Atomic Properties Sean Paul

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=20141217150415.GS2711@phenom.ffwll.local \
    --to=daniel@ffwll.ch \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=robdclark@gmail.com \
    /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 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.