Commit a59ec1e7ff98cc4365d5b1bff4e7102e86b5716b

Authored by Michael Hennerich
Committed by Linus Torvalds
1 parent 7f81c8890c

backlight: new driver for the ADP8870 backlight devices

Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
Cc: Richard Purdie <rpurdie@rpsys.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

Showing 5 changed files with 1233 additions and 0 deletions Side-by-side Diff

Documentation/ABI/testing/sysfs-class-backlight-driver-adp8870
  1 +What: /sys/class/backlight/<backlight>/<ambient light zone>_max
  2 +What: /sys/class/backlight/<backlight>/l1_daylight_max
  3 +What: /sys/class/backlight/<backlight>/l2_bright_max
  4 +What: /sys/class/backlight/<backlight>/l3_office_max
  5 +What: /sys/class/backlight/<backlight>/l4_indoor_max
  6 +What: /sys/class/backlight/<backlight>/l5_dark_max
  7 +Date: Mai 2011
  8 +KernelVersion: 2.6.40
  9 +Contact: device-drivers-devel@blackfin.uclinux.org
  10 +Description:
  11 + Control the maximum brightness for <ambient light zone>
  12 + on this <backlight>. Values are between 0 and 127. This file
  13 + will also show the brightness level stored for this
  14 + <ambient light zone>.
  15 +
  16 +What: /sys/class/backlight/<backlight>/<ambient light zone>_dim
  17 +What: /sys/class/backlight/<backlight>/l2_bright_dim
  18 +What: /sys/class/backlight/<backlight>/l3_office_dim
  19 +What: /sys/class/backlight/<backlight>/l4_indoor_dim
  20 +What: /sys/class/backlight/<backlight>/l5_dark_dim
  21 +Date: Mai 2011
  22 +KernelVersion: 2.6.40
  23 +Contact: device-drivers-devel@blackfin.uclinux.org
  24 +Description:
  25 + Control the dim brightness for <ambient light zone>
  26 + on this <backlight>. Values are between 0 and 127, typically
  27 + set to 0. Full off when the backlight is disabled.
  28 + This file will also show the dim brightness level stored for
  29 + this <ambient light zone>.
  30 +
  31 +What: /sys/class/backlight/<backlight>/ambient_light_level
  32 +Date: Mai 2011
  33 +KernelVersion: 2.6.40
  34 +Contact: device-drivers-devel@blackfin.uclinux.org
  35 +Description:
  36 + Get conversion value of the light sensor.
  37 + This value is updated every 80 ms (when the light sensor
  38 + is enabled). Returns integer between 0 (dark) and
  39 + 8000 (max ambient brightness)
  40 +
  41 +What: /sys/class/backlight/<backlight>/ambient_light_zone
  42 +Date: Mai 2011
  43 +KernelVersion: 2.6.40
  44 +Contact: device-drivers-devel@blackfin.uclinux.org
  45 +Description:
  46 + Get/Set current ambient light zone. Reading returns
  47 + integer between 1..5 (1 = daylight, 2 = bright, ..., 5 = dark).
  48 + Writing a value between 1..5 forces the backlight controller
  49 + to enter the corresponding ambient light zone.
  50 + Writing 0 returns to normal/automatic ambient light level
  51 + operation. The ambient light sensing feature on these devices
  52 + is an extension to the API documented in
  53 + Documentation/ABI/stable/sysfs-class-backlight.
  54 + It can be enabled by writing the value stored in
  55 + /sys/class/backlight/<backlight>/max_brightness to
  56 + /sys/class/backlight/<backlight>/brightness.
drivers/video/backlight/Kconfig
... ... @@ -302,6 +302,18 @@
302 302 To compile this driver as a module, choose M here: the module will
303 303 be called adp8860_bl.
304 304  
  305 +config BACKLIGHT_ADP8870
  306 + tristate "Backlight Driver for ADP8870 using WLED"
  307 + depends on BACKLIGHT_CLASS_DEVICE && I2C
  308 + select NEW_LEDS
  309 + select LEDS_CLASS
  310 + help
  311 + If you have a LCD backlight connected to the ADP8870,
  312 + say Y here to enable this driver.
  313 +
  314 + To compile this driver as a module, choose M here: the module will
  315 + be called adp8870_bl.
  316 +
305 317 config BACKLIGHT_88PM860X
306 318 tristate "Backlight Driver for 88PM8606 using WLED"
307 319 depends on MFD_88PM860X
drivers/video/backlight/Makefile
... ... @@ -34,6 +34,7 @@
34 34 obj-$(CONFIG_BACKLIGHT_ADX) += adx_bl.o
35 35 obj-$(CONFIG_BACKLIGHT_ADP5520) += adp5520_bl.o
36 36 obj-$(CONFIG_BACKLIGHT_ADP8860) += adp8860_bl.o
  37 +obj-$(CONFIG_BACKLIGHT_ADP8870) += adp8870_bl.o
