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