board: msm8960: Add low current LEDs
Add two low current LEDs with low power
configuration and default trigger
Signed-off-by: Jay Chokshi <jchokshi@codeaurora.org>
diff --git a/arch/arm/mach-msm/board-msm8960.c b/arch/arm/mach-msm/board-msm8960.c
index 2b4662b..1958ab2 100644
--- a/arch/arm/mach-msm/board-msm8960.c
+++ b/arch/arm/mach-msm/board-msm8960.c
@@ -3083,10 +3083,34 @@
.batt_data = &palladium_1500_data,
};
+#define PM8921_LC_LED_MAX_CURRENT 4 /* I = 4mA */
+
+/**
+ * 'flag' stores three values; led id, led mode, and max current of led.
+ * The bit packing format is as follow,
+ * reserved (1 byte) | max_current (2 bytes) | led_mode (1 nibble) |
+ * led_id (1 nibble)
+ */
+#define PM8XXX_SET_FLAG(led_id, led_mode, led_max_current) \
+ (((led_id << PM8XXX_LED_ID_SHIFT) & PM8XXX_LED_ID_MASK) |\
+ ((led_mode << PM8XXX_LED_MODE_SHIFT) & PM8XXX_LED_MODE_MASK) |\
+ ((led_max_current << PM8XXX_LED_MAX_CURRENT_SHIFT) & \
+ PM8XXX_LED_MAX_CURRENT_MASK))
+
static struct led_info pm8921_led_info[] = {
[0] = {
- .name = "led:drv1",
- .flags = PM8XXX_ID_LED_1,
+ .name = "led:usb",
+ .default_trigger = "usb-online",
+ .flags = PM8XXX_SET_FLAG(PM8XXX_ID_LED_0,
+ PM8XXX_LED_MODE_MANUAL,
+ PM8921_LC_LED_MAX_CURRENT),
+ },
+ [1] = {
+ .name = "led:ac",
+ .default_trigger = "ac-online",
+ .flags = PM8XXX_SET_FLAG(PM8XXX_ID_LED_1,
+ PM8XXX_LED_MODE_MANUAL,
+ PM8921_LC_LED_MAX_CURRENT),
},
};
diff --git a/drivers/leds/leds-pm8xxx.c b/drivers/leds/leds-pm8xxx.c
index b88036d..47ac04d 100644
--- a/drivers/leds/leds-pm8xxx.c
+++ b/drivers/leds/leds-pm8xxx.c
@@ -48,12 +48,19 @@
#define PM8XXX_FLASH_MODE_DBUS2 2
#define PM8XXX_FLASH_MODE_PWM 3
-#define PM8XXX_LED_OFFSET(id) ((id) - PM8XXX_ID_LED_0)
-
#define MAX_LC_LED_BRIGHTNESS 20
#define MAX_FLASH_BRIGHTNESS 15
#define MAX_KB_LED_BRIGHTNESS 15
+#define PM8XXX_LED_OFFSET(id) ((id) - PM8XXX_ID_LED_0)
+
+#define PM8XXX_GET_LED_ID(flag) (flag & PM8XXX_LED_ID_MASK >> \
+ PM8XXX_LED_ID_SHIFT)
+#define PM8XXX_GET_LED_MODE(flag) ((flag & PM8XXX_LED_MODE_MASK) >> \
+ PM8XXX_LED_MODE_SHIFT)
+#define PM8XXX_GET_LED_MAX_CURRENT(flag) ((flag & PM8XXX_LED_MAX_CURRENT_MASK)\
+ >> PM8XXX_LED_MAX_CURRENT_SHIFT)
+
/**
* struct pm8xxx_led_data - internal led data structure
* @led_classdev - led class device
@@ -71,8 +78,6 @@
struct mutex lock;
};
-static struct pm8xxx_led_data *pm8xxx_leds;
-
static void led_kp_set(struct pm8xxx_led_data *led, enum led_brightness value)
{
int rc;
@@ -135,12 +140,9 @@
led->id, rc);
}
-static void pm8xxx_led_work(struct work_struct *work)
+static void __pm8xxx_led_work(struct pm8xxx_led_data *led,
+ enum led_brightness level)
{
- struct pm8xxx_led_data *led = container_of(work,
- struct pm8xxx_led_data, work);
- int level = led->cdev.brightness;
-
mutex_lock(&led->lock);
switch (led->id) {
@@ -161,6 +163,15 @@
mutex_unlock(&led->lock);
}
+static void pm8xxx_led_work(struct work_struct *work)
+{
+ struct pm8xxx_led_data *led = container_of(work,
+ struct pm8xxx_led_data, work);
+ int level = led->cdev.brightness;
+
+ __pm8xxx_led_work(led, level);
+}
+
static void pm8xxx_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
@@ -177,43 +188,19 @@
schedule_work(&led->work);
}
-int pm8xxx_led_config(enum pm8xxx_leds led_id,
+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;
- struct pm8xxx_led_data *led = pm8xxx_leds;
- if (led_id < PM8XXX_ID_LED_KB_LIGHT ||
- led_id > PM8XXX_ID_FLASH_LED_1) {
- pr_err("Invalid led no. provided\n");
- return -EINVAL;
- }
-
- if (led_mode < PM8XXX_LED_MODE_MANUAL ||
- led_mode > PM8XXX_LED_MODE_DTEST4) {
- pr_err("Invalid led mode provided\n");
- return -EINVAL;
- }
-
- while (led->id != 0) {
- if (led->id == led_id)
- break;
- led++;
- }
-
- if (led->id == 0) {
- pr_err("Could not find LED with the given id");
- return -EINVAL;
- }
-
- mutex_lock(&led->lock);
-
- switch (led_id) {
+ switch (led->id) {
case PM8XXX_ID_LED_0:
case PM8XXX_ID_LED_1:
case PM8XXX_ID_LED_2:
led->cdev.max_brightness = max_current /
PM8XXX_ID_LED_CURRENT_FACTOR;
+ if (led->cdev.max_brightness > MAX_LC_LED_BRIGHTNESS)
+ led->cdev.max_brightness = MAX_LC_LED_BRIGHTNESS;
led->reg = led_mode;
break;
case PM8XXX_ID_LED_KB_LIGHT:
@@ -221,6 +208,9 @@
case PM8XXX_ID_FLASH_LED_1:
led->cdev.max_brightness = max_current /
PM8XXX_ID_FLASH_CURRENT_FACTOR;
+ if (led->cdev.max_brightness > MAX_FLASH_BRIGHTNESS)
+ led->cdev.max_brightness = MAX_FLASH_BRIGHTNESS;
+
switch (led_mode) {
case PM8XXX_LED_MODE_PWM1:
case PM8XXX_LED_MODE_PWM2:
@@ -244,14 +234,8 @@
break;
}
- mutex_unlock(&led->lock);
-
- if (!rc && led_mode != PM8XXX_LED_MODE_MANUAL)
- pm8xxx_led_set(&led->cdev, led->cdev.max_brightness);
-
return rc;
}
-EXPORT_SYMBOL(pm8xxx_led_config);
static enum led_brightness pm8xxx_led_get(struct led_classdev *led_cdev)
{
@@ -262,23 +246,6 @@
return led->cdev.brightness;
}
-static int __devinit get_max_brightness(enum pm8xxx_leds id)
-{
- switch (id) {
- case PM8XXX_ID_LED_KB_LIGHT:
- return MAX_KB_LED_BRIGHTNESS;
- case PM8XXX_ID_LED_0:
- case PM8XXX_ID_LED_1:
- case PM8XXX_ID_LED_2:
- return MAX_LC_LED_BRIGHTNESS;
- case PM8XXX_ID_FLASH_LED_0:
- case PM8XXX_ID_FLASH_LED_1:
- return MAX_FLASH_BRIGHTNESS;
- default:
- return 0;
- }
-}
-
static int __devinit get_init_value(struct pm8xxx_led_data *led, u8 *val)
{
int rc, offset;
@@ -315,7 +282,7 @@
const struct led_platform_data *pdata = pdev->dev.platform_data;
struct led_info *curr_led;
struct pm8xxx_led_data *led, *led_dat;
- int rc, i;
+ int rc, i, led_mode;
if (pdata == NULL) {
dev_err(&pdev->dev, "platform data not supplied\n");
@@ -335,7 +302,7 @@
curr_led = &pdata->leds[i];
led_dat = &led[i];
/* the flags variable is used for led-id */
- led_dat->id = curr_led->flags;
+ led_dat->id = PM8XXX_GET_LED_ID(curr_led->flags);
if (!((led_dat->id >= PM8XXX_ID_LED_KB_LIGHT) &&
(led_dat->id <= PM8XXX_ID_FLASH_LED_1))) {
@@ -351,14 +318,19 @@
led_dat->cdev.brightness_get = pm8xxx_led_get;
led_dat->cdev.brightness = LED_OFF;
led_dat->cdev.flags = LED_CORE_SUSPENDRESUME;
- led_dat->cdev.max_brightness =
- get_max_brightness(led_dat->id);
led_dat->dev = &pdev->dev;
rc = get_init_value(led_dat, &led_dat->reg);
if (rc < 0)
goto fail_id_check;
+ led_mode = PM8XXX_GET_LED_MODE(curr_led->flags);
+
+ rc = pm8xxx_set_led_mode_and_max_brightness(led_dat, led_mode,
+ PM8XXX_GET_LED_MAX_CURRENT(curr_led->flags));
+ if (rc < 0)
+ goto fail_id_check;
+
mutex_init(&led_dat->lock);
INIT_WORK(&led_dat->work, pm8xxx_led_work);
@@ -368,9 +340,13 @@
led_dat->id, rc);
goto fail_id_check;
}
- }
- pm8xxx_leds = led;
+ if (led_mode != PM8XXX_LED_MODE_MANUAL)
+ __pm8xxx_led_work(led_dat,
+ led_dat->cdev.max_brightness);
+ else
+ __pm8xxx_led_work(led_dat, LED_OFF);
+ }
platform_set_drvdata(pdev, led);
diff --git a/drivers/mfd/pm8921-core.c b/drivers/mfd/pm8921-core.c
index ff4e3b0..98aef9e 100644
--- a/drivers/mfd/pm8921-core.c
+++ b/drivers/mfd/pm8921-core.c
@@ -527,15 +527,6 @@
}
if (pdata->leds_pdata) {
- /* PM8921 supports only 4 LED DRVs */
- for (i = 0; i < pdata->leds_pdata->num_leds; i++) {
- if (pdata->leds_pdata->leds[i].flags >
- PM8XXX_ID_LED_2) {
- pr_err("%s: LED %d not supported\n", __func__,
- pdata->leds_pdata->leds[i].flags);
- goto bail;
- }
- }
leds_cell.platform_data = pdata->leds_pdata;
leds_cell.pdata_size = sizeof(struct led_platform_data);
ret = mfd_add_devices(pmic->dev, 0, &leds_cell, 1, NULL, 0);
diff --git a/include/linux/leds-pm8xxx.h b/include/linux/leds-pm8xxx.h
index 6e65958..4096355 100644
--- a/include/linux/leds-pm8xxx.h
+++ b/include/linux/leds-pm8xxx.h
@@ -16,6 +16,13 @@
#define PM8XXX_LEDS_DEV_NAME "pm8xxx-led"
+#define PM8XXX_LED_ID_SHIFT 0
+#define PM8XXX_LED_MODE_SHIFT 4
+#define PM8XXX_LED_MAX_CURRENT_SHIFT 8
+#define PM8XXX_LED_ID_MASK 0xF
+#define PM8XXX_LED_MODE_MASK 0xF0
+#define PM8XXX_LED_MAX_CURRENT_MASK 0xFFFF00
+
/**
* enum pm8xxx_leds - PMIC8XXX supported led ids
* @PM8XXX_ID_LED_KB_LIGHT - keyboard backlight led
@@ -48,6 +55,4 @@
PM8XXX_LED_MODE_DTEST4
};
-int pm8xxx_led_config(enum pm8xxx_leds led_id,
- enum pm8xxx_led_modes led_mode, int max_current);
#endif /* __LEDS_PM8XXX_H__ */