Loading Documentation/devicetree/bindings/leds/leds-qpnp-wled.txt +3 −0 Original line number Diff line number Diff line Loading @@ -84,6 +84,9 @@ Optional properties for WLED: These codes will be mapped to the brightness level requested in the scale of 0-4095. Code entry is of 16 bit size. - qcom,wled-stepper-en : A boolean property to specify if stepper algorithm needs to be enabled. This needs the brightness map table to be specified. 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. Loading drivers/leds/leds-qpnp-wled.c +95 −2 Original line number Diff line number Diff line Loading @@ -316,6 +316,7 @@ static struct wled_vref_setting vref_setting_pmi8998 = { * @ cdev - led class device * @ pdev - platform device * @ work - worker for led operation * @ wq - workqueue for setting brightness level * @ lock - mutex lock for exclusive access * @ fdbk_op - output feedback mode * @ dim_mode - dimming mode Loading Loading @@ -359,6 +360,7 @@ static struct wled_vref_setting vref_setting_pmi8998 = { * @ 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 * @ stepper_en - Flag to enable stepper algorithm * @ 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 Loading @@ -371,6 +373,7 @@ struct qpnp_wled { struct regmap *regmap; struct pmic_revid_data *pmic_rev_id; struct work_struct work; struct workqueue_struct *wq; struct mutex lock; struct mutex bus_lock; enum qpnp_wled_fdbk_op fdbk_op; Loading Loading @@ -415,6 +418,7 @@ struct qpnp_wled { bool disp_type_amoled; bool en_ext_pfet_sc_pro; bool prev_state; bool stepper_en; bool ovp_irq_disabled; bool auto_calib_enabled; bool auto_calib_done; Loading @@ -422,6 +426,21 @@ struct qpnp_wled { ktime_t start_ovp_fault_time; }; static int qpnp_wled_step_delay_us = 52000; module_param_named( total_step_delay_us, qpnp_wled_step_delay_us, int, 0600 ); static int qpnp_wled_step_size_threshold = 3; module_param_named( step_size_threshold, qpnp_wled_step_size_threshold, int, 0600 ); static int qpnp_wled_step_delay_gain = 2; module_param_named( step_delay_gain, qpnp_wled_step_delay_gain, int, 0600 ); /* helper to read a pmic register */ static int qpnp_wled_read_reg(struct qpnp_wled *wled, u16 addr, u8 *data) { Loading Loading @@ -583,6 +602,7 @@ static int qpnp_wled_set_level(struct qpnp_wled *wled, int level) return rc; } pr_debug("level:%d\n", level); return 0; } Loading Loading @@ -613,6 +633,65 @@ static int qpnp_wled_set_map_level(struct qpnp_wled *wled, int level) return 0; } static int qpnp_wled_set_step_level(struct qpnp_wled *wled, int new_level) { int rc, i, num_steps, delay_us; u16 level, start_level, end_level, step_size; bool level_inc = false; level = wled->prev_level; start_level = wled->brt_map_table[level]; end_level = wled->brt_map_table[new_level]; level_inc = (new_level > level); num_steps = abs(start_level - end_level); if (!num_steps) return 0; delay_us = qpnp_wled_step_delay_us / num_steps; pr_debug("level goes from [%d %d] num_steps: %d, delay: %d\n", start_level, end_level, num_steps, delay_us); if (delay_us < 500) { step_size = 1000 / delay_us; num_steps = num_steps / step_size; delay_us = 1000; } else { if (num_steps < qpnp_wled_step_size_threshold) delay_us *= qpnp_wled_step_delay_gain; step_size = 1; } i = start_level; while (num_steps--) { if (level_inc) i += step_size; else i -= step_size; rc = qpnp_wled_set_level(wled, i); if (rc < 0) return rc; if (delay_us > 0) { if (delay_us < 20000) usleep_range(delay_us, delay_us + 1); else msleep(delay_us / USEC_PER_MSEC); } } if (i != end_level) { i = end_level; rc = qpnp_wled_set_level(wled, i); if (rc < 0) return rc; } return 0; } static int qpnp_wled_psm_config(struct qpnp_wled *wled, bool enable) { int rc; Loading Loading @@ -999,6 +1078,9 @@ static void qpnp_wled_work(struct work_struct *work) level_255 = 255; pr_debug("level: %d level_255: %d\n", level, level_255); if (wled->stepper_en) rc = qpnp_wled_set_step_level(wled, level_255); else rc = qpnp_wled_set_map_level(wled, level_255); if (rc) { dev_err(&wled->pdev->dev, "wled set level failed\n"); Loading Loading @@ -1064,7 +1146,7 @@ static void qpnp_wled_set(struct led_classdev *led_cdev, level = wled->cdev.max_brightness; wled->cdev.brightness = level; schedule_work(&wled->work); queue_work(wled->wq, &wled->work); } static int qpnp_wled_set_disp(struct qpnp_wled *wled, u16 base_addr) Loading Loading @@ -2226,6 +2308,8 @@ static int qpnp_wled_parse_dt(struct qpnp_wled *wled) } } wled->stepper_en = of_property_read_bool(pdev->dev.of_node, "qcom,wled-stepper-en"); wled->disp_type_amoled = of_property_read_bool(pdev->dev.of_node, "qcom,disp-type-amoled"); if (wled->disp_type_amoled) { Loading Loading @@ -2562,6 +2646,7 @@ static int qpnp_wled_probe(struct platform_device *pdev) } wled->pmic_rev_id = get_revid_data(revid_node); of_node_put(revid_node); if (IS_ERR_OR_NULL(wled->pmic_rev_id)) { pr_err("Unable to get pmic_revid rc=%ld\n", PTR_ERR(wled->pmic_rev_id)); Loading @@ -2576,6 +2661,12 @@ static int qpnp_wled_probe(struct platform_device *pdev) pr_debug("PMIC subtype %d Digital major %d\n", wled->pmic_rev_id->pmic_subtype, wled->pmic_rev_id->rev4); wled->wq = alloc_ordered_workqueue("qpnp_wled_wq", WQ_HIGHPRI); if (!wled->wq) { pr_err("Unable to alloc workqueue for WLED\n"); return -ENOMEM; } prop = of_get_address_by_name(pdev->dev.of_node, QPNP_WLED_SINK_BASE, NULL, NULL); if (!prop) { Loading Loading @@ -2641,6 +2732,7 @@ static int qpnp_wled_probe(struct platform_device *pdev) led_classdev_unregister(&wled->cdev); wled_register_fail: cancel_work_sync(&wled->work); destroy_workqueue(wled->wq); mutex_destroy(&wled->lock); return rc; } Loading @@ -2656,6 +2748,7 @@ static int qpnp_wled_remove(struct platform_device *pdev) led_classdev_unregister(&wled->cdev); cancel_work_sync(&wled->work); destroy_workqueue(wled->wq); mutex_destroy(&wled->lock); return 0; Loading Loading
Documentation/devicetree/bindings/leds/leds-qpnp-wled.txt +3 −0 Original line number Diff line number Diff line Loading @@ -84,6 +84,9 @@ Optional properties for WLED: These codes will be mapped to the brightness level requested in the scale of 0-4095. Code entry is of 16 bit size. - qcom,wled-stepper-en : A boolean property to specify if stepper algorithm needs to be enabled. This needs the brightness map table to be specified. 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. Loading
drivers/leds/leds-qpnp-wled.c +95 −2 Original line number Diff line number Diff line Loading @@ -316,6 +316,7 @@ static struct wled_vref_setting vref_setting_pmi8998 = { * @ cdev - led class device * @ pdev - platform device * @ work - worker for led operation * @ wq - workqueue for setting brightness level * @ lock - mutex lock for exclusive access * @ fdbk_op - output feedback mode * @ dim_mode - dimming mode Loading Loading @@ -359,6 +360,7 @@ static struct wled_vref_setting vref_setting_pmi8998 = { * @ 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 * @ stepper_en - Flag to enable stepper algorithm * @ 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 Loading @@ -371,6 +373,7 @@ struct qpnp_wled { struct regmap *regmap; struct pmic_revid_data *pmic_rev_id; struct work_struct work; struct workqueue_struct *wq; struct mutex lock; struct mutex bus_lock; enum qpnp_wled_fdbk_op fdbk_op; Loading Loading @@ -415,6 +418,7 @@ struct qpnp_wled { bool disp_type_amoled; bool en_ext_pfet_sc_pro; bool prev_state; bool stepper_en; bool ovp_irq_disabled; bool auto_calib_enabled; bool auto_calib_done; Loading @@ -422,6 +426,21 @@ struct qpnp_wled { ktime_t start_ovp_fault_time; }; static int qpnp_wled_step_delay_us = 52000; module_param_named( total_step_delay_us, qpnp_wled_step_delay_us, int, 0600 ); static int qpnp_wled_step_size_threshold = 3; module_param_named( step_size_threshold, qpnp_wled_step_size_threshold, int, 0600 ); static int qpnp_wled_step_delay_gain = 2; module_param_named( step_delay_gain, qpnp_wled_step_delay_gain, int, 0600 ); /* helper to read a pmic register */ static int qpnp_wled_read_reg(struct qpnp_wled *wled, u16 addr, u8 *data) { Loading Loading @@ -583,6 +602,7 @@ static int qpnp_wled_set_level(struct qpnp_wled *wled, int level) return rc; } pr_debug("level:%d\n", level); return 0; } Loading Loading @@ -613,6 +633,65 @@ static int qpnp_wled_set_map_level(struct qpnp_wled *wled, int level) return 0; } static int qpnp_wled_set_step_level(struct qpnp_wled *wled, int new_level) { int rc, i, num_steps, delay_us; u16 level, start_level, end_level, step_size; bool level_inc = false; level = wled->prev_level; start_level = wled->brt_map_table[level]; end_level = wled->brt_map_table[new_level]; level_inc = (new_level > level); num_steps = abs(start_level - end_level); if (!num_steps) return 0; delay_us = qpnp_wled_step_delay_us / num_steps; pr_debug("level goes from [%d %d] num_steps: %d, delay: %d\n", start_level, end_level, num_steps, delay_us); if (delay_us < 500) { step_size = 1000 / delay_us; num_steps = num_steps / step_size; delay_us = 1000; } else { if (num_steps < qpnp_wled_step_size_threshold) delay_us *= qpnp_wled_step_delay_gain; step_size = 1; } i = start_level; while (num_steps--) { if (level_inc) i += step_size; else i -= step_size; rc = qpnp_wled_set_level(wled, i); if (rc < 0) return rc; if (delay_us > 0) { if (delay_us < 20000) usleep_range(delay_us, delay_us + 1); else msleep(delay_us / USEC_PER_MSEC); } } if (i != end_level) { i = end_level; rc = qpnp_wled_set_level(wled, i); if (rc < 0) return rc; } return 0; } static int qpnp_wled_psm_config(struct qpnp_wled *wled, bool enable) { int rc; Loading Loading @@ -999,6 +1078,9 @@ static void qpnp_wled_work(struct work_struct *work) level_255 = 255; pr_debug("level: %d level_255: %d\n", level, level_255); if (wled->stepper_en) rc = qpnp_wled_set_step_level(wled, level_255); else rc = qpnp_wled_set_map_level(wled, level_255); if (rc) { dev_err(&wled->pdev->dev, "wled set level failed\n"); Loading Loading @@ -1064,7 +1146,7 @@ static void qpnp_wled_set(struct led_classdev *led_cdev, level = wled->cdev.max_brightness; wled->cdev.brightness = level; schedule_work(&wled->work); queue_work(wled->wq, &wled->work); } static int qpnp_wled_set_disp(struct qpnp_wled *wled, u16 base_addr) Loading Loading @@ -2226,6 +2308,8 @@ static int qpnp_wled_parse_dt(struct qpnp_wled *wled) } } wled->stepper_en = of_property_read_bool(pdev->dev.of_node, "qcom,wled-stepper-en"); wled->disp_type_amoled = of_property_read_bool(pdev->dev.of_node, "qcom,disp-type-amoled"); if (wled->disp_type_amoled) { Loading Loading @@ -2562,6 +2646,7 @@ static int qpnp_wled_probe(struct platform_device *pdev) } wled->pmic_rev_id = get_revid_data(revid_node); of_node_put(revid_node); if (IS_ERR_OR_NULL(wled->pmic_rev_id)) { pr_err("Unable to get pmic_revid rc=%ld\n", PTR_ERR(wled->pmic_rev_id)); Loading @@ -2576,6 +2661,12 @@ static int qpnp_wled_probe(struct platform_device *pdev) pr_debug("PMIC subtype %d Digital major %d\n", wled->pmic_rev_id->pmic_subtype, wled->pmic_rev_id->rev4); wled->wq = alloc_ordered_workqueue("qpnp_wled_wq", WQ_HIGHPRI); if (!wled->wq) { pr_err("Unable to alloc workqueue for WLED\n"); return -ENOMEM; } prop = of_get_address_by_name(pdev->dev.of_node, QPNP_WLED_SINK_BASE, NULL, NULL); if (!prop) { Loading Loading @@ -2641,6 +2732,7 @@ static int qpnp_wled_probe(struct platform_device *pdev) led_classdev_unregister(&wled->cdev); wled_register_fail: cancel_work_sync(&wled->work); destroy_workqueue(wled->wq); mutex_destroy(&wled->lock); return rc; } Loading @@ -2656,6 +2748,7 @@ static int qpnp_wled_remove(struct platform_device *pdev) led_classdev_unregister(&wled->cdev); cancel_work_sync(&wled->work); destroy_workqueue(wled->wq); mutex_destroy(&wled->lock); return 0; Loading