public inbox for intel-gfx@lists.freedesktop.org
 help / color / mirror / Atom feed
From: Imre Deak <imre.deak@intel.com>
To: Patrik Jakobsson <patrik.jakobsson@linux.intel.com>
Cc: intel-gfx@lists.freedesktop.org
Subject: Re: [PATCH v2 10/12] drm/i915/gen9: Turn DC handling into a power well
Date: Fri, 13 Nov 2015 19:53:32 +0200	[thread overview]
Message-ID: <1447437212.1480.34.camel@intel.com> (raw)
In-Reply-To: <1447335042.6396.18.camel@intel.com>

On to, 2015-11-12 at 15:30 +0200, Imre Deak wrote:
> On to, 2015-11-12 at 13:24 +0100, Patrik Jakobsson wrote:
> > On Wed, Nov 11, 2015 at 08:57:19PM +0200, Imre Deak wrote:
> > > On ma, 2015-11-09 at 16:48 +0100, Patrik Jakobsson wrote:
> > > > Handle DC off as a power well where enabling the power well
> > > > will
> > > > prevent
> > > > the DMC to enter selected DC states (required around modesets
> > > > and
> > > > Aux
> > > > A). Disabling the power well will allow DC states again. For
> > > > now
> > > > the
> > > > highest DC state is DC6 for Skylake and DC5 for Broxton but
> > > > will
> > > > be
> > > > configurable for Skylake in a later patch.
> > > > 
> > > > v2: Check both DC5 and DC6 bits in power well enabled function
> > > > (Ville)
> > > > 
> > > > Signed-off-by: Patrik Jakobsson <
> > > > patrik.jakobsson@linux.intel.com
> > > > > 
> > > > ---
> > > >  drivers/gpu/drm/i915/i915_drv.c         |   6 --
> > > >  drivers/gpu/drm/i915/i915_reg.h         |   1 +
> > > >  drivers/gpu/drm/i915/intel_display.c    |   6 ++
> > > >  drivers/gpu/drm/i915/intel_runtime_pm.c | 110
> > > > +++++++++++++++++++++++---------
> > > >  4 files changed, 88 insertions(+), 35 deletions(-)
> > > > 
> > > > diff --git a/drivers/gpu/drm/i915/i915_drv.c
> > > > b/drivers/gpu/drm/i915/i915_drv.c
> > > > index 5a63f9a..0c7f435 100644
> > > > --- a/drivers/gpu/drm/i915/i915_drv.c
> > > > +++ b/drivers/gpu/drm/i915/i915_drv.c
> > > > @@ -1072,9 +1072,6 @@ static int i915_pm_resume(struct device
> > > > *dev)
> > > >  
> > > >  static int skl_suspend_complete(struct drm_i915_private
> > > > *dev_priv)
> > > >  {
> > > > -	if (dev_priv->csr.dmc_payload)
> > > > -		skl_enable_dc6(dev_priv);
> > > > -
> > > >  	return 0;
> > > >  }
> > > >  
> > > > @@ -1119,9 +1116,6 @@ static int bxt_resume_prepare(struct
> > > > drm_i915_private *dev_priv)
> > > >  
> > > >  static int skl_resume_prepare(struct drm_i915_private
> > > > *dev_priv)
> > > >  {
> > > > -	if (dev_priv->csr.dmc_payload)
> > > > -		skl_disable_dc6(dev_priv);
> > > > -
> > > >  	return 0;
> > > >  }
> > > >  
> > > > diff --git a/drivers/gpu/drm/i915/i915_reg.h
> > > > b/drivers/gpu/drm/i915/i915_reg.h
> > > > index 31b3a84..df445ba 100644
> > > > --- a/drivers/gpu/drm/i915/i915_reg.h
> > > > +++ b/drivers/gpu/drm/i915/i915_reg.h
> > > > @@ -636,6 +636,7 @@ enum skl_disp_power_wells {
> > > >  
> > > >  	/* Not actual bit groups. Used as IDs for
> > > > lookup_power_well() */
> > > >  	SKL_DISP_PW_ALWAYS_ON,
> > > > +	SKL_DISP_PW_DC_OFF,
> > > 
> > > Imo it would be less confusing to call it DC3 power well. Looking
> > > at th
> > > e DC spec, DC4 is only a transitory state to DC5/6, so what we
> > > expect
> > > when we disable DC6/5 is DC3 or shallower power states (DC2/1/0).
> > 
> > I've been changing the name quite a few times but settled on "DC
> > off"
> > to keep it
> > generic. The main mechanism for the power well is to prevent any DC
> > states that
> > can cause us problems during certain operations (i.e. modeset). The
> > DC states we
> > need to block could change or be different between platforms. For
> > that reason I
> > would prefer not to be as specific with the naming.
> 
> All the power well names are platform specific so it would be logical
> and more meaningful to give this one a platform specific name too.
> Also
> 'DC off' is kind of a double negation leading to debug messages like
> 'enable DC off' which isn't that nice. But now that I think of it my
> idea isn't that great either. 'enable DC3' is confusing too, one
> could
> think we enable some power saving state at that point, which is not
> the
> case. So let's keep things as-is for now at least:
> 
> Reviewed-by: Imre Deak <imre.deak@intel.com>

I noticed some issue only now, when testing it more:

> > >   };
> > > >  
> > > >  #define SKL_POWER_WELL_STATE(pw) (1 << ((pw) * 2))
> > > > diff --git a/drivers/gpu/drm/i915/intel_display.c
> > > > b/drivers/gpu/drm/i915/intel_display.c
> > > > index 649ac34..856d801 100644
> > > > --- a/drivers/gpu/drm/i915/intel_display.c
> > > > +++ b/drivers/gpu/drm/i915/intel_display.c
> > > > @@ -13320,6 +13320,9 @@ static int intel_atomic_commit(struct
> > > > drm_device *dev,
> > > >  			to_intel_crtc_state(crtc->state)
> > > > ->update_pipe;
> > > >  		unsigned long put_domains = 0;
> > > >  
> > > > +		if (modeset)
> > > > +			intel_display_power_get(dev_priv,
> > > > POWER_DOMAIN_MODESET);
> > > > +
> > > >  		if (modeset && crtc->state->active) {
> > > >  			update_scanline_offset(to_intel_crtc(c
> > > > rt
> > > > c));
> > > >  			dev_priv->display.crtc_enable(crtc);
> > > > @@ -13343,6 +13346,9 @@ static int intel_atomic_commit(struct
> > > > drm_device *dev,
> > > >  			modeset_put_power_domains(dev_priv,
> > > > put_domains);
> > > >  
> > > >  		intel_post_plane_update(intel_crtc);
> > > > +
> > > > +		if (modeset)
> > > > +			intel_display_power_put(dev_priv,
> > > > POWER_DOMAIN_MODESET);
> > > >  	}
> > > >  
> > > >  	/* FIXME: add subpixel order */
> > > > diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.c
> > > > b/drivers/gpu/drm/i915/intel_runtime_pm.c
> > > > index edf753e..95c3fcc 100644
> > > > --- a/drivers/gpu/drm/i915/intel_runtime_pm.c
> > > > +++ b/drivers/gpu/drm/i915/intel_runtime_pm.c
> > > > @@ -49,9 +49,6 @@
> > > >   * present for a given platform.
> > > >   */
> > > >  
> > > > -#define GEN9_ENABLE_DC5(dev) 0
> > > > -#define SKL_ENABLE_DC6(dev) IS_SKYLAKE(dev)
> > > > -
> > > >  #define for_each_power_well(i, power_well, domain_mask,
> > > > power_domains)	\
> > > >  	for (i = 0;						
> > > > 	
> > > > \
> > > >  	     i < (power_domains)->power_well_count &&		
> > > > 	\
> > > > @@ -309,9 +306,14 @@ static void hsw_set_power_well(struct
> > > > drm_i915_private *dev_priv,
> > > >  #define SKL_DISPLAY_DDI_D_POWER_DOMAINS (		\
> > > >  	BIT(POWER_DOMAIN_PORT_DDI_D_LANES) |		\
> > > >  	BIT(POWER_DOMAIN_INIT))
> > > > +#define SKL_DISPLAY_DC_OFF_POWER_DOMAINS (		\
> > > > +	BIT(POWER_DOMAIN_MODESET) |			\
> > > > +	BIT(POWER_DOMAIN_AUX_A) |			\
> > > > +	BIT(POWER_DOMAIN_INIT))

This needs SKL_DISPLAY_POWERWELL_2_POWER_DOMAINS as well, so that we
enable/disable DC5/6 whenever we toggle PW_2.

> > > >  #define SKL_DISPLAY_ALWAYS_ON_POWER_DOMAINS (		\
> > > >  	(POWER_DOMAIN_MASK & ~(				
> > > > \
> > > > -	SKL_DISPLAY_POWERWELL_2_POWER_DOMAINS)) |	\
> > > > +	SKL_DISPLAY_POWERWELL_2_POWER_DOMAINS |		
> > > > \
> > > > +	SKL_DISPLAY_DC_OFF_POWER_DOMAINS)) |		\
> > > >  	BIT(POWER_DOMAIN_INIT))
> > > >  
> > > >  #define BXT_DISPLAY_POWERWELL_2_POWER_DOMAINS (		
> > > > \
> > > > @@ -339,6 +341,10 @@ static void hsw_set_power_well(struct
> > > > drm_i915_private *dev_priv,
> > > >  	BIT(POWER_DOMAIN_AUX_A) |			\
> > > >  	BIT(POWER_DOMAIN_PLLS) |			\
> > > >  	BIT(POWER_DOMAIN_INIT))
> > > > +#define BXT_DISPLAY_DC_OFF_POWER_DOMAINS (		\
> > > > +	BIT(POWER_DOMAIN_MODESET) |			\
> > > > +	BIT(POWER_DOMAIN_AUX_A) |			\
> > > > +	BIT(POWER_DOMAIN_INIT))

