leds: pm8xxx: Add support for RGB leds
pm8038 is the first PMIC chip to support RGB led, so
driver framework must be added to support it.
Change-Id: I8103f1e5ec37cdd8718a3daa10587f56a44729c6
Signed-off-by: Amy Maloche <amaloche@codeaurora.org>
diff --git a/drivers/leds/leds-pm8xxx.c b/drivers/leds/leds-pm8xxx.c
index d1bfc07..6770ec8 100644
--- a/drivers/leds/leds-pm8xxx.c
+++ b/drivers/leds/leds-pm8xxx.c
@@ -79,6 +79,13 @@
#define WLED_SYNC_VAL 0x07
#define WLED_SYNC_RESET_VAL 0x00
+#define SSBI_REG_ADDR_RGB_CNTL1 0x12D
+#define SSBI_REG_ADDR_RGB_CNTL2 0x12E
+
+#define PM8XXX_DRV_RGB_RED_LED BIT(2)
+#define PM8XXX_DRV_RGB_GREEN_LED BIT(1)
+#define PM8XXX_DRV_RGB_BLUE_LED BIT(0)
+
#define MAX_FLASH_LED_CURRENT 300
#define MAX_LC_LED_CURRENT 40
#define MAX_KP_BL_LED_CURRENT 300
@@ -99,7 +106,7 @@
#define PM8XXX_LED_PWM_FLAGS (PM_PWM_LUT_LOOP | PM_PWM_LUT_RAMP_UP)
#define LED_MAP(_version, _kb, _led0, _led1, _led2, _flash_led0, _flash_led1, \
- _wled)\
+ _wled, _rgb_led_red, _rgb_led_green, _rgb_led_blue)\
{\
.version = _version,\
.supported = _kb << PM8XXX_ID_LED_KB_LIGHT | \
@@ -107,7 +114,10 @@
_led2 << PM8XXX_ID_LED_2 | \
_flash_led0 << PM8XXX_ID_FLASH_LED_0 | \
_flash_led1 << PM8XXX_ID_FLASH_LED_1 | \
- _wled << PM8XXX_ID_WLED, \
+ _wled << PM8XXX_ID_WLED | \
+ _rgb_led_red << PM8XXX_ID_RGB_LED_RED | \
+ _rgb_led_green << PM8XXX_ID_RGB_LED_GREEN | \
+ _rgb_led_blue << PM8XXX_ID_RGB_LED_BLUE, \
}
/**
@@ -122,11 +132,11 @@
};
static const struct supported_leds led_map[] = {
- LED_MAP(PM8XXX_VERSION_8058, 1, 1, 1, 1, 1, 1, 0),
- LED_MAP(PM8XXX_VERSION_8921, 1, 1, 1, 1, 1, 1, 0),
- LED_MAP(PM8XXX_VERSION_8018, 1, 0, 0, 0, 0, 0, 0),
- LED_MAP(PM8XXX_VERSION_8922, 0, 0, 0, 0, 1, 1, 1),
- LED_MAP(PM8XXX_VERSION_8038, 0, 0, 0, 0, 0, 0, 1),
+ LED_MAP(PM8XXX_VERSION_8058, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0),
+ LED_MAP(PM8XXX_VERSION_8921, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0),
+ LED_MAP(PM8XXX_VERSION_8018, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0),
+ LED_MAP(PM8XXX_VERSION_8922, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1),
+ LED_MAP(PM8XXX_VERSION_8038, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1),
};
/**
@@ -291,6 +301,44 @@
}
}
+static void
+led_rgb_set(struct pm8xxx_led_data *led, enum led_brightness value)
+{
+ int rc;
+ u8 val, mask;
+
+ rc = pm8xxx_readb(led->dev->parent, SSBI_REG_ADDR_RGB_CNTL2, &val);
+ if (rc) {
+ dev_err(led->cdev.dev, "can't read rgb ctrl register rc=%d\n",
+ rc);
+ return;
+ }
+
+ switch (led->id) {
+ case PM8XXX_ID_RGB_LED_RED:
+ mask = PM8XXX_DRV_RGB_RED_LED;
+ break;
+ case PM8XXX_ID_RGB_LED_GREEN:
+ mask = PM8XXX_DRV_RGB_GREEN_LED;
+ break;
+ case PM8XXX_ID_RGB_LED_BLUE:
+ mask = PM8XXX_DRV_RGB_BLUE_LED;
+ break;
+ default:
+ return;
+ }
+
+ if (value)
+ val |= mask;
+ else
+ val &= ~mask;
+
+ rc = pm8xxx_writeb(led->dev->parent, SSBI_REG_ADDR_RGB_CNTL2, val);
+ if (rc < 0)
+ dev_err(led->cdev.dev, "can't set rgb led %d level rc=%d\n",
+ led->id, rc);
+}
+
static int pm8xxx_led_pwm_work(struct pm8xxx_led_data *led)
{
int duty_us;
@@ -336,6 +384,11 @@
if (rc < 0)
pr_err("wled brightness set failed %d\n", rc);
break;
+ case PM8XXX_ID_RGB_LED_RED:
+ case PM8XXX_ID_RGB_LED_GREEN:
+ case PM8XXX_ID_RGB_LED_BLUE:
+ led_rgb_set(led, level);
+ break;
default:
dev_err(led->cdev.dev, "unknown led id %d", led->id);
break;
@@ -380,8 +433,6 @@
static int pm8xxx_set_led_mode_and_max_brightness(struct pm8xxx_led_data *led,
enum pm8xxx_led_modes led_mode, int max_current)
{
- int rc = 0;
-
switch (led->id) {
case PM8XXX_ID_LED_0:
case PM8XXX_ID_LED_1:
@@ -420,13 +471,17 @@
case PM8XXX_ID_WLED:
led->cdev.max_brightness = WLED_MAX_LEVEL;
break;
- default:
- rc = -EINVAL;
- pr_err("LED Id is invalid");
+ case PM8XXX_ID_RGB_LED_RED:
+ case PM8XXX_ID_RGB_LED_GREEN:
+ case PM8XXX_ID_RGB_LED_BLUE:
+ led->cdev.max_brightness = LED_FULL;
break;
+ default:
+ dev_err(led->cdev.dev, "LED Id is invalid");
+ return -EINVAL;
}
- return rc;
+ return 0;
}
static enum led_brightness pm8xxx_led_get(struct led_classdev *led_cdev)
@@ -581,6 +636,35 @@
return 0;
}
+static int __devinit init_rgb_led(struct pm8xxx_led_data *led)
+{
+ int rc;
+ u8 val;
+
+ rc = pm8xxx_readb(led->dev->parent, SSBI_REG_ADDR_RGB_CNTL1, &val);
+ if (rc) {
+ dev_err(led->cdev.dev, "can't read rgb ctrl1 register rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ switch (led->id) {
+ case PM8XXX_ID_RGB_LED_RED:
+ val |= PM8XXX_DRV_RGB_RED_LED;
+ break;
+ case PM8XXX_ID_RGB_LED_GREEN:
+ val |= PM8XXX_DRV_RGB_GREEN_LED;
+ break;
+ case PM8XXX_ID_RGB_LED_BLUE:
+ val |= PM8XXX_DRV_RGB_BLUE_LED;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return pm8xxx_writeb(led->dev->parent, SSBI_REG_ADDR_RGB_CNTL1, val);
+}
+
static int __devinit get_init_value(struct pm8xxx_led_data *led, u8 *val)
{
int rc, offset;
@@ -608,6 +692,17 @@
dev_err(led->cdev.dev, "can't initialize wled rc=%d\n",
rc);
return rc;
+ case PM8XXX_ID_RGB_LED_RED:
+ case PM8XXX_ID_RGB_LED_GREEN:
+ case PM8XXX_ID_RGB_LED_BLUE:
+ rc = init_rgb_led(led);
+ if (rc) {
+ dev_err(led->cdev.dev, "can't initialize rgb rc=%d\n",
+ rc);
+ return rc;
+ }
+ addr = SSBI_REG_ADDR_RGB_CNTL1;
+ break;
default:
dev_err(led->cdev.dev, "unknown led id %d", led->id);
return -EINVAL;
@@ -765,7 +860,12 @@
led->cdev.brightness = LED_OFF;
if (led_cfg->mode != PM8XXX_LED_MODE_MANUAL) {
- __pm8xxx_led_work(led_dat,
+ if (led_dat->id == PM8XXX_ID_RGB_LED_RED ||
+ led_dat->id == PM8XXX_ID_RGB_LED_GREEN ||
+ led_dat->id == PM8XXX_ID_RGB_LED_BLUE)
+ __pm8xxx_led_work(led_dat, 0);
+ else
+ __pm8xxx_led_work(led_dat,
led_dat->cdev.max_brightness);
if (led_dat->pwm_channel != -1) {
diff --git a/include/linux/leds-pm8xxx.h b/include/linux/leds-pm8xxx.h
index 18e6398..60755de 100644
--- a/include/linux/leds-pm8xxx.h
+++ b/include/linux/leds-pm8xxx.h
@@ -35,6 +35,9 @@
PM8XXX_ID_FLASH_LED_0,
PM8XXX_ID_FLASH_LED_1,
PM8XXX_ID_WLED,
+ PM8XXX_ID_RGB_LED_RED,
+ PM8XXX_ID_RGB_LED_GREEN,
+ PM8XXX_ID_RGB_LED_BLUE,
PM8XXX_ID_MAX,
};