xref: /OK3568_Linux_fs/kernel/drivers/leds/leds-bd2802.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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