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, ®_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, ®_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 { 364*51d53e5bSSam 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, ®_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, ®_val); 56002f22c00SArnd Bergmann if (!error) { 56182fd53b7SMichael Hennerich ret_val = reg_val; 56202f22c00SArnd Bergmann error = adp8860_read(data->client, ADP8860_PH1LEVH, ®_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, ®_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, ®_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, ®_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; 684858c5dfcSGustavo A. R. Silva /* fall through */ 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