From 2f74e199db7278e71d8882e89fba6472f74f0dd5 Mon Sep 17 00:00:00 2001 From: Dalingrin Date: Thu, 16 Feb 2012 19:25:07 -0600 Subject: [PATCH] isl29023: add delayed work driven mode --- drivers/input/misc/isl29023.c | 85 ++++++++++++++++++++++++++--------- include/linux/isl29023.h | 2 + 2 files changed, 65 insertions(+), 22 deletions(-) diff --git a/drivers/input/misc/isl29023.c b/drivers/input/misc/isl29023.c index 76d4cda6631..1fb56f2a605 100644 --- a/drivers/input/misc/isl29023.c +++ b/drivers/input/misc/isl29023.c @@ -62,12 +62,15 @@ struct isl29023_data { struct mutex lock; struct input_dev *input; struct work_struct work; + struct delayed_work polled_work; struct workqueue_struct *workqueue; char phys[32]; u8 reg_cache[ISL29023_NUM_CACHABLE_REGS]; u8 mode_before_suspend; u8 mode_before_interrupt; u16 rext; + u8 polled; + u16 poll_interval; }; static int gain_range[] = { @@ -286,10 +289,21 @@ static int isl29023_set_mode(struct i2c_client *client, int mode) /* power_state */ static int isl29023_set_power_state(struct i2c_client *client, int state) { - return __isl29023_write_reg(client, ISL29023_COMMAND1, - ISL29023_MODE_MASK, ISL29023_MODE_SHIFT, - state ? - ISL29023_ALS_ONCE_MODE : ISL29023_PD_MODE); + struct isl29023_data *data = i2c_get_clientdata(client); + + int rc = __isl29023_write_reg(client, ISL29023_COMMAND1, + ISL29023_MODE_MASK, ISL29023_MODE_SHIFT, + state ? + ISL29023_ALS_ONCE_MODE : ISL29023_PD_MODE); + + if (data->polled) { + if (state) + schedule_delayed_work(&data->polled_work, + msecs_to_jiffies(data->poll_interval)); + else + cancel_delayed_work_sync(&data->polled_work); + } + return rc; } static int isl29023_get_power_state(struct i2c_client *client) @@ -587,7 +601,7 @@ static ssize_t isl29023_store_mode(struct device *dev, return count; } -static DEVICE_ATTR(mode, S_IWUSR | S_IRUGO, +static DEVICE_ATTR(mode, 0666, isl29023_show_mode, isl29023_store_mode); @@ -612,10 +626,11 @@ static ssize_t isl29023_store_power_state(struct device *dev, return -EINVAL; ret = isl29023_set_power_state(client, val); + return ret ? ret : count; } -static DEVICE_ATTR(power_state, S_IWUSR | S_IRUGO, +static DEVICE_ATTR(power_state, 0666, isl29023_show_power_state, isl29023_store_power_state); /* lux */ @@ -818,25 +833,43 @@ static int isl29023_init_client(struct i2c_client *client) static void isl29023_work(struct work_struct *work) { - struct isl29023_data *data = - container_of(work, struct isl29023_data, work); - struct i2c_client *client = data->client; + struct i2c_client *client; int lux; + struct isl29023_data *data = + container_of(work, struct isl29023_data, work); - /* Clear interrupt flag */ - isl29023_set_int_flag(client, 0); + struct isl29023_data *data_polled = + container_of((struct delayed_work *)work, struct isl29023_data, + polled_work); + + if (data_polled->polled) + data = data_polled; + + client = data->client; + + if (!data->polled) { + /* Clear interrupt flag */ + isl29023_set_int_flag(client, 0); + data->mode_before_interrupt = isl29023_get_mode(client); + } - data->mode_before_interrupt = isl29023_get_mode(client); lux = isl29023_get_adc_value(client); + if (!data->polled) { /* To clear the interrpt status */ - isl29023_set_power_state(client, ISL29023_PD_MODE); - isl29023_set_mode(client, data->mode_before_interrupt); + isl29023_set_power_state(client, ISL29023_PD_MODE); + isl29023_set_mode(client, data->mode_before_interrupt); + } msleep(100); input_report_abs(data->input, ABS_MISC, lux); input_sync(data->input); + + if(data->polled) { + schedule_delayed_work(&data->polled_work, + msecs_to_jiffies(data->poll_interval)); + } } static irqreturn_t isl29023_irq_handler(int irq, void *handle) @@ -871,6 +904,8 @@ static int __devinit isl29023_probe(struct i2c_client *client, data->client = client; data->rext = ls_data->rext; + data->polled = ls_data->polled; + data->poll_interval = ls_data->poll_interval; snprintf(data->phys, sizeof(data->phys), "%s", dev_name(&client->dev)); i2c_set_clientdata(client, data); @@ -905,14 +940,18 @@ static int __devinit isl29023_probe(struct i2c_client *client, if (err) goto exit_free_input; - /* set irq type to edge falling */ - irq_set_irq_type(client->irq, IRQF_TRIGGER_FALLING); - err = request_irq(client->irq, isl29023_irq_handler, 0, - client->dev.driver->name, data); - if (err < 0) { - dev_err(&client->dev, "failed to register irq %d!\n", - client->irq); - goto exit_free_input; + if (!data->polled) { + /* set irq type to edge falling */ + irq_set_irq_type(client->irq, IRQF_TRIGGER_FALLING); + err = request_irq(client->irq, isl29023_irq_handler, 0, + client->dev.driver->name, data); + if (err < 0) { + dev_err(&client->dev, "failed to register irq %d!\n", + client->irq); + goto exit_free_input; + } + } else { + INIT_DELAYED_WORK(&data->polled_work, isl29023_work); } data->workqueue = create_singlethread_workqueue("isl29023"); @@ -939,6 +978,8 @@ static int __devexit isl29023_remove(struct i2c_client *client) { struct isl29023_data *data = i2c_get_clientdata(client); + if (data->polled) + cancel_delayed_work_sync(&data->polled_work); cancel_work_sync(&data->work); destroy_workqueue(data->workqueue); free_irq(client->irq, data); diff --git a/include/linux/isl29023.h b/include/linux/isl29023.h index 0b8c71164ff..f5c95a11c22 100644 --- a/include/linux/isl29023.h +++ b/include/linux/isl29023.h @@ -47,6 +47,8 @@ struct isl29023_platform_data { char *vdd_reg; int rext; + int polled; + int poll_interval; }; #endif