dri-devel Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Devarsh Thakkar" <devarsht@ti.com>
Cc: dri-devel@lists.freedesktop.org, devicetree@vger.kernel.org,
	robh@kernel.org, conor+dt@kernel.org
Subject: Re: [PATCH v3 14/14] arm64: dts: ti: [DO_NOT_MERGE] Add AM62L3 EVM overlay for GDEY042T81 e-paper display
Date: Mon, 19 Oct 2026 21:59:38 +0000	[thread overview]
Message-ID: <sashiko-outbox-178286@kernel.org> (raw)
In-Reply-To: <20261019213726.4145532-15-devarsht@ti.com>

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [Medium] drm/solomon: ssd16xx: broken RAM window in ssd16xx_clear_display() at 90/180 deg
- [Medium] drm/solomon: ssd16xx: missing fast refresh LUT preload on init and resume
- [Medium] drm/solomon: ssd16xx: broken deep_sleep_mode state and error rollback in ssd16xx_pm_suspend()
- [Medium] drm/solomon: ssd16xx: missing RED RAM baseline restore after mode 0x03 sleep
--

commit a52cd0ebb1dc317dd86b82ae1b73fbcb5d5fa3b6
Author: Devarsh Thakkar <devarsht@ti.com>

arm64: dts: ti: [DO_NOT_MERGE] Add AM62L3 EVM overlay for GDEY042T81 e-paper display

This series adds a DRM driver for the Solomon SSD16xx family of e-paper
display controllers (including SSD1683 and SSD1677) with runtime and system
power management, session management, and plane properties, along with the
AM62L3 EVM device tree overlay for the GDEY042T81 panel.

> diff --git a/drivers/gpu/drm/solomon/ssd16xx.c b/drivers/gpu/drm/solomon/ssd16xx.c
> new file mode 100644
> index 0000000000000..29dca237378f8
> --- /dev/null
> +++ b/drivers/gpu/drm/solomon/ssd16xx.c

[ ... ]

