From: Roman Stratiienko <r.stratiienko@gmail.com> To: mripard@kernel.org, wens@csie.org, jernej.skrabec@gmail.com, airlied@linux.ie, daniel@ffwll.ch, samuel@sholland.org, dri-devel@lists.freedesktop.org, linux-arm-kernel@lists.infradead.org, linux-sunxi@lists.linux.dev, linux-kernel@vger.kernel.org, megi@xff.cz Cc: Roman Stratiienko <roman.o.stratiienko@globallogic.com> Subject: [PATCH] drm/sun4i: Fix blend registers corruption for DE2.0/DE3.0 Date: Tue, 24 May 2022 13:52:49 +0000 [thread overview] Message-ID: <20220524135249.49993-1-roman.o.stratiienko@globallogic.com> (raw) Corruption happens when plane zpos is updated Example scenario: Initial frame blender state: PLANE_ZPOS = {0, 1, 2, 3} BLD_ROUTE = {0, 1, 2, 0} BLD_EN = {1, 1, 1, 0} New frame commit (Only ZPOS has been changed): PLANE_ZPOS = {0->2, 1->0, 2->1, 3} Expected results after plane state update: Z0 Z1 Z2 Z3 BLD_ROUTE = {1, 2, 0, 0} BLD_EN = {1, 1, 1, 0} What is currently happening: 1. sun8i_vi_layer_enable(enabled=true, zpos=2, old_zpos=0): BLD_ROUTE = {1->0, 1, 2->0, 0} BLD_EN = {1->0, 1, 1->1, 0} 2. sun8i_ui_layer_enable(enabled=true, zpos=0, old_zpos=1): BLD_ROUTE = {0->1, 1->0, 0, 0} BLD_EN = {0->1, 1->0, 1, 0} 3. sun8i_ui_layer_enable(enabled=true, zpos=1, old_zpos=2): BLD_ROUTE = {1, 0->2, 0->0, 0} BLD_EN = {1, 0->2, 1->0, 0} After updating of all the planes we are ending up with BLD_EN[2]=0, which makes this channel disabled. To fix this issue, clear BLEND registers before updating the planes and do not clear the old state while processing every plane. Signed-off-by: Roman Stratiienko <roman.o.stratiienko@globallogic.com> --- drivers/gpu/drm/sun4i/sun8i_mixer.c | 16 +++++++++++++++ drivers/gpu/drm/sun4i/sun8i_ui_layer.c | 28 ++++---------------------- drivers/gpu/drm/sun4i/sun8i_vi_layer.c | 28 ++++---------------------- 3 files changed, 24 insertions(+), 48 deletions(-) diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c index f5e8aeaa3cdf..004377a000fc 100644 --- a/drivers/gpu/drm/sun4i/sun8i_mixer.c +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c @@ -248,6 +248,21 @@ int sun8i_mixer_drm_format_to_hw(u32 format, u32 *hw_format) return -EINVAL; } +static void sun8i_atomic_begin(struct sunxi_engine *engine, + struct drm_crtc_state *old_state) +{ + struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine); + u32 bld_base = sun8i_blender_base(mixer); + + regmap_write(engine->regs, + SUN8I_MIXER_BLEND_PIPE_CTL(bld_base), + 0); + + regmap_write(engine->regs, + SUN8I_MIXER_BLEND_ROUTE(bld_base), + 0); +} + static void sun8i_mixer_commit(struct sunxi_engine *engine) { DRM_DEBUG_DRIVER("Committing changes\n"); @@ -299,6 +314,7 @@ static struct drm_plane **sun8i_layers_init(struct drm_device *drm, } static const struct sunxi_engine_ops sun8i_engine_ops = { + .atomic_begin = sun8i_atomic_begin, .commit = sun8i_mixer_commit, .layers_init = sun8i_layers_init, }; diff --git a/drivers/gpu/drm/sun4i/sun8i_ui_layer.c b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c index 7845c2a53a7f..b294a882626a 100644 --- a/drivers/gpu/drm/sun4i/sun8i_ui_layer.c +++ b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c @@ -24,8 +24,7 @@ #include "sun8i_ui_scaler.h" static void sun8i_ui_layer_enable(struct sun8i_mixer *mixer, int channel, - int overlay, bool enable, unsigned int zpos, - unsigned int old_zpos) + int overlay, bool enable, unsigned int zpos) { u32 val, bld_base, ch_base; @@ -44,18 +43,6 @@ static void sun8i_ui_layer_enable(struct sun8i_mixer *mixer, int channel, SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch_base, overlay), SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN, val); - if (!enable || zpos != old_zpos) { - regmap_update_bits(mixer->engine.regs, - SUN8I_MIXER_BLEND_PIPE_CTL(bld_base), - SUN8I_MIXER_BLEND_PIPE_CTL_EN(old_zpos), - 0); - - regmap_update_bits(mixer->engine.regs, - SUN8I_MIXER_BLEND_ROUTE(bld_base), - SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(old_zpos), - 0); - } - if (enable) { val = SUN8I_MIXER_BLEND_PIPE_CTL_EN(zpos); @@ -291,31 +278,24 @@ static int sun8i_ui_layer_atomic_check(struct drm_plane *plane, static void sun8i_ui_layer_atomic_disable(struct drm_plane *plane, struct drm_atomic_state *state) { - struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, - plane); struct sun8i_ui_layer *layer = plane_to_sun8i_ui_layer(plane); - unsigned int old_zpos = old_state->normalized_zpos; struct sun8i_mixer *mixer = layer->mixer; - sun8i_ui_layer_enable(mixer, layer->channel, layer->overlay, false, 0, - old_zpos); + sun8i_ui_layer_enable(mixer, layer->channel, layer->overlay, false, 0); } static void sun8i_ui_layer_atomic_update(struct drm_plane *plane, struct drm_atomic_state *state) { - struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, - plane); struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane); struct sun8i_ui_layer *layer = plane_to_sun8i_ui_layer(plane); unsigned int zpos = new_state->normalized_zpos; - unsigned int old_zpos = old_state->normalized_zpos; struct sun8i_mixer *mixer = layer->mixer; if (!new_state->visible) { sun8i_ui_layer_enable(mixer, layer->channel, - layer->overlay, false, 0, old_zpos); + layer->overlay, false, 0); return; } @@ -328,7 +308,7 @@ static void sun8i_ui_layer_atomic_update(struct drm_plane *plane, sun8i_ui_layer_update_buffer(mixer, layer->channel, layer->overlay, plane); sun8i_ui_layer_enable(mixer, layer->channel, layer->overlay, - true, zpos, old_zpos); + true, zpos); } static const struct drm_plane_helper_funcs sun8i_ui_layer_helper_funcs = { diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c index bb7c43036dfa..4653244b2fd8 100644 --- a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c +++ b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c @@ -18,8 +18,7 @@ #include "sun8i_vi_scaler.h" static void sun8i_vi_layer_enable(struct sun8i_mixer *mixer, int channel, - int overlay, bool enable, unsigned int zpos, - unsigned int old_zpos) + int overlay, bool enable, unsigned int zpos) { u32 val, bld_base, ch_base; @@ -38,18 +37,6 @@ static void sun8i_vi_layer_enable(struct sun8i_mixer *mixer, int channel, SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay), SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN, val); - if (!enable || zpos != old_zpos) { - regmap_update_bits(mixer->engine.regs, - SUN8I_MIXER_BLEND_PIPE_CTL(bld_base), - SUN8I_MIXER_BLEND_PIPE_CTL_EN(old_zpos), - 0); - - regmap_update_bits(mixer->engine.regs, - SUN8I_MIXER_BLEND_ROUTE(bld_base), - SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(old_zpos), - 0); - } - if (enable) { val = SUN8I_MIXER_BLEND_PIPE_CTL_EN(zpos); @@ -395,31 +382,24 @@ static int sun8i_vi_layer_atomic_check(struct drm_plane *plane, static void sun8i_vi_layer_atomic_disable(struct drm_plane *plane, struct drm_atomic_state *state) { - struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, - plane); struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane); - unsigned int old_zpos = old_state->normalized_zpos; struct sun8i_mixer *mixer = layer->mixer; - sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay, false, 0, - old_zpos); + sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay, false, 0); } static void sun8i_vi_layer_atomic_update(struct drm_plane *plane, struct drm_atomic_state *state) { - struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, - plane); struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane); struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane); unsigned int zpos = new_state->normalized_zpos; - unsigned int old_zpos = old_state->normalized_zpos; struct sun8i_mixer *mixer = layer->mixer; if (!new_state->visible) { sun8i_vi_layer_enable(mixer, layer->channel, - layer->overlay, false, 0, old_zpos); + layer->overlay, false, 0); return; } @@ -432,7 +412,7 @@ static void sun8i_vi_layer_atomic_update(struct drm_plane *plane, sun8i_vi_layer_update_buffer(mixer, layer->channel, layer->overlay, plane); sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay, - true, zpos, old_zpos); + true, zpos); } static const struct drm_plane_helper_funcs sun8i_vi_layer_helper_funcs = { -- 2.30.2
WARNING: multiple messages have this Message-ID (diff)
From: Roman Stratiienko <r.stratiienko@gmail.com> To: mripard@kernel.org, wens@csie.org, jernej.skrabec@gmail.com, airlied@linux.ie, daniel@ffwll.ch, samuel@sholland.org, dri-devel@lists.freedesktop.org, linux-arm-kernel@lists.infradead.org, linux-sunxi@lists.linux.dev, linux-kernel@vger.kernel.org, megi@xff.cz Cc: Roman Stratiienko <roman.o.stratiienko@globallogic.com> Subject: [PATCH] drm/sun4i: Fix blend registers corruption for DE2.0/DE3.0 Date: Tue, 24 May 2022 13:52:49 +0000 [thread overview] Message-ID: <20220524135249.49993-1-roman.o.stratiienko@globallogic.com> (raw) Corruption happens when plane zpos is updated Example scenario: Initial frame blender state: PLANE_ZPOS = {0, 1, 2, 3} BLD_ROUTE = {0, 1, 2, 0} BLD_EN = {1, 1, 1, 0} New frame commit (Only ZPOS has been changed): PLANE_ZPOS = {0->2, 1->0, 2->1, 3} Expected results after plane state update: Z0 Z1 Z2 Z3 BLD_ROUTE = {1, 2, 0, 0} BLD_EN = {1, 1, 1, 0} What is currently happening: 1. sun8i_vi_layer_enable(enabled=true, zpos=2, old_zpos=0): BLD_ROUTE = {1->0, 1, 2->0, 0} BLD_EN = {1->0, 1, 1->1, 0} 2. sun8i_ui_layer_enable(enabled=true, zpos=0, old_zpos=1): BLD_ROUTE = {0->1, 1->0, 0, 0} BLD_EN = {0->1, 1->0, 1, 0} 3. sun8i_ui_layer_enable(enabled=true, zpos=1, old_zpos=2): BLD_ROUTE = {1, 0->2, 0->0, 0} BLD_EN = {1, 0->2, 1->0, 0} After updating of all the planes we are ending up with BLD_EN[2]=0, which makes this channel disabled. To fix this issue, clear BLEND registers before updating the planes and do not clear the old state while processing every plane. Signed-off-by: Roman Stratiienko <roman.o.stratiienko@globallogic.com> --- drivers/gpu/drm/sun4i/sun8i_mixer.c | 16 +++++++++++++++ drivers/gpu/drm/sun4i/sun8i_ui_layer.c | 28 ++++---------------------- drivers/gpu/drm/sun4i/sun8i_vi_layer.c | 28 ++++---------------------- 3 files changed, 24 insertions(+), 48 deletions(-) diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c index f5e8aeaa3cdf..004377a000fc 100644 --- a/drivers/gpu/drm/sun4i/sun8i_mixer.c +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c @@ -248,6 +248,21 @@ int sun8i_mixer_drm_format_to_hw(u32 format, u32 *hw_format) return -EINVAL; } +static void sun8i_atomic_begin(struct sunxi_engine *engine, + struct drm_crtc_state *old_state) +{ + struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine); + u32 bld_base = sun8i_blender_base(mixer); + + regmap_write(engine->regs, + SUN8I_MIXER_BLEND_PIPE_CTL(bld_base), + 0); + + regmap_write(engine->regs, + SUN8I_MIXER_BLEND_ROUTE(bld_base), + 0); +} + static void sun8i_mixer_commit(struct sunxi_engine *engine) { DRM_DEBUG_DRIVER("Committing changes\n"); @@ -299,6 +314,7 @@ static struct drm_plane **sun8i_layers_init(struct drm_device *drm, } static const struct sunxi_engine_ops sun8i_engine_ops = { + .atomic_begin = sun8i_atomic_begin, .commit = sun8i_mixer_commit, .layers_init = sun8i_layers_init, }; diff --git a/drivers/gpu/drm/sun4i/sun8i_ui_layer.c b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c index 7845c2a53a7f..b294a882626a 100644 --- a/drivers/gpu/drm/sun4i/sun8i_ui_layer.c +++ b/drivers/gpu/drm/sun4i/sun8i_ui_layer.c @@ -24,8 +24,7 @@ #include "sun8i_ui_scaler.h" static void sun8i_ui_layer_enable(struct sun8i_mixer *mixer, int channel, - int overlay, bool enable, unsigned int zpos, - unsigned int old_zpos) + int overlay, bool enable, unsigned int zpos) { u32 val, bld_base, ch_base; @@ -44,18 +43,6 @@ static void sun8i_ui_layer_enable(struct sun8i_mixer *mixer, int channel, SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch_base, overlay), SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN, val); - if (!enable || zpos != old_zpos) { - regmap_update_bits(mixer->engine.regs, - SUN8I_MIXER_BLEND_PIPE_CTL(bld_base), - SUN8I_MIXER_BLEND_PIPE_CTL_EN(old_zpos), - 0); - - regmap_update_bits(mixer->engine.regs, - SUN8I_MIXER_BLEND_ROUTE(bld_base), - SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(old_zpos), - 0); - } - if (enable) { val = SUN8I_MIXER_BLEND_PIPE_CTL_EN(zpos); @@ -291,31 +278,24 @@ static int sun8i_ui_layer_atomic_check(struct drm_plane *plane, static void sun8i_ui_layer_atomic_disable(struct drm_plane *plane, struct drm_atomic_state *state) { - struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, - plane); struct sun8i_ui_layer *layer = plane_to_sun8i_ui_layer(plane); - unsigned int old_zpos = old_state->normalized_zpos; struct sun8i_mixer *mixer = layer->mixer; - sun8i_ui_layer_enable(mixer, layer->channel, layer->overlay, false, 0, - old_zpos); + sun8i_ui_layer_enable(mixer, layer->channel, layer->overlay, false, 0); } static void sun8i_ui_layer_atomic_update(struct drm_plane *plane, struct drm_atomic_state *state) { - struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, - plane); struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane); struct sun8i_ui_layer *layer = plane_to_sun8i_ui_layer(plane); unsigned int zpos = new_state->normalized_zpos; - unsigned int old_zpos = old_state->normalized_zpos; struct sun8i_mixer *mixer = layer->mixer; if (!new_state->visible) { sun8i_ui_layer_enable(mixer, layer->channel, - layer->overlay, false, 0, old_zpos); + layer->overlay, false, 0); return; } @@ -328,7 +308,7 @@ static void sun8i_ui_layer_atomic_update(struct drm_plane *plane, sun8i_ui_layer_update_buffer(mixer, layer->channel, layer->overlay, plane); sun8i_ui_layer_enable(mixer, layer->channel, layer->overlay, - true, zpos, old_zpos); + true, zpos); } static const struct drm_plane_helper_funcs sun8i_ui_layer_helper_funcs = { diff --git a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c index bb7c43036dfa..4653244b2fd8 100644 --- a/drivers/gpu/drm/sun4i/sun8i_vi_layer.c +++ b/drivers/gpu/drm/sun4i/sun8i_vi_layer.c @@ -18,8 +18,7 @@ #include "sun8i_vi_scaler.h" static void sun8i_vi_layer_enable(struct sun8i_mixer *mixer, int channel, - int overlay, bool enable, unsigned int zpos, - unsigned int old_zpos) + int overlay, bool enable, unsigned int zpos) { u32 val, bld_base, ch_base; @@ -38,18 +37,6 @@ static void sun8i_vi_layer_enable(struct sun8i_mixer *mixer, int channel, SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay), SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN, val); - if (!enable || zpos != old_zpos) { - regmap_update_bits(mixer->engine.regs, - SUN8I_MIXER_BLEND_PIPE_CTL(bld_base), - SUN8I_MIXER_BLEND_PIPE_CTL_EN(old_zpos), - 0); - - regmap_update_bits(mixer->engine.regs, - SUN8I_MIXER_BLEND_ROUTE(bld_base), - SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(old_zpos), - 0); - } - if (enable) { val = SUN8I_MIXER_BLEND_PIPE_CTL_EN(zpos); @@ -395,31 +382,24 @@ static int sun8i_vi_layer_atomic_check(struct drm_plane *plane, static void sun8i_vi_layer_atomic_disable(struct drm_plane *plane, struct drm_atomic_state *state) { - struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, - plane); struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane); - unsigned int old_zpos = old_state->normalized_zpos; struct sun8i_mixer *mixer = layer->mixer; - sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay, false, 0, - old_zpos); + sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay, false, 0); } static void sun8i_vi_layer_atomic_update(struct drm_plane *plane, struct drm_atomic_state *state) { - struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, - plane); struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane); struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane); unsigned int zpos = new_state->normalized_zpos; - unsigned int old_zpos = old_state->normalized_zpos; struct sun8i_mixer *mixer = layer->mixer; if (!new_state->visible) { sun8i_vi_layer_enable(mixer, layer->channel, - layer->overlay, false, 0, old_zpos); + layer->overlay, false, 0); return; } @@ -432,7 +412,7 @@ static void sun8i_vi_layer_atomic_update(struct drm_plane *plane, sun8i_vi_layer_update_buffer(mixer, layer->channel, layer->overlay, plane); sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay, - true, zpos, old_zpos); + true, zpos); } static const struct drm_plane_helper_funcs sun8i_vi_layer_helper_funcs = { -- 2.30.2 _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel@lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
next reply other threads:[~2022-05-24 13:52 UTC|newest] Thread overview: 30+ messages / expand[flat|nested] mbox.gz Atom feed top 2022-05-24 13:52 Roman Stratiienko [this message] 2022-05-24 13:52 ` [PATCH] drm/sun4i: Fix blend registers corruption for DE2.0/DE3.0 Roman Stratiienko 2022-05-24 15:31 ` Roman Stratiienko 2022-05-24 15:31 ` Roman Stratiienko 2022-05-24 15:36 ` Jernej Škrabec 2022-05-24 15:36 ` Jernej Škrabec 2022-05-24 17:10 ` Roman Stratiienko 2022-05-24 17:10 ` Roman Stratiienko 2022-05-24 17:10 ` Roman Stratiienko 2022-05-24 17:14 ` Roman Stratiienko 2022-05-24 17:14 ` Roman Stratiienko 2022-05-24 17:14 ` Roman Stratiienko 2022-05-24 19:14 ` Jernej Škrabec 2022-05-24 19:14 ` Jernej Škrabec 2022-05-24 19:14 ` Jernej Škrabec 2022-05-25 14:55 ` Roman Stratiienko 2022-05-25 14:55 ` Roman Stratiienko 2022-05-25 14:55 ` Roman Stratiienko 2022-05-25 15:14 ` Jernej Škrabec 2022-05-25 15:14 ` Jernej Škrabec 2022-05-25 15:14 ` Jernej Škrabec 2022-05-25 15:17 ` Roman Stratiienko 2022-05-25 15:17 ` Roman Stratiienko 2022-05-25 15:17 ` Roman Stratiienko 2022-05-24 19:13 ` Jernej Škrabec 2022-05-24 19:13 ` Jernej Škrabec 2022-05-24 19:13 ` Jernej Škrabec 2022-05-25 13:27 ` Roman Stratiienko 2022-05-25 13:27 ` Roman Stratiienko 2022-05-25 13:27 ` Roman Stratiienko
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=20220524135249.49993-1-roman.o.stratiienko@globallogic.com \ --to=r.stratiienko@gmail.com \ --cc=airlied@linux.ie \ --cc=daniel@ffwll.ch \ --cc=dri-devel@lists.freedesktop.org \ --cc=jernej.skrabec@gmail.com \ --cc=linux-arm-kernel@lists.infradead.org \ --cc=linux-kernel@vger.kernel.org \ --cc=linux-sunxi@lists.linux.dev \ --cc=megi@xff.cz \ --cc=mripard@kernel.org \ --cc=roman.o.stratiienko@globallogic.com \ --cc=samuel@sholland.org \ --cc=wens@csie.org \ /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: linkBe 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.