From 93aea9eb5ca4157306f41471c46cd0a7454d7f6c Mon Sep 17 00:00:00 2001 From: Anirudh Ghayal Date: Tue, 1 Mar 2016 16:28:56 +0530 Subject: [PATCH] regulator: qpnp-labibb: Add logic to skip second SWIRE command On newer AMOLED panels the second SWIRE command is expected to control the AVDD voltage. However, the PMI8950/PMI8994 IBB module interprets this command for VDISN and incorrectly reduces its voltage. Add DT properties 'qcom,skip-2nd-swire-cmd' to skip the second SWIRE command and 'qcom,swire-2nd-cmd-delay' to explicitly specify the delay between the first and second SWIRE command. CRs-Fixed: 938038 Change-Id: I617a8490784efd760651b3ec8780cc4fd4b17bae Signed-off-by: Anirudh Ghayal Signed-off-by: Subbaraman Narayanamurthy --- .../regulator/qpnp-labibb-regulator.txt | 13 +- drivers/regulator/qpnp-labibb-regulator.c | 127 ++++++++++++++++++ 2 files changed, 139 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/regulator/qpnp-labibb-regulator.txt b/Documentation/devicetree/bindings/regulator/qpnp-labibb-regulator.txt index 134bfd594994..5afaa9bfc36d 100644 --- a/Documentation/devicetree/bindings/regulator/qpnp-labibb-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/qpnp-labibb-regulator.txt @@ -34,6 +34,13 @@ Main node optional properties: if qpnp,qpnp-labibb-mode = "amoled". - qcom,labibb-ttw-force-lab-on: A boolean property which forces LAB to be always on during TTW mode. +- qcom,skip-2nd-swire-cmd: A boolean property which indicates if + the second SWIRE command needs to be skipped. +- qcom,swire-2nd-cmd-delay: A integer value which specifes the + delay in millisecs between the first and second + SWIRE command. If not specified this value + defaults to 20ms. This delay is applied only + if 'qpnp,skip-2nd-swire-cmd' is defined. LAB subnode required properties: @@ -203,12 +210,16 @@ Example: #size-cells = <1>; qpnp,qpnp-labibb-mode = "lcd"; qcom,pmic-revid = <&pmi8994_revid>; + qcom,skip-2nd-swire-cmd; lab_regulator: qcom,lab@de00 { reg = <0xde00 0x100>; reg-names = "lab"; - regulator-name = "lab_reg"; + interrupts = <0x3 0xde 0x0>; + interrupt-names = "lab-vreg-ok"; + + regulator-name = "lab_reg"; regulator-min-microvolt = <4600000>; regulator-max-microvolt = <6000000>; diff --git a/drivers/regulator/qpnp-labibb-regulator.c b/drivers/regulator/qpnp-labibb-regulator.c index 17140fc556a2..9bc42fc3f0a8 100644 --- a/drivers/regulator/qpnp-labibb-regulator.c +++ b/drivers/regulator/qpnp-labibb-regulator.c @@ -15,11 +15,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -247,6 +249,9 @@ #define IBB_DIS_DLY_MASK ((1 << IBB_DIS_DLY_BITS) - 1) #define IBB_WAIT_MBG_OK BIT(2) +/* Constants */ +#define SWIRE_DEFAULT_2ND_CMD_DLY_MS 20 + enum pmic_subtype { PMI8994 = 10, PMI8950 = 17, @@ -280,6 +285,7 @@ enum ibb_mode { IBB_SW_CONTROL_EN, IBB_SW_CONTROL_DIS, IBB_HW_CONTROL, + IBB_HW_SW_CONTROL, }; static const int ibb_discharge_resistor_plan[] = { @@ -437,6 +443,7 @@ struct lab_regulator { struct regulator_dev *rdev; struct mutex lab_mutex; + int lab_vreg_ok_irq; int curr_volt; int min_volt; @@ -480,6 +487,8 @@ struct qpnp_labibb { bool ibb_settings_saved; bool swire_control; bool ttw_force_lab_on; + bool skip_2nd_swire_cmd; + u32 swire_2nd_cmd_delay; }; enum ibb_settings_index { @@ -654,6 +663,8 @@ static int qpnp_ibb_set_mode(struct qpnp_labibb *labibb, enum ibb_mode mode) val = IBB_ENABLE_CTL_MODULE_EN; else if (mode == IBB_HW_CONTROL) val = IBB_ENABLE_CTL_SWIRE_RDY; + else if (mode == IBB_HW_SW_CONTROL) + val = IBB_ENABLE_CTL_MODULE_EN | IBB_ENABLE_CTL_SWIRE_RDY; else if (mode == IBB_SW_CONTROL_DIS) val = 0; else @@ -1556,6 +1567,77 @@ static int qpnp_lab_regulator_set_voltage(struct regulator_dev *rdev, return 0; } +static int qpnp_skip_swire_command(struct qpnp_labibb *labibb) +{ + int rc = 0, retry = 50, dly; + u8 reg; + + do { + /* poll for ibb vreg_ok */ + rc = qpnp_labibb_read(labibb, ®, + labibb->ibb_base + REG_IBB_STATUS1, 1); + if (rc) { + pr_err("Failed to read ibb_status1 reg rc=%d\n", rc); + return rc; + } + if ((reg & IBB_STATUS1_VREG_OK_MASK) == IBB_STATUS1_VREG_OK) + break; + + /* poll delay */ + usleep_range(500, 600); + + } while (--retry); + + if (!retry) { + pr_err("ibb vreg_ok failed to turn-on\n"); + return -EBUSY; + } + + /* move to SW control */ + rc = qpnp_ibb_set_mode(labibb, IBB_SW_CONTROL_EN); + if (rc) { + pr_err("Failed switch to IBB_SW_CONTROL rc=%d\n", rc); + return rc; + } + + /* delay to skip the second swire command */ + dly = labibb->swire_2nd_cmd_delay * 1000; + while (dly / 20000) { + usleep_range(20000, 20010); + dly -= 20000; + } + if (dly) + usleep_range(dly, dly + 10); + + rc = qpnp_ibb_set_mode(labibb, IBB_HW_SW_CONTROL); + if (rc) { + pr_err("Failed switch to IBB_HW_SW_CONTROL rc=%d\n", rc); + return rc; + } + + /* delay for SPMI to SWIRE transition */ + usleep_range(1000, 1100); + + /* Move back to SWIRE control */ + rc = qpnp_ibb_set_mode(labibb, IBB_HW_CONTROL); + if (rc) + pr_err("Failed switch to IBB_HW_CONTROL rc=%d\n", rc); + + return rc; +} + +static irqreturn_t lab_vreg_ok_handler(int irq, void *_labibb) +{ + struct qpnp_labibb *labibb = _labibb; + int rc; + + rc = qpnp_skip_swire_command(labibb); + if (rc) + pr_err("Failed in 'qpnp_skip_swire_command' rc=%d\n", rc); + + return IRQ_HANDLED; +} + static int qpnp_lab_regulator_get_voltage(struct regulator_dev *rdev) { struct qpnp_labibb *labibb = rdev_get_drvdata(rdev); @@ -1707,6 +1789,19 @@ static int register_qpnp_lab_regulator(struct qpnp_labibb *labibb, } } + if (labibb->skip_2nd_swire_cmd) { + rc = devm_request_threaded_irq(labibb->dev, + labibb->lab_vreg.lab_vreg_ok_irq, NULL, + lab_vreg_ok_handler, + IRQF_ONESHOT | IRQF_TRIGGER_RISING, + "lab-vreg-ok", labibb); + if (rc) { + pr_err("Failed to register 'lab-vreg-ok' irq rc=%d\n", + rc); + return rc; + } + } + rc = qpnp_labibb_read(labibb, &val, labibb->ibb_base + REG_IBB_ENABLE_CTL, 1); if (rc) { @@ -2520,6 +2615,21 @@ static int register_qpnp_ibb_regulator(struct qpnp_labibb *labibb, return 0; } +static int qpnp_lab_register_irq(struct device_node *child, + struct qpnp_labibb *labibb) +{ + if (labibb->skip_2nd_swire_cmd) { + labibb->lab_vreg.lab_vreg_ok_irq = + of_irq_get_byname(child, "lab-vreg-ok"); + if (labibb->lab_vreg.lab_vreg_ok_irq < 0) { + pr_err("Invalid lab-vreg-ok irq\n"); + return -EINVAL; + } + } + + return 0; +} + static int qpnp_labibb_check_ttw_supported(struct qpnp_labibb *labibb) { int rc = 0; @@ -2629,6 +2739,17 @@ static int qpnp_labibb_regulator_probe(struct platform_device *pdev) pr_err("Invalid mode for SWIRE control\n"); return -EINVAL; } + if (labibb->swire_control) { + labibb->skip_2nd_swire_cmd = + of_property_read_bool(labibb->dev->of_node, + "qcom,skip-2nd-swire-cmd"); + rc = of_property_read_u32(labibb->dev->of_node, + "qcom,swire-2nd-cmd-delay", + &labibb->swire_2nd_cmd_delay); + if (rc) + labibb->swire_2nd_cmd_delay = + SWIRE_DEFAULT_2ND_CMD_DLY_MS; + } if (of_get_available_child_count(pdev->dev.of_node) == 0) { pr_err("no child nodes\n"); @@ -2653,6 +2774,12 @@ static int qpnp_labibb_regulator_probe(struct platform_device *pdev) switch (type) { case QPNP_LAB_TYPE: labibb->lab_base = base; + rc = qpnp_lab_register_irq(child, labibb); + if (rc) { + pr_err("Failed to register LAB IRQ rc=%d\n", + rc); + goto fail_registration; + } rc = register_qpnp_lab_regulator(labibb, child); if (rc) goto fail_registration;