From a1e681aaf6d8be7aba7b3e36b1826fbac40fcbc0 Mon Sep 17 00:00:00 2001 From: Subbaraman Narayanamurthy Date: Tue, 5 Dec 2017 17:36:43 -0800 Subject: [PATCH] leds: qpnp-wled: add support for brightness mapping Currently, WLED driver is being passed with a 12 bit brightness level by the clients and they're set in the hardware registers. Add support for brightness mapping by providing a 256 entry table where each entry can take a 12 bit brightness level value. This will be mapped internally for controlling different brightness regions in an user preferred way. Change-Id: I7adf97918a311bf54b02b383defeb59685aa0d00 Signed-off-by: Subbaraman Narayanamurthy --- .../devicetree/bindings/leds/leds-qpnp-wled.txt | 5 ++ drivers/leds/leds-qpnp-wled.c | 100 ++++++++++++++++++++- 2 files changed, 101 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/leds/leds-qpnp-wled.txt b/Documentation/devicetree/bindings/leds/leds-qpnp-wled.txt index 42e97f765bee..a37e437d0208 100644 --- a/Documentation/devicetree/bindings/leds/leds-qpnp-wled.txt +++ b/Documentation/devicetree/bindings/leds/leds-qpnp-wled.txt @@ -80,6 +80,10 @@ Optional properties for WLED: or disabled. - qcom,auto-calibration-enable : A boolean property which enables auto-calibration of the WLED sink configuration. +- qcom,wled-brightness-map : Array of brightness map codes of size 256. + These codes will be mapped to the brightness + level requested in the scale of 0-4095. Code + entry is of 16 bit size. Optional properties if 'qcom,disp-type-amoled' is mentioned in DT: - qcom,loop-comp-res-kohm : control to select the compensation resistor in kohm. default is 320. @@ -123,4 +127,5 @@ Example: qcom,en-phase-stag; qcom,led-strings-list = [00 01 02 03]; qcom,en-ext-pfet-sc-pro; + qcom,wled-brightness-map = /bits/ 16 <0 . . 4095>; }; diff --git a/drivers/leds/leds-qpnp-wled.c b/drivers/leds/leds-qpnp-wled.c index 1d84ec4687e2..3c8019fbf904 100644 --- a/drivers/leds/leds-qpnp-wled.c +++ b/drivers/leds/leds-qpnp-wled.c @@ -210,6 +210,7 @@ #define QPNP_WLED_SEC_ACCESS_REG(b) (b + 0xD0) #define QPNP_WLED_SEC_UNLOCK 0xA5 +#define NUM_DDIC_CODES 256 #define QPNP_WLED_MAX_STRINGS 4 #define QPNP_PM660_WLED_MAX_STRINGS 3 #define WLED_MAX_LEVEL_4095 4095 @@ -340,6 +341,10 @@ static struct wled_vref_setting vref_setting_pmi8998 = { * @ ramp_ms - delay between ramp steps in ms * @ ramp_step - ramp step size * @ cons_sync_write_delay_us - delay between two consecutive writes to SYNC + * @ auto_calibration_ovp_count - OVP fault irq count to run auto calibration + * @ max_strings - Number of strings supported in WLED peripheral + * @ prev_level - Previous brightness level + * @ brt_map_table - Brightness map table * @ strings - supported list of strings * @ num_strings - number of strings * @ loop_auto_gm_thresh - the clamping level for auto gm @@ -353,6 +358,12 @@ static struct wled_vref_setting vref_setting_pmi8998 = { * @ en_cabc - enable or disable cabc * @ disp_type_amoled - type of display: LCD/AMOLED * @ en_ext_pfet_sc_pro - enable sc protection on external pfet + * @ prev_state - previous state of WLED + * @ ovp_irq_disabled - OVP interrupt disable status + * @ auto_calib_enabled - Flag to enable auto calibration feature + * @ auto_calib_done - Flag to indicate auto calibration is done + * @ module_dis_perm - Flat to keep module permanently disabled + * @ start_ovp_fault_time - Time when the OVP fault first occurred */ struct qpnp_wled { struct led_classdev cdev; @@ -388,6 +399,8 @@ struct qpnp_wled { u16 cons_sync_write_delay_us; u16 auto_calibration_ovp_count; u16 max_strings; + u16 prev_level; + u16 *brt_map_table; u8 strings[QPNP_WLED_MAX_STRINGS]; u8 num_strings; u8 loop_auto_gm_thresh; @@ -573,6 +586,33 @@ static int qpnp_wled_set_level(struct qpnp_wled *wled, int level) return 0; } +static int qpnp_wled_set_map_level(struct qpnp_wled *wled, int level) +{ + int rc, i; + + if (level < wled->prev_level) { + for (i = wled->prev_level; i >= level; i--) { + rc = qpnp_wled_set_level(wled, wled->brt_map_table[i]); + if (rc < 0) { + pr_err("set brightness level failed, rc:%d\n", + rc); + return rc; + } + } + } else if (level > wled->prev_level) { + for (i = wled->prev_level; i <= level; i++) { + rc = qpnp_wled_set_level(wled, wled->brt_map_table[i]); + if (rc < 0) { + pr_err("set brightness level failed, rc:%d\n", + rc); + return rc; + } + } + } + + return 0; +} + static int qpnp_wled_psm_config(struct qpnp_wled *wled, bool enable) { int rc; @@ -942,15 +982,30 @@ static struct device_attribute qpnp_wled_attrs[] = { static void qpnp_wled_work(struct work_struct *work) { struct qpnp_wled *wled; - int level, rc; + int level, level_255, rc; wled = container_of(work, struct qpnp_wled, work); + mutex_lock(&wled->lock); level = wled->cdev.brightness; - mutex_lock(&wled->lock); + if (wled->brt_map_table) { + /* + * Change the 12 bit level to 8 bit level and use the mapped + * values for 12 bit level from brightness map table. + */ + level_255 = DIV_ROUND_CLOSEST(level, 16); + if (level_255 > 255) + level_255 = 255; - if (level) { + pr_debug("level: %d level_255: %d\n", level, level_255); + rc = qpnp_wled_set_map_level(wled, level_255); + if (rc) { + dev_err(&wled->pdev->dev, "wled set level failed\n"); + goto unlock_mutex; + } + wled->prev_level = level_255; + } else if (level) { rc = qpnp_wled_set_level(wled, level); if (rc) { dev_err(&wled->pdev->dev, "wled set level failed\n"); @@ -2115,7 +2170,7 @@ static int qpnp_wled_parse_dt(struct qpnp_wled *wled) struct property *prop; const char *temp_str; u32 temp_val; - int rc, i; + int rc, i, size; u8 *strings; wled->cdev.name = "wled"; @@ -2134,6 +2189,43 @@ static int qpnp_wled_parse_dt(struct qpnp_wled *wled) return rc; } + if (of_find_property(pdev->dev.of_node, "qcom,wled-brightness-map", + NULL)) { + size = of_property_count_elems_of_size(pdev->dev.of_node, + "qcom,wled-brightness-map", sizeof(u16)); + if (size != NUM_DDIC_CODES) { + pr_err("Invalid WLED brightness map size:%d\n", size); + return rc; + } + + wled->brt_map_table = devm_kcalloc(&pdev->dev, NUM_DDIC_CODES, + sizeof(u16), GFP_KERNEL); + if (!wled->brt_map_table) + return -ENOMEM; + + rc = of_property_read_u16_array(pdev->dev.of_node, + "qcom,wled-brightness-map", wled->brt_map_table, + NUM_DDIC_CODES); + if (rc < 0) { + pr_err("Error in reading WLED brightness map, rc=%d\n", + rc); + return rc; + } + + for (i = 0; i < NUM_DDIC_CODES; i++) { + if (wled->brt_map_table[i] > WLED_MAX_LEVEL_4095) { + pr_err("WLED brightness map not in range\n"); + return -EDOM; + } + + if ((i > 1) && wled->brt_map_table[i] + < wled->brt_map_table[i - 1]) { + pr_err("WLED brightness map not in ascending order?\n"); + return -EDOM; + } + } + } + wled->disp_type_amoled = of_property_read_bool(pdev->dev.of_node, "qcom,disp-type-amoled"); if (wled->disp_type_amoled) { -- 2.11.0