Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 350669
b: refs/heads/master
c: 0e47e3d
h: refs/heads/master
i:
  350667: 3e6e806
v: v3
  • Loading branch information
Javier Martin authored and Dmitry Torokhov committed Jan 5, 2013
1 parent ec0a3f3 commit 56077b2
Show file tree
Hide file tree
Showing 2 changed files with 140 additions and 3 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 0e14235e6cfbc9b7a546d38b51c4e7ffdab41045
refs/heads/master: 0e47e3dccfcfaa262d3162ab353474d91d792000
141 changes: 139 additions & 2 deletions trunk/drivers/input/keyboard/qt2160.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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)

Expand All @@ -49,15 +55,79 @@ 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;
struct delayed_work dwork;
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);

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);
}

#endif /* CONFIG_LEDS_CLASS */

static int qt2160_read_block(struct i2c_client *client,
u8 inireg, u8 *buffer, unsigned int count)
{
Expand Down Expand Up @@ -216,6 +286,63 @@ static int qt2160_write(struct i2c_client *client, u8 reg, u8 data)
return ret;
}

#ifdef CONFIG_LEDS_CLASS

static int qt2160_register_leds(struct qt2160_data *qt2160)
{
struct i2c_client *client = qt2160->client;
int ret;
int i;

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);

ret = led_classdev_register(&client->dev, &led->cdev);
if (ret < 0)
return ret;
}

/* 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);

return 0;
}

static void qt2160_unregister_leds(struct qt2160_data *qt2160)
{
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);
}
}

#else

static inline int qt2160_register_leds(struct qt2160_data *qt2160)
{
return 0;
}

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

#endif

static bool qt2160_identify(struct i2c_client *client)
{
Expand Down Expand Up @@ -249,7 +376,7 @@ static bool qt2160_identify(struct i2c_client *client)
}

static int qt2160_probe(struct i2c_client *client,
const struct i2c_device_id *id)
const struct i2c_device_id *id)
{
struct qt2160_data *qt2160;
struct input_dev *input;
Expand Down Expand Up @@ -314,18 +441,26 @@ static int qt2160_probe(struct i2c_client *client,
}
}

error = qt2160_register_leds(qt2160);
if (error) {
dev_err(&client->dev, "Failed to register leds\n");
goto err_free_irq;
}

error = input_register_device(qt2160->input);
if (error) {
dev_err(&client->dev,
"Failed to register input device\n");
goto err_free_irq;
goto err_unregister_leds;
}

i2c_set_clientdata(client, qt2160);
qt2160_schedule_read(qt2160);

return 0;

err_unregister_leds:
qt2160_unregister_leds(qt2160);
err_free_irq:
if (client->irq)
free_irq(client->irq, qt2160);
Expand All @@ -339,6 +474,8 @@ static int qt2160_remove(struct i2c_client *client)
{
struct qt2160_data *qt2160 = i2c_get_clientdata(client);

qt2160_unregister_leds(qt2160);

/* Release IRQ so no queue will be scheduled */
if (client->irq)
free_irq(client->irq, qt2160);
Expand Down

0 comments on commit 56077b2

Please sign in to comment.