Merge "leds: qpnp-wled: add stepper algorithm using brightness map"
This commit is contained in:
commit
04b6b652ac
2 changed files with 98 additions and 2 deletions
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -999,7 +1078,10 @@ static void qpnp_wled_work(struct work_struct *work)
|
|||
level_255 = 255;
|
||||
|
||||
pr_debug("level: %d level_255: %d\n", level, level_255);
|
||||
rc = qpnp_wled_set_map_level(wled, 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");
|
||||
goto unlock_mutex;
|
||||
|
@ -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)
|
||||
|
@ -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) {
|
||||
|
@ -2561,6 +2645,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));
|
||||
|
@ -2575,6 +2660,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) {
|
||||
|
@ -2640,6 +2731,7 @@ sysfs_fail:
|
|||
led_classdev_unregister(&wled->cdev);
|
||||
wled_register_fail:
|
||||
cancel_work_sync(&wled->work);
|
||||
destroy_workqueue(wled->wq);
|
||||
mutex_destroy(&wled->lock);
|
||||
return rc;
|
||||
}
|
||||
|
@ -2655,6 +2747,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…
Add table
Reference in a new issue