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 <jprakash@codeaurora.org>
This commit is contained in:
Jishnu Prakash 2021-02-21 16:27:47 +05:30
commit d02fbd0d0d

View file

@ -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");