xref: /linux/drivers/video/backlight/adp8860_bl.c (revision 3f43f8b2ad9035c5440d65681079f4d80542d21f)
182fd53b7SMichael Hennerich /*
282fd53b7SMichael Hennerich  * Backlight driver for Analog Devices ADP8860 Backlight Devices
382fd53b7SMichael Hennerich  *
482fd53b7SMichael Hennerich  * Copyright 2009-2010 Analog Devices Inc.
582fd53b7SMichael Hennerich  *
682fd53b7SMichael Hennerich  * Licensed under the GPL-2 or later.
782fd53b7SMichael Hennerich  */
882fd53b7SMichael Hennerich 
982fd53b7SMichael Hennerich #include <linux/module.h>
1082fd53b7SMichael Hennerich #include <linux/version.h>
1182fd53b7SMichael Hennerich #include <linux/init.h>
1282fd53b7SMichael Hennerich #include <linux/errno.h>
1382fd53b7SMichael Hennerich #include <linux/pm.h>
1482fd53b7SMichael Hennerich #include <linux/platform_device.h>
1582fd53b7SMichael Hennerich #include <linux/i2c.h>
1682fd53b7SMichael Hennerich #include <linux/fb.h>
1782fd53b7SMichael Hennerich #include <linux/backlight.h>
1882fd53b7SMichael Hennerich #include <linux/leds.h>
19*3f43f8b2SRandy Dunlap #include <linux/slab.h>
2082fd53b7SMichael Hennerich #include <linux/workqueue.h>
2182fd53b7SMichael Hennerich 
2282fd53b7SMichael Hennerich #include <linux/i2c/adp8860.h>
2382fd53b7SMichael Hennerich #define ADP8860_EXT_FEATURES
2482fd53b7SMichael Hennerich #define ADP8860_USE_LEDS
2582fd53b7SMichael Hennerich 
2682fd53b7SMichael Hennerich #define ADP8860_MFDVID 0x00 /* Manufacturer and device ID */
2782fd53b7SMichael Hennerich #define ADP8860_MDCR 0x01 /* Device mode and status */
2882fd53b7SMichael Hennerich #define ADP8860_MDCR2 0x02 /* Device mode and Status Register 2 */
2982fd53b7SMichael Hennerich #define ADP8860_INTR_EN 0x03 /* Interrupts enable */
3082fd53b7SMichael Hennerich #define ADP8860_CFGR 0x04 /* Configuration register */
3182fd53b7SMichael Hennerich #define ADP8860_BLSEN 0x05 /* Sink enable backlight or independent */
3282fd53b7SMichael Hennerich #define ADP8860_BLOFF 0x06 /* Backlight off timeout */
3382fd53b7SMichael Hennerich #define ADP8860_BLDIM 0x07 /* Backlight dim timeout */
3482fd53b7SMichael Hennerich #define ADP8860_BLFR 0x08 /* Backlight fade in and out rates */
3582fd53b7SMichael Hennerich #define ADP8860_BLMX1 0x09 /* Backlight (Brightness Level 1-daylight) maximum current */
3682fd53b7SMichael Hennerich #define ADP8860_BLDM1 0x0A /* Backlight (Brightness Level 1-daylight) dim current */
3782fd53b7SMichael Hennerich #define ADP8860_BLMX2 0x0B /* Backlight (Brightness Level 2-office) maximum current */
3882fd53b7SMichael Hennerich #define ADP8860_BLDM2 0x0C /* Backlight (Brightness Level 2-office) dim current */
3982fd53b7SMichael Hennerich #define ADP8860_BLMX3 0x0D /* Backlight (Brightness Level 3-dark) maximum current */
4082fd53b7SMichael Hennerich #define ADP8860_BLDM3 0x0E /* Backlight (Brightness Level 3-dark) dim current */
4182fd53b7SMichael Hennerich #define ADP8860_ISCFR 0x0F /* Independent sink current fade control register */
4282fd53b7SMichael Hennerich #define ADP8860_ISCC 0x10 /* Independent sink current control register */
4382fd53b7SMichael Hennerich #define ADP8860_ISCT1 0x11 /* Independent Sink Current Timer Register LED[7:5] */
4482fd53b7SMichael Hennerich #define ADP8860_ISCT2 0x12 /* Independent Sink Current Timer Register LED[4:1] */
4582fd53b7SMichael Hennerich #define ADP8860_ISCF 0x13 /* Independent sink current fade register */
4682fd53b7SMichael Hennerich #define ADP8860_ISC7 0x14 /* Independent Sink Current LED7 */
4782fd53b7SMichael Hennerich #define ADP8860_ISC6 0x15 /* Independent Sink Current LED6 */
4882fd53b7SMichael Hennerich #define ADP8860_ISC5 0x16 /* Independent Sink Current LED5 */
4982fd53b7SMichael Hennerich #define ADP8860_ISC4 0x17 /* Independent Sink Current LED4 */
5082fd53b7SMichael Hennerich #define ADP8860_ISC3 0x18 /* Independent Sink Current LED3 */
5182fd53b7SMichael Hennerich #define ADP8860_ISC2 0x19 /* Independent Sink Current LED2 */
5282fd53b7SMichael Hennerich #define ADP8860_ISC1 0x1A /* Independent Sink Current LED1 */
5382fd53b7SMichael Hennerich #define ADP8860_CCFG 0x1B /* Comparator configuration */
5482fd53b7SMichael Hennerich #define ADP8860_CCFG2 0x1C /* Second comparator configuration */
5582fd53b7SMichael Hennerich #define ADP8860_L2_TRP 0x1D /* L2 comparator reference */
5682fd53b7SMichael Hennerich #define ADP8860_L2_HYS 0x1E /* L2 hysteresis */
5782fd53b7SMichael Hennerich #define ADP8860_L3_TRP 0x1F /* L3 comparator reference */
5882fd53b7SMichael Hennerich #define ADP8860_L3_HYS 0x20 /* L3 hysteresis */
5982fd53b7SMichael Hennerich #define ADP8860_PH1LEVL 0x21 /* First phototransistor ambient light level-low byte register */
6082fd53b7SMichael Hennerich #define ADP8860_PH1LEVH 0x22 /* First phototransistor ambient light level-high byte register */
6182fd53b7SMichael Hennerich #define ADP8860_PH2LEVL 0x23 /* Second phototransistor ambient light level-low byte register */
6282fd53b7SMichael Hennerich #define ADP8860_PH2LEVH 0x24 /* Second phototransistor ambient light level-high byte register */
6382fd53b7SMichael Hennerich 
6482fd53b7SMichael Hennerich #define ADP8860_MANUFID		0x0  /* Analog Devices ADP8860 Manufacturer ID */
6582fd53b7SMichael Hennerich #define ADP8860_DEVICEID	0x7  /* Analog Devices ADP8860 Device ID */
6682fd53b7SMichael Hennerich #define ADP8860_DEVID(x)	((x) & 0xF)
6782fd53b7SMichael Hennerich #define ADP8860_MANID(x)	((x) >> 4)
6882fd53b7SMichael Hennerich 
6982fd53b7SMichael Hennerich /* MDCR Device mode and status */
7082fd53b7SMichael Hennerich #define INT_CFG			(1 << 6)
7182fd53b7SMichael Hennerich #define NSTBY			(1 << 5)
7282fd53b7SMichael Hennerich #define DIM_EN			(1 << 4)
7382fd53b7SMichael Hennerich #define SIS_EN			(1 << 2)
7482fd53b7SMichael Hennerich #define CMP_AUTOEN		(1 << 1)
7582fd53b7SMichael Hennerich #define BLEN			(1 << 0)
7682fd53b7SMichael Hennerich 
7782fd53b7SMichael Hennerich /* ADP8860_CCFG Main ALS comparator level enable */
7882fd53b7SMichael Hennerich #define L3_EN			(1 << 1)
7982fd53b7SMichael Hennerich #define L2_EN			(1 << 0)
8082fd53b7SMichael Hennerich 
8182fd53b7SMichael Hennerich #define CFGR_BLV_SHIFT		3
8282fd53b7SMichael Hennerich #define CFGR_BLV_MASK		0x3
8382fd53b7SMichael Hennerich #define ADP8860_FLAG_LED_MASK	0xFF
8482fd53b7SMichael Hennerich 
8582fd53b7SMichael Hennerich #define FADE_VAL(in, out)	((0xF & (in)) | ((0xF & (out)) << 4))
8682fd53b7SMichael Hennerich #define BL_CFGR_VAL(law, blv)	((((blv) & CFGR_BLV_MASK) << CFGR_BLV_SHIFT) | ((0x3 & (law)) << 1))
8782fd53b7SMichael Hennerich #define ALS_CCFG_VAL(filt)	((0x7 & filt) << 5)
8882fd53b7SMichael Hennerich 
8982fd53b7SMichael Hennerich struct adp8860_led {
9082fd53b7SMichael Hennerich 	struct led_classdev	cdev;
9182fd53b7SMichael Hennerich 	struct work_struct	work;
9282fd53b7SMichael Hennerich 	struct i2c_client	*client;
9382fd53b7SMichael Hennerich 	enum led_brightness	new_brightness;
9482fd53b7SMichael Hennerich 	int			id;
9582fd53b7SMichael Hennerich 	int			flags;
9682fd53b7SMichael Hennerich };
9782fd53b7SMichael Hennerich 
9882fd53b7SMichael Hennerich struct adp8860_bl {
9982fd53b7SMichael Hennerich 	struct i2c_client *client;
10082fd53b7SMichael Hennerich 	struct backlight_device *bl;
10182fd53b7SMichael Hennerich 	struct adp8860_led *led;
10282fd53b7SMichael Hennerich 	struct adp8860_backlight_platform_data *pdata;
10382fd53b7SMichael Hennerich 	struct mutex lock;
10482fd53b7SMichael Hennerich 	unsigned long cached_daylight_max;
10582fd53b7SMichael Hennerich 	int id;
10682fd53b7SMichael Hennerich 	int revid;
10782fd53b7SMichael Hennerich 	int current_brightness;
10882fd53b7SMichael Hennerich };
10982fd53b7SMichael Hennerich 
11082fd53b7SMichael Hennerich static int adp8860_read(struct i2c_client *client, int reg, uint8_t *val)
11182fd53b7SMichael Hennerich {
11282fd53b7SMichael Hennerich 	int ret;
11382fd53b7SMichael Hennerich 
11482fd53b7SMichael Hennerich 	ret = i2c_smbus_read_byte_data(client, reg);
11582fd53b7SMichael Hennerich 	if (ret < 0) {
11682fd53b7SMichael Hennerich 		dev_err(&client->dev, "failed reading at 0x%02x\n", reg);
11782fd53b7SMichael Hennerich 		return ret;
11882fd53b7SMichael Hennerich 	}
11982fd53b7SMichael Hennerich 
12082fd53b7SMichael Hennerich 	*val = (uint8_t)ret;
12182fd53b7SMichael Hennerich 	return 0;
12282fd53b7SMichael Hennerich }
12382fd53b7SMichael Hennerich 
12482fd53b7SMichael Hennerich 
12582fd53b7SMichael Hennerich static int adp8860_write(struct i2c_client *client, u8 reg, u8 val)
12682fd53b7SMichael Hennerich {
12782fd53b7SMichael Hennerich 	return i2c_smbus_write_byte_data(client, reg, val);
12882fd53b7SMichael Hennerich }
12982fd53b7SMichael Hennerich 
13082fd53b7SMichael Hennerich static int adp8860_set_bits(struct i2c_client *client, int reg, uint8_t bit_mask)
13182fd53b7SMichael Hennerich {
13282fd53b7SMichael Hennerich 	struct adp8860_bl *data = i2c_get_clientdata(client);
13382fd53b7SMichael Hennerich 	uint8_t reg_val;
13482fd53b7SMichael Hennerich 	int ret;
13582fd53b7SMichael Hennerich 
13682fd53b7SMichael Hennerich 	mutex_lock(&data->lock);
13782fd53b7SMichael Hennerich 
13882fd53b7SMichael Hennerich 	ret = adp8860_read(client, reg, &reg_val);
13982fd53b7SMichael Hennerich 
14082fd53b7SMichael Hennerich 	if (!ret && ((reg_val & bit_mask) == 0)) {
14182fd53b7SMichael Hennerich 		reg_val |= bit_mask;
14282fd53b7SMichael Hennerich 		ret = adp8860_write(client, reg, reg_val);
14382fd53b7SMichael Hennerich 	}
14482fd53b7SMichael Hennerich 
14582fd53b7SMichael Hennerich 	mutex_unlock(&data->lock);
14682fd53b7SMichael Hennerich 	return ret;
14782fd53b7SMichael Hennerich }
14882fd53b7SMichael Hennerich 
14982fd53b7SMichael Hennerich static int adp8860_clr_bits(struct i2c_client *client, int reg, uint8_t bit_mask)
15082fd53b7SMichael Hennerich {
15182fd53b7SMichael Hennerich 	struct adp8860_bl *data = i2c_get_clientdata(client);
15282fd53b7SMichael Hennerich 	uint8_t reg_val;
15382fd53b7SMichael Hennerich 	int ret;
15482fd53b7SMichael Hennerich 
15582fd53b7SMichael Hennerich 	mutex_lock(&data->lock);
15682fd53b7SMichael Hennerich 
15782fd53b7SMichael Hennerich 	ret = adp8860_read(client, reg, &reg_val);
15882fd53b7SMichael Hennerich 
15982fd53b7SMichael Hennerich 	if (!ret && (reg_val & bit_mask)) {
16082fd53b7SMichael Hennerich 		reg_val &= ~bit_mask;
16182fd53b7SMichael Hennerich 		ret = adp8860_write(client, reg, reg_val);
16282fd53b7SMichael Hennerich 	}
16382fd53b7SMichael Hennerich 
16482fd53b7SMichael Hennerich 	mutex_unlock(&data->lock);
16582fd53b7SMichael Hennerich 	return ret;
16682fd53b7SMichael Hennerich }
16782fd53b7SMichael Hennerich 
16882fd53b7SMichael Hennerich /*
16982fd53b7SMichael Hennerich  * Independent sink / LED
17082fd53b7SMichael Hennerich  */
17182fd53b7SMichael Hennerich #if defined(ADP8860_USE_LEDS)
17282fd53b7SMichael Hennerich static void adp8860_led_work(struct work_struct *work)
17382fd53b7SMichael Hennerich {
17482fd53b7SMichael Hennerich 	struct adp8860_led *led = container_of(work, struct adp8860_led, work);
17582fd53b7SMichael Hennerich 	adp8860_write(led->client, ADP8860_ISC1 - led->id + 1,
17682fd53b7SMichael Hennerich 			 led->new_brightness >> 1);
17782fd53b7SMichael Hennerich }
17882fd53b7SMichael Hennerich 
17982fd53b7SMichael Hennerich static void adp8860_led_set(struct led_classdev *led_cdev,
18082fd53b7SMichael Hennerich 			   enum led_brightness value)
18182fd53b7SMichael Hennerich {
18282fd53b7SMichael Hennerich 	struct adp8860_led *led;
18382fd53b7SMichael Hennerich 
18482fd53b7SMichael Hennerich 	led = container_of(led_cdev, struct adp8860_led, cdev);
18582fd53b7SMichael Hennerich 	led->new_brightness = value;
18682fd53b7SMichael Hennerich 	schedule_work(&led->work);
18782fd53b7SMichael Hennerich }
18882fd53b7SMichael Hennerich 
18982fd53b7SMichael Hennerich static int adp8860_led_setup(struct adp8860_led *led)
19082fd53b7SMichael Hennerich {
19182fd53b7SMichael Hennerich 	struct i2c_client *client = led->client;
19282fd53b7SMichael Hennerich 	int ret = 0;
19382fd53b7SMichael Hennerich 
19482fd53b7SMichael Hennerich 	ret = adp8860_write(client, ADP8860_ISC1 - led->id + 1, 0);
19582fd53b7SMichael Hennerich 	ret |= adp8860_set_bits(client, ADP8860_ISCC, 1 << (led->id - 1));
19682fd53b7SMichael Hennerich 
19782fd53b7SMichael Hennerich 	if (led->id > 4)
19882fd53b7SMichael Hennerich 		ret |= adp8860_set_bits(client, ADP8860_ISCT1,
19982fd53b7SMichael Hennerich 				(led->flags & 0x3) << ((led->id - 5) * 2));
20082fd53b7SMichael Hennerich 	else
20182fd53b7SMichael Hennerich 		ret |= adp8860_set_bits(client, ADP8860_ISCT2,
20282fd53b7SMichael Hennerich 				(led->flags & 0x3) << ((led->id - 1) * 2));
20382fd53b7SMichael Hennerich 
20482fd53b7SMichael Hennerich 	return ret;
20582fd53b7SMichael Hennerich }
20682fd53b7SMichael Hennerich 
20782fd53b7SMichael Hennerich static int __devinit adp8860_led_probe(struct i2c_client *client)
20882fd53b7SMichael Hennerich {
20982fd53b7SMichael Hennerich 	struct adp8860_backlight_platform_data *pdata =
21082fd53b7SMichael Hennerich 		client->dev.platform_data;
21182fd53b7SMichael Hennerich 	struct adp8860_bl *data = i2c_get_clientdata(client);
21282fd53b7SMichael Hennerich 	struct adp8860_led *led, *led_dat;
21382fd53b7SMichael Hennerich 	struct led_info *cur_led;
21482fd53b7SMichael Hennerich 	int ret, i;
21582fd53b7SMichael Hennerich 
21682fd53b7SMichael Hennerich 	led = kzalloc(sizeof(*led) * pdata->num_leds, GFP_KERNEL);
21782fd53b7SMichael Hennerich 	if (led == NULL) {
21882fd53b7SMichael Hennerich 		dev_err(&client->dev, "failed to alloc memory\n");
21982fd53b7SMichael Hennerich 		return -ENOMEM;
22082fd53b7SMichael Hennerich 	}
22182fd53b7SMichael Hennerich 
22282fd53b7SMichael Hennerich 	ret = adp8860_write(client, ADP8860_ISCFR, pdata->led_fade_law);
22382fd53b7SMichael Hennerich 	ret = adp8860_write(client, ADP8860_ISCT1,
22482fd53b7SMichael Hennerich 			(pdata->led_on_time & 0x3) << 6);
22582fd53b7SMichael Hennerich 	ret |= adp8860_write(client, ADP8860_ISCF,
22682fd53b7SMichael Hennerich 			FADE_VAL(pdata->led_fade_in, pdata->led_fade_out));
22782fd53b7SMichael Hennerich 
22882fd53b7SMichael Hennerich 	if (ret) {
22982fd53b7SMichael Hennerich 		dev_err(&client->dev, "failed to write\n");
23082fd53b7SMichael Hennerich 		goto err_free;
23182fd53b7SMichael Hennerich 	}
23282fd53b7SMichael Hennerich 
23382fd53b7SMichael Hennerich 	for (i = 0; i < pdata->num_leds; ++i) {
23482fd53b7SMichael Hennerich 		cur_led = &pdata->leds[i];
23582fd53b7SMichael Hennerich 		led_dat = &led[i];
23682fd53b7SMichael Hennerich 
23782fd53b7SMichael Hennerich 		led_dat->id = cur_led->flags & ADP8860_FLAG_LED_MASK;
23882fd53b7SMichael Hennerich 
23982fd53b7SMichael Hennerich 		if (led_dat->id > 7 || led_dat->id < 1) {
24082fd53b7SMichael Hennerich 			dev_err(&client->dev, "Invalid LED ID %d\n",
24182fd53b7SMichael Hennerich 				led_dat->id);
24282fd53b7SMichael Hennerich 			goto err;
24382fd53b7SMichael Hennerich 		}
24482fd53b7SMichael Hennerich 
24582fd53b7SMichael Hennerich 		if (pdata->bl_led_assign & (1 << (led_dat->id - 1))) {
24682fd53b7SMichael Hennerich 			dev_err(&client->dev, "LED %d used by Backlight\n",
24782fd53b7SMichael Hennerich 				led_dat->id);
24882fd53b7SMichael Hennerich 			goto err;
24982fd53b7SMichael Hennerich 		}
25082fd53b7SMichael Hennerich 
25182fd53b7SMichael Hennerich 		led_dat->cdev.name = cur_led->name;
25282fd53b7SMichael Hennerich 		led_dat->cdev.default_trigger = cur_led->default_trigger;
25382fd53b7SMichael Hennerich 		led_dat->cdev.brightness_set = adp8860_led_set;
25482fd53b7SMichael Hennerich 		led_dat->cdev.brightness = LED_OFF;
25582fd53b7SMichael Hennerich 		led_dat->flags = cur_led->flags >> FLAG_OFFT_SHIFT;
25682fd53b7SMichael Hennerich 		led_dat->client = client;
25782fd53b7SMichael Hennerich 		led_dat->new_brightness = LED_OFF;
25882fd53b7SMichael Hennerich 		INIT_WORK(&led_dat->work, adp8860_led_work);
25982fd53b7SMichael Hennerich 
26082fd53b7SMichael Hennerich 		ret = led_classdev_register(&client->dev, &led_dat->cdev);
26182fd53b7SMichael Hennerich 		if (ret) {
26282fd53b7SMichael Hennerich 			dev_err(&client->dev, "failed to register LED %d\n",
26382fd53b7SMichael Hennerich 				led_dat->id);
26482fd53b7SMichael Hennerich 			goto err;
26582fd53b7SMichael Hennerich 		}
26682fd53b7SMichael Hennerich 
26782fd53b7SMichael Hennerich 		ret = adp8860_led_setup(led_dat);
26882fd53b7SMichael Hennerich 		if (ret) {
26982fd53b7SMichael Hennerich 			dev_err(&client->dev, "failed to write\n");
27082fd53b7SMichael Hennerich 			i++;
27182fd53b7SMichael Hennerich 			goto err;
27282fd53b7SMichael Hennerich 		}
27382fd53b7SMichael Hennerich 	}
27482fd53b7SMichael Hennerich 
27582fd53b7SMichael Hennerich 	data->led = led;
27682fd53b7SMichael Hennerich 
27782fd53b7SMichael Hennerich 	return 0;
27882fd53b7SMichael Hennerich 
27982fd53b7SMichael Hennerich  err:
28082fd53b7SMichael Hennerich 	for (i = i - 1; i >= 0; --i) {
28182fd53b7SMichael Hennerich 		led_classdev_unregister(&led[i].cdev);
28282fd53b7SMichael Hennerich 		cancel_work_sync(&led[i].work);
28382fd53b7SMichael Hennerich 	}
28482fd53b7SMichael Hennerich 
28582fd53b7SMichael Hennerich  err_free:
28682fd53b7SMichael Hennerich 	kfree(led);
28782fd53b7SMichael Hennerich 
28882fd53b7SMichael Hennerich 	return ret;
28982fd53b7SMichael Hennerich }
29082fd53b7SMichael Hennerich 
29182fd53b7SMichael Hennerich static int __devexit adp8860_led_remove(struct i2c_client *client)
29282fd53b7SMichael Hennerich {
29382fd53b7SMichael Hennerich 	struct adp8860_backlight_platform_data *pdata =
29482fd53b7SMichael Hennerich 		client->dev.platform_data;
29582fd53b7SMichael Hennerich 	struct adp8860_bl *data = i2c_get_clientdata(client);
29682fd53b7SMichael Hennerich 	int i;
29782fd53b7SMichael Hennerich 
29882fd53b7SMichael Hennerich 	for (i = 0; i < pdata->num_leds; i++) {
29982fd53b7SMichael Hennerich 		led_classdev_unregister(&data->led[i].cdev);
30082fd53b7SMichael Hennerich 		cancel_work_sync(&data->led[i].work);
30182fd53b7SMichael Hennerich 	}
30282fd53b7SMichael Hennerich 
30382fd53b7SMichael Hennerich 	kfree(data->led);
30482fd53b7SMichael Hennerich 	return 0;
30582fd53b7SMichael Hennerich }
30682fd53b7SMichael Hennerich #else
30782fd53b7SMichael Hennerich static int __devinit adp8860_led_probe(struct i2c_client *client)
30882fd53b7SMichael Hennerich {
30982fd53b7SMichael Hennerich 	return 0;
31082fd53b7SMichael Hennerich }
31182fd53b7SMichael Hennerich 
31282fd53b7SMichael Hennerich static int __devexit adp8860_led_remove(struct i2c_client *client)
31382fd53b7SMichael Hennerich {
31482fd53b7SMichael Hennerich 	return 0;
31582fd53b7SMichael Hennerich }
31682fd53b7SMichael Hennerich #endif
31782fd53b7SMichael Hennerich 
31882fd53b7SMichael Hennerich static int adp8860_bl_set(struct backlight_device *bl, int brightness)
31982fd53b7SMichael Hennerich {
32082fd53b7SMichael Hennerich 	struct adp8860_bl *data = bl_get_data(bl);
32182fd53b7SMichael Hennerich 	struct i2c_client *client = data->client;
32282fd53b7SMichael Hennerich 	int ret = 0;
32382fd53b7SMichael Hennerich 
32482fd53b7SMichael Hennerich 	if (data->pdata->en_ambl_sens) {
32582fd53b7SMichael Hennerich 		if ((brightness > 0) && (brightness < ADP8860_MAX_BRIGHTNESS)) {
32682fd53b7SMichael Hennerich 			/* Disable Ambient Light auto adjust */
32782fd53b7SMichael Hennerich 			ret |= adp8860_clr_bits(client, ADP8860_MDCR,
32882fd53b7SMichael Hennerich 					CMP_AUTOEN);
32982fd53b7SMichael Hennerich 			ret |= adp8860_write(client, ADP8860_BLMX1, brightness);
33082fd53b7SMichael Hennerich 		} else {
33182fd53b7SMichael Hennerich 			/*
33282fd53b7SMichael Hennerich 			 * MAX_BRIGHTNESS -> Enable Ambient Light auto adjust
33382fd53b7SMichael Hennerich 			 * restore daylight l1 sysfs brightness
33482fd53b7SMichael Hennerich 			 */
33582fd53b7SMichael Hennerich 			ret |= adp8860_write(client, ADP8860_BLMX1,
33682fd53b7SMichael Hennerich 					 data->cached_daylight_max);
33782fd53b7SMichael Hennerich 			ret |= adp8860_set_bits(client, ADP8860_MDCR,
33882fd53b7SMichael Hennerich 					 CMP_AUTOEN);
33982fd53b7SMichael Hennerich 		}
34082fd53b7SMichael Hennerich 	} else
34182fd53b7SMichael Hennerich 		ret |= adp8860_write(client, ADP8860_BLMX1, brightness);
34282fd53b7SMichael Hennerich 
34382fd53b7SMichael Hennerich 	if (data->current_brightness && brightness == 0)
34482fd53b7SMichael Hennerich 		ret |= adp8860_set_bits(client,
34582fd53b7SMichael Hennerich 				ADP8860_MDCR, DIM_EN);
34682fd53b7SMichael Hennerich 	else if (data->current_brightness == 0 && brightness)
34782fd53b7SMichael Hennerich 		ret |= adp8860_clr_bits(client,
34882fd53b7SMichael Hennerich 				ADP8860_MDCR, DIM_EN);
34982fd53b7SMichael Hennerich 
35082fd53b7SMichael Hennerich 	if (!ret)
35182fd53b7SMichael Hennerich 		data->current_brightness = brightness;
35282fd53b7SMichael Hennerich 
35382fd53b7SMichael Hennerich 	return ret;
35482fd53b7SMichael Hennerich }
35582fd53b7SMichael Hennerich 
35682fd53b7SMichael Hennerich static int adp8860_bl_update_status(struct backlight_device *bl)
35782fd53b7SMichael Hennerich {
35882fd53b7SMichael Hennerich 	int brightness = bl->props.brightness;
35982fd53b7SMichael Hennerich 	if (bl->props.power != FB_BLANK_UNBLANK)
36082fd53b7SMichael Hennerich 		brightness = 0;
36182fd53b7SMichael Hennerich 
36282fd53b7SMichael Hennerich 	if (bl->props.fb_blank != FB_BLANK_UNBLANK)
36382fd53b7SMichael Hennerich 		brightness = 0;
36482fd53b7SMichael Hennerich 
36582fd53b7SMichael Hennerich 	return adp8860_bl_set(bl, brightness);
36682fd53b7SMichael Hennerich }
36782fd53b7SMichael Hennerich 
36882fd53b7SMichael Hennerich static int adp8860_bl_get_brightness(struct backlight_device *bl)
36982fd53b7SMichael Hennerich {
37082fd53b7SMichael Hennerich 	struct adp8860_bl *data = bl_get_data(bl);
37182fd53b7SMichael Hennerich 
37282fd53b7SMichael Hennerich 	return data->current_brightness;
37382fd53b7SMichael Hennerich }
37482fd53b7SMichael Hennerich 
37582fd53b7SMichael Hennerich static const struct backlight_ops adp8860_bl_ops = {
37682fd53b7SMichael Hennerich 	.update_status	= adp8860_bl_update_status,
37782fd53b7SMichael Hennerich 	.get_brightness	= adp8860_bl_get_brightness,
37882fd53b7SMichael Hennerich };
37982fd53b7SMichael Hennerich 
38082fd53b7SMichael Hennerich static int adp8860_bl_setup(struct backlight_device *bl)
38182fd53b7SMichael Hennerich {
38282fd53b7SMichael Hennerich 	struct adp8860_bl *data = bl_get_data(bl);
38382fd53b7SMichael Hennerich 	struct i2c_client *client = data->client;
38482fd53b7SMichael Hennerich 	struct adp8860_backlight_platform_data *pdata = data->pdata;
38582fd53b7SMichael Hennerich 	int ret = 0;
38682fd53b7SMichael Hennerich 
38782fd53b7SMichael Hennerich 	ret |= adp8860_write(client, ADP8860_BLSEN, ~pdata->bl_led_assign);
38882fd53b7SMichael Hennerich 	ret |= adp8860_write(client, ADP8860_BLMX1, pdata->l1_daylight_max);
38982fd53b7SMichael Hennerich 	ret |= adp8860_write(client, ADP8860_BLDM1, pdata->l1_daylight_dim);
39082fd53b7SMichael Hennerich 
39182fd53b7SMichael Hennerich 	if (pdata->en_ambl_sens) {
39282fd53b7SMichael Hennerich 		data->cached_daylight_max = pdata->l1_daylight_max;
39382fd53b7SMichael Hennerich 		ret |= adp8860_write(client, ADP8860_BLMX2,
39482fd53b7SMichael Hennerich 						pdata->l2_office_max);
39582fd53b7SMichael Hennerich 		ret |= adp8860_write(client, ADP8860_BLDM2,
39682fd53b7SMichael Hennerich 						pdata->l2_office_dim);
39782fd53b7SMichael Hennerich 		ret |= adp8860_write(client, ADP8860_BLMX3,
39882fd53b7SMichael Hennerich 						pdata->l3_dark_max);
39982fd53b7SMichael Hennerich 		ret |= adp8860_write(client, ADP8860_BLDM3,
40082fd53b7SMichael Hennerich 						pdata->l3_dark_dim);
40182fd53b7SMichael Hennerich 
40282fd53b7SMichael Hennerich 		ret |= adp8860_write(client, ADP8860_L2_TRP, pdata->l2_trip);
40382fd53b7SMichael Hennerich 		ret |= adp8860_write(client, ADP8860_L2_HYS, pdata->l2_hyst);
40482fd53b7SMichael Hennerich 		ret |= adp8860_write(client, ADP8860_L3_TRP, pdata->l3_trip);
40582fd53b7SMichael Hennerich 		ret |= adp8860_write(client, ADP8860_L3_HYS, pdata->l3_hyst);
40682fd53b7SMichael Hennerich 		ret |= adp8860_write(client, ADP8860_CCFG, L2_EN | L3_EN |
40782fd53b7SMichael Hennerich 						ALS_CCFG_VAL(pdata->abml_filt));
40882fd53b7SMichael Hennerich 	}
40982fd53b7SMichael Hennerich 
41082fd53b7SMichael Hennerich 	ret |= adp8860_write(client, ADP8860_CFGR,
41182fd53b7SMichael Hennerich 			BL_CFGR_VAL(pdata->bl_fade_law, 0));
41282fd53b7SMichael Hennerich 
41382fd53b7SMichael Hennerich 	ret |= adp8860_write(client, ADP8860_BLFR, FADE_VAL(pdata->bl_fade_in,
41482fd53b7SMichael Hennerich 			pdata->bl_fade_out));
41582fd53b7SMichael Hennerich 
41682fd53b7SMichael Hennerich 	ret |= adp8860_set_bits(client, ADP8860_MDCR, BLEN | DIM_EN | NSTBY);
41782fd53b7SMichael Hennerich 
41882fd53b7SMichael Hennerich 	return ret;
41982fd53b7SMichael Hennerich }
42082fd53b7SMichael Hennerich 
42182fd53b7SMichael Hennerich static ssize_t adp8860_show(struct device *dev, char *buf, int reg)
42282fd53b7SMichael Hennerich {
42382fd53b7SMichael Hennerich 	struct adp8860_bl *data = dev_get_drvdata(dev);
42482fd53b7SMichael Hennerich 	int error;
42582fd53b7SMichael Hennerich 	uint8_t reg_val;
42682fd53b7SMichael Hennerich 
42782fd53b7SMichael Hennerich 	mutex_lock(&data->lock);
42882fd53b7SMichael Hennerich 	error = adp8860_read(data->client, reg, &reg_val);
42982fd53b7SMichael Hennerich 	mutex_unlock(&data->lock);
43082fd53b7SMichael Hennerich 
43182fd53b7SMichael Hennerich 	if (error < 0)
43282fd53b7SMichael Hennerich 		return error;
43382fd53b7SMichael Hennerich 
43482fd53b7SMichael Hennerich 	return sprintf(buf, "%u\n", reg_val);
43582fd53b7SMichael Hennerich }
43682fd53b7SMichael Hennerich 
43782fd53b7SMichael Hennerich static ssize_t adp8860_store(struct device *dev, const char *buf,
43882fd53b7SMichael Hennerich 			 size_t count, int reg)
43982fd53b7SMichael Hennerich {
44082fd53b7SMichael Hennerich 	struct adp8860_bl *data = dev_get_drvdata(dev);
44182fd53b7SMichael Hennerich 	unsigned long val;
44282fd53b7SMichael Hennerich 	int ret;
44382fd53b7SMichael Hennerich 
44482fd53b7SMichael Hennerich 	ret = strict_strtoul(buf, 10, &val);
44582fd53b7SMichael Hennerich 	if (ret)
44682fd53b7SMichael Hennerich 		return ret;
44782fd53b7SMichael Hennerich 
44882fd53b7SMichael Hennerich 	mutex_lock(&data->lock);
44982fd53b7SMichael Hennerich 	adp8860_write(data->client, reg, val);
45082fd53b7SMichael Hennerich 	mutex_unlock(&data->lock);
45182fd53b7SMichael Hennerich 
45282fd53b7SMichael Hennerich 	return count;
45382fd53b7SMichael Hennerich }
45482fd53b7SMichael Hennerich 
45582fd53b7SMichael Hennerich static ssize_t adp8860_bl_l3_dark_max_show(struct device *dev,
45682fd53b7SMichael Hennerich 				     struct device_attribute *attr, char *buf)
45782fd53b7SMichael Hennerich {
45882fd53b7SMichael Hennerich 	return adp8860_show(dev, buf, ADP8860_BLMX3);
45982fd53b7SMichael Hennerich }
46082fd53b7SMichael Hennerich 
46182fd53b7SMichael Hennerich static ssize_t adp8860_bl_l3_dark_max_store(struct device *dev,
46282fd53b7SMichael Hennerich 		struct device_attribute *attr, const char *buf, size_t count)
46382fd53b7SMichael Hennerich {
46482fd53b7SMichael Hennerich 	return adp8860_store(dev, buf, count, ADP8860_BLMX3);
46582fd53b7SMichael Hennerich }
46682fd53b7SMichael Hennerich 
46782fd53b7SMichael Hennerich static DEVICE_ATTR(l3_dark_max, 0664, adp8860_bl_l3_dark_max_show,
46882fd53b7SMichael Hennerich 			adp8860_bl_l3_dark_max_store);
46982fd53b7SMichael Hennerich 
47082fd53b7SMichael Hennerich static ssize_t adp8860_bl_l2_office_max_show(struct device *dev,
47182fd53b7SMichael Hennerich 		struct device_attribute *attr, char *buf)
47282fd53b7SMichael Hennerich {
47382fd53b7SMichael Hennerich 	return adp8860_show(dev, buf, ADP8860_BLMX2);
47482fd53b7SMichael Hennerich }
47582fd53b7SMichael Hennerich 
47682fd53b7SMichael Hennerich static ssize_t adp8860_bl_l2_office_max_store(struct device *dev,
47782fd53b7SMichael Hennerich 		struct device_attribute *attr, const char *buf, size_t count)
47882fd53b7SMichael Hennerich {
47982fd53b7SMichael Hennerich 	return adp8860_store(dev, buf, count, ADP8860_BLMX2);
48082fd53b7SMichael Hennerich }
48182fd53b7SMichael Hennerich static DEVICE_ATTR(l2_office_max, 0664, adp8860_bl_l2_office_max_show,
48282fd53b7SMichael Hennerich 			adp8860_bl_l2_office_max_store);
48382fd53b7SMichael Hennerich 
48482fd53b7SMichael Hennerich static ssize_t adp8860_bl_l1_daylight_max_show(struct device *dev,
48582fd53b7SMichael Hennerich 			struct device_attribute *attr, char *buf)
48682fd53b7SMichael Hennerich {
48782fd53b7SMichael Hennerich 	return adp8860_show(dev, buf, ADP8860_BLMX1);
48882fd53b7SMichael Hennerich }
48982fd53b7SMichael Hennerich 
49082fd53b7SMichael Hennerich static ssize_t adp8860_bl_l1_daylight_max_store(struct device *dev,
49182fd53b7SMichael Hennerich 		struct device_attribute *attr, const char *buf, size_t count)
49282fd53b7SMichael Hennerich {
49382fd53b7SMichael Hennerich 	struct adp8860_bl *data = dev_get_drvdata(dev);
49482fd53b7SMichael Hennerich 
49582fd53b7SMichael Hennerich 	strict_strtoul(buf, 10, &data->cached_daylight_max);
49682fd53b7SMichael Hennerich 	return adp8860_store(dev, buf, count, ADP8860_BLMX1);
49782fd53b7SMichael Hennerich }
49882fd53b7SMichael Hennerich static DEVICE_ATTR(l1_daylight_max, 0664, adp8860_bl_l1_daylight_max_show,
49982fd53b7SMichael Hennerich 			adp8860_bl_l1_daylight_max_store);
50082fd53b7SMichael Hennerich 
50182fd53b7SMichael Hennerich static ssize_t adp8860_bl_l3_dark_dim_show(struct device *dev,
50282fd53b7SMichael Hennerich 			struct device_attribute *attr, char *buf)
50382fd53b7SMichael Hennerich {
50482fd53b7SMichael Hennerich 	return adp8860_show(dev, buf, ADP8860_BLDM3);
50582fd53b7SMichael Hennerich }
50682fd53b7SMichael Hennerich 
50782fd53b7SMichael Hennerich static ssize_t adp8860_bl_l3_dark_dim_store(struct device *dev,
50882fd53b7SMichael Hennerich 				     struct device_attribute *attr,
50982fd53b7SMichael Hennerich 				     const char *buf, size_t count)
51082fd53b7SMichael Hennerich {
51182fd53b7SMichael Hennerich 	return adp8860_store(dev, buf, count, ADP8860_BLDM3);
51282fd53b7SMichael Hennerich }
51382fd53b7SMichael Hennerich static DEVICE_ATTR(l3_dark_dim, 0664, adp8860_bl_l3_dark_dim_show,
51482fd53b7SMichael Hennerich 			adp8860_bl_l3_dark_dim_store);
51582fd53b7SMichael Hennerich 
51682fd53b7SMichael Hennerich static ssize_t adp8860_bl_l2_office_dim_show(struct device *dev,
51782fd53b7SMichael Hennerich 			struct device_attribute *attr, char *buf)
51882fd53b7SMichael Hennerich {
51982fd53b7SMichael Hennerich 	return adp8860_show(dev, buf, ADP8860_BLDM2);
52082fd53b7SMichael Hennerich }
52182fd53b7SMichael Hennerich 
52282fd53b7SMichael Hennerich static ssize_t adp8860_bl_l2_office_dim_store(struct device *dev,
52382fd53b7SMichael Hennerich 				     struct device_attribute *attr,
52482fd53b7SMichael Hennerich 				     const char *buf, size_t count)
52582fd53b7SMichael Hennerich {
52682fd53b7SMichael Hennerich 	return adp8860_store(dev, buf, count, ADP8860_BLDM2);
52782fd53b7SMichael Hennerich }
52882fd53b7SMichael Hennerich static DEVICE_ATTR(l2_office_dim, 0664, adp8860_bl_l2_office_dim_show,
52982fd53b7SMichael Hennerich 			adp8860_bl_l2_office_dim_store);
53082fd53b7SMichael Hennerich 
53182fd53b7SMichael Hennerich static ssize_t adp8860_bl_l1_daylight_dim_show(struct device *dev,
53282fd53b7SMichael Hennerich 				     struct device_attribute *attr, char *buf)
53382fd53b7SMichael Hennerich {
53482fd53b7SMichael Hennerich 	return adp8860_show(dev, buf, ADP8860_BLDM1);
53582fd53b7SMichael Hennerich }
53682fd53b7SMichael Hennerich 
53782fd53b7SMichael Hennerich static ssize_t adp8860_bl_l1_daylight_dim_store(struct device *dev,
53882fd53b7SMichael Hennerich 				     struct device_attribute *attr,
53982fd53b7SMichael Hennerich 				     const char *buf, size_t count)
54082fd53b7SMichael Hennerich {
54182fd53b7SMichael Hennerich 	return adp8860_store(dev, buf, count, ADP8860_BLDM1);
54282fd53b7SMichael Hennerich }
54382fd53b7SMichael Hennerich static DEVICE_ATTR(l1_daylight_dim, 0664, adp8860_bl_l1_daylight_dim_show,
54482fd53b7SMichael Hennerich 			adp8860_bl_l1_daylight_dim_store);
54582fd53b7SMichael Hennerich 
54682fd53b7SMichael Hennerich #ifdef ADP8860_EXT_FEATURES
54782fd53b7SMichael Hennerich static ssize_t adp8860_bl_ambient_light_level_show(struct device *dev,
54882fd53b7SMichael Hennerich 				     struct device_attribute *attr, char *buf)
54982fd53b7SMichael Hennerich {
55082fd53b7SMichael Hennerich 	struct adp8860_bl *data = dev_get_drvdata(dev);
55182fd53b7SMichael Hennerich 	int error;
55282fd53b7SMichael Hennerich 	uint8_t reg_val;
55382fd53b7SMichael Hennerich 	uint16_t ret_val;
55482fd53b7SMichael Hennerich 
55582fd53b7SMichael Hennerich 	mutex_lock(&data->lock);
55682fd53b7SMichael Hennerich 	error = adp8860_read(data->client, ADP8860_PH1LEVL, &reg_val);
55782fd53b7SMichael Hennerich 	ret_val = reg_val;
55882fd53b7SMichael Hennerich 	error |= adp8860_read(data->client, ADP8860_PH1LEVH, &reg_val);
55982fd53b7SMichael Hennerich 	mutex_unlock(&data->lock);
56082fd53b7SMichael Hennerich 
56182fd53b7SMichael Hennerich 	if (error < 0)
56282fd53b7SMichael Hennerich 		return error;
56382fd53b7SMichael Hennerich 
56482fd53b7SMichael Hennerich 	/* Return 13-bit conversion value for the first light sensor */
56582fd53b7SMichael Hennerich 	ret_val += (reg_val & 0x1F) << 8;
56682fd53b7SMichael Hennerich 
56782fd53b7SMichael Hennerich 	return sprintf(buf, "%u\n", ret_val);
56882fd53b7SMichael Hennerich }
56982fd53b7SMichael Hennerich static DEVICE_ATTR(ambient_light_level, 0444,
57082fd53b7SMichael Hennerich 		adp8860_bl_ambient_light_level_show, NULL);
57182fd53b7SMichael Hennerich 
57282fd53b7SMichael Hennerich static ssize_t adp8860_bl_ambient_light_zone_show(struct device *dev,
57382fd53b7SMichael Hennerich 				     struct device_attribute *attr, char *buf)
57482fd53b7SMichael Hennerich {
57582fd53b7SMichael Hennerich 	struct adp8860_bl *data = dev_get_drvdata(dev);
57682fd53b7SMichael Hennerich 	int error;
57782fd53b7SMichael Hennerich 	uint8_t reg_val;
57882fd53b7SMichael Hennerich 
57982fd53b7SMichael Hennerich 	mutex_lock(&data->lock);
58082fd53b7SMichael Hennerich 	error = adp8860_read(data->client, ADP8860_CFGR, &reg_val);
58182fd53b7SMichael Hennerich 	mutex_unlock(&data->lock);
58282fd53b7SMichael Hennerich 
58382fd53b7SMichael Hennerich 	if (error < 0)
58482fd53b7SMichael Hennerich 		return error;
58582fd53b7SMichael Hennerich 
58682fd53b7SMichael Hennerich 	return sprintf(buf, "%u\n",
58782fd53b7SMichael Hennerich 		((reg_val >> CFGR_BLV_SHIFT) & CFGR_BLV_MASK) + 1);
58882fd53b7SMichael Hennerich }
58982fd53b7SMichael Hennerich 
59082fd53b7SMichael Hennerich static ssize_t adp8860_bl_ambient_light_zone_store(struct device *dev,
59182fd53b7SMichael Hennerich 				     struct device_attribute *attr,
59282fd53b7SMichael Hennerich 				     const char *buf, size_t count)
59382fd53b7SMichael Hennerich {
59482fd53b7SMichael Hennerich 	struct adp8860_bl *data = dev_get_drvdata(dev);
59582fd53b7SMichael Hennerich 	unsigned long val;
59682fd53b7SMichael Hennerich 	uint8_t reg_val;
59782fd53b7SMichael Hennerich 	int ret;
59882fd53b7SMichael Hennerich 
59982fd53b7SMichael Hennerich 	ret = strict_strtoul(buf, 10, &val);
60082fd53b7SMichael Hennerich 	if (ret)
60182fd53b7SMichael Hennerich 		return ret;
60282fd53b7SMichael Hennerich 
60382fd53b7SMichael Hennerich 	if (val == 0) {
60482fd53b7SMichael Hennerich 		/* Enable automatic ambient light sensing */
60582fd53b7SMichael Hennerich 		adp8860_set_bits(data->client, ADP8860_MDCR, CMP_AUTOEN);
60682fd53b7SMichael Hennerich 	} else if ((val > 0) && (val < 6)) {
60782fd53b7SMichael Hennerich 		/* Disable automatic ambient light sensing */
60882fd53b7SMichael Hennerich 		adp8860_clr_bits(data->client, ADP8860_MDCR, CMP_AUTOEN);
60982fd53b7SMichael Hennerich 
61082fd53b7SMichael Hennerich 		/* Set user supplied ambient light zone */
61182fd53b7SMichael Hennerich 		mutex_lock(&data->lock);
61282fd53b7SMichael Hennerich 		adp8860_read(data->client, ADP8860_CFGR, &reg_val);
61382fd53b7SMichael Hennerich 		reg_val &= ~(CFGR_BLV_MASK << CFGR_BLV_SHIFT);
61482fd53b7SMichael Hennerich 		reg_val |= val << CFGR_BLV_SHIFT;
61582fd53b7SMichael Hennerich 		adp8860_write(data->client, ADP8860_CFGR, reg_val);
61682fd53b7SMichael Hennerich 		mutex_unlock(&data->lock);
61782fd53b7SMichael Hennerich 	}
61882fd53b7SMichael Hennerich 
61982fd53b7SMichael Hennerich 	return count;
62082fd53b7SMichael Hennerich }
62182fd53b7SMichael Hennerich static DEVICE_ATTR(ambient_light_zone, 0664,
62282fd53b7SMichael Hennerich 		adp8860_bl_ambient_light_zone_show,
62382fd53b7SMichael Hennerich 		adp8860_bl_ambient_light_zone_store);
62482fd53b7SMichael Hennerich #endif
62582fd53b7SMichael Hennerich 
62682fd53b7SMichael Hennerich static struct attribute *adp8860_bl_attributes[] = {
62782fd53b7SMichael Hennerich 	&dev_attr_l3_dark_max.attr,
62882fd53b7SMichael Hennerich 	&dev_attr_l3_dark_dim.attr,
62982fd53b7SMichael Hennerich 	&dev_attr_l2_office_max.attr,
63082fd53b7SMichael Hennerich 	&dev_attr_l2_office_dim.attr,
63182fd53b7SMichael Hennerich 	&dev_attr_l1_daylight_max.attr,
63282fd53b7SMichael Hennerich 	&dev_attr_l1_daylight_dim.attr,
63382fd53b7SMichael Hennerich #ifdef ADP8860_EXT_FEATURES
63482fd53b7SMichael Hennerich 	&dev_attr_ambient_light_level.attr,
63582fd53b7SMichael Hennerich 	&dev_attr_ambient_light_zone.attr,
63682fd53b7SMichael Hennerich #endif
63782fd53b7SMichael Hennerich 	NULL
63882fd53b7SMichael Hennerich };
63982fd53b7SMichael Hennerich 
64082fd53b7SMichael Hennerich static const struct attribute_group adp8860_bl_attr_group = {
64182fd53b7SMichael Hennerich 	.attrs = adp8860_bl_attributes,
64282fd53b7SMichael Hennerich };
64382fd53b7SMichael Hennerich 
64482fd53b7SMichael Hennerich static int __devinit adp8860_probe(struct i2c_client *client,
64582fd53b7SMichael Hennerich 					const struct i2c_device_id *id)
64682fd53b7SMichael Hennerich {
64782fd53b7SMichael Hennerich 	struct backlight_device *bl;
64882fd53b7SMichael Hennerich 	struct adp8860_bl *data;
64982fd53b7SMichael Hennerich 	struct adp8860_backlight_platform_data *pdata =
65082fd53b7SMichael Hennerich 		client->dev.platform_data;
651*3f43f8b2SRandy Dunlap 	struct backlight_properties props;
65282fd53b7SMichael Hennerich 	uint8_t reg_val;
65382fd53b7SMichael Hennerich 	int ret;
65482fd53b7SMichael Hennerich 
65582fd53b7SMichael Hennerich 	if (!i2c_check_functionality(client->adapter,
65682fd53b7SMichael Hennerich 					I2C_FUNC_SMBUS_BYTE_DATA)) {
65782fd53b7SMichael Hennerich 		dev_err(&client->dev, "SMBUS Byte Data not Supported\n");
65882fd53b7SMichael Hennerich 		return -EIO;
65982fd53b7SMichael Hennerich 	}
66082fd53b7SMichael Hennerich 
66182fd53b7SMichael Hennerich 	if (!pdata) {
66282fd53b7SMichael Hennerich 		dev_err(&client->dev, "no platform data?\n");
66382fd53b7SMichael Hennerich 		return -EINVAL;
66482fd53b7SMichael Hennerich 	}
66582fd53b7SMichael Hennerich 
66682fd53b7SMichael Hennerich 	ret = adp8860_read(client, ADP8860_MFDVID, &reg_val);
66782fd53b7SMichael Hennerich 	if (ret < 0)
66882fd53b7SMichael Hennerich 		return -EIO;
66982fd53b7SMichael Hennerich 
67082fd53b7SMichael Hennerich 	if (ADP8860_MANID(reg_val) != ADP8860_MANUFID) {
67182fd53b7SMichael Hennerich 		dev_err(&client->dev, "failed to probe\n");
67282fd53b7SMichael Hennerich 		return -ENODEV;
67382fd53b7SMichael Hennerich 	}
67482fd53b7SMichael Hennerich 
67582fd53b7SMichael Hennerich 	data = kzalloc(sizeof(*data), GFP_KERNEL);
67682fd53b7SMichael Hennerich 	if (data == NULL)
67782fd53b7SMichael Hennerich 		return -ENOMEM;
67882fd53b7SMichael Hennerich 
67982fd53b7SMichael Hennerich 	/* It's confirmed that the DEVID field is actually a REVID */
68082fd53b7SMichael Hennerich 
68182fd53b7SMichael Hennerich 	data->revid = ADP8860_DEVID(reg_val);
68282fd53b7SMichael Hennerich 	data->client = client;
68382fd53b7SMichael Hennerich 	data->pdata = pdata;
68482fd53b7SMichael Hennerich 	data->id = id->driver_data;
68582fd53b7SMichael Hennerich 	data->current_brightness = 0;
68682fd53b7SMichael Hennerich 	i2c_set_clientdata(client, data);
68782fd53b7SMichael Hennerich 
688*3f43f8b2SRandy Dunlap 	memset(&props, 0, sizeof(props));
689*3f43f8b2SRandy Dunlap 	props.max_brightness = ADP8860_MAX_BRIGHTNESS;
690*3f43f8b2SRandy Dunlap 
69182fd53b7SMichael Hennerich 	mutex_init(&data->lock);
69282fd53b7SMichael Hennerich 
69382fd53b7SMichael Hennerich 	bl = backlight_device_register(dev_driver_string(&client->dev),
694*3f43f8b2SRandy Dunlap 			&client->dev, data, &adp8860_bl_ops, &props);
69582fd53b7SMichael Hennerich 	if (IS_ERR(bl)) {
69682fd53b7SMichael Hennerich 		dev_err(&client->dev, "failed to register backlight\n");
69782fd53b7SMichael Hennerich 		ret = PTR_ERR(bl);
69882fd53b7SMichael Hennerich 		goto out2;
69982fd53b7SMichael Hennerich 	}
70082fd53b7SMichael Hennerich 
70182fd53b7SMichael Hennerich 	bl->props.max_brightness =
70282fd53b7SMichael Hennerich 		bl->props.brightness = ADP8860_MAX_BRIGHTNESS;
70382fd53b7SMichael Hennerich 
70482fd53b7SMichael Hennerich 	data->bl = bl;
70582fd53b7SMichael Hennerich 
70682fd53b7SMichael Hennerich 	if (pdata->en_ambl_sens)
70782fd53b7SMichael Hennerich 		ret = sysfs_create_group(&bl->dev.kobj,
70882fd53b7SMichael Hennerich 			&adp8860_bl_attr_group);
70982fd53b7SMichael Hennerich 
71082fd53b7SMichael Hennerich 	if (ret) {
71182fd53b7SMichael Hennerich 		dev_err(&client->dev, "failed to register sysfs\n");
71282fd53b7SMichael Hennerich 		goto out1;
71382fd53b7SMichael Hennerich 	}
71482fd53b7SMichael Hennerich 
71582fd53b7SMichael Hennerich 	ret = adp8860_bl_setup(bl);
71682fd53b7SMichael Hennerich 	if (ret) {
71782fd53b7SMichael Hennerich 		ret = -EIO;
71882fd53b7SMichael Hennerich 		goto out;
71982fd53b7SMichael Hennerich 	}
72082fd53b7SMichael Hennerich 
72182fd53b7SMichael Hennerich 	backlight_update_status(bl);
72282fd53b7SMichael Hennerich 
72382fd53b7SMichael Hennerich 	dev_info(&client->dev, "Rev.%d Backlight\n", data->revid);
72482fd53b7SMichael Hennerich 
72582fd53b7SMichael Hennerich 	if (pdata->num_leds)
72682fd53b7SMichael Hennerich 		adp8860_led_probe(client);
72782fd53b7SMichael Hennerich 
72882fd53b7SMichael Hennerich 	return 0;
72982fd53b7SMichael Hennerich 
73082fd53b7SMichael Hennerich out:
73182fd53b7SMichael Hennerich 	if (data->pdata->en_ambl_sens)
73282fd53b7SMichael Hennerich 		sysfs_remove_group(&data->bl->dev.kobj,
73382fd53b7SMichael Hennerich 			&adp8860_bl_attr_group);
73482fd53b7SMichael Hennerich out1:
73582fd53b7SMichael Hennerich 	backlight_device_unregister(bl);
73682fd53b7SMichael Hennerich out2:
73782fd53b7SMichael Hennerich 	i2c_set_clientdata(client, NULL);
73882fd53b7SMichael Hennerich 	kfree(data);
73982fd53b7SMichael Hennerich 
74082fd53b7SMichael Hennerich 	return ret;
74182fd53b7SMichael Hennerich }
74282fd53b7SMichael Hennerich 
74382fd53b7SMichael Hennerich static int __devexit adp8860_remove(struct i2c_client *client)
74482fd53b7SMichael Hennerich {
74582fd53b7SMichael Hennerich 	struct adp8860_bl *data = i2c_get_clientdata(client);
74682fd53b7SMichael Hennerich 
74782fd53b7SMichael Hennerich 	adp8860_clr_bits(client, ADP8860_MDCR, NSTBY);
74882fd53b7SMichael Hennerich 
74982fd53b7SMichael Hennerich 	if (data->led)
75082fd53b7SMichael Hennerich 		adp8860_led_remove(client);
75182fd53b7SMichael Hennerich 
75282fd53b7SMichael Hennerich 	if (data->pdata->en_ambl_sens)
75382fd53b7SMichael Hennerich 		sysfs_remove_group(&data->bl->dev.kobj,
75482fd53b7SMichael Hennerich 			&adp8860_bl_attr_group);
75582fd53b7SMichael Hennerich 
75682fd53b7SMichael Hennerich 	backlight_device_unregister(data->bl);
75782fd53b7SMichael Hennerich 	i2c_set_clientdata(client, NULL);
75882fd53b7SMichael Hennerich 	kfree(data);
75982fd53b7SMichael Hennerich 
76082fd53b7SMichael Hennerich 	return 0;
76182fd53b7SMichael Hennerich }
76282fd53b7SMichael Hennerich 
76382fd53b7SMichael Hennerich #ifdef CONFIG_PM
76482fd53b7SMichael Hennerich static int adp8860_i2c_suspend(struct i2c_client *client, pm_message_t message)
76582fd53b7SMichael Hennerich {
76682fd53b7SMichael Hennerich 	adp8860_clr_bits(client, ADP8860_MDCR, NSTBY);
76782fd53b7SMichael Hennerich 
76882fd53b7SMichael Hennerich 	return 0;
76982fd53b7SMichael Hennerich }
77082fd53b7SMichael Hennerich 
77182fd53b7SMichael Hennerich static int adp8860_i2c_resume(struct i2c_client *client)
77282fd53b7SMichael Hennerich {
77382fd53b7SMichael Hennerich 	adp8860_set_bits(client, ADP8860_MDCR, NSTBY);
77482fd53b7SMichael Hennerich 
77582fd53b7SMichael Hennerich 	return 0;
77682fd53b7SMichael Hennerich }
77782fd53b7SMichael Hennerich #else
77882fd53b7SMichael Hennerich #define adp8860_i2c_suspend NULL
77982fd53b7SMichael Hennerich #define adp8860_i2c_resume NULL
78082fd53b7SMichael Hennerich #endif
78182fd53b7SMichael Hennerich 
78282fd53b7SMichael Hennerich static const struct i2c_device_id adp8860_id[] = {
78382fd53b7SMichael Hennerich 	{ "adp8860", 0 },
78482fd53b7SMichael Hennerich 	{ }
78582fd53b7SMichael Hennerich };
78682fd53b7SMichael Hennerich MODULE_DEVICE_TABLE(i2c, adp8860_id);
78782fd53b7SMichael Hennerich 
78882fd53b7SMichael Hennerich static struct i2c_driver adp8860_driver = {
78982fd53b7SMichael Hennerich 	.driver = {
79082fd53b7SMichael Hennerich 		.name = KBUILD_MODNAME,
79182fd53b7SMichael Hennerich 	},
79282fd53b7SMichael Hennerich 	.probe    = adp8860_probe,
79382fd53b7SMichael Hennerich 	.remove   = __devexit_p(adp8860_remove),
79482fd53b7SMichael Hennerich 	.suspend = adp8860_i2c_suspend,
79582fd53b7SMichael Hennerich 	.resume  = adp8860_i2c_resume,
79682fd53b7SMichael Hennerich 	.id_table = adp8860_id,
79782fd53b7SMichael Hennerich };
79882fd53b7SMichael Hennerich 
79982fd53b7SMichael Hennerich static int __init adp8860_init(void)
80082fd53b7SMichael Hennerich {
80182fd53b7SMichael Hennerich 	return i2c_add_driver(&adp8860_driver);
80282fd53b7SMichael Hennerich }
80382fd53b7SMichael Hennerich module_init(adp8860_init);
80482fd53b7SMichael Hennerich 
80582fd53b7SMichael Hennerich static void __exit adp8860_exit(void)
80682fd53b7SMichael Hennerich {
80782fd53b7SMichael Hennerich 	i2c_del_driver(&adp8860_driver);
80882fd53b7SMichael Hennerich }
80982fd53b7SMichael Hennerich module_exit(adp8860_exit);
81082fd53b7SMichael Hennerich 
81182fd53b7SMichael Hennerich MODULE_LICENSE("GPL v2");
81282fd53b7SMichael Hennerich MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
81382fd53b7SMichael Hennerich MODULE_DESCRIPTION("ADP8860 Backlight driver");
81482fd53b7SMichael Hennerich MODULE_ALIAS("i2c:adp8860-backlight");
815