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