leds: leds-qpnp: Add LUT support for KPDBL
Keypad Backlight module supports LUT mode. Add
support for LUT mode and parse the parameters
from DTS file.
Change-Id: Ie293c46b32f1bb9a09ee4ddaae54632249e17256
Signed-off-by: Mohan Pallaka <mpallaka@codeaurora.org>
diff --git a/drivers/leds/leds-qpnp.c b/drivers/leds/leds-qpnp.c
index e84b477..3c2be38 100644
--- a/drivers/leds/leds-qpnp.c
+++ b/drivers/leds/leds-qpnp.c
@@ -403,15 +403,22 @@
/**
* kpdbl_config_data - kpdbl configuration data
* @pwm_cfg - device pwm configuration
- * @row_src_sel_val - select source, 0 for vph_pwr and 1 for vbst
- * @row_scan_en - enable row scan
- * @row_scan_val - map to enable needed rows
+ * @mode - running mode: pwm or lut
+ * @row_id - row id of the led
+ * @row_src_vbst - 0 for vph_pwr and 1 for vbst
+ * @row_src_en - enable row source
+ * @always_on - always on row
+ * @lut_params - lut parameters to be used by pwm driver
+ * @duty_cycles - duty cycles for lut
*/
struct kpdbl_config_data {
struct pwm_config_data *pwm_cfg;
- u32 row_src_sel_val;
- u32 row_scan_en;
- u32 row_scan_val;
+ u32 row_id;
+ bool row_src_vbst;
+ bool row_src_en;
+ bool always_on;
+ struct pwm_duty_cycles *duty_cycles;
+ struct lut_params lut_params;
};
/**
@@ -458,6 +465,8 @@
int turn_off_delay_ms;
};
+static int num_kpbl_leds_on;
+
static int
qpnp_led_masked_write(struct qpnp_led_data *led, u16 addr, u8 mask, u8 val)
{
@@ -906,35 +915,73 @@
if (!led->kpdbl_cfg->pwm_cfg->blinking)
led->kpdbl_cfg->pwm_cfg->mode =
led->kpdbl_cfg->pwm_cfg->default_mode;
- rc = qpnp_led_masked_write(led, KPDBL_ENABLE(led->base),
- KPDBL_MODULE_EN_MASK, KPDBL_MODULE_EN);
- duty_us = (led->kpdbl_cfg->pwm_cfg->pwm_period_us *
- led->cdev.brightness) / KPDBL_MAX_LEVEL;
- rc = pwm_config(led->kpdbl_cfg->pwm_cfg->pwm_dev, duty_us,
- led->kpdbl_cfg->pwm_cfg->pwm_period_us);
- if (rc < 0) {
- dev_err(&led->spmi_dev->dev, "pwm config failed\n");
- return rc;
+ if (!num_kpbl_leds_on) {
+ rc = qpnp_led_masked_write(led, KPDBL_ENABLE(led->base),
+ KPDBL_MODULE_EN_MASK, KPDBL_MODULE_EN);
+ if (rc) {
+ dev_err(&led->spmi_dev->dev,
+ "Enable reg write failed(%d)\n", rc);
+ return rc;
+ }
}
+
+ if (led->kpdbl_cfg->pwm_cfg->mode == PWM_MODE) {
+ duty_us = (led->kpdbl_cfg->pwm_cfg->pwm_period_us *
+ led->cdev.brightness) / KPDBL_MAX_LEVEL;
+ rc = pwm_config(led->kpdbl_cfg->pwm_cfg->pwm_dev,
+ duty_us,
+ led->kpdbl_cfg->pwm_cfg->pwm_period_us);
+ if (rc < 0) {
+ dev_err(&led->spmi_dev->dev, "pwm config failed\n");
+ return rc;
+ }
+ }
+
rc = pwm_enable(led->kpdbl_cfg->pwm_cfg->pwm_dev);
if (rc < 0) {
dev_err(&led->spmi_dev->dev, "pwm enable failed\n");
return rc;
}
+
+ num_kpbl_leds_on++;
+
} else {
led->kpdbl_cfg->pwm_cfg->mode =
led->kpdbl_cfg->pwm_cfg->default_mode;
- pwm_disable(led->kpdbl_cfg->pwm_cfg->pwm_dev);
- rc = qpnp_led_masked_write(led, KPDBL_ENABLE(led->base),
- KPDBL_MODULE_EN_MASK, KPDBL_MODULE_DIS);
- if (rc) {
- dev_err(&led->spmi_dev->dev,
- "Failed to write led enable reg\n");
- return rc;
+
+ if (led->kpdbl_cfg->always_on) {
+ rc = pwm_config(led->kpdbl_cfg->pwm_cfg->pwm_dev, 0,
+ led->kpdbl_cfg->pwm_cfg->pwm_period_us);
+ if (rc < 0) {
+ dev_err(&led->spmi_dev->dev,
+ "pwm config failed\n");
+ return rc;
+ }
+
+ rc = pwm_enable(led->kpdbl_cfg->pwm_cfg->pwm_dev);
+ if (rc < 0) {
+ dev_err(&led->spmi_dev->dev, "pwm enable failed\n");
+ return rc;
+ }
+ } else
+ pwm_disable(led->kpdbl_cfg->pwm_cfg->pwm_dev);
+
+ if (num_kpbl_leds_on > 0)
+ num_kpbl_leds_on--;
+
+ if (!num_kpbl_leds_on) {
+ rc = qpnp_led_masked_write(led, KPDBL_ENABLE(led->base),
+ KPDBL_MODULE_EN_MASK, KPDBL_MODULE_DIS);
+ if (rc) {
+ dev_err(&led->spmi_dev->dev,
+ "Failed to write led enable reg\n");
+ return rc;
+ }
}
}
led->kpdbl_cfg->pwm_cfg->blinking = false;
+
qpnp_dump_regs(led, kpdbl_debug_regs, ARRAY_SIZE(kpdbl_debug_regs));
return 0;
@@ -1000,11 +1047,14 @@
struct qpnp_led_data *led;
led = container_of(led_cdev, struct qpnp_led_data, cdev);
- if (value < LED_OFF || value > led->cdev.max_brightness) {
+ if (value < LED_OFF) {
dev_err(&led->spmi_dev->dev, "Invalid brightness value\n");
return;
}
+ if (value > led->cdev.max_brightness)
+ value = led->cdev.max_brightness;
+
led->cdev.brightness = value;
schedule_work(&led->work);
}
@@ -2013,16 +2063,31 @@
int rc;
u8 val;
- /* enable row source selct */
- rc = qpnp_led_masked_write(led, KPDBL_ROW_SRC_SEL(led->base),
- KPDBL_ROW_SRC_SEL_VAL_MASK, led->kpdbl_cfg->row_src_sel_val);
+ /* select row source - vbst or vph */
+ rc = spmi_ext_register_readl(led->spmi_dev->ctrl, led->spmi_dev->sid,
+ KPDBL_ROW_SRC_SEL(led->base), &val, 1);
if (rc) {
dev_err(&led->spmi_dev->dev,
- "Enable row src sel write failed(%d)\n", rc);
+ "Unable to read from addr=%x, rc(%d)\n",
+ KPDBL_ROW_SRC_SEL(led->base), rc);
return rc;
}
- /* row source */
+ if (led->kpdbl_cfg->row_src_vbst)
+ val |= 1 << led->kpdbl_cfg->row_id;
+ else
+ val &= ~(1 << led->kpdbl_cfg->row_id);
+
+ rc = spmi_ext_register_writel(led->spmi_dev->ctrl, led->spmi_dev->sid,
+ KPDBL_ROW_SRC_SEL(led->base), &val, 1);
+ if (rc) {
+ dev_err(&led->spmi_dev->dev,
+ "Unable to read from addr=%x, rc(%d)\n",
+ KPDBL_ROW_SRC_SEL(led->base), rc);
+ return rc;
+ }
+
+ /* row source enable */
rc = spmi_ext_register_readl(led->spmi_dev->ctrl, led->spmi_dev->sid,
KPDBL_ROW_SRC(led->base), &val, 1);
if (rc) {
@@ -2032,12 +2097,10 @@
return rc;
}
- val &= ~KPDBL_ROW_SCAN_VAL_MASK;
- val |= led->kpdbl_cfg->row_scan_val;
-
- led->kpdbl_cfg->row_scan_en <<= KPDBL_ROW_SCAN_EN_SHIFT;
- val &= ~KPDBL_ROW_SCAN_EN_MASK;
- val |= led->kpdbl_cfg->row_scan_en;
+ if (led->kpdbl_cfg->row_src_en)
+ val |= KPDBL_ROW_SCAN_EN_MASK | (1 << led->kpdbl_cfg->row_id);
+ else
+ val &= ~(1 << led->kpdbl_cfg->row_id);
rc = spmi_ext_register_writel(led->spmi_dev->ctrl, led->spmi_dev->sid,
KPDBL_ROW_SRC(led->base), &val, 1);
@@ -2565,6 +2628,7 @@
dev_err(&led->spmi_dev->dev, "Unable to allocate memory\n");
return -ENOMEM;
}
+
rc = of_property_read_string(node, "qcom,mode", &mode);
if (!rc) {
led_mode = qpnp_led_get_mode(mode);
@@ -2590,23 +2654,20 @@
if (rc < 0)
return rc;
- rc = of_property_read_u32(node, "qcom,row-src-sel-val", &val);
+ rc = of_property_read_u32(node, "qcom,row-id", &val);
if (!rc)
- led->kpdbl_cfg->row_src_sel_val = val;
+ led->kpdbl_cfg->row_id = val;
else
return rc;
- rc = of_property_read_u32(node, "qcom,row-scan-val", &val);
- if (!rc)
- led->kpdbl_cfg->row_scan_val = val;
- else
- return rc;
+ led->kpdbl_cfg->row_src_vbst =
+ of_property_read_bool(node, "qcom,row-src-vbst");
- rc = of_property_read_u32(node, "qcom,row-scan-en", &val);
- if (!rc)
- led->kpdbl_cfg->row_scan_en = val;
- else
- return rc;
+ led->kpdbl_cfg->row_src_en =
+ of_property_read_bool(node, "qcom,row-src-en");
+
+ led->kpdbl_cfg->always_on =
+ of_property_read_bool(node, "qcom,always-on");
return 0;
}
@@ -2856,6 +2917,7 @@
goto fail_id_check;
}
} else if (strncmp(led_label, "kpdbl", sizeof("kpdbl")) == 0) {
+ num_kpbl_leds_on = 0;
rc = qpnp_get_config_kpdbl(led, temp);
if (rc < 0) {
dev_err(&led->spmi_dev->dev,