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

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

 



Hi Dmitry,
thank you for your review.

On 22 October 2012 18:17, Dmitry Torokhov <dmitry.torokhov@xxxxxxxxx> wrote:
> 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?

The purpose of this mutex is to avoid races between multiple calls to
this function which is the only one that access (read/modify/write)
CMD_DRIVE_X, CMD_PWMEN_X, CMD_PWM_DUTY registers.
Please, correct me if I am wrong but I don't think we have to take
this mutex anywhere else.

>> +
>> +     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)
> {
> }
>

I understand, let me fix that.

>> +
>> +#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.

Agree.

I will send a new v5 to address the issues you've pointed out but I'd
like to wait for your answer on the I2C lock issue first.

Regards.
-- 
Javier Martin
Vista Silicon S.L.
CDTUC - FASE C - Oficina S-345
Avda de los Castros s/n
39005- Santander. Cantabria. Spain
+34 942 25 32 60
www.vista-silicon.com
--
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