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@xxxxxxxxx> --- 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