From: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
To: Maxime Ripard <mripard@kernel.org>
Cc: Andrzej Hajda <andrzej.hajda@intel.com>,
Neil Armstrong <neil.armstrong@linaro.org>,
Robert Foss <rfoss@kernel.org>,
Maarten Lankhorst <maarten.lankhorst@linux.intel.com>,
Thomas Zimmermann <tzimmermann@suse.de>,
David Airlie <airlied@gmail.com>, Simona Vetter <simona@ffwll.ch>,
Jonas Karlman <jonas@kwiboo.se>,
Jernej Skrabec <jernej.skrabec@gmail.com>,
Luca Ceresoli <luca.ceresoli@bootlin.com>,
dri-devel@lists.freedesktop.org
Subject: Re: [PATCH 37/37] drm/bridge: Remove legacy bridge callback support
Date: Mon, 22 Jun 2026 14:43:14 +0300 [thread overview]
Message-ID: <20260622114314.GG3872967@killaraus.ideasonboard.com> (raw)
In-Reply-To: <20260617-drm-all-atomic-bridges-v1-37-b63e6316166b@kernel.org>
Hi Maxime,
Thank you for the patch.
On Wed, Jun 17, 2026 at 12:15:02PM +0200, Maxime Ripard wrote:
> All bridge drivers have been converted to the atomic variants of the
> enable, disable, pre_enable, and post_disable callbacks.
>
> Remove the deprecated legacy hooks from drm_bridge_funcs, the
> drm_bridge_is_atomic() helper that was only needed to distinguish
> between atomic and non-atomic bridges, and the legacy bridge test
> cases.
>
> Since all bridges are now atomic, unconditionally initialize the
> private object state at attach time.
>
> Signed-off-by: Maxime Ripard <mripard@kernel.org>
> ---
> drivers/gpu/drm/drm_bridge.c | 27 ++-------
> drivers/gpu/drm/tests/drm_bridge_test.c | 104 --------------------------------
> include/drm/drm_bridge.h | 103 -------------------------------
> 3 files changed, 5 insertions(+), 229 deletions(-)
>
> diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
> index 0cd93f966998..01101c900a39 100644
> --- a/drivers/gpu/drm/drm_bridge.c
> +++ b/drivers/gpu/drm/drm_bridge.c
> @@ -513,15 +513,10 @@ static const struct drm_private_state_funcs drm_bridge_priv_state_funcs = {
> .atomic_create_state = drm_bridge_atomic_create_priv_state,
> .atomic_duplicate_state = drm_bridge_atomic_duplicate_priv_state,
> .atomic_destroy_state = drm_bridge_atomic_destroy_priv_state,
> };
>
> -static bool drm_bridge_is_atomic(struct drm_bridge *bridge)
> -{
> - return bridge->funcs->atomic_create_state != NULL;
> -}
> -
> /**
> * drm_bridge_attach - attach the bridge to an encoder's chain
> *
> * @encoder: DRM encoder
> * @bridge: bridge to attach
> @@ -587,13 +582,12 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
> ret = bridge->funcs->attach(bridge, encoder, flags);
> if (ret < 0)
> goto err_reset_bridge;
> }
>
> - if (drm_bridge_is_atomic(bridge))
> - drm_atomic_private_obj_init(bridge->dev, &bridge->base,
> - &drm_bridge_priv_state_funcs);
> + drm_atomic_private_obj_init(bridge->dev, &bridge->base,
> + &drm_bridge_priv_state_funcs);
>
> return 0;
>
> err_reset_bridge:
> bridge->dev = NULL;
> @@ -622,12 +616,11 @@ void drm_bridge_detach(struct drm_bridge *bridge)
> return;
>
> if (WARN_ON(!bridge->dev))
> return;
>
> - if (drm_bridge_is_atomic(bridge))
> - drm_atomic_private_obj_fini(&bridge->base);
> + drm_atomic_private_obj_fini(&bridge->base);
>
> if (bridge->funcs->detach)
> bridge->funcs->detach(bridge);
>
> list_del(&bridge->chain_node);
> @@ -810,15 +803,12 @@ void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge,
> return;
>
> encoder = bridge->encoder;
> mutex_lock(&encoder->bridge_chain_mutex);
> list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) {
> - if (iter->funcs->atomic_disable) {
> + if (iter->funcs->atomic_disable)
> iter->funcs->atomic_disable(iter, state);
> - } else if (iter->funcs->disable) {
> - iter->funcs->disable(iter);
> - }
>
> if (iter == bridge)
> break;
> }
> mutex_unlock(&encoder->bridge_chain_mutex);
> @@ -828,12 +818,10 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_disable);
> static void drm_atomic_bridge_call_post_disable(struct drm_bridge *bridge,
> struct drm_atomic_commit *state)
> {
> if (state && bridge->funcs->atomic_post_disable)
> bridge->funcs->atomic_post_disable(bridge, state);
> - else if (bridge->funcs->post_disable)
> - bridge->funcs->post_disable(bridge);
> }
>
> /**
> * drm_atomic_bridge_chain_post_disable - cleans up after disabling all bridges
> * in the encoder chain
> @@ -925,12 +913,10 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable);
> static void drm_atomic_bridge_call_pre_enable(struct drm_bridge *bridge,
> struct drm_atomic_commit *state)
> {
> if (state && bridge->funcs->atomic_pre_enable)
> bridge->funcs->atomic_pre_enable(bridge, state);
> - else if (bridge->funcs->pre_enable)
> - bridge->funcs->pre_enable(bridge);
> }
>
> /**
> * drm_atomic_bridge_chain_pre_enable - prepares for enabling all bridges in
> * the encoder chain
> @@ -1029,15 +1015,12 @@ void drm_atomic_bridge_chain_enable(struct drm_bridge *first_bridge,
There's a mention of the .disable() callback in the documentation of
this function:
* Calls &drm_bridge_funcs.atomic_disable (falls back on
* &drm_bridge_funcs.disable) op for all the bridges in the encoder chain,
Same for the three other functions in the same family. With that
removed,
Reviewed-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
> {
> if (!first_bridge)
> return;
>
> drm_for_each_bridge_in_chain_from(first_bridge, bridge)
> - if (bridge->funcs->atomic_enable) {
> + if (bridge->funcs->atomic_enable)
> bridge->funcs->atomic_enable(bridge, state);
> - } else if (bridge->funcs->enable) {
> - bridge->funcs->enable(bridge);
> - }
> }
> EXPORT_SYMBOL(drm_atomic_bridge_chain_enable);
>
> static int drm_atomic_bridge_check(struct drm_bridge *bridge,
> struct drm_crtc_state *crtc_state,
> diff --git a/drivers/gpu/drm/tests/drm_bridge_test.c b/drivers/gpu/drm/tests/drm_bridge_test.c
> index 3f0269e52432..99428fc03f29 100644
> --- a/drivers/gpu/drm/tests/drm_bridge_test.c
> +++ b/drivers/gpu/drm/tests/drm_bridge_test.c
> @@ -48,30 +48,10 @@ static void drm_test_bridge_priv_destroy(struct drm_bridge *bridge)
> struct drm_bridge_init_priv *priv = (struct drm_bridge_init_priv *)bridge_priv->data;
>
> priv->destroyed = true;
> }
>
> -static void drm_test_bridge_enable(struct drm_bridge *bridge)
> -{
> - struct drm_bridge_priv *priv = bridge_to_priv(bridge);
> -
> - priv->enable_count++;
> -}
> -
> -static void drm_test_bridge_disable(struct drm_bridge *bridge)
> -{
> - struct drm_bridge_priv *priv = bridge_to_priv(bridge);
> -
> - priv->disable_count++;
> -}
> -
> -static const struct drm_bridge_funcs drm_test_bridge_legacy_funcs = {
> - .destroy = drm_test_bridge_priv_destroy,
> - .enable = drm_test_bridge_enable,
> - .disable = drm_test_bridge_disable,
> -};
> -
> static void drm_test_bridge_atomic_enable(struct drm_bridge *bridge,
> struct drm_atomic_commit *state)
> {
> struct drm_bridge_priv *priv = bridge_to_priv(bridge);
>
> @@ -234,39 +214,12 @@ static void drm_test_drm_bridge_get_current_state_atomic(struct kunit *test)
>
> drm_modeset_drop_locks(&ctx);
> drm_modeset_acquire_fini(&ctx);
> }
>
> -/*
> - * Test that drm_bridge_get_current_state() returns NULL for a
> - * non-atomic bridge.
> - */
> -static void drm_test_drm_bridge_get_current_state_legacy(struct kunit *test)
> -{
> - struct drm_bridge_init_priv *priv;
> - struct drm_bridge *bridge;
> -
> - priv = drm_test_bridge_init(test, &drm_test_bridge_legacy_funcs);
> - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
> -
> - /*
> - * NOTE: Strictly speaking, we should take the bridge->base.lock
> - * before calling that function. However, bridge->base is only
> - * initialized if the bridge is atomic, while we explicitly
> - * initialize one that isn't there.
> - *
> - * In order to avoid unnecessary warnings, let's skip the
> - * locking. The function would return NULL in all cases anyway,
> - * so we don't really have any concurrency to worry about.
> - */
> - bridge = &priv->test_bridge->bridge;
> - KUNIT_EXPECT_NULL(test, drm_bridge_get_current_state(bridge));
> -}
> -
> static struct kunit_case drm_bridge_get_current_state_tests[] = {
> KUNIT_CASE(drm_test_drm_bridge_get_current_state_atomic),
> - KUNIT_CASE(drm_test_drm_bridge_get_current_state_legacy),
> { }
> };
>
>
> static struct kunit_suite drm_bridge_get_current_state_test_suite = {
> @@ -367,70 +320,13 @@ static void drm_test_drm_bridge_helper_reset_crtc_atomic_disabled(struct kunit *
>
> KUNIT_EXPECT_EQ(test, bridge_priv->enable_count, 0);
> KUNIT_EXPECT_EQ(test, bridge_priv->disable_count, 0);
> }
>
> -/*
> - * Test that a non-atomic bridge is properly power-cycled when calling
> - * drm_bridge_helper_reset_crtc().
> - */
> -static void drm_test_drm_bridge_helper_reset_crtc_legacy(struct kunit *test)
> -{
> - struct drm_modeset_acquire_ctx ctx;
> - struct drm_bridge_init_priv *priv;
> - struct drm_display_mode *mode;
> - struct drm_bridge_priv *bridge_priv;
> - int ret;
> -
> - priv = drm_test_bridge_init(test, &drm_test_bridge_legacy_funcs);
> - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
> -
> - mode = drm_kunit_display_mode_from_cea_vic(test, &priv->drm, 16);
> - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, mode);
> -
> - drm_modeset_acquire_init(&ctx, 0);
> -
> -retry_commit:
> - ret = drm_kunit_helper_enable_crtc_connector(test,
> - &priv->drm, priv->crtc,
> - priv->connector,
> - mode,
> - &ctx);
> - if (ret == -EDEADLK) {
> - drm_modeset_backoff(&ctx);
> - goto retry_commit;
> - }
> - KUNIT_ASSERT_EQ(test, ret, 0);
> -
> - drm_modeset_drop_locks(&ctx);
> - drm_modeset_acquire_fini(&ctx);
> -
> - bridge_priv = priv->test_bridge;
> - KUNIT_ASSERT_EQ(test, bridge_priv->enable_count, 1);
> - KUNIT_ASSERT_EQ(test, bridge_priv->disable_count, 0);
> -
> - drm_modeset_acquire_init(&ctx, 0);
> -
> -retry_reset:
> - ret = drm_bridge_helper_reset_crtc(&bridge_priv->bridge, &ctx);
> - if (ret == -EDEADLK) {
> - drm_modeset_backoff(&ctx);
> - goto retry_reset;
> - }
> - KUNIT_ASSERT_EQ(test, ret, 0);
> -
> - drm_modeset_drop_locks(&ctx);
> - drm_modeset_acquire_fini(&ctx);
> -
> - KUNIT_EXPECT_EQ(test, bridge_priv->enable_count, 2);
> - KUNIT_EXPECT_EQ(test, bridge_priv->disable_count, 1);
> -}
> -
> static struct kunit_case drm_bridge_helper_reset_crtc_tests[] = {
> KUNIT_CASE(drm_test_drm_bridge_helper_reset_crtc_atomic),
> KUNIT_CASE(drm_test_drm_bridge_helper_reset_crtc_atomic_disabled),
> - KUNIT_CASE(drm_test_drm_bridge_helper_reset_crtc_legacy),
> { }
> };
>
> static struct kunit_suite drm_bridge_helper_reset_crtc_test_suite = {
> .name = "drm_test_bridge_helper_reset_crtc",
> diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
> index 18f3db367dc1..123edad5fa65 100644
> --- a/include/drm/drm_bridge.h
> +++ b/include/drm/drm_bridge.h
> @@ -171,56 +171,10 @@ struct drm_bridge_funcs {
> * operation should be rejected.
> */
> bool (*mode_fixup)(struct drm_bridge *bridge,
> const struct drm_display_mode *mode,
> struct drm_display_mode *adjusted_mode);
> - /**
> - * @disable:
> - *
> - * This callback should disable the bridge. It is called right before
> - * the preceding element in the display pipe is disabled. If the
> - * preceding element is a bridge this means it's called before that
> - * bridge's @disable vfunc. If the preceding element is a &drm_encoder
> - * it's called right before the &drm_encoder_helper_funcs.disable,
> - * &drm_encoder_helper_funcs.prepare or &drm_encoder_helper_funcs.dpms
> - * hook.
> - *
> - * The bridge can assume that the display pipe (i.e. clocks and timing
> - * signals) feeding it is still running when this callback is called.
> - *
> - * The @disable callback is optional.
> - *
> - * NOTE:
> - *
> - * This is deprecated, do not use!
> - * New drivers shall use &drm_bridge_funcs.atomic_disable.
> - */
> - void (*disable)(struct drm_bridge *bridge);
> -
> - /**
> - * @post_disable:
> - *
> - * This callback should disable the bridge. It is called right after the
> - * preceding element in the display pipe is disabled. If the preceding
> - * element is a bridge this means it's called after that bridge's
> - * @post_disable function. If the preceding element is a &drm_encoder
> - * it's called right after the encoder's
> - * &drm_encoder_helper_funcs.disable, &drm_encoder_helper_funcs.prepare
> - * or &drm_encoder_helper_funcs.dpms hook.
> - *
> - * The bridge must assume that the display pipe (i.e. clocks and timing
> - * signals) feeding it is no longer running when this callback is
> - * called.
> - *
> - * The @post_disable callback is optional.
> - *
> - * NOTE:
> - *
> - * This is deprecated, do not use!
> - * New drivers shall use &drm_bridge_funcs.atomic_post_disable.
> - */
> - void (*post_disable)(struct drm_bridge *bridge);
>
> /**
> * @mode_set:
> *
> * This callback should set the given mode on the bridge. It is called
> @@ -247,59 +201,10 @@ struct drm_bridge_funcs {
> * &drm_bridge_funcs.atomic_enable operation.
> */
> void (*mode_set)(struct drm_bridge *bridge,
> const struct drm_display_mode *mode,
> const struct drm_display_mode *adjusted_mode);
> - /**
> - * @pre_enable:
> - *
> - * This callback should enable the bridge. It is called right before
> - * the preceding element in the display pipe is enabled. If the
> - * preceding element is a bridge this means it's called before that
> - * bridge's @pre_enable function. If the preceding element is a
> - * &drm_encoder it's called right before the encoder's
> - * &drm_encoder_helper_funcs.enable, &drm_encoder_helper_funcs.commit or
> - * &drm_encoder_helper_funcs.dpms hook.
> - *
> - * The display pipe (i.e. clocks and timing signals) feeding this bridge
> - * will not yet be running when this callback is called. The bridge must
> - * not enable the display link feeding the next bridge in the chain (if
> - * there is one) when this callback is called.
> - *
> - * The @pre_enable callback is optional.
> - *
> - * NOTE:
> - *
> - * This is deprecated, do not use!
> - * New drivers shall use &drm_bridge_funcs.atomic_pre_enable.
> - */
> - void (*pre_enable)(struct drm_bridge *bridge);
> -
> - /**
> - * @enable:
> - *
> - * This callback should enable the bridge. It is called right after
> - * the preceding element in the display pipe is enabled. If the
> - * preceding element is a bridge this means it's called after that
> - * bridge's @enable function. If the preceding element is a
> - * &drm_encoder it's called right after the encoder's
> - * &drm_encoder_helper_funcs.enable, &drm_encoder_helper_funcs.commit or
> - * &drm_encoder_helper_funcs.dpms hook.
> - *
> - * The bridge can assume that the display pipe (i.e. clocks and timing
> - * signals) feeding it is running when this callback is called. This
> - * callback must enable the display link feeding the next bridge in the
> - * chain if there is one.
> - *
> - * The @enable callback is optional.
> - *
> - * NOTE:
> - *
> - * This is deprecated, do not use!
> - * New drivers shall use &drm_bridge_funcs.atomic_enable.
> - */
> - void (*enable)(struct drm_bridge *bridge);
>
> /**
> * @atomic_pre_enable:
> *
> * This callback should enable the bridge. It is called right before
> @@ -1357,18 +1262,10 @@ static inline struct drm_bridge_state *
> drm_bridge_get_current_state(struct drm_bridge *bridge)
> {
> if (!bridge)
> return NULL;
>
> - /*
> - * Only atomic bridges will have bridge->base initialized by
> - * drm_atomic_private_obj_init(), so we need to make sure we're
> - * working with one before we try to use the lock.
> - */
> - if (!bridge->funcs || !bridge->funcs->atomic_create_state)
> - return NULL;
> -
> drm_modeset_lock_assert_held(&bridge->base.lock);
>
> if (!bridge->base.state)
> return NULL;
>
--
Regards,
Laurent Pinchart
prev parent reply other threads:[~2026-06-22 11:43 UTC|newest]
Thread overview: 47+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-17 10:14 [PATCH 00/37] drm/bridge: Convert all bridges to atomic Maxime Ripard
2026-06-17 10:14 ` [PATCH 01/37] drm/adp: mipi: Switch to atomic bridge callbacks Maxime Ripard
2026-06-18 1:13 ` Joshua Peisach
2026-06-17 10:14 ` [PATCH 02/37] drm/bridge: analogix-anx6345: " Maxime Ripard
2026-06-17 10:14 ` [PATCH 03/37] drm/bridge: analogix-anx78xx: " Maxime Ripard
2026-06-17 10:14 ` [PATCH 04/37] drm/bridge: aux-bridge: " Maxime Ripard
2026-06-17 10:14 ` [PATCH 05/37] drm/bridge: aux-hpd-bridge: " Maxime Ripard
2026-06-17 10:14 ` [PATCH 06/37] drm/bridge: chrontel-ch7033: " Maxime Ripard
2026-06-17 10:14 ` [PATCH 07/37] drm/bridge: cros-ec-anx7688: " Maxime Ripard
2026-06-17 10:14 ` [PATCH 08/37] drm/bridge: lontium-lt8713sx: " Maxime Ripard
2026-06-17 10:14 ` [PATCH 09/37] drm/bridge: lontium-lt8912b: " Maxime Ripard
2026-06-22 11:34 ` Francesco Dolcini
2026-06-17 10:14 ` [PATCH 10/37] drm/bridge: lontium-lt9611uxc: " Maxime Ripard
2026-06-17 10:14 ` [PATCH 11/37] drm/bridge: lvds-codec: " Maxime Ripard
2026-06-22 11:31 ` Laurent Pinchart
2026-06-17 10:14 ` [PATCH 12/37] drm/bridge: megachips-stdpxxxx-ge-b850v3-fw: " Maxime Ripard
2026-06-17 10:14 ` [PATCH 13/37] drm/bridge: microchip-lvds: " Maxime Ripard
2026-06-22 9:00 ` Manikandan.M
2026-06-17 10:14 ` [PATCH 14/37] drm/bridge: nxp-ptn3460: " Maxime Ripard
2026-06-17 10:14 ` [PATCH 15/37] drm/bridge: of-display-mode-bridge: " Maxime Ripard
2026-06-22 11:33 ` Laurent Pinchart
2026-06-17 10:14 ` [PATCH 16/37] drm/bridge: parade-ps8622: " Maxime Ripard
2026-06-17 10:14 ` [PATCH 17/37] drm/bridge: sii9234: " Maxime Ripard
2026-06-17 10:14 ` [PATCH 18/37] drm/bridge: sil-sii8620: " Maxime Ripard
2026-06-17 10:14 ` [PATCH 19/37] drm/bridge: simple-bridge: " Maxime Ripard
2026-06-22 11:34 ` Laurent Pinchart
2026-06-17 10:14 ` [PATCH 20/37] drm/bridge: tc358764: " Maxime Ripard
2026-06-17 10:14 ` [PATCH 21/37] drm/bridge: tda998x: " Maxime Ripard
2026-06-17 10:14 ` [PATCH 22/37] drm/bridge: ti-tfp410: " Maxime Ripard
2026-06-22 11:38 ` Laurent Pinchart
2026-06-17 10:14 ` [PATCH 23/37] drm/bridge: ti-tpd12s015: " Maxime Ripard
2026-06-17 10:14 ` [PATCH 24/37] drm/bridge: thc63lvd1024: " Maxime Ripard
2026-06-17 10:14 ` [PATCH 25/37] drm/bridge: waveshare-dsi: " Maxime Ripard
2026-06-17 10:14 ` [PATCH 26/37] drm/exynos: mic: " Maxime Ripard
2026-06-17 10:14 ` [PATCH 27/37] drm/mcde: dsi: " Maxime Ripard
2026-06-17 11:58 ` Linus Walleij
2026-06-17 10:14 ` [PATCH 28/37] drm/mediatek: dpi: " Maxime Ripard
2026-06-17 10:14 ` [PATCH 29/37] drm/msm: dsi: " Maxime Ripard
2026-06-17 10:14 ` [PATCH 30/37] drm/omap: dpi: " Maxime Ripard
2026-06-17 10:14 ` [PATCH 31/37] drm/omap: dsi: " Maxime Ripard
2026-06-17 10:14 ` [PATCH 32/37] drm/omap: sdi: " Maxime Ripard
2026-06-17 10:14 ` [PATCH 33/37] drm/omap: venc: " Maxime Ripard
2026-06-17 10:14 ` [PATCH 34/37] drm/sti: dvo: " Maxime Ripard
2026-06-17 10:15 ` [PATCH 35/37] drm/sti: hda: " Maxime Ripard
2026-06-17 10:15 ` [PATCH 36/37] drm/sti: hdmi: " Maxime Ripard
2026-06-17 10:15 ` [PATCH 37/37] drm/bridge: Remove legacy bridge callback support Maxime Ripard
2026-06-22 11:43 ` Laurent Pinchart [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=20260622114314.GG3872967@killaraus.ideasonboard.com \
--to=laurent.pinchart@ideasonboard.com \
--cc=airlied@gmail.com \
--cc=andrzej.hajda@intel.com \
--cc=dri-devel@lists.freedesktop.org \
--cc=jernej.skrabec@gmail.com \
--cc=jonas@kwiboo.se \
--cc=luca.ceresoli@bootlin.com \
--cc=maarten.lankhorst@linux.intel.com \
--cc=mripard@kernel.org \
--cc=neil.armstrong@linaro.org \
--cc=rfoss@kernel.org \
--cc=simona@ffwll.ch \
--cc=tzimmermann@suse.de \
/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.