summaryrefslogtreecommitdiff
path: root/drivers/leds/leds-qpnp-flash-v2.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/leds/leds-qpnp-flash-v2.c')
-rw-r--r--drivers/leds/leds-qpnp-flash-v2.c761
1 files changed, 617 insertions, 144 deletions
diff --git a/drivers/leds/leds-qpnp-flash-v2.c b/drivers/leds/leds-qpnp-flash-v2.c
index 89636557dec1..786ffa822851 100644
--- a/drivers/leds/leds-qpnp-flash-v2.c
+++ b/drivers/leds/leds-qpnp-flash-v2.c
@@ -16,36 +16,39 @@
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio.h>
#include <linux/regmap.h>
#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
#include <linux/leds-qpnp-flash-v2.h>
#define FLASH_LED_REG_SAFETY_TMR(base) (base + 0x40)
#define FLASH_LED_REG_TGR_CURRENT(base) (base + 0x43)
#define FLASH_LED_REG_MOD_CTRL(base) (base + 0x46)
#define FLASH_LED_REG_IRES(base) (base + 0x47)
+#define FLASH_LED_REG_STROBE_CFG(base) (base + 0x48)
#define FLASH_LED_REG_STROBE_CTRL(base) (base + 0x49)
-#define FLASH_LED_REG_CHANNEL_CTRL(base) (base + 0x4C)
+#define FLASH_LED_EN_LED_CTRL(base) (base + 0x4C)
#define FLASH_LED_REG_HDRM_PRGM(base) (base + 0x4D)
#define FLASH_LED_REG_HDRM_AUTO_MODE_CTRL(base) (base + 0x50)
#define FLASH_LED_REG_ISC_DELAY(base) (base + 0x52)
-#define FLASH_LED_HDRM_MODE_PRGM_MASK 0xFF
-#define FLASH_LED_HDRM_VOL_MASK 0xF0
-#define FLASH_LED_CURRENT_MASK 0x3F
-#define FLASH_LED_STROBE_CTRL_MASK 0x07
-#define FLASH_LED_SAFETY_TMR_MASK_MASK 0x7F
-#define FLASH_LED_MOD_CTRL_MASK 0x80
-#define FLASH_LED_ISC_DELAY_MASK 0x03
+#define FLASH_LED_HDRM_MODE_PRGM_MASK GENMASK(7, 0)
+#define FLASH_LED_HDRM_VOL_MASK GENMASK(7, 4)
+#define FLASH_LED_CURRENT_MASK GENMASK(6, 0)
+#define FLASH_LED_ENABLE_MASK GENMASK(2, 0)
+#define FLASH_LED_SAFETY_TMR_MASK GENMASK(7, 0)
+#define FLASH_LED_ISC_DELAY_MASK GENMASK(1, 0)
+#define FLASH_LED_MOD_CTRL_MASK BIT(7)
+#define FLASH_LED_HW_SW_STROBE_SEL_MASK BIT(2)
-#define FLASH_LED_TYPE_FLASH 0
-#define FLASH_LED_TYPE_TORCH 1
#define FLASH_LED_HEADROOM_AUTO_MODE_ENABLED true
#define FLASH_LED_ISC_DELAY_SHIFT 6
#define FLASH_LED_ISC_DELAY_DEFAULT_US 3
#define FLASH_LED_SAFETY_TMR_VAL_OFFSET 1
#define FLASH_LED_SAFETY_TMR_VAL_DIVISOR 10
-#define FLASH_LED_SAFETY_TMR_ENABLED 0x08
+#define FLASH_LED_SAFETY_TMR_ENABLE BIT(7)
#define FLASH_LED_IRES_BASE 3
#define FLASH_LED_IRES_DIVISOR 2500
#define FLASH_LED_IRES_MIN_UA 5000
@@ -56,16 +59,74 @@
#define FLASH_LED_HDRM_VOL_HI_LO_WIN_DEFAULT_MV 0x04
#define FLASH_LED_HDRM_VOL_BASE_MV 125
#define FLASH_LED_HDRM_VOL_STEP_MV 25
-#define FLASH_LED_STROBE_ENABLE 0x01
-#define FLASH_LED_MOD_ENABLE 0x80
+#define FLASH_LED_STROBE_CFG_DEFAULT 0x00
+#define FLASH_LED_HW_STROBE_OPTION_1 0x00
+#define FLASH_LED_HW_STROBE_OPTION_2 0x01
+#define FLASH_LED_HW_STROBE_OPTION_3 0x02
+#define FLASH_LED_ENABLE BIT(0)
+#define FLASH_LED_MOD_ENABLE BIT(7)
#define FLASH_LED_DISABLE 0x00
#define FLASH_LED_SAFETY_TMR_DISABLED 0x13
+#define FLASH_LED_MIN_CURRENT_MA 25
+
+enum flash_led_type {
+ FLASH_LED_TYPE_FLASH,
+ FLASH_LED_TYPE_TORCH,
+};
+
+enum {
+ LED1 = 0,
+ LED2,
+ LED3,
+};
+
+/*
+ * Configurations for each individual LED
+ */
+struct flash_node_data {
+ struct platform_device *pdev;
+ struct led_classdev cdev;
+ struct pinctrl *pinctrl;
+ struct pinctrl_state *gpio_state_active;
+ struct pinctrl_state *gpio_state_suspend;
+ struct pinctrl_state *hw_strobe_state_active;
+ struct pinctrl_state *hw_strobe_state_suspend;
+ int hw_strobe_gpio;
+ int ires_ua;
+ int max_current;
+ int current_ma;
+ u8 duration;
+ u8 id;
+ u8 type;
+ u8 ires;
+ u8 hdrm_val;
+ u8 current_reg_val;
+ u8 trigger;
+ bool led_on;
+};
+
+struct flash_regulator_data {
+ struct regulator *vreg;
+ const char *reg_name;
+ u32 max_volt_uv;
+};
+
+struct flash_switch_data {
+ struct platform_device *pdev;
+ struct led_classdev cdev;
+ struct flash_regulator_data *reg_data;
+ int led_mask;
+ int num_regulators;
+ bool regulator_on;
+ bool enabled;
+};
/*
* Flash LED configuration read from device tree
*/
struct flash_led_platform_data {
u8 isc_delay_us;
+ u8 hw_strobe_option;
bool hdrm_auto_mode_en;
};
@@ -79,8 +140,9 @@ struct qpnp_flash_led {
struct flash_node_data *fnode;
struct flash_switch_data *snode;
spinlock_t lock;
- int num_led_nodes;
- int num_avail_leds;
+ int num_fnodes;
+ int num_snodes;
+ int enable;
u16 base;
};
@@ -91,12 +153,13 @@ qpnp_flash_led_masked_write(struct qpnp_flash_led *led, u16 addr, u8 mask,
int rc;
rc = regmap_update_bits(led->regmap, addr, mask, val);
- if (rc)
+ if (rc < 0)
dev_err(&led->pdev->dev,
"Unable to update bits from 0x%02X, rc = %d\n",
addr, rc);
-
- dev_dbg(&led->pdev->dev, "Write 0x%02X to addr 0x%02X\n", val, addr);
+ else
+ dev_dbg(&led->pdev->dev, "Wrote 0x%02X to addr 0x%02X\n",
+ val, addr);
return rc;
}
@@ -112,13 +175,13 @@ static int qpnp_flash_led_init_settings(struct qpnp_flash_led *led)
int rc, i, addr_offset;
u8 val = 0;
- for (i = 0; i < led->num_avail_leds; i++) {
+ for (i = 0; i < led->num_fnodes; i++) {
addr_offset = led->fnode[i].id;
rc = qpnp_flash_led_masked_write(led,
FLASH_LED_REG_HDRM_PRGM(led->base + addr_offset),
FLASH_LED_HDRM_MODE_PRGM_MASK,
led->fnode[i].hdrm_val);
- if (rc)
+ if (rc < 0)
return rc;
val |= 0x1 << led->fnode[i].id;
@@ -127,133 +190,306 @@ static int qpnp_flash_led_init_settings(struct qpnp_flash_led *led)
rc = qpnp_flash_led_masked_write(led,
FLASH_LED_REG_HDRM_AUTO_MODE_CTRL(led->base),
FLASH_LED_HDRM_MODE_PRGM_MASK, val);
- if (rc)
+ if (rc < 0)
return rc;
rc = qpnp_flash_led_masked_write(led,
FLASH_LED_REG_ISC_DELAY(led->base),
FLASH_LED_ISC_DELAY_MASK, led->pdata->isc_delay_us);
- if (rc)
+ if (rc < 0)
return rc;
return 0;
}
+static int qpnp_flash_led_hw_strobe_enable(struct flash_node_data *fnode,
+ int hw_strobe_option, bool on)
+{
+ int rc = 0;
+
+ /*
+ * If the LED controlled by this fnode is not GPIO controlled
+ * for the given strobe_option, return.
+ */
+ if (hw_strobe_option == FLASH_LED_HW_STROBE_OPTION_1)
+ return 0;
+ else if (hw_strobe_option == FLASH_LED_HW_STROBE_OPTION_2
+ && fnode->id != LED3)
+ return 0;
+ else if (hw_strobe_option == FLASH_LED_HW_STROBE_OPTION_3
+ && fnode->id == LED1)
+ return 0;
+
+ if (gpio_is_valid(fnode->hw_strobe_gpio)) {
+ gpio_set_value(fnode->hw_strobe_gpio, on ? 1 : 0);
+ } else if (fnode->hw_strobe_state_active &&
+ fnode->hw_strobe_state_suspend) {
+ rc = pinctrl_select_state(fnode->pinctrl,
+ on ? fnode->hw_strobe_state_active :
+ fnode->hw_strobe_state_suspend);
+ if (rc < 0) {
+ dev_err(&fnode->pdev->dev,
+ "failed to change hw strobe pin state\n");
+ return rc;
+ }
+ }
+
+ return rc;
+}
+
+static int qpnp_flash_led_regulator_enable(struct qpnp_flash_led *led,
+ struct flash_switch_data *snode, bool on)
+{
+ int i, rc = 0;
+
+ if (snode->regulator_on == on)
+ return 0;
+
+ if (on == false) {
+ i = snode->num_regulators;
+ goto out;
+ }
+
+ for (i = 0; i < snode->num_regulators; i++) {
+ rc = regulator_enable(snode->reg_data[i].vreg);
+ if (rc < 0) {
+ dev_err(&led->pdev->dev,
+ "regulator enable failed, rc=%d\n", rc);
+ goto out;
+ }
+ }
+ snode->regulator_on = true;
+
+ return rc;
+
+out:
+ while (i--)
+ regulator_disable(snode->reg_data[i].vreg);
+
+ snode->regulator_on = false;
+ return rc;
+}
+
+static int qpnp_flash_led_get_max_avail_current(struct flash_switch_data *snode,
+ struct qpnp_flash_led *led)
+{
+ return 3750;
+}
+
static void qpnp_flash_led_node_set(struct flash_node_data *fnode, int value)
{
- int prgm_current_ma;
+ int prgm_current_ma = value;
+
+ if (value <= 0)
+ prgm_current_ma = 0;
+ else if (value < FLASH_LED_MIN_CURRENT_MA)
+ prgm_current_ma = FLASH_LED_MIN_CURRENT_MA;
- prgm_current_ma = value < 0 ? 0 : value;
- prgm_current_ma = value > fnode->cdev.max_brightness ?
- fnode->cdev.max_brightness : value;
+ prgm_current_ma = min(prgm_current_ma, fnode->max_current);
+ fnode->current_ma = prgm_current_ma;
fnode->cdev.brightness = prgm_current_ma;
- fnode->brightness = prgm_current_ma * 1000 / fnode->ires_ua + 1;
+ fnode->current_reg_val = prgm_current_ma * 1000 / fnode->ires_ua + 1;
fnode->led_on = prgm_current_ma != 0;
}
+static int qpnp_flash_led_switch_disable(struct flash_switch_data *snode)
+{
+ struct qpnp_flash_led *led = dev_get_drvdata(&snode->pdev->dev);
+ int i, rc, addr_offset;
+
+ rc = qpnp_flash_led_masked_write(led,
+ FLASH_LED_EN_LED_CTRL(led->base),
+ snode->led_mask, FLASH_LED_DISABLE);
+ if (rc < 0)
+ return rc;
+
+ led->enable--;
+ if (led->enable == 0) {
+ rc = qpnp_flash_led_masked_write(led,
+ FLASH_LED_REG_MOD_CTRL(led->base),
+ FLASH_LED_MOD_CTRL_MASK, FLASH_LED_DISABLE);
+ if (rc < 0)
+ return rc;
+ }
+
+ for (i = 0; i < led->num_fnodes; i++) {
+ if (!led->fnode[i].led_on ||
+ !(snode->led_mask & BIT(led->fnode[i].id)))
+ continue;
+
+ addr_offset = led->fnode[i].id;
+ rc = qpnp_flash_led_masked_write(led,
+ FLASH_LED_REG_TGR_CURRENT(led->base + addr_offset),
+ FLASH_LED_CURRENT_MASK, 0);
+ if (rc < 0)
+ return rc;
+
+ led->fnode[i].led_on = false;
+
+ if (led->fnode[i].pinctrl) {
+ rc = pinctrl_select_state(led->fnode[i].pinctrl,
+ led->fnode[i].gpio_state_suspend);
+ if (rc < 0) {
+ dev_err(&led->pdev->dev,
+ "failed to disable GPIO, rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ if (led->fnode[i].trigger & FLASH_LED_HW_SW_STROBE_SEL_MASK) {
+ rc = qpnp_flash_led_hw_strobe_enable(&led->fnode[i],
+ led->pdata->hw_strobe_option, false);
+ if (rc < 0) {
+ dev_err(&led->pdev->dev,
+ "Unable to disable hw strobe, rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+ }
+
+ qpnp_flash_led_regulator_enable(led, snode, false);
+ snode->enabled = false;
+ return 0;
+}
+
static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on)
{
struct qpnp_flash_led *led = dev_get_drvdata(&snode->pdev->dev);
int rc, i, addr_offset;
u8 val;
- if (!on)
- goto leds_turn_off;
+ if (snode->enabled == on) {
+ dev_warn(&led->pdev->dev, "Switch node is already %s!\n",
+ on ? "enabled" : "disabled");
+ return 0;
+ }
+
+ if (!on) {
+ rc = qpnp_flash_led_switch_disable(snode);
+ return rc;
+ }
+
+ rc = qpnp_flash_led_regulator_enable(led, snode, true);
+ if (rc < 0)
+ return rc;
+ /* Iterate over all leds for this switch node */
val = 0;
- for (i = 0; i < led->num_led_nodes; i++)
- val |= led->fnode[i].ires << (led->fnode[i].id * 2);
+ for (i = 0; i < led->num_fnodes; i++)
+ if (snode->led_mask & BIT(led->fnode[i].id))
+ val |= led->fnode[i].ires << (led->fnode[i].id * 2);
+
rc = qpnp_flash_led_masked_write(led, FLASH_LED_REG_IRES(led->base),
FLASH_LED_CURRENT_MASK, val);
- if (rc)
+ if (rc < 0)
+ return rc;
+
+ rc = qpnp_flash_led_masked_write(led,
+ FLASH_LED_REG_STROBE_CFG(led->base),
+ FLASH_LED_ENABLE_MASK,
+ led->pdata->hw_strobe_option);
+ if (rc < 0)
return rc;
val = 0;
- for (i = 0; i < led->num_avail_leds; i++) {
- if (!led->fnode[i].led_on)
+ for (i = 0; i < led->num_fnodes; i++) {
+ if (!led->fnode[i].led_on ||
+ !(snode->led_mask & BIT(led->fnode[i].id)))
continue;
addr_offset = led->fnode[i].id;
rc = qpnp_flash_led_masked_write(led,
FLASH_LED_REG_STROBE_CTRL(led->base + addr_offset),
- FLASH_LED_STROBE_CTRL_MASK, FLASH_LED_STROBE_ENABLE);
- if (rc)
+ FLASH_LED_ENABLE_MASK, led->fnode[i].trigger);
+ if (rc < 0)
return rc;
rc = qpnp_flash_led_masked_write(led,
FLASH_LED_REG_TGR_CURRENT(led->base + addr_offset),
- FLASH_LED_CURRENT_MASK, led->fnode[i].brightness);
- if (rc)
+ FLASH_LED_CURRENT_MASK, led->fnode[i].current_reg_val);
+ if (rc < 0)
return rc;
rc = qpnp_flash_led_masked_write(led,
FLASH_LED_REG_SAFETY_TMR(led->base + addr_offset),
- FLASH_LED_SAFETY_TMR_MASK_MASK, led->fnode[i].duration);
- if (rc)
+ FLASH_LED_SAFETY_TMR_MASK, led->fnode[i].duration);
+ if (rc < 0)
return rc;
- val |= FLASH_LED_STROBE_ENABLE << led->fnode[i].id;
+ val |= FLASH_LED_ENABLE << led->fnode[i].id;
if (led->fnode[i].pinctrl) {
rc = pinctrl_select_state(led->fnode[i].pinctrl,
led->fnode[i].gpio_state_active);
- if (rc) {
+ if (rc < 0) {
dev_err(&led->pdev->dev,
"failed to enable GPIO\n");
return rc;
}
}
+
+ if (led->fnode[i].trigger & FLASH_LED_HW_SW_STROBE_SEL_MASK) {
+ rc = qpnp_flash_led_hw_strobe_enable(&led->fnode[i],
+ led->pdata->hw_strobe_option, true);
+ if (rc < 0) {
+ dev_err(&led->pdev->dev,
+ "Unable to enable hw strobe\n");
+ return rc;
+ }
+ }
}
- rc = qpnp_flash_led_masked_write(led, FLASH_LED_REG_MOD_CTRL(led->base),
+ if (led->enable == 0) {
+ rc = qpnp_flash_led_masked_write(led,
+ FLASH_LED_REG_MOD_CTRL(led->base),
FLASH_LED_MOD_CTRL_MASK, FLASH_LED_MOD_ENABLE);
- if (rc)
- return rc;
+ if (rc < 0)
+ return rc;
+ }
+ led->enable++;
rc = qpnp_flash_led_masked_write(led,
- FLASH_LED_REG_CHANNEL_CTRL(led->base),
- FLASH_LED_STROBE_CTRL_MASK, val);
- if (rc)
+ FLASH_LED_EN_LED_CTRL(led->base),
+ snode->led_mask, val);
+ if (rc < 0)
return rc;
+ snode->enabled = true;
return 0;
+}
-leds_turn_off:
- rc = qpnp_flash_led_masked_write(led,
- FLASH_LED_REG_CHANNEL_CTRL(led->base),
- FLASH_LED_STROBE_CTRL_MASK, FLASH_LED_DISABLE);
- if (rc)
- return rc;
-
- rc = qpnp_flash_led_masked_write(led, FLASH_LED_REG_MOD_CTRL(led->base),
- FLASH_LED_MOD_CTRL_MASK, FLASH_LED_DISABLE);
- if (rc)
- return rc;
+int qpnp_flash_led_prepare(struct led_classdev *led_cdev, int options)
+{
+ struct flash_switch_data *snode =
+ container_of(led_cdev, struct flash_switch_data, cdev);
+ struct qpnp_flash_led *led = dev_get_drvdata(&snode->pdev->dev);
+ int rc, val = 0;
- for (i = 0; i < led->num_led_nodes; i++) {
- if (!led->fnode[i].led_on)
- continue;
+ if (!(options & (ENABLE_REGULATOR | QUERY_MAX_CURRENT))) {
+ dev_err(&led->pdev->dev, "Invalid options %d\n", options);
+ return -EINVAL;
+ }
- addr_offset = led->fnode[i].id;
- rc = qpnp_flash_led_masked_write(led,
- FLASH_LED_REG_TGR_CURRENT(led->base + addr_offset),
- FLASH_LED_CURRENT_MASK, 0);
- if (rc)
+ if (options & ENABLE_REGULATOR) {
+ rc = qpnp_flash_led_regulator_enable(led, snode, true);
+ if (rc < 0) {
+ dev_err(&led->pdev->dev,
+ "enable regulator failed, rc=%d\n", rc);
return rc;
- led->fnode[i].led_on = false;
+ }
+ }
- if (led->fnode[i].pinctrl) {
- rc = pinctrl_select_state(led->fnode[i].pinctrl,
- led->fnode[i].gpio_state_suspend);
- if (rc) {
- dev_err(&led->pdev->dev,
- "failed to disable GPIO\n");
- return rc;
- }
+ if (options & QUERY_MAX_CURRENT) {
+ val = qpnp_flash_led_get_max_avail_current(snode, led);
+ if (val < 0) {
+ dev_err(&led->pdev->dev,
+ "query max current failed, rc=%d\n", val);
+ return val;
}
}
- return 0;
+ return val;
}
static void qpnp_flash_led_brightness_set(struct led_classdev *led_cdev,
@@ -264,7 +500,7 @@ static void qpnp_flash_led_brightness_set(struct led_classdev *led_cdev,
struct qpnp_flash_led *led = dev_get_drvdata(&fnode->pdev->dev);
int rc;
- if (!strcmp(led_cdev->name, "led:switch")) {
+ if (!strncmp(led_cdev->name, "led:switch", strlen("led:switch"))) {
snode = container_of(led_cdev, struct flash_switch_data, cdev);
led = dev_get_drvdata(&snode->pdev->dev);
} else {
@@ -273,53 +509,141 @@ static void qpnp_flash_led_brightness_set(struct led_classdev *led_cdev,
}
spin_lock(&led->lock);
- if (!fnode) {
+ if (snode) {
rc = qpnp_flash_led_switch_set(snode, value > 0);
- if (rc) {
+ if (rc < 0)
dev_err(&led->pdev->dev,
"Failed to set flash LED switch\n");
- goto exit;
- }
- } else {
+ } else if (fnode) {
qpnp_flash_led_node_set(fnode, value);
}
-exit:
spin_unlock(&led->lock);
}
+static int qpnp_flash_led_regulator_setup(struct qpnp_flash_led *led,
+ struct flash_switch_data *snode, bool on)
+{
+ int i, rc = 0;
+
+ if (on == false) {
+ i = snode->num_regulators;
+ goto out;
+ }
+
+ for (i = 0; i < snode->num_regulators; i++) {
+ snode->reg_data[i].vreg = regulator_get(snode->cdev.dev,
+ snode->reg_data[i].reg_name);
+ if (IS_ERR(snode->reg_data[i].vreg)) {
+ rc = PTR_ERR(snode->reg_data[i].vreg);
+ dev_err(&led->pdev->dev,
+ "Failed to get regulator, rc=%d\n", rc);
+ goto out;
+ }
+
+ if (regulator_count_voltages(snode->reg_data[i].vreg) > 0) {
+ rc = regulator_set_voltage(snode->reg_data[i].vreg,
+ snode->reg_data[i].max_volt_uv,
+ snode->reg_data[i].max_volt_uv);
+ if (rc < 0) {
+ dev_err(&led->pdev->dev,
+ "regulator set voltage failed, rc=%d\n",
+ rc);
+ regulator_put(snode->reg_data[i].vreg);
+ goto out;
+ }
+ }
+ }
+
+ return rc;
+
+out:
+ while (i--) {
+ if (regulator_count_voltages(snode->reg_data[i].vreg) > 0)
+ regulator_set_voltage(snode->reg_data[i].vreg, 0,
+ snode->reg_data[i].max_volt_uv);
+
+ regulator_put(snode->reg_data[i].vreg);
+ }
+
+ return rc;
+}
+
+static int qpnp_flash_led_regulator_parse_dt(struct qpnp_flash_led *led,
+ struct flash_switch_data *snode,
+ struct device_node *node) {
+
+ int i = 0, rc = 0, num_regs = 0;
+ struct device_node *temp = NULL;
+ const char *temp_string;
+ u32 val;
+
+ while ((temp = of_get_next_available_child(node, temp))) {
+ if (of_find_property(temp, "regulator-name", NULL))
+ num_regs++;
+ }
+ snode->num_regulators = num_regs;
+
+ if (snode->num_regulators == 0)
+ return 0;
+
+ snode->reg_data = devm_kcalloc(&led->pdev->dev, snode->num_regulators,
+ sizeof(*snode->reg_data),
+ GFP_KERNEL);
+ if (!snode->reg_data)
+ return -ENOMEM;
+
+ for_each_available_child_of_node(node, temp) {
+ rc = of_property_read_string(temp, "regulator-name",
+ &temp_string);
+ if (!rc)
+ snode->reg_data[i].reg_name = temp_string;
+ else {
+ dev_err(&led->pdev->dev,
+ "Unable to read regulator name, rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = of_property_read_u32(temp, "max-voltage-uv", &val);
+ if (!rc) {
+ snode->reg_data[i].max_volt_uv = val;
+ } else if (rc != -EINVAL) {
+ dev_err(&led->pdev->dev,
+ "Unable to read max voltage, rc=%d\n", rc);
+ return rc;
+ }
+
+ i++;
+ }
+
+ return 0;
+}
+
static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led,
struct flash_node_data *fnode, struct device_node *node)
{
const char *temp_string;
int rc;
u32 val;
+ bool strobe_sel = 0, edge_trigger = 0, active_high = 0;
fnode->pdev = led->pdev;
fnode->cdev.brightness_set = qpnp_flash_led_brightness_set;
fnode->cdev.brightness_get = qpnp_flash_led_brightness_get;
rc = of_property_read_string(node, "qcom,led-name", &fnode->cdev.name);
- if (rc) {
+ if (rc < 0) {
dev_err(&led->pdev->dev, "Unable to read flash LED names\n");
return rc;
}
- rc = of_property_read_u32(node, "qcom,max-current", &val);
- if (!rc) {
- fnode->cdev.max_brightness = val;
- } else {
- dev_err(&led->pdev->dev, "Unable to read max current\n");
- return rc;
- }
-
rc = of_property_read_string(node, "label", &temp_string);
if (!rc) {
- if (!strcmp(temp_string, "flash"))
+ if (!strcmp(temp_string, "flash")) {
fnode->type = FLASH_LED_TYPE_FLASH;
- else if (!strcmp(temp_string, "torch"))
+ } else if (!strcmp(temp_string, "torch")) {
fnode->type = FLASH_LED_TYPE_TORCH;
- else {
+ } else {
dev_err(&led->pdev->dev, "Wrong flash LED type\n");
return rc;
}
@@ -338,7 +662,7 @@ static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led,
rc = of_property_read_string(node, "qcom,default-led-trigger",
&fnode->cdev.default_trigger);
- if (rc) {
+ if (rc < 0) {
dev_err(&led->pdev->dev, "Unable to read trigger name\n");
return rc;
}
@@ -355,13 +679,43 @@ static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led,
return rc;
}
+ rc = of_property_read_u32(node, "qcom,max-current", &val);
+ if (!rc) {
+ if (val < FLASH_LED_MIN_CURRENT_MA)
+ val = FLASH_LED_MIN_CURRENT_MA;
+ fnode->max_current = val;
+ fnode->cdev.max_brightness = val;
+ } else {
+ dev_err(&led->pdev->dev,
+ "Unable to read max current, rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = of_property_read_u32(node, "qcom,current-ma", &val);
+ if (!rc) {
+ if (val < FLASH_LED_MIN_CURRENT_MA ||
+ val > fnode->max_current)
+ dev_warn(&led->pdev->dev,
+ "Invalid operational current specified, capping it\n");
+ if (val < FLASH_LED_MIN_CURRENT_MA)
+ val = FLASH_LED_MIN_CURRENT_MA;
+ if (val > fnode->max_current)
+ val = fnode->max_current;
+ fnode->current_ma = val;
+ fnode->cdev.brightness = val;
+ } else if (rc != -EINVAL) {
+ dev_err(&led->pdev->dev,
+ "Unable to read operational current, rc=%d\n", rc);
+ return rc;
+ }
+
fnode->duration = FLASH_LED_SAFETY_TMR_DISABLED;
rc = of_property_read_u32(node, "qcom,duration-ms", &val);
if (!rc) {
fnode->duration = (u8)(((val -
FLASH_LED_SAFETY_TMR_VAL_OFFSET) /
FLASH_LED_SAFETY_TMR_VAL_DIVISOR) |
- FLASH_LED_SAFETY_TMR_ENABLED);
+ FLASH_LED_SAFETY_TMR_ENABLE);
} else if (rc == -EINVAL) {
if (fnode->type == FLASH_LED_TYPE_FLASH) {
dev_err(&led->pdev->dev,
@@ -398,17 +752,62 @@ static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led,
return rc;
}
+ strobe_sel = of_property_read_bool(node, "qcom,hw-strobe-sel");
+ if (strobe_sel) {
+ edge_trigger = of_property_read_bool(node,
+ "qcom,hw-strobe-edge-trigger");
+ active_high = !of_property_read_bool(node,
+ "qcom,hw-strobe-active-low");
+ }
+ fnode->trigger = (strobe_sel << 2) | (edge_trigger << 1) | active_high;
+
+ if (fnode->trigger & FLASH_LED_HW_SW_STROBE_SEL_MASK) {
+ if (of_find_property(node, "qcom,hw-strobe-gpio", NULL)) {
+ fnode->hw_strobe_gpio = of_get_named_gpio(node,
+ "qcom,hw-strobe-gpio", 0);
+ if (fnode->hw_strobe_gpio < 0) {
+ dev_err(&led->pdev->dev,
+ "Invalid gpio specified\n");
+ return fnode->hw_strobe_gpio;
+ }
+ gpio_direction_output(fnode->hw_strobe_gpio, 0);
+ } else {
+ fnode->hw_strobe_gpio = -1;
+ fnode->hw_strobe_state_active =
+ pinctrl_lookup_state(fnode->pinctrl,
+ "strobe_enable");
+ if (IS_ERR_OR_NULL(fnode->hw_strobe_state_active)) {
+ dev_err(&led->pdev->dev,
+ "No active pin for hardware strobe, rc=%ld\n",
+ PTR_ERR(fnode->hw_strobe_state_active));
+ fnode->hw_strobe_state_active = NULL;
+ }
+
+ fnode->hw_strobe_state_suspend =
+ pinctrl_lookup_state(fnode->pinctrl,
+ "strobe_disable");
+ if (IS_ERR_OR_NULL(fnode->hw_strobe_state_suspend)) {
+ dev_err(&led->pdev->dev,
+ "No suspend pin for hardware strobe, rc=%ld\n",
+ PTR_ERR(fnode->hw_strobe_state_suspend)
+ );
+ fnode->hw_strobe_state_suspend = NULL;
+ }
+ }
+ }
+
rc = led_classdev_register(&led->pdev->dev, &fnode->cdev);
- if (rc) {
+ if (rc < 0) {
dev_err(&led->pdev->dev, "Unable to register led node %d\n",
fnode->id);
return rc;
}
+
fnode->cdev.dev->of_node = node;
fnode->pinctrl = devm_pinctrl_get(fnode->cdev.dev);
if (IS_ERR_OR_NULL(fnode->pinctrl)) {
- dev_err(&led->pdev->dev, "No pinctrl defined\n");
+ dev_warn(&led->pdev->dev, "No pinctrl defined\n");
fnode->pinctrl = NULL;
} else {
fnode->gpio_state_active =
@@ -436,34 +835,64 @@ static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led,
}
static int qpnp_flash_led_parse_and_register_switch(struct qpnp_flash_led *led,
+ struct flash_switch_data *snode,
struct device_node *node)
{
- int rc;
+ int rc = 0;
- rc = of_property_read_string(node, "qcom,led-name",
- &led->snode->cdev.name);
- if (rc) {
- dev_err(&led->pdev->dev, "Failed to read switch node name\n");
+ rc = of_property_read_string(node, "qcom,led-name", &snode->cdev.name);
+ if (rc < 0) {
+ dev_err(&led->pdev->dev,
+ "Failed to read switch node name, rc=%d\n", rc);
return rc;
}
rc = of_property_read_string(node, "qcom,default-led-trigger",
- &led->snode->cdev.default_trigger);
- if (rc) {
- dev_err(&led->pdev->dev, "Unable to read trigger name\n");
+ &snode->cdev.default_trigger);
+ if (rc < 0) {
+ dev_err(&led->pdev->dev,
+ "Unable to read trigger name, rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = of_property_read_u32(node, "qcom,led-mask", &snode->led_mask);
+ if (rc < 0) {
+ dev_err(&led->pdev->dev, "Unable to read led mask rc=%d\n", rc);
+ return rc;
+ }
+
+ if (snode->led_mask < 1 || snode->led_mask > 7) {
+ dev_err(&led->pdev->dev, "Invalid value for led-mask\n");
+ return -EINVAL;
+ }
+
+ rc = qpnp_flash_led_regulator_parse_dt(led, snode, node);
+ if (rc < 0) {
+ dev_err(&led->pdev->dev,
+ "Unable to parse regulator data, rc=%d\n", rc);
return rc;
}
- led->snode->pdev = led->pdev;
- led->snode->cdev.brightness_set = qpnp_flash_led_brightness_set;
- led->snode->cdev.brightness_get = qpnp_flash_led_brightness_get;
- rc = led_classdev_register(&led->pdev->dev, &led->snode->cdev);
- if (rc) {
+ if (snode->num_regulators) {
+ rc = qpnp_flash_led_regulator_setup(led, snode, true);
+ if (rc < 0) {
+ dev_err(&led->pdev->dev,
+ "Unable to setup regulator, rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ snode->pdev = led->pdev;
+ snode->cdev.brightness_set = qpnp_flash_led_brightness_set;
+ snode->cdev.brightness_get = qpnp_flash_led_brightness_get;
+ rc = led_classdev_register(&led->pdev->dev, &snode->cdev);
+ if (rc < 0) {
dev_err(&led->pdev->dev,
"Unable to register led switch node\n");
return rc;
}
+ snode->cdev.dev->of_node = node;
return 0;
}
@@ -486,6 +915,14 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led,
return rc;
}
+ rc = of_property_read_u32(node, "qcom,hw-strobe-option", &val);
+ if (!rc) {
+ led->pdata->hw_strobe_option = (u8)val;
+ } else if (rc != -EINVAL) {
+ dev_err(&led->pdev->dev, "Unable to parse hw strobe option\n");
+ return rc;
+ }
+
return 0;
}
@@ -493,6 +930,7 @@ static int qpnp_flash_led_probe(struct platform_device *pdev)
{
struct qpnp_flash_led *led;
struct device_node *node, *temp;
+ const char *temp_string;
unsigned int base;
int rc, i = 0;
@@ -503,7 +941,7 @@ static int qpnp_flash_led_probe(struct platform_device *pdev)
}
rc = of_property_read_u32(node, "reg", &base);
- if (rc) {
+ if (rc < 0) {
dev_err(&pdev->dev, "Couldn't find reg in node %s, rc = %d\n",
node->full_name, rc);
return rc;
@@ -528,56 +966,77 @@ static int qpnp_flash_led_probe(struct platform_device *pdev)
return -ENOMEM;
rc = qpnp_flash_led_parse_common_dt(led, node);
- if (rc) {
+ if (rc < 0) {
dev_err(&pdev->dev,
"Failed to parse common flash LED device tree\n");
return rc;
}
- for_each_child_of_node(node, temp)
- led->num_led_nodes++;
- if (!led->num_led_nodes) {
+ for_each_available_child_of_node(node, temp) {
+ rc = of_property_read_string(temp, "label", &temp_string);
+ if (rc < 0) {
+ dev_err(&pdev->dev,
+ "Failed to parse label, rc=%d\n", rc);
+ return rc;
+ }
+
+ if (!strcmp("switch", temp_string)) {
+ led->num_snodes++;
+ } else if (!strcmp("flash", temp_string) ||
+ !strcmp("torch", temp_string)) {
+ led->num_fnodes++;
+ } else {
+ dev_err(&pdev->dev,
+ "Invalid label for led node\n");
+ return -EINVAL;
+ }
+ }
+
+ if (!led->num_fnodes) {
dev_err(&pdev->dev, "No LED nodes defined\n");
return -ECHILD;
}
- led->fnode = devm_kzalloc(&pdev->dev,
- sizeof(struct flash_node_data) * (--led->num_led_nodes),
- GFP_KERNEL);
+ led->fnode = devm_kcalloc(&pdev->dev, led->num_fnodes,
+ sizeof(*led->fnode),
+ GFP_KERNEL);
if (!led->fnode)
return -ENOMEM;
+ led->snode = devm_kcalloc(&pdev->dev, led->num_snodes,
+ sizeof(*led->snode),
+ GFP_KERNEL);
+ if (!led->snode)
+ return -ENOMEM;
+
temp = NULL;
- for (i = 0; i < led->num_led_nodes; i++) {
- temp = of_get_next_child(node, temp);
+ for (i = 0; i < led->num_fnodes; i++) {
+ temp = of_get_next_available_child(node, temp);
rc = qpnp_flash_led_parse_each_led_dt(led,
&led->fnode[i], temp);
- if (rc) {
+ if (rc < 0) {
dev_err(&pdev->dev,
- "Unable to parse flash node %d\n", i);
+ "Unable to parse flash node %d rc=%d\n", i, rc);
goto error_led_register;
}
}
- led->num_avail_leds = i;
-
- led->snode = devm_kzalloc(&pdev->dev,
- sizeof(struct flash_switch_data), GFP_KERNEL);
- if (!led->snode) {
- rc = -ENOMEM;
- goto error_led_register;
- }
- temp = of_get_next_child(node, temp);
- rc = qpnp_flash_led_parse_and_register_switch(led, temp);
- if (rc) {
- dev_err(&pdev->dev,
- "Unable to parse and register switch node\n");
- goto error_led_register;
+ for (i = 0; i < led->num_snodes; i++) {
+ temp = of_get_next_available_child(node, temp);
+ rc = qpnp_flash_led_parse_and_register_switch(led,
+ &led->snode[i], temp);
+ if (rc < 0) {
+ dev_err(&pdev->dev,
+ "Unable to parse and register switch node, rc=%d\n",
+ rc);
+ goto error_switch_register;
+ }
}
rc = qpnp_flash_led_init_settings(led);
- if (rc) {
- dev_err(&pdev->dev, "Failed to initialize flash LED\n");
+ if (rc < 0) {
+ dev_err(&pdev->dev,
+ "Failed to initialize flash LED, rc=%d\n", rc);
goto error_switch_register;
}
@@ -588,7 +1047,9 @@ static int qpnp_flash_led_probe(struct platform_device *pdev)
return 0;
error_switch_register:
- led_classdev_unregister(&led->snode->cdev);
+ while (i > 0)
+ led_classdev_unregister(&led->snode[--i].cdev);
+ i = led->num_fnodes;
error_led_register:
while (i > 0)
led_classdev_unregister(&led->fnode[--i].cdev);
@@ -599,9 +1060,21 @@ error_led_register:
static int qpnp_flash_led_remove(struct platform_device *pdev)
{
struct qpnp_flash_led *led = dev_get_drvdata(&pdev->dev);
- int i = led->num_led_nodes;
+ int i;
+
+ for (i = 0; i < led->num_snodes; i++) {
+ if (led->snode[i].num_regulators) {
+ if (led->snode[i].regulator_on)
+ qpnp_flash_led_regulator_enable(led,
+ &led->snode[i], false);
+ qpnp_flash_led_regulator_setup(led,
+ &led->snode[i], false);
+ }
+ }
- led_classdev_unregister(&led->snode->cdev);
+ while (i > 0)
+ led_classdev_unregister(&led->snode[--i].cdev);
+ i = led->num_fnodes;
while (i > 0)
led_classdev_unregister(&led->fnode[--i].cdev);