xref: /openbmc/linux/drivers/leds/leds-bd2802.c (revision 2612e3bbc0386368a850140a6c9b990cd496a5ec)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
20b56129bSKim Kyuwon /*
30b56129bSKim Kyuwon  * leds-bd2802.c - RGB LED Driver
40b56129bSKim Kyuwon  *
50b56129bSKim Kyuwon  * Copyright (C) 2009 Samsung Electronics
60b56129bSKim Kyuwon  * Kim Kyuwon <q1.kim@samsung.com>
70b56129bSKim Kyuwon  *
80b56129bSKim Kyuwon  * Datasheet: http://www.rohm.com/products/databook/driver/pdf/bd2802gu-e.pdf
90b56129bSKim Kyuwon  */
100b56129bSKim Kyuwon 
110b56129bSKim Kyuwon #include <linux/module.h>
120b56129bSKim Kyuwon #include <linux/i2c.h>
134c3718f9SLinus Walleij #include <linux/gpio/consumer.h>
140b56129bSKim Kyuwon #include <linux/delay.h>
150b56129bSKim Kyuwon #include <linux/leds.h>
160b56129bSKim Kyuwon #include <linux/leds-bd2802.h>
175a0e3ad6STejun Heo #include <linux/slab.h>
18551ea738SMark Brown #include <linux/pm.h>
190b56129bSKim Kyuwon 
200b56129bSKim Kyuwon #define LED_CTL(rgb2en, rgb1en) ((rgb2en) << 4 | ((rgb1en) << 0))
210b56129bSKim Kyuwon 
220b56129bSKim Kyuwon #define BD2802_LED_OFFSET		0xa
230b56129bSKim Kyuwon #define BD2802_COLOR_OFFSET		0x3
240b56129bSKim Kyuwon 
250b56129bSKim Kyuwon #define BD2802_REG_CLKSETUP		0x00
260b56129bSKim Kyuwon #define BD2802_REG_CONTROL		0x01
270b56129bSKim Kyuwon #define BD2802_REG_HOURSETUP		0x02
280b56129bSKim Kyuwon #define BD2802_REG_CURRENT1SETUP	0x03
290b56129bSKim Kyuwon #define BD2802_REG_CURRENT2SETUP	0x04
300b56129bSKim Kyuwon #define BD2802_REG_WAVEPATTERN		0x05
310b56129bSKim Kyuwon 
320b56129bSKim Kyuwon #define BD2802_CURRENT_032		0x10 /* 3.2mA */
330b56129bSKim Kyuwon #define BD2802_CURRENT_000		0x00 /* 0.0mA */
340b56129bSKim Kyuwon 
350b56129bSKim Kyuwon #define BD2802_PATTERN_FULL		0x07
360b56129bSKim Kyuwon #define BD2802_PATTERN_HALF		0x03
370b56129bSKim Kyuwon 
380b56129bSKim Kyuwon enum led_ids {
390b56129bSKim Kyuwon 	LED1,
400b56129bSKim Kyuwon 	LED2,
410b56129bSKim Kyuwon 	LED_NUM,
420b56129bSKim Kyuwon };
430b56129bSKim Kyuwon 
440b56129bSKim Kyuwon enum led_colors {
450b56129bSKim Kyuwon 	RED,
460b56129bSKim Kyuwon 	GREEN,
470b56129bSKim Kyuwon 	BLUE,
480b56129bSKim Kyuwon };
490b56129bSKim Kyuwon 
500b56129bSKim Kyuwon enum led_bits {
510b56129bSKim Kyuwon 	BD2802_OFF,
520b56129bSKim Kyuwon 	BD2802_BLINK,
530b56129bSKim Kyuwon 	BD2802_ON,
540b56129bSKim Kyuwon };
550b56129bSKim Kyuwon 
560b56129bSKim Kyuwon /*
570b56129bSKim Kyuwon  * State '0' : 'off'
580b56129bSKim Kyuwon  * State '1' : 'blink'
590b56129bSKim Kyuwon  * State '2' : 'on'.
600b56129bSKim Kyuwon  */
610b56129bSKim Kyuwon struct led_state {
620b56129bSKim Kyuwon 	unsigned r:2;
630b56129bSKim Kyuwon 	unsigned g:2;
640b56129bSKim Kyuwon 	unsigned b:2;
650b56129bSKim Kyuwon };
660b56129bSKim Kyuwon 
670b56129bSKim Kyuwon struct bd2802_led {
680b56129bSKim Kyuwon 	struct bd2802_led_platform_data	*pdata;
690b56129bSKim Kyuwon 	struct i2c_client		*client;
704c3718f9SLinus Walleij 	struct gpio_desc		*reset;
710b56129bSKim Kyuwon 	struct rw_semaphore		rwsem;
720b56129bSKim Kyuwon 
730b56129bSKim Kyuwon 	struct led_state		led[2];
740b56129bSKim Kyuwon 
750b56129bSKim Kyuwon 	/*
760b56129bSKim Kyuwon 	 * Making led_classdev as array is not recommended, because array
770b56129bSKim Kyuwon 	 * members prevent using 'container_of' macro. So repetitive works
780b56129bSKim Kyuwon 	 * are needed.
790b56129bSKim Kyuwon 	 */
800b56129bSKim Kyuwon 	struct led_classdev		cdev_led1r;
810b56129bSKim Kyuwon 	struct led_classdev		cdev_led1g;
820b56129bSKim Kyuwon 	struct led_classdev		cdev_led1b;
830b56129bSKim Kyuwon 	struct led_classdev		cdev_led2r;
840b56129bSKim Kyuwon 	struct led_classdev		cdev_led2g;
850b56129bSKim Kyuwon 	struct led_classdev		cdev_led2b;
860b56129bSKim Kyuwon 
870b56129bSKim Kyuwon 	/*
880b56129bSKim Kyuwon 	 * Advanced Configuration Function(ADF) mode:
890b56129bSKim Kyuwon 	 * In ADF mode, user can set registers of BD2802GU directly,
900b56129bSKim Kyuwon 	 * therefore BD2802GU doesn't enter reset state.
910b56129bSKim Kyuwon 	 */
920b56129bSKim Kyuwon 	int				adf_on;
930b56129bSKim Kyuwon 
940b56129bSKim Kyuwon 	enum led_ids			led_id;
950b56129bSKim Kyuwon 	enum led_colors			color;
960b56129bSKim Kyuwon 	enum led_bits			state;
978792f7cfSKim Kyuwon 
988792f7cfSKim Kyuwon 	/* General attributes of RGB LEDs */
998792f7cfSKim Kyuwon 	int				wave_pattern;
1008792f7cfSKim Kyuwon 	int				rgb_current;
1010b56129bSKim Kyuwon };
1020b56129bSKim Kyuwon 
1030b56129bSKim Kyuwon 
1040b56129bSKim Kyuwon /*--------------------------------------------------------------*/
1050b56129bSKim Kyuwon /*	BD2802GU helper functions					*/
1060b56129bSKim Kyuwon /*--------------------------------------------------------------*/
1070b56129bSKim Kyuwon 
bd2802_is_rgb_off(struct bd2802_led * led,enum led_ids id,enum led_colors color)1080b56129bSKim Kyuwon static inline int bd2802_is_rgb_off(struct bd2802_led *led, enum led_ids id,
1090b56129bSKim Kyuwon 							enum led_colors color)
1100b56129bSKim Kyuwon {
1110b56129bSKim Kyuwon 	switch (color) {
1120b56129bSKim Kyuwon 	case RED:
1130b56129bSKim Kyuwon 		return !led->led[id].r;
1140b56129bSKim Kyuwon 	case GREEN:
1150b56129bSKim Kyuwon 		return !led->led[id].g;
1160b56129bSKim Kyuwon 	case BLUE:
1170b56129bSKim Kyuwon 		return !led->led[id].b;
1180b56129bSKim Kyuwon 	default:
1190b56129bSKim Kyuwon 		dev_err(&led->client->dev, "%s: Invalid color\n", __func__);
1200b56129bSKim Kyuwon 		return -EINVAL;
1210b56129bSKim Kyuwon 	}
1220b56129bSKim Kyuwon }
1230b56129bSKim Kyuwon 
bd2802_is_led_off(struct bd2802_led * led,enum led_ids id)1240b56129bSKim Kyuwon static inline int bd2802_is_led_off(struct bd2802_led *led, enum led_ids id)
1250b56129bSKim Kyuwon {
1260b56129bSKim Kyuwon 	if (led->led[id].r || led->led[id].g || led->led[id].b)
1270b56129bSKim Kyuwon 		return 0;
1280b56129bSKim Kyuwon 
1290b56129bSKim Kyuwon 	return 1;
1300b56129bSKim Kyuwon }
1310b56129bSKim Kyuwon 
bd2802_is_all_off(struct bd2802_led * led)1320b56129bSKim Kyuwon static inline int bd2802_is_all_off(struct bd2802_led *led)
1330b56129bSKim Kyuwon {
1340b56129bSKim Kyuwon 	int i;
1350b56129bSKim Kyuwon 
1360b56129bSKim Kyuwon 	for (i = 0; i < LED_NUM; i++)
1370b56129bSKim Kyuwon 		if (!bd2802_is_led_off(led, i))
1380b56129bSKim Kyuwon 			return 0;
1390b56129bSKim Kyuwon 
1400b56129bSKim Kyuwon 	return 1;
1410b56129bSKim Kyuwon }
1420b56129bSKim Kyuwon 
bd2802_get_base_offset(enum led_ids id,enum led_colors color)1430b56129bSKim Kyuwon static inline u8 bd2802_get_base_offset(enum led_ids id, enum led_colors color)
1440b56129bSKim Kyuwon {
1450b56129bSKim Kyuwon 	return id * BD2802_LED_OFFSET + color * BD2802_COLOR_OFFSET;
1460b56129bSKim Kyuwon }
1470b56129bSKim Kyuwon 
bd2802_get_reg_addr(enum led_ids id,enum led_colors color,u8 reg_offset)1480b56129bSKim Kyuwon static inline u8 bd2802_get_reg_addr(enum led_ids id, enum led_colors color,
1490b56129bSKim Kyuwon 								u8 reg_offset)
1500b56129bSKim Kyuwon {
1510b56129bSKim Kyuwon 	return reg_offset + bd2802_get_base_offset(id, color);
1520b56129bSKim Kyuwon }
1530b56129bSKim Kyuwon 
1540b56129bSKim Kyuwon 
1550b56129bSKim Kyuwon /*--------------------------------------------------------------*/
1560b56129bSKim Kyuwon /*	BD2802GU core functions					*/
1570b56129bSKim Kyuwon /*--------------------------------------------------------------*/
1580b56129bSKim Kyuwon 
bd2802_write_byte(struct i2c_client * client,u8 reg,u8 val)1590b56129bSKim Kyuwon static int bd2802_write_byte(struct i2c_client *client, u8 reg, u8 val)
1600b56129bSKim Kyuwon {
1610b56129bSKim Kyuwon 	int ret = i2c_smbus_write_byte_data(client, reg, val);
1620b56129bSKim Kyuwon 	if (ret >= 0)
1630b56129bSKim Kyuwon 		return 0;
1640b56129bSKim Kyuwon 
1650b56129bSKim Kyuwon 	dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n",
1660b56129bSKim Kyuwon 						__func__, reg, val, ret);
1670b56129bSKim Kyuwon 
1680b56129bSKim Kyuwon 	return ret;
1690b56129bSKim Kyuwon }
1700b56129bSKim Kyuwon 
bd2802_update_state(struct bd2802_led * led,enum led_ids id,enum led_colors color,enum led_bits led_bit)1710b56129bSKim Kyuwon static void bd2802_update_state(struct bd2802_led *led, enum led_ids id,
1720b56129bSKim Kyuwon 				enum led_colors color, enum led_bits led_bit)
1730b56129bSKim Kyuwon {
1740b56129bSKim Kyuwon 	int i;
1750b56129bSKim Kyuwon 	u8 value;
1760b56129bSKim Kyuwon 
1770b56129bSKim Kyuwon 	for (i = 0; i < LED_NUM; i++) {
1780b56129bSKim Kyuwon 		if (i == id) {
1790b56129bSKim Kyuwon 			switch (color) {
1800b56129bSKim Kyuwon 			case RED:
1810b56129bSKim Kyuwon 				led->led[i].r = led_bit;
1820b56129bSKim Kyuwon 				break;
1830b56129bSKim Kyuwon 			case GREEN:
1840b56129bSKim Kyuwon 				led->led[i].g = led_bit;
1850b56129bSKim Kyuwon 				break;
1860b56129bSKim Kyuwon 			case BLUE:
1870b56129bSKim Kyuwon 				led->led[i].b = led_bit;
1880b56129bSKim Kyuwon 				break;
1890b56129bSKim Kyuwon 			default:
1900b56129bSKim Kyuwon 				dev_err(&led->client->dev,
1910b56129bSKim Kyuwon 					"%s: Invalid color\n", __func__);
1920b56129bSKim Kyuwon 				return;
1930b56129bSKim Kyuwon 			}
1940b56129bSKim Kyuwon 		}
1950b56129bSKim Kyuwon 	}
1960b56129bSKim Kyuwon 
1970b56129bSKim Kyuwon 	if (led_bit == BD2802_BLINK || led_bit == BD2802_ON)
1980b56129bSKim Kyuwon 		return;
1990b56129bSKim Kyuwon 
2000b56129bSKim Kyuwon 	if (!bd2802_is_led_off(led, id))
2010b56129bSKim Kyuwon 		return;
2020b56129bSKim Kyuwon 
2030b56129bSKim Kyuwon 	if (bd2802_is_all_off(led) && !led->adf_on) {
2044c3718f9SLinus Walleij 		gpiod_set_value(led->reset, 1);
2050b56129bSKim Kyuwon 		return;
2060b56129bSKim Kyuwon 	}
2070b56129bSKim Kyuwon 
2080b56129bSKim Kyuwon 	/*
2090b56129bSKim Kyuwon 	 * In this case, other led is turned on, and current led is turned
2100b56129bSKim Kyuwon 	 * off. So set RGB LED Control register to stop the current RGB LED
2110b56129bSKim Kyuwon 	 */
2120b56129bSKim Kyuwon 	value = (id == LED1) ? LED_CTL(1, 0) : LED_CTL(0, 1);
2130b56129bSKim Kyuwon 	bd2802_write_byte(led->client, BD2802_REG_CONTROL, value);
2140b56129bSKim Kyuwon }
2150b56129bSKim Kyuwon 
bd2802_configure(struct bd2802_led * led)2160b56129bSKim Kyuwon static void bd2802_configure(struct bd2802_led *led)
2170b56129bSKim Kyuwon {
2180b56129bSKim Kyuwon 	struct bd2802_led_platform_data *pdata = led->pdata;
2190b56129bSKim Kyuwon 	u8 reg;
2200b56129bSKim Kyuwon 
2210b56129bSKim Kyuwon 	reg = bd2802_get_reg_addr(LED1, RED, BD2802_REG_HOURSETUP);
2220b56129bSKim Kyuwon 	bd2802_write_byte(led->client, reg, pdata->rgb_time);
2230b56129bSKim Kyuwon 
2240b56129bSKim Kyuwon 	reg = bd2802_get_reg_addr(LED2, RED, BD2802_REG_HOURSETUP);
2250b56129bSKim Kyuwon 	bd2802_write_byte(led->client, reg, pdata->rgb_time);
2260b56129bSKim Kyuwon }
2270b56129bSKim Kyuwon 
bd2802_reset_cancel(struct bd2802_led * led)2280b56129bSKim Kyuwon static void bd2802_reset_cancel(struct bd2802_led *led)
2290b56129bSKim Kyuwon {
2304c3718f9SLinus Walleij 	gpiod_set_value(led->reset, 0);
2310b56129bSKim Kyuwon 	udelay(100);
2320b56129bSKim Kyuwon 	bd2802_configure(led);
2330b56129bSKim Kyuwon }
2340b56129bSKim Kyuwon 
bd2802_enable(struct bd2802_led * led,enum led_ids id)2350b56129bSKim Kyuwon static void bd2802_enable(struct bd2802_led *led, enum led_ids id)
2360b56129bSKim Kyuwon {
2370b56129bSKim Kyuwon 	enum led_ids other_led = (id == LED1) ? LED2 : LED1;
2380b56129bSKim Kyuwon 	u8 value, other_led_on;
2390b56129bSKim Kyuwon 
2400b56129bSKim Kyuwon 	other_led_on = !bd2802_is_led_off(led, other_led);
2410b56129bSKim Kyuwon 	if (id == LED1)
2420b56129bSKim Kyuwon 		value = LED_CTL(other_led_on, 1);
2430b56129bSKim Kyuwon 	else
2440b56129bSKim Kyuwon 		value = LED_CTL(1 , other_led_on);
2450b56129bSKim Kyuwon 
2460b56129bSKim Kyuwon 	bd2802_write_byte(led->client, BD2802_REG_CONTROL, value);
2470b56129bSKim Kyuwon }
2480b56129bSKim Kyuwon 
bd2802_set_on(struct bd2802_led * led,enum led_ids id,enum led_colors color)2490b56129bSKim Kyuwon static void bd2802_set_on(struct bd2802_led *led, enum led_ids id,
2500b56129bSKim Kyuwon 							enum led_colors color)
2510b56129bSKim Kyuwon {
2520b56129bSKim Kyuwon 	u8 reg;
2530b56129bSKim Kyuwon 
2540b56129bSKim Kyuwon 	if (bd2802_is_all_off(led) && !led->adf_on)
2550b56129bSKim Kyuwon 		bd2802_reset_cancel(led);
2560b56129bSKim Kyuwon 
2570b56129bSKim Kyuwon 	reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT1SETUP);
2588792f7cfSKim Kyuwon 	bd2802_write_byte(led->client, reg, led->rgb_current);
2590b56129bSKim Kyuwon 	reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT2SETUP);
2600b56129bSKim Kyuwon 	bd2802_write_byte(led->client, reg, BD2802_CURRENT_000);
2610b56129bSKim Kyuwon 	reg = bd2802_get_reg_addr(id, color, BD2802_REG_WAVEPATTERN);
2620b56129bSKim Kyuwon 	bd2802_write_byte(led->client, reg, BD2802_PATTERN_FULL);
2630b56129bSKim Kyuwon 
2640b56129bSKim Kyuwon 	bd2802_enable(led, id);
2650b56129bSKim Kyuwon 	bd2802_update_state(led, id, color, BD2802_ON);
2660b56129bSKim Kyuwon }
2670b56129bSKim Kyuwon 
bd2802_set_blink(struct bd2802_led * led,enum led_ids id,enum led_colors color)2680b56129bSKim Kyuwon static void bd2802_set_blink(struct bd2802_led *led, enum led_ids id,
2690b56129bSKim Kyuwon 							enum led_colors color)
2700b56129bSKim Kyuwon {
2710b56129bSKim Kyuwon 	u8 reg;
2720b56129bSKim Kyuwon 
2730b56129bSKim Kyuwon 	if (bd2802_is_all_off(led) && !led->adf_on)
2740b56129bSKim Kyuwon 		bd2802_reset_cancel(led);
2750b56129bSKim Kyuwon 
2760b56129bSKim Kyuwon 	reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT1SETUP);
2770b56129bSKim Kyuwon 	bd2802_write_byte(led->client, reg, BD2802_CURRENT_000);
2780b56129bSKim Kyuwon 	reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT2SETUP);
2798792f7cfSKim Kyuwon 	bd2802_write_byte(led->client, reg, led->rgb_current);
2800b56129bSKim Kyuwon 	reg = bd2802_get_reg_addr(id, color, BD2802_REG_WAVEPATTERN);
2818792f7cfSKim Kyuwon 	bd2802_write_byte(led->client, reg, led->wave_pattern);
2820b56129bSKim Kyuwon 
2830b56129bSKim Kyuwon 	bd2802_enable(led, id);
2840b56129bSKim Kyuwon 	bd2802_update_state(led, id, color, BD2802_BLINK);
2850b56129bSKim Kyuwon }
2860b56129bSKim Kyuwon 
bd2802_turn_on(struct bd2802_led * led,enum led_ids id,enum led_colors color,enum led_bits led_bit)2870b56129bSKim Kyuwon static void bd2802_turn_on(struct bd2802_led *led, enum led_ids id,
2880b56129bSKim Kyuwon 				enum led_colors color, enum led_bits led_bit)
2890b56129bSKim Kyuwon {
2900b56129bSKim Kyuwon 	if (led_bit == BD2802_OFF) {
2910b56129bSKim Kyuwon 		dev_err(&led->client->dev,
2920b56129bSKim Kyuwon 					"Only 'blink' and 'on' are allowed\n");
2930b56129bSKim Kyuwon 		return;
2940b56129bSKim Kyuwon 	}
2950b56129bSKim Kyuwon 
2960b56129bSKim Kyuwon 	if (led_bit == BD2802_BLINK)
2970b56129bSKim Kyuwon 		bd2802_set_blink(led, id, color);
2980b56129bSKim Kyuwon 	else
2990b56129bSKim Kyuwon 		bd2802_set_on(led, id, color);
3000b56129bSKim Kyuwon }
3010b56129bSKim Kyuwon 
bd2802_turn_off(struct bd2802_led * led,enum led_ids id,enum led_colors color)3020b56129bSKim Kyuwon static void bd2802_turn_off(struct bd2802_led *led, enum led_ids id,
3030b56129bSKim Kyuwon 							enum led_colors color)
3040b56129bSKim Kyuwon {
3050b56129bSKim Kyuwon 	u8 reg;
3060b56129bSKim Kyuwon 
3070b56129bSKim Kyuwon 	if (bd2802_is_rgb_off(led, id, color))
3080b56129bSKim Kyuwon 		return;
3090b56129bSKim Kyuwon 
3100b56129bSKim Kyuwon 	reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT1SETUP);
3110b56129bSKim Kyuwon 	bd2802_write_byte(led->client, reg, BD2802_CURRENT_000);
3120b56129bSKim Kyuwon 	reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT2SETUP);
3130b56129bSKim Kyuwon 	bd2802_write_byte(led->client, reg, BD2802_CURRENT_000);
3140b56129bSKim Kyuwon 
3150b56129bSKim Kyuwon 	bd2802_update_state(led, id, color, BD2802_OFF);
3160b56129bSKim Kyuwon }
3170b56129bSKim Kyuwon 
3180b56129bSKim Kyuwon #define BD2802_SET_REGISTER(reg_addr, reg_name)				\
3190b56129bSKim Kyuwon static ssize_t bd2802_store_reg##reg_addr(struct device *dev,		\
3200b56129bSKim Kyuwon 	struct device_attribute *attr, const char *buf, size_t count)	\
3210b56129bSKim Kyuwon {									\
3220b56129bSKim Kyuwon 	struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));\
3230b56129bSKim Kyuwon 	unsigned long val;						\
3240b56129bSKim Kyuwon 	int ret;							\
3250b56129bSKim Kyuwon 	if (!count)							\
3260b56129bSKim Kyuwon 		return -EINVAL;						\
327fd80fc76SJingoo Han 	ret = kstrtoul(buf, 16, &val);					\
3280b56129bSKim Kyuwon 	if (ret)							\
3290b56129bSKim Kyuwon 		return ret;						\
3300b56129bSKim Kyuwon 	down_write(&led->rwsem);					\
3310b56129bSKim Kyuwon 	bd2802_write_byte(led->client, reg_addr, (u8) val);		\
3320b56129bSKim Kyuwon 	up_write(&led->rwsem);						\
3330b56129bSKim Kyuwon 	return count;							\
3340b56129bSKim Kyuwon }									\
3350b56129bSKim Kyuwon static struct device_attribute bd2802_reg##reg_addr##_attr = {		\
3360c8617d3SGuenter Roeck 	.attr = {.name = reg_name, .mode = 0644},			\
3370b56129bSKim Kyuwon 	.store = bd2802_store_reg##reg_addr,				\
3380b56129bSKim Kyuwon };
3390b56129bSKim Kyuwon 
3400b56129bSKim Kyuwon BD2802_SET_REGISTER(0x00, "0x00");
3410b56129bSKim Kyuwon BD2802_SET_REGISTER(0x01, "0x01");
3420b56129bSKim Kyuwon BD2802_SET_REGISTER(0x02, "0x02");
3430b56129bSKim Kyuwon BD2802_SET_REGISTER(0x03, "0x03");
3440b56129bSKim Kyuwon BD2802_SET_REGISTER(0x04, "0x04");
3450b56129bSKim Kyuwon BD2802_SET_REGISTER(0x05, "0x05");
3460b56129bSKim Kyuwon BD2802_SET_REGISTER(0x06, "0x06");
3470b56129bSKim Kyuwon BD2802_SET_REGISTER(0x07, "0x07");
3480b56129bSKim Kyuwon BD2802_SET_REGISTER(0x08, "0x08");
3490b56129bSKim Kyuwon BD2802_SET_REGISTER(0x09, "0x09");
3500b56129bSKim Kyuwon BD2802_SET_REGISTER(0x0a, "0x0a");
3510b56129bSKim Kyuwon BD2802_SET_REGISTER(0x0b, "0x0b");
3520b56129bSKim Kyuwon BD2802_SET_REGISTER(0x0c, "0x0c");
3530b56129bSKim Kyuwon BD2802_SET_REGISTER(0x0d, "0x0d");
3540b56129bSKim Kyuwon BD2802_SET_REGISTER(0x0e, "0x0e");
3550b56129bSKim Kyuwon BD2802_SET_REGISTER(0x0f, "0x0f");
3560b56129bSKim Kyuwon BD2802_SET_REGISTER(0x10, "0x10");
3570b56129bSKim Kyuwon BD2802_SET_REGISTER(0x11, "0x11");
3580b56129bSKim Kyuwon BD2802_SET_REGISTER(0x12, "0x12");
3590b56129bSKim Kyuwon BD2802_SET_REGISTER(0x13, "0x13");
3600b56129bSKim Kyuwon BD2802_SET_REGISTER(0x14, "0x14");
3610b56129bSKim Kyuwon BD2802_SET_REGISTER(0x15, "0x15");
3620b56129bSKim Kyuwon 
3630b56129bSKim Kyuwon static struct device_attribute *bd2802_addr_attributes[] = {
3640b56129bSKim Kyuwon 	&bd2802_reg0x00_attr,
3650b56129bSKim Kyuwon 	&bd2802_reg0x01_attr,
3660b56129bSKim Kyuwon 	&bd2802_reg0x02_attr,
3670b56129bSKim Kyuwon 	&bd2802_reg0x03_attr,
3680b56129bSKim Kyuwon 	&bd2802_reg0x04_attr,
3690b56129bSKim Kyuwon 	&bd2802_reg0x05_attr,
3700b56129bSKim Kyuwon 	&bd2802_reg0x06_attr,
3710b56129bSKim Kyuwon 	&bd2802_reg0x07_attr,
3720b56129bSKim Kyuwon 	&bd2802_reg0x08_attr,
3730b56129bSKim Kyuwon 	&bd2802_reg0x09_attr,
3740b56129bSKim Kyuwon 	&bd2802_reg0x0a_attr,
3750b56129bSKim Kyuwon 	&bd2802_reg0x0b_attr,
3760b56129bSKim Kyuwon 	&bd2802_reg0x0c_attr,
3770b56129bSKim Kyuwon 	&bd2802_reg0x0d_attr,
3780b56129bSKim Kyuwon 	&bd2802_reg0x0e_attr,
3790b56129bSKim Kyuwon 	&bd2802_reg0x0f_attr,
3800b56129bSKim Kyuwon 	&bd2802_reg0x10_attr,
3810b56129bSKim Kyuwon 	&bd2802_reg0x11_attr,
3820b56129bSKim Kyuwon 	&bd2802_reg0x12_attr,
3830b56129bSKim Kyuwon 	&bd2802_reg0x13_attr,
3840b56129bSKim Kyuwon 	&bd2802_reg0x14_attr,
3850b56129bSKim Kyuwon 	&bd2802_reg0x15_attr,
3860b56129bSKim Kyuwon };
3870b56129bSKim Kyuwon 
bd2802_enable_adv_conf(struct bd2802_led * led)3880b56129bSKim Kyuwon static void bd2802_enable_adv_conf(struct bd2802_led *led)
3890b56129bSKim Kyuwon {
3900b56129bSKim Kyuwon 	int i, ret;
3910b56129bSKim Kyuwon 
3920b56129bSKim Kyuwon 	for (i = 0; i < ARRAY_SIZE(bd2802_addr_attributes); i++) {
3930b56129bSKim Kyuwon 		ret = device_create_file(&led->client->dev,
3940b56129bSKim Kyuwon 						bd2802_addr_attributes[i]);
3950b56129bSKim Kyuwon 		if (ret) {
3968792f7cfSKim Kyuwon 			dev_err(&led->client->dev, "failed: sysfs file %s\n",
3970b56129bSKim Kyuwon 					bd2802_addr_attributes[i]->attr.name);
3980b56129bSKim Kyuwon 			goto failed_remove_files;
3990b56129bSKim Kyuwon 		}
4000b56129bSKim Kyuwon 	}
4010b56129bSKim Kyuwon 
4020b56129bSKim Kyuwon 	if (bd2802_is_all_off(led))
4030b56129bSKim Kyuwon 		bd2802_reset_cancel(led);
4040b56129bSKim Kyuwon 
4050b56129bSKim Kyuwon 	led->adf_on = 1;
4060b56129bSKim Kyuwon 
4070b56129bSKim Kyuwon 	return;
4080b56129bSKim Kyuwon 
4090b56129bSKim Kyuwon failed_remove_files:
4100b56129bSKim Kyuwon 	for (i--; i >= 0; i--)
4110b56129bSKim Kyuwon 		device_remove_file(&led->client->dev,
4120b56129bSKim Kyuwon 						bd2802_addr_attributes[i]);
4130b56129bSKim Kyuwon }
4140b56129bSKim Kyuwon 
bd2802_disable_adv_conf(struct bd2802_led * led)4150b56129bSKim Kyuwon static void bd2802_disable_adv_conf(struct bd2802_led *led)
4160b56129bSKim Kyuwon {
4170b56129bSKim Kyuwon 	int i;
4180b56129bSKim Kyuwon 
4190b56129bSKim Kyuwon 	for (i = 0; i < ARRAY_SIZE(bd2802_addr_attributes); i++)
4200b56129bSKim Kyuwon 		device_remove_file(&led->client->dev,
4210b56129bSKim Kyuwon 						bd2802_addr_attributes[i]);
4220b56129bSKim Kyuwon 
4230b56129bSKim Kyuwon 	if (bd2802_is_all_off(led))
4244c3718f9SLinus Walleij 		gpiod_set_value(led->reset, 1);
4250b56129bSKim Kyuwon 
4260b56129bSKim Kyuwon 	led->adf_on = 0;
4270b56129bSKim Kyuwon }
4280b56129bSKim Kyuwon 
bd2802_show_adv_conf(struct device * dev,struct device_attribute * attr,char * buf)4290b56129bSKim Kyuwon static ssize_t bd2802_show_adv_conf(struct device *dev,
4300b56129bSKim Kyuwon 	struct device_attribute *attr, char *buf)
4310b56129bSKim Kyuwon {
4320b56129bSKim Kyuwon 	struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));
4330b56129bSKim Kyuwon 	ssize_t ret;
4340b56129bSKim Kyuwon 
4350b56129bSKim Kyuwon 	down_read(&led->rwsem);
4360b56129bSKim Kyuwon 	if (led->adf_on)
4370b56129bSKim Kyuwon 		ret = sprintf(buf, "on\n");
4380b56129bSKim Kyuwon 	else
4390b56129bSKim Kyuwon 		ret = sprintf(buf, "off\n");
4400b56129bSKim Kyuwon 	up_read(&led->rwsem);
4410b56129bSKim Kyuwon 
4420b56129bSKim Kyuwon 	return ret;
4430b56129bSKim Kyuwon }
4440b56129bSKim Kyuwon 
bd2802_store_adv_conf(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)4450b56129bSKim Kyuwon static ssize_t bd2802_store_adv_conf(struct device *dev,
4460b56129bSKim Kyuwon 	struct device_attribute *attr, const char *buf, size_t count)
4470b56129bSKim Kyuwon {
4480b56129bSKim Kyuwon 	struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));
4490b56129bSKim Kyuwon 
4500b56129bSKim Kyuwon 	if (!count)
4510b56129bSKim Kyuwon 		return -EINVAL;
4520b56129bSKim Kyuwon 
4530b56129bSKim Kyuwon 	down_write(&led->rwsem);
4540b56129bSKim Kyuwon 	if (!led->adf_on && !strncmp(buf, "on", 2))
4550b56129bSKim Kyuwon 		bd2802_enable_adv_conf(led);
4560b56129bSKim Kyuwon 	else if (led->adf_on && !strncmp(buf, "off", 3))
4570b56129bSKim Kyuwon 		bd2802_disable_adv_conf(led);
4580b56129bSKim Kyuwon 	up_write(&led->rwsem);
4590b56129bSKim Kyuwon 
4600b56129bSKim Kyuwon 	return count;
4610b56129bSKim Kyuwon }
4620b56129bSKim Kyuwon 
4630b56129bSKim Kyuwon static struct device_attribute bd2802_adv_conf_attr = {
4640b56129bSKim Kyuwon 	.attr = {
4650b56129bSKim Kyuwon 		.name = "advanced_configuration",
4660b56129bSKim Kyuwon 		.mode = 0644,
4670b56129bSKim Kyuwon 	},
4680b56129bSKim Kyuwon 	.show = bd2802_show_adv_conf,
4690b56129bSKim Kyuwon 	.store = bd2802_store_adv_conf,
4700b56129bSKim Kyuwon };
4710b56129bSKim Kyuwon 
4728792f7cfSKim Kyuwon #define BD2802_CONTROL_ATTR(attr_name, name_str)			\
4738792f7cfSKim Kyuwon static ssize_t bd2802_show_##attr_name(struct device *dev,		\
4748792f7cfSKim Kyuwon 	struct device_attribute *attr, char *buf)			\
4758792f7cfSKim Kyuwon {									\
4768792f7cfSKim Kyuwon 	struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));\
4778792f7cfSKim Kyuwon 	ssize_t ret;							\
4788792f7cfSKim Kyuwon 	down_read(&led->rwsem);						\
4798792f7cfSKim Kyuwon 	ret = sprintf(buf, "0x%02x\n", led->attr_name);			\
4808792f7cfSKim Kyuwon 	up_read(&led->rwsem);						\
4818792f7cfSKim Kyuwon 	return ret;							\
4828792f7cfSKim Kyuwon }									\
4838792f7cfSKim Kyuwon static ssize_t bd2802_store_##attr_name(struct device *dev,		\
4848792f7cfSKim Kyuwon 	struct device_attribute *attr, const char *buf, size_t count)	\
4858792f7cfSKim Kyuwon {									\
4868792f7cfSKim Kyuwon 	struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));\
4878792f7cfSKim Kyuwon 	unsigned long val;						\
4888792f7cfSKim Kyuwon 	int ret;							\
4898792f7cfSKim Kyuwon 	if (!count)							\
4908792f7cfSKim Kyuwon 		return -EINVAL;						\
491fd80fc76SJingoo Han 	ret = kstrtoul(buf, 16, &val);					\
4928792f7cfSKim Kyuwon 	if (ret)							\
4938792f7cfSKim Kyuwon 		return ret;						\
4948792f7cfSKim Kyuwon 	down_write(&led->rwsem);					\
4958792f7cfSKim Kyuwon 	led->attr_name = val;						\
4968792f7cfSKim Kyuwon 	up_write(&led->rwsem);						\
4978792f7cfSKim Kyuwon 	return count;							\
4988792f7cfSKim Kyuwon }									\
4998792f7cfSKim Kyuwon static struct device_attribute bd2802_##attr_name##_attr = {		\
5008792f7cfSKim Kyuwon 	.attr = {							\
5018792f7cfSKim Kyuwon 		.name = name_str,					\
5028792f7cfSKim Kyuwon 		.mode = 0644,						\
5038792f7cfSKim Kyuwon 	},								\
5048792f7cfSKim Kyuwon 	.show = bd2802_show_##attr_name,				\
5058792f7cfSKim Kyuwon 	.store = bd2802_store_##attr_name,				\
5068792f7cfSKim Kyuwon };
5078792f7cfSKim Kyuwon 
5088792f7cfSKim Kyuwon BD2802_CONTROL_ATTR(wave_pattern, "wave_pattern");
5098792f7cfSKim Kyuwon BD2802_CONTROL_ATTR(rgb_current, "rgb_current");
5108792f7cfSKim Kyuwon 
5118792f7cfSKim Kyuwon static struct device_attribute *bd2802_attributes[] = {
5128792f7cfSKim Kyuwon 	&bd2802_adv_conf_attr,
5138792f7cfSKim Kyuwon 	&bd2802_wave_pattern_attr,
5148792f7cfSKim Kyuwon 	&bd2802_rgb_current_attr,
5158792f7cfSKim Kyuwon };
5168792f7cfSKim Kyuwon 
5170b56129bSKim Kyuwon #define BD2802_CONTROL_RGBS(name, id, clr)				\
518cd042f01SAndrew Lunn static int bd2802_set_##name##_brightness(struct led_classdev *led_cdev,\
5190b56129bSKim Kyuwon 					enum led_brightness value)	\
5200b56129bSKim Kyuwon {									\
5210b56129bSKim Kyuwon 	struct bd2802_led *led =					\
5220b56129bSKim Kyuwon 		container_of(led_cdev, struct bd2802_led, cdev_##name);	\
5230b56129bSKim Kyuwon 	led->led_id = id;						\
5240b56129bSKim Kyuwon 	led->color = clr;						\
525cd042f01SAndrew Lunn 	if (value == LED_OFF) {						\
5260b56129bSKim Kyuwon 		led->state = BD2802_OFF;				\
527cd042f01SAndrew Lunn 		bd2802_turn_off(led, led->led_id, led->color);		\
528cd042f01SAndrew Lunn 	} else {							\
5290b56129bSKim Kyuwon 		led->state = BD2802_ON;					\
530cd042f01SAndrew Lunn 		bd2802_turn_on(led, led->led_id, led->color, BD2802_ON);\
531cd042f01SAndrew Lunn 	}								\
532cd042f01SAndrew Lunn 	return 0;							\
5330b56129bSKim Kyuwon }									\
5340b56129bSKim Kyuwon static int bd2802_set_##name##_blink(struct led_classdev *led_cdev,	\
5350b56129bSKim Kyuwon 		unsigned long *delay_on, unsigned long *delay_off)	\
5360b56129bSKim Kyuwon {									\
5370b56129bSKim Kyuwon 	struct bd2802_led *led =					\
5380b56129bSKim Kyuwon 		container_of(led_cdev, struct bd2802_led, cdev_##name);	\
5390b56129bSKim Kyuwon 	if (*delay_on == 0 || *delay_off == 0)				\
5400b56129bSKim Kyuwon 		return -EINVAL;						\
5410b56129bSKim Kyuwon 	led->led_id = id;						\
5420b56129bSKim Kyuwon 	led->color = clr;						\
5430b56129bSKim Kyuwon 	led->state = BD2802_BLINK;					\
544cd042f01SAndrew Lunn 	bd2802_turn_on(led, led->led_id, led->color, BD2802_BLINK);	\
5450b56129bSKim Kyuwon 	return 0;							\
5460b56129bSKim Kyuwon }
5470b56129bSKim Kyuwon 
5480b56129bSKim Kyuwon BD2802_CONTROL_RGBS(led1r, LED1, RED);
5490b56129bSKim Kyuwon BD2802_CONTROL_RGBS(led1g, LED1, GREEN);
5500b56129bSKim Kyuwon BD2802_CONTROL_RGBS(led1b, LED1, BLUE);
5510b56129bSKim Kyuwon BD2802_CONTROL_RGBS(led2r, LED2, RED);
5520b56129bSKim Kyuwon BD2802_CONTROL_RGBS(led2g, LED2, GREEN);
5530b56129bSKim Kyuwon BD2802_CONTROL_RGBS(led2b, LED2, BLUE);
5540b56129bSKim Kyuwon 
bd2802_register_led_classdev(struct bd2802_led * led)5550b56129bSKim Kyuwon static int bd2802_register_led_classdev(struct bd2802_led *led)
5560b56129bSKim Kyuwon {
5570b56129bSKim Kyuwon 	int ret;
5580b56129bSKim Kyuwon 
5590b56129bSKim Kyuwon 	led->cdev_led1r.name = "led1_R";
5600b56129bSKim Kyuwon 	led->cdev_led1r.brightness = LED_OFF;
561cd042f01SAndrew Lunn 	led->cdev_led1r.brightness_set_blocking = bd2802_set_led1r_brightness;
5620b56129bSKim Kyuwon 	led->cdev_led1r.blink_set = bd2802_set_led1r_blink;
5630b56129bSKim Kyuwon 
5640b56129bSKim Kyuwon 	ret = led_classdev_register(&led->client->dev, &led->cdev_led1r);
5650b56129bSKim Kyuwon 	if (ret < 0) {
5660b56129bSKim Kyuwon 		dev_err(&led->client->dev, "couldn't register LED %s\n",
5670b56129bSKim Kyuwon 							led->cdev_led1r.name);
5680b56129bSKim Kyuwon 		goto failed_unregister_led1_R;
5690b56129bSKim Kyuwon 	}
5700b56129bSKim Kyuwon 
5710b56129bSKim Kyuwon 	led->cdev_led1g.name = "led1_G";
5720b56129bSKim Kyuwon 	led->cdev_led1g.brightness = LED_OFF;
573cd042f01SAndrew Lunn 	led->cdev_led1g.brightness_set_blocking = bd2802_set_led1g_brightness;
5740b56129bSKim Kyuwon 	led->cdev_led1g.blink_set = bd2802_set_led1g_blink;
5750b56129bSKim Kyuwon 
5760b56129bSKim Kyuwon 	ret = led_classdev_register(&led->client->dev, &led->cdev_led1g);
5770b56129bSKim Kyuwon 	if (ret < 0) {
5780b56129bSKim Kyuwon 		dev_err(&led->client->dev, "couldn't register LED %s\n",
5790b56129bSKim Kyuwon 							led->cdev_led1g.name);
5800b56129bSKim Kyuwon 		goto failed_unregister_led1_G;
5810b56129bSKim Kyuwon 	}
5820b56129bSKim Kyuwon 
5830b56129bSKim Kyuwon 	led->cdev_led1b.name = "led1_B";
5840b56129bSKim Kyuwon 	led->cdev_led1b.brightness = LED_OFF;
585cd042f01SAndrew Lunn 	led->cdev_led1b.brightness_set_blocking = bd2802_set_led1b_brightness;
5860b56129bSKim Kyuwon 	led->cdev_led1b.blink_set = bd2802_set_led1b_blink;
5870b56129bSKim Kyuwon 
5880b56129bSKim Kyuwon 	ret = led_classdev_register(&led->client->dev, &led->cdev_led1b);
5890b56129bSKim Kyuwon 	if (ret < 0) {
5900b56129bSKim Kyuwon 		dev_err(&led->client->dev, "couldn't register LED %s\n",
5910b56129bSKim Kyuwon 							led->cdev_led1b.name);
5920b56129bSKim Kyuwon 		goto failed_unregister_led1_B;
5930b56129bSKim Kyuwon 	}
5940b56129bSKim Kyuwon 
5950b56129bSKim Kyuwon 	led->cdev_led2r.name = "led2_R";
5960b56129bSKim Kyuwon 	led->cdev_led2r.brightness = LED_OFF;
597cd042f01SAndrew Lunn 	led->cdev_led2r.brightness_set_blocking = bd2802_set_led2r_brightness;
5980b56129bSKim Kyuwon 	led->cdev_led2r.blink_set = bd2802_set_led2r_blink;
5990b56129bSKim Kyuwon 
6000b56129bSKim Kyuwon 	ret = led_classdev_register(&led->client->dev, &led->cdev_led2r);
6010b56129bSKim Kyuwon 	if (ret < 0) {
6020b56129bSKim Kyuwon 		dev_err(&led->client->dev, "couldn't register LED %s\n",
6030b56129bSKim Kyuwon 							led->cdev_led2r.name);
6040b56129bSKim Kyuwon 		goto failed_unregister_led2_R;
6050b56129bSKim Kyuwon 	}
6060b56129bSKim Kyuwon 
6070b56129bSKim Kyuwon 	led->cdev_led2g.name = "led2_G";
6080b56129bSKim Kyuwon 	led->cdev_led2g.brightness = LED_OFF;
609cd042f01SAndrew Lunn 	led->cdev_led2g.brightness_set_blocking = bd2802_set_led2g_brightness;
6100b56129bSKim Kyuwon 	led->cdev_led2g.blink_set = bd2802_set_led2g_blink;
6110b56129bSKim Kyuwon 
6120b56129bSKim Kyuwon 	ret = led_classdev_register(&led->client->dev, &led->cdev_led2g);
6130b56129bSKim Kyuwon 	if (ret < 0) {
6140b56129bSKim Kyuwon 		dev_err(&led->client->dev, "couldn't register LED %s\n",
6150b56129bSKim Kyuwon 							led->cdev_led2g.name);
6160b56129bSKim Kyuwon 		goto failed_unregister_led2_G;
6170b56129bSKim Kyuwon 	}
6180b56129bSKim Kyuwon 
6190b56129bSKim Kyuwon 	led->cdev_led2b.name = "led2_B";
6200b56129bSKim Kyuwon 	led->cdev_led2b.brightness = LED_OFF;
621cd042f01SAndrew Lunn 	led->cdev_led2b.brightness_set_blocking = bd2802_set_led2b_brightness;
6220b56129bSKim Kyuwon 	led->cdev_led2b.blink_set = bd2802_set_led2b_blink;
6230b56129bSKim Kyuwon 	led->cdev_led2b.flags |= LED_CORE_SUSPENDRESUME;
6240b56129bSKim Kyuwon 
6250b56129bSKim Kyuwon 	ret = led_classdev_register(&led->client->dev, &led->cdev_led2b);
6260b56129bSKim Kyuwon 	if (ret < 0) {
6270b56129bSKim Kyuwon 		dev_err(&led->client->dev, "couldn't register LED %s\n",
6280b56129bSKim Kyuwon 							led->cdev_led2b.name);
6290b56129bSKim Kyuwon 		goto failed_unregister_led2_B;
6300b56129bSKim Kyuwon 	}
6310b56129bSKim Kyuwon 
6320b56129bSKim Kyuwon 	return 0;
6330b56129bSKim Kyuwon 
6340b56129bSKim Kyuwon failed_unregister_led2_B:
6350b56129bSKim Kyuwon 	led_classdev_unregister(&led->cdev_led2g);
6360b56129bSKim Kyuwon failed_unregister_led2_G:
6370b56129bSKim Kyuwon 	led_classdev_unregister(&led->cdev_led2r);
6380b56129bSKim Kyuwon failed_unregister_led2_R:
6390b56129bSKim Kyuwon 	led_classdev_unregister(&led->cdev_led1b);
6400b56129bSKim Kyuwon failed_unregister_led1_B:
6410b56129bSKim Kyuwon 	led_classdev_unregister(&led->cdev_led1g);
6420b56129bSKim Kyuwon failed_unregister_led1_G:
6430b56129bSKim Kyuwon 	led_classdev_unregister(&led->cdev_led1r);
6440b56129bSKim Kyuwon failed_unregister_led1_R:
6450b56129bSKim Kyuwon 
6460b56129bSKim Kyuwon 	return ret;
6470b56129bSKim Kyuwon }
6480b56129bSKim Kyuwon 
bd2802_unregister_led_classdev(struct bd2802_led * led)6490b56129bSKim Kyuwon static void bd2802_unregister_led_classdev(struct bd2802_led *led)
6500b56129bSKim Kyuwon {
6511424e21fSAxel Lin 	led_classdev_unregister(&led->cdev_led2b);
6521424e21fSAxel Lin 	led_classdev_unregister(&led->cdev_led2g);
6531424e21fSAxel Lin 	led_classdev_unregister(&led->cdev_led2r);
6541424e21fSAxel Lin 	led_classdev_unregister(&led->cdev_led1b);
6551424e21fSAxel Lin 	led_classdev_unregister(&led->cdev_led1g);
6560b56129bSKim Kyuwon 	led_classdev_unregister(&led->cdev_led1r);
6570b56129bSKim Kyuwon }
6580b56129bSKim Kyuwon 
bd2802_probe(struct i2c_client * client)6596900cd26SUwe Kleine-König static int bd2802_probe(struct i2c_client *client)
6600b56129bSKim Kyuwon {
6610b56129bSKim Kyuwon 	struct bd2802_led *led;
6628792f7cfSKim Kyuwon 	int ret, i;
6630b56129bSKim Kyuwon 
664ac63ff50SBryan Wu 	led = devm_kzalloc(&client->dev, sizeof(struct bd2802_led), GFP_KERNEL);
6650c9a03b6SXiubo Li 	if (!led)
6660b56129bSKim Kyuwon 		return -ENOMEM;
6670b56129bSKim Kyuwon 
6680b56129bSKim Kyuwon 	led->client = client;
6690b56129bSKim Kyuwon 	i2c_set_clientdata(client, led);
6700b56129bSKim Kyuwon 
6714c3718f9SLinus Walleij 	/*
6724c3718f9SLinus Walleij 	 * Configure RESET GPIO (L: RESET, H: RESET cancel)
6734c3718f9SLinus Walleij 	 *
6744c3718f9SLinus Walleij 	 * We request the reset GPIO as OUT_LOW which means de-asserted,
6754c3718f9SLinus Walleij 	 * board files specifying this GPIO line in a machine descriptor
6764c3718f9SLinus Walleij 	 * table should take care to specify GPIO_ACTIVE_LOW for this line.
6774c3718f9SLinus Walleij 	 */
6784c3718f9SLinus Walleij 	led->reset = devm_gpiod_get(&client->dev, "reset", GPIOD_OUT_LOW);
6794c3718f9SLinus Walleij 	if (IS_ERR(led->reset))
6804c3718f9SLinus Walleij 		return PTR_ERR(led->reset);
6810b56129bSKim Kyuwon 
6820b56129bSKim Kyuwon 	/* Tacss = min 0.1ms */
6830b56129bSKim Kyuwon 	udelay(100);
6840b56129bSKim Kyuwon 
6850b56129bSKim Kyuwon 	/* Detect BD2802GU */
6860b56129bSKim Kyuwon 	ret = bd2802_write_byte(client, BD2802_REG_CLKSETUP, 0x00);
6870b56129bSKim Kyuwon 	if (ret < 0) {
6880b56129bSKim Kyuwon 		dev_err(&client->dev, "failed to detect device\n");
689ac63ff50SBryan Wu 		return ret;
6900b56129bSKim Kyuwon 	} else
6910b56129bSKim Kyuwon 		dev_info(&client->dev, "return 0x%02x\n", ret);
6920b56129bSKim Kyuwon 
6930b56129bSKim Kyuwon 	/* To save the power, reset BD2802 after detecting */
6944c3718f9SLinus Walleij 	gpiod_set_value(led->reset, 1);
6950b56129bSKim Kyuwon 
6968792f7cfSKim Kyuwon 	/* Default attributes */
6978792f7cfSKim Kyuwon 	led->wave_pattern = BD2802_PATTERN_HALF;
6988792f7cfSKim Kyuwon 	led->rgb_current = BD2802_CURRENT_032;
6998792f7cfSKim Kyuwon 
7000b56129bSKim Kyuwon 	init_rwsem(&led->rwsem);
7010b56129bSKim Kyuwon 
7028792f7cfSKim Kyuwon 	for (i = 0; i < ARRAY_SIZE(bd2802_attributes); i++) {
7038792f7cfSKim Kyuwon 		ret = device_create_file(&led->client->dev,
7048792f7cfSKim Kyuwon 						bd2802_attributes[i]);
7050b56129bSKim Kyuwon 		if (ret) {
7068792f7cfSKim Kyuwon 			dev_err(&led->client->dev, "failed: sysfs file %s\n",
7078792f7cfSKim Kyuwon 					bd2802_attributes[i]->attr.name);
7088792f7cfSKim Kyuwon 			goto failed_unregister_dev_file;
7098792f7cfSKim Kyuwon 		}
7100b56129bSKim Kyuwon 	}
7110b56129bSKim Kyuwon 
7120b56129bSKim Kyuwon 	ret = bd2802_register_led_classdev(led);
7130b56129bSKim Kyuwon 	if (ret < 0)
7140b56129bSKim Kyuwon 		goto failed_unregister_dev_file;
7150b56129bSKim Kyuwon 
7160b56129bSKim Kyuwon 	return 0;
7170b56129bSKim Kyuwon 
7180b56129bSKim Kyuwon failed_unregister_dev_file:
7198792f7cfSKim Kyuwon 	for (i--; i >= 0; i--)
7208792f7cfSKim Kyuwon 		device_remove_file(&led->client->dev, bd2802_attributes[i]);
7210b56129bSKim Kyuwon 	return ret;
7220b56129bSKim Kyuwon }
7230b56129bSKim Kyuwon 
bd2802_remove(struct i2c_client * client)724ed5c2f5fSUwe Kleine-König static void bd2802_remove(struct i2c_client *client)
7250b56129bSKim Kyuwon {
7260b56129bSKim Kyuwon 	struct bd2802_led *led = i2c_get_clientdata(client);
7278792f7cfSKim Kyuwon 	int i;
7280b56129bSKim Kyuwon 
7294c3718f9SLinus Walleij 	gpiod_set_value(led->reset, 1);
7308792f7cfSKim Kyuwon 	bd2802_unregister_led_classdev(led);
7310b56129bSKim Kyuwon 	if (led->adf_on)
7320b56129bSKim Kyuwon 		bd2802_disable_adv_conf(led);
7338792f7cfSKim Kyuwon 	for (i = 0; i < ARRAY_SIZE(bd2802_attributes); i++)
7348792f7cfSKim Kyuwon 		device_remove_file(&led->client->dev, bd2802_attributes[i]);
7350b56129bSKim Kyuwon }
7360b56129bSKim Kyuwon 
737cceba0e4SJingoo Han #ifdef CONFIG_PM_SLEEP
bd2802_restore_state(struct bd2802_led * led)738551ea738SMark Brown static void bd2802_restore_state(struct bd2802_led *led)
7390b56129bSKim Kyuwon {
740551ea738SMark Brown 	int i;
741551ea738SMark Brown 
742551ea738SMark Brown 	for (i = 0; i < LED_NUM; i++) {
743551ea738SMark Brown 		if (led->led[i].r)
744551ea738SMark Brown 			bd2802_turn_on(led, i, RED, led->led[i].r);
745551ea738SMark Brown 		if (led->led[i].g)
746551ea738SMark Brown 			bd2802_turn_on(led, i, GREEN, led->led[i].g);
747551ea738SMark Brown 		if (led->led[i].b)
748551ea738SMark Brown 			bd2802_turn_on(led, i, BLUE, led->led[i].b);
749551ea738SMark Brown 	}
750551ea738SMark Brown }
751551ea738SMark Brown 
bd2802_suspend(struct device * dev)752551ea738SMark Brown static int bd2802_suspend(struct device *dev)
753551ea738SMark Brown {
754551ea738SMark Brown 	struct i2c_client *client = to_i2c_client(dev);
7550b56129bSKim Kyuwon 	struct bd2802_led *led = i2c_get_clientdata(client);
7560b56129bSKim Kyuwon 
7574c3718f9SLinus Walleij 	gpiod_set_value(led->reset, 1);
7580b56129bSKim Kyuwon 
7590b56129bSKim Kyuwon 	return 0;
7600b56129bSKim Kyuwon }
7610b56129bSKim Kyuwon 
bd2802_resume(struct device * dev)762551ea738SMark Brown static int bd2802_resume(struct device *dev)
7630b56129bSKim Kyuwon {
764551ea738SMark Brown 	struct i2c_client *client = to_i2c_client(dev);
7650b56129bSKim Kyuwon 	struct bd2802_led *led = i2c_get_clientdata(client);
7660b56129bSKim Kyuwon 
7670b56129bSKim Kyuwon 	if (!bd2802_is_all_off(led) || led->adf_on) {
768b8389018SKim Kyuwon 		bd2802_reset_cancel(led);
7690b56129bSKim Kyuwon 		bd2802_restore_state(led);
7700b56129bSKim Kyuwon 	}
7710b56129bSKim Kyuwon 
7720b56129bSKim Kyuwon 	return 0;
7730b56129bSKim Kyuwon }
774cceba0e4SJingoo Han #endif
7750b56129bSKim Kyuwon 
776551ea738SMark Brown static SIMPLE_DEV_PM_OPS(bd2802_pm, bd2802_suspend, bd2802_resume);
777551ea738SMark Brown 
7780b56129bSKim Kyuwon static const struct i2c_device_id bd2802_id[] = {
7790b56129bSKim Kyuwon 	{ "BD2802", 0 },
7800b56129bSKim Kyuwon 	{ }
7810b56129bSKim Kyuwon };
7820b56129bSKim Kyuwon MODULE_DEVICE_TABLE(i2c, bd2802_id);
7830b56129bSKim Kyuwon 
7840b56129bSKim Kyuwon static struct i2c_driver bd2802_i2c_driver = {
7850b56129bSKim Kyuwon 	.driver	= {
7860b56129bSKim Kyuwon 		.name	= "BD2802",
787cceba0e4SJingoo Han 		.pm	= &bd2802_pm,
7880b56129bSKim Kyuwon 	},
789*d9ff8a8eSUwe Kleine-König 	.probe		= bd2802_probe,
790e9dd68cfSJingoo Han 	.remove		= bd2802_remove,
7910b56129bSKim Kyuwon 	.id_table	= bd2802_id,
7920b56129bSKim Kyuwon };
7930b56129bSKim Kyuwon 
79409a0d183SAxel Lin module_i2c_driver(bd2802_i2c_driver);
7950b56129bSKim Kyuwon 
7960b56129bSKim Kyuwon MODULE_AUTHOR("Kim Kyuwon <q1.kim@samsung.com>");
7970b56129bSKim Kyuwon MODULE_DESCRIPTION("BD2802 LED driver");
7981b18cf41SKim Kyuwon MODULE_LICENSE("GPL v2");
799