Loading Documentation/devicetree/bindings/leds/leds-qpnp-wled.txt +3 −0 Original line number Diff line number Diff line Loading @@ -39,6 +39,8 @@ Optional properties for WLED: - qcom,en-phase-stag : boolean, specify if phase staggering is needed. - qcom,en-cabc : boolean, specify if cabc (content adaptive backlight control) is needed. - qcom,ibb-bias-active : boolean, specify to activate the bias - qcom,ibb-pwrup-dly : ibb power up delay in milli seconds. The supported values are 1 to 8. default is 1. - qcom,lab-fast-precharge: boolean, specify to activate the fast precharge - qcom,disp-type-amoled : specify if the display is amoled - qcom,led-strings-list : Wled module has four strings of leds numbered from 0 to 3. each string of leds Loading Loading @@ -74,6 +76,7 @@ Example: qcom,en-cabc; qcom,led-strings-list = [00 01 02 03]; qcom,ibb-bias-active; qcom,ibb-pwrup-dly = <8>; qcom,lab-fast-precharge; qcom,disp-type-amoled; }; drivers/leds/leds-qpnp-wled.c +97 −69 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ #include <linux/interrupt.h> #include <linux/err.h> #include <linux/delay.h> #include <linux/leds-qpnp-wled.h> /* base addresses */ #define QPNP_WLED_CTRL_BASE "qpnp-wled-ctrl-base" Loading Loading @@ -130,11 +131,20 @@ #define QPNP_WLED_IBB_BIAS_REG(b) (b + 0x58) #define QPNP_WLED_IBB_BIAS_MASK 0x7F #define QPNP_WLED_IBB_BIAS_SHIFT 7 #define QPNP_WLED_IBB_PWRUP_DLY_MASK 0xCF #define QPNP_WLED_IBB_PWRUP_DLY_SHIFT 4 #define QPNP_WLED_IBB_PWRUP_DLY_MIN_MS 1 #define QPNP_WLED_IBB_PWRUP_DLY_MAX_MS 8 #define QPNP_WLED_LAB_IBB_RDY_REG(b) (b + 0x49) #define QPNP_WLED_LAB_FAST_PC_REG(b) (b + 0x5E) #define QPNP_WLED_LAB_FAST_PC_MASK 0xFB #define QPNP_WLED_LAB_START_DLY_US 8 #define QPNP_WLED_LAB_FAST_PC_SHIFT 2 #define QPNP_WLED_SEC_ACCESS_REG(b) (b + 0xD0) #define QPNP_WLED_SEC_UNLOCK 0xA5 #define QPNP_WLED_MAX_STRINGS 4 #define WLED_MAX_LEVEL_511 511 #define WLED_MAX_LEVEL_4095 4095 Loading Loading @@ -183,12 +193,12 @@ static u8 qpnp_wled_sink_dbg_regs[] = { /* wled ibb debug registers */ static u8 qpnp_wled_ibb_dbg_regs[] = { 0x44, 0x45, 0x46, 0x58, 0x08, 0x09, 0x0A, 0x44, 0x45, 0x46, 0x50, 0x53, 0x56, 0x57, 0x58, 0x61 }; /* wled lab debug registers */ static u8 qpnp_wled_lab_dbg_regs[] = { 0x44, 0x45, 0x46, 0x5e, 0x08, 0x44, 0x45, 0x46, 0x49, 0x5e }; /** Loading Loading @@ -245,6 +255,7 @@ struct qpnp_wled { u16 ilim_ma; u16 boost_duty_ns; u16 fs_curr_ua; u16 ibb_pwrup_dly_ms; u16 ramp_ms; u16 ramp_step; u8 strings[QPNP_WLED_MAX_STRINGS]; Loading @@ -257,6 +268,8 @@ struct qpnp_wled { bool lab_fast_precharge; }; static struct qpnp_wled *gwled; /* helper to read a pmic register */ static int qpnp_wled_read_reg(struct qpnp_wled *wled, u8 *data, u16 addr) { Loading Loading @@ -287,6 +300,19 @@ static int qpnp_wled_write_reg(struct qpnp_wled *wled, u8 *data, u16 addr) return rc; } static int qpnp_wled_sec_access(struct qpnp_wled *wled, u16 base_addr) { int rc; u8 reg = QPNP_WLED_SEC_UNLOCK; rc = qpnp_wled_write_reg(wled, ®, QPNP_WLED_SEC_ACCESS_REG(base_addr)); if (rc) return rc; return 0; } /* set wled to a level of brightness */ static int qpnp_wled_set_level(struct qpnp_wled *wled, int level) { Loading Loading @@ -347,58 +373,6 @@ static int qpnp_wled_module_en(struct qpnp_wled *wled, return 0; } /* enable/disable wled module */ static int qpnp_wled_enable(struct qpnp_wled *wled, bool state) { int rc; if (state) { /* enable wled control */ rc = qpnp_wled_module_en(wled, wled->ctrl_base, state); if (rc < 0) return rc; /* enable lab */ rc = qpnp_wled_module_en(wled, wled->lab_base, state); if (rc < 0) goto lab_en_fail; /* enable ibb */ rc = qpnp_wled_module_en(wled, wled->ibb_base, state); if (rc < 0) goto ibb_en_fail; } else { /* disable ibb */ rc = qpnp_wled_module_en(wled, wled->ibb_base, state); if (rc < 0) return rc; /* disable lab */ rc = qpnp_wled_module_en(wled, wled->lab_base, state); if (rc < 0) goto lab_dis_fail; /* disable wled control */ rc = qpnp_wled_module_en(wled, wled->ctrl_base, state); if (rc < 0) goto ctrl_dis_fail; } return 0; ibb_en_fail: qpnp_wled_module_en(wled, wled->lab_base, !state); lab_en_fail: qpnp_wled_module_en(wled, wled->ctrl_base, !state); return rc; ctrl_dis_fail: qpnp_wled_module_en(wled, wled->lab_base, !state); lab_dis_fail: qpnp_wled_module_en(wled, wled->ibb_base, !state); return rc; } /* sysfs store function for ramp */ static ssize_t qpnp_wled_ramp_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) Loading @@ -409,7 +383,7 @@ static ssize_t qpnp_wled_ramp_store(struct device *dev, mutex_lock(&wled->lock); if (!wled->cdev.brightness) { rc = qpnp_wled_enable(wled, true); rc = qpnp_wled_module_en(wled, wled->ctrl_base, true); if (rc) { dev_err(&wled->spmi->dev, "wled enable failed\n"); goto unlock_mutex; Loading Loading @@ -466,7 +440,7 @@ restore_brightness: /* restore the old brightness */ qpnp_wled_set_level(wled, wled->cdev.brightness); if (!wled->cdev.brightness) { rc = qpnp_wled_enable(wled, false); rc = qpnp_wled_module_en(wled, wled->ctrl_base, false); if (rc) { dev_err(&wled->spmi->dev, "wled enable failed\n"); return rc; Loading Loading @@ -696,6 +670,44 @@ static ssize_t qpnp_wled_fs_curr_ua_show(struct device *dev, return snprintf(buf, PAGE_SIZE, "%d\n", wled->fs_curr_ua); } int qpnp_ibb_enable(bool state) { int rc; u8 reg; if (!gwled) { pr_err("%s: wled is not initialized yet\n", __func__); return -EAGAIN; } /* enable lab */ if (gwled->ibb_bias_active) { rc = qpnp_wled_module_en(gwled, gwled->lab_base, state); if (rc < 0) return rc; usleep_range(QPNP_WLED_LAB_START_DLY_US, QPNP_WLED_LAB_START_DLY_US + 1); } else { rc = qpnp_wled_read_reg(gwled, ®, QPNP_WLED_LAB_IBB_RDY_REG(gwled->lab_base)); if (rc < 0) return rc; reg &= QPNP_WLED_MODULE_EN_MASK; reg |= (state << QPNP_WLED_MODULE_EN_SHIFT); rc = qpnp_wled_write_reg(gwled, ®, QPNP_WLED_LAB_IBB_RDY_REG(gwled->lab_base)); if (rc) return rc; } rc = qpnp_wled_module_en(gwled, gwled->ibb_base, state); if (rc < 0) return rc; return 0; } EXPORT_SYMBOL(qpnp_ibb_enable); /* sysfs store function for full scale current in ua*/ static ssize_t qpnp_wled_fs_curr_ua_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) Loading Loading @@ -776,9 +788,9 @@ static void qpnp_wled_work(struct work_struct *work) dev_err(&wled->spmi->dev, "wled set level failed\n"); return; } rc = qpnp_wled_enable(wled, true); } else rc = qpnp_wled_enable(wled, false); } rc = qpnp_wled_module_en(wled, wled->ctrl_base, !!level); if (rc) { dev_err(&wled->spmi->dev, "wled %sable failed\n", Loading Loading @@ -1142,18 +1154,27 @@ static int qpnp_wled_config(struct qpnp_wled *wled) if (rc < 0) return rc; /* enable lab */ rc = qpnp_wled_module_en(wled, wled->lab_base, true); if (rc < 0) return rc; /* IBB active bias */ if (wled->ibb_pwrup_dly_ms < QPNP_WLED_IBB_PWRUP_DLY_MIN_MS) wled->ibb_pwrup_dly_ms = QPNP_WLED_IBB_PWRUP_DLY_MIN_MS; else if (wled->ibb_pwrup_dly_ms > QPNP_WLED_IBB_PWRUP_DLY_MAX_MS) wled->ibb_pwrup_dly_ms = QPNP_WLED_IBB_PWRUP_DLY_MAX_MS; rc = qpnp_wled_read_reg(wled, ®, QPNP_WLED_IBB_BIAS_REG(wled->ibb_base)); if (rc < 0) return rc; reg &= QPNP_WLED_IBB_BIAS_MASK; reg |= (!wled->ibb_bias_active << QPNP_WLED_IBB_BIAS_SHIFT); temp = fls(wled->ibb_pwrup_dly_ms) - 1; reg &= QPNP_WLED_IBB_PWRUP_DLY_MASK; reg |= (temp << QPNP_WLED_IBB_PWRUP_DLY_SHIFT); rc = qpnp_wled_sec_access(wled, wled->ibb_base); if (rc) return rc; rc = qpnp_wled_write_reg(wled, ®, QPNP_WLED_IBB_BIAS_REG(wled->ibb_base)); if (rc) Loading @@ -1169,11 +1190,6 @@ static int qpnp_wled_config(struct qpnp_wled *wled) if (rc < 0) return rc; /* enable ibb */ rc = qpnp_wled_module_en(wled, wled->ibb_base, true); if (rc < 0) return rc; return 0; } Loading Loading @@ -1368,6 +1384,16 @@ static int qpnp_wled_parse_dt(struct qpnp_wled *wled) wled->ibb_bias_active = of_property_read_bool(spmi->dev.of_node, "qcom,ibb-bias-active"); wled->ibb_pwrup_dly_ms = QPNP_WLED_IBB_PWRUP_DLY_MIN_MS; rc = of_property_read_u32(spmi->dev.of_node, "qcom,ibb-pwrup-dly", &temp_val); if (!rc) { wled->ibb_pwrup_dly_ms = temp_val; } else if (rc != -EINVAL) { dev_err(&spmi->dev, "Unable to read ibb pwrup delay\n"); return rc; } wled->lab_fast_precharge = of_property_read_bool(spmi->dev.of_node, "qcom,lab-fast-precharge"); return 0; Loading Loading @@ -1463,6 +1489,8 @@ static int qpnp_wled_probe(struct spmi_device *spmi) } } gwled = wled; return 0; sysfs_fail: Loading include/linux/leds-qpnp-wled.h 0 → 100644 +16 −0 Original line number Diff line number Diff line /* Copyright (c) 2014, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #ifndef __LEDS_QPNP_WLED_H int qpnp_ibb_enable(bool state); #endif Loading
Documentation/devicetree/bindings/leds/leds-qpnp-wled.txt +3 −0 Original line number Diff line number Diff line Loading @@ -39,6 +39,8 @@ Optional properties for WLED: - qcom,en-phase-stag : boolean, specify if phase staggering is needed. - qcom,en-cabc : boolean, specify if cabc (content adaptive backlight control) is needed. - qcom,ibb-bias-active : boolean, specify to activate the bias - qcom,ibb-pwrup-dly : ibb power up delay in milli seconds. The supported values are 1 to 8. default is 1. - qcom,lab-fast-precharge: boolean, specify to activate the fast precharge - qcom,disp-type-amoled : specify if the display is amoled - qcom,led-strings-list : Wled module has four strings of leds numbered from 0 to 3. each string of leds Loading Loading @@ -74,6 +76,7 @@ Example: qcom,en-cabc; qcom,led-strings-list = [00 01 02 03]; qcom,ibb-bias-active; qcom,ibb-pwrup-dly = <8>; qcom,lab-fast-precharge; qcom,disp-type-amoled; };
drivers/leds/leds-qpnp-wled.c +97 −69 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ #include <linux/interrupt.h> #include <linux/err.h> #include <linux/delay.h> #include <linux/leds-qpnp-wled.h> /* base addresses */ #define QPNP_WLED_CTRL_BASE "qpnp-wled-ctrl-base" Loading Loading @@ -130,11 +131,20 @@ #define QPNP_WLED_IBB_BIAS_REG(b) (b + 0x58) #define QPNP_WLED_IBB_BIAS_MASK 0x7F #define QPNP_WLED_IBB_BIAS_SHIFT 7 #define QPNP_WLED_IBB_PWRUP_DLY_MASK 0xCF #define QPNP_WLED_IBB_PWRUP_DLY_SHIFT 4 #define QPNP_WLED_IBB_PWRUP_DLY_MIN_MS 1 #define QPNP_WLED_IBB_PWRUP_DLY_MAX_MS 8 #define QPNP_WLED_LAB_IBB_RDY_REG(b) (b + 0x49) #define QPNP_WLED_LAB_FAST_PC_REG(b) (b + 0x5E) #define QPNP_WLED_LAB_FAST_PC_MASK 0xFB #define QPNP_WLED_LAB_START_DLY_US 8 #define QPNP_WLED_LAB_FAST_PC_SHIFT 2 #define QPNP_WLED_SEC_ACCESS_REG(b) (b + 0xD0) #define QPNP_WLED_SEC_UNLOCK 0xA5 #define QPNP_WLED_MAX_STRINGS 4 #define WLED_MAX_LEVEL_511 511 #define WLED_MAX_LEVEL_4095 4095 Loading Loading @@ -183,12 +193,12 @@ static u8 qpnp_wled_sink_dbg_regs[] = { /* wled ibb debug registers */ static u8 qpnp_wled_ibb_dbg_regs[] = { 0x44, 0x45, 0x46, 0x58, 0x08, 0x09, 0x0A, 0x44, 0x45, 0x46, 0x50, 0x53, 0x56, 0x57, 0x58, 0x61 }; /* wled lab debug registers */ static u8 qpnp_wled_lab_dbg_regs[] = { 0x44, 0x45, 0x46, 0x5e, 0x08, 0x44, 0x45, 0x46, 0x49, 0x5e }; /** Loading Loading @@ -245,6 +255,7 @@ struct qpnp_wled { u16 ilim_ma; u16 boost_duty_ns; u16 fs_curr_ua; u16 ibb_pwrup_dly_ms; u16 ramp_ms; u16 ramp_step; u8 strings[QPNP_WLED_MAX_STRINGS]; Loading @@ -257,6 +268,8 @@ struct qpnp_wled { bool lab_fast_precharge; }; static struct qpnp_wled *gwled; /* helper to read a pmic register */ static int qpnp_wled_read_reg(struct qpnp_wled *wled, u8 *data, u16 addr) { Loading Loading @@ -287,6 +300,19 @@ static int qpnp_wled_write_reg(struct qpnp_wled *wled, u8 *data, u16 addr) return rc; } static int qpnp_wled_sec_access(struct qpnp_wled *wled, u16 base_addr) { int rc; u8 reg = QPNP_WLED_SEC_UNLOCK; rc = qpnp_wled_write_reg(wled, ®, QPNP_WLED_SEC_ACCESS_REG(base_addr)); if (rc) return rc; return 0; } /* set wled to a level of brightness */ static int qpnp_wled_set_level(struct qpnp_wled *wled, int level) { Loading Loading @@ -347,58 +373,6 @@ static int qpnp_wled_module_en(struct qpnp_wled *wled, return 0; } /* enable/disable wled module */ static int qpnp_wled_enable(struct qpnp_wled *wled, bool state) { int rc; if (state) { /* enable wled control */ rc = qpnp_wled_module_en(wled, wled->ctrl_base, state); if (rc < 0) return rc; /* enable lab */ rc = qpnp_wled_module_en(wled, wled->lab_base, state); if (rc < 0) goto lab_en_fail; /* enable ibb */ rc = qpnp_wled_module_en(wled, wled->ibb_base, state); if (rc < 0) goto ibb_en_fail; } else { /* disable ibb */ rc = qpnp_wled_module_en(wled, wled->ibb_base, state); if (rc < 0) return rc; /* disable lab */ rc = qpnp_wled_module_en(wled, wled->lab_base, state); if (rc < 0) goto lab_dis_fail; /* disable wled control */ rc = qpnp_wled_module_en(wled, wled->ctrl_base, state); if (rc < 0) goto ctrl_dis_fail; } return 0; ibb_en_fail: qpnp_wled_module_en(wled, wled->lab_base, !state); lab_en_fail: qpnp_wled_module_en(wled, wled->ctrl_base, !state); return rc; ctrl_dis_fail: qpnp_wled_module_en(wled, wled->lab_base, !state); lab_dis_fail: qpnp_wled_module_en(wled, wled->ibb_base, !state); return rc; } /* sysfs store function for ramp */ static ssize_t qpnp_wled_ramp_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) Loading @@ -409,7 +383,7 @@ static ssize_t qpnp_wled_ramp_store(struct device *dev, mutex_lock(&wled->lock); if (!wled->cdev.brightness) { rc = qpnp_wled_enable(wled, true); rc = qpnp_wled_module_en(wled, wled->ctrl_base, true); if (rc) { dev_err(&wled->spmi->dev, "wled enable failed\n"); goto unlock_mutex; Loading Loading @@ -466,7 +440,7 @@ restore_brightness: /* restore the old brightness */ qpnp_wled_set_level(wled, wled->cdev.brightness); if (!wled->cdev.brightness) { rc = qpnp_wled_enable(wled, false); rc = qpnp_wled_module_en(wled, wled->ctrl_base, false); if (rc) { dev_err(&wled->spmi->dev, "wled enable failed\n"); return rc; Loading Loading @@ -696,6 +670,44 @@ static ssize_t qpnp_wled_fs_curr_ua_show(struct device *dev, return snprintf(buf, PAGE_SIZE, "%d\n", wled->fs_curr_ua); } int qpnp_ibb_enable(bool state) { int rc; u8 reg; if (!gwled) { pr_err("%s: wled is not initialized yet\n", __func__); return -EAGAIN; } /* enable lab */ if (gwled->ibb_bias_active) { rc = qpnp_wled_module_en(gwled, gwled->lab_base, state); if (rc < 0) return rc; usleep_range(QPNP_WLED_LAB_START_DLY_US, QPNP_WLED_LAB_START_DLY_US + 1); } else { rc = qpnp_wled_read_reg(gwled, ®, QPNP_WLED_LAB_IBB_RDY_REG(gwled->lab_base)); if (rc < 0) return rc; reg &= QPNP_WLED_MODULE_EN_MASK; reg |= (state << QPNP_WLED_MODULE_EN_SHIFT); rc = qpnp_wled_write_reg(gwled, ®, QPNP_WLED_LAB_IBB_RDY_REG(gwled->lab_base)); if (rc) return rc; } rc = qpnp_wled_module_en(gwled, gwled->ibb_base, state); if (rc < 0) return rc; return 0; } EXPORT_SYMBOL(qpnp_ibb_enable); /* sysfs store function for full scale current in ua*/ static ssize_t qpnp_wled_fs_curr_ua_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) Loading Loading @@ -776,9 +788,9 @@ static void qpnp_wled_work(struct work_struct *work) dev_err(&wled->spmi->dev, "wled set level failed\n"); return; } rc = qpnp_wled_enable(wled, true); } else rc = qpnp_wled_enable(wled, false); } rc = qpnp_wled_module_en(wled, wled->ctrl_base, !!level); if (rc) { dev_err(&wled->spmi->dev, "wled %sable failed\n", Loading Loading @@ -1142,18 +1154,27 @@ static int qpnp_wled_config(struct qpnp_wled *wled) if (rc < 0) return rc; /* enable lab */ rc = qpnp_wled_module_en(wled, wled->lab_base, true); if (rc < 0) return rc; /* IBB active bias */ if (wled->ibb_pwrup_dly_ms < QPNP_WLED_IBB_PWRUP_DLY_MIN_MS) wled->ibb_pwrup_dly_ms = QPNP_WLED_IBB_PWRUP_DLY_MIN_MS; else if (wled->ibb_pwrup_dly_ms > QPNP_WLED_IBB_PWRUP_DLY_MAX_MS) wled->ibb_pwrup_dly_ms = QPNP_WLED_IBB_PWRUP_DLY_MAX_MS; rc = qpnp_wled_read_reg(wled, ®, QPNP_WLED_IBB_BIAS_REG(wled->ibb_base)); if (rc < 0) return rc; reg &= QPNP_WLED_IBB_BIAS_MASK; reg |= (!wled->ibb_bias_active << QPNP_WLED_IBB_BIAS_SHIFT); temp = fls(wled->ibb_pwrup_dly_ms) - 1; reg &= QPNP_WLED_IBB_PWRUP_DLY_MASK; reg |= (temp << QPNP_WLED_IBB_PWRUP_DLY_SHIFT); rc = qpnp_wled_sec_access(wled, wled->ibb_base); if (rc) return rc; rc = qpnp_wled_write_reg(wled, ®, QPNP_WLED_IBB_BIAS_REG(wled->ibb_base)); if (rc) Loading @@ -1169,11 +1190,6 @@ static int qpnp_wled_config(struct qpnp_wled *wled) if (rc < 0) return rc; /* enable ibb */ rc = qpnp_wled_module_en(wled, wled->ibb_base, true); if (rc < 0) return rc; return 0; } Loading Loading @@ -1368,6 +1384,16 @@ static int qpnp_wled_parse_dt(struct qpnp_wled *wled) wled->ibb_bias_active = of_property_read_bool(spmi->dev.of_node, "qcom,ibb-bias-active"); wled->ibb_pwrup_dly_ms = QPNP_WLED_IBB_PWRUP_DLY_MIN_MS; rc = of_property_read_u32(spmi->dev.of_node, "qcom,ibb-pwrup-dly", &temp_val); if (!rc) { wled->ibb_pwrup_dly_ms = temp_val; } else if (rc != -EINVAL) { dev_err(&spmi->dev, "Unable to read ibb pwrup delay\n"); return rc; } wled->lab_fast_precharge = of_property_read_bool(spmi->dev.of_node, "qcom,lab-fast-precharge"); return 0; Loading Loading @@ -1463,6 +1489,8 @@ static int qpnp_wled_probe(struct spmi_device *spmi) } } gwled = wled; return 0; sysfs_fail: Loading
include/linux/leds-qpnp-wled.h 0 → 100644 +16 −0 Original line number Diff line number Diff line /* Copyright (c) 2014, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #ifndef __LEDS_QPNP_WLED_H int qpnp_ibb_enable(bool state); #endif