All of lore.kernel.org
 help / color / mirror / Atom feed
From: Daniel Mack <daniel@zonque.org>
To: Matt Ranostay <mranostay@gmail.com>, dmitry.torokhov@gmail.com
Cc: linux-input@vger.kernel.org
Subject: Re: [PATCH] cap11xx: add LED support
Date: Tue, 09 Jun 2015 07:25:42 +0200	[thread overview]
Message-ID: <557678D6.1020804@zonque.org> (raw)
In-Reply-To: <1433821584-20102-1-git-send-email-mranostay@gmail.com>

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

  reply	other threads:[~2015-06-09  5:32 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-06-09  3:46 [PATCH] cap11xx: add LED support Matt Ranostay
2015-06-09  5:25 ` Daniel Mack [this message]
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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=557678D6.1020804@zonque.org \
    --to=daniel@zonque.org \
    --cc=dmitry.torokhov@gmail.com \
    --cc=linux-input@vger.kernel.org \
    --cc=mranostay@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.