adp8870_bl.c (26653B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Backlight driver for Analog Devices ADP8870 Backlight Devices 4 * 5 * Copyright 2009-2011 Analog Devices Inc. 6 */ 7 8#include <linux/module.h> 9#include <linux/init.h> 10#include <linux/errno.h> 11#include <linux/pm.h> 12#include <linux/platform_device.h> 13#include <linux/i2c.h> 14#include <linux/fb.h> 15#include <linux/backlight.h> 16#include <linux/leds.h> 17#include <linux/workqueue.h> 18#include <linux/slab.h> 19 20#include <linux/platform_data/adp8870.h> 21#define ADP8870_EXT_FEATURES 22#define ADP8870_USE_LEDS 23 24 25#define ADP8870_MFDVID 0x00 /* Manufacturer and device ID */ 26#define ADP8870_MDCR 0x01 /* Device mode and status */ 27#define ADP8870_INT_STAT 0x02 /* Interrupts status */ 28#define ADP8870_INT_EN 0x03 /* Interrupts enable */ 29#define ADP8870_CFGR 0x04 /* Configuration register */ 30#define ADP8870_BLSEL 0x05 /* Sink enable backlight or independent */ 31#define ADP8870_PWMLED 0x06 /* PWM Enable Selection Register */ 32#define ADP8870_BLOFF 0x07 /* Backlight off timeout */ 33#define ADP8870_BLDIM 0x08 /* Backlight dim timeout */ 34#define ADP8870_BLFR 0x09 /* Backlight fade in and out rates */ 35#define ADP8870_BLMX1 0x0A /* Backlight (Brightness Level 1-daylight) maximum current */ 36#define ADP8870_BLDM1 0x0B /* Backlight (Brightness Level 1-daylight) dim current */ 37#define ADP8870_BLMX2 0x0C /* Backlight (Brightness Level 2-bright) maximum current */ 38#define ADP8870_BLDM2 0x0D /* Backlight (Brightness Level 2-bright) dim current */ 39#define ADP8870_BLMX3 0x0E /* Backlight (Brightness Level 3-office) maximum current */ 40#define ADP8870_BLDM3 0x0F /* Backlight (Brightness Level 3-office) dim current */ 41#define ADP8870_BLMX4 0x10 /* Backlight (Brightness Level 4-indoor) maximum current */ 42#define ADP8870_BLDM4 0x11 /* Backlight (Brightness Level 4-indoor) dim current */ 43#define ADP8870_BLMX5 0x12 /* Backlight (Brightness Level 5-dark) maximum current */ 44#define ADP8870_BLDM5 0x13 /* Backlight (Brightness Level 5-dark) dim current */ 45#define ADP8870_ISCLAW 0x1A /* Independent sink current fade law register */ 46#define ADP8870_ISCC 0x1B /* Independent sink current control register */ 47#define ADP8870_ISCT1 0x1C /* Independent Sink Current Timer Register LED[7:5] */ 48#define ADP8870_ISCT2 0x1D /* Independent Sink Current Timer Register LED[4:1] */ 49#define ADP8870_ISCF 0x1E /* Independent sink current fade register */ 50#define ADP8870_ISC1 0x1F /* Independent Sink Current LED1 */ 51#define ADP8870_ISC2 0x20 /* Independent Sink Current LED2 */ 52#define ADP8870_ISC3 0x21 /* Independent Sink Current LED3 */ 53#define ADP8870_ISC4 0x22 /* Independent Sink Current LED4 */ 54#define ADP8870_ISC5 0x23 /* Independent Sink Current LED5 */ 55#define ADP8870_ISC6 0x24 /* Independent Sink Current LED6 */ 56#define ADP8870_ISC7 0x25 /* Independent Sink Current LED7 (Brightness Level 1-daylight) */ 57#define ADP8870_ISC7_L2 0x26 /* Independent Sink Current LED7 (Brightness Level 2-bright) */ 58#define ADP8870_ISC7_L3 0x27 /* Independent Sink Current LED7 (Brightness Level 3-office) */ 59#define ADP8870_ISC7_L4 0x28 /* Independent Sink Current LED7 (Brightness Level 4-indoor) */ 60#define ADP8870_ISC7_L5 0x29 /* Independent Sink Current LED7 (Brightness Level 5-dark) */ 61#define ADP8870_CMP_CTL 0x2D /* ALS Comparator Control Register */ 62#define ADP8870_ALS1_EN 0x2E /* Main ALS comparator level enable */ 63#define ADP8870_ALS2_EN 0x2F /* Second ALS comparator level enable */ 64#define ADP8870_ALS1_STAT 0x30 /* Main ALS Comparator Status Register */ 65#define ADP8870_ALS2_STAT 0x31 /* Second ALS Comparator Status Register */ 66#define ADP8870_L2TRP 0x32 /* L2 comparator reference */ 67#define ADP8870_L2HYS 0x33 /* L2 hysteresis */ 68#define ADP8870_L3TRP 0x34 /* L3 comparator reference */ 69#define ADP8870_L3HYS 0x35 /* L3 hysteresis */ 70#define ADP8870_L4TRP 0x36 /* L4 comparator reference */ 71#define ADP8870_L4HYS 0x37 /* L4 hysteresis */ 72#define ADP8870_L5TRP 0x38 /* L5 comparator reference */ 73#define ADP8870_L5HYS 0x39 /* L5 hysteresis */ 74#define ADP8870_PH1LEVL 0x40 /* First phototransistor ambient light level-low byte register */ 75#define ADP8870_PH1LEVH 0x41 /* First phototransistor ambient light level-high byte register */ 76#define ADP8870_PH2LEVL 0x42 /* Second phototransistor ambient light level-low byte register */ 77#define ADP8870_PH2LEVH 0x43 /* Second phototransistor ambient light level-high byte register */ 78 79#define ADP8870_MANUFID 0x3 /* Analog Devices AD8870 Manufacturer and device ID */ 80#define ADP8870_DEVID(x) ((x) & 0xF) 81#define ADP8870_MANID(x) ((x) >> 4) 82 83/* MDCR Device mode and status */ 84#define D7ALSEN (1 << 7) 85#define INT_CFG (1 << 6) 86#define NSTBY (1 << 5) 87#define DIM_EN (1 << 4) 88#define GDWN_DIS (1 << 3) 89#define SIS_EN (1 << 2) 90#define CMP_AUTOEN (1 << 1) 91#define BLEN (1 << 0) 92 93/* ADP8870_ALS1_EN Main ALS comparator level enable */ 94#define L5_EN (1 << 3) 95#define L4_EN (1 << 2) 96#define L3_EN (1 << 1) 97#define L2_EN (1 << 0) 98 99#define CFGR_BLV_SHIFT 3 100#define CFGR_BLV_MASK 0x7 101#define ADP8870_FLAG_LED_MASK 0xFF 102 103#define FADE_VAL(in, out) ((0xF & (in)) | ((0xF & (out)) << 4)) 104#define BL_CFGR_VAL(law, blv) ((((blv) & CFGR_BLV_MASK) << CFGR_BLV_SHIFT) | ((0x3 & (law)) << 1)) 105#define ALS_CMPR_CFG_VAL(filt) ((0x7 & (filt)) << 1) 106 107struct adp8870_bl { 108 struct i2c_client *client; 109 struct backlight_device *bl; 110 struct adp8870_led *led; 111 struct adp8870_backlight_platform_data *pdata; 112 struct mutex lock; 113 unsigned long cached_daylight_max; 114 int id; 115 int revid; 116 int current_brightness; 117}; 118 119struct adp8870_led { 120 struct led_classdev cdev; 121 struct work_struct work; 122 struct i2c_client *client; 123 enum led_brightness new_brightness; 124 int id; 125 int flags; 126}; 127 128static int adp8870_read(struct i2c_client *client, int reg, uint8_t *val) 129{ 130 int ret; 131 132 ret = i2c_smbus_read_byte_data(client, reg); 133 if (ret < 0) { 134 dev_err(&client->dev, "failed reading at 0x%02x\n", reg); 135 return ret; 136 } 137 138 *val = ret; 139 return 0; 140} 141 142 143static int adp8870_write(struct i2c_client *client, u8 reg, u8 val) 144{ 145 int ret = i2c_smbus_write_byte_data(client, reg, val); 146 147 if (ret) 148 dev_err(&client->dev, "failed to write\n"); 149 150 return ret; 151} 152 153static int adp8870_set_bits(struct i2c_client *client, int reg, uint8_t bit_mask) 154{ 155 struct adp8870_bl *data = i2c_get_clientdata(client); 156 uint8_t reg_val; 157 int ret; 158 159 mutex_lock(&data->lock); 160 161 ret = adp8870_read(client, reg, ®_val); 162 163 if (!ret && ((reg_val & bit_mask) != bit_mask)) { 164 reg_val |= bit_mask; 165 ret = adp8870_write(client, reg, reg_val); 166 } 167 168 mutex_unlock(&data->lock); 169 return ret; 170} 171 172static int adp8870_clr_bits(struct i2c_client *client, int reg, uint8_t bit_mask) 173{ 174 struct adp8870_bl *data = i2c_get_clientdata(client); 175 uint8_t reg_val; 176 int ret; 177 178 mutex_lock(&data->lock); 179 180 ret = adp8870_read(client, reg, ®_val); 181 182 if (!ret && (reg_val & bit_mask)) { 183 reg_val &= ~bit_mask; 184 ret = adp8870_write(client, reg, reg_val); 185 } 186 187 mutex_unlock(&data->lock); 188 return ret; 189} 190 191/* 192 * Independent sink / LED 193 */ 194#if defined(ADP8870_USE_LEDS) 195static void adp8870_led_work(struct work_struct *work) 196{ 197 struct adp8870_led *led = container_of(work, struct adp8870_led, work); 198 199 adp8870_write(led->client, ADP8870_ISC1 + led->id - 1, 200 led->new_brightness >> 1); 201} 202 203static void adp8870_led_set(struct led_classdev *led_cdev, 204 enum led_brightness value) 205{ 206 struct adp8870_led *led; 207 208 led = container_of(led_cdev, struct adp8870_led, cdev); 209 led->new_brightness = value; 210 /* 211 * Use workqueue for IO since I2C operations can sleep. 212 */ 213 schedule_work(&led->work); 214} 215 216static int adp8870_led_setup(struct adp8870_led *led) 217{ 218 struct i2c_client *client = led->client; 219 int ret = 0; 220 221 ret = adp8870_write(client, ADP8870_ISC1 + led->id - 1, 0); 222 if (ret) 223 return ret; 224 225 ret = adp8870_set_bits(client, ADP8870_ISCC, 1 << (led->id - 1)); 226 if (ret) 227 return ret; 228 229 if (led->id > 4) 230 ret = adp8870_set_bits(client, ADP8870_ISCT1, 231 (led->flags & 0x3) << ((led->id - 5) * 2)); 232 else 233 ret = adp8870_set_bits(client, ADP8870_ISCT2, 234 (led->flags & 0x3) << ((led->id - 1) * 2)); 235 236 return ret; 237} 238 239static int adp8870_led_probe(struct i2c_client *client) 240{ 241 struct adp8870_backlight_platform_data *pdata = 242 dev_get_platdata(&client->dev); 243 struct adp8870_bl *data = i2c_get_clientdata(client); 244 struct adp8870_led *led, *led_dat; 245 struct led_info *cur_led; 246 int ret, i; 247 248 led = devm_kcalloc(&client->dev, pdata->num_leds, sizeof(*led), 249 GFP_KERNEL); 250 if (led == NULL) 251 return -ENOMEM; 252 253 ret = adp8870_write(client, ADP8870_ISCLAW, pdata->led_fade_law); 254 if (ret) 255 return ret; 256 257 ret = adp8870_write(client, ADP8870_ISCT1, 258 (pdata->led_on_time & 0x3) << 6); 259 if (ret) 260 return ret; 261 262 ret = adp8870_write(client, ADP8870_ISCF, 263 FADE_VAL(pdata->led_fade_in, pdata->led_fade_out)); 264 if (ret) 265 return ret; 266 267 for (i = 0; i < pdata->num_leds; ++i) { 268 cur_led = &pdata->leds[i]; 269 led_dat = &led[i]; 270 271 led_dat->id = cur_led->flags & ADP8870_FLAG_LED_MASK; 272 273 if (led_dat->id > 7 || led_dat->id < 1) { 274 dev_err(&client->dev, "Invalid LED ID %d\n", 275 led_dat->id); 276 ret = -EINVAL; 277 goto err; 278 } 279 280 if (pdata->bl_led_assign & (1 << (led_dat->id - 1))) { 281 dev_err(&client->dev, "LED %d used by Backlight\n", 282 led_dat->id); 283 ret = -EBUSY; 284 goto err; 285 } 286 287 led_dat->cdev.name = cur_led->name; 288 led_dat->cdev.default_trigger = cur_led->default_trigger; 289 led_dat->cdev.brightness_set = adp8870_led_set; 290 led_dat->cdev.brightness = LED_OFF; 291 led_dat->flags = cur_led->flags >> FLAG_OFFT_SHIFT; 292 led_dat->client = client; 293 led_dat->new_brightness = LED_OFF; 294 INIT_WORK(&led_dat->work, adp8870_led_work); 295 296 ret = led_classdev_register(&client->dev, &led_dat->cdev); 297 if (ret) { 298 dev_err(&client->dev, "failed to register LED %d\n", 299 led_dat->id); 300 goto err; 301 } 302 303 ret = adp8870_led_setup(led_dat); 304 if (ret) { 305 dev_err(&client->dev, "failed to write\n"); 306 i++; 307 goto err; 308 } 309 } 310 311 data->led = led; 312 313 return 0; 314 315 err: 316 for (i = i - 1; i >= 0; --i) { 317 led_classdev_unregister(&led[i].cdev); 318 cancel_work_sync(&led[i].work); 319 } 320 321 return ret; 322} 323 324static int adp8870_led_remove(struct i2c_client *client) 325{ 326 struct adp8870_backlight_platform_data *pdata = 327 dev_get_platdata(&client->dev); 328 struct adp8870_bl *data = i2c_get_clientdata(client); 329 int i; 330 331 for (i = 0; i < pdata->num_leds; i++) { 332 led_classdev_unregister(&data->led[i].cdev); 333 cancel_work_sync(&data->led[i].work); 334 } 335 336 return 0; 337} 338#else 339static int adp8870_led_probe(struct i2c_client *client) 340{ 341 return 0; 342} 343 344static int adp8870_led_remove(struct i2c_client *client) 345{ 346 return 0; 347} 348#endif 349 350static int adp8870_bl_set(struct backlight_device *bl, int brightness) 351{ 352 struct adp8870_bl *data = bl_get_data(bl); 353 struct i2c_client *client = data->client; 354 int ret = 0; 355 356 if (data->pdata->en_ambl_sens) { 357 if ((brightness > 0) && (brightness < ADP8870_MAX_BRIGHTNESS)) { 358 /* Disable Ambient Light auto adjust */ 359 ret = adp8870_clr_bits(client, ADP8870_MDCR, 360 CMP_AUTOEN); 361 if (ret) 362 return ret; 363 ret = adp8870_write(client, ADP8870_BLMX1, brightness); 364 if (ret) 365 return ret; 366 } else { 367 /* 368 * MAX_BRIGHTNESS -> Enable Ambient Light auto adjust 369 * restore daylight l1 sysfs brightness 370 */ 371 ret = adp8870_write(client, ADP8870_BLMX1, 372 data->cached_daylight_max); 373 if (ret) 374 return ret; 375 376 ret = adp8870_set_bits(client, ADP8870_MDCR, 377 CMP_AUTOEN); 378 if (ret) 379 return ret; 380 } 381 } else { 382 ret = adp8870_write(client, ADP8870_BLMX1, brightness); 383 if (ret) 384 return ret; 385 } 386 387 if (data->current_brightness && brightness == 0) 388 ret = adp8870_set_bits(client, 389 ADP8870_MDCR, DIM_EN); 390 else if (data->current_brightness == 0 && brightness) 391 ret = adp8870_clr_bits(client, 392 ADP8870_MDCR, DIM_EN); 393 394 if (!ret) 395 data->current_brightness = brightness; 396 397 return ret; 398} 399 400static int adp8870_bl_update_status(struct backlight_device *bl) 401{ 402 return adp8870_bl_set(bl, backlight_get_brightness(bl)); 403} 404 405static int adp8870_bl_get_brightness(struct backlight_device *bl) 406{ 407 struct adp8870_bl *data = bl_get_data(bl); 408 409 return data->current_brightness; 410} 411 412static const struct backlight_ops adp8870_bl_ops = { 413 .update_status = adp8870_bl_update_status, 414 .get_brightness = adp8870_bl_get_brightness, 415}; 416 417static int adp8870_bl_setup(struct backlight_device *bl) 418{ 419 struct adp8870_bl *data = bl_get_data(bl); 420 struct i2c_client *client = data->client; 421 struct adp8870_backlight_platform_data *pdata = data->pdata; 422 int ret = 0; 423 424 ret = adp8870_write(client, ADP8870_BLSEL, ~pdata->bl_led_assign); 425 if (ret) 426 return ret; 427 428 ret = adp8870_write(client, ADP8870_PWMLED, pdata->pwm_assign); 429 if (ret) 430 return ret; 431 432 ret = adp8870_write(client, ADP8870_BLMX1, pdata->l1_daylight_max); 433 if (ret) 434 return ret; 435 436 ret = adp8870_write(client, ADP8870_BLDM1, pdata->l1_daylight_dim); 437 if (ret) 438 return ret; 439 440 if (pdata->en_ambl_sens) { 441 data->cached_daylight_max = pdata->l1_daylight_max; 442 ret = adp8870_write(client, ADP8870_BLMX2, 443 pdata->l2_bright_max); 444 if (ret) 445 return ret; 446 ret = adp8870_write(client, ADP8870_BLDM2, 447 pdata->l2_bright_dim); 448 if (ret) 449 return ret; 450 451 ret = adp8870_write(client, ADP8870_BLMX3, 452 pdata->l3_office_max); 453 if (ret) 454 return ret; 455 ret = adp8870_write(client, ADP8870_BLDM3, 456 pdata->l3_office_dim); 457 if (ret) 458 return ret; 459 460 ret = adp8870_write(client, ADP8870_BLMX4, 461 pdata->l4_indoor_max); 462 if (ret) 463 return ret; 464 465 ret = adp8870_write(client, ADP8870_BLDM4, 466 pdata->l4_indor_dim); 467 if (ret) 468 return ret; 469 470 ret = adp8870_write(client, ADP8870_BLMX5, 471 pdata->l5_dark_max); 472 if (ret) 473 return ret; 474 475 ret = adp8870_write(client, ADP8870_BLDM5, 476 pdata->l5_dark_dim); 477 if (ret) 478 return ret; 479 480 ret = adp8870_write(client, ADP8870_L2TRP, pdata->l2_trip); 481 if (ret) 482 return ret; 483 484 ret = adp8870_write(client, ADP8870_L2HYS, pdata->l2_hyst); 485 if (ret) 486 return ret; 487 488 ret = adp8870_write(client, ADP8870_L3TRP, pdata->l3_trip); 489 if (ret) 490 return ret; 491 492 ret = adp8870_write(client, ADP8870_L3HYS, pdata->l3_hyst); 493 if (ret) 494 return ret; 495 496 ret = adp8870_write(client, ADP8870_L4TRP, pdata->l4_trip); 497 if (ret) 498 return ret; 499 500 ret = adp8870_write(client, ADP8870_L4HYS, pdata->l4_hyst); 501 if (ret) 502 return ret; 503 504 ret = adp8870_write(client, ADP8870_L5TRP, pdata->l5_trip); 505 if (ret) 506 return ret; 507 508 ret = adp8870_write(client, ADP8870_L5HYS, pdata->l5_hyst); 509 if (ret) 510 return ret; 511 512 ret = adp8870_write(client, ADP8870_ALS1_EN, L5_EN | L4_EN | 513 L3_EN | L2_EN); 514 if (ret) 515 return ret; 516 517 ret = adp8870_write(client, ADP8870_CMP_CTL, 518 ALS_CMPR_CFG_VAL(pdata->abml_filt)); 519 if (ret) 520 return ret; 521 } 522 523 ret = adp8870_write(client, ADP8870_CFGR, 524 BL_CFGR_VAL(pdata->bl_fade_law, 0)); 525 if (ret) 526 return ret; 527 528 ret = adp8870_write(client, ADP8870_BLFR, FADE_VAL(pdata->bl_fade_in, 529 pdata->bl_fade_out)); 530 if (ret) 531 return ret; 532 /* 533 * ADP8870 Rev0 requires GDWN_DIS bit set 534 */ 535 536 ret = adp8870_set_bits(client, ADP8870_MDCR, BLEN | DIM_EN | NSTBY | 537 (data->revid == 0 ? GDWN_DIS : 0)); 538 539 return ret; 540} 541 542static ssize_t adp8870_show(struct device *dev, char *buf, int reg) 543{ 544 struct adp8870_bl *data = dev_get_drvdata(dev); 545 int error; 546 uint8_t reg_val; 547 548 mutex_lock(&data->lock); 549 error = adp8870_read(data->client, reg, ®_val); 550 mutex_unlock(&data->lock); 551 552 if (error < 0) 553 return error; 554 555 return sprintf(buf, "%u\n", reg_val); 556} 557 558static ssize_t adp8870_store(struct device *dev, const char *buf, 559 size_t count, int reg) 560{ 561 struct adp8870_bl *data = dev_get_drvdata(dev); 562 unsigned long val; 563 int ret; 564 565 ret = kstrtoul(buf, 10, &val); 566 if (ret) 567 return ret; 568 569 mutex_lock(&data->lock); 570 adp8870_write(data->client, reg, val); 571 mutex_unlock(&data->lock); 572 573 return count; 574} 575 576static ssize_t adp8870_bl_l5_dark_max_show(struct device *dev, 577 struct device_attribute *attr, char *buf) 578{ 579 return adp8870_show(dev, buf, ADP8870_BLMX5); 580} 581 582static ssize_t adp8870_bl_l5_dark_max_store(struct device *dev, 583 struct device_attribute *attr, const char *buf, size_t count) 584{ 585 return adp8870_store(dev, buf, count, ADP8870_BLMX5); 586} 587static DEVICE_ATTR(l5_dark_max, 0664, adp8870_bl_l5_dark_max_show, 588 adp8870_bl_l5_dark_max_store); 589 590 591static ssize_t adp8870_bl_l4_indoor_max_show(struct device *dev, 592 struct device_attribute *attr, char *buf) 593{ 594 return adp8870_show(dev, buf, ADP8870_BLMX4); 595} 596 597static ssize_t adp8870_bl_l4_indoor_max_store(struct device *dev, 598 struct device_attribute *attr, const char *buf, size_t count) 599{ 600 return adp8870_store(dev, buf, count, ADP8870_BLMX4); 601} 602static DEVICE_ATTR(l4_indoor_max, 0664, adp8870_bl_l4_indoor_max_show, 603 adp8870_bl_l4_indoor_max_store); 604 605 606static ssize_t adp8870_bl_l3_office_max_show(struct device *dev, 607 struct device_attribute *attr, char *buf) 608{ 609 return adp8870_show(dev, buf, ADP8870_BLMX3); 610} 611 612static ssize_t adp8870_bl_l3_office_max_store(struct device *dev, 613 struct device_attribute *attr, const char *buf, size_t count) 614{ 615 return adp8870_store(dev, buf, count, ADP8870_BLMX3); 616} 617 618static DEVICE_ATTR(l3_office_max, 0664, adp8870_bl_l3_office_max_show, 619 adp8870_bl_l3_office_max_store); 620 621static ssize_t adp8870_bl_l2_bright_max_show(struct device *dev, 622 struct device_attribute *attr, char *buf) 623{ 624 return adp8870_show(dev, buf, ADP8870_BLMX2); 625} 626 627static ssize_t adp8870_bl_l2_bright_max_store(struct device *dev, 628 struct device_attribute *attr, const char *buf, size_t count) 629{ 630 return adp8870_store(dev, buf, count, ADP8870_BLMX2); 631} 632static DEVICE_ATTR(l2_bright_max, 0664, adp8870_bl_l2_bright_max_show, 633 adp8870_bl_l2_bright_max_store); 634 635static ssize_t adp8870_bl_l1_daylight_max_show(struct device *dev, 636 struct device_attribute *attr, char *buf) 637{ 638 return adp8870_show(dev, buf, ADP8870_BLMX1); 639} 640 641static ssize_t adp8870_bl_l1_daylight_max_store(struct device *dev, 642 struct device_attribute *attr, const char *buf, size_t count) 643{ 644 struct adp8870_bl *data = dev_get_drvdata(dev); 645 int ret = kstrtoul(buf, 10, &data->cached_daylight_max); 646 647 if (ret) 648 return ret; 649 650 return adp8870_store(dev, buf, count, ADP8870_BLMX1); 651} 652static DEVICE_ATTR(l1_daylight_max, 0664, adp8870_bl_l1_daylight_max_show, 653 adp8870_bl_l1_daylight_max_store); 654 655static ssize_t adp8870_bl_l5_dark_dim_show(struct device *dev, 656 struct device_attribute *attr, char *buf) 657{ 658 return adp8870_show(dev, buf, ADP8870_BLDM5); 659} 660 661static ssize_t adp8870_bl_l5_dark_dim_store(struct device *dev, 662 struct device_attribute *attr, 663 const char *buf, size_t count) 664{ 665 return adp8870_store(dev, buf, count, ADP8870_BLDM5); 666} 667static DEVICE_ATTR(l5_dark_dim, 0664, adp8870_bl_l5_dark_dim_show, 668 adp8870_bl_l5_dark_dim_store); 669 670static ssize_t adp8870_bl_l4_indoor_dim_show(struct device *dev, 671 struct device_attribute *attr, char *buf) 672{ 673 return adp8870_show(dev, buf, ADP8870_BLDM4); 674} 675 676static ssize_t adp8870_bl_l4_indoor_dim_store(struct device *dev, 677 struct device_attribute *attr, 678 const char *buf, size_t count) 679{ 680 return adp8870_store(dev, buf, count, ADP8870_BLDM4); 681} 682static DEVICE_ATTR(l4_indoor_dim, 0664, adp8870_bl_l4_indoor_dim_show, 683 adp8870_bl_l4_indoor_dim_store); 684 685 686static ssize_t adp8870_bl_l3_office_dim_show(struct device *dev, 687 struct device_attribute *attr, char *buf) 688{ 689 return adp8870_show(dev, buf, ADP8870_BLDM3); 690} 691 692static ssize_t adp8870_bl_l3_office_dim_store(struct device *dev, 693 struct device_attribute *attr, 694 const char *buf, size_t count) 695{ 696 return adp8870_store(dev, buf, count, ADP8870_BLDM3); 697} 698static DEVICE_ATTR(l3_office_dim, 0664, adp8870_bl_l3_office_dim_show, 699 adp8870_bl_l3_office_dim_store); 700 701static ssize_t adp8870_bl_l2_bright_dim_show(struct device *dev, 702 struct device_attribute *attr, char *buf) 703{ 704 return adp8870_show(dev, buf, ADP8870_BLDM2); 705} 706 707static ssize_t adp8870_bl_l2_bright_dim_store(struct device *dev, 708 struct device_attribute *attr, 709 const char *buf, size_t count) 710{ 711 return adp8870_store(dev, buf, count, ADP8870_BLDM2); 712} 713static DEVICE_ATTR(l2_bright_dim, 0664, adp8870_bl_l2_bright_dim_show, 714 adp8870_bl_l2_bright_dim_store); 715 716static ssize_t adp8870_bl_l1_daylight_dim_show(struct device *dev, 717 struct device_attribute *attr, char *buf) 718{ 719 return adp8870_show(dev, buf, ADP8870_BLDM1); 720} 721 722static ssize_t adp8870_bl_l1_daylight_dim_store(struct device *dev, 723 struct device_attribute *attr, 724 const char *buf, size_t count) 725{ 726 return adp8870_store(dev, buf, count, ADP8870_BLDM1); 727} 728static DEVICE_ATTR(l1_daylight_dim, 0664, adp8870_bl_l1_daylight_dim_show, 729 adp8870_bl_l1_daylight_dim_store); 730 731#ifdef ADP8870_EXT_FEATURES 732static ssize_t adp8870_bl_ambient_light_level_show(struct device *dev, 733 struct device_attribute *attr, char *buf) 734{ 735 struct adp8870_bl *data = dev_get_drvdata(dev); 736 int error; 737 uint8_t reg_val; 738 uint16_t ret_val; 739 740 mutex_lock(&data->lock); 741 error = adp8870_read(data->client, ADP8870_PH1LEVL, ®_val); 742 if (error < 0) { 743 mutex_unlock(&data->lock); 744 return error; 745 } 746 ret_val = reg_val; 747 error = adp8870_read(data->client, ADP8870_PH1LEVH, ®_val); 748 mutex_unlock(&data->lock); 749 750 if (error < 0) 751 return error; 752 753 /* Return 13-bit conversion value for the first light sensor */ 754 ret_val += (reg_val & 0x1F) << 8; 755 756 return sprintf(buf, "%u\n", ret_val); 757} 758static DEVICE_ATTR(ambient_light_level, 0444, 759 adp8870_bl_ambient_light_level_show, NULL); 760 761static ssize_t adp8870_bl_ambient_light_zone_show(struct device *dev, 762 struct device_attribute *attr, char *buf) 763{ 764 struct adp8870_bl *data = dev_get_drvdata(dev); 765 int error; 766 uint8_t reg_val; 767 768 mutex_lock(&data->lock); 769 error = adp8870_read(data->client, ADP8870_CFGR, ®_val); 770 mutex_unlock(&data->lock); 771 772 if (error < 0) 773 return error; 774 775 return sprintf(buf, "%u\n", 776 ((reg_val >> CFGR_BLV_SHIFT) & CFGR_BLV_MASK) + 1); 777} 778 779static ssize_t adp8870_bl_ambient_light_zone_store(struct device *dev, 780 struct device_attribute *attr, 781 const char *buf, size_t count) 782{ 783 struct adp8870_bl *data = dev_get_drvdata(dev); 784 unsigned long val; 785 uint8_t reg_val; 786 int ret; 787 788 ret = kstrtoul(buf, 10, &val); 789 if (ret) 790 return ret; 791 792 if (val == 0) { 793 /* Enable automatic ambient light sensing */ 794 adp8870_set_bits(data->client, ADP8870_MDCR, CMP_AUTOEN); 795 } else if ((val > 0) && (val < 6)) { 796 /* Disable automatic ambient light sensing */ 797 adp8870_clr_bits(data->client, ADP8870_MDCR, CMP_AUTOEN); 798 799 /* Set user supplied ambient light zone */ 800 mutex_lock(&data->lock); 801 ret = adp8870_read(data->client, ADP8870_CFGR, ®_val); 802 if (!ret) { 803 reg_val &= ~(CFGR_BLV_MASK << CFGR_BLV_SHIFT); 804 reg_val |= (val - 1) << CFGR_BLV_SHIFT; 805 adp8870_write(data->client, ADP8870_CFGR, reg_val); 806 } 807 mutex_unlock(&data->lock); 808 } 809 810 return count; 811} 812static DEVICE_ATTR(ambient_light_zone, 0664, 813 adp8870_bl_ambient_light_zone_show, 814 adp8870_bl_ambient_light_zone_store); 815#endif 816 817static struct attribute *adp8870_bl_attributes[] = { 818 &dev_attr_l5_dark_max.attr, 819 &dev_attr_l5_dark_dim.attr, 820 &dev_attr_l4_indoor_max.attr, 821 &dev_attr_l4_indoor_dim.attr, 822 &dev_attr_l3_office_max.attr, 823 &dev_attr_l3_office_dim.attr, 824 &dev_attr_l2_bright_max.attr, 825 &dev_attr_l2_bright_dim.attr, 826 &dev_attr_l1_daylight_max.attr, 827 &dev_attr_l1_daylight_dim.attr, 828#ifdef ADP8870_EXT_FEATURES 829 &dev_attr_ambient_light_level.attr, 830 &dev_attr_ambient_light_zone.attr, 831#endif 832 NULL 833}; 834 835static const struct attribute_group adp8870_bl_attr_group = { 836 .attrs = adp8870_bl_attributes, 837}; 838 839static int adp8870_probe(struct i2c_client *client, 840 const struct i2c_device_id *id) 841{ 842 struct backlight_properties props; 843 struct backlight_device *bl; 844 struct adp8870_bl *data; 845 struct adp8870_backlight_platform_data *pdata = 846 dev_get_platdata(&client->dev); 847 uint8_t reg_val; 848 int ret; 849 850 if (!i2c_check_functionality(client->adapter, 851 I2C_FUNC_SMBUS_BYTE_DATA)) { 852 dev_err(&client->dev, "SMBUS Byte Data not Supported\n"); 853 return -EIO; 854 } 855 856 if (!pdata) { 857 dev_err(&client->dev, "no platform data?\n"); 858 return -EINVAL; 859 } 860 861 ret = adp8870_read(client, ADP8870_MFDVID, ®_val); 862 if (ret < 0) 863 return -EIO; 864 865 if (ADP8870_MANID(reg_val) != ADP8870_MANUFID) { 866 dev_err(&client->dev, "failed to probe\n"); 867 return -ENODEV; 868 } 869 870 data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); 871 if (data == NULL) 872 return -ENOMEM; 873 874 data->revid = ADP8870_DEVID(reg_val); 875 data->client = client; 876 data->pdata = pdata; 877 data->id = id->driver_data; 878 data->current_brightness = 0; 879 i2c_set_clientdata(client, data); 880 881 mutex_init(&data->lock); 882 883 memset(&props, 0, sizeof(props)); 884 props.type = BACKLIGHT_RAW; 885 props.max_brightness = props.brightness = ADP8870_MAX_BRIGHTNESS; 886 bl = devm_backlight_device_register(&client->dev, 887 dev_driver_string(&client->dev), 888 &client->dev, data, &adp8870_bl_ops, &props); 889 if (IS_ERR(bl)) { 890 dev_err(&client->dev, "failed to register backlight\n"); 891 return PTR_ERR(bl); 892 } 893 894 data->bl = bl; 895 896 if (pdata->en_ambl_sens) { 897 ret = sysfs_create_group(&bl->dev.kobj, 898 &adp8870_bl_attr_group); 899 if (ret) { 900 dev_err(&client->dev, "failed to register sysfs\n"); 901 return ret; 902 } 903 } 904 905 ret = adp8870_bl_setup(bl); 906 if (ret) { 907 ret = -EIO; 908 goto out; 909 } 910 911 backlight_update_status(bl); 912 913 dev_info(&client->dev, "Rev.%d Backlight\n", data->revid); 914 915 if (pdata->num_leds) 916 adp8870_led_probe(client); 917 918 return 0; 919 920out: 921 if (data->pdata->en_ambl_sens) 922 sysfs_remove_group(&data->bl->dev.kobj, 923 &adp8870_bl_attr_group); 924 925 return ret; 926} 927 928static int adp8870_remove(struct i2c_client *client) 929{ 930 struct adp8870_bl *data = i2c_get_clientdata(client); 931 932 adp8870_clr_bits(client, ADP8870_MDCR, NSTBY); 933 934 if (data->led) 935 adp8870_led_remove(client); 936 937 if (data->pdata->en_ambl_sens) 938 sysfs_remove_group(&data->bl->dev.kobj, 939 &adp8870_bl_attr_group); 940 941 return 0; 942} 943 944#ifdef CONFIG_PM_SLEEP 945static int adp8870_i2c_suspend(struct device *dev) 946{ 947 struct i2c_client *client = to_i2c_client(dev); 948 949 adp8870_clr_bits(client, ADP8870_MDCR, NSTBY); 950 951 return 0; 952} 953 954static int adp8870_i2c_resume(struct device *dev) 955{ 956 struct i2c_client *client = to_i2c_client(dev); 957 958 adp8870_set_bits(client, ADP8870_MDCR, NSTBY | BLEN); 959 960 return 0; 961} 962#endif 963 964static SIMPLE_DEV_PM_OPS(adp8870_i2c_pm_ops, adp8870_i2c_suspend, 965 adp8870_i2c_resume); 966 967static const struct i2c_device_id adp8870_id[] = { 968 { "adp8870", 0 }, 969 { } 970}; 971MODULE_DEVICE_TABLE(i2c, adp8870_id); 972 973static struct i2c_driver adp8870_driver = { 974 .driver = { 975 .name = KBUILD_MODNAME, 976 .pm = &adp8870_i2c_pm_ops, 977 }, 978 .probe = adp8870_probe, 979 .remove = adp8870_remove, 980 .id_table = adp8870_id, 981}; 982 983module_i2c_driver(adp8870_driver); 984 985MODULE_LICENSE("GPL v2"); 986MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); 987MODULE_DESCRIPTION("ADP8870 Backlight driver");