Commit 011af7bc7cd188a0310e2d26cdc2cc5d90148b0c
Committed by
Linus Torvalds
1 parent
3b49aacd0e
Exists in
smarc-l5.0.0_1.0.0-ga
and in
5 other branches
drivers/leds/leds-lp5521.c: support led pattern data
The lp5521 has autonomous operation mode without external control. Using lp5521_platform_data, various led patterns can be configurable. For supporting this feature, new functions and device attribute are added. Structure of lp5521_led_pattern: 3 channels are supported - red, green and blue. Pattern(s) of each channel and numbers of pattern(s) are defined in the pla= tform data. Pattern data are hexa codes which include pattern commands such like set pwm, wait, ramp up/down, branch and so on. Pattern mode functions: * lp5521_clear_program_memory Before running new led pattern, program memory should be cleared. * lp5521_write_program_memory Pattern data updated in the program memory via the i2c. * lp5521_get_pattern Get pattern from predefined in the platform data. * lp5521_run_led_pattern Stop current pattern or run new pattern. Transition time is required between different operation mode. Device attribute - 'led_pattern': To load specific led pattern, new device attribute is added. When the lp5521 driver is unloaded, stop current led pattern mode. Documentation updated : description about how to define the led patterns and example. [akpm@linux-foundation.org: checkpatch fixes] Signed-off-by: Milo(Woogyom) Kim <milo.kim@ti.com> Acked-by: Linus Walleij <linus.walleij@linaro.org> Cc: Arun MURTHY <arun.murthy@stericsson.com> Cc: Srinidhi Kasagar <srinidhi.kasagar@stericsson.com> Cc: Richard Purdie <rpurdie@rpsys.net> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Showing 3 changed files with 150 additions and 1 deletions Side-by-side Diff
Documentation/leds/leds-lp5521.txt
... | ... | @@ -111,4 +111,42 @@ |
111 | 111 | .clock_mode = LP5521_CLOCK_INT, |
112 | 112 | .update_config = LP5521_CONFIGS, |
113 | 113 | }; |
114 | + | |
115 | +LED patterns : LP5521 has autonomous operation without external control. | |
116 | +Pattern data can be defined in the platform data. | |
117 | + | |
118 | +example of led pattern data : | |
119 | + | |
120 | +/* RGB(50,5,0) 500ms on, 500ms off, infinite loop */ | |
121 | +static u8 pattern_red[] = { | |
122 | + 0x40, 0x32, 0x60, 0x00, 0x40, 0x00, 0x60, 0x00, | |
123 | + }; | |
124 | + | |
125 | +static u8 pattern_green[] = { | |
126 | + 0x40, 0x05, 0x60, 0x00, 0x40, 0x00, 0x60, 0x00, | |
127 | + }; | |
128 | + | |
129 | +static struct lp5521_led_pattern board_led_patterns[] = { | |
130 | + { | |
131 | + .r = pattern_red, | |
132 | + .g = pattern_green, | |
133 | + .size_r = ARRAY_SIZE(pattern_red), | |
134 | + .size_g = ARRAY_SIZE(pattern_green), | |
135 | + }, | |
136 | +}; | |
137 | + | |
138 | +static struct lp5521_platform_data lp5521_platform_data = { | |
139 | + .led_config = lp5521_led_config, | |
140 | + .num_channels = ARRAY_SIZE(lp5521_led_config), | |
141 | + .clock_mode = LP5521_CLOCK_EXT, | |
142 | + .patterns = board_led_patterns, | |
143 | + .num_patterns = ARRAY_SIZE(board_led_patterns), | |
144 | +}; | |
145 | + | |
146 | +Then predefined led pattern(s) can be executed via the sysfs. | |
147 | +To start the pattern #1, | |
148 | +# echo 1 > /sys/bus/i2c/devices/xxxx/led_pattern | |
149 | +(xxxx : i2c bus & slave address) | |
150 | +To end the pattern, | |
151 | +# echo 0 > /sys/bus/i2c/devices/xxxx/led_pattern |
drivers/leds/leds-lp5521.c
... | ... | @@ -88,6 +88,9 @@ |
88 | 88 | /* default R channel current register value */ |
89 | 89 | #define LP5521_REG_R_CURR_DEFAULT 0xAF |
90 | 90 | |
91 | +/* Pattern Mode */ | |
92 | +#define PATTERN_OFF 0 | |
93 | + | |
91 | 94 | struct lp5521_engine { |
92 | 95 | int id; |
93 | 96 | u8 mode; |
... | ... | @@ -493,7 +496,7 @@ |
493 | 496 | ssize_t ret; |
494 | 497 | unsigned long curr; |
495 | 498 | |
496 | - if (strict_strtoul(buf, 0, &curr)) | |
499 | + if (kstrtoul(buf, 0, &curr)) | |
497 | 500 | return -EINVAL; |
498 | 501 | |
499 | 502 | if (curr > led->max_current) |
... | ... | @@ -525,6 +528,100 @@ |
525 | 528 | return sprintf(buf, "%s\n", ret ? "FAIL" : "OK"); |
526 | 529 | } |
527 | 530 | |
531 | +static void lp5521_clear_program_memory(struct i2c_client *cl) | |
532 | +{ | |
533 | + int i; | |
534 | + u8 rgb_mem[] = { | |
535 | + LP5521_REG_R_PROG_MEM, | |
536 | + LP5521_REG_G_PROG_MEM, | |
537 | + LP5521_REG_B_PROG_MEM, | |
538 | + }; | |
539 | + | |
540 | + for (i = 0; i < ARRAY_SIZE(rgb_mem); i++) { | |
541 | + lp5521_write(cl, rgb_mem[i], 0); | |
542 | + lp5521_write(cl, rgb_mem[i] + 1, 0); | |
543 | + } | |
544 | +} | |
545 | + | |
546 | +static void lp5521_write_program_memory(struct i2c_client *cl, | |
547 | + u8 base, u8 *rgb, int size) | |
548 | +{ | |
549 | + int i; | |
550 | + | |
551 | + if (!rgb || size <= 0) | |
552 | + return; | |
553 | + | |
554 | + for (i = 0; i < size; i++) | |
555 | + lp5521_write(cl, base + i, *(rgb + i)); | |
556 | + | |
557 | + lp5521_write(cl, base + i, 0); | |
558 | + lp5521_write(cl, base + i + 1, 0); | |
559 | +} | |
560 | + | |
561 | +static inline struct lp5521_led_pattern *lp5521_get_pattern | |
562 | + (struct lp5521_chip *chip, u8 offset) | |
563 | +{ | |
564 | + struct lp5521_led_pattern *ptn; | |
565 | + ptn = chip->pdata->patterns + (offset - 1); | |
566 | + return ptn; | |
567 | +} | |
568 | + | |
569 | +static void lp5521_run_led_pattern(int mode, struct lp5521_chip *chip) | |
570 | +{ | |
571 | + struct lp5521_led_pattern *ptn; | |
572 | + struct i2c_client *cl = chip->client; | |
573 | + int num_patterns = chip->pdata->num_patterns; | |
574 | + | |
575 | + if (mode > num_patterns || !(chip->pdata->patterns)) | |
576 | + return; | |
577 | + | |
578 | + if (mode == PATTERN_OFF) { | |
579 | + lp5521_write(cl, LP5521_REG_ENABLE, | |
580 | + LP5521_MASTER_ENABLE | LP5521_LOGARITHMIC_PWM); | |
581 | + usleep_range(1000, 2000); | |
582 | + lp5521_write(cl, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT); | |
583 | + } else { | |
584 | + ptn = lp5521_get_pattern(chip, mode); | |
585 | + if (!ptn) | |
586 | + return; | |
587 | + | |
588 | + lp5521_write(cl, LP5521_REG_OP_MODE, LP5521_CMD_LOAD); | |
589 | + usleep_range(1000, 2000); | |
590 | + | |
591 | + lp5521_clear_program_memory(cl); | |
592 | + | |
593 | + lp5521_write_program_memory(cl, LP5521_REG_R_PROG_MEM, | |
594 | + ptn->r, ptn->size_r); | |
595 | + lp5521_write_program_memory(cl, LP5521_REG_G_PROG_MEM, | |
596 | + ptn->g, ptn->size_g); | |
597 | + lp5521_write_program_memory(cl, LP5521_REG_B_PROG_MEM, | |
598 | + ptn->b, ptn->size_b); | |
599 | + | |
600 | + lp5521_write(cl, LP5521_REG_OP_MODE, LP5521_CMD_RUN); | |
601 | + usleep_range(1000, 2000); | |
602 | + lp5521_write(cl, LP5521_REG_ENABLE, | |
603 | + LP5521_MASTER_ENABLE | LP5521_LOGARITHMIC_PWM | | |
604 | + LP5521_EXEC_RUN); | |
605 | + } | |
606 | +} | |
607 | + | |
608 | +static ssize_t store_led_pattern(struct device *dev, | |
609 | + struct device_attribute *attr, | |
610 | + const char *buf, size_t len) | |
611 | +{ | |
612 | + struct lp5521_chip *chip = i2c_get_clientdata(to_i2c_client(dev)); | |
613 | + unsigned long val; | |
614 | + int ret; | |
615 | + | |
616 | + ret = strict_strtoul(buf, 16, &val); | |
617 | + if (ret) | |
618 | + return ret; | |
619 | + | |
620 | + lp5521_run_led_pattern(val, chip); | |
621 | + | |
622 | + return len; | |
623 | +} | |
624 | + | |
528 | 625 | /* led class device attributes */ |
529 | 626 | static DEVICE_ATTR(led_current, S_IRUGO | S_IWUSR, show_current, store_current); |
530 | 627 | static DEVICE_ATTR(max_current, S_IRUGO , show_max_current, NULL); |
... | ... | @@ -550,6 +647,7 @@ |
550 | 647 | static DEVICE_ATTR(engine2_load, S_IWUSR, NULL, store_engine2_load); |
551 | 648 | static DEVICE_ATTR(engine3_load, S_IWUSR, NULL, store_engine3_load); |
552 | 649 | static DEVICE_ATTR(selftest, S_IRUGO, lp5521_selftest, NULL); |
650 | +static DEVICE_ATTR(led_pattern, S_IWUSR, NULL, store_led_pattern); | |
553 | 651 | |
554 | 652 | static struct attribute *lp5521_attributes[] = { |
555 | 653 | &dev_attr_engine1_mode.attr, |
... | ... | @@ -559,6 +657,7 @@ |
559 | 657 | &dev_attr_engine1_load.attr, |
560 | 658 | &dev_attr_engine2_load.attr, |
561 | 659 | &dev_attr_engine3_load.attr, |
660 | + &dev_attr_led_pattern.attr, | |
562 | 661 | NULL |
563 | 662 | }; |
564 | 663 | |
... | ... | @@ -761,6 +860,7 @@ |
761 | 860 | struct lp5521_chip *chip = i2c_get_clientdata(client); |
762 | 861 | int i; |
763 | 862 | |
863 | + lp5521_run_led_pattern(PATTERN_OFF, chip); | |
764 | 864 | lp5521_unregister_sysfs(client); |
765 | 865 | |
766 | 866 | for (i = 0; i < chip->num_leds; i++) { |
include/linux/leds-lp5521.h
... | ... | @@ -32,6 +32,15 @@ |
32 | 32 | u8 max_current; |
33 | 33 | }; |
34 | 34 | |
35 | +struct lp5521_led_pattern { | |
36 | + u8 *r; | |
37 | + u8 *g; | |
38 | + u8 *b; | |
39 | + u8 size_r; | |
40 | + u8 size_g; | |
41 | + u8 size_b; | |
42 | +}; | |
43 | + | |
35 | 44 | #define LP5521_CLOCK_AUTO 0 |
36 | 45 | #define LP5521_CLOCK_INT 1 |
37 | 46 | #define LP5521_CLOCK_EXT 2 |
... | ... | @@ -57,6 +66,8 @@ |
57 | 66 | void (*enable)(bool state); |
58 | 67 | const char *label; |
59 | 68 | u8 update_config; |
69 | + struct lp5521_led_pattern *patterns; | |
70 | + int num_patterns; | |
60 | 71 | }; |
61 | 72 | |
62 | 73 | #endif /* __LINUX_LP5521_H */ |