> +	[SSD1677] = {
> +		/*
> +		 * 10-bit pixel-level X/Y addressing; only deep sleep mode
> +		 * 0x03 is supported (no RAM-retain mode).  Border waveform
> +		 * encoding matches SSD1683.
> +		 */
> +		.max_width  = 960,
> +		.max_height = 680,
> +		.ram_x_address_bits = 10,
> +		.ram_y_address_bits = 10,
> +		.has_temp_sensor_ctrl    = true,
> +		.deep_sleep_mode_level1  = SSD1683_DEEP_SLEEP_MODE_2,  /* 0x03 */
> +		.deep_sleep_mode_level2  = SSD1683_DEEP_SLEEP_MODE_2,  /* 0x03 */

[ ... ]

> +	switch (device->orientation) {
> +	case 90:
> +	case 180:
> +		data_entry_mode = SSD16XX_DATA_ENTRY_XDEC_YDEC;
> +		break;
> +	default: /* 0°/270° */
> +		data_entry_mode = SSD16XX_DATA_ENTRY_XINC_YINC;
> +		break;
> +	}
> +
> +	ssd16xx_send_cmd(device, SSD16XX_CMD_DATA_ENTRY_MODE, &err);
> +	ssd16xx_send_data(device, data_entry_mode, &err);
> +	drm_dbg(&device->drm, "hw_init: orientation=%u° data_entry=0x%02x\n",
> +		device->orientation, data_entry_mode);
> +
> +	ssd16xx_wait_for_device(device, &err);
> +
> +	if (err)
> +		drm_err(&device->drm, "Hardware initialization failed: %d\n", err);
> +
> +	return err;
> +}
> +
> +/*
> + * Clear display by writing all-white to both BW and RED RAM.
> + * The ctrl2 argument selects the waveform (full/partial/fast refresh).
> + * Border waveform is set to init value before clearing, then restored
> + * to the update value to preserve the border during subsequent updates.
> + */
> +static int ssd16xx_clear_display(struct ssd16xx_device *device, u8 ctrl2)
> +{
> +	const u8 *bw_tbl = device->controller_cfg->border_waveform_table;
> +	int err = 0;
> +	unsigned int data_size = DIV_ROUND_UP(device->width, 8) * device->height;
> +	u8 *white_buffer = device->tx_buf;
> +	u8 *red_buffer = white_buffer;
> +	u16 ram_x_end, ram_y_end;
> +
> +	memset(white_buffer, 0xFF, data_size);
> +
> +	if (device->controller_cfg->ram_x_address_bits == 8)
> +		ram_x_end = (device->width / 8) - 1;
> +	else
> +		ram_x_end = device->width - 1;
> +	ram_y_end = device->height - 1;
> +
> +	/* Reset full display RAM window before clearing */
> +	ssd16xx_send_cmd(device, SSD16XX_CMD_SET_RAM_X_ADDRESS_START_END, &err);
> +	ssd16xx_send_x_param(device, 0x00, &err);
> +	ssd16xx_send_x_param(device, ram_x_end, &err);
> +
> +	ssd16xx_send_cmd(device, SSD16XX_CMD_SET_RAM_Y_ADDRESS_START_END, &err);
> +	ssd16xx_send_y_param(device, 0x00, &err);
> +	ssd16xx_send_y_param(device, ram_y_end, &err);
> +
> +	ssd16xx_send_cmd(device, SSD16XX_CMD_SET_RAM_X_ADDRESS_COUNTER, &err);
> +	ssd16xx_send_x_param(device, 0x00, &err);
> +
> +	ssd16xx_send_cmd(device, SSD16XX_CMD_SET_RAM_Y_ADDRESS_COUNTER, &err);
> +	ssd16xx_send_y_param(device, 0x00, &err);

[Severity: Medium]
When device->orientation is 90 or 180, ssd16xx_hw_init() sets
SSD16XX_CMD_DATA_ENTRY_MODE to SSD16XX_DATA_ENTRY_XDEC_YDEC, where the
controller decrements X and Y on every byte written.

In ssd16xx_fb_dirty(), 90 and 180 degree orientations program the RAM window
with end-before-start (ram_x_end to ram_x_start, ram_y_end to ram_y_start)
and place the initial address counters at (ram_x_end, ram_y_end).

Here in ssd16xx_clear_display(), the window is unconditionally programmed
from 0x00 to ram_x_end and 0x00 to ram_y_end with the counters starting at
(0x00, 0x00). When invoked via ssd16xx_clear_display_on_init(),
ssd16xx_clear_display_on_exit(), or ssd16xx_crtc_atomic_disable() on a panel
rotated 90 or 180 degrees, won't this fail to clear the display RAM and
leave RED RAM out of sync for subsequent partial updates?

[ ... ]

> +	/* Resume lets the runtime_resume callback re-run hw_init if needed. */
> +	ret = pm_runtime_resume_and_get(device->drm.dev);
> +	if (ret < 0) {
> +		drm_err(&device->drm, "plane_atomic_update: failed to resume: %d\n", ret);
> +		goto out_exit;
> +	}
> +
> +	if (!device->initialized) {
> +		drm_err(&device->drm, "plane_atomic_update: hw not initialized after resume\n");
> +		pm_runtime_put_autosuspend(device->drm.dev);
> +		goto out_exit;
> +	}
> +
> +	drm_dbg(&device->drm, "plane_atomic_update: calling fb_dirty rect=(%d,%d)-(%d,%d)\n",
> +		rect.x1, rect.y1, rect.x2, rect.y2);
> +
> +	mutex_lock(&device->lock);
> +
> +	/* Diff refresh_mode against the plane's own previous state to see
> +	 * whether fast LUT preload is needed.
> +	 */
> +	if (old_ssd->refresh_mode != new_ssd->refresh_mode) {
> +		/*
> +		 * Fast refresh omits LOAD_LUT each update; arm the
> +		 * one-shot flag so LUT is pre-loaded before the first
> +		 * fast frame.
> +		 */
> +		if (new_ssd->refresh_mode == SSD16XX_REFRESH_FAST &&
> +		    old_ssd->refresh_mode != SSD16XX_REFRESH_FULL)
> +			device->fast_lut_pending = true;
> +		else
> +			device->fast_lut_pending = false;
> +	}
> +
> +	ssd16xx_sync_session_props(device, new_ssd);
> +
> +	/*
> +	 * When refresh_mode_init was set, use the specified mode for this first
> +	 * frame only, then restore the user-configured refresh_mode so
> +	 * subsequent updates continue with the configured mode.
> +	 */
> +	saved_mode = device->refresh_mode;
> +	saved_border_waveform_idx = device->border_waveform_update_idx;
> +	if (device->init_refresh_pending) {
> +		device->refresh_mode = device->refresh_mode_init;
> +		device->border_waveform_update_idx = device->border_waveform_init_idx;
> +	}
> +
> +	/*
> +	 * Fast refresh (0xC7) omits LOAD_LUT on every update cycle and relies
> +	 * on the LUT being pre-loaded upfront.  The property setter arms
> +	 * fast_lut_pending whenever the user switches into fast mode.  Consume
> +	 * the flag here (once) before the first fast-refresh frame so the
> +	 * controller's LUT is in the correct state.
> +	 */
> +	if (device->fast_lut_pending) {
> +		ret = ssd16xx_preload_fast_lut(device);
> +		if (ret) {
> +			drm_err(&device->drm,
> +				"plane_atomic_update: fast LUT preload failed: %d\n", ret);
> +		}
> +
> +		device->fast_lut_pending = false;
> +	}

[Severity: Medium]
Can SSD16XX_REFRESH_FAST run without pre-loading the LUT in a few paths
here and in ssd16xx_crtc_atomic_enable()?

First, in ssd16xx_plane_atomic_update(), pm_runtime_resume_and_get() runs
ssd16xx_pm_runtime_resume() -> ssd16xx_hw_init() before
ssd16xx_sync_session_props() updates device->refresh_mode. If the device
resumes from runtime suspend when old_ssd->refresh_mode was
SSD16XX_REFRESH_FULL and new_ssd->refresh_mode is SSD16XX_REFRESH_FAST,
ssd16xx_hw_init() resets the controller (wiping the LUT) and sees the stale
FULL mode, while the check above also skips setting device->fast_lut_pending
because old_ssd->refresh_mode == SSD16XX_REFRESH_FULL.

Second, in ssd16xx_crtc_atomic_enable(), both pm_runtime_resume_and_get()
and ssd16xx_hw_init() run before ssd16xx_sync_session_props(), so
ssd16xx_hw_init() checks the stale device->refresh_mode rather than the new
plane state's mode (and ssd16xx_clear_display_on_init() can also run
SSD16XX_REFRESH_FAST without a pre-loaded LUT).

