Intel-GFX Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Matthew Auld <matthew.auld@intel.com>
To: intel-gfx@lists.freedesktop.org
Cc: Abdiel Janulgue <abdiel.janulgue@linux.intel.com>,
	Lucas De Marchi <lucas.demarchi@intel.com>,
	Chris P Wilson <chris.p.wilson@intel.com>,
	Neel Desai <neel.desai@intel.com>,
	Balestrieri@freedesktop.org
Subject: [Intel-gfx] [RFC 53/60] drm/i915: Create stolen memory region from local memory
Date: Fri, 10 Jul 2020 12:57:50 +0100	[thread overview]
Message-ID: <20200710115757.290984-54-matthew.auld@intel.com> (raw)
In-Reply-To: <20200710115757.290984-1-matthew.auld@intel.com>

From: CQ Tang <cq.tang@intel.com>

Add "REGION_STOLEN" device info to dg1, create stolen memory
region from upper portion of local device memory, starting
from DSMBASE.

The memory region is marked with "is_devmem=true".

Cc: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
Cc: Matthew Auld <matthew.auld@intel.com>
Cc: Abdiel Janulgue <abdiel.janulgue@linux.intel.com>
Cc: Chris P Wilson <chris.p.wilson@intel.com>
Cc: Balestrieri, Francesco <francesco.balestrieri@intel.com>
Cc: Niranjana Vishwanathapura <niranjana.vishwanathapura@intel.com>
Cc: Venkata S Dhanalakota <venkata.s.dhanalakota@intel.com>
Cc: Neel Desai <neel.desai@intel.com>
Cc: Matthew Brost <matthew.brost@intel.com>
Cc: Sudeep Dutt <sudeep.dutt@intel.com>
Cc: Lucas De Marchi <lucas.demarchi@intel.com>
Signed-off-by: CQ Tang <cq.tang@intel.com>
---
 drivers/gpu/drm/i915/gem/i915_gem_lmem.c   | 12 ++---
 drivers/gpu/drm/i915/gem/i915_gem_lmem.h   |  7 +++
 drivers/gpu/drm/i915/gem/i915_gem_stolen.c | 56 +++++++++++++++++++++-
 drivers/gpu/drm/i915/i915_pci.c            |  2 +-
 drivers/gpu/drm/i915/i915_reg.h            |  1 +
 drivers/gpu/drm/i915/intel_memory_region.c |  5 ++
 drivers/gpu/drm/i915/intel_memory_region.h |  2 +-
 7 files changed, 75 insertions(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/i915/gem/i915_gem_lmem.c b/drivers/gpu/drm/i915/gem/i915_gem_lmem.c
index 423662cec5da..456b304b244e 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_lmem.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_lmem.c
@@ -8,8 +8,8 @@
 #include "gem/i915_gem_lmem.h"
 #include "i915_drv.h"
 
-static int lmem_pread(struct drm_i915_gem_object *obj,
-		      const struct drm_i915_gem_pread *arg)
+int i915_gem_object_lmem_pread(struct drm_i915_gem_object *obj,
+			       const struct drm_i915_gem_pread *arg)
 {
 	struct drm_i915_private *i915 = to_i915(obj->base.dev);
 	struct intel_runtime_pm *rpm = &i915->runtime_pm;
@@ -94,8 +94,8 @@ static int lmem_pread(struct drm_i915_gem_object *obj,
 	return ret;
 }
 
-static int lmem_pwrite(struct drm_i915_gem_object *obj,
-		       const struct drm_i915_gem_pwrite *arg)
+int i915_gem_object_lmem_pwrite(struct drm_i915_gem_object *obj,
+				const struct drm_i915_gem_pwrite *arg)
 {
 	struct drm_i915_private *i915 = to_i915(obj->base.dev);
 	struct intel_runtime_pm *rpm = &i915->runtime_pm;
@@ -187,8 +187,8 @@ const struct drm_i915_gem_object_ops i915_gem_lmem_obj_ops = {
 	.put_pages = i915_gem_object_put_pages_buddy,
 	.release = i915_gem_object_release_memory_region,
 
-	.pread = lmem_pread,
-	.pwrite = lmem_pwrite,
+	.pread = i915_gem_object_lmem_pread,
+	.pwrite = i915_gem_object_lmem_pwrite,
 };
 
 void __iomem *
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_lmem.h b/drivers/gpu/drm/i915/gem/i915_gem_lmem.h
index e11e0545e39c..c59aa6c014c7 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_lmem.h
+++ b/drivers/gpu/drm/i915/gem/i915_gem_lmem.h
@@ -11,9 +11,16 @@
 struct drm_i915_private;
 struct drm_i915_gem_object;
 struct intel_memory_region;
+struct drm_i915_gem_pread;
+struct drm_i915_gem_pwrite;
 
 extern const struct drm_i915_gem_object_ops i915_gem_lmem_obj_ops;
 
+int i915_gem_object_lmem_pread(struct drm_i915_gem_object *obj,
+			       const struct drm_i915_gem_pread *args);
+int i915_gem_object_lmem_pwrite(struct drm_i915_gem_object *obj,
+				const struct drm_i915_gem_pwrite *args);
+
 void __iomem *
 i915_gem_object_lmem_io_map(struct drm_i915_gem_object *obj,
 			    unsigned long n,
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_stolen.c b/drivers/gpu/drm/i915/gem/i915_gem_stolen.c
index fee5ff1a1863..0bcfe4464fa1 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_stolen.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_stolen.c
@@ -10,6 +10,7 @@
 #include <drm/drm_mm.h>
 #include <drm/i915_drm.h>
 
+#include "gem/i915_gem_lmem.h"
 #include "gem/i915_gem_region.h"
 #include "i915_drv.h"
 #include "i915_gem_stolen.h"
@@ -119,6 +120,14 @@ static int i915_adjust_stolen(struct drm_i915_private *i915,
 		}
 	}
 
+	/*
+	 * With device local memory, we don't need to check the address range,
+	 * this is device memory physical address, could overlap with system
+	 * memory.
+	 */
+	if (HAS_LMEM(i915))
+		return 0;
+
 	/*
 	 * Verify that nothing else uses this physical address. Stolen
 	 * memory should be reserved by the BIOS and hidden from the
@@ -558,7 +567,7 @@ static void i915_gem_object_put_pages_stolen(struct drm_i915_gem_object *obj,
 	kfree(pages);
 }
 
-static const struct drm_i915_gem_object_ops i915_gem_object_stolen_ops = {
+static struct drm_i915_gem_object_ops i915_gem_object_stolen_ops = {
 	.name = "i915_gem_object_stolen",
 	.get_pages = i915_gem_object_get_pages_stolen,
 	.put_pages = i915_gem_object_put_pages_stolen,
@@ -661,7 +670,19 @@ i915_gem_object_create_stolen(struct drm_i915_private *i915,
 
 static int init_stolen(struct intel_memory_region *mem)
 {
-	intel_memory_region_set_name(mem, "stolen");
+	if (mem->type == INTEL_MEMORY_STOLEN_SYSTEM)
+		intel_memory_region_set_name(mem, "stolen-system");
+	else
+		intel_memory_region_set_name(mem, "stolen-local");
+
+	if (HAS_LMEM(mem->i915)) {
+		i915_gem_object_stolen_ops.pread = i915_gem_object_lmem_pread;
+		i915_gem_object_stolen_ops.pwrite = i915_gem_object_lmem_pwrite;
+		if (!io_mapping_init_wc(&mem->iomap,
+					mem->io_start,
+					resource_size(&mem->region)))
+			return -EIO;
+	}
 
 	/*
 	 * Initialise stolen early so that we may reserve preallocated
@@ -681,8 +702,39 @@ static const struct intel_memory_region_ops i915_region_stolen_ops = {
 	.create_object = i915_gem_object_create_stolen_region,
 };
 
+static
+struct intel_memory_region *setup_lmem_stolen(struct drm_i915_private *i915)
+{
+	struct intel_uncore *uncore = &i915->uncore;
+	struct pci_dev *pdev = i915->drm.pdev;
+	struct intel_memory_region *mem;
+	resource_size_t io_start;
+	resource_size_t lmem_size;
+	u64 lmem_base;
+
+	lmem_base = intel_uncore_read64(uncore, GEN12_DSMBASE);
+	lmem_size = pci_resource_len(pdev, 2) - lmem_base;
+	io_start = pci_resource_start(pdev, 2) + lmem_base;
+
+	mem = intel_memory_region_create(i915, lmem_base, lmem_size,
+					 I915_GTT_PAGE_SIZE_4K, io_start,
+					 &i915_region_stolen_ops);
+	if (!IS_ERR(mem)) {
+		DRM_INFO("Intel graphics stolen LMEM: %pR\n", &mem->region);
+		DRM_INFO("Intel graphics stolen LMEM IO start: %llx\n",
+			 (u64)mem->io_start);
+		/* this is real device memory */
+		mem->is_devmem = true;
+	}
+
+	return mem;
+}
+
 struct intel_memory_region *i915_gem_stolen_setup(struct drm_i915_private *i915)
 {
+	if (HAS_LMEM(i915))
+		return setup_lmem_stolen(i915);
+
 	return intel_memory_region_create(i915,
 					  intel_graphics_stolen_res.start,
 					  resource_size(&intel_graphics_stolen_res),
diff --git a/drivers/gpu/drm/i915/i915_pci.c b/drivers/gpu/drm/i915/i915_pci.c
index e132fdffa432..fbb7ac96d881 100644
--- a/drivers/gpu/drm/i915/i915_pci.c
+++ b/drivers/gpu/drm/i915/i915_pci.c
@@ -897,7 +897,7 @@ static const struct intel_device_info rkl_info = {
 
 #define GEN12_DGFX_FEATURES \
 	GEN12_FEATURES, \
-	.memory_regions = REGION_SMEM | REGION_LMEM, \
+	.memory_regions = REGION_SMEM | REGION_LMEM | REGION_STOLEN_LMEM, \
 	.has_master_unit_irq = 1, \
 	.has_llc = 0, \
 	.has_snoop = 1, \
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 6e67c4ee09c2..7ee322728a65 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -12039,6 +12039,7 @@ enum skl_power_gate {
 #define   LMEM_ENABLE			(1 << 31)
 
 #define GEN12_GSMBASE			_MMIO(0x108100)
+#define GEN12_DSMBASE			_MMIO(0x1080C0)
 
 /* gamt regs */
 #define GEN8_L3_LRA_1_GPGPU _MMIO(0x4dd4)
diff --git a/drivers/gpu/drm/i915/intel_memory_region.c b/drivers/gpu/drm/i915/intel_memory_region.c
index d564b596efda..905cdfa6e3e2 100644
--- a/drivers/gpu/drm/i915/intel_memory_region.c
+++ b/drivers/gpu/drm/i915/intel_memory_region.c
@@ -19,6 +19,10 @@ const struct intel_memory_region_info intel_region_map[] = {
                .class = INTEL_MEMORY_STOLEN_SYSTEM,
                .instance = 0,
        },
+       [INTEL_REGION_STOLEN_LMEM] = {
+               .class = INTEL_MEMORY_STOLEN_LOCAL,
+               .instance = 0,
+       },
 };
 
 struct intel_memory_region *
@@ -305,6 +309,7 @@ int intel_memory_regions_hw_probe(struct drm_i915_private *i915)
 		case INTEL_MEMORY_SYSTEM:
 			mem = i915_gem_shmem_setup(i915);
 			break;
+		case INTEL_MEMORY_STOLEN_LOCAL: /* fallthrough */
 		case INTEL_MEMORY_STOLEN_SYSTEM:
 			mem = i915_gem_stolen_setup(i915);
 			break;
diff --git a/drivers/gpu/drm/i915/intel_memory_region.h b/drivers/gpu/drm/i915/intel_memory_region.h
index a08cbae18979..8f9c943a16af 100644
--- a/drivers/gpu/drm/i915/intel_memory_region.h
+++ b/drivers/gpu/drm/i915/intel_memory_region.h
@@ -93,7 +93,7 @@ struct intel_memory_region {
 	u16 type;
 	u16 instance;
 	enum intel_region_id id;
-	char name[8];
+	char name[16];
 	bool is_devmem;	/* true for device memory */
 
 	dma_addr_t remap_addr;
-- 
2.26.2

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

  parent reply	other threads:[~2020-07-10 12:01 UTC|newest]

Thread overview: 87+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-07-10 11:56 [Intel-gfx] [RFC 00/60] DG1 LMEM enabling Matthew Auld
2020-07-10 11:56 ` [Intel-gfx] [RFC 01/60] drm/i915: Add has_master_unit_irq flag Matthew Auld
2020-07-10 11:56 ` [Intel-gfx] [RFC 02/60] drm/i915/dg1: add initial DG-1 definitions Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 03/60] drm/i915/dg1: Add DG1 PCI IDs Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 04/60] drm/i915/dg1: add support for the master unit interrupt Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 05/60] drm/i915/dg1: Remove SHPD_FILTER_CNT register programming Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 06/60] drm/i915/dg1: Add fake PCH Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 07/60] drm/i915/dg1: Initialize RAWCLK properly Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 08/60] drm/i915/dg1: Define MOCS table for DG1 Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 09/60] drm/i915/dg1: Add DG1 power wells Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 10/60] drm/i915/dg1: Increase mmio size to 4MB Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 11/60] drm/i915/dg1: Wait for pcode/uncore handshake at startup Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 12/60] drm/i915/dg1: Add DPLL macros for DG1 Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 13/60] drm/i915/dg1: Add and setup DPLLs " Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 14/60] drm/i915/dg1: Enable DPLL " Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 15/60] drm/i915/dg1: add hpd interrupt handling Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 16/60] drm/i915/dg1: invert HPD pins Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 17/60] drm/i915/dg1: gmbus pin mapping Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 18/60] drm/i915/dg1: Enable first 2 ports for DG1 Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 19/60] drm/i915/dg1: Don't program PHY_MISC for PHY-C and PHY-D Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 20/60] drm/i915/dg1: Update comp master/slave relationships for PHYs Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 21/60] drm/i915/dg1: Update voltage swing tables for DP Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 22/60] drm/i915/dg1: provide port/phy mapping for vbt Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 23/60] drm/i915/dg1: map/unmap pll clocks Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 24/60] drm/i915/dg1: enable PORT C/D aka D/E Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 25/60] drm/i915/dg1: Load DMC Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 26/60] drm/i915/rkl: Add initial workarounds Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 27/60] drm/i915/dg1: Add initial DG1 workarounds Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 28/60] drm/i915/dg1: DG1 does not support DC6 Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 29/60] drm/i915/lmem: Limit block size to 4G Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 30/60] drm/i915/lmem: Do not check r->sgt.pfn for NULL Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 31/60] drm/i915/dgfx: define llc and snooping behaviour Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 32/60] drm/i915/lmem: support pread Matthew Auld
2020-07-10 12:39   ` Tvrtko Ursulin
2020-07-10 13:04     ` Chris Wilson
2020-07-10 11:57 ` [Intel-gfx] [RFC 33/60] drm/i915/lmem: support pwrite Matthew Auld
2020-07-13  5:09   ` Dave Airlie
2020-07-14 14:35     ` Matthew Auld
2020-07-16  0:43       ` Dave Airlie
2020-07-16 10:11         ` Matthew Auld
2020-07-19 21:52           ` Dave Airlie
2020-08-07  9:32             ` Joonas Lahtinen
2020-08-07  9:46     ` Joonas Lahtinen
2020-08-09 21:06       ` Dave Airlie
2020-07-10 11:57 ` [Intel-gfx] [RFC 34/60] drm/i915: introduce kernel blitter_context Matthew Auld
2020-08-03 19:59   ` Lucas De Marchi
2020-08-03 20:17     ` Lucas De Marchi
2020-07-10 11:57 ` [Intel-gfx] [RFC 35/60] drm/i915/query: Expose memory regions through the query uAPI Matthew Auld
2020-07-10 16:20   ` Tvrtko Ursulin
2020-07-10 11:57 ` [Intel-gfx] [RFC 36/60] drm/i915/uapi: introduce drm_i915_gem_create_ext Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 37/60] drm/i915/lmem: allocate cmd ring in lmem Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 38/60] drm/i915/dg1: Introduce dmabuf mmap to LMEM Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 39/60] drm/i915: setup the LMEM region Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 40/60] drm/i915: drop fake LMEM Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 41/60] drm/i915: Distinction of memory regions Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 42/60] drm/i915: PPGTT support Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 43/60] drm/i915: support GGTT LMEM entries Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 44/60] drm/i915: allocate context from LMEM Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 45/60] drm/i915: move engine scratch to LMEM Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 46/60] drm/i915: Provide a way to disable PCIe relaxed write ordering Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 47/60] drm/i915: setup GPU device lmem region Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 48/60] drm/i915: Fix object page offset within a region Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 49/60] drm/i915: add i915_gem_object_is_devmem() function Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 50/60] drm/i915: finish memory region support for stolen objects Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 51/60] drm/i915/lmem: support optional CPU clearing for special internal use Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 52/60] drm/i915/guc: put all guc objects in lmem when available Matthew Auld
2020-07-10 11:57 ` Matthew Auld [this message]
2020-07-13  4:48   ` [Intel-gfx] [RFC 53/60] drm/i915: Create stolen memory region from local memory Dave Airlie
2020-07-14 15:01     ` Matthew Auld
2020-07-14 16:57       ` Tang, CQ
2020-07-14 19:26         ` Dave Airlie
2020-08-07  9:38           ` Joonas Lahtinen
2020-08-07 16:24             ` Tang, CQ
2020-07-14 17:39       ` Ville Syrjälä
2020-07-10 11:57 ` [Intel-gfx] [RFC 54/60] drm/i915/lmem: Bypass aperture when lmem is available Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 55/60] drm/i915/lmem: reset the lmem buffer created by fbdev Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 56/60] drm/i915/dsb: Enable lmem for dsb Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 57/60] drm/i915: Reintroduce mem->reserved Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 58/60] drm/i915/dg1: Reserve first 1MB of local memory Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 59/60] drm/i915: defer pd lmem block put to worker Matthew Auld
2020-07-10 11:57 ` [Intel-gfx] [RFC 60/60] drm/i915/lmem: allocate HWSP in lmem Matthew Auld
2020-07-10 12:51 ` [Intel-gfx] ✗ Fi.CI.CHECKPATCH: warning for DG1 LMEM enabling Patchwork
2020-07-10 12:52 ` [Intel-gfx] ✗ Fi.CI.SPARSE: " Patchwork
2020-07-10 13:12 ` [Intel-gfx] ✓ Fi.CI.BAT: success " Patchwork
2020-07-10 14:50 ` [Intel-gfx] ✗ Fi.CI.IGT: failure " Patchwork
2020-08-11  3:23 ` [Intel-gfx] [RFC 00/60] " Dave Airlie
2020-08-11 10:48   ` Matthew Auld

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=20200710115757.290984-54-matthew.auld@intel.com \
    --to=matthew.auld@intel.com \
    --cc=Balestrieri@freedesktop.org \
    --cc=abdiel.janulgue@linux.intel.com \
    --cc=chris.p.wilson@intel.com \
    --cc=intel-gfx@lists.freedesktop.org \
    --cc=lucas.demarchi@intel.com \
    --cc=neel.desai@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