Input: pixcir_i2c_ts - initialize interrupt mode and power mode
[deliverable/linux.git] / drivers / input / touchscreen / pixcir_i2c_ts.c
index 5d36243bdc79455dcb8a41dcced8d287dcd98e4e..6c6f6dacb8582fe58fdf362aaeb44bfa6315a111 100644 (file)
@@ -29,7 +29,7 @@ struct pixcir_i2c_ts_data {
        struct i2c_client *client;
        struct input_dev *input;
        const struct pixcir_ts_platform_data *chip;
-       bool exiting;
+       bool running;
 };
 
 static void pixcir_ts_poscheck(struct pixcir_i2c_ts_data *data)
@@ -88,7 +88,7 @@ static irqreturn_t pixcir_ts_isr(int irq, void *dev_id)
 {
        struct pixcir_i2c_ts_data *tsdata = dev_id;
 
-       while (!tsdata->exiting) {
+       while (tsdata->running) {
                pixcir_ts_poscheck(tsdata);
 
                if (tsdata->chip->attb_read_val())
@@ -100,6 +100,164 @@ static irqreturn_t pixcir_ts_isr(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
+static int pixcir_set_power_mode(struct pixcir_i2c_ts_data *ts,
+                                enum pixcir_power_mode mode)
+{
+       struct device *dev = &ts->client->dev;
+       int ret;
+
+       ret = i2c_smbus_read_byte_data(ts->client, PIXCIR_REG_POWER_MODE);
+       if (ret < 0) {
+               dev_err(dev, "%s: can't read reg 0x%x : %d\n",
+                       __func__, PIXCIR_REG_POWER_MODE, ret);
+               return ret;
+       }
+
+       ret &= ~PIXCIR_POWER_MODE_MASK;
+       ret |= mode;
+
+       /* Always AUTO_IDLE */
+       ret |= PIXCIR_POWER_ALLOW_IDLE;
+
+       ret = i2c_smbus_write_byte_data(ts->client, PIXCIR_REG_POWER_MODE, ret);
+       if (ret < 0) {
+               dev_err(dev, "%s: can't write reg 0x%x : %d\n",
+                       __func__, PIXCIR_REG_POWER_MODE, ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+/*
+ * Set the interrupt mode for the device i.e. ATTB line behaviour
+ *
+ * @polarity : 1 for active high, 0 for active low.
+ */
+static int pixcir_set_int_mode(struct pixcir_i2c_ts_data *ts,
+                              enum pixcir_int_mode mode, bool polarity)
+{
+       struct device *dev = &ts->client->dev;
+       int ret;
+
+       ret = i2c_smbus_read_byte_data(ts->client, PIXCIR_REG_INT_MODE);
+       if (ret < 0) {
+               dev_err(dev, "%s: can't read reg 0x%x : %d\n",
+                       __func__, PIXCIR_REG_INT_MODE, ret);
+               return ret;
+       }
+
+       ret &= ~PIXCIR_INT_MODE_MASK;
+       ret |= mode;
+
+       if (polarity)
+               ret |= PIXCIR_INT_POL_HIGH;
+       else
+               ret &= ~PIXCIR_INT_POL_HIGH;
+
+       ret = i2c_smbus_write_byte_data(ts->client, PIXCIR_REG_INT_MODE, ret);
+       if (ret < 0) {
+               dev_err(dev, "%s: can't write reg 0x%x : %d\n",
+                       __func__, PIXCIR_REG_INT_MODE, ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+/*
+ * Enable/disable interrupt generation
+ */
+static int pixcir_int_enable(struct pixcir_i2c_ts_data *ts, bool enable)
+{
+       struct device *dev = &ts->client->dev;
+       int ret;
+
+       ret = i2c_smbus_read_byte_data(ts->client, PIXCIR_REG_INT_MODE);
+       if (ret < 0) {
+               dev_err(dev, "%s: can't read reg 0x%x : %d\n",
+                       __func__, PIXCIR_REG_INT_MODE, ret);
+               return ret;
+       }
+
+       if (enable)
+               ret |= PIXCIR_INT_ENABLE;
+       else
+               ret &= ~PIXCIR_INT_ENABLE;
+
+       ret = i2c_smbus_write_byte_data(ts->client, PIXCIR_REG_INT_MODE, ret);
+       if (ret < 0) {
+               dev_err(dev, "%s: can't write reg 0x%x : %d\n",
+                       __func__, PIXCIR_REG_INT_MODE, ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int pixcir_start(struct pixcir_i2c_ts_data *ts)
+{
+       struct device *dev = &ts->client->dev;
+       int error;
+
+       /* LEVEL_TOUCH interrupt with active low polarity */
+       error = pixcir_set_int_mode(ts, PIXCIR_INT_LEVEL_TOUCH, 0);
+       if (error) {
+               dev_err(dev, "Failed to set interrupt mode: %d\n", error);
+               return error;
+       }
+
+       ts->running = true;
+       mb();   /* Update status before IRQ can fire */
+
+       /* enable interrupt generation */
+       error = pixcir_int_enable(ts, true);
+       if (error) {
+               dev_err(dev, "Failed to enable interrupt generation: %d\n",
+                       error);
+               return error;
+       }
+
+       return 0;
+}
+
+static int pixcir_stop(struct pixcir_i2c_ts_data *ts)
+{
+       int error;
+
+       /* Disable interrupt generation */
+       error = pixcir_int_enable(ts, false);
+       if (error) {
+               dev_err(&ts->client->dev,
+                       "Failed to disable interrupt generation: %d\n",
+                       error);
+               return error;
+       }
+
+       /* Exit ISR if running, no more report parsing */
+       ts->running = false;
+       mb();   /* update status before we synchronize irq */
+
+       /* Wait till running ISR is complete */
+       synchronize_irq(ts->client->irq);
+
+       return 0;
+}
+
+static int pixcir_input_open(struct input_dev *dev)
+{
+       struct pixcir_i2c_ts_data *ts = input_get_drvdata(dev);
+
+       return pixcir_start(ts);
+}
+
+static void pixcir_input_close(struct input_dev *dev)
+{
+       struct pixcir_i2c_ts_data *ts = input_get_drvdata(dev);
+
+       pixcir_stop(ts);
+}
+
 #ifdef CONFIG_PM_SLEEP
 static int pixcir_i2c_ts_suspend(struct device *dev)
 {
@@ -156,6 +314,8 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client,
 
        input->name = client->name;
        input->id.bustype = BUS_I2C;
+       input->open = pixcir_input_open;
+       input->close = pixcir_input_close;
        input->dev.parent = &client->dev;
 
        __set_bit(EV_KEY, input->evbit);
@@ -176,11 +336,22 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client,
                return error;
        }
 
+       /* Always be in IDLE mode to save power, device supports auto wake */
+       error = pixcir_set_power_mode(tsdata, PIXCIR_POWER_IDLE);
+       if (error) {
+               dev_err(dev, "Failed to set IDLE mode\n");
+               return error;
+       }
+
+       /* Stop device till opened */
+       error = pixcir_stop(tsdata);
+       if (error)
+               return error;
+
        error = input_register_device(input);
        if (error)
                return error;
 
-       i2c_set_clientdata(client, tsdata);
        device_init_wakeup(&client->dev, 1);
 
        return 0;
@@ -188,13 +359,8 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client,
 
 static int pixcir_i2c_ts_remove(struct i2c_client *client)
 {
-       struct pixcir_i2c_ts_data *tsdata = i2c_get_clientdata(client);
-
        device_init_wakeup(&client->dev, 0);
 
-       tsdata->exiting = true;
-       mb();
-
        return 0;
 }
 
This page took 0.028015 seconds and 5 git commands to generate.