Re: [PATCH v4] input: qt2160: Add support for LEDs.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Javier,

On Mon, Oct 22, 2012 at 10:04:12AM +0200, Javier Martin wrote:
> Outputs x8..x0 of the qt2160 can have leds attached to it.
> This patch handles those outputs using the generic LED
> framework.
> 
> The PWM controls available in the chip are used to achieve
> different levels of brightness.
> 
> Signed-off-by: Javier Martin <javier.martin@xxxxxxxxxxxxxxxxx>
> ---
> Changes since v3:
>  - Protect led related code with #ifdefs so that users can decide
>  wether to enable LEDS_CLASS support in the kernel or not.
> 
> ---
>  drivers/input/keyboard/qt2160.c |  113 ++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 111 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/input/keyboard/qt2160.c b/drivers/input/keyboard/qt2160.c
> index 73ea4b0..482a7c5 100644
> --- a/drivers/input/keyboard/qt2160.c
> +++ b/drivers/input/keyboard/qt2160.c
> @@ -20,6 +20,7 @@
>  
>  #include <linux/kernel.h>
>  #include <linux/init.h>
> +#include <linux/leds.h>
>  #include <linux/module.h>
>  #include <linux/slab.h>
>  #include <linux/jiffies.h>
> @@ -39,6 +40,11 @@
>  #define QT2160_CMD_GPIOS      6
>  #define QT2160_CMD_SUBVER     7
>  #define QT2160_CMD_CALIBRATE  10
> +#define QT2160_CMD_DRIVE_X    70
> +#define QT2160_CMD_PWMEN_X    74
> +#define QT2160_CMD_PWM_DUTY   76
> +
> +#define QT2160_NUM_LEDS_X	8
>  
>  #define QT2160_CYCLE_INTERVAL	(2*HZ)
>  
> @@ -49,6 +55,17 @@ static unsigned char qt2160_key2code[] = {
>  	KEY_C, KEY_D, KEY_E, KEY_F,
>  };
>  
> +#ifdef CONFIG_LEDS_CLASS
> +struct qt2160_led {
> +	struct qt2160_data *qt2160;
> +	struct led_classdev cdev;
> +	struct work_struct work;
> +	char name[32];
> +	int id;
> +	enum led_brightness new_brightness;
> +};
> +#endif
> +
>  struct qt2160_data {
>  	struct i2c_client *client;
>  	struct input_dev *input;
> @@ -56,8 +73,61 @@ struct qt2160_data {
>  	spinlock_t lock;        /* Protects canceling/rescheduling of dwork */
>  	unsigned short keycodes[ARRAY_SIZE(qt2160_key2code)];
>  	u16 key_matrix;
> +#ifdef CONFIG_LEDS_CLASS
> +	struct qt2160_led leds[QT2160_NUM_LEDS_X];
> +	struct mutex led_lock;
> +#endif
>  };
>  
> +static int qt2160_read(struct i2c_client *client, u8 reg);
> +static int qt2160_write(struct i2c_client *client, u8 reg, u8 data);
> +
> +#ifdef CONFIG_LEDS_CLASS
> +
> +static void qt2160_led_work(struct work_struct *work)
> +{
> +	struct qt2160_led *led = container_of(work, struct qt2160_led, work);
> +	struct qt2160_data *qt2160 = led->qt2160;
> +	struct i2c_client *client = qt2160->client;
> +	int value = led->new_brightness;
> +	u32 drive, pwmen;
> +
> +	mutex_lock(&qt2160->led_lock);

What about accessing I2C from other contexts? Don't we need to take this
lock there too?

> +
> +	drive = qt2160_read(client, QT2160_CMD_DRIVE_X);
> +	pwmen = qt2160_read(client, QT2160_CMD_PWMEN_X);
> +	if (value != LED_OFF) {
> +		drive |= (1 << led->id);
> +		pwmen |= (1 << led->id);
> +
> +	} else {
> +		drive &= ~(1 << led->id);
> +		pwmen &= ~(1 << led->id);
> +	}
> +	qt2160_write(client, QT2160_CMD_DRIVE_X, drive);
> +	qt2160_write(client, QT2160_CMD_PWMEN_X, pwmen);
> +
> +	/*
> +	 * Changing this register will change the brightness
> +	 * of every LED in the qt2160. It's a HW limitation.
> +	 */
> +	if (value != LED_OFF)
> +		qt2160_write(client, QT2160_CMD_PWM_DUTY, value);
> +
> +	mutex_unlock(&qt2160->led_lock);
> +}
> +
> +static void qt2160_led_set(struct led_classdev *cdev,
> +			   enum led_brightness value)
> +{
> +	struct qt2160_led *led = container_of(cdev, struct qt2160_led, cdev);
> +
> +	led->new_brightness = value;
> +	schedule_work(&led->work);
> +}

static int __devinit qt2160_register_leds(struct qt2160_data *qt2160_data)
{
	...
}

static void __devexit qt2160_unregister_leds(struct qt2160_data *qt2160_data)
{
	...
}
#else
static inline int qt2160_register_leds(struct qt2160_data *qt2160_data)
{
	return 0;
}

static inline void qt2160_unregister_leds(struct qt2160_data *qt2160_data)
{
}

> +
> +#endif /* CONFIG_LEDS_CLASS */
> +
>  static int qt2160_read_block(struct i2c_client *client,
>  			     u8 inireg, u8 *buffer, unsigned int count)
>  {
> @@ -184,7 +254,7 @@ static void qt2160_worker(struct work_struct *work)
>  	qt2160_schedule_read(qt2160);
>  }
>  
> -static int __devinit qt2160_read(struct i2c_client *client, u8 reg)
> +static int qt2160_read(struct i2c_client *client, u8 reg)
>  {
>  	int ret;
>  
> @@ -205,7 +275,7 @@ static int __devinit qt2160_read(struct i2c_client *client, u8 reg)
>  	return ret;
>  }
>  
> -static int __devinit qt2160_write(struct i2c_client *client, u8 reg, u8 data)
> +static int qt2160_write(struct i2c_client *client, u8 reg, u8 data)
>  {
>  	int ret;
>  
> @@ -325,8 +395,38 @@ static int __devinit qt2160_probe(struct i2c_client *client,
>  	i2c_set_clientdata(client, qt2160);
>  	qt2160_schedule_read(qt2160);
>  
> +#ifdef CONFIG_LEDS_CLASS
> +	mutex_init(&qt2160->led_lock);
> +
> +	for (i = 0; i < QT2160_NUM_LEDS_X; i++) {
> +		struct qt2160_led *led = &qt2160->leds[i];
> +
> +		snprintf(led->name, sizeof(led->name), "qt2160:x%d", i);
> +		led->cdev.name = led->name;
> +		led->cdev.brightness_set = qt2160_led_set;
> +		led->cdev.brightness = LED_OFF;
> +		led->id = i;
> +		led->qt2160 = qt2160;
> +
> +		INIT_WORK(&led->work, qt2160_led_work);
> +
> +		error = led_classdev_register(&client->dev, &led->cdev);
> +		if (error < 0)
> +			goto err_unreg_input;
> +	}
> +
> +	/* Tur off LEDs */
> +	qt2160_write(client, QT2160_CMD_DRIVE_X, 0);
> +	qt2160_write(client, QT2160_CMD_PWMEN_X, 0);
> +	qt2160_write(client, QT2160_CMD_PWM_DUTY, 0);
> +#endif
> +
>  	return 0;
>  
> +#ifdef CONFIG_LEDS_CLASS
> +err_unreg_input:
> +	input_unregister_device(input);
> +#endif
>  err_free_irq:
>  	if (client->irq)
>  		free_irq(client->irq, qt2160);
> @@ -340,6 +440,15 @@ static int __devexit qt2160_remove(struct i2c_client *client)
>  {
>  	struct qt2160_data *qt2160 = i2c_get_clientdata(client);
>  
> +#ifdef CONFIG_LEDS_CLASS
> +	int i;
> +
> +	for (i = 0; i < QT2160_NUM_LEDS_X; i++) {
> +		led_classdev_unregister(&qt2160->leds[i].cdev);
> +		cancel_work_sync(&qt2160->leds[i].work);

This should be the other way around.

> +	}
> +#endif
> +
>  	/* Release IRQ so no queue will be scheduled */
>  	if (client->irq)
>  		free_irq(client->irq, qt2160);
> -- 
> 1.7.9.5
> 

Thanks.

-- 
Dmitry
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Media Devel]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Linux Wireless Networking]     [Linux Omap]

  Powered by Linux