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/version.h> 1182fd53b7SMichael Hennerich #include <linux/init.h> 1282fd53b7SMichael Hennerich #include <linux/errno.h> 1382fd53b7SMichael Hennerich #include <linux/pm.h> 1482fd53b7SMichael Hennerich #include <linux/platform_device.h> 1582fd53b7SMichael Hennerich #include <linux/i2c.h> 1682fd53b7SMichael Hennerich #include <linux/fb.h> 1782fd53b7SMichael Hennerich #include <linux/backlight.h> 1882fd53b7SMichael Hennerich #include <linux/leds.h> 19*3f43f8b2SRandy Dunlap #include <linux/slab.h> 2082fd53b7SMichael Hennerich #include <linux/workqueue.h> 2182fd53b7SMichael Hennerich 2282fd53b7SMichael Hennerich #include <linux/i2c/adp8860.h> 2382fd53b7SMichael Hennerich #define ADP8860_EXT_FEATURES 2482fd53b7SMichael Hennerich #define ADP8860_USE_LEDS 2582fd53b7SMichael Hennerich 2682fd53b7SMichael Hennerich #define ADP8860_MFDVID 0x00 /* Manufacturer and device ID */ 2782fd53b7SMichael Hennerich #define ADP8860_MDCR 0x01 /* Device mode and status */ 2882fd53b7SMichael Hennerich #define ADP8860_MDCR2 0x02 /* Device mode and Status Register 2 */ 2982fd53b7SMichael Hennerich #define ADP8860_INTR_EN 0x03 /* Interrupts enable */ 3082fd53b7SMichael Hennerich #define ADP8860_CFGR 0x04 /* Configuration register */ 3182fd53b7SMichael Hennerich #define ADP8860_BLSEN 0x05 /* Sink enable backlight or independent */ 3282fd53b7SMichael Hennerich #define ADP8860_BLOFF 0x06 /* Backlight off timeout */ 3382fd53b7SMichael Hennerich #define ADP8860_BLDIM 0x07 /* Backlight dim timeout */ 3482fd53b7SMichael Hennerich #define ADP8860_BLFR 0x08 /* Backlight fade in and out rates */ 3582fd53b7SMichael Hennerich #define ADP8860_BLMX1 0x09 /* Backlight (Brightness Level 1-daylight) maximum current */ 3682fd53b7SMichael Hennerich #define ADP8860_BLDM1 0x0A /* Backlight (Brightness Level 1-daylight) dim current */ 3782fd53b7SMichael Hennerich #define ADP8860_BLMX2 0x0B /* Backlight (Brightness Level 2-office) maximum current */ 3882fd53b7SMichael Hennerich #define ADP8860_BLDM2 0x0C /* Backlight (Brightness Level 2-office) dim current */ 3982fd53b7SMichael Hennerich #define ADP8860_BLMX3 0x0D /* Backlight (Brightness Level 3-dark) maximum current */ 4082fd53b7SMichael Hennerich #define ADP8860_BLDM3 0x0E /* Backlight (Brightness Level 3-dark) dim current */ 4182fd53b7SMichael Hennerich #define ADP8860_ISCFR 0x0F /* Independent sink current fade control register */ 4282fd53b7SMichael Hennerich #define ADP8860_ISCC 0x10 /* Independent sink current control register */ 4382fd53b7SMichael Hennerich #define ADP8860_ISCT1 0x11 /* Independent Sink Current Timer Register LED[7:5] */ 4482fd53b7SMichael Hennerich #define ADP8860_ISCT2 0x12 /* Independent Sink Current Timer Register LED[4:1] */ 4582fd53b7SMichael Hennerich #define ADP8860_ISCF 0x13 /* Independent sink current fade register */ 4682fd53b7SMichael Hennerich #define ADP8860_ISC7 0x14 /* Independent Sink Current LED7 */ 4782fd53b7SMichael Hennerich #define ADP8860_ISC6 0x15 /* Independent Sink Current LED6 */ 4882fd53b7SMichael Hennerich #define ADP8860_ISC5 0x16 /* Independent Sink Current LED5 */ 4982fd53b7SMichael Hennerich #define ADP8860_ISC4 0x17 /* Independent Sink Current LED4 */ 5082fd53b7SMichael Hennerich #define ADP8860_ISC3 0x18 /* Independent Sink Current LED3 */ 5182fd53b7SMichael Hennerich #define ADP8860_ISC2 0x19 /* Independent Sink Current LED2 */ 5282fd53b7SMichael Hennerich #define ADP8860_ISC1 0x1A /* Independent Sink Current LED1 */ 5382fd53b7SMichael Hennerich #define ADP8860_CCFG 0x1B /* Comparator configuration */ 5482fd53b7SMichael Hennerich #define ADP8860_CCFG2 0x1C /* Second comparator configuration */ 5582fd53b7SMichael Hennerich #define ADP8860_L2_TRP 0x1D /* L2 comparator reference */ 5682fd53b7SMichael Hennerich #define ADP8860_L2_HYS 0x1E /* L2 hysteresis */ 5782fd53b7SMichael Hennerich #define ADP8860_L3_TRP 0x1F /* L3 comparator reference */ 5882fd53b7SMichael Hennerich #define ADP8860_L3_HYS 0x20 /* L3 hysteresis */ 5982fd53b7SMichael Hennerich #define ADP8860_PH1LEVL 0x21 /* First phototransistor ambient light level-low byte register */ 6082fd53b7SMichael Hennerich #define ADP8860_PH1LEVH 0x22 /* First phototransistor ambient light level-high byte register */ 6182fd53b7SMichael Hennerich #define ADP8860_PH2LEVL 0x23 /* Second phototransistor ambient light level-low byte register */ 6282fd53b7SMichael Hennerich #define ADP8860_PH2LEVH 0x24 /* Second phototransistor ambient light level-high byte register */ 6382fd53b7SMichael Hennerich 6482fd53b7SMichael Hennerich #define ADP8860_MANUFID 0x0 /* Analog Devices ADP8860 Manufacturer ID */ 6582fd53b7SMichael Hennerich #define ADP8860_DEVICEID 0x7 /* Analog Devices ADP8860 Device ID */ 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) 7382fd53b7SMichael Hennerich #define SIS_EN (1 << 2) 7482fd53b7SMichael Hennerich #define CMP_AUTOEN (1 << 1) 7582fd53b7SMichael Hennerich #define BLEN (1 << 0) 7682fd53b7SMichael Hennerich 7782fd53b7SMichael Hennerich /* ADP8860_CCFG Main ALS comparator level enable */ 7882fd53b7SMichael Hennerich #define L3_EN (1 << 1) 7982fd53b7SMichael Hennerich #define L2_EN (1 << 0) 8082fd53b7SMichael Hennerich 8182fd53b7SMichael Hennerich #define CFGR_BLV_SHIFT 3 8282fd53b7SMichael Hennerich #define CFGR_BLV_MASK 0x3 8382fd53b7SMichael Hennerich #define ADP8860_FLAG_LED_MASK 0xFF 8482fd53b7SMichael Hennerich 8582fd53b7SMichael Hennerich #define FADE_VAL(in, out) ((0xF & (in)) | ((0xF & (out)) << 4)) 8682fd53b7SMichael Hennerich #define BL_CFGR_VAL(law, blv) ((((blv) & CFGR_BLV_MASK) << CFGR_BLV_SHIFT) | ((0x3 & (law)) << 1)) 8782fd53b7SMichael Hennerich #define ALS_CCFG_VAL(filt) ((0x7 & filt) << 5) 8882fd53b7SMichael Hennerich 8982fd53b7SMichael Hennerich struct adp8860_led { 9082fd53b7SMichael Hennerich struct led_classdev cdev; 9182fd53b7SMichael Hennerich struct work_struct work; 9282fd53b7SMichael Hennerich struct i2c_client *client; 9382fd53b7SMichael Hennerich enum led_brightness new_brightness; 9482fd53b7SMichael Hennerich int id; 9582fd53b7SMichael Hennerich int flags; 9682fd53b7SMichael Hennerich }; 9782fd53b7SMichael Hennerich 9882fd53b7SMichael Hennerich struct adp8860_bl { 9982fd53b7SMichael Hennerich struct i2c_client *client; 10082fd53b7SMichael Hennerich struct backlight_device *bl; 10182fd53b7SMichael Hennerich struct adp8860_led *led; 10282fd53b7SMichael Hennerich struct adp8860_backlight_platform_data *pdata; 10382fd53b7SMichael Hennerich struct mutex lock; 10482fd53b7SMichael Hennerich unsigned long cached_daylight_max; 10582fd53b7SMichael Hennerich int id; 10682fd53b7SMichael Hennerich int revid; 10782fd53b7SMichael Hennerich int current_brightness; 10882fd53b7SMichael Hennerich }; 10982fd53b7SMichael Hennerich 11082fd53b7SMichael Hennerich static int adp8860_read(struct i2c_client *client, int reg, uint8_t *val) 11182fd53b7SMichael Hennerich { 11282fd53b7SMichael Hennerich int ret; 11382fd53b7SMichael Hennerich 11482fd53b7SMichael Hennerich ret = i2c_smbus_read_byte_data(client, reg); 11582fd53b7SMichael Hennerich if (ret < 0) { 11682fd53b7SMichael Hennerich dev_err(&client->dev, "failed reading at 0x%02x\n", reg); 11782fd53b7SMichael Hennerich return ret; 11882fd53b7SMichael Hennerich } 11982fd53b7SMichael Hennerich 12082fd53b7SMichael Hennerich *val = (uint8_t)ret; 12182fd53b7SMichael Hennerich return 0; 12282fd53b7SMichael Hennerich } 12382fd53b7SMichael Hennerich 12482fd53b7SMichael Hennerich 12582fd53b7SMichael Hennerich static int adp8860_write(struct i2c_client *client, u8 reg, u8 val) 12682fd53b7SMichael Hennerich { 12782fd53b7SMichael Hennerich return i2c_smbus_write_byte_data(client, reg, val); 12882fd53b7SMichael Hennerich } 12982fd53b7SMichael Hennerich 13082fd53b7SMichael Hennerich static int adp8860_set_bits(struct i2c_client *client, int reg, uint8_t bit_mask) 13182fd53b7SMichael Hennerich { 13282fd53b7SMichael Hennerich struct adp8860_bl *data = i2c_get_clientdata(client); 13382fd53b7SMichael Hennerich uint8_t reg_val; 13482fd53b7SMichael Hennerich int ret; 13582fd53b7SMichael Hennerich 13682fd53b7SMichael Hennerich mutex_lock(&data->lock); 13782fd53b7SMichael Hennerich 13882fd53b7SMichael Hennerich ret = adp8860_read(client, reg, ®_val); 13982fd53b7SMichael Hennerich 14082fd53b7SMichael Hennerich if (!ret && ((reg_val & bit_mask) == 0)) { 14182fd53b7SMichael Hennerich reg_val |= bit_mask; 14282fd53b7SMichael Hennerich ret = adp8860_write(client, reg, reg_val); 14382fd53b7SMichael Hennerich } 14482fd53b7SMichael Hennerich 14582fd53b7SMichael Hennerich mutex_unlock(&data->lock); 14682fd53b7SMichael Hennerich return ret; 14782fd53b7SMichael Hennerich } 14882fd53b7SMichael Hennerich 14982fd53b7SMichael Hennerich static int adp8860_clr_bits(struct i2c_client *client, int reg, uint8_t bit_mask) 15082fd53b7SMichael Hennerich { 15182fd53b7SMichael Hennerich struct adp8860_bl *data = i2c_get_clientdata(client); 15282fd53b7SMichael Hennerich uint8_t reg_val; 15382fd53b7SMichael Hennerich int ret; 15482fd53b7SMichael Hennerich 15582fd53b7SMichael Hennerich mutex_lock(&data->lock); 15682fd53b7SMichael Hennerich 15782fd53b7SMichael Hennerich ret = adp8860_read(client, reg, ®_val); 15882fd53b7SMichael Hennerich 15982fd53b7SMichael Hennerich if (!ret && (reg_val & bit_mask)) { 16082fd53b7SMichael Hennerich reg_val &= ~bit_mask; 16182fd53b7SMichael Hennerich ret = adp8860_write(client, reg, reg_val); 16282fd53b7SMichael Hennerich } 16382fd53b7SMichael Hennerich 16482fd53b7SMichael Hennerich mutex_unlock(&data->lock); 16582fd53b7SMichael Hennerich return ret; 16682fd53b7SMichael Hennerich } 16782fd53b7SMichael Hennerich 16882fd53b7SMichael Hennerich /* 16982fd53b7SMichael Hennerich * Independent sink / LED 17082fd53b7SMichael Hennerich */ 17182fd53b7SMichael Hennerich #if defined(ADP8860_USE_LEDS) 17282fd53b7SMichael Hennerich static void adp8860_led_work(struct work_struct *work) 17382fd53b7SMichael Hennerich { 17482fd53b7SMichael Hennerich struct adp8860_led *led = container_of(work, struct adp8860_led, work); 17582fd53b7SMichael Hennerich adp8860_write(led->client, ADP8860_ISC1 - led->id + 1, 17682fd53b7SMichael Hennerich led->new_brightness >> 1); 17782fd53b7SMichael Hennerich } 17882fd53b7SMichael Hennerich 17982fd53b7SMichael Hennerich static void adp8860_led_set(struct led_classdev *led_cdev, 18082fd53b7SMichael Hennerich enum led_brightness value) 18182fd53b7SMichael Hennerich { 18282fd53b7SMichael Hennerich struct adp8860_led *led; 18382fd53b7SMichael Hennerich 18482fd53b7SMichael Hennerich led = container_of(led_cdev, struct adp8860_led, cdev); 18582fd53b7SMichael Hennerich led->new_brightness = value; 18682fd53b7SMichael Hennerich schedule_work(&led->work); 18782fd53b7SMichael Hennerich } 18882fd53b7SMichael Hennerich 18982fd53b7SMichael Hennerich static int adp8860_led_setup(struct adp8860_led *led) 19082fd53b7SMichael Hennerich { 19182fd53b7SMichael Hennerich struct i2c_client *client = led->client; 19282fd53b7SMichael Hennerich int ret = 0; 19382fd53b7SMichael Hennerich 19482fd53b7SMichael Hennerich ret = adp8860_write(client, ADP8860_ISC1 - led->id + 1, 0); 19582fd53b7SMichael Hennerich ret |= adp8860_set_bits(client, ADP8860_ISCC, 1 << (led->id - 1)); 19682fd53b7SMichael Hennerich 19782fd53b7SMichael Hennerich if (led->id > 4) 19882fd53b7SMichael Hennerich ret |= adp8860_set_bits(client, ADP8860_ISCT1, 19982fd53b7SMichael Hennerich (led->flags & 0x3) << ((led->id - 5) * 2)); 20082fd53b7SMichael Hennerich else 20182fd53b7SMichael Hennerich ret |= adp8860_set_bits(client, ADP8860_ISCT2, 20282fd53b7SMichael Hennerich (led->flags & 0x3) << ((led->id - 1) * 2)); 20382fd53b7SMichael Hennerich 20482fd53b7SMichael Hennerich return ret; 20582fd53b7SMichael Hennerich } 20682fd53b7SMichael Hennerich 20782fd53b7SMichael Hennerich static int __devinit adp8860_led_probe(struct i2c_client *client) 20882fd53b7SMichael Hennerich { 20982fd53b7SMichael Hennerich struct adp8860_backlight_platform_data *pdata = 21082fd53b7SMichael Hennerich client->dev.platform_data; 21182fd53b7SMichael Hennerich struct adp8860_bl *data = i2c_get_clientdata(client); 21282fd53b7SMichael Hennerich struct adp8860_led *led, *led_dat; 21382fd53b7SMichael Hennerich struct led_info *cur_led; 21482fd53b7SMichael Hennerich int ret, i; 21582fd53b7SMichael Hennerich 21682fd53b7SMichael Hennerich led = kzalloc(sizeof(*led) * pdata->num_leds, GFP_KERNEL); 21782fd53b7SMichael Hennerich if (led == NULL) { 21882fd53b7SMichael Hennerich dev_err(&client->dev, "failed to alloc memory\n"); 21982fd53b7SMichael Hennerich return -ENOMEM; 22082fd53b7SMichael Hennerich } 22182fd53b7SMichael Hennerich 22282fd53b7SMichael Hennerich ret = adp8860_write(client, ADP8860_ISCFR, pdata->led_fade_law); 22382fd53b7SMichael Hennerich ret = adp8860_write(client, ADP8860_ISCT1, 22482fd53b7SMichael Hennerich (pdata->led_on_time & 0x3) << 6); 22582fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_ISCF, 22682fd53b7SMichael Hennerich FADE_VAL(pdata->led_fade_in, pdata->led_fade_out)); 22782fd53b7SMichael Hennerich 22882fd53b7SMichael Hennerich if (ret) { 22982fd53b7SMichael Hennerich dev_err(&client->dev, "failed to write\n"); 23082fd53b7SMichael Hennerich goto err_free; 23182fd53b7SMichael Hennerich } 23282fd53b7SMichael Hennerich 23382fd53b7SMichael Hennerich for (i = 0; i < pdata->num_leds; ++i) { 23482fd53b7SMichael Hennerich cur_led = &pdata->leds[i]; 23582fd53b7SMichael Hennerich led_dat = &led[i]; 23682fd53b7SMichael Hennerich 23782fd53b7SMichael Hennerich led_dat->id = cur_led->flags & ADP8860_FLAG_LED_MASK; 23882fd53b7SMichael Hennerich 23982fd53b7SMichael Hennerich if (led_dat->id > 7 || led_dat->id < 1) { 24082fd53b7SMichael Hennerich dev_err(&client->dev, "Invalid LED ID %d\n", 24182fd53b7SMichael Hennerich led_dat->id); 24282fd53b7SMichael Hennerich goto err; 24382fd53b7SMichael Hennerich } 24482fd53b7SMichael Hennerich 24582fd53b7SMichael Hennerich if (pdata->bl_led_assign & (1 << (led_dat->id - 1))) { 24682fd53b7SMichael Hennerich dev_err(&client->dev, "LED %d used by Backlight\n", 24782fd53b7SMichael Hennerich led_dat->id); 24882fd53b7SMichael Hennerich goto err; 24982fd53b7SMichael Hennerich } 25082fd53b7SMichael Hennerich 25182fd53b7SMichael Hennerich led_dat->cdev.name = cur_led->name; 25282fd53b7SMichael Hennerich led_dat->cdev.default_trigger = cur_led->default_trigger; 25382fd53b7SMichael Hennerich led_dat->cdev.brightness_set = adp8860_led_set; 25482fd53b7SMichael Hennerich led_dat->cdev.brightness = LED_OFF; 25582fd53b7SMichael Hennerich led_dat->flags = cur_led->flags >> FLAG_OFFT_SHIFT; 25682fd53b7SMichael Hennerich led_dat->client = client; 25782fd53b7SMichael Hennerich led_dat->new_brightness = LED_OFF; 25882fd53b7SMichael Hennerich INIT_WORK(&led_dat->work, adp8860_led_work); 25982fd53b7SMichael Hennerich 26082fd53b7SMichael Hennerich ret = led_classdev_register(&client->dev, &led_dat->cdev); 26182fd53b7SMichael Hennerich if (ret) { 26282fd53b7SMichael Hennerich dev_err(&client->dev, "failed to register LED %d\n", 26382fd53b7SMichael Hennerich led_dat->id); 26482fd53b7SMichael Hennerich goto err; 26582fd53b7SMichael Hennerich } 26682fd53b7SMichael Hennerich 26782fd53b7SMichael Hennerich ret = adp8860_led_setup(led_dat); 26882fd53b7SMichael Hennerich if (ret) { 26982fd53b7SMichael Hennerich dev_err(&client->dev, "failed to write\n"); 27082fd53b7SMichael Hennerich i++; 27182fd53b7SMichael Hennerich goto err; 27282fd53b7SMichael Hennerich } 27382fd53b7SMichael Hennerich } 27482fd53b7SMichael Hennerich 27582fd53b7SMichael Hennerich data->led = led; 27682fd53b7SMichael Hennerich 27782fd53b7SMichael Hennerich return 0; 27882fd53b7SMichael Hennerich 27982fd53b7SMichael Hennerich err: 28082fd53b7SMichael Hennerich for (i = i - 1; i >= 0; --i) { 28182fd53b7SMichael Hennerich led_classdev_unregister(&led[i].cdev); 28282fd53b7SMichael Hennerich cancel_work_sync(&led[i].work); 28382fd53b7SMichael Hennerich } 28482fd53b7SMichael Hennerich 28582fd53b7SMichael Hennerich err_free: 28682fd53b7SMichael Hennerich kfree(led); 28782fd53b7SMichael Hennerich 28882fd53b7SMichael Hennerich return ret; 28982fd53b7SMichael Hennerich } 29082fd53b7SMichael Hennerich 29182fd53b7SMichael Hennerich static int __devexit adp8860_led_remove(struct i2c_client *client) 29282fd53b7SMichael Hennerich { 29382fd53b7SMichael Hennerich struct adp8860_backlight_platform_data *pdata = 29482fd53b7SMichael Hennerich client->dev.platform_data; 29582fd53b7SMichael Hennerich struct adp8860_bl *data = i2c_get_clientdata(client); 29682fd53b7SMichael Hennerich int i; 29782fd53b7SMichael Hennerich 29882fd53b7SMichael Hennerich for (i = 0; i < pdata->num_leds; i++) { 29982fd53b7SMichael Hennerich led_classdev_unregister(&data->led[i].cdev); 30082fd53b7SMichael Hennerich cancel_work_sync(&data->led[i].work); 30182fd53b7SMichael Hennerich } 30282fd53b7SMichael Hennerich 30382fd53b7SMichael Hennerich kfree(data->led); 30482fd53b7SMichael Hennerich return 0; 30582fd53b7SMichael Hennerich } 30682fd53b7SMichael Hennerich #else 30782fd53b7SMichael Hennerich static int __devinit adp8860_led_probe(struct i2c_client *client) 30882fd53b7SMichael Hennerich { 30982fd53b7SMichael Hennerich return 0; 31082fd53b7SMichael Hennerich } 31182fd53b7SMichael Hennerich 31282fd53b7SMichael Hennerich static int __devexit adp8860_led_remove(struct i2c_client *client) 31382fd53b7SMichael Hennerich { 31482fd53b7SMichael Hennerich return 0; 31582fd53b7SMichael Hennerich } 31682fd53b7SMichael Hennerich #endif 31782fd53b7SMichael Hennerich 31882fd53b7SMichael Hennerich static int adp8860_bl_set(struct backlight_device *bl, int brightness) 31982fd53b7SMichael Hennerich { 32082fd53b7SMichael Hennerich struct adp8860_bl *data = bl_get_data(bl); 32182fd53b7SMichael Hennerich struct i2c_client *client = data->client; 32282fd53b7SMichael Hennerich int ret = 0; 32382fd53b7SMichael Hennerich 32482fd53b7SMichael Hennerich if (data->pdata->en_ambl_sens) { 32582fd53b7SMichael Hennerich if ((brightness > 0) && (brightness < ADP8860_MAX_BRIGHTNESS)) { 32682fd53b7SMichael Hennerich /* Disable Ambient Light auto adjust */ 32782fd53b7SMichael Hennerich ret |= adp8860_clr_bits(client, ADP8860_MDCR, 32882fd53b7SMichael Hennerich CMP_AUTOEN); 32982fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_BLMX1, brightness); 33082fd53b7SMichael Hennerich } else { 33182fd53b7SMichael Hennerich /* 33282fd53b7SMichael Hennerich * MAX_BRIGHTNESS -> Enable Ambient Light auto adjust 33382fd53b7SMichael Hennerich * restore daylight l1 sysfs brightness 33482fd53b7SMichael Hennerich */ 33582fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_BLMX1, 33682fd53b7SMichael Hennerich data->cached_daylight_max); 33782fd53b7SMichael Hennerich ret |= adp8860_set_bits(client, ADP8860_MDCR, 33882fd53b7SMichael Hennerich CMP_AUTOEN); 33982fd53b7SMichael Hennerich } 34082fd53b7SMichael Hennerich } else 34182fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_BLMX1, brightness); 34282fd53b7SMichael Hennerich 34382fd53b7SMichael Hennerich if (data->current_brightness && brightness == 0) 34482fd53b7SMichael Hennerich ret |= adp8860_set_bits(client, 34582fd53b7SMichael Hennerich ADP8860_MDCR, DIM_EN); 34682fd53b7SMichael Hennerich else if (data->current_brightness == 0 && brightness) 34782fd53b7SMichael Hennerich ret |= adp8860_clr_bits(client, 34882fd53b7SMichael Hennerich ADP8860_MDCR, DIM_EN); 34982fd53b7SMichael Hennerich 35082fd53b7SMichael Hennerich if (!ret) 35182fd53b7SMichael Hennerich data->current_brightness = brightness; 35282fd53b7SMichael Hennerich 35382fd53b7SMichael Hennerich return ret; 35482fd53b7SMichael Hennerich } 35582fd53b7SMichael Hennerich 35682fd53b7SMichael Hennerich static int adp8860_bl_update_status(struct backlight_device *bl) 35782fd53b7SMichael Hennerich { 35882fd53b7SMichael Hennerich int brightness = bl->props.brightness; 35982fd53b7SMichael Hennerich if (bl->props.power != FB_BLANK_UNBLANK) 36082fd53b7SMichael Hennerich brightness = 0; 36182fd53b7SMichael Hennerich 36282fd53b7SMichael Hennerich if (bl->props.fb_blank != FB_BLANK_UNBLANK) 36382fd53b7SMichael Hennerich brightness = 0; 36482fd53b7SMichael Hennerich 36582fd53b7SMichael Hennerich return adp8860_bl_set(bl, brightness); 36682fd53b7SMichael Hennerich } 36782fd53b7SMichael Hennerich 36882fd53b7SMichael Hennerich static int adp8860_bl_get_brightness(struct backlight_device *bl) 36982fd53b7SMichael Hennerich { 37082fd53b7SMichael Hennerich struct adp8860_bl *data = bl_get_data(bl); 37182fd53b7SMichael Hennerich 37282fd53b7SMichael Hennerich return data->current_brightness; 37382fd53b7SMichael Hennerich } 37482fd53b7SMichael Hennerich 37582fd53b7SMichael Hennerich static const struct backlight_ops adp8860_bl_ops = { 37682fd53b7SMichael Hennerich .update_status = adp8860_bl_update_status, 37782fd53b7SMichael Hennerich .get_brightness = adp8860_bl_get_brightness, 37882fd53b7SMichael Hennerich }; 37982fd53b7SMichael Hennerich 38082fd53b7SMichael Hennerich static int adp8860_bl_setup(struct backlight_device *bl) 38182fd53b7SMichael Hennerich { 38282fd53b7SMichael Hennerich struct adp8860_bl *data = bl_get_data(bl); 38382fd53b7SMichael Hennerich struct i2c_client *client = data->client; 38482fd53b7SMichael Hennerich struct adp8860_backlight_platform_data *pdata = data->pdata; 38582fd53b7SMichael Hennerich int ret = 0; 38682fd53b7SMichael Hennerich 38782fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_BLSEN, ~pdata->bl_led_assign); 38882fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_BLMX1, pdata->l1_daylight_max); 38982fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_BLDM1, pdata->l1_daylight_dim); 39082fd53b7SMichael Hennerich 39182fd53b7SMichael Hennerich if (pdata->en_ambl_sens) { 39282fd53b7SMichael Hennerich data->cached_daylight_max = pdata->l1_daylight_max; 39382fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_BLMX2, 39482fd53b7SMichael Hennerich pdata->l2_office_max); 39582fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_BLDM2, 39682fd53b7SMichael Hennerich pdata->l2_office_dim); 39782fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_BLMX3, 39882fd53b7SMichael Hennerich pdata->l3_dark_max); 39982fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_BLDM3, 40082fd53b7SMichael Hennerich pdata->l3_dark_dim); 40182fd53b7SMichael Hennerich 40282fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_L2_TRP, pdata->l2_trip); 40382fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_L2_HYS, pdata->l2_hyst); 40482fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_L3_TRP, pdata->l3_trip); 40582fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_L3_HYS, pdata->l3_hyst); 40682fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_CCFG, L2_EN | L3_EN | 40782fd53b7SMichael Hennerich ALS_CCFG_VAL(pdata->abml_filt)); 40882fd53b7SMichael Hennerich } 40982fd53b7SMichael Hennerich 41082fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_CFGR, 41182fd53b7SMichael Hennerich BL_CFGR_VAL(pdata->bl_fade_law, 0)); 41282fd53b7SMichael Hennerich 41382fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_BLFR, FADE_VAL(pdata->bl_fade_in, 41482fd53b7SMichael Hennerich pdata->bl_fade_out)); 41582fd53b7SMichael Hennerich 41682fd53b7SMichael Hennerich ret |= adp8860_set_bits(client, ADP8860_MDCR, BLEN | DIM_EN | NSTBY); 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 44482fd53b7SMichael Hennerich ret = strict_strtoul(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); 49482fd53b7SMichael Hennerich 49582fd53b7SMichael Hennerich strict_strtoul(buf, 10, &data->cached_daylight_max); 49682fd53b7SMichael Hennerich return adp8860_store(dev, buf, count, ADP8860_BLMX1); 49782fd53b7SMichael Hennerich } 49882fd53b7SMichael Hennerich static DEVICE_ATTR(l1_daylight_max, 0664, adp8860_bl_l1_daylight_max_show, 49982fd53b7SMichael Hennerich adp8860_bl_l1_daylight_max_store); 50082fd53b7SMichael Hennerich 50182fd53b7SMichael Hennerich static ssize_t adp8860_bl_l3_dark_dim_show(struct device *dev, 50282fd53b7SMichael Hennerich struct device_attribute *attr, char *buf) 50382fd53b7SMichael Hennerich { 50482fd53b7SMichael Hennerich return adp8860_show(dev, buf, ADP8860_BLDM3); 50582fd53b7SMichael Hennerich } 50682fd53b7SMichael Hennerich 50782fd53b7SMichael Hennerich static ssize_t adp8860_bl_l3_dark_dim_store(struct device *dev, 50882fd53b7SMichael Hennerich struct device_attribute *attr, 50982fd53b7SMichael Hennerich const char *buf, size_t count) 51082fd53b7SMichael Hennerich { 51182fd53b7SMichael Hennerich return adp8860_store(dev, buf, count, ADP8860_BLDM3); 51282fd53b7SMichael Hennerich } 51382fd53b7SMichael Hennerich static DEVICE_ATTR(l3_dark_dim, 0664, adp8860_bl_l3_dark_dim_show, 51482fd53b7SMichael Hennerich adp8860_bl_l3_dark_dim_store); 51582fd53b7SMichael Hennerich 51682fd53b7SMichael Hennerich static ssize_t adp8860_bl_l2_office_dim_show(struct device *dev, 51782fd53b7SMichael Hennerich struct device_attribute *attr, char *buf) 51882fd53b7SMichael Hennerich { 51982fd53b7SMichael Hennerich return adp8860_show(dev, buf, ADP8860_BLDM2); 52082fd53b7SMichael Hennerich } 52182fd53b7SMichael Hennerich 52282fd53b7SMichael Hennerich static ssize_t adp8860_bl_l2_office_dim_store(struct device *dev, 52382fd53b7SMichael Hennerich struct device_attribute *attr, 52482fd53b7SMichael Hennerich const char *buf, size_t count) 52582fd53b7SMichael Hennerich { 52682fd53b7SMichael Hennerich return adp8860_store(dev, buf, count, ADP8860_BLDM2); 52782fd53b7SMichael Hennerich } 52882fd53b7SMichael Hennerich static DEVICE_ATTR(l2_office_dim, 0664, adp8860_bl_l2_office_dim_show, 52982fd53b7SMichael Hennerich adp8860_bl_l2_office_dim_store); 53082fd53b7SMichael Hennerich 53182fd53b7SMichael Hennerich static ssize_t adp8860_bl_l1_daylight_dim_show(struct device *dev, 53282fd53b7SMichael Hennerich struct device_attribute *attr, char *buf) 53382fd53b7SMichael Hennerich { 53482fd53b7SMichael Hennerich return adp8860_show(dev, buf, ADP8860_BLDM1); 53582fd53b7SMichael Hennerich } 53682fd53b7SMichael Hennerich 53782fd53b7SMichael Hennerich static ssize_t adp8860_bl_l1_daylight_dim_store(struct device *dev, 53882fd53b7SMichael Hennerich struct device_attribute *attr, 53982fd53b7SMichael Hennerich const char *buf, size_t count) 54082fd53b7SMichael Hennerich { 54182fd53b7SMichael Hennerich return adp8860_store(dev, buf, count, ADP8860_BLDM1); 54282fd53b7SMichael Hennerich } 54382fd53b7SMichael Hennerich static DEVICE_ATTR(l1_daylight_dim, 0664, adp8860_bl_l1_daylight_dim_show, 54482fd53b7SMichael Hennerich adp8860_bl_l1_daylight_dim_store); 54582fd53b7SMichael Hennerich 54682fd53b7SMichael Hennerich #ifdef ADP8860_EXT_FEATURES 54782fd53b7SMichael Hennerich static ssize_t adp8860_bl_ambient_light_level_show(struct device *dev, 54882fd53b7SMichael Hennerich struct device_attribute *attr, char *buf) 54982fd53b7SMichael Hennerich { 55082fd53b7SMichael Hennerich struct adp8860_bl *data = dev_get_drvdata(dev); 55182fd53b7SMichael Hennerich int error; 55282fd53b7SMichael Hennerich uint8_t reg_val; 55382fd53b7SMichael Hennerich uint16_t ret_val; 55482fd53b7SMichael Hennerich 55582fd53b7SMichael Hennerich mutex_lock(&data->lock); 55682fd53b7SMichael Hennerich error = adp8860_read(data->client, ADP8860_PH1LEVL, ®_val); 55782fd53b7SMichael Hennerich ret_val = reg_val; 55882fd53b7SMichael Hennerich error |= adp8860_read(data->client, ADP8860_PH1LEVH, ®_val); 55982fd53b7SMichael Hennerich mutex_unlock(&data->lock); 56082fd53b7SMichael Hennerich 56182fd53b7SMichael Hennerich if (error < 0) 56282fd53b7SMichael Hennerich return error; 56382fd53b7SMichael Hennerich 56482fd53b7SMichael Hennerich /* Return 13-bit conversion value for the first light sensor */ 56582fd53b7SMichael Hennerich ret_val += (reg_val & 0x1F) << 8; 56682fd53b7SMichael Hennerich 56782fd53b7SMichael Hennerich return sprintf(buf, "%u\n", ret_val); 56882fd53b7SMichael Hennerich } 56982fd53b7SMichael Hennerich static DEVICE_ATTR(ambient_light_level, 0444, 57082fd53b7SMichael Hennerich adp8860_bl_ambient_light_level_show, NULL); 57182fd53b7SMichael Hennerich 57282fd53b7SMichael Hennerich static ssize_t adp8860_bl_ambient_light_zone_show(struct device *dev, 57382fd53b7SMichael Hennerich struct device_attribute *attr, char *buf) 57482fd53b7SMichael Hennerich { 57582fd53b7SMichael Hennerich struct adp8860_bl *data = dev_get_drvdata(dev); 57682fd53b7SMichael Hennerich int error; 57782fd53b7SMichael Hennerich uint8_t reg_val; 57882fd53b7SMichael Hennerich 57982fd53b7SMichael Hennerich mutex_lock(&data->lock); 58082fd53b7SMichael Hennerich error = adp8860_read(data->client, ADP8860_CFGR, ®_val); 58182fd53b7SMichael Hennerich mutex_unlock(&data->lock); 58282fd53b7SMichael Hennerich 58382fd53b7SMichael Hennerich if (error < 0) 58482fd53b7SMichael Hennerich return error; 58582fd53b7SMichael Hennerich 58682fd53b7SMichael Hennerich return sprintf(buf, "%u\n", 58782fd53b7SMichael Hennerich ((reg_val >> CFGR_BLV_SHIFT) & CFGR_BLV_MASK) + 1); 58882fd53b7SMichael Hennerich } 58982fd53b7SMichael Hennerich 59082fd53b7SMichael Hennerich static ssize_t adp8860_bl_ambient_light_zone_store(struct device *dev, 59182fd53b7SMichael Hennerich struct device_attribute *attr, 59282fd53b7SMichael Hennerich const char *buf, size_t count) 59382fd53b7SMichael Hennerich { 59482fd53b7SMichael Hennerich struct adp8860_bl *data = dev_get_drvdata(dev); 59582fd53b7SMichael Hennerich unsigned long val; 59682fd53b7SMichael Hennerich uint8_t reg_val; 59782fd53b7SMichael Hennerich int ret; 59882fd53b7SMichael Hennerich 59982fd53b7SMichael Hennerich ret = strict_strtoul(buf, 10, &val); 60082fd53b7SMichael Hennerich if (ret) 60182fd53b7SMichael Hennerich return ret; 60282fd53b7SMichael Hennerich 60382fd53b7SMichael Hennerich if (val == 0) { 60482fd53b7SMichael Hennerich /* Enable automatic ambient light sensing */ 60582fd53b7SMichael Hennerich adp8860_set_bits(data->client, ADP8860_MDCR, CMP_AUTOEN); 60682fd53b7SMichael Hennerich } else if ((val > 0) && (val < 6)) { 60782fd53b7SMichael Hennerich /* Disable automatic ambient light sensing */ 60882fd53b7SMichael Hennerich adp8860_clr_bits(data->client, ADP8860_MDCR, CMP_AUTOEN); 60982fd53b7SMichael Hennerich 61082fd53b7SMichael Hennerich /* Set user supplied ambient light zone */ 61182fd53b7SMichael Hennerich mutex_lock(&data->lock); 61282fd53b7SMichael Hennerich adp8860_read(data->client, ADP8860_CFGR, ®_val); 61382fd53b7SMichael Hennerich reg_val &= ~(CFGR_BLV_MASK << CFGR_BLV_SHIFT); 61482fd53b7SMichael Hennerich reg_val |= val << CFGR_BLV_SHIFT; 61582fd53b7SMichael Hennerich adp8860_write(data->client, ADP8860_CFGR, reg_val); 61682fd53b7SMichael Hennerich mutex_unlock(&data->lock); 61782fd53b7SMichael Hennerich } 61882fd53b7SMichael Hennerich 61982fd53b7SMichael Hennerich return count; 62082fd53b7SMichael Hennerich } 62182fd53b7SMichael Hennerich static DEVICE_ATTR(ambient_light_zone, 0664, 62282fd53b7SMichael Hennerich adp8860_bl_ambient_light_zone_show, 62382fd53b7SMichael Hennerich adp8860_bl_ambient_light_zone_store); 62482fd53b7SMichael Hennerich #endif 62582fd53b7SMichael Hennerich 62682fd53b7SMichael Hennerich static struct attribute *adp8860_bl_attributes[] = { 62782fd53b7SMichael Hennerich &dev_attr_l3_dark_max.attr, 62882fd53b7SMichael Hennerich &dev_attr_l3_dark_dim.attr, 62982fd53b7SMichael Hennerich &dev_attr_l2_office_max.attr, 63082fd53b7SMichael Hennerich &dev_attr_l2_office_dim.attr, 63182fd53b7SMichael Hennerich &dev_attr_l1_daylight_max.attr, 63282fd53b7SMichael Hennerich &dev_attr_l1_daylight_dim.attr, 63382fd53b7SMichael Hennerich #ifdef ADP8860_EXT_FEATURES 63482fd53b7SMichael Hennerich &dev_attr_ambient_light_level.attr, 63582fd53b7SMichael Hennerich &dev_attr_ambient_light_zone.attr, 63682fd53b7SMichael Hennerich #endif 63782fd53b7SMichael Hennerich NULL 63882fd53b7SMichael Hennerich }; 63982fd53b7SMichael Hennerich 64082fd53b7SMichael Hennerich static const struct attribute_group adp8860_bl_attr_group = { 64182fd53b7SMichael Hennerich .attrs = adp8860_bl_attributes, 64282fd53b7SMichael Hennerich }; 64382fd53b7SMichael Hennerich 64482fd53b7SMichael Hennerich static int __devinit adp8860_probe(struct i2c_client *client, 64582fd53b7SMichael Hennerich const struct i2c_device_id *id) 64682fd53b7SMichael Hennerich { 64782fd53b7SMichael Hennerich struct backlight_device *bl; 64882fd53b7SMichael Hennerich struct adp8860_bl *data; 64982fd53b7SMichael Hennerich struct adp8860_backlight_platform_data *pdata = 65082fd53b7SMichael Hennerich client->dev.platform_data; 651*3f43f8b2SRandy Dunlap struct backlight_properties props; 65282fd53b7SMichael Hennerich uint8_t reg_val; 65382fd53b7SMichael Hennerich int ret; 65482fd53b7SMichael Hennerich 65582fd53b7SMichael Hennerich if (!i2c_check_functionality(client->adapter, 65682fd53b7SMichael Hennerich I2C_FUNC_SMBUS_BYTE_DATA)) { 65782fd53b7SMichael Hennerich dev_err(&client->dev, "SMBUS Byte Data not Supported\n"); 65882fd53b7SMichael Hennerich return -EIO; 65982fd53b7SMichael Hennerich } 66082fd53b7SMichael Hennerich 66182fd53b7SMichael Hennerich if (!pdata) { 66282fd53b7SMichael Hennerich dev_err(&client->dev, "no platform data?\n"); 66382fd53b7SMichael Hennerich return -EINVAL; 66482fd53b7SMichael Hennerich } 66582fd53b7SMichael Hennerich 66682fd53b7SMichael Hennerich ret = adp8860_read(client, ADP8860_MFDVID, ®_val); 66782fd53b7SMichael Hennerich if (ret < 0) 66882fd53b7SMichael Hennerich return -EIO; 66982fd53b7SMichael Hennerich 67082fd53b7SMichael Hennerich if (ADP8860_MANID(reg_val) != ADP8860_MANUFID) { 67182fd53b7SMichael Hennerich dev_err(&client->dev, "failed to probe\n"); 67282fd53b7SMichael Hennerich return -ENODEV; 67382fd53b7SMichael Hennerich } 67482fd53b7SMichael Hennerich 67582fd53b7SMichael Hennerich data = kzalloc(sizeof(*data), GFP_KERNEL); 67682fd53b7SMichael Hennerich if (data == NULL) 67782fd53b7SMichael Hennerich return -ENOMEM; 67882fd53b7SMichael Hennerich 67982fd53b7SMichael Hennerich /* It's confirmed that the DEVID field is actually a REVID */ 68082fd53b7SMichael Hennerich 68182fd53b7SMichael Hennerich data->revid = ADP8860_DEVID(reg_val); 68282fd53b7SMichael Hennerich data->client = client; 68382fd53b7SMichael Hennerich data->pdata = pdata; 68482fd53b7SMichael Hennerich data->id = id->driver_data; 68582fd53b7SMichael Hennerich data->current_brightness = 0; 68682fd53b7SMichael Hennerich i2c_set_clientdata(client, data); 68782fd53b7SMichael Hennerich 688*3f43f8b2SRandy Dunlap memset(&props, 0, sizeof(props)); 689*3f43f8b2SRandy Dunlap props.max_brightness = ADP8860_MAX_BRIGHTNESS; 690*3f43f8b2SRandy Dunlap 69182fd53b7SMichael Hennerich mutex_init(&data->lock); 69282fd53b7SMichael Hennerich 69382fd53b7SMichael Hennerich bl = backlight_device_register(dev_driver_string(&client->dev), 694*3f43f8b2SRandy Dunlap &client->dev, data, &adp8860_bl_ops, &props); 69582fd53b7SMichael Hennerich if (IS_ERR(bl)) { 69682fd53b7SMichael Hennerich dev_err(&client->dev, "failed to register backlight\n"); 69782fd53b7SMichael Hennerich ret = PTR_ERR(bl); 69882fd53b7SMichael Hennerich goto out2; 69982fd53b7SMichael Hennerich } 70082fd53b7SMichael Hennerich 70182fd53b7SMichael Hennerich bl->props.max_brightness = 70282fd53b7SMichael Hennerich bl->props.brightness = ADP8860_MAX_BRIGHTNESS; 70382fd53b7SMichael Hennerich 70482fd53b7SMichael Hennerich data->bl = bl; 70582fd53b7SMichael Hennerich 70682fd53b7SMichael Hennerich if (pdata->en_ambl_sens) 70782fd53b7SMichael Hennerich ret = sysfs_create_group(&bl->dev.kobj, 70882fd53b7SMichael Hennerich &adp8860_bl_attr_group); 70982fd53b7SMichael Hennerich 71082fd53b7SMichael Hennerich if (ret) { 71182fd53b7SMichael Hennerich dev_err(&client->dev, "failed to register sysfs\n"); 71282fd53b7SMichael Hennerich goto out1; 71382fd53b7SMichael Hennerich } 71482fd53b7SMichael Hennerich 71582fd53b7SMichael Hennerich ret = adp8860_bl_setup(bl); 71682fd53b7SMichael Hennerich if (ret) { 71782fd53b7SMichael Hennerich ret = -EIO; 71882fd53b7SMichael Hennerich goto out; 71982fd53b7SMichael Hennerich } 72082fd53b7SMichael Hennerich 72182fd53b7SMichael Hennerich backlight_update_status(bl); 72282fd53b7SMichael Hennerich 72382fd53b7SMichael Hennerich dev_info(&client->dev, "Rev.%d Backlight\n", data->revid); 72482fd53b7SMichael Hennerich 72582fd53b7SMichael Hennerich if (pdata->num_leds) 72682fd53b7SMichael Hennerich adp8860_led_probe(client); 72782fd53b7SMichael Hennerich 72882fd53b7SMichael Hennerich return 0; 72982fd53b7SMichael Hennerich 73082fd53b7SMichael Hennerich out: 73182fd53b7SMichael Hennerich if (data->pdata->en_ambl_sens) 73282fd53b7SMichael Hennerich sysfs_remove_group(&data->bl->dev.kobj, 73382fd53b7SMichael Hennerich &adp8860_bl_attr_group); 73482fd53b7SMichael Hennerich out1: 73582fd53b7SMichael Hennerich backlight_device_unregister(bl); 73682fd53b7SMichael Hennerich out2: 73782fd53b7SMichael Hennerich i2c_set_clientdata(client, NULL); 73882fd53b7SMichael Hennerich kfree(data); 73982fd53b7SMichael Hennerich 74082fd53b7SMichael Hennerich return ret; 74182fd53b7SMichael Hennerich } 74282fd53b7SMichael Hennerich 74382fd53b7SMichael Hennerich static int __devexit adp8860_remove(struct i2c_client *client) 74482fd53b7SMichael Hennerich { 74582fd53b7SMichael Hennerich struct adp8860_bl *data = i2c_get_clientdata(client); 74682fd53b7SMichael Hennerich 74782fd53b7SMichael Hennerich adp8860_clr_bits(client, ADP8860_MDCR, NSTBY); 74882fd53b7SMichael Hennerich 74982fd53b7SMichael Hennerich if (data->led) 75082fd53b7SMichael Hennerich adp8860_led_remove(client); 75182fd53b7SMichael Hennerich 75282fd53b7SMichael Hennerich if (data->pdata->en_ambl_sens) 75382fd53b7SMichael Hennerich sysfs_remove_group(&data->bl->dev.kobj, 75482fd53b7SMichael Hennerich &adp8860_bl_attr_group); 75582fd53b7SMichael Hennerich 75682fd53b7SMichael Hennerich backlight_device_unregister(data->bl); 75782fd53b7SMichael Hennerich i2c_set_clientdata(client, NULL); 75882fd53b7SMichael Hennerich kfree(data); 75982fd53b7SMichael Hennerich 76082fd53b7SMichael Hennerich return 0; 76182fd53b7SMichael Hennerich } 76282fd53b7SMichael Hennerich 76382fd53b7SMichael Hennerich #ifdef CONFIG_PM 76482fd53b7SMichael Hennerich static int adp8860_i2c_suspend(struct i2c_client *client, pm_message_t message) 76582fd53b7SMichael Hennerich { 76682fd53b7SMichael Hennerich adp8860_clr_bits(client, ADP8860_MDCR, NSTBY); 76782fd53b7SMichael Hennerich 76882fd53b7SMichael Hennerich return 0; 76982fd53b7SMichael Hennerich } 77082fd53b7SMichael Hennerich 77182fd53b7SMichael Hennerich static int adp8860_i2c_resume(struct i2c_client *client) 77282fd53b7SMichael Hennerich { 77382fd53b7SMichael Hennerich adp8860_set_bits(client, ADP8860_MDCR, NSTBY); 77482fd53b7SMichael Hennerich 77582fd53b7SMichael Hennerich return 0; 77682fd53b7SMichael Hennerich } 77782fd53b7SMichael Hennerich #else 77882fd53b7SMichael Hennerich #define adp8860_i2c_suspend NULL 77982fd53b7SMichael Hennerich #define adp8860_i2c_resume NULL 78082fd53b7SMichael Hennerich #endif 78182fd53b7SMichael Hennerich 78282fd53b7SMichael Hennerich static const struct i2c_device_id adp8860_id[] = { 78382fd53b7SMichael Hennerich { "adp8860", 0 }, 78482fd53b7SMichael Hennerich { } 78582fd53b7SMichael Hennerich }; 78682fd53b7SMichael Hennerich MODULE_DEVICE_TABLE(i2c, adp8860_id); 78782fd53b7SMichael Hennerich 78882fd53b7SMichael Hennerich static struct i2c_driver adp8860_driver = { 78982fd53b7SMichael Hennerich .driver = { 79082fd53b7SMichael Hennerich .name = KBUILD_MODNAME, 79182fd53b7SMichael Hennerich }, 79282fd53b7SMichael Hennerich .probe = adp8860_probe, 79382fd53b7SMichael Hennerich .remove = __devexit_p(adp8860_remove), 79482fd53b7SMichael Hennerich .suspend = adp8860_i2c_suspend, 79582fd53b7SMichael Hennerich .resume = adp8860_i2c_resume, 79682fd53b7SMichael Hennerich .id_table = adp8860_id, 79782fd53b7SMichael Hennerich }; 79882fd53b7SMichael Hennerich 79982fd53b7SMichael Hennerich static int __init adp8860_init(void) 80082fd53b7SMichael Hennerich { 80182fd53b7SMichael Hennerich return i2c_add_driver(&adp8860_driver); 80282fd53b7SMichael Hennerich } 80382fd53b7SMichael Hennerich module_init(adp8860_init); 80482fd53b7SMichael Hennerich 80582fd53b7SMichael Hennerich static void __exit adp8860_exit(void) 80682fd53b7SMichael Hennerich { 80782fd53b7SMichael Hennerich i2c_del_driver(&adp8860_driver); 80882fd53b7SMichael Hennerich } 80982fd53b7SMichael Hennerich module_exit(adp8860_exit); 81082fd53b7SMichael Hennerich 81182fd53b7SMichael Hennerich MODULE_LICENSE("GPL v2"); 81282fd53b7SMichael Hennerich MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); 81382fd53b7SMichael Hennerich MODULE_DESCRIPTION("ADP8860 Backlight driver"); 81482fd53b7SMichael Hennerich MODULE_ALIAS("i2c:adp8860-backlight"); 815