xref: /linux/drivers/video/backlight/adp8860_bl.c (revision df561f6688fef775baa341a0f5d960becd248b11)
180503b23SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
282fd53b7SMichael Hennerich /*
382fd53b7SMichael Hennerich  * Backlight driver for Analog Devices ADP8860 Backlight Devices
482fd53b7SMichael Hennerich  *
582fd53b7SMichael Hennerich  * Copyright 2009-2010 Analog Devices Inc.
682fd53b7SMichael Hennerich  */
782fd53b7SMichael Hennerich 
882fd53b7SMichael Hennerich #include <linux/module.h>
982fd53b7SMichael Hennerich #include <linux/init.h>
1082fd53b7SMichael Hennerich #include <linux/errno.h>
1182fd53b7SMichael Hennerich #include <linux/pm.h>
1282fd53b7SMichael Hennerich #include <linux/platform_device.h>
1382fd53b7SMichael Hennerich #include <linux/i2c.h>
1482fd53b7SMichael Hennerich #include <linux/fb.h>
1582fd53b7SMichael Hennerich #include <linux/backlight.h>
1682fd53b7SMichael Hennerich #include <linux/leds.h>
173f43f8b2SRandy Dunlap #include <linux/slab.h>
1882fd53b7SMichael Hennerich #include <linux/workqueue.h>
1982fd53b7SMichael Hennerich 
208476d6cdSWolfram Sang #include <linux/platform_data/adp8860.h>
2182fd53b7SMichael Hennerich #define ADP8860_EXT_FEATURES
2282fd53b7SMichael Hennerich #define ADP8860_USE_LEDS
2382fd53b7SMichael Hennerich 
2482fd53b7SMichael Hennerich #define ADP8860_MFDVID 0x00 /* Manufacturer and device ID */
2582fd53b7SMichael Hennerich #define ADP8860_MDCR 0x01 /* Device mode and status */
2682fd53b7SMichael Hennerich #define ADP8860_MDCR2 0x02 /* Device mode and Status Register 2 */
2782fd53b7SMichael Hennerich #define ADP8860_INTR_EN 0x03 /* Interrupts enable */
2882fd53b7SMichael Hennerich #define ADP8860_CFGR 0x04 /* Configuration register */
2982fd53b7SMichael Hennerich #define ADP8860_BLSEN 0x05 /* Sink enable backlight or independent */
3082fd53b7SMichael Hennerich #define ADP8860_BLOFF 0x06 /* Backlight off timeout */
3182fd53b7SMichael Hennerich #define ADP8860_BLDIM 0x07 /* Backlight dim timeout */
3282fd53b7SMichael Hennerich #define ADP8860_BLFR 0x08 /* Backlight fade in and out rates */
3382fd53b7SMichael Hennerich #define ADP8860_BLMX1 0x09 /* Backlight (Brightness Level 1-daylight) maximum current */
3482fd53b7SMichael Hennerich #define ADP8860_BLDM1 0x0A /* Backlight (Brightness Level 1-daylight) dim current */
3582fd53b7SMichael Hennerich #define ADP8860_BLMX2 0x0B /* Backlight (Brightness Level 2-office) maximum current */
3682fd53b7SMichael Hennerich #define ADP8860_BLDM2 0x0C /* Backlight (Brightness Level 2-office) dim current */
3782fd53b7SMichael Hennerich #define ADP8860_BLMX3 0x0D /* Backlight (Brightness Level 3-dark) maximum current */
3882fd53b7SMichael Hennerich #define ADP8860_BLDM3 0x0E /* Backlight (Brightness Level 3-dark) dim current */
3982fd53b7SMichael Hennerich #define ADP8860_ISCFR 0x0F /* Independent sink current fade control register */
4082fd53b7SMichael Hennerich #define ADP8860_ISCC 0x10 /* Independent sink current control register */
4182fd53b7SMichael Hennerich #define ADP8860_ISCT1 0x11 /* Independent Sink Current Timer Register LED[7:5] */
4282fd53b7SMichael Hennerich #define ADP8860_ISCT2 0x12 /* Independent Sink Current Timer Register LED[4:1] */
4382fd53b7SMichael Hennerich #define ADP8860_ISCF 0x13 /* Independent sink current fade register */
4482fd53b7SMichael Hennerich #define ADP8860_ISC7 0x14 /* Independent Sink Current LED7 */
4582fd53b7SMichael Hennerich #define ADP8860_ISC6 0x15 /* Independent Sink Current LED6 */
4682fd53b7SMichael Hennerich #define ADP8860_ISC5 0x16 /* Independent Sink Current LED5 */
4782fd53b7SMichael Hennerich #define ADP8860_ISC4 0x17 /* Independent Sink Current LED4 */
4882fd53b7SMichael Hennerich #define ADP8860_ISC3 0x18 /* Independent Sink Current LED3 */
4982fd53b7SMichael Hennerich #define ADP8860_ISC2 0x19 /* Independent Sink Current LED2 */
5082fd53b7SMichael Hennerich #define ADP8860_ISC1 0x1A /* Independent Sink Current LED1 */
5182fd53b7SMichael Hennerich #define ADP8860_CCFG 0x1B /* Comparator configuration */
5282fd53b7SMichael Hennerich #define ADP8860_CCFG2 0x1C /* Second comparator configuration */
5382fd53b7SMichael Hennerich #define ADP8860_L2_TRP 0x1D /* L2 comparator reference */
5482fd53b7SMichael Hennerich #define ADP8860_L2_HYS 0x1E /* L2 hysteresis */
5582fd53b7SMichael Hennerich #define ADP8860_L3_TRP 0x1F /* L3 comparator reference */
5682fd53b7SMichael Hennerich #define ADP8860_L3_HYS 0x20 /* L3 hysteresis */
5782fd53b7SMichael Hennerich #define ADP8860_PH1LEVL 0x21 /* First phototransistor ambient light level-low byte register */
5882fd53b7SMichael Hennerich #define ADP8860_PH1LEVH 0x22 /* First phototransistor ambient light level-high byte register */
5982fd53b7SMichael Hennerich #define ADP8860_PH2LEVL 0x23 /* Second phototransistor ambient light level-low byte register */
6082fd53b7SMichael Hennerich #define ADP8860_PH2LEVH 0x24 /* Second phototransistor ambient light level-high byte register */
6182fd53b7SMichael Hennerich 
6282fd53b7SMichael Hennerich #define ADP8860_MANUFID		0x0  /* Analog Devices ADP8860 Manufacturer ID */
63c7c06d8aSMichael Hennerich #define ADP8861_MANUFID		0x4  /* Analog Devices ADP8861 Manufacturer ID */
64c7c06d8aSMichael Hennerich #define ADP8863_MANUFID		0x2  /* Analog Devices ADP8863 Manufacturer ID */
65c7c06d8aSMichael Hennerich 
6682fd53b7SMichael Hennerich #define ADP8860_DEVID(x)	((x) & 0xF)
6782fd53b7SMichael Hennerich #define ADP8860_MANID(x)	((x) >> 4)
6882fd53b7SMichael Hennerich 
6982fd53b7SMichael Hennerich /* MDCR Device mode and status */
7082fd53b7SMichael Hennerich #define INT_CFG			(1 << 6)
7182fd53b7SMichael Hennerich #define NSTBY			(1 << 5)
7282fd53b7SMichael Hennerich #define DIM_EN			(1 << 4)
73c7c06d8aSMichael Hennerich #define GDWN_DIS		(1 << 3)
7482fd53b7SMichael Hennerich #define SIS_EN			(1 << 2)
7582fd53b7SMichael Hennerich #define CMP_AUTOEN		(1 << 1)
7682fd53b7SMichael Hennerich #define BLEN			(1 << 0)
7782fd53b7SMichael Hennerich 
7882fd53b7SMichael Hennerich /* ADP8860_CCFG Main ALS comparator level enable */
7982fd53b7SMichael Hennerich #define L3_EN			(1 << 1)
8082fd53b7SMichael Hennerich #define L2_EN			(1 << 0)
8182fd53b7SMichael Hennerich 
8282fd53b7SMichael Hennerich #define CFGR_BLV_SHIFT		3
8382fd53b7SMichael Hennerich #define CFGR_BLV_MASK		0x3
8482fd53b7SMichael Hennerich #define ADP8860_FLAG_LED_MASK	0xFF
8582fd53b7SMichael Hennerich 
8682fd53b7SMichael Hennerich #define FADE_VAL(in, out)	((0xF & (in)) | ((0xF & (out)) << 4))
8782fd53b7SMichael Hennerich #define BL_CFGR_VAL(law, blv)	((((blv) & CFGR_BLV_MASK) << CFGR_BLV_SHIFT) | ((0x3 & (law)) << 1))
8882fd53b7SMichael Hennerich #define ALS_CCFG_VAL(filt)	((0x7 & filt) << 5)
8982fd53b7SMichael Hennerich 
90c7c06d8aSMichael Hennerich enum {
91c7c06d8aSMichael Hennerich 	adp8860,
92c7c06d8aSMichael Hennerich 	adp8861,
93c7c06d8aSMichael Hennerich 	adp8863
94c7c06d8aSMichael Hennerich };
95c7c06d8aSMichael Hennerich 
9682fd53b7SMichael Hennerich struct adp8860_led {
9782fd53b7SMichael Hennerich 	struct led_classdev	cdev;
9882fd53b7SMichael Hennerich 	struct work_struct	work;
9982fd53b7SMichael Hennerich 	struct i2c_client	*client;
10082fd53b7SMichael Hennerich 	enum led_brightness	new_brightness;
10182fd53b7SMichael Hennerich 	int			id;
10282fd53b7SMichael Hennerich 	int			flags;
10382fd53b7SMichael Hennerich };
10482fd53b7SMichael Hennerich 
10582fd53b7SMichael Hennerich struct adp8860_bl {
10682fd53b7SMichael Hennerich 	struct i2c_client *client;
10782fd53b7SMichael Hennerich 	struct backlight_device *bl;
10882fd53b7SMichael Hennerich 	struct adp8860_led *led;
10982fd53b7SMichael Hennerich 	struct adp8860_backlight_platform_data *pdata;
11082fd53b7SMichael Hennerich 	struct mutex lock;
11182fd53b7SMichael Hennerich 	unsigned long cached_daylight_max;
11282fd53b7SMichael Hennerich 	int id;
11382fd53b7SMichael Hennerich 	int revid;
11482fd53b7SMichael Hennerich 	int current_brightness;
115c7c06d8aSMichael Hennerich 	unsigned en_ambl_sens:1;
116c7c06d8aSMichael Hennerich 	unsigned gdwn_dis:1;
11782fd53b7SMichael Hennerich };
11882fd53b7SMichael Hennerich 
11982fd53b7SMichael Hennerich static int adp8860_read(struct i2c_client *client, int reg, uint8_t *val)
12082fd53b7SMichael Hennerich {
12182fd53b7SMichael Hennerich 	int ret;
12282fd53b7SMichael Hennerich 
12382fd53b7SMichael Hennerich 	ret = i2c_smbus_read_byte_data(client, reg);
12482fd53b7SMichael Hennerich 	if (ret < 0) {
12582fd53b7SMichael Hennerich 		dev_err(&client->dev, "failed reading at 0x%02x\n", reg);
12682fd53b7SMichael Hennerich 		return ret;
12782fd53b7SMichael Hennerich 	}
12882fd53b7SMichael Hennerich 
12982fd53b7SMichael Hennerich 	*val = (uint8_t)ret;
13082fd53b7SMichael Hennerich 	return 0;
13182fd53b7SMichael Hennerich }
13282fd53b7SMichael Hennerich 
13382fd53b7SMichael Hennerich static int adp8860_write(struct i2c_client *client, u8 reg, u8 val)
13482fd53b7SMichael Hennerich {
13582fd53b7SMichael Hennerich 	return i2c_smbus_write_byte_data(client, reg, val);
13682fd53b7SMichael Hennerich }
13782fd53b7SMichael Hennerich 
13882fd53b7SMichael Hennerich static int adp8860_set_bits(struct i2c_client *client, int reg, uint8_t bit_mask)
13982fd53b7SMichael Hennerich {
14082fd53b7SMichael Hennerich 	struct adp8860_bl *data = i2c_get_clientdata(client);
14182fd53b7SMichael Hennerich 	uint8_t reg_val;
14282fd53b7SMichael Hennerich 	int ret;
14382fd53b7SMichael Hennerich 
14482fd53b7SMichael Hennerich 	mutex_lock(&data->lock);
14582fd53b7SMichael Hennerich 
14682fd53b7SMichael Hennerich 	ret = adp8860_read(client, reg, &reg_val);
14782fd53b7SMichael Hennerich 
14836c3e759SAxel Lin 	if (!ret && ((reg_val & bit_mask) != bit_mask)) {
14982fd53b7SMichael Hennerich 		reg_val |= bit_mask;
15082fd53b7SMichael Hennerich 		ret = adp8860_write(client, reg, reg_val);
15182fd53b7SMichael Hennerich 	}
15282fd53b7SMichael Hennerich 
15382fd53b7SMichael Hennerich 	mutex_unlock(&data->lock);
15482fd53b7SMichael Hennerich 	return ret;
15582fd53b7SMichael Hennerich }
15682fd53b7SMichael Hennerich 
15782fd53b7SMichael Hennerich static int adp8860_clr_bits(struct i2c_client *client, int reg, uint8_t bit_mask)
15882fd53b7SMichael Hennerich {
15982fd53b7SMichael Hennerich 	struct adp8860_bl *data = i2c_get_clientdata(client);
16082fd53b7SMichael Hennerich 	uint8_t reg_val;
16182fd53b7SMichael Hennerich 	int ret;
16282fd53b7SMichael Hennerich 
16382fd53b7SMichael Hennerich 	mutex_lock(&data->lock);
16482fd53b7SMichael Hennerich 
16582fd53b7SMichael Hennerich 	ret = adp8860_read(client, reg, &reg_val);
16682fd53b7SMichael Hennerich 
16782fd53b7SMichael Hennerich 	if (!ret && (reg_val & bit_mask)) {
16882fd53b7SMichael Hennerich 		reg_val &= ~bit_mask;
16982fd53b7SMichael Hennerich 		ret = adp8860_write(client, reg, reg_val);
17082fd53b7SMichael Hennerich 	}
17182fd53b7SMichael Hennerich 
17282fd53b7SMichael Hennerich 	mutex_unlock(&data->lock);
17382fd53b7SMichael Hennerich 	return ret;
17482fd53b7SMichael Hennerich }
17582fd53b7SMichael Hennerich 
17682fd53b7SMichael Hennerich /*
17782fd53b7SMichael Hennerich  * Independent sink / LED
17882fd53b7SMichael Hennerich  */
17982fd53b7SMichael Hennerich #if defined(ADP8860_USE_LEDS)
18082fd53b7SMichael Hennerich static void adp8860_led_work(struct work_struct *work)
18182fd53b7SMichael Hennerich {
18282fd53b7SMichael Hennerich 	struct adp8860_led *led = container_of(work, struct adp8860_led, work);
1835e548f0fSJingoo Han 
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 =
219c512794cSJingoo Han 		dev_get_platdata(&client->dev);
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 
225a86854d0SKees Cook 	led = devm_kcalloc(&client->dev, pdata->num_leds, sizeof(*led),
22658875ea9SJingoo Han 				GFP_KERNEL);
227ba464d0cSJingoo Han 	if (led == NULL)
22882fd53b7SMichael Hennerich 		return -ENOMEM;
22982fd53b7SMichael Hennerich 
23082fd53b7SMichael Hennerich 	ret = adp8860_write(client, ADP8860_ISCFR, pdata->led_fade_law);
23182fd53b7SMichael Hennerich 	ret = adp8860_write(client, ADP8860_ISCT1,
23282fd53b7SMichael Hennerich 			(pdata->led_on_time & 0x3) << 6);
23382fd53b7SMichael Hennerich 	ret |= adp8860_write(client, ADP8860_ISCF,
23482fd53b7SMichael Hennerich 			FADE_VAL(pdata->led_fade_in, pdata->led_fade_out));
23582fd53b7SMichael Hennerich 
23682fd53b7SMichael Hennerich 	if (ret) {
23782fd53b7SMichael Hennerich 		dev_err(&client->dev, "failed to write\n");
23858875ea9SJingoo Han 		return ret;
23982fd53b7SMichael Hennerich 	}
24082fd53b7SMichael Hennerich 
24182fd53b7SMichael Hennerich 	for (i = 0; i < pdata->num_leds; ++i) {
24282fd53b7SMichael Hennerich 		cur_led = &pdata->leds[i];
24382fd53b7SMichael Hennerich 		led_dat = &led[i];
24482fd53b7SMichael Hennerich 
24582fd53b7SMichael Hennerich 		led_dat->id = cur_led->flags & ADP8860_FLAG_LED_MASK;
24682fd53b7SMichael Hennerich 
24782fd53b7SMichael Hennerich 		if (led_dat->id > 7 || led_dat->id < 1) {
24882fd53b7SMichael Hennerich 			dev_err(&client->dev, "Invalid LED ID %d\n",
24982fd53b7SMichael Hennerich 				led_dat->id);
25084eba9edSWei Yongjun 			ret = -EINVAL;
25182fd53b7SMichael Hennerich 			goto err;
25282fd53b7SMichael Hennerich 		}
25382fd53b7SMichael Hennerich 
25482fd53b7SMichael Hennerich 		if (pdata->bl_led_assign & (1 << (led_dat->id - 1))) {
25582fd53b7SMichael Hennerich 			dev_err(&client->dev, "LED %d used by Backlight\n",
25682fd53b7SMichael Hennerich 				led_dat->id);
25784eba9edSWei Yongjun 			ret = -EBUSY;
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 =
301c512794cSJingoo Han 		dev_get_platdata(&client->dev);
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 {
36451d53e5bSSam Ravnborg 	return adp8860_bl_set(bl, backlight_get_brightness(bl));
36582fd53b7SMichael Hennerich }
36682fd53b7SMichael Hennerich 
36782fd53b7SMichael Hennerich static int adp8860_bl_get_brightness(struct backlight_device *bl)
36882fd53b7SMichael Hennerich {
36982fd53b7SMichael Hennerich 	struct adp8860_bl *data = bl_get_data(bl);
37082fd53b7SMichael Hennerich 
37182fd53b7SMichael Hennerich 	return data->current_brightness;
37282fd53b7SMichael Hennerich }
37382fd53b7SMichael Hennerich 
37482fd53b7SMichael Hennerich static const struct backlight_ops adp8860_bl_ops = {
37582fd53b7SMichael Hennerich 	.update_status	= adp8860_bl_update_status,
37682fd53b7SMichael Hennerich 	.get_brightness	= adp8860_bl_get_brightness,
37782fd53b7SMichael Hennerich };
37882fd53b7SMichael Hennerich 
37982fd53b7SMichael Hennerich static int adp8860_bl_setup(struct backlight_device *bl)
38082fd53b7SMichael Hennerich {
38182fd53b7SMichael Hennerich 	struct adp8860_bl *data = bl_get_data(bl);
38282fd53b7SMichael Hennerich 	struct i2c_client *client = data->client;
38382fd53b7SMichael Hennerich 	struct adp8860_backlight_platform_data *pdata = data->pdata;
38482fd53b7SMichael Hennerich 	int ret = 0;
38582fd53b7SMichael Hennerich 
38682fd53b7SMichael Hennerich 	ret |= adp8860_write(client, ADP8860_BLSEN, ~pdata->bl_led_assign);
38782fd53b7SMichael Hennerich 	ret |= adp8860_write(client, ADP8860_BLMX1, pdata->l1_daylight_max);
38882fd53b7SMichael Hennerich 	ret |= adp8860_write(client, ADP8860_BLDM1, pdata->l1_daylight_dim);
38982fd53b7SMichael Hennerich 
390c7c06d8aSMichael Hennerich 	if (data->en_ambl_sens) {
39182fd53b7SMichael Hennerich 		data->cached_daylight_max = pdata->l1_daylight_max;
39282fd53b7SMichael Hennerich 		ret |= adp8860_write(client, ADP8860_BLMX2,
39382fd53b7SMichael Hennerich 						pdata->l2_office_max);
39482fd53b7SMichael Hennerich 		ret |= adp8860_write(client, ADP8860_BLDM2,
39582fd53b7SMichael Hennerich 						pdata->l2_office_dim);
39682fd53b7SMichael Hennerich 		ret |= adp8860_write(client, ADP8860_BLMX3,
39782fd53b7SMichael Hennerich 						pdata->l3_dark_max);
39882fd53b7SMichael Hennerich 		ret |= adp8860_write(client, ADP8860_BLDM3,
39982fd53b7SMichael Hennerich 						pdata->l3_dark_dim);
40082fd53b7SMichael Hennerich 
40182fd53b7SMichael Hennerich 		ret |= adp8860_write(client, ADP8860_L2_TRP, pdata->l2_trip);
40282fd53b7SMichael Hennerich 		ret |= adp8860_write(client, ADP8860_L2_HYS, pdata->l2_hyst);
40382fd53b7SMichael Hennerich 		ret |= adp8860_write(client, ADP8860_L3_TRP, pdata->l3_trip);
40482fd53b7SMichael Hennerich 		ret |= adp8860_write(client, ADP8860_L3_HYS, pdata->l3_hyst);
40582fd53b7SMichael Hennerich 		ret |= adp8860_write(client, ADP8860_CCFG, L2_EN | L3_EN |
40682fd53b7SMichael Hennerich 						ALS_CCFG_VAL(pdata->abml_filt));
40782fd53b7SMichael Hennerich 	}
40882fd53b7SMichael Hennerich 
40982fd53b7SMichael Hennerich 	ret |= adp8860_write(client, ADP8860_CFGR,
41082fd53b7SMichael Hennerich 			BL_CFGR_VAL(pdata->bl_fade_law, 0));
41182fd53b7SMichael Hennerich 
41282fd53b7SMichael Hennerich 	ret |= adp8860_write(client, ADP8860_BLFR, FADE_VAL(pdata->bl_fade_in,
41382fd53b7SMichael Hennerich 			pdata->bl_fade_out));
41482fd53b7SMichael Hennerich 
415c7c06d8aSMichael Hennerich 	ret |= adp8860_set_bits(client, ADP8860_MDCR, BLEN | DIM_EN | NSTBY |
416c7c06d8aSMichael Hennerich 			(data->gdwn_dis ? GDWN_DIS : 0));
41782fd53b7SMichael Hennerich 
41882fd53b7SMichael Hennerich 	return ret;
41982fd53b7SMichael Hennerich }
42082fd53b7SMichael Hennerich 
42182fd53b7SMichael Hennerich static ssize_t adp8860_show(struct device *dev, char *buf, int reg)
42282fd53b7SMichael Hennerich {
42382fd53b7SMichael Hennerich 	struct adp8860_bl *data = dev_get_drvdata(dev);
42482fd53b7SMichael Hennerich 	int error;
42582fd53b7SMichael Hennerich 	uint8_t reg_val;
42682fd53b7SMichael Hennerich 
42782fd53b7SMichael Hennerich 	mutex_lock(&data->lock);
42882fd53b7SMichael Hennerich 	error = adp8860_read(data->client, reg, &reg_val);
42982fd53b7SMichael Hennerich 	mutex_unlock(&data->lock);
43082fd53b7SMichael Hennerich 
43182fd53b7SMichael Hennerich 	if (error < 0)
43282fd53b7SMichael Hennerich 		return error;
43382fd53b7SMichael Hennerich 
43482fd53b7SMichael Hennerich 	return sprintf(buf, "%u\n", reg_val);
43582fd53b7SMichael Hennerich }
43682fd53b7SMichael Hennerich 
43782fd53b7SMichael Hennerich static ssize_t adp8860_store(struct device *dev, const char *buf,
43882fd53b7SMichael Hennerich 			 size_t count, int reg)
43982fd53b7SMichael Hennerich {
44082fd53b7SMichael Hennerich 	struct adp8860_bl *data = dev_get_drvdata(dev);
44182fd53b7SMichael Hennerich 	unsigned long val;
44282fd53b7SMichael Hennerich 	int ret;
44382fd53b7SMichael Hennerich 
4448dd9d7f2SJingoo Han 	ret = kstrtoul(buf, 10, &val);
44582fd53b7SMichael Hennerich 	if (ret)
44682fd53b7SMichael Hennerich 		return ret;
44782fd53b7SMichael Hennerich 
44882fd53b7SMichael Hennerich 	mutex_lock(&data->lock);
44982fd53b7SMichael Hennerich 	adp8860_write(data->client, reg, val);
45082fd53b7SMichael Hennerich 	mutex_unlock(&data->lock);
45182fd53b7SMichael Hennerich 
45282fd53b7SMichael Hennerich 	return count;
45382fd53b7SMichael Hennerich }
45482fd53b7SMichael Hennerich 
45582fd53b7SMichael Hennerich static ssize_t adp8860_bl_l3_dark_max_show(struct device *dev,
45682fd53b7SMichael Hennerich 				     struct device_attribute *attr, char *buf)
45782fd53b7SMichael Hennerich {
45882fd53b7SMichael Hennerich 	return adp8860_show(dev, buf, ADP8860_BLMX3);
45982fd53b7SMichael Hennerich }
46082fd53b7SMichael Hennerich 
46182fd53b7SMichael Hennerich static ssize_t adp8860_bl_l3_dark_max_store(struct device *dev,
46282fd53b7SMichael Hennerich 		struct device_attribute *attr, const char *buf, size_t count)
46382fd53b7SMichael Hennerich {
46482fd53b7SMichael Hennerich 	return adp8860_store(dev, buf, count, ADP8860_BLMX3);
46582fd53b7SMichael Hennerich }
46682fd53b7SMichael Hennerich 
46782fd53b7SMichael Hennerich static DEVICE_ATTR(l3_dark_max, 0664, adp8860_bl_l3_dark_max_show,
46882fd53b7SMichael Hennerich 			adp8860_bl_l3_dark_max_store);
46982fd53b7SMichael Hennerich 
47082fd53b7SMichael Hennerich static ssize_t adp8860_bl_l2_office_max_show(struct device *dev,
47182fd53b7SMichael Hennerich 		struct device_attribute *attr, char *buf)
47282fd53b7SMichael Hennerich {
47382fd53b7SMichael Hennerich 	return adp8860_show(dev, buf, ADP8860_BLMX2);
47482fd53b7SMichael Hennerich }
47582fd53b7SMichael Hennerich 
47682fd53b7SMichael Hennerich static ssize_t adp8860_bl_l2_office_max_store(struct device *dev,
47782fd53b7SMichael Hennerich 		struct device_attribute *attr, const char *buf, size_t count)
47882fd53b7SMichael Hennerich {
47982fd53b7SMichael Hennerich 	return adp8860_store(dev, buf, count, ADP8860_BLMX2);
48082fd53b7SMichael Hennerich }
48182fd53b7SMichael Hennerich static DEVICE_ATTR(l2_office_max, 0664, adp8860_bl_l2_office_max_show,
48282fd53b7SMichael Hennerich 			adp8860_bl_l2_office_max_store);
48382fd53b7SMichael Hennerich 
48482fd53b7SMichael Hennerich static ssize_t adp8860_bl_l1_daylight_max_show(struct device *dev,
48582fd53b7SMichael Hennerich 			struct device_attribute *attr, char *buf)
48682fd53b7SMichael Hennerich {
48782fd53b7SMichael Hennerich 	return adp8860_show(dev, buf, ADP8860_BLMX1);
48882fd53b7SMichael Hennerich }
48982fd53b7SMichael Hennerich 
49082fd53b7SMichael Hennerich static ssize_t adp8860_bl_l1_daylight_max_store(struct device *dev,
49182fd53b7SMichael Hennerich 		struct device_attribute *attr, const char *buf, size_t count)
49282fd53b7SMichael Hennerich {
49382fd53b7SMichael Hennerich 	struct adp8860_bl *data = dev_get_drvdata(dev);
4948dd9d7f2SJingoo Han 	int ret = kstrtoul(buf, 10, &data->cached_daylight_max);
4955e548f0fSJingoo Han 
4964f1aa846SMichael Hennerich 	if (ret)
4974f1aa846SMichael Hennerich 		return ret;
49882fd53b7SMichael Hennerich 
49982fd53b7SMichael Hennerich 	return adp8860_store(dev, buf, count, ADP8860_BLMX1);
50082fd53b7SMichael Hennerich }
50182fd53b7SMichael Hennerich static DEVICE_ATTR(l1_daylight_max, 0664, adp8860_bl_l1_daylight_max_show,
50282fd53b7SMichael Hennerich 			adp8860_bl_l1_daylight_max_store);
50382fd53b7SMichael Hennerich 
50482fd53b7SMichael Hennerich static ssize_t adp8860_bl_l3_dark_dim_show(struct device *dev,
50582fd53b7SMichael Hennerich 			struct device_attribute *attr, char *buf)
50682fd53b7SMichael Hennerich {
50782fd53b7SMichael Hennerich 	return adp8860_show(dev, buf, ADP8860_BLDM3);
50882fd53b7SMichael Hennerich }
50982fd53b7SMichael Hennerich 
51082fd53b7SMichael Hennerich static ssize_t adp8860_bl_l3_dark_dim_store(struct device *dev,
51182fd53b7SMichael Hennerich 				     struct device_attribute *attr,
51282fd53b7SMichael Hennerich 				     const char *buf, size_t count)
51382fd53b7SMichael Hennerich {
51482fd53b7SMichael Hennerich 	return adp8860_store(dev, buf, count, ADP8860_BLDM3);
51582fd53b7SMichael Hennerich }
51682fd53b7SMichael Hennerich static DEVICE_ATTR(l3_dark_dim, 0664, adp8860_bl_l3_dark_dim_show,
51782fd53b7SMichael Hennerich 			adp8860_bl_l3_dark_dim_store);
51882fd53b7SMichael Hennerich 
51982fd53b7SMichael Hennerich static ssize_t adp8860_bl_l2_office_dim_show(struct device *dev,
52082fd53b7SMichael Hennerich 			struct device_attribute *attr, char *buf)
52182fd53b7SMichael Hennerich {
52282fd53b7SMichael Hennerich 	return adp8860_show(dev, buf, ADP8860_BLDM2);
52382fd53b7SMichael Hennerich }
52482fd53b7SMichael Hennerich 
52582fd53b7SMichael Hennerich static ssize_t adp8860_bl_l2_office_dim_store(struct device *dev,
52682fd53b7SMichael Hennerich 				     struct device_attribute *attr,
52782fd53b7SMichael Hennerich 				     const char *buf, size_t count)
52882fd53b7SMichael Hennerich {
52982fd53b7SMichael Hennerich 	return adp8860_store(dev, buf, count, ADP8860_BLDM2);
53082fd53b7SMichael Hennerich }
53182fd53b7SMichael Hennerich static DEVICE_ATTR(l2_office_dim, 0664, adp8860_bl_l2_office_dim_show,
53282fd53b7SMichael Hennerich 			adp8860_bl_l2_office_dim_store);
53382fd53b7SMichael Hennerich 
53482fd53b7SMichael Hennerich static ssize_t adp8860_bl_l1_daylight_dim_show(struct device *dev,
53582fd53b7SMichael Hennerich 				     struct device_attribute *attr, char *buf)
53682fd53b7SMichael Hennerich {
53782fd53b7SMichael Hennerich 	return adp8860_show(dev, buf, ADP8860_BLDM1);
53882fd53b7SMichael Hennerich }
53982fd53b7SMichael Hennerich 
54082fd53b7SMichael Hennerich static ssize_t adp8860_bl_l1_daylight_dim_store(struct device *dev,
54182fd53b7SMichael Hennerich 				     struct device_attribute *attr,
54282fd53b7SMichael Hennerich 				     const char *buf, size_t count)
54382fd53b7SMichael Hennerich {
54482fd53b7SMichael Hennerich 	return adp8860_store(dev, buf, count, ADP8860_BLDM1);
54582fd53b7SMichael Hennerich }
54682fd53b7SMichael Hennerich static DEVICE_ATTR(l1_daylight_dim, 0664, adp8860_bl_l1_daylight_dim_show,
54782fd53b7SMichael Hennerich 			adp8860_bl_l1_daylight_dim_store);
54882fd53b7SMichael Hennerich 
54982fd53b7SMichael Hennerich #ifdef ADP8860_EXT_FEATURES
55082fd53b7SMichael Hennerich static ssize_t adp8860_bl_ambient_light_level_show(struct device *dev,
55182fd53b7SMichael Hennerich 				     struct device_attribute *attr, char *buf)
55282fd53b7SMichael Hennerich {
55382fd53b7SMichael Hennerich 	struct adp8860_bl *data = dev_get_drvdata(dev);
55482fd53b7SMichael Hennerich 	int error;
55582fd53b7SMichael Hennerich 	uint8_t reg_val;
55682fd53b7SMichael Hennerich 	uint16_t ret_val;
55782fd53b7SMichael Hennerich 
55882fd53b7SMichael Hennerich 	mutex_lock(&data->lock);
55982fd53b7SMichael Hennerich 	error = adp8860_read(data->client, ADP8860_PH1LEVL, &reg_val);
56002f22c00SArnd Bergmann 	if (!error) {
56182fd53b7SMichael Hennerich 		ret_val = reg_val;
56202f22c00SArnd Bergmann 		error = adp8860_read(data->client, ADP8860_PH1LEVH, &reg_val);
56302f22c00SArnd Bergmann 	}
56482fd53b7SMichael Hennerich 	mutex_unlock(&data->lock);
56582fd53b7SMichael Hennerich 
56602f22c00SArnd Bergmann 	if (error)
56782fd53b7SMichael Hennerich 		return error;
56882fd53b7SMichael Hennerich 
56982fd53b7SMichael Hennerich 	/* Return 13-bit conversion value for the first light sensor */
57082fd53b7SMichael Hennerich 	ret_val += (reg_val & 0x1F) << 8;
57182fd53b7SMichael Hennerich 
57282fd53b7SMichael Hennerich 	return sprintf(buf, "%u\n", ret_val);
57382fd53b7SMichael Hennerich }
57482fd53b7SMichael Hennerich static DEVICE_ATTR(ambient_light_level, 0444,
57582fd53b7SMichael Hennerich 		adp8860_bl_ambient_light_level_show, NULL);
57682fd53b7SMichael Hennerich 
57782fd53b7SMichael Hennerich static ssize_t adp8860_bl_ambient_light_zone_show(struct device *dev,
57882fd53b7SMichael Hennerich 				     struct device_attribute *attr, char *buf)
57982fd53b7SMichael Hennerich {
58082fd53b7SMichael Hennerich 	struct adp8860_bl *data = dev_get_drvdata(dev);
58182fd53b7SMichael Hennerich 	int error;
58282fd53b7SMichael Hennerich 	uint8_t reg_val;
58382fd53b7SMichael Hennerich 
58482fd53b7SMichael Hennerich 	mutex_lock(&data->lock);
58582fd53b7SMichael Hennerich 	error = adp8860_read(data->client, ADP8860_CFGR, &reg_val);
58682fd53b7SMichael Hennerich 	mutex_unlock(&data->lock);
58782fd53b7SMichael Hennerich 
58882fd53b7SMichael Hennerich 	if (error < 0)
58982fd53b7SMichael Hennerich 		return error;
59082fd53b7SMichael Hennerich 
59182fd53b7SMichael Hennerich 	return sprintf(buf, "%u\n",
59282fd53b7SMichael Hennerich 		((reg_val >> CFGR_BLV_SHIFT) & CFGR_BLV_MASK) + 1);
59382fd53b7SMichael Hennerich }
59482fd53b7SMichael Hennerich 
59582fd53b7SMichael Hennerich static ssize_t adp8860_bl_ambient_light_zone_store(struct device *dev,
59682fd53b7SMichael Hennerich 				     struct device_attribute *attr,
59782fd53b7SMichael Hennerich 				     const char *buf, size_t count)
59882fd53b7SMichael Hennerich {
59982fd53b7SMichael Hennerich 	struct adp8860_bl *data = dev_get_drvdata(dev);
60082fd53b7SMichael Hennerich 	unsigned long val;
60182fd53b7SMichael Hennerich 	uint8_t reg_val;
60282fd53b7SMichael Hennerich 	int ret;
60382fd53b7SMichael Hennerich 
6048dd9d7f2SJingoo Han 	ret = kstrtoul(buf, 10, &val);
60582fd53b7SMichael Hennerich 	if (ret)
60682fd53b7SMichael Hennerich 		return ret;
60782fd53b7SMichael Hennerich 
60882fd53b7SMichael Hennerich 	if (val == 0) {
60982fd53b7SMichael Hennerich 		/* Enable automatic ambient light sensing */
61082fd53b7SMichael Hennerich 		adp8860_set_bits(data->client, ADP8860_MDCR, CMP_AUTOEN);
611c7ce2500SMichael Hennerich 	} else if ((val > 0) && (val <= 3)) {
61282fd53b7SMichael Hennerich 		/* Disable automatic ambient light sensing */
61382fd53b7SMichael Hennerich 		adp8860_clr_bits(data->client, ADP8860_MDCR, CMP_AUTOEN);
61482fd53b7SMichael Hennerich 
61582fd53b7SMichael Hennerich 		/* Set user supplied ambient light zone */
61682fd53b7SMichael Hennerich 		mutex_lock(&data->lock);
61786c68e2fSArnd Bergmann 		ret = adp8860_read(data->client, ADP8860_CFGR, &reg_val);
61886c68e2fSArnd Bergmann 		if (!ret) {
61982fd53b7SMichael Hennerich 			reg_val &= ~(CFGR_BLV_MASK << CFGR_BLV_SHIFT);
620c7ce2500SMichael Hennerich 			reg_val |= (val - 1) << CFGR_BLV_SHIFT;
62182fd53b7SMichael Hennerich 			adp8860_write(data->client, ADP8860_CFGR, reg_val);
62286c68e2fSArnd Bergmann 		}
62382fd53b7SMichael Hennerich 		mutex_unlock(&data->lock);
62482fd53b7SMichael Hennerich 	}
62582fd53b7SMichael Hennerich 
62682fd53b7SMichael Hennerich 	return count;
62782fd53b7SMichael Hennerich }
62882fd53b7SMichael Hennerich static DEVICE_ATTR(ambient_light_zone, 0664,
62982fd53b7SMichael Hennerich 		adp8860_bl_ambient_light_zone_show,
63082fd53b7SMichael Hennerich 		adp8860_bl_ambient_light_zone_store);
63182fd53b7SMichael Hennerich #endif
63282fd53b7SMichael Hennerich 
63382fd53b7SMichael Hennerich static struct attribute *adp8860_bl_attributes[] = {
63482fd53b7SMichael Hennerich 	&dev_attr_l3_dark_max.attr,
63582fd53b7SMichael Hennerich 	&dev_attr_l3_dark_dim.attr,
63682fd53b7SMichael Hennerich 	&dev_attr_l2_office_max.attr,
63782fd53b7SMichael Hennerich 	&dev_attr_l2_office_dim.attr,
63882fd53b7SMichael Hennerich 	&dev_attr_l1_daylight_max.attr,
63982fd53b7SMichael Hennerich 	&dev_attr_l1_daylight_dim.attr,
64082fd53b7SMichael Hennerich #ifdef ADP8860_EXT_FEATURES
64182fd53b7SMichael Hennerich 	&dev_attr_ambient_light_level.attr,
64282fd53b7SMichael Hennerich 	&dev_attr_ambient_light_zone.attr,
64382fd53b7SMichael Hennerich #endif
64482fd53b7SMichael Hennerich 	NULL
64582fd53b7SMichael Hennerich };
64682fd53b7SMichael Hennerich 
64782fd53b7SMichael Hennerich static const struct attribute_group adp8860_bl_attr_group = {
64882fd53b7SMichael Hennerich 	.attrs = adp8860_bl_attributes,
64982fd53b7SMichael Hennerich };
65082fd53b7SMichael Hennerich 
6511b9e450dSBill Pemberton static int adp8860_probe(struct i2c_client *client,
65282fd53b7SMichael Hennerich 					const struct i2c_device_id *id)
65382fd53b7SMichael Hennerich {
65482fd53b7SMichael Hennerich 	struct backlight_device *bl;
65582fd53b7SMichael Hennerich 	struct adp8860_bl *data;
65682fd53b7SMichael Hennerich 	struct adp8860_backlight_platform_data *pdata =
657c512794cSJingoo Han 		dev_get_platdata(&client->dev);
6583f43f8b2SRandy Dunlap 	struct backlight_properties props;
65982fd53b7SMichael Hennerich 	uint8_t reg_val;
66082fd53b7SMichael Hennerich 	int ret;
66182fd53b7SMichael Hennerich 
66282fd53b7SMichael Hennerich 	if (!i2c_check_functionality(client->adapter,
66382fd53b7SMichael Hennerich 					I2C_FUNC_SMBUS_BYTE_DATA)) {
66482fd53b7SMichael Hennerich 		dev_err(&client->dev, "SMBUS Byte Data not Supported\n");
66582fd53b7SMichael Hennerich 		return -EIO;
66682fd53b7SMichael Hennerich 	}
66782fd53b7SMichael Hennerich 
66882fd53b7SMichael Hennerich 	if (!pdata) {
66982fd53b7SMichael Hennerich 		dev_err(&client->dev, "no platform data?\n");
67082fd53b7SMichael Hennerich 		return -EINVAL;
67182fd53b7SMichael Hennerich 	}
67282fd53b7SMichael Hennerich 
67358875ea9SJingoo Han 	data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
67482fd53b7SMichael Hennerich 	if (data == NULL)
67582fd53b7SMichael Hennerich 		return -ENOMEM;
67682fd53b7SMichael Hennerich 
677c7c06d8aSMichael Hennerich 	ret = adp8860_read(client, ADP8860_MFDVID, &reg_val);
678c7c06d8aSMichael Hennerich 	if (ret < 0)
67958875ea9SJingoo Han 		return ret;
680c7c06d8aSMichael Hennerich 
681c7c06d8aSMichael Hennerich 	switch (ADP8860_MANID(reg_val)) {
682c7c06d8aSMichael Hennerich 	case ADP8863_MANUFID:
683c7c06d8aSMichael Hennerich 		data->gdwn_dis = !!pdata->gdwn_dis;
684*df561f66SGustavo A. R. Silva 		fallthrough;
685c7c06d8aSMichael Hennerich 	case ADP8860_MANUFID:
686c7c06d8aSMichael Hennerich 		data->en_ambl_sens = !!pdata->en_ambl_sens;
687c7c06d8aSMichael Hennerich 		break;
688c7c06d8aSMichael Hennerich 	case ADP8861_MANUFID:
689c7c06d8aSMichael Hennerich 		data->gdwn_dis = !!pdata->gdwn_dis;
690c7c06d8aSMichael Hennerich 		break;
691c7c06d8aSMichael Hennerich 	default:
692c7c06d8aSMichael Hennerich 		dev_err(&client->dev, "failed to probe\n");
69358875ea9SJingoo Han 		return -ENODEV;
694c7c06d8aSMichael Hennerich 	}
695c7c06d8aSMichael Hennerich 
69682fd53b7SMichael Hennerich 	/* It's confirmed that the DEVID field is actually a REVID */
69782fd53b7SMichael Hennerich 
69882fd53b7SMichael Hennerich 	data->revid = ADP8860_DEVID(reg_val);
69982fd53b7SMichael Hennerich 	data->client = client;
70082fd53b7SMichael Hennerich 	data->pdata = pdata;
70182fd53b7SMichael Hennerich 	data->id = id->driver_data;
70282fd53b7SMichael Hennerich 	data->current_brightness = 0;
70382fd53b7SMichael Hennerich 	i2c_set_clientdata(client, data);
70482fd53b7SMichael Hennerich 
7053f43f8b2SRandy Dunlap 	memset(&props, 0, sizeof(props));
706bb7ca747SMatthew Garrett 	props.type = BACKLIGHT_RAW;
7073f43f8b2SRandy Dunlap 	props.max_brightness = ADP8860_MAX_BRIGHTNESS;
7083f43f8b2SRandy Dunlap 
70982fd53b7SMichael Hennerich 	mutex_init(&data->lock);
71082fd53b7SMichael Hennerich 
71191fa4ee8SJingoo Han 	bl = devm_backlight_device_register(&client->dev,
71291fa4ee8SJingoo Han 				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");
72991fa4ee8SJingoo Han 		return ret;
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 
75382fd53b7SMichael Hennerich 	return ret;
75482fd53b7SMichael Hennerich }
75582fd53b7SMichael Hennerich 
7567e4b9d0bSBill Pemberton static int adp8860_remove(struct i2c_client *client)
75782fd53b7SMichael Hennerich {
75882fd53b7SMichael Hennerich 	struct adp8860_bl *data = i2c_get_clientdata(client);
75982fd53b7SMichael Hennerich 
76082fd53b7SMichael Hennerich 	adp8860_clr_bits(client, ADP8860_MDCR, NSTBY);
76182fd53b7SMichael Hennerich 
76282fd53b7SMichael Hennerich 	if (data->led)
76382fd53b7SMichael Hennerich 		adp8860_led_remove(client);
76482fd53b7SMichael Hennerich 
765c7c06d8aSMichael Hennerich 	if (data->en_ambl_sens)
76682fd53b7SMichael Hennerich 		sysfs_remove_group(&data->bl->dev.kobj,
76782fd53b7SMichael Hennerich 			&adp8860_bl_attr_group);
76882fd53b7SMichael Hennerich 
76982fd53b7SMichael Hennerich 	return 0;
77082fd53b7SMichael Hennerich }
77182fd53b7SMichael Hennerich 
772cf108b5cSJingoo Han #ifdef CONFIG_PM_SLEEP
773cf108b5cSJingoo Han static int adp8860_i2c_suspend(struct device *dev)
77482fd53b7SMichael Hennerich {
775cf108b5cSJingoo Han 	struct i2c_client *client = to_i2c_client(dev);
776cf108b5cSJingoo Han 
77782fd53b7SMichael Hennerich 	adp8860_clr_bits(client, ADP8860_MDCR, NSTBY);
77882fd53b7SMichael Hennerich 
77982fd53b7SMichael Hennerich 	return 0;
78082fd53b7SMichael Hennerich }
78182fd53b7SMichael Hennerich 
782cf108b5cSJingoo Han static int adp8860_i2c_resume(struct device *dev)
78382fd53b7SMichael Hennerich {
784cf108b5cSJingoo Han 	struct i2c_client *client = to_i2c_client(dev);
785cf108b5cSJingoo Han 
7865eb02c01SLars-Peter Clausen 	adp8860_set_bits(client, ADP8860_MDCR, NSTBY | BLEN);
78782fd53b7SMichael Hennerich 
78882fd53b7SMichael Hennerich 	return 0;
78982fd53b7SMichael Hennerich }
79082fd53b7SMichael Hennerich #endif
79182fd53b7SMichael Hennerich 
792cf108b5cSJingoo Han static SIMPLE_DEV_PM_OPS(adp8860_i2c_pm_ops, adp8860_i2c_suspend,
793cf108b5cSJingoo Han 			adp8860_i2c_resume);
794cf108b5cSJingoo Han 
79582fd53b7SMichael Hennerich static const struct i2c_device_id adp8860_id[] = {
796c7c06d8aSMichael Hennerich 	{ "adp8860", adp8860 },
797c7c06d8aSMichael Hennerich 	{ "adp8861", adp8861 },
798c7c06d8aSMichael Hennerich 	{ "adp8863", adp8863 },
79982fd53b7SMichael Hennerich 	{ }
80082fd53b7SMichael Hennerich };
80182fd53b7SMichael Hennerich MODULE_DEVICE_TABLE(i2c, adp8860_id);
80282fd53b7SMichael Hennerich 
80382fd53b7SMichael Hennerich static struct i2c_driver adp8860_driver = {
80482fd53b7SMichael Hennerich 	.driver = {
80582fd53b7SMichael Hennerich 		.name	= KBUILD_MODNAME,
806cf108b5cSJingoo Han 		.pm	= &adp8860_i2c_pm_ops,
80782fd53b7SMichael Hennerich 	},
80882fd53b7SMichael Hennerich 	.probe    = adp8860_probe,
809d1723fa2SBill Pemberton 	.remove   = adp8860_remove,
81082fd53b7SMichael Hennerich 	.id_table = adp8860_id,
81182fd53b7SMichael Hennerich };
81282fd53b7SMichael Hennerich 
81381ce6864SAxel Lin module_i2c_driver(adp8860_driver);
81482fd53b7SMichael Hennerich 
81582fd53b7SMichael Hennerich MODULE_LICENSE("GPL v2");
8160b193400SMichael Hennerich MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
81782fd53b7SMichael Hennerich MODULE_DESCRIPTION("ADP8860 Backlight driver");
818