From d02fbd0d0d97216a98befcd513bc6805dec17923 Mon Sep 17 00:00:00 2001 From: Jishnu Prakash Date: Sun, 21 Feb 2021 16:27:47 +0530 Subject: [PATCH] backlight: qcom-spmi-wled: Add support for exponential dimming Add support to enable writing a lookup table of brightness values into WLED_SINK peripheral to enable more granular control over brightness, to help end user see brightness change smoothly instead of in visible steps. Change-Id: Ifd2dd382bf3e1274a5b994e8d83b87833c975970 Signed-off-by: Jishnu Prakash --- drivers/video/backlight/qcom-spmi-wled.c | 109 +++++++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/drivers/video/backlight/qcom-spmi-wled.c b/drivers/video/backlight/qcom-spmi-wled.c index 676aeec47340..2e75f6087aad 100644 --- a/drivers/video/backlight/qcom-spmi-wled.c +++ b/drivers/video/backlight/qcom-spmi-wled.c @@ -180,6 +180,21 @@ #define WLED5_SINK_FLASH_SHDN_CLR_REG 0xb6 +#define WLED5_SINK_BRIGHTNESS_SLEW_RATE_CTL_REG 0xb8 +#define WLED5_EN_SLEW_CTL BIT(7) +#define WLED5_EN_EXP_LUT BIT(6) + +#define WLED5_SINK_DIMMING_EXP_LUT0_LSB_REG 0xc0 +#define WLED5_SINK_DIMMING_EXP_LUT0_MSB_REG 0xc1 +#define WLED5_SINK_DIMMING_EXP_LUT1_LSB_REG 0xc2 +#define WLED5_SINK_DIMMING_EXP_LUT1_MSB_REG 0xc3 +#define WLED5_SINK_DIMMING_EXP_LUT_LSB_MASK GENMASK(7, 0) +#define WLED5_SINK_DIMMING_EXP_LUT_MSB_MASK GENMASK(6, 0) + +#define WLED5_SINK_EXP_LUT_INDEX_REG 0xc9 + +#define EXP_DIMMING_TABLE_SIZE 256 + enum wled_version { WLED_PMI8998 = 4, WLED_PM660L, @@ -211,6 +226,7 @@ struct wled_config { bool en_cabc; bool ext_pfet_sc_pro_en; bool auto_calib_enabled; + bool use_exp_dimming; }; struct wled_flash_config { @@ -258,6 +274,7 @@ struct wled { enum wled_flash_mode flash_mode; u8 num_strings; u32 leds_per_string; + u32 exp_map[EXP_DIMMING_TABLE_SIZE]; }; enum wled5_mod_sel { @@ -1099,6 +1116,78 @@ static inline u8 get_wled_safety_time(int time_ms) return 0; } +static int wled_read_exp_dimming_map(struct device_node *node, struct wled *wled) +{ + int rc = 0, len; + + if (*wled->version != WLED_PM7325B) { + pr_err("Exponential dimming not supported for WLED version %d\n", *wled->version); + return 0; + } + + len = of_property_count_elems_of_size(node, "qcom,exp-dimming-map", sizeof(u32)); + if (len != EXP_DIMMING_TABLE_SIZE) { + pr_err("Invalid exponential map length: %d, must be 256 bytes length\n", len); + return -EINVAL; + } + + rc = of_property_read_u32_array(node, "qcom,exp-dimming-map", + wled->exp_map, EXP_DIMMING_TABLE_SIZE); + if (rc < 0) + pr_err("Error in reading qcom,exp-dimming-map, rc=%d\n", rc); + + return rc; +} + +static int wled_program_exp_dimming(struct wled *wled) +{ + int rc = 0, i; + u8 val[4]; + + if (*wled->version != WLED_PM7325B) { + pr_err("Exponential dimming not supported for WLED version %d\n", *wled->version); + return 0; + } + + wled_module_enable(wled, 0); + + rc = regmap_update_bits(wled->regmap, + wled->sink_addr + WLED5_SINK_BRIGHTNESS_SLEW_RATE_CTL_REG, + WLED5_EN_EXP_LUT, WLED5_EN_EXP_LUT); + if (rc < 0) { + wled_module_enable(wled, 1); + return rc; + } + + for (i = 0; i < EXP_DIMMING_TABLE_SIZE / 2; i++) { + val[0] = wled->exp_map[2 * i] & WLED5_SINK_DIMMING_EXP_LUT_LSB_MASK; + val[1] = (wled->exp_map[2 * i] >> 8) & WLED5_SINK_DIMMING_EXP_LUT_MSB_MASK; + val[2] = wled->exp_map[2 * i + 1] & WLED5_SINK_DIMMING_EXP_LUT_LSB_MASK; + val[3] = (wled->exp_map[2 * i + 1] >> 8) & WLED5_SINK_DIMMING_EXP_LUT_MSB_MASK; + + rc = regmap_write(wled->regmap, wled->sink_addr + WLED5_SINK_EXP_LUT_INDEX_REG, i); + if (rc < 0) + goto exp_dimm_fail; + + rc = regmap_bulk_write(wled->regmap, + wled->sink_addr + WLED5_SINK_DIMMING_EXP_LUT0_LSB_REG, + val, sizeof(val)); + if (rc < 0) + goto exp_dimm_fail; + } + + wled_module_enable(wled, 1); + return rc; + +exp_dimm_fail: + regmap_update_bits(wled->regmap, + wled->sink_addr + WLED5_SINK_BRIGHTNESS_SLEW_RATE_CTL_REG, + WLED5_EN_EXP_LUT, 0); + + wled_module_enable(wled, 1); + return rc; +} + static int wled5_setup(struct wled *wled) { int rc, temp, i; @@ -1199,6 +1288,14 @@ static int wled5_setup(struct wled *wled) if (rc < 0) return rc; + if (wled->cfg.use_exp_dimming) { + rc = wled_program_exp_dimming(wled); + if (rc < 0) { + pr_err("Programming exponential dimming map failed, rc=%d\n", rc); + return rc; + } + } + if (wled->ovp_irq >= 0) { rc = devm_request_threaded_irq(&wled->pdev->dev, wled->ovp_irq, NULL, wled_ovp_irq_handler, IRQF_ONESHOT, @@ -1374,6 +1471,7 @@ static const struct wled_config wled4_config_defaults = { .en_cabc = 0, .ext_pfet_sc_pro_en = 0, .auto_calib_enabled = 0, + .use_exp_dimming = 0, }; static const struct wled_config wled5_config_defaults = { @@ -1387,6 +1485,7 @@ static const struct wled_config wled5_config_defaults = { .en_cabc = 0, .ext_pfet_sc_pro_en = 0, .auto_calib_enabled = 0, + .use_exp_dimming = 0, }; struct wled_var_cfg { @@ -2252,6 +2351,7 @@ static int wled_configure(struct wled *wled, struct device *dev) { "qcom,en-cabc", &cfg->en_cabc, }, { "qcom,ext-pfet-sc-pro", &cfg->ext_pfet_sc_pro_en, }, { "qcom,auto-calibration", &cfg->auto_calib_enabled, }, + { "qcom,use-exp-dimming", &cfg->use_exp_dimming, }, }; prop_addr = of_get_address(dev->of_node, 0, NULL, NULL); @@ -2324,6 +2424,15 @@ static int wled_configure(struct wled *wled, struct device *dev) *bool_opts[i].val_ptr = true; } + if (wled->cfg.use_exp_dimming) { + rc = wled_read_exp_dimming_map(dev->of_node, wled); + if (rc < 0) { + dev_err(&wled->pdev->dev, + "Reading exponential dimming map failed, rc=%d\n", rc); + return rc; + } + } + wled->sc_irq = platform_get_irq_byname(wled->pdev, "sc-irq"); if (wled->sc_irq < 0) dev_dbg(&wled->pdev->dev, "sc irq is not used\n");