37 38 obj-$(CONFIG_BACKLIGHT_88PM860X) += 88pm860x_bl.o
38 39 obj-$(CONFIG_BACKLIGHT_PCF50633) += pcf50633-backlight.o
drivers/video/backlight/adp8870_bl.c
Changes suppressed. Click to show
  1 +/*
  2 + * Backlight driver for Analog Devices ADP8870 Backlight Devices
  3 + *
  4 + * Copyright 2009-2011 Analog Devices Inc.
  5 + *
  6 + * Licensed under the GPL-2 or later.
  7 + */
  8 +
  9 +#include <linux/module.h>
  10 +#include <linux/version.h>
  11 +#include <linux/init.h>
  12 +#include <linux/errno.h>
  13 +#include <linux/pm.h>
  14 +#include <linux/platform_device.h>
  15 +#include <linux/i2c.h>
  16 +#include <linux/fb.h>
  17 +#include <linux/backlight.h>
  18 +#include <linux/leds.h>
  19 +#include <linux/workqueue.h>
  20 +#include <linux/slab.h>
  21 +
  22 +#include <linux/i2c/adp8870.h>
  23 +#define ADP8870_EXT_FEATURES
  24 +#define ADP8870_USE_LEDS
  25 +
  26 +
  27 +#define ADP8870_MFDVID 0x00 /* Manufacturer and device ID */
  28 +#define ADP8870_MDCR 0x01 /* Device mode and status */
  29 +#define ADP8870_INT_STAT 0x02 /* Interrupts status */
  30 +#define ADP8870_INT_EN 0x03 /* Interrupts enable */
  31 +#define ADP8870_CFGR 0x04 /* Configuration register */
  32 +#define ADP8870_BLSEL 0x05 /* Sink enable backlight or independent */
  33 +#define ADP8870_PWMLED 0x06 /* PWM Enable Selection Register */
  34 +#define ADP8870_BLOFF 0x07 /* Backlight off timeout */
  35 +#define ADP8870_BLDIM 0x08 /* Backlight dim timeout */
  36 +#define ADP8870_BLFR 0x09 /* Backlight fade in and out rates */
  37 +#define ADP8870_BLMX1 0x0A /* Backlight (Brightness Level 1-daylight) maximum current */
  38 +#define ADP8870_BLDM1 0x0B /* Backlight (Brightness Level 1-daylight) dim current */
  39 +#define ADP8870_BLMX2 0x0C /* Backlight (Brightness Level 2-bright) maximum current */
  40 +#define ADP8870_BLDM2 0x0D /* Backlight (Brightness Level 2-bright) dim current */
  41 +#define ADP8870_BLMX3 0x0E /* Backlight (Brightness Level 3-office) maximum current */
  42 +#define ADP8870_BLDM3 0x0F /* Backlight (Brightness Level 3-office) dim current */
  43 +#define ADP8870_BLMX4 0x10 /* Backlight (Brightness Level 4-indoor) maximum current */
  44 +#define ADP8870_BLDM4 0x11 /* Backlight (Brightness Level 4-indoor) dim current */
  45 +#define ADP8870_BLMX5 0x12 /* Backlight (Brightness Level 5-dark) maximum current */
  46 +#define ADP8870_BLDM5 0x13 /* Backlight (Brightness Level 5-dark) dim current */
  47 +#define ADP8870_ISCLAW 0x1A /* Independent sink current fade law register */
  48 +#define ADP8870_ISCC 0x1B /* Independent sink current control register */
  49 +#define ADP8870_ISCT1 0x1C /* Independent Sink Current Timer Register LED[7:5] */
  50 +#define ADP8870_ISCT2 0x1D /* Independent Sink Current Timer Register LED[4:1] */
  51 +#define ADP8870_ISCF 0x1E /* Independent sink current fade register */
  52 +#define ADP8870_ISC1 0x1F /* Independent Sink Current LED1 */
  53 +#define ADP8870_ISC2 0x20 /* Independent Sink Current LED2 */
  54 +#define ADP8870_ISC3 0x21 /* Independent Sink Current LED3 */
  55 +#define ADP8870_ISC4 0x22 /* Independent Sink Current LED4 */
  56 +#define ADP8870_ISC5 0x23 /* Independent Sink Current LED5 */
  57 +#define ADP8870_ISC6 0x24 /* Independent Sink Current LED6 */
  58 +#define ADP8870_ISC7 0x25 /* Independent Sink Current LED7 (Brightness Level 1-daylight) */
  59 +#define ADP8870_ISC7_L2 0x26 /* Independent Sink Current LED7 (Brightness Level 2-bright) */
  60 +#define ADP8870_ISC7_L3 0x27 /* Independent Sink Current LED7 (Brightness Level 3-office) */
  61 +#define ADP8870_ISC7_L4 0x28 /* Independent Sink Current LED7 (Brightness Level 4-indoor) */
  62 +#define ADP8870_ISC7_L5 0x29 /* Independent Sink Current LED7 (Brightness Level 5-dark) */
  63 +#define ADP8870_CMP_CTL 0x2D /* ALS Comparator Control Register */
  64 +#define ADP8870_ALS1_EN 0x2E /* Main ALS comparator level enable */
  65 +#define ADP8870_ALS2_EN 0x2F /* Second ALS comparator level enable */
  66 +#define ADP8870_ALS1_STAT 0x30 /* Main ALS Comparator Status Register */
  67 +#define ADP8870_ALS2_STAT 0x31 /* Second ALS Comparator Status Register */
  68 +#define ADP8870_L2TRP 0x32 /* L2 comparator reference */
  69 +#define ADP8870_L2HYS 0x33 /* L2 hysteresis */
  70 +#define ADP8870_L3TRP 0x34 /* L3 comparator reference */
  71 +#define ADP8870_L3HYS 0x35 /* L3 hysteresis */
  72 +#define ADP8870_L4TRP 0x36 /* L4 comparator reference */
  73 +#define ADP8870_L4HYS 0x37 /* L4 hysteresis */
  74 +#define ADP8870_L5TRP 0x38 /* L5 comparator reference */
  75 +#define ADP8870_L5HYS 0x39 /* L5 hysteresis */
  76 +#define ADP8870_PH1LEVL 0x40 /* First phototransistor ambient light level-low byte register */
  77 +#define ADP8870_PH1LEVH 0x41 /* First phototransistor ambient light level-high byte register */
  78 +#define ADP8870_PH2LEVL 0x42 /* Second phototransistor ambient light level-low byte register */
  79 +#define ADP8870_PH2LEVH 0x43 /* Second phototransistor ambient light level-high byte register */
  80 +
  81 +#define ADP8870_MANUFID 0x3 /* Analog Devices AD8870 Manufacturer and device ID */
  82 +#define ADP8870_DEVID(x) ((x) & 0xF)
  83 +#define ADP8870_MANID(x) ((x) >> 4)
  84 +
  85 +/* MDCR Device mode and status */
  86 +#define D7ALSEN (1 << 7)
  87 +#define INT_CFG (1 << 6)
  88 +#define NSTBY (1 << 5)
  89 +#define DIM_EN (1 << 4)
  90 +#define GDWN_DIS (1 << 3)
  91 +#define SIS_EN (1 << 2)
  92 +#define CMP_AUTOEN (1 << 1)
  93 +#define BLEN (1 << 0)
  94 +
  95 +/* ADP8870_ALS1_EN Main ALS comparator level enable */
  96 +#define L5_EN (1 << 3)
  97 +#define L4_EN (1 << 2)
  98 +#define L3_EN (1 << 1)
  99 +#define L2_EN (1 << 0)
  100 +
  101 +#define CFGR_BLV_SHIFT 3
  102 +#define CFGR_BLV_MASK 0x7
  103 +#define ADP8870_FLAG_LED_MASK 0xFF
  104 +
  105 +#define FADE_VAL(in, out) ((0xF & (in)) | ((0xF & (out)) << 4))
  106 +#define BL_CFGR_VAL(law, blv) ((((blv) & CFGR_BLV_MASK) << CFGR_BLV_SHIFT) | ((0x3 & (law)) << 1))
  107 +#define ALS_CMPR_CFG_VAL(filt) ((0x7 & (filt)) << 1)
  108 +
  109 +struct adp8870_bl {
  110 + struct i2c_client *client;
  111 + struct backlight_device *bl;
  112 + struct adp8870_led *led;
  113 + struct adp8870_backlight_platform_data *pdata;
  114 + struct mutex lock;
  115 + unsigned long cached_daylight_max;
  116 + int id;
  117 + int revid;
  118 + int current_brightness;
  119 +};
  120 +
  121 +struct adp8870_led {
  122 + struct led_classdev cdev;
  123 + struct work_struct work;
  124 + struct i2c_client *client;
  125 + enum led_brightness new_brightness;
  126 + int id;
  127 + int flags;
  128 +};
  129 +
  130 +static int adp8870_read(struct i2c_client *client, int reg, uint8_t *val)
  131 +{
  132 + int ret;
  133 +
  134 + ret = i2c_smbus_read_byte_data(client, reg);
  135 + if (ret < 0) {
  136 + dev_err(&client->dev, "failed reading at 0x%02x\n", reg);
  137 + return ret;
  138 + }
  139 +
  140 + *val = ret;
  141 + return 0;
  142 +}
  143 +
  144 +
  145 +static int adp8870_write(struct i2c_client *client, u8 reg, u8 val)
  146 +{
  147 + int ret = i2c_smbus_write_byte_data(client, reg, val);
  148 + if (ret)
  149 + dev_err(&client->dev, "failed to write\n");
  150 +
  151 + return ret;
  152 +}
  153 +
  154 +static int adp8870_set_bits(struct i2c_client *client, int reg, uint8_t bit_mask)
  155 +{
  156 + struct adp8870_bl *data = i2c_get_clientdata(client);
  157 + uint8_t reg_val;
  158 + int ret;
  159 +
  160 + mutex_lock(&data->lock);
  161 +
  162 + ret = adp8870_read(client, reg, &reg_val);
  163 +
  164 + if (!ret && ((reg_val & bit_mask) == 0)) {
  165 + reg_val |= bit_mask;
  166 + ret = adp8870_write(client, reg, reg_val);
  167 + }
  168 +
  169 + mutex_unlock(&data->lock);
  170 + return ret;
  171 +}
  172 +
  173 +static int adp8870_clr_bits(struct i2c_client *client, int reg, uint8_t bit_mask)
  174 +{
  175 + struct adp8870_bl *data = i2c_get_clientdata(client);
  176 + uint8_t reg_val;
  177 + int ret;
  178 +
  179 + mutex_lock(&data->lock);
  180 +
  181 + ret = adp8870_read(client, reg, &reg_val);
  182 +
  183 + if (!ret && (reg_val & bit_mask)) {
  184 + reg_val &= ~bit_mask;
  185 + ret = adp8870_write(client, reg, reg_val);
  186 + }
  187 +
  188 + mutex_unlock(&data->lock);
  189 + return ret;
  190 +}
  191 +
  192 +/*
  193 + * Independent sink / LED
  194 + */
  195 +#if defined(ADP8870_USE_LEDS)
  196 +static void adp8870_led_work(struct work_struct *work)
  197 +{
  198 + struct adp8870_led *led = container_of(work, struct adp8870_led, work);
  199 + adp8870_write(led->client, ADP8870_ISC1 + led->id - 1,
  200 + led->new_brightness >> 1);
  201 +}
  202 +
  203 +static void adp8870_led_set(struct led_classdev *led_cdev,
  204 + enum led_brightness value)
  205 +{
  206 + struct adp8870_led *led;
  207 +
  208 + led = container_of(led_cdev, struct adp8870_led, cdev);
  209 + led->new_brightness = value;
  210 + /*
  211 + * Use workqueue for IO since I2C operations can sleep.
  212 + */
  213 + schedule_work(&led->work);
  214 +}
  215 +
  216 +static int adp8870_led_setup(struct adp8870_led *led)
  217 +{
  218 + struct i2c_client *client = led->client;
  219 + int ret = 0;
  220 +
  221 + ret = adp8870_write(client, ADP8870_ISC1 + led->id - 1, 0);
  222 + if (ret)
  223 + return ret;
  224 +
  225 + ret = adp8870_set_bits(client, ADP8870_ISCC, 1 << (led->id - 1));
  226 + if (ret)
  227 + return ret;
  228 +
  229 + if (led->id > 4)
  230 + ret = adp8870_set_bits(client, ADP8870_ISCT1,
  231 + (led->flags & 0x3) << ((led->id - 5) * 2));
  232 + else
  233 + ret = adp8870_set_bits(client, ADP8870_ISCT2,
  234 + (led->flags & 0x3) << ((led->id - 1) * 2));
  235 +
  236 + return ret;
  237 +}
  238 +
  239 +static int __devinit adp8870_led_probe(struct i2c_client *client)
  240 +{
  241 + struct adp8870_backlight_platform_data *pdata =
  242 + client->dev.platform_data;
  243 + struct adp8870_bl *data = i2c_get_clientdata(client);
  244 + struct adp8870_led *led, *led_dat;
  245 + struct led_info *cur_led;
  246 + int ret, i;
  247 +
  248 +
  249 + led = kcalloc(pdata->num_leds, sizeof(*led), GFP_KERNEL);
  250 + if (led == NULL) {
  251 + dev_err(&client->dev, "failed to alloc memory\n");
  252 + return -ENOMEM;
  253 + }
  254 +
  255 + ret = adp8870_write(client, ADP8870_ISCLAW, pdata->led_fade_law);
  256 + if (ret)
  257 + goto err_free;
  258 +
  259 + ret = adp8870_write(client, ADP8870_ISCT1,
  260 + (pdata->led_on_time & 0x3) << 6);
  261 + if (ret)
  262 + goto err_free;
  263 +
  264 + ret = adp8870_write(client, ADP8870_ISCF,
  265 + FADE_VAL(pdata->led_fade_in, pdata->led_fade_out));
  266 + if (ret)
  267 + goto err_free;
  268 +
  269 + for (i = 0; i < pdata->num_leds; ++i) {
  270 + cur_led = &pdata->leds[i];
  271 + led_dat = &led[i];
  272 +
  273 + led_dat->id = cur_led->flags & ADP8870_FLAG_LED_MASK;
  274 +
  275 + if (led_dat->id > 7 || led_dat->id < 1) {
  276 + dev_err(&client->dev, "Invalid LED ID %d\n",
  277 + led_dat->id);
  278 + goto err;
  279 + }
  280 +
  281 + if (pdata->bl_led_assign & (1 << (led_dat->id - 1))) {
  282 + dev_err(&client->dev, "LED %d used by Backlight\n",
  283 + led_dat->id);
  284 + goto err;
  285 + }
  286 +
  287 + led_dat->cdev.name = cur_led->name;
  288 + led_dat->cdev.default_trigger = cur_led->default_trigger;
  289 + led_dat->cdev.brightness_set = adp8870_led_set;
  290 + led_dat->cdev.brightness = LED_OFF;
  291 + led_dat->flags = cur_led->flags >> FLAG_OFFT_SHIFT;
  292 + led_dat->client = client;
  293 + led_dat->new_brightness = LED_OFF;
  294 + INIT_WORK(&led_dat->work, adp8870_led_work);
  295 +
  296 + ret = led_classdev_register(&client->dev, &led_dat->cdev);
  297 + if (ret) {
  298 + dev_err(&client->dev, "failed to register LED %d\n",
  299 + led_dat->id);
  300 + goto err;
  301 + }
  302 +
  303 + ret = adp8870_led_setup(led_dat);
  304 + if (ret) {
  305 + dev_err(&client->dev, "failed to write\n");
  306 + i++;
  307 + goto err;
  308 + }
  309 + }
  310 +
  311 + data->led = led;
  312 +
  313 + return 0;
  314 +
  315 + err:
  316 + for (i = i - 1; i >= 0; --i) {
  317 + led_classdev_unregister(&led[i].cdev);
  318 + cancel_work_sync(&led[i].work);
  319 + }
  320 +
  321 + err_free:
  322 + kfree(led);
  323 +
  324 + return ret;
  325 +}
  326 +
  327 +static int __devexit adp8870_led_remove(struct i2c_client *client)
  328 +{
  329 + struct adp8870_backlight_platform_data *pdata =
  330 + client->dev.platform_data;
  331 + struct adp8870_bl *data = i2c_get_clientdata(client);
  332 + int i;
  333 +
  334 + for (i = 0; i < pdata->num_leds; i++) {
  335 + led_classdev_unregister(&data->led[i].cdev);
  336 + cancel_work_sync(&data->led[i].work);
  337 + }
  338 +
  339 + kfree(data->led);
  340 + return 0;
  341 +}
  342 +#else
  343 +static int __devinit adp8870_led_probe(struct i2c_client *client)
  344 +{
  345 + return 0;
  346 +}
  347 +
  348 +static int __devexit adp8870_led_remove(struct i2c_client *client)
  349 +{
  350 + return 0;
  351 +}
  352 +#endif
  353 +
  354 +static int adp8870_bl_set(struct backlight_device *bl, int brightness)
  355 +{
  356 + struct adp8870_bl *data = bl_get_data(bl);
  357 + struct i2c_client *client = data->client;
  358 + int ret = 0;
  359 +
  360 + if (data->pdata->en_ambl_sens) {
  361 + if ((brightness > 0) && (brightness < ADP8870_MAX_BRIGHTNESS)) {
  362 + /* Disable Ambient Light auto adjust */
  363 + ret = adp8870_clr_bits(client, ADP8870_MDCR,
  364 + CMP_AUTOEN);
  365 + if (ret)
  366 + return ret;
  367 + ret = adp8870_write(client, ADP8870_BLMX1, brightness);
  368 + if (ret)
  369 + return ret;
  370 + } else {
  371 + /*
  372 + * MAX_BRIGHTNESS -> Enable Ambient Light auto adjust
  373 + * restore daylight l1 sysfs brightness
  374 + */
  375 + ret = adp8870_write(client, ADP8870_BLMX1,
  376 + data->cached_daylight_max);
  377 + if (ret)
  378 + return ret;
  379 +
  380 + ret = adp8870_set_bits(client, ADP8870_MDCR,
  381 + CMP_AUTOEN);
  382 + if (ret)
  383 + return ret;
  384 + }
  385 + } else {
  386 + ret = adp8870_write(client, ADP8870_BLMX1, brightness);
  387 + if (ret)
  388 + return ret;
  389 + }
  390 +
  391 + if (data->current_brightness && brightness == 0)
  392 + ret = adp8870_set_bits(client,
  393 + ADP8870_MDCR, DIM_EN);
  394 + else if (data->current_brightness == 0 && brightness)
  395 + ret = adp8870_clr_bits(client,
  396 + ADP8870_MDCR, DIM_EN);
  397 +
  398 + if (!ret)
  399 + data->current_brightness = brightness;
  400 +
  401 + return ret;
  402 +}
  403 +
  404 +static int adp8870_bl_update_status(struct backlight_device *bl)
  405 +{
  406 + int brightness = bl->props.brightness;
  407 + if (bl->props.power != FB_BLANK_UNBLANK)
  408 + brightness = 0;
  409 +
  410 + if (bl->props.fb_blank != FB_BLANK_UNBLANK)
  411 + brightness = 0;
  412 +
  413 + return adp8870_bl_set(bl, brightness);
  414 +}
  415 +
  416 +static int adp8870_bl_get_brightness(struct backlight_device *bl)
  417 +{
  418 + struct adp8870_bl *data = bl_get_data(bl);
  419 +
  420 + return data->current_brightness;
  421 +}
  422 +
  423 +static const struct backlight_ops adp8870_bl_ops = {
  424 + .update_status = adp8870_bl_update_status,
  425 + .get_brightness = adp8870_bl_get_brightness,
  426 +};
  427 +
  428 +static int adp8870_bl_setup(struct backlight_device *bl)
  429 +{
  430 + struct adp8870_bl *data = bl_get_data(bl);
  431 + struct i2c_client *client = data->client;
  432 + struct adp8870_backlight_platform_data *pdata = data->pdata;
  433 + int ret = 0;
  434 +
  435 + ret = adp8870_write(client, ADP8870_BLSEL, ~pdata->bl_led_assign);
  436 + if (ret)
  437 + return ret;
  438 +
  439 + ret = adp8870_write(client, ADP8870_PWMLED, pdata->pwm_assign);
  440 + if (ret)
  441 + return ret;
  442 +
  443 + ret = adp8870_write(client, ADP8870_BLMX1, pdata->l1_daylight_max);
  444 + if (ret)
  445 + return ret;
  446 +
  447 + ret = adp8870_write(client, ADP8870_BLDM1, pdata->l1_daylight_dim);
  448 + if (ret)
  449 + return ret;
  450 +
  451 + if (pdata->en_ambl_sens) {
  452 + data->cached_daylight_max = pdata->l1_daylight_max;
  453 + ret = adp8870_write(client, ADP8870_BLMX2,
  454 + pdata->l2_bright_max);
  455 + if (ret)
  456 + return ret;
  457 + ret = adp8870_write(client, ADP8870_BLDM2,
  458 + pdata->l2_bright_dim);
  459 + if (ret)
  460 + return ret;
  461 +
  462 + ret = adp8870_write(client, ADP8870_BLMX3,
  463 + pdata->l3_office_max);
  464 + if (ret)
  465 + return ret;
  466 + ret = adp8870_write(client, ADP8870_BLDM3,
  467 + pdata->l3_office_dim);
  468 + if (ret)
  469 + return ret;
  470 +
  471 + ret = adp8870_write(client, ADP8870_BLMX4,
  472 + pdata->l4_indoor_max);
  473 + if (ret)
  474 + return ret;
  475 +
  476 + ret = adp8870_write(client, ADP8870_BLDM4,
  477 + pdata->l4_indor_dim);
  478 + if (ret)
  479 + return ret;
  480 +
  481 + ret = adp8870_write(client, ADP8870_BLMX5,
  482 + pdata->l5_dark_max);
  483 + if (ret)
  484 + return ret;
  485 +
  486 + ret = adp8870_write(client, ADP8870_BLDM5,
  487 + pdata->l5_dark_dim);
  488 + if (ret)
  489 + return ret;
  490 +
  491 + ret = adp8870_write(client, ADP8870_L2TRP, pdata->l2_trip);
  492 + if (ret)
  493 + return ret;
  494 +
  495 + ret = adp8870_write(client, ADP8870_L2HYS, pdata->l2_hyst);
  496 + if (ret)
  497 + return ret;
  498 +
  499 + ret = adp8870_write(client, ADP8870_L3TRP, pdata->l3_trip);
  500 + if (ret)
  501 + return ret;
  502 +
  503 + ret = adp8870_write(client, ADP8870_L3HYS, pdata->l3_hyst);
  504 + if (ret)
  505 + return ret;
  506 +
  507 + ret = adp8870_write(client, ADP8870_L4TRP, pdata->l4_trip);
  508 + if (ret)
  509 + return ret;
  510 +
  511 + ret = adp8870_write(client, ADP8870_L4HYS, pdata->l4_hyst);
  512 + if (ret)
  513 + return ret;
  514 +
  515 + ret = adp8870_write(client, ADP8870_L5TRP, pdata->l5_trip);
  516 + if (ret)
  517 + return ret;
  518 +
  519 + ret = adp8870_write(client, ADP8870_L5HYS, pdata->l5_hyst);
  520 + if (ret)
  521 + return ret;
  522 +
  523 + ret = adp8870_write(client, ADP8870_ALS1_EN, L5_EN | L4_EN |
  524 + L3_EN | L2_EN);
  525 + if (ret)
  526 + return ret;
  527 +
  528 + ret = adp8870_write(client, ADP8870_CMP_CTL,
  529 + ALS_CMPR_CFG_VAL(pdata->abml_filt));
  530 + if (ret)
  531 + return ret;
  532 + }
  533 +
  534 + ret = adp8870_write(client, ADP8870_CFGR,
  535 + BL_CFGR_VAL(pdata->bl_fade_law, 0));
  536 + if (ret)
  537 + return ret;
  538 +
  539 + ret = adp8870_write(client, ADP8870_BLFR, FADE_VAL(pdata->bl_fade_in,
  540 + pdata->bl_fade_out));
  541 + if (ret)
  542 + return ret;
  543 + /*
  544 + * ADP8870 Rev0 requires GDWN_DIS bit set
  545 + */
  546 +
  547 + ret = adp8870_set_bits(client, ADP8870_MDCR, BLEN | DIM_EN | NSTBY |
  548 + (data->revid == 0 ? GDWN_DIS : 0));
  549 +
  550 + return ret;
  551 +}
  552 +
  553 +static ssize_t adp8870_show(struct device *dev, char *buf, int reg)
  554 +{
  555 + struct adp8870_bl *data = dev_get_drvdata(dev);
  556 + int error;
  557 + uint8_t reg_val;
  558 +
  559 + mutex_lock(&data->lock);
  560 + error = adp8870_read(data->client, reg, &reg_val);
  561 + mutex_unlock(&data->lock);
  562 +
  563 + if (error < 0)
  564 + return error;
  565 +
  566 + return sprintf(buf, "%u\n", reg_val);
  567 +}
  568 +
  569 +static ssize_t adp8870_store(struct device *dev, const char *buf,
  570 + size_t count, int reg)
  571 +{
  572 + struct adp8870_bl *data = dev_get_drvdata(dev);
  573 + unsigned long val;
  574 + int ret;
  575 +
  576 + ret = strict_strtoul(buf, 10, &val);
  577 + if (ret)
  578 + return ret;
  579 +
  580 + mutex_lock(&data->lock);
  581 + adp8870_write(data->client, reg, val);
  582 + mutex_unlock(&data->lock);
  583 +
  584 + return count;
  585 +}
  586 +
  587 +static ssize_t adp8870_bl_l5_dark_max_show(struct device *dev,
  588 + struct device_attribute *attr, char *buf)
  589 +{
  590 + return adp8870_show(dev, buf, ADP8870_BLMX5);
  591 +}
  592 +
  593 +static ssize_t adp8870_bl_l5_dark_max_store(struct device *dev,
  594 + struct device_attribute *attr, const char *buf, size_t count)
  595 +{
  596 + return adp8870_store(dev, buf, count, ADP8870_BLMX5);
  597 +}
  598 +static DEVICE_ATTR(l5_dark_max, 0664, adp8870_bl_l5_dark_max_show,
  599 + adp8870_bl_l5_dark_max_store);
  600 +
  601 +
  602 +static ssize_t adp8870_bl_l4_indoor_max_show(struct device *dev,
  603 + struct device_attribute *attr, char *buf)
  604 +{
  605 + return adp8870_show(dev, buf, ADP8870_BLMX4);
  606 +}
  607 +
  608 +static ssize_t adp8870_bl_l4_indoor_max_store(struct device *dev,
  609 + struct device_attribute *attr, const char *buf, size_t count)
  610 +{
  611 + return adp8870_store(dev, buf, count, ADP8870_BLMX4);
  612 +}
  613 +static DEVICE_ATTR(l4_indoor_max, 0664, adp8870_bl_l4_indoor_max_show,
  614 + adp8870_bl_l4_indoor_max_store);
  615 +
  616 +
  617 +static ssize_t adp8870_bl_l3_office_max_show(struct device *dev,
  618 + struct device_attribute *attr, char *buf)
  619 +{
  620 + return adp8870_show(dev, buf, ADP8870_BLMX3);
  621 +}
  622 +
  623 +static ssize_t adp8870_bl_l3_office_max_store(struct device *dev,
  624 + struct device_attribute *attr, const char *buf, size_t count)
  625 +{
  626 + return adp8870_store(dev, buf, count, ADP8870_BLMX3);
  627 +}
  628 +
  629 +static DEVICE_ATTR(l3_office_max, 0664, adp8870_bl_l3_office_max_show,
  630 + adp8870_bl_l3_office_max_store);
  631 +
  632 +static ssize_t adp8870_bl_l2_bright_max_show(struct device *dev,
  633 + struct device_attribute *attr, char *buf)
  634 +{
  635 + return adp8870_show(dev, buf, ADP8870_BLMX2);
  636 +}
  637 +
  638 +static ssize_t adp8870_bl_l2_bright_max_store(struct device *dev,
  639 + struct device_attribute *attr, const char *buf, size_t count)
  640 +{
  641 + return adp8870_store(dev, buf, count, ADP8870_BLMX2);
  642 +}
  643 +static DEVICE_ATTR(l2_bright_max, 0664, adp8870_bl_l2_bright_max_show,
  644 + adp8870_bl_l2_bright_max_store);
  645 +
  646 +static ssize_t adp8870_bl_l1_daylight_max_show(struct device *dev,
  647 + struct device_attribute *attr, char *buf)
  648 +{
  649 + return adp8870_show(dev, buf, ADP8870_BLMX1);
  650 +}
  651 +
  652 +static ssize_t adp8870_bl_l1_daylight_max_store(struct device *dev,
  653 + struct device_attribute *attr, const char *buf, size_t count)
  654 +{
  655 + struct adp8870_bl *data = dev_get_drvdata(dev);
  656 + int ret = strict_strtoul(buf, 10, &data->cached_daylight_max);
  657 + if (ret)
  658 + return ret;
  659 +
  660 + return adp8870_store(dev, buf, count, ADP8870_BLMX1);
  661 +}
  662 +static DEVICE_ATTR(l1_daylight_max, 0664, adp8870_bl_l1_daylight_max_show,
  663 + adp8870_bl_l1_daylight_max_store);
  664 +
  665 +static ssize_t adp8870_bl_l5_dark_dim_show(struct device *dev,
  666 + struct device_attribute *attr, char *buf)
  667 +{
  668 + return adp8870_show(dev, buf, ADP8870_BLDM5);
  669 +}
  670 +
  671 +static ssize_t adp8870_bl_l5_dark_dim_store(struct device *dev,
  672 + struct device_attribute *attr,
  673 + const char *buf, size_t count)
  674 +{
  675 + return adp8870_store(dev, buf, count, ADP8870_BLDM5);
  676 +}
  677 +static DEVICE_ATTR(l5_dark_dim, 0664, adp8870_bl_l5_dark_dim_show,
  678 + adp8870_bl_l5_dark_dim_store);
  679 +
  680 +static ssize_t adp8870_bl_l4_indoor_dim_show(struct device *dev,
  681 + struct device_attribute *attr, char *buf)
  682 +{
  683 + return adp8870_show(dev, buf, ADP8870_BLDM4);
  684 +}
  685 +
  686 +static ssize_t adp8870_bl_l4_indoor_dim_store(struct device *dev,
  687 + struct device_attribute *attr,
  688 + const char *buf, size_t count)
  689 +{
  690 + return adp8870_store(dev, buf, count, ADP8870_BLDM4);
  691 +}
  692 +static DEVICE_ATTR(l4_indoor_dim, 0664, adp8870_bl_l4_indoor_dim_show,
  693 + adp8870_bl_l4_indoor_dim_store);
  694 +
  695 +
  696 +static ssize_t adp8870_bl_l3_office_dim_show(struct device *dev,
  697 + struct device_attribute *attr, char *buf)
  698 +{
  699 + return adp8870_show(dev, buf, ADP8870_BLDM3);
  700 +}
  701 +
  702 +static ssize_t adp8870_bl_l3_office_dim_store(struct device *dev,
  703 + struct device_attribute *attr,
  704 + const char *buf, size_t count)
  705 +{
  706 + return adp8870_store(dev, buf, count, ADP8870_BLDM3);
  707 +}
  708 +static DEVICE_ATTR(l3_office_dim, 0664, adp8870_bl_l3_office_dim_show,
  709 + adp8870_bl_l3_office_dim_store);
  710 +
  711 +static ssize_t adp8870_bl_l2_bright_dim_show(struct device *dev,
  712 + struct device_attribute *attr, char *buf)
  713 +{
  714 + return adp8870_show(dev, buf, ADP8870_BLDM2);
  715 +}
  716 +
  717 +static ssize_t adp8870_bl_l2_bright_dim_store(struct device *dev,
  718 + struct device_attribute *attr,
  719 + const char *buf, size_t count)
  720 +{
  721 + return adp8870_store(dev, buf, count, ADP8870_BLDM2);
  722 +}
  723 +static DEVICE_ATTR(l2_bright_dim, 0664, adp8870_bl_l2_bright_dim_show,
  724 + adp8870_bl_l2_bright_dim_store);
  725 +
  726 +static ssize_t adp8870_bl_l1_daylight_dim_show(struct device *dev,
  727 + struct device_attribute *attr, char *buf)
  728 +{
  729 + return adp8870_show(dev, buf, ADP8870_BLDM1);
  730 +}
  731 +
  732 +static ssize_t adp8870_bl_l1_daylight_dim_store(struct device *dev,
  733 + struct device_attribute *attr,
  734 + const char *buf, size_t count)
  735 +{
  736 + return adp8870_store(dev, buf, count, ADP8870_BLDM1);
  737 +}
  738 +static DEVICE_ATTR(l1_daylight_dim, 0664, adp8870_bl_l1_daylight_dim_show,
  739 + adp8870_bl_l1_daylight_dim_store);
  740 +
  741 +#ifdef ADP8870_EXT_FEATURES
  742 +static ssize_t adp8870_bl_ambient_light_level_show(struct device *dev,
  743 + struct device_attribute *attr, char *buf)
  744 +{
  745 + struct adp8870_bl *data = dev_get_drvdata(dev);
  746 + int error;
  747 + uint8_t reg_val;
  748 + uint16_t ret_val;
  749 +
  750 + mutex_lock(&data->lock);
  751 + error = adp8870_read(data->client, ADP8870_PH1LEVL, &reg_val);
  752 + if (error < 0) {
  753 + mutex_unlock(&data->lock);
  754 + return error;
  755 + }
  756 + ret_val = reg_val;
  757 + error = adp8870_read(data->client, ADP8870_PH1LEVH, &reg_val);
  758 + mutex_unlock(&data->lock);
  759 +
  760 + if (error < 0)
  761 + return error;
  762 +
  763 + /* Return 13-bit conversion value for the first light sensor */
  764 + ret_val += (reg_val & 0x1F) << 8;
  765 +
  766 + return sprintf(buf, "%u\n", ret_val);
  767 +}
  768 +static DEVICE_ATTR(ambient_light_level, 0444,
  769 + adp8870_bl_ambient_light_level_show, NULL);
  770 +
  771 +static ssize_t adp8870_bl_ambient_light_zone_show(struct device *dev,
  772 + struct device_attribute *attr, char *buf)
  773 +{
  774 + struct adp8870_bl *data = dev_get_drvdata(dev);
  775 + int error;
  776 + uint8_t reg_val;
  777 +
  778 + mutex_lock(&data->lock);
  779 + error = adp8870_read(data->client, ADP8870_CFGR, &reg_val);
  780 + mutex_unlock(&data->lock);
  781 +
  782 + if (error < 0)
  783 + return error;
  784 +
  785 + return sprintf(buf, "%u\n",
  786 + ((reg_val >> CFGR_BLV_SHIFT) & CFGR_BLV_MASK) + 1);
  787 +}
  788 +
  789 +static ssize_t adp8870_bl_ambient_light_zone_store(struct device *dev,
  790 + struct device_attribute *attr,
  791 + const char *buf, size_t count)
  792 +{
  793 + struct adp8870_bl *data = dev_get_drvdata(dev);
  794 + unsigned long val;
  795 + uint8_t reg_val;
  796 + int ret;
  797 +
  798 + ret = strict_strtoul(buf, 10, &val);
  799 + if (ret)
  800 + return ret;
  801 +
  802 + if (val == 0) {
  803 + /* Enable automatic ambient light sensing */
  804 + adp8870_set_bits(data->client, ADP8870_MDCR, CMP_AUTOEN);
  805 + } else if ((val > 0) && (val < 6)) {
  806 + /* Disable automatic ambient light sensing */
  807 + adp8870_clr_bits(data->client, ADP8870_MDCR, CMP_AUTOEN);
  808 +
  809 + /* Set user supplied ambient light zone */
  810 + mutex_lock(&data->lock);
  811 + adp8870_read(data->client, ADP8870_CFGR, &reg_val);
  812 + reg_val &= ~(CFGR_BLV_MASK << CFGR_BLV_SHIFT);
  813 + reg_val |= (val - 1) << CFGR_BLV_SHIFT;
  814 + adp8870_write(data->client, ADP8870_CFGR, reg_val);
  815 + mutex_unlock(&data->lock);
  816 + }
  817 +
  818 + return count;
  819 +}
  820 +static DEVICE_ATTR(ambient_light_zone, 0664,
  821 + adp8870_bl_ambient_light_zone_show,
  822 + adp8870_bl_ambient_light_zone_store);
  823 +#endif
  824 +
  825 +static struct attribute *adp8870_bl_attributes[] = {
  826 + &dev_attr_l5_dark_max.attr,
  827 + &dev_attr_l5_dark_dim.attr,
  828 + &dev_attr_l4_indoor_max.attr,
  829 + &dev_attr_l4_indoor_dim.attr,
  830 + &dev_attr_l3_office_max.attr,
  831 + &dev_attr_l3_office_dim.attr,
  832 + &dev_attr_l2_bright_max.attr,
  833 + &dev_attr_l2_bright_dim.attr,
  834 + &dev_attr_l1_daylight_max.attr,
  835 + &dev_attr_l1_daylight_dim.attr,
  836 +#ifdef ADP8870_EXT_FEATURES
  837 + &dev_attr_ambient_light_level.attr,
  838 + &dev_attr_ambient_light_zone.attr,
  839 +#endif
  840 + NULL
  841 +};
  842 +
  843 +static const struct attribute_group adp8870_bl_attr_group = {
  844 + .attrs = adp8870_bl_attributes,
  845 +};
  846 +
  847 +static int __devinit adp8870_probe(struct i2c_client *client,
  848 + const struct i2c_device_id *id)
  849 +{
  850 + struct backlight_properties props;
  851 + struct backlight_device *bl;
  852 + struct adp8870_bl *data;
  853 + struct adp8870_backlight_platform_data *pdata =
  854 + client->dev.platform_data;
  855 + uint8_t reg_val;
  856 + int ret;
  857 +
  858 + if (!i2c_check_functionality(client->adapter,
  859 + I2C_FUNC_SMBUS_BYTE_DATA)) {
  860 + dev_err(&client->dev, "SMBUS Byte Data not Supported\n");
  861 + return -EIO;
  862 + }
  863 +
  864 + if (!pdata) {
  865 + dev_err(&client->dev, "no platform data?\n");
  866 + return -EINVAL;
  867 + }
  868 +
  869 + ret = adp8870_read(client, ADP8870_MFDVID, &reg_val);
  870 + if (ret < 0)
  871 + return -EIO;
  872 +
  873 + if (ADP8870_MANID(reg_val) != ADP8870_MANUFID) {
  874 + dev_err(&client->dev, "failed to probe\n");
  875 + return -ENODEV;
  876 + }
  877 +
  878 + data = kzalloc(sizeof(*data), GFP_KERNEL);
  879 + if (data == NULL)
  880 + return -ENOMEM;
  881 +
  882 + data->revid = ADP8870_DEVID(reg_val);
  883 + data->client = client;
  884 + data->pdata = pdata;
  885 + data->id = id->driver_data;
  886 + data->current_brightness = 0;
  887 + i2c_set_clientdata(client, data);
  888 +
  889 + mutex_init(&data->lock);
  890 +
  891 + memset(&props, 0, sizeof(props));
  892 + props.max_brightness = props.brightness = ADP8870_MAX_BRIGHTNESS;
  893 + bl = backlight_device_register(dev_driver_string(&client->dev),
  894 + &client->dev, data, &adp8870_bl_ops, &props);
  895 + if (IS_ERR(bl)) {
  896 + dev_err(&client->dev, "failed to register backlight\n");
  897 + ret = PTR_ERR(bl);
  898 + goto out2;
  899 + }
  900 +
  901 + data->bl = bl;
  902 +
  903 + if (pdata->en_ambl_sens)
  904 + ret = sysfs_create_group(&bl->dev.kobj,
  905 + &adp8870_bl_attr_group);
  906 +
  907 + if (ret) {
  908 + dev_err(&client->dev, "failed to register sysfs\n");
  909 + goto out1;
  910 + }
  911 +
  912 + ret = adp8870_bl_setup(bl);
  913 + if (ret) {
  914 + ret = -EIO;
  915 + goto out;
  916 + }
  917 +
  918 + backlight_update_status(bl);
  919 +
  920 + dev_info(&client->dev, "Rev.%d Backlight\n", data->revid);
  921 +
  922 + if (pdata->num_leds)
  923 + adp8870_led_probe(client);
  924 +
  925 + return 0;
  926 +
  927 +out:
  928 + if (data->pdata->en_ambl_sens)
  929 + sysfs_remove_group(&data->bl->dev.kobj,
  930 + &adp8870_bl_attr_group);
  931 +out1:
  932 + backlight_device_unregister(bl);
  933 +out2:
  934 + i2c_set_clientdata(client, NULL);
  935 + kfree(data);
  936 +
  937 + return ret;
  938 +}
  939 +
  940 +static int __devexit adp8870_remove(struct i2c_client *client)
  941 +{
  942 + struct adp8870_bl *data = i2c_get_clientdata(client);
  943 +
  944 + adp8870_clr_bits(client, ADP8870_MDCR, NSTBY);
  945 +
  946 + if (data->led)
  947 + adp8870_led_remove(client);
  948 +
  949 + if (data->pdata->en_ambl_sens)
  950 + sysfs_remove_group(&data->bl->dev.kobj,
  951 + &adp8870_bl_attr_group);
  952 +
  953 + backlight_device_unregister(data->bl);
  954 + i2c_set_clientdata(client, NULL);
  955 + kfree(data);
  956 +
  957 + return 0;
  958 +}
  959 +
  960 +#ifdef CONFIG_PM
  961 +static int adp8870_i2c_suspend(struct i2c_client *client, pm_message_t message)
  962 +{
  963 + adp8870_clr_bits(client, ADP8870_MDCR, NSTBY);
  964 +
  965 + return 0;
  966 +}
  967 +
  968 +static int adp8870_i2c_resume(struct i2c_client *client)
  969 +{
  970 + adp8870_set_bits(client, ADP8870_MDCR, NSTBY);
  971 +
  972 + return 0;
  973 +}
  974 +#else
  975 +#define adp8870_i2c_suspend NULL
  976 +#define adp8870_i2c_resume NULL
  977 +#endif
  978 +
  979 +static const struct i2c_device_id adp8870_id[] = {
  980 + { "adp8870", 0 },
  981 + { }
  982 +};
  983 +MODULE_DEVICE_TABLE(i2c, adp8870_id);
  984 +
  985 +static struct i2c_driver adp8870_driver = {
  986 + .driver = {
  987 + .name = KBUILD_MODNAME,
  988 + },
  989 + .probe = adp8870_probe,
  990 + .remove = __devexit_p(adp8870_remove),
  991 + .suspend = adp8870_i2c_suspend,
  992 + .resume = adp8870_i2c_resume,
  993 + .id_table = adp8870_id,
  994 +};
  995 +
  996 +static int __init adp8870_init(void)
  997 +{
  998 + return i2c_add_driver(&adp8870_driver);
  999 +}
  1000 +module_init(adp8870_init);
  1001 +
  1002 +static void __exit adp8870_exit(void)
  1003 +{
  1004 + i2c_del_driver(&adp8870_driver);
  1005 +}
  1006 +module_exit(adp8870_exit);
  1007 +
  1008 +MODULE_LICENSE("GPL v2");
  1009 +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
  1010 +MODULE_DESCRIPTION("ADP8870 Backlight driver");
  1011 +MODULE_ALIAS("platform:adp8870-backlight");
