summaryrefslogtreecommitdiff
path: root/drivers/leds
diff options
context:
space:
mode:
authorSubbaraman Narayanamurthy <subbaram@codeaurora.org>2016-05-02 17:59:40 -0700
committerSubbaraman Narayanamurthy <subbaram@codeaurora.org>2016-10-25 10:17:06 -0700
commit7a1b6d0276828f270180db40935bb034a3470865 (patch)
tree764db40d44a4e6990a869cd65a62a6e3ad68b31b /drivers/leds
parent73518d1b29f8179e7a90a30ac681886773e8301d (diff)
leds: qpnp-wled: Fix configuration of AVDD TRIM and OVP registers
Currently the AVDD TRIM registers are being configured incorrectly. Update the sequence to configure the TRIM register and the OVP register based on predetermined values mapped to the target voltage specified in the device tree. Also add support for performing masked writes to the wled registers. CRs-Fixed: 1082047 Change-Id: I4c99b598d5f484368712eab239f7de70b727c2aa Signed-off-by: Devesh Jhunjhunwala <deveshj@codeaurora.org> Signed-off-by: Subbaraman Narayanamurthy <subbaram@codeaurora.org>
Diffstat (limited to 'drivers/leds')
-rw-r--r--drivers/leds/leds-qpnp-wled.c222
1 files changed, 169 insertions, 53 deletions
diff --git a/drivers/leds/leds-qpnp-wled.c b/drivers/leds/leds-qpnp-wled.c
index 7e129e1b6532..18d968d3711d 100644
--- a/drivers/leds/leds-qpnp-wled.c
+++ b/drivers/leds/leds-qpnp-wled.c
@@ -50,10 +50,12 @@
#define QPNP_WLED_VLOOP_COMP_GM_REG(b) (b + 0x56)
#define QPNP_WLED_PSM_CTRL_REG(b) (b + 0x5B)
#define QPNP_WLED_SC_PRO_REG(b) (b + 0x5E)
+#define QPNP_WLED_CTRL_SPARE_REG(b) (b + 0xDF)
#define QPNP_WLED_TEST1_REG(b) (b + 0xE2)
#define QPNP_WLED_TEST4_REG(b) (b + 0xE5)
#define QPNP_WLED_REF_7P7_TRIM_REG(b) (b + 0xF2)
+#define QPNP_WLED_7P7_TRIM_MASK GENMASK(3, 0)
#define QPNP_WLED_EN_MASK 0x7F
#define QPNP_WLED_EN_SHIFT 7
#define QPNP_WLED_FDBK_OP_MASK 0xF8
@@ -80,9 +82,6 @@
#define QPNP_WLED_VREF_PSM_MAX_MV 750
#define QPNP_WLED_VREF_PSM_DFLT_AMOLED_MV 450
#define QPNP_WLED_PSM_CTRL_OVERWRITE 0x80
-#define QPNP_WLED_AVDD_MIN_TRIM_VALUE -7
-#define QPNP_WLED_AVDD_MAX_TRIM_VALUE 8
-#define QPNP_WLED_AVDD_TRIM_CENTER_VALUE 7
#define QPNP_WLED_ILIM_MASK 0xF8
#define QPNP_WLED_ILIM_MIN_MA 105
@@ -99,11 +98,7 @@
#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_OVP_MASK 0xFC
-#define QPNP_WLED_OVP_17800_MV 17800
-#define QPNP_WLED_OVP_19400_MV 19400
-#define QPNP_WLED_OVP_29500_MV 29500
-#define QPNP_WLED_OVP_31000_MV 31000
+#define QPNP_WLED_OVP_MASK GENMASK(1, 0)
#define QPNP_WLED_TEST4_EN_VREF_UP 0x32
#define QPNP_WLED_INT_EN_SET_OVP_EN 0x02
#define QPNP_WLED_OVP_FLT_SLEEP_US 10
@@ -199,6 +194,14 @@
#define QPNP_WLED_MIN_MSLEEP 20
#define QPNP_WLED_SC_DLY_MS 20
+#define NUM_SUPPORTED_AVDD_VOLTAGES 6
+#define QPNP_WLED_DFLT_AVDD_MV 7600
+#define QPNP_WLED_AVDD_MIN_TRIM_VAL 0x0
+#define QPNP_WLED_AVDD_MAX_TRIM_VAL 0xF
+#define QPNP_WLED_AVDD_SET_BIT BIT(4)
+
+#define NUM_SUPPORTED_OVP_THRESHOLDS 4
+
/* output feedback mode */
enum qpnp_wled_fdbk_op {
QPNP_WLED_FDBK_AUTO,
@@ -231,10 +234,30 @@ static u8 qpnp_wled_sink_dbg_regs[] = {
0xe6,
};
+static int qpnp_wled_avdd_target_voltages[NUM_SUPPORTED_AVDD_VOLTAGES] = {
+ 7900, 7600, 7300, 6400, 6100, 5800,
+};
+
+static u8 qpnp_wled_ovp_reg_settings[NUM_SUPPORTED_AVDD_VOLTAGES] = {
+ 0x0, 0x0, 0x1, 0x2, 0x2, 0x3,
+};
+
+static int qpnp_wled_avdd_trim_adjustments[NUM_SUPPORTED_AVDD_VOLTAGES] = {
+ 3, 0, -2, 7, 3, 3,
+};
+
+static int qpnp_wled_ovp_thresholds_pmi8994[NUM_SUPPORTED_OVP_THRESHOLDS] = {
+ 31000, 29500, 19400, 17800,
+};
+
+static int qpnp_wled_ovp_thresholds_pmicobalt[NUM_SUPPORTED_OVP_THRESHOLDS] = {
+ 31100, 29600, 19600, 18100,
+};
+
/**
* qpnp_wled - wed data structure
* @ cdev - led class device
- * @ spmi - spmi device
+ * @ pdev - platform device
* @ work - worker for led operation
* @ lock - mutex lock for exclusive access
* @ fdbk_op - output feedback mode
@@ -242,7 +265,7 @@ static u8 qpnp_wled_sink_dbg_regs[] = {
* @ ovp_irq - over voltage protection irq
* @ sc_irq - short circuit irq
* @ sc_cnt - short circuit irq count
- * @ avdd_trim_steps_from_center - number of steps to trim from center value
+ * @ avdd_target_voltage_mv - target voltage for AVDD module in mV
* @ ctrl_base - base address for wled ctrl
* @ sink_base - base address for wled sink
* @ ibb_base - base address for IBB(Inverting Buck Boost)
@@ -286,7 +309,7 @@ struct qpnp_wled {
int ovp_irq;
int sc_irq;
u32 sc_cnt;
- u32 avdd_trim_steps_from_center;
+ u32 avdd_target_voltage_mv;
u16 ctrl_base;
u16 sink_base;
u16 mod_freq_khz;
@@ -351,6 +374,24 @@ out:
return rc;
}
+static int qpnp_wled_masked_write_reg(struct qpnp_wled *wled, u8 mask, u8 *data,
+ u16 addr)
+{
+ u8 reg;
+ int rc;
+
+ rc = qpnp_wled_read_reg(wled, &reg, addr);
+ if (rc < 0)
+ return rc;
+
+ reg &= ~mask;
+ reg |= *data & mask;
+
+ rc = qpnp_wled_write_reg(wled, reg, addr);
+
+ return rc;
+}
+
static int qpnp_wled_sec_write_reg(struct qpnp_wled *wled, u8 data, u16 addr)
{
int rc;
@@ -981,6 +1022,73 @@ static irqreturn_t qpnp_wled_sc_irq(int irq, void *_wled)
return IRQ_HANDLED;
}
+static bool is_avdd_trim_adjustment_required(struct qpnp_wled *wled)
+{
+ int rc;
+ u8 reg = 0;
+
+ /*
+ * AVDD trim adjustment is not required for pmicobalt/pm2falcon and not
+ * supported for pmi8994.
+ */
+ if (wled->pmic_rev_id->pmic_subtype == PMICOBALT_SUBTYPE ||
+ wled->pmic_rev_id->pmic_subtype == PM2FALCON_SUBTYPE ||
+ wled->pmic_rev_id->pmic_subtype == PMI8994_SUBTYPE)
+ return false;
+
+ /*
+ * Configure TRIM_REG only if disp_type_amoled and it has
+ * not already been programmed by bootloader.
+ */
+ if (!wled->disp_type_amoled)
+ return false;
+
+ rc = qpnp_wled_read_reg(wled, &reg,
+ QPNP_WLED_CTRL_SPARE_REG(wled->ctrl_base));
+ if (rc < 0)
+ return false;
+
+ return !(reg & QPNP_WLED_AVDD_SET_BIT);
+}
+
+static int qpnp_wled_ovp_config(struct qpnp_wled *wled)
+{
+ int rc, i, *ovp_table;
+ u8 reg;
+
+ /*
+ * Configure the OVP register based on ovp_mv only if display type is
+ * not AMOLED.
+ */
+ if (wled->disp_type_amoled)
+ return 0;
+
+ if (wled->pmic_rev_id->pmic_subtype == PMICOBALT_SUBTYPE ||
+ wled->pmic_rev_id->pmic_subtype == PM2FALCON_SUBTYPE)
+ ovp_table = qpnp_wled_ovp_thresholds_pmicobalt;
+ else
+ ovp_table = qpnp_wled_ovp_thresholds_pmi8994;
+
+ for (i = 0; i < NUM_SUPPORTED_OVP_THRESHOLDS; i++) {
+ if (wled->ovp_mv == ovp_table[i])
+ break;
+ }
+
+ if (i == NUM_SUPPORTED_OVP_THRESHOLDS) {
+ dev_err(&wled->pdev->dev,
+ "Invalid ovp threshold specified in device tree\n");
+ return -EINVAL;
+ }
+
+ reg = i & QPNP_WLED_OVP_MASK;
+ rc = qpnp_wled_masked_write_reg(wled, QPNP_WLED_OVP_MASK, &reg,
+ QPNP_WLED_OVP_REG(wled->ctrl_base));
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
/* Configure WLED registers */
static int qpnp_wled_config(struct qpnp_wled *wled)
{
@@ -1084,47 +1192,51 @@ static int qpnp_wled_config(struct qpnp_wled *wled)
if (rc)
return rc;
- /* Configure the OVP register */
- if (wled->ovp_mv <= QPNP_WLED_OVP_17800_MV) {
- wled->ovp_mv = QPNP_WLED_OVP_17800_MV;
- temp = 3;
- } else if (wled->ovp_mv <= QPNP_WLED_OVP_19400_MV) {
- wled->ovp_mv = QPNP_WLED_OVP_19400_MV;
- temp = 2;
- } else if (wled->ovp_mv <= QPNP_WLED_OVP_29500_MV) {
- wled->ovp_mv = QPNP_WLED_OVP_29500_MV;
- temp = 1;
- } else {
- wled->ovp_mv = QPNP_WLED_OVP_31000_MV;
- temp = 0;
+ rc = qpnp_wled_ovp_config(wled);
+ if (rc < 0) {
+ pr_err("Error in configuring OVP threshold, rc=%d\n", rc);
+ return rc;
}
- rc = qpnp_wled_read_reg(wled, &reg,
- QPNP_WLED_OVP_REG(wled->ctrl_base));
- if (rc < 0)
- return rc;
- reg &= QPNP_WLED_OVP_MASK;
- reg |= temp;
- rc = qpnp_wled_write_reg(wled, reg,
- QPNP_WLED_OVP_REG(wled->ctrl_base));
- if (rc)
- return rc;
+ if (is_avdd_trim_adjustment_required(wled)) {
+ for (i = 0; i < NUM_SUPPORTED_AVDD_VOLTAGES; i++) {
+ if (wled->avdd_target_voltage_mv ==
+ qpnp_wled_avdd_target_voltages[i])
+ break;
+ }
- if (wled->disp_type_amoled) {
- /* Check if wled->avdd_trim_steps_from_center is negative */
- if ((s32)wled->avdd_trim_steps_from_center <
- QPNP_WLED_AVDD_MIN_TRIM_VALUE) {
- wled->avdd_trim_steps_from_center =
- QPNP_WLED_AVDD_MIN_TRIM_VALUE;
- } else if ((s32)wled->avdd_trim_steps_from_center >
- QPNP_WLED_AVDD_MAX_TRIM_VALUE) {
- wled->avdd_trim_steps_from_center =
- QPNP_WLED_AVDD_MAX_TRIM_VALUE;
+ if (i == NUM_SUPPORTED_AVDD_VOLTAGES) {
+ dev_err(&wled->pdev->dev,
+ "Invalid avdd target voltage specified in device tree\n");
+ return -EINVAL;
}
- reg = wled->avdd_trim_steps_from_center +
- QPNP_WLED_AVDD_TRIM_CENTER_VALUE;
- /* Configure avdd trim register */
+ /* Update WLED_OVP register based on desired target voltage */
+ reg = qpnp_wled_ovp_reg_settings[i];
+ rc = qpnp_wled_masked_write_reg(wled, QPNP_WLED_OVP_MASK, &reg,
+ QPNP_WLED_OVP_REG(wled->ctrl_base));
+ if (rc)
+ return rc;
+
+ /* Update WLED_TRIM register based on desired target voltage */
+ rc = qpnp_wled_read_reg(wled, &reg,
+ QPNP_WLED_REF_7P7_TRIM_REG(wled->ctrl_base));
+ if (rc)
+ return rc;
+
+ reg += qpnp_wled_avdd_trim_adjustments[i];
+ if ((s8)reg < QPNP_WLED_AVDD_MIN_TRIM_VAL ||
+ (s8)reg > QPNP_WLED_AVDD_MAX_TRIM_VAL) {
+ dev_info(&wled->pdev->dev,
+ "adjusted trim %d is not within range, capping it\n",
+ (s8)reg);
+ if ((s8)reg < QPNP_WLED_AVDD_MIN_TRIM_VAL)
+ reg = QPNP_WLED_AVDD_MIN_TRIM_VAL;
+ else
+ reg = QPNP_WLED_AVDD_MAX_TRIM_VAL;
+ }
+
+ reg &= QPNP_WLED_7P7_TRIM_MASK;
rc = qpnp_wled_sec_write_reg(wled, reg,
QPNP_WLED_REF_7P7_TRIM_REG(wled->ctrl_base));
if (rc)
@@ -1449,13 +1561,13 @@ static int qpnp_wled_parse_dt(struct qpnp_wled *wled)
return rc;
}
- wled->avdd_trim_steps_from_center = 0;
+ wled->avdd_target_voltage_mv = QPNP_WLED_DFLT_AVDD_MV;
rc = of_property_read_u32(pdev->dev.of_node,
- "qcom,avdd-trim-steps-from-center", &temp_val);
+ "qcom,avdd-target-voltage-mv", &temp_val);
if (!rc) {
- wled->avdd_trim_steps_from_center = temp_val;
+ wled->avdd_target_voltage_mv = temp_val;
} else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read avdd trim steps from center value\n");
+ dev_err(&pdev->dev, "Unable to read avdd target voltage\n");
return rc;
}
}
@@ -1509,13 +1621,17 @@ static int qpnp_wled_parse_dt(struct qpnp_wled *wled)
return rc;
}
- wled->ovp_mv = QPNP_WLED_OVP_29500_MV;
+ if (wled->pmic_rev_id->pmic_subtype == PMICOBALT_SUBTYPE ||
+ wled->pmic_rev_id->pmic_subtype == PM2FALCON_SUBTYPE)
+ wled->ovp_mv = 29600;
+ else
+ wled->ovp_mv = 29500;
rc = of_property_read_u32(pdev->dev.of_node,
"qcom,ovp-mv", &temp_val);
if (!rc) {
wled->ovp_mv = temp_val;
} else if (rc != -EINVAL) {
- dev_err(&pdev->dev, "Unable to read vref\n");
+ dev_err(&pdev->dev, "Unable to read ovp\n");
return rc;
}