Third, when device->init_refresh_pending is true, device->refresh_mode is
overridden with device->refresh_mode_init after device->fast_lut_pending is
computed from old_ssd and new_ssd. If refresh_mode_init is
SSD16XX_REFRESH_FAST while new_ssd->refresh_mode is SSD16XX_REFRESH_PARTIAL,
fast_lut_pending is not armed for the init frame. Conversely, if
new_ssd->refresh_mode switches from PARTIAL to FAST while refresh_mode_init
is PARTIAL, fast_lut_pending is consumed before the PARTIAL init frame
(which loads the Mode 2 partial LUT), leaving subsequent FAST frames without
a Mode 1 LUT preload.

[ ... ]

> +static void ssd16xx_crtc_atomic_disable(struct drm_crtc *crtc,
> +					struct drm_atomic_commit *state)
> +{
> +	struct ssd16xx_device *device = crtc_to_ssd16xx_device(crtc);
> +	int ret, idx;
> +
> +	if (!drm_dev_enter(&device->drm, &idx))
> +		return;
> +
> +	mutex_lock(&device->lock);
> +
> +	if (device->clear_on_disable < 0 || device->cleared_on_disable)
> +		goto out;
> +
> +	ret = pm_runtime_resume_and_get(device->drm.dev);
> +	if (ret < 0)
> +		goto out;
> +
> +	drm_dbg(&device->drm, "clear_on_disable: running, mode=%d\n",
> +		device->clear_on_disable);
> +	ret = ssd16xx_clear_display(device,
> +				    ssd16xx_refresh_mode_to_ctrl2(device,
> +								  device->clear_on_disable));
> +	if (ret) {
> +		drm_err(&device->drm, "atomic_disable: clear failed: %d\n", ret);
> +		pm_runtime_put_autosuspend(device->drm.dev);
> +		goto out;
> +	}
> +
> +	device->cleared_on_disable = true;
> +	pm_runtime_put_sync_suspend(device->drm.dev);
> +out:
> +	mutex_unlock(&device->lock);
> +	drm_dev_exit(idx);
> +}

