summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinux Build Service Account <lnxbuild@localhost>2016-10-27 15:48:52 -0700
committerGerrit - the friendly Code Review server <code-review@localhost>2016-10-27 15:48:51 -0700
commit4efc0ba57b7df3e1ab6aa16bec173e3d706cf31d (patch)
treef2226f40a1a131794d0ceb8a8ffeaafa45bbb0ce
parent6250b1993828af53d4a71b24b53d62cbf9a31318 (diff)
parent7a1b6d0276828f270180db40935bb034a3470865 (diff)
Merge "leds: qpnp-wled: Fix configuration of AVDD TRIM and OVP registers"
-rw-r--r--Documentation/devicetree/bindings/leds/leds-qpnp-wled.txt14
-rw-r--r--arch/arm/boot/dts/qcom/msm-pm2falcon.dtsi3
-rw-r--r--arch/arm/boot/dts/qcom/msm-pmi8994.dtsi1
-rw-r--r--arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi3
-rw-r--r--drivers/leds/leds-qpnp-wled.c381
-rw-r--r--drivers/platform/msm/qpnp-revid.c2
-rw-r--r--include/linux/qpnp/qpnp-revid.h4
7 files changed, 284 insertions, 124 deletions
diff --git a/Documentation/devicetree/bindings/leds/leds-qpnp-wled.txt b/Documentation/devicetree/bindings/leds/leds-qpnp-wled.txt
index 8389fe57898a..1ca2b6dd6d5c 100644
--- a/Documentation/devicetree/bindings/leds/leds-qpnp-wled.txt
+++ b/Documentation/devicetree/bindings/leds/leds-qpnp-wled.txt
@@ -12,6 +12,8 @@ Required properties:
- reg-names : names associated with base addresses. It
should be "qpnp-wled-ctrl-base", "qpnp-wled-sink-base",
"qpnp-wled-ibb-base", "qpnp-wled-lab-base".
+- qcom,pmic-revid : phandle of PMIC revid module. This is used to
+ identify the PMIC subtype.
Optional properties for WLED:
- interrupts : Specifies the interrupts associated with WLED. The available
@@ -25,7 +27,12 @@ Optional properties for WLED:
are "wled1", "wled2", "wled3", "wled4" and "auto". default is "auto".
- qcom,vref-mv : maximum reference voltage in mv. default is 350.
- qcom,switch-freq-khz : switch frequency in khz. default is 800.
-- qcom,ovp-mv : over voltage protection value in mv. default is 17800.
+- qcom,ovp-mv : Over voltage protection threshold in mV. Default is
+ 29500. Supported values are:
+ - 31000, 29500, 19400, 17800 for pmi8994/8952/8996.
+ - 31100, 29600, 19600, 18100 for pmicobalt/pm2falcon.
+ Should only be used if qcom,disp-type-amoled is not
+ specified.
- qcom,ilim-ma : maximum current limiter in ma. default is 980.
- qcom,boost-duty-ns : maximum boost duty cycle in ns. default is 104.
- qcom,mod-freq-khz : modulation frequency in khz. default is 9600.
@@ -54,7 +61,10 @@ Optional properties if 'qcom,disp-type-amoled' is mentioned in DT:
- qcom,loop-ea-gm : control the gm for gm stage in control loop. default is 3.
- qcom,loop-comp-res-kohm : control to select the compensation resistor in kohm. default is 320.
- qcom,vref-psm-mv : reference psm voltage in mv. default for amoled is 450.
-- qcom,avdd-trim-steps-from-center : The number of steps to trim the OVP threshold voltage. The possible values can be between -7 to 8.
+- qcom,avdd-target-voltage-mv: The target voltage desired for the AVDD module in mV.
+ The supported values are:
+ 7900, 7600, 7300, 6400, 6100, 5800.
+ If not specified, default value used is 7600.
Example:
qcom,leds@d800 {
diff --git a/arch/arm/boot/dts/qcom/msm-pm2falcon.dtsi b/arch/arm/boot/dts/qcom/msm-pm2falcon.dtsi
index 399892f52b6f..91b4cc351010 100644
--- a/arch/arm/boot/dts/qcom/msm-pm2falcon.dtsi
+++ b/arch/arm/boot/dts/qcom/msm-pm2falcon.dtsi
@@ -236,7 +236,7 @@
qcom,fdbk-output = "auto";
qcom,vref-mv = <350>;
qcom,switch-freq-khz = <800>;
- qcom,ovp-mv = <29500>;
+ qcom,ovp-mv = <29600>;
qcom,ilim-ma = <980>;
qcom,boost-duty-ns = <26>;
qcom,mod-freq-khz = <9600>;
@@ -248,6 +248,7 @@
qcom,en-phase-stag;
qcom,led-strings-list = [00 01 02];
qcom,en-ext-pfet-sc-pro;
+ qcom,pmic-revid = <&pm2falcon_revid>;
status = "ok";
};
diff --git a/arch/arm/boot/dts/qcom/msm-pmi8994.dtsi b/arch/arm/boot/dts/qcom/msm-pmi8994.dtsi
index 96dfed8464e9..bba70329c819 100644
--- a/arch/arm/boot/dts/qcom/msm-pmi8994.dtsi
+++ b/arch/arm/boot/dts/qcom/msm-pmi8994.dtsi
@@ -537,6 +537,7 @@
qcom,en-phase-stag;
qcom,led-strings-list = [00 01 02 03];
qcom,en-ext-pfet-sc-pro;
+ qcom,pmic-revid = <&pmi8994_revid>;
};
pmi8994_haptics: qcom,haptic@c000 {
diff --git a/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi b/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi
index 28d230dfb6bf..ad56f1d3dd74 100644
--- a/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi
+++ b/arch/arm/boot/dts/qcom/msm-pmicobalt.dtsi
@@ -596,7 +596,7 @@
qcom,fdbk-output = "auto";
qcom,vref-mv = <350>;
qcom,switch-freq-khz = <800>;
- qcom,ovp-mv = <29500>;
+ qcom,ovp-mv = <29600>;
qcom,ilim-ma = <980>;
qcom,boost-duty-ns = <26>;
qcom,mod-freq-khz = <9600>;
@@ -608,6 +608,7 @@
qcom,en-phase-stag;
qcom,led-strings-list = [00 01 02 03];
qcom,en-ext-pfet-sc-pro;
+ qcom,pmic-revid = <&pmicobalt_revid>;
};
pmicobalt_haptics: qcom,haptic@c000 {
diff --git a/drivers/leds/leds-qpnp-wled.c b/drivers/leds/leds-qpnp-wled.c
index d9626c29ce76..18d968d3711d 100644
--- a/drivers/leds/leds-qpnp-wled.c
+++ b/drivers/leds/leds-qpnp-wled.c
@@ -25,6 +25,7 @@
#include <linux/err.h>
#include <linux/delay.h>
#include <linux/leds-qpnp-wled.h>
+#include <linux/qpnp/qpnp-revid.h>
#define QPNP_IRQ_FLAGS (IRQF_TRIGGER_RISING | \
IRQF_TRIGGER_FALLING | \
@@ -49,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
@@ -79,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
@@ -98,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
@@ -198,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,
@@ -230,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
@@ -241,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)
@@ -276,14 +300,16 @@ struct qpnp_wled {
struct led_classdev cdev;
struct platform_device *pdev;
struct regmap *regmap;
+ struct pmic_revid_data *pmic_rev_id;
struct work_struct work;
struct mutex lock;
+ struct mutex bus_lock;
enum qpnp_wled_fdbk_op fdbk_op;
enum qpnp_wled_dim_mode dim_mode;
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;
@@ -319,39 +345,79 @@ static int qpnp_wled_read_reg(struct qpnp_wled *wled, u8 *data, u16 addr)
uint val;
rc = regmap_read(wled->regmap, addr, &val);
- if (rc < 0)
+ if (rc < 0) {
dev_err(&wled->pdev->dev,
"Error reading address: %x(%d)\n", addr, rc);
+ return rc;
+ }
+
*data = (u8)val;
- return rc;
+ return 0;
}
/* helper to write a pmic register */
-static int qpnp_wled_write_reg(struct qpnp_wled *wled, u8 *data, u16 addr)
+static int qpnp_wled_write_reg(struct qpnp_wled *wled, u8 data, u16 addr)
{
int rc;
- rc = regmap_write(wled->regmap, addr, *data);
+ mutex_lock(&wled->bus_lock);
+ rc = regmap_write(wled->regmap, addr, data);
+ if (rc < 0) {
+ dev_err(&wled->pdev->dev, "Error writing address: %x(%d)\n",
+ addr, rc);
+ goto out;
+ }
+
+ dev_dbg(&wled->pdev->dev, "wrote: WLED_0x%x = 0x%x\n", addr, data);
+out:
+ mutex_unlock(&wled->bus_lock);
+ 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)
- dev_err(&wled->pdev->dev,
- "Error writing address: %x(%d)\n", addr, rc);
+ return rc;
+
+ reg &= ~mask;
+ reg |= *data & mask;
- dev_dbg(&wled->pdev->dev, "write: WLED_0x%x = 0x%x\n", addr, *data);
+ rc = qpnp_wled_write_reg(wled, reg, addr);
return rc;
}
-static int qpnp_wled_sec_access(struct qpnp_wled *wled, u16 base_addr)
+static int qpnp_wled_sec_write_reg(struct qpnp_wled *wled, u8 data, u16 addr)
{
int rc;
u8 reg = QPNP_WLED_SEC_UNLOCK;
+ u16 base_addr = addr & 0xFF00;
- rc = qpnp_wled_write_reg(wled, &reg,
- QPNP_WLED_SEC_ACCESS_REG(base_addr));
- if (rc)
- return rc;
+ mutex_lock(&wled->bus_lock);
+ rc = regmap_write(wled->regmap, QPNP_WLED_SEC_ACCESS_REG(base_addr),
+ reg);
+ if (rc < 0) {
+ dev_err(&wled->pdev->dev, "Error writing address: %x(%d)\n",
+ QPNP_WLED_SEC_ACCESS_REG(base_addr), rc);
+ goto out;
+ }
- return 0;
+ rc = regmap_write(wled->regmap, addr, data);
+ if (rc < 0) {
+ dev_err(&wled->pdev->dev, "Error writing address: %x(%d)\n",
+ addr, rc);
+ goto out;
+ }
+
+ dev_dbg(&wled->pdev->dev, "wrote: WLED_0x%x = 0x%x\n", addr, data);
+out:
+ mutex_unlock(&wled->bus_lock);
+ return rc;
}
static int qpnp_wled_sync_reg_toggle(struct qpnp_wled *wled)
@@ -361,7 +427,7 @@ static int qpnp_wled_sync_reg_toggle(struct qpnp_wled *wled)
/* sync */
reg = QPNP_WLED_SYNC;
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_SYNC_REG(wled->sink_base));
if (rc < 0)
return rc;
@@ -371,7 +437,7 @@ static int qpnp_wled_sync_reg_toggle(struct qpnp_wled *wled)
wled->cons_sync_write_delay_us + 1);
reg = QPNP_WLED_SYNC_RESET;
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_SYNC_REG(wled->sink_base));
if (rc < 0)
return rc;
@@ -388,7 +454,7 @@ static int qpnp_wled_set_level(struct qpnp_wled *wled, int level)
/* set brightness registers */
for (i = 0; i < wled->num_strings; i++) {
reg = level & QPNP_WLED_BRIGHT_LSB_MASK;
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_BRIGHT_LSB_REG(wled->sink_base,
wled->strings[i]));
if (rc < 0)
@@ -396,7 +462,7 @@ static int qpnp_wled_set_level(struct qpnp_wled *wled, int level)
reg = level >> QPNP_WLED_BRIGHT_MSB_SHIFT;
reg = reg & QPNP_WLED_BRIGHT_MSB_MASK;
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_BRIGHT_MSB_REG(wled->sink_base,
wled->strings[i]));
if (rc < 0)
@@ -421,7 +487,7 @@ static int qpnp_wled_module_en(struct qpnp_wled *wled,
/* disable OVP fault interrupt */
if (state) {
reg = QPNP_WLED_INT_EN_SET_OVP_EN;
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_INT_EN_CLR(base_addr));
if (rc)
return rc;
@@ -433,7 +499,7 @@ static int qpnp_wled_module_en(struct qpnp_wled *wled,
return rc;
reg &= QPNP_WLED_MODULE_EN_MASK;
reg |= (state << QPNP_WLED_MODULE_EN_SHIFT);
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_MODULE_EN_REG(base_addr));
if (rc)
return rc;
@@ -442,7 +508,7 @@ static int qpnp_wled_module_en(struct qpnp_wled *wled,
if (state && (wled->ovp_irq > 0)) {
udelay(QPNP_WLED_OVP_FLT_SLEEP_US);
reg = QPNP_WLED_INT_EN_SET_OVP_EN;
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_INT_EN_SET(base_addr));
if (rc)
return rc;
@@ -678,7 +744,7 @@ static ssize_t qpnp_wled_dim_mode_store(struct device *dev,
reg |= temp;
}
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_MOD_REG(wled->sink_base));
if (rc)
return rc;
@@ -722,7 +788,7 @@ static ssize_t qpnp_wled_fs_curr_ua_store(struct device *dev,
reg &= QPNP_WLED_FS_CURR_MASK;
temp = data / QPNP_WLED_FS_CURR_STEP_UA;
reg |= temp;
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_FS_CURR_REG(wled->sink_base,
wled->strings[i]));
if (rc)
@@ -838,11 +904,7 @@ static int qpnp_wled_set_disp(struct qpnp_wled *wled, u16 base_addr)
reg &= QPNP_WLED_DISP_SEL_MASK;
reg |= (wled->disp_type_amoled << QPNP_WLED_DISP_SEL_SHIFT);
- rc = qpnp_wled_sec_access(wled, base_addr);
- if (rc)
- return rc;
-
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_sec_write_reg(wled, reg,
QPNP_WLED_DISP_SEL_REG(base_addr));
if (rc)
return rc;
@@ -863,7 +925,7 @@ static int qpnp_wled_set_disp(struct qpnp_wled *wled, u16 base_addr)
reg |= ((wled->vref_psm_mv - QPNP_WLED_VREF_PSM_MIN_MV)/
QPNP_WLED_VREF_PSM_STEP_MV);
reg |= QPNP_WLED_PSM_CTRL_OVERWRITE;
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_PSM_CTRL_REG(wled->ctrl_base));
if (rc)
return rc;
@@ -887,7 +949,7 @@ static int qpnp_wled_set_disp(struct qpnp_wled *wled, u16 base_addr)
QPNP_WLED_LOOP_COMP_RES_MIN_KOHM)/
QPNP_WLED_LOOP_COMP_RES_STEP_KOHM);
reg |= QPNP_WLED_VLOOP_COMP_RES_OVERWRITE;
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_VLOOP_COMP_RES_REG(wled->ctrl_base));
if (rc)
return rc;
@@ -905,7 +967,7 @@ static int qpnp_wled_set_disp(struct qpnp_wled *wled, u16 base_addr)
reg &= QPNP_WLED_VLOOP_COMP_GM_MASK;
reg |= (wled->loop_ea_gm | QPNP_WLED_VLOOP_COMP_GM_OVERWRITE);
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_VLOOP_COMP_GM_REG(wled->ctrl_base));
if (rc)
return rc;
@@ -916,12 +978,8 @@ static int qpnp_wled_set_disp(struct qpnp_wled *wled, u16 base_addr)
if (rc < 0)
return rc;
- rc = qpnp_wled_sec_access(wled, base_addr);
- if (rc)
- return rc;
-
reg |= QPNP_WLED_TEST4_EN_IIND_UP;
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_sec_write_reg(wled, reg,
QPNP_WLED_TEST4_REG(base_addr));
if (rc)
return rc;
@@ -929,12 +987,8 @@ static int qpnp_wled_set_disp(struct qpnp_wled *wled, u16 base_addr)
/*
* enable VREF_UP to avoid false ovp on low brightness for LCD
*/
- rc = qpnp_wled_sec_access(wled, base_addr);
- if (rc)
- return rc;
-
reg = QPNP_WLED_TEST4_EN_VREF_UP;
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_sec_write_reg(wled, reg,
QPNP_WLED_TEST4_REG(base_addr));
if (rc)
return rc;
@@ -968,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)
{
@@ -986,7 +1107,7 @@ static int qpnp_wled_config(struct qpnp_wled *wled)
return rc;
reg &= QPNP_WLED_FDBK_OP_MASK;
reg |= wled->fdbk_op;
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_FDBK_OP_REG(wled->ctrl_base));
if (rc)
return rc;
@@ -1004,7 +1125,7 @@ static int qpnp_wled_config(struct qpnp_wled *wled)
reg &= QPNP_WLED_VREF_MASK;
temp = wled->vref_mv - QPNP_WLED_VREF_MIN_MV;
reg |= (temp / QPNP_WLED_VREF_STEP_MV);
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_VREF_REG(wled->ctrl_base));
if (rc)
return rc;
@@ -1024,7 +1145,7 @@ static int qpnp_wled_config(struct qpnp_wled *wled)
reg &= QPNP_WLED_ILIM_MASK;
reg |= temp;
reg |= QPNP_WLED_ILIM_OVERWRITE;
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_ILIM_REG(wled->ctrl_base));
if (rc)
return rc;
@@ -1032,7 +1153,7 @@ 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, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_SOFTSTART_RAMP_DLY(wled->ctrl_base));
if (rc)
return rc;
@@ -1049,7 +1170,7 @@ static int qpnp_wled_config(struct qpnp_wled *wled)
return rc;
reg &= QPNP_WLED_BOOST_DUTY_MASK;
reg |= (wled->boost_duty_ns / QPNP_WLED_BOOST_DUTY_STEP_NS);
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_BOOST_DUTY_REG(wled->ctrl_base));
if (rc)
return rc;
@@ -1066,57 +1187,57 @@ static int qpnp_wled_config(struct qpnp_wled *wled)
return rc;
reg &= QPNP_WLED_SWITCH_FREQ_MASK;
reg |= (temp | QPNP_WLED_SWITCH_FREQ_OVERWRITE);
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_SWITCH_FREQ_REG(wled->ctrl_base));
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) {
- /* Configure avdd trim register */
- rc = qpnp_wled_sec_access(wled, wled->ctrl_base);
+ if (i == NUM_SUPPORTED_AVDD_VOLTAGES) {
+ dev_err(&wled->pdev->dev,
+ "Invalid avdd target voltage specified in device tree\n");
+ return -EINVAL;
+ }
+
+ /* 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;
- /* 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;
+ 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 = wled->avdd_trim_steps_from_center +
- QPNP_WLED_AVDD_TRIM_CENTER_VALUE;
- rc = qpnp_wled_write_reg(wled, &reg,
+ 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)
return rc;
@@ -1166,7 +1287,7 @@ static int qpnp_wled_config(struct qpnp_wled *wled)
reg |= wled->dim_mode;
}
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_MOD_REG(wled->sink_base));
if (rc)
return rc;
@@ -1184,7 +1305,7 @@ static int qpnp_wled_config(struct qpnp_wled *wled)
reg &= QPNP_WLED_HYB_THRES_MASK;
temp = fls(wled->hyb_thres / QPNP_WLED_HYB_THRES_MIN) - 1;
reg |= temp;
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_HYB_THRES_REG(wled->sink_base));
if (rc)
return rc;
@@ -1195,17 +1316,14 @@ static int qpnp_wled_config(struct qpnp_wled *wled)
else
reg = QPNP_WLED_SINK_TEST5_HYB;
- rc = qpnp_wled_sec_access(wled, wled->sink_base);
- if (rc)
- return rc;
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_sec_write_reg(wled, reg,
QPNP_WLED_SINK_TEST5_REG(wled->sink_base));
if (rc)
return rc;
/* disable all current sinks and enable selected strings */
reg = 0x00;
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_CURR_SINK_REG(wled->sink_base));
for (i = 0; i < wled->num_strings; i++) {
@@ -1228,7 +1346,7 @@ static int qpnp_wled_config(struct qpnp_wled *wled)
else
reg |= ~QPNP_WLED_GATE_DRV_MASK;
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_MOD_EN_REG(wled->sink_base,
wled->strings[i]));
if (rc)
@@ -1246,7 +1364,7 @@ static int qpnp_wled_config(struct qpnp_wled *wled)
reg &= QPNP_WLED_SYNC_DLY_MASK;
temp = wled->sync_dly_us / QPNP_WLED_SYNC_DLY_STEP_US;
reg |= temp;
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_SYNC_DLY_REG(wled->sink_base,
wled->strings[i]));
if (rc)
@@ -1264,7 +1382,7 @@ static int qpnp_wled_config(struct qpnp_wled *wled)
reg &= QPNP_WLED_FS_CURR_MASK;
temp = wled->fs_curr_ua / QPNP_WLED_FS_CURR_STEP_UA;
reg |= temp;
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_FS_CURR_REG(wled->sink_base,
wled->strings[i]));
if (rc)
@@ -1278,7 +1396,7 @@ static int qpnp_wled_config(struct qpnp_wled *wled)
return rc;
reg &= QPNP_WLED_CABC_MASK;
reg |= (wled->en_cabc << QPNP_WLED_CABC_SHIFT);
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_CABC_REG(wled->sink_base,
wled->strings[i]));
if (rc)
@@ -1291,7 +1409,7 @@ static int qpnp_wled_config(struct qpnp_wled *wled)
return rc;
temp = wled->strings[i] + QPNP_WLED_CURR_SINK_SHIFT;
reg |= (1 << temp);
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_CURR_SINK_REG(wled->sink_base));
if (rc)
return rc;
@@ -1348,18 +1466,14 @@ static int qpnp_wled_config(struct qpnp_wled *wled)
if (wled->disp_type_amoled)
reg |= QPNP_WLED_SC_PRO_EN_DSCHGR;
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_SC_PRO_REG(wled->ctrl_base));
if (rc)
return rc;
if (wled->en_ext_pfet_sc_pro) {
- rc = qpnp_wled_sec_access(wled, wled->ctrl_base);
- if (rc)
- return rc;
-
reg = QPNP_WLED_EXT_FET_DTEST2;
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_sec_write_reg(wled, reg,
QPNP_WLED_TEST1_REG(wled->ctrl_base));
if (rc)
return rc;
@@ -1378,7 +1492,7 @@ static int qpnp_wled_config(struct qpnp_wled *wled)
temp = fls(wled->sc_deb_cycles) - QPNP_WLED_SC_DEB_CYCLES_SUB;
reg |= (temp << 1);
- rc = qpnp_wled_write_reg(wled, &reg,
+ rc = qpnp_wled_write_reg(wled, reg,
QPNP_WLED_SC_PRO_REG(wled->ctrl_base));
if (rc)
return rc;
@@ -1447,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;
}
}
@@ -1507,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;
}
@@ -1638,6 +1756,7 @@ static int qpnp_wled_parse_dt(struct qpnp_wled *wled)
static int qpnp_wled_probe(struct platform_device *pdev)
{
struct qpnp_wled *wled;
+ struct device_node *revid_node;
int rc = 0, i;
const __be32 *prop;
@@ -1652,6 +1771,27 @@ static int qpnp_wled_probe(struct platform_device *pdev)
wled->pdev = pdev;
+ revid_node = of_parse_phandle(pdev->dev.of_node, "qcom,pmic-revid", 0);
+ if (!revid_node) {
+ pr_err("Missing qcom,pmic-revid property - driver failed\n");
+ return -EINVAL;
+ }
+
+ wled->pmic_rev_id = get_revid_data(revid_node);
+ if (IS_ERR_OR_NULL(wled->pmic_rev_id)) {
+ pr_err("Unable to get pmic_revid rc=%ld\n",
+ PTR_ERR(wled->pmic_rev_id));
+ /*
+ * the revid peripheral must be registered, any failure
+ * here only indicates that the rev-id module has not
+ * probed yet.
+ */
+ return -EPROBE_DEFER;
+ }
+
+ pr_debug("PMIC subtype %d Digital major %d\n",
+ 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);
if (!prop) {
@@ -1676,6 +1816,7 @@ static int qpnp_wled_probe(struct platform_device *pdev)
return rc;
}
+ mutex_init(&wled->bus_lock);
rc = qpnp_wled_config(wled);
if (rc) {
dev_err(&pdev->dev, "wled config failed\n");
diff --git a/drivers/platform/msm/qpnp-revid.c b/drivers/platform/msm/qpnp-revid.c
index 78e685f789cd..1ef8ebe3ed7d 100644
--- a/drivers/platform/msm/qpnp-revid.c
+++ b/drivers/platform/msm/qpnp-revid.c
@@ -56,6 +56,8 @@ static const char *const pmic_names[] = {
[PMICOBALT_SUBTYPE] = "PMICOBALT",
[PM8005_SUBTYPE] = "PM8005",
[PM8937_SUBTYPE] = "PM8937",
+ [PM2FALCON_SUBTYPE] = "PM2FALCON",
+ [PMFALCON_SUBTYPE] = "PMFALCON",
[PMI8937_SUBTYPE] = "PMI8937",
};
diff --git a/include/linux/qpnp/qpnp-revid.h b/include/linux/qpnp/qpnp-revid.h
index 7c12823894df..652d68ac63bf 100644
--- a/include/linux/qpnp/qpnp-revid.h
+++ b/include/linux/qpnp/qpnp-revid.h
@@ -177,6 +177,10 @@
/* PMICOBALT */
#define PMICOBALT_SUBTYPE 0x15
+/* PMFALCON */
+#define PM2FALCON_SUBTYPE 0x1A
+#define PMFALCON_SUBTYPE 0x1B
+
#define PMICOBALT_V1P0_REV1 0x00
#define PMICOBALT_V1P0_REV2 0x00
#define PMICOBALT_V1P0_REV3 0x00