AMD-GFX Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] drm: Move AMD VSDB parsing into DRM EDID core
@ 2026-03-27  8:21 Chenyu Chen
  2026-03-27  8:21 ` [PATCH 1/2] drm/edid: Parse AMD Vendor-Specific Data Block Chenyu Chen
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Chenyu Chen @ 2026-03-27  8:21 UTC (permalink / raw)
  To: dri-devel, amd-gfx; +Cc: Harry Wentland, Leo Li, Limonciello Mario, Chenyu Chen

The AMD Vendor-Specific Data Block (VSDB) v3 parsing currently lives
in amdgpu_dm, using raw byte-walking over EDID extension blocks. This
series moves the parsing into the DRM EDID core so the data is
available through drm_display_info for any driver.

Patch 1 adds the parser in drm_edid.c and the new drm_amd_vsdb_info
struct in drm_connector.h.

Patch 2 updates amdgpu_dm to consume the parsed data from
display_info instead of doing its own parsing, and factors out
panel type determination into a dedicated function.

Chenyu Chen (2):
  drm/edid: Parse AMD Vendor-Specific Data Block
  drm/amd/display: Use drm_display_info for AMD VSDB data

 .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 120 ++++++++++--------
 .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h |  14 --
 drivers/gpu/drm/drm_edid.c                    |  72 +++++++++++
 include/drm/drm_connector.h                   |  38 ++++++
 4 files changed, 178 insertions(+), 66 deletions(-)

-- 
2.43.0


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

* [PATCH 1/2] drm/edid: Parse AMD Vendor-Specific Data Block
  2026-03-27  8:21 [PATCH 0/2] drm: Move AMD VSDB parsing into DRM EDID core Chenyu Chen
@ 2026-03-27  8:21 ` Chenyu Chen
  2026-03-27  8:21 ` [PATCH 2/2] drm/amd/display: Use drm_display_info for AMD VSDB data Chenyu Chen
  2026-03-27 13:04 ` [PATCH 0/2] drm: Move AMD VSDB parsing into DRM EDID core Mario Limonciello
  2 siblings, 0 replies; 4+ messages in thread
From: Chenyu Chen @ 2026-03-27  8:21 UTC (permalink / raw)
  To: dri-devel, amd-gfx; +Cc: Harry Wentland, Leo Li, Limonciello Mario, Chenyu Chen

Parse the AMD VSDB v3 from CTA extension blocks and store the result
in struct drm_amd_vsdb_info, a new field of drm_display_info. This
includes replay mode, panel type, and luminance ranges.

Signed-off-by: Chenyu Chen <chen-yu.chen@amd.com>
---
 drivers/gpu/drm/drm_edid.c  | 72 +++++++++++++++++++++++++++++++++++++
 include/drm/drm_connector.h | 38 ++++++++++++++++++++
 2 files changed, 110 insertions(+)

diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 26bb7710a462..76280e6e1892 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -99,6 +99,29 @@ enum drm_edid_internal_quirk {
 };
 
 #define MICROSOFT_IEEE_OUI	0xca125c
+#define AMD_IEEE_OUI        0x00001A
+
+#define AMD_VSDB_V3_PAYLOAD_MIN_LEN 15
+#define AMD_VSDB_V3_PAYLOAD_MAX_LEN 20
+
+struct amd_vsdb_v3_payload {
+	u8 oui[3];
+	u8 version;
+	u8 feature_caps;
+	u8 rsvd0[3];
+	u8 cs_eotf_support;
+	u8 lum1_max;
+	u8 lum1_min;
+	u8 lum2_max;
+	u8 lum2_min;
+	u8 rsvd1[2];
+	/*
+	 * Bytes beyond AMD_VSDB_V3_PAYLOAD_MIN_LEN are optional; a
+	 * monitor may provide a payload as short as 15 bytes.  Always
+	 * check cea_db_payload_len() before accessing extra[].
+	 */
+	u8 extra[AMD_VSDB_V3_PAYLOAD_MAX_LEN - AMD_VSDB_V3_PAYLOAD_MIN_LEN];
+} __packed;
 
 struct detailed_mode_closure {
 	struct drm_connector *connector;
@@ -5205,6 +5228,13 @@ static bool cea_db_is_microsoft_vsdb(const struct cea_db *db)
 		cea_db_payload_len(db) == 21;
 }
 