[ ... ]

> +static int ssd16xx_pm_suspend(struct device *dev)
> +{
> +	struct ssd16xx_device *device = dev_get_drvdata(dev);
> +	struct drm_device *drm = &device->drm;
> +	int ret;
> +
> +	/* Use level2 deep sleep for system suspend; SSD1683 loses RAM but saves more power. */
> +	device->deep_sleep_mode = device->controller_cfg->deep_sleep_mode_level2;
> +	dev_dbg(dev, "system suspend: entering deep sleep mode 0x%02x\n",
> +		device->deep_sleep_mode);
> +
> +	ret = drm_mode_config_helper_suspend(drm);
> +	if (ret)
> +		return ret;
> +
> +	/*
> +	 * If the autosuspend timer already fired the device is RPM_SUSPENDED
> +	 * at level1.  pm_runtime_force_suspend() would skip its callback, so
> +	 * level2 would never reach the hardware.
> +	 *
> +	 * SPI is unresponsive in deep sleep; HWRESET is the only exit.
> +	 * After the reset, level2 can be sent directly without a full hw_init.
> +	 *
> +	 * Skip this for controllers where level1 == level2 (e.g. SSD1677 which
> +	 * has only one deep sleep mode): the device is already in the deepest
> +	 * available state and no upgrade is needed.
> +	 */
> +	if (pm_runtime_status_suspended(dev)) {
> +		int err = 0;
> +
> +		if (device->controller_cfg->deep_sleep_mode_level1 !=
> +		    device->controller_cfg->deep_sleep_mode_level2) {
> +			dev_dbg(dev, "system suspend: upgrading level1->level2 via HWRESET\n");
> +			ssd16xx_hw_reset(device);
> +			ssd16xx_wait_for_device(device, &err);
> +			ssd16xx_send_cmd(device, SSD16XX_CMD_DEEP_SLEEP_MODE, &err);
> +			ssd16xx_send_data(device,
> +					  device->controller_cfg->deep_sleep_mode_level2,
> +					  &err);
> +		} else {
> +			dev_dbg(dev, "system suspend: already at deepest sleep mode\n");
> +		}
> +		device->initialized = false;
> +		device->pm_force_suspended = false;  /* Skip force_resume on resume */
> +		return err;
> +	}
> +
> +	/* Force runtime-suspended state for clean resume. */
> +	ret = pm_runtime_force_suspend(dev);
> +	if (ret) {
> +		drm_mode_config_helper_resume(drm);
> +		return ret;
> +	}
> +	device->pm_force_suspended = true;  /* Resume must call force_resume */
> +
> +	return 0;
> +}

[Severity: Medium]
In ssd16xx_pm_suspend(), device->deep_sleep_mode is updated to
deep_sleep_mode_level2 before calling drm_mode_config_helper_suspend(drm).
Could this cause three issues during system suspend?

1. When clear_on_disable >= 0, drm_mode_config_helper_suspend(drm) calls
ssd16xx_crtc_atomic_disable(), which clears the display and invokes
pm_runtime_put_sync_suspend(). Because device->deep_sleep_mode was already
set to level2, ssd16xx_pm_runtime_suspend() puts the panel into level2 sleep
and marks the device RPM_SUSPENDED. When control returns to
ssd16xx_pm_suspend(), pm_runtime_status_suspended(dev) is true, so it
assumes the panel was in level1 sleep and performs a second hardware reset
and level2 sleep command while skipping pm_runtime_force_suspend(dev).

