@@ -12,6 +12,7 @@
*/
#include <linux/leds.h>
+#include <linux/led-class-rgb.h>
#include <linux/mfd/motorola-cpcap.h>
#include <linux/module.h>
#include <linux/mutex.h>
@@ -28,6 +29,10 @@ struct cpcap_led_info {
u16 limit;
u16 init_mask;
u16 init_val;
+ u16 rgb_combo;
+ u16 red_reg;
+ u16 green_reg;
+ u16 blue_reg;
};
static const struct cpcap_led_info cpcap_led_red = {
@@ -48,6 +53,15 @@ static const struct cpcap_led_info cpcap_led_blue = {
.limit = 31,
};
+static const struct cpcap_led_info cpcap_led_rgb = {
+ .red_reg = CPCAP_REG_REDC,
+ .green_reg = CPCAP_REG_GREENC,
+ .blue_reg = CPCAP_REG_BLUEC,
+ .rgb_combo = 0x1,
+ .mask = 0x03FF,
+ .limit = 31,
+};
+
/* aux display light */
static const struct cpcap_led_info cpcap_led_adl = {
.reg = CPCAP_REG_ADLC,
@@ -68,6 +82,7 @@ static const struct cpcap_led_info cpcap_led_cp = {
struct cpcap_led {
struct led_classdev led;
+ struct led_classdev_rgb rgb_cdev;
const struct cpcap_led_info *info;
struct device *dev;
struct regmap *regmap;
@@ -116,6 +131,11 @@ static int cpcap_led_set(struct led_classdev *ledc, enum led_brightness value)
mutex_lock(&led->update_lock);
+ if (led->info->rgb_combo) {
+ err = 0;
+ goto exit;
+ }
+
if (value > LED_OFF) {
err = cpcap_led_set_power(led, true);
if (err)
@@ -154,10 +174,232 @@ static int cpcap_led_set(struct led_classdev *ledc, enum led_brightness value)
return err;
}
+static struct cpcap_led *rgbled_cdev_to_led(struct led_classdev_rgb *rgbled_cdev)
+{
+ return container_of(rgbled_cdev, struct cpcap_led, rgb_cdev);
+}
+
+static int cpcap_led_set_red(struct led_classdev_rgb *rgbled_cdev,
+ enum led_brightness value)
+{
+ struct cpcap_led *led = rgbled_cdev_to_led(rgbled_cdev);
+ int brightness;
+ int err;
+
+ mutex_lock(&led->update_lock);
+
+ if (value > LED_OFF) {
+ err = cpcap_led_set_power(led, true);
+ if (err)
+ goto exit;
+ }
+
+ if (value == LED_OFF) {
+ /* Avoid HW issue by turning off current before duty cycle */
+ err = regmap_update_bits(led->regmap,
+ led->info->red_reg, led->info->mask, CPCAP_LED_NO_CURRENT);
+ if (err) {
+ dev_err(led->dev, "regmap failed: %d", err);
+ goto exit;
+ }
+
+ brightness = cpcap_led_val(value, LED_OFF);
+ } else {
+ brightness = cpcap_led_val(value, LED_ON);
+ }
+
+ err = regmap_update_bits(led->regmap, led->info->red_reg, led->info->mask,
+ brightness);
+ if (err) {
+ dev_err(led->dev, "regmap failed: %d", err);
+ goto exit;
+ }
+
+ if (value == LED_OFF)
+ err = cpcap_led_set_power(led, false);
+
+exit:
+ mutex_unlock(&led->update_lock);
+ return err;
+}
+
+static int cpcap_led_set_green(struct led_classdev_rgb *rgbled_cdev,
+ enum led_brightness value)
+{
+ struct cpcap_led *led = rgbled_cdev_to_led(rgbled_cdev);
+ int brightness;
+ int err;
+
+ mutex_lock(&led->update_lock);
+
+ if (value > LED_OFF) {
+ err = cpcap_led_set_power(led, true);
+ if (err)
+ goto exit;
+ }
+
+ if (value == LED_OFF) {
+ /* Avoid HW issue by turning off current before duty cycle */
+ err = regmap_update_bits(led->regmap,
+ led->info->green_reg, led->info->mask, CPCAP_LED_NO_CURRENT);
+ if (err) {
+ dev_err(led->dev, "regmap failed: %d", err);
+ goto exit;
+ }
+
+ brightness = cpcap_led_val(value, LED_OFF);
+ } else {
+ brightness = cpcap_led_val(value, LED_ON);
+ }
+
+ err = regmap_update_bits(led->regmap, led->info->green_reg, led->info->mask,
+ brightness);
+ if (err) {
+ dev_err(led->dev, "regmap failed: %d", err);
+ goto exit;
+ }
+
+ if (value == LED_OFF)
+ err = cpcap_led_set_power(led, false);
+exit:
+ mutex_unlock(&led->update_lock);
+ return err;
+}
+
+static int cpcap_led_set_blue(struct led_classdev_rgb *rgbled_cdev,
+ enum led_brightness value)
+{
+ struct cpcap_led *led = rgbled_cdev_to_led(rgbled_cdev);
+ int brightness;
+ int err;
+
+ mutex_lock(&led->update_lock);
+
+ if (value > LED_OFF) {
+ err = cpcap_led_set_power(led, true);
+ if (err)
+ goto exit;
+ }
+
+ if (value == LED_OFF) {
+ /* Avoid HW issue by turning off current before duty cycle */
+ err = regmap_update_bits(led->regmap,
+ led->info->blue_reg, led->info->mask, CPCAP_LED_NO_CURRENT);
+ if (err) {
+ dev_err(led->dev, "regmap failed: %d", err);
+ goto exit;
+ }
+
+ brightness = cpcap_led_val(value, LED_OFF);
+ } else {
+ brightness = cpcap_led_val(value, LED_ON);
+ }
+
+ err = regmap_update_bits(led->regmap, led->info->blue_reg, led->info->mask,
+ brightness);
+ if (err) {
+ dev_err(led->dev, "regmap failed: %d", err);
+ goto exit;
+ }
+
+ if (value == LED_OFF)
+ err = cpcap_led_set_power(led, false);
+
+exit:
+ mutex_unlock(&led->update_lock);
+ return err;
+}
+static int cpcap_led_set_color(struct led_classdev_rgb *rgbled_cdev)
+{
+ struct led_rgb_colors *colors = &rgbled_cdev->rgb_colors;
+ struct cpcap_led *led = rgbled_cdev_to_led(rgbled_cdev);
+ int red_brightness, green_brightness, blue_brightness;
+ int err;
+
+ mutex_lock(&led->update_lock);
+
+ if (colors->red > LED_OFF || colors->green > LED_OFF ||
+ colors->blue > LED_OFF) {
+ err = cpcap_led_set_power(led, true);
+ if (err)
+ goto exit;
+ }
+
+ if (colors->red == LED_OFF && colors->green == LED_OFF &&
+ colors->blue == LED_OFF) {
+ /* Avoid HW issue by turning off current before duty cycle */
+ err = regmap_update_bits(led->regmap,
+ led->info->red_reg, led->info->mask, CPCAP_LED_NO_CURRENT);
+ if (err) {
+ dev_err(led->dev, "regmap failed: %d", err);
+ goto exit;
+ }
+
+ err = regmap_update_bits(led->regmap,
+ led->info->green_reg, led->info->mask, CPCAP_LED_NO_CURRENT);
+ if (err) {
+ dev_err(led->dev, "regmap failed: %d", err);
+ goto exit;
+ }
+
+ err = regmap_update_bits(led->regmap,
+ led->info->blue_reg, led->info->mask, CPCAP_LED_NO_CURRENT);
+ if (err) {
+ dev_err(led->dev, "regmap failed: %d", err);
+ goto exit;
+ }
+
+ red_brightness = cpcap_led_val(colors->red, LED_OFF);
+ green_brightness = cpcap_led_val(colors->green, LED_OFF);
+ blue_brightness = cpcap_led_val(colors->blue, LED_OFF);
+ } else {
+ red_brightness = cpcap_led_val(colors->red, LED_ON);
+ green_brightness = cpcap_led_val(colors->green, LED_ON);
+ blue_brightness = cpcap_led_val(colors->blue, LED_ON);
+ }
+
+ err = regmap_update_bits(led->regmap, led->info->red_reg, led->info->mask,
+ red_brightness);
+ if (err) {
+ dev_err(led->dev, "regmap failed: %d", err);
+ goto exit;
+ }
+
+ err = regmap_update_bits(led->regmap, led->info->green_reg, led->info->mask,
+ green_brightness);
+ if (err) {
+ dev_err(led->dev, "regmap failed: %d", err);
+ goto exit;
+ }
+
+ err = regmap_update_bits(led->regmap, led->info->blue_reg, led->info->mask,
+ blue_brightness);
+ if (err) {
+ dev_err(led->dev, "regmap failed: %d", err);
+ goto exit;
+ }
+
+ if (colors->red == LED_OFF && colors->green == LED_OFF &&
+ colors->blue == LED_OFF)
+ err = cpcap_led_set_power(led, false);
+
+exit:
+ mutex_unlock(&led->update_lock);
+ return err;
+}
+
+static struct led_rgb_ops cpcap_led_rgb_ops = {
+ .set_color = cpcap_led_set_color,
+ .set_red_brightness = cpcap_led_set_red,
+ .set_green_brightness = cpcap_led_set_green,
+ .set_blue_brightness = cpcap_led_set_blue,
+};
+
static const struct of_device_id cpcap_led_of_match[] = {
{ .compatible = "motorola,cpcap-led-red", .data = &cpcap_led_red },
{ .compatible = "motorola,cpcap-led-green", .data = &cpcap_led_green },
{ .compatible = "motorola,cpcap-led-blue", .data = &cpcap_led_blue },
+ { .compatible = "motorola,cpcap-led-rgb", .data = &cpcap_led_rgb },
{ .compatible = "motorola,cpcap-led-adl", .data = &cpcap_led_adl },
{ .compatible = "motorola,cpcap-led-cp", .data = &cpcap_led_cp },
{},
@@ -167,6 +409,7 @@ MODULE_DEVICE_TABLE(of, cpcap_led_of_match);
static int cpcap_led_probe(struct platform_device *pdev)
{
const struct of_device_id *match;
+ struct led_classdev *led_cdev;
struct cpcap_led *led;
int err;
@@ -181,7 +424,7 @@ static int cpcap_led_probe(struct platform_device *pdev)
led->info = match->data;
led->dev = &pdev->dev;
- if (led->info->reg == 0x0000) {
+ if (!led->info->rgb_combo && led->info->reg == 0x0000) {
dev_err(led->dev, "Unsupported LED");
return -ENODEV;
}
@@ -204,19 +447,49 @@ static int cpcap_led_probe(struct platform_device *pdev)
}
if (led->info->init_mask) {
- err = regmap_update_bits(led->regmap, led->info->reg,
- led->info->init_mask, led->info->init_val);
- if (err) {
- dev_err(led->dev, "regmap failed: %d", err);
- return err;
+ if (led->info->rgb_combo) {
+ err = regmap_update_bits(led->regmap, led->info->red_reg,
+ led->info->init_mask, led->info->init_val);
+ if (err) {
+ dev_err(led->dev, "regmap failed: %d", err);
+ return err;
+ }
+ err = regmap_update_bits(led->regmap, led->info->green_reg,
+ led->info->init_mask, led->info->init_val);
+ if (err) {
+ dev_err(led->dev, "regmap failed: %d", err);
+ return err;
+ }
+ err = regmap_update_bits(led->regmap, led->info->blue_reg,
+ led->info->init_mask, led->info->init_val);
+ if (err) {
+ dev_err(led->dev, "regmap failed: %d", err);
+ return err;
+ }
+ } else {
+ err = regmap_update_bits(led->regmap, led->info->reg,
+ led->info->init_mask, led->info->init_val);
+ if (err) {
+ dev_err(led->dev, "regmap failed: %d", err);
+ return err;
+ }
}
}
mutex_init(&led->update_lock);
- led->led.max_brightness = led->info->limit;
- led->led.brightness_set_blocking = cpcap_led_set;
- err = devm_led_classdev_register(&pdev->dev, &led->led);
+ if (!led->info->rgb_combo) {
+ led->led.max_brightness = led->info->limit;
+ led->led.brightness_set_blocking = cpcap_led_set;
+ err = devm_led_classdev_register(&pdev->dev, &led->led);
+ } else {
+ led->rgb_cdev.ops = &cpcap_led_rgb_ops;
+ led_cdev = &led->rgb_cdev.led_cdev;
+ led_cdev->name = led->led.name;
+ led_cdev->brightness_set_blocking = cpcap_led_set;
+ err = led_classdev_rgb_register(&pdev->dev, &led->rgb_cdev);
+ }
+
if (err) {
dev_err(led->dev, "Couldn't register LED: %d", err);
return err;
@@ -253,11 +253,15 @@ static const struct mfd_cell cpcap_mfd_devices[] = {
.of_compatible = "motorola,cpcap-led-blue",
}, {
.name = "cpcap-led",
- .id = 3,
+ .id = 4,
+ .of_compatible = "motorola,cpcap-led-rgb",
+ }, {
+ .name = "cpcap-led",
+ .id = 5,
.of_compatible = "motorola,cpcap-led-adl",
}, {
.name = "cpcap-led",
- .id = 4,
+ .id = 6,
.of_compatible = "motorola,cpcap-led-cp",
}, {
.name = "cpcap-codec",
Signed-off-by: Dan Murphy <dmurphy@ti.com> --- drivers/leds/leds-cpcap.c | 291 +++++++++++++++++++++++++++++++++-- drivers/mfd/motorola-cpcap.c | 8 +- 2 files changed, 288 insertions(+), 11 deletions(-) -- 2.20.1.98.gecbdaf0899