leds: leds-qpnp: add synchronized rgb control
Change-Id: Idd480a7d251a550f02f30e84f29a3f87f4e91323 Signed-off-by: Sorayukii <sorayukii69@gmail.com>
This commit is contained in:
parent
cf0ec1927e
commit
704e34282e
1 changed files with 177 additions and 1 deletions
|
|
@ -268,6 +268,8 @@ enum qpnp_leds {
|
|||
QPNP_ID_MAX,
|
||||
};
|
||||
|
||||
#define QPNP_ID_TO_RGB_IDX(id) (id - QPNP_ID_RGB_RED)
|
||||
|
||||
/* current boost limit */
|
||||
enum wled_current_boost_limit {
|
||||
WLED_CURR_LIMIT_105mA,
|
||||
|
|
@ -558,6 +560,15 @@ struct qpnp_led_data {
|
|||
int turn_off_delay_ms;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct rgb_sync - rgb led synchrnize structure
|
||||
*/
|
||||
struct rgb_sync {
|
||||
struct led_classdev cdev;
|
||||
struct platform_device *pdev;
|
||||
struct qpnp_led_data *led_data[3];
|
||||
};
|
||||
|
||||
static DEFINE_MUTEX(flash_lock);
|
||||
static struct pwm_device *kpdbl_master;
|
||||
static u32 kpdbl_master_period_us;
|
||||
|
|
@ -2722,6 +2733,130 @@ static ssize_t blink_store(struct device *dev,
|
|||
return count;
|
||||
}
|
||||
|
||||
static inline void rgb_lock_leds(struct rgb_sync *rgb)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
if (rgb->led_data[i]) {
|
||||
flush_work(&rgb->led_data[i]->work);
|
||||
mutex_lock(&rgb->led_data[i]->lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void rgb_unlock_leds(struct rgb_sync *rgb)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
if (rgb->led_data[i]) {
|
||||
mutex_unlock(&rgb->led_data[i]->lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void rgb_disable_leds(struct rgb_sync *rgb)
|
||||
{
|
||||
int i;
|
||||
struct qpnp_led_data *led;
|
||||
|
||||
// TODO Implement synchronized off
|
||||
for (i = 0; i < 3; i++) {
|
||||
led = rgb->led_data[i];
|
||||
if (led && led->rgb_cfg->pwm_cfg->pwm_enabled) {
|
||||
led->rgb_cfg->pwm_cfg->mode =
|
||||
led->rgb_cfg->pwm_cfg->default_mode;
|
||||
led->rgb_cfg->pwm_cfg->blinking = false;
|
||||
pwm_disable(led->rgb_cfg->pwm_cfg->pwm_dev);
|
||||
led->rgb_cfg->pwm_cfg->pwm_enabled = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Should only be called when all RGB leds are off
|
||||
*/
|
||||
static int rgb_enable_leds(struct rgb_sync *rgb)
|
||||
{
|
||||
struct qpnp_led_data *led;
|
||||
struct pwm_device *pwm_dev[3];
|
||||
int i, rc;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
led = rgb->led_data[i];
|
||||
if (!led)
|
||||
continue;
|
||||
|
||||
led->rgb_cfg->pwm_cfg->mode = LPG_MODE;
|
||||
pwm_free(led->rgb_cfg->pwm_cfg->pwm_dev);
|
||||
qpnp_pwm_init(led->rgb_cfg->pwm_cfg, led->pdev, led->cdev.name);
|
||||
pwm_dev[i] = led->rgb_cfg->pwm_cfg->pwm_dev;
|
||||
}
|
||||
|
||||
if (i == 0)
|
||||
return 0;
|
||||
|
||||
rc = pwm_enable_synchronized(pwm_dev, i);
|
||||
if (rc) {
|
||||
dev_err(&rgb->pdev->dev, "Unable to enable pwms\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
led = rgb->led_data[i];
|
||||
if (!led)
|
||||
continue;
|
||||
led->rgb_cfg->pwm_cfg->blinking = true;
|
||||
led->rgb_cfg->pwm_cfg->pwm_enabled = 1;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static ssize_t rgb_blink_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct rgb_sync *rgb_sync;
|
||||
struct qpnp_led_data *led;
|
||||
unsigned long blinking;
|
||||
struct led_classdev *led_cdev = dev_get_drvdata(dev);
|
||||
ssize_t rc = -EINVAL, i;
|
||||
u8 enable = 0;
|
||||
|
||||
rc = kstrtoul(buf, 10, &blinking);
|
||||
if (rc)
|
||||
return rc;
|
||||
rgb_sync = container_of(led_cdev, struct rgb_sync, cdev);
|
||||
|
||||
rgb_lock_leds(rgb_sync);
|
||||
for (i = 0; i < 3; i++) {
|
||||
if (rgb_sync->led_data[i]) {
|
||||
led = rgb_sync->led_data[i];
|
||||
enable |= led->rgb_cfg->enable;
|
||||
}
|
||||
}
|
||||
|
||||
if (!led)
|
||||
return count;
|
||||
|
||||
rc = qpnp_led_masked_write(led,
|
||||
RGB_LED_EN_CTL(led->base),
|
||||
enable, blinking ? enable : RGB_LED_DISABLE);
|
||||
if (rc) {
|
||||
dev_err(&led->pdev->dev,
|
||||
"Failed to write led enable reg\n");
|
||||
rgb_unlock_leds(rgb_sync);
|
||||
return rc;
|
||||
}
|
||||
rgb_disable_leds(rgb_sync);
|
||||
if (blinking)
|
||||
rgb_enable_leds(rgb_sync);
|
||||
rgb_unlock_leds(rgb_sync);
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(led_mode, 0664, NULL, led_mode_store);
|
||||
static DEVICE_ATTR(strobe, 0664, NULL, led_strobe_type_store);
|
||||
static DEVICE_ATTR(pwm_us, 0664, NULL, pwm_us_store);
|
||||
|
|
@ -2732,7 +2867,7 @@ static DEVICE_ATTR(ramp_step_ms, 0664, NULL, ramp_step_ms_store);
|
|||
static DEVICE_ATTR(lut_flags, 0664, NULL, lut_flags_store);
|
||||
static DEVICE_ATTR(duty_pcts, 0664, NULL, duty_pcts_store);
|
||||
static DEVICE_ATTR(blink, 0664, NULL, blink_store);
|
||||
|
||||
static DEVICE_ATTR(rgb_blink, 0664, NULL, rgb_blink_store);
|
||||
static struct attribute *led_attrs[] = {
|
||||
&dev_attr_led_mode.attr,
|
||||
&dev_attr_strobe.attr,
|
||||
|
|
@ -2743,6 +2878,11 @@ static const struct attribute_group led_attr_group = {
|
|||
.attrs = led_attrs,
|
||||
};
|
||||
|
||||
static struct attribute *rgb_blink_attrs[] = {
|
||||
&dev_attr_rgb_blink.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct attribute *pwm_attrs[] = {
|
||||
&dev_attr_pwm_us.attr,
|
||||
NULL
|
||||
|
|
@ -2775,6 +2915,10 @@ static const struct attribute_group blink_attr_group = {
|
|||
.attrs = blink_attrs,
|
||||
};
|
||||
|
||||
static const struct attribute_group rgb_blink_attr_group = {
|
||||
.attrs = rgb_blink_attrs,
|
||||
};
|
||||
|
||||
static int qpnp_flash_init(struct qpnp_led_data *led)
|
||||
{
|
||||
int rc;
|
||||
|
|
@ -3872,6 +4016,7 @@ 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;
|
||||
struct rgb_sync *rgb_sync = NULL;
|
||||
|
||||
node = pdev->dev.of_node;
|
||||
if (node == NULL)
|
||||
|
|
@ -3889,6 +4034,29 @@ static int qpnp_leds_probe(struct platform_device *pdev)
|
|||
if (!led_array)
|
||||
return -ENOMEM;
|
||||
|
||||
if (of_property_read_bool(node, "qcom,rgb_sync")) {
|
||||
rgb_sync = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct rgb_sync), GFP_KERNEL);
|
||||
if (!rgb_sync) {
|
||||
dev_err(&pdev->dev, "Unable to allocate memory\n");
|
||||
kfree(led_array);
|
||||
return -ENOMEM;
|
||||
}
|
||||
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);
|
||||
goto fail_id_check;
|
||||
}
|
||||
rc = sysfs_create_group(&rgb_sync->cdev.dev->kobj,
|
||||
&rgb_blink_attr_group);
|
||||
if (rc) {
|
||||
dev_err(&pdev->dev, "unable to create rgb sysfs %d\n", rc);
|
||||
goto fail_id_check;
|
||||
}
|
||||
}
|
||||
|
||||
for_each_child_of_node(node, temp) {
|
||||
led = &led_array[parsed_leds];
|
||||
led->num_leds = num_leds;
|
||||
|
|
@ -4092,6 +4260,10 @@ static int qpnp_leds_probe(struct platform_device *pdev)
|
|||
&lpg_attr_group);
|
||||
if (rc)
|
||||
goto fail_id_check;
|
||||
|
||||
if (rgb_sync)
|
||||
rgb_sync->led_data[QPNP_ID_TO_RGB_IDX(led->id)] = led;
|
||||
|
||||
} else if (led->rgb_cfg->pwm_cfg->mode == LPG_MODE) {
|
||||
rc = sysfs_create_group(&led->cdev.dev->kobj,
|
||||
&lpg_attr_group);
|
||||
|
|
@ -4138,6 +4310,10 @@ static int qpnp_leds_probe(struct platform_device *pdev)
|
|||
return 0;
|
||||
|
||||
fail_id_check:
|
||||
if (rgb_sync) {
|
||||
led_classdev_unregister(&rgb_sync->cdev);
|
||||
kfree(rgb_sync);
|
||||
}
|
||||
for (i = 0; i < parsed_leds; i++) {
|
||||
if (led_array[i].id != QPNP_ID_FLASH1_LED0 &&
|
||||
led_array[i].id != QPNP_ID_FLASH1_LED1)
|
||||
|
|
|
|||
Loading…
Reference in a new issue