2. If drm_mode_config_helper_suspend(drm) or pm_runtime_force_suspend(dev)
returns an error, ssd16xx_pm_suspend() exits without restoring
device->deep_sleep_mode to deep_sleep_mode_level1, leaving subsequent
runtime PM autosuspends using level2 sleep (0x03) and wiping controller RAM.

3. In the pm_runtime_status_suspended(dev) branch, if
ssd16xx_wait_for_device(), ssd16xx_send_cmd(), or ssd16xx_send_data() fails,
ssd16xx_pm_suspend() returns err without calling
drm_mode_config_helper_resume(drm) or restoring device->deep_sleep_mode,
leaving the DRM mode config suspended and leaking
drm->mode_config.suspend_state.

> +static int ssd16xx_pm_resume(struct device *dev)
> +{
> +	struct ssd16xx_device *device = dev_get_drvdata(dev);
> +	struct drm_device *drm = &device->drm;
> +
> +	dev_dbg(dev, "system resume: restoring state\n");
> +
> +	/* Only force_resume if we force_suspended; the level1→level2 upgrade path did not. */
> +	if (device->pm_force_suspended) {
> +		pm_runtime_force_resume(dev);
> +		device->pm_force_suspended = false;
> +	}
> +
> +	/* Restore level1 for subsequent runtime PM cycles. */
> +	device->deep_sleep_mode = device->controller_cfg->deep_sleep_mode_level1;
> +
> +	return drm_mode_config_helper_resume(drm);
> +}
> +
> +static int ssd16xx_pm_runtime_suspend(struct device *dev)
> +{
> +	struct ssd16xx_device *device = dev_get_drvdata(dev);
> +	int err = 0;
> +
> +	dev_dbg(dev, "runtime suspend: entering deep sleep mode 0x%02x\n",
> +		device->deep_sleep_mode);
> +
> +	ssd16xx_send_cmd(device, SSD16XX_CMD_DEEP_SLEEP_MODE, &err);
> +	ssd16xx_send_data(device, device->deep_sleep_mode, &err);
> +	device->initialized = false;
> +
> +	/*
> +	 * Don't propagate SPI errors: a failure here would latch runtime_error
> +	 * and block all future resumes; the next resume resets the panel anyway.
> +	 */
> +	if (err)
> +		dev_warn(dev, "failed to enter deep sleep: %d\n", err);
> +
> +	return 0;
> +}
> +
> +static int ssd16xx_pm_runtime_resume(struct device *dev)
> +{
> +	struct ssd16xx_device *device = dev_get_drvdata(dev);
> +	int ret;
> +
> +	dev_dbg(dev, "runtime resume: initialized=%d%s\n",
> +		device->initialized,
> +		!device->initialized ? " (running hw_init)" : "");
> +
> +	/*
> +	 * pm_runtime_suspend clears initialized after sending the deep sleep
> +	 * command.  If a new app opens before the suspend fires (the put was
> +	 * cancelled), initialized is still true and hw_init is unnecessary.
> +	 */
> +	if (!device->initialized) {
> +		ret = ssd16xx_hw_init(device);
> +		if (ret)
> +			return ret;
> +
> +		device->initialized = true;
> +	}
> +
> +	return 0;
> +}

[Severity: Medium]
When resuming from SSD1683_DEEP_SLEEP_MODE_2 (0x03), the controller loses
its RAM contents, including the RED RAM reference frame required by
SSD16XX_REFRESH_PARTIAL in ssd16xx_fb_dirty().

On SSD1677, deep_sleep_mode_level1 is SSD1683_DEEP_SLEEP_MODE_2 (0x03).
After runtime autosuspend fires while the CRTC remains enabled, the next
plane update calls ssd16xx_plane_atomic_update() ->
pm_runtime_resume_and_get() -> ssd16xx_pm_runtime_resume() ->
ssd16xx_hw_init() without running ssd16xx_crtc_atomic_enable(), so
init_refresh_pending remains false and ssd16xx_fb_dirty() performs a partial
refresh with ctrl1_normal against lost RED RAM.

