diff mbox series

[3/3] backlight/arcxcnn add support for arc1 an arc3 chip families

Message ID 1541592640-18478-4-git-send-email-bdodge09@gmail.com (mailing list archive)
State New, archived
Headers show
Series backlight/arcxcnn fix vendor prefix in driver and bindings and add support for arc1, arc3 | expand

Commit Message

Brian Dodge Nov. 7, 2018, 12:10 p.m. UTC
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(-)

--
2.7.4
diff mbox series

Patch

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);