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, ®_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, ®_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); 1845e548f0fSJingoo 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; 3665e548f0fSJingoo 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, ®_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); 5045e548f0fSJingoo 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, ®_val); 56982fd53b7SMichael Hennerich ret_val = reg_val; 57082fd53b7SMichael Hennerich error |= adp8860_read(data->client, ADP8860_PH1LEVH, ®_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, ®_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); 624*86c68e2fSArnd Bergmann ret = adp8860_read(data->client, ADP8860_CFGR, ®_val); 625*86c68e2fSArnd Bergmann if (!ret) { 62682fd53b7SMichael Hennerich reg_val &= ~(CFGR_BLV_MASK << CFGR_BLV_SHIFT); 627c7ce2500SMichael Hennerich reg_val |= (val - 1) << CFGR_BLV_SHIFT; 62882fd53b7SMichael Hennerich adp8860_write(data->client, ADP8860_CFGR, reg_val); 629*86c68e2fSArnd Bergmann } 63082fd53b7SMichael Hennerich mutex_unlock(&data->lock); 63182fd53b7SMichael Hennerich } 63282fd53b7SMichael Hennerich 63382fd53b7SMichael Hennerich return count; 63482fd53b7SMichael Hennerich } 63582fd53b7SMichael Hennerich static DEVICE_ATTR(ambient_light_zone, 0664, 63682fd53b7SMichael Hennerich adp8860_bl_ambient_light_zone_show, 63782fd53b7SMichael Hennerich adp8860_bl_ambient_light_zone_store); 63882fd53b7SMichael Hennerich #endif 63982fd53b7SMichael Hennerich 64082fd53b7SMichael Hennerich static struct attribute *adp8860_bl_attributes[] = { 64182fd53b7SMichael Hennerich &dev_attr_l3_dark_max.attr, 64282fd53b7SMichael Hennerich &dev_attr_l3_dark_dim.attr, 64382fd53b7SMichael Hennerich &dev_attr_l2_office_max.attr, 64482fd53b7SMichael Hennerich &dev_attr_l2_office_dim.attr, 64582fd53b7SMichael Hennerich &dev_attr_l1_daylight_max.attr, 64682fd53b7SMichael Hennerich &dev_attr_l1_daylight_dim.attr, 64782fd53b7SMichael Hennerich #ifdef ADP8860_EXT_FEATURES 64882fd53b7SMichael Hennerich &dev_attr_ambient_light_level.attr, 64982fd53b7SMichael Hennerich &dev_attr_ambient_light_zone.attr, 65082fd53b7SMichael Hennerich #endif 65182fd53b7SMichael Hennerich NULL 65282fd53b7SMichael Hennerich }; 65382fd53b7SMichael Hennerich 65482fd53b7SMichael Hennerich static const struct attribute_group adp8860_bl_attr_group = { 65582fd53b7SMichael Hennerich .attrs = adp8860_bl_attributes, 65682fd53b7SMichael Hennerich }; 65782fd53b7SMichael Hennerich 6581b9e450dSBill Pemberton static int adp8860_probe(struct i2c_client *client, 65982fd53b7SMichael Hennerich const struct i2c_device_id *id) 66082fd53b7SMichael Hennerich { 66182fd53b7SMichael Hennerich struct backlight_device *bl; 66282fd53b7SMichael Hennerich struct adp8860_bl *data; 66382fd53b7SMichael Hennerich struct adp8860_backlight_platform_data *pdata = 664c512794cSJingoo Han dev_get_platdata(&client->dev); 6653f43f8b2SRandy Dunlap struct backlight_properties props; 66682fd53b7SMichael Hennerich uint8_t reg_val; 66782fd53b7SMichael Hennerich int ret; 66882fd53b7SMichael Hennerich 66982fd53b7SMichael Hennerich if (!i2c_check_functionality(client->adapter, 67082fd53b7SMichael Hennerich I2C_FUNC_SMBUS_BYTE_DATA)) { 67182fd53b7SMichael Hennerich dev_err(&client->dev, "SMBUS Byte Data not Supported\n"); 67282fd53b7SMichael Hennerich return -EIO; 67382fd53b7SMichael Hennerich } 67482fd53b7SMichael Hennerich 67582fd53b7SMichael Hennerich if (!pdata) { 67682fd53b7SMichael Hennerich dev_err(&client->dev, "no platform data?\n"); 67782fd53b7SMichael Hennerich return -EINVAL; 67882fd53b7SMichael Hennerich } 67982fd53b7SMichael Hennerich 68058875ea9SJingoo Han data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); 68182fd53b7SMichael Hennerich if (data == NULL) 68282fd53b7SMichael Hennerich return -ENOMEM; 68382fd53b7SMichael Hennerich 684c7c06d8aSMichael Hennerich ret = adp8860_read(client, ADP8860_MFDVID, ®_val); 685c7c06d8aSMichael Hennerich if (ret < 0) 68658875ea9SJingoo Han return ret; 687c7c06d8aSMichael Hennerich 688c7c06d8aSMichael Hennerich switch (ADP8860_MANID(reg_val)) { 689c7c06d8aSMichael Hennerich case ADP8863_MANUFID: 690c7c06d8aSMichael Hennerich data->gdwn_dis = !!pdata->gdwn_dis; 691c7c06d8aSMichael Hennerich case ADP8860_MANUFID: 692c7c06d8aSMichael Hennerich data->en_ambl_sens = !!pdata->en_ambl_sens; 693c7c06d8aSMichael Hennerich break; 694c7c06d8aSMichael Hennerich case ADP8861_MANUFID: 695c7c06d8aSMichael Hennerich data->gdwn_dis = !!pdata->gdwn_dis; 696c7c06d8aSMichael Hennerich break; 697c7c06d8aSMichael Hennerich default: 698c7c06d8aSMichael Hennerich dev_err(&client->dev, "failed to probe\n"); 69958875ea9SJingoo Han return -ENODEV; 700c7c06d8aSMichael Hennerich } 701c7c06d8aSMichael Hennerich 70282fd53b7SMichael Hennerich /* It's confirmed that the DEVID field is actually a REVID */ 70382fd53b7SMichael Hennerich 70482fd53b7SMichael Hennerich data->revid = ADP8860_DEVID(reg_val); 70582fd53b7SMichael Hennerich data->client = client; 70682fd53b7SMichael Hennerich data->pdata = pdata; 70782fd53b7SMichael Hennerich data->id = id->driver_data; 70882fd53b7SMichael Hennerich data->current_brightness = 0; 70982fd53b7SMichael Hennerich i2c_set_clientdata(client, data); 71082fd53b7SMichael Hennerich 7113f43f8b2SRandy Dunlap memset(&props, 0, sizeof(props)); 712bb7ca747SMatthew Garrett props.type = BACKLIGHT_RAW; 7133f43f8b2SRandy Dunlap props.max_brightness = ADP8860_MAX_BRIGHTNESS; 7143f43f8b2SRandy Dunlap 71582fd53b7SMichael Hennerich mutex_init(&data->lock); 71682fd53b7SMichael Hennerich 71791fa4ee8SJingoo Han bl = devm_backlight_device_register(&client->dev, 71891fa4ee8SJingoo Han dev_driver_string(&client->dev), 7193f43f8b2SRandy Dunlap &client->dev, data, &adp8860_bl_ops, &props); 72082fd53b7SMichael Hennerich if (IS_ERR(bl)) { 72182fd53b7SMichael Hennerich dev_err(&client->dev, "failed to register backlight\n"); 72258875ea9SJingoo Han return PTR_ERR(bl); 72382fd53b7SMichael Hennerich } 72482fd53b7SMichael Hennerich 72582fd53b7SMichael Hennerich bl->props.brightness = ADP8860_MAX_BRIGHTNESS; 72682fd53b7SMichael Hennerich 72782fd53b7SMichael Hennerich data->bl = bl; 72882fd53b7SMichael Hennerich 729c7c06d8aSMichael Hennerich if (data->en_ambl_sens) 73082fd53b7SMichael Hennerich ret = sysfs_create_group(&bl->dev.kobj, 73182fd53b7SMichael Hennerich &adp8860_bl_attr_group); 73282fd53b7SMichael Hennerich 73382fd53b7SMichael Hennerich if (ret) { 73482fd53b7SMichael Hennerich dev_err(&client->dev, "failed to register sysfs\n"); 73591fa4ee8SJingoo Han return ret; 73682fd53b7SMichael Hennerich } 73782fd53b7SMichael Hennerich 73882fd53b7SMichael Hennerich ret = adp8860_bl_setup(bl); 73982fd53b7SMichael Hennerich if (ret) { 74082fd53b7SMichael Hennerich ret = -EIO; 74182fd53b7SMichael Hennerich goto out; 74282fd53b7SMichael Hennerich } 74382fd53b7SMichael Hennerich 74482fd53b7SMichael Hennerich backlight_update_status(bl); 74582fd53b7SMichael Hennerich 746c7c06d8aSMichael Hennerich dev_info(&client->dev, "%s Rev.%d Backlight\n", 747c7c06d8aSMichael Hennerich client->name, data->revid); 74882fd53b7SMichael Hennerich 74982fd53b7SMichael Hennerich if (pdata->num_leds) 75082fd53b7SMichael Hennerich adp8860_led_probe(client); 75182fd53b7SMichael Hennerich 75282fd53b7SMichael Hennerich return 0; 75382fd53b7SMichael Hennerich 75482fd53b7SMichael Hennerich out: 755c7c06d8aSMichael Hennerich if (data->en_ambl_sens) 75682fd53b7SMichael Hennerich sysfs_remove_group(&data->bl->dev.kobj, 75782fd53b7SMichael Hennerich &adp8860_bl_attr_group); 75882fd53b7SMichael Hennerich 75982fd53b7SMichael Hennerich return ret; 76082fd53b7SMichael Hennerich } 76182fd53b7SMichael Hennerich 7627e4b9d0bSBill Pemberton static int adp8860_remove(struct i2c_client *client) 76382fd53b7SMichael Hennerich { 76482fd53b7SMichael Hennerich struct adp8860_bl *data = i2c_get_clientdata(client); 76582fd53b7SMichael Hennerich 76682fd53b7SMichael Hennerich adp8860_clr_bits(client, ADP8860_MDCR, NSTBY); 76782fd53b7SMichael Hennerich 76882fd53b7SMichael Hennerich if (data->led) 76982fd53b7SMichael Hennerich adp8860_led_remove(client); 77082fd53b7SMichael Hennerich 771c7c06d8aSMichael Hennerich if (data->en_ambl_sens) 77282fd53b7SMichael Hennerich sysfs_remove_group(&data->bl->dev.kobj, 77382fd53b7SMichael Hennerich &adp8860_bl_attr_group); 77482fd53b7SMichael Hennerich 77582fd53b7SMichael Hennerich return 0; 77682fd53b7SMichael Hennerich } 77782fd53b7SMichael Hennerich 778cf108b5cSJingoo Han #ifdef CONFIG_PM_SLEEP 779cf108b5cSJingoo Han static int adp8860_i2c_suspend(struct device *dev) 78082fd53b7SMichael Hennerich { 781cf108b5cSJingoo Han struct i2c_client *client = to_i2c_client(dev); 782cf108b5cSJingoo Han 78382fd53b7SMichael Hennerich adp8860_clr_bits(client, ADP8860_MDCR, NSTBY); 78482fd53b7SMichael Hennerich 78582fd53b7SMichael Hennerich return 0; 78682fd53b7SMichael Hennerich } 78782fd53b7SMichael Hennerich 788cf108b5cSJingoo Han static int adp8860_i2c_resume(struct device *dev) 78982fd53b7SMichael Hennerich { 790cf108b5cSJingoo Han struct i2c_client *client = to_i2c_client(dev); 791cf108b5cSJingoo Han 7925eb02c01SLars-Peter Clausen adp8860_set_bits(client, ADP8860_MDCR, NSTBY | BLEN); 79382fd53b7SMichael Hennerich 79482fd53b7SMichael Hennerich return 0; 79582fd53b7SMichael Hennerich } 79682fd53b7SMichael Hennerich #endif 79782fd53b7SMichael Hennerich 798cf108b5cSJingoo Han static SIMPLE_DEV_PM_OPS(adp8860_i2c_pm_ops, adp8860_i2c_suspend, 799cf108b5cSJingoo Han adp8860_i2c_resume); 800cf108b5cSJingoo Han 80182fd53b7SMichael Hennerich static const struct i2c_device_id adp8860_id[] = { 802c7c06d8aSMichael Hennerich { "adp8860", adp8860 }, 803c7c06d8aSMichael Hennerich { "adp8861", adp8861 }, 804c7c06d8aSMichael Hennerich { "adp8863", adp8863 }, 80582fd53b7SMichael Hennerich { } 80682fd53b7SMichael Hennerich }; 80782fd53b7SMichael Hennerich MODULE_DEVICE_TABLE(i2c, adp8860_id); 80882fd53b7SMichael Hennerich 80982fd53b7SMichael Hennerich static struct i2c_driver adp8860_driver = { 81082fd53b7SMichael Hennerich .driver = { 81182fd53b7SMichael Hennerich .name = KBUILD_MODNAME, 812cf108b5cSJingoo Han .pm = &adp8860_i2c_pm_ops, 81382fd53b7SMichael Hennerich }, 81482fd53b7SMichael Hennerich .probe = adp8860_probe, 815d1723fa2SBill Pemberton .remove = adp8860_remove, 81682fd53b7SMichael Hennerich .id_table = adp8860_id, 81782fd53b7SMichael Hennerich }; 81882fd53b7SMichael Hennerich 81981ce6864SAxel Lin module_i2c_driver(adp8860_driver); 82082fd53b7SMichael Hennerich 82182fd53b7SMichael Hennerich MODULE_LICENSE("GPL v2"); 82282fd53b7SMichael Hennerich MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); 82382fd53b7SMichael Hennerich MODULE_DESCRIPTION("ADP8860 Backlight driver"); 824