Similarly, across system suspend and resume (which uses mode 0x03 on SSD1683
as well), if the userspace refresh_mode_init plane property is set to
disabled (-1) or partial (0) while refresh_mode is partial,
ssd16xx_pm_resume() -> drm_mode_config_helper_resume() ->
ssd16xx_crtc_atomic_enable() -> ssd16xx_plane_atomic_update() will also
execute a partial refresh with ctrl1_normal without re-establishing the RED
RAM baseline.

Should resuming from mode 0x03 sleep force a baseline full/fast refresh or
restore RED RAM before the next partial update?

[ ... ]

> -- 
> 2.34.1

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261019213726.4145532-1-devarsht@ti.com?part=14

      reply	other threads:[~2026-10-19 21:59 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-19 21:37 [PATCH v3 00/14] Add DRM driver for Solomon SSD16xx e-paper display controllers Devarsh Thakkar
2026-10-19 21:37 ` [PATCH v3 01/14] dt-bindings: vendor-prefixes: Add Dalian Good Display Co., Ltd Devarsh Thakkar
2026-10-19 21:37 ` [PATCH v3 02/14] dt-bindings: display: Add Solomon SSD16xx e-paper controller Devarsh Thakkar
2026-10-20  8:34   ` Krzysztof Kozlowski
2026-10-19 21:37 ` [PATCH v3 03/14] dt-bindings: display: solomon, ssd16xx: Add Solomon SSD1677 controller Devarsh Thakkar
2026-10-19 21:44   ` [PATCH v3 03/14] dt-bindings: display: solomon,ssd16xx: " sashiko-bot
2026-10-19 21:37 ` [PATCH v3 04/14] drm/solomon: Add DRM driver for Solomon SSD16xx e-paper display controllers Devarsh Thakkar
2026-10-19 22:02   ` sashiko-bot
2026-10-19 21:37 ` [PATCH v3 05/14] drm/solomon: ssd16xx: Add clear_on_init/close/disable session management Devarsh Thakkar
2026-10-19 21:59   ` sashiko-bot
2026-10-19 21:37 ` [PATCH v3 06/14] drm/solomon: ssd16xx: Add support for Solomon SSD1677 controller Devarsh Thakkar
2026-10-19 21:37 ` [PATCH v3 07/14] drm/solomon: ssd16xx: Add power management support Devarsh Thakkar
2026-10-19 21:57   ` sashiko-bot
2026-10-19 21:37 ` [PATCH v3 08/14] drm/solomon: ssd16xx: Expose refresh mode as plane property Devarsh Thakkar
2026-10-19 22:02   ` sashiko-bot
2026-10-19 21:37 ` [PATCH v3 09/14] drm/solomon: ssd16xx: Expose color " Devarsh Thakkar
2026-10-19 21:52   ` sashiko-bot
2026-10-19 21:37 ` [PATCH v3 10/14] drm/solomon: ssd16xx: Expose session management as plane properties Devarsh Thakkar
2026-10-19 21:58   ` sashiko-bot
2026-10-20 10:32   ` Markus Elfring
2026-10-19 21:37 ` [PATCH v3 11/14] drm/solomon: ssd16xx: support panels whose RAM X order is reversed Devarsh Thakkar
2026-10-19 21:37 ` [PATCH v3 12/14] MAINTAINERS: Add entry for Solomon SSD16xx DRM driver Devarsh Thakkar
2026-10-19 21:37 ` [PATCH v3 13/14] arm64: defconfig: Enable DRM_SSD16XX for AM62L3 EVM Devarsh Thakkar
2026-10-19 21:37 ` [PATCH v3 14/14] arm64: dts: ti: [DO_NOT_MERGE] Add AM62L3 EVM overlay for GDEY042T81 e-paper display Devarsh Thakkar
2026-10-19 21:59   ` sashiko-bot [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=sashiko-outbox-178286@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=conor+dt@kernel.org \
    --cc=devarsht@ti.com \
    --cc=devicetree@vger.kernel.org \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=robh@kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    /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