Currently, WLED driver supports only WLED4 peripherals that is present
on pmi8998 and pm660L. This patch series converts the existing WLED4
bindings from .txt to .yaml format and adds the support for WLED5 peripheral
that is present on PM8150L.
PM8150L WLED supports the following.
- Two modulators and each sink can use any of the modulator
- Multiple CABC selection options
- Multiple brightness width selection (12 bits to 15 bits)
Changes from V1:
- Rebased on top of the below commit.
backlight: qcom-wled: Fix unsigned comparison to zero
Changes from V2:
- Addressed Bjorn's comments by splitting the WLED4 changes
in a seperate patch.
- Added WLED5 auto calibration support
Changes from V3:
- Addressed comments from Daniel Thompson and Rob Herring
- Seperated the WLED5 bindings from the driver changes
- Squashed wled5 auto string detection and wled5 basic changes
to avoid the NULL callback function pointer issue.
Changes from V4:
- Addressed the yaml formatting comments from Rob Herring.
- Addressed the comments from Daniel Thompson on the below patch
"backlight: qcom-wled: Add callback functions"
Kiran Gunda (3):
backlight: qcom-wled: convert the wled bindings to .yaml format
backlight: qcom-wled: Add callback functions
backlight: qcom-wled: Add WLED5 bindings
Subbaraman Narayanamurthy (1):
backlight: qcom-wled: Add support for WLED5 peripheral that is present
on PM8150L PMICs
.../bindings/leds/backlight/qcom-wled.txt | 154 -----
.../bindings/leds/backlight/qcom-wled.yaml | 255 ++++++++
drivers/video/backlight/qcom-wled.c | 659 ++++++++++++++++++---
3 files changed, 841 insertions(+), 227 deletions(-)
delete mode 100644 Documentation/devicetree/bindings/leds/backlight/qcom-wled.txt
create mode 100644 Documentation/devicetree/bindings/leds/backlight/qcom-wled.yaml
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
From: Subbaraman Narayanamurthy <[email protected]>
PM8150L WLED supports the following:
- Two modulators and each sink can use any of the modulator
- Multiple CABC selection options from which one can be selected/enabled
- Multiple brightness width selection (12 bits to 15 bits)
Signed-off-by: Subbaraman Narayanamurthy <[email protected]>
Signed-off-by: Kiran Gunda <[email protected]>
---
drivers/video/backlight/qcom-wled.c | 443 +++++++++++++++++++++++++++++++++++-
1 file changed, 442 insertions(+), 1 deletion(-)
diff --git a/drivers/video/backlight/qcom-wled.c b/drivers/video/backlight/qcom-wled.c
index a6ddaa9..3a57011 100644
--- a/drivers/video/backlight/qcom-wled.c
+++ b/drivers/video/backlight/qcom-wled.c
@@ -19,12 +19,15 @@
#define WLED_DEFAULT_BRIGHTNESS 2048
#define WLED_SOFT_START_DLY_US 10000
#define WLED3_SINK_REG_BRIGHT_MAX 0xFFF
+#define WLED5_SINK_REG_BRIGHT_MAX_12B 0xFFF
+#define WLED5_SINK_REG_BRIGHT_MAX_15B 0x7FFF
/* WLED3/WLED4 control registers */
#define WLED3_CTRL_REG_FAULT_STATUS 0x08
#define WLED3_CTRL_REG_ILIM_FAULT_BIT BIT(0)
#define WLED3_CTRL_REG_OVP_FAULT_BIT BIT(1)
#define WLED4_CTRL_REG_SC_FAULT_BIT BIT(2)
+#define WLED5_CTRL_REG_OVP_PRE_ALARM_BIT BIT(4)
#define WLED3_CTRL_REG_INT_RT_STS 0x10
#define WLED3_CTRL_REG_OVP_FAULT_STATUS BIT(1)
@@ -40,6 +43,7 @@
#define WLED3_CTRL_REG_OVP 0x4d
#define WLED3_CTRL_REG_OVP_MASK GENMASK(1, 0)
+#define WLED5_CTRL_REG_OVP_MASK GENMASK(3, 0)
#define WLED3_CTRL_REG_ILIMIT 0x4e
#define WLED3_CTRL_REG_ILIMIT_MASK GENMASK(2, 0)
@@ -101,6 +105,44 @@
#define WLED4_SINK_REG_BRIGHT(n) (0x57 + (n * 0x10))
+/* WLED5 specific control registers */
+#define WLED5_CTRL_REG_OVP_INT_CTL 0x5f
+#define WLED5_CTRL_REG_OVP_INT_TIMER_MASK GENMASK(2, 0)
+
+/* WLED5 specific sink registers */
+#define WLED5_SINK_REG_MOD_A_EN 0x50
+#define WLED5_SINK_REG_MOD_B_EN 0x60
+#define WLED5_SINK_REG_MOD_EN_MASK BIT(7)
+
+#define WLED5_SINK_REG_MOD_A_SRC_SEL 0x51
+#define WLED5_SINK_REG_MOD_B_SRC_SEL 0x61
+#define WLED5_SINK_REG_MOD_SRC_SEL_HIGH 0
+#define WLED5_SINK_REG_MOD_SRC_SEL_EXT 0x03
+#define WLED5_SINK_REG_MOD_SRC_SEL_MASK GENMASK(1, 0)
+
+#define WLED5_SINK_REG_MOD_A_BRIGHTNESS_WIDTH_SEL 0x52
+#define WLED5_SINK_REG_MOD_B_BRIGHTNESS_WIDTH_SEL 0x62
+#define WLED5_SINK_REG_BRIGHTNESS_WIDTH_12B 0
+#define WLED5_SINK_REG_BRIGHTNESS_WIDTH_15B 1
+
+#define WLED5_SINK_REG_MOD_A_BRIGHTNESS_LSB 0x53
+#define WLED5_SINK_REG_MOD_A_BRIGHTNESS_MSB 0x54
+#define WLED5_SINK_REG_MOD_B_BRIGHTNESS_LSB 0x63
+#define WLED5_SINK_REG_MOD_B_BRIGHTNESS_MSB 0x64
+
+#define WLED5_SINK_REG_MOD_SYNC_BIT 0x65
+#define WLED5_SINK_REG_SYNC_MOD_A_BIT BIT(0)
+#define WLED5_SINK_REG_SYNC_MOD_B_BIT BIT(1)
+#define WLED5_SINK_REG_SYNC_MASK GENMASK(1, 0)
+
+/* WLED5 specific per-'string' registers below */
+#define WLED5_SINK_REG_STR_FULL_SCALE_CURR(n) (0x72 + (n * 0x10))
+
+#define WLED5_SINK_REG_STR_SRC_SEL(n) (0x73 + (n * 0x10))
+#define WLED5_SINK_REG_SRC_SEL_MOD_A 0
+#define WLED5_SINK_REG_SRC_SEL_MOD_B 1
+#define WLED5_SINK_REG_SRC_SEL_MASK GENMASK(1, 0)
+
struct wled_var_cfg {
const u32 *values;
u32 (*fn)(u32);
@@ -125,6 +167,8 @@ struct wled_config {
u32 num_strings;
u32 string_i_limit;
u32 enabled_strings[WLED_MAX_STRINGS];
+ u32 mod_sel;
+ u32 cabc_sel;
bool cs_out_en;
bool ext_gen;
bool cabc;
@@ -150,6 +194,7 @@ struct wled {
u32 version;
bool disabled_by_short;
bool has_short_detect;
+ bool cabc_disabled;
int short_irq;
int ovp_irq;
@@ -184,6 +229,27 @@ struct wled {
bool (*wled_auto_detection_required)(struct wled *wled);
};
+enum wled5_mod_sel {
+ MOD_A,
+ MOD_B,
+ MOD_MAX,
+};
+
+static const u8 wled5_brightness_reg[MOD_MAX] = {
+ [MOD_A] = WLED5_SINK_REG_MOD_A_BRIGHTNESS_LSB,
+ [MOD_B] = WLED5_SINK_REG_MOD_B_BRIGHTNESS_LSB,
+};
+
+static const u8 wled5_src_sel_reg[MOD_MAX] = {
+ [MOD_A] = WLED5_SINK_REG_MOD_A_SRC_SEL,
+ [MOD_B] = WLED5_SINK_REG_MOD_B_SRC_SEL,
+};
+
+static const u8 wled5_brt_wid_sel_reg[MOD_MAX] = {
+ [MOD_A] = WLED5_SINK_REG_MOD_A_BRIGHTNESS_WIDTH_SEL,
+ [MOD_B] = WLED5_SINK_REG_MOD_B_BRIGHTNESS_WIDTH_SEL,
+};
+
static int wled3_set_brightness(struct wled *wled, u16 brightness)
{
int rc, i;
@@ -225,6 +291,25 @@ static int wled4_set_brightness(struct wled *wled, u16 brightness)
return 0;
}
+static int wled5_set_brightness(struct wled *wled, u16 brightness)
+{
+ int rc, offset;
+ u16 low_limit = wled->max_brightness * 1 / 1000;
+ u8 v[2];
+
+ /* WLED5's lower limit is 0.1% */
+ if (brightness < low_limit)
+ brightness = low_limit;
+
+ v[0] = brightness & 0xff;
+ v[1] = (brightness >> 8) & 0x7f;
+
+ offset = wled5_brightness_reg[wled->cfg.mod_sel];
+ rc = regmap_bulk_write(wled->regmap, wled->sink_addr + offset,
+ v, 2);
+ return rc;
+}
+
static void wled_ovp_work(struct work_struct *work)
{
struct wled *wled = container_of(work,
@@ -282,6 +367,24 @@ static int wled3_sync_toggle(struct wled *wled)
return rc;
}
+static int wled5_sync_toggle(struct wled *wled)
+{
+ int rc;
+ u8 val;
+
+ val = (wled->cfg.mod_sel == MOD_A) ? WLED5_SINK_REG_SYNC_MOD_A_BIT :
+ WLED5_SINK_REG_SYNC_MOD_B_BIT;
+ rc = regmap_update_bits(wled->regmap,
+ wled->sink_addr + WLED5_SINK_REG_MOD_SYNC_BIT,
+ WLED5_SINK_REG_SYNC_MASK, val);
+ if (rc < 0)
+ return rc;
+
+ return regmap_update_bits(wled->regmap,
+ wled->sink_addr + WLED5_SINK_REG_MOD_SYNC_BIT,
+ WLED5_SINK_REG_SYNC_MASK, 0);
+}
+
static int wled4_ovp_fault_status(struct wled *wled, bool *fault_set)
{
int rc;
@@ -317,11 +420,67 @@ static int wled4_ovp_fault_status(struct wled *wled, bool *fault_set)
return rc;
}
+static int wled5_ovp_fault_status(struct wled *wled, bool *fault_set)
+{
+ int rc;
+ u32 int_rt_sts, fault_sts;
+
+ *fault_set = false;
+ rc = regmap_read(wled->regmap,
+ wled->ctrl_addr + WLED3_CTRL_REG_INT_RT_STS,
+ &int_rt_sts);
+ if (rc < 0) {
+ dev_err(wled->dev, "Failed to read INT_RT_STS rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = regmap_read(wled->regmap,
+ wled->ctrl_addr + WLED3_CTRL_REG_FAULT_STATUS,
+ &fault_sts);
+ if (rc < 0) {
+ dev_err(wled->dev, "Failed to read FAULT_STATUS rc=%d\n", rc);
+ return rc;
+ }
+
+ if (int_rt_sts & WLED3_CTRL_REG_OVP_FAULT_STATUS)
+ *fault_set = true;
+
+ if (fault_sts & (WLED3_CTRL_REG_OVP_FAULT_BIT |
+ WLED5_CTRL_REG_OVP_PRE_ALARM_BIT))
+ *fault_set = true;
+
+ if (*fault_set)
+ dev_dbg(wled->dev, "WLED OVP fault detected, int_rt_sts=0x%x fault_sts=0x%x\n",
+ int_rt_sts, fault_sts);
+
+ return rc;
+}
+
static int wled4_ovp_delay(struct wled *wled)
{
return WLED_SOFT_START_DLY_US;
}
+static int wled5_ovp_delay(struct wled *wled)
+{
+ int rc, delay_us;
+ u32 val;
+ u8 ovp_timer_ms[8] = {1, 2, 4, 8, 12, 16, 20, 24};
+
+ /* For WLED5, get the delay based on OVP timer */
+ rc = regmap_read(wled->regmap, wled->ctrl_addr +
+ WLED5_CTRL_REG_OVP_INT_CTL, &val);
+ if (rc < 0)
+ delay_us =
+ ovp_timer_ms[val & WLED5_CTRL_REG_OVP_INT_TIMER_MASK] * 1000;
+ else
+ delay_us = 2 * WLED_SOFT_START_DLY_US;
+
+ dev_dbg(wled->dev, "delay_time_us: %d\n", delay_us);
+
+ return delay_us;
+}
+
static int wled_update_status(struct backlight_device *bl)
{
struct wled *wled = bl_get_data(bl);
@@ -384,6 +543,29 @@ static int wled4_cabc_config(struct wled *wled, bool enable)
return 0;
}
+static int wled5_cabc_config(struct wled *wled, bool enable)
+{
+ int rc, offset;
+ u8 reg;
+
+ if (wled->cabc_disabled)
+ return 0;
+
+ reg = enable ? wled->cfg.cabc_sel : 0;
+ offset = wled5_src_sel_reg[wled->cfg.mod_sel];
+ rc = regmap_update_bits(wled->regmap, wled->sink_addr + offset,
+ WLED5_SINK_REG_MOD_SRC_SEL_MASK, reg);
+ if (rc < 0) {
+ pr_err("Error in configuring CABC rc=%d\n", rc);
+ return rc;
+ }
+
+ if (!wled->cfg.cabc_sel)
+ wled->cabc_disabled = true;
+
+ return 0;
+}
+
#define WLED_SHORT_DLY_MS 20
#define WLED_SHORT_CNT_MAX 5
#define WLED_SHORT_RESET_CNT_DLY_US USEC_PER_SEC
@@ -615,6 +797,7 @@ static void wled_auto_string_detection(struct wled *wled)
#define WLED_AUTO_DETECT_OVP_COUNT 5
#define WLED_AUTO_DETECT_CNT_DLY_US USEC_PER_SEC
+
static bool wled4_auto_detection_required(struct wled *wled)
{
s64 elapsed_time_us;
@@ -648,6 +831,46 @@ static bool wled4_auto_detection_required(struct wled *wled)
return false;
}
+static bool wled5_auto_detection_required(struct wled *wled)
+{
+ s64 elapsed_time_us;
+
+ if (!wled->cfg.auto_detection_enabled)
+ return false;
+
+ /*
+ * Check if the OVP fault was an occasional one
+ * or if it's firing continuously, the latter qualifies
+ * for an auto-detection check.
+ */
+ if (!wled->auto_detection_ovp_count) {
+ wled->start_ovp_fault_time = ktime_get();
+ wled->auto_detection_ovp_count++;
+ } else {
+ /*
+ * WLED5 has OVP fault density interrupt configuration i.e. to
+ * count the number of OVP alarms for a certain duration before
+ * triggering OVP fault interrupt. By default, number of OVP
+ * fault events counted before an interrupt is fired is 32 and
+ * the time interval is 12 ms. If we see more than one OVP fault
+ * interrupt, then that should qualify for a real OVP fault
+ * condition to run auto calibration algorithm.
+ */
+
+ if (wled->auto_detection_ovp_count > 1) {
+ elapsed_time_us = ktime_us_delta(ktime_get(),
+ wled->start_ovp_fault_time);
+ wled->auto_detection_ovp_count = 0;
+ dev_dbg(wled->dev, "Elapsed time: %lld us\n",
+ elapsed_time_us);
+ return true;
+ }
+ wled->auto_detection_ovp_count++;
+ }
+
+ return false;
+}
+
static int wled_auto_detection_at_init(struct wled *wled)
{
int rc;
@@ -923,6 +1146,115 @@ static const struct wled_config wled4_config_defaults = {
.auto_detection_enabled = false,
};
+static int wled5_setup(struct wled *wled)
+{
+ int rc, temp, i, j;
+ u8 sink_en = 0;
+ u16 addr;
+ u32 val;
+
+ rc = regmap_update_bits(wled->regmap,
+ wled->ctrl_addr + WLED3_CTRL_REG_OVP,
+ WLED5_CTRL_REG_OVP_MASK, wled->cfg.ovp);
+ if (rc < 0)
+ return rc;
+
+ rc = regmap_update_bits(wled->regmap,
+ wled->ctrl_addr + WLED3_CTRL_REG_ILIMIT,
+ WLED3_CTRL_REG_ILIMIT_MASK,
+ wled->cfg.boost_i_limit);
+ if (rc < 0)
+ return rc;
+
+ rc = regmap_update_bits(wled->regmap,
+ wled->ctrl_addr + WLED3_CTRL_REG_FREQ,
+ WLED3_CTRL_REG_FREQ_MASK,
+ wled->cfg.switch_freq);
+ if (rc < 0)
+ return rc;
+
+ /* Per sink/string configuration */
+ for (i = 0; i < wled->cfg.num_strings; ++i) {
+ j = wled->cfg.enabled_strings[i];
+ addr = wled->sink_addr +
+ WLED4_SINK_REG_STR_FULL_SCALE_CURR(j);
+ rc = regmap_update_bits(wled->regmap, addr,
+ WLED4_SINK_REG_STR_FULL_SCALE_CURR_MASK,
+ wled->cfg.string_i_limit);
+ if (rc < 0)
+ return rc;
+
+ addr = wled->sink_addr + WLED5_SINK_REG_STR_SRC_SEL(j);
+ rc = regmap_update_bits(wled->regmap, addr,
+ WLED5_SINK_REG_SRC_SEL_MASK,
+ wled->cfg.mod_sel == MOD_A ?
+ WLED5_SINK_REG_SRC_SEL_MOD_A :
+ WLED5_SINK_REG_SRC_SEL_MOD_B);
+
+ temp = j + WLED4_SINK_REG_CURR_SINK_SHFT;
+ sink_en |= 1 << temp;
+ }
+
+ rc = wled5_cabc_config(wled, wled->cfg.cabc_sel ? true : false);
+ if (rc < 0)
+ return rc;
+
+ /* Enable one of the modulators A or B based on mod_sel */
+ addr = wled->sink_addr + WLED5_SINK_REG_MOD_A_EN;
+ val = (wled->cfg.mod_sel == MOD_A) ? WLED5_SINK_REG_MOD_EN_MASK : 0;
+ rc = regmap_update_bits(wled->regmap, addr,
+ WLED5_SINK_REG_MOD_EN_MASK, val);
+ if (rc < 0)
+ return rc;
+
+ addr = wled->sink_addr + WLED5_SINK_REG_MOD_B_EN;
+ val = (wled->cfg.mod_sel == MOD_B) ? WLED5_SINK_REG_MOD_EN_MASK : 0;
+ rc = regmap_update_bits(wled->regmap, addr,
+ WLED5_SINK_REG_MOD_EN_MASK, val);
+ if (rc < 0)
+ return rc;
+
+ addr = wled->sink_addr + wled5_brt_wid_sel_reg[wled->cfg.mod_sel];
+ val = (wled->max_brightness == WLED5_SINK_REG_BRIGHT_MAX_15B) ?
+ WLED5_SINK_REG_BRIGHTNESS_WIDTH_15B :
+ WLED5_SINK_REG_BRIGHTNESS_WIDTH_12B;
+ rc = regmap_write(wled->regmap, addr, val);
+ if (rc < 0)
+ return rc;
+
+ rc = regmap_update_bits(wled->regmap,
+ wled->sink_addr + WLED4_SINK_REG_CURR_SINK,
+ WLED4_SINK_REG_CURR_SINK_MASK, sink_en);
+ if (rc < 0)
+ return rc;
+
+ /* This updates only FSC configuration in WLED5 */
+ rc = wled->wled_sync_toggle(wled);
+ if (rc < 0) {
+ pr_err("Failed to toggle sync reg rc:%d\n", rc);
+ return rc;
+ }
+
+ rc = wled_auto_detection_at_init(wled);
+ if (rc < 0)
+ return rc;
+
+ return 0;
+}
+
+static const struct wled_config wled5_config_defaults = {
+ .boost_i_limit = 5,
+ .string_i_limit = 10,
+ .ovp = 4,
+ .num_strings = 4,
+ .switch_freq = 11,
+ .mod_sel = 0,
+ .cabc_sel = 0,
+ .cabc = false,
+ .external_pfet = false,
+ .auto_detection_enabled = false,
+};
+
static const u32 wled3_boost_i_limit_values[] = {
105, 385, 525, 805, 980, 1260, 1400, 1680,
};
@@ -941,6 +1273,16 @@ static const struct wled_var_cfg wled4_boost_i_limit_cfg = {
.size = ARRAY_SIZE(wled4_boost_i_limit_values),
};
+static inline u32 wled5_boost_i_limit_values_fn(u32 idx)
+{
+ return 525 + (idx * 175);
+}
+
+static const struct wled_var_cfg wled5_boost_i_limit_cfg = {
+ .fn = wled5_boost_i_limit_values_fn,
+ .size = 8,
+};
+
static const u32 wled3_ovp_values[] = {
35, 32, 29, 27,
};
@@ -959,6 +1301,21 @@ static const struct wled_var_cfg wled4_ovp_cfg = {
.size = ARRAY_SIZE(wled4_ovp_values),
};
+static inline u32 wled5_ovp_values_fn(u32 idx)
+{
+ /*
+ * 0000 - 38.5 V
+ * 0001 - 37 V ..
+ * 1111 - 16 V
+ */
+ return 38500 - (idx * 1500);
+}
+
+static const struct wled_var_cfg wled5_ovp_cfg = {
+ .fn = wled5_ovp_values_fn,
+ .size = 16,
+};
+
static u32 wled3_num_strings_values_fn(u32 idx)
{
return idx + 1;
@@ -1006,6 +1363,14 @@ static const struct wled_var_cfg wled4_string_cfg = {
.size = 16,
};
+static const struct wled_var_cfg wled5_mod_sel_cfg = {
+ .size = 2,
+};
+
+static const struct wled_var_cfg wled5_cabc_sel_cfg = {
+ .size = 4,
+};
+
static u32 wled_values(const struct wled_var_cfg *cfg, u32 idx)
{
if (idx >= cfg->size)
@@ -1082,6 +1447,44 @@ static int wled_configure(struct wled *wled)
},
};
+ const struct wled_u32_opts wled5_opts[] = {
+ {
+ .name = "qcom,current-boost-limit",
+ .val_ptr = &cfg->boost_i_limit,
+ .cfg = &wled5_boost_i_limit_cfg,
+ },
+ {
+ .name = "qcom,current-limit-microamp",
+ .val_ptr = &cfg->string_i_limit,
+ .cfg = &wled4_string_i_limit_cfg,
+ },
+ {
+ .name = "qcom,ovp-millivolt",
+ .val_ptr = &cfg->ovp,
+ .cfg = &wled5_ovp_cfg,
+ },
+ {
+ .name = "qcom,switching-freq",
+ .val_ptr = &cfg->switch_freq,
+ .cfg = &wled3_switch_freq_cfg,
+ },
+ {
+ .name = "qcom,num-strings",
+ .val_ptr = &cfg->num_strings,
+ .cfg = &wled4_num_strings_cfg,
+ },
+ {
+ .name = "qcom,modulator-sel",
+ .val_ptr = &cfg->mod_sel,
+ .cfg = &wled5_mod_sel_cfg,
+ },
+ {
+ .name = "qcom,cabc-sel",
+ .val_ptr = &cfg->cabc_sel,
+ .cfg = &wled5_cabc_sel_cfg,
+ },
+ };
+
const struct wled_bool_opts bool_opts[] = {
{ "qcom,cs-out", &cfg->cs_out_en, },
{ "qcom,ext-gen", &cfg->ext_gen, },
@@ -1133,6 +1536,27 @@ static int wled_configure(struct wled *wled)
wled->sink_addr = be32_to_cpu(*prop_addr);
break;
+ case 5:
+ u32_opts = wled5_opts;
+ size = ARRAY_SIZE(wled5_opts);
+ *cfg = wled5_config_defaults;
+ wled->wled_set_brightness = wled5_set_brightness;
+ wled->wled_sync_toggle = wled5_sync_toggle;
+ wled->wled_cabc_config = wled5_cabc_config;
+ wled->wled_ovp_fault_status = wled5_ovp_fault_status;
+ wled->wled_ovp_delay = wled5_ovp_delay;
+ wled->wled_auto_detection_required =
+ wled5_auto_detection_required;
+ wled->max_string_count = 4;
+
+ prop_addr = of_get_address(dev->of_node, 1, NULL, NULL);
+ if (!prop_addr) {
+ dev_err(wled->dev, "invalid IO resources\n");
+ return -EINVAL;
+ }
+ wled->sink_addr = be32_to_cpu(*prop_addr);
+ break;
+
default:
dev_err(wled->dev, "Invalid WLED version\n");
return -EINVAL;
@@ -1286,6 +1710,10 @@ static int wled_probe(struct platform_device *pdev)
if (rc)
return rc;
+ val = WLED3_SINK_REG_BRIGHT_MAX;
+ of_property_read_u32(pdev->dev.of_node, "max-brightness", &val);
+ wled->max_brightness = val;
+
switch (wled->version) {
case 3:
wled->cfg.auto_detection_enabled = false;
@@ -1305,6 +1733,18 @@ static int wled_probe(struct platform_device *pdev)
}
break;
+ case 5:
+ wled->has_short_detect = true;
+ if (wled->cfg.cabc_sel)
+ wled->max_brightness = WLED5_SINK_REG_BRIGHT_MAX_12B;
+
+ rc = wled5_setup(wled);
+ if (rc) {
+ dev_err(&pdev->dev, "wled5_setup failed\n");
+ return rc;
+ }
+ break;
+
default:
dev_err(wled->dev, "Invalid WLED version\n");
break;
@@ -1326,7 +1766,7 @@ static int wled_probe(struct platform_device *pdev)
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_RAW;
props.brightness = val;
- props.max_brightness = WLED3_SINK_REG_BRIGHT_MAX;
+ props.max_brightness = wled->max_brightness;
bl = devm_backlight_device_register(&pdev->dev, wled->name,
&pdev->dev, wled,
&wled_ops, &props);
@@ -1349,6 +1789,7 @@ static const struct of_device_id wled_match_table[] = {
{ .compatible = "qcom,pm8941-wled", .data = (void *)3 },
{ .compatible = "qcom,pmi8998-wled", .data = (void *)4 },
{ .compatible = "qcom,pm660l-wled", .data = (void *)4 },
+ { .compatible = "qcom,pm8150l-wled", .data = (void *)5 },
{}
};
MODULE_DEVICE_TABLE(of, wled_match_table);
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
Add WLED5 specific bindings.
Signed-off-by: Kiran Gunda <[email protected]>
Signed-off-by: Subbaraman Narayanamurthy <[email protected]>
---
.../bindings/leds/backlight/qcom-wled.yaml | 60 ++++++++++++++++++++--
1 file changed, 57 insertions(+), 3 deletions(-)
diff --git a/Documentation/devicetree/bindings/leds/backlight/qcom-wled.yaml b/Documentation/devicetree/bindings/leds/backlight/qcom-wled.yaml
index 770e780..5714631 100644
--- a/Documentation/devicetree/bindings/leds/backlight/qcom-wled.yaml
+++ b/Documentation/devicetree/bindings/leds/backlight/qcom-wled.yaml
@@ -21,6 +21,7 @@ properties:
- qcom,pm8941-wled
- qcom,pmi8998-wled
- qcom,pm660l-wled
+ - qcom,pm8150l-wled
reg:
maxItems: 1
@@ -28,12 +29,13 @@ properties:
default-brightness:
description:
brightness value on boot.
- minimum: 0
- maximum: 4095
- default: 2048
label: true
+ max-brightness:
+ description:
+ Maximum brightness level.
+
qcom,cs-out:
description:
enable current sink output.
@@ -130,6 +132,31 @@ properties:
This feature is not supported for WLED3.
type: boolean
+ qcom,modulator-sel:
+ description:
+ Selects the modulator used for brightness modulation.
+ Allowed values are,
+ 0 - Modulator A
+ 1 - Modulator B
+ This property is applicable only to WLED5 peripheral.
+ allOf:
+ - $ref: /schemas/types.yaml#/definitions/uint32
+ - enum: [ 0, 1 ]
+ - default: 0
+
+ qcom,cabc-sel:
+ description:
+ Selects the CABC pin signal used for brightness modulation.
+ Allowed values are,
+ 0 - CABC disabled
+ 1 - CABC 1
+ 2 - CABC 2
+ 3 - External signal (e.g. LPG) is used for dimming
+ This property is applicable only to WLED5 peripheral.
+ allOf:
+ - $ref: /schemas/types.yaml#/definitions/uint32
+ - enum: [ 0, 1, 2, 3 ]
+
allOf:
- if:
properties:
@@ -179,6 +206,33 @@ allOf:
- const: ovp
- const: short
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - qcom,pm8150l-wled
+
+ then:
+ properties:
+ default-brightness:
+ minimum: 0
+ maximum: 32767
+
+ max-brightness:
+ minimum: 0
+ maximum: 32767
+
+ else:
+ properties:
+ default-brightness:
+ minimum: 0
+ maximum: 4095
+
+ max-brightness:
+ minimum: 0
+ maximum: 4095
+
required:
- compatible
- reg
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
On Tue, Apr 07, 2020 at 09:17:09PM +0530, Kiran Gunda wrote:
> Add WLED5 specific bindings.
>
checkpatch.pl complains about some trailing whitespace. The previous
patch too.
> Signed-off-by: Kiran Gunda <[email protected]>
> Signed-off-by: Subbaraman Narayanamurthy <[email protected]>
> ---
> .../bindings/leds/backlight/qcom-wled.yaml | 60 ++++++++++++++++++++--
> 1 file changed, 57 insertions(+), 3 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/leds/backlight/qcom-wled.yaml b/Documentation/devicetree/bindings/leds/backlight/qcom-wled.yaml
> index 770e780..5714631 100644
> --- a/Documentation/devicetree/bindings/leds/backlight/qcom-wled.yaml
> +++ b/Documentation/devicetree/bindings/leds/backlight/qcom-wled.yaml
> @@ -21,6 +21,7 @@ properties:
> - qcom,pm8941-wled
> - qcom,pmi8998-wled
> - qcom,pm660l-wled
> + - qcom,pm8150l-wled
>
> reg:
> maxItems: 1
> @@ -28,12 +29,13 @@ properties:
> default-brightness:
> description:
> brightness value on boot.
> - minimum: 0
> - maximum: 4095
> - default: 2048
>
> label: true
>
> + max-brightness:
> + description:
> + Maximum brightness level.
> +
> qcom,cs-out:
> description:
> enable current sink output.
> @@ -130,6 +132,31 @@ properties:
> This feature is not supported for WLED3.
> type: boolean
>
> + qcom,modulator-sel:
> + description:
Need a '|' at the end to preserve formatting.
> + Selects the modulator used for brightness modulation.
> + Allowed values are,
> + 0 - Modulator A
> + 1 - Modulator B
> + This property is applicable only to WLED5 peripheral.
> + allOf:
> + - $ref: /schemas/types.yaml#/definitions/uint32
> + - enum: [ 0, 1 ]
> + - default: 0
> +
> + qcom,cabc-sel:
> + description:
Need a '|'.
> + Selects the CABC pin signal used for brightness modulation.
> + Allowed values are,
> + 0 - CABC disabled
> + 1 - CABC 1
> + 2 - CABC 2
> + 3 - External signal (e.g. LPG) is used for dimming
> + This property is applicable only to WLED5 peripheral.
> + allOf:
> + - $ref: /schemas/types.yaml#/definitions/uint32
> + - enum: [ 0, 1, 2, 3 ]
> +
> allOf:
> - if:
> properties:
> @@ -179,6 +206,33 @@ allOf:
> - const: ovp
> - const: short
>
> + - if:
> + properties:
> + compatible:
> + contains:
> + enum:
> + - qcom,pm8150l-wled
> +
> + then:
> + properties:
> + default-brightness:
> + minimum: 0
> + maximum: 32767
> +
> + max-brightness:
> + minimum: 0
> + maximum: 32767
> +
> + else:
> + properties:
> + default-brightness:
> + minimum: 0
> + maximum: 4095
Wrong indentation.
> +
> + max-brightness:
> + minimum: 0
> + maximum: 4095
> +
> required:
> - compatible
> - reg
> --
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> a Linux Foundation Collaborative Project
>
On 2020-04-15 20:42, Rob Herring wrote:
> On Tue, Apr 07, 2020 at 09:17:09PM +0530, Kiran Gunda wrote:
>> Add WLED5 specific bindings.
>>
>
> checkpatch.pl complains about some trailing whitespace. The previous
> patch too.
>
>> Signed-off-by: Kiran Gunda <[email protected]>
>> Signed-off-by: Subbaraman Narayanamurthy <[email protected]>
>> ---
>> .../bindings/leds/backlight/qcom-wled.yaml | 60
>> ++++++++++++++++++++--
>> 1 file changed, 57 insertions(+), 3 deletions(-)
>>
>> diff --git
>> a/Documentation/devicetree/bindings/leds/backlight/qcom-wled.yaml
>> b/Documentation/devicetree/bindings/leds/backlight/qcom-wled.yaml
>> index 770e780..5714631 100644
>> --- a/Documentation/devicetree/bindings/leds/backlight/qcom-wled.yaml
>> +++ b/Documentation/devicetree/bindings/leds/backlight/qcom-wled.yaml
>> @@ -21,6 +21,7 @@ properties:
>> - qcom,pm8941-wled
>> - qcom,pmi8998-wled
>> - qcom,pm660l-wled
>> + - qcom,pm8150l-wled
>>
>> reg:
>> maxItems: 1
>> @@ -28,12 +29,13 @@ properties:
>> default-brightness:
>> description:
>> brightness value on boot.
>> - minimum: 0
>> - maximum: 4095
>> - default: 2048
>>
>> label: true
>>
>> + max-brightness:
>> + description:
>> + Maximum brightness level.
>> +
>> qcom,cs-out:
>> description:
>> enable current sink output.
>> @@ -130,6 +132,31 @@ properties:
>> This feature is not supported for WLED3.
>> type: boolean
>>
>> + qcom,modulator-sel:
>> + description:
>
> Need a '|' at the end to preserve formatting.
>
>> + Selects the modulator used for brightness modulation.
>> + Allowed values are,
>> + 0 - Modulator A
>> + 1 - Modulator B
>> + This property is applicable only to WLED5 peripheral.
>> + allOf:
>> + - $ref: /schemas/types.yaml#/definitions/uint32
>> + - enum: [ 0, 1 ]
>> + - default: 0
>> +
>> + qcom,cabc-sel:
>> + description:
>
> Need a '|'.
>
>> + Selects the CABC pin signal used for brightness modulation.
>> + Allowed values are,
>> + 0 - CABC disabled
>> + 1 - CABC 1
>> + 2 - CABC 2
>> + 3 - External signal (e.g. LPG) is used for dimming
>> + This property is applicable only to WLED5 peripheral.
>> + allOf:
>> + - $ref: /schemas/types.yaml#/definitions/uint32
>> + - enum: [ 0, 1, 2, 3 ]
>> +
>> allOf:
>> - if:
>> properties:
>> @@ -179,6 +206,33 @@ allOf:
>> - const: ovp
>> - const: short
>>
>> + - if:
>> + properties:
>> + compatible:
>> + contains:
>> + enum:
>> + - qcom,pm8150l-wled
>> +
>> + then:
>> + properties:
>> + default-brightness:
>> + minimum: 0
>> + maximum: 32767
>> +
>> + max-brightness:
>> + minimum: 0
>> + maximum: 32767
>> +
>> + else:
>> + properties:
>> + default-brightness:
>> + minimum: 0
>> + maximum: 4095
>
> Wrong indentation.
>
>> +
>> + max-brightness:
>> + minimum: 0
>> + maximum: 4095
>> +
>> required:
>> - compatible
>> - reg
>> --
>> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora
>> Forum,
>> a Linux Foundation Collaborative Project
>>
Thanks for reviewing. I will submit the next revision with all the
fixes.
On Tue, Apr 07, 2020 at 09:17:09PM +0530, Kiran Gunda wrote:
> Add WLED5 specific bindings.
>
> Signed-off-by: Kiran Gunda <[email protected]>
> Signed-off-by: Subbaraman Narayanamurthy <[email protected]>
If v6 is just reacting to Rob's feedback then feel free to add the
following when you recirculate:
Acked-by: Daniel Thompson <[email protected]>
> ---
> .../bindings/leds/backlight/qcom-wled.yaml | 60 ++++++++++++++++++++--
> 1 file changed, 57 insertions(+), 3 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/leds/backlight/qcom-wled.yaml b/Documentation/devicetree/bindings/leds/backlight/qcom-wled.yaml
> index 770e780..5714631 100644
> --- a/Documentation/devicetree/bindings/leds/backlight/qcom-wled.yaml
> +++ b/Documentation/devicetree/bindings/leds/backlight/qcom-wled.yaml
> @@ -21,6 +21,7 @@ properties:
> - qcom,pm8941-wled
> - qcom,pmi8998-wled
> - qcom,pm660l-wled
> + - qcom,pm8150l-wled
>
> reg:
> maxItems: 1
> @@ -28,12 +29,13 @@ properties:
> default-brightness:
> description:
> brightness value on boot.
> - minimum: 0
> - maximum: 4095
> - default: 2048
>
> label: true
>
> + max-brightness:
> + description:
> + Maximum brightness level.
> +
> qcom,cs-out:
> description:
> enable current sink output.
> @@ -130,6 +132,31 @@ properties:
> This feature is not supported for WLED3.
> type: boolean
>
> + qcom,modulator-sel:
> + description:
> + Selects the modulator used for brightness modulation.
> + Allowed values are,
> + 0 - Modulator A
> + 1 - Modulator B
> + This property is applicable only to WLED5 peripheral.
> + allOf:
> + - $ref: /schemas/types.yaml#/definitions/uint32
> + - enum: [ 0, 1 ]
> + - default: 0
> +
> + qcom,cabc-sel:
> + description:
> + Selects the CABC pin signal used for brightness modulation.
> + Allowed values are,
> + 0 - CABC disabled
> + 1 - CABC 1
> + 2 - CABC 2
> + 3 - External signal (e.g. LPG) is used for dimming
> + This property is applicable only to WLED5 peripheral.
> + allOf:
> + - $ref: /schemas/types.yaml#/definitions/uint32
> + - enum: [ 0, 1, 2, 3 ]
> +
> allOf:
> - if:
> properties:
> @@ -179,6 +206,33 @@ allOf:
> - const: ovp
> - const: short
>
> + - if:
> + properties:
> + compatible:
> + contains:
> + enum:
> + - qcom,pm8150l-wled
> +
> + then:
> + properties:
> + default-brightness:
> + minimum: 0
> + maximum: 32767
> +
> + max-brightness:
> + minimum: 0
> + maximum: 32767
> +
> + else:
> + properties:
> + default-brightness:
> + minimum: 0
> + maximum: 4095
> +
> + max-brightness:
> + minimum: 0
> + maximum: 4095
> +
> required:
> - compatible
> - reg
> --
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> a Linux Foundation Collaborative Project
On Tue, Apr 07, 2020 at 09:17:10PM +0530, Kiran Gunda wrote:
> From: Subbaraman Narayanamurthy <[email protected]>
>
> PM8150L WLED supports the following:
> - Two modulators and each sink can use any of the modulator
> - Multiple CABC selection options from which one can be selected/enabled
> - Multiple brightness width selection (12 bits to 15 bits)
>
> Signed-off-by: Subbaraman Narayanamurthy <[email protected]>
> Signed-off-by: Kiran Gunda <[email protected]>
> ---
> drivers/video/backlight/qcom-wled.c | 443 +++++++++++++++++++++++++++++++++++-
> 1 file changed, 442 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/video/backlight/qcom-wled.c b/drivers/video/backlight/qcom-wled.c
> index a6ddaa9..3a57011 100644
> --- a/drivers/video/backlight/qcom-wled.c
> +++ b/drivers/video/backlight/qcom-wled.c
> ...
> +static const u8 wled5_brightness_reg[MOD_MAX] = {
> + [MOD_A] = WLED5_SINK_REG_MOD_A_BRIGHTNESS_LSB,
> + [MOD_B] = WLED5_SINK_REG_MOD_B_BRIGHTNESS_LSB,
> +};
> +
> +static const u8 wled5_src_sel_reg[MOD_MAX] = {
> + [MOD_A] = WLED5_SINK_REG_MOD_A_SRC_SEL,
> + [MOD_B] = WLED5_SINK_REG_MOD_B_SRC_SEL,
> +};
> +
> +static const u8 wled5_brt_wid_sel_reg[MOD_MAX] = {
> + [MOD_A] = WLED5_SINK_REG_MOD_A_BRIGHTNESS_WIDTH_SEL,
> + [MOD_B] = WLED5_SINK_REG_MOD_B_BRIGHTNESS_WIDTH_SEL,
> +};
> +
Each of these lookup tables are used exactly once... and half the time
when this code chooses between MOD_A and MOD_B a ternary is used and
half the time these lookup tables.
I suggest these be removed.
> static int wled3_set_brightness(struct wled *wled, u16 brightness)
> {
> int rc, i;
> @@ -225,6 +291,25 @@ static int wled4_set_brightness(struct wled *wled, u16 brightness)
> return 0;
> }
>
> +static int wled5_set_brightness(struct wled *wled, u16 brightness)
> +{
> + int rc, offset;
> + u16 low_limit = wled->max_brightness * 1 / 1000;
Multiplying by 1 is redundant.
> + u8 v[2];
> +
> + /* WLED5's lower limit is 0.1% */
> + if (brightness < low_limit)
> + brightness = low_limit;
> +
> + v[0] = brightness & 0xff;
> + v[1] = (brightness >> 8) & 0x7f;
> +
> + offset = wled5_brightness_reg[wled->cfg.mod_sel];
> + rc = regmap_bulk_write(wled->regmap, wled->sink_addr + offset,
> + v, 2);
> + return rc;
> +}
> +
> static void wled_ovp_work(struct work_struct *work)
> {
> struct wled *wled = container_of(work,
> @@ -317,11 +420,67 @@ static int wled4_ovp_fault_status(struct wled *wled, bool *fault_set)
> return rc;
> }
>
> +static int wled5_ovp_fault_status(struct wled *wled, bool *fault_set)
> +{
> + int rc;
> + u32 int_rt_sts, fault_sts;
> +
> + *fault_set = false;
> + rc = regmap_read(wled->regmap,
> + wled->ctrl_addr + WLED3_CTRL_REG_INT_RT_STS,
> + &int_rt_sts);
> + if (rc < 0) {
> + dev_err(wled->dev, "Failed to read INT_RT_STS rc=%d\n", rc);
> + return rc;
> + }
> +
> + rc = regmap_read(wled->regmap,
> + wled->ctrl_addr + WLED3_CTRL_REG_FAULT_STATUS,
> + &fault_sts);
> + if (rc < 0) {
> + dev_err(wled->dev, "Failed to read FAULT_STATUS rc=%d\n", rc);
> + return rc;
> + }
> +
> + if (int_rt_sts & WLED3_CTRL_REG_OVP_FAULT_STATUS)
> + *fault_set = true;
> +
> + if (fault_sts & (WLED3_CTRL_REG_OVP_FAULT_BIT |
> + WLED5_CTRL_REG_OVP_PRE_ALARM_BIT))
Correct me if I'm wrong but isn't the only difference between the WLED4
and WLED5 code that the wled5 code also checks the
WLED5_CTRL_REG_OVP_PRE_ALARM_BIT ?
If so why do we need to pull out (and duplicate) this code code using
the function pointers?
> + *fault_set = true;
> +
> + if (*fault_set)
> + dev_dbg(wled->dev, "WLED OVP fault detected, int_rt_sts=0x%x fault_sts=0x%x\n",
> + int_rt_sts, fault_sts);
> +
> + return rc;
> +}
> +
> @@ -615,6 +797,7 @@ static void wled_auto_string_detection(struct wled *wled)
>
> #define WLED_AUTO_DETECT_OVP_COUNT 5
> #define WLED_AUTO_DETECT_CNT_DLY_US USEC_PER_SEC
> +
Nit picking but this additional line is in the wrong patch ;-)
> static bool wled4_auto_detection_required(struct wled *wled)
> {
> s64 elapsed_time_us;
> @@ -648,6 +831,46 @@ static bool wled4_auto_detection_required(struct wled *wled)
> return false;
> }
>
> +static bool wled5_auto_detection_required(struct wled *wled)
> +{
> + s64 elapsed_time_us;
> +
> + if (!wled->cfg.auto_detection_enabled)
> + return false;
> +
> + /*
> + * Check if the OVP fault was an occasional one
> + * or if it's firing continuously, the latter qualifies
> + * for an auto-detection check.
> + */
> + if (!wled->auto_detection_ovp_count) {
> + wled->start_ovp_fault_time = ktime_get();
> + wled->auto_detection_ovp_count++;
> + } else {
> + /*
> + * WLED5 has OVP fault density interrupt configuration i.e. to
> + * count the number of OVP alarms for a certain duration before
> + * triggering OVP fault interrupt. By default, number of OVP
> + * fault events counted before an interrupt is fired is 32 and
> + * the time interval is 12 ms. If we see more than one OVP fault
> + * interrupt, then that should qualify for a real OVP fault
> + * condition to run auto calibration algorithm.
> + */
Given the above why do we have a software mechanism to wait until the
second time the interrupt fires? I'm a bit rusty on this driver but
wasn't there already some mechanism to slightly delay turning on the
fault detection?
Daniel.