* [PATCH] cap11xx: add LED support
@ 2015-06-09 3:46 Matt Ranostay
2015-06-09 5:25 ` Daniel Mack
2015-06-09 5:26 ` Daniel Mack
0 siblings, 2 replies; 7+ messages in thread
From: Matt Ranostay @ 2015-06-09 3:46 UTC (permalink / raw)
To: dmitry.torokhov, zonque; +Cc: linux-input, Matt Ranostay
Several cap11xx variants have LEDs that be can be controlled, this
patchset implements this functionality.
Signed-off-by: Matt Ranostay <mranostay@gmail.com>
---
drivers/input/keyboard/cap11xx.c | 102 +++++++++++++++++++++++++++++++++++++--
1 file changed, 99 insertions(+), 3 deletions(-)
diff --git a/drivers/input/keyboard/cap11xx.c b/drivers/input/keyboard/cap11xx.c
index f07461a..dc48936 100644
--- a/drivers/input/keyboard/cap11xx.c
+++ b/drivers/input/keyboard/cap11xx.c
@@ -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,8 @@ 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_LED_OUTPUT_CONTROL, 0x00 },
{ CAP11XX_REG_SENSOR_CALIB_LSB1, 0x00 },
{ CAP11XX_REG_SENSOR_CALIB_LSB2, 0x00 },
};
@@ -196,6 +217,69 @@ 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;
+
+ 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 = led_classdev_register(dev, &led->cdev);
+ if (ret < 0)
+ return ret;
+
+ INIT_WORK(&led->work, cap11xx_led_work);
+ };
+ return 0;
+}
+
+static int cap11xx_i2c_remove(struct i2c_client *client)
+{
+ struct cap11xx_priv *priv = i2c_get_clientdata(client);
+ int i;
+
+ for (i = 0; i < priv->num_leds; i++) {
+ led_classdev_unregister(&priv->leds[i].cdev);
+ cancel_work_sync(&priv->leds[i].work);
+ }
+ regmap_update_bits(priv->regmap,
+ CAP11XX_REG_LED_OUTPUT_CONTROL, 0xff, 0);
+
+ return 0;
+}
+#else
+#define cap11xx_i2c_remove NULL
+#endif
+
static int cap11xx_i2c_probe(struct i2c_client *i2c_client,
const struct i2c_device_id *id)
{
@@ -316,6 +400,17 @@ 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_kzalloc(dev,
+ cap->num_leds * sizeof(struct cap11xx_led),
+ GFP_KERNEL);
+ if (!priv->leds)
+ return -ENOMEM;
+ priv->num_leds = cap->num_leds;
+ cap11xx_register_leds(dev, priv);
+ }
+#endif
input_set_drvdata(priv->idev, priv);
/*
@@ -366,6 +461,7 @@ static struct i2c_driver cap11xx_i2c_driver = {
},
.id_table = cap11xx_i2c_ids,
.probe = cap11xx_i2c_probe,
+ .remove = cap11xx_i2c_remove,
};
module_i2c_driver(cap11xx_i2c_driver);
--
1.9.1
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH] cap11xx: add LED support
2015-06-09 3:46 [PATCH] cap11xx: add LED support Matt Ranostay
@ 2015-06-09 5:25 ` Daniel Mack
2015-06-09 17:12 ` Dmitry Torokhov
2015-06-09 5:26 ` Daniel Mack
1 sibling, 1 reply; 7+ messages in thread
From: Daniel Mack @ 2015-06-09 5:25 UTC (permalink / raw)
To: Matt Ranostay, dmitry.torokhov; +Cc: linux-input
Hi Matt,
On 06/09/2015 05:46 AM, Matt Ranostay wrote:
> Several cap11xx variants have LEDs that be can be controlled, this
> patchset implements this functionality.
>
> Signed-off-by: Matt Ranostay <mranostay@gmail.com>
> ---
> drivers/input/keyboard/cap11xx.c | 102 +++++++++++++++++++++++++++++++++++++--
> 1 file changed, 99 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/input/keyboard/cap11xx.c b/drivers/input/keyboard/cap11xx.c
> index f07461a..dc48936 100644
> --- a/drivers/input/keyboard/cap11xx.c
> +++ b/drivers/input/keyboard/cap11xx.c
> @@ -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,8 @@ 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_LED_OUTPUT_CONTROL, 0x00 },
> { CAP11XX_REG_SENSOR_CALIB_LSB1, 0x00 },
> { CAP11XX_REG_SENSOR_CALIB_LSB2, 0x00 },
> };
> @@ -196,6 +217,69 @@ 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;
> +
> + 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;
Use the .brightness_set_sync callback, so you don't have to dance around
with the workers at all.
> + led->cdev.brightness = LED_OFF;
> + led->cdev.max_brightness = 1;
> + led->id = i;
> + led->priv = priv;
> +
> + ret = led_classdev_register(dev, &led->cdev);
You can use the devm_variant of this function. That will solve the
tricky part of unwinding if registration of only one LED fails.
> + if (ret < 0)
> + return ret;
> +
> + INIT_WORK(&led->work, cap11xx_led_work);
> + };
> + return 0;
> +}
> +
> +static int cap11xx_i2c_remove(struct i2c_client *client)
> +{
> + struct cap11xx_priv *priv = i2c_get_clientdata(client);
> + int i;
> +
> + for (i = 0; i < priv->num_leds; i++) {
> + led_classdev_unregister(&priv->leds[i].cdev);
> + cancel_work_sync(&priv->leds[i].work);
> + }
With the two changes from above, this code can go away.
> + regmap_update_bits(priv->regmap,
> + CAP11XX_REG_LED_OUTPUT_CONTROL, 0xff, 0);
Well, we don't reset the other registers either, so I don't know if it's
worth it. After all, the entire function can go away if you left this
register untouched on removal time.
> +
> + return 0;
> +}
> +#else
> +#define cap11xx_i2c_remove NULL
> +#endif
> +
> static int cap11xx_i2c_probe(struct i2c_client *i2c_client,
> const struct i2c_device_id *id)
> {
> @@ -316,6 +400,17 @@ 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_kzalloc(dev,
> + cap->num_leds * sizeof(struct cap11xx_led),
> + GFP_KERNEL);
There's devm_kcalloc(), but that's nitpicking.
> + if (!priv->leds)
> + return -ENOMEM;
> + priv->num_leds = cap->num_leds;
> + cap11xx_register_leds(dev, priv);
You need to check the return value here and bail.
Thanks,
Daniel
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] cap11xx: add LED support
2015-06-09 3:46 [PATCH] cap11xx: add LED support Matt Ranostay
2015-06-09 5:25 ` Daniel Mack
@ 2015-06-09 5:26 ` Daniel Mack
2015-06-09 16:18 ` Matt Ranostay
1 sibling, 1 reply; 7+ messages in thread
From: Daniel Mack @ 2015-06-09 5:26 UTC (permalink / raw)
To: Matt Ranostay, dmitry.torokhov; +Cc: linux-input
On 06/09/2015 05:46 AM, Matt Ranostay wrote:
> Several cap11xx variants have LEDs that be can be controlled, this
> patchset implements this functionality.
>
> Signed-off-by: Matt Ranostay <mranostay@gmail.com>
> ---
> drivers/input/keyboard/cap11xx.c | 102 +++++++++++++++++++++++++++++++++++++--
> 1 file changed, 99 insertions(+), 3 deletions(-)
Ah, and please include the LED maintainers in v2, they might spot other
details :)
Thanks,
Daniel
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] cap11xx: add LED support
2015-06-09 5:26 ` Daniel Mack
@ 2015-06-09 16:18 ` Matt Ranostay
0 siblings, 0 replies; 7+ messages in thread
From: Matt Ranostay @ 2015-06-09 16:18 UTC (permalink / raw)
To: Daniel Mack; +Cc: Dmitry Torokhov, linux-input@vger.kernel.org
On Mon, Jun 8, 2015 at 10:26 PM, Daniel Mack <daniel@zonque.org> wrote:
> On 06/09/2015 05:46 AM, Matt Ranostay wrote:
>> Several cap11xx variants have LEDs that be can be controlled, this
>> patchset implements this functionality.
>>
>> Signed-off-by: Matt Ranostay <mranostay@gmail.com>
>> ---
>> drivers/input/keyboard/cap11xx.c | 102 +++++++++++++++++++++++++++++++++++++--
>> 1 file changed, 99 insertions(+), 3 deletions(-)
>
> Ah, and please include the LED maintainers in v2, they might spot other
> details :)
>
Will do.
>
> Thanks,
> Daniel
>
>
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] cap11xx: add LED support
2015-06-09 5:25 ` Daniel Mack
@ 2015-06-09 17:12 ` Dmitry Torokhov
2015-06-09 17:30 ` Matt Ranostay
2015-06-09 22:40 ` Daniel Mack
0 siblings, 2 replies; 7+ messages in thread
From: Dmitry Torokhov @ 2015-06-09 17:12 UTC (permalink / raw)
To: Daniel Mack; +Cc: Matt Ranostay, linux-input
On Tue, Jun 09, 2015 at 07:25:42AM +0200, Daniel Mack wrote:
> Hi Matt,
>
> On 06/09/2015 05:46 AM, Matt Ranostay wrote:
> > Several cap11xx variants have LEDs that be can be controlled, this
> > patchset implements this functionality.
> >
> > Signed-off-by: Matt Ranostay <mranostay@gmail.com>
> > ---
> > drivers/input/keyboard/cap11xx.c | 102 +++++++++++++++++++++++++++++++++++++--
> > 1 file changed, 99 insertions(+), 3 deletions(-)
> >
> > diff --git a/drivers/input/keyboard/cap11xx.c b/drivers/input/keyboard/cap11xx.c
> > index f07461a..dc48936 100644
> > --- a/drivers/input/keyboard/cap11xx.c
> > +++ b/drivers/input/keyboard/cap11xx.c
> > @@ -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,8 @@ 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_LED_OUTPUT_CONTROL, 0x00 },
> > { CAP11XX_REG_SENSOR_CALIB_LSB1, 0x00 },
> > { CAP11XX_REG_SENSOR_CALIB_LSB2, 0x00 },
> > };
> > @@ -196,6 +217,69 @@ 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;
> > +
> > + 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;
>
> Use the .brightness_set_sync callback, so you don't have to dance around
> with the workers at all.
Umm, how will it help? You still can't sleep in set_brightness().
brightness_set_sync() just says that the driver guarantees that the LED
state will be set (or error returned) when the function returns.
Thanks.
--
Dmitry
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] cap11xx: add LED support
2015-06-09 17:12 ` Dmitry Torokhov
@ 2015-06-09 17:30 ` Matt Ranostay
2015-06-09 22:40 ` Daniel Mack
1 sibling, 0 replies; 7+ messages in thread
From: Matt Ranostay @ 2015-06-09 17:30 UTC (permalink / raw)
To: Dmitry Torokhov; +Cc: Daniel Mack, linux-input@vger.kernel.org
On Tue, Jun 9, 2015 at 10:12 AM, Dmitry Torokhov
<dmitry.torokhov@gmail.com> wrote:
> On Tue, Jun 09, 2015 at 07:25:42AM +0200, Daniel Mack wrote:
>> Hi Matt,
>>
>> On 06/09/2015 05:46 AM, Matt Ranostay wrote:
>> > Several cap11xx variants have LEDs that be can be controlled, this
>> > patchset implements this functionality.
>> >
>> > Signed-off-by: Matt Ranostay <mranostay@gmail.com>
>> > ---
>> > drivers/input/keyboard/cap11xx.c | 102 +++++++++++++++++++++++++++++++++++++--
>> > 1 file changed, 99 insertions(+), 3 deletions(-)
>> >
>> > diff --git a/drivers/input/keyboard/cap11xx.c b/drivers/input/keyboard/cap11xx.c
>> > index f07461a..dc48936 100644
>> > --- a/drivers/input/keyboard/cap11xx.c
>> > +++ b/drivers/input/keyboard/cap11xx.c
>> > @@ -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,8 @@ 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_LED_OUTPUT_CONTROL, 0x00 },
>> > { CAP11XX_REG_SENSOR_CALIB_LSB1, 0x00 },
>> > { CAP11XX_REG_SENSOR_CALIB_LSB2, 0x00 },
>> > };
>> > @@ -196,6 +217,69 @@ 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;
>> > +
>> > + 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;
>>
>> Use the .brightness_set_sync callback, so you don't have to dance around
>> with the workers at all.
>
> Umm, how will it help? You still can't sleep in set_brightness().
> brightness_set_sync() just says that the driver guarantees that the LED
> state will be set (or error returned) when the function returns.
>
Yeah the worker queues are for sure needed.
> Thanks.
>
> --
> Dmitry
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH] cap11xx: add LED support
2015-06-09 17:12 ` Dmitry Torokhov
2015-06-09 17:30 ` Matt Ranostay
@ 2015-06-09 22:40 ` Daniel Mack
1 sibling, 0 replies; 7+ messages in thread
From: Daniel Mack @ 2015-06-09 22:40 UTC (permalink / raw)
To: Dmitry Torokhov; +Cc: Matt Ranostay, linux-input
On 06/09/2015 07:12 PM, Dmitry Torokhov wrote:
> On Tue, Jun 09, 2015 at 07:25:42AM +0200, Daniel Mack wrote:
>> Use the .brightness_set_sync callback, so you don't have to dance around
>> with the workers at all.
>
> Umm, how will it help? You still can't sleep in set_brightness().
> brightness_set_sync() just says that the driver guarantees that the LED
> state will be set (or error returned) when the function returns.
Hmm, right. I was mislead by this comment in include/linux/leds.h:
/*
* Set LED brightness level immediately - it can block the caller for
* the time required for accessing a LED device register.
*/
But I in fact don't see any wrapper logic that magically uses a worker
when called from interrupt context.
Thanks for the heads-up!
Daniel
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2015-06-09 22:40 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-06-09 3:46 [PATCH] cap11xx: add LED support Matt Ranostay
2015-06-09 5:25 ` Daniel Mack
2015-06-09 17:12 ` Dmitry Torokhov
2015-06-09 17:30 ` Matt Ranostay
2015-06-09 22:40 ` Daniel Mack
2015-06-09 5:26 ` Daniel Mack
2015-06-09 16:18 ` Matt Ranostay
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.