diff mbox series

[RFC,6/6] leds: cpcap: Use the RGB framework for Droid4

Message ID 20190121215659.31125-7-dmurphy@ti.com
State New
Headers show
Series Simplified RGB class Version 1 | expand

Commit Message

Dan Murphy Jan. 21, 2019, 9:56 p.m. UTC
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
diff mbox series

Patch

diff --git a/drivers/leds/leds-cpcap.c b/drivers/leds/leds-cpcap.c
index f0f28c442807..4088a52de26b 100644
--- a/drivers/leds/leds-cpcap.c
+++ b/drivers/leds/leds-cpcap.c
@@ -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;
diff --git a/drivers/mfd/motorola-cpcap.c b/drivers/mfd/motorola-cpcap.c
index 20d9692640e1..beb7063559bb 100644
--- a/drivers/mfd/motorola-cpcap.c
+++ b/drivers/mfd/motorola-cpcap.c
@@ -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",