Skip to contentSkip to conversation

cap11xx: add LED support

PATCH Superseded Matt Ranostay 3 participants 5 messages Jun 10, 2015 – Jun 11, 2015

Subsystems (3)

v2 of 6 Superseded Latest is v6 →

lore
Share & tools
lore
#0 lore raw

PATCH v2 1/1 cap11xx: add LED support

Several cap11xx variants have LEDs that be can be controlled, this patchset implements this functionality.

---
 drivers/input/keyboard/cap11xx.c | 90 ++++++++++++++++++++++++++++++++++++++--
 1 file changed, 87 insertions(+), 3 deletions(-)
drivers/input/keyboard/cap11xx.c +87 -3
index f07461a..af1daa0 100644
@@ -12,6 +12,7 @@
 #include <linux/module.h>
 #include <linux/interrupt.h>
 #include <linux/input.h>
+#include <linux/leds.h>
 #include <linux/of_irq.h>
 #include <linux/regmap.h>
 #include <linux/i2c.h>
@@ -47,6 +48,8 @@
 #define CAP11XX_REG_CONFIG2		0x44
 #define CAP11XX_REG_CONFIG2_ALT_POL	BIT(6)
 #define CAP11XX_REG_SENSOR_BASE_CNT(X)	(0x50 + (X))
+#define CAP11XX_REG_LED_POLARITY	0x73
+#define CAP11XX_REG_LED_OUTPUT_CONTROL	0x74
 #define CAP11XX_REG_SENSOR_CALIB	(0xb1 + (X))
 #define CAP11XX_REG_SENSOR_CALIB_LSB1	0xb9
 #define CAP11XX_REG_SENSOR_CALIB_LSB2	0xba
@@ -56,10 +59,25 @@
 
 #define CAP11XX_MANUFACTURER_ID	0x5d
 
+#ifdef CONFIG_LEDS_CLASS
+struct cap11xx_led {
+	struct cap11xx_priv *priv;
+	struct led_classdev cdev;
+	struct work_struct work;
+	char name[32];
+	int id;
+	enum led_brightness new_brightness;
+};
+#endif
+
 struct cap11xx_priv {
 	struct regmap *regmap;
 	struct input_dev *idev;
 
+#ifdef CONFIG_LEDS_CLASS
+	struct cap11xx_led *leds;
+	int num_leds;
+#endif
 	/* config */
 	u32 keycodes[];
 };
@@ -67,6 +85,7 @@ struct cap11xx_priv {
 struct cap11xx_hw_model {
 	u8 product_id;
 	unsigned int num_channels;
+	unsigned int num_leds;
 };
 
 enum {
@@ -76,9 +95,9 @@ enum {
 };
 
 static const struct cap11xx_hw_model cap11xx_devices[] = {
-	[CAP1106] = { .product_id = 0x55, .num_channels = 6 },
-	[CAP1126] = { .product_id = 0x53, .num_channels = 6 },
-	[CAP1188] = { .product_id = 0x50, .num_channels = 8 },
+	[CAP1106] = { .product_id = 0x55, .num_channels = 6, .num_leds = 0 },
+	[CAP1126] = { .product_id = 0x53, .num_channels = 6, .num_leds = 2 },
+	[CAP1188] = { .product_id = 0x50, .num_channels = 8, .num_leds = 8 },
 };
 
 static const struct reg_default cap11xx_reg_defaults[] = {
@@ -111,6 +130,7 @@ static const struct reg_default cap11xx_reg_defaults[] = {
 	{ CAP11XX_REG_STANDBY_SENSITIVITY,	0x02 },
 	{ CAP11XX_REG_STANDBY_THRESH,		0x40 },
 	{ CAP11XX_REG_CONFIG2,			0x40 },
+	{ CAP11XX_REG_LED_POLARITY,		0x00 },
 	{ CAP11XX_REG_SENSOR_CALIB_LSB1,	0x00 },
 	{ CAP11XX_REG_SENSOR_CALIB_LSB2,	0x00 },
 };
@@ -196,6 +216,58 @@ static void cap11xx_input_close(struct input_dev *idev)
 	cap11xx_set_sleep(priv, true);
 }
 
+#ifdef CONFIG_LEDS_CLASS
+static void cap11xx_led_work(struct work_struct *work)
+{
+	struct cap11xx_led *led = container_of(work, struct cap11xx_led, work);
+	struct cap11xx_priv *priv = led->priv;
+	int value = led->new_brightness;
+
+	regmap_update_bits(priv->regmap, CAP11XX_REG_LED_OUTPUT_CONTROL,
+				BIT(led->id), !!value ? BIT(led->id) : 0);
+}
+
+static void cap11xx_led_set(struct led_classdev *cdev,
+			   enum led_brightness value)
+{
+	struct cap11xx_led *led = container_of(cdev, struct cap11xx_led, cdev);
+
+	led->new_brightness = value;
+	schedule_work(&led->work);
+}
+
+static int cap11xx_register_leds(struct device *dev, struct cap11xx_priv *priv)
+{
+	int ret;
+	int i;
+
+	/*
+	 * Be sure all LEDS are in off-state
+	 */
+	regmap_update_bits(priv->regmap,
+			CAP11XX_REG_LED_OUTPUT_CONTROL, 0xff, 0);
+
+	for (i = 0; i < priv->num_leds; i++) {
+		struct cap11xx_led *led = &priv->leds[i];
+
+		snprintf(led->name, sizeof(led->name), "cap11xx:led%d", i + 1);
+		led->cdev.name = led->name;
+		led->cdev.brightness_set = cap11xx_led_set;
+		led->cdev.brightness = LED_OFF;
+		led->cdev.max_brightness = 1;
+		led->id = i;
+		led->priv = priv;
+
+		ret = devm_led_classdev_register(dev, &led->cdev);
+		if (ret < 0)
+			return ret;
+
+		INIT_WORK(&led->work, cap11xx_led_work);
+	};
+	return 0;
+}
+#endif
+
 static int cap11xx_i2c_probe(struct i2c_client *i2c_client,
 			     const struct i2c_device_id *id)
 {
@@ -316,6 +388,18 @@ static int cap11xx_i2c_probe(struct i2c_client *i2c_client,
 	priv->idev->open = cap11xx_input_open;
 	priv->idev->close = cap11xx_input_close;
 
+#ifdef CONFIG_LEDS_CLASS
+	if (cap->num_leds > 0) {
+		priv->leds = devm_kcalloc(dev, cap->num_leds,
+				sizeof(struct cap11xx_led), GFP_KERNEL);
+		if (!priv->leds)
+			return -ENOMEM;
+		priv->num_leds = cap->num_leds;
+		error = cap11xx_register_leds(dev, priv);
+		if (error)
+			return error;
+	}
+#endif
 	input_set_drvdata(priv->idev, priv);
 
 	/*
Signature · 1 line
1.9.1
#1 lore raw

Hi Matt,

Quoted 13 lines · Matt Ranostay

Several cap11xx variants have LEDs that be can be controlled, this patchset implements this functionality.

---
  drivers/input/keyboard/cap11xx.c | 90 ++++++++++++++++++++++++++++++++++++++--
  1 file changed, 87 insertions(+), 3 deletions(-)
drivers/input/keyboard/cap11xx.c +0 -0
index f07461a..af1daa0 100644

drivers/input/keyboard/cap11xx.c line 18

Quoted 7 lines
drivers/input/keyboard/cap11xx.c +1 -0
@@ -12,6 +12,7 @@
  #include <linux/module.h>
  #include <linux/interrupt.h>
  #include <linux/input.h>
+#include <linux/leds.h>
  #include <linux/of_irq.h>
  #include <linux/regmap.h>
  #include <linux/i2c.h>

These include directives aren't arranged in an alphabetical order. You could fix that as you are modifying this file.

Quoted 10 lines
drivers/input/keyboard/cap11xx.c +2 -0
@@ -47,6 +48,8 @@
  #define CAP11XX_REG_CONFIG2		0x44
  #define CAP11XX_REG_CONFIG2_ALT_POL	BIT(6)
  #define CAP11XX_REG_SENSOR_BASE_CNT(X)	(0x50 + (X))
+#define CAP11XX_REG_LED_POLARITY	0x73
+#define CAP11XX_REG_LED_OUTPUT_CONTROL	0x74
  #define CAP11XX_REG_SENSOR_CALIB	(0xb1 + (X))
  #define CAP11XX_REG_SENSOR_CALIB_LSB1	0xb9
  #define CAP11XX_REG_SENSOR_CALIB_LSB2	0xba

drivers/input/keyboard/cap11xx.c line 67

Quoted 9 lines
drivers/input/keyboard/cap11xx.c +6 -0
@@ -56,10 +59,25 @@
 
  #define CAP11XX_MANUFACTURER_ID	0x5d
 
+#ifdef CONFIG_LEDS_CLASS
+struct cap11xx_led {
+	struct cap11xx_priv *priv;
+	struct led_classdev cdev;
+	struct work_struct work;
+	char name[32];

This 'name' field is not needed. Instead the 'name' field of struct led_classdev should be initialized with the address of either a 'label' property from DT child node representing a LED, or the DT child node name if the label is absent. Please refer to Documentation/devicetree/bindings/leds/common.txt.

This is usually accomplished in the LED subsystem with following instruction:

led_cdev->name = of_get_property(child_node, "label", NULL) ? :

                                                child_node->name;
Quoted 25 lines
drivers/input/keyboard/cap11xx.c +10 -0
+	int id;
+	enum led_brightness new_brightness;
+};
+#endif
+
  struct cap11xx_priv {
  	struct regmap *regmap;
  	struct input_dev *idev;
 
+#ifdef CONFIG_LEDS_CLASS
+	struct cap11xx_led *leds;
+	int num_leds;
+#endif
  	/* config */
  	u32 keycodes[];
  };
@@ -67,6 +85,7 @@ struct cap11xx_priv {
  struct cap11xx_hw_model {
  	u8 product_id;
  	unsigned int num_channels;
+	unsigned int num_leds;
  };
 
  enum {

drivers/input/keyboard/cap11xx.c enum { · line 101

Quoted 10 lines
drivers/input/keyboard/cap11xx.c +3 -3
@@ -76,9 +95,9 @@ enum {
  };
 
  static const struct cap11xx_hw_model cap11xx_devices[] = {
-	[CAP1106] = { .product_id = 0x55, .num_channels = 6 },
-	[CAP1126] = { .product_id = 0x53, .num_channels = 6 },
-	[CAP1188] = { .product_id = 0x50, .num_channels = 8 },
+	[CAP1106] = { .product_id = 0x55, .num_channels = 6, .num_leds = 0 },
+	[CAP1126] = { .product_id = 0x53, .num_channels = 6, .num_leds = 2 },
+	[CAP1188] = { .product_id = 0x50, .num_channels = 8, .num_leds = 8 },
  };

The number of LEDs should be determined by the number of DT child nodes representing each LED. Please refer to Documentation/devicetree/bindings/leds/common.txt and other LED subsystem drivers to find out how it should be implemented.

Quoted 88 lines
drivers/input/keyboard/cap11xx.c +65 -0
  static const struct reg_default cap11xx_reg_defaults[] = {
@@ -111,6 +130,7 @@ static const struct reg_default cap11xx_reg_defaults[] = {
  	{ CAP11XX_REG_STANDBY_SENSITIVITY,	0x02 },
  	{ CAP11XX_REG_STANDBY_THRESH,		0x40 },
  	{ CAP11XX_REG_CONFIG2,			0x40 },
+	{ CAP11XX_REG_LED_POLARITY,		0x00 },
  	{ CAP11XX_REG_SENSOR_CALIB_LSB1,	0x00 },
  	{ CAP11XX_REG_SENSOR_CALIB_LSB2,	0x00 },
  };
@@ -196,6 +216,58 @@ static void cap11xx_input_close(struct input_dev *idev)
  	cap11xx_set_sleep(priv, true);
  }
 
+#ifdef CONFIG_LEDS_CLASS
+static void cap11xx_led_work(struct work_struct *work)
+{
+	struct cap11xx_led *led = container_of(work, struct cap11xx_led, work);
+	struct cap11xx_priv *priv = led->priv;
+	int value = led->new_brightness;
+
+	regmap_update_bits(priv->regmap, CAP11XX_REG_LED_OUTPUT_CONTROL,
+				BIT(led->id), !!value ? BIT(led->id) : 0);
+}
+
+static void cap11xx_led_set(struct led_classdev *cdev,
+			   enum led_brightness value)
+{
+	struct cap11xx_led *led = container_of(cdev, struct cap11xx_led, cdev);
+
+	led->new_brightness = value;
+	schedule_work(&led->work);
+}
+
+static int cap11xx_register_leds(struct device *dev, struct cap11xx_priv *priv)
+{
+	int ret;
+	int i;
+
+	/*
+	 * Be sure all LEDS are in off-state
+	 */
+	regmap_update_bits(priv->regmap,
+			CAP11XX_REG_LED_OUTPUT_CONTROL, 0xff, 0);
+
+	for (i = 0; i < priv->num_leds; i++) {
+		struct cap11xx_led *led = &priv->leds[i];
+
+		snprintf(led->name, sizeof(led->name), "cap11xx:led%d", i + 1);
+		led->cdev.name = led->name;
+		led->cdev.brightness_set = cap11xx_led_set;
+		led->cdev.brightness = LED_OFF;
+		led->cdev.max_brightness = 1;
+		led->id = i;
+		led->priv = priv;
+
+		ret = devm_led_classdev_register(dev, &led->cdev);
+		if (ret < 0)
+			return ret;
+
+		INIT_WORK(&led->work, cap11xx_led_work);
+	};
+	return 0;
+}
+#endif
+
  static int cap11xx_i2c_probe(struct i2c_client *i2c_client,
  			     const struct i2c_device_id *id)
  {
@@ -316,6 +388,18 @@ static int cap11xx_i2c_probe(struct i2c_client *i2c_client,
  	priv->idev->open = cap11xx_input_open;
  	priv->idev->close = cap11xx_input_close;
 
+#ifdef CONFIG_LEDS_CLASS
+	if (cap->num_leds > 0) {
+		priv->leds = devm_kcalloc(dev, cap->num_leds,
+				sizeof(struct cap11xx_led), GFP_KERNEL);
+		if (!priv->leds)
+			return -ENOMEM;
+		priv->num_leds = cap->num_leds;
+		error = cap11xx_register_leds(dev, priv);
+		if (error)
+			return error;
+	}
+#endif
  	input_set_drvdata(priv->idev, priv);
 
  	/*
Signature · 2 lines
Best Regards,
Jacek Anaszewski
#2 lore raw
Quoted 102 lines · Jacek Anaszewski

Hi Matt,

Quoted 16 lines · Matt Ranostay

Several cap11xx variants have LEDs that be can be controlled, this patchset implements this functionality.

---
  drivers/input/keyboard/cap11xx.c | 90
++++++++++++++++++++++++++++++++++++++--
  1 file changed, 87 insertions(+), 3 deletions(-)
drivers/input/keyboard/cap11xx.c +0 -0
index f07461a..af1daa0 100644

drivers/input/keyboard/cap11xx.c line 18

Quoted 7 lines
drivers/input/keyboard/cap11xx.c +1 -0
@@ -12,6 +12,7 @@
  #include <linux/module.h>
  #include <linux/interrupt.h>
  #include <linux/input.h>
+#include <linux/leds.h>
  #include <linux/of_irq.h>
  #include <linux/regmap.h>
  #include <linux/i2c.h>

These include directives aren't arranged in an alphabetical order. You could fix that as you are modifying this file.

Quoted 10 lines
drivers/input/keyboard/cap11xx.c +2 -0
@@ -47,6 +48,8 @@
  #define CAP11XX_REG_CONFIG2           0x44
  #define CAP11XX_REG_CONFIG2_ALT_POL   BIT(6)
  #define CAP11XX_REG_SENSOR_BASE_CNT(X)        (0x50 + (X))
+#define CAP11XX_REG_LED_POLARITY       0x73
+#define CAP11XX_REG_LED_OUTPUT_CONTROL 0x74
  #define CAP11XX_REG_SENSOR_CALIB      (0xb1 + (X))
  #define CAP11XX_REG_SENSOR_CALIB_LSB1 0xb9
  #define CAP11XX_REG_SENSOR_CALIB_LSB2 0xba

drivers/input/keyboard/cap11xx.c line 67

Quoted 9 lines
drivers/input/keyboard/cap11xx.c +6 -0
@@ -56,10 +59,25 @@
 
  #define CAP11XX_MANUFACTURER_ID       0x5d
 
+#ifdef CONFIG_LEDS_CLASS
+struct cap11xx_led {
+       struct cap11xx_priv *priv;
+       struct led_classdev cdev;
+       struct work_struct work;
+       char name[32];

This 'name' field is not needed. Instead the 'name' field of struct led_classdev should be initialized with the address of either a 'label' property from DT child node representing a LED, or the DT child node name if the label is absent. Please refer to Documentation/devicetree/bindings/leds/common.txt.

This is usually accomplished in the LED subsystem with following instruction:

led_cdev->name = of_get_property(child_node, "label", NULL) ? :

                                                child_node->name;
Quoted 36 lines
drivers/input/keyboard/cap11xx.c +13 -3
+       int id;
+       enum led_brightness new_brightness;
+};
+#endif
+
  struct cap11xx_priv {
        struct regmap *regmap;
        struct input_dev *idev;
 
+#ifdef CONFIG_LEDS_CLASS
+       struct cap11xx_led *leds;
+       int num_leds;
+#endif
        /* config */
        u32 keycodes[];
  };
@@ -67,6 +85,7 @@ struct cap11xx_priv {
  struct cap11xx_hw_model {
        u8 product_id;
        unsigned int num_channels;
+       unsigned int num_leds;
  };
 
  enum {
@@ -76,9 +95,9 @@ enum {
  };
 
  static const struct cap11xx_hw_model cap11xx_devices[] = {
-       [CAP1106] = { .product_id = 0x55, .num_channels = 6 },
-       [CAP1126] = { .product_id = 0x53, .num_channels = 6 },
-       [CAP1188] = { .product_id = 0x50, .num_channels = 8 },
+       [CAP1106] = { .product_id = 0x55, .num_channels = 6, .num_leds = 0
 },
+       [CAP1126] = { .product_id = 0x53, .num_channels = 6, .num_leds = 2
 },
+       [CAP1188] = { .product_id = 0x50, .num_channels = 8, .num_leds = 8
Quoted 9 lines
Quoted 2 lines
drivers/input/keyboard/cap11xx.c +0 -0
 },
  };

The number of LEDs should be determined by the number of DT child nodes representing each LED. Please refer to Documentation/devicetree/bindings/leds/common.txt and other LED subsystem drivers to find out how it should be implemented.

Ok but this is still needed as a check so you don't "overflow" the number the chipset supports, correct?

Quoted 102 lines
Quoted 95 lines

  static const struct reg_default cap11xx_reg_defaults[] = {
drivers/input/keyboard/cap11xx.c +1 -0
@@ -111,6 +130,7 @@ static const struct reg_default cap11xx_reg_defaults[]
 = {
        { CAP11XX_REG_STANDBY_SENSITIVITY,      0x02 },
        { CAP11XX_REG_STANDBY_THRESH,           0x40 },
        { CAP11XX_REG_CONFIG2,                  0x40 },
+       { CAP11XX_REG_LED_POLARITY,             0x00 },
        { CAP11XX_REG_SENSOR_CALIB_LSB1,        0x00 },
        { CAP11XX_REG_SENSOR_CALIB_LSB2,        0x00 },
  };
drivers/input/keyboard/cap11xx.c +21 -0
@@ -196,6 +216,58 @@ static void cap11xx_input_close(struct input_dev
 *idev)
        cap11xx_set_sleep(priv, true);
  }
 
+#ifdef CONFIG_LEDS_CLASS
+static void cap11xx_led_work(struct work_struct *work)
+{
+       struct cap11xx_led *led = container_of(work, struct cap11xx_led,
 work);
+       struct cap11xx_priv *priv = led->priv;
+       int value = led->new_brightness;
+
+       regmap_update_bits(priv->regmap, CAP11XX_REG_LED_OUTPUT_CONTROL,
+                               BIT(led->id), !!value ? BIT(led->id) : 0);
+}
+
+static void cap11xx_led_set(struct led_classdev *cdev,
+                          enum led_brightness value)
+{
+       struct cap11xx_led *led = container_of(cdev, struct cap11xx_led,
 cdev);
+
+       led->new_brightness = value;
+       schedule_work(&led->work);
+}
+
+static int cap11xx_register_leds(struct device *dev, struct cap11xx_priv

*priv)

drivers/input/keyboard/cap11xx.c +44 -0
+{
+       int ret;
+       int i;
+
+       /*
+        * Be sure all LEDS are in off-state
+        */
+       regmap_update_bits(priv->regmap,
+                       CAP11XX_REG_LED_OUTPUT_CONTROL, 0xff, 0);
+
+       for (i = 0; i < priv->num_leds; i++) {
+               struct cap11xx_led *led = &priv->leds[i];
+
+               snprintf(led->name, sizeof(led->name), "cap11xx:led%d", i
+ 1);
+               led->cdev.name = led->name;
+               led->cdev.brightness_set = cap11xx_led_set;
+               led->cdev.brightness = LED_OFF;
+               led->cdev.max_brightness = 1;
+               led->id = i;
+               led->priv = priv;
+
+               ret = devm_led_classdev_register(dev, &led->cdev);
+               if (ret < 0)
+                       return ret;
+
+               INIT_WORK(&led->work, cap11xx_led_work);
+       };
+       return 0;
+}
+#endif
+
  static int cap11xx_i2c_probe(struct i2c_client *i2c_client,
                             const struct i2c_device_id *id)
  {
@@ -316,6 +388,18 @@ static int cap11xx_i2c_probe(struct i2c_client
 *i2c_client,
        priv->idev->open = cap11xx_input_open;
        priv->idev->close = cap11xx_input_close;
 
+#ifdef CONFIG_LEDS_CLASS
+       if (cap->num_leds > 0) {
+               priv->leds = devm_kcalloc(dev, cap->num_leds,
+                               sizeof(struct cap11xx_led), GFP_KERNEL);
+               if (!priv->leds)
+                       return -ENOMEM;
+               priv->num_leds = cap->num_leds;
+               error = cap11xx_register_leds(dev, priv);
+               if (error)
+                       return error;
+       }
+#endif
        input_set_drvdata(priv->idev, priv);
 
Quoted 2 lines
        /*

--
Best Regards,
Jacek Anaszewski

#3 lore raw

On 06/11/2015 12:07 AM, Matt Ranostay wrote:
[...]

Quoted 13 lines
Quoted 13 lines
Quoted 11 lines
   static const struct cap11xx_hw_model cap11xx_devices[] = {
drivers/input/keyboard/cap11xx.c +1 -3
-       [CAP1106] = { .product_id = 0x55, .num_channels = 6 },
-       [CAP1126] = { .product_id = 0x53, .num_channels = 6 },
-       [CAP1188] = { .product_id = 0x50, .num_channels = 8 },
+       [CAP1106] = { .product_id = 0x55, .num_channels = 6, .num_leds = 0

},
+ [CAP1126] = { .product_id = 0x53, .num_channels = 6, .num_leds = 2
},
+ [CAP1188] = { .product_id = 0x50, .num_channels = 8, .num_leds = 8
},
};

Quoted 7 lines
Quoted 5 lines

The number of LEDs should be determined by the number of DT child nodes representing each LED. Please refer to Documentation/devicetree/bindings/leds/common.txt and other LED subsystem drivers to find out how it should be implemented.

Ok but this is still needed as a check so you don't "overflow" the number the chipset supports, correct?

Right, the above modification is correct then.

Signature · 2 lines
Best Regards,
Jacek Anaszewski
maintainer
#4 lore raw
In reply to Matt Ranostay

Hi Matt,

Quoted 87 lines · Matt Ranostay

Several cap11xx variants have LEDs that be can be controlled, this patchset implements this functionality.

---
 drivers/input/keyboard/cap11xx.c | 90 ++++++++++++++++++++++++++++++++++++++--
 1 file changed, 87 insertions(+), 3 deletions(-)
drivers/input/keyboard/cap11xx.c +23 -3
index f07461a..af1daa0 100644
@@ -12,6 +12,7 @@
 #include <linux/module.h>
 #include <linux/interrupt.h>
 #include <linux/input.h>
+#include <linux/leds.h>
 #include <linux/of_irq.h>
 #include <linux/regmap.h>
 #include <linux/i2c.h>
@@ -47,6 +48,8 @@
 #define CAP11XX_REG_CONFIG2		0x44
 #define CAP11XX_REG_CONFIG2_ALT_POL	BIT(6)
 #define CAP11XX_REG_SENSOR_BASE_CNT(X)	(0x50 + (X))
+#define CAP11XX_REG_LED_POLARITY	0x73
+#define CAP11XX_REG_LED_OUTPUT_CONTROL	0x74
 #define CAP11XX_REG_SENSOR_CALIB	(0xb1 + (X))
 #define CAP11XX_REG_SENSOR_CALIB_LSB1	0xb9
 #define CAP11XX_REG_SENSOR_CALIB_LSB2	0xba
@@ -56,10 +59,25 @@
 
 #define CAP11XX_MANUFACTURER_ID	0x5d
 
+#ifdef CONFIG_LEDS_CLASS
+struct cap11xx_led {
+	struct cap11xx_priv *priv;
+	struct led_classdev cdev;
+	struct work_struct work;
+	char name[32];
+	int id;
+	enum led_brightness new_brightness;
+};
+#endif
+
 struct cap11xx_priv {
 	struct regmap *regmap;
 	struct input_dev *idev;
 
+#ifdef CONFIG_LEDS_CLASS
+	struct cap11xx_led *leds;
+	int num_leds;
+#endif
 	/* config */
 	u32 keycodes[];
 };
@@ -67,6 +85,7 @@ struct cap11xx_priv {
 struct cap11xx_hw_model {
 	u8 product_id;
 	unsigned int num_channels;
+	unsigned int num_leds;
 };
 
 enum {
@@ -76,9 +95,9 @@ enum {
 };
 
 static const struct cap11xx_hw_model cap11xx_devices[] = {
-	[CAP1106] = { .product_id = 0x55, .num_channels = 6 },
-	[CAP1126] = { .product_id = 0x53, .num_channels = 6 },
-	[CAP1188] = { .product_id = 0x50, .num_channels = 8 },
+	[CAP1106] = { .product_id = 0x55, .num_channels = 6, .num_leds = 0 },
+	[CAP1126] = { .product_id = 0x53, .num_channels = 6, .num_leds = 2 },
+	[CAP1188] = { .product_id = 0x50, .num_channels = 8, .num_leds = 8 },
 };
 
 static const struct reg_default cap11xx_reg_defaults[] = {
@@ -111,6 +130,7 @@ static const struct reg_default cap11xx_reg_defaults[] = {
 	{ CAP11XX_REG_STANDBY_SENSITIVITY,	0x02 },
 	{ CAP11XX_REG_STANDBY_THRESH,		0x40 },
 	{ CAP11XX_REG_CONFIG2,			0x40 },
+	{ CAP11XX_REG_LED_POLARITY,		0x00 },
 	{ CAP11XX_REG_SENSOR_CALIB_LSB1,	0x00 },
 	{ CAP11XX_REG_SENSOR_CALIB_LSB2,	0x00 },
 };
@@ -196,6 +216,58 @@ static void cap11xx_input_close(struct input_dev *idev)
 	cap11xx_set_sleep(priv, true);
 }

drivers/input/keyboard/cap11xx.c static void cap11xx_input_close(struct input_dev *idev) · line 227

Quoted 10 lines
drivers/input/keyboard/cap11xx.c +9 -0
 
+#ifdef CONFIG_LEDS_CLASS
+static void cap11xx_led_work(struct work_struct *work)
+{
+	struct cap11xx_led *led = container_of(work, struct cap11xx_led, work);
+	struct cap11xx_priv *priv = led->priv;
+	int value = led->new_brightness;
+
+	regmap_update_bits(priv->regmap, CAP11XX_REG_LED_OUTPUT_CONTROL,
+				BIT(led->id), !!value ? BIT(led->id) : 0);

Why do we need to convert value to bool before doing the tertiary operation?

Quoted 4 lines
drivers/input/keyboard/cap11xx.c +4 -0
+}
+
+static void cap11xx_led_set(struct led_classdev *cdev,
+			   enum led_brightness value)

drivers/input/keyboard/cap11xx.c static void cap11xx_input_close(struct input_dev *idev) · line 241

Quoted 10 lines
drivers/input/keyboard/cap11xx.c +10 -0
+{
+	struct cap11xx_led *led = container_of(cdev, struct cap11xx_led, cdev);
+
+	led->new_brightness = value;
+	schedule_work(&led->work);
+}
+
+static int cap11xx_register_leds(struct device *dev, struct cap11xx_priv *priv)
+{
+	int ret;

Let's call it "error" please.

Quoted 14 lines
drivers/input/keyboard/cap11xx.c +14 -0
+	int i;
+
+	/*
+	 * Be sure all LEDS are in off-state
+	 */
+	regmap_update_bits(priv->regmap,
+			CAP11XX_REG_LED_OUTPUT_CONTROL, 0xff, 0);
+
+	for (i = 0; i < priv->num_leds; i++) {
+		struct cap11xx_led *led = &priv->leds[i];
+
+		snprintf(led->name, sizeof(led->name), "cap11xx:led%d", i + 1);
+		led->cdev.name = led->name;
+		led->cdev.brightness_set = cap11xx_led_set;

drivers/input/keyboard/cap11xx.c static void cap11xx_input_close(struct input_dev *idev) · line 265

Quoted 10 lines
drivers/input/keyboard/cap11xx.c +10 -0
+		led->cdev.brightness = LED_OFF;
+		led->cdev.max_brightness = 1;
+		led->id = i;
+		led->priv = priv;
+
+		ret = devm_led_classdev_register(dev, &led->cdev);
+		if (ret < 0)
+			return ret;
+
+		INIT_WORK(&led->work, cap11xx_led_work);

This is too late, request may come even before you return from devm_led_classdev_register. Please move to a spot above.

drivers/input/keyboard/cap11xx.c static void cap11xx_input_close(struct input_dev *idev) · line 266

Quoted 1 line
drivers/input/keyboard/cap11xx.c +1 -0
+	};

A stray semicolon.

Quoted 13 lines
drivers/input/keyboard/cap11xx.c +6 -0
+	return 0;
+}
+#endif
+
 static int cap11xx_i2c_probe(struct i2c_client *i2c_client,
 			     const struct i2c_device_id *id)
 {
@@ -316,6 +388,18 @@ static int cap11xx_i2c_probe(struct i2c_client *i2c_client,
 	priv->idev->open = cap11xx_input_open;
 	priv->idev->close = cap11xx_input_close;
 
+#ifdef CONFIG_LEDS_CLASS
+	if (cap->num_leds > 0) {

drivers/input/keyboard/cap11xx.c static int cap11xx_i2c_probe(struct i2c_client *i2c_client, · line 402

Quoted 10 lines
drivers/input/keyboard/cap11xx.c +10 -0
+		priv->leds = devm_kcalloc(dev, cap->num_leds,
+				sizeof(struct cap11xx_led), GFP_KERNEL);
+		if (!priv->leds)
+			return -ENOMEM;
+		priv->num_leds = cap->num_leds;
+		error = cap11xx_register_leds(dev, priv);
+		if (error)
+			return error;
+	}
+#endif

Instead of inline #ifdef would prefer you moved memory allocation into cap11xx_register_leds() and stub it out of leds are disabled.

Quoted 6 lines
drivers/input/keyboard/cap11xx.c +0 -0
 	input_set_drvdata(priv->idev, priv);
 
 	/*
Signature · 1 line
1.9.1

Thanks.

Signature · 1 line
Dmitry