summaryrefslogtreecommitdiff
path: root/drivers/leds/leds-qpnp-wled.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/leds/leds-qpnp-wled.c')
-rw-r--r--drivers/leds/leds-qpnp-wled.c238
1 files changed, 159 insertions, 79 deletions
diff --git a/drivers/leds/leds-qpnp-wled.c b/drivers/leds/leds-qpnp-wled.c
index c85b3e42c8c8..b0519544759e 100644
--- a/drivers/leds/leds-qpnp-wled.c
+++ b/drivers/leds/leds-qpnp-wled.c
@@ -63,7 +63,6 @@
#define QPNP_WLED_VLOOP_COMP_RES_MASK 0xF0
#define QPNP_WLED_VLOOP_COMP_RES_OVERWRITE 0x80
-#define QPNP_WLED_LOOP_COMP_RES_DFLT_AMOLED_KOHM 320
#define QPNP_WLED_LOOP_COMP_RES_STEP_KOHM 20
#define QPNP_WLED_LOOP_COMP_RES_MIN_KOHM 20
#define QPNP_WLED_LOOP_COMP_RES_MAX_KOHM 320
@@ -106,10 +105,8 @@
#define QPNP_WLED_BOOST_DUTY_MIN_NS 26
#define QPNP_WLED_BOOST_DUTY_MAX_NS 156
#define QPNP_WLED_DEF_BOOST_DUTY_NS 104
-#define QPNP_WLED_SWITCH_FREQ_MASK 0x70
-#define QPNP_WLED_SWITCH_FREQ_800_KHZ 800
-#define QPNP_WLED_SWITCH_FREQ_1600_KHZ 1600
-#define QPNP_WLED_SWITCH_FREQ_OVERWRITE 0x80
+#define QPNP_WLED_SWITCH_FREQ_MASK GENMASK(3, 0)
+#define QPNP_WLED_SWITCH_FREQ_OVERWRITE BIT(7)
#define QPNP_WLED_OVP_MASK GENMASK(1, 0)
#define QPNP_WLED_TEST4_EN_DEB_BYPASS_ILIM_BIT BIT(6)
#define QPNP_WLED_TEST4_EN_SH_FOR_SS_BIT BIT(5)
@@ -123,6 +120,11 @@
#define QPNP_WLED_ILIM_FAULT_BIT BIT(0)
#define QPNP_WLED_OVP_FAULT_BIT BIT(1)
#define QPNP_WLED_SC_FAULT_BIT BIT(2)
+#define QPNP_WLED_OVP_FLT_RT_STS_BIT BIT(1)
+
+/* QPNP_WLED_SOFTSTART_RAMP_DLY */
+#define SOFTSTART_OVERWRITE_BIT BIT(7)
+#define SOFTSTART_RAMP_DELAY_MASK GENMASK(2, 0)
/* sink registers */
#define QPNP_WLED_CURR_SINK_REG(b) (b + 0x46)
@@ -403,6 +405,7 @@ struct qpnp_wled {
bool ovp_irq_disabled;
bool auto_calib_enabled;
bool auto_calib_done;
+ bool module_dis_perm;
ktime_t start_ovp_fault_time;
};
@@ -599,6 +602,9 @@ static int qpnp_wled_module_en(struct qpnp_wled *wled,
{
int rc;
+ if (wled->module_dis_perm)
+ return 0;
+
rc = qpnp_wled_masked_write_reg(wled,
QPNP_WLED_MODULE_EN_REG(base_addr),
QPNP_WLED_MODULE_EN_MASK,
@@ -1097,20 +1103,12 @@ static int qpnp_wled_set_disp(struct qpnp_wled *wled, u16 base_addr)
return 0;
}
-#define AUTO_CALIB_BRIGHTNESS 16
+#define AUTO_CALIB_BRIGHTNESS 200
static int wled_auto_calibrate(struct qpnp_wled *wled)
{
int rc = 0, i;
u8 reg = 0, sink_config = 0, sink_test = 0, sink_valid = 0, int_sts;
- mutex_lock(&wled->lock);
-
- /* disable OVP IRQ */
- if (wled->ovp_irq > 0 && !wled->ovp_irq_disabled) {
- disable_irq_nosync(wled->ovp_irq);
- wled->ovp_irq_disabled = true;
- }
-
/* read configured sink configuration */
rc = qpnp_wled_read_reg(wled,
QPNP_WLED_CURR_SINK_REG(wled->sink_base), &sink_config);
@@ -1135,6 +1133,17 @@ static int wled_auto_calibrate(struct qpnp_wled *wled)
goto failed_calib;
}
+ if (wled->en_cabc) {
+ for (i = 0; i < wled->max_strings; i++) {
+ reg = 0;
+ rc = qpnp_wled_masked_write_reg(wled,
+ QPNP_WLED_CABC_REG(wled->sink_base, i),
+ QPNP_WLED_CABC_MASK, reg);
+ if (rc < 0)
+ goto failed_calib;
+ }
+ }
+
/* disable all sinks */
rc = qpnp_wled_write_reg(wled,
QPNP_WLED_CURR_SINK_REG(wled->sink_base), 0);
@@ -1143,21 +1152,6 @@ static int wled_auto_calibrate(struct qpnp_wled *wled)
goto failed_calib;
}
- rc = qpnp_wled_masked_write_reg(wled,
- QPNP_WLED_MODULE_EN_REG(wled->ctrl_base),
- QPNP_WLED_MODULE_EN_MASK,
- QPNP_WLED_MODULE_EN_MASK);
- if (rc < 0) {
- pr_err("Failed to enable WLED module rc=%d\n", rc);
- goto failed_calib;
- }
- /*
- * Delay for the WLED soft-start, check the OVP status
- * only after soft-start is complete
- */
- usleep_range(QPNP_WLED_SOFT_START_DLY_US,
- QPNP_WLED_SOFT_START_DLY_US + 1000);
-
/* iterate through the strings one by one */
for (i = 0; i < wled->max_strings; i++) {
sink_test = 1 << (QPNP_WLED_CURR_SINK_SHIFT + i);
@@ -1181,6 +1175,15 @@ static int wled_auto_calibrate(struct qpnp_wled *wled)
goto failed_calib;
}
+ /* Enable the module */
+ rc = qpnp_wled_masked_write_reg(wled,
+ QPNP_WLED_MODULE_EN_REG(wled->ctrl_base),
+ QPNP_WLED_MODULE_EN_MASK, QPNP_WLED_MODULE_EN_MASK);
+ if (rc < 0) {
+ pr_err("Failed to enable WLED module rc=%d\n", rc);
+ goto failed_calib;
+ }
+
/* delay for WLED soft-start */
usleep_range(QPNP_WLED_SOFT_START_DLY_US,
QPNP_WLED_SOFT_START_DLY_US + 1000);
@@ -1197,6 +1200,15 @@ static int wled_auto_calibrate(struct qpnp_wled *wled)
i + 1);
else
sink_valid |= sink_test;
+
+ /* Disable the module */
+ rc = qpnp_wled_masked_write_reg(wled,
+ QPNP_WLED_MODULE_EN_REG(wled->ctrl_base),
+ QPNP_WLED_MODULE_EN_MASK, 0);
+ if (rc < 0) {
+ pr_err("Failed to disable WLED module rc=%d\n", rc);
+ goto failed_calib;
+ }
}
if (sink_valid == sink_config) {
@@ -1210,14 +1222,7 @@ static int wled_auto_calibrate(struct qpnp_wled *wled)
if (!sink_config) {
pr_warn("No valid WLED sinks found\n");
- goto failed_calib;
- }
-
- rc = qpnp_wled_masked_write_reg(wled,
- QPNP_WLED_MODULE_EN_REG(wled->ctrl_base),
- QPNP_WLED_MODULE_EN_MASK, 0);
- if (rc < 0) {
- pr_err("Failed to disable WLED module rc=%d\n", rc);
+ wled->module_dis_perm = true;
goto failed_calib;
}
@@ -1231,6 +1236,15 @@ static int wled_auto_calibrate(struct qpnp_wled *wled)
/* MODULATOR_EN setting for valid sinks */
for (i = 0; i < wled->max_strings; i++) {
+ if (wled->en_cabc) {
+ reg = 1 << QPNP_WLED_CABC_SHIFT;
+ rc = qpnp_wled_masked_write_reg(wled,
+ QPNP_WLED_CABC_REG(wled->sink_base, i),
+ QPNP_WLED_CABC_MASK, reg);
+ if (rc < 0)
+ goto failed_calib;
+ }
+
if (sink_config & (1 << (QPNP_WLED_CURR_SINK_SHIFT + i)))
reg = (QPNP_WLED_MOD_EN << QPNP_WLED_MOD_EN_SHFT);
else
@@ -1259,7 +1273,8 @@ static int wled_auto_calibrate(struct qpnp_wled *wled)
}
/* restore brightness */
- rc = qpnp_wled_set_level(wled, wled->cdev.brightness);
+ rc = qpnp_wled_set_level(wled, !wled->cdev.brightness ?
+ AUTO_CALIB_BRIGHTNESS : wled->cdev.brightness);
if (rc < 0) {
pr_err("Failed to set brightness after calibration rc=%d\n",
rc);
@@ -1280,11 +1295,6 @@ static int wled_auto_calibrate(struct qpnp_wled *wled)
QPNP_WLED_SOFT_START_DLY_US + 1000);
failed_calib:
- if (wled->ovp_irq > 0 && wled->ovp_irq_disabled) {
- enable_irq(wled->ovp_irq);
- wled->ovp_irq_disabled = false;
- }
- mutex_unlock(&wled->lock);
return rc;
}
@@ -1320,6 +1330,38 @@ static bool qpnp_wled_auto_cal_required(struct qpnp_wled *wled)
return false;
}
+static int qpnp_wled_auto_calibrate_at_init(struct qpnp_wled *wled)
+{
+ int rc;
+ u8 fault_status = 0, rt_status = 0;
+
+ if (!wled->auto_calib_enabled)
+ return 0;
+
+ rc = qpnp_wled_read_reg(wled,
+ QPNP_WLED_INT_RT_STS(wled->ctrl_base), &rt_status);
+ if (rc < 0)
+ pr_err("Failed to read RT status rc=%d\n", rc);
+
+ rc = qpnp_wled_read_reg(wled,
+ QPNP_WLED_FAULT_STATUS(wled->ctrl_base), &fault_status);
+ if (rc < 0)
+ pr_err("Failed to read fault status rc=%d\n", rc);
+
+ if ((rt_status & QPNP_WLED_OVP_FLT_RT_STS_BIT) ||
+ (fault_status & QPNP_WLED_OVP_FAULT_BIT)) {
+ mutex_lock(&wled->lock);
+ rc = wled_auto_calibrate(wled);
+ if (rc < 0)
+ pr_err("Failed auto-calibration rc=%d\n", rc);
+ else
+ wled->auto_calib_done = true;
+ mutex_unlock(&wled->lock);
+ }
+
+ return rc;
+}
+
/* ovp irq handler */
static irqreturn_t qpnp_wled_ovp_irq_handler(int irq, void *_wled)
{
@@ -1348,13 +1390,26 @@ static irqreturn_t qpnp_wled_ovp_irq_handler(int irq, void *_wled)
if (fault_sts & QPNP_WLED_OVP_FAULT_BIT) {
if (wled->auto_calib_enabled && !wled->auto_calib_done) {
if (qpnp_wled_auto_cal_required(wled)) {
+ mutex_lock(&wled->lock);
+ if (wled->ovp_irq > 0 &&
+ !wled->ovp_irq_disabled) {
+ disable_irq_nosync(wled->ovp_irq);
+ wled->ovp_irq_disabled = true;
+ }
+
rc = wled_auto_calibrate(wled);
- if (rc < 0) {
+ if (rc < 0)
pr_err("Failed auto-calibration rc=%d\n",
- rc);
- return IRQ_HANDLED;
+ rc);
+ else
+ wled->auto_calib_done = true;
+
+ if (wled->ovp_irq > 0 &&
+ wled->ovp_irq_disabled) {
+ enable_irq(wled->ovp_irq);
+ wled->ovp_irq_disabled = false;
}
- wled->auto_calib_done = true;
+ mutex_unlock(&wled->lock);
}
}
}
@@ -1422,20 +1477,26 @@ static int qpnp_wled_gm_config(struct qpnp_wled *wled)
u8 mask = 0, reg = 0;
/* Configure the LOOP COMP GM register */
- if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
- wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) {
- if (wled->loop_auto_gm_en)
- reg |= QPNP_WLED_VLOOP_COMP_AUTO_GM_EN;
-
- if (wled->loop_auto_gm_thresh >
- QPNP_WLED_LOOP_AUTO_GM_THRESH_MAX)
- wled->loop_auto_gm_thresh =
- QPNP_WLED_LOOP_AUTO_GM_THRESH_MAX;
-
- reg |= wled->loop_auto_gm_thresh <<
- QPNP_WLED_VLOOP_COMP_AUTO_GM_THRESH_SHIFT;
- mask |= QPNP_WLED_VLOOP_COMP_AUTO_GM_EN |
- QPNP_WLED_VLOOP_COMP_AUTO_GM_THRESH_MASK;
+ if ((wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
+ wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)) {
+ if (wled->disp_type_amoled) {
+ reg = 0;
+ mask |= QPNP_WLED_VLOOP_COMP_AUTO_GM_EN |
+ QPNP_WLED_VLOOP_COMP_AUTO_GM_THRESH_MASK;
+ } else {
+ if (wled->loop_auto_gm_en)
+ reg |= QPNP_WLED_VLOOP_COMP_AUTO_GM_EN;
+
+ if (wled->loop_auto_gm_thresh >
+ QPNP_WLED_LOOP_AUTO_GM_THRESH_MAX)
+ wled->loop_auto_gm_thresh =
+ QPNP_WLED_LOOP_AUTO_GM_THRESH_MAX;
+
+ reg |= wled->loop_auto_gm_thresh <<
+ QPNP_WLED_VLOOP_COMP_AUTO_GM_THRESH_SHIFT;
+ mask |= QPNP_WLED_VLOOP_COMP_AUTO_GM_EN |
+ QPNP_WLED_VLOOP_COMP_AUTO_GM_THRESH_MASK;
+ }
}
if (wled->loop_ea_gm < QPNP_WLED_LOOP_EA_GM_MIN)
@@ -1728,8 +1789,17 @@ static int qpnp_wled_config(struct qpnp_wled *wled)
/* Configure the Soft start Ramp delay: for AMOLED - 0,for LCD - 2 */
reg = (wled->disp_type_amoled) ? 0 : 2;
- rc = qpnp_wled_write_reg(wled,
- QPNP_WLED_SOFTSTART_RAMP_DLY(wled->ctrl_base), reg);
+ mask = SOFTSTART_RAMP_DELAY_MASK;
+ if ((wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
+ wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
+ && wled->disp_type_amoled) {
+ reg |= SOFTSTART_OVERWRITE_BIT;
+ mask |= SOFTSTART_OVERWRITE_BIT;
+ }
+
+ rc = qpnp_wled_masked_write_reg(wled,
+ QPNP_WLED_SOFTSTART_RAMP_DLY(wled->ctrl_base),
+ mask, reg);
if (rc)
return rc;
@@ -1751,21 +1821,24 @@ static int qpnp_wled_config(struct qpnp_wled *wled)
return rc;
/* Configure the SWITCHING FREQ register */
- if (wled->switch_freq_khz == QPNP_WLED_SWITCH_FREQ_1600_KHZ)
- temp = QPNP_WLED_SWITCH_FREQ_1600_KHZ_CODE;
+ if (wled->switch_freq_khz == 1600)
+ reg = QPNP_WLED_SWITCH_FREQ_1600_KHZ_CODE;
else
- temp = QPNP_WLED_SWITCH_FREQ_800_KHZ_CODE;
+ reg = QPNP_WLED_SWITCH_FREQ_800_KHZ_CODE;
- rc = qpnp_wled_read_reg(wled,
- QPNP_WLED_SWITCH_FREQ_REG(wled->ctrl_base), &reg);
+ /*
+ * Do not set the overwrite bit when switching frequency is selected
+ * for AMOLED. This register is in logic reset block which can cause
+ * the value to be overwritten during module enable/disable.
+ */
+ mask = QPNP_WLED_SWITCH_FREQ_MASK | QPNP_WLED_SWITCH_FREQ_OVERWRITE;
+ if (!wled->disp_type_amoled)
+ reg |= QPNP_WLED_SWITCH_FREQ_OVERWRITE;
+
+ rc = qpnp_wled_masked_write_reg(wled,
+ QPNP_WLED_SWITCH_FREQ_REG(wled->ctrl_base), mask, reg);
if (rc < 0)
return rc;
- reg &= QPNP_WLED_SWITCH_FREQ_MASK;
- reg |= (temp | QPNP_WLED_SWITCH_FREQ_OVERWRITE);
- rc = qpnp_wled_write_reg(wled,
- QPNP_WLED_SWITCH_FREQ_REG(wled->ctrl_base), reg);
- if (rc)
- return rc;
rc = qpnp_wled_ovp_config(wled);
if (rc < 0) {
@@ -1946,6 +2019,10 @@ static int qpnp_wled_config(struct qpnp_wled *wled)
return rc;
}
+ rc = qpnp_wled_auto_calibrate_at_init(wled);
+ if (rc < 0)
+ pr_err("Failed to auto-calibrate at init rc=%d\n", rc);
+
/* setup ovp and sc irqs */
if (wled->ovp_irq >= 0) {
rc = devm_request_threaded_irq(&wled->pdev->dev, wled->ovp_irq,
@@ -2070,8 +2147,11 @@ static int qpnp_wled_parse_dt(struct qpnp_wled *wled)
return rc;
}
- wled->loop_comp_res_kohm =
- QPNP_WLED_LOOP_COMP_RES_DFLT_AMOLED_KOHM;
+ wled->loop_comp_res_kohm = 320;
+ if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
+ wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
+ wled->loop_comp_res_kohm = 300;
+
rc = of_property_read_u32(pdev->dev.of_node,
"qcom,loop-comp-res-kohm", &temp_val);
if (!rc) {
@@ -2199,7 +2279,7 @@ static int qpnp_wled_parse_dt(struct qpnp_wled *wled)
return rc;
}
- wled->switch_freq_khz = QPNP_WLED_SWITCH_FREQ_800_KHZ;
+ wled->switch_freq_khz = wled->disp_type_amoled ? 1600 : 800;
rc = of_property_read_u32(pdev->dev.of_node,
"qcom,switch-freq-khz", &temp_val);
if (!rc) {
@@ -2403,7 +2483,7 @@ static int qpnp_wled_probe(struct platform_device *pdev)
wled->pmic_rev_id->pmic_subtype, wled->pmic_rev_id->rev4);
prop = of_get_address_by_name(pdev->dev.of_node, QPNP_WLED_SINK_BASE,
- 0, 0);
+ NULL, NULL);
if (!prop) {
dev_err(&pdev->dev, "Couldnt find sink's addr rc %d\n", rc);
return rc;
@@ -2411,7 +2491,7 @@ static int qpnp_wled_probe(struct platform_device *pdev)
wled->sink_base = be32_to_cpu(*prop);
prop = of_get_address_by_name(pdev->dev.of_node, QPNP_WLED_CTRL_BASE,
- 0, 0);
+ NULL, NULL);
if (!prop) {
dev_err(&pdev->dev, "Couldnt find ctrl's addr rc = %d\n", rc);
return rc;