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