drivers/leds: Import Sony LED driver
*52.1.A.3.74 Signed-off-by: Sorayukii <sorayukii69@gmail.com>
This commit is contained in:
parent
713032beec
commit
bbec06cc19
4 changed files with 1303 additions and 43 deletions
|
|
@ -234,6 +234,7 @@ struct flash_node_data {
|
|||
int current_ma;
|
||||
int prev_current_ma;
|
||||
u8 duration;
|
||||
u32 duration_ms;
|
||||
u8 id;
|
||||
u8 ires_idx;
|
||||
u8 default_ires_idx;
|
||||
|
|
@ -349,6 +350,14 @@ static int max_ires_curr_ma_table[MAX_IRES_LEVELS] = {
|
|||
FLASH_LED_IRES7P5_MAX_CURR_MA, FLASH_LED_IRES5P0_MAX_CURR_MA
|
||||
};
|
||||
|
||||
static inline u8 get_safety_timer_code(u32 duration_ms)
|
||||
{
|
||||
if (!duration_ms)
|
||||
return 0;
|
||||
|
||||
return (duration_ms / 10) - 1;
|
||||
}
|
||||
|
||||
static inline int get_current_reg_code(int target_curr_ma, int ires_ua)
|
||||
{
|
||||
if (!ires_ua || !target_curr_ma || (target_curr_ma < (ires_ua / 1000)))
|
||||
|
|
@ -1802,10 +1811,150 @@ static ssize_t qpnp_flash_led_max_current_show(struct device *dev,
|
|||
return snprintf(buf, PAGE_SIZE, "%d\n", max_current);
|
||||
}
|
||||
|
||||
/* sysfs show function for flash_led_fault_status */
|
||||
static ssize_t qpnp_flash_led_fault_status_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct flash_switch_data *snode;
|
||||
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||
struct qpnp_flash_led *led;
|
||||
int rc;
|
||||
uint val;
|
||||
|
||||
snode = container_of(led_cdev, struct flash_switch_data, cdev);
|
||||
led = dev_get_drvdata(&snode->pdev->dev);
|
||||
|
||||
rc = regmap_read(led->regmap,
|
||||
FLASH_LED_REG_LED_STATUS1(led->base), &val);
|
||||
if (rc) {
|
||||
pr_err("Unable to read fault status rc(%d)\n", rc);
|
||||
return rc;
|
||||
}
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
}
|
||||
|
||||
/* sysfs store function for flash strobe */
|
||||
static ssize_t qpnp_flash_led_strobe_type_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct flash_node_data *fnode;
|
||||
unsigned long state;
|
||||
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||
ssize_t ret = -EINVAL;
|
||||
|
||||
ret = kstrtoul(buf, 10, &state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
fnode = container_of(led_cdev, struct flash_node_data, cdev);
|
||||
|
||||
/* '0' for sw strobe; '1' for hw strobe */
|
||||
if (state == 1)
|
||||
fnode->strobe_ctrl |= FLASH_LED_HW_SW_STROBE_SEL_BIT;
|
||||
else
|
||||
fnode->strobe_ctrl &= ~FLASH_LED_HW_SW_STROBE_SEL_BIT;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/* sysfs show function for flash duration */
|
||||
static ssize_t qpnp_flash_led_duration_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct qpnp_flash_led *led;
|
||||
struct flash_node_data *flash_node;
|
||||
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||
|
||||
flash_node = container_of(led_cdev, struct flash_node_data, cdev);
|
||||
led = dev_get_drvdata(&flash_node->pdev->dev);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", flash_node->duration_ms);
|
||||
}
|
||||
|
||||
/* sysfs store function for flash duration */
|
||||
static ssize_t qpnp_flash_led_duration_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct qpnp_flash_led *led;
|
||||
struct flash_node_data *flash_node;
|
||||
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||
ssize_t ret;
|
||||
unsigned long state;
|
||||
|
||||
flash_node = container_of(led_cdev, struct flash_node_data, cdev);
|
||||
led = dev_get_drvdata(&flash_node->pdev->dev);
|
||||
|
||||
ret = kstrtoul(buf, 10, &state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
flash_node->duration_ms = state;
|
||||
flash_node->duration = (u8)(get_safety_timer_code(flash_node->duration_ms) |
|
||||
FLASH_LED_SAFETY_TMR_ENABLE);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/* sysfs show function for flash ires_ua */
|
||||
static ssize_t qpnp_flash_led_ires_ua_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct qpnp_flash_led *led;
|
||||
struct flash_node_data *flash_node;
|
||||
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||
|
||||
flash_node = container_of(led_cdev, struct flash_node_data, cdev);
|
||||
led = dev_get_drvdata(&flash_node->pdev->dev);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n", flash_node->ires_ua);
|
||||
}
|
||||
|
||||
/* sysfs store function for flash ires_ua */
|
||||
static ssize_t qpnp_flash_led_ires_ua_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct qpnp_flash_led *led;
|
||||
struct flash_node_data *flash_node;
|
||||
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||
ssize_t ret;
|
||||
unsigned long state;
|
||||
|
||||
flash_node = container_of(led_cdev, struct flash_node_data, cdev);
|
||||
led = dev_get_drvdata(&flash_node->pdev->dev);
|
||||
|
||||
ret = kstrtoul(buf, 10, &state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
flash_node->default_ires_ua = flash_node->ires_ua = state;
|
||||
flash_node->default_ires_idx = flash_node->ires_idx = FLASH_LED_IRES_BASE -
|
||||
(flash_node->ires_ua - FLASH_LED_IRES_MIN_UA) / FLASH_LED_IRES_DIVISOR;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/* sysfs attributes exported by flash_led */
|
||||
static struct device_attribute qpnp_flash_led_attrs[] = {
|
||||
__ATTR(max_current, 0664, qpnp_flash_led_max_current_show, NULL),
|
||||
__ATTR(enable, 0664, NULL, qpnp_flash_led_prepare_store),
|
||||
__ATTR(fault_status, S_IRUSR | S_IRGRP,
|
||||
qpnp_flash_led_fault_status_show,
|
||||
NULL),
|
||||
};
|
||||
|
||||
static struct device_attribute qpnp_flash_fnode_attrs[] = {
|
||||
__ATTR(strobe, (S_IRUGO | S_IWUSR | S_IWGRP),
|
||||
NULL,
|
||||
qpnp_flash_led_strobe_type_store),
|
||||
__ATTR(duration, (S_IRUGO | S_IWUSR | S_IWGRP),
|
||||
qpnp_flash_led_duration_show,
|
||||
qpnp_flash_led_duration_store),
|
||||
__ATTR(ires_ua, (S_IRUGO | S_IWUSR | S_IWGRP),
|
||||
qpnp_flash_led_ires_ua_show,
|
||||
qpnp_flash_led_ires_ua_store),
|
||||
};
|
||||
|
||||
/* irq handler */
|
||||
|
|
@ -1876,14 +2025,6 @@ int qpnp_flash_led_unregister_irq_notifier(struct notifier_block *nb)
|
|||
return atomic_notifier_chain_unregister(&irq_notifier_list, nb);
|
||||
}
|
||||
|
||||
static inline u8 get_safety_timer_code(u32 duration_ms)
|
||||
{
|
||||
if (!duration_ms)
|
||||
return 0;
|
||||
|
||||
return (duration_ms / 10) - 1;
|
||||
}
|
||||
|
||||
static inline u8 get_vph_droop_thresh_code(u32 val_mv)
|
||||
{
|
||||
if (!val_mv)
|
||||
|
|
@ -1989,6 +2130,7 @@ static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led,
|
|||
fnode->duration = FLASH_LED_SAFETY_TMR_DISABLED;
|
||||
rc = of_property_read_u32(node, "qcom,duration-ms", &val);
|
||||
if (!rc) {
|
||||
fnode->duration_ms = val;
|
||||
fnode->duration = get_safety_timer_code(val);
|
||||
if (fnode->duration)
|
||||
fnode->duration |= FLASH_LED_SAFETY_TMR_ENABLE;
|
||||
|
|
@ -2062,7 +2204,7 @@ static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led,
|
|||
"qcom,hw-strobe-edge-trigger");
|
||||
active_high = !of_property_read_bool(node,
|
||||
"qcom,hw-strobe-active-low");
|
||||
hw_strobe = 1;
|
||||
hw_strobe = !of_property_read_bool(node, "somc,sw-strobe-init");
|
||||
} else if (fnode->strobe_sel == LPG_STROBE) {
|
||||
/* LPG strobe requires level trigger and active high */
|
||||
edge_trigger = 0;
|
||||
|
|
@ -2646,6 +2788,29 @@ static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int qpnp_flash_led_init_strobe_settings(struct qpnp_flash_led *led)
|
||||
{
|
||||
int rc = 0, i, addr_offset;
|
||||
|
||||
rc = qpnp_flash_led_masked_write(led,
|
||||
FLASH_LED_REG_STROBE_CFG(led->base),
|
||||
FLASH_LED_STROBE_MASK,
|
||||
led->pdata->hw_strobe_option);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
for (i = 0; i < led->num_fnodes; i++) {
|
||||
addr_offset = led->fnode[i].id;
|
||||
rc = qpnp_flash_led_masked_write(led,
|
||||
FLASH_LED_REG_STROBE_CTRL(led->base + addr_offset),
|
||||
FLASH_LED_STROBE_MASK, led->fnode[i].strobe_ctrl);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int qpnp_flash_led_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct qpnp_flash_led *led;
|
||||
|
|
@ -2807,6 +2972,23 @@ static int qpnp_flash_led_probe(struct platform_device *pdev)
|
|||
goto unreg_notifier;
|
||||
}
|
||||
|
||||
rc = qpnp_flash_led_init_strobe_settings(led);
|
||||
if (rc < 0) {
|
||||
pr_err("Failed to initialize flash strobe, rc=%d\n", rc);
|
||||
goto unreg_notifier;
|
||||
}
|
||||
|
||||
for (i = 0; i < led->num_fnodes; i++) {
|
||||
for (j = 0; j < ARRAY_SIZE(qpnp_flash_fnode_attrs); j++) {
|
||||
rc = sysfs_create_file(&led->fnode[i].cdev.dev->kobj,
|
||||
&qpnp_flash_fnode_attrs[j].attr);
|
||||
if (rc < 0) {
|
||||
pr_err("sysfs creation failed, rc=%d\n", rc);
|
||||
goto sysfs_fnode_fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < led->num_snodes; i++) {
|
||||
for (j = 0; j < ARRAY_SIZE(qpnp_flash_led_attrs); j++) {
|
||||
rc = sysfs_create_file(&led->snode[i].cdev.dev->kobj,
|
||||
|
|
@ -2824,6 +3006,16 @@ static int qpnp_flash_led_probe(struct platform_device *pdev)
|
|||
|
||||
return 0;
|
||||
|
||||
sysfs_fnode_fail:
|
||||
for (--j; j >= 0; j--)
|
||||
sysfs_remove_file(&led->fnode[i].cdev.dev->kobj,
|
||||
&qpnp_flash_fnode_attrs[j].attr);
|
||||
|
||||
for (--i; i >= 0; i--) {
|
||||
for (j = 0; j < ARRAY_SIZE(qpnp_flash_fnode_attrs); j++)
|
||||
sysfs_remove_file(&led->fnode[i].cdev.dev->kobj,
|
||||
&qpnp_flash_fnode_attrs[j].attr);
|
||||
}
|
||||
sysfs_fail:
|
||||
for (--j; j >= 0; j--)
|
||||
sysfs_remove_file(&led->snode[i].cdev.dev->kobj,
|
||||
|
|
@ -2868,6 +3060,13 @@ static int qpnp_flash_led_remove(struct platform_device *pdev)
|
|||
led_classdev_unregister(&led->snode[--i].cdev);
|
||||
|
||||
i = led->num_fnodes;
|
||||
|
||||
for (i = 0; i < led->num_fnodes; i++) {
|
||||
for (j = 0; j < ARRAY_SIZE(qpnp_flash_fnode_attrs); j++)
|
||||
sysfs_remove_file(&led->fnode[i].cdev.dev->kobj,
|
||||
&qpnp_flash_fnode_attrs[j].attr);
|
||||
}
|
||||
|
||||
while (i > 0)
|
||||
led_classdev_unregister(&led->fnode[--i].cdev);
|
||||
|
||||
|
|
|
|||
|
|
@ -168,7 +168,7 @@
|
|||
/* Other definitions */
|
||||
#define HAP_BRAKE_PAT_LEN 4
|
||||
#define HAP_WAVE_SAMP_LEN 8
|
||||
#define NUM_WF_SET 4
|
||||
#define NUM_WF_SET 10
|
||||
#define HAP_WAVE_SAMP_SET_LEN (HAP_WAVE_SAMP_LEN * NUM_WF_SET)
|
||||
#define HAP_RATE_CFG_STEP_US 5
|
||||
#define HAP_WAVE_PLAY_RATE_US_MIN 0
|
||||
|
|
@ -176,6 +176,10 @@
|
|||
#define HAP_WAVE_PLAY_RATE_US_MAX 20475
|
||||
#define HAP_MAX_PLAY_TIME_MS 15000
|
||||
|
||||
/* Auto resonance error retry count */
|
||||
#define HAP_AUTO_RES_MAX_CNT_AUDIO 6
|
||||
#define HAP_AUTO_RES_ERR_RETRY_MAX_CNT_AUDIO 500
|
||||
|
||||
enum hap_brake_pat {
|
||||
NO_BRAKE = 0,
|
||||
BRAKE_VMAX_4,
|
||||
|
|
@ -358,6 +362,9 @@ struct hap_chip {
|
|||
bool play_irq_en;
|
||||
bool auto_res_err_recovery_hw;
|
||||
bool vcc_pon_enabled;
|
||||
bool auto_res_err_audio;
|
||||
u8 auto_res_cnt_audio;
|
||||
u16 auto_res_err_retry_cnt_audio;
|
||||
};
|
||||
|
||||
static int qpnp_haptics_parse_buffer_dt(struct hap_chip *chip);
|
||||
|
|
@ -596,16 +603,22 @@ static void qpnp_haptics_update_lra_frequency(struct hap_chip *chip)
|
|||
if ((val & AUTO_RES_ERROR_BIT) ||
|
||||
((play_rate_code <= chip->drive_period_code_min_limit) ||
|
||||
(play_rate_code >= chip->drive_period_code_max_limit))) {
|
||||
if (val & AUTO_RES_ERROR_BIT)
|
||||
if (val & AUTO_RES_ERROR_BIT) {
|
||||
pr_debug("Auto-resonance error %x\n", val);
|
||||
else
|
||||
chip->auto_res_cnt_audio = 0;
|
||||
} else
|
||||
pr_debug("play rate %x out of bounds [min: 0x%x, max: 0x%x]\n",
|
||||
play_rate_code,
|
||||
chip->drive_period_code_min_limit,
|
||||
chip->drive_period_code_max_limit);
|
||||
rc = qpnp_haptics_auto_res_enable(chip, false);
|
||||
if (rc < 0)
|
||||
pr_debug("Auto-resonance disable failed\n");
|
||||
|
||||
if (chip->play_mode == HAP_AUDIO) {
|
||||
chip->auto_res_err_audio = true;
|
||||
} else {
|
||||
rc = qpnp_haptics_auto_res_enable(chip, false);
|
||||
if (rc < 0)
|
||||
pr_debug("Auto-resonance disable failed\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -755,10 +768,11 @@ static int qpnp_haptics_play(struct hap_chip *chip, bool enable)
|
|||
|
||||
if (chip->play_mode == HAP_BUFFER)
|
||||
time_ms = get_buffer_mode_duration(chip);
|
||||
hrtimer_start(&chip->stop_timer,
|
||||
ktime_set(time_ms / MSEC_PER_SEC,
|
||||
(time_ms % MSEC_PER_SEC) * NSEC_PER_MSEC),
|
||||
HRTIMER_MODE_REL);
|
||||
if (chip->play_mode != HAP_AUDIO)
|
||||
hrtimer_start(&chip->stop_timer,
|
||||
ktime_set(time_ms / MSEC_PER_SEC,
|
||||
(time_ms % MSEC_PER_SEC) * NSEC_PER_MSEC),
|
||||
HRTIMER_MODE_REL);
|
||||
|
||||
rc = qpnp_haptics_auto_res_enable(chip, true);
|
||||
if (rc < 0) {
|
||||
|
|
@ -766,6 +780,12 @@ static int qpnp_haptics_play(struct hap_chip *chip, bool enable)
|
|||
goto out;
|
||||
}
|
||||
|
||||
if (chip->play_mode == HAP_AUDIO) {
|
||||
chip->auto_res_err_audio = false;
|
||||
chip->auto_res_cnt_audio = 0;
|
||||
chip->auto_res_err_retry_cnt_audio = 0;
|
||||
}
|
||||
|
||||
if (is_sw_lra_auto_resonance_control(chip))
|
||||
hrtimer_start(&chip->auto_res_err_poll_timer,
|
||||
ktime_set(0, AUTO_RES_ERR_POLL_TIME_NS),
|
||||
|
|
@ -844,6 +864,33 @@ static enum hrtimer_restart hap_auto_res_err_poll_timer(struct hrtimer *timer)
|
|||
{
|
||||
struct hap_chip *chip = container_of(timer, struct hap_chip,
|
||||
auto_res_err_poll_timer);
|
||||
int rc = 0;
|
||||
u8 val = 0;
|
||||
if (chip->play_mode == HAP_AUDIO) {
|
||||
if (chip->auto_res_err_audio) {
|
||||
val = 0;
|
||||
rc = qpnp_haptics_write_reg(chip, HAP_EN_CTL_REG(chip), &val, 1);
|
||||
if (rc < 0)
|
||||
pr_debug("Error in disabling module forcibly\n");
|
||||
val = HAP_EN_BIT;
|
||||
rc = qpnp_haptics_write_reg(chip, HAP_EN_CTL_REG(chip), &val, 1);
|
||||
if (rc < 0)
|
||||
pr_debug("Error in enbling module forcibly\n");
|
||||
chip->auto_res_err_audio = false;
|
||||
chip->auto_res_err_retry_cnt_audio++;
|
||||
} else {
|
||||
chip->auto_res_cnt_audio++;
|
||||
}
|
||||
qpnp_haptics_update_lra_frequency(chip);
|
||||
if ((HAP_AUTO_RES_MAX_CNT_AUDIO > chip->auto_res_cnt_audio)
|
||||
&& (HAP_AUTO_RES_ERR_RETRY_MAX_CNT_AUDIO > chip->auto_res_err_retry_cnt_audio)) {
|
||||
hrtimer_forward(&chip->auto_res_err_poll_timer, ktime_get(),
|
||||
ktime_set(0, AUTO_RES_ERR_POLL_TIME_NS));
|
||||
return HRTIMER_RESTART;
|
||||
} else {
|
||||
return HRTIMER_NORESTART;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(chip->status_flags & AUTO_RESONANCE_ENABLED))
|
||||
return HRTIMER_NORESTART;
|
||||
|
|
@ -1335,24 +1382,25 @@ static irqreturn_t qpnp_haptics_play_irq_handler(int irq, void *data)
|
|||
struct hap_chip *chip = data;
|
||||
int rc;
|
||||
|
||||
if (chip->module_en == 0)
|
||||
goto irq_handled;
|
||||
|
||||
if (chip->play_mode != HAP_BUFFER)
|
||||
goto irq_handled;
|
||||
|
||||
if (chip->wave_samp[chip->wave_samp_idx + HAP_WAVE_SAMP_LEN] > 0) {
|
||||
chip->wave_samp_idx += HAP_WAVE_SAMP_LEN;
|
||||
if (chip->wave_samp_idx >= ARRAY_SIZE(chip->wave_samp)) {
|
||||
pr_debug("Samples over\n");
|
||||
} else {
|
||||
pr_debug("moving to next sample set %d\n",
|
||||
chip->wave_samp_idx);
|
||||
chip->wave_samp_idx += HAP_WAVE_SAMP_LEN;
|
||||
if (chip->wave_samp_idx >= ARRAY_SIZE(chip->wave_samp)) {
|
||||
pr_debug("Samples over\n");
|
||||
} else {
|
||||
pr_debug("moving to next sample set %d\n",
|
||||
chip->wave_samp_idx);
|
||||
|
||||
/* Moving to next set of wave sample */
|
||||
rc = qpnp_haptics_buffer_config(chip, NULL, false);
|
||||
if (rc < 0) {
|
||||
pr_err("Error in configuring buffer, rc=%d\n",
|
||||
rc);
|
||||
goto irq_handled;
|
||||
}
|
||||
/* Moving to next set of wave sample */
|
||||
rc = qpnp_haptics_buffer_config(chip, NULL, false);
|
||||
if (rc < 0) {
|
||||
pr_err("Error in configuring buffer, rc=%d\n",
|
||||
rc);
|
||||
goto irq_handled;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1529,6 +1577,8 @@ static ssize_t qpnp_haptics_store_activate(struct device *dev,
|
|||
atomic_set(&chip->state, 1);
|
||||
schedule_work(&chip->haptics_work);
|
||||
} else {
|
||||
if (is_sw_lra_auto_resonance_control(chip))
|
||||
hrtimer_cancel(&chip->auto_res_err_poll_timer);
|
||||
rc = qpnp_haptics_mod_enable(chip, false);
|
||||
if (rc < 0) {
|
||||
pr_err("Error in disabling module, rc=%d\n", rc);
|
||||
|
|
@ -1567,6 +1617,7 @@ static ssize_t qpnp_haptics_store_play_mode(struct device *dev,
|
|||
struct hap_chip *chip = container_of(cdev, struct hap_chip, cdev);
|
||||
char str[HAP_STR_SIZE + 1];
|
||||
int rc = 0, temp, old_mode;
|
||||
u8 val = 0;
|
||||
|
||||
rc = parse_string(buf, str);
|
||||
if (rc < 0)
|
||||
|
|
@ -1583,6 +1634,8 @@ static ssize_t qpnp_haptics_store_play_mode(struct device *dev,
|
|||
else
|
||||
return -EINVAL;
|
||||
|
||||
rc = qpnp_haptics_write_reg(chip, HAP_BRAKE_REG(chip), &val, 1);
|
||||
|
||||
if (temp == chip->play_mode)
|
||||
return count;
|
||||
|
||||
|
|
@ -1598,7 +1651,7 @@ static ssize_t qpnp_haptics_store_play_mode(struct device *dev,
|
|||
}
|
||||
}
|
||||
|
||||
rc = qpnp_haptics_buffer_config(chip, NULL, true);
|
||||
rc = qpnp_haptics_buffer_config(chip, NULL, false);
|
||||
} else if (temp == HAP_PWM) {
|
||||
rc = qpnp_haptics_parse_pwm_dt(chip);
|
||||
if (!rc)
|
||||
|
|
@ -1669,6 +1722,8 @@ static ssize_t qpnp_haptics_store_wf_samp(struct device *dev,
|
|||
for (i = 0; i < ARRAY_SIZE(chip->wave_samp); i++)
|
||||
chip->wave_samp[i] = samp[i];
|
||||
|
||||
chip->wave_samp_idx = 0;
|
||||
|
||||
rc = qpnp_haptics_buffer_config(chip, NULL, false);
|
||||
if (rc < 0) {
|
||||
pr_err("Error in configuring buffer mode %d\n", rc);
|
||||
|
|
@ -1971,7 +2026,10 @@ static int qpnp_haptics_parse_buffer_dt(struct hap_chip *chip)
|
|||
{
|
||||
struct device_node *node = chip->pdev->dev.of_node;
|
||||
u32 temp;
|
||||
/*
|
||||
int rc, i, wf_samp_len;
|
||||
*/
|
||||
int rc, wf_samp_len;
|
||||
|
||||
if (chip->wave_rep_cnt > 0 || chip->wave_s_rep_cnt > 0)
|
||||
return 0;
|
||||
|
|
@ -2012,8 +2070,10 @@ static int qpnp_haptics_parse_buffer_dt(struct hap_chip *chip)
|
|||
}
|
||||
} else {
|
||||
/* Use default values */
|
||||
/*
|
||||
for (i = 0; i < HAP_WAVE_SAMP_LEN; i++)
|
||||
chip->wave_samp[i] = HAP_WF_SAMP_MAX;
|
||||
*/
|
||||
|
||||
wf_samp_len = HAP_WAVE_SAMP_LEN;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -238,6 +238,13 @@
|
|||
#define QPNP_WLED_AVDD_MV_TO_REG(val) \
|
||||
((val - QPNP_WLED_AVDD_MIN_MV) / QPNP_WLED_AVDD_STEP_MV)
|
||||
|
||||
#define QPNP_WLED_CURR_SCALE_MAX 100
|
||||
#define QPNP_WLED_BL_SCALE_MAX 1000
|
||||
#define QPNP_WLED_BUFF_SIZE 128
|
||||
|
||||
#define BR_MAX_FIGURE 9
|
||||
#define AREA_COUNT_MAX 9999999
|
||||
|
||||
/* output feedback mode */
|
||||
enum qpnp_wled_fdbk_op {
|
||||
QPNP_WLED_FDBK_AUTO,
|
||||
|
|
@ -312,6 +319,12 @@ static struct wled_vref_setting vref_setting_pmi8998 = {
|
|||
60000, 397500, 22500, 127500,
|
||||
};
|
||||
|
||||
static unsigned long *area_count;
|
||||
static int *area_count_table;
|
||||
static int area_count_table_size;
|
||||
static int now_area;
|
||||
static unsigned long start_jiffies;
|
||||
|
||||
/**
|
||||
* qpnp_wled - wed data structure
|
||||
* @ cdev - led class device
|
||||
|
|
@ -426,6 +439,11 @@ struct qpnp_wled {
|
|||
bool auto_calib_done;
|
||||
bool module_dis_perm;
|
||||
ktime_t start_ovp_fault_time;
|
||||
int init_br_ua;
|
||||
bool bl_scale_enabled;
|
||||
int bl_scale;
|
||||
bool calc_curr;
|
||||
int curr_scale;
|
||||
};
|
||||
|
||||
static int qpnp_wled_step_delay_us = 52000;
|
||||
|
|
@ -576,10 +594,20 @@ static int qpnp_wled_set_level(struct qpnp_wled *wled, int level)
|
|||
u8 reg;
|
||||
u16 low_limit = WLED_MAX_LEVEL_4095 * 4 / 1000;
|
||||
|
||||
if (wled->calc_curr &&
|
||||
wled->curr_scale != QPNP_WLED_CURR_SCALE_MAX)
|
||||
level = level * wled->curr_scale / QPNP_WLED_CURR_SCALE_MAX;
|
||||
|
||||
if (wled->bl_scale_enabled && wled->bl_scale > 0 &&
|
||||
wled->bl_scale < QPNP_WLED_BL_SCALE_MAX)
|
||||
level = level * wled->bl_scale / QPNP_WLED_BL_SCALE_MAX;
|
||||
|
||||
/* WLED's lower limit of operation is 0.4% */
|
||||
if (level > 0 && level < low_limit)
|
||||
level = low_limit;
|
||||
|
||||
pr_debug("%s: brightness=%d level=%d\n",
|
||||
__func__, wled->cdev.brightness, level);
|
||||
/* set brightness registers */
|
||||
for (i = 0; i < wled->max_strings; i++) {
|
||||
reg = level & QPNP_WLED_BRIGHT_LSB_MASK;
|
||||
|
|
@ -1067,7 +1095,11 @@ static ssize_t qpnp_wled_fs_curr_ua_store(struct device *dev,
|
|||
else if (data > QPNP_WLED_FS_CURR_MAX_UA)
|
||||
data = QPNP_WLED_FS_CURR_MAX_UA;
|
||||
|
||||
reg = data / QPNP_WLED_FS_CURR_STEP_UA;
|
||||
if (wled->calc_curr)
|
||||
reg = (data + (QPNP_WLED_FS_CURR_STEP_UA - 1)) /
|
||||
QPNP_WLED_FS_CURR_STEP_UA;
|
||||
else
|
||||
reg = data / QPNP_WLED_FS_CURR_STEP_UA;
|
||||
rc = qpnp_wled_masked_write_reg(wled,
|
||||
QPNP_WLED_FS_CURR_REG(wled->sink_base, i),
|
||||
QPNP_WLED_FS_CURR_MASK, reg);
|
||||
|
|
@ -1086,6 +1118,148 @@ static ssize_t qpnp_wled_fs_curr_ua_store(struct device *dev,
|
|||
return count;
|
||||
}
|
||||
|
||||
static ssize_t qpnp_wled_bl_scale_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct qpnp_wled *wled = dev_get_drvdata(dev);
|
||||
if (!wled->bl_scale_enabled)
|
||||
wled->bl_scale = QPNP_WLED_BL_SCALE_MAX;
|
||||
dev_dbg(&wled->pdev->dev, "%s: bl_scale = %d\n", __func__, wled->bl_scale);
|
||||
|
||||
return snprintf(buf, QPNP_WLED_BUFF_SIZE, "%u\n", wled->bl_scale);
|
||||
}
|
||||
|
||||
static ssize_t qpnp_wled_bl_scale_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
struct qpnp_wled *wled = dev_get_drvdata(dev);
|
||||
unsigned long scale = 0;
|
||||
ssize_t ret = -EINVAL;
|
||||
|
||||
if (!wled->bl_scale_enabled) {
|
||||
wled->bl_scale = QPNP_WLED_BL_SCALE_MAX;
|
||||
dev_err(&wled->pdev->dev, "Sysfs bl_scale is not enabled\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = kstrtoul(buf, 10, &scale);
|
||||
if (!ret) {
|
||||
ret = size;
|
||||
if (scale > QPNP_WLED_BL_SCALE_MAX)
|
||||
scale = QPNP_WLED_BL_SCALE_MAX;
|
||||
wled->bl_scale = scale;
|
||||
dev_dbg(&wled->pdev->dev, "%s: bl_scale = %d\n", __func__, wled->bl_scale);
|
||||
} else {
|
||||
dev_err(&wled->pdev->dev, "Failure to set sysfs bl_scale\n");
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void update_areacount(int new_area)
|
||||
{
|
||||
unsigned long now;
|
||||
unsigned long duration;
|
||||
|
||||
now = jiffies;
|
||||
duration = (now - start_jiffies) >= 0 ?
|
||||
(now - start_jiffies) : (now + start_jiffies);
|
||||
area_count[now_area] = area_count[now_area]
|
||||
+ jiffies_to_msecs(duration);
|
||||
|
||||
now_area = new_area;
|
||||
start_jiffies = now;
|
||||
}
|
||||
|
||||
static ssize_t qpnp_wled_area_count_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct qpnp_wled *wled = dev_get_drvdata(dev);
|
||||
int i;
|
||||
char area_count_str[QPNP_WLED_BUFF_SIZE];
|
||||
char count_data[BR_MAX_FIGURE];
|
||||
|
||||
if (!area_count_table_size)
|
||||
return snprintf(buf, QPNP_WLED_BUFF_SIZE, "area_count is not supported\n");
|
||||
|
||||
mutex_lock(&wled->cdev.led_access);
|
||||
/* fixed statistics */
|
||||
update_areacount(now_area);
|
||||
|
||||
memset(area_count_str, 0, sizeof(char) * QPNP_WLED_BUFF_SIZE);
|
||||
for (i = 0; i < area_count_table_size; i++) {
|
||||
dev_dbg(&wled->pdev->dev, "%ld, ", area_count[i]);
|
||||
memset(count_data, 0, sizeof(char) * BR_MAX_FIGURE);
|
||||
if (area_count[i] > AREA_COUNT_MAX) {
|
||||
/* over 167 min */
|
||||
area_count[i] = AREA_COUNT_MAX;
|
||||
}
|
||||
if (i == 0) {
|
||||
snprintf(count_data, BR_MAX_FIGURE,
|
||||
"%ld", area_count[i]);
|
||||
strlcpy(area_count_str, count_data, QPNP_WLED_BUFF_SIZE);
|
||||
} else {
|
||||
snprintf(count_data, BR_MAX_FIGURE,
|
||||
",%ld", area_count[i]);
|
||||
strlcat(area_count_str, count_data, QPNP_WLED_BUFF_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
memset(area_count, 0, sizeof(unsigned long) * area_count_table_size);
|
||||
mutex_unlock(&wled->cdev.led_access);
|
||||
|
||||
return snprintf(buf, QPNP_WLED_BUFF_SIZE, "%s\n", area_count_str);
|
||||
}
|
||||
|
||||
static ssize_t qpnp_wled_en_cabc_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct qpnp_wled *wled = dev_get_drvdata(dev);
|
||||
char *str = wled->en_cabc ? "true" : "false";
|
||||
|
||||
dev_dbg(&wled->pdev->dev, "%s: en_cabc:%s\n", __func__, str);
|
||||
|
||||
return snprintf(buf, QPNP_WLED_BUFF_SIZE, "%s\n", str);
|
||||
}
|
||||
|
||||
static ssize_t qpnp_wled_en_cabc_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
struct qpnp_wled *wled = dev_get_drvdata(dev);
|
||||
int data, i, rc;
|
||||
u8 reg, mask;
|
||||
|
||||
rc = kstrtoint(buf, 10, &data);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (data)
|
||||
wled->en_cabc = true;
|
||||
else
|
||||
wled->en_cabc = false;
|
||||
|
||||
pr_info("%s: set wled->en_cabc:%d\n", __func__, wled->en_cabc);
|
||||
for (i = 0; i < wled->num_strings; i++) {
|
||||
reg = wled->en_cabc ? (1 << QPNP_WLED_CABC_SHIFT) : 0;
|
||||
mask = QPNP_WLED_CABC_MASK;
|
||||
rc = qpnp_wled_masked_write_reg(wled,
|
||||
QPNP_WLED_CABC_REG(wled->sink_base, i),
|
||||
mask, reg);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = qpnp_wled_sync_reg_toggle(wled);
|
||||
if (rc < 0) {
|
||||
dev_err(&wled->pdev->dev, "Failed to toggle sync reg %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/* sysfs attributes exported by wled */
|
||||
static struct device_attribute qpnp_wled_attrs[] = {
|
||||
__ATTR(dump_regs, 0664, qpnp_wled_dump_regs_show, NULL),
|
||||
|
|
@ -1098,8 +1272,42 @@ static struct device_attribute qpnp_wled_attrs[] = {
|
|||
__ATTR(ramp_step, 0664, qpnp_wled_ramp_step_show,
|
||||
qpnp_wled_ramp_step_store),
|
||||
__ATTR(secure_mode, 0664, NULL, qpnp_wled_irq_control),
|
||||
__ATTR(bl_scale, (S_IRUGO | S_IWUSR | S_IWGRP),
|
||||
qpnp_wled_bl_scale_show,
|
||||
qpnp_wled_bl_scale_store),
|
||||
__ATTR(area_count, (S_IRUSR | S_IRGRP),
|
||||
qpnp_wled_area_count_show,
|
||||
NULL),
|
||||
__ATTR(en_cabc, (S_IRUGO | S_IWUSR | S_IWGRP),
|
||||
qpnp_wled_en_cabc_show,
|
||||
qpnp_wled_en_cabc_store),
|
||||
};
|
||||
|
||||
static int get_area(int level)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < area_count_table_size; i++) {
|
||||
if (i == 0) {
|
||||
if (level == area_count_table[i])
|
||||
break;
|
||||
} else {
|
||||
if (level <= area_count_table[i])
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static void qpnp_wled_update_area(int level)
|
||||
{
|
||||
int new_area = get_area(level);
|
||||
|
||||
if (now_area != new_area)
|
||||
update_areacount(new_area);
|
||||
}
|
||||
|
||||
/* worker for setting wled brightness */
|
||||
static void qpnp_wled_work(struct work_struct *work)
|
||||
{
|
||||
|
|
@ -1116,6 +1324,7 @@ static void qpnp_wled_work(struct work_struct *work)
|
|||
}
|
||||
|
||||
level = wled->cdev.brightness;
|
||||
qpnp_wled_update_area(level);
|
||||
|
||||
if (wled->brt_map_table) {
|
||||
/*
|
||||
|
|
@ -1164,6 +1373,7 @@ static void qpnp_wled_work(struct work_struct *work)
|
|||
level ? "en" : "dis");
|
||||
goto unlock_mutex;
|
||||
}
|
||||
usleep_range(100, 101);
|
||||
}
|
||||
|
||||
wled->prev_state = !!level;
|
||||
|
|
@ -2146,8 +2356,11 @@ static int qpnp_wled_config(struct qpnp_wled *wled)
|
|||
/* FULL SCALE CURRENT */
|
||||
if (wled->fs_curr_ua > QPNP_WLED_FS_CURR_MAX_UA)
|
||||
wled->fs_curr_ua = QPNP_WLED_FS_CURR_MAX_UA;
|
||||
|
||||
reg = wled->fs_curr_ua / QPNP_WLED_FS_CURR_STEP_UA;
|
||||
if (wled->calc_curr)
|
||||
reg = (wled->fs_curr_ua + (QPNP_WLED_FS_CURR_STEP_UA - 1)) /
|
||||
QPNP_WLED_FS_CURR_STEP_UA;
|
||||
else
|
||||
reg = wled->fs_curr_ua / QPNP_WLED_FS_CURR_STEP_UA;
|
||||
mask = QPNP_WLED_FS_CURR_MASK;
|
||||
rc = qpnp_wled_masked_write_reg(wled,
|
||||
QPNP_WLED_FS_CURR_REG(wled->sink_base, i),
|
||||
|
|
@ -2304,6 +2517,39 @@ static int qpnp_wled_config(struct qpnp_wled *wled)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void qpnp_wled_set_init_br(struct qpnp_wled *wled)
|
||||
{
|
||||
int calc_init_br;
|
||||
if (wled->fs_curr_ua) {
|
||||
calc_init_br = (wled->cdev.max_brightness * wled->init_br_ua)
|
||||
/ wled->fs_curr_ua;
|
||||
} else {
|
||||
calc_init_br = (wled->cdev.max_brightness * wled->init_br_ua)
|
||||
/ QPNP_WLED_FS_CURR_MAX_UA;
|
||||
}
|
||||
qpnp_wled_set(&wled->cdev, calc_init_br);
|
||||
}
|
||||
|
||||
static int qpnp_wled_set_scale(struct qpnp_wled *wled)
|
||||
{
|
||||
u8 reg = 0;
|
||||
int rc, curr;
|
||||
|
||||
wled->curr_scale = QPNP_WLED_CURR_SCALE_MAX;
|
||||
rc = qpnp_wled_read_reg(wled, QPNP_WLED_FS_CURR_REG(wled->sink_base,
|
||||
wled->strings[0]), ®);
|
||||
if (rc)
|
||||
return rc;
|
||||
reg &= QPNP_WLED_FS_CURR_MASK;
|
||||
curr = reg * QPNP_WLED_FS_CURR_STEP_UA;
|
||||
if (curr > wled->fs_curr_ua)
|
||||
wled->curr_scale = (wled->fs_curr_ua *
|
||||
QPNP_WLED_CURR_SCALE_MAX) / curr;
|
||||
dev_dbg(&wled->pdev->dev, "%s: curr = %d fs_curr_ua = %d curr_scale = %d\n",
|
||||
__func__, curr, wled->fs_curr_ua, wled->curr_scale);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* parse wled dtsi parameters */
|
||||
static int qpnp_wled_parse_dt(struct qpnp_wled *wled)
|
||||
{
|
||||
|
|
@ -2634,6 +2880,10 @@ static int qpnp_wled_parse_dt(struct qpnp_wled *wled)
|
|||
if (!rc)
|
||||
wled->cons_sync_write_delay_us = temp_val;
|
||||
|
||||
wled->calc_curr = of_property_read_bool(pdev->dev.of_node,
|
||||
"somc,calc-curr");
|
||||
wled->bl_scale_enabled = of_property_read_bool(pdev->dev.of_node,
|
||||
"somc,bl-scale-enabled");
|
||||
wled->en_9b_dim_res = of_property_read_bool(pdev->dev.of_node,
|
||||
"qcom,en-9b-dim-res");
|
||||
wled->en_phase_stag = of_property_read_bool(pdev->dev.of_node,
|
||||
|
|
@ -2677,6 +2927,51 @@ static int qpnp_wled_parse_dt(struct qpnp_wled *wled)
|
|||
|
||||
wled->auto_calib_enabled = of_property_read_bool(pdev->dev.of_node,
|
||||
"qcom,auto-calibration-enable");
|
||||
|
||||
wled->init_br_ua = LED_OFF;
|
||||
rc = of_property_read_u32(pdev->dev.of_node,
|
||||
"somc,init-br-ua", &temp_val);
|
||||
if (!rc) {
|
||||
wled->init_br_ua = temp_val;
|
||||
} else if (rc != -EINVAL) {
|
||||
dev_err(&pdev->dev, "Unable to read init ua\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = of_property_read_u32(pdev->dev.of_node,
|
||||
"somc,area_count_table_size", &temp_val);
|
||||
if (!rc) {
|
||||
area_count_table_size = temp_val;
|
||||
} else if (rc != -EINVAL) {
|
||||
dev_err(&pdev->dev, "Unable to read area_count_table_size\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (area_count_table_size > 0) {
|
||||
area_count_table = devm_kzalloc(&pdev->dev,
|
||||
sizeof(int) * area_count_table_size, GFP_KERNEL);
|
||||
if (!area_count_table)
|
||||
return -ENOMEM;
|
||||
|
||||
area_count = devm_kzalloc(&pdev->dev,
|
||||
sizeof(unsigned long) * area_count_table_size, GFP_KERNEL);
|
||||
if (!area_count_table)
|
||||
return -ENOMEM;
|
||||
|
||||
rc = of_property_read_u32_array(pdev->dev.of_node, "somc,area_count_table",
|
||||
area_count_table, area_count_table_size);
|
||||
if (rc < 0) {
|
||||
dev_err(&pdev->dev, "Unable to read somc,area_count_table\n");
|
||||
temp_val = (WLED_MAX_LEVEL_4095 % (area_count_table_size - 1) > 0 ?
|
||||
((WLED_MAX_LEVEL_4095 / (area_count_table_size - 1)) + 1) :
|
||||
(WLED_MAX_LEVEL_4095 / (area_count_table_size - 1)));
|
||||
area_count_table[0] = 0;
|
||||
for (i = 1; i < area_count_table_size; i++) {
|
||||
area_count_table[i] = temp_val * i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -2758,6 +3053,13 @@ static int qpnp_wled_probe(struct platform_device *pdev)
|
|||
dev_err(&pdev->dev, "wled config failed\n");
|
||||
return rc;
|
||||
}
|
||||
if (wled->calc_curr) {
|
||||
rc = qpnp_wled_set_scale(wled);
|
||||
if (rc) {
|
||||
dev_err(&pdev->dev, "wled config failed\n");
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
INIT_WORK(&wled->work, qpnp_wled_work);
|
||||
wled->ramp_ms = QPNP_WLED_RAMP_DLY_MS;
|
||||
|
|
@ -2767,6 +3069,8 @@ static int qpnp_wled_probe(struct platform_device *pdev)
|
|||
wled->cdev.brightness_get = qpnp_wled_get;
|
||||
|
||||
wled->cdev.max_brightness = WLED_MAX_LEVEL_4095;
|
||||
now_area = 0;
|
||||
start_jiffies = jiffies;
|
||||
|
||||
rc = led_classdev_register(&pdev->dev, &wled->cdev);
|
||||
if (rc) {
|
||||
|
|
@ -2783,6 +3087,9 @@ static int qpnp_wled_probe(struct platform_device *pdev)
|
|||
}
|
||||
}
|
||||
|
||||
if (wled->init_br_ua)
|
||||
qpnp_wled_set_init_br(wled);
|
||||
|
||||
return 0;
|
||||
|
||||
sysfs_fail:
|
||||
|
|
@ -2810,6 +3117,8 @@ static int qpnp_wled_remove(struct platform_device *pdev)
|
|||
cancel_work_sync(&wled->work);
|
||||
destroy_workqueue(wled->wq);
|
||||
mutex_destroy(&wled->lock);
|
||||
devm_kfree(&pdev->dev, area_count_table);
|
||||
devm_kfree(&pdev->dev, area_count);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -175,7 +175,7 @@
|
|||
#define RGB_LED_EN_CTL(base) (base + 0x46)
|
||||
#define RGB_LED_ATC_CTL(base) (base + 0x47)
|
||||
|
||||
#define RGB_MAX_LEVEL LED_FULL
|
||||
#define RGB_MAX_LEVEL 512
|
||||
#define RGB_LED_ENABLE_RED 0x80
|
||||
#define RGB_LED_ENABLE_GREEN 0x40
|
||||
#define RGB_LED_ENABLE_BLUE 0x20
|
||||
|
|
@ -187,6 +187,7 @@
|
|||
#define PWM_LUT_MAX_SIZE 63
|
||||
#define PWM_GPLED_LUT_MAX_SIZE 31
|
||||
#define RGB_LED_DISABLE 0x00
|
||||
#define PWM_US 1000
|
||||
|
||||
#define MPP_MAX_LEVEL LED_FULL
|
||||
#define LED_MPP_MODE_CTRL(base) (base + 0x40)
|
||||
|
|
@ -251,6 +252,13 @@
|
|||
#define NUM_KPDBL_LEDS 4
|
||||
#define KPDBL_MASTER_BIT_INDEX 0
|
||||
|
||||
#define RGB_LED_MAX_PAUSE_REG 254
|
||||
#define RGB_LED_MAX_PAUSE_TIME 130048
|
||||
#define RGB_LED_LUT_MAX_PAUSE 255
|
||||
#define RGB_LUT_INDEX_OFFSET 1
|
||||
#define RGB_CURR_DEFAULT_PATTERN 1
|
||||
#define RGB_CURR_UNIT_NUM 3
|
||||
|
||||
/**
|
||||
* enum qpnp_leds - QPNP supported led ids
|
||||
* @QPNP_ID_WLED - White led backlight
|
||||
|
|
@ -306,6 +314,8 @@ enum led_mode {
|
|||
PWM_MODE = 0,
|
||||
LPG_MODE,
|
||||
MANUAL_MODE,
|
||||
PWM_DIRECT_MODE,
|
||||
LPG_SYNC_MODE,
|
||||
};
|
||||
|
||||
static u8 wled_debug_regs[] = {
|
||||
|
|
@ -353,6 +363,7 @@ static u8 gpio_debug_regs[] = {
|
|||
* @default_mode - default mode of LED as set in device tree
|
||||
* @use_blink - use blink sysfs entry
|
||||
* @blinking - device is currently blinking w/LPG mode
|
||||
* @pwm_channel - pwm channel to be configured for led
|
||||
*/
|
||||
struct pwm_config_data {
|
||||
struct lut_params lut_params;
|
||||
|
|
@ -365,6 +376,7 @@ struct pwm_config_data {
|
|||
bool pwm_enabled;
|
||||
bool use_blink;
|
||||
bool blinking;
|
||||
int pwm_channel;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -499,10 +511,33 @@ struct kpdbl_config_data {
|
|||
* rgb_config_data - rgb configuration data
|
||||
* @pwm_cfg - device pwm configuration
|
||||
* @enable - bits to enable led
|
||||
* @single_pwm_value - single color max value
|
||||
* @mix_pwm_value - max color max value
|
||||
*/
|
||||
struct rgb_config_data {
|
||||
struct pwm_config_data *pwm_cfg;
|
||||
u8 enable;
|
||||
u32 single_pwm_value;
|
||||
u32 mix_pwm_value;
|
||||
};
|
||||
|
||||
struct qpnp_led_data;
|
||||
enum rgb_sync_state {
|
||||
RGBSYNC_STATE_NOT_BLINK,
|
||||
RGBSYNC_STATE_CONFIGURE_TO_BLINK,
|
||||
RGBSYNC_STATE_BLINKING,
|
||||
};
|
||||
#define LPG_CHANNEL_MAX 6
|
||||
|
||||
/**
|
||||
* struct rgb_sync - rgb led synchrnize structure
|
||||
* @config - pointer to rgb config data
|
||||
*/
|
||||
struct rgb_sync {
|
||||
struct led_classdev cdev;
|
||||
struct platform_device *pdev;
|
||||
enum rgb_sync_state sync_state;
|
||||
struct qpnp_led_data *led_data[LPG_CHANNEL_MAX];
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -552,6 +587,7 @@ struct qpnp_led_data {
|
|||
struct rgb_config_data *rgb_cfg;
|
||||
struct mpp_config_data *mpp_cfg;
|
||||
struct gpio_config_data *gpio_cfg;
|
||||
struct rgb_sync *rgb_sync;
|
||||
int max_current;
|
||||
bool default_on;
|
||||
bool in_order_command_processing;
|
||||
|
|
@ -564,6 +600,19 @@ static u32 kpdbl_master_period_us;
|
|||
DECLARE_BITMAP(kpdbl_leds_in_use, NUM_KPDBL_LEDS);
|
||||
static bool is_kpdbl_master_turn_on;
|
||||
|
||||
static int rgb_current_index;
|
||||
static int __init rgb_current_setup(char *str)
|
||||
{
|
||||
unsigned int res;
|
||||
|
||||
if (!*str)
|
||||
return 0;
|
||||
if (!kstrtouint(str, 16, &res))
|
||||
rgb_current_index = res;
|
||||
return 1;
|
||||
}
|
||||
__setup("oemandroidboot.babe137e=", rgb_current_setup);
|
||||
|
||||
static int
|
||||
qpnp_led_masked_write(struct qpnp_led_data *led, u16 addr, u8 mask, u8 val)
|
||||
{
|
||||
|
|
@ -1708,11 +1757,75 @@ static int qpnp_kpdbl_set(struct qpnp_led_data *led)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int qpnp_rgb_set_direct(struct qpnp_led_data *led)
|
||||
{
|
||||
struct pwm_period_config pwm_conf = {
|
||||
.pwm_size = PM_PWM_SIZE_9BIT,
|
||||
.clk = PM_PWM_CLK_19P2MHZ,
|
||||
.pre_div = PM_PWM_PDIV_5,
|
||||
.pre_div_exp = 3,
|
||||
};
|
||||
int rc;
|
||||
int duty_ns, period_us;
|
||||
|
||||
if (led->rgb_cfg->pwm_cfg->mode == LPG_SYNC_MODE) {
|
||||
dev_err(&led->pdev->dev, "%s wrong mode\n", __func__);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
if (led->cdev.brightness) {
|
||||
period_us = PWM_US;
|
||||
duty_ns = ((period_us * NSEC_PER_USEC) /
|
||||
(RGB_MAX_LEVEL-1)) * led->cdev.brightness;
|
||||
rc = pwm_config(led->rgb_cfg->pwm_cfg->pwm_dev, duty_ns,
|
||||
period_us * NSEC_PER_USEC);
|
||||
rc = pwm_config_period_value(led->rgb_cfg->pwm_cfg->pwm_dev,
|
||||
&pwm_conf, led->cdev.brightness);
|
||||
if (rc < 0) {
|
||||
dev_err(&led->pdev->dev,
|
||||
"pwm config failed\n");
|
||||
return rc;
|
||||
}
|
||||
rc = qpnp_led_masked_write(led,
|
||||
RGB_LED_EN_CTL(led->base),
|
||||
led->rgb_cfg->enable, led->rgb_cfg->enable);
|
||||
if (rc) {
|
||||
dev_err(&led->pdev->dev,
|
||||
"Failed to write led enable reg\n");
|
||||
return rc;
|
||||
}
|
||||
if (led->rgb_cfg->pwm_cfg->pwm_enabled) {
|
||||
pwm_disable(led->rgb_cfg->pwm_cfg->pwm_dev);
|
||||
led->rgb_cfg->pwm_cfg->pwm_enabled = 0;
|
||||
}
|
||||
rc = pwm_enable(led->rgb_cfg->pwm_cfg->pwm_dev);
|
||||
if (!rc)
|
||||
led->rgb_cfg->pwm_cfg->pwm_enabled = 1;
|
||||
} else {
|
||||
pwm_disable(led->rgb_cfg->pwm_cfg->pwm_dev);
|
||||
led->rgb_cfg->pwm_cfg->pwm_enabled = 0;
|
||||
rc = qpnp_led_masked_write(led,
|
||||
RGB_LED_EN_CTL(led->base),
|
||||
led->rgb_cfg->enable, RGB_LED_DISABLE);
|
||||
if (rc) {
|
||||
dev_err(&led->pdev->dev,
|
||||
"Failed to write led enable reg\n");
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qpnp_rgb_set(struct qpnp_led_data *led)
|
||||
{
|
||||
int rc;
|
||||
int duty_us, duty_ns, period_us;
|
||||
|
||||
if ((led->rgb_cfg->pwm_cfg->mode == PWM_DIRECT_MODE)
|
||||
|| (led->rgb_cfg->pwm_cfg->mode == LPG_SYNC_MODE))
|
||||
return qpnp_rgb_set_direct(led);
|
||||
|
||||
if (led->cdev.brightness) {
|
||||
if (!led->rgb_cfg->pwm_cfg->blinking)
|
||||
led->rgb_cfg->pwm_cfg->mode =
|
||||
|
|
@ -1887,7 +2000,7 @@ static int qpnp_led_set_max_brightness(struct qpnp_led_data *led)
|
|||
case QPNP_ID_RGB_RED:
|
||||
case QPNP_ID_RGB_GREEN:
|
||||
case QPNP_ID_RGB_BLUE:
|
||||
led->cdev.max_brightness = RGB_MAX_LEVEL;
|
||||
led->cdev.max_brightness = led->max_current;
|
||||
break;
|
||||
case QPNP_ID_LED_MPP:
|
||||
if (led->mpp_cfg->pwm_mode == MANUAL_MODE)
|
||||
|
|
@ -2958,9 +3071,456 @@ static int qpnp_kpdbl_init(struct qpnp_led_data *led)
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void qpnp_rgb_set_duration(struct qpnp_led_data *led)
|
||||
{
|
||||
unsigned long long_pause, duration;
|
||||
const unsigned max_pause = RGB_LED_MAX_PAUSE_REG;
|
||||
const unsigned max_pause_time = RGB_LED_MAX_PAUSE_TIME;
|
||||
|
||||
if (led->rgb_cfg->pwm_cfg->lut_params.lut_pause_hi > max_pause_time)
|
||||
led->rgb_cfg->pwm_cfg->lut_params.lut_pause_hi
|
||||
= max_pause_time;
|
||||
if (led->rgb_cfg->pwm_cfg->lut_params.lut_pause_lo > max_pause_time)
|
||||
led->rgb_cfg->pwm_cfg->lut_params.lut_pause_lo
|
||||
= max_pause_time;
|
||||
|
||||
long_pause = ((led->rgb_cfg->pwm_cfg->lut_params.lut_pause_hi
|
||||
>= led->rgb_cfg->pwm_cfg->lut_params.lut_pause_lo) ?
|
||||
led->rgb_cfg->pwm_cfg->lut_params.lut_pause_hi :
|
||||
led->rgb_cfg->pwm_cfg->lut_params.lut_pause_lo);
|
||||
duration = (long_pause + (max_pause - 1)) / max_pause;
|
||||
led->rgb_cfg->pwm_cfg->lut_params.lut_pause_hi
|
||||
= (led->rgb_cfg->pwm_cfg->lut_params.lut_pause_hi / duration) + 1;
|
||||
led->rgb_cfg->pwm_cfg->lut_params.lut_pause_lo
|
||||
= (led->rgb_cfg->pwm_cfg->lut_params.lut_pause_lo / duration) + 1;
|
||||
led->rgb_cfg->pwm_cfg->lut_params.ramp_step_ms = (duration - 1);
|
||||
led->rgb_cfg->pwm_cfg->lut_params.use_duration = false;
|
||||
|
||||
pr_debug("%s : pause_hi=%d pause_lo=%d step_ms=%d\n", __func__,
|
||||
led->rgb_cfg->pwm_cfg->lut_params.lut_pause_hi,
|
||||
led->rgb_cfg->pwm_cfg->lut_params.lut_pause_lo,
|
||||
led->rgb_cfg->pwm_cfg->lut_params.ramp_step_ms);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static int rgbled_set_lut_to_register(struct qpnp_led_data *led)
|
||||
{
|
||||
int rc;
|
||||
int i;
|
||||
|
||||
/* set PWM period to 1.06666[ms] (2^9 {9bit} * 5 * 2^3 / 19.2 [us]) */
|
||||
struct pwm_period_config pwm_config = {
|
||||
.pwm_size = PM_PWM_SIZE_9BIT,
|
||||
.clk = PM_PWM_CLK_19P2MHZ,
|
||||
.pre_div = PM_PWM_PDIV_5,
|
||||
.pre_div_exp = 3,
|
||||
};
|
||||
struct lut_config lut_conf = {
|
||||
.hi_index = led->rgb_cfg->pwm_cfg->duty_cycles->start_idx
|
||||
+ led->rgb_cfg->pwm_cfg->lut_params.idx_len - 1,
|
||||
.lo_index = led->rgb_cfg->pwm_cfg->duty_cycles->start_idx,
|
||||
.lut_pause_hi = led->rgb_cfg->pwm_cfg->lut_params.lut_pause_hi,
|
||||
.lut_pause_lo = led->rgb_cfg->pwm_cfg->lut_params.lut_pause_lo,
|
||||
.ramp_step_ms = led->rgb_cfg->pwm_cfg->lut_params.ramp_step_ms,
|
||||
.flags = PM_PWM_LUT_LOOP | PM_PWM_LUT_RAMP_UP
|
||||
| PM_PWM_LUT_PAUSE_HI_EN
|
||||
| PM_PWM_LUT_PAUSE_LO_EN | PM_PWM_LUT_USE_RAW_VALUE,
|
||||
};
|
||||
int *lut = led->rgb_cfg->pwm_cfg->duty_cycles->duty_pcts;
|
||||
|
||||
/* set LPG offset 0x41,0x42 */
|
||||
rc = pwm_config_period(led->rgb_cfg->pwm_cfg->pwm_dev, &pwm_config);
|
||||
for (i = 0; i < INDEX_MAX_EACH_LED; ++i)
|
||||
lut_conf.lut[i] = lut[i];
|
||||
/* set LPG offset 0x40,0x46,0x50-0x57 and LUT */
|
||||
rc = pwm_config_lut(led->rgb_cfg->pwm_cfg->pwm_dev, &lut_conf);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static ssize_t rgbled_lut_pwm_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct qpnp_led_data *led;
|
||||
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||
ssize_t ret = -EINVAL;
|
||||
int *lut;
|
||||
|
||||
led = container_of(led_cdev, struct qpnp_led_data, cdev);
|
||||
lut = led->rgb_cfg->pwm_cfg->duty_cycles->duty_pcts;
|
||||
|
||||
ret = sscanf(buf, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
|
||||
lut, lut+1, lut+2, lut+3, lut+4, lut+5, lut+6, lut+7,
|
||||
lut+8, lut+9, lut+10, lut+11, lut+12, lut+13, lut+14, lut+15);
|
||||
if (ret == 0) {
|
||||
lut[0] = 0;
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
led->rgb_cfg->pwm_cfg->duty_cycles->start_idx
|
||||
= led->rgb_cfg->pwm_cfg->lut_params.start_idx
|
||||
= (led->rgb_cfg->pwm_cfg->pwm_channel * INDEX_MAX_EACH_LED)
|
||||
+ RGB_LUT_INDEX_OFFSET;
|
||||
led->rgb_cfg->pwm_cfg->lut_params.idx_len = ret;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t rgbled_lut_pwm_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||
struct qpnp_led_data *led;
|
||||
int *lut;
|
||||
|
||||
led = container_of(led_cdev, struct qpnp_led_data, cdev);
|
||||
lut = led->rgb_cfg->pwm_cfg->duty_cycles->duty_pcts;
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "len:%u,[0]:%d,[1]:%d\n",
|
||||
led->rgb_cfg->pwm_cfg->lut_params.idx_len, lut[0], lut[1]);
|
||||
}
|
||||
|
||||
static ssize_t rgbled_step_duration_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct qpnp_led_data *led;
|
||||
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||
ssize_t ret = -EINVAL;
|
||||
unsigned long duration;
|
||||
const unsigned ramp_step_duration_max = 511;
|
||||
|
||||
led = container_of(led_cdev, struct qpnp_led_data, cdev);
|
||||
|
||||
ret = kstrtoul(buf, 10, &duration);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (duration > ramp_step_duration_max)
|
||||
duration = ramp_step_duration_max;
|
||||
led->rgb_cfg->pwm_cfg->lut_params.ramp_step_ms = duration;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t rgbled_step_duration_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||
struct qpnp_led_data *led;
|
||||
|
||||
led = container_of(led_cdev, struct qpnp_led_data, cdev);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n",
|
||||
led->rgb_cfg->pwm_cfg->lut_params.ramp_step_ms);
|
||||
}
|
||||
|
||||
static ssize_t rgbled_pause_hi_multi_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct qpnp_led_data *led;
|
||||
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||
ssize_t ret = -EINVAL;
|
||||
unsigned long lut_pause;
|
||||
const unsigned lut_pause_max = RGB_LED_LUT_MAX_PAUSE;
|
||||
|
||||
led = container_of(led_cdev, struct qpnp_led_data, cdev);
|
||||
|
||||
ret = kstrtoul(buf, 10, &lut_pause);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (lut_pause > lut_pause_max)
|
||||
led->rgb_cfg->pwm_cfg->lut_params.use_duration = true;
|
||||
|
||||
led->rgb_cfg->pwm_cfg->lut_params.lut_pause_hi = lut_pause;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t rgbled_pause_hi_multi_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||
struct qpnp_led_data *led;
|
||||
|
||||
led = container_of(led_cdev, struct qpnp_led_data, cdev);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n",
|
||||
led->rgb_cfg->pwm_cfg->lut_params.lut_pause_hi);
|
||||
}
|
||||
|
||||
static ssize_t rgbled_pause_lo_multi_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct qpnp_led_data *led;
|
||||
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||
ssize_t ret = -EINVAL;
|
||||
unsigned long lut_pause;
|
||||
const unsigned lut_pause_max = RGB_LED_LUT_MAX_PAUSE;
|
||||
|
||||
led = container_of(led_cdev, struct qpnp_led_data, cdev);
|
||||
|
||||
ret = kstrtoul(buf, 10, &lut_pause);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (lut_pause > lut_pause_max)
|
||||
led->rgb_cfg->pwm_cfg->lut_params.use_duration = true;
|
||||
|
||||
led->rgb_cfg->pwm_cfg->lut_params.lut_pause_lo = lut_pause;
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t rgbled_pause_lo_multi_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||
struct qpnp_led_data *led;
|
||||
|
||||
led = container_of(led_cdev, struct qpnp_led_data, cdev);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n",
|
||||
led->rgb_cfg->pwm_cfg->lut_params.lut_pause_lo);
|
||||
}
|
||||
|
||||
static ssize_t rgbled_single_value_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||
struct qpnp_led_data *led;
|
||||
|
||||
led = container_of(led_cdev, struct qpnp_led_data, cdev);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n",
|
||||
led->rgb_cfg->single_pwm_value);
|
||||
}
|
||||
|
||||
static ssize_t rgbled_mix_value_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||
struct qpnp_led_data *led;
|
||||
|
||||
led = container_of(led_cdev, struct qpnp_led_data, cdev);
|
||||
|
||||
return scnprintf(buf, PAGE_SIZE, "%d\n",
|
||||
led->rgb_cfg->mix_pwm_value);
|
||||
}
|
||||
|
||||
static struct device_attribute rgbled_attrs[] = {
|
||||
__ATTR(lut_pwm, S_IRUGO | S_IWUSR | S_IWGRP,
|
||||
rgbled_lut_pwm_show, rgbled_lut_pwm_store),
|
||||
__ATTR(step_duration, S_IRUGO | S_IWUSR | S_IWGRP,
|
||||
rgbled_step_duration_show, rgbled_step_duration_store),
|
||||
__ATTR(pause_hi_multi, S_IRUGO | S_IWUSR | S_IWGRP,
|
||||
rgbled_pause_hi_multi_show, rgbled_pause_hi_multi_store),
|
||||
__ATTR(pause_lo_multi, S_IRUGO | S_IWUSR | S_IWGRP,
|
||||
rgbled_pause_lo_multi_show, rgbled_pause_lo_multi_store),
|
||||
__ATTR(max_single_brightness, S_IRUGO,
|
||||
rgbled_single_value_show, NULL),
|
||||
__ATTR(max_mix_brightness, S_IRUGO,
|
||||
rgbled_mix_value_show, NULL),
|
||||
__ATTR_NULL,
|
||||
};
|
||||
|
||||
static int rgbcommon_turn_off_force(struct rgb_sync *rgb_sync)
|
||||
{
|
||||
int i;
|
||||
int rc = 0;
|
||||
for (i = 0; i < LPG_CHANNEL_MAX; ++i)
|
||||
if (rgb_sync->led_data[i]) {
|
||||
struct qpnp_led_data *led = rgb_sync->led_data[i];
|
||||
led->cdev.brightness = 0;
|
||||
led->rgb_cfg->pwm_cfg->lut_params.idx_len = 0;
|
||||
rc = qpnp_led_masked_write(led,
|
||||
RGB_LED_EN_CTL(led->base),
|
||||
led->rgb_cfg->enable, RGB_LED_DISABLE);
|
||||
if (rc)
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static ssize_t rgbcommon_sync_state_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct rgb_sync *rgb_sync;
|
||||
unsigned long state;
|
||||
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||
ssize_t ret = -EINVAL;
|
||||
int i;
|
||||
int rc;
|
||||
enum led_mode led_mode;
|
||||
enum pm_pwm_mode pwm_mode;
|
||||
|
||||
ret = kstrtoul(buf, 10, &state);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (state > RGBSYNC_STATE_BLINKING) {
|
||||
dev_err(dev, "%s out of range\n", __func__);
|
||||
return -EFAULT;
|
||||
}
|
||||
rgb_sync = container_of(led_cdev, struct rgb_sync, cdev);
|
||||
if (!rgb_sync) {
|
||||
dev_err(dev, "%s rgb_sync NULL\n", __func__);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
if (rgb_sync->sync_state == state) {
|
||||
dev_err(dev, "%s do nothing. same state", __func__);
|
||||
return count;
|
||||
} else if ((rgb_sync->sync_state == RGBSYNC_STATE_BLINKING)
|
||||
|| ((rgb_sync->sync_state == RGBSYNC_STATE_NOT_BLINK)
|
||||
&& (state == RGBSYNC_STATE_CONFIGURE_TO_BLINK))) {
|
||||
dev_err(dev, "%s turn off force. state changed\n", __func__);
|
||||
rc = rgbcommon_turn_off_force(rgb_sync);
|
||||
if (rc)
|
||||
dev_err(dev, "Failed to turn off force\n");
|
||||
}
|
||||
switch (rgb_sync->sync_state = state) {
|
||||
case RGBSYNC_STATE_NOT_BLINK:
|
||||
led_mode = PWM_DIRECT_MODE;
|
||||
pwm_mode = PM_PWM_MODE_PWM;
|
||||
break;
|
||||
case RGBSYNC_STATE_CONFIGURE_TO_BLINK:
|
||||
led_mode = LPG_SYNC_MODE;
|
||||
pwm_mode = PM_PWM_MODE_LPG;
|
||||
break;
|
||||
default:
|
||||
return count;
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < LPG_CHANNEL_MAX; ++i)
|
||||
if (rgb_sync->led_data[i]) {
|
||||
struct qpnp_led_data *led = rgb_sync->led_data[i];
|
||||
led->rgb_cfg->pwm_cfg->mode = led_mode;
|
||||
rc = pwm_change_mode(led->rgb_cfg->pwm_cfg->pwm_dev,
|
||||
pwm_mode);
|
||||
if (rc)
|
||||
break;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t rgbcommon_sync_state_show(struct device *ldev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct led_classdev *led_cdev = dev_get_drvdata(ldev);
|
||||
struct device *dev = led_cdev->dev->parent;
|
||||
struct rgb_sync *rgb_sync;
|
||||
|
||||
rgb_sync = container_of(led_cdev, struct rgb_sync, cdev);
|
||||
if (!rgb_sync) {
|
||||
dev_err(dev, "%s rgb_sync NULL\n", __func__);
|
||||
return -EFAULT;
|
||||
}
|
||||
return scnprintf(buf, PAGE_SIZE, "%u\n", rgb_sync->sync_state);
|
||||
}
|
||||
|
||||
static ssize_t rgbcommon_start_blink_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct rgb_sync *rgb_sync;
|
||||
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||
int i;
|
||||
struct qpnp_led_data *led = NULL;
|
||||
int rc;
|
||||
int ramp_control = 0;
|
||||
|
||||
rgb_sync = container_of(led_cdev, struct rgb_sync, cdev);
|
||||
if (!rgb_sync) {
|
||||
dev_err(dev, "%s rgb_sync NULL\n", __func__);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
if (rgb_sync->sync_state != RGBSYNC_STATE_CONFIGURE_TO_BLINK) {
|
||||
dev_err(dev, "%s ignore. not configure to blink state\n",
|
||||
__func__);
|
||||
return count;
|
||||
}
|
||||
|
||||
rgb_sync->sync_state = RGBSYNC_STATE_BLINKING;
|
||||
/* must set only led which is blinked */
|
||||
for (i = 0; i < LPG_CHANNEL_MAX; ++i) {
|
||||
if (!rgb_sync->led_data[i])
|
||||
continue;
|
||||
led = rgb_sync->led_data[i];
|
||||
if (led->rgb_cfg->pwm_cfg->lut_params.use_duration)
|
||||
qpnp_rgb_set_duration(led);
|
||||
if (led->rgb_cfg->pwm_cfg->lut_params.idx_len) {
|
||||
rgbled_set_lut_to_register(led);
|
||||
ramp_control |= (1 << i);
|
||||
rc = qpnp_led_masked_write(led,
|
||||
RGB_LED_EN_CTL(led->base),
|
||||
led->rgb_cfg->enable, led->rgb_cfg->enable);
|
||||
}
|
||||
}
|
||||
|
||||
if (led) {
|
||||
rc = pwm_start_lut_ramp(led->rgb_cfg->pwm_cfg->pwm_dev,
|
||||
ramp_control);
|
||||
if (rc)
|
||||
dev_err(dev, "Failed to start blink\n");
|
||||
} else {
|
||||
dev_err(dev, "try to start blink. however not configured.\n");
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static struct device_attribute rgbcommon_attr[] = {
|
||||
__ATTR(sync_state, S_IRUGO | S_IWUSR | S_IWGRP,
|
||||
rgbcommon_sync_state_show, rgbcommon_sync_state_store),
|
||||
__ATTR(start_blink, S_IWUSR | S_IWGRP,
|
||||
NULL, rgbcommon_start_blink_store),
|
||||
__ATTR_NULL,
|
||||
};
|
||||
|
||||
static void qpnp_remove_attributes(struct device *dev,
|
||||
struct device_attribute *attrs)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; attrs[i].attr.name; i++)
|
||||
device_remove_file(dev, &attrs[i]);
|
||||
}
|
||||
|
||||
static int qpnp_add_attributes(struct device *dev,
|
||||
struct device_attribute *attrs)
|
||||
{
|
||||
int error = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; attrs[i].attr.name; i++) {
|
||||
error = device_create_file(dev, &attrs[i]);
|
||||
if (error) {
|
||||
dev_err(dev, "%s: failed.\n", __func__);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
err:
|
||||
while (--i >= 0)
|
||||
device_remove_file(dev, &attrs[i]);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
static int qpnp_rgb_init(struct qpnp_led_data *led)
|
||||
{
|
||||
int rc;
|
||||
int max_pwm_value;
|
||||
|
||||
rc = qpnp_led_masked_write(led, RGB_LED_SRC_SEL(led->base),
|
||||
RGB_LED_SRC_MASK, RGB_LED_SOURCE_VPH_PWR);
|
||||
|
|
@ -2975,6 +3535,24 @@ static int qpnp_rgb_init(struct qpnp_led_data *led)
|
|||
dev_err(&led->pdev->dev, "Failed to initialize pwm\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (led->rgb_sync) {
|
||||
led->rgb_sync->led_data[led->rgb_cfg->pwm_cfg->pwm_channel]
|
||||
= led;
|
||||
led->rgb_cfg->pwm_cfg->mode = PWM_DIRECT_MODE;
|
||||
rc = pwm_change_mode(led->rgb_cfg->pwm_cfg->pwm_dev,
|
||||
PM_PWM_MODE_PWM);
|
||||
if (rc < 0) {
|
||||
dev_err(&led->pdev->dev, "Failed to " \
|
||||
"configure pwm mode\n");
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
pwm_set_max_pwm_value(led->rgb_cfg->pwm_cfg->pwm_dev, led->max_current);
|
||||
max_pwm_value = pwm_get_max_pwm_value(led->rgb_cfg->pwm_cfg->pwm_dev);
|
||||
led->max_current
|
||||
= max_pwm_value ? max_pwm_value : RGB_MAX_LEVEL;
|
||||
|
||||
/* Initialize led for use in auto trickle charging mode */
|
||||
rc = qpnp_led_masked_write(led, RGB_LED_ATC_CTL(led->base),
|
||||
led->rgb_cfg->enable, led->rgb_cfg->enable);
|
||||
|
|
@ -3631,13 +4209,13 @@ static int qpnp_get_config_kpdbl(struct qpnp_led_data *led,
|
|||
return rc;
|
||||
|
||||
led->kpdbl_cfg->row_src_vbst =
|
||||
of_property_read_bool(node, "qcom,row-src-vbst");
|
||||
of_property_read_bool(node, "qcom,row-src-vbst");
|
||||
|
||||
led->kpdbl_cfg->row_src_en =
|
||||
of_property_read_bool(node, "qcom,row-src-en");
|
||||
of_property_read_bool(node, "qcom,row-src-en");
|
||||
|
||||
led->kpdbl_cfg->always_on =
|
||||
of_property_read_bool(node, "qcom,always-on");
|
||||
of_property_read_bool(node, "qcom,always-on");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -3645,7 +4223,9 @@ static int qpnp_get_config_kpdbl(struct qpnp_led_data *led,
|
|||
static int qpnp_get_config_rgb(struct qpnp_led_data *led,
|
||||
struct device_node *node)
|
||||
{
|
||||
int rc;
|
||||
int rc, current_index = 0, i;
|
||||
u32 val, color_variation_max_num;
|
||||
u32 *rgb_current_table;
|
||||
u8 led_mode;
|
||||
const char *mode;
|
||||
|
||||
|
|
@ -3684,6 +4264,82 @@ static int qpnp_get_config_rgb(struct qpnp_led_data *led,
|
|||
return rc;
|
||||
}
|
||||
|
||||
rc = of_property_read_u32(node,
|
||||
"somc,color_variation_max_num",
|
||||
&color_variation_max_num);
|
||||
if (rc < 0) {
|
||||
dev_err(&led->pdev->dev, "Unable to read color_variation_max_num\n");
|
||||
color_variation_max_num = RGB_CURR_DEFAULT_PATTERN;
|
||||
}
|
||||
dev_info(&led->pdev->dev, "color_variation_max_num[%d] rgb_current_index[%d]\n",
|
||||
color_variation_max_num, rgb_current_index);
|
||||
|
||||
rgb_current_table = devm_kzalloc(&led->pdev->dev,
|
||||
sizeof(u32) * color_variation_max_num * RGB_CURR_UNIT_NUM,
|
||||
GFP_KERNEL);
|
||||
if (rgb_current_table) {
|
||||
rc = of_property_read_u32_array(node, "somc,max_current",
|
||||
rgb_current_table,
|
||||
color_variation_max_num * RGB_CURR_UNIT_NUM);
|
||||
if (rc < 0) {
|
||||
dev_err(&led->pdev->dev, "Unable to read max_mix_current\n");
|
||||
led->rgb_cfg->single_pwm_value = RGB_MAX_LEVEL - 1;
|
||||
led->rgb_cfg->mix_pwm_value = RGB_MAX_LEVEL - 1;
|
||||
} else {
|
||||
for (i = 0; i < color_variation_max_num; i++) {
|
||||
current_index = i * RGB_CURR_UNIT_NUM;
|
||||
if (rgb_current_index == rgb_current_table[current_index]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
led->rgb_cfg->single_pwm_value =
|
||||
rgb_current_table[current_index + 1];
|
||||
led->rgb_cfg->mix_pwm_value =
|
||||
rgb_current_table[current_index + 2];
|
||||
}
|
||||
} else {
|
||||
dev_err(&led->pdev->dev, "Unable to allocate memory\n");
|
||||
led->rgb_cfg->single_pwm_value = RGB_MAX_LEVEL - 1;
|
||||
led->rgb_cfg->mix_pwm_value = RGB_MAX_LEVEL - 1;
|
||||
}
|
||||
devm_kfree(&led->pdev->dev, rgb_current_table);
|
||||
|
||||
led->max_current = (led->rgb_cfg->single_pwm_value >
|
||||
led->rgb_cfg->mix_pwm_value ?
|
||||
led->rgb_cfg->single_pwm_value :
|
||||
led->rgb_cfg->mix_pwm_value);
|
||||
dev_info(&led->pdev->dev, "single_pwm_value[%d] mix_pwm_value[%d]\n",
|
||||
led->rgb_cfg->single_pwm_value, led->rgb_cfg->mix_pwm_value);
|
||||
dev_info(&led->pdev->dev, "max_current[%d]\n", led->max_current);
|
||||
|
||||
if (led->rgb_sync) {
|
||||
rc = of_property_read_u32(node, "somc,pwm-channel", &val);
|
||||
if (!rc)
|
||||
led->rgb_cfg->pwm_cfg->pwm_channel = val;
|
||||
else
|
||||
return rc;
|
||||
|
||||
/* allocate memory for simple use case */
|
||||
led->rgb_cfg->pwm_cfg->duty_cycles =
|
||||
devm_kzalloc(&led->pdev->dev,
|
||||
sizeof(struct pwm_duty_cycles), GFP_KERNEL);
|
||||
if (!led->rgb_cfg->pwm_cfg->duty_cycles) {
|
||||
dev_err(&led->pdev->dev,
|
||||
"Unable to allocate memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
led->rgb_cfg->pwm_cfg->duty_cycles->duty_pcts =
|
||||
devm_kzalloc(&led->pdev->dev,
|
||||
sizeof(int) * INDEX_MAX_EACH_LED, GFP_KERNEL);
|
||||
if (!led->rgb_cfg->pwm_cfg->duty_cycles->duty_pcts) {
|
||||
dev_err(&led->pdev->dev,
|
||||
"Unable to allocate memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
led->rgb_cfg->pwm_cfg->pwm_dev = of_pwm_get(node, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
rc = qpnp_get_config_pwm(led->rgb_cfg->pwm_cfg, led->pdev, node);
|
||||
if (rc < 0) {
|
||||
if (led->rgb_cfg->pwm_cfg->pwm_dev)
|
||||
|
|
@ -3872,6 +4528,8 @@ static int qpnp_leds_probe(struct platform_device *pdev)
|
|||
int rc, i, num_leds = 0, parsed_leds = 0;
|
||||
const char *led_label;
|
||||
bool regulator_probe = false;
|
||||
int prepare_rgb_sync;
|
||||
struct rgb_sync *rgb_sync = NULL;
|
||||
|
||||
node = pdev->dev.of_node;
|
||||
if (node == NULL)
|
||||
|
|
@ -3889,10 +4547,34 @@ static int qpnp_leds_probe(struct platform_device *pdev)
|
|||
if (!led_array)
|
||||
return -ENOMEM;
|
||||
|
||||
rc = of_property_read_u32(node, "qcom,rgb_sync", &prepare_rgb_sync);
|
||||
if (rc < 0)
|
||||
prepare_rgb_sync = 0;
|
||||
if (prepare_rgb_sync)
|
||||
rgb_sync = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct rgb_sync), GFP_KERNEL);
|
||||
if (rgb_sync) {
|
||||
rgb_sync->cdev.name = "rgb";
|
||||
rgb_sync->pdev = pdev;
|
||||
rc = led_classdev_register(&pdev->dev, &rgb_sync->cdev);
|
||||
if (rc) {
|
||||
dev_err(&pdev->dev, "unable to register rgb %d\n", rc);
|
||||
rgb_sync = 0;
|
||||
}
|
||||
rc = rc ? rc :
|
||||
qpnp_add_attributes(rgb_sync->cdev.dev, rgbcommon_attr);
|
||||
if (rc) {
|
||||
dev_err(&pdev->dev, "unable to create sysfs %d\n", rc);
|
||||
rgb_sync = 0;
|
||||
}
|
||||
}
|
||||
dev_info(&pdev->dev, "rgb_sync prepare %d\n", prepare_rgb_sync);
|
||||
|
||||
for_each_child_of_node(node, temp) {
|
||||
led = &led_array[parsed_leds];
|
||||
led->num_leds = num_leds;
|
||||
led->regmap = dev_get_regmap(pdev->dev.parent, NULL);
|
||||
led->rgb_sync = rgb_sync;
|
||||
if (!led->regmap) {
|
||||
dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
|
||||
return -EINVAL;
|
||||
|
|
@ -4048,6 +4730,14 @@ static int qpnp_leds_probe(struct platform_device *pdev)
|
|||
|
||||
}
|
||||
|
||||
if (led->id == QPNP_ID_RGB_RED ||
|
||||
led->id == QPNP_ID_RGB_GREEN ||
|
||||
led->id == QPNP_ID_RGB_BLUE) {
|
||||
rc = qpnp_add_attributes(led->cdev.dev, rgbled_attrs);
|
||||
if (rc)
|
||||
goto fail_id_check;
|
||||
}
|
||||
|
||||
if (led->id == QPNP_ID_LED_MPP) {
|
||||
if (!led->mpp_cfg->pwm_cfg)
|
||||
break;
|
||||
|
|
@ -4182,6 +4872,8 @@ static int qpnp_leds_remove(struct platform_device *pdev)
|
|||
case QPNP_ID_RGB_RED:
|
||||
case QPNP_ID_RGB_GREEN:
|
||||
case QPNP_ID_RGB_BLUE:
|
||||
qpnp_remove_attributes(led_array[i].cdev.dev,
|
||||
rgbled_attrs);
|
||||
if (led_array[i].rgb_cfg->pwm_cfg->mode == PWM_MODE)
|
||||
sysfs_remove_group(&led_array[i].cdev.dev->kobj,
|
||||
&pwm_attr_group);
|
||||
|
|
|
|||
Loading…
Reference in a new issue