]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - mlp-open-source/kernel.git/blob - drivers/video/backlight/lp8860_bl.c
LP8758 - consolidated version
[mlp-open-source/kernel.git] / drivers / video / backlight / lp8860_bl.c
1 /*
2 * Simple driver for Texas Instruments lp8860 Backlight driver chip
3 *
4 * Copyright (C) 2014 Texas Instruments
5 * Author: Daniel Jeong  <gshark.jeong@gmail.com>
6 *                 Ldd Mlp <ldd-mlp@list.ti.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 *
12 */
13 #include <linux/module.h>
14 #include <linux/backlight.h>
15 #include <linux/delay.h>
16 #include <linux/err.h>
17 #include <linux/i2c.h>
18 #include <linux/platform_data/lp8860_bl.h>
19 #include <linux/regmap.h>
20 #include <linux/slab.h>
21 #include <linux/uaccess.h>
23 #define REG_CL0_BRT_H   0x00
24 #define REG_CL0_BRT_L   0x01
25 #define REG_CL0_I_H             0x02
26 #define REG_CL0_I_L             0x03
28 #define REG_CL1_BRT_H   0x04
29 #define REG_CL1_BRT_L   0x05
30 #define REG_CL1_I               0x06
32 #define REG_CL2_BRT_H   0x07
33 #define REG_CL2_BRT_L   0x08
34 #define REG_CL2_I               0x09
36 #define REG_CL3_BRT_H   0x0a
37 #define REG_CL3_BRT_L   0x0b
38 #define REG_CL3_I               0x0c
40 #define REG_CONF        0x0d
41 #define REG_STATUS      0x0e
42 #define REG_ID          0x12
44 #define REG_ROM_CTRL    0x19
45 #define REG_ROM_ULOCK   0x1a
46 #define REG_ROM_START   0x60
47 #define REG_ROM_END             0x78
49 #define REG_EEPROM_START        0x60
50 #define REG_EEPROM_END          0x78
51 #define REG_MAX 0xFF
53 #define ULCODE0 0x08
54 #define ULCODE1 0xba
55 #define ULCODE2 0xef
56 #define LCODE   0x00
58 struct lp8860_chip {
59         struct device *dev;
61         struct lp8860_platform_data *pdata;
62         struct backlight_device *bled[LP8860_LED_MAX];
63         struct regmap *regmap;
65         /* mlp test interface */
66         unsigned int reg;
67         unsigned int reg_data;
68 };
70 /* brightness control */
71 static int lp8860_bled_update_status(struct backlight_device *bl,
72                                      enum lp8860_leds nsr)
73 {
74         int ret = -EINVAL;
75         struct lp8860_chip *pchip = bl_get_data(bl);
77         if (pchip->pdata->mode)
78                 return 0;
80         if (bl->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK))
81                 bl->props.brightness = 0;
83         switch (nsr) {
84         case LP8860_LED0:
85                 ret = regmap_write(pchip->regmap,
86                                    REG_CL0_BRT_H, bl->props.brightness >> 8);
87                 ret |= regmap_write(pchip->regmap,
88                                     REG_CL0_BRT_L, bl->props.brightness & 0xff);
89                 break;
90         case LP8860_LED1:
91                 ret = regmap_write(pchip->regmap,
92                                    REG_CL1_BRT_H,
93                                    (bl->props.brightness >> 8) & 0x1f);
94                 ret |= regmap_write(pchip->regmap, REG_CL1_BRT_L,
95                                  bl->props.brightness & 0xff);
96                 break;
97         case LP8860_LED2:
98                 ret = regmap_write(pchip->regmap,
99                                    REG_CL2_BRT_H,
100                                    (bl->props.brightness >> 8) & 0x1f);
101                 ret |= regmap_write(pchip->regmap, REG_CL2_BRT_L,
102                                  bl->props.brightness & 0xff);
103                 break;
104         case LP8860_LED3:
105                 ret = regmap_write(pchip->regmap,
106                                    REG_CL3_BRT_H,
107                                    (bl->props.brightness >> 8) & 0x1f);
108                 ret |= regmap_write(pchip->regmap, REG_CL3_BRT_L,
109                                  bl->props.brightness & 0xff);
110                 break;
111         default:
112                 BUG();
113         }
114         if (ret < 0)
115                 dev_err(pchip->dev, "fail : i2c access to register.\n");
116         else
117                 ret = bl->props.brightness;
119         return ret;
122 static int lp8860_bled_get_brightness(struct backlight_device *bl,
123                                       enum lp8860_leds nsr)
125         struct lp8860_chip *pchip = bl_get_data(bl);
126         unsigned int rval_h, rval_l;
127         int ret = -EINVAL;
129         switch (nsr) {
130         case LP8860_LED0:
131                 ret = regmap_read(pchip->regmap, REG_CL0_BRT_H, &rval_h);
132                 ret |= regmap_read(pchip->regmap, REG_CL0_BRT_L, &rval_l);
133                 break;
134         case LP8860_LED1:
135                 ret = regmap_read(pchip->regmap, REG_CL1_BRT_H, &rval_h);
136                 ret |= regmap_read(pchip->regmap, REG_CL1_BRT_L, &rval_l);
137                 break;
138         case LP8860_LED2:
139                 ret = regmap_read(pchip->regmap, REG_CL2_BRT_H, &rval_h);
140                 ret |= regmap_read(pchip->regmap, REG_CL2_BRT_L, &rval_l);
141                 break;
142         case LP8860_LED3:
143                 ret = regmap_read(pchip->regmap, REG_CL3_BRT_H, &rval_h);
144                 ret |= regmap_read(pchip->regmap, REG_CL3_BRT_L, &rval_l);
145                 break;
146         default:
147                 BUG();
148         }
149         if (ret < 0) {
150                 dev_err(pchip->dev, "fail : i2c access to register.\n");
151                 return ret;
152         }
153         bl->props.brightness = (rval_h << 8) | rval_l;
154         return bl->props.brightness;
157 static int lp8860_update_status_bled0(struct backlight_device *bl)
159         return lp8860_bled_update_status(bl, LP8860_LED0);
162 static int lp8860_get_brightness_bled0(struct backlight_device *bl)
164         return lp8860_bled_get_brightness(bl, LP8860_LED0);
167 static int lp8860_update_status_bled1(struct backlight_device *bl)
169         return lp8860_bled_update_status(bl, LP8860_LED1);
172 static int lp8860_get_brightness_bled1(struct backlight_device *bl)
174         return lp8860_bled_get_brightness(bl, LP8860_LED1);
177 static int lp8860_update_status_bled2(struct backlight_device *bl)
179         return lp8860_bled_update_status(bl, LP8860_LED2);
182 static int lp8860_get_brightness_bled2(struct backlight_device *bl)
184         return lp8860_bled_get_brightness(bl, LP8860_LED2);
187 static int lp8860_update_status_bled3(struct backlight_device *bl)
189         return lp8860_bled_update_status(bl, LP8860_LED3);
192 static int lp8860_get_brightness_bled3(struct backlight_device *bl)
194         return lp8860_bled_get_brightness(bl, LP8860_LED3);
197 #define lp8860_bled_ops(_id)\
198 {\
199         .options = BL_CORE_SUSPENDRESUME,\
200         .update_status = lp8860_update_status_bled##_id,\
201         .get_brightness = lp8860_get_brightness_bled##_id,\
204 static const struct backlight_ops lp8860_bled_ops[LP8860_LED_MAX] = {
205         [LP8860_LED0] = lp8860_bled_ops(0),
206         [LP8860_LED1] = lp8860_bled_ops(1),
207         [LP8860_LED2] = lp8860_bled_ops(2),
208         [LP8860_LED3] = lp8860_bled_ops(3),
209 };
211 /* current control */
212 static int lp8860_set_current(struct device *dev,
213                               const char *buf, enum lp8860_leds nsr)
215         struct lp8860_chip *pchip = dev_get_drvdata(dev);
216         unsigned int ival;
217         ssize_t ret;
219         ret = kstrtouint(buf, 10, &ival);
220         if (ret)
221                 return ret;
223         switch (nsr) {
224         case LP8860_LED0:
225                 ival = min_t(unsigned int, ival, LP8860_LED0_BR_MAX);
226                 ret = regmap_write(pchip->regmap, REG_CL0_I_H, ival >> 8);
227                 ret |= regmap_write(pchip->regmap, REG_CL0_I_L, ival & 0xff);
228                 break;
229         case LP8860_LED1:
230                 ival = min_t(unsigned int, ival, LP8860_LED1_BR_MAX);
231                 ret = regmap_write(pchip->regmap, REG_CL1_I, ival & 0xff);
232                 break;
233         case LP8860_LED2:
234                 ival = min_t(unsigned int, ival, LP8860_LED2_BR_MAX);
235                 ret = regmap_write(pchip->regmap, REG_CL2_I, ival & 0xff);
236                 break;
237         case LP8860_LED3:
238                 ival = min_t(unsigned int, ival, LP8860_LED3_BR_MAX);
239                 ret = regmap_write(pchip->regmap, REG_CL3_I, ival & 0xff);
240                 break;
241         default:
242                 BUG();
243         }
244         if (ret < 0)
245                 dev_err(pchip->dev, "fail : i2c access error.\n");
247         return ret;
250 static ssize_t lp8860_current_store_bled0(struct device *dev,
251                                           struct device_attribute *devAttr,
252                                           const char *buf, size_t size)
254         int ret;
256         ret = lp8860_set_current(dev, buf, LP8860_LED0);
257         if (ret < 0)
258                 return ret;
259         return size;
262 static ssize_t lp8860_current_store_bled1(struct device *dev,
263                                           struct device_attribute *devAttr,
264                                           const char *buf, size_t size)
266         int ret;
268         ret = lp8860_set_current(dev, buf, LP8860_LED1);
269         if (ret < 0)
270                 return ret;
271         return size;
274 static ssize_t lp8860_current_store_bled2(struct device *dev,
275                                           struct device_attribute *devAttr,
276                                           const char *buf, size_t size)
278         int ret;
280         ret = lp8860_set_current(dev, buf, LP8860_LED2);
281         if (ret < 0)
282                 return ret;
283         return size;
286 static ssize_t lp8860_current_store_bled3(struct device *dev,
287                                           struct device_attribute *devAttr,
288                                           const char *buf, size_t size)
290         int ret;
292         ret = lp8860_set_current(dev, buf, LP8860_LED3);
293         if (ret < 0)
294                 return ret;
295         return size;
298 #define lp8860_attr(_name, _show, _store)\
299 {\
300         .attr = {\
301                 .name = _name,\
302                 .mode = S_IWUSR|S_IRUSR,\
303         },\
304         .show = _show,\
305         .store = _store,\
308 static struct device_attribute lp8860_dev_attr[LP8860_LED_MAX] = {
309         [LP8860_LED0] = lp8860_attr("current", NULL,
310                                     lp8860_current_store_bled0),
311         [LP8860_LED1] = lp8860_attr("current", NULL,
312                                     lp8860_current_store_bled1),
313         [LP8860_LED2] = lp8860_attr("current", NULL,
314                                     lp8860_current_store_bled2),
315         [LP8860_LED3] = lp8860_attr("current", NULL,
316                                     lp8860_current_store_bled3),
317 };
319 /* get argument from input buffer */
320 static int token_store_data(char *buf, char *cmd, unsigned int *reg,
321                             unsigned int *mask, unsigned int *data)
323         char *token;
324         char *desc = " >\t\n";
325         unsigned int tokenval;
327         /* command */
328         token = strsep(&buf, desc);
329         if (token == NULL)
330                 goto err_out;
331         *cmd = *token;
332         if (*cmd == 'w' || *cmd == 'r') {
333                 /* register no. to read/write */
334                 token = strsep(&buf, desc);
335                 if (token == NULL)
336                         goto err_out;
337                 if (kstrtouint((const char *)token, 16, &tokenval) < 0)
338                         goto err_out;
339                 *reg = tokenval;
341                 /* write command  */
342                 if (*cmd == 'w') {
343                         /* mask bit */
344                         token = strsep(&buf, desc);
345                         if (token == NULL)
346                                 goto err_out;
347                         if (kstrtouint((const char *)token, 16, &tokenval) < 0)
348                                 goto err_out;
349                         *mask = tokenval;
351                         /* write data */
352                         token = strsep(&buf, desc);
353                         if (token == NULL)
354                                 goto err_out;
355                         if (kstrtouint((const char *)token, 16, &tokenval) < 0)
356                                 goto err_out;
357                         *data = tokenval;
358                 } else {
359                         *mask = 0;
360                         *data = 0;
361                 }
362                 return 0;
363         }
364 err_out:
365         return -EINVAL;
368 /* register access */
369 static ssize_t lp8860_register_show(struct device *dev,
370                                     struct device_attribute *devAttr, char *buf)
372         struct lp8860_chip *pchip = dev_get_drvdata(dev);
374         if (pchip->reg < 0x0 || pchip->reg > 0xff)
375                 return sprintf(buf, "Fail : invalid access %x\n", pchip->reg);
376         return sprintf(buf, "%x %x", pchip->reg, pchip->reg_data);
379 static ssize_t lp8860_register_store(struct device *dev,
380                                      struct device_attribute *devAttr,
381                                      const char *buf, size_t size)
383         struct lp8860_chip *pchip = dev_get_drvdata(dev);
384         int ret;
385         char cmd;
386         unsigned int reg, mask, data;
388         ret = token_store_data((char *)buf, &cmd, &reg, &mask, &data);
389         if (ret < 0)
390                 goto err_input;
391         pchip->reg = reg;
392         if (cmd == 'w' || cmd == 'W') {
393                 ret = regmap_update_bits(pchip->regmap, reg, mask, data);
394                 if (ret < 0) {
395                         pchip->reg_data = ret;
396                         goto err_i2c;
397                 }
398         }
399         ret = regmap_read(pchip->regmap, reg, &data);
400         if (ret < 0) {
401                 pchip->reg_data = ret;
402                 goto err_i2c;
403         }
404         pchip->reg_data = data;
405         return size;
407 err_i2c:
408         dev_err(pchip->dev, "fail : i2c access error\n");
409         return size;
411 err_input:
412         dev_err(pchip->dev, "failed : input fail\n");
413         return size;
416 static DEVICE_ATTR(registers, S_IWUSR | S_IRUSR,
417                    lp8860_register_show, lp8860_register_store);
419 /*
420  * Input data
421  * 0 : read from eeprom to eeprom registers.
422  * 1 : eeprom unlock before writing
423  * 2 : brun and lock
424  */
425 static ssize_t lp8860_eeprom_store(struct device *dev,
426                                    struct device_attribute *devAttr,
427                                    const char *buf, size_t size)
429         struct lp8860_chip *pchip = dev_get_drvdata(dev);
430         unsigned int ival;
431         int ret;
433         ret = kstrtouint(buf, 10, &ival);
434         if (ret)
435                 goto err_input;
437         switch (ival) {
438                 /*
439                  * Read sequence 
440                  * 1. Unlock EEPROM by writing
441                  *    the unlock codes to register 1Ah(08, BA, EF)
442                  * 2. Write EE_PROG to 1 in address 19h. (02h to address 19h)
443                  * 3. Wait 100ms
444                  * 4. Write EE_PROG to 0 in address 19h. (00h to address 19h)
445                  * 5. Lock EEPROM by writing 0h to register 1Ah.
446                  */
447         case 0:
448                 ret = regmap_write(pchip->regmap, REG_ROM_ULOCK, ULCODE0);
449                 ret |= regmap_write(pchip->regmap, REG_ROM_ULOCK, ULCODE1);
450                 ret |= regmap_write(pchip->regmap, REG_ROM_ULOCK, ULCODE2);
451                 ret |= regmap_write(pchip->regmap, REG_ROM_CTRL, 0x01);
452                 msleep(100);
453                 ret |= regmap_write(pchip->regmap, REG_ROM_CTRL, 0x00);
454                 ret |= regmap_write(pchip->regmap, REG_ROM_ULOCK, LCODE);
455                 if (ret < 0)
456                         goto err_i2c;
457                 dev_info(pchip->dev, "LP8860 EEPROM Read Done\n");
458                 break;
459                 /*
460                  * Programming sequence 
461                  *    (program data permanently from registers to NVM)
462                  * 1. Unlock EEPROM by writing
463                  *    the unlock codes to register 1Ah(08, BA, EF)
464                  * 2. Write data to EEPROM registers (address 60h...78h)
465                  *    (use registers device node)
466                  * 3. Write EE_PROG to 1 in address 19h. (02h to address 19h)
467                  * 4. Wait 100ms
468                  * 5. Write EE_PROG to 0 in address 19h. (00h to address 19h)
469                  * 6. Lock EEPROM by writing 0h to register 1Ah.
470                  */
471         case 1:
472                 ret = regmap_write(pchip->regmap, REG_ROM_ULOCK, ULCODE0);
473                 ret |= regmap_write(pchip->regmap, REG_ROM_ULOCK, ULCODE1);
474                 ret |= regmap_write(pchip->regmap, REG_ROM_ULOCK, ULCODE2);
475                 if (ret < 0)
476                         goto err_i2c;
477                 dev_info(pchip->dev, "LP8860 EEPROM is Unlocked\n");
478                 break;
479         case 2:
480                 ret = regmap_write(pchip->regmap, REG_ROM_CTRL, 0x02);
481                 msleep(100);
482                 ret |= regmap_write(pchip->regmap, REG_ROM_CTRL, 0x00);
483                 ret |= regmap_write(pchip->regmap, REG_ROM_ULOCK, LCODE);
484                 if (ret < 0)
485                         goto err_i2c;
486                 dev_info(pchip->dev, "LP8860 EEPROM is Locked\n");
487                 break;
488         default:
489                 goto err_input;
490         }
492         return size;
494 err_i2c:
495         dev_err(pchip->dev, "fail : i2c access error.\n");
496         return ret;
498 err_input:
499         dev_err(pchip->dev, "fail : input fail.\n");
500         return -EINVAL;
503 static DEVICE_ATTR(eeprom, S_IWUSR, NULL, lp8860_eeprom_store);
505 /* backlight register and remove */
506 static char *lp8860_bled_name[LP8860_LED_MAX] = {
507         [LP8860_LED0] = "bled0",
508         [LP8860_LED1] = "bled1",
509         [LP8860_LED2] = "bled2",
510         [LP8860_LED3] = "bled3",
511 };
513 static int lp8860_backlight_remove(struct lp8860_chip *pchip)
515         int icnt;
517         device_remove_file(&(pchip->bled[0]->dev), &dev_attr_registers);
518         device_remove_file(&(pchip->bled[0]->dev), &dev_attr_eeprom);
519         for (icnt = LP8860_LED0; icnt < LP8860_LED_MAX; icnt++) {
520                 if (pchip->bled[icnt]) {
521                         backlight_device_unregister(pchip->bled[icnt]);
522                         device_remove_file(&(pchip->bled[icnt]->dev),
523                                            &lp8860_dev_attr[icnt]);
524                 }
525         }
526         return 0;
529 static int lp8860_backlight_registers(struct lp8860_chip *pchip)
531         struct backlight_properties props;
532         struct lp8860_platform_data *pdata = pchip->pdata;
533         int icnt, ret;
535         props.type = BACKLIGHT_RAW;
536         for (icnt = LP8860_LED0; icnt < LP8860_LED_MAX; icnt++) {
537                 props.max_brightness = pdata->max_brt[icnt];
538                 pchip->bled[icnt] =
539                     backlight_device_register(lp8860_bled_name[icnt],
540                                               pchip->dev, pchip,
541                                               &lp8860_bled_ops[icnt], &props);
542                 if (IS_ERR(pchip->bled[icnt])) {
543                         dev_err(pchip->dev, "fail : backlight register.\n");
544                         ret = PTR_ERR(pchip->bled);
545                         goto err_out;
546                 }
548                 ret = device_create_file(&(pchip->bled[icnt]->dev),
549                                          &lp8860_dev_attr[icnt]);
550                 if (ret < 0) {
551                         dev_err(pchip->dev, "fail : to add sysfs entries.\n");
552                         goto err_out;
553                 }
554         }
555         /* to access eeprom */
556         ret = device_create_file(&(pchip->bled[LP8860_LED0]->dev),
557                                  &dev_attr_eeprom);
558         if (ret < 0) {
559                 dev_err(pchip->dev, "fail : to add sysfs entries.\n");
560                 goto err_out;
561         }
562         /* to access registers */
563         ret = device_create_file(&(pchip->bled[LP8860_LED0]->dev),
564                                  &dev_attr_registers);
565         if (ret < 0) {
566                 dev_err(pchip->dev, "failed : add sysfs entries\n");
567                 goto err_out;
568         }
569         return 0;
571 err_out:
572         lp8860_backlight_remove(pchip);
573         return ret;
576 static const struct regmap_config lp8860_regmap = {
577         .reg_bits = 8,
578         .val_bits = 8,
579         .max_register = REG_MAX,
580 };
582 static int lp8860_probe(struct i2c_client *client,
583                         const struct i2c_device_id *id)
585         struct lp8860_chip *pchip;
586         struct lp8860_platform_data *pdata = dev_get_platdata(&client->dev);
587         int ret, icnt;
589         if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
590                 dev_err(&client->dev, "fail : i2c functionality check.\n");
591                 return -EOPNOTSUPP;
592         }
594         pchip = devm_kzalloc(&client->dev,
595                              sizeof(struct lp8860_chip), GFP_KERNEL);
596         if (!pchip)
597                 return -ENOMEM;
598         pchip->dev = &client->dev;
600         pchip->regmap = devm_regmap_init_i2c(client, &lp8860_regmap);
601         if (IS_ERR(pchip->regmap)) {
602                 ret = PTR_ERR(pchip->regmap);
603                 dev_err(pchip->dev, "fail : allocate i2c register map.\n");
604                 return ret;
605         }
607         if (pdata == NULL) {
608                 pdata = devm_kzalloc(pchip->dev,
609                                      sizeof(struct lp8860_platform_data),
610                                      GFP_KERNEL);
611                 if (pdata == NULL)
612                         return -ENOMEM;
613                 pdata->max_brt[LP8860_LED0] = LP8860_LED0_BR_MAX;
614                 for (icnt = LP8860_LED1; icnt < LP8860_LED_MAX; icnt++)
615                         pdata->max_brt[icnt] = LP8860_LED1_BR_MAX;
616                 pchip->pdata = pdata;
617         } else {
618                 pchip->pdata = pdata;
619         }
620         i2c_set_clientdata(client, pchip);
621         ret = lp8860_backlight_registers(pchip);
622         return ret;
625 static int lp8860_remove(struct i2c_client *client)
627         return lp8860_backlight_remove(i2c_get_clientdata(client));
630 static const struct i2c_device_id lp8860_id[] = {
631         {LP8860_NAME, 0},
632         {}
633 };
635 MODULE_DEVICE_TABLE(i2c, lp8860_id);
636 static struct i2c_driver lp8860_i2c_driver = {
637         .driver = {
638                    .name = LP8860_NAME,
639                    },
640         .probe = lp8860_probe,
641         .remove = lp8860_remove,
642         .id_table = lp8860_id,
643 };
645 module_i2c_driver(lp8860_i2c_driver);
647 MODULE_DESCRIPTION("Texas Instruments LP8860 Backlight Driver");
648 MODULE_AUTHOR("Daniel Jeong <gshark.jeong@gmail.com>");
649 MODULE_AUTHOR("Ldd Mlp <ldd-mlp@list.ti.com>");
650 MODULE_LICENSE("GPL v2");