As above, this needs BXT_DISPLAY_POWERWELL_2_POWER_DOMAINS.

> > > >  #define BXT_DISPLAY_ALWAYS_ON_POWER_DOMAINS (		\
> > > >  	(POWER_DOMAIN_MASK &
> > > > ~(BXT_DISPLAY_POWERWELL_1_POWER_DOMAINS
> > > > > 	\
> > > >  	BXT_DISPLAY_POWERWELL_2_POWER_DOMAINS)) |	\
> > > > @@ -486,15 +492,6 @@ static void gen9_enable_dc5(struct
> > > > drm_i915_private *dev_priv)
> > > >  	gen9_set_dc_state(dev_priv, DC_STATE_EN_UPTO_DC5);
> > > >  }
> > > >  
> > > > -static void gen9_disable_dc5(struct drm_i915_private
> > > > *dev_priv)
> > > > -{
> > > > -	assert_can_disable_dc5(dev_priv);
> > > > -
> > > > -	DRM_DEBUG_KMS("Disabling DC5\n");
> > > > -
> > > > -	gen9_set_dc_state(dev_priv, DC_STATE_DISABLE);
> > > > -}
> > > > -
> > > >  static void assert_can_enable_dc6(struct drm_i915_private
> > > > *dev_priv)
> > > >  {
> > > >  	struct drm_device *dev = dev_priv->dev;
> > > > @@ -522,6 +519,14 @@ static void assert_can_disable_dc6(struct
> > > > drm_i915_private *dev_priv)
> > > >  		  "DC6 already programmed to be disabled.\n");
> > > >  }
> > > >  
> > > > +static void gen9_disable_dc5_dc6(struct drm_i915_private
> > > > *dev_priv)
> > > > +{
> > > > +	assert_can_disable_dc5(dev_priv);
> > > > +	assert_can_disable_dc6(dev_priv);
> > > > +
> > > > +	gen9_set_dc_state(dev_priv, DC_STATE_DISABLE);
> > > > +}
> > > > +
> > > >  void skl_enable_dc6(struct drm_i915_private *dev_priv)
> > > >  {
> > > >  	assert_can_enable_dc6(dev_priv);
> > > > @@ -571,6 +576,7 @@ static void skl_set_power_well(struct
> > > > drm_i915_private *dev_priv,
> > > >  	case SKL_DISP_PW_DDI_C:
> > > >  	case SKL_DISP_PW_DDI_D:
> > > >  	case SKL_DISP_PW_MISC_IO:
> > > > +	case SKL_DISP_PW_DC_OFF:
> > > >  		break;
> > > >  	default:
> > > >  		WARN(1, "Unknown power well %lu\n", power_well
> > > > ->data);
> > > > @@ -589,17 +595,13 @@ static void skl_set_power_well(struct
> > > > drm_i915_private *dev_priv,
> > > >  				"Invalid for power well status
> > > > to be
> > > > enabled, unless done by the BIOS, \
> > > >  				when request is to
> > > > disable!\n");
> > > >  			if (power_well->data == SKL_DISP_PW_2)
> > > > {
> > > > -				if (GEN9_ENABLE_DC5(dev))
> > > > -					gen9_disable_dc5(dev_p
> > > > ri
> > > > v);
> > > > -				if (SKL_ENABLE_DC6(dev)) {
> > > > -					/*
> > > > -					 * DDI buffer
> > > > programming
> > > > unnecessary during driver-load/resume
> > > > -					 * as it's already
> > > > done
> > > > during modeset initialization then.
> > > > -					 * It's also invalid
> > > > here as
> > > > encoder list is still uninitialized.
> > > > -					 */
> > > > -					if (!dev_priv
> > > > ->power_domains.initializing)
> > > > -						intel_prepare_
> > > > dd
> > > > i(de
> > > > v);
> > > > -				}
> > > > +				/*
> > > > +				 * DDI buffer programming
> > > > unnecessary during driver-load/resume
> > > > +				 * as it's already done during
> > > > modeset initialization then.
> > > > +				 * It's also invalid here as
> > > > encoder
> > > > list is still uninitialized.
> > > > +				 */
> > > > +				if (!dev_priv
> > > > ->power_domains.initializing)
> > > > +					intel_prepare_ddi(dev)
> > > > ;
> > > >  			}
> > > >  			I915_WRITE(HSW_PWR_WELL_DRIVER, tmp |
> > > > req_mask);
> > > >  		}
> > > > @@ -617,10 +619,6 @@ static void skl_set_power_well(struct
> > > > drm_i915_private *dev_priv,
> > > >  			I915_WRITE(HSW_PWR_WELL_DRIVER,	
> > > > t
> > > > mp &
> > > > ~req_mask);
> > > >  			POSTING_READ(HSW_PWR_WELL_DRIVER);
> > > >  			DRM_DEBUG_KMS("Disabling %s\n",
> > > > power_well
> > > > ->name);
> > > > -
> > > > -			if (GEN9_ENABLE_DC5(dev) &&
> > > > -				power_well->data ==
> > > > SKL_DISP_PW_2)
> > > > -					gen9_enable_dc5(dev_pr
> > > > iv
> > > > );
> > > >  		}
> > > >  	}
> > > >  
> > > > @@ -695,6 +693,40 @@ static void skl_power_well_disable(struct
> > > > drm_i915_private *dev_priv,
> > > >  	skl_set_power_well(dev_priv, power_well, false);
> > > >  }
> > > >  
> > > > +static bool gen9_dc_off_power_well_enabled(struct
> > > > drm_i915_private
> > > > *dev_priv,
> > > > +					  struct
> > > > i915_power_well
> > > > *power_well)
> > > > +{
> > > > +	return (I915_READ(DC_STATE_EN) &
> > > > DC_STATE_EN_UPTO_DC5_DC6_MASK) == 0;
> > > > +}
> > > > +
> > > > +static void gen9_dc_off_power_well_enable(struct
> > > > drm_i915_private
> > > > *dev_priv,
> > > > +					 struct
> > > > i915_power_well
> > > > *power_well)
> > > > +{
> > > > +	gen9_disable_dc5_dc6(dev_priv);
> > > > +}
> > > > +
> > > > +static void gen9_dc_off_power_well_disable(struct
> > > > drm_i915_private
> > > > *dev_priv,
> > > > +					  struct
> > > > i915_power_well
> > > > *power_well)
> > > > +{
> > > > +	if (IS_SKYLAKE(dev_priv))
> > > > +		skl_enable_dc6(dev_priv);
> > > > +	else
> > > > +		gen9_enable_dc5(dev_priv);
> > > > +}
> > > > +
> > > > +static void gen9_dc_off_power_well_sync_hw(struct
> > > > drm_i915_private
> > > > *dev_priv,
> > > > +					  struct
> > > > i915_power_well
> > > > *power_well)
> > > > +{
> > > > +	if (power_well->count > 0) {
> > > > +		gen9_set_dc_state(dev_priv, DC_STATE_DISABLE);
> > > > +	} else {
> > > > +		if (IS_SKYLAKE(dev_priv))
> > > > +			gen9_set_dc_state(dev_priv,
> > > > DC_STATE_EN_UPTO_DC6);
> > > > +		else
> > > > +			gen9_set_dc_state(dev_priv,
> > > > DC_STATE_EN_UPTO_DC5);
> > > > +	}
> > > > +}
> > > > +
> > > >  static void i9xx_always_on_power_well_noop(struct
> > > > drm_i915_private
> > > > *dev_priv,
> > > >  					   struct
> > > > i915_power_well
> > > > *power_well)
> > > >  {
> > > > @@ -1517,6 +1549,13 @@ static const struct i915_power_well_ops
> > > > skl_power_well_ops = {
> > > >  	.is_enabled = skl_power_well_enabled,
> > > >  };
> > > >  
> > > > +static const struct i915_power_well_ops
> > > > gen9_dc_off_power_well_ops =
> > > > {
> > > > +	.sync_hw = gen9_dc_off_power_well_sync_hw,
> > > > +	.enable = gen9_dc_off_power_well_enable,
> > > > +	.disable = gen9_dc_off_power_well_disable,
> > > > +	.is_enabled = gen9_dc_off_power_well_enabled,
> > > > +};
> > > > +
> > > >  static struct i915_power_well hsw_power_wells[] = {
> > > >  	{
> > > >  		.name = "always-on",
> > > > @@ -1697,6 +1736,12 @@ static struct i915_power_well
> > > > skl_power_wells[] = {
> > > >  		.data = SKL_DISP_PW_2,
> > > >  	},
> > > >  	{
> > > > +		.name = "DC off",
> > > > +		.domains = SKL_DISPLAY_DC_OFF_POWER_DOMAINS,
> > > > +		.ops = &gen9_dc_off_power_well_ops,
> > > > +		.data = SKL_DISP_PW_DC_OFF,
> > > > +	},

This should be listed right before the PW_2 well, so that the we have
the following sequences:

1. disable DC5/6
2. enable PW_2

1. disable PW_2
2. enable DC5/6

> > > > +	{
> > > >  		.name = "DDI A/E power well",
> > > >  		.domains = SKL_DISPLAY_DDI_A_E_POWER_DOMAINS,
> > > >  		.ops = &skl_power_well_ops,
> > > > @@ -1769,7 +1814,14 @@ static struct i915_power_well
> > > > bxt_power_wells[] = {
> > > >  		.domains =
> > > > BXT_DISPLAY_POWERWELL_2_POWER_DOMAINS,
> > > >  		.ops = &skl_power_well_ops,
> > > >  		.data = SKL_DISP_PW_2,
> > > > -	}
> > > > +	},
> > > > +	{
> > > > +		.name = "DC off",
> > > > +		.domains = BXT_DISPLAY_DC_OFF_POWER_DOMAINS,
> > > > +		.ops = &gen9_dc_off_power_well_ops,
> > > > +		.data = SKL_DISP_PW_DC_OFF,
> > > > +	},

As above, this should be listed right before the PW_2 well.

> > > > +
> > > >  };
> > > >  
> > > >  #define set_power_wells(power_domains, __power_wells) ({	
> > > > 	
> > > > \
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/intel-gfx
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

  reply	other threads:[~2015-11-13 17:55 UTC|newest]

Thread overview: 47+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-11-09 15:48 [PATCH v2 00/12] Skylake DMC/DC-state fixes and redesign Patrik Jakobsson
2015-11-09 15:48 ` [PATCH 01/12] drm/i915: Don't trust CSR program memory contents Patrik Jakobsson
2015-11-11 19:05   ` Imre Deak
2015-11-09 15:48 ` [PATCH 02/12] drm/i915/gen9: Always set mask memory up when enabling DC5 or DC6 Patrik Jakobsson
2015-11-11 19:08   ` Imre Deak
2015-11-09 15:48 ` [PATCH 03/12] drm/i915: Clean up AUX power domain handling Patrik Jakobsson
2015-11-11 18:22   ` Imre Deak
2015-11-11 18:37     ` Ville Syrjälä
2015-11-12  9:02       ` Patrik Jakobsson
2015-11-12 10:15         ` Ville Syrjälä
2015-11-16 14:01   ` [PATCH v2 " Patrik Jakobsson
2015-11-09 15:48 ` [PATCH 04/12] drm/i915: Introduce a gmbus power domain Patrik Jakobsson
2015-11-09 15:48 ` [PATCH 05/12] drm/i915: Remove DDI power domain exclusion SKL_DISPLAY_ALWAYS_ON_POWER_DOMAINS Patrik Jakobsson
2015-11-09 15:48 ` [PATCH 06/12] drm/i915: Remove distinction between DDI 2 vs 4 lanes Patrik Jakobsson
2015-11-11 19:10   ` Imre Deak
2015-11-09 15:48 ` [PATCH 07/12] drm/i915: Add a modeset power domain Patrik Jakobsson
2015-11-11 19:11   ` Imre Deak
2015-11-09 15:48 ` [PATCH 08/12] drm/i915: Do not warn on PG2 enabled in gen9_disable_dc5() Patrik Jakobsson
2015-11-09 15:48 ` [PATCH 09/12] drm/i915: Explain usage of power well IDs vs bit groups Patrik Jakobsson
2015-11-11 19:13   ` Imre Deak
2015-11-12 13:15     ` Patrik Jakobsson
2015-11-16 14:01   ` [PATCH v2 " Patrik Jakobsson
2015-11-09 15:48 ` [PATCH v2 10/12] drm/i915/gen9: Turn DC handling into a power well Patrik Jakobsson
2015-11-11 18:57   ` Imre Deak
2015-11-12 12:24     ` Patrik Jakobsson
2015-11-12 13:30       ` Imre Deak
2015-11-13 17:53         ` Imre Deak [this message]
2015-11-11 19:23   ` Imre Deak
2015-11-12 12:55     ` Patrik Jakobsson
2015-11-16 14:01   ` [PATCH v3 " Patrik Jakobsson
2015-11-16 14:41     ` Patrik Jakobsson
2015-11-16 15:20     ` [PATCH v4 " Patrik Jakobsson
2015-11-17 19:21       ` Imre Deak
2015-11-23 22:58       ` Matt Roper
2015-11-23 23:09         ` Imre Deak
2015-11-24 12:24           ` Daniel Vetter
2015-11-16 19:28     ` [PATCH v3 " Imre Deak
2015-11-16 19:46       ` Patrik Jakobsson
2015-11-09 15:48 ` [PATCH v2 11/12] drm/i915/gen9: Add boot parameter for disabling DC6 Patrik Jakobsson
2015-11-11 19:04   ` Imre Deak
2015-11-12 12:51     ` Patrik Jakobsson
2015-11-12 13:52       ` Imre Deak
2015-11-16 14:01   ` [PATCH v3 " Patrik Jakobsson
2015-11-16 19:25     ` Imre Deak
2015-11-09 15:48 ` [PATCH 12/12] drm/i915/skl: Remove unused suspend and resume callbacks Patrik Jakobsson
2015-11-17 18:28   ` Imre Deak
2015-11-17 19:54 ` [PATCH v2 00/12] Skylake DMC/DC-state fixes and redesign Imre Deak

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=1447437212.1480.34.camel@intel.com \
    --to=imre.deak@intel.com \
    --cc=intel-gfx@lists.freedesktop.org \
    --cc=patrik.jakobsson@linux.intel.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox