All of lore.kernel.org
 help / color / mirror / Atom feed
From: Brian Dodge <bdodge09@gmail.com>
To: lee.jones@linaro.org, daniel.thompson@linaro.org,
	jingoohan1@gmail.com, jacek.anaszewski@gmail.com, pavel@ucw.cz,
	robh+dt@kernel.org, mark.rutland@arm.com,
	dri-devel@lists.freedesktop.org, linux-leds@vger.kernel.org,
	devicetree@vger.kernel.org
Cc: Brian Dodge <bdodge09@gmail.com>
Subject: [PATCH 3/3] backlight/arcxcnn add support for arc1 an arc3 chip families
Date: Wed,  7 Nov 2018 07:10:40 -0500	[thread overview]
Message-ID: <1541592640-18478-4-git-send-email-bdodge09@gmail.com> (raw)
In-Reply-To: <1541592640-18478-1-git-send-email-bdodge09@gmail.com>

Support for newer ArcticSand LED drivers is added. The
i2c device id is used to modify some limits and set
some device specific register addresses

Signed-off-by: Brian Dodge <bdodge09@gmail.com>
---
 drivers/video/backlight/arcxcnn_bl.c | 261 +++++++++++++++++++++++++----------
 1 file changed, 190 insertions(+), 71 deletions(-)

diff --git a/drivers/video/backlight/arcxcnn_bl.c b/drivers/video/backlight/arcxcnn_bl.c
index bebefc6..30c07cb 100644
--- a/drivers/video/backlight/arcxcnn_bl.c
+++ b/drivers/video/backlight/arcxcnn_bl.c
@@ -25,7 +25,9 @@
 #include <linux/slab.h>

 enum arcxcnn_chip_id {
-	ARC2C0608
+	ARC1C0608,
+	ARC2C0608,
+	ARC3C0845
 };

 /**
@@ -64,42 +66,77 @@ struct arcxcnn_platform_data {
 #define ARCXCNN_CMD_EXT_COMP	0x01	/*   part (0) or full (1) ext. comp */

 #define ARCXCNN_CONFIG		0x01	/* Configuration */
-#define ARCXCNN_STATUS1		0x02	/* Status 1 */
-#define ARCXCNN_STATUS2		0x03	/* Status 2 */
+
+#define ARCXCNN_STATUS1		0x02	/* Status 1 (6 str) */
+#define ARCXCNN_STATUS2		0x03	/* Status 2 (6 str)*/
 #define ARCXCNN_FADECTRL	0x04	/* Fading Control */
+#define ARC3CNN_FADECTRL	0x02	/* Fading Control */
 #define ARCXCNN_ILED_CONFIG	0x05	/* ILED Configuration */
+#define ARC3CNN_ILED_CONFIG	0x03	/* ILED Configuration */
 #define ARCXCNN_ILED_DIM_PWM	0x00	/*   config dim mode pwm */
-#define ARCXCNN_ILED_DIM_INT	0x04	/*   config dim mode internal */
-#define ARCXCNN_LEDEN		0x06	/* LED Enable Register */
+#define ARCXCNN_ILED_DIM_INT	0x44	/*   config dim mode int+iset (6 str) */
+#define ARC3CNN_ILED_DIM_INT	0x20	/*   config dim mode internal (8 str) */
+#define ARCXCNN_LEDEN		0x06	/* LED Enable Register (6 str) */
+#define ARC3CNN_LEDEN		0x04	/* LED Enable Register (8 str) */
+
 #define ARCXCNN_LEDEN_ISETEXT	0x80	/*   Full-scale current set extern */
-#define ARCXCNN_LEDEN_MASK	0x3F	/*   LED string enables mask */
-#define ARCXCNN_LEDEN_BITS	0x06	/*   Bits of LED string enables */
+
+#define ARCXCNN_LEDEN_MASK	0x3F	/*   LED string enables mask (6 str) */
+#define ARCXCNN_LEDEN_BITS	0x06	/*   Bits of string enables (6 str) */
+#define ARC3CNN_LEDEN_MASK	0xFF	/*   LED string enables mask (8 str) */
+#define ARC3CNN_LEDEN_BITS	0x08	/*   Bits of string enables (8 str) */
 #define ARCXCNN_LEDEN_LED1	0x01
 #define ARCXCNN_LEDEN_LED2	0x02
 #define ARCXCNN_LEDEN_LED3	0x04
 #define ARCXCNN_LEDEN_LED4	0x08
 #define ARCXCNN_LEDEN_LED5	0x10
 #define ARCXCNN_LEDEN_LED6	0x20
+#define ARCXCNN_LEDEN_LED7	0x40
+#define ARCXCNN_LEDEN_LED8	0x80
+
+#define ARCXCNN_WLED_ISET_LSB		0x07	/* LED ISET LSB */
+#define ARCXCNN_WLED_ISET_LSB_SHIFT	0x04	/* ISET LSB Left Shift */
+#define ARCXCNN_WLED_ISET_MSB		0x08	/* LED ISET MSB (8 bits) */
+#define ARC3CNN_WLED_ISET_LSB		0x05	/* LED ISET LSB */
+#define ARC3CNN_WLED_ISET_LSB_SHIFT	0x01	/* ISET LSB Left Shift */
+#define ARC3CNN_WLED_ISET_MSB		0x06	/* LED ISET MSB (8 bits) */

-#define ARCXCNN_WLED_ISET_LSB	0x07	/* LED ISET LSB (in upper nibble) */
-#define ARCXCNN_WLED_ISET_LSB_SHIFT 0x04  /* ISET LSB Left Shift */
-#define ARCXCNN_WLED_ISET_MSB	0x08	/* LED ISET MSB (8 bits) */
+#define ARC2CNN_DIMFREQ		0x09
+
+/* NO COMP CONFIG OR FILT CONFIG IN ARC1CNN */
+#define ARC2CNN_COMP_CONFIG	0x0A
+#define ARC3CNN_COMP_CONFIG	0x08
+#define ARC2CNN_FILT_CONFIG	0x0B
+#define ARC3CNN_FILT_CONFIG	0x09
+
+#define ARC3CNN_FILT_DIMCODE    0x60	/* Force DITHER_ENABLE and code 01 */
+
+#define ARC2CNN_IMAXTUNE	0x0C
+#define ARC3CNN_IMAXTUNE	0x0A

-#define ARCXCNN_DIMFREQ		0x09
-#define ARCXCNN_COMP_CONFIG	0x0A
-#define ARCXCNN_FILT_CONFIG	0x0B
-#define ARCXCNN_IMAXTUNE	0x0C
 #define ARCXCNN_ID_MSB		0x1E
 #define ARCXCNN_ID_LSB		0x1F
+#define ARC3CNN_ID_MSB		0xFE
+#define ARC3CNN_ID_LSB		0xFF

-#define MAX_BRIGHTNESS		4095
-#define INIT_BRIGHT		60
+#define ARC_MAX_BRIGHTNESS_1	4095
+#define ARC_MAX_BRIGHTNESS_2	4095
+#define ARC_MAX_BRIGHTNESS_3	32767
+#define ARC_INIT_BRIGHT		60

 struct arcxcnn {
 	struct i2c_client *client;
 	struct backlight_device *bl;
 	struct device *dev;
 	struct arcxcnn_platform_data *pdata;
+	u8 chipid;
+	u16 max_brightness;
+	u8 rst_reg;
+	u8 fade_reg;
+	u8 iled_config_reg, dim_mode_bits;
+	u8 iset_lsb_reg, iset_msb_reg, iset_shift;
+	u8 leden_reg, leden_mask, leden_bits;
+	u8 comp_config_reg, filter_reg, maxtune_reg;
 };

 static int arcxcnn_update_field(struct arcxcnn *lp, u8 reg, u8 mask, u8 data)
@@ -125,17 +162,16 @@ static int arcxcnn_set_brightness(struct arcxcnn *lp, u32 brightness)
 	int ret;
 	u8 val;

-	/* lower nibble of brightness goes in upper nibble of LSB register */
-	val = (brightness & 0xF) << ARCXCNN_WLED_ISET_LSB_SHIFT;
+	/* brightness is split across two registers */
+	val = brightness << lp->iset_shift;
 	ret = i2c_smbus_write_byte_data(lp->client,
-		ARCXCNN_WLED_ISET_LSB, val);
+		lp->iset_lsb_reg, val);
 	if (ret < 0)
 		return ret;

-	/* remaining 8 bits of brightness go in MSB register */
-	val = (brightness >> 4);
+	val = (u8)(brightness >> (8 - lp->iset_shift));
 	return i2c_smbus_write_byte_data(lp->client,
-		ARCXCNN_WLED_ISET_MSB, val);
+		lp->iset_msb_reg, val);
 }

 static int arcxcnn_bl_update_status(struct backlight_device *bl)
@@ -152,7 +188,7 @@ static int arcxcnn_bl_update_status(struct backlight_device *bl)
 		return ret;

 	/* set power-on/off/save modes */
-	return arcxcnn_update_field(lp, ARCXCNN_CMD, ARCXCNN_CMD_STDBY,
+	return arcxcnn_update_field(lp, lp->rst_reg, ARCXCNN_CMD_STDBY,
 		(bl->props.power == 0) ? 0 : ARCXCNN_CMD_STDBY);
 }

@@ -171,7 +207,7 @@ static int arcxcnn_backlight_register(struct arcxcnn *lp)
 		return -ENOMEM;

 	props->type = BACKLIGHT_PLATFORM;
-	props->max_brightness = MAX_BRIGHTNESS;
+	props->max_brightness = lp->max_brightness;

 	if (lp->pdata->initial_brightness > props->max_brightness)
 		lp->pdata->initial_brightness = props->max_brightness;
@@ -187,7 +223,7 @@ static void arcxcnn_parse_dt(struct arcxcnn *lp)
 {
 	struct device *dev = lp->dev;
 	struct device_node *node = dev->of_node;
-	u32 prog_val, num_entry, entry, sources[ARCXCNN_LEDEN_BITS];
+	u32 prog_val, num_entry, entry, sources[ARC3CNN_LEDEN_BITS];
 	int ret;

 	/* device tree entry isn't required, defaults are OK */
@@ -228,11 +264,11 @@ static void arcxcnn_parse_dt(struct arcxcnn *lp)

 	ret = of_property_count_u32_elems(node, "led-sources");
 	if (ret < 0) {
-		lp->pdata->leden = ARCXCNN_LEDEN_MASK; /* all on is default */
+		lp->pdata->leden = lp->leden_mask; /* all on is default */
 	} else {
 		num_entry = ret;
-		if (num_entry > ARCXCNN_LEDEN_BITS)
-			num_entry = ARCXCNN_LEDEN_BITS;
+		if (num_entry > lp->leden_bits)
+			num_entry = lp->leden_bits;

 		ret = of_property_read_u32_array(node, "led-sources", sources,
 					num_entry);
@@ -266,14 +302,84 @@ static int arcxcnn_probe(struct i2c_client *cl, const struct i2c_device_id *id)

 	lp->client = cl;
 	lp->dev = &cl->dev;
-	lp->pdata = dev_get_platdata(&cl->dev);
+
+	/* read device id (class) */
+	lp->chipid = i2c_smbus_read_byte_data(cl, ARCXCNN_ID_MSB);
+	if (lp->chipid > 2) {
+		lp->chipid = i2c_smbus_read_byte_data(cl, ARC3CNN_ID_MSB);
+		if (lp->chipid != 3) {
+			dev_err(lp->dev,
+				"Unknown device Id %02X\n", lp->chipid);
+			ret = -ENODEV;
+			goto probe_err;
+		}
+	}
+
+	if (lp->chipid == 0) {
+		/* treat id 0 as older class 1 chips */
+		lp->chipid = 1;
+	}
+
+	switch (lp->chipid) {
+	case 3:
+		/* class 3 device, 8 strings */
+		lp->max_brightness = ARC_MAX_BRIGHTNESS_3;
+		lp->rst_reg = ARC3CNN_COMP_CONFIG;
+		lp->fade_reg = ARC3CNN_FADECTRL;
+		lp->iled_config_reg = ARC3CNN_ILED_CONFIG;
+		lp->dim_mode_bits = ARC3CNN_ILED_DIM_INT;
+		lp->leden_reg = ARC3CNN_LEDEN;
+		lp->leden_mask = ARC3CNN_LEDEN_MASK;
+		lp->leden_bits = ARC3CNN_LEDEN_BITS;
+		lp->iset_lsb_reg = ARC3CNN_WLED_ISET_LSB;
+		lp->iset_msb_reg = ARC3CNN_WLED_ISET_MSB;
+		lp->iset_shift = ARC3CNN_WLED_ISET_LSB_SHIFT;
+		lp->comp_config_reg = ARC3CNN_COMP_CONFIG;
+		lp->filter_reg = ARC3CNN_FILT_CONFIG;
+		lp->maxtune_reg = ARC3CNN_IMAXTUNE;
+		break;
+	case 2:
+		/* class 2 device, 6 strings */
+		lp->max_brightness = ARC_MAX_BRIGHTNESS_2;
+		lp->rst_reg = ARCXCNN_CMD;
+		lp->fade_reg = ARCXCNN_FADECTRL;
+		lp->iled_config_reg = ARCXCNN_ILED_CONFIG;
+		lp->dim_mode_bits = ARCXCNN_ILED_DIM_INT;
+		lp->leden_reg = ARCXCNN_LEDEN;
+		lp->leden_mask = ARCXCNN_LEDEN_MASK;
+		lp->leden_bits = ARCXCNN_LEDEN_BITS;
+		lp->iset_lsb_reg = ARCXCNN_WLED_ISET_LSB;
+		lp->iset_msb_reg = ARCXCNN_WLED_ISET_MSB;
+		lp->iset_shift = ARCXCNN_WLED_ISET_LSB_SHIFT;
+		lp->comp_config_reg = ARC2CNN_COMP_CONFIG;
+		lp->filter_reg = ARC2CNN_FILT_CONFIG;
+		lp->maxtune_reg = ARC2CNN_IMAXTUNE;
+		break;
+	case 1:
+	default:
+		/* class 1 device, 6 strings */
+		lp->max_brightness = ARC_MAX_BRIGHTNESS_1;
+		lp->rst_reg = ARCXCNN_CMD;
+		lp->fade_reg = ARCXCNN_FADECTRL;
+		lp->iled_config_reg = ARCXCNN_ILED_CONFIG;
+		lp->dim_mode_bits = ARCXCNN_ILED_DIM_INT;
+		lp->leden_reg = ARCXCNN_LEDEN;
+		lp->leden_mask = ARCXCNN_LEDEN_MASK;
+		lp->leden_bits = ARCXCNN_LEDEN_BITS;
+		lp->iset_lsb_reg = ARCXCNN_WLED_ISET_LSB;
+		lp->iset_msb_reg = ARCXCNN_WLED_ISET_MSB;
+		lp->iset_shift = ARCXCNN_WLED_ISET_LSB_SHIFT;
+		break;
+	}

 	/* reset the device */
 	ret = i2c_smbus_write_byte_data(lp->client,
-		ARCXCNN_CMD, ARCXCNN_CMD_RESET);
+		lp->rst_reg, ARCXCNN_CMD_RESET);
 	if (ret)
 		goto probe_err;

+	lp->pdata = dev_get_platdata(&cl->dev);
+
 	if (!lp->pdata) {
 		lp->pdata = devm_kzalloc(lp->dev,
 				sizeof(*lp->pdata), GFP_KERNEL);
@@ -282,29 +388,31 @@ static int arcxcnn_probe(struct i2c_client *cl, const struct i2c_device_id *id)

 		/* Setup defaults based on power-on defaults */
 		lp->pdata->name = NULL;
-		lp->pdata->initial_brightness = INIT_BRIGHT;
-		lp->pdata->leden = ARCXCNN_LEDEN_MASK;
+		lp->pdata->initial_brightness = ARC_INIT_BRIGHT;
+		lp->pdata->leden = lp->leden_mask;

 		lp->pdata->led_config_0 = i2c_smbus_read_byte_data(
-			lp->client, ARCXCNN_FADECTRL);
+			lp->client, lp->fade_reg);

 		lp->pdata->led_config_1 = i2c_smbus_read_byte_data(
-			lp->client, ARCXCNN_ILED_CONFIG);
+			lp->client, lp->iled_config_reg);
 		/* insure dim mode is not default pwm */
-		lp->pdata->led_config_1 |= ARCXCNN_ILED_DIM_INT;
-
-		lp->pdata->dim_freq = i2c_smbus_read_byte_data(
-			lp->client, ARCXCNN_DIMFREQ);
+		lp->pdata->led_config_1 |= lp->dim_mode_bits;

-		lp->pdata->comp_config = i2c_smbus_read_byte_data(
-			lp->client, ARCXCNN_COMP_CONFIG);
+		if (lp->chipid == 2)
+			lp->pdata->dim_freq = i2c_smbus_read_byte_data(
+				lp->client, ARC2CNN_DIMFREQ);

-		lp->pdata->filter_config = i2c_smbus_read_byte_data(
-			lp->client, ARCXCNN_FILT_CONFIG);
+		if (lp->chipid > 1) {
+			lp->pdata->comp_config = i2c_smbus_read_byte_data(
+				lp->client, lp->comp_config_reg);

-		lp->pdata->trim_config = i2c_smbus_read_byte_data(
-			lp->client, ARCXCNN_IMAXTUNE);
+			lp->pdata->filter_config = i2c_smbus_read_byte_data(
+				lp->client, lp->filter_reg);

+			lp->pdata->trim_config = i2c_smbus_read_byte_data(
+				lp->client, lp->maxtune_reg);
+		}
 		if (IS_ENABLED(CONFIG_OF))
 			arcxcnn_parse_dt(lp);
 	}
@@ -312,48 +420,55 @@ static int arcxcnn_probe(struct i2c_client *cl, const struct i2c_device_id *id)
 	i2c_set_clientdata(cl, lp);

 	/* constrain settings to what is possible */
-	if (lp->pdata->initial_brightness > MAX_BRIGHTNESS)
-		lp->pdata->initial_brightness = MAX_BRIGHTNESS;
+	if (lp->pdata->initial_brightness > lp->max_brightness)
+		lp->pdata->initial_brightness = lp->max_brightness;

 	/* set initial brightness */
 	ret = arcxcnn_set_brightness(lp, lp->pdata->initial_brightness);
 	if (ret)
 		goto probe_err;

-	/* set other register values directly */
-	ret = i2c_smbus_write_byte_data(lp->client, ARCXCNN_FADECTRL,
-		lp->pdata->led_config_0);
+	/* set other register values directly from platform data */
+	ret = i2c_smbus_write_byte_data(lp->client,
+		lp->fade_reg, lp->pdata->led_config_0);
 	if (ret)
 		goto probe_err;

-	ret = i2c_smbus_write_byte_data(lp->client, ARCXCNN_ILED_CONFIG,
-		lp->pdata->led_config_1);
+	ret = i2c_smbus_write_byte_data(lp->client,
+		lp->iled_config_reg, lp->pdata->led_config_1);
 	if (ret)
 		goto probe_err;

-	ret = i2c_smbus_write_byte_data(lp->client, ARCXCNN_DIMFREQ,
-		lp->pdata->dim_freq);
-	if (ret)
-		goto probe_err;
+	if (lp->chipid == 2) {
+		ret = i2c_smbus_write_byte_data(lp->client, ARC2CNN_DIMFREQ,
+			lp->pdata->dim_freq);
+		if (ret)
+			goto probe_err;
+	}

