All of lore.kernel.org
 help / color / mirror / Atom feed
From: <gregkh@linuxfoundation.org>
To: niederp@physik.uni-kl.de, broonie@kernel.org
Cc: <stable@vger.kernel.org>
Subject: FAILED: patch "[PATCH] ASoC: sta32x: Convert to direct regmap API usage." failed to apply to 4.4-stable tree
Date: Sun, 11 Nov 2018 06:21:27 -0800	[thread overview]
Message-ID: <1541946087214229@kroah.com> (raw)


The patch below does not apply to the 4.4-stable tree.
If someone wants it applied there, or to any other stable or longterm
tree, then please email the backport, including the original git commit
id to <stable@vger.kernel.org>.

thanks,

greg k-h

------------------ original commit in Linus's tree ------------------

>From a1be4cead9b9504aa6fc93b624975601cec8c188 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Thomas=20Niederpr=C3=BCm?= <niederp@physik.uni-kl.de>
Date: Thu, 22 Jan 2015 00:01:53 +0100
Subject: [PATCH] ASoC: sta32x: Convert to direct regmap API usage.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

use the regmap API directly rather than relying on the snd_soc_read/write
functions as this seems to be in accordance with common practice.

Signed-off-by: Thomas Niederprüm <niederp@physik.uni-kl.de>
Signed-off-by: Mark Brown <broonie@kernel.org>

diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 8349f982a586..27e1e3b59649 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -581,6 +581,7 @@ config SND_SOC_SSM4567
 
 config SND_SOC_STA32X
 	tristate
+	select REGMAP_I2C
 
 config SND_SOC_STA350
 	tristate "STA350 speaker amplifier"
diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c
index 7e18200dd6a9..4517453b33b6 100644
--- a/sound/soc/codecs/sta32x.c
+++ b/sound/soc/codecs/sta32x.c
@@ -102,6 +102,35 @@ static const struct reg_default sta32x_regs[] = {
 	{ 0x2c, 0x0c },
 };
 
+static const struct regmap_range sta32x_write_regs_range[] = {
+	regmap_reg_range(STA32X_CONFA,  STA32X_AUTO2),
+	regmap_reg_range(STA32X_C1CFG,  STA32X_FDRC2),
+};
+
+static const struct regmap_range sta32x_read_regs_range[] = {
+	regmap_reg_range(STA32X_CONFA,  STA32X_AUTO2),
+	regmap_reg_range(STA32X_C1CFG,  STA32X_FDRC2),
+};
+
+static const struct regmap_range sta32x_volatile_regs_range[] = {
+	regmap_reg_range(STA32X_CFADDR2, STA32X_CFUD),
+};
+
+static const struct regmap_access_table sta32x_write_regs = {
+	.yes_ranges =	sta32x_write_regs_range,
+	.n_yes_ranges =	ARRAY_SIZE(sta32x_write_regs_range),
+};
+
+static const struct regmap_access_table sta32x_read_regs = {
+	.yes_ranges =	sta32x_read_regs_range,
+	.n_yes_ranges =	ARRAY_SIZE(sta32x_read_regs_range),
+};
+
+static const struct regmap_access_table sta32x_volatile_regs = {
+	.yes_ranges =	sta32x_volatile_regs_range,
+	.n_yes_ranges =	ARRAY_SIZE(sta32x_volatile_regs_range),
+};
+
 /* regulator power supply names */
 static const char *sta32x_supply_names[] = {
 	"Vdda",	/* analog supply, 3.3VV */
@@ -122,6 +151,7 @@ struct sta32x_priv {
 	u32 coef_shadow[STA32X_COEF_COUNT];
 	struct delayed_work watchdog_work;
 	int shutdown;
+	struct mutex coeff_lock;
 };
 
 static const DECLARE_TLV_DB_SCALE(mvol_tlv, -12700, 50, 1);
@@ -244,29 +274,42 @@ static int sta32x_coefficient_get(struct snd_kcontrol *kcontrol,
 				  struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
 	int numcoef = kcontrol->private_value >> 16;
 	int index = kcontrol->private_value & 0xffff;
-	unsigned int cfud;
-	int i;
+	unsigned int cfud, val;
+	int i, ret = 0;
+
+	mutex_lock(&sta32x->coeff_lock);
 
 	/* preserve reserved bits in STA32X_CFUD */
-	cfud = snd_soc_read(codec, STA32X_CFUD) & 0xf0;
-	/* chip documentation does not say if the bits are self clearing,
-	 * so do it explicitly */
-	snd_soc_write(codec, STA32X_CFUD, cfud);
+	regmap_read(sta32x->regmap, STA32X_CFUD, &cfud);
+	cfud &= 0xf0;
+	/*
+	 * chip documentation does not say if the bits are self clearing,
+	 * so do it explicitly
+	 */
+	regmap_write(sta32x->regmap, STA32X_CFUD, cfud);
 
-	snd_soc_write(codec, STA32X_CFADDR2, index);
-	if (numcoef == 1)
-		snd_soc_write(codec, STA32X_CFUD, cfud | 0x04);
-	else if (numcoef == 5)
-		snd_soc_write(codec, STA32X_CFUD, cfud | 0x08);
-	else
-		return -EINVAL;
-	for (i = 0; i < 3 * numcoef; i++)
-		ucontrol->value.bytes.data[i] =
-			snd_soc_read(codec, STA32X_B1CF1 + i);
+	regmap_write(sta32x->regmap, STA32X_CFADDR2, index);
+	if (numcoef == 1) {
+		regmap_write(sta32x->regmap, STA32X_CFUD, cfud | 0x04);
+	} else if (numcoef == 5) {
+		regmap_write(sta32x->regmap, STA32X_CFUD, cfud | 0x08);
+	} else {
+		ret = -EINVAL;
+		goto exit_unlock;
+	}
 
-	return 0;
+	for (i = 0; i < 3 * numcoef; i++) {
+		regmap_read(sta32x->regmap, STA32X_B1CF1 + i, &val);
+		ucontrol->value.bytes.data[i] = val;
+	}
+
+exit_unlock:
+	mutex_unlock(&sta32x->coeff_lock);
+
+	return ret;
 }
 
 static int sta32x_coefficient_put(struct snd_kcontrol *kcontrol,
@@ -280,24 +323,27 @@ static int sta32x_coefficient_put(struct snd_kcontrol *kcontrol,
 	int i;
 
 	/* preserve reserved bits in STA32X_CFUD */
-	cfud = snd_soc_read(codec, STA32X_CFUD) & 0xf0;
-	/* chip documentation does not say if the bits are self clearing,
-	 * so do it explicitly */
-	snd_soc_write(codec, STA32X_CFUD, cfud);
+	regmap_read(sta32x->regmap, STA32X_CFUD, &cfud);
+	cfud &= 0xf0;
+	/*
+	 * chip documentation does not say if the bits are self clearing,
+	 * so do it explicitly
+	 */
+	regmap_write(sta32x->regmap, STA32X_CFUD, cfud);
 
-	snd_soc_write(codec, STA32X_CFADDR2, index);
+	regmap_write(sta32x->regmap, STA32X_CFADDR2, index);
 	for (i = 0; i < numcoef && (index + i < STA32X_COEF_COUNT); i++)
 		sta32x->coef_shadow[index + i] =
 			  (ucontrol->value.bytes.data[3 * i] << 16)
 			| (ucontrol->value.bytes.data[3 * i + 1] << 8)
 			| (ucontrol->value.bytes.data[3 * i + 2]);
 	for (i = 0; i < 3 * numcoef; i++)
-		snd_soc_write(codec, STA32X_B1CF1 + i,
-			      ucontrol->value.bytes.data[i]);
+		regmap_write(sta32x->regmap, STA32X_B1CF1 + i,
+			     ucontrol->value.bytes.data[i]);
 	if (numcoef == 1)
-		snd_soc_write(codec, STA32X_CFUD, cfud | 0x01);
+		regmap_write(sta32x->regmap, STA32X_CFUD, cfud | 0x01);
 	else if (numcoef == 5)
-		snd_soc_write(codec, STA32X_CFUD, cfud | 0x02);
+		regmap_write(sta32x->regmap, STA32X_CFUD, cfud | 0x02);
 	else
 		return -EINVAL;
 
@@ -311,20 +357,23 @@ static int sta32x_sync_coef_shadow(struct snd_soc_codec *codec)
 	int i;
 
 	/* preserve reserved bits in STA32X_CFUD */
-	cfud = snd_soc_read(codec, STA32X_CFUD) & 0xf0;
+	regmap_read(sta32x->regmap, STA32X_CFUD, &cfud);
+	cfud &= 0xf0;
 
 	for (i = 0; i < STA32X_COEF_COUNT; i++) {
-		snd_soc_write(codec, STA32X_CFADDR2, i);
-		snd_soc_write(codec, STA32X_B1CF1,
-			      (sta32x->coef_shadow[i] >> 16) & 0xff);
-		snd_soc_write(codec, STA32X_B1CF2,
-			      (sta32x->coef_shadow[i] >> 8) & 0xff);
-		snd_soc_write(codec, STA32X_B1CF3,
-			      (sta32x->coef_shadow[i]) & 0xff);
-		/* chip documentation does not say if the bits are
-		 * self-clearing, so do it explicitly */
-		snd_soc_write(codec, STA32X_CFUD, cfud);
-		snd_soc_write(codec, STA32X_CFUD, cfud | 0x01);
+		regmap_write(sta32x->regmap, STA32X_CFADDR2, i);
+		regmap_write(sta32x->regmap, STA32X_B1CF1,
+			     (sta32x->coef_shadow[i] >> 16) & 0xff);
+		regmap_write(sta32x->regmap, STA32X_B1CF2,
+			     (sta32x->coef_shadow[i] >> 8) & 0xff);
+		regmap_write(sta32x->regmap, STA32X_B1CF3,
+			     (sta32x->coef_shadow[i]) & 0xff);
+		/*
+		 * chip documentation does not say if the bits are
+		 * self-clearing, so do it explicitly
+		 */
+		regmap_write(sta32x->regmap, STA32X_CFUD, cfud);
+		regmap_write(sta32x->regmap, STA32X_CFUD, cfud | 0x01);
 	}
 	return 0;
 }
@@ -336,11 +385,11 @@ static int sta32x_cache_sync(struct snd_soc_codec *codec)
 	int rc;
 
 	/* mute during register sync */
-	mute = snd_soc_read(codec, STA32X_MMUTE);
-	snd_soc_write(codec, STA32X_MMUTE, mute | STA32X_MMUTE_MMUTE);
+	regmap_read(sta32x->regmap, STA32X_MMUTE, &mute);
+	regmap_write(sta32x->regmap, STA32X_MMUTE, mute | STA32X_MMUTE_MMUTE);
 	sta32x_sync_coef_shadow(codec);
 	rc = regcache_sync(sta32x->regmap);
-	snd_soc_write(codec, STA32X_MMUTE, mute);
+	regmap_write(sta32x->regmap, STA32X_MMUTE, mute);
 	return rc;
 }
 
@@ -599,10 +648,7 @@ static int sta32x_set_dai_fmt(struct snd_soc_dai *codec_dai,
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
 	struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
-	u8 confb = snd_soc_read(codec, STA32X_CONFB);
-
-	pr_debug("\n");
-	confb &= ~(STA32X_CONFB_C1IM | STA32X_CONFB_C2IM);
+	u8 confb = 0;
 
 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
 	case SND_SOC_DAIFMT_CBS_CFS:
@@ -632,8 +678,8 @@ static int sta32x_set_dai_fmt(struct snd_soc_dai *codec_dai,
 		return -EINVAL;
 	}
 
-	snd_soc_write(codec, STA32X_CONFB, confb);
-	return 0;
+	return regmap_update_bits(sta32x->regmap, STA32X_CONFB,
+				  STA32X_CONFB_C1IM | STA32X_CONFB_C2IM, confb);
 }
 
 /**
@@ -653,7 +699,7 @@ static int sta32x_hw_params(struct snd_pcm_substream *substream,
 	struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
 	unsigned int rate;
 	int i, mcs = -1, ir = -1;
-	u8 confa, confb;
+	unsigned int confa, confb;
 
 	rate = params_rate(params);
 	pr_debug("rate: %u\n", rate);
@@ -672,12 +718,10 @@ static int sta32x_hw_params(struct snd_pcm_substream *substream,
 	if (mcs < 0)
 		return -EINVAL;
 
-	confa = snd_soc_read(codec, STA32X_CONFA);
-	confa &= ~(STA32X_CONFA_MCS_MASK | STA32X_CONFA_IR_MASK);
-	confa |= (ir << STA32X_CONFA_IR_SHIFT) | (mcs << STA32X_CONFA_MCS_SHIFT);
+	confa = (ir << STA32X_CONFA_IR_SHIFT) |
+		(mcs << STA32X_CONFA_MCS_SHIFT);
+	confb = 0;
 
-	confb = snd_soc_read(codec, STA32X_CONFB);
-	confb &= ~(STA32X_CONFB_SAI_MASK | STA32X_CONFB_SAIFB);
 	switch (params_width(params)) {
 	case 24:
 		pr_debug("24bit\n");
@@ -746,8 +790,20 @@ static int sta32x_hw_params(struct snd_pcm_substream *substream,
 		return -EINVAL;
 	}
 
-	snd_soc_write(codec, STA32X_CONFA, confa);
-	snd_soc_write(codec, STA32X_CONFB, confb);
+	ret = regmap_update_bits(sta32x->regmap, STA32X_CONFA,
+				 STA32X_CONFA_MCS_MASK | STA32X_CONFA_IR_MASK,
+				 confa);
+	if (ret < 0)
+		return ret;
+
+	ret = regmap_update_bits(sta32x->regmap, STA32X_CONFB,
+				 STA32X_CONFB_SAI_MASK | STA32X_CONFB_SAIFB,
+				 confb);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
 	return 0;
 }
 
@@ -773,7 +829,7 @@ static int sta32x_set_bias_level(struct snd_soc_codec *codec,
 
 	case SND_SOC_BIAS_PREPARE:
 		/* Full power on */
-		snd_soc_update_bits(codec, STA32X_CONFF,
+		regmap_update_bits(sta32x->regmap, STA32X_CONFF,
 				    STA32X_CONFF_PWDN | STA32X_CONFF_EAPD,
 				    STA32X_CONFF_PWDN | STA32X_CONFF_EAPD);
 		break;
@@ -792,19 +848,17 @@ static int sta32x_set_bias_level(struct snd_soc_codec *codec,
 			sta32x_watchdog_start(sta32x);
 		}
 
-		/* Power up to mute */
-		/* FIXME */
-		snd_soc_update_bits(codec, STA32X_CONFF,
-				    STA32X_CONFF_PWDN | STA32X_CONFF_EAPD,
-				    STA32X_CONFF_PWDN | STA32X_CONFF_EAPD);
+		/* Power down */
+		regmap_update_bits(sta32x->regmap, STA32X_CONFF,
+				   STA32X_CONFF_PWDN | STA32X_CONFF_EAPD,
+				   0);
 
 		break;
 
 	case SND_SOC_BIAS_OFF:
 		/* The chip runs through the power down sequence for us. */
-		snd_soc_update_bits(codec, STA32X_CONFF,
-				    STA32X_CONFF_PWDN | STA32X_CONFF_EAPD,
-				    STA32X_CONFF_PWDN);
+		regmap_update_bits(sta32x->regmap, STA32X_CONFF,
+				   STA32X_CONFF_PWDN | STA32X_CONFF_EAPD, 0);
 		msleep(300);
 		sta32x_watchdog_stop(sta32x);
 		regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies),
@@ -836,11 +890,8 @@ static struct snd_soc_dai_driver sta32x_dai = {
 static int sta32x_probe(struct snd_soc_codec *codec)
 {
 	struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
+	struct sta32x_platform_data *pdata = sta32x->pdata;
 	int i, ret = 0, thermal = 0;
-
-	sta32x->codec = codec;
-	sta32x->pdata = dev_get_platdata(codec->dev);
-
 	ret = regulator_bulk_enable(ARRAY_SIZE(sta32x->supplies),
 				    sta32x->supplies);
 	if (ret != 0) {
@@ -848,50 +899,34 @@ static int sta32x_probe(struct snd_soc_codec *codec)
 		return ret;
 	}
 
-	/* Chip documentation explicitly requires that the reset values
-	 * of reserved register bits are left untouched.
-	 * Write the register default value to cache for reserved registers,
-	 * so the write to the these registers are suppressed by the cache
-	 * restore code when it skips writes of default registers.
-	 */
-	regcache_cache_only(sta32x->regmap, true);
-	snd_soc_write(codec, STA32X_CONFC, 0xc2);
-	snd_soc_write(codec, STA32X_CONFE, 0xc2);
-	snd_soc_write(codec, STA32X_CONFF, 0x5c);
-	snd_soc_write(codec, STA32X_MMUTE, 0x10);
-	snd_soc_write(codec, STA32X_AUTO1, 0x60);
-	snd_soc_write(codec, STA32X_AUTO3, 0x00);
-	snd_soc_write(codec, STA32X_C3CFG, 0x40);
-	regcache_cache_only(sta32x->regmap, false);
-
 	/* set thermal warning adjustment and recovery */
-	if (!(sta32x->pdata->thermal_conf & STA32X_THERMAL_ADJUSTMENT_ENABLE))
+	if (!pdata->thermal_warning_recovery)
 		thermal |= STA32X_CONFA_TWAB;
-	if (!(sta32x->pdata->thermal_conf & STA32X_THERMAL_RECOVERY_ENABLE))
+	if (!pdata->thermal_warning_adjustment)
 		thermal |= STA32X_CONFA_TWRB;
-	snd_soc_update_bits(codec, STA32X_CONFA,
-			    STA32X_CONFA_TWAB | STA32X_CONFA_TWRB,
-			    thermal);
+	regmap_update_bits(sta32x->regmap, STA32X_CONFA,
+			   STA32X_CONFA_TWAB | STA32X_CONFA_TWRB,
+			   thermal);
 
 	/* select output configuration  */
-	snd_soc_update_bits(codec, STA32X_CONFF,
-			    STA32X_CONFF_OCFG_MASK,
-			    sta32x->pdata->output_conf
-			    << STA32X_CONFF_OCFG_SHIFT);
+	regmap_update_bits(sta32x->regmap, STA32X_CONFF,
+			   STA32X_CONFF_OCFG_MASK,
+			   pdata->output_conf
+				<< STA32X_CONFF_OCFG_SHIFT);
 
 	/* channel to output mapping */
-	snd_soc_update_bits(codec, STA32X_C1CFG,
-			    STA32X_CxCFG_OM_MASK,
-			    sta32x->pdata->ch1_output_mapping
-			    << STA32X_CxCFG_OM_SHIFT);
-	snd_soc_update_bits(codec, STA32X_C2CFG,
-			    STA32X_CxCFG_OM_MASK,
-			    sta32x->pdata->ch2_output_mapping
-			    << STA32X_CxCFG_OM_SHIFT);
-	snd_soc_update_bits(codec, STA32X_C3CFG,
-			    STA32X_CxCFG_OM_MASK,
-			    sta32x->pdata->ch3_output_mapping
-			    << STA32X_CxCFG_OM_SHIFT);
+	regmap_update_bits(sta32x->regmap, STA32X_C1CFG,
+			   STA32X_CxCFG_OM_MASK,
+			   pdata->ch1_output_mapping
+				<< STA32X_CxCFG_OM_SHIFT);
+	regmap_update_bits(sta32x->regmap, STA32X_C2CFG,
+			   STA32X_CxCFG_OM_MASK,
+			   pdata->ch2_output_mapping
+				<< STA32X_CxCFG_OM_SHIFT);
+	regmap_update_bits(sta32x->regmap, STA32X_C3CFG,
+			   STA32X_CxCFG_OM_MASK,
+			   pdata->ch3_output_mapping
+				<< STA32X_CxCFG_OM_SHIFT);
 
 	/* initialize coefficient shadow RAM with reset values */
 	for (i = 4; i <= 49; i += 5)
@@ -924,16 +959,6 @@ static int sta32x_remove(struct snd_soc_codec *codec)
 	return 0;
 }
 
-static bool sta32x_reg_is_volatile(struct device *dev, unsigned int reg)
-{
-	switch (reg) {
-	case STA32X_CONFA ... STA32X_L2ATRT:
-	case STA32X_MPCC1 ... STA32X_FDRC2:
-		return 0;
-	}
-	return 1;
-}
-
 static const struct snd_soc_codec_driver sta32x_codec = {
 	.probe =		sta32x_probe,
 	.remove =		sta32x_remove,
@@ -954,12 +979,16 @@ static const struct regmap_config sta32x_regmap = {
 	.reg_defaults =		sta32x_regs,
 	.num_reg_defaults =	ARRAY_SIZE(sta32x_regs),
 	.cache_type =		REGCACHE_RBTREE,
-	.volatile_reg =		sta32x_reg_is_volatile,
+	.wr_table =		&sta32x_write_regs,
+	.rd_table =		&sta32x_read_regs,
+	.volatile_table =	&sta32x_volatile_regs,
+};
 };
 
 static int sta32x_i2c_probe(struct i2c_client *i2c,
 			    const struct i2c_device_id *id)
 {
+	struct device *dev = &i2c->dev;
 	struct sta32x_priv *sta32x;
 	int ret, i;
 
@@ -968,6 +997,8 @@ static int sta32x_i2c_probe(struct i2c_client *i2c,
 	if (!sta32x)
 		return -ENOMEM;
 
+	mutex_init(&sta32x->coeff_lock);
+	sta32x->pdata = dev_get_platdata(dev);
 	/* regulators */
 	for (i = 0; i < ARRAY_SIZE(sta32x->supplies); i++)
 		sta32x->supplies[i].supply = sta32x_supply_names[i];
@@ -982,15 +1013,15 @@ static int sta32x_i2c_probe(struct i2c_client *i2c,
 	sta32x->regmap = devm_regmap_init_i2c(i2c, &sta32x_regmap);
 	if (IS_ERR(sta32x->regmap)) {
 		ret = PTR_ERR(sta32x->regmap);
-		dev_err(&i2c->dev, "Failed to init regmap: %d\n", ret);
+		dev_err(dev, "Failed to init regmap: %d\n", ret);
 		return ret;
 	}
 
 	i2c_set_clientdata(i2c, sta32x);
 
-	ret = snd_soc_register_codec(&i2c->dev, &sta32x_codec, &sta32x_dai, 1);
-	if (ret != 0)
-		dev_err(&i2c->dev, "Failed to register codec (%d)\n", ret);
+	ret = snd_soc_register_codec(dev, &sta32x_codec, &sta32x_dai, 1);
+	if (ret < 0)
+		dev_err(dev, "Failed to register codec (%d)\n", ret);
 
 	return ret;
 }

                 reply	other threads:[~2018-11-12  0:10 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

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=1541946087214229@kroah.com \
    --to=gregkh@linuxfoundation.org \
    --cc=broonie@kernel.org \
    --cc=niederp@physik.uni-kl.de \
    --cc=stable@vger.kernel.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: 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.