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 <bdodg...@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

Reply via email to