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); 18482fd53b7SMichael Hennerich adp8860_write(led->client, ADP8860_ISC1 - led->id + 1, 18582fd53b7SMichael Hennerich led->new_brightness >> 1); 18682fd53b7SMichael Hennerich } 18782fd53b7SMichael Hennerich 18882fd53b7SMichael Hennerich static void adp8860_led_set(struct led_classdev *led_cdev, 18982fd53b7SMichael Hennerich enum led_brightness value) 19082fd53b7SMichael Hennerich { 19182fd53b7SMichael Hennerich struct adp8860_led *led; 19282fd53b7SMichael Hennerich 19382fd53b7SMichael Hennerich led = container_of(led_cdev, struct adp8860_led, cdev); 19482fd53b7SMichael Hennerich led->new_brightness = value; 19582fd53b7SMichael Hennerich schedule_work(&led->work); 19682fd53b7SMichael Hennerich } 19782fd53b7SMichael Hennerich 19882fd53b7SMichael Hennerich static int adp8860_led_setup(struct adp8860_led *led) 19982fd53b7SMichael Hennerich { 20082fd53b7SMichael Hennerich struct i2c_client *client = led->client; 20182fd53b7SMichael Hennerich int ret = 0; 20282fd53b7SMichael Hennerich 20382fd53b7SMichael Hennerich ret = adp8860_write(client, ADP8860_ISC1 - led->id + 1, 0); 20482fd53b7SMichael Hennerich ret |= adp8860_set_bits(client, ADP8860_ISCC, 1 << (led->id - 1)); 20582fd53b7SMichael Hennerich 20682fd53b7SMichael Hennerich if (led->id > 4) 20782fd53b7SMichael Hennerich ret |= adp8860_set_bits(client, ADP8860_ISCT1, 20882fd53b7SMichael Hennerich (led->flags & 0x3) << ((led->id - 5) * 2)); 20982fd53b7SMichael Hennerich else 21082fd53b7SMichael Hennerich ret |= adp8860_set_bits(client, ADP8860_ISCT2, 21182fd53b7SMichael Hennerich (led->flags & 0x3) << ((led->id - 1) * 2)); 21282fd53b7SMichael Hennerich 21382fd53b7SMichael Hennerich return ret; 21482fd53b7SMichael Hennerich } 21582fd53b7SMichael Hennerich 2161b9e450dSBill Pemberton static int adp8860_led_probe(struct i2c_client *client) 21782fd53b7SMichael Hennerich { 21882fd53b7SMichael Hennerich struct adp8860_backlight_platform_data *pdata = 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 22558875ea9SJingoo Han led = devm_kzalloc(&client->dev, sizeof(*led) * pdata->num_leds, 22658875ea9SJingoo Han GFP_KERNEL); 22782fd53b7SMichael Hennerich if (led == NULL) { 22882fd53b7SMichael Hennerich dev_err(&client->dev, "failed to alloc memory\n"); 22982fd53b7SMichael Hennerich return -ENOMEM; 23082fd53b7SMichael Hennerich } 23182fd53b7SMichael Hennerich 23282fd53b7SMichael Hennerich ret = adp8860_write(client, ADP8860_ISCFR, pdata->led_fade_law); 23382fd53b7SMichael Hennerich ret = adp8860_write(client, ADP8860_ISCT1, 23482fd53b7SMichael Hennerich (pdata->led_on_time & 0x3) << 6); 23582fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_ISCF, 23682fd53b7SMichael Hennerich FADE_VAL(pdata->led_fade_in, pdata->led_fade_out)); 23782fd53b7SMichael Hennerich 23882fd53b7SMichael Hennerich if (ret) { 23982fd53b7SMichael Hennerich dev_err(&client->dev, "failed to write\n"); 24058875ea9SJingoo Han return ret; 24182fd53b7SMichael Hennerich } 24282fd53b7SMichael Hennerich 24382fd53b7SMichael Hennerich for (i = 0; i < pdata->num_leds; ++i) { 24482fd53b7SMichael Hennerich cur_led = &pdata->leds[i]; 24582fd53b7SMichael Hennerich led_dat = &led[i]; 24682fd53b7SMichael Hennerich 24782fd53b7SMichael Hennerich led_dat->id = cur_led->flags & ADP8860_FLAG_LED_MASK; 24882fd53b7SMichael Hennerich 24982fd53b7SMichael Hennerich if (led_dat->id > 7 || led_dat->id < 1) { 25082fd53b7SMichael Hennerich dev_err(&client->dev, "Invalid LED ID %d\n", 25182fd53b7SMichael Hennerich led_dat->id); 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); 25882fd53b7SMichael Hennerich goto err; 25982fd53b7SMichael Hennerich } 26082fd53b7SMichael Hennerich 26182fd53b7SMichael Hennerich led_dat->cdev.name = cur_led->name; 26282fd53b7SMichael Hennerich led_dat->cdev.default_trigger = cur_led->default_trigger; 26382fd53b7SMichael Hennerich led_dat->cdev.brightness_set = adp8860_led_set; 26482fd53b7SMichael Hennerich led_dat->cdev.brightness = LED_OFF; 26582fd53b7SMichael Hennerich led_dat->flags = cur_led->flags >> FLAG_OFFT_SHIFT; 26682fd53b7SMichael Hennerich led_dat->client = client; 26782fd53b7SMichael Hennerich led_dat->new_brightness = LED_OFF; 26882fd53b7SMichael Hennerich INIT_WORK(&led_dat->work, adp8860_led_work); 26982fd53b7SMichael Hennerich 27082fd53b7SMichael Hennerich ret = led_classdev_register(&client->dev, &led_dat->cdev); 27182fd53b7SMichael Hennerich if (ret) { 27282fd53b7SMichael Hennerich dev_err(&client->dev, "failed to register LED %d\n", 27382fd53b7SMichael Hennerich led_dat->id); 27482fd53b7SMichael Hennerich goto err; 27582fd53b7SMichael Hennerich } 27682fd53b7SMichael Hennerich 27782fd53b7SMichael Hennerich ret = adp8860_led_setup(led_dat); 27882fd53b7SMichael Hennerich if (ret) { 27982fd53b7SMichael Hennerich dev_err(&client->dev, "failed to write\n"); 28082fd53b7SMichael Hennerich i++; 28182fd53b7SMichael Hennerich goto err; 28282fd53b7SMichael Hennerich } 28382fd53b7SMichael Hennerich } 28482fd53b7SMichael Hennerich 28582fd53b7SMichael Hennerich data->led = led; 28682fd53b7SMichael Hennerich 28782fd53b7SMichael Hennerich return 0; 28882fd53b7SMichael Hennerich 28982fd53b7SMichael Hennerich err: 29082fd53b7SMichael Hennerich for (i = i - 1; i >= 0; --i) { 29182fd53b7SMichael Hennerich led_classdev_unregister(&led[i].cdev); 29282fd53b7SMichael Hennerich cancel_work_sync(&led[i].work); 29382fd53b7SMichael Hennerich } 29482fd53b7SMichael Hennerich 29582fd53b7SMichael Hennerich return ret; 29682fd53b7SMichael Hennerich } 29782fd53b7SMichael Hennerich 2987e4b9d0bSBill Pemberton static int adp8860_led_remove(struct i2c_client *client) 29982fd53b7SMichael Hennerich { 30082fd53b7SMichael Hennerich struct adp8860_backlight_platform_data *pdata = 30182fd53b7SMichael Hennerich client->dev.platform_data; 30282fd53b7SMichael Hennerich struct adp8860_bl *data = i2c_get_clientdata(client); 30382fd53b7SMichael Hennerich int i; 30482fd53b7SMichael Hennerich 30582fd53b7SMichael Hennerich for (i = 0; i < pdata->num_leds; i++) { 30682fd53b7SMichael Hennerich led_classdev_unregister(&data->led[i].cdev); 30782fd53b7SMichael Hennerich cancel_work_sync(&data->led[i].work); 30882fd53b7SMichael Hennerich } 30982fd53b7SMichael Hennerich 31082fd53b7SMichael Hennerich return 0; 31182fd53b7SMichael Hennerich } 31282fd53b7SMichael Hennerich #else 3131b9e450dSBill Pemberton static int adp8860_led_probe(struct i2c_client *client) 31482fd53b7SMichael Hennerich { 31582fd53b7SMichael Hennerich return 0; 31682fd53b7SMichael Hennerich } 31782fd53b7SMichael Hennerich 3187e4b9d0bSBill Pemberton static int adp8860_led_remove(struct i2c_client *client) 31982fd53b7SMichael Hennerich { 32082fd53b7SMichael Hennerich return 0; 32182fd53b7SMichael Hennerich } 32282fd53b7SMichael Hennerich #endif 32382fd53b7SMichael Hennerich 32482fd53b7SMichael Hennerich static int adp8860_bl_set(struct backlight_device *bl, int brightness) 32582fd53b7SMichael Hennerich { 32682fd53b7SMichael Hennerich struct adp8860_bl *data = bl_get_data(bl); 32782fd53b7SMichael Hennerich struct i2c_client *client = data->client; 32882fd53b7SMichael Hennerich int ret = 0; 32982fd53b7SMichael Hennerich 330c7c06d8aSMichael Hennerich if (data->en_ambl_sens) { 33182fd53b7SMichael Hennerich if ((brightness > 0) && (brightness < ADP8860_MAX_BRIGHTNESS)) { 33282fd53b7SMichael Hennerich /* Disable Ambient Light auto adjust */ 33382fd53b7SMichael Hennerich ret |= adp8860_clr_bits(client, ADP8860_MDCR, 33482fd53b7SMichael Hennerich CMP_AUTOEN); 33582fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_BLMX1, brightness); 33682fd53b7SMichael Hennerich } else { 33782fd53b7SMichael Hennerich /* 33882fd53b7SMichael Hennerich * MAX_BRIGHTNESS -> Enable Ambient Light auto adjust 33982fd53b7SMichael Hennerich * restore daylight l1 sysfs brightness 34082fd53b7SMichael Hennerich */ 34182fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_BLMX1, 34282fd53b7SMichael Hennerich data->cached_daylight_max); 34382fd53b7SMichael Hennerich ret |= adp8860_set_bits(client, ADP8860_MDCR, 34482fd53b7SMichael Hennerich CMP_AUTOEN); 34582fd53b7SMichael Hennerich } 34682fd53b7SMichael Hennerich } else 34782fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_BLMX1, brightness); 34882fd53b7SMichael Hennerich 34982fd53b7SMichael Hennerich if (data->current_brightness && brightness == 0) 35082fd53b7SMichael Hennerich ret |= adp8860_set_bits(client, 35182fd53b7SMichael Hennerich ADP8860_MDCR, DIM_EN); 35282fd53b7SMichael Hennerich else if (data->current_brightness == 0 && brightness) 35382fd53b7SMichael Hennerich ret |= adp8860_clr_bits(client, 35482fd53b7SMichael Hennerich ADP8860_MDCR, DIM_EN); 35582fd53b7SMichael Hennerich 35682fd53b7SMichael Hennerich if (!ret) 35782fd53b7SMichael Hennerich data->current_brightness = brightness; 35882fd53b7SMichael Hennerich 35982fd53b7SMichael Hennerich return ret; 36082fd53b7SMichael Hennerich } 36182fd53b7SMichael Hennerich 36282fd53b7SMichael Hennerich static int adp8860_bl_update_status(struct backlight_device *bl) 36382fd53b7SMichael Hennerich { 36482fd53b7SMichael Hennerich int brightness = bl->props.brightness; 36582fd53b7SMichael Hennerich if (bl->props.power != FB_BLANK_UNBLANK) 36682fd53b7SMichael Hennerich brightness = 0; 36782fd53b7SMichael Hennerich 36882fd53b7SMichael Hennerich if (bl->props.fb_blank != FB_BLANK_UNBLANK) 36982fd53b7SMichael Hennerich brightness = 0; 37082fd53b7SMichael Hennerich 37182fd53b7SMichael Hennerich return adp8860_bl_set(bl, brightness); 37282fd53b7SMichael Hennerich } 37382fd53b7SMichael Hennerich 37482fd53b7SMichael Hennerich static int adp8860_bl_get_brightness(struct backlight_device *bl) 37582fd53b7SMichael Hennerich { 37682fd53b7SMichael Hennerich struct adp8860_bl *data = bl_get_data(bl); 37782fd53b7SMichael Hennerich 37882fd53b7SMichael Hennerich return data->current_brightness; 37982fd53b7SMichael Hennerich } 38082fd53b7SMichael Hennerich 38182fd53b7SMichael Hennerich static const struct backlight_ops adp8860_bl_ops = { 38282fd53b7SMichael Hennerich .update_status = adp8860_bl_update_status, 38382fd53b7SMichael Hennerich .get_brightness = adp8860_bl_get_brightness, 38482fd53b7SMichael Hennerich }; 38582fd53b7SMichael Hennerich 38682fd53b7SMichael Hennerich static int adp8860_bl_setup(struct backlight_device *bl) 38782fd53b7SMichael Hennerich { 38882fd53b7SMichael Hennerich struct adp8860_bl *data = bl_get_data(bl); 38982fd53b7SMichael Hennerich struct i2c_client *client = data->client; 39082fd53b7SMichael Hennerich struct adp8860_backlight_platform_data *pdata = data->pdata; 39182fd53b7SMichael Hennerich int ret = 0; 39282fd53b7SMichael Hennerich 39382fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_BLSEN, ~pdata->bl_led_assign); 39482fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_BLMX1, pdata->l1_daylight_max); 39582fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_BLDM1, pdata->l1_daylight_dim); 39682fd53b7SMichael Hennerich 397c7c06d8aSMichael Hennerich if (data->en_ambl_sens) { 39882fd53b7SMichael Hennerich data->cached_daylight_max = pdata->l1_daylight_max; 39982fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_BLMX2, 40082fd53b7SMichael Hennerich pdata->l2_office_max); 40182fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_BLDM2, 40282fd53b7SMichael Hennerich pdata->l2_office_dim); 40382fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_BLMX3, 40482fd53b7SMichael Hennerich pdata->l3_dark_max); 40582fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_BLDM3, 40682fd53b7SMichael Hennerich pdata->l3_dark_dim); 40782fd53b7SMichael Hennerich 40882fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_L2_TRP, pdata->l2_trip); 40982fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_L2_HYS, pdata->l2_hyst); 41082fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_L3_TRP, pdata->l3_trip); 41182fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_L3_HYS, pdata->l3_hyst); 41282fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_CCFG, L2_EN | L3_EN | 41382fd53b7SMichael Hennerich ALS_CCFG_VAL(pdata->abml_filt)); 41482fd53b7SMichael Hennerich } 41582fd53b7SMichael Hennerich 41682fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_CFGR, 41782fd53b7SMichael Hennerich BL_CFGR_VAL(pdata->bl_fade_law, 0)); 41882fd53b7SMichael Hennerich 41982fd53b7SMichael Hennerich ret |= adp8860_write(client, ADP8860_BLFR, FADE_VAL(pdata->bl_fade_in, 42082fd53b7SMichael Hennerich pdata->bl_fade_out)); 42182fd53b7SMichael Hennerich 422c7c06d8aSMichael Hennerich ret |= adp8860_set_bits(client, ADP8860_MDCR, BLEN | DIM_EN | NSTBY | 423c7c06d8aSMichael Hennerich (data->gdwn_dis ? GDWN_DIS : 0)); 42482fd53b7SMichael Hennerich 42582fd53b7SMichael Hennerich return ret; 42682fd53b7SMichael Hennerich } 42782fd53b7SMichael Hennerich 42882fd53b7SMichael Hennerich static ssize_t adp8860_show(struct device *dev, char *buf, int reg) 42982fd53b7SMichael Hennerich { 43082fd53b7SMichael Hennerich struct adp8860_bl *data = dev_get_drvdata(dev); 43182fd53b7SMichael Hennerich int error; 43282fd53b7SMichael Hennerich uint8_t reg_val; 43382fd53b7SMichael Hennerich 43482fd53b7SMichael Hennerich mutex_lock(&data->lock); 43582fd53b7SMichael Hennerich error = adp8860_read(data->client, reg, ®_val); 43682fd53b7SMichael Hennerich mutex_unlock(&data->lock); 43782fd53b7SMichael Hennerich 43882fd53b7SMichael Hennerich if (error < 0) 43982fd53b7SMichael Hennerich return error; 44082fd53b7SMichael Hennerich 44182fd53b7SMichael Hennerich return sprintf(buf, "%u\n", reg_val); 44282fd53b7SMichael Hennerich } 44382fd53b7SMichael Hennerich 44482fd53b7SMichael Hennerich static ssize_t adp8860_store(struct device *dev, const char *buf, 44582fd53b7SMichael Hennerich size_t count, int reg) 44682fd53b7SMichael Hennerich { 44782fd53b7SMichael Hennerich struct adp8860_bl *data = dev_get_drvdata(dev); 44882fd53b7SMichael Hennerich unsigned long val; 44982fd53b7SMichael Hennerich int ret; 45082fd53b7SMichael Hennerich 4518dd9d7f2SJingoo Han ret = kstrtoul(buf, 10, &val); 45282fd53b7SMichael Hennerich if (ret) 45382fd53b7SMichael Hennerich return ret; 45482fd53b7SMichael Hennerich 45582fd53b7SMichael Hennerich mutex_lock(&data->lock); 45682fd53b7SMichael Hennerich adp8860_write(data->client, reg, val); 45782fd53b7SMichael Hennerich mutex_unlock(&data->lock); 45882fd53b7SMichael Hennerich 45982fd53b7SMichael Hennerich return count; 46082fd53b7SMichael Hennerich } 46182fd53b7SMichael Hennerich 46282fd53b7SMichael Hennerich static ssize_t adp8860_bl_l3_dark_max_show(struct device *dev, 46382fd53b7SMichael Hennerich struct device_attribute *attr, char *buf) 46482fd53b7SMichael Hennerich { 46582fd53b7SMichael Hennerich return adp8860_show(dev, buf, ADP8860_BLMX3); 46682fd53b7SMichael Hennerich } 46782fd53b7SMichael Hennerich 46882fd53b7SMichael Hennerich static ssize_t adp8860_bl_l3_dark_max_store(struct device *dev, 46982fd53b7SMichael Hennerich struct device_attribute *attr, const char *buf, size_t count) 47082fd53b7SMichael Hennerich { 47182fd53b7SMichael Hennerich return adp8860_store(dev, buf, count, ADP8860_BLMX3); 47282fd53b7SMichael Hennerich } 47382fd53b7SMichael Hennerich 47482fd53b7SMichael Hennerich static DEVICE_ATTR(l3_dark_max, 0664, adp8860_bl_l3_dark_max_show, 47582fd53b7SMichael Hennerich adp8860_bl_l3_dark_max_store); 47682fd53b7SMichael Hennerich 47782fd53b7SMichael Hennerich static ssize_t adp8860_bl_l2_office_max_show(struct device *dev, 47882fd53b7SMichael Hennerich struct device_attribute *attr, char *buf) 47982fd53b7SMichael Hennerich { 48082fd53b7SMichael Hennerich return adp8860_show(dev, buf, ADP8860_BLMX2); 48182fd53b7SMichael Hennerich } 48282fd53b7SMichael Hennerich 48382fd53b7SMichael Hennerich static ssize_t adp8860_bl_l2_office_max_store(struct device *dev, 48482fd53b7SMichael Hennerich struct device_attribute *attr, const char *buf, size_t count) 48582fd53b7SMichael Hennerich { 48682fd53b7SMichael Hennerich return adp8860_store(dev, buf, count, ADP8860_BLMX2); 48782fd53b7SMichael Hennerich } 48882fd53b7SMichael Hennerich static DEVICE_ATTR(l2_office_max, 0664, adp8860_bl_l2_office_max_show, 48982fd53b7SMichael Hennerich adp8860_bl_l2_office_max_store); 49082fd53b7SMichael Hennerich 49182fd53b7SMichael Hennerich static ssize_t adp8860_bl_l1_daylight_max_show(struct device *dev, 49282fd53b7SMichael Hennerich struct device_attribute *attr, char *buf) 49382fd53b7SMichael Hennerich { 49482fd53b7SMichael Hennerich return adp8860_show(dev, buf, ADP8860_BLMX1); 49582fd53b7SMichael Hennerich } 49682fd53b7SMichael Hennerich 49782fd53b7SMichael Hennerich static ssize_t adp8860_bl_l1_daylight_max_store(struct device *dev, 49882fd53b7SMichael Hennerich struct device_attribute *attr, const char *buf, size_t count) 49982fd53b7SMichael Hennerich { 50082fd53b7SMichael Hennerich struct adp8860_bl *data = dev_get_drvdata(dev); 5018dd9d7f2SJingoo Han int ret = kstrtoul(buf, 10, &data->cached_daylight_max); 5024f1aa846SMichael Hennerich if (ret) 5034f1aa846SMichael Hennerich return ret; 50482fd53b7SMichael Hennerich 50582fd53b7SMichael Hennerich return adp8860_store(dev, buf, count, ADP8860_BLMX1); 50682fd53b7SMichael Hennerich } 50782fd53b7SMichael Hennerich static DEVICE_ATTR(l1_daylight_max, 0664, adp8860_bl_l1_daylight_max_show, 50882fd53b7SMichael Hennerich adp8860_bl_l1_daylight_max_store); 50982fd53b7SMichael Hennerich 51082fd53b7SMichael Hennerich static ssize_t adp8860_bl_l3_dark_dim_show(struct device *dev, 51182fd53b7SMichael Hennerich struct device_attribute *attr, char *buf) 51282fd53b7SMichael Hennerich { 51382fd53b7SMichael Hennerich return adp8860_show(dev, buf, ADP8860_BLDM3); 51482fd53b7SMichael Hennerich } 51582fd53b7SMichael Hennerich 51682fd53b7SMichael Hennerich static ssize_t adp8860_bl_l3_dark_dim_store(struct device *dev, 51782fd53b7SMichael Hennerich struct device_attribute *attr, 51882fd53b7SMichael Hennerich const char *buf, size_t count) 51982fd53b7SMichael Hennerich { 52082fd53b7SMichael Hennerich return adp8860_store(dev, buf, count, ADP8860_BLDM3); 52182fd53b7SMichael Hennerich } 52282fd53b7SMichael Hennerich static DEVICE_ATTR(l3_dark_dim, 0664, adp8860_bl_l3_dark_dim_show, 52382fd53b7SMichael Hennerich adp8860_bl_l3_dark_dim_store); 52482fd53b7SMichael Hennerich 52582fd53b7SMichael Hennerich static ssize_t adp8860_bl_l2_office_dim_show(struct device *dev, 52682fd53b7SMichael Hennerich struct device_attribute *attr, char *buf) 52782fd53b7SMichael Hennerich { 52882fd53b7SMichael Hennerich return adp8860_show(dev, buf, ADP8860_BLDM2); 52982fd53b7SMichael Hennerich } 53082fd53b7SMichael Hennerich 53182fd53b7SMichael Hennerich static ssize_t adp8860_bl_l2_office_dim_store(struct device *dev, 53282fd53b7SMichael Hennerich struct device_attribute *attr, 53382fd53b7SMichael Hennerich const char *buf, size_t count) 53482fd53b7SMichael Hennerich { 53582fd53b7SMichael Hennerich return adp8860_store(dev, buf, count, ADP8860_BLDM2); 53682fd53b7SMichael Hennerich } 53782fd53b7SMichael Hennerich static DEVICE_ATTR(l2_office_dim, 0664, adp8860_bl_l2_office_dim_show, 53882fd53b7SMichael Hennerich adp8860_bl_l2_office_dim_store); 53982fd53b7SMichael Hennerich 54082fd53b7SMichael Hennerich static ssize_t adp8860_bl_l1_daylight_dim_show(struct device *dev, 54182fd53b7SMichael Hennerich struct device_attribute *attr, char *buf) 54282fd53b7SMichael Hennerich { 54382fd53b7SMichael Hennerich return adp8860_show(dev, buf, ADP8860_BLDM1); 54482fd53b7SMichael Hennerich } 54582fd53b7SMichael Hennerich 54682fd53b7SMichael Hennerich static ssize_t adp8860_bl_l1_daylight_dim_store(struct device *dev, 54782fd53b7SMichael Hennerich struct device_attribute *attr, 54882fd53b7SMichael Hennerich const char *buf, size_t count) 54982fd53b7SMichael Hennerich { 55082fd53b7SMichael Hennerich return adp8860_store(dev, buf, count, ADP8860_BLDM1); 55182fd53b7SMichael Hennerich } 55282fd53b7SMichael Hennerich static DEVICE_ATTR(l1_daylight_dim, 0664, adp8860_bl_l1_daylight_dim_show, 55382fd53b7SMichael Hennerich adp8860_bl_l1_daylight_dim_store); 55482fd53b7SMichael Hennerich 55582fd53b7SMichael Hennerich #ifdef ADP8860_EXT_FEATURES 55682fd53b7SMichael Hennerich static ssize_t adp8860_bl_ambient_light_level_show(struct device *dev, 55782fd53b7SMichael Hennerich struct device_attribute *attr, char *buf) 55882fd53b7SMichael Hennerich { 55982fd53b7SMichael Hennerich struct adp8860_bl *data = dev_get_drvdata(dev); 56082fd53b7SMichael Hennerich int error; 56182fd53b7SMichael Hennerich uint8_t reg_val; 56282fd53b7SMichael Hennerich uint16_t ret_val; 56382fd53b7SMichael Hennerich 56482fd53b7SMichael Hennerich mutex_lock(&data->lock); 56582fd53b7SMichael Hennerich error = adp8860_read(data->client, ADP8860_PH1LEVL, ®_val); 56682fd53b7SMichael Hennerich ret_val = reg_val; 56782fd53b7SMichael Hennerich error |= adp8860_read(data->client, ADP8860_PH1LEVH, ®_val); 56882fd53b7SMichael Hennerich mutex_unlock(&data->lock); 56982fd53b7SMichael Hennerich 57082fd53b7SMichael Hennerich if (error < 0) 57182fd53b7SMichael Hennerich return error; 57282fd53b7SMichael Hennerich 57382fd53b7SMichael Hennerich /* Return 13-bit conversion value for the first light sensor */ 57482fd53b7SMichael Hennerich ret_val += (reg_val & 0x1F) << 8; 57582fd53b7SMichael Hennerich 57682fd53b7SMichael Hennerich return sprintf(buf, "%u\n", ret_val); 57782fd53b7SMichael Hennerich } 57882fd53b7SMichael Hennerich static DEVICE_ATTR(ambient_light_level, 0444, 57982fd53b7SMichael Hennerich adp8860_bl_ambient_light_level_show, NULL); 58082fd53b7SMichael Hennerich 58182fd53b7SMichael Hennerich static ssize_t adp8860_bl_ambient_light_zone_show(struct device *dev, 58282fd53b7SMichael Hennerich struct device_attribute *attr, char *buf) 58382fd53b7SMichael Hennerich { 58482fd53b7SMichael Hennerich struct adp8860_bl *data = dev_get_drvdata(dev); 58582fd53b7SMichael Hennerich int error; 58682fd53b7SMichael Hennerich uint8_t reg_val; 58782fd53b7SMichael Hennerich 58882fd53b7SMichael Hennerich mutex_lock(&data->lock); 58982fd53b7SMichael Hennerich error = adp8860_read(data->client, ADP8860_CFGR, ®_val); 59082fd53b7SMichael Hennerich mutex_unlock(&data->lock); 59182fd53b7SMichael Hennerich 59282fd53b7SMichael Hennerich if (error < 0) 59382fd53b7SMichael Hennerich return error; 59482fd53b7SMichael Hennerich 59582fd53b7SMichael Hennerich return sprintf(buf, "%u\n", 59682fd53b7SMichael Hennerich ((reg_val >> CFGR_BLV_SHIFT) & CFGR_BLV_MASK) + 1); 59782fd53b7SMichael Hennerich } 59882fd53b7SMichael Hennerich 59982fd53b7SMichael Hennerich static ssize_t adp8860_bl_ambient_light_zone_store(struct device *dev, 60082fd53b7SMichael Hennerich struct device_attribute *attr, 60182fd53b7SMichael Hennerich const char *buf, size_t count) 60282fd53b7SMichael Hennerich { 60382fd53b7SMichael Hennerich struct adp8860_bl *data = dev_get_drvdata(dev); 60482fd53b7SMichael Hennerich unsigned long val; 60582fd53b7SMichael Hennerich uint8_t reg_val; 60682fd53b7SMichael Hennerich int ret; 60782fd53b7SMichael Hennerich 6088dd9d7f2SJingoo Han ret = kstrtoul(buf, 10, &val); 60982fd53b7SMichael Hennerich if (ret) 61082fd53b7SMichael Hennerich return ret; 61182fd53b7SMichael Hennerich 61282fd53b7SMichael Hennerich if (val == 0) { 61382fd53b7SMichael Hennerich /* Enable automatic ambient light sensing */ 61482fd53b7SMichael Hennerich adp8860_set_bits(data->client, ADP8860_MDCR, CMP_AUTOEN); 615c7ce2500SMichael Hennerich } else if ((val > 0) && (val <= 3)) { 61682fd53b7SMichael Hennerich /* Disable automatic ambient light sensing */ 61782fd53b7SMichael Hennerich adp8860_clr_bits(data->client, ADP8860_MDCR, CMP_AUTOEN); 61882fd53b7SMichael Hennerich 61982fd53b7SMichael Hennerich /* Set user supplied ambient light zone */ 62082fd53b7SMichael Hennerich mutex_lock(&data->lock); 62182fd53b7SMichael Hennerich adp8860_read(data->client, ADP8860_CFGR, ®_val); 62282fd53b7SMichael Hennerich reg_val &= ~(CFGR_BLV_MASK << CFGR_BLV_SHIFT); 623c7ce2500SMichael Hennerich reg_val |= (val - 1) << CFGR_BLV_SHIFT; 62482fd53b7SMichael Hennerich adp8860_write(data->client, ADP8860_CFGR, reg_val); 62582fd53b7SMichael Hennerich mutex_unlock(&data->lock); 62682fd53b7SMichael Hennerich } 62782fd53b7SMichael Hennerich 62882fd53b7SMichael Hennerich return count; 62982fd53b7SMichael Hennerich } 63082fd53b7SMichael Hennerich static DEVICE_ATTR(ambient_light_zone, 0664, 63182fd53b7SMichael Hennerich adp8860_bl_ambient_light_zone_show, 63282fd53b7SMichael Hennerich adp8860_bl_ambient_light_zone_store); 63382fd53b7SMichael Hennerich #endif 63482fd53b7SMichael Hennerich 63582fd53b7SMichael Hennerich static struct attribute *adp8860_bl_attributes[] = { 63682fd53b7SMichael Hennerich &dev_attr_l3_dark_max.attr, 63782fd53b7SMichael Hennerich &dev_attr_l3_dark_dim.attr, 63882fd53b7SMichael Hennerich &dev_attr_l2_office_max.attr, 63982fd53b7SMichael Hennerich &dev_attr_l2_office_dim.attr, 64082fd53b7SMichael Hennerich &dev_attr_l1_daylight_max.attr, 64182fd53b7SMichael Hennerich &dev_attr_l1_daylight_dim.attr, 64282fd53b7SMichael Hennerich #ifdef ADP8860_EXT_FEATURES 64382fd53b7SMichael Hennerich &dev_attr_ambient_light_level.attr, 64482fd53b7SMichael Hennerich &dev_attr_ambient_light_zone.attr, 64582fd53b7SMichael Hennerich #endif 64682fd53b7SMichael Hennerich NULL 64782fd53b7SMichael Hennerich }; 64882fd53b7SMichael Hennerich 64982fd53b7SMichael Hennerich static const struct attribute_group adp8860_bl_attr_group = { 65082fd53b7SMichael Hennerich .attrs = adp8860_bl_attributes, 65182fd53b7SMichael Hennerich }; 65282fd53b7SMichael Hennerich 6531b9e450dSBill Pemberton static int adp8860_probe(struct i2c_client *client, 65482fd53b7SMichael Hennerich const struct i2c_device_id *id) 65582fd53b7SMichael Hennerich { 65682fd53b7SMichael Hennerich struct backlight_device *bl; 65782fd53b7SMichael Hennerich struct adp8860_bl *data; 65882fd53b7SMichael Hennerich struct adp8860_backlight_platform_data *pdata = 65982fd53b7SMichael Hennerich client->dev.platform_data; 6603f43f8b2SRandy Dunlap struct backlight_properties props; 66182fd53b7SMichael Hennerich uint8_t reg_val; 66282fd53b7SMichael Hennerich int ret; 66382fd53b7SMichael Hennerich 66482fd53b7SMichael Hennerich if (!i2c_check_functionality(client->adapter, 66582fd53b7SMichael Hennerich I2C_FUNC_SMBUS_BYTE_DATA)) { 66682fd53b7SMichael Hennerich dev_err(&client->dev, "SMBUS Byte Data not Supported\n"); 66782fd53b7SMichael Hennerich return -EIO; 66882fd53b7SMichael Hennerich } 66982fd53b7SMichael Hennerich 67082fd53b7SMichael Hennerich if (!pdata) { 67182fd53b7SMichael Hennerich dev_err(&client->dev, "no platform data?\n"); 67282fd53b7SMichael Hennerich return -EINVAL; 67382fd53b7SMichael Hennerich } 67482fd53b7SMichael Hennerich 67558875ea9SJingoo Han data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); 67682fd53b7SMichael Hennerich if (data == NULL) 67782fd53b7SMichael Hennerich return -ENOMEM; 67882fd53b7SMichael Hennerich 679c7c06d8aSMichael Hennerich ret = adp8860_read(client, ADP8860_MFDVID, ®_val); 680c7c06d8aSMichael Hennerich if (ret < 0) 68158875ea9SJingoo Han return ret; 682c7c06d8aSMichael Hennerich 683c7c06d8aSMichael Hennerich switch (ADP8860_MANID(reg_val)) { 684c7c06d8aSMichael Hennerich case ADP8863_MANUFID: 685c7c06d8aSMichael Hennerich data->gdwn_dis = !!pdata->gdwn_dis; 686c7c06d8aSMichael Hennerich case ADP8860_MANUFID: 687c7c06d8aSMichael Hennerich data->en_ambl_sens = !!pdata->en_ambl_sens; 688c7c06d8aSMichael Hennerich break; 689c7c06d8aSMichael Hennerich case ADP8861_MANUFID: 690c7c06d8aSMichael Hennerich data->gdwn_dis = !!pdata->gdwn_dis; 691c7c06d8aSMichael Hennerich break; 692c7c06d8aSMichael Hennerich default: 693c7c06d8aSMichael Hennerich dev_err(&client->dev, "failed to probe\n"); 69458875ea9SJingoo Han return -ENODEV; 695c7c06d8aSMichael Hennerich } 696c7c06d8aSMichael Hennerich 69782fd53b7SMichael Hennerich /* It's confirmed that the DEVID field is actually a REVID */ 69882fd53b7SMichael Hennerich 69982fd53b7SMichael Hennerich data->revid = ADP8860_DEVID(reg_val); 70082fd53b7SMichael Hennerich data->client = client; 70182fd53b7SMichael Hennerich data->pdata = pdata; 70282fd53b7SMichael Hennerich data->id = id->driver_data; 70382fd53b7SMichael Hennerich data->current_brightness = 0; 70482fd53b7SMichael Hennerich i2c_set_clientdata(client, data); 70582fd53b7SMichael Hennerich 7063f43f8b2SRandy Dunlap memset(&props, 0, sizeof(props)); 707bb7ca747SMatthew Garrett props.type = BACKLIGHT_RAW; 7083f43f8b2SRandy Dunlap props.max_brightness = ADP8860_MAX_BRIGHTNESS; 7093f43f8b2SRandy Dunlap 71082fd53b7SMichael Hennerich mutex_init(&data->lock); 71182fd53b7SMichael Hennerich 71282fd53b7SMichael Hennerich bl = backlight_device_register(dev_driver_string(&client->dev), 7133f43f8b2SRandy Dunlap &client->dev, data, &adp8860_bl_ops, &props); 71482fd53b7SMichael Hennerich if (IS_ERR(bl)) { 71582fd53b7SMichael Hennerich dev_err(&client->dev, "failed to register backlight\n"); 71658875ea9SJingoo Han return PTR_ERR(bl); 71782fd53b7SMichael Hennerich } 71882fd53b7SMichael Hennerich 71982fd53b7SMichael Hennerich bl->props.brightness = ADP8860_MAX_BRIGHTNESS; 72082fd53b7SMichael Hennerich 72182fd53b7SMichael Hennerich data->bl = bl; 72282fd53b7SMichael Hennerich 723c7c06d8aSMichael Hennerich if (data->en_ambl_sens) 72482fd53b7SMichael Hennerich ret = sysfs_create_group(&bl->dev.kobj, 72582fd53b7SMichael Hennerich &adp8860_bl_attr_group); 72682fd53b7SMichael Hennerich 72782fd53b7SMichael Hennerich if (ret) { 72882fd53b7SMichael Hennerich dev_err(&client->dev, "failed to register sysfs\n"); 72982fd53b7SMichael Hennerich goto out1; 73082fd53b7SMichael Hennerich } 73182fd53b7SMichael Hennerich 73282fd53b7SMichael Hennerich ret = adp8860_bl_setup(bl); 73382fd53b7SMichael Hennerich if (ret) { 73482fd53b7SMichael Hennerich ret = -EIO; 73582fd53b7SMichael Hennerich goto out; 73682fd53b7SMichael Hennerich } 73782fd53b7SMichael Hennerich 73882fd53b7SMichael Hennerich backlight_update_status(bl); 73982fd53b7SMichael Hennerich 740c7c06d8aSMichael Hennerich dev_info(&client->dev, "%s Rev.%d Backlight\n", 741c7c06d8aSMichael Hennerich client->name, data->revid); 74282fd53b7SMichael Hennerich 74382fd53b7SMichael Hennerich if (pdata->num_leds) 74482fd53b7SMichael Hennerich adp8860_led_probe(client); 74582fd53b7SMichael Hennerich 74682fd53b7SMichael Hennerich return 0; 74782fd53b7SMichael Hennerich 74882fd53b7SMichael Hennerich out: 749c7c06d8aSMichael Hennerich if (data->en_ambl_sens) 75082fd53b7SMichael Hennerich sysfs_remove_group(&data->bl->dev.kobj, 75182fd53b7SMichael Hennerich &adp8860_bl_attr_group); 75282fd53b7SMichael Hennerich out1: 75382fd53b7SMichael Hennerich backlight_device_unregister(bl); 75482fd53b7SMichael Hennerich 75582fd53b7SMichael Hennerich return ret; 75682fd53b7SMichael Hennerich } 75782fd53b7SMichael Hennerich 7587e4b9d0bSBill Pemberton static int adp8860_remove(struct i2c_client *client) 75982fd53b7SMichael Hennerich { 76082fd53b7SMichael Hennerich struct adp8860_bl *data = i2c_get_clientdata(client); 76182fd53b7SMichael Hennerich 76282fd53b7SMichael Hennerich adp8860_clr_bits(client, ADP8860_MDCR, NSTBY); 76382fd53b7SMichael Hennerich 76482fd53b7SMichael Hennerich if (data->led) 76582fd53b7SMichael Hennerich adp8860_led_remove(client); 76682fd53b7SMichael Hennerich 767c7c06d8aSMichael Hennerich if (data->en_ambl_sens) 76882fd53b7SMichael Hennerich sysfs_remove_group(&data->bl->dev.kobj, 76982fd53b7SMichael Hennerich &adp8860_bl_attr_group); 77082fd53b7SMichael Hennerich 77182fd53b7SMichael Hennerich backlight_device_unregister(data->bl); 77282fd53b7SMichael Hennerich 77382fd53b7SMichael Hennerich return 0; 77482fd53b7SMichael Hennerich } 77582fd53b7SMichael Hennerich 77682fd53b7SMichael Hennerich #ifdef CONFIG_PM 77782fd53b7SMichael Hennerich static int adp8860_i2c_suspend(struct i2c_client *client, pm_message_t message) 77882fd53b7SMichael Hennerich { 77982fd53b7SMichael Hennerich adp8860_clr_bits(client, ADP8860_MDCR, NSTBY); 78082fd53b7SMichael Hennerich 78182fd53b7SMichael Hennerich return 0; 78282fd53b7SMichael Hennerich } 78382fd53b7SMichael Hennerich 78482fd53b7SMichael Hennerich static int adp8860_i2c_resume(struct i2c_client *client) 78582fd53b7SMichael Hennerich { 786*5eb02c01SLars-Peter Clausen adp8860_set_bits(client, ADP8860_MDCR, NSTBY | BLEN); 78782fd53b7SMichael Hennerich 78882fd53b7SMichael Hennerich return 0; 78982fd53b7SMichael Hennerich } 79082fd53b7SMichael Hennerich #else 79182fd53b7SMichael Hennerich #define adp8860_i2c_suspend NULL 79282fd53b7SMichael Hennerich #define adp8860_i2c_resume NULL 79382fd53b7SMichael Hennerich #endif 79482fd53b7SMichael Hennerich 79582fd53b7SMichael Hennerich static const struct i2c_device_id adp8860_id[] = { 796c7c06d8aSMichael Hennerich { "adp8860", adp8860 }, 797c7c06d8aSMichael Hennerich { "adp8861", adp8861 }, 798c7c06d8aSMichael Hennerich { "adp8863", adp8863 }, 79982fd53b7SMichael Hennerich { } 80082fd53b7SMichael Hennerich }; 80182fd53b7SMichael Hennerich MODULE_DEVICE_TABLE(i2c, adp8860_id); 80282fd53b7SMichael Hennerich 80382fd53b7SMichael Hennerich static struct i2c_driver adp8860_driver = { 80482fd53b7SMichael Hennerich .driver = { 80582fd53b7SMichael Hennerich .name = KBUILD_MODNAME, 80682fd53b7SMichael Hennerich }, 80782fd53b7SMichael Hennerich .probe = adp8860_probe, 808d1723fa2SBill Pemberton .remove = adp8860_remove, 80982fd53b7SMichael Hennerich .suspend = adp8860_i2c_suspend, 81082fd53b7SMichael Hennerich .resume = adp8860_i2c_resume, 81182fd53b7SMichael Hennerich .id_table = adp8860_id, 81282fd53b7SMichael Hennerich }; 81382fd53b7SMichael Hennerich 81481ce6864SAxel Lin module_i2c_driver(adp8860_driver); 81582fd53b7SMichael Hennerich 81682fd53b7SMichael Hennerich MODULE_LICENSE("GPL v2"); 81782fd53b7SMichael Hennerich MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); 81882fd53b7SMichael Hennerich MODULE_DESCRIPTION("ADP8860 Backlight driver"); 81982fd53b7SMichael Hennerich MODULE_ALIAS("i2c:adp8860-backlight"); 820