-	ret = i2c_smbus_write_byte_data(lp->client, ARCXCNN_COMP_CONFIG,
-		lp->pdata->comp_config);
-	if (ret)
-		goto probe_err;
+	if (lp->chipid > 1) {
+		ret = i2c_smbus_write_byte_data(lp->client,
+			lp->comp_config_reg, lp->pdata->comp_config);
+		if (ret)
+			goto probe_err;

-	ret = i2c_smbus_write_byte_data(lp->client, ARCXCNN_FILT_CONFIG,
-		lp->pdata->filter_config);
-	if (ret)
-		goto probe_err;
+		if (lp->chipid == 3)
+			lp->pdata->filter_config = ARC3CNN_FILT_DIMCODE;

-	ret = i2c_smbus_write_byte_data(lp->client, ARCXCNN_IMAXTUNE,
-		lp->pdata->trim_config);
-	if (ret)
-		goto probe_err;
+		ret = i2c_smbus_write_byte_data(lp->client,
+			lp->filter_reg, lp->pdata->filter_config);
+		if (ret)
+			goto probe_err;
+
+		ret = i2c_smbus_write_byte_data(lp->client,
+			lp->maxtune_reg, lp->pdata->trim_config);
+		if (ret)
+			goto probe_err;
+	}

 	/* set initial LED Enables */
-	arcxcnn_update_field(lp, ARCXCNN_LEDEN,
-		ARCXCNN_LEDEN_MASK, lp->pdata->leden);
+	arcxcnn_update_field(lp, lp->leden_reg,
+		lp->leden_mask, lp->pdata->leden);

 	ret = arcxcnn_backlight_register(lp);
 	if (ret)
@@ -379,10 +494,10 @@ static int arcxcnn_remove(struct i2c_client *cl)

 	/* disable all strings (ignore errors) */
 	i2c_smbus_write_byte_data(lp->client,
-		ARCXCNN_LEDEN, 0x00);
+		lp->leden_reg, 0x00);
 	/* reset the device (ignore errors) */
 	i2c_smbus_write_byte_data(lp->client,
-		ARCXCNN_CMD, ARCXCNN_CMD_RESET);
+		lp->rst_reg, ARCXCNN_CMD_RESET);

 	lp->bl->props.brightness = 0;

@@ -392,13 +507,17 @@ static int arcxcnn_remove(struct i2c_client *cl)
 }

 static const struct of_device_id arcxcnn_dt_ids[] = {
+	{ .compatible = "arctic,arc1c0608" },
 	{ .compatible = "arctic,arc2c0608" },
+	{ .compatible = "arctic,arc3c0845" },
 	{ }
 };
 MODULE_DEVICE_TABLE(of, arcxcnn_dt_ids);

 static const struct i2c_device_id arcxcnn_ids[] = {
+	{"arc1c0608", ARC1C0608},
 	{"arc2c0608", ARC2C0608},
+	{"arc3c0845", ARC3C0845},
 	{ }
 };
 MODULE_DEVICE_TABLE(i2c, arcxcnn_ids);
--
2.7.4

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-11-07 12:10 [PATCH 0/3] backlight/arcxcnn fix vendor prefix in driver and bindings and add support for arc1, arc3 Brian Dodge
2018-11-07 12:10 ` [PATCH 1/3] dt-bindings: backlight: fix vendor prefix for ArcticSand arcxcnn driver bindings Brian Dodge
2018-11-12 18:42   ` Rob Herring
2018-11-07 12:10 ` [PATCH 2/3] backlight/arcxcnn fix vendor prefix Brian Dodge
2018-11-11 11:30   ` Pavel Machek
2018-11-27  0:44     ` Brian Dodge
2019-06-21 13:39       ` Daniel Thompson
2019-06-21 13:46       ` Daniel Thompson
2019-06-21 13:48         ` Daniel Thompson
2019-06-21 22:13         ` Pavel Machek
2019-06-24 10:24           ` Daniel Thompson
2019-06-24 11:29             ` Brian Dodge
2019-06-24 13:59               ` Daniel Thompson
2018-11-07 12:10 ` Brian Dodge [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=1541592640-18478-4-git-send-email-bdodge09@gmail.com \
    --to=bdodge09@gmail.com \
    --cc=daniel.thompson@linaro.org \
    --cc=devicetree@vger.kernel.org \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=jacek.anaszewski@gmail.com \
    --cc=jingoohan1@gmail.com \
    --cc=lee.jones@linaro.org \
    --cc=linux-leds@vger.kernel.org \
    --cc=mark.rutland@arm.com \
    --cc=pavel@ucw.cz \
    --cc=robh+dt@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.