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 149*36c3e759SAxel 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); 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 21682fd53b7SMichael Hennerich static int __devinit adp8860_led_probe(struct i2c_client *client) 21782fd53b7SMichael Hennerich { 21882fd53b7SMichael Hennerich struct adp8860_backlight_platform_data *pdata = 21982fd53b7SMichael Hennerich client->dev.platform_data; 22082fd53b7SMichael Hennerich struct adp8860_bl *data = i2c_get_clientdata(client); 22182fd53b7SMichael Hennerich struct adp8860_led *led, *led_dat; 22282fd53b7SMichael Hennerich struct led_info *cur_led; 22382fd53b7SMichael Hennerich int ret, i; 22482fd53b7SMichael Hennerich 22582fd53b7SMichael Hennerich led = kzalloc(sizeof(*led) * pdata->num_leds, GFP_KERNEL); 22682fd53b7SMichael Hennerich if (led == NULL) { 22782fd53b7SMichael Hennerich dev_err(&client->dev, "failed to alloc memory\n"); 22882fd53b7SMichael Hennerich return -ENOMEM; 22982fd53b7SMichael Hennerich } 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"); 23982fd53b7SMichael Hennerich goto err_free; 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); 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); 25782fd53b7SMichael Hennerich goto err; 25882fd53b7SMichael Hennerich } 25982fd53b7SMichael Hennerich 26082fd53b7SMichael Hennerich led_dat->cdev.name = cur_led->name; 26182fd53b7SMichael Hennerich led_dat->cdev.default_trigger = cur_led->default_trigger; 26282fd53b7SMichael Hennerich led_dat->cdev.brightness_set = adp8860_led_set; 26382fd53b7SMichael Hennerich led_dat->cdev.brightness = LED_OFF; 26482fd53b7SMichael Hennerich led_dat->flags = cur_led->flags >> FLAG_OFFT_SHIFT; 26582fd53b7SMichael Hennerich led_dat->client = client; 26682fd53b7SMichael Hennerich led_dat->new_brightness = LED_OFF; 26782fd53b7SMichael Hennerich INIT_WORK(&led_dat->work, adp8860_led_work); 26882fd53b7SMichael Hennerich 26982fd53b7SMichael Hennerich ret = led_classdev_register(&client->dev, &led_dat->cdev); 27082fd53b7SMichael Hennerich if (ret) { 27182fd53b7SMichael Hennerich dev_err(&client->dev, "failed to register LED %d\n", 27282fd53b7SMichael Hennerich led_dat->id); 27382fd53b7SMichael Hennerich goto err; 27482fd53b7SMichael Hennerich } 27582fd53b7SMichael Hennerich 27682fd53b7SMichael Hennerich ret = adp8860_led_setup(led_dat); 27782fd53b7SMichael Hennerich if (ret) { 27882fd53b7SMichael Hennerich dev_err(&client->dev, "failed to write\n"); 27982fd53b7SMichael Hennerich i++; 28082fd53b7SMichael Hennerich goto err; 28182fd53b7SMichael Hennerich } 28282fd53b7SMichael Hennerich } 28382fd53b7SMichael Hennerich 28482fd53b7SMichael Hennerich data->led = led; 28582fd53b7SMichael Hennerich 28682fd53b7SMichael Hennerich return 0; 28782fd53b7SMichael Hennerich 28882fd53b7SMichael Hennerich err: 28982fd53b7SMichael Hennerich for (i = i - 1; i >= 0; --i) { 29082fd53b7SMichael Hennerich led_classdev_unregister(&led[i].cdev); 29182fd53b7SMichael Hennerich cancel_work_sync(&led[i].work); 29282fd53b7SMichael Hennerich } 29382fd53b7SMichael Hennerich 29482fd53b7SMichael Hennerich err_free: 29582fd53b7SMichael Hennerich kfree(led); 29682fd53b7SMichael Hennerich 29782fd53b7SMichael Hennerich return ret; 29882fd53b7SMichael Hennerich } 29982fd53b7SMichael Hennerich 30082fd53b7SMichael Hennerich static int __devexit adp8860_led_remove(struct i2c_client *client) 30182fd53b7SMichael Hennerich { 30282fd53b7SMichael Hennerich struct adp8860_backlight_platform_data *pdata = 30382fd53b7SMichael Hennerich client->dev.platform_data; 30482fd53b7SMichael Hennerich struct adp8860_bl *data = i2c_get_clientdata(client); 30582fd53b7SMichael Hennerich int i; 30682fd53b7SMichael Hennerich 30782fd53b7SMichael Hennerich for (i = 0; i < pdata->num_leds; i++) { 30882fd53b7SMichael Hennerich led_classdev_unregister(&data->led[i].cdev); 30982fd53b7SMichael Hennerich cancel_work_sync(&data->led[i].work); 31082fd53b7SMichael Hennerich } 31182fd53b7SMichael Hennerich 31282fd53b7SMichael Hennerich kfree(data->led); 31382fd53b7SMichael Hennerich return 0; 31482fd53b7SMichael Hennerich } 31582fd53b7SMichael Hennerich #else 31682fd53b7SMichael Hennerich static int __devinit adp8860_led_probe(struct i2c_client *client) 31782fd53b7SMichael Hennerich { 31882fd53b7SMichael Hennerich return 0; 31982fd53b7SMichael Hennerich } 32082fd53b7SMichael Hennerich 32182fd53b7SMichael Hennerich static int __devexit adp8860_led_remove(struct i2c_client *client) 32282fd53b7SMichael Hennerich { 32382fd53b7SMichael Hennerich return 0; 32482fd53b7SMichael Hennerich } 32582fd53b7SMichael Hennerich #endif 32682fd53b7SMichael Hennerich 32782fd53b7SMichael Hennerich static int adp8860_bl_set(struct backlight_device *bl, int brightness) 32882fd53b7SMichael Hennerich { 32982fd53b7SMichael Hennerich struct adp8860_bl *data = bl_get_data(bl); 33082fd53b7SMichael Hennerich struct i2c_client *client = data->client; 33182fd53b7SMichael Hennerich int ret = 0; 33282fd53b7SMichael Hennerich 333c7c06d8aSMichael Hennerich if (data->en_ambl_sens) { 33482fd53b7SMichael Hennerich if ((brightness > 0) && (brightness < ADP8860_MAX_BRIGHTNESS)) { 33582fd53b7SMichael Hennerich /* Disable Ambient Light auto adjust */ 33682fd53b7SMichael Hennerich ret |= adp8860_clr_bits(client, ADP8860_MDCR, 33782fd53b7SMichael Hennerich CMP_AUTOEN); 33882fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_BLMX1, brightness); 33982fd53b7SMichael Hennerich } else { 34082fd53b7SMichael Hennerich /* 34182fd53b7SMichael Hennerich * MAX_BRIGHTNESS -> Enable Ambient Light auto adjust 34282fd53b7SMichael Hennerich * restore daylight l1 sysfs brightness 34382fd53b7SMichael Hennerich */ 34482fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_BLMX1, 34582fd53b7SMichael Hennerich data->cached_daylight_max); 34682fd53b7SMichael Hennerich ret |= adp8860_set_bits(client, ADP8860_MDCR, 34782fd53b7SMichael Hennerich CMP_AUTOEN); 34882fd53b7SMichael Hennerich } 34982fd53b7SMichael Hennerich } else 35082fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_BLMX1, brightness); 35182fd53b7SMichael Hennerich 35282fd53b7SMichael Hennerich if (data->current_brightness && brightness == 0) 35382fd53b7SMichael Hennerich ret |= adp8860_set_bits(client, 35482fd53b7SMichael Hennerich ADP8860_MDCR, DIM_EN); 35582fd53b7SMichael Hennerich else if (data->current_brightness == 0 && brightness) 35682fd53b7SMichael Hennerich ret |= adp8860_clr_bits(client, 35782fd53b7SMichael Hennerich ADP8860_MDCR, DIM_EN); 35882fd53b7SMichael Hennerich 35982fd53b7SMichael Hennerich if (!ret) 36082fd53b7SMichael Hennerich data->current_brightness = brightness; 36182fd53b7SMichael Hennerich 36282fd53b7SMichael Hennerich return ret; 36382fd53b7SMichael Hennerich } 36482fd53b7SMichael Hennerich 36582fd53b7SMichael Hennerich static int adp8860_bl_update_status(struct backlight_device *bl) 36682fd53b7SMichael Hennerich { 36782fd53b7SMichael Hennerich int brightness = bl->props.brightness; 36882fd53b7SMichael Hennerich if (bl->props.power != FB_BLANK_UNBLANK) 36982fd53b7SMichael Hennerich brightness = 0; 37082fd53b7SMichael Hennerich 37182fd53b7SMichael Hennerich if (bl->props.fb_blank != FB_BLANK_UNBLANK) 37282fd53b7SMichael Hennerich brightness = 0; 37382fd53b7SMichael Hennerich 37482fd53b7SMichael Hennerich return adp8860_bl_set(bl, brightness); 37582fd53b7SMichael Hennerich } 37682fd53b7SMichael Hennerich 37782fd53b7SMichael Hennerich static int adp8860_bl_get_brightness(struct backlight_device *bl) 37882fd53b7SMichael Hennerich { 37982fd53b7SMichael Hennerich struct adp8860_bl *data = bl_get_data(bl); 38082fd53b7SMichael Hennerich 38182fd53b7SMichael Hennerich return data->current_brightness; 38282fd53b7SMichael Hennerich } 38382fd53b7SMichael Hennerich 38482fd53b7SMichael Hennerich static const struct backlight_ops adp8860_bl_ops = { 38582fd53b7SMichael Hennerich .update_status = adp8860_bl_update_status, 38682fd53b7SMichael Hennerich .get_brightness = adp8860_bl_get_brightness, 38782fd53b7SMichael Hennerich }; 38882fd53b7SMichael Hennerich 38982fd53b7SMichael Hennerich static int adp8860_bl_setup(struct backlight_device *bl) 39082fd53b7SMichael Hennerich { 39182fd53b7SMichael Hennerich struct adp8860_bl *data = bl_get_data(bl); 39282fd53b7SMichael Hennerich struct i2c_client *client = data->client; 39382fd53b7SMichael Hennerich struct adp8860_backlight_platform_data *pdata = data->pdata; 39482fd53b7SMichael Hennerich int ret = 0; 39582fd53b7SMichael Hennerich 39682fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_BLSEN, ~pdata->bl_led_assign); 39782fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_BLMX1, pdata->l1_daylight_max); 39882fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_BLDM1, pdata->l1_daylight_dim); 39982fd53b7SMichael Hennerich 400c7c06d8aSMichael Hennerich if (data->en_ambl_sens) { 40182fd53b7SMichael Hennerich data->cached_daylight_max = pdata->l1_daylight_max; 40282fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_BLMX2, 40382fd53b7SMichael Hennerich pdata->l2_office_max); 40482fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_BLDM2, 40582fd53b7SMichael Hennerich pdata->l2_office_dim); 40682fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_BLMX3, 40782fd53b7SMichael Hennerich pdata->l3_dark_max); 40882fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_BLDM3, 40982fd53b7SMichael Hennerich pdata->l3_dark_dim); 41082fd53b7SMichael Hennerich 41182fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_L2_TRP, pdata->l2_trip); 41282fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_L2_HYS, pdata->l2_hyst); 41382fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_L3_TRP, pdata->l3_trip); 41482fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_L3_HYS, pdata->l3_hyst); 41582fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_CCFG, L2_EN | L3_EN | 41682fd53b7SMichael Hennerich ALS_CCFG_VAL(pdata->abml_filt)); 41782fd53b7SMichael Hennerich } 41882fd53b7SMichael Hennerich 41982fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_CFGR, 42082fd53b7SMichael Hennerich BL_CFGR_VAL(pdata->bl_fade_law, 0)); 42182fd53b7SMichael Hennerich 42282fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_BLFR, FADE_VAL(pdata->bl_fade_in, 42382fd53b7SMichael Hennerich pdata->bl_fade_out)); 42482fd53b7SMichael Hennerich 425c7c06d8aSMichael Hennerich ret |= adp8860_set_bits(client, ADP8860_MDCR, BLEN | DIM_EN | NSTBY | 426c7c06d8aSMichael Hennerich (data->gdwn_dis ? GDWN_DIS : 0)); 42782fd53b7SMichael Hennerich 42882fd53b7SMichael Hennerich return ret; 42982fd53b7SMichael Hennerich } 43082fd53b7SMichael Hennerich 43182fd53b7SMichael Hennerich static ssize_t adp8860_show(struct device *dev, char *buf, int reg) 43282fd53b7SMichael Hennerich { 43382fd53b7SMichael Hennerich struct adp8860_bl *data = dev_get_drvdata(dev); 43482fd53b7SMichael Hennerich int error; 43582fd53b7SMichael Hennerich uint8_t reg_val; 43682fd53b7SMichael Hennerich 43782fd53b7SMichael Hennerich mutex_lock(&data->lock); 43882fd53b7SMichael Hennerich error = adp8860_read(data->client, reg, ®_val); 43982fd53b7SMichael Hennerich mutex_unlock(&data->lock); 44082fd53b7SMichael Hennerich 44182fd53b7SMichael Hennerich if (error < 0) 44282fd53b7SMichael Hennerich return error; 44382fd53b7SMichael Hennerich 44482fd53b7SMichael Hennerich return sprintf(buf, "%u\n", reg_val); 44582fd53b7SMichael Hennerich } 44682fd53b7SMichael Hennerich 44782fd53b7SMichael Hennerich static ssize_t adp8860_store(struct device *dev, const char *buf, 44882fd53b7SMichael Hennerich size_t count, int reg) 44982fd53b7SMichael Hennerich { 45082fd53b7SMichael Hennerich struct adp8860_bl *data = dev_get_drvdata(dev); 45182fd53b7SMichael Hennerich unsigned long val; 45282fd53b7SMichael Hennerich int ret; 45382fd53b7SMichael Hennerich 45482fd53b7SMichael Hennerich ret = strict_strtoul(buf, 10, &val); 45582fd53b7SMichael Hennerich if (ret) 45682fd53b7SMichael Hennerich return ret; 45782fd53b7SMichael Hennerich 45882fd53b7SMichael Hennerich mutex_lock(&data->lock); 45982fd53b7SMichael Hennerich adp8860_write(data->client, reg, val); 46082fd53b7SMichael Hennerich mutex_unlock(&data->lock); 46182fd53b7SMichael Hennerich 46282fd53b7SMichael Hennerich return count; 46382fd53b7SMichael Hennerich } 46482fd53b7SMichael Hennerich 46582fd53b7SMichael Hennerich static ssize_t adp8860_bl_l3_dark_max_show(struct device *dev, 46682fd53b7SMichael Hennerich struct device_attribute *attr, char *buf) 46782fd53b7SMichael Hennerich { 46882fd53b7SMichael Hennerich return adp8860_show(dev, buf, ADP8860_BLMX3); 46982fd53b7SMichael Hennerich } 47082fd53b7SMichael Hennerich 47182fd53b7SMichael Hennerich static ssize_t adp8860_bl_l3_dark_max_store(struct device *dev, 47282fd53b7SMichael Hennerich struct device_attribute *attr, const char *buf, size_t count) 47382fd53b7SMichael Hennerich { 47482fd53b7SMichael Hennerich return adp8860_store(dev, buf, count, ADP8860_BLMX3); 47582fd53b7SMichael Hennerich } 47682fd53b7SMichael Hennerich 47782fd53b7SMichael Hennerich static DEVICE_ATTR(l3_dark_max, 0664, adp8860_bl_l3_dark_max_show, 47882fd53b7SMichael Hennerich adp8860_bl_l3_dark_max_store); 47982fd53b7SMichael Hennerich 48082fd53b7SMichael Hennerich static ssize_t adp8860_bl_l2_office_max_show(struct device *dev, 48182fd53b7SMichael Hennerich struct device_attribute *attr, char *buf) 48282fd53b7SMichael Hennerich { 48382fd53b7SMichael Hennerich return adp8860_show(dev, buf, ADP8860_BLMX2); 48482fd53b7SMichael Hennerich } 48582fd53b7SMichael Hennerich 48682fd53b7SMichael Hennerich static ssize_t adp8860_bl_l2_office_max_store(struct device *dev, 48782fd53b7SMichael Hennerich struct device_attribute *attr, const char *buf, size_t count) 48882fd53b7SMichael Hennerich { 48982fd53b7SMichael Hennerich return adp8860_store(dev, buf, count, ADP8860_BLMX2); 49082fd53b7SMichael Hennerich } 49182fd53b7SMichael Hennerich static DEVICE_ATTR(l2_office_max, 0664, adp8860_bl_l2_office_max_show, 49282fd53b7SMichael Hennerich adp8860_bl_l2_office_max_store); 49382fd53b7SMichael Hennerich 49482fd53b7SMichael Hennerich static ssize_t adp8860_bl_l1_daylight_max_show(struct device *dev, 49582fd53b7SMichael Hennerich struct device_attribute *attr, char *buf) 49682fd53b7SMichael Hennerich { 49782fd53b7SMichael Hennerich return adp8860_show(dev, buf, ADP8860_BLMX1); 49882fd53b7SMichael Hennerich } 49982fd53b7SMichael Hennerich 50082fd53b7SMichael Hennerich static ssize_t adp8860_bl_l1_daylight_max_store(struct device *dev, 50182fd53b7SMichael Hennerich struct device_attribute *attr, const char *buf, size_t count) 50282fd53b7SMichael Hennerich { 50382fd53b7SMichael Hennerich struct adp8860_bl *data = dev_get_drvdata(dev); 5044f1aa846SMichael Hennerich int ret = strict_strtoul(buf, 10, &data->cached_daylight_max); 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 61182fd53b7SMichael Hennerich ret = strict_strtoul(buf, 10, &val); 61282fd53b7SMichael Hennerich if (ret) 61382fd53b7SMichael Hennerich return ret; 61482fd53b7SMichael Hennerich 61582fd53b7SMichael Hennerich if (val == 0) { 61682fd53b7SMichael Hennerich /* Enable automatic ambient light sensing */ 61782fd53b7SMichael Hennerich adp8860_set_bits(data->client, ADP8860_MDCR, CMP_AUTOEN); 618c7ce2500SMichael Hennerich } else if ((val > 0) && (val <= 3)) { 61982fd53b7SMichael Hennerich /* Disable automatic ambient light sensing */ 62082fd53b7SMichael Hennerich adp8860_clr_bits(data->client, ADP8860_MDCR, CMP_AUTOEN); 62182fd53b7SMichael Hennerich 62282fd53b7SMichael Hennerich /* Set user supplied ambient light zone */ 62382fd53b7SMichael Hennerich mutex_lock(&data->lock); 62482fd53b7SMichael Hennerich adp8860_read(data->client, ADP8860_CFGR, ®_val); 62582fd53b7SMichael Hennerich reg_val &= ~(CFGR_BLV_MASK << CFGR_BLV_SHIFT); 626c7ce2500SMichael Hennerich reg_val |= (val - 1) << CFGR_BLV_SHIFT; 62782fd53b7SMichael Hennerich adp8860_write(data->client, ADP8860_CFGR, reg_val); 62882fd53b7SMichael Hennerich mutex_unlock(&data->lock); 62982fd53b7SMichael Hennerich } 63082fd53b7SMichael Hennerich 63182fd53b7SMichael Hennerich return count; 63282fd53b7SMichael Hennerich } 63382fd53b7SMichael Hennerich static DEVICE_ATTR(ambient_light_zone, 0664, 63482fd53b7SMichael Hennerich adp8860_bl_ambient_light_zone_show, 63582fd53b7SMichael Hennerich adp8860_bl_ambient_light_zone_store); 63682fd53b7SMichael Hennerich #endif 63782fd53b7SMichael Hennerich 63882fd53b7SMichael Hennerich static struct attribute *adp8860_bl_attributes[] = { 63982fd53b7SMichael Hennerich &dev_attr_l3_dark_max.attr, 64082fd53b7SMichael Hennerich &dev_attr_l3_dark_dim.attr, 64182fd53b7SMichael Hennerich &dev_attr_l2_office_max.attr, 64282fd53b7SMichael Hennerich &dev_attr_l2_office_dim.attr, 64382fd53b7SMichael Hennerich &dev_attr_l1_daylight_max.attr, 64482fd53b7SMichael Hennerich &dev_attr_l1_daylight_dim.attr, 64582fd53b7SMichael Hennerich #ifdef ADP8860_EXT_FEATURES 64682fd53b7SMichael Hennerich &dev_attr_ambient_light_level.attr, 64782fd53b7SMichael Hennerich &dev_attr_ambient_light_zone.attr, 64882fd53b7SMichael Hennerich #endif 64982fd53b7SMichael Hennerich NULL 65082fd53b7SMichael Hennerich }; 65182fd53b7SMichael Hennerich 65282fd53b7SMichael Hennerich static const struct attribute_group adp8860_bl_attr_group = { 65382fd53b7SMichael Hennerich .attrs = adp8860_bl_attributes, 65482fd53b7SMichael Hennerich }; 65582fd53b7SMichael Hennerich 65682fd53b7SMichael Hennerich static int __devinit adp8860_probe(struct i2c_client *client, 65782fd53b7SMichael Hennerich const struct i2c_device_id *id) 65882fd53b7SMichael Hennerich { 65982fd53b7SMichael Hennerich struct backlight_device *bl; 66082fd53b7SMichael Hennerich struct adp8860_bl *data; 66182fd53b7SMichael Hennerich struct adp8860_backlight_platform_data *pdata = 66282fd53b7SMichael Hennerich client->dev.platform_data; 6633f43f8b2SRandy Dunlap struct backlight_properties props; 66482fd53b7SMichael Hennerich uint8_t reg_val; 66582fd53b7SMichael Hennerich int ret; 66682fd53b7SMichael Hennerich 66782fd53b7SMichael Hennerich if (!i2c_check_functionality(client->adapter, 66882fd53b7SMichael Hennerich I2C_FUNC_SMBUS_BYTE_DATA)) { 66982fd53b7SMichael Hennerich dev_err(&client->dev, "SMBUS Byte Data not Supported\n"); 67082fd53b7SMichael Hennerich return -EIO; 67182fd53b7SMichael Hennerich } 67282fd53b7SMichael Hennerich 67382fd53b7SMichael Hennerich if (!pdata) { 67482fd53b7SMichael Hennerich dev_err(&client->dev, "no platform data?\n"); 67582fd53b7SMichael Hennerich return -EINVAL; 67682fd53b7SMichael Hennerich } 67782fd53b7SMichael Hennerich 67882fd53b7SMichael Hennerich data = kzalloc(sizeof(*data), GFP_KERNEL); 67982fd53b7SMichael Hennerich if (data == NULL) 68082fd53b7SMichael Hennerich return -ENOMEM; 68182fd53b7SMichael Hennerich 682c7c06d8aSMichael Hennerich ret = adp8860_read(client, ADP8860_MFDVID, ®_val); 683c7c06d8aSMichael Hennerich if (ret < 0) 684c7c06d8aSMichael Hennerich goto out2; 685c7c06d8aSMichael Hennerich 686c7c06d8aSMichael Hennerich switch (ADP8860_MANID(reg_val)) { 687c7c06d8aSMichael Hennerich case ADP8863_MANUFID: 688c7c06d8aSMichael Hennerich data->gdwn_dis = !!pdata->gdwn_dis; 689c7c06d8aSMichael Hennerich case ADP8860_MANUFID: 690c7c06d8aSMichael Hennerich data->en_ambl_sens = !!pdata->en_ambl_sens; 691c7c06d8aSMichael Hennerich break; 692c7c06d8aSMichael Hennerich case ADP8861_MANUFID: 693c7c06d8aSMichael Hennerich data->gdwn_dis = !!pdata->gdwn_dis; 694c7c06d8aSMichael Hennerich break; 695c7c06d8aSMichael Hennerich default: 696c7c06d8aSMichael Hennerich dev_err(&client->dev, "failed to probe\n"); 697c7c06d8aSMichael Hennerich ret = -ENODEV; 698c7c06d8aSMichael Hennerich goto out2; 699c7c06d8aSMichael Hennerich } 700c7c06d8aSMichael Hennerich 70182fd53b7SMichael Hennerich /* It's confirmed that the DEVID field is actually a REVID */ 70282fd53b7SMichael Hennerich 70382fd53b7SMichael Hennerich data->revid = ADP8860_DEVID(reg_val); 70482fd53b7SMichael Hennerich data->client = client; 70582fd53b7SMichael Hennerich data->pdata = pdata; 70682fd53b7SMichael Hennerich data->id = id->driver_data; 70782fd53b7SMichael Hennerich data->current_brightness = 0; 70882fd53b7SMichael Hennerich i2c_set_clientdata(client, data); 70982fd53b7SMichael Hennerich 7103f43f8b2SRandy Dunlap memset(&props, 0, sizeof(props)); 711bb7ca747SMatthew Garrett props.type = BACKLIGHT_RAW; 7123f43f8b2SRandy Dunlap props.max_brightness = ADP8860_MAX_BRIGHTNESS; 7133f43f8b2SRandy Dunlap 71482fd53b7SMichael Hennerich mutex_init(&data->lock); 71582fd53b7SMichael Hennerich 71682fd53b7SMichael Hennerich bl = backlight_device_register(dev_driver_string(&client->dev), 7173f43f8b2SRandy Dunlap &client->dev, data, &adp8860_bl_ops, &props); 71882fd53b7SMichael Hennerich if (IS_ERR(bl)) { 71982fd53b7SMichael Hennerich dev_err(&client->dev, "failed to register backlight\n"); 72082fd53b7SMichael Hennerich ret = PTR_ERR(bl); 72182fd53b7SMichael Hennerich goto out2; 72282fd53b7SMichael Hennerich } 72382fd53b7SMichael Hennerich 72482fd53b7SMichael Hennerich bl->props.brightness = ADP8860_MAX_BRIGHTNESS; 72582fd53b7SMichael Hennerich 72682fd53b7SMichael Hennerich data->bl = bl; 72782fd53b7SMichael Hennerich 728c7c06d8aSMichael Hennerich if (data->en_ambl_sens) 72982fd53b7SMichael Hennerich ret = sysfs_create_group(&bl->dev.kobj, 73082fd53b7SMichael Hennerich &adp8860_bl_attr_group); 73182fd53b7SMichael Hennerich 73282fd53b7SMichael Hennerich if (ret) { 73382fd53b7SMichael Hennerich dev_err(&client->dev, "failed to register sysfs\n"); 73482fd53b7SMichael Hennerich goto out1; 73582fd53b7SMichael Hennerich } 73682fd53b7SMichael Hennerich 73782fd53b7SMichael Hennerich ret = adp8860_bl_setup(bl); 73882fd53b7SMichael Hennerich if (ret) { 73982fd53b7SMichael Hennerich ret = -EIO; 74082fd53b7SMichael Hennerich goto out; 74182fd53b7SMichael Hennerich } 74282fd53b7SMichael Hennerich 74382fd53b7SMichael Hennerich backlight_update_status(bl); 74482fd53b7SMichael Hennerich 745c7c06d8aSMichael Hennerich dev_info(&client->dev, "%s Rev.%d Backlight\n", 746c7c06d8aSMichael Hennerich client->name, data->revid); 74782fd53b7SMichael Hennerich 74882fd53b7SMichael Hennerich if (pdata->num_leds) 74982fd53b7SMichael Hennerich adp8860_led_probe(client); 75082fd53b7SMichael Hennerich 75182fd53b7SMichael Hennerich return 0; 75282fd53b7SMichael Hennerich 75382fd53b7SMichael Hennerich out: 754c7c06d8aSMichael Hennerich if (data->en_ambl_sens) 75582fd53b7SMichael Hennerich sysfs_remove_group(&data->bl->dev.kobj, 75682fd53b7SMichael Hennerich &adp8860_bl_attr_group); 75782fd53b7SMichael Hennerich out1: 75882fd53b7SMichael Hennerich backlight_device_unregister(bl); 75982fd53b7SMichael Hennerich out2: 76082fd53b7SMichael Hennerich kfree(data); 76182fd53b7SMichael Hennerich 76282fd53b7SMichael Hennerich return ret; 76382fd53b7SMichael Hennerich } 76482fd53b7SMichael Hennerich 76582fd53b7SMichael Hennerich static int __devexit adp8860_remove(struct i2c_client *client) 76682fd53b7SMichael Hennerich { 76782fd53b7SMichael Hennerich struct adp8860_bl *data = i2c_get_clientdata(client); 76882fd53b7SMichael Hennerich 76982fd53b7SMichael Hennerich adp8860_clr_bits(client, ADP8860_MDCR, NSTBY); 77082fd53b7SMichael Hennerich 77182fd53b7SMichael Hennerich if (data->led) 77282fd53b7SMichael Hennerich adp8860_led_remove(client); 77382fd53b7SMichael Hennerich 774c7c06d8aSMichael Hennerich if (data->en_ambl_sens) 77582fd53b7SMichael Hennerich sysfs_remove_group(&data->bl->dev.kobj, 77682fd53b7SMichael Hennerich &adp8860_bl_attr_group); 77782fd53b7SMichael Hennerich 77882fd53b7SMichael Hennerich backlight_device_unregister(data->bl); 77982fd53b7SMichael Hennerich kfree(data); 78082fd53b7SMichael Hennerich 78182fd53b7SMichael Hennerich return 0; 78282fd53b7SMichael Hennerich } 78382fd53b7SMichael Hennerich 78482fd53b7SMichael Hennerich #ifdef CONFIG_PM 78582fd53b7SMichael Hennerich static int adp8860_i2c_suspend(struct i2c_client *client, pm_message_t message) 78682fd53b7SMichael Hennerich { 78782fd53b7SMichael Hennerich adp8860_clr_bits(client, ADP8860_MDCR, NSTBY); 78882fd53b7SMichael Hennerich 78982fd53b7SMichael Hennerich return 0; 79082fd53b7SMichael Hennerich } 79182fd53b7SMichael Hennerich 79282fd53b7SMichael Hennerich static int adp8860_i2c_resume(struct i2c_client *client) 79382fd53b7SMichael Hennerich { 79482fd53b7SMichael Hennerich adp8860_set_bits(client, ADP8860_MDCR, NSTBY); 79582fd53b7SMichael Hennerich 79682fd53b7SMichael Hennerich return 0; 79782fd53b7SMichael Hennerich } 79882fd53b7SMichael Hennerich #else 79982fd53b7SMichael Hennerich #define adp8860_i2c_suspend NULL 80082fd53b7SMichael Hennerich #define adp8860_i2c_resume NULL 80182fd53b7SMichael Hennerich #endif 80282fd53b7SMichael Hennerich 80382fd53b7SMichael Hennerich static const struct i2c_device_id adp8860_id[] = { 804c7c06d8aSMichael Hennerich { "adp8860", adp8860 }, 805c7c06d8aSMichael Hennerich { "adp8861", adp8861 }, 806c7c06d8aSMichael Hennerich { "adp8863", adp8863 }, 80782fd53b7SMichael Hennerich { } 80882fd53b7SMichael Hennerich }; 80982fd53b7SMichael Hennerich MODULE_DEVICE_TABLE(i2c, adp8860_id); 81082fd53b7SMichael Hennerich 81182fd53b7SMichael Hennerich static struct i2c_driver adp8860_driver = { 81282fd53b7SMichael Hennerich .driver = { 81382fd53b7SMichael Hennerich .name = KBUILD_MODNAME, 81482fd53b7SMichael Hennerich }, 81582fd53b7SMichael Hennerich .probe = adp8860_probe, 81682fd53b7SMichael Hennerich .remove = __devexit_p(adp8860_remove), 81782fd53b7SMichael Hennerich .suspend = adp8860_i2c_suspend, 81882fd53b7SMichael Hennerich .resume = adp8860_i2c_resume, 81982fd53b7SMichael Hennerich .id_table = adp8860_id, 82082fd53b7SMichael Hennerich }; 82182fd53b7SMichael Hennerich 82282fd53b7SMichael Hennerich static int __init adp8860_init(void) 82382fd53b7SMichael Hennerich { 82482fd53b7SMichael Hennerich return i2c_add_driver(&adp8860_driver); 82582fd53b7SMichael Hennerich } 82682fd53b7SMichael Hennerich module_init(adp8860_init); 82782fd53b7SMichael Hennerich 82882fd53b7SMichael Hennerich static void __exit adp8860_exit(void) 82982fd53b7SMichael Hennerich { 83082fd53b7SMichael Hennerich i2c_del_driver(&adp8860_driver); 83182fd53b7SMichael Hennerich } 83282fd53b7SMichael Hennerich module_exit(adp8860_exit); 83382fd53b7SMichael Hennerich 83482fd53b7SMichael Hennerich MODULE_LICENSE("GPL v2"); 83582fd53b7SMichael Hennerich MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); 83682fd53b7SMichael Hennerich MODULE_DESCRIPTION("ADP8860 Backlight driver"); 83782fd53b7SMichael Hennerich MODULE_ALIAS("i2c:adp8860-backlight"); 838