+static bool cea_db_is_amd_vsdb(const struct cea_db *db)
+{
+	return cea_db_is_vendor(db, AMD_IEEE_OUI) &&
+		cea_db_payload_len(db) >= AMD_VSDB_V3_PAYLOAD_MIN_LEN &&
+		cea_db_payload_len(db) <= AMD_VSDB_V3_PAYLOAD_MAX_LEN;
+}
+
 static bool cea_db_is_vcdb(const struct cea_db *db)
 {
 	return cea_db_is_extended_tag(db, CTA_EXT_DB_VIDEO_CAP) &&
@@ -6401,6 +6431,45 @@ static void drm_parse_microsoft_vsdb(struct drm_connector *connector,
 		    connector->base.id, connector->name, version, db[5]);
 }
 
+static void drm_parse_amd_vsdb(struct drm_connector *connector,
+							   const struct cea_db *db)
+{
+	struct drm_display_info *info = &connector->display_info;
+	const u8 *data = cea_db_data(db);
+	const struct amd_vsdb_v3_payload *p;
+
+	p = (const struct amd_vsdb_v3_payload *)data;
+
+	if (p->version != 0x03) {
+		drm_dbg_kms(connector->dev,
+			    "[CONNECTOR:%d:%s] Unsupported AMD VSDB version %u\n",
+			    connector->base.id, connector->name, p->version);
+		return;
+	}
+
+	info->amd_vsdb.version = p->version;
+	info->amd_vsdb.replay_mode = p->feature_caps & 0x40;
+	info->amd_vsdb.panel_type = (p->cs_eotf_support & 0xC0) >> 6;
+	info->amd_vsdb.luminance_range1.max_luminance = p->lum1_max;
+	info->amd_vsdb.luminance_range1.min_luminance = p->lum1_min;
+	info->amd_vsdb.luminance_range2.max_luminance = p->lum2_max;
+	info->amd_vsdb.luminance_range2.min_luminance = p->lum2_min;
+
+	/*
+	 * The AMD VSDB v3 payload length is variable (15..20 bytes).
+	 * All fields through p->rsvd1 (byte 14) are always present,
+	 * but p->extra[] (bytes 15+) may not be.  Any future access to
+	 * extra[] must be guarded with a runtime length check to avoid
+	 * out-of-bounds reads on shorter (but spec-valid) payloads.
+	 * For example:
+	 *
+	 *   int len = cea_db_payload_len(db);
+	 *
+	 *   if (len > AMD_VSDB_V3_PAYLOAD_MIN_LEN)
+	 *       info->amd_vsdb.foo = p->extra[0];
+	 */
+}
+
 static void drm_parse_cea_ext(struct drm_connector *connector,
 			      const struct drm_edid *drm_edid)
 {
@@ -6449,6 +6518,8 @@ static void drm_parse_cea_ext(struct drm_connector *connector,
 			drm_parse_hdmi_forum_scds(connector, data);
 		else if (cea_db_is_microsoft_vsdb(db))
 			drm_parse_microsoft_vsdb(connector, data);
+		else if (cea_db_is_amd_vsdb(db))
+			drm_parse_amd_vsdb(connector, db);
 		else if (cea_db_is_y420cmdb(db))
 			parse_cta_y420cmdb(connector, db, &y420cmdb_map);
 		else if (cea_db_is_y420vdb(db))
@@ -6641,6 +6712,7 @@ static void drm_reset_display_info(struct drm_connector *connector)
 	info->quirks = 0;
 
 	info->source_physical_address = CEC_PHYS_ADDR_INVALID;
+	memset(&info->amd_vsdb, 0, sizeof(info->amd_vsdb));
 }
 
 static void update_displayid_info(struct drm_connector *connector,
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index c18be8c19de0..c398dbc68bbc 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -667,6 +667,39 @@ enum drm_bus_flags {
 	DRM_BUS_FLAG_SHARP_SIGNALS = BIT(8),
 };
 
+/**
+ * struct drm_amd_vsdb_info - AMD-specific VSDB information
+ *
+ * This structure holds information parsed from the AMD Vendor-Specific Data
+ * Block (VSDB) version 3.
+ */
+struct drm_amd_vsdb_info {
+	/**
+	 * @version: Version of the Vendor-Specific Data Block (VSDB)
+	 */
+	u8 version;
+
+	/**
+	 * @replay_mode: Panel Replay supported
+	 */
+	bool replay_mode;
+
+	/**
+	 * @panel_type: Panel technology type
+	 */
+	u8 panel_type;
+
+	/**
+	 * @luminance_range1: Luminance for max back light
+	 */
+	struct drm_luminance_range_info luminance_range1;
+
+	/**
+	 * @luminance_range2: Luminance for min back light
+	 */
+	struct drm_luminance_range_info luminance_range2;
+};
+
 /**
  * struct drm_display_info - runtime data about the connected sink
  *
@@ -861,6 +894,11 @@ struct drm_display_info {
 	 * Defaults to CEC_PHYS_ADDR_INVALID (0xffff).
 	 */
 	u16 source_physical_address;
+
+	/**
+	 * @amd_vsdb: AMD-specific VSDB information.
+	 */
+	struct drm_amd_vsdb_info amd_vsdb;
 };
 
 int drm_display_info_set_bus_formats(struct drm_display_info *info,
-- 
2.43.0


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

* [PATCH 2/2] drm/amd/display: Use drm_display_info for AMD VSDB data
  2026-03-27  8:21 [PATCH 0/2] drm: Move AMD VSDB parsing into DRM EDID core Chenyu Chen
  2026-03-27  8:21 ` [PATCH 1/2] drm/edid: Parse AMD Vendor-Specific Data Block Chenyu Chen
@ 2026-03-27  8:21 ` Chenyu Chen
  2026-03-27 13:04 ` [PATCH 0/2] drm: Move AMD VSDB parsing into DRM EDID core Mario Limonciello
  2 siblings, 0 replies; 4+ messages in thread
From: Chenyu Chen @ 2026-03-27  8:21 UTC (permalink / raw)
  To: dri-devel, amd-gfx; +Cc: Harry Wentland, Leo Li, Limonciello Mario, Chenyu Chen

Replace the raw EDID byte-walking in parse_amd_vsdb() with a read
from connector->display_info.amd_vsdb, now populated by drm_edid.

Factor out panel type determination into dm_set_panel_type(), which
checks VSDB panel_type, DPCD ext caps, and a luminance heuristic as
fallbacks.

Signed-off-by: Chenyu Chen <chen-yu.chen@amd.com>
---
 .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 120 ++++++++++--------
 .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h |  14 --
 2 files changed, 68 insertions(+), 66 deletions(-)

diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index 48e12f9a1818..717e8d3feb8d 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -3823,6 +3823,66 @@ static struct drm_mode_config_helper_funcs amdgpu_dm_mode_config_helperfuncs = {
 	.atomic_commit_setup = amdgpu_dm_atomic_setup_commit,
 };
 
+#define DDC_MANUFACTURERNAME_SAMSUNG 0x2D4C
+
+static void dm_set_panel_type(struct amdgpu_dm_connector *aconnector)
+{
+	struct drm_connector *connector = &aconnector->base;
+	struct drm_display_info *display_info = &connector->display_info;
+	struct dc_link *link = aconnector->dc_link;
+	struct amdgpu_device *adev;
+
+	adev = drm_to_adev(connector->dev);
+
+	link->panel_type = PANEL_TYPE_NONE;
+
+	switch (display_info->amd_vsdb.panel_type) {
+	case AMD_VSDB_PANEL_TYPE_OLED:
+		link->panel_type = PANEL_TYPE_OLED;
+		break;
+	case AMD_VSDB_PANEL_TYPE_MINILED:
+		link->panel_type = PANEL_TYPE_MINILED;
+		break;
+	}
+
+	/* If VSDB didn't determine panel type, check DPCD ext caps */
+	if (link->panel_type == PANEL_TYPE_NONE) {
+		if (link->dpcd_sink_ext_caps.bits.miniled == 1)
+			link->panel_type = PANEL_TYPE_MINILED;
+		if (link->dpcd_sink_ext_caps.bits.oled == 1)
+			link->panel_type = PANEL_TYPE_OLED;
+	}
+
+	/*
+	 * TODO: get panel type from DID2 that has device technology field
+	 * to specify if it's OLED or not. But we need to wait for DID2
+	 * support in DC and EDID parser to be able to use it here.
+	 */
+
+	if (link->panel_type == PANEL_TYPE_NONE) {
+		struct drm_amd_vsdb_info *vsdb = &display_info->amd_vsdb;
+		u32 lum1_max = vsdb->luminance_range1.max_luminance;
+		u32 lum2_max = vsdb->luminance_range2.max_luminance;
+
+		if (vsdb->version && link->local_sink &&
+		    link->local_sink->edid_caps.manufacturer_id ==
+		    DDC_MANUFACTURERNAME_SAMSUNG &&
+		    lum1_max >= ((lum2_max * 3) / 2))
+			link->panel_type = PANEL_TYPE_MINILED;
+	}
+
+	if (link->panel_type == PANEL_TYPE_OLED)
+		drm_object_property_set_value(&connector->base,
+			adev_to_drm(adev)->mode_config.panel_type_property,
+			DRM_MODE_PANEL_TYPE_OLED);
+	else
+		drm_object_property_set_value(&connector->base,
+			adev_to_drm(adev)->mode_config.panel_type_property,
+			DRM_MODE_PANEL_TYPE_UNKNOWN);
+
+	drm_dbg_kms(aconnector->base.dev, "Panel type: %d\n", link->panel_type);
+}
+
 static void update_connector_ext_caps(struct amdgpu_dm_connector *aconnector)
 {
 	const struct drm_panel_backlight_quirk *panel_backlight_quirk;
@@ -3844,10 +3904,6 @@ static void update_connector_ext_caps(struct amdgpu_dm_connector *aconnector)
 	caps->ext_caps = &aconnector->dc_link->dpcd_sink_ext_caps;
 	caps->aux_support = false;
 
-	drm_object_property_set_value(&conn_base->base,
-				      adev_to_drm(adev)->mode_config.panel_type_property,
-				      caps->ext_caps->bits.oled ? DRM_MODE_PANEL_TYPE_OLED : DRM_MODE_PANEL_TYPE_UNKNOWN);
-
 	if (caps->ext_caps->bits.oled == 1
 	    /*
 	     * ||
@@ -4021,6 +4077,7 @@ void amdgpu_dm_update_connector_after_detect(
 
 		amdgpu_dm_update_freesync_caps(connector, aconnector->drm_edid);
 		update_connector_ext_caps(aconnector);
+		dm_set_panel_type(aconnector);
 	} else {
 		hdmi_cec_unset_edid(aconnector);
 		drm_dp_cec_unset_edid(&aconnector->dm_dp_aux.aux);
@@ -13167,56 +13224,15 @@ static void parse_edid_displayid_vrr(struct drm_connector *connector,
 	}
 }
 
-static int parse_amd_vsdb(struct amdgpu_dm_connector *aconnector,
-			  const struct edid *edid, struct amdgpu_hdmi_vsdb_info *vsdb_info)
+static int get_amd_vsdb(struct amdgpu_dm_connector *aconnector,
+			struct amdgpu_hdmi_vsdb_info *vsdb_info)
 {
-	u8 *edid_ext = NULL;
-	int i;
-	int j = 0;
-	int total_ext_block_len;
-
-	if (edid == NULL || edid->extensions == 0)
-		return -ENODEV;
-
-	/* Find DisplayID extension */
-	for (i = 0; i < edid->extensions; i++) {
-		edid_ext = (void *)(edid + (i + 1));
-		if (edid_ext[0] == DISPLAYID_EXT)
-			break;
-	}
-
-	total_ext_block_len = EDID_LENGTH * edid->extensions;
-	while (j < total_ext_block_len - sizeof(struct amd_vsdb_block)) {
-		struct amd_vsdb_block *amd_vsdb = (struct amd_vsdb_block *)&edid_ext[j];
-		unsigned int ieeeId = (amd_vsdb->ieee_id[2] << 16) | (amd_vsdb->ieee_id[1] << 8) | (amd_vsdb->ieee_id[0]);
-
-		if (ieeeId == HDMI_AMD_VENDOR_SPECIFIC_DATA_BLOCK_IEEE_REGISTRATION_ID &&
-				amd_vsdb->version == HDMI_AMD_VENDOR_SPECIFIC_DATA_BLOCK_VERSION_3) {
-			u8 panel_type;
-			vsdb_info->replay_mode = (amd_vsdb->feature_caps & AMD_VSDB_VERSION_3_FEATURECAP_REPLAYMODE) ? true : false;
-			vsdb_info->amd_vsdb_version = HDMI_AMD_VENDOR_SPECIFIC_DATA_BLOCK_VERSION_3;
-			drm_dbg_kms(aconnector->base.dev, "Panel supports Replay Mode: %d\n", vsdb_info->replay_mode);
-			panel_type = (amd_vsdb->color_space_eotf_support & AMD_VDSB_VERSION_3_PANEL_TYPE_MASK) >> AMD_VDSB_VERSION_3_PANEL_TYPE_SHIFT;
-			switch (panel_type) {
-			case AMD_VSDB_PANEL_TYPE_OLED:
-				aconnector->dc_link->panel_type = PANEL_TYPE_OLED;
-				break;
-			case AMD_VSDB_PANEL_TYPE_MINILED:
-				aconnector->dc_link->panel_type = PANEL_TYPE_MINILED;
-				break;
-			default:
-				aconnector->dc_link->panel_type = PANEL_TYPE_NONE;
-				break;
-			}
-			drm_dbg_kms(aconnector->base.dev, "Panel type: %d\n",
-				    aconnector->dc_link->panel_type);
+	struct drm_connector *connector = &aconnector->base;
 
-			return true;
-		}
-		j++;
-	}
+	vsdb_info->replay_mode = connector->display_info.amd_vsdb.replay_mode;
+	vsdb_info->amd_vsdb_version = connector->display_info.amd_vsdb.version;
 
-	return false;
+	return connector->display_info.amd_vsdb.version != 0;
 }
 
 static int parse_hdmi_amd_vsdb(struct amdgpu_dm_connector *aconnector,
@@ -13319,7 +13335,7 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector,
 				freesync_capable = true;
 		}
 
-		parse_amd_vsdb(amdgpu_dm_connector, edid, &vsdb_info);
+		get_amd_vsdb(amdgpu_dm_connector, &vsdb_info);
 
 		if (vsdb_info.replay_mode) {
 			amdgpu_dm_connector->vsdb_info.replay_mode = vsdb_info.replay_mode;
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
index d1a14e0c12bd..63ce1f52b697 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
@@ -53,12 +53,6 @@
 
 #define AMDGPU_DMUB_NOTIFICATION_MAX 8
 
-#define HDMI_AMD_VENDOR_SPECIFIC_DATA_BLOCK_IEEE_REGISTRATION_ID 0x00001A
-#define AMD_VSDB_VERSION_3_FEATURECAP_REPLAYMODE 0x40
-#define AMD_VDSB_VERSION_3_PANEL_TYPE_MASK 0xC0
-#define AMD_VDSB_VERSION_3_PANEL_TYPE_SHIFT 6
-#define HDMI_AMD_VENDOR_SPECIFIC_DATA_BLOCK_VERSION_3 0x3
-
 enum amd_vsdb_panel_type {
 	AMD_VSDB_PANEL_TYPE_DEFAULT = 0,
 	AMD_VSDB_PANEL_TYPE_MINILED,
@@ -97,14 +91,6 @@ struct dc_plane_state;
 struct dmub_notification;
 struct dmub_cmd_fused_request;
 
-struct amd_vsdb_block {
-	unsigned char ieee_id[3];
-	unsigned char version;
-	unsigned char feature_caps;
-	unsigned char reserved[3];
-	unsigned char color_space_eotf_support;
-};
-
 struct common_irq_params {
 	struct amdgpu_device *adev;
 	enum dc_irq_source irq_src;
-- 
2.43.0


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

* Re: [PATCH 0/2] drm: Move AMD VSDB parsing into DRM EDID core
  2026-03-27  8:21 [PATCH 0/2] drm: Move AMD VSDB parsing into DRM EDID core Chenyu Chen
  2026-03-27  8:21 ` [PATCH 1/2] drm/edid: Parse AMD Vendor-Specific Data Block Chenyu Chen
  2026-03-27  8:21 ` [PATCH 2/2] drm/amd/display: Use drm_display_info for AMD VSDB data Chenyu Chen
@ 2026-03-27 13:04 ` Mario Limonciello
  2 siblings, 0 replies; 4+ messages in thread
From: Mario Limonciello @ 2026-03-27 13:04 UTC (permalink / raw)
  To: Chenyu Chen, dri-devel, amd-gfx; +Cc: Harry Wentland, Leo Li



On 3/27/26 03:21, Chenyu Chen wrote:
> The AMD Vendor-Specific Data Block (VSDB) v3 parsing currently lives
> in amdgpu_dm, using raw byte-walking over EDID extension blocks. This
> series moves the parsing into the DRM EDID core so the data is
> available through drm_display_info for any driver.
> 
> Patch 1 adds the parser in drm_edid.c and the new drm_amd_vsdb_info
> struct in drm_connector.h.
> 
> Patch 2 updates amdgpu_dm to consume the parsed data from
> display_info instead of doing its own parsing, and factors out
> panel type determination into a dedicated function.
> 
> Chenyu Chen (2):
>    drm/edid: Parse AMD Vendor-Specific Data Block
>    drm/amd/display: Use drm_display_info for AMD VSDB data
> 
>   .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 120 ++++++++++--------
>   .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h |  14 --
>   drivers/gpu/drm/drm_edid.c                    |  72 +++++++++++
>   include/drm/drm_connector.h                   |  38 ++++++
>   4 files changed, 178 insertions(+), 66 deletions(-)
> 

Reviewed-by: Mario Limonciello (AMD) <superm1@kernel.org>

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

end of thread, other threads:[~2026-03-27 13:04 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-27  8:21 [PATCH 0/2] drm: Move AMD VSDB parsing into DRM EDID core Chenyu Chen
2026-03-27  8:21 ` [PATCH 1/2] drm/edid: Parse AMD Vendor-Specific Data Block Chenyu Chen
2026-03-27  8:21 ` [PATCH 2/2] drm/amd/display: Use drm_display_info for AMD VSDB data Chenyu Chen
2026-03-27 13:04 ` [PATCH 0/2] drm: Move AMD VSDB parsing into DRM EDID core Mario Limonciello

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox