File Index
Files and directories
Sign in
FILE

adp8870_bl.c

File preview with syntax highlighting.

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