leds: leds-qpnp: add synchronized rgb control

Change-Id: Idd480a7d251a550f02f30e84f29a3f87f4e91323
Signed-off-by: Sorayukii <sorayukii69@gmail.com>
This commit is contained in:
Scott Mertz 2015-08-04 14:26:46 -07:00 • committed by Sorayukii
commit 704e34282e

View file

@ -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)