PageRenderTime 62ms CodeModel.GetById 21ms app.highlight 27ms RepoModel.GetById 1ms app.codeStats 0ms

/drivers/video/backlight/adp8870_bl.c

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