xref: /linux/drivers/video/backlight/adp8860_bl.c (revision 5e548f0f55a9f8165a3f36226ac5d3f42a05cf47)
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);
184*5e548f0fSJingoo Han 
18582fd53b7SMichael Hennerich 	adp8860_write(led->client, ADP8860_ISC1 - led->id + 1,
18682fd53b7SMichael Hennerich 			 led->new_brightness >> 1);
18782fd53b7SMichael Hennerich }
18882fd53b7SMichael Hennerich 
18982fd53b7SMichael Hennerich static void adp8860_led_set(struct led_classdev *led_cdev,
19082fd53b7SMichael Hennerich 			   enum led_brightness value)
19182fd53b7SMichael Hennerich {
19282fd53b7SMichael Hennerich 	struct adp8860_led *led;
19382fd53b7SMichael Hennerich 
19482fd53b7SMichael Hennerich 	led = container_of(led_cdev, struct adp8860_led, cdev);
19582fd53b7SMichael Hennerich 	led->new_brightness = value;
19682fd53b7SMichael Hennerich 	schedule_work(&led->work);
19782fd53b7SMichael Hennerich }
19882fd53b7SMichael Hennerich 
19982fd53b7SMichael Hennerich static int adp8860_led_setup(struct adp8860_led *led)
20082fd53b7SMichael Hennerich {
20182fd53b7SMichael Hennerich 	struct i2c_client *client = led->client;
20282fd53b7SMichael Hennerich 	int ret = 0;
20382fd53b7SMichael Hennerich 
20482fd53b7SMichael Hennerich 	ret = adp8860_write(client, ADP8860_ISC1 - led->id + 1, 0);
20582fd53b7SMichael Hennerich 	ret |= adp8860_set_bits(client, ADP8860_ISCC, 1 << (led->id - 1));
20682fd53b7SMichael Hennerich 
20782fd53b7SMichael Hennerich 	if (led->id > 4)
20882fd53b7SMichael Hennerich 		ret |= adp8860_set_bits(client, ADP8860_ISCT1,
20982fd53b7SMichael Hennerich 				(led->flags & 0x3) << ((led->id - 5) * 2));
21082fd53b7SMichael Hennerich 	else
21182fd53b7SMichael Hennerich 		ret |= adp8860_set_bits(client, ADP8860_ISCT2,
21282fd53b7SMichael Hennerich 				(led->flags & 0x3) << ((led->id - 1) * 2));
21382fd53b7SMichael Hennerich 
21482fd53b7SMichael Hennerich 	return ret;
21582fd53b7SMichael Hennerich }
21682fd53b7SMichael Hennerich 
2171b9e450dSBill Pemberton static int adp8860_led_probe(struct i2c_client *client)
21882fd53b7SMichael Hennerich {
21982fd53b7SMichael Hennerich 	struct adp8860_backlight_platform_data *pdata =
220c512794cSJingoo Han 		dev_get_platdata(&client->dev);
22182fd53b7SMichael Hennerich 	struct adp8860_bl *data = i2c_get_clientdata(client);
22282fd53b7SMichael Hennerich 	struct adp8860_led *led, *led_dat;
22382fd53b7SMichael Hennerich 	struct led_info *cur_led;
22482fd53b7SMichael Hennerich 	int ret, i;
22582fd53b7SMichael Hennerich 
22658875ea9SJingoo Han 	led = devm_kzalloc(&client->dev, sizeof(*led) * pdata->num_leds,
22758875ea9SJingoo Han 				GFP_KERNEL);
228ba464d0cSJingoo Han 	if (led == NULL)
22982fd53b7SMichael Hennerich 		return -ENOMEM;
23082fd53b7SMichael Hennerich 
23182fd53b7SMichael Hennerich 	ret = adp8860_write(client, ADP8860_ISCFR, pdata->led_fade_law);
23282fd53b7SMichael Hennerich 	ret = adp8860_write(client, ADP8860_ISCT1,
23382fd53b7SMichael Hennerich 			(pdata->led_on_time & 0x3) << 6);
23482fd53b7SMichael Hennerich 	ret |= adp8860_write(client, ADP8860_ISCF,
23582fd53b7SMichael Hennerich 			FADE_VAL(pdata->led_fade_in, pdata->led_fade_out));
23682fd53b7SMichael Hennerich 
23782fd53b7SMichael Hennerich 	if (ret) {
23882fd53b7SMichael Hennerich 		dev_err(&client->dev, "failed to write\n");
23958875ea9SJingoo Han 		return ret;
24082fd53b7SMichael Hennerich 	}
24182fd53b7SMichael Hennerich 
24282fd53b7SMichael Hennerich 	for (i = 0; i < pdata->num_leds; ++i) {
24382fd53b7SMichael Hennerich 		cur_led = &pdata->leds[i];
24482fd53b7SMichael Hennerich 		led_dat = &led[i];
24582fd53b7SMichael Hennerich 
24682fd53b7SMichael Hennerich 		led_dat->id = cur_led->flags & ADP8860_FLAG_LED_MASK;
24782fd53b7SMichael Hennerich 
24882fd53b7SMichael Hennerich 		if (led_dat->id > 7 || led_dat->id < 1) {
24982fd53b7SMichael Hennerich 			dev_err(&client->dev, "Invalid LED ID %d\n",
25082fd53b7SMichael Hennerich 				led_dat->id);
25184eba9edSWei Yongjun 			ret = -EINVAL;
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);
25884eba9edSWei Yongjun 			ret = -EBUSY;
25982fd53b7SMichael Hennerich 			goto err;
26082fd53b7SMichael Hennerich 		}
26182fd53b7SMichael Hennerich 
26282fd53b7SMichael Hennerich 		led_dat->cdev.name = cur_led->name;
26382fd53b7SMichael Hennerich 		led_dat->cdev.default_trigger = cur_led->default_trigger;
26482fd53b7SMichael Hennerich 		led_dat->cdev.brightness_set = adp8860_led_set;
26582fd53b7SMichael Hennerich 		led_dat->cdev.brightness = LED_OFF;
26682fd53b7SMichael Hennerich 		led_dat->flags = cur_led->flags >> FLAG_OFFT_SHIFT;
26782fd53b7SMichael Hennerich 		led_dat->client = client;
26882fd53b7SMichael Hennerich 		led_dat->new_brightness = LED_OFF;
26982fd53b7SMichael Hennerich 		INIT_WORK(&led_dat->work, adp8860_led_work);
27082fd53b7SMichael Hennerich 
27182fd53b7SMichael Hennerich 		ret = led_classdev_register(&client->dev, &led_dat->cdev);
27282fd53b7SMichael Hennerich 		if (ret) {
27382fd53b7SMichael Hennerich 			dev_err(&client->dev, "failed to register LED %d\n",
27482fd53b7SMichael Hennerich 				led_dat->id);
27582fd53b7SMichael Hennerich 			goto err;
27682fd53b7SMichael Hennerich 		}
27782fd53b7SMichael Hennerich 
27882fd53b7SMichael Hennerich 		ret = adp8860_led_setup(led_dat);
27982fd53b7SMichael Hennerich 		if (ret) {
28082fd53b7SMichael Hennerich 			dev_err(&client->dev, "failed to write\n");
28182fd53b7SMichael Hennerich 			i++;
28282fd53b7SMichael Hennerich 			goto err;
28382fd53b7SMichael Hennerich 		}
28482fd53b7SMichael Hennerich 	}
28582fd53b7SMichael Hennerich 
28682fd53b7SMichael Hennerich 	data->led = led;
28782fd53b7SMichael Hennerich 
28882fd53b7SMichael Hennerich 	return 0;
28982fd53b7SMichael Hennerich 
29082fd53b7SMichael Hennerich  err:
29182fd53b7SMichael Hennerich 	for (i = i - 1; i >= 0; --i) {
29282fd53b7SMichael Hennerich 		led_classdev_unregister(&led[i].cdev);
29382fd53b7SMichael Hennerich 		cancel_work_sync(&led[i].work);
29482fd53b7SMichael Hennerich 	}
29582fd53b7SMichael Hennerich 
29682fd53b7SMichael Hennerich 	return ret;
29782fd53b7SMichael Hennerich }
29882fd53b7SMichael Hennerich 
2997e4b9d0bSBill Pemberton static int adp8860_led_remove(struct i2c_client *client)
30082fd53b7SMichael Hennerich {
30182fd53b7SMichael Hennerich 	struct adp8860_backlight_platform_data *pdata =
302c512794cSJingoo Han 		dev_get_platdata(&client->dev);
30382fd53b7SMichael Hennerich 	struct adp8860_bl *data = i2c_get_clientdata(client);
30482fd53b7SMichael Hennerich 	int i;
30582fd53b7SMichael Hennerich 
30682fd53b7SMichael Hennerich 	for (i = 0; i < pdata->num_leds; i++) {
30782fd53b7SMichael Hennerich 		led_classdev_unregister(&data->led[i].cdev);
30882fd53b7SMichael Hennerich 		cancel_work_sync(&data->led[i].work);
30982fd53b7SMichael Hennerich 	}
31082fd53b7SMichael Hennerich 
31182fd53b7SMichael Hennerich 	return 0;
31282fd53b7SMichael Hennerich }
31382fd53b7SMichael Hennerich #else
3141b9e450dSBill Pemberton static int adp8860_led_probe(struct i2c_client *client)
31582fd53b7SMichael Hennerich {
31682fd53b7SMichael Hennerich 	return 0;
31782fd53b7SMichael Hennerich }
31882fd53b7SMichael Hennerich 
3197e4b9d0bSBill Pemberton static int adp8860_led_remove(struct i2c_client *client)
32082fd53b7SMichael Hennerich {
32182fd53b7SMichael Hennerich 	return 0;
32282fd53b7SMichael Hennerich }
32382fd53b7SMichael Hennerich #endif
32482fd53b7SMichael Hennerich 
32582fd53b7SMichael Hennerich static int adp8860_bl_set(struct backlight_device *bl, int brightness)
32682fd53b7SMichael Hennerich {
32782fd53b7SMichael Hennerich 	struct adp8860_bl *data = bl_get_data(bl);
32882fd53b7SMichael Hennerich 	struct i2c_client *client = data->client;
32982fd53b7SMichael Hennerich 	int ret = 0;
33082fd53b7SMichael Hennerich 
331c7c06d8aSMichael Hennerich 	if (data->en_ambl_sens) {
33282fd53b7SMichael Hennerich 		if ((brightness > 0) && (brightness < ADP8860_MAX_BRIGHTNESS)) {
33382fd53b7SMichael Hennerich 			/* Disable Ambient Light auto adjust */
33482fd53b7SMichael Hennerich 			ret |= adp8860_clr_bits(client, ADP8860_MDCR,
33582fd53b7SMichael Hennerich 					CMP_AUTOEN);
33682fd53b7SMichael Hennerich 			ret |= adp8860_write(client, ADP8860_BLMX1, brightness);
33782fd53b7SMichael Hennerich 		} else {
33882fd53b7SMichael Hennerich 			/*
33982fd53b7SMichael Hennerich 			 * MAX_BRIGHTNESS -> Enable Ambient Light auto adjust
34082fd53b7SMichael Hennerich 			 * restore daylight l1 sysfs brightness
34182fd53b7SMichael Hennerich 			 */
34282fd53b7SMichael Hennerich 			ret |= adp8860_write(client, ADP8860_BLMX1,
34382fd53b7SMichael Hennerich 					 data->cached_daylight_max);
34482fd53b7SMichael Hennerich 			ret |= adp8860_set_bits(client, ADP8860_MDCR,
34582fd53b7SMichael Hennerich 					 CMP_AUTOEN);
34682fd53b7SMichael Hennerich 		}
34782fd53b7SMichael Hennerich 	} else
34882fd53b7SMichael Hennerich 		ret |= adp8860_write(client, ADP8860_BLMX1, brightness);
34982fd53b7SMichael Hennerich 
35082fd53b7SMichael Hennerich 	if (data->current_brightness && brightness == 0)
35182fd53b7SMichael Hennerich 		ret |= adp8860_set_bits(client,
35282fd53b7SMichael Hennerich 				ADP8860_MDCR, DIM_EN);
35382fd53b7SMichael Hennerich 	else if (data->current_brightness == 0 && brightness)
35482fd53b7SMichael Hennerich 		ret |= adp8860_clr_bits(client,
35582fd53b7SMichael Hennerich 				ADP8860_MDCR, DIM_EN);
35682fd53b7SMichael Hennerich 
35782fd53b7SMichael Hennerich 	if (!ret)
35882fd53b7SMichael Hennerich 		data->current_brightness = brightness;
35982fd53b7SMichael Hennerich 
36082fd53b7SMichael Hennerich 	return ret;
36182fd53b7SMichael Hennerich }
36282fd53b7SMichael Hennerich 
36382fd53b7SMichael Hennerich static int adp8860_bl_update_status(struct backlight_device *bl)
36482fd53b7SMichael Hennerich {
36582fd53b7SMichael Hennerich 	int brightness = bl->props.brightness;
366*5e548f0fSJingoo Han 
36782fd53b7SMichael Hennerich 	if (bl->props.power != FB_BLANK_UNBLANK)
36882fd53b7SMichael Hennerich 		brightness = 0;
36982fd53b7SMichael Hennerich 
37082fd53b7SMichael Hennerich 	if (bl->props.fb_blank != FB_BLANK_UNBLANK)
37182fd53b7SMichael Hennerich 		brightness = 0;
37282fd53b7SMichael Hennerich 
37382fd53b7SMichael Hennerich 	return adp8860_bl_set(bl, brightness);
37482fd53b7SMichael Hennerich }
37582fd53b7SMichael Hennerich 
37682fd53b7SMichael Hennerich static int adp8860_bl_get_brightness(struct backlight_device *bl)
37782fd53b7SMichael Hennerich {
37882fd53b7SMichael Hennerich 	struct adp8860_bl *data = bl_get_data(bl);
37982fd53b7SMichael Hennerich 
38082fd53b7SMichael Hennerich 	return data->current_brightness;
38182fd53b7SMichael Hennerich }
38282fd53b7SMichael Hennerich 
38382fd53b7SMichael Hennerich static const struct backlight_ops adp8860_bl_ops = {
38482fd53b7SMichael Hennerich 	.update_status	= adp8860_bl_update_status,
38582fd53b7SMichael Hennerich 	.get_brightness	= adp8860_bl_get_brightness,
38682fd53b7SMichael Hennerich };
38782fd53b7SMichael Hennerich 
38882fd53b7SMichael Hennerich static int adp8860_bl_setup(struct backlight_device *bl)
38982fd53b7SMichael Hennerich {
39082fd53b7SMichael Hennerich 	struct adp8860_bl *data = bl_get_data(bl);
39182fd53b7SMichael Hennerich 	struct i2c_client *client = data->client;
39282fd53b7SMichael Hennerich 	struct adp8860_backlight_platform_data *pdata = data->pdata;
39382fd53b7SMichael Hennerich 	int ret = 0;
39482fd53b7SMichael Hennerich 
39582fd53b7SMichael Hennerich 	ret |= adp8860_write(client, ADP8860_BLSEN, ~pdata->bl_led_assign);
39682fd53b7SMichael Hennerich 	ret |= adp8860_write(client, ADP8860_BLMX1, pdata->l1_daylight_max);
39782fd53b7SMichael Hennerich 	ret |= adp8860_write(client, ADP8860_BLDM1, pdata->l1_daylight_dim);
39882fd53b7SMichael Hennerich 
399c7c06d8aSMichael Hennerich 	if (data->en_ambl_sens) {
40082fd53b7SMichael Hennerich 		data->cached_daylight_max = pdata->l1_daylight_max;
40182fd53b7SMichael Hennerich 		ret |= adp8860_write(client, ADP8860_BLMX2,
40282fd53b7SMichael Hennerich 						pdata->l2_office_max);
40382fd53b7SMichael Hennerich 		ret |= adp8860_write(client, ADP8860_BLDM2,
40482fd53b7SMichael Hennerich 						pdata->l2_office_dim);
40582fd53b7SMichael Hennerich 		ret |= adp8860_write(client, ADP8860_BLMX3,
40682fd53b7SMichael Hennerich 						pdata->l3_dark_max);
40782fd53b7SMichael Hennerich 		ret |= adp8860_write(client, ADP8860_BLDM3,
40882fd53b7SMichael Hennerich 						pdata->l3_dark_dim);
40982fd53b7SMichael Hennerich 
41082fd53b7SMichael Hennerich 		ret |= adp8860_write(client, ADP8860_L2_TRP, pdata->l2_trip);
41182fd53b7SMichael Hennerich 		ret |= adp8860_write(client, ADP8860_L2_HYS, pdata->l2_hyst);
41282fd53b7SMichael Hennerich 		ret |= adp8860_write(client, ADP8860_L3_TRP, pdata->l3_trip);
41382fd53b7SMichael Hennerich 		ret |= adp8860_write(client, ADP8860_L3_HYS, pdata->l3_hyst);
41482fd53b7SMichael Hennerich 		ret |= adp8860_write(client, ADP8860_CCFG, L2_EN | L3_EN |
41582fd53b7SMichael Hennerich 						ALS_CCFG_VAL(pdata->abml_filt));
41682fd53b7SMichael Hennerich 	}
41782fd53b7SMichael Hennerich 
41882fd53b7SMichael Hennerich 	ret |= adp8860_write(client, ADP8860_CFGR,
41982fd53b7SMichael Hennerich 			BL_CFGR_VAL(pdata->bl_fade_law, 0));
42082fd53b7SMichael Hennerich 
42182fd53b7SMichael Hennerich 	ret |= adp8860_write(client, ADP8860_BLFR, FADE_VAL(pdata->bl_fade_in,
42282fd53b7SMichael Hennerich 			pdata->bl_fade_out));
42382fd53b7SMichael Hennerich 
424c7c06d8aSMichael Hennerich 	ret |= adp8860_set_bits(client, ADP8860_MDCR, BLEN | DIM_EN | NSTBY |
425c7c06d8aSMichael Hennerich 			(data->gdwn_dis ? GDWN_DIS : 0));
42682fd53b7SMichael Hennerich 
42782fd53b7SMichael Hennerich 	return ret;
42882fd53b7SMichael Hennerich }
42982fd53b7SMichael Hennerich 
43082fd53b7SMichael Hennerich static ssize_t adp8860_show(struct device *dev, char *buf, int reg)
43182fd53b7SMichael Hennerich {
43282fd53b7SMichael Hennerich 	struct adp8860_bl *data = dev_get_drvdata(dev);
43382fd53b7SMichael Hennerich 	int error;
43482fd53b7SMichael Hennerich 	uint8_t reg_val;
43582fd53b7SMichael Hennerich 
43682fd53b7SMichael Hennerich 	mutex_lock(&data->lock);
43782fd53b7SMichael Hennerich 	error = adp8860_read(data->client, reg, &reg_val);
43882fd53b7SMichael Hennerich 	mutex_unlock(&data->lock);
43982fd53b7SMichael Hennerich 
44082fd53b7SMichael Hennerich 	if (error < 0)
44182fd53b7SMichael Hennerich 		return error;
44282fd53b7SMichael Hennerich 
44382fd53b7SMichael Hennerich 	return sprintf(buf, "%u\n", reg_val);
44482fd53b7SMichael Hennerich }
44582fd53b7SMichael Hennerich 
44682fd53b7SMichael Hennerich static ssize_t adp8860_store(struct device *dev, const char *buf,
44782fd53b7SMichael Hennerich 			 size_t count, int reg)
44882fd53b7SMichael Hennerich {
44982fd53b7SMichael Hennerich 	struct adp8860_bl *data = dev_get_drvdata(dev);
45082fd53b7SMichael Hennerich 	unsigned long val;
45182fd53b7SMichael Hennerich 	int ret;
45282fd53b7SMichael Hennerich 
4538dd9d7f2SJingoo Han 	ret = kstrtoul(buf, 10, &val);
45482fd53b7SMichael Hennerich 	if (ret)
45582fd53b7SMichael Hennerich 		return ret;
45682fd53b7SMichael Hennerich 
45782fd53b7SMichael Hennerich 	mutex_lock(&data->lock);
45882fd53b7SMichael Hennerich 	adp8860_write(data->client, reg, val);
45982fd53b7SMichael Hennerich 	mutex_unlock(&data->lock);
46082fd53b7SMichael Hennerich 
46182fd53b7SMichael Hennerich 	return count;
46282fd53b7SMichael Hennerich }
46382fd53b7SMichael Hennerich 
46482fd53b7SMichael Hennerich static ssize_t adp8860_bl_l3_dark_max_show(struct device *dev,
46582fd53b7SMichael Hennerich 				     struct device_attribute *attr, char *buf)
46682fd53b7SMichael Hennerich {
46782fd53b7SMichael Hennerich 	return adp8860_show(dev, buf, ADP8860_BLMX3);
46882fd53b7SMichael Hennerich }
46982fd53b7SMichael Hennerich 
47082fd53b7SMichael Hennerich static ssize_t adp8860_bl_l3_dark_max_store(struct device *dev,
47182fd53b7SMichael Hennerich 		struct device_attribute *attr, const char *buf, size_t count)
47282fd53b7SMichael Hennerich {
47382fd53b7SMichael Hennerich 	return adp8860_store(dev, buf, count, ADP8860_BLMX3);
47482fd53b7SMichael Hennerich }
47582fd53b7SMichael Hennerich 
47682fd53b7SMichael Hennerich static DEVICE_ATTR(l3_dark_max, 0664, adp8860_bl_l3_dark_max_show,
47782fd53b7SMichael Hennerich 			adp8860_bl_l3_dark_max_store);
47882fd53b7SMichael Hennerich 
47982fd53b7SMichael Hennerich static ssize_t adp8860_bl_l2_office_max_show(struct device *dev,
48082fd53b7SMichael Hennerich 		struct device_attribute *attr, char *buf)
48182fd53b7SMichael Hennerich {
48282fd53b7SMichael Hennerich 	return adp8860_show(dev, buf, ADP8860_BLMX2);
48382fd53b7SMichael Hennerich }
48482fd53b7SMichael Hennerich 
48582fd53b7SMichael Hennerich static ssize_t adp8860_bl_l2_office_max_store(struct device *dev,
48682fd53b7SMichael Hennerich 		struct device_attribute *attr, const char *buf, size_t count)
48782fd53b7SMichael Hennerich {
48882fd53b7SMichael Hennerich 	return adp8860_store(dev, buf, count, ADP8860_BLMX2);
48982fd53b7SMichael Hennerich }
49082fd53b7SMichael Hennerich static DEVICE_ATTR(l2_office_max, 0664, adp8860_bl_l2_office_max_show,
49182fd53b7SMichael Hennerich 			adp8860_bl_l2_office_max_store);
49282fd53b7SMichael Hennerich 
49382fd53b7SMichael Hennerich static ssize_t adp8860_bl_l1_daylight_max_show(struct device *dev,
49482fd53b7SMichael Hennerich 			struct device_attribute *attr, char *buf)
49582fd53b7SMichael Hennerich {
49682fd53b7SMichael Hennerich 	return adp8860_show(dev, buf, ADP8860_BLMX1);
49782fd53b7SMichael Hennerich }
49882fd53b7SMichael Hennerich 
49982fd53b7SMichael Hennerich static ssize_t adp8860_bl_l1_daylight_max_store(struct device *dev,
50082fd53b7SMichael Hennerich 		struct device_attribute *attr, const char *buf, size_t count)
50182fd53b7SMichael Hennerich {
50282fd53b7SMichael Hennerich 	struct adp8860_bl *data = dev_get_drvdata(dev);
5038dd9d7f2SJingoo Han 	int ret = kstrtoul(buf, 10, &data->cached_daylight_max);
504*5e548f0fSJingoo Han 
5054f1aa846SMichael Hennerich 	if (ret)
5064f1aa846SMichael Hennerich 		return ret;
50782fd53b7SMichael Hennerich 
50882fd53b7SMichael Hennerich 	return adp8860_store(dev, buf, count, ADP8860_BLMX1);
50982fd53b7SMichael Hennerich }
51082fd53b7SMichael Hennerich static DEVICE_ATTR(l1_daylight_max, 0664, adp8860_bl_l1_daylight_max_show,
51182fd53b7SMichael Hennerich 			adp8860_bl_l1_daylight_max_store);
51282fd53b7SMichael Hennerich 
51382fd53b7SMichael Hennerich static ssize_t adp8860_bl_l3_dark_dim_show(struct device *dev,
51482fd53b7SMichael Hennerich 			struct device_attribute *attr, char *buf)
51582fd53b7SMichael Hennerich {
51682fd53b7SMichael Hennerich 	return adp8860_show(dev, buf, ADP8860_BLDM3);
51782fd53b7SMichael Hennerich }
51882fd53b7SMichael Hennerich 
51982fd53b7SMichael Hennerich static ssize_t adp8860_bl_l3_dark_dim_store(struct device *dev,
52082fd53b7SMichael Hennerich 				     struct device_attribute *attr,
52182fd53b7SMichael Hennerich 				     const char *buf, size_t count)
52282fd53b7SMichael Hennerich {
52382fd53b7SMichael Hennerich 	return adp8860_store(dev, buf, count, ADP8860_BLDM3);
52482fd53b7SMichael Hennerich }
52582fd53b7SMichael Hennerich static DEVICE_ATTR(l3_dark_dim, 0664, adp8860_bl_l3_dark_dim_show,
52682fd53b7SMichael Hennerich 			adp8860_bl_l3_dark_dim_store);
52782fd53b7SMichael Hennerich 
52882fd53b7SMichael Hennerich static ssize_t adp8860_bl_l2_office_dim_show(struct device *dev,
52982fd53b7SMichael Hennerich 			struct device_attribute *attr, char *buf)
53082fd53b7SMichael Hennerich {
53182fd53b7SMichael Hennerich 	return adp8860_show(dev, buf, ADP8860_BLDM2);
53282fd53b7SMichael Hennerich }
53382fd53b7SMichael Hennerich 
53482fd53b7SMichael Hennerich static ssize_t adp8860_bl_l2_office_dim_store(struct device *dev,
53582fd53b7SMichael Hennerich 				     struct device_attribute *attr,
53682fd53b7SMichael Hennerich 				     const char *buf, size_t count)
53782fd53b7SMichael Hennerich {
53882fd53b7SMichael Hennerich 	return adp8860_store(dev, buf, count, ADP8860_BLDM2);
53982fd53b7SMichael Hennerich }
54082fd53b7SMichael Hennerich static DEVICE_ATTR(l2_office_dim, 0664, adp8860_bl_l2_office_dim_show,
54182fd53b7SMichael Hennerich 			adp8860_bl_l2_office_dim_store);
54282fd53b7SMichael Hennerich 
54382fd53b7SMichael Hennerich static ssize_t adp8860_bl_l1_daylight_dim_show(struct device *dev,
54482fd53b7SMichael Hennerich 				     struct device_attribute *attr, char *buf)
54582fd53b7SMichael Hennerich {
54682fd53b7SMichael Hennerich 	return adp8860_show(dev, buf, ADP8860_BLDM1);
54782fd53b7SMichael Hennerich }
54882fd53b7SMichael Hennerich 
54982fd53b7SMichael Hennerich static ssize_t adp8860_bl_l1_daylight_dim_store(struct device *dev,
55082fd53b7SMichael Hennerich 				     struct device_attribute *attr,
55182fd53b7SMichael Hennerich 				     const char *buf, size_t count)
55282fd53b7SMichael Hennerich {
55382fd53b7SMichael Hennerich 	return adp8860_store(dev, buf, count, ADP8860_BLDM1);
55482fd53b7SMichael Hennerich }
55582fd53b7SMichael Hennerich static DEVICE_ATTR(l1_daylight_dim, 0664, adp8860_bl_l1_daylight_dim_show,
55682fd53b7SMichael Hennerich 			adp8860_bl_l1_daylight_dim_store);
55782fd53b7SMichael Hennerich 
55882fd53b7SMichael Hennerich #ifdef ADP8860_EXT_FEATURES
55982fd53b7SMichael Hennerich static ssize_t adp8860_bl_ambient_light_level_show(struct device *dev,
56082fd53b7SMichael Hennerich 				     struct device_attribute *attr, char *buf)
56182fd53b7SMichael Hennerich {
56282fd53b7SMichael Hennerich 	struct adp8860_bl *data = dev_get_drvdata(dev);
56382fd53b7SMichael Hennerich 	int error;
56482fd53b7SMichael Hennerich 	uint8_t reg_val;
56582fd53b7SMichael Hennerich 	uint16_t ret_val;
56682fd53b7SMichael Hennerich 
56782fd53b7SMichael Hennerich 	mutex_lock(&data->lock);
56882fd53b7SMichael Hennerich 	error = adp8860_read(data->client, ADP8860_PH1LEVL, &reg_val);
56982fd53b7SMichael Hennerich 	ret_val = reg_val;
57082fd53b7SMichael Hennerich 	error |= adp8860_read(data->client, ADP8860_PH1LEVH, &reg_val);
57182fd53b7SMichael Hennerich 	mutex_unlock(&data->lock);
57282fd53b7SMichael Hennerich 
57382fd53b7SMichael Hennerich 	if (error < 0)
57482fd53b7SMichael Hennerich 		return error;
57582fd53b7SMichael Hennerich 
57682fd53b7SMichael Hennerich 	/* Return 13-bit conversion value for the first light sensor */
57782fd53b7SMichael Hennerich 	ret_val += (reg_val & 0x1F) << 8;
57882fd53b7SMichael Hennerich 
57982fd53b7SMichael Hennerich 	return sprintf(buf, "%u\n", ret_val);
58082fd53b7SMichael Hennerich }
58182fd53b7SMichael Hennerich static DEVICE_ATTR(ambient_light_level, 0444,
58282fd53b7SMichael Hennerich 		adp8860_bl_ambient_light_level_show, NULL);
58382fd53b7SMichael Hennerich 
58482fd53b7SMichael Hennerich static ssize_t adp8860_bl_ambient_light_zone_show(struct device *dev,
58582fd53b7SMichael Hennerich 				     struct device_attribute *attr, char *buf)
58682fd53b7SMichael Hennerich {
58782fd53b7SMichael Hennerich 	struct adp8860_bl *data = dev_get_drvdata(dev);
58882fd53b7SMichael Hennerich 	int error;
58982fd53b7SMichael Hennerich 	uint8_t reg_val;
59082fd53b7SMichael Hennerich 
59182fd53b7SMichael Hennerich 	mutex_lock(&data->lock);
59282fd53b7SMichael Hennerich 	error = adp8860_read(data->client, ADP8860_CFGR, &reg_val);
59382fd53b7SMichael Hennerich 	mutex_unlock(&data->lock);
59482fd53b7SMichael Hennerich 
59582fd53b7SMichael Hennerich 	if (error < 0)
59682fd53b7SMichael Hennerich 		return error;
59782fd53b7SMichael Hennerich 
59882fd53b7SMichael Hennerich 	return sprintf(buf, "%u\n",
59982fd53b7SMichael Hennerich 		((reg_val >> CFGR_BLV_SHIFT) & CFGR_BLV_MASK) + 1);
60082fd53b7SMichael Hennerich }
60182fd53b7SMichael Hennerich 
60282fd53b7SMichael Hennerich static ssize_t adp8860_bl_ambient_light_zone_store(struct device *dev,
60382fd53b7SMichael Hennerich 				     struct device_attribute *attr,
60482fd53b7SMichael Hennerich 				     const char *buf, size_t count)
60582fd53b7SMichael Hennerich {
60682fd53b7SMichael Hennerich 	struct adp8860_bl *data = dev_get_drvdata(dev);
60782fd53b7SMichael Hennerich 	unsigned long val;
60882fd53b7SMichael Hennerich 	uint8_t reg_val;
60982fd53b7SMichael Hennerich 	int ret;
61082fd53b7SMichael Hennerich 
6118dd9d7f2SJingoo Han 	ret = kstrtoul(buf, 10, &val);
61282fd53b7SMichael Hennerich 	if (ret)
61382fd53b7SMichael Hennerich 		return ret;
61482fd53b7SMichael Hennerich 
61582fd53b7SMichael Hennerich 	if (val == 0) {
61682fd53b7SMichael Hennerich 		/* Enable automatic ambient light sensing */
61782fd53b7SMichael Hennerich 		adp8860_set_bits(data->client, ADP8860_MDCR, CMP_AUTOEN);
618c7ce2500SMichael Hennerich 	} else if ((val > 0) && (val <= 3)) {
61982fd53b7SMichael Hennerich 		/* Disable automatic ambient light sensing */
62082fd53b7SMichael Hennerich 		adp8860_clr_bits(data->client, ADP8860_MDCR, CMP_AUTOEN);
62182fd53b7SMichael Hennerich 
62282fd53b7SMichael Hennerich 		/* Set user supplied ambient light zone */
62382fd53b7SMichael Hennerich 		mutex_lock(&data->lock);
62482fd53b7SMichael Hennerich 		adp8860_read(data->client, ADP8860_CFGR, &reg_val);
62582fd53b7SMichael Hennerich 		reg_val &= ~(CFGR_BLV_MASK << CFGR_BLV_SHIFT);
626c7ce2500SMichael Hennerich 		reg_val |= (val - 1) << CFGR_BLV_SHIFT;
62782fd53b7SMichael Hennerich 		adp8860_write(data->client, ADP8860_CFGR, reg_val);
62882fd53b7SMichael Hennerich 		mutex_unlock(&data->lock);
62982fd53b7SMichael Hennerich 	}
63082fd53b7SMichael Hennerich 
63182fd53b7SMichael Hennerich 	return count;
63282fd53b7SMichael Hennerich }
63382fd53b7SMichael Hennerich static DEVICE_ATTR(ambient_light_zone, 0664,
63482fd53b7SMichael Hennerich 		adp8860_bl_ambient_light_zone_show,
63582fd53b7SMichael Hennerich 		adp8860_bl_ambient_light_zone_store);
63682fd53b7SMichael Hennerich #endif
63782fd53b7SMichael Hennerich 
63882fd53b7SMichael Hennerich static struct attribute *adp8860_bl_attributes[] = {
63982fd53b7SMichael Hennerich 	&dev_attr_l3_dark_max.attr,
64082fd53b7SMichael Hennerich 	&dev_attr_l3_dark_dim.attr,
64182fd53b7SMichael Hennerich 	&dev_attr_l2_office_max.attr,
64282fd53b7SMichael Hennerich 	&dev_attr_l2_office_dim.attr,
64382fd53b7SMichael Hennerich 	&dev_attr_l1_daylight_max.attr,
64482fd53b7SMichael Hennerich 	&dev_attr_l1_daylight_dim.attr,
64582fd53b7SMichael Hennerich #ifdef ADP8860_EXT_FEATURES
64682fd53b7SMichael Hennerich 	&dev_attr_ambient_light_level.attr,
64782fd53b7SMichael Hennerich 	&dev_attr_ambient_light_zone.attr,
64882fd53b7SMichael Hennerich #endif
64982fd53b7SMichael Hennerich 	NULL
65082fd53b7SMichael Hennerich };
65182fd53b7SMichael Hennerich 
65282fd53b7SMichael Hennerich static const struct attribute_group adp8860_bl_attr_group = {
65382fd53b7SMichael Hennerich 	.attrs = adp8860_bl_attributes,
65482fd53b7SMichael Hennerich };
65582fd53b7SMichael Hennerich 
6561b9e450dSBill Pemberton static int adp8860_probe(struct i2c_client *client,
65782fd53b7SMichael Hennerich 					const struct i2c_device_id *id)
65882fd53b7SMichael Hennerich {
65982fd53b7SMichael Hennerich 	struct backlight_device *bl;
66082fd53b7SMichael Hennerich 	struct adp8860_bl *data;
66182fd53b7SMichael Hennerich 	struct adp8860_backlight_platform_data *pdata =
662c512794cSJingoo Han 		dev_get_platdata(&client->dev);
6633f43f8b2SRandy Dunlap 	struct backlight_properties props;
66482fd53b7SMichael Hennerich 	uint8_t reg_val;
66582fd53b7SMichael Hennerich 	int ret;
66682fd53b7SMichael Hennerich 
66782fd53b7SMichael Hennerich 	if (!i2c_check_functionality(client->adapter,
66882fd53b7SMichael Hennerich 					I2C_FUNC_SMBUS_BYTE_DATA)) {
66982fd53b7SMichael Hennerich 		dev_err(&client->dev, "SMBUS Byte Data not Supported\n");
67082fd53b7SMichael Hennerich 		return -EIO;
67182fd53b7SMichael Hennerich 	}
67282fd53b7SMichael Hennerich 
67382fd53b7SMichael Hennerich 	if (!pdata) {
67482fd53b7SMichael Hennerich 		dev_err(&client->dev, "no platform data?\n");
67582fd53b7SMichael Hennerich 		return -EINVAL;
67682fd53b7SMichael Hennerich 	}
67782fd53b7SMichael Hennerich 
67858875ea9SJingoo Han 	data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
67982fd53b7SMichael Hennerich 	if (data == NULL)
68082fd53b7SMichael Hennerich 		return -ENOMEM;
68182fd53b7SMichael Hennerich 
682c7c06d8aSMichael Hennerich 	ret = adp8860_read(client, ADP8860_MFDVID, &reg_val);
683c7c06d8aSMichael Hennerich 	if (ret < 0)
68458875ea9SJingoo Han 		return ret;
685c7c06d8aSMichael Hennerich 
686c7c06d8aSMichael Hennerich 	switch (ADP8860_MANID(reg_val)) {
687c7c06d8aSMichael Hennerich 	case ADP8863_MANUFID:
688c7c06d8aSMichael Hennerich 		data->gdwn_dis = !!pdata->gdwn_dis;
689c7c06d8aSMichael Hennerich 	case ADP8860_MANUFID:
690c7c06d8aSMichael Hennerich 		data->en_ambl_sens = !!pdata->en_ambl_sens;
691c7c06d8aSMichael Hennerich 		break;
692c7c06d8aSMichael Hennerich 	case ADP8861_MANUFID:
693c7c06d8aSMichael Hennerich 		data->gdwn_dis = !!pdata->gdwn_dis;
694c7c06d8aSMichael Hennerich 		break;
695c7c06d8aSMichael Hennerich 	default:
696c7c06d8aSMichael Hennerich 		dev_err(&client->dev, "failed to probe\n");
69758875ea9SJingoo Han 		return -ENODEV;
698c7c06d8aSMichael Hennerich 	}
699c7c06d8aSMichael Hennerich 
70082fd53b7SMichael Hennerich 	/* It's confirmed that the DEVID field is actually a REVID */
70182fd53b7SMichael Hennerich 
70282fd53b7SMichael Hennerich 	data->revid = ADP8860_DEVID(reg_val);
70382fd53b7SMichael Hennerich 	data->client = client;
70482fd53b7SMichael Hennerich 	data->pdata = pdata;
70582fd53b7SMichael Hennerich 	data->id = id->driver_data;
70682fd53b7SMichael Hennerich 	data->current_brightness = 0;
70782fd53b7SMichael Hennerich 	i2c_set_clientdata(client, data);
70882fd53b7SMichael Hennerich 
7093f43f8b2SRandy Dunlap 	memset(&props, 0, sizeof(props));
710bb7ca747SMatthew Garrett 	props.type = BACKLIGHT_RAW;
7113f43f8b2SRandy Dunlap 	props.max_brightness = ADP8860_MAX_BRIGHTNESS;
7123f43f8b2SRandy Dunlap 
71382fd53b7SMichael Hennerich 	mutex_init(&data->lock);
71482fd53b7SMichael Hennerich 
71591fa4ee8SJingoo Han 	bl = devm_backlight_device_register(&client->dev,
71691fa4ee8SJingoo Han 				dev_driver_string(&client->dev),
7173f43f8b2SRandy Dunlap 				&client->dev, data, &adp8860_bl_ops, &props);
71882fd53b7SMichael Hennerich 	if (IS_ERR(bl)) {
71982fd53b7SMichael Hennerich 		dev_err(&client->dev, "failed to register backlight\n");
72058875ea9SJingoo Han 		return PTR_ERR(bl);
72182fd53b7SMichael Hennerich 	}
72282fd53b7SMichael Hennerich 
72382fd53b7SMichael Hennerich 	bl->props.brightness = ADP8860_MAX_BRIGHTNESS;
72482fd53b7SMichael Hennerich 
72582fd53b7SMichael Hennerich 	data->bl = bl;
72682fd53b7SMichael Hennerich 
727c7c06d8aSMichael Hennerich 	if (data->en_ambl_sens)
72882fd53b7SMichael Hennerich 		ret = sysfs_create_group(&bl->dev.kobj,
72982fd53b7SMichael Hennerich 			&adp8860_bl_attr_group);
73082fd53b7SMichael Hennerich 
73182fd53b7SMichael Hennerich 	if (ret) {
73282fd53b7SMichael Hennerich 		dev_err(&client->dev, "failed to register sysfs\n");
73391fa4ee8SJingoo Han 		return ret;
73482fd53b7SMichael Hennerich 	}
73582fd53b7SMichael Hennerich 
73682fd53b7SMichael Hennerich 	ret = adp8860_bl_setup(bl);
73782fd53b7SMichael Hennerich 	if (ret) {
73882fd53b7SMichael Hennerich 		ret = -EIO;
73982fd53b7SMichael Hennerich 		goto out;
74082fd53b7SMichael Hennerich 	}
74182fd53b7SMichael Hennerich 
74282fd53b7SMichael Hennerich 	backlight_update_status(bl);
74382fd53b7SMichael Hennerich 
744c7c06d8aSMichael Hennerich 	dev_info(&client->dev, "%s Rev.%d Backlight\n",
745c7c06d8aSMichael Hennerich 		client->name, data->revid);
74682fd53b7SMichael Hennerich 
74782fd53b7SMichael Hennerich 	if (pdata->num_leds)
74882fd53b7SMichael Hennerich 		adp8860_led_probe(client);
74982fd53b7SMichael Hennerich 
75082fd53b7SMichael Hennerich 	return 0;
75182fd53b7SMichael Hennerich 
75282fd53b7SMichael Hennerich out:
753c7c06d8aSMichael Hennerich 	if (data->en_ambl_sens)
75482fd53b7SMichael Hennerich 		sysfs_remove_group(&data->bl->dev.kobj,
75582fd53b7SMichael Hennerich 			&adp8860_bl_attr_group);
75682fd53b7SMichael Hennerich 
75782fd53b7SMichael Hennerich 	return ret;
75882fd53b7SMichael Hennerich }
75982fd53b7SMichael Hennerich 
7607e4b9d0bSBill Pemberton static int adp8860_remove(struct i2c_client *client)
76182fd53b7SMichael Hennerich {
76282fd53b7SMichael Hennerich 	struct adp8860_bl *data = i2c_get_clientdata(client);
76382fd53b7SMichael Hennerich 
76482fd53b7SMichael Hennerich 	adp8860_clr_bits(client, ADP8860_MDCR, NSTBY);
76582fd53b7SMichael Hennerich 
76682fd53b7SMichael Hennerich 	if (data->led)
76782fd53b7SMichael Hennerich 		adp8860_led_remove(client);
76882fd53b7SMichael Hennerich 
769c7c06d8aSMichael Hennerich 	if (data->en_ambl_sens)
77082fd53b7SMichael Hennerich 		sysfs_remove_group(&data->bl->dev.kobj,
77182fd53b7SMichael Hennerich 			&adp8860_bl_attr_group);
77282fd53b7SMichael Hennerich 
77382fd53b7SMichael Hennerich 	return 0;
77482fd53b7SMichael Hennerich }
77582fd53b7SMichael Hennerich 
776cf108b5cSJingoo Han #ifdef CONFIG_PM_SLEEP
777cf108b5cSJingoo Han static int adp8860_i2c_suspend(struct device *dev)
77882fd53b7SMichael Hennerich {
779cf108b5cSJingoo Han 	struct i2c_client *client = to_i2c_client(dev);
780cf108b5cSJingoo Han 
78182fd53b7SMichael Hennerich 	adp8860_clr_bits(client, ADP8860_MDCR, NSTBY);
78282fd53b7SMichael Hennerich 
78382fd53b7SMichael Hennerich 	return 0;
78482fd53b7SMichael Hennerich }
78582fd53b7SMichael Hennerich 
786cf108b5cSJingoo Han static int adp8860_i2c_resume(struct device *dev)
78782fd53b7SMichael Hennerich {
788cf108b5cSJingoo Han 	struct i2c_client *client = to_i2c_client(dev);
789cf108b5cSJingoo 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 
796cf108b5cSJingoo Han static SIMPLE_DEV_PM_OPS(adp8860_i2c_pm_ops, adp8860_i2c_suspend,
797cf108b5cSJingoo Han 			adp8860_i2c_resume);
798cf108b5cSJingoo 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,
810cf108b5cSJingoo 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