1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * leds-bd2802.c - RGB LED Driver
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (C) 2009 Samsung Electronics
6*4882a593Smuzhiyun * Kim Kyuwon <q1.kim@samsung.com>
7*4882a593Smuzhiyun *
8*4882a593Smuzhiyun * Datasheet: http://www.rohm.com/products/databook/driver/pdf/bd2802gu-e.pdf
9*4882a593Smuzhiyun */
10*4882a593Smuzhiyun
11*4882a593Smuzhiyun #include <linux/module.h>
12*4882a593Smuzhiyun #include <linux/i2c.h>
13*4882a593Smuzhiyun #include <linux/gpio/consumer.h>
14*4882a593Smuzhiyun #include <linux/delay.h>
15*4882a593Smuzhiyun #include <linux/leds.h>
16*4882a593Smuzhiyun #include <linux/leds-bd2802.h>
17*4882a593Smuzhiyun #include <linux/slab.h>
18*4882a593Smuzhiyun #include <linux/pm.h>
19*4882a593Smuzhiyun
20*4882a593Smuzhiyun #define LED_CTL(rgb2en, rgb1en) ((rgb2en) << 4 | ((rgb1en) << 0))
21*4882a593Smuzhiyun
22*4882a593Smuzhiyun #define BD2802_LED_OFFSET 0xa
23*4882a593Smuzhiyun #define BD2802_COLOR_OFFSET 0x3
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun #define BD2802_REG_CLKSETUP 0x00
26*4882a593Smuzhiyun #define BD2802_REG_CONTROL 0x01
27*4882a593Smuzhiyun #define BD2802_REG_HOURSETUP 0x02
28*4882a593Smuzhiyun #define BD2802_REG_CURRENT1SETUP 0x03
29*4882a593Smuzhiyun #define BD2802_REG_CURRENT2SETUP 0x04
30*4882a593Smuzhiyun #define BD2802_REG_WAVEPATTERN 0x05
31*4882a593Smuzhiyun
32*4882a593Smuzhiyun #define BD2802_CURRENT_032 0x10 /* 3.2mA */
33*4882a593Smuzhiyun #define BD2802_CURRENT_000 0x00 /* 0.0mA */
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun #define BD2802_PATTERN_FULL 0x07
36*4882a593Smuzhiyun #define BD2802_PATTERN_HALF 0x03
37*4882a593Smuzhiyun
38*4882a593Smuzhiyun enum led_ids {
39*4882a593Smuzhiyun LED1,
40*4882a593Smuzhiyun LED2,
41*4882a593Smuzhiyun LED_NUM,
42*4882a593Smuzhiyun };
43*4882a593Smuzhiyun
44*4882a593Smuzhiyun enum led_colors {
45*4882a593Smuzhiyun RED,
46*4882a593Smuzhiyun GREEN,
47*4882a593Smuzhiyun BLUE,
48*4882a593Smuzhiyun };
49*4882a593Smuzhiyun
50*4882a593Smuzhiyun enum led_bits {
51*4882a593Smuzhiyun BD2802_OFF,
52*4882a593Smuzhiyun BD2802_BLINK,
53*4882a593Smuzhiyun BD2802_ON,
54*4882a593Smuzhiyun };
55*4882a593Smuzhiyun
56*4882a593Smuzhiyun /*
57*4882a593Smuzhiyun * State '0' : 'off'
58*4882a593Smuzhiyun * State '1' : 'blink'
59*4882a593Smuzhiyun * State '2' : 'on'.
60*4882a593Smuzhiyun */
61*4882a593Smuzhiyun struct led_state {
62*4882a593Smuzhiyun unsigned r:2;
63*4882a593Smuzhiyun unsigned g:2;
64*4882a593Smuzhiyun unsigned b:2;
65*4882a593Smuzhiyun };
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun struct bd2802_led {
68*4882a593Smuzhiyun struct bd2802_led_platform_data *pdata;
69*4882a593Smuzhiyun struct i2c_client *client;
70*4882a593Smuzhiyun struct gpio_desc *reset;
71*4882a593Smuzhiyun struct rw_semaphore rwsem;
72*4882a593Smuzhiyun
73*4882a593Smuzhiyun struct led_state led[2];
74*4882a593Smuzhiyun
75*4882a593Smuzhiyun /*
76*4882a593Smuzhiyun * Making led_classdev as array is not recommended, because array
77*4882a593Smuzhiyun * members prevent using 'container_of' macro. So repetitive works
78*4882a593Smuzhiyun * are needed.
79*4882a593Smuzhiyun */
80*4882a593Smuzhiyun struct led_classdev cdev_led1r;
81*4882a593Smuzhiyun struct led_classdev cdev_led1g;
82*4882a593Smuzhiyun struct led_classdev cdev_led1b;
83*4882a593Smuzhiyun struct led_classdev cdev_led2r;
84*4882a593Smuzhiyun struct led_classdev cdev_led2g;
85*4882a593Smuzhiyun struct led_classdev cdev_led2b;
86*4882a593Smuzhiyun
87*4882a593Smuzhiyun /*
88*4882a593Smuzhiyun * Advanced Configuration Function(ADF) mode:
89*4882a593Smuzhiyun * In ADF mode, user can set registers of BD2802GU directly,
90*4882a593Smuzhiyun * therefore BD2802GU doesn't enter reset state.
91*4882a593Smuzhiyun */
92*4882a593Smuzhiyun int adf_on;
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun enum led_ids led_id;
95*4882a593Smuzhiyun enum led_colors color;
96*4882a593Smuzhiyun enum led_bits state;
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun /* General attributes of RGB LEDs */
99*4882a593Smuzhiyun int wave_pattern;
100*4882a593Smuzhiyun int rgb_current;
101*4882a593Smuzhiyun };
102*4882a593Smuzhiyun
103*4882a593Smuzhiyun
104*4882a593Smuzhiyun /*--------------------------------------------------------------*/
105*4882a593Smuzhiyun /* BD2802GU helper functions */
106*4882a593Smuzhiyun /*--------------------------------------------------------------*/
107*4882a593Smuzhiyun
bd2802_is_rgb_off(struct bd2802_led * led,enum led_ids id,enum led_colors color)108*4882a593Smuzhiyun static inline int bd2802_is_rgb_off(struct bd2802_led *led, enum led_ids id,
109*4882a593Smuzhiyun enum led_colors color)
110*4882a593Smuzhiyun {
111*4882a593Smuzhiyun switch (color) {
112*4882a593Smuzhiyun case RED:
113*4882a593Smuzhiyun return !led->led[id].r;
114*4882a593Smuzhiyun case GREEN:
115*4882a593Smuzhiyun return !led->led[id].g;
116*4882a593Smuzhiyun case BLUE:
117*4882a593Smuzhiyun return !led->led[id].b;
118*4882a593Smuzhiyun default:
119*4882a593Smuzhiyun dev_err(&led->client->dev, "%s: Invalid color\n", __func__);
120*4882a593Smuzhiyun return -EINVAL;
121*4882a593Smuzhiyun }
122*4882a593Smuzhiyun }
123*4882a593Smuzhiyun
bd2802_is_led_off(struct bd2802_led * led,enum led_ids id)124*4882a593Smuzhiyun static inline int bd2802_is_led_off(struct bd2802_led *led, enum led_ids id)
125*4882a593Smuzhiyun {
126*4882a593Smuzhiyun if (led->led[id].r || led->led[id].g || led->led[id].b)
127*4882a593Smuzhiyun return 0;
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun return 1;
130*4882a593Smuzhiyun }
131*4882a593Smuzhiyun
bd2802_is_all_off(struct bd2802_led * led)132*4882a593Smuzhiyun static inline int bd2802_is_all_off(struct bd2802_led *led)
133*4882a593Smuzhiyun {
134*4882a593Smuzhiyun int i;
135*4882a593Smuzhiyun
136*4882a593Smuzhiyun for (i = 0; i < LED_NUM; i++)
137*4882a593Smuzhiyun if (!bd2802_is_led_off(led, i))
138*4882a593Smuzhiyun return 0;
139*4882a593Smuzhiyun
140*4882a593Smuzhiyun return 1;
141*4882a593Smuzhiyun }
142*4882a593Smuzhiyun
bd2802_get_base_offset(enum led_ids id,enum led_colors color)143*4882a593Smuzhiyun static inline u8 bd2802_get_base_offset(enum led_ids id, enum led_colors color)
144*4882a593Smuzhiyun {
145*4882a593Smuzhiyun return id * BD2802_LED_OFFSET + color * BD2802_COLOR_OFFSET;
146*4882a593Smuzhiyun }
147*4882a593Smuzhiyun
bd2802_get_reg_addr(enum led_ids id,enum led_colors color,u8 reg_offset)148*4882a593Smuzhiyun static inline u8 bd2802_get_reg_addr(enum led_ids id, enum led_colors color,
149*4882a593Smuzhiyun u8 reg_offset)
150*4882a593Smuzhiyun {
151*4882a593Smuzhiyun return reg_offset + bd2802_get_base_offset(id, color);
152*4882a593Smuzhiyun }
153*4882a593Smuzhiyun
154*4882a593Smuzhiyun
155*4882a593Smuzhiyun /*--------------------------------------------------------------*/
156*4882a593Smuzhiyun /* BD2802GU core functions */
157*4882a593Smuzhiyun /*--------------------------------------------------------------*/
158*4882a593Smuzhiyun
bd2802_write_byte(struct i2c_client * client,u8 reg,u8 val)159*4882a593Smuzhiyun static int bd2802_write_byte(struct i2c_client *client, u8 reg, u8 val)
160*4882a593Smuzhiyun {
161*4882a593Smuzhiyun int ret = i2c_smbus_write_byte_data(client, reg, val);
162*4882a593Smuzhiyun if (ret >= 0)
163*4882a593Smuzhiyun return 0;
164*4882a593Smuzhiyun
165*4882a593Smuzhiyun dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n",
166*4882a593Smuzhiyun __func__, reg, val, ret);
167*4882a593Smuzhiyun
168*4882a593Smuzhiyun return ret;
169*4882a593Smuzhiyun }
170*4882a593Smuzhiyun
bd2802_update_state(struct bd2802_led * led,enum led_ids id,enum led_colors color,enum led_bits led_bit)171*4882a593Smuzhiyun static void bd2802_update_state(struct bd2802_led *led, enum led_ids id,
172*4882a593Smuzhiyun enum led_colors color, enum led_bits led_bit)
173*4882a593Smuzhiyun {
174*4882a593Smuzhiyun int i;
175*4882a593Smuzhiyun u8 value;
176*4882a593Smuzhiyun
177*4882a593Smuzhiyun for (i = 0; i < LED_NUM; i++) {
178*4882a593Smuzhiyun if (i == id) {
179*4882a593Smuzhiyun switch (color) {
180*4882a593Smuzhiyun case RED:
181*4882a593Smuzhiyun led->led[i].r = led_bit;
182*4882a593Smuzhiyun break;
183*4882a593Smuzhiyun case GREEN:
184*4882a593Smuzhiyun led->led[i].g = led_bit;
185*4882a593Smuzhiyun break;
186*4882a593Smuzhiyun case BLUE:
187*4882a593Smuzhiyun led->led[i].b = led_bit;
188*4882a593Smuzhiyun break;
189*4882a593Smuzhiyun default:
190*4882a593Smuzhiyun dev_err(&led->client->dev,
191*4882a593Smuzhiyun "%s: Invalid color\n", __func__);
192*4882a593Smuzhiyun return;
193*4882a593Smuzhiyun }
194*4882a593Smuzhiyun }
195*4882a593Smuzhiyun }
196*4882a593Smuzhiyun
197*4882a593Smuzhiyun if (led_bit == BD2802_BLINK || led_bit == BD2802_ON)
198*4882a593Smuzhiyun return;
199*4882a593Smuzhiyun
200*4882a593Smuzhiyun if (!bd2802_is_led_off(led, id))
201*4882a593Smuzhiyun return;
202*4882a593Smuzhiyun
203*4882a593Smuzhiyun if (bd2802_is_all_off(led) && !led->adf_on) {
204*4882a593Smuzhiyun gpiod_set_value(led->reset, 1);
205*4882a593Smuzhiyun return;
206*4882a593Smuzhiyun }
207*4882a593Smuzhiyun
208*4882a593Smuzhiyun /*
209*4882a593Smuzhiyun * In this case, other led is turned on, and current led is turned
210*4882a593Smuzhiyun * off. So set RGB LED Control register to stop the current RGB LED
211*4882a593Smuzhiyun */
212*4882a593Smuzhiyun value = (id == LED1) ? LED_CTL(1, 0) : LED_CTL(0, 1);
213*4882a593Smuzhiyun bd2802_write_byte(led->client, BD2802_REG_CONTROL, value);
214*4882a593Smuzhiyun }
215*4882a593Smuzhiyun
bd2802_configure(struct bd2802_led * led)216*4882a593Smuzhiyun static void bd2802_configure(struct bd2802_led *led)
217*4882a593Smuzhiyun {
218*4882a593Smuzhiyun struct bd2802_led_platform_data *pdata = led->pdata;
219*4882a593Smuzhiyun u8 reg;
220*4882a593Smuzhiyun
221*4882a593Smuzhiyun reg = bd2802_get_reg_addr(LED1, RED, BD2802_REG_HOURSETUP);
222*4882a593Smuzhiyun bd2802_write_byte(led->client, reg, pdata->rgb_time);
223*4882a593Smuzhiyun
224*4882a593Smuzhiyun reg = bd2802_get_reg_addr(LED2, RED, BD2802_REG_HOURSETUP);
225*4882a593Smuzhiyun bd2802_write_byte(led->client, reg, pdata->rgb_time);
226*4882a593Smuzhiyun }
227*4882a593Smuzhiyun
bd2802_reset_cancel(struct bd2802_led * led)228*4882a593Smuzhiyun static void bd2802_reset_cancel(struct bd2802_led *led)
229*4882a593Smuzhiyun {
230*4882a593Smuzhiyun gpiod_set_value(led->reset, 0);
231*4882a593Smuzhiyun udelay(100);
232*4882a593Smuzhiyun bd2802_configure(led);
233*4882a593Smuzhiyun }
234*4882a593Smuzhiyun
bd2802_enable(struct bd2802_led * led,enum led_ids id)235*4882a593Smuzhiyun static void bd2802_enable(struct bd2802_led *led, enum led_ids id)
236*4882a593Smuzhiyun {
237*4882a593Smuzhiyun enum led_ids other_led = (id == LED1) ? LED2 : LED1;
238*4882a593Smuzhiyun u8 value, other_led_on;
239*4882a593Smuzhiyun
240*4882a593Smuzhiyun other_led_on = !bd2802_is_led_off(led, other_led);
241*4882a593Smuzhiyun if (id == LED1)
242*4882a593Smuzhiyun value = LED_CTL(other_led_on, 1);
243*4882a593Smuzhiyun else
244*4882a593Smuzhiyun value = LED_CTL(1 , other_led_on);
245*4882a593Smuzhiyun
246*4882a593Smuzhiyun bd2802_write_byte(led->client, BD2802_REG_CONTROL, value);
247*4882a593Smuzhiyun }
248*4882a593Smuzhiyun
bd2802_set_on(struct bd2802_led * led,enum led_ids id,enum led_colors color)249*4882a593Smuzhiyun static void bd2802_set_on(struct bd2802_led *led, enum led_ids id,
250*4882a593Smuzhiyun enum led_colors color)
251*4882a593Smuzhiyun {
252*4882a593Smuzhiyun u8 reg;
253*4882a593Smuzhiyun
254*4882a593Smuzhiyun if (bd2802_is_all_off(led) && !led->adf_on)
255*4882a593Smuzhiyun bd2802_reset_cancel(led);
256*4882a593Smuzhiyun
257*4882a593Smuzhiyun reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT1SETUP);
258*4882a593Smuzhiyun bd2802_write_byte(led->client, reg, led->rgb_current);
259*4882a593Smuzhiyun reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT2SETUP);
260*4882a593Smuzhiyun bd2802_write_byte(led->client, reg, BD2802_CURRENT_000);
261*4882a593Smuzhiyun reg = bd2802_get_reg_addr(id, color, BD2802_REG_WAVEPATTERN);
262*4882a593Smuzhiyun bd2802_write_byte(led->client, reg, BD2802_PATTERN_FULL);
263*4882a593Smuzhiyun
264*4882a593Smuzhiyun bd2802_enable(led, id);
265*4882a593Smuzhiyun bd2802_update_state(led, id, color, BD2802_ON);
266*4882a593Smuzhiyun }
267*4882a593Smuzhiyun
bd2802_set_blink(struct bd2802_led * led,enum led_ids id,enum led_colors color)268*4882a593Smuzhiyun static void bd2802_set_blink(struct bd2802_led *led, enum led_ids id,
269*4882a593Smuzhiyun enum led_colors color)
270*4882a593Smuzhiyun {
271*4882a593Smuzhiyun u8 reg;
272*4882a593Smuzhiyun
273*4882a593Smuzhiyun if (bd2802_is_all_off(led) && !led->adf_on)
274*4882a593Smuzhiyun bd2802_reset_cancel(led);
275*4882a593Smuzhiyun
276*4882a593Smuzhiyun reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT1SETUP);
277*4882a593Smuzhiyun bd2802_write_byte(led->client, reg, BD2802_CURRENT_000);
278*4882a593Smuzhiyun reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT2SETUP);
279*4882a593Smuzhiyun bd2802_write_byte(led->client, reg, led->rgb_current);
280*4882a593Smuzhiyun reg = bd2802_get_reg_addr(id, color, BD2802_REG_WAVEPATTERN);
281*4882a593Smuzhiyun bd2802_write_byte(led->client, reg, led->wave_pattern);
282*4882a593Smuzhiyun
283*4882a593Smuzhiyun bd2802_enable(led, id);
284*4882a593Smuzhiyun bd2802_update_state(led, id, color, BD2802_BLINK);
285*4882a593Smuzhiyun }
286*4882a593Smuzhiyun
bd2802_turn_on(struct bd2802_led * led,enum led_ids id,enum led_colors color,enum led_bits led_bit)287*4882a593Smuzhiyun static void bd2802_turn_on(struct bd2802_led *led, enum led_ids id,
288*4882a593Smuzhiyun enum led_colors color, enum led_bits led_bit)
289*4882a593Smuzhiyun {
290*4882a593Smuzhiyun if (led_bit == BD2802_OFF) {
291*4882a593Smuzhiyun dev_err(&led->client->dev,
292*4882a593Smuzhiyun "Only 'blink' and 'on' are allowed\n");
293*4882a593Smuzhiyun return;
294*4882a593Smuzhiyun }
295*4882a593Smuzhiyun
296*4882a593Smuzhiyun if (led_bit == BD2802_BLINK)
297*4882a593Smuzhiyun bd2802_set_blink(led, id, color);
298*4882a593Smuzhiyun else
299*4882a593Smuzhiyun bd2802_set_on(led, id, color);
300*4882a593Smuzhiyun }
301*4882a593Smuzhiyun
bd2802_turn_off(struct bd2802_led * led,enum led_ids id,enum led_colors color)302*4882a593Smuzhiyun static void bd2802_turn_off(struct bd2802_led *led, enum led_ids id,
303*4882a593Smuzhiyun enum led_colors color)
304*4882a593Smuzhiyun {
305*4882a593Smuzhiyun u8 reg;
306*4882a593Smuzhiyun
307*4882a593Smuzhiyun if (bd2802_is_rgb_off(led, id, color))
308*4882a593Smuzhiyun return;
309*4882a593Smuzhiyun
310*4882a593Smuzhiyun reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT1SETUP);
311*4882a593Smuzhiyun bd2802_write_byte(led->client, reg, BD2802_CURRENT_000);
312*4882a593Smuzhiyun reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT2SETUP);
313*4882a593Smuzhiyun bd2802_write_byte(led->client, reg, BD2802_CURRENT_000);
314*4882a593Smuzhiyun
315*4882a593Smuzhiyun bd2802_update_state(led, id, color, BD2802_OFF);
316*4882a593Smuzhiyun }
317*4882a593Smuzhiyun
318*4882a593Smuzhiyun #define BD2802_SET_REGISTER(reg_addr, reg_name) \
319*4882a593Smuzhiyun static ssize_t bd2802_store_reg##reg_addr(struct device *dev, \
320*4882a593Smuzhiyun struct device_attribute *attr, const char *buf, size_t count) \
321*4882a593Smuzhiyun { \
322*4882a593Smuzhiyun struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));\
323*4882a593Smuzhiyun unsigned long val; \
324*4882a593Smuzhiyun int ret; \
325*4882a593Smuzhiyun if (!count) \
326*4882a593Smuzhiyun return -EINVAL; \
327*4882a593Smuzhiyun ret = kstrtoul(buf, 16, &val); \
328*4882a593Smuzhiyun if (ret) \
329*4882a593Smuzhiyun return ret; \
330*4882a593Smuzhiyun down_write(&led->rwsem); \
331*4882a593Smuzhiyun bd2802_write_byte(led->client, reg_addr, (u8) val); \
332*4882a593Smuzhiyun up_write(&led->rwsem); \
333*4882a593Smuzhiyun return count; \
334*4882a593Smuzhiyun } \
335*4882a593Smuzhiyun static struct device_attribute bd2802_reg##reg_addr##_attr = { \
336*4882a593Smuzhiyun .attr = {.name = reg_name, .mode = 0644}, \
337*4882a593Smuzhiyun .store = bd2802_store_reg##reg_addr, \
338*4882a593Smuzhiyun };
339*4882a593Smuzhiyun
340*4882a593Smuzhiyun BD2802_SET_REGISTER(0x00, "0x00");
341*4882a593Smuzhiyun BD2802_SET_REGISTER(0x01, "0x01");
342*4882a593Smuzhiyun BD2802_SET_REGISTER(0x02, "0x02");
343*4882a593Smuzhiyun BD2802_SET_REGISTER(0x03, "0x03");
344*4882a593Smuzhiyun BD2802_SET_REGISTER(0x04, "0x04");
345*4882a593Smuzhiyun BD2802_SET_REGISTER(0x05, "0x05");
346*4882a593Smuzhiyun BD2802_SET_REGISTER(0x06, "0x06");
347*4882a593Smuzhiyun BD2802_SET_REGISTER(0x07, "0x07");
348*4882a593Smuzhiyun BD2802_SET_REGISTER(0x08, "0x08");
349*4882a593Smuzhiyun BD2802_SET_REGISTER(0x09, "0x09");
350*4882a593Smuzhiyun BD2802_SET_REGISTER(0x0a, "0x0a");
351*4882a593Smuzhiyun BD2802_SET_REGISTER(0x0b, "0x0b");
352*4882a593Smuzhiyun BD2802_SET_REGISTER(0x0c, "0x0c");
353*4882a593Smuzhiyun BD2802_SET_REGISTER(0x0d, "0x0d");
354*4882a593Smuzhiyun BD2802_SET_REGISTER(0x0e, "0x0e");
355*4882a593Smuzhiyun BD2802_SET_REGISTER(0x0f, "0x0f");
356*4882a593Smuzhiyun BD2802_SET_REGISTER(0x10, "0x10");
357*4882a593Smuzhiyun BD2802_SET_REGISTER(0x11, "0x11");
358*4882a593Smuzhiyun BD2802_SET_REGISTER(0x12, "0x12");
359*4882a593Smuzhiyun BD2802_SET_REGISTER(0x13, "0x13");
360*4882a593Smuzhiyun BD2802_SET_REGISTER(0x14, "0x14");
361*4882a593Smuzhiyun BD2802_SET_REGISTER(0x15, "0x15");
362*4882a593Smuzhiyun
363*4882a593Smuzhiyun static struct device_attribute *bd2802_addr_attributes[] = {
364*4882a593Smuzhiyun &bd2802_reg0x00_attr,
365*4882a593Smuzhiyun &bd2802_reg0x01_attr,
366*4882a593Smuzhiyun &bd2802_reg0x02_attr,
367*4882a593Smuzhiyun &bd2802_reg0x03_attr,
368*4882a593Smuzhiyun &bd2802_reg0x04_attr,
369*4882a593Smuzhiyun &bd2802_reg0x05_attr,
370*4882a593Smuzhiyun &bd2802_reg0x06_attr,
371*4882a593Smuzhiyun &bd2802_reg0x07_attr,
372*4882a593Smuzhiyun &bd2802_reg0x08_attr,
373*4882a593Smuzhiyun &bd2802_reg0x09_attr,
374*4882a593Smuzhiyun &bd2802_reg0x0a_attr,
375*4882a593Smuzhiyun &bd2802_reg0x0b_attr,
376*4882a593Smuzhiyun &bd2802_reg0x0c_attr,
377*4882a593Smuzhiyun &bd2802_reg0x0d_attr,
378*4882a593Smuzhiyun &bd2802_reg0x0e_attr,
379*4882a593Smuzhiyun &bd2802_reg0x0f_attr,
380*4882a593Smuzhiyun &bd2802_reg0x10_attr,
381*4882a593Smuzhiyun &bd2802_reg0x11_attr,
382*4882a593Smuzhiyun &bd2802_reg0x12_attr,
383*4882a593Smuzhiyun &bd2802_reg0x13_attr,
384*4882a593Smuzhiyun &bd2802_reg0x14_attr,
385*4882a593Smuzhiyun &bd2802_reg0x15_attr,
386*4882a593Smuzhiyun };
387*4882a593Smuzhiyun
bd2802_enable_adv_conf(struct bd2802_led * led)388*4882a593Smuzhiyun static void bd2802_enable_adv_conf(struct bd2802_led *led)
389*4882a593Smuzhiyun {
390*4882a593Smuzhiyun int i, ret;
391*4882a593Smuzhiyun
392*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(bd2802_addr_attributes); i++) {
393*4882a593Smuzhiyun ret = device_create_file(&led->client->dev,
394*4882a593Smuzhiyun bd2802_addr_attributes[i]);
395*4882a593Smuzhiyun if (ret) {
396*4882a593Smuzhiyun dev_err(&led->client->dev, "failed: sysfs file %s\n",
397*4882a593Smuzhiyun bd2802_addr_attributes[i]->attr.name);
398*4882a593Smuzhiyun goto failed_remove_files;
399*4882a593Smuzhiyun }
400*4882a593Smuzhiyun }
401*4882a593Smuzhiyun
402*4882a593Smuzhiyun if (bd2802_is_all_off(led))
403*4882a593Smuzhiyun bd2802_reset_cancel(led);
404*4882a593Smuzhiyun
405*4882a593Smuzhiyun led->adf_on = 1;
406*4882a593Smuzhiyun
407*4882a593Smuzhiyun return;
408*4882a593Smuzhiyun
409*4882a593Smuzhiyun failed_remove_files:
410*4882a593Smuzhiyun for (i--; i >= 0; i--)
411*4882a593Smuzhiyun device_remove_file(&led->client->dev,
412*4882a593Smuzhiyun bd2802_addr_attributes[i]);
413*4882a593Smuzhiyun }
414*4882a593Smuzhiyun
bd2802_disable_adv_conf(struct bd2802_led * led)415*4882a593Smuzhiyun static void bd2802_disable_adv_conf(struct bd2802_led *led)
416*4882a593Smuzhiyun {
417*4882a593Smuzhiyun int i;
418*4882a593Smuzhiyun
419*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(bd2802_addr_attributes); i++)
420*4882a593Smuzhiyun device_remove_file(&led->client->dev,
421*4882a593Smuzhiyun bd2802_addr_attributes[i]);
422*4882a593Smuzhiyun
423*4882a593Smuzhiyun if (bd2802_is_all_off(led))
424*4882a593Smuzhiyun gpiod_set_value(led->reset, 1);
425*4882a593Smuzhiyun
426*4882a593Smuzhiyun led->adf_on = 0;
427*4882a593Smuzhiyun }
428*4882a593Smuzhiyun
bd2802_show_adv_conf(struct device * dev,struct device_attribute * attr,char * buf)429*4882a593Smuzhiyun static ssize_t bd2802_show_adv_conf(struct device *dev,
430*4882a593Smuzhiyun struct device_attribute *attr, char *buf)
431*4882a593Smuzhiyun {
432*4882a593Smuzhiyun struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));
433*4882a593Smuzhiyun ssize_t ret;
434*4882a593Smuzhiyun
435*4882a593Smuzhiyun down_read(&led->rwsem);
436*4882a593Smuzhiyun if (led->adf_on)
437*4882a593Smuzhiyun ret = sprintf(buf, "on\n");
438*4882a593Smuzhiyun else
439*4882a593Smuzhiyun ret = sprintf(buf, "off\n");
440*4882a593Smuzhiyun up_read(&led->rwsem);
441*4882a593Smuzhiyun
442*4882a593Smuzhiyun return ret;
443*4882a593Smuzhiyun }
444*4882a593Smuzhiyun
bd2802_store_adv_conf(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)445*4882a593Smuzhiyun static ssize_t bd2802_store_adv_conf(struct device *dev,
446*4882a593Smuzhiyun struct device_attribute *attr, const char *buf, size_t count)
447*4882a593Smuzhiyun {
448*4882a593Smuzhiyun struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));
449*4882a593Smuzhiyun
450*4882a593Smuzhiyun if (!count)
451*4882a593Smuzhiyun return -EINVAL;
452*4882a593Smuzhiyun
453*4882a593Smuzhiyun down_write(&led->rwsem);
454*4882a593Smuzhiyun if (!led->adf_on && !strncmp(buf, "on", 2))
455*4882a593Smuzhiyun bd2802_enable_adv_conf(led);
456*4882a593Smuzhiyun else if (led->adf_on && !strncmp(buf, "off", 3))
457*4882a593Smuzhiyun bd2802_disable_adv_conf(led);
458*4882a593Smuzhiyun up_write(&led->rwsem);
459*4882a593Smuzhiyun
460*4882a593Smuzhiyun return count;
461*4882a593Smuzhiyun }
462*4882a593Smuzhiyun
463*4882a593Smuzhiyun static struct device_attribute bd2802_adv_conf_attr = {
464*4882a593Smuzhiyun .attr = {
465*4882a593Smuzhiyun .name = "advanced_configuration",
466*4882a593Smuzhiyun .mode = 0644,
467*4882a593Smuzhiyun },
468*4882a593Smuzhiyun .show = bd2802_show_adv_conf,
469*4882a593Smuzhiyun .store = bd2802_store_adv_conf,
470*4882a593Smuzhiyun };
471*4882a593Smuzhiyun
472*4882a593Smuzhiyun #define BD2802_CONTROL_ATTR(attr_name, name_str) \
473*4882a593Smuzhiyun static ssize_t bd2802_show_##attr_name(struct device *dev, \
474*4882a593Smuzhiyun struct device_attribute *attr, char *buf) \
475*4882a593Smuzhiyun { \
476*4882a593Smuzhiyun struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));\
477*4882a593Smuzhiyun ssize_t ret; \
478*4882a593Smuzhiyun down_read(&led->rwsem); \
479*4882a593Smuzhiyun ret = sprintf(buf, "0x%02x\n", led->attr_name); \
480*4882a593Smuzhiyun up_read(&led->rwsem); \
481*4882a593Smuzhiyun return ret; \
482*4882a593Smuzhiyun } \
483*4882a593Smuzhiyun static ssize_t bd2802_store_##attr_name(struct device *dev, \
484*4882a593Smuzhiyun struct device_attribute *attr, const char *buf, size_t count) \
485*4882a593Smuzhiyun { \
486*4882a593Smuzhiyun struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));\
487*4882a593Smuzhiyun unsigned long val; \
488*4882a593Smuzhiyun int ret; \
489*4882a593Smuzhiyun if (!count) \
490*4882a593Smuzhiyun return -EINVAL; \
491*4882a593Smuzhiyun ret = kstrtoul(buf, 16, &val); \
492*4882a593Smuzhiyun if (ret) \
493*4882a593Smuzhiyun return ret; \
494*4882a593Smuzhiyun down_write(&led->rwsem); \
495*4882a593Smuzhiyun led->attr_name = val; \
496*4882a593Smuzhiyun up_write(&led->rwsem); \
497*4882a593Smuzhiyun return count; \
498*4882a593Smuzhiyun } \
499*4882a593Smuzhiyun static struct device_attribute bd2802_##attr_name##_attr = { \
500*4882a593Smuzhiyun .attr = { \
501*4882a593Smuzhiyun .name = name_str, \
502*4882a593Smuzhiyun .mode = 0644, \
503*4882a593Smuzhiyun }, \
504*4882a593Smuzhiyun .show = bd2802_show_##attr_name, \
505*4882a593Smuzhiyun .store = bd2802_store_##attr_name, \
506*4882a593Smuzhiyun };
507*4882a593Smuzhiyun
508*4882a593Smuzhiyun BD2802_CONTROL_ATTR(wave_pattern, "wave_pattern");
509*4882a593Smuzhiyun BD2802_CONTROL_ATTR(rgb_current, "rgb_current");
510*4882a593Smuzhiyun
511*4882a593Smuzhiyun static struct device_attribute *bd2802_attributes[] = {
512*4882a593Smuzhiyun &bd2802_adv_conf_attr,
513*4882a593Smuzhiyun &bd2802_wave_pattern_attr,
514*4882a593Smuzhiyun &bd2802_rgb_current_attr,
515*4882a593Smuzhiyun };
516*4882a593Smuzhiyun
517*4882a593Smuzhiyun #define BD2802_CONTROL_RGBS(name, id, clr) \
518*4882a593Smuzhiyun static int bd2802_set_##name##_brightness(struct led_classdev *led_cdev,\
519*4882a593Smuzhiyun enum led_brightness value) \
520*4882a593Smuzhiyun { \
521*4882a593Smuzhiyun struct bd2802_led *led = \
522*4882a593Smuzhiyun container_of(led_cdev, struct bd2802_led, cdev_##name); \
523*4882a593Smuzhiyun led->led_id = id; \
524*4882a593Smuzhiyun led->color = clr; \
525*4882a593Smuzhiyun if (value == LED_OFF) { \
526*4882a593Smuzhiyun led->state = BD2802_OFF; \
527*4882a593Smuzhiyun bd2802_turn_off(led, led->led_id, led->color); \
528*4882a593Smuzhiyun } else { \
529*4882a593Smuzhiyun led->state = BD2802_ON; \
530*4882a593Smuzhiyun bd2802_turn_on(led, led->led_id, led->color, BD2802_ON);\
531*4882a593Smuzhiyun } \
532*4882a593Smuzhiyun return 0; \
533*4882a593Smuzhiyun } \
534*4882a593Smuzhiyun static int bd2802_set_##name##_blink(struct led_classdev *led_cdev, \
535*4882a593Smuzhiyun unsigned long *delay_on, unsigned long *delay_off) \
536*4882a593Smuzhiyun { \
537*4882a593Smuzhiyun struct bd2802_led *led = \
538*4882a593Smuzhiyun container_of(led_cdev, struct bd2802_led, cdev_##name); \
539*4882a593Smuzhiyun if (*delay_on == 0 || *delay_off == 0) \
540*4882a593Smuzhiyun return -EINVAL; \
541*4882a593Smuzhiyun led->led_id = id; \
542*4882a593Smuzhiyun led->color = clr; \
543*4882a593Smuzhiyun led->state = BD2802_BLINK; \
544*4882a593Smuzhiyun bd2802_turn_on(led, led->led_id, led->color, BD2802_BLINK); \
545*4882a593Smuzhiyun return 0; \
546*4882a593Smuzhiyun }
547*4882a593Smuzhiyun
548*4882a593Smuzhiyun BD2802_CONTROL_RGBS(led1r, LED1, RED);
549*4882a593Smuzhiyun BD2802_CONTROL_RGBS(led1g, LED1, GREEN);
550*4882a593Smuzhiyun BD2802_CONTROL_RGBS(led1b, LED1, BLUE);
551*4882a593Smuzhiyun BD2802_CONTROL_RGBS(led2r, LED2, RED);
552*4882a593Smuzhiyun BD2802_CONTROL_RGBS(led2g, LED2, GREEN);
553*4882a593Smuzhiyun BD2802_CONTROL_RGBS(led2b, LED2, BLUE);
554*4882a593Smuzhiyun
bd2802_register_led_classdev(struct bd2802_led * led)555*4882a593Smuzhiyun static int bd2802_register_led_classdev(struct bd2802_led *led)
556*4882a593Smuzhiyun {
557*4882a593Smuzhiyun int ret;
558*4882a593Smuzhiyun
559*4882a593Smuzhiyun led->cdev_led1r.name = "led1_R";
560*4882a593Smuzhiyun led->cdev_led1r.brightness = LED_OFF;
561*4882a593Smuzhiyun led->cdev_led1r.brightness_set_blocking = bd2802_set_led1r_brightness;
562*4882a593Smuzhiyun led->cdev_led1r.blink_set = bd2802_set_led1r_blink;
563*4882a593Smuzhiyun
564*4882a593Smuzhiyun ret = led_classdev_register(&led->client->dev, &led->cdev_led1r);
565*4882a593Smuzhiyun if (ret < 0) {
566*4882a593Smuzhiyun dev_err(&led->client->dev, "couldn't register LED %s\n",
567*4882a593Smuzhiyun led->cdev_led1r.name);
568*4882a593Smuzhiyun goto failed_unregister_led1_R;
569*4882a593Smuzhiyun }
570*4882a593Smuzhiyun
571*4882a593Smuzhiyun led->cdev_led1g.name = "led1_G";
572*4882a593Smuzhiyun led->cdev_led1g.brightness = LED_OFF;
573*4882a593Smuzhiyun led->cdev_led1g.brightness_set_blocking = bd2802_set_led1g_brightness;
574*4882a593Smuzhiyun led->cdev_led1g.blink_set = bd2802_set_led1g_blink;
575*4882a593Smuzhiyun
576*4882a593Smuzhiyun ret = led_classdev_register(&led->client->dev, &led->cdev_led1g);
577*4882a593Smuzhiyun if (ret < 0) {
578*4882a593Smuzhiyun dev_err(&led->client->dev, "couldn't register LED %s\n",
579*4882a593Smuzhiyun led->cdev_led1g.name);
580*4882a593Smuzhiyun goto failed_unregister_led1_G;
581*4882a593Smuzhiyun }
582*4882a593Smuzhiyun
583*4882a593Smuzhiyun led->cdev_led1b.name = "led1_B";
584*4882a593Smuzhiyun led->cdev_led1b.brightness = LED_OFF;
585*4882a593Smuzhiyun led->cdev_led1b.brightness_set_blocking = bd2802_set_led1b_brightness;
586*4882a593Smuzhiyun led->cdev_led1b.blink_set = bd2802_set_led1b_blink;
587*4882a593Smuzhiyun
588*4882a593Smuzhiyun ret = led_classdev_register(&led->client->dev, &led->cdev_led1b);
589*4882a593Smuzhiyun if (ret < 0) {
590*4882a593Smuzhiyun dev_err(&led->client->dev, "couldn't register LED %s\n",
591*4882a593Smuzhiyun led->cdev_led1b.name);
592*4882a593Smuzhiyun goto failed_unregister_led1_B;
593*4882a593Smuzhiyun }
594*4882a593Smuzhiyun
595*4882a593Smuzhiyun led->cdev_led2r.name = "led2_R";
596*4882a593Smuzhiyun led->cdev_led2r.brightness = LED_OFF;
597*4882a593Smuzhiyun led->cdev_led2r.brightness_set_blocking = bd2802_set_led2r_brightness;
598*4882a593Smuzhiyun led->cdev_led2r.blink_set = bd2802_set_led2r_blink;
599*4882a593Smuzhiyun
600*4882a593Smuzhiyun ret = led_classdev_register(&led->client->dev, &led->cdev_led2r);
601*4882a593Smuzhiyun if (ret < 0) {
602*4882a593Smuzhiyun dev_err(&led->client->dev, "couldn't register LED %s\n",
603*4882a593Smuzhiyun led->cdev_led2r.name);
604*4882a593Smuzhiyun goto failed_unregister_led2_R;
605*4882a593Smuzhiyun }
606*4882a593Smuzhiyun
607*4882a593Smuzhiyun led->cdev_led2g.name = "led2_G";
608*4882a593Smuzhiyun led->cdev_led2g.brightness = LED_OFF;
609*4882a593Smuzhiyun led->cdev_led2g.brightness_set_blocking = bd2802_set_led2g_brightness;
610*4882a593Smuzhiyun led->cdev_led2g.blink_set = bd2802_set_led2g_blink;
611*4882a593Smuzhiyun
612*4882a593Smuzhiyun ret = led_classdev_register(&led->client->dev, &led->cdev_led2g);
613*4882a593Smuzhiyun if (ret < 0) {
614*4882a593Smuzhiyun dev_err(&led->client->dev, "couldn't register LED %s\n",
615*4882a593Smuzhiyun led->cdev_led2g.name);
616*4882a593Smuzhiyun goto failed_unregister_led2_G;
617*4882a593Smuzhiyun }
618*4882a593Smuzhiyun
619*4882a593Smuzhiyun led->cdev_led2b.name = "led2_B";
620*4882a593Smuzhiyun led->cdev_led2b.brightness = LED_OFF;
621*4882a593Smuzhiyun led->cdev_led2b.brightness_set_blocking = bd2802_set_led2b_brightness;
622*4882a593Smuzhiyun led->cdev_led2b.blink_set = bd2802_set_led2b_blink;
623*4882a593Smuzhiyun led->cdev_led2b.flags |= LED_CORE_SUSPENDRESUME;
624*4882a593Smuzhiyun
625*4882a593Smuzhiyun ret = led_classdev_register(&led->client->dev, &led->cdev_led2b);
626*4882a593Smuzhiyun if (ret < 0) {
627*4882a593Smuzhiyun dev_err(&led->client->dev, "couldn't register LED %s\n",
628*4882a593Smuzhiyun led->cdev_led2b.name);
629*4882a593Smuzhiyun goto failed_unregister_led2_B;
630*4882a593Smuzhiyun }
631*4882a593Smuzhiyun
632*4882a593Smuzhiyun return 0;
633*4882a593Smuzhiyun
634*4882a593Smuzhiyun failed_unregister_led2_B:
635*4882a593Smuzhiyun led_classdev_unregister(&led->cdev_led2g);
636*4882a593Smuzhiyun failed_unregister_led2_G:
637*4882a593Smuzhiyun led_classdev_unregister(&led->cdev_led2r);
638*4882a593Smuzhiyun failed_unregister_led2_R:
639*4882a593Smuzhiyun led_classdev_unregister(&led->cdev_led1b);
640*4882a593Smuzhiyun failed_unregister_led1_B:
641*4882a593Smuzhiyun led_classdev_unregister(&led->cdev_led1g);
642*4882a593Smuzhiyun failed_unregister_led1_G:
643*4882a593Smuzhiyun led_classdev_unregister(&led->cdev_led1r);
644*4882a593Smuzhiyun failed_unregister_led1_R:
645*4882a593Smuzhiyun
646*4882a593Smuzhiyun return ret;
647*4882a593Smuzhiyun }
648*4882a593Smuzhiyun
bd2802_unregister_led_classdev(struct bd2802_led * led)649*4882a593Smuzhiyun static void bd2802_unregister_led_classdev(struct bd2802_led *led)
650*4882a593Smuzhiyun {
651*4882a593Smuzhiyun led_classdev_unregister(&led->cdev_led2b);
652*4882a593Smuzhiyun led_classdev_unregister(&led->cdev_led2g);
653*4882a593Smuzhiyun led_classdev_unregister(&led->cdev_led2r);
654*4882a593Smuzhiyun led_classdev_unregister(&led->cdev_led1b);
655*4882a593Smuzhiyun led_classdev_unregister(&led->cdev_led1g);
656*4882a593Smuzhiyun led_classdev_unregister(&led->cdev_led1r);
657*4882a593Smuzhiyun }
658*4882a593Smuzhiyun
bd2802_probe(struct i2c_client * client,const struct i2c_device_id * id)659*4882a593Smuzhiyun static int bd2802_probe(struct i2c_client *client,
660*4882a593Smuzhiyun const struct i2c_device_id *id)
661*4882a593Smuzhiyun {
662*4882a593Smuzhiyun struct bd2802_led *led;
663*4882a593Smuzhiyun int ret, i;
664*4882a593Smuzhiyun
665*4882a593Smuzhiyun led = devm_kzalloc(&client->dev, sizeof(struct bd2802_led), GFP_KERNEL);
666*4882a593Smuzhiyun if (!led)
667*4882a593Smuzhiyun return -ENOMEM;
668*4882a593Smuzhiyun
669*4882a593Smuzhiyun led->client = client;
670*4882a593Smuzhiyun i2c_set_clientdata(client, led);
671*4882a593Smuzhiyun
672*4882a593Smuzhiyun /*
673*4882a593Smuzhiyun * Configure RESET GPIO (L: RESET, H: RESET cancel)
674*4882a593Smuzhiyun *
675*4882a593Smuzhiyun * We request the reset GPIO as OUT_LOW which means de-asserted,
676*4882a593Smuzhiyun * board files specifying this GPIO line in a machine descriptor
677*4882a593Smuzhiyun * table should take care to specify GPIO_ACTIVE_LOW for this line.
678*4882a593Smuzhiyun */
679*4882a593Smuzhiyun led->reset = devm_gpiod_get(&client->dev, "reset", GPIOD_OUT_LOW);
680*4882a593Smuzhiyun if (IS_ERR(led->reset))
681*4882a593Smuzhiyun return PTR_ERR(led->reset);
682*4882a593Smuzhiyun
683*4882a593Smuzhiyun /* Tacss = min 0.1ms */
684*4882a593Smuzhiyun udelay(100);
685*4882a593Smuzhiyun
686*4882a593Smuzhiyun /* Detect BD2802GU */
687*4882a593Smuzhiyun ret = bd2802_write_byte(client, BD2802_REG_CLKSETUP, 0x00);
688*4882a593Smuzhiyun if (ret < 0) {
689*4882a593Smuzhiyun dev_err(&client->dev, "failed to detect device\n");
690*4882a593Smuzhiyun return ret;
691*4882a593Smuzhiyun } else
692*4882a593Smuzhiyun dev_info(&client->dev, "return 0x%02x\n", ret);
693*4882a593Smuzhiyun
694*4882a593Smuzhiyun /* To save the power, reset BD2802 after detecting */
695*4882a593Smuzhiyun gpiod_set_value(led->reset, 1);
696*4882a593Smuzhiyun
697*4882a593Smuzhiyun /* Default attributes */
698*4882a593Smuzhiyun led->wave_pattern = BD2802_PATTERN_HALF;
699*4882a593Smuzhiyun led->rgb_current = BD2802_CURRENT_032;
700*4882a593Smuzhiyun
701*4882a593Smuzhiyun init_rwsem(&led->rwsem);
702*4882a593Smuzhiyun
703*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(bd2802_attributes); i++) {
704*4882a593Smuzhiyun ret = device_create_file(&led->client->dev,
705*4882a593Smuzhiyun bd2802_attributes[i]);
706*4882a593Smuzhiyun if (ret) {
707*4882a593Smuzhiyun dev_err(&led->client->dev, "failed: sysfs file %s\n",
708*4882a593Smuzhiyun bd2802_attributes[i]->attr.name);
709*4882a593Smuzhiyun goto failed_unregister_dev_file;
710*4882a593Smuzhiyun }
711*4882a593Smuzhiyun }
712*4882a593Smuzhiyun
713*4882a593Smuzhiyun ret = bd2802_register_led_classdev(led);
714*4882a593Smuzhiyun if (ret < 0)
715*4882a593Smuzhiyun goto failed_unregister_dev_file;
716*4882a593Smuzhiyun
717*4882a593Smuzhiyun return 0;
718*4882a593Smuzhiyun
719*4882a593Smuzhiyun failed_unregister_dev_file:
720*4882a593Smuzhiyun for (i--; i >= 0; i--)
721*4882a593Smuzhiyun device_remove_file(&led->client->dev, bd2802_attributes[i]);
722*4882a593Smuzhiyun return ret;
723*4882a593Smuzhiyun }
724*4882a593Smuzhiyun
bd2802_remove(struct i2c_client * client)725*4882a593Smuzhiyun static int bd2802_remove(struct i2c_client *client)
726*4882a593Smuzhiyun {
727*4882a593Smuzhiyun struct bd2802_led *led = i2c_get_clientdata(client);
728*4882a593Smuzhiyun int i;
729*4882a593Smuzhiyun
730*4882a593Smuzhiyun gpiod_set_value(led->reset, 1);
731*4882a593Smuzhiyun bd2802_unregister_led_classdev(led);
732*4882a593Smuzhiyun if (led->adf_on)
733*4882a593Smuzhiyun bd2802_disable_adv_conf(led);
734*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(bd2802_attributes); i++)
735*4882a593Smuzhiyun device_remove_file(&led->client->dev, bd2802_attributes[i]);
736*4882a593Smuzhiyun
737*4882a593Smuzhiyun return 0;
738*4882a593Smuzhiyun }
739*4882a593Smuzhiyun
740*4882a593Smuzhiyun #ifdef CONFIG_PM_SLEEP
bd2802_restore_state(struct bd2802_led * led)741*4882a593Smuzhiyun static void bd2802_restore_state(struct bd2802_led *led)
742*4882a593Smuzhiyun {
743*4882a593Smuzhiyun int i;
744*4882a593Smuzhiyun
745*4882a593Smuzhiyun for (i = 0; i < LED_NUM; i++) {
746*4882a593Smuzhiyun if (led->led[i].r)
747*4882a593Smuzhiyun bd2802_turn_on(led, i, RED, led->led[i].r);
748*4882a593Smuzhiyun if (led->led[i].g)
749*4882a593Smuzhiyun bd2802_turn_on(led, i, GREEN, led->led[i].g);
750*4882a593Smuzhiyun if (led->led[i].b)
751*4882a593Smuzhiyun bd2802_turn_on(led, i, BLUE, led->led[i].b);
752*4882a593Smuzhiyun }
753*4882a593Smuzhiyun }
754*4882a593Smuzhiyun
bd2802_suspend(struct device * dev)755*4882a593Smuzhiyun static int bd2802_suspend(struct device *dev)
756*4882a593Smuzhiyun {
757*4882a593Smuzhiyun struct i2c_client *client = to_i2c_client(dev);
758*4882a593Smuzhiyun struct bd2802_led *led = i2c_get_clientdata(client);
759*4882a593Smuzhiyun
760*4882a593Smuzhiyun gpiod_set_value(led->reset, 1);
761*4882a593Smuzhiyun
762*4882a593Smuzhiyun return 0;
763*4882a593Smuzhiyun }
764*4882a593Smuzhiyun
bd2802_resume(struct device * dev)765*4882a593Smuzhiyun static int bd2802_resume(struct device *dev)
766*4882a593Smuzhiyun {
767*4882a593Smuzhiyun struct i2c_client *client = to_i2c_client(dev);
768*4882a593Smuzhiyun struct bd2802_led *led = i2c_get_clientdata(client);
769*4882a593Smuzhiyun
770*4882a593Smuzhiyun if (!bd2802_is_all_off(led) || led->adf_on) {
771*4882a593Smuzhiyun bd2802_reset_cancel(led);
772*4882a593Smuzhiyun bd2802_restore_state(led);
773*4882a593Smuzhiyun }
774*4882a593Smuzhiyun
775*4882a593Smuzhiyun return 0;
776*4882a593Smuzhiyun }
777*4882a593Smuzhiyun #endif
778*4882a593Smuzhiyun
779*4882a593Smuzhiyun static SIMPLE_DEV_PM_OPS(bd2802_pm, bd2802_suspend, bd2802_resume);
780*4882a593Smuzhiyun
781*4882a593Smuzhiyun static const struct i2c_device_id bd2802_id[] = {
782*4882a593Smuzhiyun { "BD2802", 0 },
783*4882a593Smuzhiyun { }
784*4882a593Smuzhiyun };
785*4882a593Smuzhiyun MODULE_DEVICE_TABLE(i2c, bd2802_id);
786*4882a593Smuzhiyun
787*4882a593Smuzhiyun static struct i2c_driver bd2802_i2c_driver = {
788*4882a593Smuzhiyun .driver = {
789*4882a593Smuzhiyun .name = "BD2802",
790*4882a593Smuzhiyun .pm = &bd2802_pm,
791*4882a593Smuzhiyun },
792*4882a593Smuzhiyun .probe = bd2802_probe,
793*4882a593Smuzhiyun .remove = bd2802_remove,
794*4882a593Smuzhiyun .id_table = bd2802_id,
795*4882a593Smuzhiyun };
796*4882a593Smuzhiyun
797*4882a593Smuzhiyun module_i2c_driver(bd2802_i2c_driver);
798*4882a593Smuzhiyun
799*4882a593Smuzhiyun MODULE_AUTHOR("Kim Kyuwon <q1.kim@samsung.com>");
800*4882a593Smuzhiyun MODULE_DESCRIPTION("BD2802 LED driver");
801*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
802