include/linux/i2c/adp8870.h
  1 +/*
  2 + * Definitions and platform data for Analog Devices
  3 + * Backlight drivers ADP8870
  4 + *
  5 + * Copyright 2009-2010 Analog Devices Inc.
  6 + *
  7 + * Licensed under the GPL-2 or later.
  8 + */
  9 +
  10 +#ifndef __LINUX_I2C_ADP8870_H
  11 +#define __LINUX_I2C_ADP8870_H
  12 +
  13 +#define ID_ADP8870 8870
  14 +
  15 +#define ADP8870_MAX_BRIGHTNESS 0x7F
  16 +#define FLAG_OFFT_SHIFT 8
  17 +
  18 +/*
  19 + * LEDs subdevice platform data
  20 + */
  21 +
  22 +#define ADP8870_LED_DIS_BLINK (0 << FLAG_OFFT_SHIFT)
  23 +#define ADP8870_LED_OFFT_600ms (1 << FLAG_OFFT_SHIFT)
  24 +#define ADP8870_LED_OFFT_1200ms (2 << FLAG_OFFT_SHIFT)
  25 +#define ADP8870_LED_OFFT_1800ms (3 << FLAG_OFFT_SHIFT)
  26 +
  27 +#define ADP8870_LED_ONT_200ms 0
  28 +#define ADP8870_LED_ONT_600ms 1
  29 +#define ADP8870_LED_ONT_800ms 2
  30 +#define ADP8870_LED_ONT_1200ms 3
  31 +
  32 +#define ADP8870_LED_D7 (7)
  33 +#define ADP8870_LED_D6 (6)
  34 +#define ADP8870_LED_D5 (5)
  35 +#define ADP8870_LED_D4 (4)
  36 +#define ADP8870_LED_D3 (3)
  37 +#define ADP8870_LED_D2 (2)
  38 +#define ADP8870_LED_D1 (1)
  39 +
  40 +/*
  41 + * Backlight subdevice platform data
  42 + */
  43 +
  44 +#define ADP8870_BL_D7 (1 << 6)
  45 +#define ADP8870_BL_D6 (1 << 5)
  46 +#define ADP8870_BL_D5 (1 << 4)
  47 +#define ADP8870_BL_D4 (1 << 3)
  48 +#define ADP8870_BL_D3 (1 << 2)
  49 +#define ADP8870_BL_D2 (1 << 1)
  50 +#define ADP8870_BL_D1 (1 << 0)
  51 +
  52 +#define ADP8870_FADE_T_DIS 0 /* Fade Timer Disabled */
  53 +#define ADP8870_FADE_T_300ms 1 /* 0.3 Sec */
  54 +#define ADP8870_FADE_T_600ms 2
  55 +#define ADP8870_FADE_T_900ms 3
  56 +#define ADP8870_FADE_T_1200ms 4
  57 +#define ADP8870_FADE_T_1500ms 5
  58 +#define ADP8870_FADE_T_1800ms 6
  59 +#define ADP8870_FADE_T_2100ms 7
  60 +#define ADP8870_FADE_T_2400ms 8
  61 +#define ADP8870_FADE_T_2700ms 9
  62 +#define ADP8870_FADE_T_3000ms 10
  63 +#define ADP8870_FADE_T_3500ms 11
  64 +#define ADP8870_FADE_T_4000ms 12
  65 +#define ADP8870_FADE_T_4500ms 13
  66 +#define ADP8870_FADE_T_5000ms 14
  67 +#define ADP8870_FADE_T_5500ms 15 /* 5.5 Sec */
  68 +
  69 +#define ADP8870_FADE_LAW_LINEAR 0
  70 +#define ADP8870_FADE_LAW_SQUARE 1
  71 +#define ADP8870_FADE_LAW_CUBIC1 2
  72 +#define ADP8870_FADE_LAW_CUBIC2 3
  73 +
  74 +#define ADP8870_BL_AMBL_FILT_80ms 0 /* Light sensor filter time */
  75 +#define ADP8870_BL_AMBL_FILT_160ms 1
  76 +#define ADP8870_BL_AMBL_FILT_320ms 2
  77 +#define ADP8870_BL_AMBL_FILT_640ms 3
  78 +#define ADP8870_BL_AMBL_FILT_1280ms 4
  79 +#define ADP8870_BL_AMBL_FILT_2560ms 5
  80 +#define ADP8870_BL_AMBL_FILT_5120ms 6
  81 +#define ADP8870_BL_AMBL_FILT_10240ms 7 /* 10.24 sec */
  82 +
  83 +/*
  84 + * Blacklight current 0..30mA
  85 + */
  86 +#define ADP8870_BL_CUR_mA(I) ((I * 127) / 30)
  87 +
  88 +/*
  89 + * L2 comparator current 0..1106uA
  90 + */
  91 +#define ADP8870_L2_COMP_CURR_uA(I) ((I * 255) / 1106)
  92 +
  93 +/*
  94 + * L3 comparator current 0..551uA
  95 + */
  96 +#define ADP8870_L3_COMP_CURR_uA(I) ((I * 255) / 551)
  97 +
  98 +/*
  99 + * L4 comparator current 0..275uA
  100 + */
  101 +#define ADP8870_L4_COMP_CURR_uA(I) ((I * 255) / 275)
  102 +
  103 +/*
  104 + * L5 comparator current 0..138uA
  105 + */
  106 +#define ADP8870_L5_COMP_CURR_uA(I) ((I * 255) / 138)
  107 +
  108 +struct adp8870_backlight_platform_data {
  109 + u8 bl_led_assign; /* 1 = Backlight 0 = Individual LED */
  110 + u8 pwm_assign; /* 1 = Enables PWM mode */
  111 +
  112 + u8 bl_fade_in; /* Backlight Fade-In Timer */
  113 + u8 bl_fade_out; /* Backlight Fade-Out Timer */
  114 + u8 bl_fade_law; /* fade-on/fade-off transfer characteristic */
  115 +
  116 + u8 en_ambl_sens; /* 1 = enable ambient light sensor */
  117 + u8 abml_filt; /* Light sensor filter time */
  118 +
  119 + u8 l1_daylight_max; /* use BL_CUR_mA(I) 0 <= I <= 30 mA */
  120 + u8 l1_daylight_dim; /* typ = 0, use BL_CUR_mA(I) 0 <= I <= 30 mA */
  121 + u8 l2_bright_max; /* use BL_CUR_mA(I) 0 <= I <= 30 mA */
  122 + u8 l2_bright_dim; /* typ = 0, use BL_CUR_mA(I) 0 <= I <= 30 mA */
  123 + u8 l3_office_max; /* use BL_CUR_mA(I) 0 <= I <= 30 mA */
  124 + u8 l3_office_dim; /* typ = 0, use BL_CUR_mA(I) 0 <= I <= 30 mA */
  125 + u8 l4_indoor_max; /* use BL_CUR_mA(I) 0 <= I <= 30 mA */
  126 + u8 l4_indor_dim; /* typ = 0, use BL_CUR_mA(I) 0 <= I <= 30 mA */
  127 + u8 l5_dark_max; /* use BL_CUR_mA(I) 0 <= I <= 30 mA */
  128 + u8 l5_dark_dim; /* typ = 0, use BL_CUR_mA(I) 0 <= I <= 30 mA */
  129 +
  130 + u8 l2_trip; /* use L2_COMP_CURR_uA(I) 0 <= I <= 1106 uA */
  131 + u8 l2_hyst; /* use L2_COMP_CURR_uA(I) 0 <= I <= 1106 uA */
  132 + u8 l3_trip; /* use L3_COMP_CURR_uA(I) 0 <= I <= 551 uA */
  133 + u8 l3_hyst; /* use L3_COMP_CURR_uA(I) 0 <= I <= 551 uA */
  134 + u8 l4_trip; /* use L4_COMP_CURR_uA(I) 0 <= I <= 275 uA */
  135 + u8 l4_hyst; /* use L4_COMP_CURR_uA(I) 0 <= I <= 275 uA */
  136 + u8 l5_trip; /* use L5_COMP_CURR_uA(I) 0 <= I <= 138 uA */
  137 + u8 l5_hyst; /* use L6_COMP_CURR_uA(I) 0 <= I <= 138 uA */
  138 +
  139 + /**
  140 + * Independent Current Sinks / LEDS
  141 + * Sinks not assigned to the Backlight can be exposed to
  142 + * user space using the LEDS CLASS interface
  143 + */
  144 +
  145 + int num_leds;
  146 + struct led_info *leds;
  147 + u8 led_fade_in; /* LED Fade-In Timer */
  148 + u8 led_fade_out; /* LED Fade-Out Timer */
  149 + u8 led_fade_law; /* fade-on/fade-off transfer characteristic */
  150 + u8 led_on_time;
  151 +};
  152 +
  153 +#endif /* __LINUX_I2C_ADP8870_H */