From 9a44ff2a6d82348ecda1405bddca06c89ca14a10 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Tue, 18 Oct 2011 21:58:21 +0200 Subject: input: update bu21013 touchscreen controller staging: update Synaptics RMI4 touchpad driver Signed-off-by: Robert Marklund --- Documentation/DocBook/touchp.tmpl | 104 ++++ drivers/input/touchscreen/bu21013_ts.c | 489 +++++++++++++------ drivers/input/touchscreen/synaptics_i2c_rmi.c | 675 ++++++++++++++++++++++++++ drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c | 309 +++++++++--- include/linux/input/bu21013.h | 26 +- 5 files changed, 1372 insertions(+), 231 deletions(-) create mode 100644 Documentation/DocBook/touchp.tmpl create mode 100644 drivers/input/touchscreen/synaptics_i2c_rmi.c diff --git a/Documentation/DocBook/touchp.tmpl b/Documentation/DocBook/touchp.tmpl new file mode 100644 index 00000000000..4301b23bfc0 --- /dev/null +++ b/Documentation/DocBook/touchp.tmpl @@ -0,0 +1,104 @@ + + + + + + Touch screen ROHM BU21013MWV + + + + Naveen Kumar + Gaddipati + +
+ naveen.gaddipati@stericsson.com +
+
+
+
+ + + 2009 + ST-Ericsson + + + + + Linux standard functions + + + + + + + + This documentation is free software; you can redistribute + it and/or modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later + version. + + + + This program is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + MA 02111-1307 USA + + + + For more details see the file COPYING in the source + distribution of Linux. + + +
+ + + + + Introduction + + This documentation describes the functions provided by the driver of touch panel for BU21013 controller + + + + + Known Bugs And Assumptions + + + + None + + + None. + + + + + + + + + Public Functions Provided + + Not Applicable. + + + + + Internal Functions Provided + + This chapter contains the autogenerated documentation of the internal functions of the Tocuh panel driver. + +!Idrivers/input/touchscreen/bu21013_ts.c + + +
diff --git a/drivers/input/touchscreen/bu21013_ts.c b/drivers/input/touchscreen/bu21013_ts.c index 902c7214e88..62dbc183347 100644 --- a/drivers/input/touchscreen/bu21013_ts.c +++ b/drivers/input/touchscreen/bu21013_ts.c @@ -1,5 +1,5 @@ /* - * Copyright (C) ST-Ericsson SA 2010 + * Copyright (C) ST-Ericsson SA 2009 * Author: Naveen Kumar G for ST-Ericsson * License terms:GNU General Public License (GPL) version 2 */ @@ -12,13 +12,14 @@ #include #include #include +#include #include #include #define PEN_DOWN_INTR 0 -#define MAX_FINGERS 2 #define RESET_DELAY 30 -#define PENUP_TIMEOUT (10) +#define PENUP_TIMEOUT 2 /* 2msecs */ +#define SCALE_FACTOR 1000 #define DELTA_MIN 16 #define MASK_BITS 0x03 #define SHIFT_8 8 @@ -131,7 +132,7 @@ #define BU21013_NUMBER_OF_X_SENSORS (6) #define BU21013_NUMBER_OF_Y_SENSORS (11) -#define DRIVER_TP "bu21013_tp" +#define DRIVER_TP "bu21013_ts" /** * struct bu21013_ts_data - touch panel data structure @@ -142,6 +143,12 @@ * @in_dev: pointer to the input device structure * @intr_pin: interrupt pin value * @regulator: pointer to the Regulator used for touch screen + * @enable: variable to indicate the enable/disable of touch screen + * @ext_clk_enable: true if running on ext clk + * @ext_clk_state: Saved state for suspend/resume of ext clk + * @factor_x: x scale factor + * @factor_y: y scale factor + * @tpclk: pointer to clock structure * * Touch panel device data structure */ @@ -149,12 +156,216 @@ struct bu21013_ts_data { struct i2c_client *client; wait_queue_head_t wait; bool touch_stopped; - const struct bu21013_platform_device *chip; + struct bu21013_platform_device *chip; struct input_dev *in_dev; unsigned int intr_pin; struct regulator *regulator; + bool enable; + bool ext_clk_enable; + bool ext_clk_state; + unsigned int factor_x; + unsigned int factor_y; + struct clk *tpclk; }; +static int bu21013_init_chip(struct bu21013_ts_data *data, bool on_ext_clk); + +/** + * bu21013_ext_clk() - enable/disable the external clock + * @pdata: touch screen data + * @enable: enable external clock + * @reconfig: reconfigure chip upon external clock off. + * + * This function used to enable or disable the external clock and possible + * reconfigure hw. + */ +static int bu21013_ext_clk(struct bu21013_ts_data *pdata, bool enable, + bool reconfig) +{ + int retval = 0; + + if (!pdata->tpclk || pdata->ext_clk_enable == enable) + return retval; + + if (enable) { + pdata->ext_clk_enable = true; + clk_enable(pdata->tpclk); + retval = bu21013_init_chip(pdata, true); + } else { + pdata->ext_clk_enable = false; + if (reconfig) + retval = bu21013_init_chip(pdata, false); + clk_disable(pdata->tpclk); + } + return retval; +} + +/** + * bu21013_enable() - enable the touch driver event + * @pdata: touch screen data + * + * This function used to enable the driver and returns integer + */ +static int bu21013_enable(struct bu21013_ts_data *pdata) +{ + int retval; + + if (pdata->regulator) + regulator_enable(pdata->regulator); + + if (pdata->ext_clk_state) + retval = bu21013_ext_clk(pdata, true, true); + else + retval = bu21013_init_chip(pdata, false); + + if (retval < 0) { + dev_err(&pdata->client->dev, "enable hw failed\n"); + return retval; + } + pdata->touch_stopped = false; + enable_irq(pdata->chip->irq); + + return 0; +} + +/** + * bu21013_disable() - disable the touch driver event + * @pdata: touch screen data + * + * This function used to disable the driver and returns integer + */ +static void bu21013_disable(struct bu21013_ts_data *pdata) +{ + pdata->touch_stopped = true; + + pdata->ext_clk_state = pdata->ext_clk_enable; + (void) bu21013_ext_clk(pdata, false, false); + + disable_irq(pdata->chip->irq); + if (pdata->regulator) + regulator_disable(pdata->regulator); +} + +/** + * bu21013_show_attr_enable() - show the touch screen controller status + * @dev: pointer to device structure + * @attr: pointer to device attribute + * @buf: parameter buffer + * + * This funtion is used to show whether the touch screen is enabled or + * disabled + */ +static ssize_t bu21013_show_attr_enable(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct bu21013_ts_data *pdata = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", pdata->enable); +} + +/** + * bu21013_store_attr_enable() - Enable/Disable the touchscreen. + * @dev: pointer to device structure + * @attr: pointer to device attribute + * @buf: parameter buffer + * @count: number of parameters + * + * This funtion is used to enable or disable the touch screen controller. + */ +static ssize_t bu21013_store_attr_enable(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int ret = 0; + unsigned long val; + + struct bu21013_ts_data *pdata = dev_get_drvdata(dev); + + if (strict_strtoul(buf, 0, &val)) + return -EINVAL; + + if ((val != 0) && (val != 1)) + return -EINVAL; + + if (pdata->enable != val) { + pdata->enable = val ? true : false; + if (pdata->enable) { + ret = bu21013_enable(pdata); + if (ret < 0) + return ret; + } else + bu21013_disable(pdata); + } + return count; +} + +/** + * bu21013_show_attr_extclk() - shows the external clock status + * @dev: pointer to device structure + * @attr: pointer to device attribute + * @buf: parameter buffer + * + * This funtion is used to show whether the external clock for the touch + * screen is enabled or disabled. + */ +static ssize_t bu21013_show_attr_extclk(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct bu21013_ts_data *pdata = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", pdata->ext_clk_enable); +} + +/** + * bu21013_store_attr_extclk() - Enable/Disable the external clock + * for the tocuh screen controller. + * @dev: pointer to device structure + * @attr: pointer to device attribute + * @buf: parameter buffer + * @count: number of parameters + * + * This funtion is used enabled or disable the external clock for the touch + * screen controller. + */ +static ssize_t bu21013_store_attr_extclk(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval = 0; + struct bu21013_ts_data *pdata = dev_get_drvdata(dev); + unsigned long val; + + if (strict_strtoul(buf, 0, &val)) + return -EINVAL; + + if ((val != 0) && (val != 1)) + return -EINVAL; + + if (pdata->chip->has_ext_clk) { + if (pdata->enable) + retval = bu21013_ext_clk(pdata, val, true); + else + pdata->ext_clk_state = val; + if (retval < 0) + return retval; + } + return count; +} + +static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO, + bu21013_show_attr_enable, bu21013_store_attr_enable); + +static DEVICE_ATTR(ext_clk, S_IWUSR | S_IRUGO, + bu21013_show_attr_extclk, bu21013_store_attr_extclk); + + +static struct attribute *bu21013_attribute[] = { + &dev_attr_enable.attr, + &dev_attr_ext_clk.attr, + NULL, +}; + +static struct attribute_group bu21013_attr_group = { + .attrs = bu21013_attribute, +}; + + /** * bu21013_read_block_data(): read the touch co-ordinates * @data: bu21013_ts_data structure pointer @@ -204,12 +415,14 @@ static int bu21013_do_touch_report(struct bu21013_ts_data *data) if (!has_x_sensors || !has_y_sensors) return 0; - for (i = 0; i < MAX_FINGERS; i++) { + for (i = 0; i < 2; i++) { const u8 *p = &buf[4 * i + 3]; unsigned int x = p[0] << SHIFT_2 | (p[1] & MASK_BITS); unsigned int y = p[2] << SHIFT_2 | (p[3] & MASK_BITS); if (x == 0 || y == 0) continue; + x = x * data->factor_x / SCALE_FACTOR; + y = y * data->factor_y / SCALE_FACTOR; pos_x[finger_down_count] = x; pos_y[finger_down_count] = y; finger_down_count++; @@ -217,21 +430,21 @@ static int bu21013_do_touch_report(struct bu21013_ts_data *data) if (finger_down_count) { if (finger_down_count == 2 && - (abs(pos_x[0] - pos_x[1]) < DELTA_MIN || - abs(pos_y[0] - pos_y[1]) < DELTA_MIN)) { + (abs(pos_x[0] - pos_x[1]) < DELTA_MIN || + abs(pos_y[0] - pos_y[1]) < DELTA_MIN)) return 0; - } for (i = 0; i < finger_down_count; i++) { - if (data->chip->x_flip) - pos_x[i] = data->chip->touch_x_max - pos_x[i]; - if (data->chip->y_flip) - pos_y[i] = data->chip->touch_y_max - pos_y[i]; - - input_report_abs(data->in_dev, - ABS_MT_POSITION_X, pos_x[i]); - input_report_abs(data->in_dev, - ABS_MT_POSITION_Y, pos_y[i]); + if (data->chip->portrait && data->chip->x_flip) + pos_x[i] = data->chip->x_max_res - pos_x[i]; + if (data->chip->portrait && data->chip->y_flip) + pos_y[i] = data->chip->y_max_res - pos_y[i]; + input_report_abs(data->in_dev, ABS_MT_TOUCH_MAJOR, + max(pos_x[i], pos_y[i])); + input_report_abs(data->in_dev, ABS_MT_POSITION_X, + pos_x[i]); + input_report_abs(data->in_dev, ABS_MT_POSITION_Y, + pos_y[i]); input_mt_sync(data->in_dev); } } else @@ -261,24 +474,23 @@ static irqreturn_t bu21013_gpio_irq(int irq, void *device_data) dev_err(&i2c->dev, "bu21013_do_touch_report failed\n"); return IRQ_NONE; } - data->intr_pin = data->chip->irq_read_val(); if (data->intr_pin == PEN_DOWN_INTR) wait_event_timeout(data->wait, data->touch_stopped, - msecs_to_jiffies(2)); + msecs_to_jiffies(PENUP_TIMEOUT)); } while (!data->intr_pin && !data->touch_stopped); - return IRQ_HANDLED; } /** * bu21013_init_chip() - power on sequence for the bu21013 controller * @data: device structure pointer + * @on_ext_clk: Run on external clock * * This function is used to power on * the bu21013 controller and returns integer. */ -static int bu21013_init_chip(struct bu21013_ts_data *data) +static int bu21013_init_chip(struct bu21013_ts_data *data, bool on_ext_clk) { int retval; struct i2c_client *i2c = data->client; @@ -297,28 +509,24 @@ static int bu21013_init_chip(struct bu21013_ts_data *data) dev_err(&i2c->dev, "BU21013_SENSOR_0_7 reg write failed\n"); return retval; } - retval = i2c_smbus_write_byte_data(i2c, BU21013_SENSOR_8_15_REG, BU21013_SENSORS_EN_8_15); if (retval < 0) { dev_err(&i2c->dev, "BU21013_SENSOR_8_15 reg write failed\n"); return retval; } - retval = i2c_smbus_write_byte_data(i2c, BU21013_SENSOR_16_23_REG, BU21013_SENSORS_EN_16_23); if (retval < 0) { dev_err(&i2c->dev, "BU21013_SENSOR_16_23 reg write failed\n"); return retval; } - retval = i2c_smbus_write_byte_data(i2c, BU21013_POS_MODE1_REG, (BU21013_POS_MODE1_0 | BU21013_POS_MODE1_1)); if (retval < 0) { dev_err(&i2c->dev, "BU21013_POS_MODE1 reg write failed\n"); return retval; } - retval = i2c_smbus_write_byte_data(i2c, BU21013_POS_MODE2_REG, (BU21013_POS_MODE2_ZERO | BU21013_POS_MODE2_AVG1 | BU21013_POS_MODE2_AVG2 | BU21013_POS_MODE2_EN_RAW | @@ -327,8 +535,7 @@ static int bu21013_init_chip(struct bu21013_ts_data *data) dev_err(&i2c->dev, "BU21013_POS_MODE2 reg write failed\n"); return retval; } - - if (data->chip->ext_clk) + if (on_ext_clk) retval = i2c_smbus_write_byte_data(i2c, BU21013_CLK_MODE_REG, (BU21013_CLK_MODE_EXT | BU21013_CLK_MODE_CALIB)); else @@ -338,21 +545,18 @@ static int bu21013_init_chip(struct bu21013_ts_data *data) dev_err(&i2c->dev, "BU21013_CLK_MODE reg write failed\n"); return retval; } - retval = i2c_smbus_write_byte_data(i2c, BU21013_IDLE_REG, (BU21013_IDLET_0 | BU21013_IDLE_INTERMIT_EN)); if (retval < 0) { dev_err(&i2c->dev, "BU21013_IDLE reg write failed\n"); return retval; } - retval = i2c_smbus_write_byte_data(i2c, BU21013_INT_MODE_REG, BU21013_INT_MODE_LEVEL); if (retval < 0) { dev_err(&i2c->dev, "BU21013_INT_MODE reg write failed\n"); return retval; } - retval = i2c_smbus_write_byte_data(i2c, BU21013_FILTER_REG, (BU21013_DELTA_0_6 | BU21013_FILTER_EN)); @@ -367,14 +571,12 @@ static int bu21013_init_chip(struct bu21013_ts_data *data) dev_err(&i2c->dev, "BU21013_TH_ON reg write failed\n"); return retval; } - retval = i2c_smbus_write_byte_data(i2c, BU21013_TH_OFF_REG, BU21013_TH_OFF_4 | BU21013_TH_OFF_3); if (retval < 0) { dev_err(&i2c->dev, "BU21013_TH_OFF reg write failed\n"); return retval; } - retval = i2c_smbus_write_byte_data(i2c, BU21013_GAIN_REG, (BU21013_GAIN_0 | BU21013_GAIN_1)); if (retval < 0) { @@ -388,7 +590,6 @@ static int bu21013_init_chip(struct bu21013_ts_data *data) dev_err(&i2c->dev, "BU21013_OFFSET_MODE reg write failed\n"); return retval; } - retval = i2c_smbus_write_byte_data(i2c, BU21013_XY_EDGE_REG, (BU21013_X_EDGE_0 | BU21013_X_EDGE_2 | BU21013_Y_EDGE_1 | BU21013_Y_EDGE_3)); @@ -396,7 +597,6 @@ static int bu21013_init_chip(struct bu21013_ts_data *data) dev_err(&i2c->dev, "BU21013_XY_EDGE reg write failed\n"); return retval; } - retval = i2c_smbus_write_byte_data(i2c, BU21013_DONE_REG, BU21013_DONE); if (retval < 0) { @@ -404,25 +604,15 @@ static int bu21013_init_chip(struct bu21013_ts_data *data) return retval; } - return 0; -} - -/** - * bu21013_free_irq() - frees IRQ registered for touchscreen - * @bu21013_data: device structure pointer - * - * This function signals interrupt thread to stop processing and - * frees interrupt. - */ -static void bu21013_free_irq(struct bu21013_ts_data *bu21013_data) -{ - bu21013_data->touch_stopped = true; - wake_up(&bu21013_data->wait); - free_irq(bu21013_data->chip->irq, bu21013_data); + data->factor_x = (data->chip->x_max_res * SCALE_FACTOR / + data->chip->touch_x_max); + data->factor_y = (data->chip->y_max_res * SCALE_FACTOR / + data->chip->touch_y_max); + return retval; } /** - * bu21013_probe() - initializes the i2c-client touchscreen driver + * bu21013_probe() - initialzes the i2c-client touchscreen driver * @client: i2c client structure pointer * @id: i2c device id pointer * @@ -432,11 +622,11 @@ static void bu21013_free_irq(struct bu21013_ts_data *bu21013_data) static int __devinit bu21013_probe(struct i2c_client *client, const struct i2c_device_id *id) { + int retval; struct bu21013_ts_data *bu21013_data; struct input_dev *in_dev; - const struct bu21013_platform_device *pdata = + struct bu21013_platform_device *pdata = client->dev.platform_data; - int error; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { @@ -446,53 +636,72 @@ static int __devinit bu21013_probe(struct i2c_client *client, if (!pdata) { dev_err(&client->dev, "platform data not defined\n"); - return -EINVAL; + retval = -EINVAL; + return retval; } bu21013_data = kzalloc(sizeof(struct bu21013_ts_data), GFP_KERNEL); - in_dev = input_allocate_device(); - if (!bu21013_data || !in_dev) { + if (!bu21013_data) { dev_err(&client->dev, "device memory alloc failed\n"); - error = -ENOMEM; - goto err_free_mem; + retval = -ENOMEM; + return retval; + } + /* allocate input device */ + in_dev = input_allocate_device(); + if (!in_dev) { + dev_err(&client->dev, "input device memory alloc failed\n"); + retval = -ENOMEM; + goto err_alloc; } bu21013_data->in_dev = in_dev; bu21013_data->chip = pdata; bu21013_data->client = client; - bu21013_data->regulator = regulator_get(&client->dev, "V-TOUCH"); + bu21013_data->regulator = regulator_get(&client->dev, "avdd"); if (IS_ERR(bu21013_data->regulator)) { - dev_err(&client->dev, "regulator_get failed\n"); - error = PTR_ERR(bu21013_data->regulator); - goto err_free_mem; + dev_warn(&client->dev, "regulator_get failed\n"); + bu21013_data->regulator = NULL; } - - error = regulator_enable(bu21013_data->regulator); - if (error < 0) { - dev_err(&client->dev, "regulator enable failed\n"); - goto err_put_regulator; - } - - bu21013_data->touch_stopped = false; - init_waitqueue_head(&bu21013_data->wait); + if (bu21013_data->regulator) + regulator_enable(bu21013_data->regulator); /* configure the gpio pins */ if (pdata->cs_en) { - error = pdata->cs_en(pdata->cs_pin); - if (error < 0) { + retval = pdata->cs_en(pdata->cs_pin); + if (retval < 0) { dev_err(&client->dev, "chip init failed\n"); - goto err_disable_regulator; + goto err_init_cs; + } + } + + if (pdata->has_ext_clk) { + bu21013_data->tpclk = clk_get(&client->dev, NULL); + if (IS_ERR(bu21013_data->tpclk)) { + dev_warn(&client->dev, "get extern clock failed\n"); + bu21013_data->tpclk = NULL; + } + } + + if (pdata->enable_ext_clk && bu21013_data->tpclk) { + retval = clk_enable(bu21013_data->tpclk); + if (retval < 0) { + dev_err(&client->dev, "clock enable failed\n"); + goto err_ext_clk; } + bu21013_data->ext_clk_enable = true; } /* configure the touch panel controller */ - error = bu21013_init_chip(bu21013_data); - if (error) { + retval = bu21013_init_chip(bu21013_data, bu21013_data->ext_clk_enable); + if (retval < 0) { dev_err(&client->dev, "error in bu21013 config\n"); - goto err_cs_disable; + goto err_init_config; } + init_waitqueue_head(&bu21013_data->wait); + bu21013_data->touch_stopped = false; + /* register the device to input subsystem */ in_dev->name = DRIVER_TP; in_dev->id.bustype = BUS_I2C; @@ -503,44 +712,63 @@ static int __devinit bu21013_probe(struct i2c_client *client, __set_bit(EV_ABS, in_dev->evbit); input_set_abs_params(in_dev, ABS_MT_POSITION_X, 0, - pdata->touch_x_max, 0, 0); + pdata->x_max_res, 0, 0); input_set_abs_params(in_dev, ABS_MT_POSITION_Y, 0, - pdata->touch_y_max, 0, 0); + pdata->y_max_res, 0, 0); + input_set_abs_params(in_dev, ABS_MT_TOUCH_MAJOR, 0, + max(pdata->x_max_res , pdata->y_max_res), 0, 0); input_set_drvdata(in_dev, bu21013_data); - - error = request_threaded_irq(pdata->irq, NULL, bu21013_gpio_irq, - IRQF_TRIGGER_FALLING | IRQF_SHARED, - DRIVER_TP, bu21013_data); - if (error) { + retval = input_register_device(in_dev); + if (retval) + goto err_input_register; + + retval = request_threaded_irq(pdata->irq, NULL, bu21013_gpio_irq, + (IRQF_TRIGGER_FALLING | IRQF_SHARED), + DRIVER_TP, bu21013_data); + if (retval) { dev_err(&client->dev, "request irq %d failed\n", pdata->irq); - goto err_cs_disable; + goto err_init_irq; } + bu21013_data->enable = true; + i2c_set_clientdata(client, bu21013_data); - error = input_register_device(in_dev); - if (error) { - dev_err(&client->dev, "failed to register input device\n"); - goto err_free_irq; + /* sysfs implementation for dynamic enable/disable the input event */ + retval = sysfs_create_group(&client->dev.kobj, &bu21013_attr_group); + if (retval) { + dev_err(&client->dev, "failed to create sysfs entries\n"); + goto err_sysfs_create; } - device_init_wakeup(&client->dev, pdata->wakeup); - i2c_set_clientdata(client, bu21013_data); - - return 0; + return retval; -err_free_irq: - bu21013_free_irq(bu21013_data); -err_cs_disable: - pdata->cs_dis(pdata->cs_pin); -err_disable_regulator: - regulator_disable(bu21013_data->regulator); -err_put_regulator: - regulator_put(bu21013_data->regulator); -err_free_mem: - input_free_device(in_dev); +err_sysfs_create: + free_irq(pdata->irq, bu21013_data); + i2c_set_clientdata(client, NULL); +err_init_irq: + input_unregister_device(bu21013_data->in_dev); +err_input_register: + wake_up(&bu21013_data->wait); +err_init_config: + if (bu21013_data->tpclk) { + if (bu21013_data->ext_clk_enable) + clk_disable(bu21013_data->tpclk); + clk_put(bu21013_data->tpclk); + } +err_ext_clk: + if (pdata->cs_dis) + pdata->cs_dis(pdata->cs_pin); +err_init_cs: + if (bu21013_data->regulator) { + regulator_disable(bu21013_data->regulator); + regulator_put(bu21013_data->regulator); + } + input_free_device(bu21013_data->in_dev); +err_alloc: kfree(bu21013_data); - return error; + return retval; } + /** * bu21013_remove() - removes the i2c-client touchscreen driver * @client: i2c client structure pointer @@ -552,19 +780,24 @@ static int __devexit bu21013_remove(struct i2c_client *client) { struct bu21013_ts_data *bu21013_data = i2c_get_clientdata(client); - bu21013_free_irq(bu21013_data); - + bu21013_data->touch_stopped = true; + sysfs_remove_group(&client->dev.kobj, &bu21013_attr_group); + wake_up(&bu21013_data->wait); + free_irq(bu21013_data->chip->irq, bu21013_data); bu21013_data->chip->cs_dis(bu21013_data->chip->cs_pin); - input_unregister_device(bu21013_data->in_dev); - regulator_disable(bu21013_data->regulator); - regulator_put(bu21013_data->regulator); - + if (bu21013_data->tpclk) { + if (bu21013_data->ext_clk_enable) + clk_disable(bu21013_data->tpclk); + clk_put(bu21013_data->tpclk); + } + if (bu21013_data->regulator) { + regulator_disable(bu21013_data->regulator); + regulator_put(bu21013_data->regulator); + } kfree(bu21013_data); - device_init_wakeup(&client->dev, false); - return 0; } @@ -579,15 +812,8 @@ static int __devexit bu21013_remove(struct i2c_client *client) static int bu21013_suspend(struct device *dev) { struct bu21013_ts_data *bu21013_data = dev_get_drvdata(dev); - struct i2c_client *client = bu21013_data->client; - bu21013_data->touch_stopped = true; - if (device_may_wakeup(&client->dev)) - enable_irq_wake(bu21013_data->chip->irq); - else - disable_irq(bu21013_data->chip->irq); - - regulator_disable(bu21013_data->regulator); + bu21013_disable(bu21013_data); return 0; } @@ -602,29 +828,8 @@ static int bu21013_suspend(struct device *dev) static int bu21013_resume(struct device *dev) { struct bu21013_ts_data *bu21013_data = dev_get_drvdata(dev); - struct i2c_client *client = bu21013_data->client; - int retval; - retval = regulator_enable(bu21013_data->regulator); - if (retval < 0) { - dev_err(&client->dev, "bu21013 regulator enable failed\n"); - return retval; - } - - retval = bu21013_init_chip(bu21013_data); - if (retval < 0) { - dev_err(&client->dev, "bu21013 controller config failed\n"); - return retval; - } - - bu21013_data->touch_stopped = false; - - if (device_may_wakeup(&client->dev)) - disable_irq_wake(bu21013_data->chip->irq); - else - enable_irq(bu21013_data->chip->irq); - - return 0; + return bu21013_enable(bu21013_data); } static const struct dev_pm_ops bu21013_dev_pm_ops = { diff --git a/drivers/input/touchscreen/synaptics_i2c_rmi.c b/drivers/input/touchscreen/synaptics_i2c_rmi.c new file mode 100644 index 00000000000..5729602cbb6 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_i2c_rmi.c @@ -0,0 +1,675 @@ +/* drivers/input/keyboard/synaptics_i2c_rmi.c + * + * Copyright (C) 2007 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct workqueue_struct *synaptics_wq; + +struct synaptics_ts_data { + uint16_t addr; + struct i2c_client *client; + struct input_dev *input_dev; + int use_irq; + bool has_relative_report; + struct hrtimer timer; + struct work_struct work; + uint16_t max[2]; + int snap_state[2][2]; + int snap_down_on[2]; + int snap_down_off[2]; + int snap_up_on[2]; + int snap_up_off[2]; + int snap_down[2]; + int snap_up[2]; + uint32_t flags; + int reported_finger_count; + int8_t sensitivity_adjust; + int (*power)(int on); + struct early_suspend early_suspend; +}; + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void synaptics_ts_early_suspend(struct early_suspend *h); +static void synaptics_ts_late_resume(struct early_suspend *h); +#endif + +static int synaptics_init_panel(struct synaptics_ts_data *ts) +{ + int ret; + + ret = i2c_smbus_write_byte_data(ts->client, 0xff, 0x10); /* page select = 0x10 */ + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_write_byte_data failed for page select\n"); + goto err_page_select_failed; + } + ret = i2c_smbus_write_byte_data(ts->client, 0x41, 0x04); /* Set "No Clip Z" */ + if (ret < 0) + printk(KERN_ERR "i2c_smbus_write_byte_data failed for No Clip Z\n"); + + ret = i2c_smbus_write_byte_data(ts->client, 0x44, + ts->sensitivity_adjust); + if (ret < 0) + pr_err("synaptics_ts: failed to set Sensitivity Adjust\n"); + +err_page_select_failed: + ret = i2c_smbus_write_byte_data(ts->client, 0xff, 0x04); /* page select = 0x04 */ + if (ret < 0) + printk(KERN_ERR "i2c_smbus_write_byte_data failed for page select\n"); + ret = i2c_smbus_write_byte_data(ts->client, 0xf0, 0x81); /* normal operation, 80 reports per second */ + if (ret < 0) + printk(KERN_ERR "synaptics_ts_resume: i2c_smbus_write_byte_data failed\n"); + return ret; +} + +static void synaptics_ts_work_func(struct work_struct *work) +{ + int i; + int ret; + int bad_data = 0; + struct i2c_msg msg[2]; + uint8_t start_reg; + uint8_t buf[15]; + struct synaptics_ts_data *ts = container_of(work, struct synaptics_ts_data, work); + int buf_len = ts->has_relative_report ? 15 : 13; + + msg[0].addr = ts->client->addr; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = &start_reg; + start_reg = 0x00; + msg[1].addr = ts->client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = buf_len; + msg[1].buf = buf; + + /* printk("synaptics_ts_work_func\n"); */ + for (i = 0; i < ((ts->use_irq && !bad_data) ? 1 : 10); i++) { + ret = i2c_transfer(ts->client->adapter, msg, 2); + if (ret < 0) { + printk(KERN_ERR "synaptics_ts_work_func: i2c_transfer failed\n"); + bad_data = 1; + } else { + /* printk("synaptics_ts_work_func: %x %x %x %x %x %x" */ + /* " %x %x %x %x %x %x %x %x %x, ret %d\n", */ + /* buf[0], buf[1], buf[2], buf[3], */ + /* buf[4], buf[5], buf[6], buf[7], */ + /* buf[8], buf[9], buf[10], buf[11], */ + /* buf[12], buf[13], buf[14], ret); */ + if ((buf[buf_len - 1] & 0xc0) != 0x40) { + printk(KERN_WARNING "synaptics_ts_work_func:" + " bad read %x %x %x %x %x %x %x %x %x" + " %x %x %x %x %x %x, ret %d\n", + buf[0], buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6], buf[7], + buf[8], buf[9], buf[10], buf[11], + buf[12], buf[13], buf[14], ret); + if (bad_data) + synaptics_init_panel(ts); + bad_data = 1; + continue; + } + bad_data = 0; + if ((buf[buf_len - 1] & 1) == 0) { + /* printk("read %d coordinates\n", i); */ + break; + } else { + int pos[2][2]; + int f, a; + int base; + /* int x = buf[3] | (uint16_t)(buf[2] & 0x1f) << 8; */ + /* int y = buf[5] | (uint16_t)(buf[4] & 0x1f) << 8; */ + int z = buf[1]; + int w = buf[0] >> 4; + int finger = buf[0] & 7; + + /* int x2 = buf[3+6] | (uint16_t)(buf[2+6] & 0x1f) << 8; */ + /* int y2 = buf[5+6] | (uint16_t)(buf[4+6] & 0x1f) << 8; */ + /* int z2 = buf[1+6]; */ + /* int w2 = buf[0+6] >> 4; */ + /* int finger2 = buf[0+6] & 7; */ + + /* int dx = (int8_t)buf[12]; */ + /* int dy = (int8_t)buf[13]; */ + int finger2_pressed; + + /* printk("x %4d, y %4d, z %3d, w %2d, F %d, 2nd: x %4d, y %4d, z %3d, w %2d, F %d, dx %4d, dy %4d\n", */ + /* x, y, z, w, finger, */ + /* x2, y2, z2, w2, finger2, */ + /* dx, dy); */ + + base = 2; + for (f = 0; f < 2; f++) { + uint32_t flip_flag = SYNAPTICS_FLIP_X; + for (a = 0; a < 2; a++) { + int p = buf[base + 1]; + p |= (uint16_t)(buf[base] & 0x1f) << 8; + if (ts->flags & flip_flag) + p = ts->max[a] - p; + if (ts->flags & SYNAPTICS_SNAP_TO_INACTIVE_EDGE) { + if (ts->snap_state[f][a]) { + if (p <= ts->snap_down_off[a]) + p = ts->snap_down[a]; + else if (p >= ts->snap_up_off[a]) + p = ts->snap_up[a]; + else + ts->snap_state[f][a] = 0; + } else { + if (p <= ts->snap_down_on[a]) { + p = ts->snap_down[a]; + ts->snap_state[f][a] = 1; + } else if (p >= ts->snap_up_on[a]) { + p = ts->snap_up[a]; + ts->snap_state[f][a] = 1; + } + } + } + pos[f][a] = p; + base += 2; + flip_flag <<= 1; + } + base += 2; + if (ts->flags & SYNAPTICS_SWAP_XY) + swap(pos[f][0], pos[f][1]); + } + if (z) { + input_report_abs(ts->input_dev, ABS_X, pos[0][0]); + input_report_abs(ts->input_dev, ABS_Y, pos[0][1]); + } + input_report_abs(ts->input_dev, ABS_PRESSURE, z); + input_report_abs(ts->input_dev, ABS_TOOL_WIDTH, w); + input_report_key(ts->input_dev, BTN_TOUCH, finger); + finger2_pressed = finger > 1 && finger != 7; + input_report_key(ts->input_dev, BTN_2, finger2_pressed); + if (finger2_pressed) { + input_report_abs(ts->input_dev, ABS_HAT0X, pos[1][0]); + input_report_abs(ts->input_dev, ABS_HAT0Y, pos[1][1]); + } + + if (!finger) + z = 0; + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, z); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, w); + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, pos[0][0]); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, pos[0][1]); + input_mt_sync(ts->input_dev); + if (finger2_pressed) { + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, z); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, w); + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, pos[1][0]); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, pos[1][1]); + input_mt_sync(ts->input_dev); + } else if (ts->reported_finger_count > 1) { + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0); + input_mt_sync(ts->input_dev); + } + ts->reported_finger_count = finger; + input_sync(ts->input_dev); + } + } + } + if (ts->use_irq) + enable_irq(ts->client->irq); +} + +static enum hrtimer_restart synaptics_ts_timer_func(struct hrtimer *timer) +{ + struct synaptics_ts_data *ts = container_of(timer, struct synaptics_ts_data, timer); + /* printk("synaptics_ts_timer_func\n"); */ + + queue_work(synaptics_wq, &ts->work); + + hrtimer_start(&ts->timer, ktime_set(0, 12500000), HRTIMER_MODE_REL); + return HRTIMER_NORESTART; +} + +static irqreturn_t synaptics_ts_irq_handler(int irq, void *dev_id) +{ + struct synaptics_ts_data *ts = dev_id; + + /* printk("synaptics_ts_irq_handler\n"); */ + disable_irq_nosync(ts->client->irq); + queue_work(synaptics_wq, &ts->work); + return IRQ_HANDLED; +} + +static int synaptics_ts_probe( + struct i2c_client *client, const struct i2c_device_id *id) +{ + struct synaptics_ts_data *ts; + uint8_t buf0[4]; + uint8_t buf1[8]; + struct i2c_msg msg[2]; + int ret = 0; + uint16_t max_x, max_y; + int fuzz_x, fuzz_y, fuzz_p, fuzz_w; + struct synaptics_i2c_rmi_platform_data *pdata; + unsigned long irqflags; + int inactive_area_left; + int inactive_area_right; + int inactive_area_top; + int inactive_area_bottom; + int snap_left_on; + int snap_left_off; + int snap_right_on; + int snap_right_off; + int snap_top_on; + int snap_top_off; + int snap_bottom_on; + int snap_bottom_off; + uint32_t panel_version; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + printk(KERN_ERR "synaptics_ts_probe: need I2C_FUNC_I2C\n"); + ret = -ENODEV; + goto err_check_functionality_failed; + } + + ts = kzalloc(sizeof(*ts), GFP_KERNEL); + if (ts == NULL) { + ret = -ENOMEM; + goto err_alloc_data_failed; + } + INIT_WORK(&ts->work, synaptics_ts_work_func); + ts->client = client; + i2c_set_clientdata(client, ts); + pdata = client->dev.platform_data; + if (pdata) + ts->power = pdata->power; + if (ts->power) { + ret = ts->power(1); + if (ret < 0) { + printk(KERN_ERR "synaptics_ts_probe power on failed\n"); + goto err_power_failed; + } + } + + ret = i2c_smbus_write_byte_data(ts->client, 0xf4, 0x01); /* device command = reset */ + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_write_byte_data failed\n"); + /* fail? */ + } + { + int retry = 10; + while (retry-- > 0) { + ret = i2c_smbus_read_byte_data(ts->client, 0xe4); + if (ret >= 0) + break; + msleep(100); + } + } + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_read_byte_data failed\n"); + goto err_detect_failed; + } + printk(KERN_INFO "synaptics_ts_probe: Product Major Version %x\n", ret); + panel_version = ret << 8; + ret = i2c_smbus_read_byte_data(ts->client, 0xe5); + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_read_byte_data failed\n"); + goto err_detect_failed; + } + printk(KERN_INFO "synaptics_ts_probe: Product Minor Version %x\n", ret); + panel_version |= ret; + + ret = i2c_smbus_read_byte_data(ts->client, 0xe3); + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_read_byte_data failed\n"); + goto err_detect_failed; + } + printk(KERN_INFO "synaptics_ts_probe: product property %x\n", ret); + + if (pdata) { + while (pdata->version > panel_version) + pdata++; + ts->flags = pdata->flags; + ts->sensitivity_adjust = pdata->sensitivity_adjust; + irqflags = pdata->irqflags; + inactive_area_left = pdata->inactive_left; + inactive_area_right = pdata->inactive_right; + inactive_area_top = pdata->inactive_top; + inactive_area_bottom = pdata->inactive_bottom; + snap_left_on = pdata->snap_left_on; + snap_left_off = pdata->snap_left_off; + snap_right_on = pdata->snap_right_on; + snap_right_off = pdata->snap_right_off; + snap_top_on = pdata->snap_top_on; + snap_top_off = pdata->snap_top_off; + snap_bottom_on = pdata->snap_bottom_on; + snap_bottom_off = pdata->snap_bottom_off; + fuzz_x = pdata->fuzz_x; + fuzz_y = pdata->fuzz_y; + fuzz_p = pdata->fuzz_p; + fuzz_w = pdata->fuzz_w; + } else { + irqflags = 0; + inactive_area_left = 0; + inactive_area_right = 0; + inactive_area_top = 0; + inactive_area_bottom = 0; + snap_left_on = 0; + snap_left_off = 0; + snap_right_on = 0; + snap_right_off = 0; + snap_top_on = 0; + snap_top_off = 0; + snap_bottom_on = 0; + snap_bottom_off = 0; + fuzz_x = 0; + fuzz_y = 0; + fuzz_p = 0; + fuzz_w = 0; + } + + ret = i2c_smbus_read_byte_data(ts->client, 0xf0); + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_read_byte_data failed\n"); + goto err_detect_failed; + } + printk(KERN_INFO "synaptics_ts_probe: device control %x\n", ret); + + ret = i2c_smbus_read_byte_data(ts->client, 0xf1); + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_read_byte_data failed\n"); + goto err_detect_failed; + } + printk(KERN_INFO "synaptics_ts_probe: interrupt enable %x\n", ret); + + ret = i2c_smbus_write_byte_data(ts->client, 0xf1, 0); /* disable interrupt */ + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_write_byte_data failed\n"); + goto err_detect_failed; + } + + msg[0].addr = ts->client->addr; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = buf0; + buf0[0] = 0xe0; + msg[1].addr = ts->client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = 8; + msg[1].buf = buf1; + ret = i2c_transfer(ts->client->adapter, msg, 2); + if (ret < 0) { + printk(KERN_ERR "i2c_transfer failed\n"); + goto err_detect_failed; + } + printk(KERN_INFO "synaptics_ts_probe: 0xe0: %x %x %x %x %x %x %x %x\n", + buf1[0], buf1[1], buf1[2], buf1[3], + buf1[4], buf1[5], buf1[6], buf1[7]); + + ret = i2c_smbus_write_byte_data(ts->client, 0xff, 0x10); /* page select = 0x10 */ + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_write_byte_data failed for page select\n"); + goto err_detect_failed; + } + ret = i2c_smbus_read_word_data(ts->client, 0x02); + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_read_word_data failed\n"); + goto err_detect_failed; + } + ts->has_relative_report = !(ret & 0x100); + printk(KERN_INFO "synaptics_ts_probe: Sensor properties %x\n", ret); + ret = i2c_smbus_read_word_data(ts->client, 0x04); + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_read_word_data failed\n"); + goto err_detect_failed; + } + ts->max[0] = max_x = (ret >> 8 & 0xff) | ((ret & 0x1f) << 8); + ret = i2c_smbus_read_word_data(ts->client, 0x06); + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_read_word_data failed\n"); + goto err_detect_failed; + } + ts->max[1] = max_y = (ret >> 8 & 0xff) | ((ret & 0x1f) << 8); + if (ts->flags & SYNAPTICS_SWAP_XY) + swap(max_x, max_y); + + ret = synaptics_init_panel(ts); /* will also switch back to page 0x04 */ + if (ret < 0) { + printk(KERN_ERR "synaptics_init_panel failed\n"); + goto err_detect_failed; + } + + ts->input_dev = input_allocate_device(); + if (ts->input_dev == NULL) { + ret = -ENOMEM; + printk(KERN_ERR "synaptics_ts_probe: Failed to allocate input device\n"); + goto err_input_dev_alloc_failed; + } + ts->input_dev->name = "synaptics-rmi-touchscreen"; + set_bit(EV_SYN, ts->input_dev->evbit); + set_bit(EV_KEY, ts->input_dev->evbit); + set_bit(BTN_TOUCH, ts->input_dev->keybit); + set_bit(BTN_2, ts->input_dev->keybit); + set_bit(EV_ABS, ts->input_dev->evbit); + inactive_area_left = inactive_area_left * max_x / 0x10000; + inactive_area_right = inactive_area_right * max_x / 0x10000; + inactive_area_top = inactive_area_top * max_y / 0x10000; + inactive_area_bottom = inactive_area_bottom * max_y / 0x10000; + snap_left_on = snap_left_on * max_x / 0x10000; + snap_left_off = snap_left_off * max_x / 0x10000; + snap_right_on = snap_right_on * max_x / 0x10000; + snap_right_off = snap_right_off * max_x / 0x10000; + snap_top_on = snap_top_on * max_y / 0x10000; + snap_top_off = snap_top_off * max_y / 0x10000; + snap_bottom_on = snap_bottom_on * max_y / 0x10000; + snap_bottom_off = snap_bottom_off * max_y / 0x10000; + fuzz_x = fuzz_x * max_x / 0x10000; + fuzz_y = fuzz_y * max_y / 0x10000; + ts->snap_down[!!(ts->flags & SYNAPTICS_SWAP_XY)] = -inactive_area_left; + ts->snap_up[!!(ts->flags & SYNAPTICS_SWAP_XY)] = max_x + inactive_area_right; + ts->snap_down[!(ts->flags & SYNAPTICS_SWAP_XY)] = -inactive_area_top; + ts->snap_up[!(ts->flags & SYNAPTICS_SWAP_XY)] = max_y + inactive_area_bottom; + ts->snap_down_on[!!(ts->flags & SYNAPTICS_SWAP_XY)] = snap_left_on; + ts->snap_down_off[!!(ts->flags & SYNAPTICS_SWAP_XY)] = snap_left_off; + ts->snap_up_on[!!(ts->flags & SYNAPTICS_SWAP_XY)] = max_x - snap_right_on; + ts->snap_up_off[!!(ts->flags & SYNAPTICS_SWAP_XY)] = max_x - snap_right_off; + ts->snap_down_on[!(ts->flags & SYNAPTICS_SWAP_XY)] = snap_top_on; + ts->snap_down_off[!(ts->flags & SYNAPTICS_SWAP_XY)] = snap_top_off; + ts->snap_up_on[!(ts->flags & SYNAPTICS_SWAP_XY)] = max_y - snap_bottom_on; + ts->snap_up_off[!(ts->flags & SYNAPTICS_SWAP_XY)] = max_y - snap_bottom_off; + printk(KERN_INFO "synaptics_ts_probe: max_x %d, max_y %d\n", max_x, max_y); + printk(KERN_INFO "synaptics_ts_probe: inactive_x %d %d, inactive_y %d %d\n", + inactive_area_left, inactive_area_right, + inactive_area_top, inactive_area_bottom); + printk(KERN_INFO "synaptics_ts_probe: snap_x %d-%d %d-%d, snap_y %d-%d %d-%d\n", + snap_left_on, snap_left_off, snap_right_on, snap_right_off, + snap_top_on, snap_top_off, snap_bottom_on, snap_bottom_off); + input_set_abs_params(ts->input_dev, ABS_X, -inactive_area_left, max_x + inactive_area_right, fuzz_x, 0); + input_set_abs_params(ts->input_dev, ABS_Y, -inactive_area_top, max_y + inactive_area_bottom, fuzz_y, 0); + input_set_abs_params(ts->input_dev, ABS_PRESSURE, 0, 255, fuzz_p, 0); + input_set_abs_params(ts->input_dev, ABS_TOOL_WIDTH, 0, 15, fuzz_w, 0); + input_set_abs_params(ts->input_dev, ABS_HAT0X, -inactive_area_left, max_x + inactive_area_right, fuzz_x, 0); + input_set_abs_params(ts->input_dev, ABS_HAT0Y, -inactive_area_top, max_y + inactive_area_bottom, fuzz_y, 0); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, -inactive_area_left, max_x + inactive_area_right, fuzz_x, 0); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, -inactive_area_top, max_y + inactive_area_bottom, fuzz_y, 0); + input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, fuzz_p, 0); + input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0, 15, fuzz_w, 0); + /* ts->input_dev->name = ts->keypad_info->name; */ + ret = input_register_device(ts->input_dev); + if (ret) { + printk(KERN_ERR "synaptics_ts_probe: Unable to register %s input device\n", ts->input_dev->name); + goto err_input_register_device_failed; + } + if (client->irq) { + ret = request_irq(client->irq, synaptics_ts_irq_handler, irqflags, client->name, ts); + if (ret == 0) { + ret = i2c_smbus_write_byte_data(ts->client, 0xf1, 0x01); /* enable abs int */ + if (ret) + free_irq(client->irq, ts); + } + if (ret == 0) + ts->use_irq = 1; + else + dev_err(&client->dev, "request_irq failed\n"); + } + if (!ts->use_irq) { + hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ts->timer.function = synaptics_ts_timer_func; + hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL); + } +#ifdef CONFIG_HAS_EARLYSUSPEND + ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + ts->early_suspend.suspend = synaptics_ts_early_suspend; + ts->early_suspend.resume = synaptics_ts_late_resume; + register_early_suspend(&ts->early_suspend); +#endif + + printk(KERN_INFO "synaptics_ts_probe: Start touchscreen %s in %s mode\n", ts->input_dev->name, ts->use_irq ? "interrupt" : "polling"); + + return 0; + +err_input_register_device_failed: + input_free_device(ts->input_dev); + +err_input_dev_alloc_failed: +err_detect_failed: +err_power_failed: + kfree(ts); +err_alloc_data_failed: +err_check_functionality_failed: + return ret; +} + +static int synaptics_ts_remove(struct i2c_client *client) +{ + struct synaptics_ts_data *ts = i2c_get_clientdata(client); + unregister_early_suspend(&ts->early_suspend); + if (ts->use_irq) + free_irq(client->irq, ts); + else + hrtimer_cancel(&ts->timer); + input_unregister_device(ts->input_dev); + kfree(ts); + return 0; +} + +static int synaptics_ts_suspend(struct i2c_client *client, pm_message_t mesg) +{ + int ret; + struct synaptics_ts_data *ts = i2c_get_clientdata(client); + + if (ts->use_irq) + disable_irq(client->irq); + else + hrtimer_cancel(&ts->timer); + ret = cancel_work_sync(&ts->work); + if (ret && ts->use_irq) /* if work was pending disable-count is now 2 */ + enable_irq(client->irq); + ret = i2c_smbus_write_byte_data(ts->client, 0xf1, 0); /* disable interrupt */ + if (ret < 0) + printk(KERN_ERR "synaptics_ts_suspend: i2c_smbus_write_byte_data failed\n"); + + ret = i2c_smbus_write_byte_data(client, 0xf0, 0x86); /* deep sleep */ + if (ret < 0) + printk(KERN_ERR "synaptics_ts_suspend: i2c_smbus_write_byte_data failed\n"); + if (ts->power) { + ret = ts->power(0); + if (ret < 0) + printk(KERN_ERR "synaptics_ts_resume power off failed\n"); + } + return 0; +} + +static int synaptics_ts_resume(struct i2c_client *client) +{ + int ret; + struct synaptics_ts_data *ts = i2c_get_clientdata(client); + + if (ts->power) { + ret = ts->power(1); + if (ret < 0) + printk(KERN_ERR "synaptics_ts_resume power on failed\n"); + } + + synaptics_init_panel(ts); + + if (ts->use_irq) + enable_irq(client->irq); + + if (!ts->use_irq) + hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL); + else + i2c_smbus_write_byte_data(ts->client, 0xf1, 0x01); /* enable abs int */ + + return 0; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void synaptics_ts_early_suspend(struct early_suspend *h) +{ + struct synaptics_ts_data *ts; + ts = container_of(h, struct synaptics_ts_data, early_suspend); + synaptics_ts_suspend(ts->client, PMSG_SUSPEND); +} + +static void synaptics_ts_late_resume(struct early_suspend *h) +{ + struct synaptics_ts_data *ts; + ts = container_of(h, struct synaptics_ts_data, early_suspend); + synaptics_ts_resume(ts->client); +} +#endif + +static const struct i2c_device_id synaptics_ts_id[] = { + { SYNAPTICS_I2C_RMI_NAME, 0 }, + { } +}; + +static struct i2c_driver synaptics_ts_driver = { + .probe = synaptics_ts_probe, + .remove = synaptics_ts_remove, +#ifndef CONFIG_HAS_EARLYSUSPEND + .suspend = synaptics_ts_suspend, + .resume = synaptics_ts_resume, +#endif + .id_table = synaptics_ts_id, + .driver = { + .name = SYNAPTICS_I2C_RMI_NAME, + }, +}; + +static int __devinit synaptics_ts_init(void) +{ + synaptics_wq = create_singlethread_workqueue("synaptics_wq"); + if (!synaptics_wq) + return -ENOMEM; + return i2c_add_driver(&synaptics_ts_driver); +} + +static void __exit synaptics_ts_exit(void) +{ + i2c_del_driver(&synaptics_ts_driver); + if (synaptics_wq) + destroy_workqueue(synaptics_wq); +} + +module_init(synaptics_ts_init); +module_exit(synaptics_ts_exit); + +MODULE_DESCRIPTION("Synaptics Touchscreen Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c b/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c index 11728a03f8a..2659ea1d70d 100644 --- a/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c +++ b/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c @@ -5,7 +5,7 @@ * * Author: Js HA for ST-Ericsson * Author: Naveen Kumar G for ST-Ericsson - * Copyright 2010 (c) ST-Ericsson AB + * Copyright 2010 (c) ST-Ericsson SA */ /* * This file is licensed under the GPL2 license. @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -36,8 +37,10 @@ /* TODO: for multiple device support will need a per-device mutex */ #define DRIVER_NAME "synaptics_rmi4_i2c" +#define DELTA 8 #define MAX_ERROR_REPORT 6 -#define MAX_TOUCH_MAJOR 15 +#define TIMEOUT_PERIOD 1 +#define MAX_WIDTH_MAJOR 255 #define MAX_RETRY_COUNT 5 #define STD_QUERY_LEN 21 #define PAGE_LEN 2 @@ -45,6 +48,7 @@ #define BUF_LEN 37 #define QUERY_LEN 9 #define DATA_LEN 12 +#define RESUME_DELAY 100 /* msecs */ #define HAS_TAP 0x01 #define HAS_PALMDETECT 0x01 #define HAS_ROTATE 0x02 @@ -164,6 +168,8 @@ struct synaptics_rmi4_device_info { * @regulator: pointer to the regulator structure * @wait: wait queue structure variable * @touch_stopped: flag to stop the thread function + * @enable: flag to enable/disable the driver event. + * @resume_wq_handler: work queue for resume the device * * This structure gives the device data information. */ @@ -184,6 +190,8 @@ struct synaptics_rmi4_data { struct regulator *regulator; wait_queue_head_t wait; bool touch_stopped; + bool enable; + struct work_struct resume_wq_handler; }; /** @@ -290,6 +298,133 @@ exit: return retval; } +/** + * synaptics_rmi4_enable() - enable the touchpad driver event + * @pdata: pointer to synaptics_rmi4_data structure + * + * This function is to enable the touchpad driver event and returns integer. + */ +static int synaptics_rmi4_enable(struct synaptics_rmi4_data *pdata) +{ + int retval; + unsigned char intr_status; + + if (pdata->board->regulator_en) + regulator_enable(pdata->regulator); + enable_irq(pdata->board->irq_number); + pdata->touch_stopped = false; + + msleep(RESUME_DELAY); + retval = synaptics_rmi4_i2c_block_read(pdata, + pdata->fn01_data_base_addr + 1, + &intr_status, + pdata->number_of_interrupt_register); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_i2c_byte_write(pdata, + pdata->fn01_ctrl_base_addr + 1, + (intr_status | TOUCHPAD_CTRL_INTR)); + if (retval < 0) + return retval; + + return 0; +} + +/** + * synaptics_rmi4_disable() - disable the touchpad driver event + * @pdata: pointer to synaptics_rmi4_data structure + * + * This function is to disable the driver event and returns integer. + */ + +static int synaptics_rmi4_disable(struct synaptics_rmi4_data *pdata) +{ + int retval; + unsigned char intr_status; + + pdata->touch_stopped = true; + disable_irq(pdata->board->irq_number); + + retval = synaptics_rmi4_i2c_block_read(pdata, + pdata->fn01_data_base_addr + 1, + &intr_status, + pdata->number_of_interrupt_register); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_i2c_byte_write(pdata, + pdata->fn01_ctrl_base_addr + 1, + (intr_status & ~TOUCHPAD_CTRL_INTR)); + if (retval < 0) + return retval; + if (pdata->board->regulator_en) + regulator_disable(pdata->regulator); + + return 0; +} + +/** + * synaptics_rmi4_show_attr_enable() - show the touchpad enable value + * @dev: pointer to device data structure + * @attr: pointer to attribute structure + * @buf: pointer to character buffer + * + * This function is to show the touchpad enable value and returns ssize_t. + */ +static ssize_t synaptics_rmi4_show_attr_enable(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *pdata = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", pdata->enable); +} + +/** + * synaptics_rmi4_store_attr_enable() - store the touchpad enable value + * @dev: pointer to device data structure + * @attr: pointer to attribute structure + * @buf: pointer to character buffer + * @count: number fo arguments + * + * This function is to store the touchpad enable value and returns ssize_t. + */ +static ssize_t synaptics_rmi4_store_attr_enable(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct synaptics_rmi4_data *pdata = dev_get_drvdata(dev); + unsigned long val; + int retval = 0; + + if (strict_strtoul(buf, 0, &val)) + return -EINVAL; + + if ((val != 0) && (val != 1)) + return -EINVAL; + + if (pdata->enable != val) { + pdata->enable = val ? true : false; + if (pdata->enable) + retval = synaptics_rmi4_enable(pdata); + else + retval = synaptics_rmi4_disable(pdata); + + } + return ((retval < 0) ? retval : count); +} + +static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO, + synaptics_rmi4_show_attr_enable, synaptics_rmi4_store_attr_enable); + +static struct attribute *synaptics_rmi4_attrs[] = { + &dev_attr_enable.attr, + NULL, +}; + +static struct attribute_group synaptics_rmi4_attr_group = { + .attrs = synaptics_rmi4_attrs, +}; + /** * synpatics_rmi4_touchpad_report() - reports for the rmi4 touchpad device * @pdata: pointer to synaptics_rmi4_data structure @@ -316,8 +451,9 @@ static int synpatics_rmi4_touchpad_report(struct synaptics_rmi4_data *pdata, unsigned char data[DATA_LEN]; int x[RMI4_NUMBER_OF_MAX_FINGERS]; int y[RMI4_NUMBER_OF_MAX_FINGERS]; - int wx[RMI4_NUMBER_OF_MAX_FINGERS]; - int wy[RMI4_NUMBER_OF_MAX_FINGERS]; + int w[RMI4_NUMBER_OF_MAX_FINGERS]; + static int prv_x[RMI4_NUMBER_OF_MAX_FINGERS]; + static int prv_y[RMI4_NUMBER_OF_MAX_FINGERS]; struct i2c_client *client = pdata->i2c_client; /* get 2D sensor finger data */ @@ -376,11 +512,7 @@ static int synpatics_rmi4_touchpad_report(struct synaptics_rmi4_data *pdata, y[touch_count] = (data[1] << 4) | ((data[2] >> 4) & MASK_4BIT); - wy[touch_count] = - (data[3] >> 4) & MASK_4BIT; - wx[touch_count] = - (data[3] & MASK_4BIT); - + w[touch_count] = data[3]; if (pdata->board->x_flip) x[touch_count] = pdata->sensor_max_x - @@ -389,6 +521,25 @@ static int synpatics_rmi4_touchpad_report(struct synaptics_rmi4_data *pdata, y[touch_count] = pdata->sensor_max_y - y[touch_count]; + if (x[touch_count] < 0) + x[touch_count] = 0; + else if (x[touch_count] >= pdata->sensor_max_x) + x[touch_count] = + pdata->sensor_max_x - 1; + + if (y[touch_count] < 0) + y[touch_count] = 0; + else if (y[touch_count] >= pdata->sensor_max_y) + y[touch_count] = + pdata->sensor_max_y - 1; + } + if ((abs(x[finger] - prv_x[finger]) < DELTA) && + (abs(y[finger] - prv_y[finger]) < DELTA)) { + x[finger] = prv_x[finger]; + y[finger] = prv_y[finger]; + } else { + prv_x[finger] = x[finger]; + prv_y[finger] = y[finger]; } /* number of active touch points */ touch_count++; @@ -399,7 +550,9 @@ static int synpatics_rmi4_touchpad_report(struct synaptics_rmi4_data *pdata, if (touch_count) { for (finger = 0; finger < touch_count; finger++) { input_report_abs(pdata->input_dev, ABS_MT_TOUCH_MAJOR, - max(wx[finger] , wy[finger])); + max(x[finger] , y[finger])); + input_report_abs(pdata->input_dev, ABS_MT_WIDTH_MAJOR, + w[finger]); input_report_abs(pdata->input_dev, ABS_MT_POSITION_X, x[finger]); input_report_abs(pdata->input_dev, ABS_MT_POSITION_Y, @@ -502,7 +655,7 @@ static irqreturn_t synaptics_rmi4_irq(int irq, void *data) touch_count = synaptics_rmi4_sensor_report(pdata); if (touch_count) wait_event_timeout(pdata->wait, pdata->touch_stopped, - msecs_to_jiffies(1)); + msecs_to_jiffies(TIMEOUT_PERIOD)); else break; } while (!pdata->touch_stopped); @@ -880,6 +1033,24 @@ static int synaptics_rmi4_i2c_query_device(struct synaptics_rmi4_data *pdata) return 0; } +/** + * synaptics_rmi4_resume_handler() - work queue for resume handler + * @work:work_struct structure pointer + * + * This work queue handler used to resume the device and returns none + */ +static void synaptics_rmi4_resume_handler(struct work_struct *work) +{ + struct synaptics_rmi4_data *prmi4_data = container_of(work, + struct synaptics_rmi4_data, resume_wq_handler); + struct i2c_client *client = prmi4_data->i2c_client; + int retval; + + retval = synaptics_rmi4_enable(prmi4_data); + if (retval < 0) + dev_err(&client->dev, "%s: resume failed\n", __func__); +} + /** * synaptics_rmi4_probe() - Initialze the i2c-client touchscreen driver * @i2c: i2c client structure pointer @@ -927,19 +1098,17 @@ static int __devinit synaptics_rmi4_probe goto err_input; } - rmi4_data->regulator = regulator_get(&client->dev, "vdd"); - if (IS_ERR(rmi4_data->regulator)) { - dev_err(&client->dev, "%s:get regulator failed\n", - __func__); - retval = PTR_ERR(rmi4_data->regulator); - goto err_get_regulator; - } - retval = regulator_enable(rmi4_data->regulator); - if (retval < 0) { - dev_err(&client->dev, "%s:regulator enable failed\n", - __func__); - goto err_regulator_enable; + if (platformdata->regulator_en) { + rmi4_data->regulator = regulator_get(&client->dev, "vdd"); + if (IS_ERR(rmi4_data->regulator)) { + dev_err(&client->dev, "%s:get regulator failed\n", + __func__); + retval = PTR_ERR(rmi4_data->regulator); + goto err_regulator; + } + regulator_enable(rmi4_data->regulator); } + init_waitqueue_head(&rmi4_data->wait); /* * Copy i2c_client pointer into RTID's i2c_client pointer for @@ -987,7 +1156,16 @@ static int __devinit synaptics_rmi4_probe input_set_abs_params(rmi4_data->input_dev, ABS_MT_POSITION_Y, 0, rmi4_data->sensor_max_y, 0, 0); input_set_abs_params(rmi4_data->input_dev, ABS_MT_TOUCH_MAJOR, 0, - MAX_TOUCH_MAJOR, 0, 0); + max(rmi4_data->sensor_max_y, rmi4_data->sensor_max_y), + 0, 0); + input_set_abs_params(rmi4_data->input_dev, ABS_MT_WIDTH_MAJOR, 0, + MAX_WIDTH_MAJOR, 0, 0); + + retval = input_register_device(rmi4_data->input_dev); + if (retval) { + dev_err(&client->dev, "%s:input register failed\n", __func__); + goto err_input_register; + } /* Clear interrupts */ synaptics_rmi4_i2c_block_read(rmi4_data, @@ -1000,24 +1178,34 @@ static int __devinit synaptics_rmi4_probe if (retval) { dev_err(&client->dev, "%s:Unable to get attn irq %d\n", __func__, platformdata->irq_number); - goto err_query_dev; + goto err_request_irq; } - retval = input_register_device(rmi4_data->input_dev); + INIT_WORK(&rmi4_data->resume_wq_handler, synaptics_rmi4_resume_handler); + + /* sysfs implementation for dynamic enable/disable the input event */ + retval = sysfs_create_group(&client->dev.kobj, + &synaptics_rmi4_attr_group); if (retval) { - dev_err(&client->dev, "%s:input register failed\n", __func__); - goto err_free_irq; + dev_err(&client->dev, "failed to create sysfs entries\n"); + goto err_sysfs; } - + rmi4_data->enable = true; return retval; -err_free_irq: +err_sysfs: + cancel_work_sync(&rmi4_data->resume_wq_handler); +err_request_irq: free_irq(platformdata->irq_number, rmi4_data); + input_unregister_device(rmi4_data->input_dev); +err_input_register: + i2c_set_clientdata(client, NULL); err_query_dev: - regulator_disable(rmi4_data->regulator); -err_regulator_enable: - regulator_put(rmi4_data->regulator); -err_get_regulator: + if (platformdata->regulator_en) { + regulator_disable(rmi4_data->regulator); + regulator_put(rmi4_data->regulator); + } +err_regulator: input_free_device(rmi4_data->input_dev); rmi4_data->input_dev = NULL; err_input: @@ -1037,12 +1225,16 @@ static int __devexit synaptics_rmi4_remove(struct i2c_client *client) struct synaptics_rmi4_data *rmi4_data = i2c_get_clientdata(client); const struct synaptics_rmi4_platform_data *pdata = rmi4_data->board; + sysfs_remove_group(&client->dev.kobj, &synaptics_rmi4_attr_group); rmi4_data->touch_stopped = true; wake_up(&rmi4_data->wait); + cancel_work_sync(&rmi4_data->resume_wq_handler); free_irq(pdata->irq_number, rmi4_data); input_unregister_device(rmi4_data->input_dev); - regulator_disable(rmi4_data->regulator); - regulator_put(rmi4_data->regulator); + if (pdata->regulator_en) { + regulator_disable(rmi4_data->regulator); + regulator_put(rmi4_data->regulator); + } kfree(rmi4_data); return 0; @@ -1059,31 +1251,11 @@ static int __devexit synaptics_rmi4_remove(struct i2c_client *client) static int synaptics_rmi4_suspend(struct device *dev) { /* Touch sleep mode */ - int retval; - unsigned char intr_status; struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); - const struct synaptics_rmi4_platform_data *pdata = rmi4_data->board; - - rmi4_data->touch_stopped = true; - disable_irq(pdata->irq_number); - - retval = synaptics_rmi4_i2c_block_read(rmi4_data, - rmi4_data->fn01_data_base_addr + 1, - &intr_status, - rmi4_data->number_of_interrupt_register); - if (retval < 0) - return retval; - - retval = synaptics_rmi4_i2c_byte_write(rmi4_data, - rmi4_data->fn01_ctrl_base_addr + 1, - (intr_status & ~TOUCHPAD_CTRL_INTR)); - if (retval < 0) - return retval; - - regulator_disable(rmi4_data->regulator); - return 0; + return synaptics_rmi4_disable(rmi4_data); } + /** * synaptics_rmi4_resume() - resume the touch screen controller * @dev: pointer to device structure @@ -1093,28 +1265,9 @@ static int synaptics_rmi4_suspend(struct device *dev) */ static int synaptics_rmi4_resume(struct device *dev) { - int retval; - unsigned char intr_status; struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); - const struct synaptics_rmi4_platform_data *pdata = rmi4_data->board; - - regulator_enable(rmi4_data->regulator); - enable_irq(pdata->irq_number); - rmi4_data->touch_stopped = false; - - retval = synaptics_rmi4_i2c_block_read(rmi4_data, - rmi4_data->fn01_data_base_addr + 1, - &intr_status, - rmi4_data->number_of_interrupt_register); - if (retval < 0) - return retval; - - retval = synaptics_rmi4_i2c_byte_write(rmi4_data, - rmi4_data->fn01_ctrl_base_addr + 1, - (intr_status | TOUCHPAD_CTRL_INTR)); - if (retval < 0) - return retval; + schedule_work(&rmi4_data->resume_wq_handler); return 0; } diff --git a/include/linux/input/bu21013.h b/include/linux/input/bu21013.h index 05e03284b92..143f433b9ee 100644 --- a/include/linux/input/bu21013.h +++ b/include/linux/input/bu21013.h @@ -1,5 +1,5 @@ /* - * Copyright (C) ST-Ericsson SA 2010 + * Copyright (C) ST-Ericsson SA 2009 * Author: Naveen Kumar G for ST-Ericsson * License terms:GNU General Public License (GPL) version 2 */ @@ -9,32 +9,36 @@ /** * struct bu21013_platform_device - Handle the platform data - * @cs_en: pointer to the cs enable function - * @cs_dis: pointer to the cs disable function - * @irq_read_val: pointer to read the pen irq value function + * @cs_en: pointer to the cs enable function + * @cs_dis: pointer to the cs disable function + * @irq_read_val: pointer to read the pen irq value function + * @x_max_res: xmax resolution + * @y_max_res: ymax resolution * @touch_x_max: touch x max * @touch_y_max: touch y max * @cs_pin: chip select pin * @irq: irq pin - * @ext_clk: external clock flag + * @has_ext_clk: has external clock + * @enable_ext_clk: enable external clock + * @portrait: portrait mode flag * @x_flip: x flip flag * @y_flip: y flip flag - * @wakeup: wakeup flag - * * This is used to handle the platform data - */ + **/ struct bu21013_platform_device { int (*cs_en)(int reset_pin); int (*cs_dis)(int reset_pin); int (*irq_read_val)(void); + int x_max_res; + int y_max_res; int touch_x_max; int touch_y_max; unsigned int cs_pin; unsigned int irq; - bool ext_clk; + bool has_ext_clk; + bool enable_ext_clk; + bool portrait; bool x_flip; bool y_flip; - bool wakeup; }; - #endif -- cgit v1.2.3 From 77fc2a261d311ed1eeded422052be678ddc50091 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Tue, 18 Oct 2011 21:59:09 +0200 Subject: u5500: add DB5500 keypad driver ST-Ericsson ID: AP273221 Change-Id: I0f096b8364797595084a84cb61bf13de61a3c0a2 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/5408 Tested-by: Rabin VINCENT Tested-by: build servers Reviewed-by: Linus WALLEIJ --- arch/arm/mach-ux500/include/mach/db5500-keypad.h | 25 ++ drivers/input/keyboard/Kconfig | 10 + drivers/input/keyboard/Makefile | 1 + drivers/input/keyboard/db5500_keypad.c | 422 +++++++++++++++++++++++ 4 files changed, 458 insertions(+) create mode 100644 arch/arm/mach-ux500/include/mach/db5500-keypad.h create mode 100644 drivers/input/keyboard/db5500_keypad.c diff --git a/arch/arm/mach-ux500/include/mach/db5500-keypad.h b/arch/arm/mach-ux500/include/mach/db5500-keypad.h new file mode 100644 index 00000000000..66b4c07f838 --- /dev/null +++ b/arch/arm/mach-ux500/include/mach/db5500-keypad.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * License terms: GNU General Public License, version 2 + * Author: Sundar Iyer for ST-Ericsson + */ + +#ifndef __DB5500_KEYPAD_H +#define __DB5500_KEYPAD_H + +#include + +/** + * struct db5500_keypad_platform_data - structure for platform specific data + * @keymap_data: matrix scan code table for keycodes + * @debounce_ms: platform specific debounce time + * @no_autorepeat: flag for auto repetition + */ +struct db5500_keypad_platform_data { + const struct matrix_keymap_data *keymap_data; + u8 debounce_ms; + bool no_autorepeat; +}; + +#endif diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index cdc385b2cf7..f6f82bbfdff 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -151,6 +151,16 @@ config KEYBOARD_BFIN To compile this driver as a module, choose M here: the module will be called bf54x-keys. +config KEYBOARD_DB5500 + tristate "DB5500 keyboard" + depends on UX500_SOC_DB5500 + help + Say Y here to enable the on-chip keypad controller on the + ST-Ericsson U5500 platform. + + To compile this driver as a module, choose M here: the + module will be called db5500_keypad. + config KEYBOARD_LKKBD tristate "DECstation/VAXstation LK201/LK401 keyboard" select SERIO diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index df7061f1291..1b664b4166c 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o obj-$(CONFIG_KEYBOARD_DAVINCI) += davinci_keyscan.o +obj-$(CONFIG_KEYBOARD_DB5500) += db5500_keypad.o obj-$(CONFIG_KEYBOARD_EP93XX) += ep93xx_keypad.o obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o obj-$(CONFIG_KEYBOARD_GPIO_POLLED) += gpio_keys_polled.o diff --git a/drivers/input/keyboard/db5500_keypad.c b/drivers/input/keyboard/db5500_keypad.c new file mode 100644 index 00000000000..a6d0fe08961 --- /dev/null +++ b/drivers/input/keyboard/db5500_keypad.c @@ -0,0 +1,422 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * License terms: GNU General Public License, version 2 + * Author: Sundar Iyer for ST-Ericsson + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define KEYPAD_CTR 0x0 +#define KEYPAD_IRQ_CLEAR 0x4 +#define KEYPAD_INT_ENABLE 0x8 +#define KEYPAD_INT_STATUS 0xC +#define KEYPAD_ARRAY_01 0x18 + +#define KEYPAD_NUM_ARRAY_REGS 5 + +#define KEYPAD_CTR_WRITE_IRQ_ENABLE (1 << 10) +#define KEYPAD_CTR_SCAN_ENABLE (1 << 7) + +#define KEYPAD_ARRAY_CHANGEBIT (1 << 15) + +#define KEYPAD_DEBOUNCE_PERIOD_MIN 5 /* ms */ +#define KEYPAD_DEBOUNCE_PERIOD_MAX 80 /* ms */ + +#define KEYPAD_GND_ROW 8 + +#define KEYPAD_MAX_ROWS 9 +#define KEYPAD_MAX_COLS 8 +#define KEYPAD_ROW_SHIFT 4 +#define KEYPAD_KEYMAP_SIZE \ + (KEYPAD_MAX_ROWS * KEYPAD_MAX_COLS) + +/** + * struct db5500_keypad - data structure used by keypad driver + * @irq: irq number + * @base: keypad registers base address + * @input: pointer to input device object + * @board: keypad platform data + * @keymap: matrix scan code table for keycodes + * @clk: clock structure pointer + * @previous_set: previous set of registers + */ +struct db5500_keypad { + int irq; + void __iomem *base; + struct input_dev *input; + const struct db5500_keypad_platform_data *board; + unsigned short keymap[KEYPAD_KEYMAP_SIZE]; + struct clk *clk; + u8 previous_set[KEYPAD_MAX_ROWS]; +}; + +/* + * By default all column reads are 1111 1111b. Any press will pull the column + * down, leading to a 0 in any of these locations. We invert these values so + * that a 1 means means "column pressed". + * + * If curr changes from the previous from 0 to 1, we report it as a key press. + * If curr changes from the previous from 1 to 0, we report it as a key + * release. + */ +static void db5500_keypad_report(struct db5500_keypad *keypad, int row, + u8 curr, u8 previous) +{ + struct input_dev *input = keypad->input; + u8 changed = curr ^ previous; + + while (changed) { + int col = __ffs(changed); + bool press = curr & BIT(col); + int code = MATRIX_SCAN_CODE(row, col, KEYPAD_ROW_SHIFT); + + input_event(input, EV_MSC, MSC_SCAN, code); + input_report_key(input, keypad->keymap[code], press); + input_sync(input); + + changed &= ~BIT(col); + } +} + +static irqreturn_t db5500_keypad_irq(int irq, void *dev_id) +{ + struct db5500_keypad *keypad = dev_id; + u8 current_set[ARRAY_SIZE(keypad->previous_set)]; + int tries = 100; + bool changebit; + u32 data_reg; + u8 allrows; + u8 common; + int i; + + writel(0x1, keypad->base + KEYPAD_IRQ_CLEAR); + +again: + if (!tries--) { + dev_warn(&keypad->input->dev, "values failed to stabilize\n"); + return IRQ_HANDLED; + } + + changebit = readl(keypad->base + KEYPAD_ARRAY_01) + & KEYPAD_ARRAY_CHANGEBIT; + + for (i = 0; i < KEYPAD_NUM_ARRAY_REGS; i++) { + data_reg = readl(keypad->base + KEYPAD_ARRAY_01 + 4 * i); + + /* If the change bit changed, we need to reread the data */ + if (changebit != !!(data_reg & KEYPAD_ARRAY_CHANGEBIT)) + goto again; + + current_set[2 * i] = ~(data_reg & 0xff); + + /* Last array reg has only one valid set of columns */ + if (i != KEYPAD_NUM_ARRAY_REGS - 1) + current_set[2 * i + 1] = ~((data_reg & 0xff0000) >> 16); + } + + allrows = current_set[KEYPAD_GND_ROW]; + + /* + * Sometimes during a GND row release, an incorrect report is received + * where the ARRAY8 all rows setting does not match the other ARRAY* + * rows. Ignore this report; the correct one has been observed to + * follow it. + */ + common = 0xff; + for (i = 0; i < KEYPAD_GND_ROW; i++) + common &= current_set[i]; + + if ((allrows & common) != common) + return IRQ_HANDLED; + + for (i = 0; i < ARRAY_SIZE(current_set); i++) { + /* + * If there is an allrows press (GND row), we need to ignore + * the allrows values from the reset of the ARRAYs. + */ + if (i < KEYPAD_GND_ROW && allrows) + current_set[i] &= ~allrows; + + if (keypad->previous_set[i] == current_set[i]) + continue; + + db5500_keypad_report(keypad, i, current_set[i], + keypad->previous_set[i]); + } + + /* update the reference set of array registers */ + memcpy(keypad->previous_set, current_set, sizeof(keypad->previous_set)); + + return IRQ_HANDLED; +} + +static int __devinit db5500_keypad_chip_init(struct db5500_keypad *keypad) +{ + int debounce = keypad->board->debounce_ms; + int debounce_hits = 0; + int timeout = 100; + u32 val; + + if (debounce < KEYPAD_DEBOUNCE_PERIOD_MIN) + debounce = KEYPAD_DEBOUNCE_PERIOD_MIN; + + if (debounce > KEYPAD_DEBOUNCE_PERIOD_MAX) { + debounce_hits = DIV_ROUND_UP(debounce, + KEYPAD_DEBOUNCE_PERIOD_MAX) - 1; + debounce = KEYPAD_DEBOUNCE_PERIOD_MAX; + } + + /* Convert the milliseconds to the bit mask */ + debounce = DIV_ROUND_UP(debounce, KEYPAD_DEBOUNCE_PERIOD_MIN) - 1; + + writel(KEYPAD_CTR_SCAN_ENABLE + | ((debounce_hits & 0x7) << 4) + | debounce, keypad->base + KEYPAD_CTR); + + do { + val = readl(keypad->base + KEYPAD_CTR); + } while ((!(val & KEYPAD_CTR_WRITE_IRQ_ENABLE)) && --timeout); + + if (!timeout) + return -EINVAL; + + writel(0x1, keypad->base + KEYPAD_INT_ENABLE); + + return 0; +} + +static int __devinit db5500_keypad_probe(struct platform_device *pdev) +{ + const struct db5500_keypad_platform_data *plat; + struct db5500_keypad *keypad; + struct resource *res; + struct input_dev *input; + void __iomem *base; + struct clk *clk; + int ret; + int irq; + + plat = pdev->dev.platform_data; + if (!plat) { + dev_err(&pdev->dev, "invalid keypad platform data\n"); + ret = -EINVAL; + goto out_ret; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "failed to get keypad irq\n"); + ret = -EINVAL; + goto out_ret; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(&pdev->dev, "missing platform resources\n"); + ret = -EINVAL; + goto out_ret; + } + + res = request_mem_region(res->start, resource_size(res), pdev->name); + if (!res) { + dev_err(&pdev->dev, "failed to request I/O memory\n"); + ret = -EBUSY; + goto out_ret; + } + + base = ioremap(res->start, resource_size(res)); + if (!base) { + dev_err(&pdev->dev, "failed to remap I/O memory\n"); + ret = -ENXIO; + goto out_freerequest_memregions; + } + + clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "failed to clk_get\n"); + + /* + * FIXME: error out here once DB5500 clock framework is in + * place, and remove all the !IS_ERR(clk) checks. + */ + } + + keypad = kzalloc(sizeof(struct db5500_keypad), GFP_KERNEL); + if (!keypad) { + dev_err(&pdev->dev, "failed to allocate keypad memory\n"); + ret = -ENOMEM; + goto out_freeclk; + } + + input = input_allocate_device(); + if (!input) { + dev_err(&pdev->dev, "failed to input_allocate_device\n"); + ret = -ENOMEM; + goto out_freekeypad; + } + + input->id.bustype = BUS_HOST; + input->name = "db5500-keypad"; + input->dev.parent = &pdev->dev; + + input->keycode = keypad->keymap; + input->keycodesize = sizeof(keypad->keymap[0]); + input->keycodemax = ARRAY_SIZE(keypad->keymap); + + input_set_capability(input, EV_MSC, MSC_SCAN); + + __set_bit(EV_KEY, input->evbit); + if (!plat->no_autorepeat) + __set_bit(EV_REP, input->evbit); + + matrix_keypad_build_keymap(plat->keymap_data, KEYPAD_ROW_SHIFT, + input->keycode, input->keybit); + + ret = input_register_device(input); + if (ret) { + dev_err(&pdev->dev, + "unable to register input device: %d\n", ret); + goto out_freeinput; + } + + keypad->irq = irq; + keypad->board = plat; + keypad->input = input; + keypad->base = base; + keypad->clk = clk; + + /* allocations are sane, we begin HW initialization */ + if (!IS_ERR(keypad->clk)) + clk_enable(keypad->clk); + + ret = db5500_keypad_chip_init(keypad); + if (ret < 0) { + dev_err(&pdev->dev, "unable to init keypad hardware\n"); + goto out_unregisterinput; + } + + ret = request_threaded_irq(keypad->irq, NULL, db5500_keypad_irq, + IRQF_ONESHOT, "db5500-keypad", keypad); + if (ret) { + dev_err(&pdev->dev, "allocate irq %d failed\n", keypad->irq); + goto out_unregisterinput; + } + + device_init_wakeup(&pdev->dev, true); + + platform_set_drvdata(pdev, keypad); + + return 0; + +out_unregisterinput: + input_unregister_device(input); + input = NULL; + if (!IS_ERR(keypad->clk)) + clk_disable(keypad->clk); +out_freeinput: + input_free_device(input); +out_freekeypad: + kfree(keypad); +out_freeclk: + if (!IS_ERR(clk)) + clk_put(clk); + iounmap(base); +out_freerequest_memregions: + release_mem_region(res->start, resource_size(res)); +out_ret: + return ret; +} + +static int __devexit db5500_keypad_remove(struct platform_device *pdev) +{ + struct db5500_keypad *keypad = platform_get_drvdata(pdev); + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + free_irq(keypad->irq, keypad); + input_unregister_device(keypad->input); + + if (!IS_ERR(keypad->clk)) { + clk_disable(keypad->clk); + clk_put(keypad->clk); + } + + iounmap(keypad->base); + release_mem_region(res->start, resource_size(res)); + kfree(keypad); + + return 0; +} + +#ifdef CONFIG_PM +static int db5500_keypad_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct db5500_keypad *keypad = platform_get_drvdata(pdev); + int irq = platform_get_irq(pdev, 0); + + if (device_may_wakeup(dev)) + enable_irq_wake(irq); + else + /* disable IRQ here */ + + return 0; +} + +static int db5500_keypad_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct db5500_keypad *keypad = platform_get_drvdata(pdev); + int irq = platform_get_irq(pdev, 0); + + if (device_may_wakeup(dev)) + disable_irq_wake(irq); + else + /* enable IRQ here */ + + return 0; +} + +static const struct dev_pm_ops db5500_keypad_dev_pm_ops = { + .suspend = db5500_keypad_suspend, + .resume = db5500_keypad_resume, +}; +#endif + +static struct platform_driver db5500_keypad_driver = { + .driver = { + .name = "db5500-keypad", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &db5500_keypad_dev_pm_ops, +#endif + }, + .probe = db5500_keypad_probe, + .remove = __devexit_p(db5500_keypad_remove), +}; + +static int __init db5500_keypad_init(void) +{ + return platform_driver_register(&db5500_keypad_driver); +} +module_init(db5500_keypad_init); + +static void __exit db5500_keypad_exit(void) +{ + platform_driver_unregister(&db5500_keypad_driver); +} +module_exit(db5500_keypad_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Sundar Iyer "); +MODULE_DESCRIPTION("DB5500 Keypad Driver"); +MODULE_ALIAS("platform:db5500-keypad"); -- cgit v1.2.3 From 3a670aa260cc2f7990a1260bf35af3bbd2ae30d5 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Tue, 11 Oct 2011 09:00:40 +0200 Subject: keyboard: add tc35893 driver This patch is based on the following work: Keypad tc35893: Fix the Keypad Driver interrupt enable/disable ST-Ericsson ID: ER274519 Author: Ankur Vaish Keypad: Disable EV_REP to disable reporting repeated key events ST-Ericsson Id:ER 264416 Author: Jayeeta Banerjee Add support for TC35893 Keypad Controller ST-Ericsson ID: AP261147 Author: Jayeeta Banerjee Signed-off-by: Mian Yousaf Kaukab Conflicts: Documentation/DocBook/Makefile --- Documentation/DocBook/tc_keypad.tmpl | 113 +++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 Documentation/DocBook/tc_keypad.tmpl diff --git a/Documentation/DocBook/tc_keypad.tmpl b/Documentation/DocBook/tc_keypad.tmpl new file mode 100644 index 00000000000..8c69345df44 --- /dev/null +++ b/Documentation/DocBook/tc_keypad.tmpl @@ -0,0 +1,113 @@ + + + + + + TC35893 Keypad + + + + Jayeeta + Banerjee + +
+ jayeeta.banerjee@stericsson.com +
+
+
+
+ + + 2010 + ST-Ericsson + + + + + Linux standard functions + + + + + + + + This documentation is free software; you can redistribute + it and/or modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later + version. + + + + This program is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + MA 02111-1307 USA + + + + For more details see the file COPYING in the source + distribution of Linux. + + +
+ + + + + Introduction + + This documentation describes the API provided by the keypad driver for TC35893 controller + + + + + Known Bugs And Assumptions + + + + None + + + None. + + + + + + + + + Structures + + This chapter contains the autogenerated documentation of the structures which are + used in the keypad driver. + +!Iarch/arm/mach-ux500/include/mach/tc35893-keypad.h + + + + Public Functions Provided + + Not Applicable. + + + + + Internal Functions Provided + + This chapter contains the autogenerated documentation of the internal functions. + +!Idrivers/input/keyboard/tc35893-keypad.c + + +
-- cgit v1.2.3 From 47eb14c98637b79d986c9694974fdf8acd24a455 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Tue, 11 Oct 2011 09:11:35 +0200 Subject: input: abx500: big merge from GLK 2.6.35 Signed-off-by: Philippe Langlais --- drivers/input/misc/ab8500-ponkey.c | 123 +++++++++++++++++++------------------ 1 file changed, 62 insertions(+), 61 deletions(-) diff --git a/drivers/input/misc/ab8500-ponkey.c b/drivers/input/misc/ab8500-ponkey.c index 350fd0c385d..f19eb400df0 100644 --- a/drivers/input/misc/ab8500-ponkey.c +++ b/drivers/input/misc/ab8500-ponkey.c @@ -6,7 +6,6 @@ * * AB8500 Power-On Key handler */ - #include #include #include @@ -16,13 +15,13 @@ #include /** - * struct ab8500_ponkey - ab8500 ponkey information + * struct ab8500_ponkey_info - ab8500 ponkey information * @input_dev: pointer to input device * @ab8500: ab8500 parent * @irq_dbf: irq number for falling transition * @irq_dbr: irq number for rising transition */ -struct ab8500_ponkey { +struct ab8500_ponkey_info { struct input_dev *idev; struct ab8500 *ab8500; int irq_dbf; @@ -32,14 +31,14 @@ struct ab8500_ponkey { /* AB8500 gives us an interrupt when ONKEY is held */ static irqreturn_t ab8500_ponkey_handler(int irq, void *data) { - struct ab8500_ponkey *ponkey = data; + struct ab8500_ponkey_info *info = data; - if (irq == ponkey->irq_dbf) - input_report_key(ponkey->idev, KEY_POWER, true); - else if (irq == ponkey->irq_dbr) - input_report_key(ponkey->idev, KEY_POWER, false); + if (irq == info->irq_dbf) + input_report_key(info->idev, KEY_POWER, true); + else if (irq == info->irq_dbr) + input_report_key(info->idev, KEY_POWER, false); - input_sync(ponkey->idev); + input_sync(info->idev); return IRQ_HANDLED; } @@ -47,87 +46,89 @@ static irqreturn_t ab8500_ponkey_handler(int irq, void *data) static int __devinit ab8500_ponkey_probe(struct platform_device *pdev) { struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent); - struct ab8500_ponkey *ponkey; - struct input_dev *input; - int irq_dbf, irq_dbr; - int error; + struct ab8500_ponkey_info *info; + int irq_dbf, irq_dbr, ret; irq_dbf = platform_get_irq_byname(pdev, "ONKEY_DBF"); if (irq_dbf < 0) { - dev_err(&pdev->dev, "No IRQ for ONKEY_DBF, error=%d\n", irq_dbf); + dev_err(&pdev->dev, "No IRQ for ONKEY_DBF,error=%d\n", irq_dbf); return irq_dbf; } irq_dbr = platform_get_irq_byname(pdev, "ONKEY_DBR"); if (irq_dbr < 0) { - dev_err(&pdev->dev, "No IRQ for ONKEY_DBR, error=%d\n", irq_dbr); + dev_err(&pdev->dev, "No IRQ for ONKEY_DBR,error=%d\n", irq_dbr); return irq_dbr; } - ponkey = kzalloc(sizeof(struct ab8500_ponkey), GFP_KERNEL); - input = input_allocate_device(); - if (!ponkey || !input) { - error = -ENOMEM; - goto err_free_mem; - } + info = kzalloc(sizeof(struct ab8500_ponkey_info), GFP_KERNEL); + if (!info) + return -ENOMEM; - ponkey->idev = input; - ponkey->ab8500 = ab8500; - ponkey->irq_dbf = irq_dbf; - ponkey->irq_dbr = irq_dbr; + info->ab8500 = ab8500; + info->irq_dbf = irq_dbf; + info->irq_dbr = irq_dbr; - input->name = "AB8500 POn(PowerOn) Key"; - input->dev.parent = &pdev->dev; + info->idev = input_allocate_device(); + if (!info->idev) { + dev_err(ab8500->dev, "Failed to allocate input dev\n"); + ret = -ENOMEM; + goto out; + } - input_set_capability(input, EV_KEY, KEY_POWER); + info->idev->name = "AB8500 POn(PowerOn) Key"; + info->idev->dev.parent = &pdev->dev; + info->idev->evbit[0] = BIT_MASK(EV_KEY); + info->idev->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER); + + ret = input_register_device(info->idev); + if (ret) { + dev_err(ab8500->dev, "Can't register input device: %d\n", ret); + goto out_unfreedevice; + } - error = request_any_context_irq(ponkey->irq_dbf, ab8500_ponkey_handler, - 0, "ab8500-ponkey-dbf", ponkey); - if (error < 0) { + ret = request_threaded_irq(info->irq_dbf, NULL, ab8500_ponkey_handler, + IRQF_NO_SUSPEND, "ab8500-ponkey-dbf", + info); + if (ret < 0) { dev_err(ab8500->dev, "Failed to request dbf IRQ#%d: %d\n", - ponkey->irq_dbf, error); - goto err_free_mem; + info->irq_dbf, ret); + goto out_unregisterdevice; } - error = request_any_context_irq(ponkey->irq_dbr, ab8500_ponkey_handler, - 0, "ab8500-ponkey-dbr", ponkey); - if (error < 0) { + ret = request_threaded_irq(info->irq_dbr, NULL, ab8500_ponkey_handler, + IRQF_NO_SUSPEND, "ab8500-ponkey-dbr", + info); + if (ret < 0) { dev_err(ab8500->dev, "Failed to request dbr IRQ#%d: %d\n", - ponkey->irq_dbr, error); - goto err_free_dbf_irq; + info->irq_dbr, ret); + goto out_irq_dbf; } - error = input_register_device(ponkey->idev); - if (error) { - dev_err(ab8500->dev, "Can't register input device: %d\n", error); - goto err_free_dbr_irq; - } + platform_set_drvdata(pdev, info); - platform_set_drvdata(pdev, ponkey); return 0; -err_free_dbr_irq: - free_irq(ponkey->irq_dbr, ponkey); -err_free_dbf_irq: - free_irq(ponkey->irq_dbf, ponkey); -err_free_mem: - input_free_device(input); - kfree(ponkey); - - return error; +out_irq_dbf: + free_irq(info->irq_dbf, info); +out_unregisterdevice: + input_unregister_device(info->idev); + info->idev = NULL; +out_unfreedevice: + input_free_device(info->idev); +out: + kfree(info); + return ret; } static int __devexit ab8500_ponkey_remove(struct platform_device *pdev) { - struct ab8500_ponkey *ponkey = platform_get_drvdata(pdev); - - free_irq(ponkey->irq_dbf, ponkey); - free_irq(ponkey->irq_dbr, ponkey); - input_unregister_device(ponkey->idev); - kfree(ponkey); - - platform_set_drvdata(pdev, NULL); + struct ab8500_ponkey_info *info = platform_get_drvdata(pdev); + free_irq(info->irq_dbf, info); + free_irq(info->irq_dbr, info); + input_unregister_device(info->idev); + kfree(info); return 0; } -- cgit v1.2.3 From d3712c9a6cc47b2d9c72e3317be86635259fcdb2 Mon Sep 17 00:00:00 2001 From: Naveen Kumar Gaddipati Date: Tue, 5 Oct 2010 10:31:54 +0530 Subject: input: Dynamic enable or disable of SKE keypad events Added support for dynamic enable or disable of SKE keypad events by using sysfs commands. Enable SKE keypad event: echo 1 >/sys/devices/platform/nmk-ske-keypad/enable Disable SKE keypad event: echo 0 >/sys/devices/platform/nmk-ske-keypad/enable ST-Ericsson Id: WP 273474 Change-Id:Ia7c98d67609129ee661eec2b3f036bc24402b166 Signed-off-by: Naveen Kumar Gaddipati Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/6202 Reviewed-by: Linus WALLEIJ --- drivers/input/keyboard/nomadik-ske-keypad.c | 198 ++++++++++++++++++++-------- 1 file changed, 140 insertions(+), 58 deletions(-) diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c index e35566aa102..174ee3ef0d8 100644 --- a/drivers/input/keyboard/nomadik-ske-keypad.c +++ b/drivers/input/keyboard/nomadik-ske-keypad.c @@ -57,6 +57,7 @@ * @board: keypad platform device * @keymap: matrix scan code table for keycodes * @clk: clock structure pointer + * @enable: flag to enable the driver event */ struct ske_keypad { int irq; @@ -66,6 +67,7 @@ struct ske_keypad { unsigned short keymap[SKE_KPD_KEYMAP_SIZE]; struct clk *clk; spinlock_t ske_keypad_lock; + bool enable; }; static void ske_keypad_set_bits(struct ske_keypad *keypad, u16 addr, @@ -83,6 +85,54 @@ static void ske_keypad_set_bits(struct ske_keypad *keypad, u16 addr, spin_unlock(&keypad->ske_keypad_lock); } +static ssize_t ske_show_attr_enable(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct ske_keypad *keypad = platform_get_drvdata(pdev); + return sprintf(buf, "%d\n", keypad->enable); +} + +static ssize_t ske_store_attr_enable(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct ske_keypad *keypad = platform_get_drvdata(pdev); + unsigned long val; + + if (strict_strtoul(buf, 0, &val)) + return -EINVAL; + + if ((val != 0) && (val != 1)) + return -EINVAL; + + if (keypad->enable != val) { + keypad->enable = val ? true : false; + if (!keypad->enable) { + disable_irq(keypad->irq); + ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0); + clk_disable(keypad->clk); + } else { + clk_enable(keypad->clk); + enable_irq(keypad->irq); + ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA); + } + } + return count; +} + +static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO, + ske_show_attr_enable, ske_store_attr_enable); + +static struct attribute *ske_keypad_attrs[] = { + &dev_attr_enable.attr, + NULL, +}; + +static struct attribute_group ske_attr_group = { + .attrs = ske_keypad_attrs, +}; + /* * ske_keypad_chip_init: init keypad controller configuration * @@ -91,7 +141,7 @@ static void ske_keypad_set_bits(struct ske_keypad *keypad, u16 addr, static int __devinit ske_keypad_chip_init(struct ske_keypad *keypad) { u32 value; - int timeout = 50; + int timeout = keypad->board->debounce_ms; /* check SKE_RIS to be 0 */ while ((readl(keypad->reg_base + SKE_RIS) != 0x00000000) && timeout--) @@ -200,12 +250,14 @@ static irqreturn_t ske_keypad_irq(int irq, void *dev_id) static int __devinit ske_keypad_probe(struct platform_device *pdev) { - const struct ske_keypad_platform_data *plat = pdev->dev.platform_data; struct ske_keypad *keypad; + struct resource *res = NULL; struct input_dev *input; - struct resource *res; + struct clk *clk; + void __iomem *reg_base; + int ret = 0; int irq; - int error; + struct ske_keypad_platform_data *plat = pdev->dev.platform_data; if (!plat) { dev_err(&pdev->dev, "invalid keypad platform data\n"); @@ -219,42 +271,43 @@ static int __devinit ske_keypad_probe(struct platform_device *pdev) } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { + if (res == NULL) { dev_err(&pdev->dev, "missing platform resources\n"); - return -EINVAL; + return -ENXIO; } - keypad = kzalloc(sizeof(struct ske_keypad), GFP_KERNEL); - input = input_allocate_device(); - if (!keypad || !input) { - dev_err(&pdev->dev, "failed to allocate keypad memory\n"); - error = -ENOMEM; - goto err_free_mem; + res = request_mem_region(res->start, resource_size(res), pdev->name); + if (!res) { + dev_err(&pdev->dev, "failed to request I/O memory\n"); + return -EBUSY; } - keypad->irq = irq; - keypad->board = plat; - keypad->input = input; - spin_lock_init(&keypad->ske_keypad_lock); + reg_base = ioremap(res->start, resource_size(res)); + if (!reg_base) { + dev_err(&pdev->dev, "failed to remap I/O memory\n"); + ret = -ENXIO; + goto out_freerequest_memregions; + } - if (!request_mem_region(res->start, resource_size(res), pdev->name)) { - dev_err(&pdev->dev, "failed to request I/O memory\n"); - error = -EBUSY; - goto err_free_mem; + clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "failed to clk_get\n"); + ret = PTR_ERR(clk); + goto out_freeioremap; } - keypad->reg_base = ioremap(res->start, resource_size(res)); - if (!keypad->reg_base) { - dev_err(&pdev->dev, "failed to remap I/O memory\n"); - error = -ENXIO; - goto err_free_mem_region; + /* resources are sane; we begin allocation */ + keypad = kzalloc(sizeof(struct ske_keypad), GFP_KERNEL); + if (!keypad) { + dev_err(&pdev->dev, "failed to allocate keypad memory\n"); + goto out_freeclk; } - keypad->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(keypad->clk)) { - dev_err(&pdev->dev, "failed to get clk\n"); - error = PTR_ERR(keypad->clk); - goto err_iounmap; + input = input_allocate_device(); + if (!input) { + dev_err(&pdev->dev, "failed to input_allocate_device\n"); + ret = -ENOMEM; + goto out_freekeypad; } input->id.bustype = BUS_HOST; @@ -266,60 +319,88 @@ static int __devinit ske_keypad_probe(struct platform_device *pdev) input->keycodemax = ARRAY_SIZE(keypad->keymap); input_set_capability(input, EV_MSC, MSC_SCAN); + input_set_drvdata(input, keypad); __set_bit(EV_KEY, input->evbit); if (!plat->no_autorepeat) __set_bit(EV_REP, input->evbit); matrix_keypad_build_keymap(plat->keymap_data, SKE_KEYPAD_ROW_SHIFT, - input->keycode, input->keybit); + input->keycode, input->keybit); + ret = input_register_device(input); + if (ret) { + dev_err(&pdev->dev, + "unable to register input device: %d\n", ret); + goto out_freeinput; + } + + keypad->board = plat; + keypad->irq = irq; + keypad->board = plat; + keypad->input = input; + keypad->reg_base = reg_base; + keypad->clk = clk; + keypad->enable = true; + + /* allocations are sane, we begin HW initialization */ clk_enable(keypad->clk); - /* go through board initialization helpers */ - if (keypad->board->init) - keypad->board->init(); + if (!keypad->board->init) { + dev_err(&pdev->dev, "NULL board initialization helper\n"); + ret = -EINVAL; + goto out_unregisterinput; + } + + if (keypad->board->init() < 0) { + dev_err(&pdev->dev, "unable to set keypad board config\n"); + ret = -EINVAL; + goto out_unregisterinput; + } - error = ske_keypad_chip_init(keypad); - if (error) { + ret = ske_keypad_chip_init(keypad); + if (ret < 0) { dev_err(&pdev->dev, "unable to init keypad hardware\n"); - goto err_clk_disable; + goto out_unregisterinput; } - error = request_threaded_irq(keypad->irq, NULL, ske_keypad_irq, - IRQF_ONESHOT, "ske-keypad", keypad); - if (error) { + ret = request_threaded_irq(keypad->irq, NULL, ske_keypad_irq, + IRQF_ONESHOT, "ske-keypad", keypad); + if (ret) { dev_err(&pdev->dev, "allocate irq %d failed\n", keypad->irq); - goto err_clk_disable; + goto out_unregisterinput; } - error = input_register_device(input); - if (error) { - dev_err(&pdev->dev, - "unable to register input device: %d\n", error); - goto err_free_irq; + /* sysfs implementation for dynamic enable/disable the input event */ + ret = sysfs_create_group(&pdev->dev.kobj, &ske_attr_group); + if (ret) { + dev_err(&pdev->dev, "failed to create sysfs entries\n"); + goto out_free_irq; } - if (plat->wakeup_enable) - device_init_wakeup(&pdev->dev, true); + device_init_wakeup(&pdev->dev, true); platform_set_drvdata(pdev, keypad); return 0; -err_free_irq: +out_free_irq: free_irq(keypad->irq, keypad); -err_clk_disable: +out_unregisterinput: + input_unregister_device(input); + input = NULL; clk_disable(keypad->clk); - clk_put(keypad->clk); -err_iounmap: - iounmap(keypad->reg_base); -err_free_mem_region: - release_mem_region(res->start, resource_size(res)); -err_free_mem: +out_freeinput: input_free_device(input); +out_freekeypad: kfree(keypad); - return error; +out_freeclk: + clk_put(keypad->clk); +out_freeioremap: + iounmap(reg_base); +out_freerequest_memregions: + release_mem_region(res->start, resource_size(res)); + return ret; } static int __devexit ske_keypad_remove(struct platform_device *pdev) @@ -331,6 +412,7 @@ static int __devexit ske_keypad_remove(struct platform_device *pdev) input_unregister_device(keypad->input); + sysfs_remove_group(&pdev->dev.kobj, &ske_attr_group); clk_disable(keypad->clk); clk_put(keypad->clk); @@ -393,7 +475,7 @@ static struct platform_driver ske_keypad_driver = { static int __init ske_keypad_init(void) { - return platform_driver_probe(&ske_keypad_driver, ske_keypad_probe); + return platform_driver_register(&ske_keypad_driver); } module_init(ske_keypad_init); -- cgit v1.2.3 From f171f959ca56da5f944a07784095a47dd183413f Mon Sep 17 00:00:00 2001 From: Bade Appala Naidu Date: Wed, 6 Oct 2010 15:10:57 +0530 Subject: input: Dynamic enable or disable of STMPE1601 keypad events Added support for dynamic enable or disable of STMPE1601 keypad events by using sysfs commands. Enable STMPE1601 keypad event: echo 1 > /sys/devices/platform/nmk-i2c.0/i2c-0/0-0040/stmpe-keypad.1/enable Disable STMPE1601 keypad event: echo 0 > /sys/devices/platform/nmk-i2c.0/i2c-0/0-0040/stmpe-keypad.1/enable ST-Ericsson Id: WP 273474 Change-Id:I12281c9a3ae3b275b3e1efaeecfb7817c93290b9 Signed-off-by: Bade Appala Naidu Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/6266 Reviewed-by: Linus WALLEIJ --- drivers/input/keyboard/stmpe-keypad.c | 53 +++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/drivers/input/keyboard/stmpe-keypad.c b/drivers/input/keyboard/stmpe-keypad.c index 9397cf9c625..be8f177767b 100644 --- a/drivers/input/keyboard/stmpe-keypad.c +++ b/drivers/input/keyboard/stmpe-keypad.c @@ -108,10 +108,52 @@ struct stmpe_keypad { unsigned int rows; unsigned int cols; + bool enable; unsigned short keymap[STMPE_KEYPAD_KEYMAP_SIZE]; }; +static ssize_t stmpe_show_attr_enable(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct stmpe_keypad *keypad = platform_get_drvdata(pdev); + return sprintf(buf, "%d\n", keypad->enable); +} + +static ssize_t stmpe_store_attr_enable(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct stmpe_keypad *keypad = platform_get_drvdata(pdev); + struct stmpe *stmpe = keypad->stmpe; + unsigned long val; + + if (strict_strtoul(buf, 0, &val)) + return -EINVAL; + + if (keypad->enable != val) { + keypad->enable = val; + if (!val) + stmpe_disable(stmpe, STMPE_BLOCK_KEYPAD); + else + stmpe_enable(stmpe, STMPE_BLOCK_KEYPAD); + } + return count; +} + +static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO, + stmpe_show_attr_enable, stmpe_store_attr_enable); + +static struct attribute *stmpe_keypad_attrs[] = { + &dev_attr_enable.attr, + NULL, +}; + +static struct attribute_group stmpe_attr_group = { + .attrs = stmpe_keypad_attrs, +}; + static int stmpe_keypad_read_data(struct stmpe_keypad *keypad, u8 *data) { const struct stmpe_keypad_variant *variant = keypad->variant; @@ -331,11 +373,21 @@ static int __devinit stmpe_keypad_probe(struct platform_device *pdev) dev_err(&pdev->dev, "unable to get irq: %d\n", ret); goto out_unregisterinput; } + /* sysfs implementation for dynamic enable/disable the input event */ + ret = sysfs_create_group(&pdev->dev.kobj, &stmpe_attr_group); + if (ret) { + dev_err(&pdev->dev, "failed to create sysfs entries\n"); + goto out_free_irq; + } + + keypad->enable = true; platform_set_drvdata(pdev, keypad); return 0; +out_free_irq: + free_irq(irq, keypad); out_unregisterinput: input_unregister_device(input); input = NULL; @@ -354,6 +406,7 @@ static int __devexit stmpe_keypad_remove(struct platform_device *pdev) stmpe_disable(stmpe, STMPE_BLOCK_KEYPAD); + sysfs_remove_group(&pdev->dev.kobj, &stmpe_attr_group); free_irq(irq, keypad); input_unregister_device(keypad->input); platform_set_drvdata(pdev, NULL); -- cgit v1.2.3 From 716ccfc7b541225bd97e260fd2b9ead1fba18c6d Mon Sep 17 00:00:00 2001 From: Sundar Iyer Date: Fri, 22 Oct 2010 11:43:17 +0530 Subject: input/keypad-stmpe: add suspend/resume support ST-Ericcson ID: TASK_ER170552 Change-Id: Id814bec06a86e8a3215ef662d5f01a7ee929d26a Signed-off-by: Sundar Iyer Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/7526 Reviewed-by: Jonas ABERG --- drivers/input/keyboard/stmpe-keypad.c | 38 +++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/drivers/input/keyboard/stmpe-keypad.c b/drivers/input/keyboard/stmpe-keypad.c index be8f177767b..0fa2ec00b57 100644 --- a/drivers/input/keyboard/stmpe-keypad.c +++ b/drivers/input/keyboard/stmpe-keypad.c @@ -373,14 +373,14 @@ static int __devinit stmpe_keypad_probe(struct platform_device *pdev) dev_err(&pdev->dev, "unable to get irq: %d\n", ret); goto out_unregisterinput; } - /* sysfs implementation for dynamic enable/disable the input event */ + + /* sysfs implementation for dynamic enable/disable the input event */ ret = sysfs_create_group(&pdev->dev.kobj, &stmpe_attr_group); if (ret) { dev_err(&pdev->dev, "failed to create sysfs entries\n"); goto out_free_irq; } - keypad->enable = true; platform_set_drvdata(pdev, keypad); @@ -415,9 +415,43 @@ static int __devexit stmpe_keypad_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM +static int stmpe_keypad_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct stmpe_keypad *keypad = platform_get_drvdata(pdev); + struct stmpe *stmpe = keypad->stmpe; + + if (!device_may_wakeup(stmpe->dev)) + stmpe_disable(stmpe, STMPE_BLOCK_KEYPAD); + + return 0; +} + +static int stmpe_keypad_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct stmpe_keypad *keypad = platform_get_drvdata(pdev); + struct stmpe *stmpe = keypad->stmpe; + + if (!device_may_wakeup(stmpe->dev)) + stmpe_enable(stmpe, STMPE_BLOCK_KEYPAD); + + return 0; +} + +static const struct dev_pm_ops stmpe_keypad_dev_pm_ops = { + .suspend = stmpe_keypad_suspend, + .resume = stmpe_keypad_resume, +}; +#endif + static struct platform_driver stmpe_keypad_driver = { .driver.name = "stmpe-keypad", .driver.owner = THIS_MODULE, +#if CONFIG_PM + .driver.pm = &stmpe_keypad_dev_pm_ops, +#endif .probe = stmpe_keypad_probe, .remove = __devexit_p(stmpe_keypad_remove), }; -- cgit v1.2.3 From e94fc7e50ff0c12d2a72754a664a91b8ee95b344 Mon Sep 17 00:00:00 2001 From: Naveen Kumar Gaddipati Date: Wed, 17 Nov 2010 13:31:56 +0530 Subject: input: Multi key press support is added for SKE keypad Added the multi key press support for SKE keypad by modifying the irq function for handling the two different keys on the same column and also pressing the two different keys of different columns on the same ASR register. ST-Ericsson Id: ER 279197 Signed-off-by: Naveen Kumar Gaddipati Change-Id: Ia83a8f6ba1f5fee47f97b9476bb59cf2460b8e14 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/13192 Reviewed-by: QATOOLS Reviewed-by: Srinidhi KASAGAR --- drivers/input/keyboard/Makefile | 2 +- drivers/input/keyboard/nomadik-ske-keypad.c | 65 ++++++++++++++++++----------- 2 files changed, 41 insertions(+), 26 deletions(-) diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 1b664b4166c..90a01405e51 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -32,7 +32,7 @@ obj-$(CONFIG_KEYBOARD_MAX7359) += max7359_keypad.o obj-$(CONFIG_KEYBOARD_MCS) += mcs_touchkey.o obj-$(CONFIG_KEYBOARD_MPR121) += mpr121_touchkey.o obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o -obj-$(CONFIG_KEYBOARD_NOMADIK) += nomadik-ske-keypad.o +obj-$(CONFIG_KEYBOARD_NOMADIK_SKE) += nomadik-ske-keypad.o obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o obj-$(CONFIG_KEYBOARD_OMAP4) += omap4-keypad.o obj-$(CONFIG_KEYBOARD_OPENCORES) += opencores-kbd.o diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c index 174ee3ef0d8..a93a9ffc24a 100644 --- a/drivers/input/keyboard/nomadik-ske-keypad.c +++ b/drivers/input/keyboard/nomadik-ske-keypad.c @@ -184,12 +184,37 @@ static int __devinit ske_keypad_chip_init(struct ske_keypad *keypad) return 0; } -static void ske_keypad_read_data(struct ske_keypad *keypad) +static void ske_keypad_report(struct ske_keypad *keypad, u8 status, int col) { + int row = 0, code, pos; struct input_dev *input = keypad->input; - u16 status; - int col = 0, row = 0, code; - int ske_asr, ske_ris, key_pressed, i; + u32 ske_ris; + int key_pressed; + int num_of_rows; + + /* find out the row */ + num_of_rows = hweight8(status); + do { + pos = __ffs(status); + row = pos; + status &= ~(1 << pos); + + code = MATRIX_SCAN_CODE(row, col, SKE_KEYPAD_ROW_SHIFT); + ske_ris = readl(keypad->reg_base + SKE_RIS); + key_pressed = ske_ris & SKE_KPRISA; + + input_event(input, EV_MSC, MSC_SCAN, code); + input_report_key(input, keypad->keymap[code], key_pressed); + input_sync(input); + num_of_rows--; + } while (num_of_rows); +} + +static void ske_keypad_read_data(struct ske_keypad *keypad) +{ + u8 status; + int col = 0; + int ske_asr, i; /* * Read the auto scan registers @@ -203,25 +228,17 @@ static void ske_keypad_read_data(struct ske_keypad *keypad) if (!ske_asr) continue; - /* now that ASRx is zero, find out the column x and row y*/ - if (ske_asr & 0xff) { + /* now that ASRx is zero, find out the coloumn x and row y */ + status = ske_asr & 0xff; + if (status) { col = i * 2; - status = ske_asr & 0xff; - } else { + ske_keypad_report(keypad, status, col); + } + status = (ske_asr & 0xff00) >> 8; + if (status) { col = (i * 2) + 1; - status = (ske_asr & 0xff00) >> 8; + ske_keypad_report(keypad, status, col); } - - /* find out the row */ - row = __ffs(status); - - code = MATRIX_SCAN_CODE(row, col, SKE_KEYPAD_ROW_SHIFT); - ske_ris = readl(keypad->reg_base + SKE_RIS); - key_pressed = ske_ris & SKE_KPRISA; - - input_event(input, EV_MSC, MSC_SCAN, code); - input_report_key(input, keypad->keymap[code], key_pressed); - input_sync(input); } } @@ -235,12 +252,10 @@ static irqreturn_t ske_keypad_irq(int irq, void *dev_id) ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA); while ((readl(keypad->reg_base + SKE_CR) & SKE_KPASON) && --retries) - msleep(5); + cpu_relax(); - if (retries) { - /* SKEx registers are stable and can be read */ - ske_keypad_read_data(keypad); - } + /* SKEx registers are stable and can be read */ + ske_keypad_read_data(keypad); /* enable auto scan interrupts */ ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA); -- cgit v1.2.3 From cfeb00d23bf3d605b67c858261728cfab1bd1e78 Mon Sep 17 00:00:00 2001 From: Naveen Kumar Gaddipati Date: Mon, 31 Jan 2011 19:01:22 +0530 Subject: input:ske: Suspend and resume support for ske keypad Suspend and resume support for ske keypad by using disable or enable of keypad. ST-Ericsson Id: ER 320090 Change-Id: I5ae732be0e2f6074048fa6351a9f7b9ee33f380f Signed-off-by: Naveen Kumar Gaddipati Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/13977 Reviewed-by: QATOOLS Reviewed-by: Srinidhi KASAGAR --- drivers/input/keyboard/nomadik-ske-keypad.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c index a93a9ffc24a..91dd7be0638 100644 --- a/drivers/input/keyboard/nomadik-ske-keypad.c +++ b/drivers/input/keyboard/nomadik-ske-keypad.c @@ -393,7 +393,8 @@ static int __devinit ske_keypad_probe(struct platform_device *pdev) goto out_free_irq; } - device_init_wakeup(&pdev->dev, true); + if (plat->wakeup_enable) + device_init_wakeup(&pdev->dev, true); platform_set_drvdata(pdev, keypad); @@ -450,8 +451,11 @@ static int ske_keypad_suspend(struct device *dev) if (device_may_wakeup(dev)) enable_irq_wake(irq); - else + else { + disable_irq(keypad->irq); ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0); + clk_disable(keypad->clk); + } return 0; } @@ -464,8 +468,11 @@ static int ske_keypad_resume(struct device *dev) if (device_may_wakeup(dev)) disable_irq_wake(irq); - else - ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA); + else { + clk_enable(keypad->clk); + enable_irq(keypad->irq); + ske_keypad_chip_init(keypad); + } return 0; } -- cgit v1.2.3 From f5f6c17ad24e3e09bf014d6b79bf64845e292a5c Mon Sep 17 00:00:00 2001 From: Naveen Kumar Gaddipati Date: Fri, 4 Feb 2011 14:46:08 +0530 Subject: input:ske: Remove multiple interrupts for each keypress Remove the multiple interrupts for each key press of the ske keypad. ST-Ericsson Id: ER 323157 Change-Id: Ib0d9827895c72c47e83006a1498401849c1c16db Signed-off-by: Naveen Kumar Gaddipati Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/14599 Reviewed-by: Srinidhi KASAGAR --- drivers/input/keyboard/nomadik-ske-keypad.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c index 91dd7be0638..585aa683162 100644 --- a/drivers/input/keyboard/nomadik-ske-keypad.c +++ b/drivers/input/keyboard/nomadik-ske-keypad.c @@ -245,18 +245,21 @@ static void ske_keypad_read_data(struct ske_keypad *keypad) static irqreturn_t ske_keypad_irq(int irq, void *dev_id) { struct ske_keypad *keypad = dev_id; - int retries = 20; /* disable auto scan interrupt; mask the interrupt generated */ ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0); ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA); - while ((readl(keypad->reg_base + SKE_CR) & SKE_KPASON) && --retries) + while (readl(keypad->reg_base + SKE_CR) & SKE_KPASON) cpu_relax(); /* SKEx registers are stable and can be read */ ske_keypad_read_data(keypad); + /* wait one debounce ms */ + while (readl(keypad->reg_base + SKE_RIS)) + cpu_relax(); + /* enable auto scan interrupts */ ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA); @@ -350,7 +353,6 @@ static int __devinit ske_keypad_probe(struct platform_device *pdev) goto out_freeinput; } - keypad->board = plat; keypad->irq = irq; keypad->board = plat; keypad->input = input; -- cgit v1.2.3 From 689f0ae4c0941e2e56d049324e4e5532f23d0c28 Mon Sep 17 00:00:00 2001 From: Naveen Kumar Gaddipati Date: Tue, 8 Feb 2011 18:56:54 +0530 Subject: input:ske:Decrease the CPU load during continue key press Decrease the CPU load during continuous key press in SKE keypad driver. ST-Ericsson Id: ER 323157 Signed-off-by: Naveen Kumar Gaddipati Change-Id: Ic450157de6cb21d3d36a51ffb54e5d3467163335 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/14705 Reviewed-by: QATOOLS Reviewed-by: Rikard OLSSON Reviewed-by: Srinidhi KASAGAR --- drivers/input/keyboard/nomadik-ske-keypad.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c index 585aa683162..fdd6796078e 100644 --- a/drivers/input/keyboard/nomadik-ske-keypad.c +++ b/drivers/input/keyboard/nomadik-ske-keypad.c @@ -48,6 +48,7 @@ #define SKE_ASR3 0x2C #define SKE_NUM_ASRX_REGISTERS (4) +#define KEY_PRESSED_DELAY 10 /** * struct ske_keypad - data structure used by keypad driver @@ -245,20 +246,21 @@ static void ske_keypad_read_data(struct ske_keypad *keypad) static irqreturn_t ske_keypad_irq(int irq, void *dev_id) { struct ske_keypad *keypad = dev_id; + int timeout = keypad->board->debounce_ms; /* disable auto scan interrupt; mask the interrupt generated */ ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0); ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA); - while (readl(keypad->reg_base + SKE_CR) & SKE_KPASON) + while ((readl(keypad->reg_base + SKE_CR) & SKE_KPASON) && --timeout) cpu_relax(); /* SKEx registers are stable and can be read */ ske_keypad_read_data(keypad); - /* wait one debounce ms */ - while (readl(keypad->reg_base + SKE_RIS)) - cpu_relax(); + /* wait until raw interrupt is clear */ + while ((readl(keypad->reg_base + SKE_RIS)) && --timeout) + msleep(KEY_PRESSED_DELAY); /* enable auto scan interrupts */ ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA); -- cgit v1.2.3 From 247a553ed9c5cb1f93605ed759679d8a66c7ac4c Mon Sep 17 00:00:00 2001 From: Naveen Kumar Gaddipati Date: Wed, 9 Feb 2011 18:58:57 +0530 Subject: input:ske:Add the regulator support in ske driver Add the regulator calls for the ske keypad controller driver. ST-Ericsson Id: AP 323445 Change-Id: I950ce9c9415bc283e2d9c3174f7178b2f5555248 Signed-off-by: Naveen Kumar Gaddipati Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/14806 Reviewed-by: Srinidhi KASAGAR --- drivers/input/keyboard/nomadik-ske-keypad.c | 140 +++++++++++++++++----------- 1 file changed, 84 insertions(+), 56 deletions(-) diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c index fdd6796078e..c7c59adb101 100644 --- a/drivers/input/keyboard/nomadik-ske-keypad.c +++ b/drivers/input/keyboard/nomadik-ske-keypad.c @@ -19,6 +19,7 @@ #include #include #include +#include #include @@ -59,6 +60,7 @@ * @keymap: matrix scan code table for keycodes * @clk: clock structure pointer * @enable: flag to enable the driver event + * @regulator: pointer to the regulator used for ske kyepad */ struct ske_keypad { int irq; @@ -69,6 +71,7 @@ struct ske_keypad { struct clk *clk; spinlock_t ske_keypad_lock; bool enable; + struct regulator *regulator; }; static void ske_keypad_set_bits(struct ske_keypad *keypad, u16 addr, @@ -86,54 +89,6 @@ static void ske_keypad_set_bits(struct ske_keypad *keypad, u16 addr, spin_unlock(&keypad->ske_keypad_lock); } -static ssize_t ske_show_attr_enable(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct platform_device *pdev = to_platform_device(dev); - struct ske_keypad *keypad = platform_get_drvdata(pdev); - return sprintf(buf, "%d\n", keypad->enable); -} - -static ssize_t ske_store_attr_enable(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - struct platform_device *pdev = to_platform_device(dev); - struct ske_keypad *keypad = platform_get_drvdata(pdev); - unsigned long val; - - if (strict_strtoul(buf, 0, &val)) - return -EINVAL; - - if ((val != 0) && (val != 1)) - return -EINVAL; - - if (keypad->enable != val) { - keypad->enable = val ? true : false; - if (!keypad->enable) { - disable_irq(keypad->irq); - ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0); - clk_disable(keypad->clk); - } else { - clk_enable(keypad->clk); - enable_irq(keypad->irq); - ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA); - } - } - return count; -} - -static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO, - ske_show_attr_enable, ske_store_attr_enable); - -static struct attribute *ske_keypad_attrs[] = { - &dev_attr_enable.attr, - NULL, -}; - -static struct attribute_group ske_attr_group = { - .attrs = ske_keypad_attrs, -}; - /* * ske_keypad_chip_init: init keypad controller configuration * @@ -185,6 +140,61 @@ static int __devinit ske_keypad_chip_init(struct ske_keypad *keypad) return 0; } +static void ske_enable(struct ske_keypad *keypad) +{ + if (keypad->enable) { + regulator_enable(keypad->regulator); + clk_enable(keypad->clk); + enable_irq(keypad->irq); + ske_keypad_chip_init(keypad); + } else { + disable_irq(keypad->irq); + ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0); + clk_disable(keypad->clk); + regulator_disable(keypad->regulator); + } +} + +static ssize_t ske_show_attr_enable(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct ske_keypad *keypad = platform_get_drvdata(pdev); + return sprintf(buf, "%d\n", keypad->enable); +} + +static ssize_t ske_store_attr_enable(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct ske_keypad *keypad = platform_get_drvdata(pdev); + unsigned long val; + + if (strict_strtoul(buf, 0, &val)) + return -EINVAL; + + if ((val != 0) && (val != 1)) + return -EINVAL; + + if (keypad->enable != val) { + keypad->enable = val ? true : false; + ske_enable(keypad); + } + return count; +} + +static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO, + ske_show_attr_enable, ske_store_attr_enable); + +static struct attribute *ske_keypad_attrs[] = { + &dev_attr_enable.attr, + NULL, +}; + +static struct attribute_group ske_attr_group = { + .attrs = ske_keypad_attrs, +}; + static void ske_keypad_report(struct ske_keypad *keypad, u8 status, int col) { int row = 0, code, pos; @@ -330,6 +340,19 @@ static int __devinit ske_keypad_probe(struct platform_device *pdev) goto out_freekeypad; } + keypad->regulator = regulator_get(&pdev->dev, "v-ape"); + if (IS_ERR(keypad->regulator)) { + dev_err(&pdev->dev, "regulator_get failed\n"); + keypad->regulator = NULL; + goto out_regulator_get; + } else { + ret = regulator_enable(keypad->regulator); + if (ret < 0) { + dev_err(&pdev->dev, "regulator_enable failed\n"); + goto out_regulator_enable; + } + } + input->id.bustype = BUS_HOST; input->name = "ux500-ske-keypad"; input->dev.parent = &pdev->dev; @@ -411,6 +434,10 @@ out_unregisterinput: input = NULL; clk_disable(keypad->clk); out_freeinput: + regulator_disable(keypad->regulator); +out_regulator_enable: + regulator_put(keypad->regulator); +out_regulator_get: input_free_device(input); out_freekeypad: kfree(keypad); @@ -439,6 +466,9 @@ static int __devexit ske_keypad_remove(struct platform_device *pdev) if (keypad->board->exit) keypad->board->exit(); + regulator_disable(keypad->regulator); + regulator_put(keypad->regulator); + iounmap(keypad->reg_base); release_mem_region(res->start, resource_size(res)); kfree(keypad); @@ -455,10 +485,9 @@ static int ske_keypad_suspend(struct device *dev) if (device_may_wakeup(dev)) enable_irq_wake(irq); - else { - disable_irq(keypad->irq); - ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0); - clk_disable(keypad->clk); + else if (keypad->enable) { + keypad->enable = false; + ske_enable(keypad); } return 0; @@ -472,10 +501,9 @@ static int ske_keypad_resume(struct device *dev) if (device_may_wakeup(dev)) disable_irq_wake(irq); - else { - clk_enable(keypad->clk); - enable_irq(keypad->irq); - ske_keypad_chip_init(keypad); + else if (!keypad->enable) { + keypad->enable = true; + ske_enable(keypad); } return 0; -- cgit v1.2.3 From 5abc63a964b270f5df060fcc4dbdd7a68147b566 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Wed, 23 Mar 2011 15:18:00 +0100 Subject: input:ske: Fix rename CONFIG_KEYBOARD_NOMADIK to CONFIG_KEYBOARD_NOMADIK_SKE --- drivers/input/keyboard/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index f6f82bbfdff..1cfd730f212 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -391,7 +391,7 @@ config KEYBOARD_NEWTON To compile this driver as a module, choose M here: the module will be called newtonkbd. -config KEYBOARD_NOMADIK +config KEYBOARD_NOMADIK_SKE tristate "ST-Ericsson Nomadik SKE keyboard" depends on PLAT_NOMADIK help -- cgit v1.2.3 From 83bc81c1d63cfd9dea5894fc45028e8115a335ea Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Tue, 18 Oct 2011 22:00:05 +0200 Subject: input: Fix warnings Signed-off-by: Philippe Langlais --- drivers/input/keyboard/stmpe-keypad.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/keyboard/stmpe-keypad.c b/drivers/input/keyboard/stmpe-keypad.c index 0fa2ec00b57..fb3427d5ff7 100644 --- a/drivers/input/keyboard/stmpe-keypad.c +++ b/drivers/input/keyboard/stmpe-keypad.c @@ -449,7 +449,7 @@ static const struct dev_pm_ops stmpe_keypad_dev_pm_ops = { static struct platform_driver stmpe_keypad_driver = { .driver.name = "stmpe-keypad", .driver.owner = THIS_MODULE, -#if CONFIG_PM +#ifdef CONFIG_PM .driver.pm = &stmpe_keypad_dev_pm_ops, #endif .probe = stmpe_keypad_probe, -- cgit v1.2.3 From 794be3982d62200c096652172b3d331d4bb3bbdd Mon Sep 17 00:00:00 2001 From: Robert Marklund Date: Wed, 27 Apr 2011 16:53:05 +0200 Subject: DocBook: Update tc_keypad doc for new files Signed-off-by: Robert Marklund --- Documentation/DocBook/tc_keypad.tmpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/DocBook/tc_keypad.tmpl b/Documentation/DocBook/tc_keypad.tmpl index 8c69345df44..3f2630d9d6c 100644 --- a/Documentation/DocBook/tc_keypad.tmpl +++ b/Documentation/DocBook/tc_keypad.tmpl @@ -92,7 +92,7 @@ This chapter contains the autogenerated documentation of the structures which are used in the keypad driver. -!Iarch/arm/mach-ux500/include/mach/tc35893-keypad.h +!Iinclude/linux/mfd/tc3589x.h @@ -107,7 +107,7 @@ This chapter contains the autogenerated documentation of the internal functions. -!Idrivers/input/keyboard/tc35893-keypad.c +!Idrivers/input/keyboard//tc3589x-keypad.c -- cgit v1.2.3 From 23481cb78c8dbb961831b735b22d7fd844d7c914 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Tue, 11 Oct 2011 14:38:51 +0200 Subject: u5500: Fix compilation errors after rmi4 ts driver moved in staging area Signed-off-by: Philippe Langlais Conflicts: arch/arm/mach-ux500/board-u5500.c --- drivers/staging/ste_rmi4/synaptics_i2c_rmi4.h | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.h b/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.h index 384436ef806..973abc97374 100644 --- a/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.h +++ b/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.h @@ -42,6 +42,7 @@ struct synaptics_rmi4_platform_data { int irq_type; bool x_flip; bool y_flip; + bool regulator_en; }; #endif -- cgit v1.2.3 From 917398ffa877c0d610fd633ff515ce89ce73f1a3 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Tue, 18 Oct 2011 22:01:13 +0200 Subject: Input: Add AB8500 AV Acc. Detection as platform drv Adds AV Accessory detection driver functionality as a platform driver. Driver allows detection of basic AV-Accessory connected to 3.5mm AV-Connector. Supported accessories include headphone, headset, video and carkit. Driver controls the audio-in/video-out switch based on the detected accessory type and notifies about button presses from basic headsets (No ECI Support). Accessory status and button presses are reported as standard input events, if AB8500 ASoC Machine driver is configured. For accessory properties reporting, ALSA JACK definitions (SND_JACK_*) are used to report the features supported by the attached accessory. Button presses are reported with KEY_MEDIA keycode. Depends on: http://gerrit.lud.stericsson.com/gerrit/16910 for GPIO framework update. ST-Ericsson ID: ER 275366 Signed-off-by: Rahul Venkatram --- arch/arm/mach-ux500/include/mach/ab8500-accdet.h | 93 ++ drivers/input/misc/Kconfig | 7 + drivers/input/misc/Makefile | 1 + drivers/input/misc/ab8500-accdet.c | 1327 ++++++++++++++++++++++ 4 files changed, 1428 insertions(+) create mode 100644 arch/arm/mach-ux500/include/mach/ab8500-accdet.h create mode 100644 drivers/input/misc/ab8500-accdet.c diff --git a/arch/arm/mach-ux500/include/mach/ab8500-accdet.h b/arch/arm/mach-ux500/include/mach/ab8500-accdet.h new file mode 100644 index 00000000000..5742d7b797a --- /dev/null +++ b/arch/arm/mach-ux500/include/mach/ab8500-accdet.h @@ -0,0 +1,93 @@ +/* + * Copyright ST-Ericsson 2011. + * + * Author: Jarmo K. Kuronen for ST Ericsson. + * Licensed under GPLv2. + */ + +#ifndef _AB8500_ACCDET_H +#define _AB8500_ACCDET_H + +/* +* Debounce times for AccDet1 input +* @0x880 [2:0] +*/ +#define ACCDET1_DB_0ms 0x00 +#define ACCDET1_DB_10ms 0x01 +#define ACCDET1_DB_20ms 0x02 +#define ACCDET1_DB_30ms 0x03 +#define ACCDET1_DB_40ms 0x04 +#define ACCDET1_DB_50ms 0x05 +#define ACCDET1_DB_60ms 0x06 +#define ACCDET1_DB_70ms 0x07 + +/* +* Voltage threshold for AccDet1 input +* @0x880 [6:3] +*/ +#define ACCDET1_TH_1100mV 0x40 +#define ACCDET1_TH_1200mV 0x48 +#define ACCDET1_TH_1300mV 0x50 +#define ACCDET1_TH_1400mV 0x58 +#define ACCDET1_TH_1500mV 0x60 +#define ACCDET1_TH_1600mV 0x68 +#define ACCDET1_TH_1700mV 0x70 +#define ACCDET1_TH_1800mV 0x78 + +/* +* Voltage threshold for AccDet21 input +* @0x881 [3:0] +*/ +#define ACCDET21_TH_300mV 0x00 +#define ACCDET21_TH_400mV 0x01 +#define ACCDET21_TH_500mV 0x02 +#define ACCDET21_TH_600mV 0x03 +#define ACCDET21_TH_700mV 0x04 +#define ACCDET21_TH_800mV 0x05 +#define ACCDET21_TH_900mV 0x06 +#define ACCDET21_TH_1000mV 0x07 +#define ACCDET21_TH_1100mV 0x08 +#define ACCDET21_TH_1200mV 0x09 +#define ACCDET21_TH_1300mV 0x0a +#define ACCDET21_TH_1400mV 0x0b +#define ACCDET21_TH_1500mV 0x0c +#define ACCDET21_TH_1600mV 0x0d +#define ACCDET21_TH_1700mV 0x0e +#define ACCDET21_TH_1800mV 0x0f + +/* +* Voltage threshold for AccDet22 input +* @0x881 [7:4] +*/ +#define ACCDET22_TH_300mV 0x00 +#define ACCDET22_TH_400mV 0x10 +#define ACCDET22_TH_500mV 0x20 +#define ACCDET22_TH_600mV 0x30 +#define ACCDET22_TH_700mV 0x40 +#define ACCDET22_TH_800mV 0x50 +#define ACCDET22_TH_900mV 0x60 +#define ACCDET22_TH_1000mV 0x70 +#define ACCDET22_TH_1100mV 0x80 +#define ACCDET22_TH_1200mV 0x90 +#define ACCDET22_TH_1300mV 0xa0 +#define ACCDET22_TH_1400mV 0xb0 +#define ACCDET22_TH_1500mV 0xc0 +#define ACCDET22_TH_1600mV 0xd0 +#define ACCDET22_TH_1700mV 0xe0 +#define ACCDET22_TH_1800mV 0xf0 + +/** + * struct ab8500_accdet_platform_data - AV Accessory detection specific + * platform data + * @btn_keycode Keycode to be sent when accessory button is pressed. + * @accdet1_dbth Debounce time + voltage threshold for accdet 1 input. + * @accdet2122_th Voltage thresholds for accdet21 and accdet22 inputs. + */ +struct ab8500_accdet_platform_data { + int btn_keycode; + u8 accdet1_dbth; + u8 accdet2122_th; + unsigned int video_ctrl_gpio; +}; + +#endif /* _AB8500_ACCDET_H */ diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 7b46781c30c..cef9fd21554 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -22,6 +22,13 @@ config INPUT_88PM860X_ONKEY To compile this driver as a module, choose M here: the module will be called 88pm860x_onkey. +config INPUT_AB8500_ACCDET + bool "AB8500 AV Accessory detection" + depends on AB8500_CORE && AB8500_GPADC && AB8500_GPIO + help + Say Y here to enable AV accessory detection features for ST-Ericsson's + AB8500 Mix-Sig PMIC. + config INPUT_AB8500_PONKEY tristate "AB8500 Pon (PowerOn) Key" depends on AB8500_CORE diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 46671a875b9..aca455b733d 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -5,6 +5,7 @@ # Each configuration option enables a list of files. obj-$(CONFIG_INPUT_88PM860X_ONKEY) += 88pm860x_onkey.o +obj-$(CONFIG_INPUT_AB8500_ACCDET) += ab8500-accdet.o obj-$(CONFIG_INPUT_AB8500_PONKEY) += ab8500-ponkey.o obj-$(CONFIG_INPUT_AD714X) += ad714x.o obj-$(CONFIG_INPUT_AD714X_I2C) += ad714x-i2c.o diff --git a/drivers/input/misc/ab8500-accdet.c b/drivers/input/misc/ab8500-accdet.c new file mode 100644 index 00000000000..2433e7bc999 --- /dev/null +++ b/drivers/input/misc/ab8500-accdet.c @@ -0,0 +1,1327 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Jarmo K. Kuronen + * for ST-Ericsson. + * + * License terms: GPL V2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include /* Needed by all modules */ +#include /* Needed for KERN_INFO */ +#include /* Needed for the macros */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_SND_SOC_UX500_AB8500 +#include +#else +#define ux500_ab8500_jack_report(i) +#endif + +#define MAX_DET_COUNT 10 +#define MAX_VOLT_DIFF 30 +#define MIN_MIC_POWER -100 + +/* Unique value used to identify Headset button input device */ +#define BTN_INPUT_UNIQUE_VALUE "AB8500HsBtn" +#define BTN_INPUT_DEV_NAME "AB8500 Hs Button" + +#define DEBOUNCE_PLUG_EVENT_MS 100 +#define DEBOUNCE_PLUG_RETEST_MS 25 +#define DEBOUNCE_UNPLUG_EVENT_MS 0 + +/* + * Register definition for accessory detection. + */ +#define AB8500_REGU_CTRL1_SPARE_REG 0x84 +#define AB8500_ACC_DET_DB1_REG 0x80 +#define AB8500_ACC_DET_DB2_REG 0x81 +#define AB8500_ACC_DET_CTRL_REG 0x82 +#define AB8500_IT_SOURCE5_REG 0x04 + +/* REGISTER: AB8500_ACC_DET_CTRL_REG */ +#define BITS_ACCDETCTRL2_ENA (0x20 | 0x10 | 0x08) +#define BITS_ACCDETCTRL1_ENA (0x02 | 0x01) + +/* REGISTER: AB8500_REGU_CTRL1_SPARE_REG */ +#define BIT_REGUCTRL1SPARE_VAMIC1_GROUND 0x01 + +/* REGISTER: AB8500_IT_SOURCE5_REG */ +#define BIT_ITSOURCE5_ACCDET1 0x04 + +/* After being loaded, how fast the first check is to be made */ +#define INIT_DELAY_MS 3000 + +/* Voltage limits (mV) for various types of AV Accessories */ +#define ACCESSORY_DET_VOL_DONTCARE -1 +#define ACCESSORY_HEADPHONE_DET_VOL_MIN 0 +#define ACCESSORY_HEADPHONE_DET_VOL_MAX 40 +#define ACCESSORY_CVIDEO_DET_VOL_MIN 41 +#define ACCESSORY_CVIDEO_DET_VOL_MAX 105 +#define ACCESSORY_CARKIT_DET_VOL_MIN 1100 +#define ACCESSORY_CARKIT_DET_VOL_MAX 1300 +#define ACCESSORY_HEADSET_DET_VOL_MIN 0 +#define ACCESSORY_HEADSET_DET_VOL_MAX 200 +#define ACCESSORY_OPENCABLE_DET_VOL_MIN 1730 +#define ACCESSORY_OPENCABLE_DET_VOL_MAX 2150 + +/* Macros */ + +/* + * Conviniency macros to check jack characteristics. + */ +#define jack_supports_mic(type) \ + (type == JACK_TYPE_HEADSET || type == JACK_TYPE_CARKIT) +#define jack_supports_spkr(type) \ + ((type != JACK_TYPE_DISCONNECTED) && (type != JACK_TYPE_CONNECTED)) +#define jack_supports_buttons(type) \ + ((type == JACK_TYPE_HEADSET) ||\ + (type == JACK_TYPE_CARKIT) ||\ + (type == JACK_TYPE_OPENCABLE) ||\ + (type == JACK_TYPE_CONNECTED)) + + +/* Enumerations */ + +/** + * @JACK_TYPE_UNSPECIFIED Not known whether any accessories are connected. + * @JACK_TYPE_DISCONNECTED No accessories connected. + * @JACK_TYPE_CONNECTED Accessory is connected but functionality was unable to + * detect the actual type. In this mode, possible button events are reported. + * @JACK_TYPE_HEADPHONE Headphone type of accessory (spkrs only) connected + * @JACK_TYPE_HEADSET Headset type of accessory (mic+spkrs) connected + * @JACK_TYPE_CARKIT Carkit type of accessory connected + * @JACK_TYPE_OPENCABLE Open cable connected + * @JACK_TYPE_CVIDEO CVideo type of accessory connected. + */ +enum accessory_jack_type { + JACK_TYPE_UNSPECIFIED, + JACK_TYPE_DISCONNECTED, + JACK_TYPE_CONNECTED, + JACK_TYPE_HEADPHONE, + JACK_TYPE_HEADSET, + JACK_TYPE_CARKIT, + JACK_TYPE_OPENCABLE, + JACK_TYPE_CVIDEO +}; + +/** + * @BUTTON_UNK Button state not known + * @BUTTON_PRESSED Button "down" + * @BUTTON_RELEASED Button "up" + */ +enum accessory_button_state { + BUTTON_UNK, + BUTTON_PRESSED, + BUTTON_RELEASED +}; + +/** + * @PLUG_IRQ Interrupt gen. when accessory plugged in + * @UNPLUG_IRQ Interrupt gen. when accessory plugged out + * @BUTTON_PRESS_IRQ Interrupt gen. when accessory button pressed. + * @BUTTON_RELEASE_IRQ Interrupt gen. when accessory button released. + */ +enum accessory_irq { + PLUG_IRQ, + UNPLUG_IRQ, + BUTTON_PRESS_IRQ, + BUTTON_RELEASE_IRQ +}; + +/** + * Enumerates the op. modes of the avcontrol switch + * @AUDIO_IN Audio input is selected + * @VIDEO_OUT Video output is selected + */ +enum accessory_avcontrol_dir { + AUDIO_IN, + VIDEO_OUT, +}; + +/** + * @REGULATOR_VAUDIO v-audio regulator + * @REGULATOR_VAMIC1 v-amic1 regulator + * @REGULATOR_AVSWITCH Audio/Video select switch regulator + * @REGULATOR_ALL All regulators combined + */ +enum accessory_regulator { + REGULATOR_NONE = 0x0, + REGULATOR_VAUDIO = 0x1, + REGULATOR_VAMIC1 = 0x2, + REGULATOR_AVSWITCH = 0x4, + REGULATOR_ALL = 0xFF +}; + +/* Structures */ + +/** + * Describes an interrupt + * @irq interrupt identifier + * @name name of the irq in platform data + * @isr interrupt service routine + * @register are we currently registered to receive interrupts from this source. + */ +struct accessory_irq_descriptor { + enum accessory_irq irq; + const char *name; + irq_handler_t isr; + int registered; +}; + +/** + * Encapsulates info of single regulator. + * @id regulator identifier + * @name name of the regulator + * @enabled flag indicating whether regu is currently enabled. + * @handle regulator handle + */ +struct accessory_regu_descriptor { + enum accessory_regulator id; + const char *name; + int enabled; + struct regulator *handle; +}; + +/** + * Defines attributes for accessory detection operation. + * @typename type as string + * @type Type of accessory this task tests + * @req_det_count How many times this particular type of accessory + * needs to be detected in sequence in order to accept. Multidetection + * implemented to avoid false detections during plug-in. + * @meas_mv Should ACCDETECT2 input voltage be measured just before + * making the decision or can cached voltage be used instead. + * @minvol minimum voltage (mV) for decision + * @maxvol maximum voltage (mV) for decision + */ +struct accessory_detect_task { + const char *typename; + enum accessory_jack_type type; + int req_det_count; + int meas_mv; + int minvol; + int maxvol; +}; + +/** + * Device data, capsulates all relevant device data structures. + * + * @pdev pointer to platform device + * @pdata Platform data + * @gpadc interface for ADC data + * @irq_work_queue Work queue for deferred interrupt processing + * + * @detect_work work item to perform detection work + * @unplug_irq_work work item to process unplug event + * @init_work work item to process initialization work. + * + * @btn_input_dev button input device used to report btn presses + * @btn_state Current state of accessory button + * + * @jack_type type of currently connected accessory + * @reported_jack_type previously reported jack type. + * @jack_type_temp temporary storage for currently connected accessory + * + * @jack_det_count counter how many times in sequence the accessory + * type detection has produced same result. + * @total_jack_det_count after plug-in irq, how many times detection + * has totally been made in order to detect the accessory type + * + * @detect_jiffies Used to save timestamp when detection was made. Timestamp + * used to filter out spurious button presses that might occur during the + * plug-in procedure. + * + * @accdet1_th_set flag to indicate whether accdet1 threshold and debounce + * times are configured + * @accdet2_th_set flag to indicate whether accdet2 thresholds are configured + * @gpio35_dir_set flag to indicate whether GPIO35 (VIDEOCTRL) direction + * has been configured. + */ +struct ab8500_ad { + struct platform_device *pdev; + struct ab8500_accdet_platform_data *pdata; + struct ab8500_gpadc *gpadc; + struct workqueue_struct *irq_work_queue; + + struct delayed_work detect_work; + struct delayed_work unplug_irq_work; + struct delayed_work init_work; + + struct input_dev *btn_input_dev; + enum accessory_button_state btn_state; + + enum accessory_jack_type jack_type; + enum accessory_jack_type reported_jack_type; + enum accessory_jack_type jack_type_temp; + + int jack_det_count; + int total_jack_det_count; + + unsigned long detect_jiffies; + + int accdet1_th_set; + int accdet2_th_set; + int gpio35_dir_set; +}; + +/* Forward declarations */ + +static void config_accdetect(struct ab8500_ad *dd); + +static void release_irq(struct ab8500_ad *dd, enum accessory_irq irq_id); +static void claim_irq(struct ab8500_ad *dd, enum accessory_irq irq_id); + +static irqreturn_t unplug_irq_handler(int irq, void *_userdata); +static irqreturn_t plug_irq_handler(int irq, void *_userdata); +static irqreturn_t button_press_irq_handler(int irq, void *_userdata); +static irqreturn_t button_release_irq_handler(int irq, void *_userdata); + +static void unplug_irq_handler_work(struct work_struct *work); +static void detect_work(struct work_struct *work); +static void init_work(struct work_struct *work); + +static enum accessory_jack_type detect(struct ab8500_ad *dd, int *required_det); +static void set_av_switch(struct ab8500_ad *dd, + enum accessory_avcontrol_dir dir); + +/* Static data initialization */ + +static struct accessory_detect_task detect_ops[] = { + { + .type = JACK_TYPE_DISCONNECTED, + .typename = "DISCONNECTED", + .meas_mv = 1, + .req_det_count = 1, + .minvol = ACCESSORY_DET_VOL_DONTCARE, + .maxvol = ACCESSORY_DET_VOL_DONTCARE + }, + { + .type = JACK_TYPE_HEADPHONE, + .typename = "HEADPHONE", + .meas_mv = 1, + .req_det_count = 1, + .minvol = ACCESSORY_HEADPHONE_DET_VOL_MIN, + .maxvol = ACCESSORY_HEADPHONE_DET_VOL_MAX + }, + { + .type = JACK_TYPE_CVIDEO, + .typename = "CVIDEO", + .meas_mv = 0, + .req_det_count = 4, + .minvol = ACCESSORY_CVIDEO_DET_VOL_MIN, + .maxvol = ACCESSORY_CVIDEO_DET_VOL_MAX + }, + { + .type = JACK_TYPE_OPENCABLE, + .typename = "OPENCABLE", + .meas_mv = 0, + .req_det_count = 4, + .minvol = ACCESSORY_OPENCABLE_DET_VOL_MIN, + .maxvol = ACCESSORY_OPENCABLE_DET_VOL_MAX + }, + { + .type = JACK_TYPE_CARKIT, + .typename = "CARKIT", + .meas_mv = 1, + .req_det_count = 1, + .minvol = ACCESSORY_CARKIT_DET_VOL_MIN, + .maxvol = ACCESSORY_CARKIT_DET_VOL_MAX + }, + { + .type = JACK_TYPE_HEADSET, + .typename = "HEADSET", + .meas_mv = 0, + .req_det_count = 2, + .minvol = ACCESSORY_HEADSET_DET_VOL_MIN, + .maxvol = ACCESSORY_HEADSET_DET_VOL_MAX + }, + { + .type = JACK_TYPE_CONNECTED, + .typename = "CONNECTED", + .meas_mv = 0, + .req_det_count = 4, + .minvol = ACCESSORY_DET_VOL_DONTCARE, + .maxvol = ACCESSORY_DET_VOL_DONTCARE + } +}; + +static struct accessory_regu_descriptor regu_desc[3] = { + { + .id = REGULATOR_VAUDIO, + .name = "v-audio", + }, + { + .id = REGULATOR_VAMIC1, + .name = "v-amic1", + }, + { + .id = REGULATOR_AVSWITCH, + .name = "vcc-avswitch", + }, +}; + +static struct accessory_irq_descriptor irq_desc[] = { + { + .irq = PLUG_IRQ, + .name = "ACC_DETECT_1DB_F", + .isr = plug_irq_handler, + }, + { + .irq = UNPLUG_IRQ, + .name = "ACC_DETECT_1DB_R", + .isr = unplug_irq_handler, + }, + { + .irq = BUTTON_PRESS_IRQ, + .name = "ACC_DETECT_22DB_F", + .isr = button_press_irq_handler, + }, + { + .irq = BUTTON_RELEASE_IRQ, + .name = "ACC_DETECT_22DB_R", + .isr = button_release_irq_handler, + }, +}; + +/* + * textual represenation of the accessory type + */ +static const char *accessory_str(enum accessory_jack_type type) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(detect_ops); i++) + if (type == detect_ops[i].type) + return detect_ops[i].typename; + + return "UNKNOWN?"; +} + +/* + * enables regulator but only if it has not been enabled earlier. + */ +static void accessory_regulator_enable(enum accessory_regulator reg) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(regu_desc); i++) { + if (reg & regu_desc[i].id) { + if (!regu_desc[i].enabled) { + if (!regulator_enable(regu_desc[i].handle)) + regu_desc[i].enabled = 1; + } + } + } +} + +/* + * disables regulator but only if it has been previously enabled. + */ +static void accessory_regulator_disable(enum accessory_regulator reg) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(regu_desc); i++) { + if (reg & regu_desc[i].id) { + if (regu_desc[i].enabled) { + if (!regulator_disable(regu_desc[i].handle)) + regu_desc[i].enabled = 0; + } + } + } +} + +/* + * frees previously retrieved regulators. + */ +static void free_regulators(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(regu_desc); i++) { + if (regu_desc[i].handle) { + regulator_put(regu_desc[i].handle); + regu_desc[i].handle = NULL; + } + } +} + +/* + * gets required regulators. + */ +static int create_regulators(struct ab8500_ad *dd) +{ + int i; + int status = 0; + + for (i = 0; i < ARRAY_SIZE(regu_desc); i++) { + struct regulator *regu = + regulator_get(&dd->pdev->dev, regu_desc[i].name); + if (IS_ERR(regu)) { + status = PTR_ERR(regu); + dev_err(&dd->pdev->dev, + "%s: Failed to get supply '%s' (%d).\n", + __func__, regu_desc[i].name, status); + free_regulators(); + goto out; + } else { + regu_desc[i].handle = regu; + } + } + +out: + return status; +} + +/* + * configures accdet2 input on/off + */ +static void config_accdetect2_hw(struct ab8500_ad *dd, int enable) +{ + int ret = 0; + + if (!dd->accdet2_th_set) { + /* Configure accdetect21+22 thresholds */ + ret = abx500_set_register_interruptible(&dd->pdev->dev, + AB8500_ECI_AV_ACC, + AB8500_ACC_DET_DB2_REG, + dd->pdata->accdet2122_th); + if (ret < 0) { + dev_err(&dd->pdev->dev, + "%s: Failed to write reg (%d).\n", __func__, + ret); + goto out; + } else { + dd->accdet2_th_set = 1; + } + } + + /* Enable/Disable accdetect21 comparators + pullup */ + ret = abx500_mask_and_set_register_interruptible( + &dd->pdev->dev, + AB8500_ECI_AV_ACC, + AB8500_ACC_DET_CTRL_REG, + BITS_ACCDETCTRL2_ENA, + enable ? BITS_ACCDETCTRL2_ENA : 0); + + if (ret < 0) + dev_err(&dd->pdev->dev, "%s: Failed to update reg (%d).\n", + __func__, ret); + +out: + return; +} + +/* + * configures accdet1 input on/off + */ +void config_accdetect1_hw(struct ab8500_ad *dd, int enable) +{ + int ret; + + if (!dd->accdet1_th_set) { + ret = abx500_set_register_interruptible(&dd->pdev->dev, + AB8500_ECI_AV_ACC, + AB8500_ACC_DET_DB1_REG, + dd->pdata->accdet1_dbth); + if (ret < 0) + dev_err(&dd->pdev->dev, + "%s: Failed to write reg (%d).\n", __func__, + ret); + else + dd->accdet1_th_set = 1; + } + + /* enable accdetect1 comparator */ + ret = abx500_mask_and_set_register_interruptible( + &dd->pdev->dev, + AB8500_ECI_AV_ACC, + AB8500_ACC_DET_CTRL_REG, + BITS_ACCDETCTRL1_ENA, + enable ? BITS_ACCDETCTRL1_ENA : 0); + + if (ret < 0) + dev_err(&dd->pdev->dev, + "%s: Failed to update reg (%d).\n", __func__, ret); +} + +/* + * create input device for button press reporting + */ +static int create_btn_input_dev(struct ab8500_ad *dd) +{ + int err; + + dd->btn_input_dev = input_allocate_device(); + if (!dd->btn_input_dev) { + dev_err(&dd->pdev->dev, "%s: Failed to allocate input dev.\n", + __func__); + err = -ENOMEM; + goto out; + } + + input_set_capability(dd->btn_input_dev, + EV_KEY, + dd->pdata->btn_keycode); + + dd->btn_input_dev->name = BTN_INPUT_DEV_NAME; + dd->btn_input_dev->uniq = BTN_INPUT_UNIQUE_VALUE; + dd->btn_input_dev->dev.parent = &dd->pdev->dev; + + err = input_register_device(dd->btn_input_dev); + if (err) { + dev_err(&dd->pdev->dev, + "%s: register_input_device failed (%d).\n", __func__, + err); + input_free_device(dd->btn_input_dev); + dd->btn_input_dev = NULL; + goto out; + } +out: + return err; +} + +/* + * reports jack status + */ +void report_jack_status(struct ab8500_ad *dd) +{ + int value = 0; + + /* Never report possible open cable */ + if (dd->jack_type == JACK_TYPE_OPENCABLE) + goto out; + + /* Never report same state twice in a row */ + if (dd->jack_type == dd->reported_jack_type) + goto out; + dd->reported_jack_type = dd->jack_type; + + dev_info(&dd->pdev->dev, "Accessory: %s\n", + accessory_str(dd->jack_type)); + + if (dd->jack_type != JACK_TYPE_DISCONNECTED && + dd->jack_type != JACK_TYPE_UNSPECIFIED) + value |= SND_JACK_MECHANICAL; + if (jack_supports_mic(dd->jack_type)) + value |= SND_JACK_MICROPHONE; + if (jack_supports_spkr(dd->jack_type)) + value |= (SND_JACK_HEADPHONE | SND_JACK_LINEOUT); + if (dd->jack_type == JACK_TYPE_CVIDEO) { + value |= SND_JACK_VIDEOOUT; + set_av_switch(dd, VIDEO_OUT); + } + + ux500_ab8500_jack_report(value); + +out: return; +} + +/* + * returns the high level status whether some accessory is connected (1|0). + */ +static int detect_plugged_in(struct ab8500_ad *dd) +{ + u8 value = 0; + int status = abx500_get_register_interruptible( + &dd->pdev->dev, + AB8500_INTERRUPT, + AB8500_IT_SOURCE5_REG, + &value); + if (status < 0) { + dev_err(&dd->pdev->dev, "%s: reg read failed (%d).\n", + __func__, status); + return 0; + } + + return value & BIT_ITSOURCE5_ACCDET1 ? 0 : 1; +} + +/* + * mic_line_voltage_stable - measures a relative stable voltage from spec. input + */ +static int meas_voltage_stable(struct ab8500_ad *dd, u8 input) +{ + int iterations = 2; + int v1, v2, dv; + + v1 = ab8500_gpadc_convert(dd->gpadc, input); + do { + msleep(1); + --iterations; + v2 = ab8500_gpadc_convert(dd->gpadc, input); + dv = abs(v2 - v1); + v1 = v2; + } while (iterations > 0 && dv > MAX_VOLT_DIFF); + + return v1; +} + +/* + * worker routine to handle accessory unplug case + */ +static void unplug_irq_handler_work(struct work_struct *work) +{ + struct ab8500_ad *dd = container_of(work, + struct ab8500_ad, unplug_irq_work.work); + + dev_dbg(&dd->pdev->dev, "%s: Enter\n", __func__); + + dd->jack_type = dd->jack_type_temp = JACK_TYPE_DISCONNECTED; + dd->jack_det_count = dd->total_jack_det_count = 0; + dd->btn_state = BUTTON_UNK; + config_accdetect(dd); + + set_av_switch(dd, AUDIO_IN); + + accessory_regulator_disable(REGULATOR_ALL); + + report_jack_status(dd); +} + +/* + * interrupt service routine for accessory unplug. + */ +static irqreturn_t unplug_irq_handler(int irq, void *_userdata) +{ + struct ab8500_ad *dd = _userdata; + + dev_dbg(&dd->pdev->dev, "%s: Enter (irq=%d)\n", __func__, irq); + + queue_delayed_work(dd->irq_work_queue, &dd->unplug_irq_work, + msecs_to_jiffies(DEBOUNCE_UNPLUG_EVENT_MS)); + + return IRQ_HANDLED; +} + +/* + * interrupt service routine for accessory plug. + */ +static irqreturn_t plug_irq_handler(int irq, void *_userdata) +{ + struct ab8500_ad *dd = _userdata; + + dev_dbg(&dd->pdev->dev, "%s: Enter (irq=%d)\n", + __func__, irq); + + switch (dd->jack_type) { + case JACK_TYPE_DISCONNECTED: + case JACK_TYPE_UNSPECIFIED: + queue_delayed_work(dd->irq_work_queue, &dd->detect_work, + msecs_to_jiffies(DEBOUNCE_PLUG_EVENT_MS)); + break; + + default: + dev_err(&dd->pdev->dev, "%s: Unexpected plug IRQ\n", __func__); + break; + } + + return IRQ_HANDLED; +} + +/* + * worker routine to perform detection. + */ +static void detect_work(struct work_struct *work) +{ + int req_det_count = 1; + enum accessory_jack_type new_type; + struct ab8500_ad *dd = container_of(work, + struct ab8500_ad, detect_work.work); + + dev_dbg(&dd->pdev->dev, "%s: Enter\n", __func__); + + new_type = detect(dd, &req_det_count); + + dd->total_jack_det_count++; + if (dd->jack_type_temp == new_type) { + dd->jack_det_count++; + } else { + dd->jack_det_count = 1; + dd->jack_type_temp = new_type; + } + + if (dd->total_jack_det_count >= MAX_DET_COUNT) { + dev_err(&dd->pdev->dev, + "%s: MAX_DET_COUNT(=%d) reached. Bailing out.\n", + __func__, MAX_DET_COUNT); + queue_delayed_work(dd->irq_work_queue, &dd->unplug_irq_work, + msecs_to_jiffies(DEBOUNCE_UNPLUG_EVENT_MS)); + } else if (dd->jack_det_count >= req_det_count) { + dd->total_jack_det_count = dd->jack_det_count = 0; + dd->jack_type = new_type; + dd->detect_jiffies = jiffies; + report_jack_status(dd); + config_accdetect(dd); + } else { + queue_delayed_work(dd->irq_work_queue, + &dd->detect_work, + msecs_to_jiffies(DEBOUNCE_PLUG_RETEST_MS)); + } +} + +/* + * reports a button event (pressed, released). + */ +static void report_btn_event(struct ab8500_ad *dd, int down) +{ + input_report_key(dd->btn_input_dev, dd->pdata->btn_keycode, down); + input_sync(dd->btn_input_dev); + + dev_dbg(&dd->pdev->dev, "HS-BTN: %s\n", down ? "PRESSED" : "RELEASED"); +} + +/* + * interrupt service routine invoked when hs button is pressed down. + */ +static irqreturn_t button_press_irq_handler(int irq, void *_userdata) +{ + struct ab8500_ad *dd = _userdata; + + unsigned long accept_jiffies = dd->detect_jiffies + + msecs_to_jiffies(1000); + if (time_before(jiffies, accept_jiffies)) { + dev_dbg(&dd->pdev->dev, "%s: Skipped spurious btn press.\n", + __func__); + return IRQ_HANDLED; + } + + dev_dbg(&dd->pdev->dev, "%s: Enter (irq=%d)\n", __func__, irq); + + if (dd->jack_type == JACK_TYPE_OPENCABLE) { + /* Someting got connected to open cable -> detect.. */ + config_accdetect2_hw(dd, 0); + queue_delayed_work(dd->irq_work_queue, &dd->detect_work, + msecs_to_jiffies(DEBOUNCE_PLUG_EVENT_MS)); + return IRQ_HANDLED; + } + + if (dd->btn_state == BUTTON_PRESSED) + return IRQ_HANDLED; + + if (jack_supports_buttons(dd->jack_type)) { + dd->btn_state = BUTTON_PRESSED; + report_btn_event(dd, 1); + } else { + dd->btn_state = BUTTON_UNK; + } + + return IRQ_HANDLED; +} + +/* + * interrupts service routine invoked when hs button is released. + */ +static irqreturn_t button_release_irq_handler(int irq, void *_userdata) +{ + struct ab8500_ad *dd = _userdata; + + dev_dbg(&dd->pdev->dev, "%s: Enter (irq=%d)\n", __func__, irq); + + if (dd->jack_type == JACK_TYPE_OPENCABLE) + return IRQ_HANDLED; + + if (dd->btn_state != BUTTON_PRESSED) + return IRQ_HANDLED; + + if (jack_supports_buttons(dd->jack_type)) { + report_btn_event(dd, 0); + dd->btn_state = BUTTON_RELEASED; + } else { + dd->btn_state = BUTTON_UNK; + } + + return IRQ_HANDLED; +} + +/* + * configures HW so that it is possible to make decision whether + * accessory is connected or not. + */ +static void config_hw_test_plug_connected(struct ab8500_ad *dd, int enable) +{ + int ret; + + dev_dbg(&dd->pdev->dev, "%s:%d\n", __func__, enable); + + ret = ab8500_config_pull_up_or_down(dd->pdev, + dd->pdata->video_ctrl_gpio, !enable); + if (ret < 0) { + dev_err(&dd->pdev->dev, + "%s: Failed to update reg (%d).\n", __func__, ret); + return; + } + + if (enable) + accessory_regulator_enable(REGULATOR_VAMIC1); +} + +/* + * configures HW so that carkit/headset detection can be accomplished. + */ +static void config_hw_test_basic_carkit(struct ab8500_ad *dd, int enable) +{ + int ret; + + dev_dbg(&dd->pdev->dev, "%s:%d\n", __func__, enable); + + if (enable) + accessory_regulator_disable(REGULATOR_VAMIC1); + + /* Un-Ground the VAMic1 output when enabled */ + ret = abx500_mask_and_set_register_interruptible( + &dd->pdev->dev, + AB8500_REGU_CTRL1, + AB8500_REGU_CTRL1_SPARE_REG, + BIT_REGUCTRL1SPARE_VAMIC1_GROUND, + enable ? BIT_REGUCTRL1SPARE_VAMIC1_GROUND : 0); + if (ret < 0) + dev_err(&dd->pdev->dev, + "%s: Failed to update reg (%d).\n", __func__, ret); +} + +/* + * checks whether measured voltage is in given range. depending on arguments, + * voltage might be re-measured or previously measured voltage is reused. + */ +static int mic_vol_in_range(struct ab8500_ad *dd, + int lo, int hi, int force_read) +{ + static int mv = MIN_MIC_POWER; + + if (mv == -100 || force_read) + mv = meas_voltage_stable(dd, ACC_DETECT2); + + return (mv >= lo && mv <= hi) ? 1 : 0; +} + +/* + * checks whether the currently connected HW is of given type. + */ +static int detect_hw(struct ab8500_ad *dd, + struct accessory_detect_task *task) +{ + int status; + + switch (task->type) { + case JACK_TYPE_DISCONNECTED: + config_hw_test_plug_connected(dd, 1); + status = !detect_plugged_in(dd); + break; + case JACK_TYPE_CONNECTED: + config_hw_test_plug_connected(dd, 1); + status = detect_plugged_in(dd); + break; + case JACK_TYPE_CARKIT: + config_hw_test_basic_carkit(dd, 1); + /* flow through.. */ + case JACK_TYPE_HEADPHONE: + case JACK_TYPE_CVIDEO: + case JACK_TYPE_HEADSET: + case JACK_TYPE_OPENCABLE: + status = mic_vol_in_range(dd, + task->minvol, + task->maxvol, + task->meas_mv); + break; + default: + status = 0; + } + + return status; +} + +/* + * sets the av switch direction - audio-in vs video-out + */ +static void set_av_switch(struct ab8500_ad *dd, + enum accessory_avcontrol_dir dir) +{ + int ret; + + dev_dbg(&dd->pdev->dev, "%s: Enter\n", __func__); + if (!dd->gpio35_dir_set) { + ret = gpio_direction_output(dd->pdata->video_ctrl_gpio, + dir == AUDIO_IN ? 1 : 0); + if (ret < 0) { + dev_err(&dd->pdev->dev, + "%s: Output video ctrl signal failed (%d).\n", + __func__, ret); + } else { + dd->gpio35_dir_set = 1; + dev_dbg(&dd->pdev->dev, "AV-SWITCH: %s\n", + dir == AUDIO_IN ? "AUDIO_IN" : "VIDEO_OUT"); + } + } else { + gpio_set_value(dd->pdata->video_ctrl_gpio, + dir == AUDIO_IN ? 1 : 0); + } +} + +/* + * Tries to detect the currently attached accessory + */ +static enum accessory_jack_type detect(struct ab8500_ad *dd, + int *req_det_count) +{ + enum accessory_jack_type type = JACK_TYPE_DISCONNECTED; + int i; + + accessory_regulator_enable(REGULATOR_VAUDIO | REGULATOR_AVSWITCH); + + for (i = 0; i < ARRAY_SIZE(detect_ops); ++i) { + if (detect_hw(dd, &detect_ops[i])) { + type = detect_ops[i].type; + *req_det_count = detect_ops[i].req_det_count; + break; + } + } + + config_hw_test_basic_carkit(dd, 0); + config_hw_test_plug_connected(dd, 0); + + if (jack_supports_buttons(type)) + accessory_regulator_enable(REGULATOR_VAMIC1); + else + accessory_regulator_disable(REGULATOR_VAMIC1 | + REGULATOR_AVSWITCH); + + accessory_regulator_disable(REGULATOR_VAUDIO); + + return type; +} + +/* + * registers to specific interrupt + */ +static void claim_irq(struct ab8500_ad *dd, enum accessory_irq irq_id) +{ + int ret; + int irq; + + if (irq_desc[irq_id].registered) + return; + + irq = platform_get_irq_byname( + dd->pdev, + irq_desc[irq_id].name); + if (irq < 0) { + dev_err(&dd->pdev->dev, + "%s: Failed to get irq %s\n", __func__, + irq_desc[irq_id].name); + return; + } + + ret = request_threaded_irq(irq, + NULL, + irq_desc[irq_id].isr, + IRQF_NO_SUSPEND | IRQF_SHARED, + irq_desc[irq_id].name, + dd); + if (ret != 0) { + dev_err(&dd->pdev->dev, + "%s: Failed to claim irq %s (%d)\n", + __func__, + irq_desc[irq_id].name, + ret); + } else { + irq_desc[irq_id].registered = 1; + dev_dbg(&dd->pdev->dev, "%s: %s\n", + __func__, irq_desc[irq_id].name); + } +} + +/* + * releases specific interrupt + */ +static void release_irq(struct ab8500_ad *dd, enum accessory_irq irq_id) +{ + int irq; + + if (!irq_desc[irq_id].registered) + return; + + irq = platform_get_irq_byname( + dd->pdev, + irq_desc[irq_id].name); + if (irq < 0) { + dev_err(&dd->pdev->dev, + "%s: Failed to get irq %s (%d)\n", + __func__, + irq_desc[irq_id].name, irq); + } else { + free_irq(irq, dd); + irq_desc[irq_id].registered = 0; + dev_dbg(&dd->pdev->dev, "%s: %s\n", + __func__, irq_desc[irq_id].name); + } +} + +/* + * configures interrupts + detection hardware to meet the requirements + * set by currently attached accessory type. + */ +static void config_accdetect(struct ab8500_ad *dd) +{ + switch (dd->jack_type) { + case JACK_TYPE_UNSPECIFIED: + config_accdetect1_hw(dd, 1); + config_accdetect2_hw(dd, 0); + + release_irq(dd, PLUG_IRQ); + release_irq(dd, UNPLUG_IRQ); + release_irq(dd, BUTTON_PRESS_IRQ); + release_irq(dd, BUTTON_RELEASE_IRQ); + break; + + case JACK_TYPE_DISCONNECTED: + case JACK_TYPE_HEADPHONE: + case JACK_TYPE_CVIDEO: + config_accdetect1_hw(dd, 1); + config_accdetect2_hw(dd, 0); + + claim_irq(dd, PLUG_IRQ); + claim_irq(dd, UNPLUG_IRQ); + release_irq(dd, BUTTON_PRESS_IRQ); + release_irq(dd, BUTTON_RELEASE_IRQ); + break; + + case JACK_TYPE_CONNECTED: + case JACK_TYPE_HEADSET: + case JACK_TYPE_CARKIT: + case JACK_TYPE_OPENCABLE: + config_accdetect1_hw(dd, 1); + config_accdetect2_hw(dd, 1); + + release_irq(dd, PLUG_IRQ); + claim_irq(dd, UNPLUG_IRQ); + claim_irq(dd, BUTTON_PRESS_IRQ); + claim_irq(dd, BUTTON_RELEASE_IRQ); + break; + + default: + dev_err(&dd->pdev->dev, "%s: Unknown type: %d\n", + __func__, dd->jack_type); + } +} + +/* + * Deferred initialization of the work. + */ +static void init_work(struct work_struct *work) +{ + struct ab8500_ad *dd = container_of(work, + struct ab8500_ad, init_work.work); + + dev_dbg(&dd->pdev->dev, "%s: Enter\n", __func__); + + set_av_switch(dd, AUDIO_IN); + dd->jack_type = dd->reported_jack_type = JACK_TYPE_UNSPECIFIED; + config_accdetect(dd); + queue_delayed_work(dd->irq_work_queue, + &dd->detect_work, + msecs_to_jiffies(0)); +} + +/* + * performs platform device initialization + */ +int ab8500_accessory_init(struct platform_device *pdev) +{ + struct ab8500_ad *dd; + struct ab8500_platform_data *plat; + + dev_dbg(&pdev->dev, "Enter: %s\n", __func__); + + dd = kzalloc(sizeof(struct ab8500_ad), GFP_KERNEL); + if (!dd) { + dev_err(&pdev->dev, "%s: Mem. alloc failed\n", __func__); + goto fail_no_mem_for_devdata; + } + + dd->pdev = pdev; + dd->pdata = pdev->dev.platform_data; + plat = dev_get_platdata(pdev->dev.parent); + + if (!plat || !plat->accdet) { + dev_err(&pdev->dev, "%s: Failed to get accdet plat data.\n", + __func__); + goto fail_no_ab8500_dev; + } + dd->pdata = plat->accdet; + + if (dd->pdata->video_ctrl_gpio) { + if (!gpio_is_valid(dd->pdata->video_ctrl_gpio)) { + dev_err(&pdev->dev, + "%s: Video ctrl GPIO invalid (%d).\n", __func__, + dd->pdata->video_ctrl_gpio); + goto fail_video_ctrl_gpio; + } + if (gpio_request(dd->pdata->video_ctrl_gpio, "Video Control")) { + dev_err(&pdev->dev, "%s: Get video ctrl GPIO failed.\n", + __func__); + goto fail_video_ctrl_gpio; + } + } + + if (create_btn_input_dev(dd) < 0) { + dev_err(&pdev->dev, "%s: create_button_input_dev failed.\n", + __func__); + goto fail_no_btn_input_dev; + } + + if (create_regulators(dd) < 0) { + dev_err(&pdev->dev, "%s: failed to create regulators\n", + __func__); + goto fail_no_regulators; + } + dd->btn_state = BUTTON_UNK; + + dd->irq_work_queue = create_singlethread_workqueue("ab8500_accdet_wq"); + if (!dd->irq_work_queue) { + dev_err(&pdev->dev, "%s: Failed to create wq\n", __func__); + goto fail_no_mem_for_wq; + } + dd->gpadc = ab8500_gpadc_get(); + + INIT_DELAYED_WORK(&dd->detect_work, detect_work); + INIT_DELAYED_WORK(&dd->unplug_irq_work, unplug_irq_handler_work); + INIT_DELAYED_WORK(&dd->init_work, init_work); + + /* Deferred init/detect since no use for the info early in boot */ + queue_delayed_work(dd->irq_work_queue, + &dd->init_work, + msecs_to_jiffies(INIT_DELAY_MS)); + + platform_set_drvdata(pdev, dd); + + return 0; + +fail_no_mem_for_wq: + free_regulators(); +fail_no_regulators: + input_unregister_device(dd->btn_input_dev); +fail_no_btn_input_dev: + gpio_free(dd->pdata->video_ctrl_gpio); +fail_video_ctrl_gpio: +fail_no_ab8500_dev: + kfree(dd); +fail_no_mem_for_devdata: + + return -ENOMEM; +} + +/* + * Performs platform device cleanup + */ +void ab8500_accessory_cleanup(struct ab8500_ad *dd) +{ + dev_dbg(&dd->pdev->dev, "Enter: %s\n", __func__); + + dd->jack_type = JACK_TYPE_UNSPECIFIED; + config_accdetect(dd); + + gpio_free(dd->pdata->video_ctrl_gpio); + input_unregister_device(dd->btn_input_dev); + free_regulators(); + + cancel_delayed_work(&dd->detect_work); + cancel_delayed_work(&dd->unplug_irq_work); + cancel_delayed_work(&dd->init_work); + flush_workqueue(dd->irq_work_queue); + destroy_workqueue(dd->irq_work_queue); + + kfree(dd); +} + +static int __devinit ab8500_acc_detect_probe(struct platform_device *pdev) +{ + return ab8500_accessory_init(pdev); +} + + +static int __devexit ab8500_acc_detect_remove(struct platform_device *pdev) +{ + ab8500_accessory_cleanup(platform_get_drvdata(pdev)); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +#if defined(CONFIG_PM) +static int ab8500_acc_detect_suspend(struct platform_device *pdev, + pm_message_t state) +{ + struct ab8500_ad *dd = platform_get_drvdata(pdev); + + dev_dbg(&dd->pdev->dev, "%s: Enter\n", __func__); + + cancel_delayed_work_sync(&dd->unplug_irq_work); + cancel_delayed_work_sync(&dd->detect_work); + cancel_delayed_work_sync(&dd->init_work); + + return 0; +} + +static int ab8500_acc_detect_resume(struct platform_device *pdev) +{ + struct ab8500_ad *dd = platform_get_drvdata(pdev); + + dev_dbg(&dd->pdev->dev, "%s: Enter\n", __func__); + + /* After resume, reinitialize */ + dd->gpio35_dir_set = dd->accdet1_th_set = dd->accdet2_th_set = 0; + queue_delayed_work(dd->irq_work_queue, &dd->init_work, 0); + + return 0; +} +#else +#define ab8500_acc_detect_suspend NULL +#define ab8500_acc_detect_resume NULL +#endif + +static struct platform_driver ab8500_acc_detect_platform_driver = { + .driver = { + .name = "ab8500-acc-det", + .owner = THIS_MODULE, + }, + .probe = ab8500_acc_detect_probe, + .remove = __devexit_p(ab8500_acc_detect_remove), + .suspend = ab8500_acc_detect_suspend, + .resume = ab8500_acc_detect_resume, +}; + +static int __init ab8500_acc_detect_init(void) +{ + return platform_driver_register(&ab8500_acc_detect_platform_driver); +} + +static void __exit ab8500_acc_detect_exit(void) +{ + platform_driver_unregister(&ab8500_acc_detect_platform_driver); +} + +module_init(ab8500_acc_detect_init); +module_exit(ab8500_acc_detect_exit); + +MODULE_DESCRIPTION("AB8500 AV Accessory detection driver"); +MODULE_ALIAS("platform:ab8500-acc-det"); +MODULE_AUTHOR("ST-Ericsson"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 275828a74e26fd91ae95756165310bc47e163454 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Tue, 11 Oct 2011 15:00:28 +0200 Subject: input: accdet: Workaround for AB8500 gpio (now use AB8500 offset) Signed-off-by: Philippe Langlais Conflicts: drivers/gpio/ab8500-gpio.c include/linux/mfd/ab8500/gpio.h --- drivers/input/misc/ab8500-accdet.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/misc/ab8500-accdet.c b/drivers/input/misc/ab8500-accdet.c index 2433e7bc999..9eeccd34d52 100644 --- a/drivers/input/misc/ab8500-accdet.c +++ b/drivers/input/misc/ab8500-accdet.c @@ -861,7 +861,7 @@ static void config_hw_test_plug_connected(struct ab8500_ad *dd, int enable) dev_dbg(&dd->pdev->dev, "%s:%d\n", __func__, enable); - ret = ab8500_config_pull_up_or_down(dd->pdev, + ret = ab8500_config_pull_up_or_down(&dd->pdev->dev, dd->pdata->video_ctrl_gpio, !enable); if (ret < 0) { dev_err(&dd->pdev->dev, -- cgit v1.2.3 From 6339d22910ceb0c4a5ba0717cb25671651bc0180 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Tue, 18 Oct 2011 22:06:06 +0200 Subject: drivers: input: enable accessory detect on default Enables the accessory detect on default for the mop500_power_defconfig. It also fixes the syntax in in the corresponding Kconfig file. ST-Ericsson ID: ER 327965 Linux-next: ST-Ericsson ID: ER 282779 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I8c58b419dacd0b3ef65f3be15d5ea7d98e32e3f0 Signed-off-by: Marcel Tunnissen Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/17991 Reviewed-by: Rahul VENKATRAM Tested-by: Martin TRULSSON Reviewed-by: Dan JOHANSSON Reviewed-by: Jonas ABERG --- drivers/input/misc/Kconfig | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index cef9fd21554..61521a59d00 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -23,11 +23,11 @@ config INPUT_88PM860X_ONKEY will be called 88pm860x_onkey. config INPUT_AB8500_ACCDET - bool "AB8500 AV Accessory detection" - depends on AB8500_CORE && AB8500_GPADC && AB8500_GPIO - help - Say Y here to enable AV accessory detection features for ST-Ericsson's - AB8500 Mix-Sig PMIC. + bool "AB8500 AV Accessory detection" + depends on AB8500_CORE && AB8500_GPADC && AB8500_GPIO + help + Say Y here to enable AV accessory detection features for ST-Ericsson's + AB8500 Mix-Sig PMIC. config INPUT_AB8500_PONKEY tristate "AB8500 Pon (PowerOn) Key" -- cgit v1.2.3 From b7cefe813f30f70fce70e64fda81881b0daec33c Mon Sep 17 00:00:00 2001 From: Srikanth Shivanand Date: Sat, 26 Mar 2011 17:11:41 +0530 Subject: input: misc: ab8500-accdet: Power optimisation fix. The detection of the accessory is made independent of GPIO35. The GPIO is used only after detection. This saves power. ST-Ericsson ID: ER 330343 Change-Id: Ifbf0028777d242a09067eb560f4476d5522e9074 Signed-off-by: Srikanth Shivanand Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/19312 Reviewed-by: Marcel TUNNISSEN Tested-by: Marcel TUNNISSEN Reviewed-by: Martin PERSSON Tested-by: Martin PERSSON --- drivers/input/misc/ab8500-accdet.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/input/misc/ab8500-accdet.c b/drivers/input/misc/ab8500-accdet.c index 9eeccd34d52..77634d51c7a 100644 --- a/drivers/input/misc/ab8500-accdet.c +++ b/drivers/input/misc/ab8500-accdet.c @@ -149,10 +149,12 @@ enum accessory_irq { * Enumerates the op. modes of the avcontrol switch * @AUDIO_IN Audio input is selected * @VIDEO_OUT Video output is selected + * @NOT_SET The av-switch control signal is disconnected. */ enum accessory_avcontrol_dir { AUDIO_IN, VIDEO_OUT, + NOT_SET, }; /** @@ -689,8 +691,6 @@ static void unplug_irq_handler_work(struct work_struct *work) dd->btn_state = BUTTON_UNK; config_accdetect(dd); - set_av_switch(dd, AUDIO_IN); - accessory_regulator_disable(REGULATOR_ALL); report_jack_status(dd); @@ -748,6 +748,8 @@ static void detect_work(struct work_struct *work) dev_dbg(&dd->pdev->dev, "%s: Enter\n", __func__); + set_av_switch(dd, AUDIO_IN); + new_type = detect(dd, &req_det_count); dd->total_jack_det_count++; @@ -956,8 +958,12 @@ static void set_av_switch(struct ab8500_ad *dd, { int ret; - dev_dbg(&dd->pdev->dev, "%s: Enter\n", __func__); - if (!dd->gpio35_dir_set) { + dev_dbg(&dd->pdev->dev, "%s: Enter (%d)\n", __func__, dir); + if (dir == NOT_SET) { + ret = gpio_direction_input(dd->pdata->video_ctrl_gpio); + dd->gpio35_dir_set = 0; + ret = gpio_direction_output(dd->pdata->video_ctrl_gpio, 0); + } else if (!dd->gpio35_dir_set) { ret = gpio_direction_output(dd->pdata->video_ctrl_gpio, dir == AUDIO_IN ? 1 : 0); if (ret < 0) { @@ -1089,9 +1095,11 @@ static void config_accdetect(struct ab8500_ad *dd) release_irq(dd, UNPLUG_IRQ); release_irq(dd, BUTTON_PRESS_IRQ); release_irq(dd, BUTTON_RELEASE_IRQ); + set_av_switch(dd, NOT_SET); break; case JACK_TYPE_DISCONNECTED: + set_av_switch(dd, NOT_SET); case JACK_TYPE_HEADPHONE: case JACK_TYPE_CVIDEO: config_accdetect1_hw(dd, 1); @@ -1132,7 +1140,6 @@ static void init_work(struct work_struct *work) dev_dbg(&dd->pdev->dev, "%s: Enter\n", __func__); - set_av_switch(dd, AUDIO_IN); dd->jack_type = dd->reported_jack_type = JACK_TYPE_UNSPECIFIED; config_accdetect(dd); queue_delayed_work(dd->irq_work_queue, -- cgit v1.2.3 From 9d025b8242dca95cb49fd09c101b6bf6ecc9fba4 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Tue, 18 Oct 2011 22:02:25 +0200 Subject: bu21013: Proper reset implementation during suspend and resume Proper reset implementation during suspend and resume for BU21013 touch screen driver by initializing the reset gpio pin. ST-Ericsson Id: ER 334949 Signed-off-by: Naveen Kumar Gaddipati Change-Id: I75469d92cf0d49d75500e0e24d34ca0bb5de6311 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/20840 Reviewed-by: Jonas ABERG Reviewed-by: Srinidhi KASAGAR --- drivers/input/touchscreen/bu21013_ts.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/input/touchscreen/bu21013_ts.c b/drivers/input/touchscreen/bu21013_ts.c index 62dbc183347..857a21db0eb 100644 --- a/drivers/input/touchscreen/bu21013_ts.c +++ b/drivers/input/touchscreen/bu21013_ts.c @@ -213,6 +213,14 @@ static int bu21013_enable(struct bu21013_ts_data *pdata) if (pdata->regulator) regulator_enable(pdata->regulator); + if (pdata->chip->cs_en) { + retval = pdata->chip->cs_en(pdata->chip->cs_pin); + if (retval < 0) { + dev_err(&pdata->client->dev, "enable hw failed\n"); + return retval; + } + } + if (pdata->ext_clk_state) retval = bu21013_ext_clk(pdata, true, true); else @@ -242,6 +250,8 @@ static void bu21013_disable(struct bu21013_ts_data *pdata) (void) bu21013_ext_clk(pdata, false, false); disable_irq(pdata->chip->irq); + if (pdata->chip->cs_dis) + pdata->chip->cs_dis(pdata->chip->cs_pin); if (pdata->regulator) regulator_disable(pdata->regulator); } -- cgit v1.2.3 From 8b3d0dcaf2cc5815c48347e731abcef4e3003692 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Wed, 12 Oct 2011 14:32:10 +0200 Subject: ske keypad: Proper regulator and clock framework support SKE keypad doesn't block the APSleep during CPU Idle by proper implementation of regulator and clock support for SKE. ST-Ericsson Id: ER 323894 Change-Id: I242f9618439e3004b5458981235b7079a97aa7dd Signed-off-by: Naveen Kumar Gaddipati Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/20127 Reviewed-by: Jonas ABERG --- arch/arm/plat-nomadik/include/plat/ske.h | 9 + drivers/input/keyboard/nomadik-ske-keypad.c | 268 +++++++++++++++++++++++++--- 2 files changed, 249 insertions(+), 28 deletions(-) diff --git a/arch/arm/plat-nomadik/include/plat/ske.h b/arch/arm/plat-nomadik/include/plat/ske.h index 31382fbc07d..7a4fbdf3c13 100644 --- a/arch/arm/plat-nomadik/include/plat/ske.h +++ b/arch/arm/plat-nomadik/include/plat/ske.h @@ -22,6 +22,9 @@ #define SKE_MIS 0x18 #define SKE_ICR 0x1C +#define SKE_KPD_MAX_ROWS 8 +#define SKE_KPD_MAX_COLS 8 + /* * Keypad module */ @@ -30,21 +33,27 @@ * struct keypad_platform_data - structure for platform specific data * @init: pointer to keypad init function * @exit: pointer to keypad deinitialisation function + * @gpio_input_pins: pointer to gpio input pins + * @gpio_output_pins: pointer to gpio output pins * @keymap_data: matrix scan code table for keycodes * @krow: maximum number of rows * @kcol: maximum number of columns * @debounce_ms: platform specific debounce time * @no_autorepeat: flag for auto repetition * @wakeup_enable: allow waking up the system + * @switch_delay: gpio switch_delay */ struct ske_keypad_platform_data { int (*init)(void); int (*exit)(void); + int *gpio_input_pins; + int *gpio_output_pins; const struct matrix_keymap_data *keymap_data; u8 krow; u8 kcol; u8 debounce_ms; bool no_autorepeat; bool wakeup_enable; + int switch_delay; }; #endif /*__SKE_KPD_H*/ diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c index c7c59adb101..a779e8ca6fe 100644 --- a/drivers/input/keyboard/nomadik-ske-keypad.c +++ b/drivers/input/keyboard/nomadik-ske-keypad.c @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -22,6 +23,7 @@ #include #include +#include /* SKE_CR bits */ #define SKE_KPMLT (0x1 << 6) @@ -61,6 +63,14 @@ * @clk: clock structure pointer * @enable: flag to enable the driver event * @regulator: pointer to the regulator used for ske kyepad + * @gpio_input_irq: array for gpio irqs + * @key_pressed: hold the key state + * @work: delayed work variable for gpio switch + * @ske_rows: rows gpio array for ske + * @ske_cols: columns gpio array for ske + * @gpio_row: gpio row + * @gpio_col: gpio column + * @gpio_work: delayed work variable for release gpio key */ struct ske_keypad { int irq; @@ -72,6 +82,14 @@ struct ske_keypad { spinlock_t ske_keypad_lock; bool enable; struct regulator *regulator; + int gpio_input_irq[SKE_KPD_MAX_ROWS]; + int key_pressed; + struct delayed_work work; + int ske_rows[SKE_KPD_MAX_ROWS]; + int ske_cols[SKE_KPD_MAX_COLS]; + int gpio_row; + int gpio_col; + struct delayed_work gpio_work; }; static void ske_keypad_set_bits(struct ske_keypad *keypad, u16 addr, @@ -140,18 +158,41 @@ static int __devinit ske_keypad_chip_init(struct ske_keypad *keypad) return 0; } -static void ske_enable(struct ske_keypad *keypad) +static void ske_mode_enable(struct ske_keypad *keypad, bool enable) { - if (keypad->enable) { + int i; + + if (!enable) { + writel(0, keypad->reg_base + SKE_CR); + if (keypad->board->exit) + keypad->board->exit(); + for (i = 0; i < keypad->board->krow; i++) { + enable_irq(keypad->gpio_input_irq[i]); + enable_irq_wake(keypad->gpio_input_irq[i]); + } + clk_disable(keypad->clk); + regulator_disable(keypad->regulator); + } else { regulator_enable(keypad->regulator); clk_enable(keypad->clk); - enable_irq(keypad->irq); + for (i = 0; i < keypad->board->krow; i++) { + disable_irq_nosync(keypad->gpio_input_irq[i]); + disable_irq_wake(keypad->gpio_input_irq[i]); + } + if (keypad->board->init) + keypad->board->init(); ske_keypad_chip_init(keypad); + } +} +static void ske_enable(struct ske_keypad *keypad, bool enable) +{ + keypad->enable = enable; + if (keypad->enable) { + enable_irq(keypad->irq); + ske_mode_enable(keypad, true); } else { + ske_mode_enable(keypad, false); disable_irq(keypad->irq); - ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0); - clk_disable(keypad->clk); - regulator_disable(keypad->regulator); } } @@ -178,7 +219,7 @@ static ssize_t ske_store_attr_enable(struct device *dev, if (keypad->enable != val) { keypad->enable = val ? true : false; - ske_enable(keypad); + ske_enable(keypad, keypad->enable); } return count; } @@ -200,7 +241,6 @@ static void ske_keypad_report(struct ske_keypad *keypad, u8 status, int col) int row = 0, code, pos; struct input_dev *input = keypad->input; u32 ske_ris; - int key_pressed; int num_of_rows; /* find out the row */ @@ -212,10 +252,11 @@ static void ske_keypad_report(struct ske_keypad *keypad, u8 status, int col) code = MATRIX_SCAN_CODE(row, col, SKE_KEYPAD_ROW_SHIFT); ske_ris = readl(keypad->reg_base + SKE_RIS); - key_pressed = ske_ris & SKE_KPRISA; + keypad->key_pressed = ske_ris & SKE_KPRISA; input_event(input, EV_MSC, MSC_SCAN, code); - input_report_key(input, keypad->keymap[code], key_pressed); + input_report_key(input, keypad->keymap[code], + keypad->key_pressed); input_sync(input); num_of_rows--; } while (num_of_rows); @@ -252,10 +293,8 @@ static void ske_keypad_read_data(struct ske_keypad *keypad) } } } - -static irqreturn_t ske_keypad_irq(int irq, void *dev_id) +static void ske_keypad_scan(struct ske_keypad *keypad) { - struct ske_keypad *keypad = dev_id; int timeout = keypad->board->debounce_ms; /* disable auto scan interrupt; mask the interrupt generated */ @@ -274,6 +313,139 @@ static irqreturn_t ske_keypad_irq(int irq, void *dev_id) /* enable auto scan interrupts */ ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA); +} + +static void ske_gpio_switch_work(struct work_struct *work) +{ + struct ske_keypad *keypad = container_of(work, + struct ske_keypad, work.work); + + ske_mode_enable(keypad, false); + keypad->enable = false; +} + +static void ske_gpio_release_work(struct work_struct *work) +{ + int code; + struct ske_keypad *keypad = container_of(work, + struct ske_keypad, gpio_work.work); + struct input_dev *input = keypad->input; + + code = MATRIX_SCAN_CODE(keypad->gpio_row, keypad->gpio_col, + SKE_KEYPAD_ROW_SHIFT); + input_event(input, EV_MSC, MSC_SCAN, code); + input_report_key(input, keypad->keymap[code], 1); + input_sync(input); + input_report_key(input, keypad->keymap[code], 0); + input_sync(input); +} + +static int ske_read_get_gpio_row(struct ske_keypad *keypad) +{ + int row; + int value = 0; + int ret; + + /* read all rows GPIO data register values */ + for (row = 0; row < SKE_KPD_MAX_ROWS ; row++) { + ret = gpio_get_value(keypad->ske_rows[row]); + value += (1 << row) * ret; + } + + /* get the exact row */ + for (row = 0; row < keypad->board->krow; row++) { + if (((1 << row) & value) == 0) + return row; + } + + return -1; +} + +static void ske_set_cols(struct ske_keypad *keypad, int col) +{ + int i ; + int value; + + /* + * Set all columns except the requested column + * output pin as high + */ + for (i = 0; i < SKE_KPD_MAX_COLS; i++) { + if (i == col) + value = 0; + else + value = 1; + gpio_request(keypad->ske_cols[i], "ske-kp"); + gpio_direction_output(keypad->ske_cols[i], value); + gpio_free(keypad->ske_cols[i]); + } +} + +static void ske_free_cols(struct ske_keypad *keypad) +{ + int i ; + + for (i = 0; i < SKE_KPD_MAX_COLS; i++) { + gpio_request(keypad->ske_cols[i], "ske-kp"); + gpio_direction_output(keypad->ske_cols[i], 0); + gpio_free(keypad->ske_cols[i]); + } +} + +static void ske_manual_scan(struct ske_keypad *keypad) +{ + int row; + int col; + + for (col = 0; col < keypad->board->kcol; col++) { + ske_set_cols(keypad, col); + row = ske_read_get_gpio_row(keypad); + if (row >= 0) { + keypad->key_pressed = 1; + keypad->gpio_row = row; + keypad->gpio_col = col; + break; + } + } + ske_free_cols(keypad); +} + +static irqreturn_t ske_keypad_gpio_irq(int irq, void *dev_id) +{ + struct ske_keypad *keypad = dev_id; + + if (!gpio_get_value(NOMADIK_IRQ_TO_GPIO(irq))) { + ske_manual_scan(keypad); + if (!keypad->enable) { + keypad->enable = true; + ske_mode_enable(keypad, true); + } + /* + * Schedule the work queue to change it to + * report the key pressed, if it is not detected in SKE mode. + */ + if (keypad->key_pressed) + schedule_delayed_work(&keypad->gpio_work, + KEY_PRESSED_DELAY); + } + + return IRQ_HANDLED; +} +static irqreturn_t ske_keypad_irq(int irq, void *dev_id) +{ + struct ske_keypad *keypad = dev_id; + + cancel_delayed_work_sync(&keypad->gpio_work); + cancel_delayed_work_sync(&keypad->work); + ske_keypad_scan(keypad); + + /* + * Schedule the work queue to change it to + * GPIO mode, if there is no activity in SKE mode + */ + if (!keypad->key_pressed && keypad->enable) + schedule_delayed_work(&keypad->work, + keypad->board->switch_delay); return IRQ_HANDLED; } @@ -287,6 +459,7 @@ static int __devinit ske_keypad_probe(struct platform_device *pdev) void __iomem *reg_base; int ret = 0; int irq; + int i; struct ske_keypad_platform_data *plat = pdev->dev.platform_data; if (!plat) { @@ -339,7 +512,6 @@ static int __devinit ske_keypad_probe(struct platform_device *pdev) ret = -ENOMEM; goto out_freekeypad; } - keypad->regulator = regulator_get(&pdev->dev, "v-ape"); if (IS_ERR(keypad->regulator)) { dev_err(&pdev->dev, "regulator_get failed\n"); @@ -383,29 +555,57 @@ static int __devinit ske_keypad_probe(struct platform_device *pdev) keypad->input = input; keypad->reg_base = reg_base; keypad->clk = clk; - keypad->enable = true; + INIT_DELAYED_WORK(&keypad->work, ske_gpio_switch_work); + INIT_DELAYED_WORK(&keypad->gpio_work, ske_gpio_release_work); /* allocations are sane, we begin HW initialization */ clk_enable(keypad->clk); if (!keypad->board->init) { - dev_err(&pdev->dev, "NULL board initialization helper\n"); + dev_err(&pdev->dev, "init funtion not defined\n"); ret = -EINVAL; goto out_unregisterinput; } if (keypad->board->init() < 0) { - dev_err(&pdev->dev, "unable to set keypad board config\n"); + dev_err(&pdev->dev, "keyboard init config failed\n"); ret = -EINVAL; goto out_unregisterinput; } - ret = ske_keypad_chip_init(keypad); - if (ret < 0) { - dev_err(&pdev->dev, "unable to init keypad hardware\n"); + if (!keypad->board->exit) { + dev_err(&pdev->dev, "exit funtion not defined\n"); + ret = -EINVAL; goto out_unregisterinput; } + if (keypad->board->exit() < 0) { + dev_err(&pdev->dev, "keyboard exit config failed\n"); + ret = -EINVAL; + goto out_unregisterinput; + } + for (i = 0; i < SKE_KPD_MAX_ROWS; i++) { + keypad->ske_rows[i] = *plat->gpio_input_pins; + keypad->ske_cols[i] = *plat->gpio_output_pins; + keypad->gpio_input_irq[i] = + NOMADIK_GPIO_TO_IRQ(keypad->ske_rows[i]); + plat->gpio_input_pins++; + plat->gpio_output_pins++; + } + + for (i = 0; i < keypad->board->krow; i++) { + ret = request_threaded_irq(keypad->gpio_input_irq[i], + NULL, ske_keypad_gpio_irq, + IRQF_TRIGGER_FALLING | IRQF_NO_SUSPEND, + "ske-keypad-gpio", keypad); + if (ret) { + dev_err(&pdev->dev, "allocate gpio irq %d failed\n", + keypad->gpio_input_irq[i]); + goto out_unregisterinput; + } + enable_irq_wake(keypad->gpio_input_irq[i]); + } + ret = request_threaded_irq(keypad->irq, NULL, ske_keypad_irq, IRQF_ONESHOT, "ske-keypad", keypad); if (ret) { @@ -425,6 +625,9 @@ static int __devinit ske_keypad_probe(struct platform_device *pdev) platform_set_drvdata(pdev, keypad); + clk_disable(keypad->clk); + regulator_disable(keypad->regulator); + return 0; out_free_irq: @@ -455,6 +658,8 @@ static int __devexit ske_keypad_remove(struct platform_device *pdev) struct ske_keypad *keypad = platform_get_drvdata(pdev); struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + cancel_delayed_work_sync(&keypad->gpio_work); + cancel_delayed_work_sync(&keypad->work); free_irq(keypad->irq, keypad); input_unregister_device(keypad->input); @@ -466,7 +671,6 @@ static int __devexit ske_keypad_remove(struct platform_device *pdev) if (keypad->board->exit) keypad->board->exit(); - regulator_disable(keypad->regulator); regulator_put(keypad->regulator); iounmap(keypad->reg_base); @@ -485,9 +689,14 @@ static int ske_keypad_suspend(struct device *dev) if (device_may_wakeup(dev)) enable_irq_wake(irq); - else if (keypad->enable) { - keypad->enable = false; - ske_enable(keypad); + else { + cancel_delayed_work_sync(&keypad->gpio_work); + cancel_delayed_work_sync(&keypad->work); + disable_irq(irq); + if (keypad->enable) { + ske_mode_enable(keypad, false); + keypad->enable = false; + } } return 0; @@ -501,9 +710,12 @@ static int ske_keypad_resume(struct device *dev) if (device_may_wakeup(dev)) disable_irq_wake(irq); - else if (!keypad->enable) { - keypad->enable = true; - ske_enable(keypad); + else { + if (!keypad->enable) { + keypad->enable = true; + ske_mode_enable(keypad, true); + } + enable_irq(irq); } return 0; @@ -540,6 +752,6 @@ static void __exit ske_keypad_exit(void) module_exit(ske_keypad_exit); MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Naveen Kumar / Sundar Iyer "); +MODULE_AUTHOR("Naveen Kumar "); MODULE_DESCRIPTION("Nomadik Scroll-Key-Encoder Keypad Driver"); MODULE_ALIAS("platform:nomadik-ske-keypad"); -- cgit v1.2.3 From c06d7a4e93b008d170006e1ba73c5036ff7cf1c0 Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Wed, 20 Apr 2011 10:42:15 +0530 Subject: input: ab8500-ponkey: make generic Don't reference the AB8500 structure (which is anyway unused), and use an id table to support different variants. ST-Ericsson Linux next: - ST-Ericsson ID: WP257121 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I3afc1f534c6f5c2b8aa2450eaef18cd0f638206c Signed-off-by: Rabin Vincent Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/21179 Reviewed-by: QATEST Reviewed-by: Vijaya Kumar K-1 Reviewed-by: Srinidhi KASAGAR --- drivers/input/misc/ab8500-ponkey.c | 43 +++++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/drivers/input/misc/ab8500-ponkey.c b/drivers/input/misc/ab8500-ponkey.c index f19eb400df0..fe107402475 100644 --- a/drivers/input/misc/ab8500-ponkey.c +++ b/drivers/input/misc/ab8500-ponkey.c @@ -14,16 +14,24 @@ #include #include +struct ab8500_ponkey_variant { + const char *irq_falling; + const char *irq_rising; +}; + +static const struct ab8500_ponkey_variant ab8500_ponkey = { + .irq_falling = "ONKEY_DBF", + .irq_rising = "ONKEY_DBR", +}; + /** * struct ab8500_ponkey_info - ab8500 ponkey information * @input_dev: pointer to input device - * @ab8500: ab8500 parent * @irq_dbf: irq number for falling transition * @irq_dbr: irq number for rising transition */ struct ab8500_ponkey_info { struct input_dev *idev; - struct ab8500 *ab8500; int irq_dbf; int irq_dbr; }; @@ -45,19 +53,24 @@ static irqreturn_t ab8500_ponkey_handler(int irq, void *data) static int __devinit ab8500_ponkey_probe(struct platform_device *pdev) { - struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent); + const struct ab8500_ponkey_variant *variant; struct ab8500_ponkey_info *info; int irq_dbf, irq_dbr, ret; - irq_dbf = platform_get_irq_byname(pdev, "ONKEY_DBF"); + variant = (const struct ab8500_ponkey_variant *) + pdev->id_entry->driver_data; + + irq_dbf = platform_get_irq_byname(pdev, variant->irq_falling); if (irq_dbf < 0) { - dev_err(&pdev->dev, "No IRQ for ONKEY_DBF,error=%d\n", irq_dbf); + dev_err(&pdev->dev, "No IRQ for %s: %d\n", + variant->irq_falling, irq_dbf); return irq_dbf; } - irq_dbr = platform_get_irq_byname(pdev, "ONKEY_DBR"); + irq_dbr = platform_get_irq_byname(pdev, variant->irq_rising); if (irq_dbr < 0) { - dev_err(&pdev->dev, "No IRQ for ONKEY_DBR,error=%d\n", irq_dbr); + dev_err(&pdev->dev, "No IRQ for %s: %d\n", + variant->irq_rising, irq_dbr); return irq_dbr; } @@ -65,13 +78,12 @@ static int __devinit ab8500_ponkey_probe(struct platform_device *pdev) if (!info) return -ENOMEM; - info->ab8500 = ab8500; info->irq_dbf = irq_dbf; info->irq_dbr = irq_dbr; info->idev = input_allocate_device(); if (!info->idev) { - dev_err(ab8500->dev, "Failed to allocate input dev\n"); + dev_err(&pdev->dev, "Failed to allocate input dev\n"); ret = -ENOMEM; goto out; } @@ -83,7 +95,7 @@ static int __devinit ab8500_ponkey_probe(struct platform_device *pdev) ret = input_register_device(info->idev); if (ret) { - dev_err(ab8500->dev, "Can't register input device: %d\n", ret); + dev_err(&pdev->dev, "Can't register input device: %d\n", ret); goto out_unfreedevice; } @@ -91,7 +103,7 @@ static int __devinit ab8500_ponkey_probe(struct platform_device *pdev) IRQF_NO_SUSPEND, "ab8500-ponkey-dbf", info); if (ret < 0) { - dev_err(ab8500->dev, "Failed to request dbf IRQ#%d: %d\n", + dev_err(&pdev->dev, "Failed to request dbf IRQ#%d: %d\n", info->irq_dbf, ret); goto out_unregisterdevice; } @@ -100,7 +112,7 @@ static int __devinit ab8500_ponkey_probe(struct platform_device *pdev) IRQF_NO_SUSPEND, "ab8500-ponkey-dbr", info); if (ret < 0) { - dev_err(ab8500->dev, "Failed to request dbr IRQ#%d: %d\n", + dev_err(&pdev->dev, "Failed to request dbr IRQ#%d: %d\n", info->irq_dbr, ret); goto out_irq_dbf; } @@ -132,11 +144,18 @@ static int __devexit ab8500_ponkey_remove(struct platform_device *pdev) return 0; } +static struct platform_device_id ab8500_ponkey_id_table[] = { + { "ab8500-poweron-key", (kernel_ulong_t)&ab8500_ponkey, }, + { }, +}; +MODULE_DEVICE_TABLE(platform, ab8500_ponkey_id_table); + static struct platform_driver ab8500_ponkey_driver = { .driver = { .name = "ab8500-poweron-key", .owner = THIS_MODULE, }, + .id_table = ab8500_ponkey_id_table, .probe = ab8500_ponkey_probe, .remove = __devexit_p(ab8500_ponkey_remove), }; -- cgit v1.2.3 From 7e07d5eda8287e473fa95d4c91c8a798c8f42921 Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Tue, 26 Apr 2011 14:05:11 +0530 Subject: db5500-keypad: remove clk hacks Now that we have a clock framework in place, remove these hacks. ST-Ericsson Linux next: - ST-Ericsson ID: WP264487 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I41feadd787132c4c03509d47ddd73400d81cafae Signed-off-by: Rabin Vincent Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/21574 Reviewed-by: Naveen Kumar GADDIPATI Reviewed-by: QATEST Reviewed-by: Srinidhi KASAGAR --- drivers/input/keyboard/db5500_keypad.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/drivers/input/keyboard/db5500_keypad.c b/drivers/input/keyboard/db5500_keypad.c index a6d0fe08961..398cf83a05f 100644 --- a/drivers/input/keyboard/db5500_keypad.c +++ b/drivers/input/keyboard/db5500_keypad.c @@ -244,11 +244,8 @@ static int __devinit db5500_keypad_probe(struct platform_device *pdev) clk = clk_get(&pdev->dev, NULL); if (IS_ERR(clk)) { dev_err(&pdev->dev, "failed to clk_get\n"); - - /* - * FIXME: error out here once DB5500 clock framework is in - * place, and remove all the !IS_ERR(clk) checks. - */ + ret = PTR_ERR(clk); + goto out_iounmap; } keypad = kzalloc(sizeof(struct db5500_keypad), GFP_KERNEL); @@ -296,8 +293,7 @@ static int __devinit db5500_keypad_probe(struct platform_device *pdev) keypad->clk = clk; /* allocations are sane, we begin HW initialization */ - if (!IS_ERR(keypad->clk)) - clk_enable(keypad->clk); + clk_enable(keypad->clk); ret = db5500_keypad_chip_init(keypad); if (ret < 0) { @@ -321,15 +317,14 @@ static int __devinit db5500_keypad_probe(struct platform_device *pdev) out_unregisterinput: input_unregister_device(input); input = NULL; - if (!IS_ERR(keypad->clk)) - clk_disable(keypad->clk); + clk_disable(keypad->clk); out_freeinput: input_free_device(input); out_freekeypad: kfree(keypad); out_freeclk: - if (!IS_ERR(clk)) - clk_put(clk); + clk_put(clk); +out_iounmap: iounmap(base); out_freerequest_memregions: release_mem_region(res->start, resource_size(res)); @@ -345,10 +340,8 @@ static int __devexit db5500_keypad_remove(struct platform_device *pdev) free_irq(keypad->irq, keypad); input_unregister_device(keypad->input); - if (!IS_ERR(keypad->clk)) { - clk_disable(keypad->clk); - clk_put(keypad->clk); - } + clk_disable(keypad->clk); + clk_put(keypad->clk); iounmap(keypad->base); release_mem_region(res->start, resource_size(res)); -- cgit v1.2.3 From 4320e06f9d84d7117c96fe7a4dca8e628d02f16c Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Tue, 26 Apr 2011 14:36:16 +0530 Subject: db5500-keypad: handle suspend/resume The onchip keyboard cannot wake up the system from sleep, so disable it in suspend and remove the device_init_wakeup call. Also introduce proper write allowed bit checks. ST-Ericsson Linux next: - ST-Ericsson ID: WP264487 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I01f065422ba86ef4990223682f351d49ec931434 Signed-off-by: Rabin Vincent Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/21575 Reviewed-by: Naveen Kumar GADDIPATI Reviewed-by: QATEST Reviewed-by: Srinidhi KASAGAR --- drivers/input/keyboard/db5500_keypad.c | 78 ++++++++++++++++++++++++---------- 1 file changed, 56 insertions(+), 22 deletions(-) diff --git a/drivers/input/keyboard/db5500_keypad.c b/drivers/input/keyboard/db5500_keypad.c index 398cf83a05f..8767b7f81ba 100644 --- a/drivers/input/keyboard/db5500_keypad.c +++ b/drivers/input/keyboard/db5500_keypad.c @@ -25,6 +25,7 @@ #define KEYPAD_NUM_ARRAY_REGS 5 #define KEYPAD_CTR_WRITE_IRQ_ENABLE (1 << 10) +#define KEYPAD_CTR_WRITE_CONTROL (1 << 8) #define KEYPAD_CTR_SCAN_ENABLE (1 << 7) #define KEYPAD_ARRAY_CHANGEBIT (1 << 15) @@ -160,12 +161,41 @@ again: return IRQ_HANDLED; } -static int __devinit db5500_keypad_chip_init(struct db5500_keypad *keypad) +static void db5500_keypad_writel(struct db5500_keypad *keypad, u32 val, u32 reg) +{ + int timeout = 4; + int allowedbit; + + switch (reg) { + case KEYPAD_CTR: + allowedbit = KEYPAD_CTR_WRITE_CONTROL; + break; + case KEYPAD_INT_ENABLE: + allowedbit = KEYPAD_CTR_WRITE_IRQ_ENABLE; + break; + default: + BUG(); + } + + do { + u32 ctr = readl(keypad->base + KEYPAD_CTR); + + if (ctr & allowedbit) + break; + + udelay(50); + } while (--timeout); + + /* Five 32k clk cycles (~150us) required, we waited 200us */ + WARN_ON(!timeout); + + writel(val, keypad->base + reg); +} + +static int db5500_keypad_chip_init(struct db5500_keypad *keypad) { int debounce = keypad->board->debounce_ms; int debounce_hits = 0; - int timeout = 100; - u32 val; if (debounce < KEYPAD_DEBOUNCE_PERIOD_MIN) debounce = KEYPAD_DEBOUNCE_PERIOD_MIN; @@ -179,22 +209,27 @@ static int __devinit db5500_keypad_chip_init(struct db5500_keypad *keypad) /* Convert the milliseconds to the bit mask */ debounce = DIV_ROUND_UP(debounce, KEYPAD_DEBOUNCE_PERIOD_MIN) - 1; - writel(KEYPAD_CTR_SCAN_ENABLE - | ((debounce_hits & 0x7) << 4) - | debounce, keypad->base + KEYPAD_CTR); - - do { - val = readl(keypad->base + KEYPAD_CTR); - } while ((!(val & KEYPAD_CTR_WRITE_IRQ_ENABLE)) && --timeout); + clk_enable(keypad->clk); - if (!timeout) - return -EINVAL; + db5500_keypad_writel(keypad, + KEYPAD_CTR_SCAN_ENABLE + | ((debounce_hits & 0x7) << 4) + | debounce, + KEYPAD_CTR); - writel(0x1, keypad->base + KEYPAD_INT_ENABLE); + db5500_keypad_writel(keypad, 0x1, KEYPAD_INT_ENABLE); return 0; } +static void db5500_keypad_close(struct db5500_keypad *keypad) +{ + db5500_keypad_writel(keypad, 0, KEYPAD_CTR); + db5500_keypad_writel(keypad, 0, KEYPAD_INT_ENABLE); + + clk_disable(keypad->clk); +} + static int __devinit db5500_keypad_probe(struct platform_device *pdev) { const struct db5500_keypad_platform_data *plat; @@ -292,9 +327,6 @@ static int __devinit db5500_keypad_probe(struct platform_device *pdev) keypad->base = base; keypad->clk = clk; - /* allocations are sane, we begin HW initialization */ - clk_enable(keypad->clk); - ret = db5500_keypad_chip_init(keypad); if (ret < 0) { dev_err(&pdev->dev, "unable to init keypad hardware\n"); @@ -308,8 +340,6 @@ static int __devinit db5500_keypad_probe(struct platform_device *pdev) goto out_unregisterinput; } - device_init_wakeup(&pdev->dev, true); - platform_set_drvdata(pdev, keypad); return 0; @@ -359,8 +389,10 @@ static int db5500_keypad_suspend(struct device *dev) if (device_may_wakeup(dev)) enable_irq_wake(irq); - else - /* disable IRQ here */ + else { + disable_irq(irq); + db5500_keypad_close(keypad); + } return 0; } @@ -373,8 +405,10 @@ static int db5500_keypad_resume(struct device *dev) if (device_may_wakeup(dev)) disable_irq_wake(irq); - else - /* enable IRQ here */ + else { + db5500_keypad_chip_init(keypad); + enable_irq(irq); + } return 0; } -- cgit v1.2.3 From f4ce4b3b7c3c3931e2a02e93474b01833c234fa3 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Wed, 12 Oct 2011 15:16:03 +0200 Subject: input/mfd: AB5500 ONSWa support ST-Ericsson Linux next: - ST-Ericsson ID: WP257121 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I19122bc76c83545ffa15e6997320eacf58b8c0f6 Signed-off-by: Rabin Vincent Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/21180 Reviewed-by: Vijaya Kumar K-1 Reviewed-by: QATEST Reviewed-by: Srinidhi KASAGAR Conflicts: drivers/mfd/ab5500-core.c include/linux/mfd/abx500/ab5500.h --- drivers/input/misc/Kconfig | 8 ++++---- drivers/input/misc/ab8500-ponkey.c | 6 ++++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 61521a59d00..7842fdcb238 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -30,11 +30,11 @@ config INPUT_AB8500_ACCDET AB8500 Mix-Sig PMIC. config INPUT_AB8500_PONKEY - tristate "AB8500 Pon (PowerOn) Key" - depends on AB8500_CORE + tristate "AB5500/AB8500 Pon (PowerOn) Key" + depends on AB5500_CORE || AB8500_CORE help - Say Y here to use the PowerOn Key for ST-Ericsson's AB8500 - Mix-Sig PMIC. + Say Y here to use the PowerOn Key for ST-Ericsson's AB5500/AB8500 + Mix-Sig PMICs. To compile this driver as a module, choose M here: the module will be called ab8500-ponkey. diff --git a/drivers/input/misc/ab8500-ponkey.c b/drivers/input/misc/ab8500-ponkey.c index fe107402475..a6ade3aaf14 100644 --- a/drivers/input/misc/ab8500-ponkey.c +++ b/drivers/input/misc/ab8500-ponkey.c @@ -19,6 +19,11 @@ struct ab8500_ponkey_variant { const char *irq_rising; }; +static const struct ab8500_ponkey_variant ab5500_onswa = { + .irq_falling = "ONSWAn_falling", + .irq_rising = "ONSWAn_rising", +}; + static const struct ab8500_ponkey_variant ab8500_ponkey = { .irq_falling = "ONKEY_DBF", .irq_rising = "ONKEY_DBR", @@ -145,6 +150,7 @@ static int __devexit ab8500_ponkey_remove(struct platform_device *pdev) } static struct platform_device_id ab8500_ponkey_id_table[] = { + { "ab5500-onswa", (kernel_ulong_t)&ab5500_onswa, }, { "ab8500-poweron-key", (kernel_ulong_t)&ab8500_ponkey, }, { }, }; -- cgit v1.2.3 From ca9ca0065c00f9f42a362423d7b1b13a2c5a224c Mon Sep 17 00:00:00 2001 From: Mattias Wallin Date: Thu, 5 May 2011 10:28:24 +0200 Subject: input: ab8500-accdetect suspend modification This patch disable the AccDetect comparator and pull-up in supend to save 100uA idle current. The comparator and pull-up is restored in resume. Signed-off-by: Mattias Wallin ST-Ericsson ID: 335994 ST-Ericsson FOSS-OUT ID: Trivial ST-Ericsson Linux next: na Change-Id: I81be240d06731e828f435eab9c967776c7cd00e7 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/22395 Reviewed-by: QATOOLS Tested-by: Mattias WALLIN Reviewed-by: QATEST Reviewed-by: Jonas ABERG --- drivers/input/misc/ab8500-accdet.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/input/misc/ab8500-accdet.c b/drivers/input/misc/ab8500-accdet.c index 77634d51c7a..837bb304d4d 100644 --- a/drivers/input/misc/ab8500-accdet.c +++ b/drivers/input/misc/ab8500-accdet.c @@ -1273,6 +1273,8 @@ static int __devexit ab8500_acc_detect_remove(struct platform_device *pdev) } #if defined(CONFIG_PM) +static u8 acc_det_ctrl_suspend_val; + static int ab8500_acc_detect_suspend(struct platform_device *pdev, pm_message_t state) { @@ -1284,6 +1286,17 @@ static int ab8500_acc_detect_suspend(struct platform_device *pdev, cancel_delayed_work_sync(&dd->detect_work); cancel_delayed_work_sync(&dd->init_work); + /* Turn off AccDetect comparators and pull-up */ + (void) abx500_get_register_interruptible( + &dd->pdev->dev, + AB8500_ECI_AV_ACC, + AB8500_ACC_DET_CTRL_REG, + &acc_det_ctrl_suspend_val); + (void) abx500_set_register_interruptible( + &dd->pdev->dev, + AB8500_ECI_AV_ACC, + AB8500_ACC_DET_CTRL_REG, + 0); return 0; } @@ -1293,6 +1306,13 @@ static int ab8500_acc_detect_resume(struct platform_device *pdev) dev_dbg(&dd->pdev->dev, "%s: Enter\n", __func__); + /* Turn on AccDetect comparators and pull-up */ + (void) abx500_set_register_interruptible( + &dd->pdev->dev, + AB8500_ECI_AV_ACC, + AB8500_ACC_DET_CTRL_REG, + acc_det_ctrl_suspend_val); + /* After resume, reinitialize */ dd->gpio35_dir_set = dd->accdet1_th_set = dd->accdet2_th_set = 0; queue_delayed_work(dd->irq_work_queue, &dd->init_work, 0); -- cgit v1.2.3 From b79ccfbb8d0daa99920e2d8eb209f8ad9f243231 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Wed, 12 Oct 2011 15:41:42 +0200 Subject: input:ab8500-accdet: Add accessory detect for hrefv60 On board hrefv60 and recent one there is change in the voltage levels observed ( compared hrefpv50 board ) when a accessory is connected/disconnected or on press/release of a button on the accessory. patch takes care of this. ST-Ericsson ID: ER334414 Change-Id: I2df990e79f3d6a812c9d3e5b18c6b24142aa46c9 Signed-off-by: Virupax Sadashivpetimath Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/22128 Reviewed-by: Srinidhi KASAGAR --- arch/arm/mach-ux500/include/mach/ab8500-accdet.h | 3 ++ drivers/input/misc/ab8500-accdet.c | 43 ++++++++++++++++++++++-- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-ux500/include/mach/ab8500-accdet.h b/arch/arm/mach-ux500/include/mach/ab8500-accdet.h index 5742d7b797a..b1b157e317e 100644 --- a/arch/arm/mach-ux500/include/mach/ab8500-accdet.h +++ b/arch/arm/mach-ux500/include/mach/ab8500-accdet.h @@ -82,12 +82,15 @@ * @btn_keycode Keycode to be sent when accessory button is pressed. * @accdet1_dbth Debounce time + voltage threshold for accdet 1 input. * @accdet2122_th Voltage thresholds for accdet21 and accdet22 inputs. + * @is_detection_inverted Whether the accessory insert/removal, button + * press/release irq's are inverted. */ struct ab8500_accdet_platform_data { int btn_keycode; u8 accdet1_dbth; u8 accdet2122_th; unsigned int video_ctrl_gpio; + bool is_detection_inverted; }; #endif /* _AB8500_ACCDET_H */ diff --git a/drivers/input/misc/ab8500-accdet.c b/drivers/input/misc/ab8500-accdet.c index 837bb304d4d..b6ddb1fa4d4 100644 --- a/drivers/input/misc/ab8500-accdet.c +++ b/drivers/input/misc/ab8500-accdet.c @@ -379,7 +379,7 @@ static struct accessory_regu_descriptor regu_desc[3] = { }, }; -static struct accessory_irq_descriptor irq_desc[] = { +static struct accessory_irq_descriptor irq_desc_norm[] = { { .irq = PLUG_IRQ, .name = "ACC_DETECT_1DB_F", @@ -402,6 +402,31 @@ static struct accessory_irq_descriptor irq_desc[] = { }, }; +static struct accessory_irq_descriptor irq_desc_inverted[] = { + { + .irq = PLUG_IRQ, + .name = "ACC_DETECT_1DB_R", + .isr = plug_irq_handler, + }, + { + .irq = UNPLUG_IRQ, + .name = "ACC_DETECT_1DB_F", + .isr = unplug_irq_handler, + }, + { + .irq = BUTTON_PRESS_IRQ, + .name = "ACC_DETECT_22DB_R", + .isr = button_press_irq_handler, + }, + { + .irq = BUTTON_RELEASE_IRQ, + .name = "ACC_DETECT_22DB_F", + .isr = button_release_irq_handler, + }, +}; + +static struct accessory_irq_descriptor *irq_desc; + /* * textual represenation of the accessory type */ @@ -642,6 +667,7 @@ out: return; static int detect_plugged_in(struct ab8500_ad *dd) { u8 value = 0; + int status = abx500_get_register_interruptible( &dd->pdev->dev, AB8500_INTERRUPT, @@ -653,7 +679,10 @@ static int detect_plugged_in(struct ab8500_ad *dd) return 0; } - return value & BIT_ITSOURCE5_ACCDET1 ? 0 : 1; + if (dd->pdata->is_detection_inverted) + return value & BIT_ITSOURCE5_ACCDET1 ? 1 : 0; + else + return value & BIT_ITSOURCE5_ACCDET1 ? 0 : 1; } /* @@ -1022,6 +1051,11 @@ static void claim_irq(struct ab8500_ad *dd, enum accessory_irq irq_id) int ret; int irq; + if (dd->pdata->is_detection_inverted) + irq_desc = irq_desc_inverted; + else + irq_desc = irq_desc_norm; + if (irq_desc[irq_id].registered) return; @@ -1061,6 +1095,11 @@ static void release_irq(struct ab8500_ad *dd, enum accessory_irq irq_id) { int irq; + if (dd->pdata->is_detection_inverted) + irq_desc = irq_desc_inverted; + else + irq_desc = irq_desc_norm; + if (!irq_desc[irq_id].registered) return; -- cgit v1.2.3 From f1eaa47c48a6e4b19fad72aeb87b681fd392cf75 Mon Sep 17 00:00:00 2001 From: Virupax Sadashivpetimath Date: Sat, 7 May 2011 21:13:10 +0530 Subject: ab8500-accdet: Handle interrupts on comparator disable/enable Writing to the accessory control register to enable and disable the comparator results in the accessory detection interrupts, which results in un-intentional play of the music. Solve this by disabling interrupts in suspend and enabling them back in resume. ST-Ericsson ID: ER338639 Change-Id: Ie5b585b05e2ccda4fd3f0f42f94d61ecd4be2409 Signed-off-by: Virupax Sadashivpetimath Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/22598 Reviewed-by: Srinidhi KASAGAR --- drivers/input/misc/ab8500-accdet.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/drivers/input/misc/ab8500-accdet.c b/drivers/input/misc/ab8500-accdet.c index b6ddb1fa4d4..c249de60eac 100644 --- a/drivers/input/misc/ab8500-accdet.c +++ b/drivers/input/misc/ab8500-accdet.c @@ -1318,6 +1318,7 @@ static int ab8500_acc_detect_suspend(struct platform_device *pdev, pm_message_t state) { struct ab8500_ad *dd = platform_get_drvdata(pdev); + int irq_id, irq; dev_dbg(&dd->pdev->dev, "%s: Enter\n", __func__); @@ -1325,6 +1326,21 @@ static int ab8500_acc_detect_suspend(struct platform_device *pdev, cancel_delayed_work_sync(&dd->detect_work); cancel_delayed_work_sync(&dd->init_work); + if (dd->pdata->is_detection_inverted) + irq_desc = irq_desc_inverted; + else + irq_desc = irq_desc_norm; + + for (irq_id = 0; irq_id < ARRAY_SIZE(irq_desc_norm); irq_id++) { + if (irq_desc[irq_id].registered == 1) { + irq = platform_get_irq_byname( + dd->pdev, + irq_desc[irq_id].name); + + disable_irq(irq); + } + } + /* Turn off AccDetect comparators and pull-up */ (void) abx500_get_register_interruptible( &dd->pdev->dev, @@ -1342,6 +1358,7 @@ static int ab8500_acc_detect_suspend(struct platform_device *pdev, static int ab8500_acc_detect_resume(struct platform_device *pdev) { struct ab8500_ad *dd = platform_get_drvdata(pdev); + int irq_id, irq; dev_dbg(&dd->pdev->dev, "%s: Enter\n", __func__); @@ -1352,6 +1369,22 @@ static int ab8500_acc_detect_resume(struct platform_device *pdev) AB8500_ACC_DET_CTRL_REG, acc_det_ctrl_suspend_val); + if (dd->pdata->is_detection_inverted) + irq_desc = irq_desc_inverted; + else + irq_desc = irq_desc_norm; + + for (irq_id = 0; irq_id < ARRAY_SIZE(irq_desc_norm); irq_id++) { + if (irq_desc[irq_id].registered == 1) { + irq = platform_get_irq_byname( + dd->pdev, + irq_desc[irq_id].name); + + enable_irq(irq); + + } + } + /* After resume, reinitialize */ dd->gpio35_dir_set = dd->accdet1_th_set = dd->accdet2_th_set = 0; queue_delayed_work(dd->irq_work_queue, &dd->init_work, 0); -- cgit v1.2.3 From 9027cff5107a273269dfe2e79a84af3c19fee76d Mon Sep 17 00:00:00 2001 From: Jonas Aaberg Date: Wed, 18 May 2011 13:43:48 +0200 Subject: input; ab8500 accdet: Fix sparse warnings ST-Ericsson Linux next: Not tested, ask SSM for ER ST-Ericsson ID: - ST-Ericsson FOSS-OUT ID: Trivial Signed-off-by: Jonas Aaberg Change-Id: I44dde2edc3b04f8b2cd0a2f6c83a9c1609777042 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/23292 Reviewed-by: QATEST Reviewed-by: Mattias WALLIN --- drivers/input/misc/ab8500-accdet.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/input/misc/ab8500-accdet.c b/drivers/input/misc/ab8500-accdet.c index c249de60eac..bb2748b7668 100644 --- a/drivers/input/misc/ab8500-accdet.c +++ b/drivers/input/misc/ab8500-accdet.c @@ -559,7 +559,7 @@ out: /* * configures accdet1 input on/off */ -void config_accdetect1_hw(struct ab8500_ad *dd, int enable) +static void config_accdetect1_hw(struct ab8500_ad *dd, int enable) { int ret; @@ -628,7 +628,7 @@ out: /* * reports jack status */ -void report_jack_status(struct ab8500_ad *dd) +static void report_jack_status(struct ab8500_ad *dd) { int value = 0; @@ -1189,7 +1189,7 @@ static void init_work(struct work_struct *work) /* * performs platform device initialization */ -int ab8500_accessory_init(struct platform_device *pdev) +static int ab8500_accessory_init(struct platform_device *pdev) { struct ab8500_ad *dd; struct ab8500_platform_data *plat; @@ -1277,7 +1277,7 @@ fail_no_mem_for_devdata: /* * Performs platform device cleanup */ -void ab8500_accessory_cleanup(struct ab8500_ad *dd) +static void ab8500_accessory_cleanup(struct ab8500_ad *dd) { dev_dbg(&dd->pdev->dev, "Enter: %s\n", __func__); -- cgit v1.2.3 From c8d7d9ca9ea6c74d28d3c9023e683d5ed7e0b8fd Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Thu, 19 May 2011 15:07:39 +0530 Subject: db5500-keypad: fix ROW_SHIFT ROW_SHIFT is based on the number of columns, and should be 3 since there are only 8 columns. This also prevents this driver from writing beyond its allocated memory. ST-Ericsson Linux next: - ST-Ericsson ID: WP257121 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I9cb8e08ee6a008760071fd411fefde33f20cf9af Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/23385 Tested-by: Rabin VINCENT Reviewed-by: Naveen Kumar GADDIPATI Reviewed-by: Srinidhi KASAGAR --- drivers/input/keyboard/db5500_keypad.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/keyboard/db5500_keypad.c b/drivers/input/keyboard/db5500_keypad.c index 8767b7f81ba..8a127eaa94c 100644 --- a/drivers/input/keyboard/db5500_keypad.c +++ b/drivers/input/keyboard/db5500_keypad.c @@ -37,7 +37,7 @@ #define KEYPAD_MAX_ROWS 9 #define KEYPAD_MAX_COLS 8 -#define KEYPAD_ROW_SHIFT 4 +#define KEYPAD_ROW_SHIFT 3 #define KEYPAD_KEYMAP_SIZE \ (KEYPAD_MAX_ROWS * KEYPAD_MAX_COLS) -- cgit v1.2.3 From cac25e7d6d6f1765efd3f88865b333423b972bc8 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Fri, 14 Oct 2011 11:12:33 +0200 Subject: u5500:Docbook:Added the tmpl files for drivers Added the tmpl files for keypad and touchpad drivers with repect to kernel doc compliance. ST-Ericsson Linux next: - ST-Ericsson ID: AP 277198 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: Ie704c10ff44f2b5b7f3806252cbcdd3d0f91f849 Signed-off-by: Naveen Kumar Gaddipati Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/23735 Reviewed-by: QATEST Reviewed-by: Srinidhi KASAGAR Conflicts: Documentation/DocBook/Makefile --- Documentation/DocBook/db5500_keypad.tmpl | 91 +++++++++++++++++++ Documentation/DocBook/synaptics_rmi4_touchp.tmpl | 106 +++++++++++++++++++++++ drivers/input/keyboard/db5500_keypad.c | 85 +++++++++++++++++- drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c | 4 +- 4 files changed, 281 insertions(+), 5 deletions(-) create mode 100644 Documentation/DocBook/db5500_keypad.tmpl create mode 100644 Documentation/DocBook/synaptics_rmi4_touchp.tmpl diff --git a/Documentation/DocBook/db5500_keypad.tmpl b/Documentation/DocBook/db5500_keypad.tmpl new file mode 100644 index 00000000000..a25fc990516 --- /dev/null +++ b/Documentation/DocBook/db5500_keypad.tmpl @@ -0,0 +1,91 @@ + + + + + + DB5500 Keypad + + + + NaveenKumar + Gaddipati + +
+ naveen.gaddipati@stericsson.com +
+
+
+
+ + + 2010 + ST-Ericsson + + + + + Linux standard functions + + + + + + License terms: GNU General Public License (GPL) version 2. + + + +
+ + + + + Introduction + + This documentation describes the API provided by the keypad + driver for internal keypad. + + + + + Known Bugs And Assumptions + + + + None + + + None. + + + + + + + + + Public Functions Provided + + This db5500-keypad driver doesn't export any functions. + + + + + Structures + + This chapter contains the autogenerated documentation of the + structures which are used in the keypad driver. + +!Iarch/arm/mach-ux500/include/mach/db5500-keypad.h + + + + Internal Functions Provided + + This chapter contains the autogenerated documentation of the + internal functions. + +!Idrivers/input/keyboard/db5500_keypad.c + + +
diff --git a/Documentation/DocBook/synaptics_rmi4_touchp.tmpl b/Documentation/DocBook/synaptics_rmi4_touchp.tmpl new file mode 100644 index 00000000000..bc104eb4840 --- /dev/null +++ b/Documentation/DocBook/synaptics_rmi4_touchp.tmpl @@ -0,0 +1,106 @@ + + + + + + Synaptics RMI4 Touch screen + + + + Naveen Kumar + Gaddipati + +
+ naveen.gaddipati@stericsson.com +
+
+
+
+ + + 2010 + ST-Ericsson + + + + + Linux standard functions + + + + + + + + This documentation is free software; you can redistribute + it and/or modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later + version. + + + + This program is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + MA 02111-1307 USA + + + + For more details see the file COPYING in the source + distribution of Linux. + + +
+ + + + + Introduction + + This documentation describes the functions provided by the + driver of touch panel for Synaptics RMI4 controller + + + + + Known Bugs And Assumptions + + + + None + + + None. + + + + + + + + + Public Functions Provided + + Not Applicable. + + + + + Internal Functions Provided + + This chapter contains the autogenerated documentation of the internal + functions of the Tocuh panel driver. + +!Idrivers/staging/ste_rmi4/synaptics_i2c_rmi4.c + + +
diff --git a/drivers/input/keyboard/db5500_keypad.c b/drivers/input/keyboard/db5500_keypad.c index 8a127eaa94c..a53d72e8ab4 100644 --- a/drivers/input/keyboard/db5500_keypad.c +++ b/drivers/input/keyboard/db5500_keypad.c @@ -61,11 +61,19 @@ struct db5500_keypad { u8 previous_set[KEYPAD_MAX_ROWS]; }; -/* +/** + * db5500_keypad_report() - reports the keypad event + * @keypad: pointer to device structure + * @row: row value of keypad + * @curr: current event + * @previous: previous event + * + * This function uses to reports the event of the keypad + * and returns NONE. + * * By default all column reads are 1111 1111b. Any press will pull the column * down, leading to a 0 in any of these locations. We invert these values so - * that a 1 means means "column pressed". - * + * that a 1 means means "column pressed". * * If curr changes from the previous from 0 to 1, we report it as a key press. * If curr changes from the previous from 1 to 0, we report it as a key * release. @@ -89,6 +97,14 @@ static void db5500_keypad_report(struct db5500_keypad *keypad, int row, } } +/** + * db5500_keypad_irq() - irq handler for keypad + * @irq: irq value for keypad + * @dev_id: pointer for device id + * + * This function uses to handle the interrupt of the keypad + * and returns irqreturn. + */ static irqreturn_t db5500_keypad_irq(int irq, void *dev_id) { struct db5500_keypad *keypad = dev_id; @@ -161,6 +177,15 @@ again: return IRQ_HANDLED; } +/** + * db5500_keypad_writel() - write into keypad registers + * @keypad: pointer to device structure + * @val: value to write into register + * @reg: register offset + * + * This function uses to write into the keypad registers + * and returns NONE. + */ static void db5500_keypad_writel(struct db5500_keypad *keypad, u32 val, u32 reg) { int timeout = 4; @@ -192,6 +217,13 @@ static void db5500_keypad_writel(struct db5500_keypad *keypad, u32 val, u32 reg) writel(val, keypad->base + reg); } +/** + * db5500_keypad_chip_init() - initialize the keypad chip + * @keypad: pointer to device structure + * + * This function uses to initializes the keypad controller + * and returns integer. + */ static int db5500_keypad_chip_init(struct db5500_keypad *keypad) { int debounce = keypad->board->debounce_ms; @@ -222,6 +254,13 @@ static int db5500_keypad_chip_init(struct db5500_keypad *keypad) return 0; } +/** + * db5500_keypad_close() - stops the keypad driver + * @keypad: pointer to device structure + * + * This function uses to stop the keypad + * driver and returns integer. + */ static void db5500_keypad_close(struct db5500_keypad *keypad) { db5500_keypad_writel(keypad, 0, KEYPAD_CTR); @@ -230,6 +269,13 @@ static void db5500_keypad_close(struct db5500_keypad *keypad) clk_disable(keypad->clk); } +/** + * db5500_keypad_probe() - Initialze the the keypad driver + * @pdev: pointer to platform device structure + * + * This function will allocate and initialize the instance + * data and request the irq and register to input subsystem driver. + */ static int __devinit db5500_keypad_probe(struct platform_device *pdev) { const struct db5500_keypad_platform_data *plat; @@ -362,6 +408,13 @@ out_ret: return ret; } +/** + * db5500_keypad_remove() - Removes the keypad driver + * @pdev: pointer to platform device structure + * + * This function uses to remove the keypad + * driver and returns integer. + */ static int __devexit db5500_keypad_remove(struct platform_device *pdev) { struct db5500_keypad *keypad = platform_get_drvdata(pdev); @@ -381,6 +434,13 @@ static int __devexit db5500_keypad_remove(struct platform_device *pdev) } #ifdef CONFIG_PM +/** + * db5500_keypad_suspend() - suspend the keypad controller + * @dev: pointer to device structure + * + * This function is used to suspend the + * keypad controller and returns integer + */ static int db5500_keypad_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); @@ -397,6 +457,13 @@ static int db5500_keypad_suspend(struct device *dev) return 0; } +/** + * db5500_keypad_resume() - resume the keypad controller + * @dev: pointer to device structure + * + * This function is used to resume the keypad + * controller and returns integer. + */ static int db5500_keypad_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); @@ -431,12 +498,24 @@ static struct platform_driver db5500_keypad_driver = { .remove = __devexit_p(db5500_keypad_remove), }; +/** + * db5500_keypad_init() - Initialize the keypad driver + * + * This function uses to initializes the db5500 + * keypad driver and returns integer. + */ static int __init db5500_keypad_init(void) { return platform_driver_register(&db5500_keypad_driver); } module_init(db5500_keypad_init); +/** + * db5500_keypad_exit() - De-initialize the keypad driver + * + * This function uses to de-initialize the db5500 + * keypad driver and returns none. + */ static void __exit db5500_keypad_exit(void) { platform_driver_unregister(&db5500_keypad_driver); diff --git a/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c b/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c index 2659ea1d70d..e8afb75590a 100644 --- a/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c +++ b/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c @@ -1053,8 +1053,8 @@ static void synaptics_rmi4_resume_handler(struct work_struct *work) /** * synaptics_rmi4_probe() - Initialze the i2c-client touchscreen driver - * @i2c: i2c client structure pointer - * @id:i2c device id pointer + * @client: i2c client structure pointer + * @dev_id:i2c device id pointer * * This function will allocate and initialize the instance * data and request the irq and set the instance data as the clients -- cgit v1.2.3 From dcb94cfaf72194ee2ba6f65c958afee8b0db8d35 Mon Sep 17 00:00:00 2001 From: Robert Marklund Date: Thu, 23 Jun 2011 15:16:10 +0200 Subject: input: ske: Fix for gpio header move Signed-off-by: Robert Marklund --- drivers/input/keyboard/nomadik-ske-keypad.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c index a779e8ca6fe..8c492429c54 100644 --- a/drivers/input/keyboard/nomadik-ske-keypad.c +++ b/drivers/input/keyboard/nomadik-ske-keypad.c @@ -23,7 +23,7 @@ #include #include -#include +#include /* SKE_CR bits */ #define SKE_KPMLT (0x1 << 6) -- cgit v1.2.3 From 6791fbdb1aeefc86a817fb3280615b315a815c0e Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Fri, 22 Jul 2011 11:57:36 +0200 Subject: gpio: nomadik: Rename AB8500_GPIO by GPIO_AB8500 To be compliant with future gpio framework Signed-off-by: Philippe Langlais --- drivers/input/misc/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 7842fdcb238..340d0aec249 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -24,7 +24,7 @@ config INPUT_88PM860X_ONKEY config INPUT_AB8500_ACCDET bool "AB8500 AV Accessory detection" - depends on AB8500_CORE && AB8500_GPADC && AB8500_GPIO + depends on AB8500_CORE && AB8500_GPADC && GPIO_AB8500 help Say Y here to enable AV accessory detection features for ST-Ericsson's AB8500 Mix-Sig PMIC. -- cgit v1.2.3 From 676981e301b647f7c5a2b7acabae816a981647fb Mon Sep 17 00:00:00 2001 From: Sundar Iyer Date: Mon, 4 Oct 2010 12:42:23 +0530 Subject: input: fix STMPE keypad input device name by removing spaces Android layer mangles up spaces in the input device name; hence renaming the device name by introducing a '-' ST-Ericsson ID: ER316265/ER269804 (old) Change-Id: I6940f747e910d58b855c2d7e5c67c2bbe4fb5513 Reported-by: Imran SIDDIQUE Signed-off-by: Sundar Iyer Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/12729 Reviewed-by: Srinidhi KASAGAR --- drivers/input/keyboard/stmpe-keypad.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/keyboard/stmpe-keypad.c b/drivers/input/keyboard/stmpe-keypad.c index fb3427d5ff7..892335275dd 100644 --- a/drivers/input/keyboard/stmpe-keypad.c +++ b/drivers/input/keyboard/stmpe-keypad.c @@ -327,7 +327,7 @@ static int __devinit stmpe_keypad_probe(struct platform_device *pdev) goto out_freekeypad; } - input->name = "STMPE keypad"; + input->name = "STMPE-keypad"; input->id.bustype = BUS_I2C; input->dev.parent = &pdev->dev; -- cgit v1.2.3 From ea043bc06721198bd6161fdb8178b7eec8c38abc Mon Sep 17 00:00:00 2001 From: Rajagopala V Date: Thu, 25 Aug 2011 16:57:56 +0530 Subject: u5500: ponkey: shutdown time configuration support for compile time configuration of poweron key shutdown time. Default is 10sec and can be set to 5sec or disabled ST-Ericsson Linux next: NA ST-Ericsson ID: WP257125 ST-Ericsson FOSS-OUT ID: NA Change-Id: I7fbf57d21ff218370dba24182716222c354f4dc5 Signed-off-by: Rajagopala V Tested-by: Rajagopala VENKATARAVANAPPA X Reviewed-by: Srinidhi KASAGAR Signed-off-by: Robert Marklund --- drivers/input/misc/ab8500-ponkey.c | 52 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/drivers/input/misc/ab8500-ponkey.c b/drivers/input/misc/ab8500-ponkey.c index a6ade3aaf14..60471ba3086 100644 --- a/drivers/input/misc/ab8500-ponkey.c +++ b/drivers/input/misc/ab8500-ponkey.c @@ -13,15 +13,28 @@ #include #include #include +#include + +/* Ponkey time control bits */ +#define AB5500_MCB 0x2F +#define AB5500_PONKEY_10SEC 0x0 +#define AB5500_PONKEY_5SEC 0x1 +#define AB5500_PONKEY_DISABLE 0x2 +#define AB5500_PONKEY_TMR_MASK 0x1 +#define AB5500_PONKEY_TR_MASK 0x2 + +static int ab5500_ponkey_hw_init(struct platform_device *); struct ab8500_ponkey_variant { const char *irq_falling; const char *irq_rising; + int (*hw_init)(struct platform_device *); }; static const struct ab8500_ponkey_variant ab5500_onswa = { .irq_falling = "ONSWAn_falling", .irq_rising = "ONSWAn_rising", + .hw_init = ab5500_ponkey_hw_init, }; static const struct ab8500_ponkey_variant ab8500_ponkey = { @@ -41,6 +54,37 @@ struct ab8500_ponkey_info { int irq_dbr; }; +static int ab5500_ponkey_hw_init(struct platform_device *pdev) +{ + u8 val; + struct ab5500_ponkey_platform_data *pdata; + + pdata = pdev->dev.platform_data; + if (pdata) { + switch (pdata->shutdown_secs) { + case 0: + val = AB5500_PONKEY_DISABLE; + break; + case 5: + val = AB5500_PONKEY_5SEC; + break; + case 10: + val = AB5500_PONKEY_10SEC; + break; + default: + val = AB5500_PONKEY_10SEC; + } + } else { + val = AB5500_PONKEY_10SEC; + } + return abx500_mask_and_set( + &pdev->dev, + AB5500_BANK_STARTUP, + AB5500_MCB, + AB5500_PONKEY_TMR_MASK | AB5500_PONKEY_TR_MASK, + val); +} + /* AB8500 gives us an interrupt when ONKEY is held */ static irqreturn_t ab8500_ponkey_handler(int irq, void *data) { @@ -65,6 +109,14 @@ static int __devinit ab8500_ponkey_probe(struct platform_device *pdev) variant = (const struct ab8500_ponkey_variant *) pdev->id_entry->driver_data; + if (variant->hw_init) { + ret = variant->hw_init(pdev); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to init hw"); + return ret; + } + } + irq_dbf = platform_get_irq_byname(pdev, variant->irq_falling); if (irq_dbf < 0) { dev_err(&pdev->dev, "No IRQ for %s: %d\n", -- cgit v1.2.3 From 2465ff97362698b2495a10bde67b5de1d292cd5b Mon Sep 17 00:00:00 2001 From: Virupax Sadashivpetimath Date: Thu, 7 Jul 2011 16:25:59 +0530 Subject: ab8500-accdet: Disable vamic1 in suspend Since the button press detection is not needed in suspend, disable the V-amic1 incase of a Headset connected to the board. ST-Ericsson ID: 351150 ST-Ericsson Linux next: - ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I24fcaddcd404605d19ea113fd1d2bbf888e2fb62 Signed-off-by: Virupax Sadashivpetimath Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/26726 Reviewed-by: QATEST Reviewed-by: Srinidhi KASAGAR --- drivers/input/misc/ab8500-accdet.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/input/misc/ab8500-accdet.c b/drivers/input/misc/ab8500-accdet.c index bb2748b7668..21d00b82165 100644 --- a/drivers/input/misc/ab8500-accdet.c +++ b/drivers/input/misc/ab8500-accdet.c @@ -1352,6 +1352,10 @@ static int ab8500_acc_detect_suspend(struct platform_device *pdev, AB8500_ECI_AV_ACC, AB8500_ACC_DET_CTRL_REG, 0); + + if (dd->jack_type == JACK_TYPE_HEADSET) + accessory_regulator_disable(REGULATOR_VAMIC1); + return 0; } @@ -1362,6 +1366,9 @@ static int ab8500_acc_detect_resume(struct platform_device *pdev) dev_dbg(&dd->pdev->dev, "%s: Enter\n", __func__); + if (dd->jack_type == JACK_TYPE_HEADSET) + accessory_regulator_enable(REGULATOR_VAMIC1); + /* Turn on AccDetect comparators and pull-up */ (void) abx500_set_register_interruptible( &dd->pdev->dev, -- cgit v1.2.3 From 96a1ed9c7f2e7685b1309c3b313fa064931cb543 Mon Sep 17 00:00:00 2001 From: Chethan Krishna N Date: Wed, 6 Jul 2011 12:30:13 +0530 Subject: db5500_keypad: check return values from all calls checking if NULL is returned from any call of platform_get_resource function. ST-Ericsson ID: 348573 ST-Ericsson FOSS-OUT ID: NA ST-Ericsson Linux next: Not tested Change-Id: I9e26e30cf075d6a5d85a23306d6c85212a20cc40 Signed-off-by: Chethan Krishna N Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/26641 Reviewed-by: Srinidhi KASAGAR Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/29580 Reviewed-by: Naga RADHESH Y Tested-by: Naga RADHESH Y --- drivers/input/keyboard/db5500_keypad.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/input/keyboard/db5500_keypad.c b/drivers/input/keyboard/db5500_keypad.c index a53d72e8ab4..8ef1bd43b7f 100644 --- a/drivers/input/keyboard/db5500_keypad.c +++ b/drivers/input/keyboard/db5500_keypad.c @@ -427,7 +427,10 @@ static int __devexit db5500_keypad_remove(struct platform_device *pdev) clk_put(keypad->clk); iounmap(keypad->base); - release_mem_region(res->start, resource_size(res)); + + if (res) + release_mem_region(res->start, resource_size(res)); + kfree(keypad); return 0; -- cgit v1.2.3 From 333ea0ba6544a8b83095e33e211c3249c5439bcc Mon Sep 17 00:00:00 2001 From: Marcin Mielczarczyk Date: Tue, 30 Aug 2011 10:40:09 +0530 Subject: vibrator: Force feedback vibrator driver Implementation of ST-Ericsson's force feedback vibrator driver. ST-Ericsson ID: WP261766 Ref: Commit-id: 3f5213a6837ad774628c20b006958183b2c1932a Signed-off-by: Avinash A Change-Id: Iadea56106111af2f7b05b3f74a4aa58cda6721b1 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/29682 Reviewed-by: Avinash A Tested-by: Avinash A --- Documentation/DocBook/ste_ff_vibra.tmpl | 217 +++++++++++++++++++++++++++++ drivers/input/misc/Kconfig | 10 ++ drivers/input/misc/Makefile | 1 + drivers/input/misc/ste_ff_vibra.c | 234 ++++++++++++++++++++++++++++++++ 4 files changed, 462 insertions(+) create mode 100644 Documentation/DocBook/ste_ff_vibra.tmpl create mode 100644 drivers/input/misc/ste_ff_vibra.c diff --git a/Documentation/DocBook/ste_ff_vibra.tmpl b/Documentation/DocBook/ste_ff_vibra.tmpl new file mode 100644 index 00000000000..cf5f159bed0 --- /dev/null +++ b/Documentation/DocBook/ste_ff_vibra.tmpl @@ -0,0 +1,217 @@ + + + + + Force Feedback Vibrator Driver + + + + Marcin + Mielczarczyk + +
+ marcin.mielczarczyk@tieto.com +
+
+
+
+ + + 2010 + ST-Ericsson + + + + + Linux standard functions + + + + + + + This documentation is free software; you can redistribute + it and/or modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later + version. + + + + This program is distributed in the hope that it will be + useful, but WITHOUT ANY WARRANTY; without even the implied + warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the GNU General Public License for more details. + + + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, + MA 02111-1307 USA + + + + For more details see the file COPYING in the source + distribution of Linux. + + +
+ + + + + Introduction + + This documentation describes the implementation of ST-Ericsson's + Force Feedback Vibrator driver for the ST-Ericsson Linux platforms. + + + + + Getting Started + + There are no special compilation flags needed to build the + Force Feedback Vibrator driver. + + +
+ Basic Tutorial + + To enable the Force Feedback Vibrator driver using Kconfig, go to + Device Drivers -> Input Device Support -> Miscellaneous devices + and enable the following: + + ST-Ericsson Force Feedback Vibrator + + +
+ +
+ + + Concepts + + Vibrator driver registers as memless force feedback input device. + + + + + Driver Configuration and Interaction + + There are no configuration parameters for Force Feedback Vibrator driver. + +
+ Implemented operations in driver + + All available operations are provided by Memless Input Device class driver. + + + + Supported device driver operations + + open Calls ste_ff_vibra_open() function which initializaes workqueue + close Calls ste_ff_vibra_close() function which cancels and destroys workqueue + +
+
+
+
+ Driver loading parameters + + Not Applicable. + +
+
+ Driver IO Control + + Not Applicable. + +
+ +
+ Driver Interaction with Sysfs + + Not Applicable. + +
+
+ Driver Interaction using /proc filesystem + + Not Applicable. + +
+ +
+ Other means for Driver Interaction + + Not Applicable. + +
+ +
+ Driver Node File + + Force Feedback Vibrator driver provides following node files: + + + + eventX - Force Feedback Vibrator node file + + + + File + /dev/input/eventX + + + Description + + Node file of Force Feedback Vibrator driver + + + + + + + +
+ + +
+ + + + Known Bugs And Assumptions + + + + None. + + + + + + + + + + + + + Public Functions Provided + + Not Applicable. + + + + + + Internal Functions Provided + + This chapter contains the autogenerated documentation of the internal functions. + +!Edrivers/input/misc/ste_ff_vibra.c + + +
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 340d0aec249..1339e97a647 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -576,4 +576,14 @@ config INPUT_XEN_KBDDEV_FRONTEND To compile this driver as a module, choose M here: the module will be called xen-kbdfront. +config INPUT_STE_FF_VIBRA + tristate "ST-Ericsson Force Feedback Vibrator" + depends on STE_AUDIO_IO_DEV + select INPUT_FF_MEMLESS + help + This option enables support for ST-Ericsson's Vibrator which + registers as an input force feedback driver. + + To compile this driver as a module, choose M here. The module will + be called ste_ff_vibra. endif diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index aca455b733d..f64914025ac 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -54,3 +54,4 @@ obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o obj-$(CONFIG_INPUT_YEALINK) += yealink.o +obj-$(CONFIG_INPUT_STE_FF_VIBRA) += ste_ff_vibra.o diff --git a/drivers/input/misc/ste_ff_vibra.c b/drivers/input/misc/ste_ff_vibra.c new file mode 100644 index 00000000000..9038e6be046 --- /dev/null +++ b/drivers/input/misc/ste_ff_vibra.c @@ -0,0 +1,234 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Marcin Mielczarczyk + * for ST-Ericsson + * License Terms: GNU General Public License v2 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define FF_VIBRA_DOWN 0x0000 /* 0 degrees */ +#define FF_VIBRA_LEFT 0x4000 /* 90 degrees */ +#define FF_VIBRA_UP 0x8000 /* 180 degrees */ +#define FF_VIBRA_RIGHT 0xC000 /* 270 degrees */ + +/** + * struct vibra_info - Vibrator information structure + * @idev: Pointer to input device structure + * @vibra_workqueue: Pointer to vibrator workqueue structure + * @vibra_work: Vibrator work + * @direction: Vibration direction + * @speed: Vibration speed + * + * Structure vibra_info holds vibrator informations + **/ +struct vibra_info { + struct input_dev *idev; + struct workqueue_struct *vibra_workqueue; + struct work_struct vibra_work; + int direction; + unsigned char speed; +}; + +/** + * vibra_play_work() - Vibrator work, sets speed and direction + * @work: Pointer to work structure + * + * This function is called from workqueue, turns on/off vibrator + **/ +static void vibra_play_work(struct work_struct *work) +{ + struct vibra_info *vinfo = container_of(work, + struct vibra_info, vibra_work); + struct ste_vibra_speed left_speed = { + .positive = 0, + .negative = 0, + }; + struct ste_vibra_speed right_speed = { + .positive = 0, + .negative = 0, + }; + + /* Divide by 2 because supported range by PWM is 0-100 */ + vinfo->speed /= 2; + + if ((vinfo->direction > FF_VIBRA_DOWN) && + (vinfo->direction < FF_VIBRA_UP)) { + /* 1 - 179 degrees, turn on left vibrator */ + left_speed.positive = vinfo->speed; + } else if (vinfo->direction > FF_VIBRA_UP) { + /* more than 180 degrees, turn on right vibrator */ + right_speed.positive = vinfo->speed; + } else { + /* 0 (down) or 180 (up) degrees, turn on 2 vibrators */ + left_speed.positive = vinfo->speed; + right_speed.positive = vinfo->speed; + } + + ste_audioio_vibrator_pwm_control(STE_AUDIOIO_CLIENT_FF_VIBRA, + left_speed, right_speed); +} + +/** + * vibra_play() - Memless device control function + * @idev: Pointer to input device structure + * @data: Pointer to private data (not used) + * @effect: Pointer to force feedback effect structure + * + * This function controls memless device + * + * Returns: + * 0 - success + **/ +static int vibra_play(struct input_dev *idev, void *data, + struct ff_effect *effect) +{ + struct vibra_info *vinfo = input_get_drvdata(idev); + + vinfo->direction = effect->direction; + vinfo->speed = effect->u.rumble.strong_magnitude >> 8; + if (!vinfo->speed) + /* Shift weak magnitude to make it feelable on vibrator */ + vinfo->speed = effect->u.rumble.weak_magnitude >> 9; + + queue_work(vinfo->vibra_workqueue, &vinfo->vibra_work); + + return 0; +} + +/** + * ste_ff_vibra_open() - Input device open function + * @idev: Pointer to input device structure + * + * This function is called on opening input device + * + * Returns: + * -ENOMEM - no memory left + * 0 - success + **/ +static int ste_ff_vibra_open(struct input_dev *idev) +{ + struct vibra_info *vinfo = input_get_drvdata(idev); + + vinfo->vibra_workqueue = + create_singlethread_workqueue("ste_ff-ff-vibra"); + if (!vinfo->vibra_workqueue) { + dev_err(&idev->dev, "couldn't create vibra workqueue\n"); + return -ENOMEM; + } + return 0; +} + +/** + * ste_ff_vibra_close() - Input device close function + * @idev: Pointer to input device structure + * + * This function is called on closing input device + **/ +static void ste_ff_vibra_close(struct input_dev *idev) +{ + struct vibra_info *vinfo = input_get_drvdata(idev); + + cancel_work_sync(&vinfo->vibra_work); + INIT_WORK(&vinfo->vibra_work, vibra_play_work); + destroy_workqueue(vinfo->vibra_workqueue); + vinfo->vibra_workqueue = NULL; +} + +static int __devinit ste_ff_vibra_probe(struct platform_device *pdev) +{ + struct vibra_info *vinfo; + int ret; + + vinfo = kmalloc(sizeof *vinfo, GFP_KERNEL); + if (!vinfo) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + + vinfo->idev = input_allocate_device(); + if (!vinfo->idev) { + dev_err(&pdev->dev, "failed to allocate input device\n"); + ret = -ENOMEM; + goto exit_vinfo_free; + } + + vinfo->idev->name = "ste-ff-vibra"; + vinfo->idev->dev.parent = pdev->dev.parent; + vinfo->idev->open = ste_ff_vibra_open; + vinfo->idev->close = ste_ff_vibra_close; + INIT_WORK(&vinfo->vibra_work, vibra_play_work); + __set_bit(FF_RUMBLE, vinfo->idev->ffbit); + + ret = input_ff_create_memless(vinfo->idev, NULL, vibra_play); + if (ret) { + dev_err(&pdev->dev, "failed to create memless device\n"); + goto exit_idev_free; + } + + ret = input_register_device(vinfo->idev); + if (ret) { + dev_err(&pdev->dev, "failed to register input device\n"); + goto exit_destroy_memless; + } + + input_set_drvdata(vinfo->idev, vinfo); + platform_set_drvdata(pdev, vinfo); + return 0; + +exit_destroy_memless: + input_ff_destroy(vinfo->idev); +exit_idev_free: + input_free_device(vinfo->idev); +exit_vinfo_free: + kfree(vinfo); + return ret; +} + +static int __devexit ste_ff_vibra_remove(struct platform_device *pdev) +{ + struct vibra_info *vinfo = platform_get_drvdata(pdev); + + /* + * Function device_release() will call input_dev_release() + * which will free ff and input device. No need to call + * input_ff_destroy() and input_free_device() explicitly. + */ + input_unregister_device(vinfo->idev); + kfree(vinfo); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver ste_ff_vibra_driver = { + .driver = { + .name = "ste_ff_vibra", + .owner = THIS_MODULE, + }, + .probe = ste_ff_vibra_probe, + .remove = __devexit_p(ste_ff_vibra_remove) +}; + +static int __init ste_ff_vibra_init(void) +{ + return platform_driver_register(&ste_ff_vibra_driver); +} +module_init(ste_ff_vibra_init); + +static void __exit ste_ff_vibra_exit(void) +{ + platform_driver_unregister(&ste_ff_vibra_driver); +} +module_exit(ste_ff_vibra_exit); + +MODULE_AUTHOR("Marcin Mielczarczyk "); +MODULE_DESCRIPTION("STE Force Feedback Vibrator Driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 02052e890fa875b4d7489dc2b84a05eebcf9ebf0 Mon Sep 17 00:00:00 2001 From: Chethan Krishna N Date: Tue, 30 Aug 2011 15:28:29 +0530 Subject: db5500_keypad: handle regulator in apsleep db5500_keypad doesn't block APSleep during CPU Idle proper implementation of regluator framework. Key presses are also detected using manual scan. ST-Ericsson ID: 342613 ST-Ericsson FOSS-OUT ID: NA ST-Ericsson Linux next : Not tested, NA Change-Id: I9e42c56f8f2f270ce0197b035222aa9d7fecdb70 Signed-off-by: Chethan Krishna N Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/26050 Reviewed-by: Srinidhi KASAGAR Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/29724 Reviewed-by: Naga RADHESH Y Tested-by: Naga RADHESH Y --- arch/arm/mach-ux500/include/mach/db5500-keypad.h | 12 + drivers/input/keyboard/db5500_keypad.c | 308 ++++++++++++++++++++--- 2 files changed, 288 insertions(+), 32 deletions(-) diff --git a/arch/arm/mach-ux500/include/mach/db5500-keypad.h b/arch/arm/mach-ux500/include/mach/db5500-keypad.h index 66b4c07f838..8db5a05017b 100644 --- a/arch/arm/mach-ux500/include/mach/db5500-keypad.h +++ b/arch/arm/mach-ux500/include/mach/db5500-keypad.h @@ -10,16 +10,28 @@ #include +#define KEYPAD_MAX_ROWS 9 +#define KEYPAD_MAX_COLS 8 /** * struct db5500_keypad_platform_data - structure for platform specific data * @keymap_data: matrix scan code table for keycodes * @debounce_ms: platform specific debounce time * @no_autorepeat: flag for auto repetition + * @init : pointer to keypad init function + * @exit : pointer to keypad exit function + * @gpio_input_pins: pointer to gpio input pins + * @gpio_output_pins: pointer to gpio output pins + * @switch_delay : gpio switch_delay */ struct db5500_keypad_platform_data { const struct matrix_keymap_data *keymap_data; u8 debounce_ms; bool no_autorepeat; + int (*init)(void); + int (*exit)(void); + int *gpio_input_pins; + int *gpio_output_pins; + int switch_delay; }; #endif diff --git a/drivers/input/keyboard/db5500_keypad.c b/drivers/input/keyboard/db5500_keypad.c index 8ef1bd43b7f..61cda09cacf 100644 --- a/drivers/input/keyboard/db5500_keypad.c +++ b/drivers/input/keyboard/db5500_keypad.c @@ -15,6 +15,7 @@ #include #include #include +#include #define KEYPAD_CTR 0x0 #define KEYPAD_IRQ_CLEAR 0x4 @@ -35,12 +36,11 @@ #define KEYPAD_GND_ROW 8 -#define KEYPAD_MAX_ROWS 9 -#define KEYPAD_MAX_COLS 8 #define KEYPAD_ROW_SHIFT 3 #define KEYPAD_KEYMAP_SIZE \ (KEYPAD_MAX_ROWS * KEYPAD_MAX_COLS) +#define KEY_PRESSED_DELAY 10 /** * struct db5500_keypad - data structure used by keypad driver * @irq: irq number @@ -49,7 +49,17 @@ * @board: keypad platform data * @keymap: matrix scan code table for keycodes * @clk: clock structure pointer + * @regulator : regulator used by keypad + * @switch_work : delayed work variable for switching to gpio + * @gpio_work : delayed work variable for reporting key event in gpio mode * @previous_set: previous set of registers + * @enable : flag to enable the driver event + * @valid_key : hold the state of valid key press + * @db5500_rows : rows gpio array for db5500 keypad + * @db5500_cols : cols gpio array for db5500 keypad + * @gpio_input_irq : array for gpio irqs + * @gpio_row : gpio row + * @gpio_col : gpio_col */ struct db5500_keypad { int irq; @@ -58,7 +68,17 @@ struct db5500_keypad { const struct db5500_keypad_platform_data *board; unsigned short keymap[KEYPAD_KEYMAP_SIZE]; struct clk *clk; + struct regulator *regulator; + struct delayed_work switch_work; + struct delayed_work gpio_work; u8 previous_set[KEYPAD_MAX_ROWS]; + bool enable; + bool valid_key; + int db5500_rows[KEYPAD_MAX_ROWS - 1]; + int db5500_cols[KEYPAD_MAX_COLS]; + int gpio_input_irq[KEYPAD_MAX_ROWS - 1]; + int gpio_row; + int gpio_col; }; /** @@ -97,17 +117,8 @@ static void db5500_keypad_report(struct db5500_keypad *keypad, int row, } } -/** - * db5500_keypad_irq() - irq handler for keypad - * @irq: irq value for keypad - * @dev_id: pointer for device id - * - * This function uses to handle the interrupt of the keypad - * and returns irqreturn. - */ -static irqreturn_t db5500_keypad_irq(int irq, void *dev_id) +static void db5500_keypad_scan(struct db5500_keypad *keypad) { - struct db5500_keypad *keypad = dev_id; u8 current_set[ARRAY_SIZE(keypad->previous_set)]; int tries = 100; bool changebit; @@ -121,7 +132,7 @@ static irqreturn_t db5500_keypad_irq(int irq, void *dev_id) again: if (!tries--) { dev_warn(&keypad->input->dev, "values failed to stabilize\n"); - return IRQ_HANDLED; + return; } changebit = readl(keypad->base + KEYPAD_ARRAY_01) @@ -154,7 +165,7 @@ again: common &= current_set[i]; if ((allrows & common) != common) - return IRQ_HANDLED; + return; for (i = 0; i < ARRAY_SIZE(current_set); i++) { /* @@ -174,7 +185,7 @@ again: /* update the reference set of array registers */ memcpy(keypad->previous_set, current_set, sizeof(keypad->previous_set)); - return IRQ_HANDLED; + return; } /** @@ -254,19 +265,172 @@ static int db5500_keypad_chip_init(struct db5500_keypad *keypad) return 0; } -/** - * db5500_keypad_close() - stops the keypad driver - * @keypad: pointer to device structure - * - * This function uses to stop the keypad - * driver and returns integer. - */ -static void db5500_keypad_close(struct db5500_keypad *keypad) +static void db5500_mode_enable(struct db5500_keypad *keypad, bool enable) { - db5500_keypad_writel(keypad, 0, KEYPAD_CTR); - db5500_keypad_writel(keypad, 0, KEYPAD_INT_ENABLE); + int i; - clk_disable(keypad->clk); + if (!enable) { + db5500_keypad_writel(keypad, 0, KEYPAD_CTR); + db5500_keypad_writel(keypad, 0, KEYPAD_INT_ENABLE); + if (keypad->board->exit) + keypad->board->exit(); + for (i = 0; i < KEYPAD_MAX_ROWS - 1; i++) { + enable_irq(keypad->gpio_input_irq[i]); + enable_irq_wake(keypad->gpio_input_irq[i]); + } + clk_disable(keypad->clk); + regulator_disable(keypad->regulator); + } else { + regulator_enable(keypad->regulator); + clk_enable(keypad->clk); + for (i = 0; i < KEYPAD_MAX_ROWS - 1; i++) { + disable_irq_nosync(keypad->gpio_input_irq[i]); + disable_irq_wake(keypad->gpio_input_irq[i]); + } + if (keypad->board->init) + keypad->board->init(); + db5500_keypad_chip_init(keypad); + } +} + +static void db5500_gpio_switch_work(struct work_struct *work) +{ + struct db5500_keypad *keypad = container_of(work, + struct db5500_keypad, switch_work.work); + + db5500_mode_enable(keypad, false); + keypad->enable = false; +} + +static void db5500_gpio_release_work(struct work_struct *work) +{ + int code; + struct db5500_keypad *keypad = container_of(work, + struct db5500_keypad, gpio_work.work); + struct input_dev *input = keypad->input; + + code = MATRIX_SCAN_CODE(keypad->gpio_col, keypad->gpio_row, + KEYPAD_ROW_SHIFT); + input_event(input, EV_MSC, MSC_SCAN, code); + input_report_key(input, keypad->keymap[code], 1); + input_sync(input); + input_report_key(input, keypad->keymap[code], 0); + input_sync(input); +} + +static int db5500_read_get_gpio_row(struct db5500_keypad *keypad) +{ + int row; + int value = 0; + int ret; + + /* read all rows GPIO data register values */ + for (row = 0; row < KEYPAD_MAX_ROWS - 1; row++) { + ret = gpio_get_value(keypad->db5500_rows[row]); + value += (1 << row) * ret; + } + + /* get the exact row */ + for (row = 0; row < KEYPAD_MAX_ROWS - 1; row++) { + if (((1 << row) & value) == 0) + return row; + } + + return -1; +} + +static void db5500_set_cols(struct db5500_keypad *keypad, int col) +{ + int i ; + int value; + + /* + * Set all columns except the requested column + * output pin as high + */ + for (i = 0; i < KEYPAD_MAX_COLS; i++) { + if (i == col) + value = 0; + else + value = 1; + gpio_request(keypad->db5500_cols[i], "db5500-kpd"); + gpio_direction_output(keypad->db5500_cols[i], value); + gpio_free(keypad->db5500_cols[i]); + } +} + +static void db5500_free_cols(struct db5500_keypad *keypad) +{ + int i ; + + for (i = 0; i < KEYPAD_MAX_COLS; i++) { + gpio_request(keypad->db5500_cols[i], "db5500-kpd"); + gpio_direction_output(keypad->db5500_cols[i], 0); + gpio_free(keypad->db5500_cols[i]); + } +} + +static void db5500_manual_scan(struct db5500_keypad *keypad) +{ + int row; + int col; + + keypad->valid_key = false; + + for (col = 0; col < KEYPAD_MAX_COLS; col++) { + db5500_set_cols(keypad, col); + row = db5500_read_get_gpio_row(keypad); + if (row >= 0) { + keypad->valid_key = true; + keypad->gpio_row = row; + keypad->gpio_col = col; + break; + } + } + db5500_free_cols(keypad); +} + +static irqreturn_t db5500_keypad_gpio_irq(int irq, void *dev_id) +{ + struct db5500_keypad *keypad = dev_id; + + if (!gpio_get_value(IRQ_TO_GPIO(irq))) { + db5500_manual_scan(keypad); + if (!keypad->enable) { + keypad->enable = true; + db5500_mode_enable(keypad, true); + } + + /* + * Schedule the work queue to change it to + * report the key pressed, if it is not detected in keypad mode. + */ + if (keypad->valid_key) { + schedule_delayed_work(&keypad->gpio_work, + KEY_PRESSED_DELAY); + } + } + + return IRQ_HANDLED; +} + +static irqreturn_t db5500_keypad_irq(int irq, void *dev_id) +{ + struct db5500_keypad *keypad = dev_id; + + cancel_delayed_work_sync(&keypad->gpio_work); + cancel_delayed_work_sync(&keypad->switch_work); + db5500_keypad_scan(keypad); + + /* + * Schedule the work queue to change it to + * GPIO mode, if there is no activity in keypad mode + */ + if (keypad->enable) + schedule_delayed_work(&keypad->switch_work, + keypad->board->switch_delay); + + return IRQ_HANDLED; } /** @@ -278,7 +442,7 @@ static void db5500_keypad_close(struct db5500_keypad *keypad) */ static int __devinit db5500_keypad_probe(struct platform_device *pdev) { - const struct db5500_keypad_platform_data *plat; + struct db5500_keypad_platform_data *plat; struct db5500_keypad *keypad; struct resource *res; struct input_dev *input; @@ -286,6 +450,7 @@ static int __devinit db5500_keypad_probe(struct platform_device *pdev) struct clk *clk; int ret; int irq; + int i; plat = pdev->dev.platform_data; if (!plat) { @@ -343,6 +508,20 @@ static int __devinit db5500_keypad_probe(struct platform_device *pdev) goto out_freekeypad; } + keypad->regulator = regulator_get(&pdev->dev, "v-ape"); + if (IS_ERR(keypad->regulator)) { + dev_err(&pdev->dev, "regulator_get failed\n"); + keypad->regulator = NULL; + ret = -EINVAL; + goto out_regulator_get; + } else { + ret = regulator_enable(keypad->regulator); + if (ret < 0) { + dev_err(&pdev->dev, "regulator_enable failed\n"); + goto out_regulator_enable; + } + } + input->id.bustype = BUS_HOST; input->name = "db5500-keypad"; input->dev.parent = &pdev->dev; @@ -373,12 +552,56 @@ static int __devinit db5500_keypad_probe(struct platform_device *pdev) keypad->base = base; keypad->clk = clk; - ret = db5500_keypad_chip_init(keypad); - if (ret < 0) { - dev_err(&pdev->dev, "unable to init keypad hardware\n"); + INIT_DELAYED_WORK(&keypad->switch_work, db5500_gpio_switch_work); + INIT_DELAYED_WORK(&keypad->gpio_work, db5500_gpio_release_work); + + clk_enable(keypad->clk); +if (!keypad->board->init) { + dev_err(&pdev->dev, "init funtion not defined\n"); + ret = -EINVAL; goto out_unregisterinput; } + if (keypad->board->init() < 0) { + dev_err(&pdev->dev, "keyboard init config failed\n"); + ret = -EINVAL; + goto out_unregisterinput; + } + + if (!keypad->board->exit) { + dev_err(&pdev->dev, "exit funtion not defined\n"); + ret = -EINVAL; + goto out_unregisterinput; + } + + if (keypad->board->exit() < 0) { + dev_err(&pdev->dev, "keyboard exit config failed\n"); + ret = -EINVAL; + goto out_unregisterinput; + } + + for (i = 0; i < KEYPAD_MAX_ROWS - 1; i++) { + keypad->db5500_rows[i] = *plat->gpio_input_pins; + keypad->db5500_cols[i] = *plat->gpio_output_pins; + keypad->gpio_input_irq[i] = + GPIO_TO_IRQ(keypad->db5500_rows[i]); + plat->gpio_input_pins++; + plat->gpio_output_pins++; + } + + for (i = 0; i < KEYPAD_MAX_ROWS - 1; i++) { + ret = request_threaded_irq(keypad->gpio_input_irq[i], + NULL, db5500_keypad_gpio_irq, + IRQF_TRIGGER_FALLING | IRQF_NO_SUSPEND, + "db5500-keypad-gpio", keypad); + if (ret) { + dev_err(&pdev->dev, "allocate gpio irq %d failed\n", + keypad->gpio_input_irq[i]); + goto out_unregisterinput; + } + enable_irq_wake(keypad->gpio_input_irq[i]); + } + ret = request_threaded_irq(keypad->irq, NULL, db5500_keypad_irq, IRQF_ONESHOT, "db5500-keypad", keypad); if (ret) { @@ -388,6 +611,8 @@ static int __devinit db5500_keypad_probe(struct platform_device *pdev) platform_set_drvdata(pdev, keypad); + clk_disable(keypad->clk); + regulator_disable(keypad->regulator); return 0; out_unregisterinput: @@ -396,6 +621,10 @@ out_unregisterinput: clk_disable(keypad->clk); out_freeinput: input_free_device(input); +out_regulator_enable: + regulator_put(keypad->regulator); +out_regulator_get: + input_free_device(input); out_freekeypad: kfree(keypad); out_freeclk: @@ -420,12 +649,19 @@ static int __devexit db5500_keypad_remove(struct platform_device *pdev) struct db5500_keypad *keypad = platform_get_drvdata(pdev); struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + cancel_delayed_work_sync(&keypad->gpio_work); + cancel_delayed_work_sync(&keypad->switch_work); free_irq(keypad->irq, keypad); input_unregister_device(keypad->input); clk_disable(keypad->clk); clk_put(keypad->clk); + if (keypad->board->exit) + keypad->board->exit(); + + regulator_put(keypad->regulator); + iounmap(keypad->base); if (res) @@ -453,8 +689,13 @@ static int db5500_keypad_suspend(struct device *dev) if (device_may_wakeup(dev)) enable_irq_wake(irq); else { + cancel_delayed_work_sync(&keypad->gpio_work); + cancel_delayed_work_sync(&keypad->switch_work); disable_irq(irq); - db5500_keypad_close(keypad); + if (keypad->enable) { + db5500_mode_enable(keypad, false); + keypad->enable = false; + } } return 0; @@ -476,7 +717,10 @@ static int db5500_keypad_resume(struct device *dev) if (device_may_wakeup(dev)) disable_irq_wake(irq); else { - db5500_keypad_chip_init(keypad); + if (!keypad->enable) { + keypad->enable = true; + db5500_mode_enable(keypad, true); + } enable_irq(irq); } -- cgit v1.2.3 From 56abac8f926d4fe4c5677851fa6d4af404e2b917 Mon Sep 17 00:00:00 2001 From: Chethan Krishna N Date: Tue, 26 Jul 2011 12:37:10 +0530 Subject: db5500_keypad: don't configure all keypad pins Some keypad pins are not connected to keypad controller, do not configure them. ST-Ericsson ID: 353260 ST-Ericsson FOSS-OUT ID: NA ST-Ericsson Linux next: NA Change-Id: I4943a3febe4ad38c4e7322638c49ac91d838271a Signed-off-by: Chethan Krishna N Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/27885 Reviewed-by: QATOOLS Reviewed-by: QATEST Reviewed-by: Srinidhi KASAGAR Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/29725 Reviewed-by: Naga RADHESH Y Tested-by: Naga RADHESH Y --- arch/arm/mach-ux500/include/mach/db5500-keypad.h | 5 +++++ drivers/input/keyboard/db5500_keypad.c | 25 +++++++++++++----------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/arch/arm/mach-ux500/include/mach/db5500-keypad.h b/arch/arm/mach-ux500/include/mach/db5500-keypad.h index 8db5a05017b..d9d23419ab3 100644 --- a/arch/arm/mach-ux500/include/mach/db5500-keypad.h +++ b/arch/arm/mach-ux500/include/mach/db5500-keypad.h @@ -12,6 +12,7 @@ #define KEYPAD_MAX_ROWS 9 #define KEYPAD_MAX_COLS 8 + /** * struct db5500_keypad_platform_data - structure for platform specific data * @keymap_data: matrix scan code table for keycodes @@ -19,6 +20,8 @@ * @no_autorepeat: flag for auto repetition * @init : pointer to keypad init function * @exit : pointer to keypad exit function + * @krow : maximum number of rows + * @kcol : maximum number of cols * @gpio_input_pins: pointer to gpio input pins * @gpio_output_pins: pointer to gpio output pins * @switch_delay : gpio switch_delay @@ -29,6 +32,8 @@ struct db5500_keypad_platform_data { bool no_autorepeat; int (*init)(void); int (*exit)(void); + u8 krow; + u8 kcol; int *gpio_input_pins; int *gpio_output_pins; int switch_delay; diff --git a/drivers/input/keyboard/db5500_keypad.c b/drivers/input/keyboard/db5500_keypad.c index 61cda09cacf..b1f069ea825 100644 --- a/drivers/input/keyboard/db5500_keypad.c +++ b/drivers/input/keyboard/db5500_keypad.c @@ -74,9 +74,9 @@ struct db5500_keypad { u8 previous_set[KEYPAD_MAX_ROWS]; bool enable; bool valid_key; - int db5500_rows[KEYPAD_MAX_ROWS - 1]; + int db5500_rows[KEYPAD_MAX_ROWS]; int db5500_cols[KEYPAD_MAX_COLS]; - int gpio_input_irq[KEYPAD_MAX_ROWS - 1]; + int gpio_input_irq[KEYPAD_MAX_ROWS]; int gpio_row; int gpio_col; }; @@ -274,7 +274,7 @@ static void db5500_mode_enable(struct db5500_keypad *keypad, bool enable) db5500_keypad_writel(keypad, 0, KEYPAD_INT_ENABLE); if (keypad->board->exit) keypad->board->exit(); - for (i = 0; i < KEYPAD_MAX_ROWS - 1; i++) { + for (i = 0; i < keypad->board->krow; i++) { enable_irq(keypad->gpio_input_irq[i]); enable_irq_wake(keypad->gpio_input_irq[i]); } @@ -283,7 +283,7 @@ static void db5500_mode_enable(struct db5500_keypad *keypad, bool enable) } else { regulator_enable(keypad->regulator); clk_enable(keypad->clk); - for (i = 0; i < KEYPAD_MAX_ROWS - 1; i++) { + for (i = 0; i < keypad->board->krow; i++) { disable_irq_nosync(keypad->gpio_input_irq[i]); disable_irq_wake(keypad->gpio_input_irq[i]); } @@ -325,13 +325,13 @@ static int db5500_read_get_gpio_row(struct db5500_keypad *keypad) int ret; /* read all rows GPIO data register values */ - for (row = 0; row < KEYPAD_MAX_ROWS - 1; row++) { + for (row = 0; row < keypad->board->krow; row++) { ret = gpio_get_value(keypad->db5500_rows[row]); value += (1 << row) * ret; } /* get the exact row */ - for (row = 0; row < KEYPAD_MAX_ROWS - 1; row++) { + for (row = 0; row < keypad->board->krow; row++) { if (((1 << row) & value) == 0) return row; } @@ -363,7 +363,7 @@ static void db5500_free_cols(struct db5500_keypad *keypad) { int i ; - for (i = 0; i < KEYPAD_MAX_COLS; i++) { + for (i = 0; i < keypad->board->kcol; i++) { gpio_request(keypad->db5500_cols[i], "db5500-kpd"); gpio_direction_output(keypad->db5500_cols[i], 0); gpio_free(keypad->db5500_cols[i]); @@ -377,7 +377,7 @@ static void db5500_manual_scan(struct db5500_keypad *keypad) keypad->valid_key = false; - for (col = 0; col < KEYPAD_MAX_COLS; col++) { + for (col = 0; col < keypad->board->kcol; col++) { db5500_set_cols(keypad, col); row = db5500_read_get_gpio_row(keypad); if (row >= 0) { @@ -580,16 +580,19 @@ if (!keypad->board->init) { goto out_unregisterinput; } - for (i = 0; i < KEYPAD_MAX_ROWS - 1; i++) { + for (i = 0; i < keypad->board->krow; i++) { keypad->db5500_rows[i] = *plat->gpio_input_pins; - keypad->db5500_cols[i] = *plat->gpio_output_pins; keypad->gpio_input_irq[i] = GPIO_TO_IRQ(keypad->db5500_rows[i]); plat->gpio_input_pins++; + } + + for (i = 0; i < keypad->board->kcol; i++) { + keypad->db5500_cols[i] = *plat->gpio_output_pins; plat->gpio_output_pins++; } - for (i = 0; i < KEYPAD_MAX_ROWS - 1; i++) { + for (i = 0; i < keypad->board->krow; i++) { ret = request_threaded_irq(keypad->gpio_input_irq[i], NULL, db5500_keypad_gpio_irq, IRQF_TRIGGER_FALLING | IRQF_NO_SUSPEND, -- cgit v1.2.3 From c485fa1859a22f13937d9b51233bf0593e97252e Mon Sep 17 00:00:00 2001 From: Chethan Krishna N Date: Fri, 29 Jul 2011 18:51:57 +0530 Subject: db5500_keypad: check return value of request_gpio check return value everytime request_gpio() is called ST-Ericsson ID: 353939 ST-Ericsson FOSS-OUT ID: NA ST-Ericsson Linux next: NA Change-Id: Idb7c1be6a278e72ee96d4e69b595abb104dfd5a2 Signed-off-by: Chethan Krishna N Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/27989 Reviewed-by: QATOOLS Reviewed-by: QATEST Reviewed-by: Srinidhi KASAGAR Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/29726 Reviewed-by: Naga RADHESH Y Tested-by: Naga RADHESH Y --- drivers/input/keyboard/db5500_keypad.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/input/keyboard/db5500_keypad.c b/drivers/input/keyboard/db5500_keypad.c index b1f069ea825..45d2f652d60 100644 --- a/drivers/input/keyboard/db5500_keypad.c +++ b/drivers/input/keyboard/db5500_keypad.c @@ -341,19 +341,25 @@ static int db5500_read_get_gpio_row(struct db5500_keypad *keypad) static void db5500_set_cols(struct db5500_keypad *keypad, int col) { - int i ; + int i, ret; int value; /* * Set all columns except the requested column * output pin as high */ - for (i = 0; i < KEYPAD_MAX_COLS; i++) { + for (i = 0; i < keypad->board->kcol; i++) { if (i == col) value = 0; else value = 1; - gpio_request(keypad->db5500_cols[i], "db5500-kpd"); + ret = gpio_request(keypad->db5500_cols[i], "db5500-kpd"); + + if (ret < 0) { + pr_err("db5500_set_cols: gpio request failed\n"); + continue; + } + gpio_direction_output(keypad->db5500_cols[i], value); gpio_free(keypad->db5500_cols[i]); } @@ -361,10 +367,16 @@ static void db5500_set_cols(struct db5500_keypad *keypad, int col) static void db5500_free_cols(struct db5500_keypad *keypad) { - int i ; + int i, ret; for (i = 0; i < keypad->board->kcol; i++) { - gpio_request(keypad->db5500_cols[i], "db5500-kpd"); + ret = gpio_request(keypad->db5500_cols[i], "db5500-kpd"); + + if (ret < 0) { + pr_err("db5500_free_cols: gpio request failed\n"); + continue; + } + gpio_direction_output(keypad->db5500_cols[i], 0); gpio_free(keypad->db5500_cols[i]); } -- cgit v1.2.3 From 51f7bbe84cf1a425c3b47302b5cc91fe44ca3169 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Mon, 17 Oct 2011 08:47:48 +0200 Subject: ab5500: move to separate header Change-Id: I11dd2a23323229096b6e0ee2d4dc68cab8919bff --- drivers/input/misc/ab8500-ponkey.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/input/misc/ab8500-ponkey.c b/drivers/input/misc/ab8500-ponkey.c index 60471ba3086..c3c3c51d302 100644 --- a/drivers/input/misc/ab8500-ponkey.c +++ b/drivers/input/misc/ab8500-ponkey.c @@ -14,6 +14,7 @@ #include #include #include +#include /* Ponkey time control bits */ #define AB5500_MCB 0x2F -- cgit v1.2.3 From b3787c823179a63ae957572e45410a074a6ff47f Mon Sep 17 00:00:00 2001 From: Avinash A Date: Fri, 9 Sep 2011 12:09:21 +0530 Subject: touchscreen: Add Cypress driver Add the Cypress SPI based touchscreen for ux500. ST-Ericsson ID: CR321190,279760 ST-Ericsson Linux next: NA ST-Ericsson FOSS-OUT ID:10249 Ref: Commit-ids: I0b25b7e97968901ff747be62e571134ebb6ad3de Id: Iac75daa197826d8a0bb4c1d394a834575bf601eb Id: Iec8c5516768d85f9f8b301984618bc5b394d4927 Change-Id: I56a3a6080a90553a2a8099f2dc243d85a8229688 Signed-off-by: Avinash A Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/30533 Reviewed-by: Avinash A Tested-by: Avinash A --- drivers/input/touchscreen/Kconfig | 17 + drivers/input/touchscreen/Makefile | 4 +- drivers/input/touchscreen/cyttsp_core.c | 2215 +++++++++++++++++++++++++++++++ drivers/input/touchscreen/cyttsp_core.h | 44 + drivers/input/touchscreen/cyttsp_ldr.h | 333 +++++ drivers/input/touchscreen/cyttsp_spi.c | 302 +++++ include/linux/cyttsp.h | 114 ++ 7 files changed, 3028 insertions(+), 1 deletion(-) create mode 100755 drivers/input/touchscreen/cyttsp_core.c create mode 100755 drivers/input/touchscreen/cyttsp_core.h create mode 100755 drivers/input/touchscreen/cyttsp_ldr.h create mode 100755 drivers/input/touchscreen/cyttsp_spi.c create mode 100755 include/linux/cyttsp.h diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 4af2a18eb3b..643b969ae61 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -135,6 +135,23 @@ config TOUCHSCREEN_BU21013 To compile this driver as a module, choose M here: the module will be called bu21013_ts. +config TOUCHSCREEN_CYTTSP_CORE + tristate "Cypress TTSP touchscreen core" + help + Always activated for Cypress TTSP touchscreen. + +config TOUCHSCREEN_CYTTSP_SPI + tristate "Cypress TTSP spi touchscreen" + depends on SPI_MASTER && TOUCHSCREEN_CYTTSP_CORE + help + Say Y here if you have a Cypress TTSP touchscreen + connected to your with an SPI interface. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called cyttsp-spi. + config TOUCHSCREEN_CY8CTMG110 tristate "cy8ctmg110 touchscreen" depends on I2C diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 496091e8846..5b8bdb04f8c 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -17,7 +17,9 @@ obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o obj-$(CONFIG_TOUCHSCREEN_AUO_PIXCIR) += auo-pixcir-ts.o obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o -obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o +obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE) += cyttsp_core.o +obj-$(CONFIG_TOUCHSCREEN_CYTTSP_SPI) += cyttsp_spi.o +bj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o diff --git a/drivers/input/touchscreen/cyttsp_core.c b/drivers/input/touchscreen/cyttsp_core.c new file mode 100755 index 00000000000..076417439ab --- /dev/null +++ b/drivers/input/touchscreen/cyttsp_core.c @@ -0,0 +1,2215 @@ +/* Source for: + * Cypress TrueTouch(TM) Standard Product touchscreen driver. + * drivers/input/touchscreen/cyttsp_core.c + * + * Copyright (C) 2009-2011 Cypress Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Cypress reserves the right to make changes without further notice + * to the materials described herein. Cypress does not assume any + * liability arising out of the application described herein. + * + * Contact Cypress Semiconductor at www.cypress.com + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cyttsp_core.h" + +#define DBG(x) +#define DBG2(x) +#define DBG3(x) + +/* rely on kernel input.h to define Multi-Touch capability */ +#ifndef ABS_MT_TRACKING_ID +/* define only if not defined already by system; */ +/* value based on linux kernel 2.6.30.10 */ +#define ABS_MT_TRACKING_ID (ABS_MT_BLOB_ID + 1) +#endif /* ABS_MT_TRACKING_ID */ + +#define TOUCHSCREEN_TIMEOUT (msecs_to_jiffies(28)) +/* Bootloader File 0 offset */ +#define CY_BL_FILE0 0x00 +/* Bootloader command directive */ +#define CY_BL_CMD 0xFF +/* Bootloader Enter Loader mode */ +#define CY_BL_ENTER 0x38 +/* Bootloader Write a Block */ +#define CY_BL_WRITE_BLK 0x39 +/* Bootloader Terminate Loader mode */ +#define CY_BL_TERMINATE 0x3B +/* Bootloader Exit and Verify Checksum command */ +#define CY_BL_EXIT 0xA5 +/* Bootloader default keys */ +#define CY_BL_KEY0 0 +#define CY_BL_KEY1 1 +#define CY_BL_KEY2 2 +#define CY_BL_KEY3 3 +#define CY_BL_KEY4 4 +#define CY_BL_KEY5 5 +#define CY_BL_KEY6 6 +#define CY_BL_KEY7 7 + +#define CY_DIFF(m, n) ((m) != (n)) +#define GET_NUM_TOUCHES(x) ((x) & 0x0F) +#define GET_TOUCH1_ID(x) (((x) & 0xF0) >> 4) +#define GET_TOUCH2_ID(x) ((x) & 0x0F) +#define GET_TOUCH3_ID(x) (((x) & 0xF0) >> 4) +#define GET_TOUCH4_ID(x) ((x) & 0x0F) +#define IS_LARGE_AREA(x) (((x) & 0x10) >> 4) +#define IS_BAD_PKT(x) ((x) & 0x20) +#define FLIP_DATA_FLAG 0x01 +#define REVERSE_X_FLAG 0x02 +#define REVERSE_Y_FLAG 0x04 +#define FLIP_DATA(flags) ((flags) & FLIP_DATA_FLAG) +#define REVERSE_X(flags) ((flags) & REVERSE_X_FLAG) +#define REVERSE_Y(flags) ((flags) & REVERSE_Y_FLAG) +#define FLIP_XY(x, y) {typeof(x) tmp; tmp = (x); (x) = (y); (y) = tmp; } +#define INVERT_X(x, xmax) ((xmax) - (x)) +#define INVERT_Y(y, ymax) ((ymax) - (y)) +#define SET_HSTMODE(reg, mode) ((reg) & (mode)) +#define GET_HSTMODE(reg) ((reg & 0x70) >> 4) +#define GET_BOOTLOADERMODE(reg) ((reg & 0x10) >> 4) + +/* Watchdog timeout to check if device is reset and no interrupts running */ +#define CY_WDG_TIMEOUT msecs_to_jiffies(3000) +#define CY_MODE_ERROR(ps, hst_mode, tt_mode) \ + ((ps == CY_ACTIVE_STATE && GET_HSTMODE(hst_mode) != CY_OPERATE_MODE) ||\ + GET_BOOTLOADERMODE(tt_mode)) + +/* maximum number of concurrent ST track IDs */ +#define CY_NUM_ST_TCH_ID 2 +/* maximum number of concurrent MT track IDs */ +#define CY_NUM_MT_TCH_ID 4 +/* maximum number of track IDs */ +#define CY_NUM_TRK_ID 16 +/* + * maximum number of concurrent touches + * (only CY_NUM_MT_TCH_ID have coord data) + */ +#define CY_MAX_TCH 10 +/* + * maximum number of touch reports with + * current touches=0 before performing Driver reset + */ +#define CY_MAX_NTCH 10 + +#define CY_NTCH 0 /* lift off */ +#define CY_TCH 1 /* touch down */ +#define CY_ST_FNGR1_IDX 0 +#define CY_ST_FNGR2_IDX 1 +#define CY_MT_TCH1_IDX 0 +#define CY_MT_TCH2_IDX 1 +#define CY_MT_TCH3_IDX 2 +#define CY_MT_TCH4_IDX 3 +#define CY_XPOS 0 +#define CY_YPOS 1 +#define CY_IGNR_TCH (-1) +#define CY_SMALL_TOOL_WIDTH 10 +#define CY_LARGE_TOOL_WIDTH 255 +#define CY_REG_BASE 0x00 +#define CY_REG_GEST_SET 0x1E +#define CY_REG_SCN_TYP 0x1C +#define CY_REG_ACT_INTRVL 0x1D +#define CY_REG_TCH_TMOUT (CY_REG_ACT_INTRVL+1) +#define CY_REG_LP_INTRVL (CY_REG_TCH_TMOUT+1) +#define CY_SOFT_RESET (1 << 0) +#define CY_DEEP_SLEEP (1 << 1) +#define CY_LOW_POWER (1 << 2) +#define CY_MAXZ 255 +#define CY_OK 0 +#define CY_INIT 1 +#define CY_DELAY_DFLT 20 /* ms */ +#define CY_DELAY_MAX (500/CY_DELAY_DFLT) /* half second */ +#define CY_DELAY_SYSINFO 20 /* ms */ +#define CY_DELAY_BL 300 +#define CY_DELAY_DNLOAD 100 /* ms */ +#define CY_HNDSHK_BIT 0x80 +/* device mode bits */ +#define CY_OPERATE_MODE 0x00 +#define CY_SYSINFO_MODE 0x10 +/* power mode select bits */ +#define CY_SOFT_RESET_MODE 0x01 /* return to Bootloader mode */ +#define CY_DEEP_SLEEP_MODE 0x02 +#define CY_LOW_POWER_MODE 0x04 +#define CY_NUM_KEY 8 + +/* TrueTouch Standard Product Gen3 (Txx3xx) interface definition */ +struct cyttsp_xydata { + u8 hst_mode; + u8 tt_mode; + u8 tt_stat; + u16 x1 __attribute__ ((packed)); + u16 y1 __attribute__ ((packed)); + u8 z1; + u8 touch12_id; + u16 x2 __attribute__ ((packed)); + u16 y2 __attribute__ ((packed)); + u8 z2; + u8 gest_cnt; + u8 gest_id; + u16 x3 __attribute__ ((packed)); + u16 y3 __attribute__ ((packed)); + u8 z3; + u8 touch34_id; + u16 x4 __attribute__ ((packed)); + u16 y4 __attribute__ ((packed)); + u8 z4; + u8 tt_undef[3]; + u8 gest_set; + u8 tt_reserved; +}; + +struct cyttsp_xydata_gen2 { + u8 hst_mode; + u8 tt_mode; + u8 tt_stat; + u16 x1 __attribute__ ((packed)); + u16 y1 __attribute__ ((packed)); + u8 z1; + u8 evnt_idx; + u16 x2 __attribute__ ((packed)); + u16 y2 __attribute__ ((packed)); + u8 tt_undef1; + u8 gest_cnt; + u8 gest_id; + u8 tt_undef[14]; + u8 gest_set; + u8 tt_reserved; +}; + +/* TrueTouch Standard Product Gen2 (Txx2xx) interface definition */ +enum cyttsp_gen2_std { + CY_GEN2_NOTOUCH = 0x03, /* Both touches removed */ + CY_GEN2_GHOST = 0x02, /* ghost */ + CY_GEN2_2TOUCH = 0x03, /* 2 touch; no ghost */ + CY_GEN2_1TOUCH = 0x01, /* 1 touch only */ + CY_GEN2_TOUCH2 = 0x01, /* 1st touch removed; 2nd touch remains */ +}; + +/* TTSP System Information interface definition */ +struct cyttsp_sysinfo_data { + u8 hst_mode; + u8 mfg_stat; + u8 mfg_cmd; + u8 cid[3]; + u8 tt_undef1; + u8 uid[8]; + u8 bl_verh; + u8 bl_verl; + u8 tts_verh; + u8 tts_verl; + u8 app_idh; + u8 app_idl; + u8 app_verh; + u8 app_verl; + u8 tt_undef[5]; + u8 scn_typ; /* Gen3 only: scan type [0:Mutual, 1:Self] */ + u8 act_intrvl; + u8 tch_tmout; + u8 lp_intrvl; +}; + +/* TTSP Bootloader Register Map interface definition */ +#define CY_BL_CHKSUM_OK 0x01 +struct cyttsp_bootloader_data { + u8 bl_file; + u8 bl_status; + u8 bl_error; + u8 blver_hi; + u8 blver_lo; + u8 bld_blver_hi; + u8 bld_blver_lo; + u8 ttspver_hi; + u8 ttspver_lo; + u8 appid_hi; + u8 appid_lo; + u8 appver_hi; + u8 appver_lo; + u8 cid_0; + u8 cid_1; + u8 cid_2; +}; + +#define cyttsp_wake_data cyttsp_xydata + +struct cyttsp { + struct device *pdev; + int irq; + struct input_dev *input; + struct work_struct work; + struct timer_list timer; + struct mutex mutex; + struct early_suspend early_suspend; + char phys[32]; + struct cyttsp_platform_data *platform_data; + struct cyttsp_bootloader_data bl_data; + struct cyttsp_sysinfo_data sysinfo_data; + u8 num_prv_st_tch; + u16 act_trk[CY_NUM_TRK_ID]; + u16 prv_mt_tch[CY_NUM_MT_TCH_ID]; + u16 prv_st_tch[CY_NUM_ST_TCH_ID]; + u16 prv_mt_pos[CY_NUM_TRK_ID][2]; + struct cyttsp_bus_ops *bus_ops; + struct regulator *regulator; + unsigned fw_loader_mode:1; + unsigned suspended:1; + struct timer_list to_timer; + bool to_timeout; + struct completion int_running; + bool bl_ready; + u8 reg_id; + u8 ntch_count; +}; + +struct cyttsp_track_data { + u8 prv_tch; + u8 cur_tch; + u16 tmp_trk[CY_NUM_MT_TCH_ID]; + u16 snd_trk[CY_NUM_MT_TCH_ID]; + u16 cur_trk[CY_NUM_TRK_ID]; + u16 cur_st_tch[CY_NUM_ST_TCH_ID]; + u16 cur_mt_tch[CY_NUM_MT_TCH_ID]; + /* if NOT CY_USE_TRACKING_ID then only */ + /* uses CY_NUM_MT_TCH_ID positions */ + u16 cur_mt_pos[CY_NUM_TRK_ID][2]; + /* if NOT CY_USE_TRACKING_ID then only */ + /* uses CY_NUM_MT_TCH_ID positions */ + u8 cur_mt_z[CY_NUM_TRK_ID]; + u8 tool_width; + u16 st_x1; + u16 st_y1; + u8 st_z1; + u16 st_x2; + u16 st_y2; + u8 st_z2; +}; + +static const u8 bl_cmd[] = { + CY_BL_FILE0, CY_BL_CMD, CY_BL_EXIT, + CY_BL_KEY0, CY_BL_KEY1, CY_BL_KEY2, + CY_BL_KEY3, CY_BL_KEY4, CY_BL_KEY5, + CY_BL_KEY6, CY_BL_KEY7 +}; + +#define LOCK(m) do { \ + DBG(printk(KERN_INFO "%s: lock\n", __func__);) \ + mutex_lock(&(m)); \ +} while (0); + +#define UNLOCK(m) do { \ + DBG(printk(KERN_INFO "%s: unlock\n", __func__);) \ + mutex_unlock(&(m)); \ +} while (0); + +DBG( +static void print_data_block(const char *func, u8 command, + u8 length, void *data) +{ + char buf[1024]; + unsigned buf_len = sizeof(buf); + char *p = buf; + int i; + int l; + + l = snprintf(p, buf_len, "cmd 0x%x: ", command); + buf_len -= l; + p += l; + for (i = 0; i < length && buf_len; i++, p += l, buf_len -= l) + l = snprintf(p, buf_len, "%02x ", *((char *)data + i)); + printk(KERN_DEBUG "%s: %s\n", func, buf); +}) + +static int cyttsp_soft_reset(struct cyttsp *ts); +static int cyttsp_set_operational_mode(struct cyttsp *ts); +static int cyttsp_exit_bl_mode(struct cyttsp *ts); +static int cyttsp_power_on(struct cyttsp *ts); +static void cyttsp_init_tch(struct cyttsp *ts) +{ + /* init the touch structures */ + ts->num_prv_st_tch = CY_NTCH; + memset(ts->act_trk, CY_NTCH, sizeof(ts->act_trk)); + memset(ts->prv_mt_pos, CY_NTCH, sizeof(ts->prv_mt_pos)); + memset(ts->prv_mt_tch, CY_IGNR_TCH, sizeof(ts->prv_mt_tch)); + memset(ts->prv_st_tch, CY_IGNR_TCH, sizeof(ts->prv_st_tch)); + ts->ntch_count = 0; +} + +static u8 ttsp_convert_gen2(u8 cur_tch, struct cyttsp_xydata *pxy_data) +{ + struct cyttsp_xydata_gen2 *pxy_data_gen2; + pxy_data_gen2 = (struct cyttsp_xydata_gen2 *)(pxy_data); + + if (pxy_data_gen2->evnt_idx == CY_GEN2_NOTOUCH) { + cur_tch = 0; + } else if (cur_tch == CY_GEN2_GHOST) { + cur_tch = 0; + } else if (cur_tch == CY_GEN2_2TOUCH) { + /* stuff artificial track ID1 and ID2 */ + pxy_data->touch12_id = 0x12; + pxy_data->z1 = CY_MAXZ; + pxy_data->z2 = CY_MAXZ; + cur_tch--; /* 2 touches */ + } else if (cur_tch == CY_GEN2_1TOUCH) { + /* stuff artificial track ID1 and ID2 */ + pxy_data->touch12_id = 0x12; + pxy_data->z1 = CY_MAXZ; + pxy_data->z2 = CY_NTCH; + if (pxy_data_gen2->evnt_idx == CY_GEN2_TOUCH2) { + /* push touch 2 data into touch1 + * (first finger up; second finger down) */ + /* stuff artificial track ID1 for touch2 info */ + pxy_data->touch12_id = 0x20; + /* stuff touch 1 with touch 2 coordinate data */ + pxy_data->x1 = pxy_data->x2; + pxy_data->y1 = pxy_data->y2; + } + } else { + cur_tch = 0; + } + return cur_tch; +} + +static int ttsp_read_block_data(struct cyttsp *ts, u8 command, + u8 length, void *buf) +{ + int rc; + int tries; + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + + if (!buf || !length) { + printk(KERN_ERR "%s: Error, buf:%s len:%u\n", + __func__, !buf ? "NULL" : "OK", length); + return -EIO; + } + + for (tries = 0, rc = -1; tries < CY_NUM_RETRY && (rc < 0); tries++) { + rc = ts->bus_ops->read(ts->bus_ops, command, length, buf); + if (rc) + msleep(CY_DELAY_DFLT); + } + + if (rc < 0) + printk(KERN_ERR "%s: error %d\n", __func__, rc); + DBG(print_data_block(__func__, command, length, buf);) + return rc; +} + +static int ttsp_write_block_data(struct cyttsp *ts, u8 command, + u8 length, void *buf) +{ + int rc; + int tries; + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + + if (!buf || !length) { + printk(KERN_ERR "%s: Error, buf:%s len:%u\n", + __func__, !buf ? "NULL" : "OK", length); + return -EIO; + } + + for (tries = 0, rc = -1; tries < CY_NUM_RETRY && (rc < 0); tries++) { + rc = ts->bus_ops->write(ts->bus_ops, command, length, buf); + if (rc) + msleep(CY_DELAY_DFLT); + } + + if (rc < 0) + printk(KERN_ERR "%s: error %d\n", __func__, rc); + DBG(print_data_block(__func__, command, length, buf);) + return rc; +} + +static int ttsp_tch_ext(struct cyttsp *ts, void *buf) +{ + int rc; + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + + if (!buf) { + printk(KERN_ERR "%s: Error, buf:%s\n", + __func__, !buf ? "NULL" : "OK"); + return -EIO; + } + rc = ts->bus_ops->ext(ts->bus_ops, buf); + if (rc < 0) + printk(KERN_ERR "%s: error %d\n", __func__, rc); + return rc; +} + +/* ************************************************************************ + * The cyttsp_xy_worker function reads the XY coordinates and sends them to + * the input layer. It is called from the Touch Interrupt. + * *************************************************************************/ +static int cyttsp_inlist(u16 prev_track[], u8 cur_trk_id, u8 *prev_loc, + u8 num_touches) +{ + u8 id = 0; + + DBG(printk(KERN_INFO"%s: IN p[%d]=%d c=%d n=%d loc=%d\n", + __func__, id, prev_track[id], cur_trk_id, + num_touches, *prev_loc);) + + for (*prev_loc = CY_IGNR_TCH; id < num_touches; id++) { + DBG(printk(KERN_INFO"%s: p[%d]=%d c=%d n=%d loc=%d\n", + __func__, id, prev_track[id], cur_trk_id, + num_touches, *prev_loc);) + if (prev_track[id] == cur_trk_id) { + *prev_loc = id; + break; + } + } + DBG(printk(KERN_INFO"%s: OUT p[%d]=%d c=%d n=%d loc=%d\n", __func__, + id, prev_track[id], cur_trk_id, num_touches, *prev_loc);) + + return *prev_loc < CY_NUM_TRK_ID; +} + +static int cyttsp_next_avail_inlist(u16 cur_trk[], u8 *new_loc, + u8 num_touches) +{ + u8 id = 0; + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + + for (*new_loc = CY_IGNR_TCH; id < num_touches; id++) { + if (cur_trk[id] > CY_NUM_TRK_ID) { + *new_loc = id; + break; + } + } + return *new_loc < CY_NUM_TRK_ID; +} + +static void handle_single_touch(struct cyttsp_xydata *xy, + struct cyttsp_track_data *t, struct cyttsp *ts) +{ + u8 id; + u8 use_trk_id = ts->platform_data->use_trk_id; + + DBG(printk(KERN_INFO"%s: ST STEP 0 - ST1 ID=%d ST2 ID=%d\n", + __func__, t->cur_st_tch[CY_ST_FNGR1_IDX], + t->cur_st_tch[CY_ST_FNGR2_IDX]);) + + if (t->cur_st_tch[CY_ST_FNGR1_IDX] > CY_NUM_TRK_ID) { + /* reassign finger 1 and 2 positions to new tracks */ + if (t->cur_tch > 0) { + /* reassign st finger1 */ + if (use_trk_id) { + id = CY_MT_TCH1_IDX; + t->cur_st_tch[CY_ST_FNGR1_IDX] = + t->cur_mt_tch[id]; + } else { + id = GET_TOUCH1_ID(xy->touch12_id); + t->cur_st_tch[CY_ST_FNGR1_IDX] = id; + } + t->st_x1 = t->cur_mt_pos[id][CY_XPOS]; + t->st_y1 = t->cur_mt_pos[id][CY_YPOS]; + t->st_z1 = t->cur_mt_z[id]; + + DBG(printk(KERN_INFO"%s: ST STEP 1 - ST1 ID=%3d\n", + __func__, t->cur_st_tch[CY_ST_FNGR1_IDX]);) + + if ((t->cur_tch > 1) && + (t->cur_st_tch[CY_ST_FNGR2_IDX] > + CY_NUM_TRK_ID)) { + /* reassign st finger2 */ + if (use_trk_id) { + id = CY_MT_TCH2_IDX; + t->cur_st_tch[CY_ST_FNGR2_IDX] = + t->cur_mt_tch[id]; + } else { + id = GET_TOUCH2_ID(xy->touch12_id); + t->cur_st_tch[CY_ST_FNGR2_IDX] = id; + } + t->st_x2 = t->cur_mt_pos[id][CY_XPOS]; + t->st_y2 = t->cur_mt_pos[id][CY_YPOS]; + t->st_z2 = t->cur_mt_z[id]; + + DBG( + printk(KERN_INFO"%s: ST STEP 2 - ST2 ID=%3d\n", + __func__, t->cur_st_tch[CY_ST_FNGR2_IDX]);) + } + } + } else if (t->cur_st_tch[CY_ST_FNGR2_IDX] > CY_NUM_TRK_ID) { + if (t->cur_tch > 1) { + /* reassign st finger2 */ + if (use_trk_id) { + /* reassign st finger2 */ + id = CY_MT_TCH2_IDX; + t->cur_st_tch[CY_ST_FNGR2_IDX] = + t->cur_mt_tch[id]; + } else { + /* reassign st finger2 */ + id = GET_TOUCH2_ID(xy->touch12_id); + t->cur_st_tch[CY_ST_FNGR2_IDX] = id; + } + t->st_x2 = t->cur_mt_pos[id][CY_XPOS]; + t->st_y2 = t->cur_mt_pos[id][CY_YPOS]; + t->st_z2 = t->cur_mt_z[id]; + + DBG(printk(KERN_INFO"%s: ST STEP 3 - ST2 ID=%3d\n", + __func__, t->cur_st_tch[CY_ST_FNGR2_IDX]);) + } + } + /* if the 1st touch is missing and there is a 2nd touch, + * then set the 1st touch to 2nd touch and terminate 2nd touch + */ + if ((t->cur_st_tch[CY_ST_FNGR1_IDX] > CY_NUM_TRK_ID) && + (t->cur_st_tch[CY_ST_FNGR2_IDX] < CY_NUM_TRK_ID)) { + t->st_x1 = t->st_x2; + t->st_y1 = t->st_y2; + t->st_z1 = t->st_z2; + t->cur_st_tch[CY_ST_FNGR1_IDX] = t->cur_st_tch[CY_ST_FNGR2_IDX]; + t->cur_st_tch[CY_ST_FNGR2_IDX] = CY_IGNR_TCH; + } + /* if the 2nd touch ends up equal to the 1st touch, + * then just report a single touch */ + if (t->cur_st_tch[CY_ST_FNGR1_IDX] == t->cur_st_tch[CY_ST_FNGR2_IDX]) + t->cur_st_tch[CY_ST_FNGR2_IDX] = CY_IGNR_TCH; + + /* set Single Touch current event signals */ + if (t->cur_st_tch[CY_ST_FNGR1_IDX] < CY_NUM_TRK_ID) { + input_report_abs(ts->input, ABS_X, t->st_x1); + input_report_abs(ts->input, ABS_Y, t->st_y1); + input_report_abs(ts->input, ABS_PRESSURE, t->st_z1); + input_report_key(ts->input, BTN_TOUCH, CY_TCH); + input_report_abs(ts->input, ABS_TOOL_WIDTH, t->tool_width); + + DBG2(printk(KERN_INFO"%s:ST->F1:%3d X:%3d Y:%3d Z:%3d\n", + __func__, t->cur_st_tch[CY_ST_FNGR1_IDX], + t->st_x1, t->st_y1, t->st_z1);) + + if (t->cur_st_tch[CY_ST_FNGR2_IDX] < CY_NUM_TRK_ID) { + input_report_key(ts->input, BTN_2, CY_TCH); + input_report_abs(ts->input, ABS_HAT0X, t->st_x2); + input_report_abs(ts->input, ABS_HAT0Y, t->st_y2); + + DBG2(printk(KERN_INFO + "%s:ST->F2:%3d X:%3d Y:%3d Z:%3d\n", + __func__, t->cur_st_tch[CY_ST_FNGR2_IDX], + t->st_x2, t->st_y2, t->st_z2);) + } else { + input_report_key(ts->input, BTN_2, CY_NTCH); + } + } else { + input_report_abs(ts->input, ABS_PRESSURE, CY_NTCH); + input_report_key(ts->input, BTN_TOUCH, CY_NTCH); + input_report_key(ts->input, BTN_2, CY_NTCH); + } + /* update platform data for the current single touch info */ + ts->prv_st_tch[CY_ST_FNGR1_IDX] = t->cur_st_tch[CY_ST_FNGR1_IDX]; + ts->prv_st_tch[CY_ST_FNGR2_IDX] = t->cur_st_tch[CY_ST_FNGR2_IDX]; + +} + +static void handle_multi_touch(struct cyttsp_track_data *t, struct cyttsp *ts) +{ + + u8 id; + u8 i, loc; + void (*mt_sync_func)(struct input_dev *) = ts->platform_data->mt_sync; + + if (!ts->platform_data->use_trk_id) + goto no_track_id; + + /* terminate any previous touch where the track + * is missing from the current event */ + for (id = 0; id < CY_NUM_TRK_ID; id++) { + if ((ts->act_trk[id] == CY_NTCH) || (t->cur_trk[id] != CY_NTCH)) + continue; + + input_report_abs(ts->input, ABS_MT_TRACKING_ID, id); + input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, CY_NTCH); + input_report_abs(ts->input, ABS_MT_WIDTH_MAJOR, t->tool_width); + input_report_abs(ts->input, ABS_MT_POSITION_X, + ts->prv_mt_pos[id][CY_XPOS]); + input_report_abs(ts->input, ABS_MT_POSITION_Y, + ts->prv_mt_pos[id][CY_YPOS]); + if (mt_sync_func) + mt_sync_func(ts->input); + ts->act_trk[id] = CY_NTCH; + ts->prv_mt_pos[id][CY_XPOS] = 0; + ts->prv_mt_pos[id][CY_YPOS] = 0; + } + /* set Multi-Touch current event signals */ + for (id = 0; id < CY_NUM_MT_TCH_ID; id++) { + if (t->cur_mt_tch[id] >= CY_NUM_TRK_ID) + continue; + + input_report_abs(ts->input, ABS_MT_TRACKING_ID, + t->cur_mt_tch[id]); + input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, + t->cur_mt_z[id]); + input_report_abs(ts->input, ABS_MT_WIDTH_MAJOR, + t->tool_width); + input_report_abs(ts->input, ABS_MT_POSITION_X, + t->cur_mt_pos[id][CY_XPOS]); + input_report_abs(ts->input, ABS_MT_POSITION_Y, + t->cur_mt_pos[id][CY_YPOS]); + if (mt_sync_func) + mt_sync_func(ts->input); + + ts->act_trk[id] = CY_TCH; + ts->prv_mt_pos[id][CY_XPOS] = t->cur_mt_pos[id][CY_XPOS]; + ts->prv_mt_pos[id][CY_YPOS] = t->cur_mt_pos[id][CY_YPOS]; + } + return; +no_track_id: + + /* set temporary track array elements to voids */ + memset(t->tmp_trk, CY_IGNR_TCH, sizeof(t->tmp_trk)); + memset(t->snd_trk, CY_IGNR_TCH, sizeof(t->snd_trk)); + + /* get what is currently active */ + for (i = id = 0; id < CY_NUM_TRK_ID && i < CY_NUM_MT_TCH_ID; id++) { + if (t->cur_trk[id] == CY_TCH) { + /* only incr counter if track found */ + t->tmp_trk[i] = id; + i++; + } + } + DBG(printk(KERN_INFO"%s: T1: t0=%d, t1=%d, t2=%d, t3=%d\n", __func__, + t->tmp_trk[0], t->tmp_trk[1], + t->tmp_trk[2], t->tmp_trk[3]);) + DBG(printk(KERN_INFO"%s: T1: p0=%d, p1=%d, p2=%d, p3=%d\n", __func__, + ts->prv_mt_tch[0], ts->prv_mt_tch[1], + ts->prv_mt_tch[2], ts->prv_mt_tch[3]);) + + /* pack in still active previous touches */ + for (id = t->prv_tch = 0; id < CY_NUM_MT_TCH_ID; id++) { + if (t->tmp_trk[id] >= CY_NUM_TRK_ID) + continue; + + if (cyttsp_inlist(ts->prv_mt_tch, t->tmp_trk[id], &loc, + CY_NUM_MT_TCH_ID)) { + loc &= CY_NUM_MT_TCH_ID - 1; + t->snd_trk[loc] = t->tmp_trk[id]; + t->prv_tch++; + DBG(printk(KERN_INFO"%s: in list s[%d]=%d " + "t[%d]=%d, loc=%d p=%d\n", __func__, + loc, t->snd_trk[loc], + id, t->tmp_trk[id], + loc, t->prv_tch);) + } else { + DBG(printk(KERN_INFO"%s: is not in list s[%d]=%d" + " t[%d]=%d loc=%d\n", __func__, + id, t->snd_trk[id], + id, t->tmp_trk[id], + loc);) + } + } + DBG(printk(KERN_INFO"%s: S1: s0=%d, s1=%d, s2=%d, s3=%d p=%d\n", + __func__, + t->snd_trk[0], t->snd_trk[1], t->snd_trk[2], + t->snd_trk[3], t->prv_tch);) + + /* pack in new touches */ + for (id = 0; id < CY_NUM_MT_TCH_ID; id++) { + if (t->tmp_trk[id] >= CY_NUM_TRK_ID) + continue; + + if (!cyttsp_inlist(t->snd_trk, t->tmp_trk[id], &loc, + CY_NUM_MT_TCH_ID)) { + + DBG( + printk(KERN_INFO"%s: not in list t[%d]=%d, loc=%d\n", + __func__, + id, t->tmp_trk[id], loc);) + + if (cyttsp_next_avail_inlist(t->snd_trk, &loc, + CY_NUM_MT_TCH_ID)) { + loc &= CY_NUM_MT_TCH_ID - 1; + t->snd_trk[loc] = t->tmp_trk[id]; + DBG(printk(KERN_INFO "%s: put in list s[%d]=%d" + " t[%d]=%d\n", __func__, + loc, + t->snd_trk[loc], id, t->tmp_trk[id]); + ) + } + } else { + DBG(printk(KERN_INFO"%s: is in list s[%d]=%d " + "t[%d]=%d loc=%d\n", __func__, + id, t->snd_trk[id], id, t->tmp_trk[id], loc);) + } + } + DBG(printk(KERN_INFO"%s: S2: s0=%d, s1=%d, s2=%d, s3=%d\n", __func__, + t->snd_trk[0], t->snd_trk[1], + t->snd_trk[2], t->snd_trk[3]);) + + /* sync motion event signals for each current touch */ + for (id = 0; id < CY_NUM_MT_TCH_ID; id++) { + /* z will either be 0 (NOTOUCH) or + * some pressure (TOUCH) + */ + DBG(printk(KERN_INFO "%s: MT0 prev[%d]=%d " + "temp[%d]=%d send[%d]=%d\n", + __func__, id, ts->prv_mt_tch[id], + id, t->tmp_trk[id], id, t->snd_trk[id]);) + + if (ts->platform_data->invert) { + t->cur_mt_pos[t->snd_trk[id]][CY_XPOS] = + INVERT_X(t->cur_mt_pos[t->snd_trk[id]] + [CY_XPOS], ts->platform_data->maxx); + t->cur_mt_pos[t->snd_trk[id]][CY_YPOS] = + INVERT_X(t->cur_mt_pos[t->snd_trk[id]] + [CY_YPOS], ts->platform_data->maxy); + } + if (t->snd_trk[id] < CY_NUM_TRK_ID) { + input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, + t->cur_mt_z[t->snd_trk[id]]); + input_report_abs(ts->input, ABS_MT_WIDTH_MAJOR, + t->tool_width); + input_report_abs(ts->input, ABS_MT_POSITION_X, + t->cur_mt_pos[t->snd_trk[id]][CY_XPOS]); + input_report_abs(ts->input, ABS_MT_POSITION_Y, + t->cur_mt_pos[t->snd_trk[id]][CY_YPOS]); + if (mt_sync_func) + mt_sync_func(ts->input); + + DBG2(printk(KERN_INFO"%s: MT1 -> TID:" + "%3d X:%3d Y:%3d Z:%3d\n", __func__, + t->snd_trk[id], + t->cur_mt_pos[t->snd_trk[id]][CY_XPOS], + t->cur_mt_pos[t->snd_trk[id]][CY_YPOS], + t->cur_mt_z[t->snd_trk[id]]);) + + } else if (ts->prv_mt_tch[id] < CY_NUM_TRK_ID) { + /* void out this touch */ + input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, + CY_NTCH); + input_report_abs(ts->input, ABS_MT_WIDTH_MAJOR, + t->tool_width); + input_report_abs(ts->input, ABS_MT_POSITION_X, + ts->prv_mt_pos[ts->prv_mt_tch[id]][CY_XPOS]); + input_report_abs(ts->input, ABS_MT_POSITION_Y, + ts->prv_mt_pos[ts->prv_mt_tch[id]][CY_YPOS]); + + if (mt_sync_func) + mt_sync_func(ts->input); + + DBG2(printk(KERN_INFO"%s: " + "MT2->TID:%2d X:%3d Y:%3d Z:%3d liftoff-sent\n", + __func__, ts->prv_mt_tch[id], + ts->prv_mt_pos[ts->prv_mt_tch[id]][CY_XPOS], + ts->prv_mt_pos[ts->prv_mt_tch[id]][CY_YPOS], + CY_NTCH);) + } else { + /* do not stuff any signals for this + * previously and currently void touches + */ + DBG(printk(KERN_INFO"%s: " + "MT3->send[%d]=%d - No touch - NOT sent\n", + __func__, id, t->snd_trk[id]);) + } + } + + /* save current posted tracks to + * previous track memory */ + for (id = 0; id < CY_NUM_MT_TCH_ID; id++) { + ts->prv_mt_tch[id] = t->snd_trk[id]; + if (t->snd_trk[id] < CY_NUM_TRK_ID) { + ts->prv_mt_pos[t->snd_trk[id]][CY_XPOS] = + t->cur_mt_pos[t->snd_trk[id]][CY_XPOS]; + ts->prv_mt_pos[t->snd_trk[id]][CY_YPOS] = + t->cur_mt_pos[t->snd_trk[id]][CY_YPOS]; + DBG(printk(KERN_INFO"%s: " + "MT4->TID:%2d X:%3d Y:%3d Z:%3d save for prv\n", + __func__, t->snd_trk[id], + ts->prv_mt_pos[t->snd_trk[id]][CY_XPOS], + ts->prv_mt_pos[t->snd_trk[id]][CY_YPOS], + CY_NTCH);) + } + } + memset(ts->act_trk, CY_NTCH, sizeof(ts->act_trk)); + for (id = 0; id < CY_NUM_MT_TCH_ID; id++) { + if (t->snd_trk[id] < CY_NUM_TRK_ID) + ts->act_trk[t->snd_trk[id]] = CY_TCH; + } +} + +static void cyttsp_xy_worker(struct cyttsp *ts) +{ + struct cyttsp_xydata xy_data; + u8 id, tilt, rev_x, rev_y; + struct cyttsp_track_data trc; + s32 retval; + + DBG(printk(KERN_INFO "%s: Enter\n", __func__);) + /* get event data from CYTTSP device */ + retval = ttsp_read_block_data(ts, CY_REG_BASE, + sizeof(xy_data), &xy_data); + + if (retval < 0) { + printk(KERN_ERR "%s: Error, fail to read device on host interface bus\n", + __func__); + goto exit_xy_worker; + } + + if (CY_MODE_ERROR(ts->platform_data->power_state, + xy_data.hst_mode, xy_data.tt_mode)) { + /* TTSP device has switched to non-operational mode */ + printk(KERN_ERR "%s: Error, bad mode ps=%d hm=%02X tm=%02X\n", + __func__, ts->platform_data->power_state, + xy_data.hst_mode, xy_data.tt_mode); + retval = cyttsp_power_on(ts); + if (retval < 0) + printk(KERN_ERR "%s: Error, power on fail\n", __func__); + goto exit_xy_worker; + } + + /* touch extension handling */ + retval = ttsp_tch_ext(ts, &xy_data); + + if (retval < 0) { + printk(KERN_ERR "%s: Error, touch extension handling\n", + __func__); + goto exit_xy_worker; + } else if (retval > 0) { + DBG(printk(KERN_INFO "%s: Touch extension handled\n", + __func__);) + goto exit_xy_worker; + } + + /* provide flow control handshake */ + if (ts->irq) { + if (ts->platform_data->use_hndshk) { + u8 cmd; + cmd = xy_data.hst_mode & CY_HNDSHK_BIT ? + xy_data.hst_mode & ~CY_HNDSHK_BIT : + xy_data.hst_mode | CY_HNDSHK_BIT; + retval = ttsp_write_block_data(ts, CY_REG_BASE, + sizeof(cmd), (u8 *)&cmd); + } + } + trc.cur_tch = GET_NUM_TOUCHES(xy_data.tt_stat); + + if (IS_LARGE_AREA(xy_data.tt_stat) == 1) { + /* terminate all active tracks */ + trc.cur_tch = CY_NTCH; + DBG(printk(KERN_INFO "%s: Large area detected\n", + __func__);) + } else if (trc.cur_tch > CY_MAX_TCH) { + /* terminate all active tracks */ + trc.cur_tch = CY_NTCH; + DBG(printk(KERN_INFO "%s: Num touch error detected\n", + __func__);) + } else if (IS_BAD_PKT(xy_data.tt_mode)) { + /* terminate all active tracks */ + trc.cur_tch = CY_NTCH; + DBG(printk(KERN_INFO "%s: Invalid buffer detected\n", + __func__);) + } + + /* set tool size */ + trc.tool_width = CY_SMALL_TOOL_WIDTH; + + if (ts->platform_data->gen == CY_GEN2) { + /* translate Gen2 interface data into comparable Gen3 data */ + trc.cur_tch = ttsp_convert_gen2(trc.cur_tch, &xy_data); + } + + /* clear current active track ID array and count previous touches */ + for (id = 0, trc.prv_tch = CY_NTCH; id < CY_NUM_TRK_ID; id++) { + trc.cur_trk[id] = CY_NTCH; + trc.prv_tch += ts->act_trk[id]; + } + + /* + * send no events if there were no + * previous touches and no new touches + */ + if ((trc.prv_tch == CY_NTCH) && ((trc.cur_tch == CY_NTCH) || + (trc.cur_tch > CY_MAX_TCH))) { + if (++ts->ntch_count > CY_MAX_NTCH) { + /* TTSP device has a stuck operational mode */ + printk(KERN_ERR "%s: Error, stuck no-touch ct=%d\n", + __func__, trc.cur_tch); + retval = cyttsp_power_on(ts); + if (retval < 0) + printk(KERN_ERR "%s: Error, power on fail\n", + __func__); + goto exit_xy_worker; + } + } else + ts->ntch_count = 0; + + DBG(printk(KERN_INFO "%s: prev=%d curr=%d\n", __func__, + trc.prv_tch, trc.cur_tch);) + + /* clear current single-touch array */ + memset(trc.cur_st_tch, CY_IGNR_TCH, sizeof(trc.cur_st_tch)); + + /* clear single touch positions */ + trc.st_x1 = trc.st_y1 = trc.st_z1 = + trc.st_x2 = trc.st_y2 = trc.st_z2 = CY_NTCH; + + /* clear current multi-touch arrays */ + memset(trc.cur_mt_tch, CY_IGNR_TCH, sizeof(trc.cur_mt_tch)); + memset(trc.cur_mt_pos, CY_NTCH, sizeof(trc.cur_mt_pos)); + memset(trc.cur_mt_z, CY_NTCH, sizeof(trc.cur_mt_z)); + + DBG( + if (trc.cur_tch) { + unsigned i; + u8 *pdata = (u8 *)&xy_data; + + printk(KERN_INFO "%s: TTSP data_pack: ", __func__); + for (i = 0; i < sizeof(struct cyttsp_xydata); i++) + printk(KERN_INFO "[%d]=0x%x ", i, pdata[i]); + printk(KERN_INFO "\n"); + }) + + /* Determine if display is tilted */ + tilt = !!FLIP_DATA(ts->platform_data->flags); + /* Check for switch in origin */ + rev_x = !!REVERSE_X(ts->platform_data->flags); + rev_y = !!REVERSE_Y(ts->platform_data->flags); + + /* process the touches */ + switch (trc.cur_tch) { + case 4: + xy_data.x4 = be16_to_cpu(xy_data.x4); + xy_data.y4 = be16_to_cpu(xy_data.y4); + if (tilt) + FLIP_XY(xy_data.x4, xy_data.y4); + + if (rev_x) + xy_data.x4 = INVERT_X(xy_data.x4, + ts->platform_data->maxx); + if (rev_y) + xy_data.y4 = INVERT_X(xy_data.y4, + ts->platform_data->maxy); + + id = GET_TOUCH4_ID(xy_data.touch34_id); + if (ts->platform_data->use_trk_id) { + trc.cur_mt_pos[CY_MT_TCH4_IDX][CY_XPOS] = xy_data.x4; + trc.cur_mt_pos[CY_MT_TCH4_IDX][CY_YPOS] = xy_data.y4; + trc.cur_mt_z[CY_MT_TCH4_IDX] = xy_data.z4; + } else { + trc.cur_mt_pos[id][CY_XPOS] = xy_data.x4; + trc.cur_mt_pos[id][CY_YPOS] = xy_data.y4; + trc.cur_mt_z[id] = xy_data.z4; + } + trc.cur_mt_tch[CY_MT_TCH4_IDX] = id; + trc.cur_trk[id] = CY_TCH; + if (ts->prv_st_tch[CY_ST_FNGR1_IDX] < CY_NUM_TRK_ID) { + if (ts->prv_st_tch[CY_ST_FNGR1_IDX] == id) { + trc.st_x1 = xy_data.x4; + trc.st_y1 = xy_data.y4; + trc.st_z1 = xy_data.z4; + trc.cur_st_tch[CY_ST_FNGR1_IDX] = id; + } else if (ts->prv_st_tch[CY_ST_FNGR2_IDX] == id) { + trc.st_x2 = xy_data.x4; + trc.st_y2 = xy_data.y4; + trc.st_z2 = xy_data.z4; + trc.cur_st_tch[CY_ST_FNGR2_IDX] = id; + } + } + DBG(printk(KERN_INFO"%s: 4th XYZ:% 3d,% 3d,% 3d ID:% 2d\n\n", + __func__, xy_data.x4, xy_data.y4, xy_data.z4, + (xy_data.touch34_id & 0x0F));) + + /* do not break */ + case 3: + xy_data.x3 = be16_to_cpu(xy_data.x3); + xy_data.y3 = be16_to_cpu(xy_data.y3); + if (tilt) + FLIP_XY(xy_data.x3, xy_data.y3); + + if (rev_x) + xy_data.x3 = INVERT_X(xy_data.x3, + ts->platform_data->maxx); + if (rev_y) + xy_data.y3 = INVERT_X(xy_data.y3, + ts->platform_data->maxy); + + id = GET_TOUCH3_ID(xy_data.touch34_id); + if (ts->platform_data->use_trk_id) { + trc.cur_mt_pos[CY_MT_TCH3_IDX][CY_XPOS] = xy_data.x3; + trc.cur_mt_pos[CY_MT_TCH3_IDX][CY_YPOS] = xy_data.y3; + trc.cur_mt_z[CY_MT_TCH3_IDX] = xy_data.z3; + } else { + trc.cur_mt_pos[id][CY_XPOS] = xy_data.x3; + trc.cur_mt_pos[id][CY_YPOS] = xy_data.y3; + trc.cur_mt_z[id] = xy_data.z3; + } + trc.cur_mt_tch[CY_MT_TCH3_IDX] = id; + trc.cur_trk[id] = CY_TCH; + if (ts->prv_st_tch[CY_ST_FNGR1_IDX] < CY_NUM_TRK_ID) { + if (ts->prv_st_tch[CY_ST_FNGR1_IDX] == id) { + trc.st_x1 = xy_data.x3; + trc.st_y1 = xy_data.y3; + trc.st_z1 = xy_data.z3; + trc.cur_st_tch[CY_ST_FNGR1_IDX] = id; + } else if (ts->prv_st_tch[CY_ST_FNGR2_IDX] == id) { + trc.st_x2 = xy_data.x3; + trc.st_y2 = xy_data.y3; + trc.st_z2 = xy_data.z3; + trc.cur_st_tch[CY_ST_FNGR2_IDX] = id; + } + } + DBG(printk(KERN_INFO"%s: 3rd XYZ:% 3d,% 3d,% 3d ID:% 2d\n", + __func__, xy_data.x3, xy_data.y3, xy_data.z3, + ((xy_data.touch34_id >> 4) & 0x0F));) + + /* do not break */ + case 2: + xy_data.x2 = be16_to_cpu(xy_data.x2); + xy_data.y2 = be16_to_cpu(xy_data.y2); + if (tilt) + FLIP_XY(xy_data.x2, xy_data.y2); + + if (rev_x) + xy_data.x2 = INVERT_X(xy_data.x2, + ts->platform_data->maxx); + if (rev_y) + xy_data.y2 = INVERT_X(xy_data.y2, + ts->platform_data->maxy); + id = GET_TOUCH2_ID(xy_data.touch12_id); + if (ts->platform_data->use_trk_id) { + trc.cur_mt_pos[CY_MT_TCH2_IDX][CY_XPOS] = xy_data.x2; + trc.cur_mt_pos[CY_MT_TCH2_IDX][CY_YPOS] = xy_data.y2; + trc.cur_mt_z[CY_MT_TCH2_IDX] = xy_data.z2; + } else { + trc.cur_mt_pos[id][CY_XPOS] = xy_data.x2; + trc.cur_mt_pos[id][CY_YPOS] = xy_data.y2; + trc.cur_mt_z[id] = xy_data.z2; + } + trc.cur_mt_tch[CY_MT_TCH2_IDX] = id; + trc.cur_trk[id] = CY_TCH; + if (ts->prv_st_tch[CY_ST_FNGR1_IDX] < CY_NUM_TRK_ID) { + if (ts->prv_st_tch[CY_ST_FNGR1_IDX] == id) { + trc.st_x1 = xy_data.x2; + trc.st_y1 = xy_data.y2; + trc.st_z1 = xy_data.z2; + trc.cur_st_tch[CY_ST_FNGR1_IDX] = id; + } else if (ts->prv_st_tch[CY_ST_FNGR2_IDX] == id) { + trc.st_x2 = xy_data.x2; + trc.st_y2 = xy_data.y2; + trc.st_z2 = xy_data.z2; + trc.cur_st_tch[CY_ST_FNGR2_IDX] = id; + } + } + DBG(printk(KERN_INFO"%s: 2nd XYZ:% 3d,% 3d,% 3d ID:% 2d\n", + __func__, xy_data.x2, xy_data.y2, xy_data.z2, + (xy_data.touch12_id & 0x0F));) + + /* do not break */ + case 1: + xy_data.x1 = be16_to_cpu(xy_data.x1); + xy_data.y1 = be16_to_cpu(xy_data.y1); + if (tilt) + FLIP_XY(xy_data.x1, xy_data.y1); + + if (rev_x) + xy_data.x1 = INVERT_X(xy_data.x1, + ts->platform_data->maxx); + if (rev_y) + xy_data.y1 = INVERT_X(xy_data.y1, + ts->platform_data->maxy); + + id = GET_TOUCH1_ID(xy_data.touch12_id); + if (ts->platform_data->use_trk_id) { + trc.cur_mt_pos[CY_MT_TCH1_IDX][CY_XPOS] = xy_data.x1; + trc.cur_mt_pos[CY_MT_TCH1_IDX][CY_YPOS] = xy_data.y1; + trc.cur_mt_z[CY_MT_TCH1_IDX] = xy_data.z1; + } else { + trc.cur_mt_pos[id][CY_XPOS] = xy_data.x1; + trc.cur_mt_pos[id][CY_YPOS] = xy_data.y1; + trc.cur_mt_z[id] = xy_data.z1; + } + trc.cur_mt_tch[CY_MT_TCH1_IDX] = id; + trc.cur_trk[id] = CY_TCH; + if (ts->prv_st_tch[CY_ST_FNGR1_IDX] < CY_NUM_TRK_ID) { + if (ts->prv_st_tch[CY_ST_FNGR1_IDX] == id) { + trc.st_x1 = xy_data.x1; + trc.st_y1 = xy_data.y1; + trc.st_z1 = xy_data.z1; + trc.cur_st_tch[CY_ST_FNGR1_IDX] = id; + } else if (ts->prv_st_tch[CY_ST_FNGR2_IDX] == id) { + trc.st_x2 = xy_data.x1; + trc.st_y2 = xy_data.y1; + trc.st_z2 = xy_data.z1; + trc.cur_st_tch[CY_ST_FNGR2_IDX] = id; + } + } + DBG(printk(KERN_INFO"%s: S1st XYZ:% 3d,% 3d,% 3d ID:% 2d\n", + __func__, xy_data.x1, xy_data.y1, xy_data.z1, + ((xy_data.touch12_id >> 4) & 0x0F));) + + break; + case 0: + default: + break; + } + + if (ts->platform_data->use_st) + handle_single_touch(&xy_data, &trc, ts); + + if (ts->platform_data->use_mt) + handle_multi_touch(&trc, ts); + + /* handle gestures */ + if (ts->platform_data->use_gestures && xy_data.gest_id) { + input_report_key(ts->input, BTN_3, CY_TCH); + input_report_abs(ts->input, ABS_HAT1X, xy_data.gest_id); + input_report_abs(ts->input, ABS_HAT1Y, xy_data.gest_cnt); + } + + /* signal the view motion event */ + input_sync(ts->input); + + /* update platform data for the current multi-touch information */ + memcpy(ts->act_trk, trc.cur_trk, CY_NUM_TRK_ID); + +exit_xy_worker: + /* reset the watchdog */ + mod_timer(&ts->timer, jiffies + CY_WDG_TIMEOUT); + DBG(printk(KERN_INFO"%s: finished.\n", __func__);) + return; +} + +static irqreturn_t cyttsp_irq(int irq, void *handle) +{ + struct cyttsp *ts = (struct cyttsp *)handle; + + DBG(printk(KERN_INFO"%s: Got IRQ!\n", __func__);) + switch (ts->platform_data->power_state) { + case CY_BL_STATE: + case CY_SYSINFO_STATE: + complete(&ts->int_running); + break; + case CY_LDR_STATE: + ts->bl_ready = true; + break; + case CY_ACTIVE_STATE: + cyttsp_xy_worker(ts); + break; + case CY_READY_STATE: + default: + break; + } + + return IRQ_HANDLED; +} + +/* Timer function used as watchdog */ +static void cyttsp_timer(unsigned long handle) +{ + struct cyttsp *ts = (struct cyttsp *)handle; + + DBG(printk(KERN_INFO"%s: Watchdog timer event\n", __func__);) + if (!work_pending(&ts->work)) + schedule_work(&ts->work); + return; +} +/* + ************************************************************************ + * Probe initialization functions + ************************************************************************ + */ +static int cyttsp_load_bl_regs(struct cyttsp *ts) +{ + int retval; + + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + + memset(&ts->bl_data, 0, sizeof(ts->bl_data)); + + retval = ttsp_read_block_data(ts, CY_REG_BASE, + sizeof(ts->bl_data), &(ts->bl_data)); + + if (retval < 0) { + DBG2(printk(KERN_INFO "%s: Failed reading block data, err:%d\n", + __func__, retval);) + goto fail; + } + + DBG({ + int i; + u8 *bl_data = (u8 *)&(ts->bl_data); + for (i = 0; i < sizeof(struct cyttsp_bootloader_data); i++) + printk(KERN_INFO "%s bl_data[%d]=0x%x\n", + __func__, i, bl_data[i]); + }) + DBG2(printk(KERN_INFO "%s: bl f=%02X s=%02X e=%02X\n", + __func__, + ts->bl_data.bl_file, + ts->bl_data.bl_status, + ts->bl_data.bl_error);) + + return 0; +fail: + return retval; +} + +static bool cyttsp_bl_app_valid(struct cyttsp *ts) +{ + int retval; + + ts->bl_data.bl_status = 0x00; + + retval = cyttsp_load_bl_regs(ts); + + if (retval < 0) + return false; + + if (ts->bl_data.bl_status == 0x11) { + printk(KERN_INFO "%s: App found; normal boot\n", __func__); + return true; + } else if (ts->bl_data.bl_status == 0x10) { + printk(KERN_INFO "%s: NO APP; load firmware!!\n", __func__); + return false; + } else { + if (ts->bl_data.bl_file == CY_OPERATE_MODE) { + int invalid_op_mode_status; + invalid_op_mode_status = ts->bl_data.bl_status & 0x3f; + if (invalid_op_mode_status) + return false; + else { + if (ts->platform_data->power_state == + CY_ACTIVE_STATE) + printk(KERN_INFO "%s: Operational\n", + __func__); + else + printk(KERN_INFO "%s: No bootloader\n", + __func__); + } + } + return true; + } +} + +static int cyttsp_exit_bl_mode(struct cyttsp *ts) +{ + int retval; + int tries = 0; + + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + + /* check if in bootloader mode; + * if in operational mode then just return without fail + */ + cyttsp_load_bl_regs(ts); + if (!GET_BOOTLOADERMODE(ts->bl_data.bl_status)) + return 0; + + retval = ttsp_write_block_data(ts, CY_REG_BASE, sizeof(bl_cmd), + (void *)bl_cmd); + if (retval < 0) { + printk(KERN_ERR "%s: Failed writing block data, err:%d\n", + __func__, retval); + goto fail; + } + do { + msleep(20); + cyttsp_load_bl_regs(ts); + } while (GET_BOOTLOADERMODE(ts->bl_data.bl_status) && tries++ < 10); + + DBG2(printk(KERN_INFO "%s: read tries=%d\n", __func__, tries);) + + DBG(printk(KERN_INFO"%s: Exit " + "f=%02X s=%02X e=%02X\n", + __func__, + ts->bl_data.bl_file, ts->bl_data.bl_status, + ts->bl_data.bl_error);) + + return 0; +fail: + return retval; +} + +static int cyttsp_set_sysinfo_mode(struct cyttsp *ts) +{ + int retval; + u8 cmd = CY_SYSINFO_MODE; + + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + memset(&(ts->sysinfo_data), 0, sizeof(struct cyttsp_sysinfo_data)); + + /* switch to sysinfo mode */ + retval = ttsp_write_block_data(ts, CY_REG_BASE, sizeof(cmd), &cmd); + if (retval < 0) { + printk(KERN_ERR "%s: Failed writing block data, err:%d\n", + __func__, retval); + goto exit_sysinfo_mode; + } + + if (!(retval < 0)) { + /* wait for interrupt to set ready completion */ + ts->platform_data->power_state = CY_SYSINFO_STATE; + INIT_COMPLETION(ts->int_running); + retval = wait_for_completion_interruptible_timeout( + &ts->int_running, + msecs_to_jiffies(CY_DELAY_DFLT * CY_DELAY_MAX)); + ts->platform_data->power_state = CY_READY_STATE; + } + + if (retval < 0) { + ts->platform_data->power_state = CY_IDLE_STATE; + printk(KERN_ERR "%s: reset timeout fail (ret=%d)\n", + __func__, retval); + } + +exit_sysinfo_mode: + /* read sysinfo registers */ + retval = ttsp_read_block_data(ts, CY_REG_BASE, + sizeof(ts->sysinfo_data), &(ts->sysinfo_data)); + + DBG(printk(KERN_INFO"%s:SI2: hst_mode=0x%02X mfg_cmd=0x%02X " + "mfg_stat=0x%02X\n", __func__, ts->sysinfo_data.hst_mode, + ts->sysinfo_data.mfg_cmd, + ts->sysinfo_data.mfg_stat);) + + DBG(printk(KERN_INFO"%s:SI2: bl_ver=0x%02X%02X\n", + __func__, ts->sysinfo_data.bl_verh, ts->sysinfo_data.bl_verl);) + + DBG(printk(KERN_INFO"%s:SI2: sysinfo act_intrvl=0x%02X " + "tch_tmout=0x%02X lp_intrvl=0x%02X\n", + __func__, ts->sysinfo_data.act_intrvl, + ts->sysinfo_data.tch_tmout, + ts->sysinfo_data.lp_intrvl);) + + printk(KERN_INFO"%s:SI2:tts_ver=%02X%02X app_id=%02X%02X " + "app_ver=%02X%02X c_id=%02X%02X%02X " + "u_id=%02X%02X%02X%02X%02X%02X%02X%02X\n", + __func__, + ts->sysinfo_data.tts_verh, ts->sysinfo_data.tts_verl, + ts->sysinfo_data.app_idh, ts->sysinfo_data.app_idl, + ts->sysinfo_data.app_verh, ts->sysinfo_data.app_verl, + ts->sysinfo_data.cid[0], ts->sysinfo_data.cid[1], + ts->sysinfo_data.cid[2], + ts->sysinfo_data.uid[0], ts->sysinfo_data.uid[1], + ts->sysinfo_data.uid[2], ts->sysinfo_data.uid[3], + ts->sysinfo_data.uid[4], ts->sysinfo_data.uid[5], + ts->sysinfo_data.uid[6], ts->sysinfo_data.uid[7]); + + return retval; +} + +static int cyttsp_set_sysinfo_regs(struct cyttsp *ts) +{ + int retval = 0; + + if (ts->platform_data->scn_typ != CY_SCN_TYP_DFLT) { + u8 scn_typ = ts->platform_data->scn_typ; + + retval = ttsp_write_block_data(ts, + CY_REG_SCN_TYP, + sizeof(scn_typ), &scn_typ); + } + + if (retval < 0) + return retval; + + if ((ts->platform_data->act_intrvl != CY_ACT_INTRVL_DFLT) || + (ts->platform_data->tch_tmout != CY_TCH_TMOUT_DFLT) || + (ts->platform_data->lp_intrvl != CY_LP_INTRVL_DFLT)) { + + u8 intrvl_ray[3]; + + intrvl_ray[0] = ts->platform_data->act_intrvl; + intrvl_ray[1] = ts->platform_data->tch_tmout; + intrvl_ray[2] = ts->platform_data->lp_intrvl; + + /* set intrvl registers */ + retval = ttsp_write_block_data(ts, + CY_REG_ACT_INTRVL, + sizeof(intrvl_ray), intrvl_ray); + + msleep(CY_DELAY_SYSINFO); + } + + return retval; +} + +static int cyttsp_set_operational_mode(struct cyttsp *ts) +{ + int retval; + int tries; + u8 cmd = CY_OPERATE_MODE; + u8 gest_default; + struct cyttsp_xydata xy_data; + + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + + retval = ttsp_write_block_data(ts, CY_REG_BASE, sizeof(cmd), &cmd); + if (retval < 0) { + printk(KERN_ERR "%s: Failed writing block data, err:%d\n", + __func__, retval); + goto write_block_data_fail; + } + + /* wait for TTSP Device to complete switch to Operational mode */ + msleep(20); + + tries = 0; + gest_default = + CY_GEST_GRP1 + CY_GEST_GRP2 + + CY_GEST_GRP3 + CY_GEST_GRP4 + + CY_ACT_DIST_DFLT; + tries = 0; + do { + msleep(CY_DELAY_DFLT); + memset(&xy_data, 0, sizeof(xy_data)); + retval = ttsp_read_block_data(ts, CY_REG_BASE, + sizeof(struct cyttsp_xydata), &xy_data); + } while (!((retval == 0) && + ((xy_data.gest_set & CY_ACT_DIST_BITS) == + (CY_ACT_DIST_DFLT & CY_ACT_DIST_BITS))) && + (tries++ < CY_DELAY_MAX)); + + DBG2(printk(KERN_INFO "%s: read tries=%d\n", __func__, tries);) + + return 0; +write_block_data_fail: + return retval; +} + +static int cyttsp_soft_reset(struct cyttsp *ts) +{ + int retval; + u8 cmd = CY_SOFT_RESET_MODE; + + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + + /* reset TTSP Device back to bootloader mode */ + ts->platform_data->power_state = CY_BL_STATE; + retval = ttsp_write_block_data(ts, CY_REG_BASE, sizeof(cmd), &cmd); + + if (!(retval < 0)) { + /* wait for interrupt to set ready completion */ + INIT_COMPLETION(ts->int_running); + retval = wait_for_completion_interruptible_timeout( + &ts->int_running, + msecs_to_jiffies(CY_DELAY_DFLT * CY_DELAY_MAX)); + } + + if (retval < 0) { + ts->platform_data->power_state = CY_IDLE_STATE; + printk(KERN_ERR "%s: reset timeout fail (ret=%d)\n", + __func__, retval); + } + + if (retval > 0) + retval = 0; + + return retval; +} + +static int cyttsp_gesture_setup(struct cyttsp *ts) +{ + int retval; + u8 gesture_setup; + + DBG(printk(KERN_INFO"%s: Init gesture; active distance setup\n", + __func__);) + + gesture_setup = ts->platform_data->gest_set; + retval = ttsp_write_block_data(ts, CY_REG_GEST_SET, + sizeof(gesture_setup), &gesture_setup); + + return retval; +} + +#ifdef CY_INCLUDE_LOAD_RECS +#include "cyttsp_ldr.h" +#else +static bool cyttsp_bl_status(struct cyttsp *ts) +{ + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + return ((ts->bl_data.bl_status == 0x10) || + (ts->bl_data.bl_status == 0x11)); +} + +static int cyttsp_loader(struct cyttsp *ts) +{ + void *fptr = cyttsp_bl_status; /* kill warning */ + + if (ts) { + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + DBG(printk(KERN_INFO"%s: Exit\n", __func__);) + } + + if (!fptr) + return -EIO; + else + return 0; +} +#endif + +static int cyttsp_power_on(struct cyttsp *ts) +{ + int retval = 0; + + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + + /* (re-)start watchdog */ + mod_timer(&ts->timer, jiffies + CY_WDG_TIMEOUT); + + cyttsp_init_tch(ts); + retval = cyttsp_soft_reset(ts); + DBG2(printk(KERN_INFO"%s: after softreset r=%d\n", __func__, retval);) + if (retval < 0) + goto bypass; + + if (ts->platform_data->use_load_file) + retval = cyttsp_loader(ts); + + if (!cyttsp_bl_app_valid(ts)) { + retval = 1; + goto bypass; + } + + retval = cyttsp_exit_bl_mode(ts); + DBG2(printk(KERN_INFO"%s: after exit bl r=%d\n", __func__, retval);) + + if (retval < 0) + goto bypass; + + /* switch to System Information mode to read */ + /* versions and set interval registers */ + ts->platform_data->power_state = CY_READY_STATE; + retval = cyttsp_set_sysinfo_mode(ts); + if (retval < 0) + goto bypass; + + retval = cyttsp_set_sysinfo_regs(ts); + if (retval < 0) + goto bypass; + + /* switch back to Operational mode */ + DBG2(printk(KERN_INFO"%s: switch back to operational mode\n", + __func__);) + retval = cyttsp_set_operational_mode(ts); + if (retval < 0) + goto bypass; + + /* init gesture setup; required for active distance */ + cyttsp_gesture_setup(ts); + +bypass: + if (retval) + ts->platform_data->power_state = CY_IDLE_STATE; + else + ts->platform_data->power_state = CY_ACTIVE_STATE; + + printk(KERN_INFO"%s: Power state is %s\n", + __func__, (ts->platform_data->power_state == CY_ACTIVE_STATE) ? + "ACTIVE" : "IDLE"); + return retval; +} + +static void cyttsp_check_bl(struct work_struct *work) +{ + struct cyttsp *ts = container_of(work, struct cyttsp, work); + s32 retval; + struct cyttsp_xydata xy_data; + + retval = ttsp_read_block_data(ts, CY_REG_BASE, 2, &xy_data); + if (retval < 0) { + printk(KERN_ERR "%s: Error, fail to read device\n", __func__); + goto reserve_next; + } + + if (CY_MODE_ERROR(ts->platform_data->power_state, + xy_data.hst_mode, xy_data.tt_mode)) { + printk(KERN_ERR "%s: Error, mode error detected " + "on watchdog timeout ps=%d mode=%02X bl_mode=%02X\n", + __func__, ts->platform_data->power_state, + xy_data.hst_mode, xy_data.tt_mode); + retval = cyttsp_power_on(ts); + if (retval < 0) + printk(KERN_ERR "%s: Error, power on fail\n", __func__); + } + +reserve_next: + mod_timer(&ts->timer, jiffies + CY_WDG_TIMEOUT); +} + +static int cyttsp_resume(struct cyttsp *ts) +{ + int retval = 0; + struct cyttsp_xydata xydata; + + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + if (ts->platform_data->use_sleep && (ts->platform_data->power_state != + CY_ACTIVE_STATE)) { + if (ts->platform_data->wakeup) { + retval = ts->platform_data->wakeup(); + if (retval < 0) + printk(KERN_ERR "%s: Error, wakeup failed!\n", + __func__); + } else { + printk(KERN_ERR "%s: Error, wakeup not implemented " + "(check board file).\n", __func__); + retval = -ENOSYS; + } + if (!(retval < 0)) { + retval = ttsp_read_block_data(ts, CY_REG_BASE, + sizeof(xydata), &xydata); + if (!(retval < 0) && !GET_HSTMODE(xydata.hst_mode)) + ts->platform_data->power_state = + CY_ACTIVE_STATE; + } + } + DBG(printk(KERN_INFO"%s: Wake Up %s\n", __func__, + (retval < 0) ? "FAIL" : "PASS");) + return retval; +} + +static int cyttsp_suspend(struct cyttsp *ts) +{ + u8 sleep_mode = 0; + int retval = 0; + + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + if (ts->platform_data->use_sleep && + (ts->platform_data->power_state == CY_ACTIVE_STATE)) { + sleep_mode = CY_DEEP_SLEEP_MODE; + retval = ttsp_write_block_data(ts, + CY_REG_BASE, sizeof(sleep_mode), &sleep_mode); + if (!(retval < 0)) + ts->platform_data->power_state = CY_SLEEP_STATE; + } + DBG(printk(KERN_INFO"%s: Sleep Power state is %s\n", __func__, + (ts->platform_data->power_state == CY_ACTIVE_STATE) ? + "ACTIVE" : + ((ts->platform_data->power_state == CY_SLEEP_STATE) ? + "SLEEP" : "LOW POWER"));) + return retval; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void cyttsp_ts_early_suspend(struct early_suspend *h) +{ + struct cyttsp *ts = container_of(h, struct cyttsp, early_suspend); + + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + LOCK(ts->mutex); + if (!ts->fw_loader_mode) { + disable_irq_nosync(ts->irq); + ts->suspended = 1; + /* kill watchdog */ + del_timer(&ts->timer); + cancel_work_sync(&ts->work); + cyttsp_suspend(ts); + } + regulator_disable(ts->regulator); + UNLOCK(ts->mutex); +} + +static void cyttsp_ts_late_resume(struct early_suspend *h) +{ + struct cyttsp *ts = container_of(h, struct cyttsp, early_suspend); + + regulator_enable(ts->regulator); + + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + LOCK(ts->mutex); + if (!ts->fw_loader_mode && ts->suspended) { + ts->suspended = 0; + if (cyttsp_resume(ts) < 0) + printk(KERN_ERR "%s: Error, cyttsp_resume.\n", + __func__); + enable_irq(ts->irq); + /* resume watchdog */ + mod_timer(&ts->timer, jiffies + CY_WDG_TIMEOUT); + } + UNLOCK(ts->mutex); +} +#endif + +static int cyttsp_wr_reg(struct cyttsp *ts, u8 reg_id, u8 reg_data) +{ + + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + + return ttsp_write_block_data(ts, + CY_REG_BASE + reg_id, sizeof(u8), ®_data); +} + +static int cyttsp_rd_reg(struct cyttsp *ts, u8 reg_id, u8 *reg_data) +{ + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + return ttsp_read_block_data(ts, + CY_REG_BASE + reg_id, sizeof(u8), reg_data); +} + +static ssize_t firmware_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t pos, size_t size) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct cyttsp *ts = dev_get_drvdata(dev); + LOCK(ts->mutex); + if (!ts->fw_loader_mode) { + unsigned short val = *(unsigned short *)buf; + u8 reg_data = val & 0xff; + ts->reg_id = (val & 0xff00) >> 8; + if (!(ts->reg_id & 0x80)) { + /* write user specified operational register */ + if (ts->reg_id == 0x00) { + switch (GET_HSTMODE(reg_data)) { + case GET_HSTMODE(CY_OPERATE_MODE): + cyttsp_wr_reg(ts, ts->reg_id, reg_data); + ts->platform_data->power_state = + CY_ACTIVE_STATE; + enable_irq(ts->irq); + mod_timer(&ts->timer, CY_WDG_TIMEOUT); + printk(KERN_INFO "%s: " + "Switch to Operational Mode " + "ps=%d\n", __func__, + ts->platform_data->power_state); + break; + case GET_HSTMODE(CY_SYSINFO_MODE): + ts->platform_data->power_state = + CY_READY_STATE; + disable_irq_nosync(ts->irq); + /* kill watchdog */ + del_timer(&ts->timer); + cancel_work_sync(&ts->work); + cyttsp_wr_reg(ts, ts->reg_id, reg_data); + printk(KERN_INFO "%s: " + "Switch to SysInfo Mode " + "ps=%d\n", __func__, + ts->platform_data->power_state); + break; + default: + cyttsp_wr_reg(ts, ts->reg_id, reg_data); + break; + } + } else + cyttsp_wr_reg(ts, ts->reg_id, reg_data); + printk(KERN_INFO "%s: " + "write(reg=%02X(%d) dat=0x%02X(%d))\n", + __func__, + ts->reg_id & ~0x80, ts->reg_id & ~0x80, + reg_data, reg_data); + } else { + /* save user specified operational read register */ + DBG2(printk(KERN_INFO "%s: read(r=0x%02X)\n", + __func__, ts->reg_id);) + } + } else { + int retval = 0; + int tries = 0; + DBG({ + char str[128]; + char *p = str; + int i; + for (i = 0; i < size; i++, p += 2) + sprintf(p, "%02x", (unsigned char)buf[i]); + printk(KERN_DEBUG "%s: size %d, pos %ld payload %s\n", + __func__, size, (long)pos, str); + }) + do { + retval = ttsp_write_block_data(ts, + CY_REG_BASE, size, buf); + if (retval < 0) + msleep(500); + } while ((retval < 0) && (tries++ < 10)); + } + UNLOCK(ts->mutex); + return size; +} + +static ssize_t firmware_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *ba, + char *buf, loff_t pos, size_t size) +{ + int count = 0; + u8 reg_data; + struct device *dev = container_of(kobj, struct device, kobj); + struct cyttsp *ts = dev_get_drvdata(dev); + + DBG2(printk(KERN_INFO"%s: Enter (mode=%d)\n", + __func__, ts->fw_loader_mode);) + + LOCK(ts->mutex); + if (!ts->fw_loader_mode) { + /* read user specified operational register */ + cyttsp_rd_reg(ts, ts->reg_id & ~0x80, ®_data); + *(unsigned short *)buf = reg_data << 8; + count = sizeof(unsigned short); + printk(KERN_INFO "%s: read(reg=%02X(%d) dat=0x%02X(%d))\n", + __func__, ts->reg_id & ~0x80, ts->reg_id & ~0x80, + reg_data, reg_data); + } else { + int retval = 0; + int tries = 0; + + do { + retval = cyttsp_load_bl_regs(ts); + if (retval < 0) + msleep(500); + } while ((retval < 0) && (tries++ < 10)); + + if (retval < 0) { + printk(KERN_ERR "%s: error reading status\n", __func__); + count = 0; + } else { + *(unsigned short *)buf = ts->bl_data.bl_status << 8 | + ts->bl_data.bl_error; + count = sizeof(unsigned short); + } + + DBG2(printk(KERN_INFO + "%s:bl_f=0x%02X bl_s=0x%02X bl_e=0x%02X\n", + __func__, + ts->bl_data.bl_file, + ts->bl_data.bl_status, + ts->bl_data.bl_error);) + } + UNLOCK(ts->mutex); + return count; +} + +static struct bin_attribute cyttsp_firmware = { + .attr = { + .name = "firmware", + .mode = 0644, + }, + .size = 128, + .read = firmware_read, + .write = firmware_write, +}; + +static ssize_t attr_fwloader_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cyttsp *ts = dev_get_drvdata(dev); + return sprintf(buf, "0x%02X%02X 0x%02X%02X 0x%02X%02X 0x%02X%02X%02X\n", + ts->sysinfo_data.tts_verh, ts->sysinfo_data.tts_verl, + ts->sysinfo_data.app_idh, ts->sysinfo_data.app_idl, + ts->sysinfo_data.app_verh, ts->sysinfo_data.app_verl, + ts->sysinfo_data.cid[0], ts->sysinfo_data.cid[1], + ts->sysinfo_data.cid[2]); +} + +static ssize_t attr_fwloader_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + char *p; + int ret; + struct cyttsp *ts = dev_get_drvdata(dev); + unsigned val = simple_strtoul(buf, &p, 10); + + ret = p - buf; + if (*p && isspace(*p)) + ret++; + DBG2(printk(KERN_DEBUG "%s: %u\n", __func__, val);) + + LOCK(ts->mutex) + if (val == 3) { + sysfs_remove_bin_file(&dev->kobj, &cyttsp_firmware); + DBG2(printk(KERN_INFO "%s: FW loader closed for reg r/w\n", + __func__);) + } else if (val == 2) { + if (sysfs_create_bin_file(&dev->kobj, &cyttsp_firmware)) + printk(KERN_ERR "%s: unable to create file\n", + __func__); + DBG2(printk(KERN_INFO "%s: FW loader opened for reg r/w\n", + __func__);) + if (ts->suspended) { + cyttsp_resume(ts); + ts->suspended = 0; + enable_irq(ts->irq); + /* resume watchdog */ + mod_timer(&ts->timer, jiffies + CY_WDG_TIMEOUT); + } + } else if ((val == 1) && !ts->fw_loader_mode) { + ts->fw_loader_mode = 1; + if (ts->suspended) { + cyttsp_resume(ts); + ts->suspended = 0; + enable_irq(ts->irq); + /* resume watchdog */ + mod_timer(&ts->timer, jiffies + CY_WDG_TIMEOUT); + } + disable_irq_nosync(ts->irq); + /* kill watchdog */ + del_timer(&ts->timer); + cancel_work_sync(&ts->work); + if (sysfs_create_bin_file(&dev->kobj, &cyttsp_firmware)) + printk(KERN_ERR "%s: unable to create file\n", + __func__); + DBG2(printk(KERN_INFO + "%s: FW loader opened for start load: ps=%d mode=%d\n", + __func__, + ts->platform_data->power_state, ts->fw_loader_mode);) + cyttsp_soft_reset(ts); + printk(KERN_INFO "%s: FW loader started.\n", __func__); + ts->platform_data->power_state = CY_LDR_STATE; + } else if (!val && ts->fw_loader_mode) { + sysfs_remove_bin_file(&dev->kobj, &cyttsp_firmware); + ts->fw_loader_mode = 0; + printk(KERN_INFO "%s: FW loader finished.\n", __func__); + enable_irq(ts->irq); + ret = cyttsp_power_on(ts); + if (ret < 0) + printk(KERN_ERR "%s: Error, power on fail\n", __func__); + /* resume watchdog */ + mod_timer(&ts->timer, jiffies + CY_WDG_TIMEOUT); + } + UNLOCK(ts->mutex); + return ret == size ? ret : -EINVAL; +} + +int cyttsp_reset_controller(struct cyttsp *ts) +{ + int ret; + + ret = gpio_request(ts->platform_data->rst_gpio, "reset_pin"); + if (ret) { + printk(KERN_ERR "touch gpio failed\n"); + return ret; + } + ret = gpio_direction_output(ts->platform_data->rst_gpio, 1); + if (ret < 0) { + printk(KERN_ERR "reset gpio direction failed\n"); + goto out; + } + /* + * The start up procedure + * Set the RESET pin to low + * Wait for a period of 1 milisecond + * Set the RESET pin to high + * Wait for a period of 5 milisecond + * Start the initial Sequence + */ + gpio_set_value(ts->platform_data->rst_gpio, 0); + mdelay(1); + gpio_set_value(ts->platform_data->rst_gpio, 1); + mdelay(5); +out: + gpio_free(ts->platform_data->rst_gpio); + return 0; +} + +static struct device_attribute fwloader = + __ATTR(fwloader, 0644, attr_fwloader_show, attr_fwloader_store); + +void *cyttsp_core_init(struct cyttsp_bus_ops *bus_ops, struct device *pdev) +{ + struct input_dev *input_device; + struct cyttsp *ts; + int retval = 0, ret = 0; + + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + + ts = kzalloc(sizeof(*ts), GFP_KERNEL); + if (ts == NULL) { + printk(KERN_ERR "%s: Error, kzalloc\n", __func__); + goto error_alloc_data_failed; + } + mutex_init(&ts->mutex); + ts->pdev = pdev; + ts->platform_data = pdev->platform_data; + ts->bus_ops = bus_ops; + init_completion(&ts->int_running); + + if (ts->platform_data->init) + retval = ts->platform_data->init(1); + if (retval) { + printk(KERN_ERR "%s: platform init failed!\n", __func__); + goto error_init; + } + ret = cyttsp_reset_controller(ts); + if (ret < 0) { + printk(KERN_ERR "controller reset failed\n"); + goto error_reset; + } + + /* Create the input device and register it. */ + input_device = input_allocate_device(); + if (!input_device) { + retval = -ENOMEM; + printk(KERN_ERR "%s: Error, failed to allocate input device\n", + __func__); + goto error_input_allocate_device; + } + + ts->input = input_device; + input_device->name = ts->platform_data->name; + input_device->phys = ts->phys; + input_device->dev.parent = ts->pdev; + + /* enable interrupts */ + ts->irq = gpio_to_irq(ts->platform_data->irq_gpio); + retval = request_threaded_irq(ts->irq, NULL, cyttsp_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + ts->input->name, ts); + if (retval < 0) { + printk(KERN_ERR "%s: IRQ request failed r=%d\n", + __func__, retval); + ts->platform_data->power_state = CY_INVALID_STATE; + goto error_set_irq; + } + /* setup watchdog */ + INIT_WORK(&ts->work, cyttsp_check_bl); + setup_timer(&ts->timer, cyttsp_timer, (unsigned long) ts); + + DBG(printk(KERN_INFO "%s: call power_on\n", __func__);) + retval = cyttsp_power_on(ts); + if (retval < 0) { + printk(KERN_ERR "%s: Error, power on failed!\n", __func__); + goto error_power_on; + } + + set_bit(EV_SYN, input_device->evbit); + set_bit(EV_KEY, input_device->evbit); + set_bit(EV_ABS, input_device->evbit); + set_bit(BTN_TOUCH, input_device->keybit); + set_bit(BTN_2, input_device->keybit); + if (ts->platform_data->use_gestures) + set_bit(BTN_3, input_device->keybit); + + input_set_abs_params(input_device, ABS_X, 0, ts->platform_data->maxx, + 0, 0); + input_set_abs_params(input_device, ABS_Y, 0, ts->platform_data->maxy, + 0, 0); + input_set_abs_params(input_device, ABS_TOOL_WIDTH, 0, + CY_LARGE_TOOL_WIDTH, 0, 0); + input_set_abs_params(input_device, ABS_PRESSURE, 0, CY_MAXZ, 0, 0); + input_set_abs_params(input_device, ABS_HAT0X, 0, + ts->platform_data->maxx, 0, 0); + input_set_abs_params(input_device, ABS_HAT0Y, 0, + ts->platform_data->maxy, 0, 0); + if (ts->platform_data->use_gestures) { + input_set_abs_params(input_device, ABS_HAT1X, 0, CY_MAXZ, + 0, 0); + input_set_abs_params(input_device, ABS_HAT1Y, 0, CY_MAXZ, + 0, 0); + } + if (ts->platform_data->use_mt) { + input_set_abs_params(input_device, ABS_MT_POSITION_X, 0, + ts->platform_data->maxx, 0, 0); + input_set_abs_params(input_device, ABS_MT_POSITION_Y, 0, + ts->platform_data->maxy, 0, 0); + input_set_abs_params(input_device, ABS_MT_TOUCH_MAJOR, 0, + CY_MAXZ, 0, 0); + input_set_abs_params(input_device, ABS_MT_WIDTH_MAJOR, 0, + CY_LARGE_TOOL_WIDTH, 0, 0); + if (ts->platform_data->use_trk_id) + input_set_abs_params(input_device, ABS_MT_TRACKING_ID, + 0, CY_NUM_TRK_ID, 0, 0); + } + + if (ts->platform_data->use_virtual_keys) + input_set_capability(input_device, EV_KEY, KEY_PROG1); + + retval = input_register_device(input_device); + if (retval) { + printk(KERN_ERR "%s: Error, failed to register input device\n", + __func__); + goto error_input_register_device; + } + DBG(printk(KERN_INFO "%s: Registered input device %s\n", + __func__, input_device->name);) + ts->regulator = regulator_get(ts->pdev, "vcpin"); + if (IS_ERR(ts->regulator)) { + printk(KERN_ERR "%s: Error, regulator_get failed\n", __func__); + ts->regulator = NULL; + goto error_input_register_device; + } else { + ret = regulator_enable(ts->regulator); + if (ret < 0) { + printk(KERN_ERR "%s: regulator enable failed\n", + __func__); + goto out_regulator_error; + } + } + +#ifdef CONFIG_HAS_EARLYSUSPEND + ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + ts->early_suspend.suspend = cyttsp_ts_early_suspend; + ts->early_suspend.resume = cyttsp_ts_late_resume; + register_early_suspend(&ts->early_suspend); +#endif + retval = device_create_file(pdev, &fwloader); + if (retval) { + printk(KERN_ERR "%s: Error, could not create attribute\n", + __func__); + goto device_create_error; + } + dev_set_drvdata(pdev, ts); + + return ts; + +device_create_error: + regulator_disable(ts->regulator); +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&ts->early_suspend); +#endif +out_regulator_error: + regulator_put(ts->regulator); +error_input_register_device: + input_unregister_device(input_device); +error_power_on: + cancel_work_sync(&ts->work); + del_timer_sync(&ts->timer); + if (ts->irq >= 0) + free_irq(ts->irq, ts); +error_set_irq: + input_free_device(input_device); +error_reset: +error_input_allocate_device: + if (ts->platform_data->init) + ts->platform_data->init(0); +error_init: + kfree(ts); +error_alloc_data_failed: + return NULL; +} + +/* registered in driver struct */ +void cyttsp_core_release(void *handle) +{ + struct cyttsp *ts = handle; + + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&ts->early_suspend); +#endif + del_timer_sync(&ts->timer); + cancel_work_sync(&ts->work); + free_irq(ts->irq, ts); + input_unregister_device(ts->input); + input_free_device(ts->input); + if (ts->platform_data->init) + ts->platform_data->init(0); + kfree(ts); +} +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard touchscreen driver core"); +MODULE_AUTHOR("Cypress"); + diff --git a/drivers/input/touchscreen/cyttsp_core.h b/drivers/input/touchscreen/cyttsp_core.h new file mode 100755 index 00000000000..6af486177a0 --- /dev/null +++ b/drivers/input/touchscreen/cyttsp_core.h @@ -0,0 +1,44 @@ +/* Header file for: + * Cypress TrueTouch(TM) Standard Product I2C touchscreen driver. + * drivers/input/touchscreen/cyttsp_core.h + * + * Copyright (C) 2009-2011 Cypress Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Cypress reserves the right to make changes without further notice + * to the materials described herein. Cypress does not assume any + * liability arising out of the application described herein. + * + * Contact Cypress Semiconductor at www.cypress.com + * + */ + + +#ifndef __CYTTSP_CORE_H__ +#define __CYTTSP_CORE_H__ + +#include + +struct cyttsp_bus_ops { + s32 (*write)(void *handle, u8 addr, u8 length, const void *values); + s32 (*read)(void *handle, u8 addr, u8 length, void *values); + s32 (*ext)(void *handle, void *values); +}; + +void *cyttsp_core_init(struct cyttsp_bus_ops *bus_ops, struct device *pdev); +void cyttsp_core_release(void *handle); + +#endif /* __CYTTSP_CORE_H__ */ diff --git a/drivers/input/touchscreen/cyttsp_ldr.h b/drivers/input/touchscreen/cyttsp_ldr.h new file mode 100755 index 00000000000..95db89d0d13 --- /dev/null +++ b/drivers/input/touchscreen/cyttsp_ldr.h @@ -0,0 +1,333 @@ +/* + * Source for: + * Cypress TrueTouch(TM) Standard Product touchscreen driver. + * + * Copyright (C) 2009-2011 Cypress Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Cypress reserves the right to make changes without further notice + * to the materials described herein. Cypress does not assume any + * liability arising out of the application described herein. + * + * Contact Cypress Semiconductor at www.cypress.com + * + */ +/* + ************************************************************************ + * Compiled image bootloader functions + ************************************************************************ + */ +#include "cyttsp_fw.h" +#define CY_BL_PAGE_SIZE 16 +#define CY_BL_NUM_PAGES 5 +#define CY_MAX_DATA_LEN (CY_BL_PAGE_SIZE * 2) + +/* Timeout timer */ +static int cyttsp_check_polling(struct cyttsp *ts) +{ + return ts->platform_data->use_timer; +} + +static void cyttsp_to_timer(unsigned long handle) +{ + struct cyttsp *ts = (struct cyttsp *)handle; + + DBG(printk(KERN_INFO"%s: TTSP timeout timer event!\n", __func__);) + ts->to_timeout = true; + return; +} + +static void cyttsp_setup_to_timer(struct cyttsp *ts) +{ + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + setup_timer(&ts->to_timer, cyttsp_to_timer, (unsigned long) ts); +} + +static void cyttsp_kill_to_timer(struct cyttsp *ts) +{ + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + del_timer(&ts->to_timer); +} + +static void cyttsp_start_to_timer(struct cyttsp *ts, int ms) +{ + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + ts->to_timeout = false; + mod_timer(&ts->to_timer, jiffies + ms); +} + +static bool cyttsp_timeout(struct cyttsp *ts) +{ + if (cyttsp_check_polling(ts)) + return false; + else + return ts->to_timeout; +} + +static void cyttsp_set_bl_ready(struct cyttsp *ts, bool set) +{ + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + ts->bl_ready = set; + DBG(printk(KERN_INFO"%s: bl_ready=%d\n", __func__, (int)ts->bl_ready);) +} + +static bool cyttsp_check_bl_ready(struct cyttsp *ts) +{ + if (cyttsp_check_polling(ts)) + return true; + else + return ts->bl_ready; +} + +static bool cyttsp_bl_err_status(struct cyttsp *ts) +{ + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + return (((ts->bl_data.bl_status == 0x10) && + (ts->bl_data.bl_error == 0x20)) || + ((ts->bl_data.bl_status == 0x11) && + (ts->bl_data.bl_error == 0x20))); +} + +static bool cyttsp_wait_bl_ready(struct cyttsp *ts, + int pre_delay, int loop_delay, int max_try, + bool (*done)(struct cyttsp *ts)) +{ + int tries; + bool rdy = false, tmo = false; + + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + DBG(printk(KERN_INFO"%s: pre-dly=%d loop-dly=%d, max-try=%d\n", + __func__, pre_delay, loop_delay, max_try);) + + tries = 0; + ts->bl_data.bl_file = 0; + ts->bl_data.bl_status = 0; + ts->bl_data.bl_error = 0; + if (cyttsp_check_polling(ts)) { + msleep(pre_delay); + do { + msleep(abs(loop_delay)); + cyttsp_load_bl_regs(ts); + } while (!done(ts) && + tries++ < max_try); + DBG(printk(KERN_INFO"%s: polling mode tries=%d\n", + __func__, tries);) + } else { + cyttsp_start_to_timer(ts, abs(loop_delay) * max_try); + while (!rdy && !tmo) { + rdy = cyttsp_check_bl_ready(ts); + tmo = cyttsp_timeout(ts); + if (loop_delay < 0) + udelay(abs(loop_delay)); + else + msleep(abs(loop_delay)); + tries++; + } + DBG2(printk(KERN_INFO"%s: irq mode tries=%d rdy=%d tmo=%d\n", + __func__, tries, (int)rdy, (int)tmo);) + cyttsp_load_bl_regs(ts); + } + + if (tries >= max_try || tmo) + return true; /* timeout */ + else + return false; +} + +static int cyttsp_wr_blk_chunks(struct cyttsp *ts, u8 cmd, + u8 length, const u8 *values) +{ + int retval = 0; + int block = 1; + bool timeout; + + u8 dataray[CY_MAX_DATA_LEN]; + + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + + /* first page already includes the bl page offset */ + memcpy(dataray, values, CY_BL_PAGE_SIZE + 1); + cyttsp_set_bl_ready(ts, false); + retval = ttsp_write_block_data(ts, cmd, CY_BL_PAGE_SIZE + 1, dataray); + values += CY_BL_PAGE_SIZE + 1; + length -= CY_BL_PAGE_SIZE + 1; + if (retval) + return retval; + + /* remaining blocks require bl page offset stuffing */ + while (length && (block < CY_BL_NUM_PAGES) && !(retval < 0)) { + dataray[0] = CY_BL_PAGE_SIZE * block; + timeout = cyttsp_wait_bl_ready(ts, + 1, -100, 100, cyttsp_bl_err_status); + if (timeout) + return -EIO; + memcpy(&dataray[1], values, length >= CY_BL_PAGE_SIZE ? + CY_BL_PAGE_SIZE : length); + cyttsp_set_bl_ready(ts, false); + retval = ttsp_write_block_data(ts, cmd, + length >= CY_BL_PAGE_SIZE ? + CY_BL_PAGE_SIZE + 1 : length + 1, dataray); + values += CY_BL_PAGE_SIZE; + length = length >= CY_BL_PAGE_SIZE ? + length - CY_BL_PAGE_SIZE : 0; + block++; + } + + return retval; +} + +static int cyttsp_load_app(struct cyttsp *ts) +{ + int retval = 0; + int rec; + bool timeout; + + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + + printk(KERN_INFO "%s: " + "load file - tver=0x%02X%02X a_id=0x%02X%02X aver=0x%02X%02X\n", + __func__, + cyttsp_fw_tts_verh, cyttsp_fw_tts_verl, + cyttsp_fw_app_idh, cyttsp_fw_app_idl, + cyttsp_fw_app_verh, cyttsp_fw_app_verl); + + /* download new TTSP Application to the Bootloader */ + rec = 0; + + /* send bootload initiation command */ + printk(KERN_INFO"%s: Send BL Enter\n", __func__); + cyttsp_set_bl_ready(ts, false); + retval = ttsp_write_block_data(ts, CY_REG_BASE, + cyttsp_fw[rec].Length, cyttsp_fw[rec].Block); + rec++; + if (retval) + return retval; + timeout = cyttsp_wait_bl_ready(ts, 1, 100, 100, cyttsp_bl_err_status); + DBG(printk(KERN_INFO "%s: BL ENTER f=%02X s=%02X e=%02X t=%d\n", + __func__, + ts->bl_data.bl_file, ts->bl_data.bl_status, + ts->bl_data.bl_error, timeout);) + if (timeout) + goto loader_exit; + + /* send bootload firmware load blocks */ + printk(KERN_INFO"%s: Send BL Blocks\n", __func__); + while (cyttsp_fw[rec].Command == CY_BL_WRITE_BLK) { + DBG2(printk(KERN_INFO "%s:" + "BL DNLD Rec=% 3d Len=% 3d Addr=%04X\n", + __func__, + cyttsp_fw[rec].Record, cyttsp_fw[rec].Length, + cyttsp_fw[rec].Address); + ) + retval = cyttsp_wr_blk_chunks(ts, CY_REG_BASE, + cyttsp_fw[rec].Length, cyttsp_fw[rec].Block); + if (retval < 0) { + DBG(printk(KERN_INFO "%s:" + "BL fail Rec=%3d retval=%d\n", + __func__, + cyttsp_fw[rec].Record, retval); + ) + break; + } else { + cyttsp_wait_bl_ready(ts, 10, 1, 1000, + cyttsp_bl_err_status); + DBG(printk(KERN_INFO "%s: BL _LOAD " + "f=%02X s=%02X e=%02X\n", + __func__, + ts->bl_data.bl_file, ts->bl_data.bl_status, + ts->bl_data.bl_error);) + } + rec++; + } + if (retval < 0) + goto loader_exit; + + /* send bootload terminate command */ + printk(KERN_INFO"%s: Send BL Terminate\n", __func__); + cyttsp_set_bl_ready(ts, false); + retval = ttsp_write_block_data(ts, CY_REG_BASE, + cyttsp_fw[rec].Length, cyttsp_fw[rec].Block); + if (retval < 0) + goto loader_exit; + else + cyttsp_wait_bl_ready(ts, 1, 100, 100, cyttsp_bl_err_status); + +loader_exit: + /* reset TTSP Device back to bootloader mode */ + retval = cyttsp_soft_reset(ts); + + return retval; +} + +static int cyttsp_loader(struct cyttsp *ts) +{ + int retval; + + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + + retval = cyttsp_load_bl_regs(ts); + if (retval < 0) + return retval; + + printk(KERN_INFO "%s:" + "blttsp=0x%02X%02X flttsp=0x%02X%02X force=%d\n", + __func__, + ts->bl_data.ttspver_hi, ts->bl_data.ttspver_lo, + cyttsp_fw_tts_verh, cyttsp_fw_tts_verl, + ts->platform_data->use_force_fw_update); + printk(KERN_INFO "%s:" + "blappid=0x%02X%02X flappid=0x%02X%02X\n", + __func__, + ts->bl_data.appid_hi, ts->bl_data.appid_lo, + cyttsp_fw_app_idh, cyttsp_fw_app_idl); + printk(KERN_INFO "%s:" + "blappver=0x%02X%02X flappver=0x%02X%02X\n", + __func__, + ts->bl_data.appver_hi, ts->bl_data.appver_lo, + cyttsp_fw_app_verh, cyttsp_fw_app_verl); + printk(KERN_INFO "%s:" + "blcid=0x%02X%02X%02X flcid=0x%02X%02X%02X\n", + __func__, + ts->bl_data.cid_0, ts->bl_data.cid_1, ts->bl_data.cid_2, + cyttsp_fw_cid_0, cyttsp_fw_cid_1, cyttsp_fw_cid_2); + + if (CY_DIFF(ts->bl_data.ttspver_hi, cyttsp_fw_tts_verh) || + CY_DIFF(ts->bl_data.ttspver_lo, cyttsp_fw_tts_verl) || + CY_DIFF(ts->bl_data.appid_hi, cyttsp_fw_app_idh) || + CY_DIFF(ts->bl_data.appid_lo, cyttsp_fw_app_idl) || + CY_DIFF(ts->bl_data.appver_hi, cyttsp_fw_app_verh) || + CY_DIFF(ts->bl_data.appver_lo, cyttsp_fw_app_verl) || + CY_DIFF(ts->bl_data.cid_0, cyttsp_fw_cid_0) || + CY_DIFF(ts->bl_data.cid_1, cyttsp_fw_cid_1) || + CY_DIFF(ts->bl_data.cid_2, cyttsp_fw_cid_2) || + ts->platform_data->use_force_fw_update) { + /* load new app into TTSP Device */ + cyttsp_setup_to_timer(ts); + ts->platform_data->power_state = CY_LDR_STATE; + retval = cyttsp_load_app(ts); + cyttsp_kill_to_timer(ts); + + } else { + /* firmware file is a match with firmware in the TTSP device */ + DBG(printk(KERN_INFO "%s: FW matches - no loader\n", __func__);) + } + + if (retval < 0) + return retval; + + return retval; +} + diff --git a/drivers/input/touchscreen/cyttsp_spi.c b/drivers/input/touchscreen/cyttsp_spi.c new file mode 100755 index 00000000000..d4f7ffeed1b --- /dev/null +++ b/drivers/input/touchscreen/cyttsp_spi.c @@ -0,0 +1,302 @@ +/* Source for: + * Cypress TrueTouch(TM) Standard Product I2C touchscreen driver. + * drivers/input/touchscreen/cyttsp_spi.c + * + * Copyright (C) 2009-2011 Cypress Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Cypress reserves the right to make changes without further notice + * to the materials described herein. Cypress does not assume any + * liability arising out of the application described herein. + * + * Contact Cypress Semiconductor at www.cypress.com + * + */ + +#include +#include +#include +#include +#include +#include "cyttsp_core.h" + +#define DBG(x) + +#define CY_SPI_WR_OP 0x00 /* r/~w */ +#define CY_SPI_RD_OP 0x01 +#define CY_SPI_CMD_BYTES 4 +#define CY_SPI_SYNC_BYTE 2 +#define CY_SPI_SYNC_ACK1 0x62 +#define CY_SPI_SYNC_ACK2 0x9D +#define CY_SPI_DATA_SIZE 128 +#define CY_SPI_DATA_BUF_SIZE (CY_SPI_CMD_BYTES + CY_SPI_DATA_SIZE) +#define CY_SPI_BITS_PER_WORD 8 + +struct cyttsp_spi { + struct cyttsp_bus_ops ops; + struct spi_device *spi_client; + void *ttsp_client; + u8 wr_buf[CY_SPI_DATA_BUF_SIZE]; + u8 rd_buf[CY_SPI_DATA_BUF_SIZE]; +}; + +static int cyttsp_spi_xfer_(u8 op, struct cyttsp_spi *ts_spi, + u8 reg, u8 *buf, int length) +{ + struct spi_message msg; + struct spi_transfer xfer[2]; + u8 *wr_buf = ts_spi->wr_buf; + u8 *rd_buf = ts_spi->rd_buf; + int retval; + DBG(printk(KERN_INFO "%s: Enter\n", __func__);) + if (length > CY_SPI_DATA_SIZE) { + printk(KERN_ERR "%s: length %d is too big.\n", + __func__, length); + return -EINVAL; + } + DBG(printk(KERN_INFO "%s: OP=%s length=%d\n", __func__, + op == CY_SPI_RD_OP ? "Read" : "Write", length);) + + memset(wr_buf, 0, CY_SPI_DATA_BUF_SIZE); + memset(rd_buf, 0, CY_SPI_DATA_BUF_SIZE); + + wr_buf[0] = 0x00; /* header byte 0 */ + wr_buf[1] = 0xFF; /* header byte 1 */ + wr_buf[2] = reg; /* reg index */ + wr_buf[3] = op; /* r/~w */ + if (op == CY_SPI_WR_OP) + memcpy(wr_buf + CY_SPI_CMD_BYTES, buf, length); + DBG( + if (op == CY_SPI_RD_OP) + memset(rd_buf, CY_SPI_SYNC_NACK, CY_SPI_DATA_BUF_SIZE);) + DBG( + for (i = 0; i < (length + CY_SPI_CMD_BYTES); i++) { + if ((op == CY_SPI_RD_OP) && (i < CY_SPI_CMD_BYTES)) + printk(KERN_INFO "%s: read op. wr[%d]:0x%02x\n", + __func__, i, wr_buf[i]); + if (op == CY_SPI_WR_OP) + printk(KERN_INFO "%s: write op. wr[%d]:0x%02x\n", + __func__, i, wr_buf[i]); + }) + + memset((void *)xfer, 0, sizeof(xfer)); + spi_message_init(&msg); + xfer[0].tx_buf = wr_buf; + xfer[0].rx_buf = rd_buf; + if (op == CY_SPI_WR_OP) { + xfer[0].len = length + CY_SPI_CMD_BYTES; + spi_message_add_tail(&xfer[0], &msg); + } else if (op == CY_SPI_RD_OP) { + xfer[0].len = CY_SPI_CMD_BYTES; + spi_message_add_tail(&xfer[0], &msg); + + xfer[1].rx_buf = buf; + xfer[1].len = length; + spi_message_add_tail(&xfer[1], &msg); + } + + retval = spi_sync(ts_spi->spi_client, &msg); + if (retval < 0) { + printk(KERN_ERR "%s: spi sync error %d, len=%d, op=%d\n", + __func__, retval, xfer[1].len, op); + retval = 0; + } + + if ((rd_buf[CY_SPI_SYNC_BYTE] == CY_SPI_SYNC_ACK1) && + (rd_buf[CY_SPI_SYNC_BYTE+1] == CY_SPI_SYNC_ACK2)) + retval = 0; + else { + DBG( + for (i = 0; i < (CY_SPI_CMD_BYTES); i++) + printk(KERN_INFO "%s: test rd_buf[%d]:0x%02x\n", + __func__, i, rd_buf[i]); + for (i = 0; i < (length); i++) + printk(KERN_INFO "%s: test buf[%d]:0x%02x\n", + __func__, i, buf[i]);) + retval = 1; + } + return retval; +} + +static int cyttsp_spi_xfer(u8 op, struct cyttsp_spi *ts, + u8 reg, u8 *buf, int length) +{ + int tries; + int retval; + DBG(printk(KERN_INFO "%s: Enter\n", __func__);) + + if (op == CY_SPI_RD_OP) { + for (tries = CY_NUM_RETRY; tries; tries--) { + retval = cyttsp_spi_xfer_(op, ts, reg, buf, length); + if (retval == 0) + break; + else + msleep(10); + } + } else { + retval = cyttsp_spi_xfer_(op, ts, reg, buf, length); + } + return retval; +} + +static s32 ttsp_spi_read_block_data(void *handle, u8 addr, + u8 length, void *data) +{ + int retval; + struct cyttsp_spi *ts = container_of(handle, struct cyttsp_spi, ops); + + DBG(printk(KERN_INFO "%s: Enter\n", __func__);) + + retval = cyttsp_spi_xfer(CY_SPI_RD_OP, ts, addr, data, length); + if (retval < 0) + printk(KERN_ERR "%s: ttsp_spi_read_block_data failed\n", + __func__); + + /* Do not print the above error if the data sync bytes were not found. + This is a normal condition for the bootloader loader startup and need + to retry until data sync bytes are found. */ + if (retval > 0) + retval = -1; /* now signal fail; so retry can be done */ + + return retval; +} + +static s32 ttsp_spi_write_block_data(void *handle, u8 addr, + u8 length, const void *data) +{ + int retval; + struct cyttsp_spi *ts = container_of(handle, struct cyttsp_spi, ops); + + DBG(printk(KERN_INFO "%s: Enter\n", __func__);) + + retval = cyttsp_spi_xfer(CY_SPI_WR_OP, ts, addr, (void *)data, length); + if (retval < 0) + printk(KERN_ERR "%s: ttsp_spi_write_block_data failed\n", + __func__); + + /* Do not print the above error if the data sync bytes were not found. + This is a normal condition for the bootloader loader startup and need + to retry until data sync bytes are found. */ + if (retval > 0) + retval = -1; /* now signal fail; so retry can be done */ + + return retval; +} + +static s32 ttsp_spi_tch_ext(void *handle, void *values) +{ + int retval = 0; + struct cyttsp_spi *ts = container_of(handle, struct cyttsp_spi, ops); + + DBG(printk(KERN_INFO "%s: Enter\n", __func__);) + + /* Add custom touch extension handling code here */ + /* set: retval < 0 for any returned system errors, + retval = 0 if normal touch handling is required, + retval > 0 if normal touch handling is *not* required */ + if (!ts || !values) + retval = -EIO; + + return retval; +} + +static int __devinit cyttsp_spi_probe(struct spi_device *spi) +{ + struct cyttsp_spi *ts_spi; + int retval; + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + + /* Set up SPI*/ + spi->bits_per_word = CY_SPI_BITS_PER_WORD; + spi->mode = SPI_MODE_0; + retval = spi_setup(spi); + if (retval < 0) { + printk(KERN_ERR "%s: SPI setup error %d\n", __func__, retval); + return retval; + } + ts_spi = kzalloc(sizeof(*ts_spi), GFP_KERNEL); + if (ts_spi == NULL) { + printk(KERN_ERR "%s: Error, kzalloc\n", __func__); + retval = -ENOMEM; + goto error_alloc_data_failed; + } + ts_spi->spi_client = spi; + dev_set_drvdata(&spi->dev, ts_spi); + ts_spi->ops.write = ttsp_spi_write_block_data; + ts_spi->ops.read = ttsp_spi_read_block_data; + ts_spi->ops.ext = ttsp_spi_tch_ext; + + ts_spi->ttsp_client = cyttsp_core_init(&ts_spi->ops, &spi->dev); + if (!ts_spi->ttsp_client) { + retval = -ENODEV; + goto ttsp_core_err; + } + printk(KERN_INFO "%s: Successful registration %s\n", + __func__, CY_SPI_NAME); + + return 0; + +ttsp_core_err: + kfree(ts_spi); +error_alloc_data_failed: + return retval; +} + +/* registered in driver struct */ +static int __devexit cyttsp_spi_remove(struct spi_device *spi) +{ + struct cyttsp_spi *ts_spi = dev_get_drvdata(&spi->dev); + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + cyttsp_core_release(ts_spi->ttsp_client); + kfree(ts_spi); + return 0; +} + + +static struct spi_driver cyttsp_spi_driver = { + .driver = { + .name = CY_SPI_NAME, + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = cyttsp_spi_probe, + .remove = __devexit_p(cyttsp_spi_remove), +}; + +static int __init cyttsp_spi_init(void) +{ + int err; + + err = spi_register_driver(&cyttsp_spi_driver); + printk(KERN_INFO "%s: Cypress TrueTouch(R) Standard Product SPI " + "Touchscreen Driver (Built %s @ %s) returned %d\n", + __func__, __DATE__, __TIME__, err); + + return err; +} +module_init(cyttsp_spi_init); + +static void __exit cyttsp_spi_exit(void) +{ + spi_unregister_driver(&cyttsp_spi_driver); + printk(KERN_INFO "%s: module exit\n", __func__); +} +module_exit(cyttsp_spi_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product SPI driver"); +MODULE_AUTHOR("Cypress"); + diff --git a/include/linux/cyttsp.h b/include/linux/cyttsp.h new file mode 100755 index 00000000000..38ef1236d7a --- /dev/null +++ b/include/linux/cyttsp.h @@ -0,0 +1,114 @@ +/* Header file for: + * Cypress TrueTouch(TM) Standard Product I2C touchscreen driver. + * include/linux/cyttsp.h + * + * Copyright (C) 2009-2011 Cypress Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Cypress reserves the right to make changes without further notice + * to the materials described herein. Cypress does not assume any + * liability arising out of the application described herein. + * + * Contact Cypress Semiconductor at www.cypress.com + * + */ +#include + +#ifndef _CYTTSP_H_ +#define _CYTTSP_H_ + +#include + +#define CY_SPI_NAME "cyttsp-spi" +#define CY_I2C_NAME "cyttsp-i2c" +/* Scan Type selection for finger and/or stylus activation */ +#define CY_SCN_TYP_DFLT 0x01 /* finger only mutual scan */ +/* Active Power state scanning/processing refresh interval */ +#define CY_ACT_INTRVL_DFLT 0x00 /* ms */ +/* touch timeout for the Active power */ +#define CY_TCH_TMOUT_DFLT 0x64 /* ms */ +/* Low Power state scanning/processing refresh interval */ +#define CY_LP_INTRVL_DFLT 0x32 /* ms */ +/* + *defines for Gen2 (Txx2xx); Gen3 (Txx3xx) + * use these defines to set cyttsp_platform_data.gen in board config file + */ +enum cyttsp_gen { + CY_GEN2, + CY_GEN3, +}; +/* + * Active distance in pixels for a gesture to be reported + * if set to 0, then all gesture movements are reported + * Valid range is 0 - 15 + */ +#define CY_ACT_DIST_DFLT 8 +#define CY_ACT_DIST CY_ACT_DIST_DFLT +#define CY_ACT_DIST_BITS 0x0F +/* max retries for read/write ops */ +#define CY_NUM_RETRY 6 + +enum cyttsp_gest { + CY_GEST_GRP_NONE = 0, + CY_GEST_GRP1 = 0x10, + CY_GEST_GRP2 = 0x20, + CY_GEST_GRP3 = 0x40, + CY_GEST_GRP4 = 0x80, +}; + +enum cyttsp_powerstate { + CY_IDLE_STATE, /* IC cannot be reached */ + CY_READY_STATE, /* pre-operational; ready to go to ACTIVE */ + CY_ACTIVE_STATE, /* app is running, IC is scanning */ + CY_LOW_PWR_STATE, /* not currently used */ + CY_SLEEP_STATE, /* app is running, IC is idle */ + CY_BL_STATE, /* bootloader is running */ + CY_LDR_STATE, /* loader is running */ + CY_SYSINFO_STATE, /* Switching to SysInfo mode */ + CY_INVALID_STATE /* always last in the list */ +}; + +struct cyttsp_platform_data { + u32 maxx; + u32 maxy; + u32 flags; + enum cyttsp_gen gen; + unsigned use_st:1; + unsigned use_mt:1; + unsigned use_trk_id:1; + unsigned use_hndshk:1; + unsigned use_timer:1; + unsigned use_sleep:1; + unsigned use_gestures:1; + unsigned use_load_file:1; + unsigned use_force_fw_update:1; + unsigned use_virtual_keys:1; + enum cyttsp_powerstate power_state; + u8 gest_set; + u8 scn_typ; /* finger and/or stylus scanning */ + u8 act_intrvl; /* Active refresh interval; ms */ + u8 tch_tmout; /* Active touch timeout; ms */ + u8 lp_intrvl; /* Low power refresh interval; ms */ + int (*wakeup)(void); + int (*init)(int on_off); + void (*mt_sync)(struct input_dev *); + char *name; + s16 irq_gpio; + s16 rst_gpio; + bool invert; +}; + +#endif /* _CYTTSP_H_ */ -- cgit v1.2.3 From b1452519c321c5d526709070d8ff63db4a069c87 Mon Sep 17 00:00:00 2001 From: Virupax Sadashivpetimath Date: Thu, 22 Sep 2011 16:39:54 +0530 Subject: input:misc: Add accessory driver Add driver for the accessory detection block of the ab5500 PMIC. The common functions from the 8500 accessory driver are moved to abx500-accdet generic driver. This generic driver uses callbacks registerd from the 8500 and 5500 specific driver to work as a accessory driver for a perticluar platform. Signed-off-by: Virupax Sadashivpetimath Signed-off-by: Robert Marklund --- arch/arm/mach-ux500/include/mach/ab8500-accdet.h | 96 -- arch/arm/mach-ux500/include/mach/abx500-accdet.h | 357 +++++++ drivers/input/misc/Kconfig | 7 + drivers/input/misc/Makefile | 3 +- drivers/input/misc/ab5500-accdet.c | 363 +++++++ drivers/input/misc/ab8500-accdet.c | 1147 ++-------------------- drivers/input/misc/abx500-accdet.c | 962 ++++++++++++++++++ 7 files changed, 1752 insertions(+), 1183 deletions(-) delete mode 100644 arch/arm/mach-ux500/include/mach/ab8500-accdet.h create mode 100644 arch/arm/mach-ux500/include/mach/abx500-accdet.h create mode 100644 drivers/input/misc/ab5500-accdet.c create mode 100644 drivers/input/misc/abx500-accdet.c diff --git a/arch/arm/mach-ux500/include/mach/ab8500-accdet.h b/arch/arm/mach-ux500/include/mach/ab8500-accdet.h deleted file mode 100644 index b1b157e317e..00000000000 --- a/arch/arm/mach-ux500/include/mach/ab8500-accdet.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright ST-Ericsson 2011. - * - * Author: Jarmo K. Kuronen for ST Ericsson. - * Licensed under GPLv2. - */ - -#ifndef _AB8500_ACCDET_H -#define _AB8500_ACCDET_H - -/* -* Debounce times for AccDet1 input -* @0x880 [2:0] -*/ -#define ACCDET1_DB_0ms 0x00 -#define ACCDET1_DB_10ms 0x01 -#define ACCDET1_DB_20ms 0x02 -#define ACCDET1_DB_30ms 0x03 -#define ACCDET1_DB_40ms 0x04 -#define ACCDET1_DB_50ms 0x05 -#define ACCDET1_DB_60ms 0x06 -#define ACCDET1_DB_70ms 0x07 - -/* -* Voltage threshold for AccDet1 input -* @0x880 [6:3] -*/ -#define ACCDET1_TH_1100mV 0x40 -#define ACCDET1_TH_1200mV 0x48 -#define ACCDET1_TH_1300mV 0x50 -#define ACCDET1_TH_1400mV 0x58 -#define ACCDET1_TH_1500mV 0x60 -#define ACCDET1_TH_1600mV 0x68 -#define ACCDET1_TH_1700mV 0x70 -#define ACCDET1_TH_1800mV 0x78 - -/* -* Voltage threshold for AccDet21 input -* @0x881 [3:0] -*/ -#define ACCDET21_TH_300mV 0x00 -#define ACCDET21_TH_400mV 0x01 -#define ACCDET21_TH_500mV 0x02 -#define ACCDET21_TH_600mV 0x03 -#define ACCDET21_TH_700mV 0x04 -#define ACCDET21_TH_800mV 0x05 -#define ACCDET21_TH_900mV 0x06 -#define ACCDET21_TH_1000mV 0x07 -#define ACCDET21_TH_1100mV 0x08 -#define ACCDET21_TH_1200mV 0x09 -#define ACCDET21_TH_1300mV 0x0a -#define ACCDET21_TH_1400mV 0x0b -#define ACCDET21_TH_1500mV 0x0c -#define ACCDET21_TH_1600mV 0x0d -#define ACCDET21_TH_1700mV 0x0e -#define ACCDET21_TH_1800mV 0x0f - -/* -* Voltage threshold for AccDet22 input -* @0x881 [7:4] -*/ -#define ACCDET22_TH_300mV 0x00 -#define ACCDET22_TH_400mV 0x10 -#define ACCDET22_TH_500mV 0x20 -#define ACCDET22_TH_600mV 0x30 -#define ACCDET22_TH_700mV 0x40 -#define ACCDET22_TH_800mV 0x50 -#define ACCDET22_TH_900mV 0x60 -#define ACCDET22_TH_1000mV 0x70 -#define ACCDET22_TH_1100mV 0x80 -#define ACCDET22_TH_1200mV 0x90 -#define ACCDET22_TH_1300mV 0xa0 -#define ACCDET22_TH_1400mV 0xb0 -#define ACCDET22_TH_1500mV 0xc0 -#define ACCDET22_TH_1600mV 0xd0 -#define ACCDET22_TH_1700mV 0xe0 -#define ACCDET22_TH_1800mV 0xf0 - -/** - * struct ab8500_accdet_platform_data - AV Accessory detection specific - * platform data - * @btn_keycode Keycode to be sent when accessory button is pressed. - * @accdet1_dbth Debounce time + voltage threshold for accdet 1 input. - * @accdet2122_th Voltage thresholds for accdet21 and accdet22 inputs. - * @is_detection_inverted Whether the accessory insert/removal, button - * press/release irq's are inverted. - */ -struct ab8500_accdet_platform_data { - int btn_keycode; - u8 accdet1_dbth; - u8 accdet2122_th; - unsigned int video_ctrl_gpio; - bool is_detection_inverted; -}; - -#endif /* _AB8500_ACCDET_H */ diff --git a/arch/arm/mach-ux500/include/mach/abx500-accdet.h b/arch/arm/mach-ux500/include/mach/abx500-accdet.h new file mode 100644 index 00000000000..914a7087ddb --- /dev/null +++ b/arch/arm/mach-ux500/include/mach/abx500-accdet.h @@ -0,0 +1,357 @@ +/* + * Copyright ST-Ericsson 2011. + * + * Author: Jarmo K. Kuronen for ST Ericsson. + * Licensed under GPLv2. + */ + +#ifndef _ABx500_ACCDET_H +#define _ABx500_ACCDET_H + +/* +* Debounce times for AccDet1 input +* @0x880 [2:0] +*/ +#define ACCDET1_DB_0ms 0x00 +#define ACCDET1_DB_10ms 0x01 +#define ACCDET1_DB_20ms 0x02 +#define ACCDET1_DB_30ms 0x03 +#define ACCDET1_DB_40ms 0x04 +#define ACCDET1_DB_50ms 0x05 +#define ACCDET1_DB_60ms 0x06 +#define ACCDET1_DB_70ms 0x07 + +/* +* Voltage threshold for AccDet1 input +* @0x880 [6:3] +*/ +#define ACCDET1_TH_1100mV 0x40 +#define ACCDET1_TH_1200mV 0x48 +#define ACCDET1_TH_1300mV 0x50 +#define ACCDET1_TH_1400mV 0x58 +#define ACCDET1_TH_1500mV 0x60 +#define ACCDET1_TH_1600mV 0x68 +#define ACCDET1_TH_1700mV 0x70 +#define ACCDET1_TH_1800mV 0x78 + +/* +* Voltage threshold for AccDet21 input +* @0x881 [3:0] +*/ +#define ACCDET21_TH_300mV 0x00 +#define ACCDET21_TH_400mV 0x01 +#define ACCDET21_TH_500mV 0x02 +#define ACCDET21_TH_600mV 0x03 +#define ACCDET21_TH_700mV 0x04 +#define ACCDET21_TH_800mV 0x05 +#define ACCDET21_TH_900mV 0x06 +#define ACCDET21_TH_1000mV 0x07 +#define ACCDET21_TH_1100mV 0x08 +#define ACCDET21_TH_1200mV 0x09 +#define ACCDET21_TH_1300mV 0x0a +#define ACCDET21_TH_1400mV 0x0b +#define ACCDET21_TH_1500mV 0x0c +#define ACCDET21_TH_1600mV 0x0d +#define ACCDET21_TH_1700mV 0x0e +#define ACCDET21_TH_1800mV 0x0f + +/* +* Voltage threshold for AccDet22 input +* @0x881 [7:4] +*/ +#define ACCDET22_TH_300mV 0x00 +#define ACCDET22_TH_400mV 0x10 +#define ACCDET22_TH_500mV 0x20 +#define ACCDET22_TH_600mV 0x30 +#define ACCDET22_TH_700mV 0x40 +#define ACCDET22_TH_800mV 0x50 +#define ACCDET22_TH_900mV 0x60 +#define ACCDET22_TH_1000mV 0x70 +#define ACCDET22_TH_1100mV 0x80 +#define ACCDET22_TH_1200mV 0x90 +#define ACCDET22_TH_1300mV 0xa0 +#define ACCDET22_TH_1400mV 0xb0 +#define ACCDET22_TH_1500mV 0xc0 +#define ACCDET22_TH_1600mV 0xd0 +#define ACCDET22_TH_1700mV 0xe0 +#define ACCDET22_TH_1800mV 0xf0 + +/* +* Voltage threshold for AccDet1 input +* @0x880 [6:3] +*/ +#define ACCDET1_TH_300mV 0x00 +#define ACCDET1_TH_400mV 0x01 +#define ACCDET1_TH_500mV 0x02 +#define ACCDET1_TH_600mV 0x03 +#define ACCDET1_TH_700mV 0x04 +#define ACCDET1_TH_800mV 0x05 +#define ACCDET1_TH_900mV 0x06 +#define ACCDET1_TH_1000mV 0x07 + +#define MAX_DET_COUNT 10 +#define MAX_VOLT_DIFF 30 +#define MIN_MIC_POWER -100 + +/** + * struct abx500_accdet_platform_data - AV Accessory detection specific + * platform data + * @btn_keycode Keycode to be sent when accessory button is pressed. + * @accdet1_dbth Debounce time + voltage threshold for accdet 1 input. + * @accdet2122_th Voltage thresholds for accdet21 and accdet22 inputs. + * @is_detection_inverted Whether the accessory insert/removal, button + * press/release irq's are inverted. + */ +struct abx500_accdet_platform_data { + int btn_keycode; + u8 accdet1_dbth; + u8 accdet2122_th; + unsigned int video_ctrl_gpio; + bool is_detection_inverted; +}; + +/* Enumerations */ + +/** + * @JACK_TYPE_UNSPECIFIED Not known whether any accessories are connected. + * @JACK_TYPE_DISCONNECTED No accessories connected. + * @JACK_TYPE_CONNECTED Accessory is connected but functionality was unable to + * detect the actual type. In this mode, possible button events are reported. + * @JACK_TYPE_HEADPHONE Headphone type of accessory (spkrs only) connected + * @JACK_TYPE_HEADSET Headset type of accessory (mic+spkrs) connected + * @JACK_TYPE_CARKIT Carkit type of accessory connected + * @JACK_TYPE_OPENCABLE Open cable connected + * @JACK_TYPE_CVIDEO CVideo type of accessory connected. + */ +enum accessory_jack_type { + JACK_TYPE_UNSPECIFIED, + JACK_TYPE_DISCONNECTED, + JACK_TYPE_CONNECTED, + JACK_TYPE_HEADPHONE, + JACK_TYPE_HEADSET, + JACK_TYPE_CARKIT, + JACK_TYPE_OPENCABLE, + JACK_TYPE_CVIDEO +}; + +/** + * @BUTTON_UNK Button state not known + * @BUTTON_PRESSED Button "down" + * @BUTTON_RELEASED Button "up" + */ +enum accessory_button_state { + BUTTON_UNK, + BUTTON_PRESSED, + BUTTON_RELEASED +}; + +/** + * @PLUG_IRQ Interrupt gen. when accessory plugged in + * @UNPLUG_IRQ Interrupt gen. when accessory plugged out + * @BUTTON_PRESS_IRQ Interrupt gen. when accessory button pressed. + * @BUTTON_RELEASE_IRQ Interrupt gen. when accessory button released. + */ +enum accessory_irq { + PLUG_IRQ, + UNPLUG_IRQ, + BUTTON_PRESS_IRQ, + BUTTON_RELEASE_IRQ, +}; + +/** + * Enumerates the op. modes of the avcontrol switch + * @AUDIO_IN Audio input is selected + * @VIDEO_OUT Video output is selected + * @NOT_SET The av-switch control signal is disconnected. + */ +enum accessory_avcontrol_dir { + AUDIO_IN, + VIDEO_OUT, + NOT_SET, +}; + +/** + * @REGULATOR_VAUDIO v-audio regulator + * @REGULATOR_VAMIC1 v-amic1 regulator + * @REGULATOR_AVSWITCH Audio/Video select switch regulator + * @REGULATOR_ALL All regulators combined + */ +enum accessory_regulator { + REGULATOR_NONE = 0x0, + REGULATOR_VAUDIO = 0x1, + REGULATOR_VAMIC1 = 0x2, + REGULATOR_AVSWITCH = 0x4, + REGULATOR_ALL = 0xFF +}; + +/* Structures */ + +/** + * Describes an interrupt + * @irq interrupt identifier + * @name name of the irq in platform data + * @isr interrupt service routine + * @register are we currently registered to receive interrupts from this source. + */ +struct accessory_irq_descriptor { + enum accessory_irq irq; + const char *name; + irq_handler_t isr; + int registered; +}; + +/** + * Encapsulates info of single regulator. + * @id regulator identifier + * @name name of the regulator + * @enabled flag indicating whether regu is currently enabled. + * @handle regulator handle + */ +struct accessory_regu_descriptor { + enum accessory_regulator id; + const char *name; + int enabled; + struct regulator *handle; +}; + +/** + * Defines attributes for accessory detection operation. + * @typename type as string + * @type Type of accessory this task tests + * @req_det_count How many times this particular type of accessory + * needs to be detected in sequence in order to accept. Multidetection + * implemented to avoid false detections during plug-in. + * @meas_mv Should ACCDETECT2 input voltage be measured just before + * making the decision or can cached voltage be used instead. + * @minvol minimum voltage (mV) for decision + * @maxvol maximum voltage (mV) for decision + */ +struct accessory_detect_task { + const char *typename; + enum accessory_jack_type type; + int req_det_count; + int meas_mv; + int minvol; + int maxvol; +}; + +/** + * Device data, capsulates all relevant device data structures. + * + * @pdev: pointer to platform device + * @pdata: Platform data + * @gpadc: interface for ADC data + * @irq_work_queue: Work queue for deferred interrupt processing + * @detect_work: work item to perform detection work + * @unplug_irq_work: work item to process unplug event + * @init_work: work item to process initialization work. + * @btn_input_dev: button input device used to report btn presses + * @btn_state: Current state of accessory button + * @jack_type: type of currently connected accessory + * @reported_jack_type: previously reported jack type. + * @jack_type_temp: temporary storage for currently connected accessory + * @jack_det_count: counter how many times in sequence the accessory + * type detection has produced same result. + * @total_jack_det_count: after plug-in irq, how many times detection + * has totally been made in order to detect the accessory type + * @detect_jiffies: Used to save timestamp when detection was made. Timestamp + * used to filter out spurious button presses that might occur during the + * plug-in procedure. + * @accdet1_th_set: flag to indicate whether accdet1 threshold and debounce + * times are configured + * @accdet2_th_set: flag to indicate whether accdet2 thresholds are configured + * @gpio35_dir_set: flag to indicate whether GPIO35 (VIDEOCTRL) direction + * has been configured. + * @irq_desc_norm: irq's as specified in the initial versions of ab + * @irq_desc_inverted: irq's inverted as seen in the latest versions of ab + * @no_irqs: Total number of irq's + * @regu_desc: Pointer to the regulator descriptors. + * @no_of_regu_desc: Total nummber of descriptors. + * @config_accdetect2_hw: Callback for configuring accdet2 comparator. + * @config_accdetect1_hw: Callback for configuring accdet1 comparator. + * @detect_plugged_in: Callback to detect type of accessory connected. + * @meas_voltage_stable: Callback to read present accdet voltage. + * @config_hw_test_basic_carkit: Callback to configure hw for carkit + * detect. + * @turn_of_accdet_comparator: Call back to turn off comparators. + * @turn_on_accdet_comparator: Call back to turn ON comparators. + * @accdet_abx500_gpadc_get Call back to get a instance of the + * GPADC convertor. + * @config_hw_test_plug_connected: Call back to configure the hw for + * accessory detection. + * @set_av_switch: Call back to configure the switch for tvout or audioout. + * @startup: Call back to do the Chip specific initialization. + * @get_platform_data: call to get platform specific data. + */ +struct abx500_ad { + struct platform_device *pdev; + struct abx500_accdet_platform_data *pdata; + void *gpadc; + struct workqueue_struct *irq_work_queue; + + struct delayed_work detect_work; + struct delayed_work unplug_irq_work; + struct delayed_work init_work; + + struct input_dev *btn_input_dev; + enum accessory_button_state btn_state; + + enum accessory_jack_type jack_type; + enum accessory_jack_type reported_jack_type; + enum accessory_jack_type jack_type_temp; + + int jack_det_count; + int total_jack_det_count; + + unsigned long detect_jiffies; + + int accdet1_th_set; + int accdet2_th_set; + int gpio35_dir_set; + + struct accessory_irq_descriptor *irq_desc_norm; + struct accessory_irq_descriptor *irq_desc_inverted; + int no_irqs; + + struct accessory_regu_descriptor *regu_desc; + int no_of_regu_desc; + + bool tv_out_connected; + + void (*config_accdetect2_hw)(struct abx500_ad *, int); + void (*config_accdetect1_hw)(struct abx500_ad *, int); + int (*detect_plugged_in)(struct abx500_ad *); + int (*meas_voltage_stable)(struct abx500_ad *); + void (*config_hw_test_basic_carkit)(struct abx500_ad *, int); + void (*turn_off_accdet_comparator)(struct platform_device *pdev); + void (*turn_on_accdet_comparator)(struct platform_device *pdev); + void* (*accdet_abx500_gpadc_get)(void); + void (*config_hw_test_plug_connected)(struct abx500_ad *dd, int enable); + void (*set_av_switch)(struct abx500_ad *dd, + enum accessory_avcontrol_dir dir); + int (*startup)(struct abx500_ad *dd); + struct abx500_accdet_platform_data * + (*get_platform_data)(struct platform_device *pdev); +}; + +/* Forward declarations */ +extern irqreturn_t unplug_irq_handler(int irq, void *_userdata); +extern irqreturn_t plug_irq_handler(int irq, void *_userdata); +extern irqreturn_t button_press_irq_handler(int irq, void *_userdata); +extern irqreturn_t button_release_irq_handler(int irq, void *_userdata); +extern void accessory_regulator_enable(struct abx500_ad *dd, + enum accessory_regulator reg); +extern void accessory_regulator_disable(struct abx500_ad *dd, + enum accessory_regulator reg); +extern void report_jack_status(struct abx500_ad *dd); + +#ifdef CONFIG_INPUT_AB5500_ACCDET +extern struct abx500_ad ab5500_accessory_det_callbacks; +#endif + +#ifdef CONFIG_INPUT_AB8500_ACCDET +extern struct abx500_ad ab8500_accessory_det_callbacks; +#endif + +#endif /* _ABx500_ACCDET_H */ + diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 1339e97a647..d2d0fa8977e 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -29,6 +29,13 @@ config INPUT_AB8500_ACCDET Say Y here to enable AV accessory detection features for ST-Ericsson's AB8500 Mix-Sig PMIC. +config INPUT_AB5500_ACCDET + bool "AB5500 AV Accessory detection" + depends on AB5500_CORE && AB5500_GPADC + help + Say Y here to enable AV accessory detection features for ST-Ericsson's + AB5500 Mix-Sig PMIC. + config INPUT_AB8500_PONKEY tristate "AB5500/AB8500 Pon (PowerOn) Key" depends on AB5500_CORE || AB8500_CORE diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index f64914025ac..3903f026f70 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -5,7 +5,8 @@ # Each configuration option enables a list of files. obj-$(CONFIG_INPUT_88PM860X_ONKEY) += 88pm860x_onkey.o -obj-$(CONFIG_INPUT_AB8500_ACCDET) += ab8500-accdet.o +obj-$(CONFIG_INPUT_AB8500_ACCDET) += abx500-accdet.o ab8500-accdet.o +obj-$(CONFIG_INPUT_AB5500_ACCDET) += abx500-accdet.o ab5500-accdet.o obj-$(CONFIG_INPUT_AB8500_PONKEY) += ab8500-ponkey.o obj-$(CONFIG_INPUT_AD714X) += ad714x.o obj-$(CONFIG_INPUT_AD714X_I2C) += ad714x-i2c.o diff --git a/drivers/input/misc/ab5500-accdet.c b/drivers/input/misc/ab5500-accdet.c new file mode 100644 index 00000000000..9858d93a475 --- /dev/null +++ b/drivers/input/misc/ab5500-accdet.c @@ -0,0 +1,363 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Jarmo K. Kuronen + * for ST-Ericsson. + * + * License terms: GPL V2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * Register definition for accessory detection. + */ +#define AB5500_REGU_CTRL1_SPARE_REG 0x84 +#define AB5500_ACC_DET_DB1_REG 0x20 +#define AB5500_ACC_DET_DB2_REG 0x21 +#define AB5500_ACC_DET_CTRL_REG 0x23 +#define AB5500_VDENC_CTRL0 0x80 + +#define ENABLE_TV_PLUG_DETECT 0x01 + +/* REGISTER: AB8500_ACC_DET_CTRL_REG */ +#define BITS_ACCDETCTRL2_ENA (0x20 | 0x10 | 0x08) +#define BITS_ACCDETCTRL1_ENA (0x02 | 0x01) + +/* REGISTER: AB8500_REGU_CTRL1_SPARE_REG */ +#define BIT_REGUCTRL1SPARE_VAMIC1_GROUND 0x01 + +/* REGISTER: AB8500_IT_SOURCE5_REG */ +#define BIT_ITSOURCE5_ACCDET1 0x02 + +static struct accessory_irq_descriptor ab5500_irq_desc[] = { + { + .irq = PLUG_IRQ, + .name = "acc_detedt1db_falling", + .isr = plug_irq_handler, + }, + { + .irq = UNPLUG_IRQ, + .name = "acc_detedt1db_rising", + .isr = unplug_irq_handler, + }, + { + .irq = BUTTON_PRESS_IRQ, + .name = "acc_detedt21db_falling", + .isr = button_press_irq_handler, + }, + { + .irq = BUTTON_RELEASE_IRQ, + .name = "acc_detedt21db_rising", + .isr = button_release_irq_handler, + }, +}; + +static struct accessory_regu_descriptor ab5500_regu_desc[] = { + { + .id = REGULATOR_VAMIC1, + .name = "v-amic", + }, +}; + + +/* + * configures accdet2 input on/off + */ +static void ab5500_config_accdetect2_hw(struct abx500_ad *dd, int enable) +{ + int ret = 0; + + if (!dd->accdet2_th_set) { + /* Configure accdetect21+22 thresholds */ + ret = abx500_set_register_interruptible(&dd->pdev->dev, + AB5500_BANK_FG_BATTCOM_ACC, + AB5500_ACC_DET_DB2_REG, + dd->pdata->accdet2122_th); + if (ret < 0) { + dev_err(&dd->pdev->dev, + "%s: Failed to write reg (%d).\n", __func__, + ret); + goto out; + } else { + dd->accdet2_th_set = 1; + } + } + + /* Enable/Disable accdetect21 comparators + pullup */ + ret = abx500_mask_and_set_register_interruptible( + &dd->pdev->dev, + AB5500_BANK_FG_BATTCOM_ACC, + AB5500_ACC_DET_CTRL_REG, + BITS_ACCDETCTRL2_ENA, + enable ? BITS_ACCDETCTRL2_ENA : 0); + + if (ret < 0) + dev_err(&dd->pdev->dev, "%s: Failed to update reg (%d).\n", + __func__, ret); +out: + return; +} + +/* + * configures accdet1 input on/off + */ +static void ab5500_config_accdetect1_hw(struct abx500_ad *dd, int enable) +{ + int ret; + + if (!dd->accdet1_th_set) { + ret = abx500_set_register_interruptible(&dd->pdev->dev, + AB5500_BANK_FG_BATTCOM_ACC, + AB5500_ACC_DET_DB1_REG, + dd->pdata->accdet1_dbth); + if (ret < 0) + dev_err(&dd->pdev->dev, + "%s: Failed to write reg (%d).\n", __func__, + ret); + else + dd->accdet1_th_set = 1; + } + + /* enable accdetect1 comparator */ + ret = abx500_mask_and_set_register_interruptible( + &dd->pdev->dev, + AB5500_BANK_FG_BATTCOM_ACC, + AB5500_ACC_DET_CTRL_REG, + BITS_ACCDETCTRL1_ENA, + enable ? BITS_ACCDETCTRL1_ENA : 0); + + if (ret < 0) + dev_err(&dd->pdev->dev, + "%s: Failed to update reg (%d).\n", __func__, ret); +} + +/* + * returns the high level status whether some accessory is connected (1|0). + */ +static int ab5500_detect_plugged_in(struct abx500_ad *dd) +{ + u8 value = 0; + + int status = abx500_get_register_interruptible( + &dd->pdev->dev, + AB5500_BANK_IT, + AB5500_IT_SOURCE3_REG, + &value); + if (status < 0) { + dev_err(&dd->pdev->dev, "%s: reg read failed (%d).\n", + __func__, status); + return 0; + } + + if (dd->pdata->is_detection_inverted) + return value & BIT_ITSOURCE5_ACCDET1 ? 1 : 0; + else + return value & BIT_ITSOURCE5_ACCDET1 ? 0 : 1; +} + +/* + * mic_line_voltage_stable - measures a relative stable voltage from spec. input + */ +static int ab5500_meas_voltage_stable(struct abx500_ad *dd) +{ + int iterations = 2; + int v1, v2, dv; + + v1 = ab5500_gpadc_convert((struct ab5500_gpadc *)dd->gpadc, + ACC_DETECT2); + do { + msleep(1); + --iterations; + v2 = ab5500_gpadc_convert((struct ab5500_gpadc *)dd->gpadc, + ACC_DETECT2); + dv = abs(v2 - v1); + v1 = v2; + } while (iterations > 0 && dv > MAX_VOLT_DIFF); + + return v1; +} + +/* + * configures HW so that it is possible to make decision whether + * accessory is connected or not. + */ +static void ab5500_config_hw_test_plug_connected(struct abx500_ad *dd, + int enable) +{ + dev_dbg(&dd->pdev->dev, "%s:%d\n", __func__, enable); + + /* enable mic BIAS2 */ + if (enable) + accessory_regulator_enable(dd, REGULATOR_VAMIC1); +} + +/* + * configures HW so that carkit/headset detection can be accomplished. + */ +static void ab5500_config_hw_test_basic_carkit(struct abx500_ad *dd, int enable) +{ + /* enable mic BIAS2 */ + if (enable) + accessory_regulator_disable(dd, REGULATOR_VAMIC1); +} + +static u8 acc_det_ctrl_suspend_val; + +static void ab5500_turn_off_accdet_comparator(struct platform_device *pdev) +{ + struct abx500_ad *dd = platform_get_drvdata(pdev); + + /* Turn off AccDetect comparators and pull-up */ + (void) abx500_get_register_interruptible( + &dd->pdev->dev, + AB5500_BANK_FG_BATTCOM_ACC, + AB5500_ACC_DET_CTRL_REG, + &acc_det_ctrl_suspend_val); + (void) abx500_set_register_interruptible( + &dd->pdev->dev, + AB5500_BANK_FG_BATTCOM_ACC, + AB5500_ACC_DET_CTRL_REG, + 0); +} + +static void ab5500_turn_on_accdet_comparator(struct platform_device *pdev) +{ + struct abx500_ad *dd = platform_get_drvdata(pdev); + + /* Turn on AccDetect comparators and pull-up */ + (void) abx500_set_register_interruptible( + &dd->pdev->dev, + AB5500_BANK_FG_BATTCOM_ACC, + AB5500_ACC_DET_CTRL_REG, + acc_det_ctrl_suspend_val); +} + +irqreturn_t plug_tv_connect_irq_handler(int irq, void *_userdata) +{ + struct abx500_ad *dd = _userdata; + + dev_dbg(&dd->pdev->dev, "%s: Enter (irq=%d)\n", __func__, irq); + + dd->tv_out_connected = true; + report_jack_status(dd); + + return IRQ_HANDLED; +} + +irqreturn_t plug_tv_removal_irq_handler(int irq, void *_userdata) +{ + struct abx500_ad *dd = _userdata; + + dev_dbg(&dd->pdev->dev, "%s: Enter (irq=%d)\n", __func__, irq); + + dd->tv_out_connected = false; + report_jack_status(dd); + + return IRQ_HANDLED; +} + +static void *ab5500_accdet_abx500_gpadc_get(void) +{ + return ab5500_gpadc_get("ab5500-adc.0"); +} + +static int ab5500_startup(struct abx500_ad *dd) +{ + int ret; + int irq, irq_removal; + + irq = platform_get_irq_byname(dd->pdev, + "plugTVdet"); + if (irq < 0) { + dev_err(&dd->pdev->dev, "%s: Failed to get irq plugTVdet \n", + __func__); + return irq; + } + + irq_removal = platform_get_irq_byname(dd->pdev, + "plugTVdet_removal"); + if (irq_removal < 0) { + dev_err(&dd->pdev->dev, "%s: Failed to get irq" + "plugTVdet_removal \n", __func__); + return irq_removal; + } + + ret = request_threaded_irq(irq, NULL, + plug_tv_connect_irq_handler, + IRQF_NO_SUSPEND | IRQF_SHARED, + "plugTVdet", + dd); + if (ret != 0) { + dev_err(&dd->pdev->dev, + "%s: Failed to claim irq plugTVdet (%d)\n", + __func__, ret); + return ret; + } + + ret = request_threaded_irq(irq_removal, NULL, + plug_tv_removal_irq_handler, + IRQF_NO_SUSPEND | IRQF_SHARED, + "plugTVdet_removal", + dd); + if (ret != 0) { + dev_err(&dd->pdev->dev, + "%s: Failed to claim irq plugTVdet_removal (%d)\n", + __func__, ret); + goto req_irq_fail; + } + + ret = abx500_set_register_interruptible(&dd->pdev->dev, + AB5500_BANK_VDENC, AB5500_VDENC_CTRL0, + ENABLE_TV_PLUG_DETECT); + if (ret < 0) { + dev_err(&dd->pdev->dev, + "%s: Failed to update reg (%d).\n", __func__, ret); + goto ab_write_fail; + } + + return 0; + +req_irq_fail: +ab_write_fail: + free_irq(irq, dd); + return ret; +} + +struct abx500_accdet_platform_data * + ab5500_get_platform_data(struct platform_device *pdev) +{ + return pdev->dev.platform_data; +} + +struct abx500_ad ab5500_accessory_det_callbacks = { + .irq_desc_norm = ab5500_irq_desc, + .irq_desc_inverted = NULL, + .no_irqs = ARRAY_SIZE(ab5500_irq_desc), + .regu_desc = ab5500_regu_desc, + .no_of_regu_desc = ARRAY_SIZE(ab5500_regu_desc), + .config_accdetect2_hw = ab5500_config_accdetect2_hw, + .config_accdetect1_hw = ab5500_config_accdetect1_hw, + .detect_plugged_in = ab5500_detect_plugged_in, + .meas_voltage_stable = ab5500_meas_voltage_stable, + .config_hw_test_basic_carkit = ab5500_config_hw_test_basic_carkit, + .turn_off_accdet_comparator = ab5500_turn_off_accdet_comparator, + .turn_on_accdet_comparator = ab5500_turn_on_accdet_comparator, + .accdet_abx500_gpadc_get = ab5500_accdet_abx500_gpadc_get, + .config_hw_test_plug_connected = ab5500_config_hw_test_plug_connected, + .startup = ab5500_startup, + .set_av_switch = NULL, + .get_platform_data = ab5500_get_platform_data, +}; + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/misc/ab8500-accdet.c b/drivers/input/misc/ab8500-accdet.c index 21d00b82165..369cd0ab3b3 100644 --- a/drivers/input/misc/ab8500-accdet.c +++ b/drivers/input/misc/ab8500-accdet.c @@ -11,28 +11,15 @@ * by the Free Software Foundation. */ -#include /* Needed by all modules */ -#include /* Needed for KERN_INFO */ -#include /* Needed for the macros */ -#include #include -#include -#include -#include -#include -#include -#include -#include +#include #include +#include #include #include #include -#include -#ifdef CONFIG_SND_SOC_UX500_AB8500 -#include -#else -#define ux500_ab8500_jack_report(i) -#endif +#include +#include #define MAX_DET_COUNT 10 #define MAX_VOLT_DIFF 30 @@ -81,290 +68,9 @@ #define ACCESSORY_OPENCABLE_DET_VOL_MIN 1730 #define ACCESSORY_OPENCABLE_DET_VOL_MAX 2150 -/* Macros */ - -/* - * Conviniency macros to check jack characteristics. - */ -#define jack_supports_mic(type) \ - (type == JACK_TYPE_HEADSET || type == JACK_TYPE_CARKIT) -#define jack_supports_spkr(type) \ - ((type != JACK_TYPE_DISCONNECTED) && (type != JACK_TYPE_CONNECTED)) -#define jack_supports_buttons(type) \ - ((type == JACK_TYPE_HEADSET) ||\ - (type == JACK_TYPE_CARKIT) ||\ - (type == JACK_TYPE_OPENCABLE) ||\ - (type == JACK_TYPE_CONNECTED)) - - -/* Enumerations */ - -/** - * @JACK_TYPE_UNSPECIFIED Not known whether any accessories are connected. - * @JACK_TYPE_DISCONNECTED No accessories connected. - * @JACK_TYPE_CONNECTED Accessory is connected but functionality was unable to - * detect the actual type. In this mode, possible button events are reported. - * @JACK_TYPE_HEADPHONE Headphone type of accessory (spkrs only) connected - * @JACK_TYPE_HEADSET Headset type of accessory (mic+spkrs) connected - * @JACK_TYPE_CARKIT Carkit type of accessory connected - * @JACK_TYPE_OPENCABLE Open cable connected - * @JACK_TYPE_CVIDEO CVideo type of accessory connected. - */ -enum accessory_jack_type { - JACK_TYPE_UNSPECIFIED, - JACK_TYPE_DISCONNECTED, - JACK_TYPE_CONNECTED, - JACK_TYPE_HEADPHONE, - JACK_TYPE_HEADSET, - JACK_TYPE_CARKIT, - JACK_TYPE_OPENCABLE, - JACK_TYPE_CVIDEO -}; - -/** - * @BUTTON_UNK Button state not known - * @BUTTON_PRESSED Button "down" - * @BUTTON_RELEASED Button "up" - */ -enum accessory_button_state { - BUTTON_UNK, - BUTTON_PRESSED, - BUTTON_RELEASED -}; - -/** - * @PLUG_IRQ Interrupt gen. when accessory plugged in - * @UNPLUG_IRQ Interrupt gen. when accessory plugged out - * @BUTTON_PRESS_IRQ Interrupt gen. when accessory button pressed. - * @BUTTON_RELEASE_IRQ Interrupt gen. when accessory button released. - */ -enum accessory_irq { - PLUG_IRQ, - UNPLUG_IRQ, - BUTTON_PRESS_IRQ, - BUTTON_RELEASE_IRQ -}; - -/** - * Enumerates the op. modes of the avcontrol switch - * @AUDIO_IN Audio input is selected - * @VIDEO_OUT Video output is selected - * @NOT_SET The av-switch control signal is disconnected. - */ -enum accessory_avcontrol_dir { - AUDIO_IN, - VIDEO_OUT, - NOT_SET, -}; - -/** - * @REGULATOR_VAUDIO v-audio regulator - * @REGULATOR_VAMIC1 v-amic1 regulator - * @REGULATOR_AVSWITCH Audio/Video select switch regulator - * @REGULATOR_ALL All regulators combined - */ -enum accessory_regulator { - REGULATOR_NONE = 0x0, - REGULATOR_VAUDIO = 0x1, - REGULATOR_VAMIC1 = 0x2, - REGULATOR_AVSWITCH = 0x4, - REGULATOR_ALL = 0xFF -}; - -/* Structures */ - -/** - * Describes an interrupt - * @irq interrupt identifier - * @name name of the irq in platform data - * @isr interrupt service routine - * @register are we currently registered to receive interrupts from this source. - */ -struct accessory_irq_descriptor { - enum accessory_irq irq; - const char *name; - irq_handler_t isr; - int registered; -}; - -/** - * Encapsulates info of single regulator. - * @id regulator identifier - * @name name of the regulator - * @enabled flag indicating whether regu is currently enabled. - * @handle regulator handle - */ -struct accessory_regu_descriptor { - enum accessory_regulator id; - const char *name; - int enabled; - struct regulator *handle; -}; - -/** - * Defines attributes for accessory detection operation. - * @typename type as string - * @type Type of accessory this task tests - * @req_det_count How many times this particular type of accessory - * needs to be detected in sequence in order to accept. Multidetection - * implemented to avoid false detections during plug-in. - * @meas_mv Should ACCDETECT2 input voltage be measured just before - * making the decision or can cached voltage be used instead. - * @minvol minimum voltage (mV) for decision - * @maxvol maximum voltage (mV) for decision - */ -struct accessory_detect_task { - const char *typename; - enum accessory_jack_type type; - int req_det_count; - int meas_mv; - int minvol; - int maxvol; -}; - -/** - * Device data, capsulates all relevant device data structures. - * - * @pdev pointer to platform device - * @pdata Platform data - * @gpadc interface for ADC data - * @irq_work_queue Work queue for deferred interrupt processing - * - * @detect_work work item to perform detection work - * @unplug_irq_work work item to process unplug event - * @init_work work item to process initialization work. - * - * @btn_input_dev button input device used to report btn presses - * @btn_state Current state of accessory button - * - * @jack_type type of currently connected accessory - * @reported_jack_type previously reported jack type. - * @jack_type_temp temporary storage for currently connected accessory - * - * @jack_det_count counter how many times in sequence the accessory - * type detection has produced same result. - * @total_jack_det_count after plug-in irq, how many times detection - * has totally been made in order to detect the accessory type - * - * @detect_jiffies Used to save timestamp when detection was made. Timestamp - * used to filter out spurious button presses that might occur during the - * plug-in procedure. - * - * @accdet1_th_set flag to indicate whether accdet1 threshold and debounce - * times are configured - * @accdet2_th_set flag to indicate whether accdet2 thresholds are configured - * @gpio35_dir_set flag to indicate whether GPIO35 (VIDEOCTRL) direction - * has been configured. - */ -struct ab8500_ad { - struct platform_device *pdev; - struct ab8500_accdet_platform_data *pdata; - struct ab8500_gpadc *gpadc; - struct workqueue_struct *irq_work_queue; - - struct delayed_work detect_work; - struct delayed_work unplug_irq_work; - struct delayed_work init_work; - - struct input_dev *btn_input_dev; - enum accessory_button_state btn_state; - - enum accessory_jack_type jack_type; - enum accessory_jack_type reported_jack_type; - enum accessory_jack_type jack_type_temp; - - int jack_det_count; - int total_jack_det_count; - - unsigned long detect_jiffies; - - int accdet1_th_set; - int accdet2_th_set; - int gpio35_dir_set; -}; - -/* Forward declarations */ - -static void config_accdetect(struct ab8500_ad *dd); - -static void release_irq(struct ab8500_ad *dd, enum accessory_irq irq_id); -static void claim_irq(struct ab8500_ad *dd, enum accessory_irq irq_id); - -static irqreturn_t unplug_irq_handler(int irq, void *_userdata); -static irqreturn_t plug_irq_handler(int irq, void *_userdata); -static irqreturn_t button_press_irq_handler(int irq, void *_userdata); -static irqreturn_t button_release_irq_handler(int irq, void *_userdata); - -static void unplug_irq_handler_work(struct work_struct *work); -static void detect_work(struct work_struct *work); -static void init_work(struct work_struct *work); - -static enum accessory_jack_type detect(struct ab8500_ad *dd, int *required_det); -static void set_av_switch(struct ab8500_ad *dd, - enum accessory_avcontrol_dir dir); - /* Static data initialization */ -static struct accessory_detect_task detect_ops[] = { - { - .type = JACK_TYPE_DISCONNECTED, - .typename = "DISCONNECTED", - .meas_mv = 1, - .req_det_count = 1, - .minvol = ACCESSORY_DET_VOL_DONTCARE, - .maxvol = ACCESSORY_DET_VOL_DONTCARE - }, - { - .type = JACK_TYPE_HEADPHONE, - .typename = "HEADPHONE", - .meas_mv = 1, - .req_det_count = 1, - .minvol = ACCESSORY_HEADPHONE_DET_VOL_MIN, - .maxvol = ACCESSORY_HEADPHONE_DET_VOL_MAX - }, - { - .type = JACK_TYPE_CVIDEO, - .typename = "CVIDEO", - .meas_mv = 0, - .req_det_count = 4, - .minvol = ACCESSORY_CVIDEO_DET_VOL_MIN, - .maxvol = ACCESSORY_CVIDEO_DET_VOL_MAX - }, - { - .type = JACK_TYPE_OPENCABLE, - .typename = "OPENCABLE", - .meas_mv = 0, - .req_det_count = 4, - .minvol = ACCESSORY_OPENCABLE_DET_VOL_MIN, - .maxvol = ACCESSORY_OPENCABLE_DET_VOL_MAX - }, - { - .type = JACK_TYPE_CARKIT, - .typename = "CARKIT", - .meas_mv = 1, - .req_det_count = 1, - .minvol = ACCESSORY_CARKIT_DET_VOL_MIN, - .maxvol = ACCESSORY_CARKIT_DET_VOL_MAX - }, - { - .type = JACK_TYPE_HEADSET, - .typename = "HEADSET", - .meas_mv = 0, - .req_det_count = 2, - .minvol = ACCESSORY_HEADSET_DET_VOL_MIN, - .maxvol = ACCESSORY_HEADSET_DET_VOL_MAX - }, - { - .type = JACK_TYPE_CONNECTED, - .typename = "CONNECTED", - .meas_mv = 0, - .req_det_count = 4, - .minvol = ACCESSORY_DET_VOL_DONTCARE, - .maxvol = ACCESSORY_DET_VOL_DONTCARE - } -}; - -static struct accessory_regu_descriptor regu_desc[3] = { +static struct accessory_regu_descriptor ab8500_regu_desc[3] = { { .id = REGULATOR_VAUDIO, .name = "v-audio", @@ -379,7 +85,7 @@ static struct accessory_regu_descriptor regu_desc[3] = { }, }; -static struct accessory_irq_descriptor irq_desc_norm[] = { +static struct accessory_irq_descriptor ab8500_irq_desc_norm[] = { { .irq = PLUG_IRQ, .name = "ACC_DETECT_1DB_F", @@ -402,7 +108,7 @@ static struct accessory_irq_descriptor irq_desc_norm[] = { }, }; -static struct accessory_irq_descriptor irq_desc_inverted[] = { +static struct accessory_irq_descriptor ab8500_irq_desc_inverted[] = { { .irq = PLUG_IRQ, .name = "ACC_DETECT_1DB_R", @@ -425,102 +131,10 @@ static struct accessory_irq_descriptor irq_desc_inverted[] = { }, }; -static struct accessory_irq_descriptor *irq_desc; - -/* - * textual represenation of the accessory type - */ -static const char *accessory_str(enum accessory_jack_type type) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(detect_ops); i++) - if (type == detect_ops[i].type) - return detect_ops[i].typename; - - return "UNKNOWN?"; -} - -/* - * enables regulator but only if it has not been enabled earlier. - */ -static void accessory_regulator_enable(enum accessory_regulator reg) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(regu_desc); i++) { - if (reg & regu_desc[i].id) { - if (!regu_desc[i].enabled) { - if (!regulator_enable(regu_desc[i].handle)) - regu_desc[i].enabled = 1; - } - } - } -} - -/* - * disables regulator but only if it has been previously enabled. - */ -static void accessory_regulator_disable(enum accessory_regulator reg) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(regu_desc); i++) { - if (reg & regu_desc[i].id) { - if (regu_desc[i].enabled) { - if (!regulator_disable(regu_desc[i].handle)) - regu_desc[i].enabled = 0; - } - } - } -} - -/* - * frees previously retrieved regulators. - */ -static void free_regulators(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(regu_desc); i++) { - if (regu_desc[i].handle) { - regulator_put(regu_desc[i].handle); - regu_desc[i].handle = NULL; - } - } -} - -/* - * gets required regulators. - */ -static int create_regulators(struct ab8500_ad *dd) -{ - int i; - int status = 0; - - for (i = 0; i < ARRAY_SIZE(regu_desc); i++) { - struct regulator *regu = - regulator_get(&dd->pdev->dev, regu_desc[i].name); - if (IS_ERR(regu)) { - status = PTR_ERR(regu); - dev_err(&dd->pdev->dev, - "%s: Failed to get supply '%s' (%d).\n", - __func__, regu_desc[i].name, status); - free_regulators(); - goto out; - } else { - regu_desc[i].handle = regu; - } - } - -out: - return status; -} - /* * configures accdet2 input on/off */ -static void config_accdetect2_hw(struct ab8500_ad *dd, int enable) +static void ab8500_config_accdetect2_hw(struct abx500_ad *dd, int enable) { int ret = 0; @@ -559,7 +173,7 @@ out: /* * configures accdet1 input on/off */ -static void config_accdetect1_hw(struct ab8500_ad *dd, int enable) +static void ab8500_config_accdetect1_hw(struct abx500_ad *dd, int enable) { int ret; @@ -589,82 +203,10 @@ static void config_accdetect1_hw(struct ab8500_ad *dd, int enable) "%s: Failed to update reg (%d).\n", __func__, ret); } -/* - * create input device for button press reporting - */ -static int create_btn_input_dev(struct ab8500_ad *dd) -{ - int err; - - dd->btn_input_dev = input_allocate_device(); - if (!dd->btn_input_dev) { - dev_err(&dd->pdev->dev, "%s: Failed to allocate input dev.\n", - __func__); - err = -ENOMEM; - goto out; - } - - input_set_capability(dd->btn_input_dev, - EV_KEY, - dd->pdata->btn_keycode); - - dd->btn_input_dev->name = BTN_INPUT_DEV_NAME; - dd->btn_input_dev->uniq = BTN_INPUT_UNIQUE_VALUE; - dd->btn_input_dev->dev.parent = &dd->pdev->dev; - - err = input_register_device(dd->btn_input_dev); - if (err) { - dev_err(&dd->pdev->dev, - "%s: register_input_device failed (%d).\n", __func__, - err); - input_free_device(dd->btn_input_dev); - dd->btn_input_dev = NULL; - goto out; - } -out: - return err; -} - -/* - * reports jack status - */ -static void report_jack_status(struct ab8500_ad *dd) -{ - int value = 0; - - /* Never report possible open cable */ - if (dd->jack_type == JACK_TYPE_OPENCABLE) - goto out; - - /* Never report same state twice in a row */ - if (dd->jack_type == dd->reported_jack_type) - goto out; - dd->reported_jack_type = dd->jack_type; - - dev_info(&dd->pdev->dev, "Accessory: %s\n", - accessory_str(dd->jack_type)); - - if (dd->jack_type != JACK_TYPE_DISCONNECTED && - dd->jack_type != JACK_TYPE_UNSPECIFIED) - value |= SND_JACK_MECHANICAL; - if (jack_supports_mic(dd->jack_type)) - value |= SND_JACK_MICROPHONE; - if (jack_supports_spkr(dd->jack_type)) - value |= (SND_JACK_HEADPHONE | SND_JACK_LINEOUT); - if (dd->jack_type == JACK_TYPE_CVIDEO) { - value |= SND_JACK_VIDEOOUT; - set_av_switch(dd, VIDEO_OUT); - } - - ux500_ab8500_jack_report(value); - -out: return; -} - /* * returns the high level status whether some accessory is connected (1|0). */ -static int detect_plugged_in(struct ab8500_ad *dd) +static int ab8500_detect_plugged_in(struct abx500_ad *dd) { u8 value = 0; @@ -688,16 +230,18 @@ static int detect_plugged_in(struct ab8500_ad *dd) /* * mic_line_voltage_stable - measures a relative stable voltage from spec. input */ -static int meas_voltage_stable(struct ab8500_ad *dd, u8 input) +static int ab8500_meas_voltage_stable(struct abx500_ad *dd) { int iterations = 2; int v1, v2, dv; - v1 = ab8500_gpadc_convert(dd->gpadc, input); + v1 = ab8500_gpadc_convert((struct ab8500_gpadc *)dd->gpadc, + ACC_DETECT2); do { msleep(1); --iterations; - v2 = ab8500_gpadc_convert(dd->gpadc, input); + v2 = ab8500_gpadc_convert((struct ab8500_gpadc *)dd->gpadc, + ACC_DETECT2); dv = abs(v2 - v1); v1 = v2; } while (iterations > 0 && dv > MAX_VOLT_DIFF); @@ -705,188 +249,12 @@ static int meas_voltage_stable(struct ab8500_ad *dd, u8 input) return v1; } -/* - * worker routine to handle accessory unplug case - */ -static void unplug_irq_handler_work(struct work_struct *work) -{ - struct ab8500_ad *dd = container_of(work, - struct ab8500_ad, unplug_irq_work.work); - - dev_dbg(&dd->pdev->dev, "%s: Enter\n", __func__); - - dd->jack_type = dd->jack_type_temp = JACK_TYPE_DISCONNECTED; - dd->jack_det_count = dd->total_jack_det_count = 0; - dd->btn_state = BUTTON_UNK; - config_accdetect(dd); - - accessory_regulator_disable(REGULATOR_ALL); - - report_jack_status(dd); -} - -/* - * interrupt service routine for accessory unplug. - */ -static irqreturn_t unplug_irq_handler(int irq, void *_userdata) -{ - struct ab8500_ad *dd = _userdata; - - dev_dbg(&dd->pdev->dev, "%s: Enter (irq=%d)\n", __func__, irq); - - queue_delayed_work(dd->irq_work_queue, &dd->unplug_irq_work, - msecs_to_jiffies(DEBOUNCE_UNPLUG_EVENT_MS)); - - return IRQ_HANDLED; -} - -/* - * interrupt service routine for accessory plug. - */ -static irqreturn_t plug_irq_handler(int irq, void *_userdata) -{ - struct ab8500_ad *dd = _userdata; - - dev_dbg(&dd->pdev->dev, "%s: Enter (irq=%d)\n", - __func__, irq); - - switch (dd->jack_type) { - case JACK_TYPE_DISCONNECTED: - case JACK_TYPE_UNSPECIFIED: - queue_delayed_work(dd->irq_work_queue, &dd->detect_work, - msecs_to_jiffies(DEBOUNCE_PLUG_EVENT_MS)); - break; - - default: - dev_err(&dd->pdev->dev, "%s: Unexpected plug IRQ\n", __func__); - break; - } - - return IRQ_HANDLED; -} - -/* - * worker routine to perform detection. - */ -static void detect_work(struct work_struct *work) -{ - int req_det_count = 1; - enum accessory_jack_type new_type; - struct ab8500_ad *dd = container_of(work, - struct ab8500_ad, detect_work.work); - - dev_dbg(&dd->pdev->dev, "%s: Enter\n", __func__); - - set_av_switch(dd, AUDIO_IN); - - new_type = detect(dd, &req_det_count); - - dd->total_jack_det_count++; - if (dd->jack_type_temp == new_type) { - dd->jack_det_count++; - } else { - dd->jack_det_count = 1; - dd->jack_type_temp = new_type; - } - - if (dd->total_jack_det_count >= MAX_DET_COUNT) { - dev_err(&dd->pdev->dev, - "%s: MAX_DET_COUNT(=%d) reached. Bailing out.\n", - __func__, MAX_DET_COUNT); - queue_delayed_work(dd->irq_work_queue, &dd->unplug_irq_work, - msecs_to_jiffies(DEBOUNCE_UNPLUG_EVENT_MS)); - } else if (dd->jack_det_count >= req_det_count) { - dd->total_jack_det_count = dd->jack_det_count = 0; - dd->jack_type = new_type; - dd->detect_jiffies = jiffies; - report_jack_status(dd); - config_accdetect(dd); - } else { - queue_delayed_work(dd->irq_work_queue, - &dd->detect_work, - msecs_to_jiffies(DEBOUNCE_PLUG_RETEST_MS)); - } -} - -/* - * reports a button event (pressed, released). - */ -static void report_btn_event(struct ab8500_ad *dd, int down) -{ - input_report_key(dd->btn_input_dev, dd->pdata->btn_keycode, down); - input_sync(dd->btn_input_dev); - - dev_dbg(&dd->pdev->dev, "HS-BTN: %s\n", down ? "PRESSED" : "RELEASED"); -} - -/* - * interrupt service routine invoked when hs button is pressed down. - */ -static irqreturn_t button_press_irq_handler(int irq, void *_userdata) -{ - struct ab8500_ad *dd = _userdata; - - unsigned long accept_jiffies = dd->detect_jiffies + - msecs_to_jiffies(1000); - if (time_before(jiffies, accept_jiffies)) { - dev_dbg(&dd->pdev->dev, "%s: Skipped spurious btn press.\n", - __func__); - return IRQ_HANDLED; - } - - dev_dbg(&dd->pdev->dev, "%s: Enter (irq=%d)\n", __func__, irq); - - if (dd->jack_type == JACK_TYPE_OPENCABLE) { - /* Someting got connected to open cable -> detect.. */ - config_accdetect2_hw(dd, 0); - queue_delayed_work(dd->irq_work_queue, &dd->detect_work, - msecs_to_jiffies(DEBOUNCE_PLUG_EVENT_MS)); - return IRQ_HANDLED; - } - - if (dd->btn_state == BUTTON_PRESSED) - return IRQ_HANDLED; - - if (jack_supports_buttons(dd->jack_type)) { - dd->btn_state = BUTTON_PRESSED; - report_btn_event(dd, 1); - } else { - dd->btn_state = BUTTON_UNK; - } - - return IRQ_HANDLED; -} - -/* - * interrupts service routine invoked when hs button is released. - */ -static irqreturn_t button_release_irq_handler(int irq, void *_userdata) -{ - struct ab8500_ad *dd = _userdata; - - dev_dbg(&dd->pdev->dev, "%s: Enter (irq=%d)\n", __func__, irq); - - if (dd->jack_type == JACK_TYPE_OPENCABLE) - return IRQ_HANDLED; - - if (dd->btn_state != BUTTON_PRESSED) - return IRQ_HANDLED; - - if (jack_supports_buttons(dd->jack_type)) { - report_btn_event(dd, 0); - dd->btn_state = BUTTON_RELEASED; - } else { - dd->btn_state = BUTTON_UNK; - } - - return IRQ_HANDLED; -} - /* * configures HW so that it is possible to make decision whether * accessory is connected or not. */ -static void config_hw_test_plug_connected(struct ab8500_ad *dd, int enable) +static void ab8500_config_hw_test_plug_connected(struct abx500_ad *dd, + int enable) { int ret; @@ -901,20 +269,20 @@ static void config_hw_test_plug_connected(struct ab8500_ad *dd, int enable) } if (enable) - accessory_regulator_enable(REGULATOR_VAMIC1); + accessory_regulator_enable(dd, REGULATOR_VAMIC1); } /* * configures HW so that carkit/headset detection can be accomplished. */ -static void config_hw_test_basic_carkit(struct ab8500_ad *dd, int enable) +static void ab8500_config_hw_test_basic_carkit(struct abx500_ad *dd, int enable) { int ret; dev_dbg(&dd->pdev->dev, "%s:%d\n", __func__, enable); if (enable) - accessory_regulator_disable(REGULATOR_VAMIC1); + accessory_regulator_disable(dd, REGULATOR_VAMIC1); /* Un-Ground the VAMic1 output when enabled */ ret = abx500_mask_and_set_register_interruptible( @@ -928,61 +296,10 @@ static void config_hw_test_basic_carkit(struct ab8500_ad *dd, int enable) "%s: Failed to update reg (%d).\n", __func__, ret); } -/* - * checks whether measured voltage is in given range. depending on arguments, - * voltage might be re-measured or previously measured voltage is reused. - */ -static int mic_vol_in_range(struct ab8500_ad *dd, - int lo, int hi, int force_read) -{ - static int mv = MIN_MIC_POWER; - - if (mv == -100 || force_read) - mv = meas_voltage_stable(dd, ACC_DETECT2); - - return (mv >= lo && mv <= hi) ? 1 : 0; -} - -/* - * checks whether the currently connected HW is of given type. - */ -static int detect_hw(struct ab8500_ad *dd, - struct accessory_detect_task *task) -{ - int status; - - switch (task->type) { - case JACK_TYPE_DISCONNECTED: - config_hw_test_plug_connected(dd, 1); - status = !detect_plugged_in(dd); - break; - case JACK_TYPE_CONNECTED: - config_hw_test_plug_connected(dd, 1); - status = detect_plugged_in(dd); - break; - case JACK_TYPE_CARKIT: - config_hw_test_basic_carkit(dd, 1); - /* flow through.. */ - case JACK_TYPE_HEADPHONE: - case JACK_TYPE_CVIDEO: - case JACK_TYPE_HEADSET: - case JACK_TYPE_OPENCABLE: - status = mic_vol_in_range(dd, - task->minvol, - task->maxvol, - task->meas_mv); - break; - default: - status = 0; - } - - return status; -} - /* * sets the av switch direction - audio-in vs video-out */ -static void set_av_switch(struct ab8500_ad *dd, +static void ab8500_set_av_switch(struct abx500_ad *dd, enum accessory_avcontrol_dir dir) { int ret; @@ -1010,336 +327,11 @@ static void set_av_switch(struct ab8500_ad *dd, } } -/* - * Tries to detect the currently attached accessory - */ -static enum accessory_jack_type detect(struct ab8500_ad *dd, - int *req_det_count) -{ - enum accessory_jack_type type = JACK_TYPE_DISCONNECTED; - int i; - - accessory_regulator_enable(REGULATOR_VAUDIO | REGULATOR_AVSWITCH); - - for (i = 0; i < ARRAY_SIZE(detect_ops); ++i) { - if (detect_hw(dd, &detect_ops[i])) { - type = detect_ops[i].type; - *req_det_count = detect_ops[i].req_det_count; - break; - } - } - - config_hw_test_basic_carkit(dd, 0); - config_hw_test_plug_connected(dd, 0); - - if (jack_supports_buttons(type)) - accessory_regulator_enable(REGULATOR_VAMIC1); - else - accessory_regulator_disable(REGULATOR_VAMIC1 | - REGULATOR_AVSWITCH); - - accessory_regulator_disable(REGULATOR_VAUDIO); - - return type; -} - -/* - * registers to specific interrupt - */ -static void claim_irq(struct ab8500_ad *dd, enum accessory_irq irq_id) -{ - int ret; - int irq; - - if (dd->pdata->is_detection_inverted) - irq_desc = irq_desc_inverted; - else - irq_desc = irq_desc_norm; - - if (irq_desc[irq_id].registered) - return; - - irq = platform_get_irq_byname( - dd->pdev, - irq_desc[irq_id].name); - if (irq < 0) { - dev_err(&dd->pdev->dev, - "%s: Failed to get irq %s\n", __func__, - irq_desc[irq_id].name); - return; - } - - ret = request_threaded_irq(irq, - NULL, - irq_desc[irq_id].isr, - IRQF_NO_SUSPEND | IRQF_SHARED, - irq_desc[irq_id].name, - dd); - if (ret != 0) { - dev_err(&dd->pdev->dev, - "%s: Failed to claim irq %s (%d)\n", - __func__, - irq_desc[irq_id].name, - ret); - } else { - irq_desc[irq_id].registered = 1; - dev_dbg(&dd->pdev->dev, "%s: %s\n", - __func__, irq_desc[irq_id].name); - } -} - -/* - * releases specific interrupt - */ -static void release_irq(struct ab8500_ad *dd, enum accessory_irq irq_id) -{ - int irq; - - if (dd->pdata->is_detection_inverted) - irq_desc = irq_desc_inverted; - else - irq_desc = irq_desc_norm; - - if (!irq_desc[irq_id].registered) - return; - - irq = platform_get_irq_byname( - dd->pdev, - irq_desc[irq_id].name); - if (irq < 0) { - dev_err(&dd->pdev->dev, - "%s: Failed to get irq %s (%d)\n", - __func__, - irq_desc[irq_id].name, irq); - } else { - free_irq(irq, dd); - irq_desc[irq_id].registered = 0; - dev_dbg(&dd->pdev->dev, "%s: %s\n", - __func__, irq_desc[irq_id].name); - } -} - -/* - * configures interrupts + detection hardware to meet the requirements - * set by currently attached accessory type. - */ -static void config_accdetect(struct ab8500_ad *dd) -{ - switch (dd->jack_type) { - case JACK_TYPE_UNSPECIFIED: - config_accdetect1_hw(dd, 1); - config_accdetect2_hw(dd, 0); - - release_irq(dd, PLUG_IRQ); - release_irq(dd, UNPLUG_IRQ); - release_irq(dd, BUTTON_PRESS_IRQ); - release_irq(dd, BUTTON_RELEASE_IRQ); - set_av_switch(dd, NOT_SET); - break; - - case JACK_TYPE_DISCONNECTED: - set_av_switch(dd, NOT_SET); - case JACK_TYPE_HEADPHONE: - case JACK_TYPE_CVIDEO: - config_accdetect1_hw(dd, 1); - config_accdetect2_hw(dd, 0); - - claim_irq(dd, PLUG_IRQ); - claim_irq(dd, UNPLUG_IRQ); - release_irq(dd, BUTTON_PRESS_IRQ); - release_irq(dd, BUTTON_RELEASE_IRQ); - break; - - case JACK_TYPE_CONNECTED: - case JACK_TYPE_HEADSET: - case JACK_TYPE_CARKIT: - case JACK_TYPE_OPENCABLE: - config_accdetect1_hw(dd, 1); - config_accdetect2_hw(dd, 1); - - release_irq(dd, PLUG_IRQ); - claim_irq(dd, UNPLUG_IRQ); - claim_irq(dd, BUTTON_PRESS_IRQ); - claim_irq(dd, BUTTON_RELEASE_IRQ); - break; - - default: - dev_err(&dd->pdev->dev, "%s: Unknown type: %d\n", - __func__, dd->jack_type); - } -} - -/* - * Deferred initialization of the work. - */ -static void init_work(struct work_struct *work) -{ - struct ab8500_ad *dd = container_of(work, - struct ab8500_ad, init_work.work); - - dev_dbg(&dd->pdev->dev, "%s: Enter\n", __func__); - - dd->jack_type = dd->reported_jack_type = JACK_TYPE_UNSPECIFIED; - config_accdetect(dd); - queue_delayed_work(dd->irq_work_queue, - &dd->detect_work, - msecs_to_jiffies(0)); -} - -/* - * performs platform device initialization - */ -static int ab8500_accessory_init(struct platform_device *pdev) -{ - struct ab8500_ad *dd; - struct ab8500_platform_data *plat; - - dev_dbg(&pdev->dev, "Enter: %s\n", __func__); - - dd = kzalloc(sizeof(struct ab8500_ad), GFP_KERNEL); - if (!dd) { - dev_err(&pdev->dev, "%s: Mem. alloc failed\n", __func__); - goto fail_no_mem_for_devdata; - } - - dd->pdev = pdev; - dd->pdata = pdev->dev.platform_data; - plat = dev_get_platdata(pdev->dev.parent); - - if (!plat || !plat->accdet) { - dev_err(&pdev->dev, "%s: Failed to get accdet plat data.\n", - __func__); - goto fail_no_ab8500_dev; - } - dd->pdata = plat->accdet; - - if (dd->pdata->video_ctrl_gpio) { - if (!gpio_is_valid(dd->pdata->video_ctrl_gpio)) { - dev_err(&pdev->dev, - "%s: Video ctrl GPIO invalid (%d).\n", __func__, - dd->pdata->video_ctrl_gpio); - goto fail_video_ctrl_gpio; - } - if (gpio_request(dd->pdata->video_ctrl_gpio, "Video Control")) { - dev_err(&pdev->dev, "%s: Get video ctrl GPIO failed.\n", - __func__); - goto fail_video_ctrl_gpio; - } - } - - if (create_btn_input_dev(dd) < 0) { - dev_err(&pdev->dev, "%s: create_button_input_dev failed.\n", - __func__); - goto fail_no_btn_input_dev; - } - - if (create_regulators(dd) < 0) { - dev_err(&pdev->dev, "%s: failed to create regulators\n", - __func__); - goto fail_no_regulators; - } - dd->btn_state = BUTTON_UNK; - - dd->irq_work_queue = create_singlethread_workqueue("ab8500_accdet_wq"); - if (!dd->irq_work_queue) { - dev_err(&pdev->dev, "%s: Failed to create wq\n", __func__); - goto fail_no_mem_for_wq; - } - dd->gpadc = ab8500_gpadc_get(); - - INIT_DELAYED_WORK(&dd->detect_work, detect_work); - INIT_DELAYED_WORK(&dd->unplug_irq_work, unplug_irq_handler_work); - INIT_DELAYED_WORK(&dd->init_work, init_work); - - /* Deferred init/detect since no use for the info early in boot */ - queue_delayed_work(dd->irq_work_queue, - &dd->init_work, - msecs_to_jiffies(INIT_DELAY_MS)); - - platform_set_drvdata(pdev, dd); - - return 0; - -fail_no_mem_for_wq: - free_regulators(); -fail_no_regulators: - input_unregister_device(dd->btn_input_dev); -fail_no_btn_input_dev: - gpio_free(dd->pdata->video_ctrl_gpio); -fail_video_ctrl_gpio: -fail_no_ab8500_dev: - kfree(dd); -fail_no_mem_for_devdata: - - return -ENOMEM; -} - -/* - * Performs platform device cleanup - */ -static void ab8500_accessory_cleanup(struct ab8500_ad *dd) -{ - dev_dbg(&dd->pdev->dev, "Enter: %s\n", __func__); - - dd->jack_type = JACK_TYPE_UNSPECIFIED; - config_accdetect(dd); - - gpio_free(dd->pdata->video_ctrl_gpio); - input_unregister_device(dd->btn_input_dev); - free_regulators(); - - cancel_delayed_work(&dd->detect_work); - cancel_delayed_work(&dd->unplug_irq_work); - cancel_delayed_work(&dd->init_work); - flush_workqueue(dd->irq_work_queue); - destroy_workqueue(dd->irq_work_queue); - - kfree(dd); -} - -static int __devinit ab8500_acc_detect_probe(struct platform_device *pdev) -{ - return ab8500_accessory_init(pdev); -} - - -static int __devexit ab8500_acc_detect_remove(struct platform_device *pdev) -{ - ab8500_accessory_cleanup(platform_get_drvdata(pdev)); - platform_set_drvdata(pdev, NULL); - - return 0; -} - -#if defined(CONFIG_PM) static u8 acc_det_ctrl_suspend_val; -static int ab8500_acc_detect_suspend(struct platform_device *pdev, - pm_message_t state) +static void ab8500_turn_off_accdet_comparator(struct platform_device *pdev) { - struct ab8500_ad *dd = platform_get_drvdata(pdev); - int irq_id, irq; - - dev_dbg(&dd->pdev->dev, "%s: Enter\n", __func__); - - cancel_delayed_work_sync(&dd->unplug_irq_work); - cancel_delayed_work_sync(&dd->detect_work); - cancel_delayed_work_sync(&dd->init_work); - - if (dd->pdata->is_detection_inverted) - irq_desc = irq_desc_inverted; - else - irq_desc = irq_desc_norm; - - for (irq_id = 0; irq_id < ARRAY_SIZE(irq_desc_norm); irq_id++) { - if (irq_desc[irq_id].registered == 1) { - irq = platform_get_irq_byname( - dd->pdev, - irq_desc[irq_id].name); - - disable_irq(irq); - } - } + struct abx500_ad *dd = platform_get_drvdata(pdev); /* Turn off AccDetect comparators and pull-up */ (void) abx500_get_register_interruptible( @@ -1353,21 +345,11 @@ static int ab8500_acc_detect_suspend(struct platform_device *pdev, AB8500_ACC_DET_CTRL_REG, 0); - if (dd->jack_type == JACK_TYPE_HEADSET) - accessory_regulator_disable(REGULATOR_VAMIC1); - - return 0; } -static int ab8500_acc_detect_resume(struct platform_device *pdev) +static void ab8500_turn_on_accdet_comparator(struct platform_device *pdev) { - struct ab8500_ad *dd = platform_get_drvdata(pdev); - int irq_id, irq; - - dev_dbg(&dd->pdev->dev, "%s: Enter\n", __func__); - - if (dd->jack_type == JACK_TYPE_HEADSET) - accessory_regulator_enable(REGULATOR_VAMIC1); + struct abx500_ad *dd = platform_get_drvdata(pdev); /* Turn on AccDetect comparators and pull-up */ (void) abx500_set_register_interruptible( @@ -1376,56 +358,49 @@ static int ab8500_acc_detect_resume(struct platform_device *pdev) AB8500_ACC_DET_CTRL_REG, acc_det_ctrl_suspend_val); - if (dd->pdata->is_detection_inverted) - irq_desc = irq_desc_inverted; - else - irq_desc = irq_desc_norm; - - for (irq_id = 0; irq_id < ARRAY_SIZE(irq_desc_norm); irq_id++) { - if (irq_desc[irq_id].registered == 1) { - irq = platform_get_irq_byname( - dd->pdev, - irq_desc[irq_id].name); - - enable_irq(irq); - - } - } - - /* After resume, reinitialize */ - dd->gpio35_dir_set = dd->accdet1_th_set = dd->accdet2_th_set = 0; - queue_delayed_work(dd->irq_work_queue, &dd->init_work, 0); - - return 0; } -#else -#define ab8500_acc_detect_suspend NULL -#define ab8500_acc_detect_resume NULL -#endif -static struct platform_driver ab8500_acc_detect_platform_driver = { - .driver = { - .name = "ab8500-acc-det", - .owner = THIS_MODULE, - }, - .probe = ab8500_acc_detect_probe, - .remove = __devexit_p(ab8500_acc_detect_remove), - .suspend = ab8500_acc_detect_suspend, - .resume = ab8500_acc_detect_resume, -}; - -static int __init ab8500_acc_detect_init(void) +static void *ab8500_accdet_abx500_gpadc_get(void) { - return platform_driver_register(&ab8500_acc_detect_platform_driver); + return ab8500_gpadc_get(); } -static void __exit ab8500_acc_detect_exit(void) +struct abx500_accdet_platform_data * + ab8500_get_platform_data(struct platform_device *pdev) { - platform_driver_unregister(&ab8500_acc_detect_platform_driver); -} + struct ab8500_platform_data *plat; -module_init(ab8500_acc_detect_init); -module_exit(ab8500_acc_detect_exit); + plat = dev_get_platdata(pdev->dev.parent); + + if (!plat || !plat->accdet) { + dev_err(&pdev->dev, "%s: Failed to get accdet plat data.\n", + __func__); + return ERR_PTR(-ENODEV); + } + + return plat->accdet; +} + +struct abx500_ad ab8500_accessory_det_callbacks = { + .irq_desc_norm = ab8500_irq_desc_norm, + .irq_desc_inverted = ab8500_irq_desc_inverted, + .no_irqs = ARRAY_SIZE(ab8500_irq_desc_norm), + .regu_desc = ab8500_regu_desc, + .no_of_regu_desc = ARRAY_SIZE(ab8500_regu_desc), + .config_accdetect2_hw = ab8500_config_accdetect2_hw, + .config_accdetect1_hw = ab8500_config_accdetect1_hw, + .detect_plugged_in = ab8500_detect_plugged_in, + .meas_voltage_stable = ab8500_meas_voltage_stable, + .config_hw_test_basic_carkit = ab8500_config_hw_test_basic_carkit, + .turn_off_accdet_comparator = ab8500_turn_off_accdet_comparator, + .turn_on_accdet_comparator = ab8500_turn_on_accdet_comparator, + .accdet_abx500_gpadc_get = ab8500_accdet_abx500_gpadc_get, + .config_hw_test_plug_connected = ab8500_config_hw_test_plug_connected, + .startup = NULL, + .set_av_switch = ab8500_set_av_switch, + .tv_out_connected = false, + .get_platform_data = ab8500_get_platform_data, +}; MODULE_DESCRIPTION("AB8500 AV Accessory detection driver"); MODULE_ALIAS("platform:ab8500-acc-det"); diff --git a/drivers/input/misc/abx500-accdet.c b/drivers/input/misc/abx500-accdet.c new file mode 100644 index 00000000000..717bbc09f8f --- /dev/null +++ b/drivers/input/misc/abx500-accdet.c @@ -0,0 +1,962 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Jarmo K. Kuronen + * for ST-Ericsson. + * + * License terms: GPL V2 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_SND_SOC_UX500_AB8500 +#include +#else +#define ux500_ab8500_jack_report(i) +#endif + +/* Unique value used to identify Headset button input device */ +#define BTN_INPUT_UNIQUE_VALUE "AB8500HsBtn" +#define BTN_INPUT_DEV_NAME "AB8500 Hs Button" + +#define DEBOUNCE_PLUG_EVENT_MS 100 +#define DEBOUNCE_PLUG_RETEST_MS 25 +#define DEBOUNCE_UNPLUG_EVENT_MS 0 + +/* After being loaded, how fast the first check is to be made */ +#define INIT_DELAY_MS 3000 + +/* Voltage limits (mV) for various types of AV Accessories */ +#define ACCESSORY_DET_VOL_DONTCARE -1 +#define ACCESSORY_HEADPHONE_DET_VOL_MIN 0 +#define ACCESSORY_HEADPHONE_DET_VOL_MAX 40 +#define ACCESSORY_CVIDEO_DET_VOL_MIN 41 +#define ACCESSORY_CVIDEO_DET_VOL_MAX 105 +#define ACCESSORY_CARKIT_DET_VOL_MIN 1100 +#define ACCESSORY_CARKIT_DET_VOL_MAX 1300 +#define ACCESSORY_HEADSET_DET_VOL_MIN 0 +#define ACCESSORY_HEADSET_DET_VOL_MAX 200 +#define ACCESSORY_OPENCABLE_DET_VOL_MIN 1730 +#define ACCESSORY_OPENCABLE_DET_VOL_MAX 2150 + + +/* Macros */ + +/* + * Conviniency macros to check jack characteristics. + */ +#define jack_supports_mic(type) \ + (type == JACK_TYPE_HEADSET || type == JACK_TYPE_CARKIT) +#define jack_supports_spkr(type) \ + ((type != JACK_TYPE_DISCONNECTED) && (type != JACK_TYPE_CONNECTED)) +#define jack_supports_buttons(type) \ + ((type == JACK_TYPE_HEADSET) ||\ + (type == JACK_TYPE_CARKIT) ||\ + (type == JACK_TYPE_OPENCABLE) ||\ + (type == JACK_TYPE_CONNECTED)) + + +/* Forward declarations */ +static void config_accdetect(struct abx500_ad *dd); +static enum accessory_jack_type detect(struct abx500_ad *dd, int *required_det); + +/* Static data initialization */ +static struct accessory_detect_task detect_ops[] = { + { + .type = JACK_TYPE_DISCONNECTED, + .typename = "DISCONNECTED", + .meas_mv = 1, + .req_det_count = 1, + .minvol = ACCESSORY_DET_VOL_DONTCARE, + .maxvol = ACCESSORY_DET_VOL_DONTCARE + }, + { + .type = JACK_TYPE_HEADPHONE, + .typename = "HEADPHONE", + .meas_mv = 1, + .req_det_count = 1, + .minvol = ACCESSORY_HEADPHONE_DET_VOL_MIN, + .maxvol = ACCESSORY_HEADPHONE_DET_VOL_MAX + }, + { + .type = JACK_TYPE_CVIDEO, + .typename = "CVIDEO", + .meas_mv = 0, + .req_det_count = 4, + .minvol = ACCESSORY_CVIDEO_DET_VOL_MIN, + .maxvol = ACCESSORY_CVIDEO_DET_VOL_MAX + }, + { + .type = JACK_TYPE_OPENCABLE, + .typename = "OPENCABLE", + .meas_mv = 0, + .req_det_count = 4, + .minvol = ACCESSORY_OPENCABLE_DET_VOL_MIN, + .maxvol = ACCESSORY_OPENCABLE_DET_VOL_MAX + }, + { + .type = JACK_TYPE_CARKIT, + .typename = "CARKIT", + .meas_mv = 1, + .req_det_count = 1, + .minvol = ACCESSORY_CARKIT_DET_VOL_MIN, + .maxvol = ACCESSORY_CARKIT_DET_VOL_MAX + }, + { + .type = JACK_TYPE_HEADSET, + .typename = "HEADSET", + .meas_mv = 0, + .req_det_count = 2, + .minvol = ACCESSORY_HEADSET_DET_VOL_MIN, + .maxvol = ACCESSORY_HEADSET_DET_VOL_MAX + }, + { + .type = JACK_TYPE_CONNECTED, + .typename = "CONNECTED", + .meas_mv = 0, + .req_det_count = 4, + .minvol = ACCESSORY_DET_VOL_DONTCARE, + .maxvol = ACCESSORY_DET_VOL_DONTCARE + } +}; + +static struct accessory_irq_descriptor *abx500_accdet_irq_desc; + +/* + * textual represenation of the accessory type + */ +static const char *accessory_str(enum accessory_jack_type type) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(detect_ops); i++) + if (type == detect_ops[i].type) + return detect_ops[i].typename; + + return "UNKNOWN?"; +} + +/* + * enables regulator but only if it has not been enabled earlier. + */ +void accessory_regulator_enable(struct abx500_ad *dd, + enum accessory_regulator reg) +{ + int i; + + for (i = 0; i < dd->no_of_regu_desc; i++) { + if (reg & dd->regu_desc[i].id) { + if (!dd->regu_desc[i].enabled) { + if (!regulator_enable(dd->regu_desc[i].handle)) + dd->regu_desc[i].enabled = 1; + } + } + } +} + +/* + * disables regulator but only if it has been previously enabled. + */ +void accessory_regulator_disable(struct abx500_ad *dd, + enum accessory_regulator reg) +{ + int i; + + for (i = 0; i < dd->no_of_regu_desc; i++) { + if (reg & dd->regu_desc[i].id) { + if (dd->regu_desc[i].enabled) { + if (!regulator_disable(dd->regu_desc[i].handle)) + dd->regu_desc[i].enabled = 0; + } + } + } +} + +/* + * frees previously retrieved regulators. + */ +static void free_regulators(struct abx500_ad *dd) +{ + int i; + + for (i = 0; i < dd->no_of_regu_desc; i++) { + if (dd->regu_desc[i].handle) { + regulator_put(dd->regu_desc[i].handle); + dd->regu_desc[i].handle = NULL; + } + } +} + +/* + * gets required regulators. + */ +static int create_regulators(struct abx500_ad *dd) +{ + int i; + int status = 0; + + for (i = 0; i < dd->no_of_regu_desc; i++) { + struct regulator *regu = + regulator_get(&dd->pdev->dev, dd->regu_desc[i].name); + if (IS_ERR(regu)) { + status = PTR_ERR(regu); + dev_err(&dd->pdev->dev, + "%s: Failed to get supply '%s' (%d).\n", + __func__, dd->regu_desc[i].name, status); + free_regulators(dd); + goto out; + } else { + dd->regu_desc[i].handle = regu; + } + } + +out: + return status; +} + +/* + * create input device for button press reporting + */ +static int create_btn_input_dev(struct abx500_ad *dd) +{ + int err; + + dd->btn_input_dev = input_allocate_device(); + if (!dd->btn_input_dev) { + dev_err(&dd->pdev->dev, "%s: Failed to allocate input dev.\n", + __func__); + err = -ENOMEM; + goto out; + } + + input_set_capability(dd->btn_input_dev, + EV_KEY, + dd->pdata->btn_keycode); + + dd->btn_input_dev->name = BTN_INPUT_DEV_NAME; + dd->btn_input_dev->uniq = BTN_INPUT_UNIQUE_VALUE; + dd->btn_input_dev->dev.parent = &dd->pdev->dev; + + err = input_register_device(dd->btn_input_dev); + if (err) { + dev_err(&dd->pdev->dev, + "%s: register_input_device failed (%d).\n", __func__, + err); + input_free_device(dd->btn_input_dev); + dd->btn_input_dev = NULL; + goto out; + } +out: + return err; +} + +/* + * reports jack status + */ +void report_jack_status(struct abx500_ad *dd) +{ + int value = 0; + + if (dd->tv_out_connected == true) + value = SND_JACK_VIDEOOUT; + + /* Never report possible open cable */ + if (dd->jack_type == JACK_TYPE_OPENCABLE) + goto out; + + /* Never report same state twice in a row */ + if (dd->jack_type == dd->reported_jack_type) + goto out; + dd->reported_jack_type = dd->jack_type; + + dev_info(&dd->pdev->dev, "Accessory: %s\n", + accessory_str(dd->jack_type)); + + if (dd->jack_type != JACK_TYPE_DISCONNECTED && + dd->jack_type != JACK_TYPE_UNSPECIFIED) + value |= SND_JACK_MECHANICAL; + if (jack_supports_mic(dd->jack_type)) + value |= SND_JACK_MICROPHONE; + if (jack_supports_spkr(dd->jack_type)) + value |= (SND_JACK_HEADPHONE | SND_JACK_LINEOUT); + if (dd->jack_type == JACK_TYPE_CVIDEO) { + value |= SND_JACK_VIDEOOUT; + if (dd->set_av_switch) + dd->set_av_switch(dd, VIDEO_OUT); + } + ux500_ab8500_jack_report(value); + +out: return; +} + +/* + * worker routine to handle accessory unplug case + */ +void unplug_irq_handler_work(struct work_struct *work) +{ + struct abx500_ad *dd = container_of(work, + struct abx500_ad, unplug_irq_work.work); + + dev_dbg(&dd->pdev->dev, "%s: Enter\n", __func__); + + dd->jack_type = dd->jack_type_temp = JACK_TYPE_DISCONNECTED; + dd->jack_det_count = dd->total_jack_det_count = 0; + dd->btn_state = BUTTON_UNK; + config_accdetect(dd); + + accessory_regulator_disable(dd, REGULATOR_ALL); + + report_jack_status(dd); +} + +/* + * interrupt service routine for accessory unplug. + */ +irqreturn_t unplug_irq_handler(int irq, void *_userdata) +{ + struct abx500_ad *dd = _userdata; + + dev_dbg(&dd->pdev->dev, "%s: Enter (irq=%d)\n", __func__, irq); + + queue_delayed_work(dd->irq_work_queue, &dd->unplug_irq_work, + msecs_to_jiffies(DEBOUNCE_UNPLUG_EVENT_MS)); + + return IRQ_HANDLED; +} + +/* + * interrupt service routine for accessory plug. + */ +irqreturn_t plug_irq_handler(int irq, void *_userdata) +{ + struct abx500_ad *dd = _userdata; + + dev_dbg(&dd->pdev->dev, "%s: Enter (irq=%d)\n", + __func__, irq); + + switch (dd->jack_type) { + case JACK_TYPE_DISCONNECTED: + case JACK_TYPE_UNSPECIFIED: + queue_delayed_work(dd->irq_work_queue, &dd->detect_work, + msecs_to_jiffies(DEBOUNCE_PLUG_EVENT_MS)); + break; + + default: + dev_err(&dd->pdev->dev, "%s: Unexpected plug IRQ\n", __func__); + break; + } + + return IRQ_HANDLED; +} + +/* + * worker routine to perform detection. + */ +static void detect_work(struct work_struct *work) +{ + int req_det_count = 1; + enum accessory_jack_type new_type; + struct abx500_ad *dd = container_of(work, + struct abx500_ad, detect_work.work); + + dev_dbg(&dd->pdev->dev, "%s: Enter\n", __func__); + + if (dd->set_av_switch) + dd->set_av_switch(dd, AUDIO_IN); + + new_type = detect(dd, &req_det_count); + + dd->total_jack_det_count++; + if (dd->jack_type_temp == new_type) { + dd->jack_det_count++; + } else { + dd->jack_det_count = 1; + dd->jack_type_temp = new_type; + } + + if (dd->total_jack_det_count >= MAX_DET_COUNT) { + dev_err(&dd->pdev->dev, + "%s: MAX_DET_COUNT(=%d) reached. Bailing out.\n", + __func__, MAX_DET_COUNT); + queue_delayed_work(dd->irq_work_queue, &dd->unplug_irq_work, + msecs_to_jiffies(DEBOUNCE_UNPLUG_EVENT_MS)); + } else if (dd->jack_det_count >= req_det_count) { + dd->total_jack_det_count = dd->jack_det_count = 0; + dd->jack_type = new_type; + dd->detect_jiffies = jiffies; + report_jack_status(dd); + config_accdetect(dd); + } else { + queue_delayed_work(dd->irq_work_queue, + &dd->detect_work, + msecs_to_jiffies(DEBOUNCE_PLUG_RETEST_MS)); + } +} + +/* + * reports a button event (pressed, released). + */ +static void report_btn_event(struct abx500_ad *dd, int down) +{ + input_report_key(dd->btn_input_dev, dd->pdata->btn_keycode, down); + input_sync(dd->btn_input_dev); + + dev_dbg(&dd->pdev->dev, "HS-BTN: %s\n", down ? "PRESSED" : "RELEASED"); +} + +/* + * interrupt service routine invoked when hs button is pressed down. + */ +irqreturn_t button_press_irq_handler(int irq, void *_userdata) +{ + struct abx500_ad *dd = _userdata; + + unsigned long accept_jiffies = dd->detect_jiffies + + msecs_to_jiffies(1000); + if (time_before(jiffies, accept_jiffies)) { + dev_dbg(&dd->pdev->dev, "%s: Skipped spurious btn press.\n", + __func__); + return IRQ_HANDLED; + } + + dev_dbg(&dd->pdev->dev, "%s: Enter (irq=%d)\n", __func__, irq); + + if (dd->jack_type == JACK_TYPE_OPENCABLE) { + /* Someting got connected to open cable -> detect.. */ + dd->config_accdetect2_hw(dd, 0); + queue_delayed_work(dd->irq_work_queue, &dd->detect_work, + msecs_to_jiffies(DEBOUNCE_PLUG_EVENT_MS)); + return IRQ_HANDLED; + } + + if (dd->btn_state == BUTTON_PRESSED) + return IRQ_HANDLED; + + if (jack_supports_buttons(dd->jack_type)) { + dd->btn_state = BUTTON_PRESSED; + report_btn_event(dd, 1); + } else { + dd->btn_state = BUTTON_UNK; + } + + return IRQ_HANDLED; +} + +/* + * interrupts service routine invoked when hs button is released. + */ +irqreturn_t button_release_irq_handler(int irq, void *_userdata) +{ + struct abx500_ad *dd = _userdata; + + dev_dbg(&dd->pdev->dev, "%s: Enter (irq=%d)\n", __func__, irq); + + if (dd->jack_type == JACK_TYPE_OPENCABLE) + return IRQ_HANDLED; + + if (dd->btn_state != BUTTON_PRESSED) + return IRQ_HANDLED; + + if (jack_supports_buttons(dd->jack_type)) { + report_btn_event(dd, 0); + dd->btn_state = BUTTON_RELEASED; + } else { + dd->btn_state = BUTTON_UNK; + } + + return IRQ_HANDLED; +} + +/* + * checks whether measured voltage is in given range. depending on arguments, + * voltage might be re-measured or previously measured voltage is reused. + */ +static int mic_vol_in_range(struct abx500_ad *dd, + int lo, int hi, int force_read) +{ + static int mv = MIN_MIC_POWER; + + if (mv == -100 || force_read) + mv = dd->meas_voltage_stable(dd); + + return (mv >= lo && mv <= hi) ? 1 : 0; +} + +/* + * checks whether the currently connected HW is of given type. + */ +static int detect_hw(struct abx500_ad *dd, + struct accessory_detect_task *task) +{ + int status; + + switch (task->type) { + case JACK_TYPE_DISCONNECTED: + dd->config_hw_test_plug_connected(dd, 1); + status = !dd->detect_plugged_in(dd); + break; + case JACK_TYPE_CONNECTED: + dd->config_hw_test_plug_connected(dd, 1); + status = dd->detect_plugged_in(dd); + break; + case JACK_TYPE_CARKIT: + dd->config_hw_test_basic_carkit(dd, 1); + /* flow through.. */ + case JACK_TYPE_HEADPHONE: + case JACK_TYPE_CVIDEO: + case JACK_TYPE_HEADSET: + case JACK_TYPE_OPENCABLE: + status = mic_vol_in_range(dd, + task->minvol, + task->maxvol, + task->meas_mv); + break; + default: + status = 0; + } + + return status; +} + +/* + * Tries to detect the currently attached accessory + */ +static enum accessory_jack_type detect(struct abx500_ad *dd, + int *req_det_count) +{ + enum accessory_jack_type type = JACK_TYPE_DISCONNECTED; + int i; + + accessory_regulator_enable(dd, REGULATOR_VAUDIO | REGULATOR_AVSWITCH); + + for (i = 0; i < ARRAY_SIZE(detect_ops); ++i) { + if (detect_hw(dd, &detect_ops[i])) { + type = detect_ops[i].type; + *req_det_count = detect_ops[i].req_det_count; + break; + } + } + + dd->config_hw_test_basic_carkit(dd, 0); + dd->config_hw_test_plug_connected(dd, 0); + + if (jack_supports_buttons(type)) + accessory_regulator_enable(dd, REGULATOR_VAMIC1); + else + accessory_regulator_disable(dd, REGULATOR_VAMIC1 | + REGULATOR_AVSWITCH); + + accessory_regulator_disable(dd, REGULATOR_VAUDIO); + + return type; +} + +/* + * registers to specific interrupt + */ +static void claim_irq(struct abx500_ad *dd, enum accessory_irq irq_id) +{ + int ret; + int irq; + + if (dd->pdata->is_detection_inverted) + abx500_accdet_irq_desc = dd->irq_desc_inverted; + else + abx500_accdet_irq_desc = dd->irq_desc_norm; + + if (abx500_accdet_irq_desc[irq_id].registered) + return; + + irq = platform_get_irq_byname( + dd->pdev, + abx500_accdet_irq_desc[irq_id].name); + if (irq < 0) { + dev_err(&dd->pdev->dev, + "%s: Failed to get irq %s\n", __func__, + abx500_accdet_irq_desc[irq_id].name); + return; + } + + ret = request_threaded_irq(irq, + NULL, + abx500_accdet_irq_desc[irq_id].isr, + IRQF_NO_SUSPEND | IRQF_SHARED, + abx500_accdet_irq_desc[irq_id].name, + dd); + if (ret != 0) { + dev_err(&dd->pdev->dev, + "%s: Failed to claim irq %s (%d)\n", + __func__, + abx500_accdet_irq_desc[irq_id].name, + ret); + } else { + abx500_accdet_irq_desc[irq_id].registered = 1; + dev_dbg(&dd->pdev->dev, "%s: %s\n", + __func__, abx500_accdet_irq_desc[irq_id].name); + } +} + +/* + * releases specific interrupt + */ +static void release_irq(struct abx500_ad *dd, enum accessory_irq irq_id) +{ + int irq; + + if (dd->pdata->is_detection_inverted) + abx500_accdet_irq_desc = dd->irq_desc_inverted; + else + abx500_accdet_irq_desc = dd->irq_desc_norm; + + if (!abx500_accdet_irq_desc[irq_id].registered) + return; + + irq = platform_get_irq_byname( + dd->pdev, + abx500_accdet_irq_desc[irq_id].name); + if (irq < 0) { + dev_err(&dd->pdev->dev, + "%s: Failed to get irq %s (%d)\n", + __func__, + abx500_accdet_irq_desc[irq_id].name, irq); + } else { + free_irq(irq, dd); + abx500_accdet_irq_desc[irq_id].registered = 0; + dev_dbg(&dd->pdev->dev, "%s: %s\n", + __func__, abx500_accdet_irq_desc[irq_id].name); + } +} + +/* + * configures interrupts + detection hardware to meet the requirements + * set by currently attached accessory type. + */ +static void config_accdetect(struct abx500_ad *dd) +{ + switch (dd->jack_type) { + case JACK_TYPE_UNSPECIFIED: + dd->config_accdetect1_hw(dd, 1); + dd->config_accdetect2_hw(dd, 0); + + release_irq(dd, PLUG_IRQ); + release_irq(dd, UNPLUG_IRQ); + release_irq(dd, BUTTON_PRESS_IRQ); + release_irq(dd, BUTTON_RELEASE_IRQ); + if (dd->set_av_switch) + dd->set_av_switch(dd, NOT_SET); + break; + + case JACK_TYPE_DISCONNECTED: + if (dd->set_av_switch) + dd->set_av_switch(dd, NOT_SET); + case JACK_TYPE_HEADPHONE: + case JACK_TYPE_CVIDEO: + dd->config_accdetect1_hw(dd, 1); + dd->config_accdetect2_hw(dd, 0); + + claim_irq(dd, PLUG_IRQ); + claim_irq(dd, UNPLUG_IRQ); + release_irq(dd, BUTTON_PRESS_IRQ); + release_irq(dd, BUTTON_RELEASE_IRQ); + break; + + case JACK_TYPE_CONNECTED: + case JACK_TYPE_HEADSET: + case JACK_TYPE_CARKIT: + case JACK_TYPE_OPENCABLE: + dd->config_accdetect1_hw(dd, 1); + dd->config_accdetect2_hw(dd, 1); + + release_irq(dd, PLUG_IRQ); + claim_irq(dd, UNPLUG_IRQ); + claim_irq(dd, BUTTON_PRESS_IRQ); + claim_irq(dd, BUTTON_RELEASE_IRQ); + break; + + default: + dev_err(&dd->pdev->dev, "%s: Unknown type: %d\n", + __func__, dd->jack_type); + } +} + +/* + * Deferred initialization of the work. + */ +static void init_work(struct work_struct *work) +{ + struct abx500_ad *dd = container_of(work, + struct abx500_ad, init_work.work); + + dev_dbg(&dd->pdev->dev, "%s: Enter\n", __func__); + + dd->jack_type = dd->reported_jack_type = JACK_TYPE_UNSPECIFIED; + config_accdetect(dd); + queue_delayed_work(dd->irq_work_queue, + &dd->detect_work, + msecs_to_jiffies(0)); +} + +/* + * performs platform device initialization + */ +static int abx500_accessory_init(struct platform_device *pdev) +{ + int ret; + struct abx500_ad *dd = (struct abx500_ad *)pdev->id_entry->driver_data; + + dev_dbg(&pdev->dev, "Enter: %s\n", __func__); + + dd->pdev = pdev; + dd->pdata = dd->get_platform_data(pdev); + if (IS_ERR(dd->pdata)) + return PTR_ERR(dd->pdata); + + if (dd->pdata->video_ctrl_gpio) { + ret = gpio_is_valid(dd->pdata->video_ctrl_gpio); + if (!ret) { + dev_err(&pdev->dev, + "%s: Video ctrl GPIO invalid (%d).\n", __func__, + dd->pdata->video_ctrl_gpio); + + return ret; + } + ret = gpio_request(dd->pdata->video_ctrl_gpio, + "Video Control"); + if (ret) { + dev_err(&pdev->dev, "%s: Get video ctrl GPIO" + "failed.\n", __func__); + return ret; + } + } + + (ret = create_btn_input_dev(dd)); + if (ret < 0) { + dev_err(&pdev->dev, "%s: create_button_input_dev failed.\n", + __func__); + goto fail_no_btn_input_dev; + } + + if (dd->startup) { + ret = dd->startup(dd); + if (ret < 0) { + dev_err(&pdev->dev, "Chip specific init failed\n"); + goto fail_no_regulators; + } + } + + ret = create_regulators(dd); + if (ret < 0) { + dev_err(&pdev->dev, "%s: failed to create regulators\n", + __func__); + goto fail_no_regulators; + } + dd->btn_state = BUTTON_UNK; + + dd->irq_work_queue = create_singlethread_workqueue("abx500_accdet_wq"); + if (!dd->irq_work_queue) { + dev_err(&pdev->dev, "%s: Failed to create wq\n", __func__); + ret = -ENOMEM; + goto fail_no_mem_for_wq; + } + + dd->gpadc = dd->accdet_abx500_gpadc_get(); + + INIT_DELAYED_WORK(&dd->detect_work, detect_work); + INIT_DELAYED_WORK(&dd->unplug_irq_work, unplug_irq_handler_work); + INIT_DELAYED_WORK(&dd->init_work, init_work); + + /* Deferred init/detect since no use for the info early in boot */ + queue_delayed_work(dd->irq_work_queue, + &dd->init_work, + msecs_to_jiffies(INIT_DELAY_MS)); + + platform_set_drvdata(pdev, dd); + + return 0; +fail_no_mem_for_wq: + free_regulators(dd); +fail_no_regulators: + input_unregister_device(dd->btn_input_dev); +fail_no_btn_input_dev: + if (dd->pdata->video_ctrl_gpio) + gpio_free(dd->pdata->video_ctrl_gpio); + return ret; +} + +/* + * Performs platform device cleanup + */ +static void abx500_accessory_cleanup(struct abx500_ad *dd) +{ + dev_dbg(&dd->pdev->dev, "Enter: %s\n", __func__); + + dd->jack_type = JACK_TYPE_UNSPECIFIED; + config_accdetect(dd); + + gpio_free(dd->pdata->video_ctrl_gpio); + input_unregister_device(dd->btn_input_dev); + free_regulators(dd); + + cancel_delayed_work(&dd->detect_work); + cancel_delayed_work(&dd->unplug_irq_work); + cancel_delayed_work(&dd->init_work); + flush_workqueue(dd->irq_work_queue); + destroy_workqueue(dd->irq_work_queue); + + kfree(dd); +} + +static int __devinit abx500_acc_detect_probe(struct platform_device *pdev) +{ + + return abx500_accessory_init(pdev); +} + +static int __devexit abx500_acc_detect_remove(struct platform_device *pdev) +{ + abx500_accessory_cleanup(platform_get_drvdata(pdev)); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +#if defined(CONFIG_PM) +static int abx500_acc_detect_suspend(struct device *dev) +{ + struct platform_device *pdev = container_of(dev, + struct platform_device, dev); + struct abx500_ad *dd = platform_get_drvdata(pdev); + int irq_id, irq; + + dev_dbg(&dd->pdev->dev, "%s: Enter\n", __func__); + + cancel_delayed_work_sync(&dd->unplug_irq_work); + cancel_delayed_work_sync(&dd->detect_work); + cancel_delayed_work_sync(&dd->init_work); + + if (dd->pdata->is_detection_inverted) + abx500_accdet_irq_desc = dd->irq_desc_inverted; + else + abx500_accdet_irq_desc = dd->irq_desc_norm; + + for (irq_id = 0; irq_id < dd->no_irqs; irq_id++) { + if (abx500_accdet_irq_desc[irq_id].registered == 1) { + irq = platform_get_irq_byname( + dd->pdev, + abx500_accdet_irq_desc[irq_id].name); + + disable_irq(irq); + } + } + + dd->turn_off_accdet_comparator(pdev); + + if (dd->jack_type == JACK_TYPE_HEADSET) + accessory_regulator_disable(dd, REGULATOR_VAMIC1); + + return 0; +} + +static int abx500_acc_detect_resume(struct device *dev) +{ + struct platform_device *pdev = container_of(dev, + struct platform_device, dev); + struct abx500_ad *dd = platform_get_drvdata(pdev); + int irq_id, irq; + + dev_dbg(&dd->pdev->dev, "%s: Enter\n", __func__); + + if (dd->jack_type == JACK_TYPE_HEADSET) + accessory_regulator_enable(dd, REGULATOR_VAMIC1); + + dd->turn_on_accdet_comparator(pdev); + + if (dd->pdata->is_detection_inverted) + abx500_accdet_irq_desc = dd->irq_desc_inverted; + else + abx500_accdet_irq_desc = dd->irq_desc_norm; + + for (irq_id = 0; irq_id < dd->no_irqs; irq_id++) { + if (abx500_accdet_irq_desc[irq_id].registered == 1) { + irq = platform_get_irq_byname( + dd->pdev, + abx500_accdet_irq_desc[irq_id].name); + + enable_irq(irq); + + } + } + + /* After resume, reinitialize */ + dd->gpio35_dir_set = dd->accdet1_th_set = dd->accdet2_th_set = 0; + queue_delayed_work(dd->irq_work_queue, &dd->init_work, 0); + + return 0; +} +#else +#define abx500_acc_detect_suspend NULL +#define abx500_acc_detect_resume NULL +#endif + +static struct platform_device_id abx500_accdet_ids[] = { +#ifdef CONFIG_INPUT_AB5500_ACCDET + { "ab5500-acc-det", (kernel_ulong_t)&ab5500_accessory_det_callbacks, }, +#endif +#ifdef CONFIG_INPUT_AB8500_ACCDET + { "ab8500-acc-det", (kernel_ulong_t)&ab8500_accessory_det_callbacks, }, +#endif + { }, +}; + +static const struct dev_pm_ops abx_ops = { + .suspend = abx500_acc_detect_suspend, + .resume = abx500_acc_detect_resume, +}; + +static struct platform_driver abx500_acc_detect_platform_driver = { + .driver = { + .name = "abx500-acc-det", + .owner = THIS_MODULE, + .pm = &abx_ops, + }, + .probe = abx500_acc_detect_probe, + .id_table = abx500_accdet_ids, + .remove = __devexit_p(abx500_acc_detect_remove), +}; + +static int __init abx500_acc_detect_init(void) +{ + return platform_driver_register(&abx500_acc_detect_platform_driver); +} + +static void __exit abx500_acc_detect_exit(void) +{ + platform_driver_unregister(&abx500_acc_detect_platform_driver); +} + +module_init(abx500_acc_detect_init); +module_exit(abx500_acc_detect_exit); + +MODULE_DESCRIPTION("ABx500 AV Accessory detection driver"); +MODULE_ALIAS("platform:abx500-acc-det"); +MODULE_AUTHOR("ST-Ericsson"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From bbbf3dd941c299e15fc97b0252677fc63356dca6 Mon Sep 17 00:00:00 2001 From: Avinash A Date: Mon, 26 Sep 2011 19:47:27 +0530 Subject: input: ts-cypress proper timing for each mode change The time interval for the cypress controller in deep sleep state after the last communication should be Tdelay > 2*Trefresh. Trefresh is 14.3ms ST-Ericsson ID: 359690, 357430 ST-Ericsson Linux next: NA ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I86aedb29157ea8a2e3d685747b02f8c596945cdf Signed-off-by: Avinash A Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/31993 Reviewed-by: Avinash A Tested-by: Avinash A Reviewed-by: Rabin VINCENT --- drivers/input/touchscreen/cyttsp_core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/input/touchscreen/cyttsp_core.c b/drivers/input/touchscreen/cyttsp_core.c index 076417439ab..7a2aa109034 100755 --- a/drivers/input/touchscreen/cyttsp_core.c +++ b/drivers/input/touchscreen/cyttsp_core.c @@ -150,6 +150,7 @@ #define CY_DELAY_DFLT 20 /* ms */ #define CY_DELAY_MAX (500/CY_DELAY_DFLT) /* half second */ #define CY_DELAY_SYSINFO 20 /* ms */ +#define CY_MODE_CHANGE_DELAY 30 /* ms */ #define CY_DELAY_BL 300 #define CY_DELAY_DNLOAD 100 /* ms */ #define CY_HNDSHK_BIT 0x80 @@ -1698,6 +1699,7 @@ static int cyttsp_suspend(struct cyttsp *ts) CY_REG_BASE, sizeof(sleep_mode), &sleep_mode); if (!(retval < 0)) ts->platform_data->power_state = CY_SLEEP_STATE; + msleep(CY_MODE_CHANGE_DELAY); } DBG(printk(KERN_INFO"%s: Sleep Power state is %s\n", __func__, (ts->platform_data->power_state == CY_ACTIVE_STATE) ? -- cgit v1.2.3 From 2a17011c61875a1f28953dc216803f71f74224b8 Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Tue, 20 Sep 2011 13:35:56 +0200 Subject: input: ab8500: update function call for gpio configuration ab8500_config_pull_up_or_down is renamed to ab8500_config_pulldown to match the functionality implemented in the function. Change-Id: Ie9566657972b67842828ce115ff5d7072450e163 Signed-off-by: Mian Yousaf Kaukab Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/32119 Reviewed-by: Jonas ABERG Tested-by: Jonas ABERG --- drivers/input/misc/ab8500-accdet.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/misc/ab8500-accdet.c b/drivers/input/misc/ab8500-accdet.c index 369cd0ab3b3..83b999a1597 100644 --- a/drivers/input/misc/ab8500-accdet.c +++ b/drivers/input/misc/ab8500-accdet.c @@ -260,7 +260,7 @@ static void ab8500_config_hw_test_plug_connected(struct abx500_ad *dd, dev_dbg(&dd->pdev->dev, "%s:%d\n", __func__, enable); - ret = ab8500_config_pull_up_or_down(&dd->pdev->dev, + ret = ab8500_config_pulldown(&dd->pdev->dev, dd->pdata->video_ctrl_gpio, !enable); if (ret < 0) { dev_err(&dd->pdev->dev, -- cgit v1.2.3 From b20a5e43161d4b3ff9369543b7cb8862b1f9eedf Mon Sep 17 00:00:00 2001 From: Mian Yousaf Kaukab Date: Wed, 21 Sep 2011 11:20:43 +0200 Subject: input: ab8500: update supply name Regulator supply name update according to the mainline version of regulator framework. Change-Id: If18e24491ac80369a7325536c8353758aff33056 Signed-off-by: Mian Yousaf Kaukab Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/32122 Reviewed-by: Jonas ABERG Tested-by: Jonas ABERG --- drivers/input/misc/ab8500-accdet.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/misc/ab8500-accdet.c b/drivers/input/misc/ab8500-accdet.c index 83b999a1597..35ddcc08e5f 100644 --- a/drivers/input/misc/ab8500-accdet.c +++ b/drivers/input/misc/ab8500-accdet.c @@ -81,7 +81,7 @@ static struct accessory_regu_descriptor ab8500_regu_desc[3] = { }, { .id = REGULATOR_AVSWITCH, - .name = "vcc-avswitch", + .name = "vcc-N2158", }, }; -- cgit v1.2.3 From 47dfdc89c8f3ac2d87363a6ac2401d2ec8ce1176 Mon Sep 17 00:00:00 2001 From: Virupax Sadashivpetimath Date: Mon, 26 Sep 2011 21:19:13 +0530 Subject: input:misc: Remove TVout support in accessory driver The TVout hardware block is not used, so remove the related code. ST-Ericsson ID: 355539 ST-Ericsson Linux next: 344984 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I0f0f39b13f5b48de6096d076b9dd5253a3c14346 Signed-off-by: Virupax Sadashivpetimath Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/32003 Reviewed-by: Srinidhi KASAGAR --- arch/arm/mach-ux500/include/mach/abx500-accdet.h | 4 -- drivers/input/misc/ab5500-accdet.c | 89 ------------------------ drivers/input/misc/ab8500-accdet.c | 2 - drivers/input/misc/abx500-accdet.c | 11 --- 4 files changed, 106 deletions(-) diff --git a/arch/arm/mach-ux500/include/mach/abx500-accdet.h b/arch/arm/mach-ux500/include/mach/abx500-accdet.h index 914a7087ddb..03c627f6011 100644 --- a/arch/arm/mach-ux500/include/mach/abx500-accdet.h +++ b/arch/arm/mach-ux500/include/mach/abx500-accdet.h @@ -280,7 +280,6 @@ struct accessory_detect_task { * @config_hw_test_plug_connected: Call back to configure the hw for * accessory detection. * @set_av_switch: Call back to configure the switch for tvout or audioout. - * @startup: Call back to do the Chip specific initialization. * @get_platform_data: call to get platform specific data. */ struct abx500_ad { @@ -316,8 +315,6 @@ struct abx500_ad { struct accessory_regu_descriptor *regu_desc; int no_of_regu_desc; - bool tv_out_connected; - void (*config_accdetect2_hw)(struct abx500_ad *, int); void (*config_accdetect1_hw)(struct abx500_ad *, int); int (*detect_plugged_in)(struct abx500_ad *); @@ -329,7 +326,6 @@ struct abx500_ad { void (*config_hw_test_plug_connected)(struct abx500_ad *dd, int enable); void (*set_av_switch)(struct abx500_ad *dd, enum accessory_avcontrol_dir dir); - int (*startup)(struct abx500_ad *dd); struct abx500_accdet_platform_data * (*get_platform_data)(struct platform_device *pdev); }; diff --git a/drivers/input/misc/ab5500-accdet.c b/drivers/input/misc/ab5500-accdet.c index 9858d93a475..828ff29cb1e 100644 --- a/drivers/input/misc/ab5500-accdet.c +++ b/drivers/input/misc/ab5500-accdet.c @@ -28,8 +28,6 @@ #define AB5500_ACC_DET_CTRL_REG 0x23 #define AB5500_VDENC_CTRL0 0x80 -#define ENABLE_TV_PLUG_DETECT 0x01 - /* REGISTER: AB8500_ACC_DET_CTRL_REG */ #define BITS_ACCDETCTRL2_ENA (0x20 | 0x10 | 0x08) #define BITS_ACCDETCTRL1_ENA (0x02 | 0x01) @@ -243,97 +241,11 @@ static void ab5500_turn_on_accdet_comparator(struct platform_device *pdev) acc_det_ctrl_suspend_val); } -irqreturn_t plug_tv_connect_irq_handler(int irq, void *_userdata) -{ - struct abx500_ad *dd = _userdata; - - dev_dbg(&dd->pdev->dev, "%s: Enter (irq=%d)\n", __func__, irq); - - dd->tv_out_connected = true; - report_jack_status(dd); - - return IRQ_HANDLED; -} - -irqreturn_t plug_tv_removal_irq_handler(int irq, void *_userdata) -{ - struct abx500_ad *dd = _userdata; - - dev_dbg(&dd->pdev->dev, "%s: Enter (irq=%d)\n", __func__, irq); - - dd->tv_out_connected = false; - report_jack_status(dd); - - return IRQ_HANDLED; -} - static void *ab5500_accdet_abx500_gpadc_get(void) { return ab5500_gpadc_get("ab5500-adc.0"); } -static int ab5500_startup(struct abx500_ad *dd) -{ - int ret; - int irq, irq_removal; - - irq = platform_get_irq_byname(dd->pdev, - "plugTVdet"); - if (irq < 0) { - dev_err(&dd->pdev->dev, "%s: Failed to get irq plugTVdet \n", - __func__); - return irq; - } - - irq_removal = platform_get_irq_byname(dd->pdev, - "plugTVdet_removal"); - if (irq_removal < 0) { - dev_err(&dd->pdev->dev, "%s: Failed to get irq" - "plugTVdet_removal \n", __func__); - return irq_removal; - } - - ret = request_threaded_irq(irq, NULL, - plug_tv_connect_irq_handler, - IRQF_NO_SUSPEND | IRQF_SHARED, - "plugTVdet", - dd); - if (ret != 0) { - dev_err(&dd->pdev->dev, - "%s: Failed to claim irq plugTVdet (%d)\n", - __func__, ret); - return ret; - } - - ret = request_threaded_irq(irq_removal, NULL, - plug_tv_removal_irq_handler, - IRQF_NO_SUSPEND | IRQF_SHARED, - "plugTVdet_removal", - dd); - if (ret != 0) { - dev_err(&dd->pdev->dev, - "%s: Failed to claim irq plugTVdet_removal (%d)\n", - __func__, ret); - goto req_irq_fail; - } - - ret = abx500_set_register_interruptible(&dd->pdev->dev, - AB5500_BANK_VDENC, AB5500_VDENC_CTRL0, - ENABLE_TV_PLUG_DETECT); - if (ret < 0) { - dev_err(&dd->pdev->dev, - "%s: Failed to update reg (%d).\n", __func__, ret); - goto ab_write_fail; - } - - return 0; - -req_irq_fail: -ab_write_fail: - free_irq(irq, dd); - return ret; -} - struct abx500_accdet_platform_data * ab5500_get_platform_data(struct platform_device *pdev) { @@ -355,7 +267,6 @@ struct abx500_ad ab5500_accessory_det_callbacks = { .turn_on_accdet_comparator = ab5500_turn_on_accdet_comparator, .accdet_abx500_gpadc_get = ab5500_accdet_abx500_gpadc_get, .config_hw_test_plug_connected = ab5500_config_hw_test_plug_connected, - .startup = ab5500_startup, .set_av_switch = NULL, .get_platform_data = ab5500_get_platform_data, }; diff --git a/drivers/input/misc/ab8500-accdet.c b/drivers/input/misc/ab8500-accdet.c index 35ddcc08e5f..5b51d2bd5e2 100644 --- a/drivers/input/misc/ab8500-accdet.c +++ b/drivers/input/misc/ab8500-accdet.c @@ -396,9 +396,7 @@ struct abx500_ad ab8500_accessory_det_callbacks = { .turn_on_accdet_comparator = ab8500_turn_on_accdet_comparator, .accdet_abx500_gpadc_get = ab8500_accdet_abx500_gpadc_get, .config_hw_test_plug_connected = ab8500_config_hw_test_plug_connected, - .startup = NULL, .set_av_switch = ab8500_set_av_switch, - .tv_out_connected = false, .get_platform_data = ab8500_get_platform_data, }; diff --git a/drivers/input/misc/abx500-accdet.c b/drivers/input/misc/abx500-accdet.c index 717bbc09f8f..67403e5ae5d 100644 --- a/drivers/input/misc/abx500-accdet.c +++ b/drivers/input/misc/abx500-accdet.c @@ -276,9 +276,6 @@ void report_jack_status(struct abx500_ad *dd) { int value = 0; - if (dd->tv_out_connected == true) - value = SND_JACK_VIDEOOUT; - /* Never report possible open cable */ if (dd->jack_type == JACK_TYPE_OPENCABLE) goto out; @@ -755,14 +752,6 @@ static int abx500_accessory_init(struct platform_device *pdev) goto fail_no_btn_input_dev; } - if (dd->startup) { - ret = dd->startup(dd); - if (ret < 0) { - dev_err(&pdev->dev, "Chip specific init failed\n"); - goto fail_no_regulators; - } - } - ret = create_regulators(dd); if (ret < 0) { dev_err(&pdev->dev, "%s: failed to create regulators\n", -- cgit v1.2.3 From 2b539339799cd8923ce73f236fb74fc40be6715f Mon Sep 17 00:00:00 2001 From: Karl-Johan Perntz Date: Thu, 29 Sep 2011 13:22:20 +0530 Subject: input: Report all key actions in SKE autoscan mode Driver now use a delayed work to poll the autoscan registers after a key press interrupt. Polling is performed until all keys are released. Solution was chosen since the hardware does not clear the KPRISA interrupt bit while a key is pressed, making detection of a second key action impossible. ST-Ericsson ID: 361135 ST-Ericsson Linux next: Not tested ST-Ericsson FOSS-OUT ID: Trivial Change-Id:Ic258f380ed5a9df78bce68362d4d2ca3e18a9cf1 Signed-off-by: Karl-Johan Perntz Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/32458 Reviewed-by: Naga RADHESH Y Tested-by: Naga RADHESH Y Reviewed-by: Srinidhi KASAGAR --- drivers/input/keyboard/nomadik-ske-keypad.c | 195 ++++++++++++++++++++-------- 1 file changed, 144 insertions(+), 51 deletions(-) diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c index 8c492429c54..72fe5ee790a 100644 --- a/drivers/input/keyboard/nomadik-ske-keypad.c +++ b/drivers/input/keyboard/nomadik-ske-keypad.c @@ -1,4 +1,4 @@ -/* +/** * Copyright (C) ST-Ericsson SA 2010 * * Author: Naveen Kumar G for ST-Ericsson @@ -53,26 +53,34 @@ #define SKE_NUM_ASRX_REGISTERS (4) #define KEY_PRESSED_DELAY 10 + +#define KEY_REPORTED 1 +#define KEY_PRESSED 2 + /** * struct ske_keypad - data structure used by keypad driver - * @irq: irq no - * @reg_base: ske regsiters base address - * @input: pointer to input device object - * @board: keypad platform device - * @keymap: matrix scan code table for keycodes - * @clk: clock structure pointer - * @enable: flag to enable the driver event - * @regulator: pointer to the regulator used for ske kyepad - * @gpio_input_irq: array for gpio irqs - * @key_pressed: hold the key state - * @work: delayed work variable for gpio switch - * @ske_rows: rows gpio array for ske - * @ske_cols: columns gpio array for ske - * @gpio_row: gpio row - * @gpio_col: gpio column - * @gpio_work: delayed work variable for release gpio key + * @dev: Pointer to the structure device + * @irq: irq no + * @reg_base: ske regsiters base address + * @input: pointer to input device object + * @board: keypad platform device + * @keymap: matrix scan code table for keycodes + * @clk: clock structure pointer + * @enable: flag to enable the driver event + * @regulator: pointer to the regulator used for ske kyepad + * @gpio_input_irq: array for gpio irqs + * @key_pressed: hold the key state + * @work: delayed work variable for gpio switch + * @ske_rows: rows gpio array for ske + * @ske_cols: columns gpio array for ske + * @gpio_row: gpio row + * @gpio_col: gpio column + * @gpio_work: delayed work variable for release gpio key + * @keys: matrix holding key status + * @scan_work: delayed work for scaning new key actions */ struct ske_keypad { + struct device *dev; int irq; void __iomem *reg_base; struct input_dev *input; @@ -90,6 +98,8 @@ struct ske_keypad { int gpio_row; int gpio_col; struct delayed_work gpio_work; + u8 keys[SKE_KPD_MAX_ROWS][SKE_KPD_MAX_COLS]; + struct delayed_work scan_work; }; static void ske_keypad_set_bits(struct ske_keypad *keypad, u16 addr, @@ -107,7 +117,7 @@ static void ske_keypad_set_bits(struct ske_keypad *keypad, u16 addr, spin_unlock(&keypad->ske_keypad_lock); } -/* +/** * ske_keypad_chip_init: init keypad controller configuration * * Enable Multi key press detection, auto scan mode @@ -124,7 +134,7 @@ static int __devinit ske_keypad_chip_init(struct ske_keypad *keypad) if (!timeout) return -EINVAL; - /* + /** * set debounce value * keypad dbounce is configured in DBCR[15:8] * dbounce value in steps of 32/32.768 ms @@ -139,7 +149,7 @@ static int __devinit ske_keypad_chip_init(struct ske_keypad *keypad) /* enable multi key detection */ ske_keypad_set_bits(keypad, SKE_CR, 0x0, SKE_KPMLT); - /* + /** * set up the number of columns * KPCN[5:3] defines no. of keypad columns to be auto scanned */ @@ -163,6 +173,7 @@ static void ske_mode_enable(struct ske_keypad *keypad, bool enable) int i; if (!enable) { + dev_dbg(keypad->dev, "%s disable keypad\n", __func__); writel(0, keypad->reg_base + SKE_CR); if (keypad->board->exit) keypad->board->exit(); @@ -173,6 +184,7 @@ static void ske_mode_enable(struct ske_keypad *keypad, bool enable) clk_disable(keypad->clk); regulator_disable(keypad->regulator); } else { + dev_dbg(keypad->dev, "%s enable keypad\n", __func__); regulator_enable(keypad->regulator); clk_enable(keypad->clk); for (i = 0; i < keypad->board->krow; i++) { @@ -239,7 +251,6 @@ static struct attribute_group ske_attr_group = { static void ske_keypad_report(struct ske_keypad *keypad, u8 status, int col) { int row = 0, code, pos; - struct input_dev *input = keypad->input; u32 ske_ris; int num_of_rows; @@ -254,10 +265,13 @@ static void ske_keypad_report(struct ske_keypad *keypad, u8 status, int col) ske_ris = readl(keypad->reg_base + SKE_RIS); keypad->key_pressed = ske_ris & SKE_KPRISA; - input_event(input, EV_MSC, MSC_SCAN, code); - input_report_key(input, keypad->keymap[code], - keypad->key_pressed); - input_sync(input); + dev_dbg(keypad->dev, + "%s key_pressed:%d code:%d row:%d col:%d\n", + __func__, keypad->key_pressed, code, row, col); + + if (keypad->key_pressed) + keypad->keys[row][col] |= KEY_PRESSED; + num_of_rows--; } while (num_of_rows); } @@ -268,7 +282,7 @@ static void ske_keypad_read_data(struct ske_keypad *keypad) int col = 0; int ske_asr, i; - /* + /** * Read the auto scan registers * * Each SKE_ASRx (x=0 to x=3) contains two row values. @@ -293,26 +307,95 @@ static void ske_keypad_read_data(struct ske_keypad *keypad) } } } -static void ske_keypad_scan(struct ske_keypad *keypad) -{ - int timeout = keypad->board->debounce_ms; - /* disable auto scan interrupt; mask the interrupt generated */ - ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0); - ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA); +static void ske_keypad_scan_work(struct work_struct *work) +{ + int timeout = 10; + int i, j, code; + struct ske_keypad *keypad = container_of(work, + struct ske_keypad, scan_work.work); + struct input_dev *input = keypad->input; - while ((readl(keypad->reg_base + SKE_CR) & SKE_KPASON) && --timeout) + /* Wait for autoscan to complete */ + while (readl(keypad->reg_base + SKE_CR) & SKE_KPASON) cpu_relax(); /* SKEx registers are stable and can be read */ ske_keypad_read_data(keypad); - /* wait until raw interrupt is clear */ - while ((readl(keypad->reg_base + SKE_RIS)) && --timeout) - msleep(KEY_PRESSED_DELAY); + /* Check for key actions */ + for (i = 0; i < SKE_KPD_MAX_ROWS; i++) { + for (j = 0; j < SKE_KPD_MAX_COLS; j++) { + switch (keypad->keys[i][j]) { + case KEY_REPORTED: + /** + * Key was reported but is no longer pressed, + * report it as released. + */ + code = MATRIX_SCAN_CODE(i, j, + SKE_KEYPAD_ROW_SHIFT); + input_event(input, EV_MSC, MSC_SCAN, code); + input_report_key(input, keypad->keymap[code], + 0); + input_sync(input); + keypad->keys[i][j] = 0; + dev_dbg(keypad->dev, + "%s Key release reported, code:%d\n", + __func__, code); + break; + case KEY_PRESSED: + /* Key pressed but not yet reported, report */ + code = MATRIX_SCAN_CODE(i, j, + SKE_KEYPAD_ROW_SHIFT); + input_event(input, EV_MSC, MSC_SCAN, code); + input_report_key(input, keypad->keymap[code], + 1); + input_sync(input); + dev_dbg(keypad->dev, + "%s Key press reported, code:%d\n", + __func__, code); + /* Intentional fall though */ + case (KEY_REPORTED | KEY_PRESSED): + /** + * Key pressed and reported, just reset + * KEY_PRESSED for next scan + */ + keypad->keys[i][j] = KEY_REPORTED; + break; + } + } + } + + if (keypad->key_pressed) { + /* Key still pressed, schedule work to poll changes in 50 ms */ + schedule_delayed_work(&keypad->scan_work, + msecs_to_jiffies(50)); + } else { + /* For safty measure, clear interrupt once more */ + ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA); - /* enable auto scan interrupts */ - ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA); + /* Wait for raw interrupt to clear */ + while ((readl(keypad->reg_base + SKE_RIS) & SKE_KPRISA) && + --timeout) { + udelay(10); + } + + if (!timeout) + dev_err(keypad->dev, + "%s Timeed out waiting on irq to clear\n", + __func__); + + /* enable auto scan interrupts */ + ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA); + + /** + * Schedule the work queue to change it to GPIO mode + * if there is no activity in SKE mode + */ + if (!keypad->key_pressed && keypad->enable) + schedule_delayed_work(&keypad->work, + keypad->board->switch_delay); + } } static void ske_gpio_switch_work(struct work_struct *work) @@ -333,6 +416,10 @@ static void ske_gpio_release_work(struct work_struct *work) code = MATRIX_SCAN_CODE(keypad->gpio_row, keypad->gpio_col, SKE_KEYPAD_ROW_SHIFT); + + dev_dbg(keypad->dev, "%s Key press reported, code:%d\n", + __func__, code); + input_event(input, EV_MSC, MSC_SCAN, code); input_report_key(input, keypad->keymap[code], 1); input_sync(input); @@ -366,7 +453,7 @@ static void ske_set_cols(struct ske_keypad *keypad, int col) int i ; int value; - /* + /** * Set all columns except the requested column * output pin as high */ @@ -419,10 +506,16 @@ static irqreturn_t ske_keypad_gpio_irq(int irq, void *dev_id) if (!keypad->enable) { keypad->enable = true; ske_mode_enable(keypad, true); + /** + * Schedule the work queue to change it back to GPIO + * mode if there is no activity in SKE mode + */ + schedule_delayed_work(&keypad->work, + keypad->board->switch_delay); } - /* - * Schedule the work queue to change it to - * report the key pressed, if it is not detected in SKE mode. + /** + * Schedule delayed work to report key press if it is not + * detected in SKE mode. */ if (keypad->key_pressed) schedule_delayed_work(&keypad->gpio_work, @@ -434,18 +527,14 @@ static irqreturn_t ske_keypad_gpio_irq(int irq, void *dev_id) static irqreturn_t ske_keypad_irq(int irq, void *dev_id) { struct ske_keypad *keypad = dev_id; - cancel_delayed_work_sync(&keypad->gpio_work); cancel_delayed_work_sync(&keypad->work); - ske_keypad_scan(keypad); - /* - * Schedule the work queue to change it to - * GPIO mode, if there is no activity in SKE mode - */ - if (!keypad->key_pressed && keypad->enable) - schedule_delayed_work(&keypad->work, - keypad->board->switch_delay); + /* disable auto scan interrupt; mask the interrupt generated */ + ske_keypad_set_bits(keypad, SKE_IMSC, SKE_KPIMA, 0x0); + ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA); + + schedule_delayed_work(&keypad->scan_work, 0); return IRQ_HANDLED; } @@ -505,6 +594,7 @@ static int __devinit ske_keypad_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to allocate keypad memory\n"); goto out_freeclk; } + keypad->dev = &pdev->dev; input = input_allocate_device(); if (!input) { @@ -557,6 +647,7 @@ static int __devinit ske_keypad_probe(struct platform_device *pdev) keypad->clk = clk; INIT_DELAYED_WORK(&keypad->work, ske_gpio_switch_work); INIT_DELAYED_WORK(&keypad->gpio_work, ske_gpio_release_work); + INIT_DELAYED_WORK(&keypad->scan_work, ske_keypad_scan_work); /* allocations are sane, we begin HW initialization */ clk_enable(keypad->clk); @@ -660,6 +751,7 @@ static int __devexit ske_keypad_remove(struct platform_device *pdev) cancel_delayed_work_sync(&keypad->gpio_work); cancel_delayed_work_sync(&keypad->work); + cancel_delayed_work_sync(&keypad->scan_work); free_irq(keypad->irq, keypad); input_unregister_device(keypad->input); @@ -692,6 +784,7 @@ static int ske_keypad_suspend(struct device *dev) else { cancel_delayed_work_sync(&keypad->gpio_work); cancel_delayed_work_sync(&keypad->work); + cancel_delayed_work_sync(&keypad->scan_work); disable_irq(irq); if (keypad->enable) { ske_mode_enable(keypad, false); -- cgit v1.2.3 From 7a36dbfc706d61fa0b17acf6bbc9b936a283c024 Mon Sep 17 00:00:00 2001 From: Robert Marklund Date: Tue, 18 Oct 2011 08:16:07 +0200 Subject: touchscreen: Fix build problems in cyttsp driver The cyttsp driver dont have proper ifdefs around EARLYSUSPEND code. Change-Id: I03ae51f6b2593ce5cfa601158477eeb485e9f82b Signed-off-by: Robert Marklund --- drivers/input/touchscreen/cyttsp_core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/input/touchscreen/cyttsp_core.c b/drivers/input/touchscreen/cyttsp_core.c index 7a2aa109034..2d9ce61bf54 100755 --- a/drivers/input/touchscreen/cyttsp_core.c +++ b/drivers/input/touchscreen/cyttsp_core.c @@ -38,7 +38,9 @@ #include #include #include +#ifdef CONFIG_HAS_EARLYSUSPEND #include +#endif #include #include #include @@ -269,7 +271,9 @@ struct cyttsp { struct work_struct work; struct timer_list timer; struct mutex mutex; +#ifdef CONFIG_HAS_EARLYSUSPEND struct early_suspend early_suspend; +#endif char phys[32]; struct cyttsp_platform_data *platform_data; struct cyttsp_bootloader_data bl_data; -- cgit v1.2.3 From cc0a7272ac859ea8e4f4563ac6f9d9c2a51ea689 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Fri, 18 Nov 2011 12:53:43 +0100 Subject: mach-ux500: bu21013 touchscreen update Signed-off-by: Philippe Langlais --- arch/arm/mach-ux500/board-mop500-stuib.c | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-ux500/board-mop500-stuib.c b/arch/arm/mach-ux500/board-mop500-stuib.c index 8c979770d87..e01bec73b6d 100644 --- a/arch/arm/mach-ux500/board-mop500-stuib.c +++ b/arch/arm/mach-ux500/board-mop500-stuib.c @@ -157,9 +157,19 @@ static struct bu21013_platform_device tsc_plat_device = { .irq = NOMADIK_GPIO_TO_IRQ(TOUCH_GPIO_PIN), .touch_x_max = TOUCH_XMAX, .touch_y_max = TOUCH_YMAX, - .ext_clk = false, - .x_flip = false, - .y_flip = true, + .x_max_res = 480, + .y_max_res = 864, + .portrait = true, + .has_ext_clk = true, + .enable_ext_clk = false, +#if defined(CONFIG_DISPLAY_GENERIC_DSI_PRIMARY_ROTATION_ANGLE) && \ + CONFIG_DISPLAY_GENERIC_DSI_PRIMARY_ROTATION_ANGLE == 270 + .x_flip = true, + .y_flip = false, +#else + .x_flip = false, + .y_flip = true, +#endif }; static struct bu21013_platform_device tsc_plat2_device = { @@ -169,9 +179,19 @@ static struct bu21013_platform_device tsc_plat2_device = { .irq = NOMADIK_GPIO_TO_IRQ(TOUCH_GPIO_PIN), .touch_x_max = TOUCH_XMAX, .touch_y_max = TOUCH_YMAX, - .ext_clk = false, - .x_flip = false, - .y_flip = true, + .x_max_res = 480, + .y_max_res = 864, + .portrait = true, + .has_ext_clk = true, + .enable_ext_clk = false, +#if defined(CONFIG_DISPLAY_GENERIC_DSI_PRIMARY_ROTATION_ANGLE) && \ + CONFIG_DISPLAY_GENERIC_DSI_PRIMARY_ROTATION_ANGLE == 270 + .x_flip = true, + .y_flip = false, +#else + .x_flip = false, + .y_flip = true, +#endif }; static struct i2c_board_info __initdata u8500_i2c3_devices_stuib[] = { -- cgit v1.2.3 From 64c4c18d65a184d1b2317242de768320c4fb3d5b Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Thu, 1 Dec 2011 16:20:59 +0100 Subject: input: ab8500-accdet: Include module.h after 3.2 update Signed-off-by: Philippe Langlais --- drivers/input/misc/ab8500-accdet.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/input/misc/ab8500-accdet.c b/drivers/input/misc/ab8500-accdet.c index 5b51d2bd5e2..2d9c740bc17 100644 --- a/drivers/input/misc/ab8500-accdet.c +++ b/drivers/input/misc/ab8500-accdet.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include -- cgit v1.2.3 From 5dc2fc2d46c93f9db397a75acb0e17e0351bf150 Mon Sep 17 00:00:00 2001 From: Naga Radhesh Date: Tue, 11 Oct 2011 13:51:01 +0530 Subject: input: Increase schedule Scan work delay Increase Scan work delay,to reduce CPU load ST-Ericsson ID: 365018 ST-Ericsson Linux next: NA ST-Ericsson FOSS-OUT ID: NA Signed-off-by: Naga Radhesh Change-Id: Ibf21ffd807d7603ad4ac68558cff723b801d8c80 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33604 Reviewed-by: QABUILD Reviewed-by: Naveen Kumar GADDIPATI Reviewed-by: Karl-Johan PERNTZ Reviewed-by: Srinidhi KASAGAR --- drivers/input/keyboard/nomadik-ske-keypad.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c index 72fe5ee790a..c483363d6e5 100644 --- a/drivers/input/keyboard/nomadik-ske-keypad.c +++ b/drivers/input/keyboard/nomadik-ske-keypad.c @@ -367,9 +367,13 @@ static void ske_keypad_scan_work(struct work_struct *work) } if (keypad->key_pressed) { - /* Key still pressed, schedule work to poll changes in 50 ms */ + /* + * Key still pressed, schedule work to poll changes in 100 ms + * After increasing the delay from 50 to 100 it is taking + * 2% to 3% load on average. + */ schedule_delayed_work(&keypad->scan_work, - msecs_to_jiffies(50)); + msecs_to_jiffies(100)); } else { /* For safty measure, clear interrupt once more */ ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA); -- cgit v1.2.3 From 45ec2882843ccc71f3c6ed33a9435226489796db Mon Sep 17 00:00:00 2001 From: Avinash A Date: Wed, 19 Oct 2011 10:49:37 +0530 Subject: input: ts-cypress reduce the delays The gesture set up is done after it is checked for correct values in the operational mode. In the operational mode if there is failure it is retried 26 times which delays the bootup This fixes these issue along with the boot delay reduction ST-Ericsson ID: 364302 ST-Ericsson Linux next: NA ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I80b2dc32434b839933f5378e892ffc336e989de2 Signed-off-by: Avinash A Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/34418 Reviewed-by: Avinash A Tested-by: Avinash A Reviewed-by: Srinidhi KASAGAR --- drivers/input/touchscreen/cyttsp_core.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/input/touchscreen/cyttsp_core.c b/drivers/input/touchscreen/cyttsp_core.c index 2d9ce61bf54..fe15cc51a67 100755 --- a/drivers/input/touchscreen/cyttsp_core.c +++ b/drivers/input/touchscreen/cyttsp_core.c @@ -149,7 +149,7 @@ #define CY_MAXZ 255 #define CY_OK 0 #define CY_INIT 1 -#define CY_DELAY_DFLT 20 /* ms */ +#define CY_DELAY_DFLT 10 /* ms */ #define CY_DELAY_MAX (500/CY_DELAY_DFLT) /* half second */ #define CY_DELAY_SYSINFO 20 /* ms */ #define CY_MODE_CHANGE_DELAY 30 /* ms */ @@ -1334,9 +1334,9 @@ static int cyttsp_exit_bl_mode(struct cyttsp *ts) goto fail; } do { - msleep(20); + msleep(10); cyttsp_load_bl_regs(ts); - } while (GET_BOOTLOADERMODE(ts->bl_data.bl_status) && tries++ < 10); + } while (GET_BOOTLOADERMODE(ts->bl_data.bl_status) && tries++ < 1); DBG2(printk(KERN_INFO "%s: read tries=%d\n", __func__, tries);) @@ -1473,7 +1473,7 @@ static int cyttsp_set_operational_mode(struct cyttsp *ts) } /* wait for TTSP Device to complete switch to Operational mode */ - msleep(20); + msleep(10); tries = 0; gest_default = @@ -1610,6 +1610,9 @@ static int cyttsp_power_on(struct cyttsp *ts) if (retval < 0) goto bypass; + /* init gesture setup; required for active distance */ + cyttsp_gesture_setup(ts); + /* switch back to Operational mode */ DBG2(printk(KERN_INFO"%s: switch back to operational mode\n", __func__);) @@ -1617,9 +1620,6 @@ static int cyttsp_power_on(struct cyttsp *ts) if (retval < 0) goto bypass; - /* init gesture setup; required for active distance */ - cyttsp_gesture_setup(ts); - bypass: if (retval) ts->platform_data->power_state = CY_IDLE_STATE; -- cgit v1.2.3 From ef76f388b9cace00bfe2e17ef97b7df5c442ad4a Mon Sep 17 00:00:00 2001 From: Jonas Aaberg Date: Fri, 30 Sep 2011 13:10:26 +0200 Subject: input: gpio-keys: Disable hw on suspend Disable hw if active when suspending if the hw can't wake the system from suspend. ST-Ericsson Linux next: - ST-Ericsson ID: - ST-Ericsson FOSS-OUT ID: Trivial Change-Id: Ieaf2a2022b23782bc19afa152dc5e3fa5a0024ad Signed-off-by: Jonas Aaberg Signed-off-by: Philippe Langlais Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/32721 Reviewed-by: Bengt JONSSON --- drivers/input/keyboard/gpio_keys.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index ed1ed469d08..523ef6cd03c 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -42,6 +42,8 @@ struct gpio_keys_drvdata { struct input_dev *input; struct mutex disable_lock; unsigned int n_buttons; + bool enabled; + bool enable_after_suspend; int (*enable)(struct device *dev); void (*disable)(struct device *dev); struct gpio_button_data data[0]; @@ -437,6 +439,7 @@ static int gpio_keys_open(struct input_dev *input) { struct gpio_keys_drvdata *ddata = input_get_drvdata(input); + ddata->enabled = true; return ddata->enable ? ddata->enable(input->dev.parent) : 0; } @@ -446,6 +449,7 @@ static void gpio_keys_close(struct input_dev *input) if (ddata->disable) ddata->disable(input->dev.parent); + ddata->enabled = false; } /* @@ -578,6 +582,7 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) ddata->n_buttons = pdata->nbuttons; ddata->enable = pdata->enable; ddata->disable = pdata->disable; + ddata->enabled = false; mutex_init(&ddata->disable_lock); platform_set_drvdata(pdev, ddata); @@ -709,6 +714,10 @@ static int gpio_keys_suspend(struct device *dev) enable_irq_wake(irq); } } + } else { + ddata->enable_after_suspend = ddata->enabled; + if (ddata->enabled) + gpio_keys_close(ddata->input); } return 0; @@ -729,6 +738,10 @@ static int gpio_keys_resume(struct device *dev) gpio_keys_report_event(&ddata->data[i]); } + + if (!device_may_wakeup(dev) && ddata->enable_after_suspend) + gpio_keys_open(ddata->input); + input_sync(ddata->input); return 0; -- cgit v1.2.3 From f4b9fbd86682cd7c9770e0a237680571f13d76c3 Mon Sep 17 00:00:00 2001 From: Jonas Aaberg Date: Fri, 21 Oct 2011 07:48:23 +0200 Subject: input: keyboard: nomadik-ske: Do not always enable on suspend The resume hook did always enable the ske driver on resume. ST-Ericsson Linux next: - ST-Ericsson ID: 369127 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I613c0d0b0d626c77a422eaa65e3060dcc866ed56 Signed-off-by: Jonas Aaberg Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/34793 Reviewed-by: QABUILD Reviewed-by: Bengt JONSSON --- drivers/input/keyboard/nomadik-ske-keypad.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c index c483363d6e5..791c535a66f 100644 --- a/drivers/input/keyboard/nomadik-ske-keypad.c +++ b/drivers/input/keyboard/nomadik-ske-keypad.c @@ -67,6 +67,7 @@ * @keymap: matrix scan code table for keycodes * @clk: clock structure pointer * @enable: flag to enable the driver event + * @enable_on_resume: set if keypad should be enabled on resume * @regulator: pointer to the regulator used for ske kyepad * @gpio_input_irq: array for gpio irqs * @key_pressed: hold the key state @@ -89,6 +90,7 @@ struct ske_keypad { struct clk *clk; spinlock_t ske_keypad_lock; bool enable; + bool enable_on_resume; struct regulator *regulator; int gpio_input_irq[SKE_KPD_MAX_ROWS]; int key_pressed; @@ -790,6 +792,9 @@ static int ske_keypad_suspend(struct device *dev) cancel_delayed_work_sync(&keypad->work); cancel_delayed_work_sync(&keypad->scan_work); disable_irq(irq); + + keypad->enable_on_resume = keypad->enable; + if (keypad->enable) { ske_mode_enable(keypad, false); keypad->enable = false; @@ -808,7 +813,7 @@ static int ske_keypad_resume(struct device *dev) if (device_may_wakeup(dev)) disable_irq_wake(irq); else { - if (!keypad->enable) { + if (keypad->enable_on_resume) { keypad->enable = true; ske_mode_enable(keypad, true); } -- cgit v1.2.3 From 0c0bfac359131e590ddd585e222d0fa4c2e06098 Mon Sep 17 00:00:00 2001 From: Avinash A Date: Wed, 2 Nov 2011 15:53:46 +0530 Subject: mach-ux500: add regulator flag for synaptic touchscreen This adds the missing regulator enable flag in the platform data ST-Ericsson ID: 370880 ST-Ericsson Linux next: NA ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I37082ddb48dea0a9e8e04bb395f2993b988dcdcd Signed-off-by: Avinash A Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/36066 Reviewed-by: Avinash A Tested-by: Avinash A Reviewed-by: Srinidhi KASAGAR --- drivers/staging/ste_rmi4/board-mop500-u8500uib-rmi4.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/ste_rmi4/board-mop500-u8500uib-rmi4.c b/drivers/staging/ste_rmi4/board-mop500-u8500uib-rmi4.c index a272e488e5b..6f9029d81ab 100644 --- a/drivers/staging/ste_rmi4/board-mop500-u8500uib-rmi4.c +++ b/drivers/staging/ste_rmi4/board-mop500-u8500uib-rmi4.c @@ -22,6 +22,7 @@ static struct synaptics_rmi4_platform_data rmi4_i2c_dev_platformdata = { .irq_type = (IRQF_TRIGGER_FALLING | IRQF_SHARED), .x_flip = false, .y_flip = true, + .regulator_en = true, }; struct i2c_board_info __initdata mop500_i2c3_devices_u8500[] = { -- cgit v1.2.3 From 26dc4fb5da3a9009ae1f4d71c49c6fe7fe2f7162 Mon Sep 17 00:00:00 2001 From: Virupax Sadashivpetimath Date: Mon, 10 Oct 2011 15:29:59 +0530 Subject: abx500-accdet: Enable Vamic1 for the microphone detection Vamic1 needs to be enabled for detecting accessories with microphone in it, so enable it before checking for the type of accessory device connected. ST-Ericsson Linux next: NA ST-Ericsson ID: 357950 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I4ca9ece6eea9a16eaae8a1372741fbf6e556eb38 Signed-off-by: Virupax Sadashivpetimath Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/33446 Reviewed-by: Srinidhi KASAGAR --- drivers/input/misc/abx500-accdet.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/input/misc/abx500-accdet.c b/drivers/input/misc/abx500-accdet.c index 67403e5ae5d..92aa69d1176 100644 --- a/drivers/input/misc/abx500-accdet.c +++ b/drivers/input/misc/abx500-accdet.c @@ -53,9 +53,9 @@ #define ACCESSORY_CVIDEO_DET_VOL_MAX 105 #define ACCESSORY_CARKIT_DET_VOL_MIN 1100 #define ACCESSORY_CARKIT_DET_VOL_MAX 1300 -#define ACCESSORY_HEADSET_DET_VOL_MIN 0 -#define ACCESSORY_HEADSET_DET_VOL_MAX 200 -#define ACCESSORY_OPENCABLE_DET_VOL_MIN 1730 +#define ACCESSORY_HEADSET_DET_VOL_MIN 1301 +#define ACCESSORY_HEADSET_DET_VOL_MAX 2000 +#define ACCESSORY_OPENCABLE_DET_VOL_MIN 2001 #define ACCESSORY_OPENCABLE_DET_VOL_MAX 2150 @@ -516,8 +516,6 @@ static int detect_hw(struct abx500_ad *dd, status = dd->detect_plugged_in(dd); break; case JACK_TYPE_CARKIT: - dd->config_hw_test_basic_carkit(dd, 1); - /* flow through.. */ case JACK_TYPE_HEADPHONE: case JACK_TYPE_CVIDEO: case JACK_TYPE_HEADSET: @@ -544,6 +542,8 @@ static enum accessory_jack_type detect(struct abx500_ad *dd, int i; accessory_regulator_enable(dd, REGULATOR_VAUDIO | REGULATOR_AVSWITCH); + /* enable the VAMIC1 regulator */ + dd->config_hw_test_basic_carkit(dd, 0); for (i = 0; i < ARRAY_SIZE(detect_ops); ++i) { if (detect_hw(dd, &detect_ops[i])) { @@ -553,7 +553,6 @@ static enum accessory_jack_type detect(struct abx500_ad *dd, } } - dd->config_hw_test_basic_carkit(dd, 0); dd->config_hw_test_plug_connected(dd, 0); if (jack_supports_buttons(type)) -- cgit v1.2.3 From 5e7b36a05debcf57fd9d421a50c2b41f250ded37 Mon Sep 17 00:00:00 2001 From: Karl-Johan Perntz Date: Tue, 8 Nov 2011 10:37:22 +0100 Subject: input:ske:Use gpio mode after resume to save power Add delayed work to switch to gpio mode if no key action occurs after resume. This will prevent the keypad from getting stuck in ske mode that consumes more power than gpio mode. ST-Ericsson Linux next: Not tested ST-Ericsson ID: 372052 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I090a9067828a1f706f427f793f685ac298cf2a2e Signed-off-by: Karl-Johan Perntz Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/36836 Reviewed-by: Naveen Kumar GADDIPATI Reviewed-by: Naga RADHESH Y Reviewed-by: Srinidhi KASAGAR --- drivers/input/keyboard/nomadik-ske-keypad.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c index 791c535a66f..7142ad9bf5f 100644 --- a/drivers/input/keyboard/nomadik-ske-keypad.c +++ b/drivers/input/keyboard/nomadik-ske-keypad.c @@ -816,6 +816,13 @@ static int ske_keypad_resume(struct device *dev) if (keypad->enable_on_resume) { keypad->enable = true; ske_mode_enable(keypad, true); + /* + * Schedule the work queue to change it to GPIO mode + * if there is no activity in SKE mode + */ + if (!keypad->key_pressed) + schedule_delayed_work(&keypad->work, + keypad->board->switch_delay); } enable_irq(irq); } -- cgit v1.2.3 From 48dd540fd2a8eb81694876c3b95adeb164c3609d Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Thu, 12 Jan 2012 13:04:14 +0100 Subject: input: ux500: Coding style fixes Fix the most obvious violations of the kernel coding style Signed-off-by: Jonas Aaberg --- arch/arm/mach-ux500/include/mach/abx500-accdet.h | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/arch/arm/mach-ux500/include/mach/abx500-accdet.h b/arch/arm/mach-ux500/include/mach/abx500-accdet.h index 03c627f6011..04082b824ae 100644 --- a/arch/arm/mach-ux500/include/mach/abx500-accdet.h +++ b/arch/arm/mach-ux500/include/mach/abx500-accdet.h @@ -124,14 +124,14 @@ struct abx500_accdet_platform_data { * @JACK_TYPE_CVIDEO CVideo type of accessory connected. */ enum accessory_jack_type { - JACK_TYPE_UNSPECIFIED, - JACK_TYPE_DISCONNECTED, - JACK_TYPE_CONNECTED, - JACK_TYPE_HEADPHONE, - JACK_TYPE_HEADSET, - JACK_TYPE_CARKIT, - JACK_TYPE_OPENCABLE, - JACK_TYPE_CVIDEO + JACK_TYPE_UNSPECIFIED, + JACK_TYPE_DISCONNECTED, + JACK_TYPE_CONNECTED, + JACK_TYPE_HEADPHONE, + JACK_TYPE_HEADSET, + JACK_TYPE_CARKIT, + JACK_TYPE_OPENCABLE, + JACK_TYPE_CVIDEO }; /** @@ -350,4 +350,3 @@ extern struct abx500_ad ab8500_accessory_det_callbacks; #endif #endif /* _ABx500_ACCDET_H */ - -- cgit v1.2.3 From 3c928fd140087dec030bb3ca191e4417926ce89a Mon Sep 17 00:00:00 2001 From: Naga Radhesh Date: Mon, 14 Nov 2011 16:39:31 +0530 Subject: input:ske:Check for ske_enable in resume In resume function check for keypad->enable flag before switching to ske mode. ST-Ericsson ID: 373297 ST-Ericsson Linux next: NA ST-Ericsson FOSS-OUT ID: Trivial Signed-off-by: Naga Radhesh Change-Id: Iccbe3be311821516c5bdf6333bfbf1d3779a58c9 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/37602 Reviewed-by: QATOOLS Tested-by: Karl-Johan PERNTZ Reviewed-by: Karl-Johan PERNTZ Reviewed-by: Naveen Kumar GADDIPATI Reviewed-by: Srinidhi KASAGAR --- drivers/input/keyboard/nomadik-ske-keypad.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c index 7142ad9bf5f..946ec69c03d 100644 --- a/drivers/input/keyboard/nomadik-ske-keypad.c +++ b/drivers/input/keyboard/nomadik-ske-keypad.c @@ -813,7 +813,7 @@ static int ske_keypad_resume(struct device *dev) if (device_may_wakeup(dev)) disable_irq_wake(irq); else { - if (keypad->enable_on_resume) { + if (keypad->enable_on_resume && !keypad->enable) { keypad->enable = true; ske_mode_enable(keypad, true); /* -- cgit v1.2.3 From 9e22ccda81083d59d416423001a4a5f5c5ff785b Mon Sep 17 00:00:00 2001 From: Naga Radhesh Date: Tue, 15 Nov 2011 12:09:57 +0530 Subject: input:db5500:Do not switch to keypad mode always Do not switch to keypad mode always after resume,switch depending on the mode before suspend. ST-Ericsson ID: 373356 ST-Ericsson Linux next: NA ST-Ericsson FOSS-OUT ID: Trivial Signed-off-by: Naga Radhesh Change-Id: Ic0e0b6c1711145cb00839c9f8f125916f8d1aea4 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/37740 Reviewed-by: QABUILD Reviewed-by: Naveen Kumar GADDIPATI Reviewed-by: Rabin VINCENT --- drivers/input/keyboard/db5500_keypad.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/input/keyboard/db5500_keypad.c b/drivers/input/keyboard/db5500_keypad.c index 45d2f652d60..729775d99e8 100644 --- a/drivers/input/keyboard/db5500_keypad.c +++ b/drivers/input/keyboard/db5500_keypad.c @@ -54,6 +54,7 @@ * @gpio_work : delayed work variable for reporting key event in gpio mode * @previous_set: previous set of registers * @enable : flag to enable the driver event + * @enable_on_resume: set if keypad should be enabled on resume * @valid_key : hold the state of valid key press * @db5500_rows : rows gpio array for db5500 keypad * @db5500_cols : cols gpio array for db5500 keypad @@ -73,6 +74,7 @@ struct db5500_keypad { struct delayed_work gpio_work; u8 previous_set[KEYPAD_MAX_ROWS]; bool enable; + bool enable_on_resume; bool valid_key; int db5500_rows[KEYPAD_MAX_ROWS]; int db5500_cols[KEYPAD_MAX_COLS]; @@ -707,6 +709,7 @@ static int db5500_keypad_suspend(struct device *dev) cancel_delayed_work_sync(&keypad->gpio_work); cancel_delayed_work_sync(&keypad->switch_work); disable_irq(irq); + keypad->enable_on_resume = keypad->enable; if (keypad->enable) { db5500_mode_enable(keypad, false); keypad->enable = false; @@ -732,9 +735,15 @@ static int db5500_keypad_resume(struct device *dev) if (device_may_wakeup(dev)) disable_irq_wake(irq); else { - if (!keypad->enable) { + if (keypad->enable_on_resume && !keypad->enable) { keypad->enable = true; db5500_mode_enable(keypad, true); + /* + * Schedule the work queue to change it to GPIO mode + * if there is no activity keypad mode + */ + schedule_delayed_work(&keypad->switch_work, + keypad->board->switch_delay); } enable_irq(irq); } -- cgit v1.2.3 From 60a369de2c9adf0bda48479f6f7ff24811609f3f Mon Sep 17 00:00:00 2001 From: Jonas Aaberg Date: Tue, 15 Nov 2011 10:41:34 +0100 Subject: drivers: gpio-keys: Add runtime support ST-Ericsson Linux next: - ST-Ericsson ID: 370799 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I14fc29c024e8d9b970cb9c0753cef72e2e61a0ca Signed-off-by: Jonas Aaberg Signed-off-by: Philippe Langlais Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/37778 Reviewed-by: QABUILD Reviewed-by: Bengt JONSSON --- drivers/input/keyboard/gpio_keys.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index 523ef6cd03c..a6d4ae1a23a 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -28,6 +28,7 @@ #include #include #include +#include struct gpio_button_data { struct gpio_keys_button *button; @@ -439,6 +440,7 @@ static int gpio_keys_open(struct input_dev *input) { struct gpio_keys_drvdata *ddata = input_get_drvdata(input); + pm_runtime_get_sync(input->dev.parent); ddata->enabled = true; return ddata->enable ? ddata->enable(input->dev.parent) : 0; } @@ -450,6 +452,7 @@ static void gpio_keys_close(struct input_dev *input) if (ddata->disable) ddata->disable(input->dev.parent); ddata->enabled = false; + pm_runtime_put(input->dev.parent); } /* @@ -599,6 +602,8 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) input->id.product = 0x0001; input->id.version = 0x0100; + pm_runtime_enable(&pdev->dev); + /* Enable auto repeat feature of Linux input subsystem */ if (pdata->rep) __set_bit(EV_REP, input->evbit); @@ -672,6 +677,8 @@ static int __devexit gpio_keys_remove(struct platform_device *pdev) struct input_dev *input = ddata->input; int i; + pm_runtime_disable(&pdev->dev); + sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group); device_init_wakeup(&pdev->dev, 0); @@ -716,8 +723,8 @@ static int gpio_keys_suspend(struct device *dev) } } else { ddata->enable_after_suspend = ddata->enabled; - if (ddata->enabled) - gpio_keys_close(ddata->input); + if (ddata->enabled && ddata->disable) + ddata->disable(dev); } return 0; @@ -739,8 +746,9 @@ static int gpio_keys_resume(struct device *dev) gpio_keys_report_event(&ddata->data[i]); } - if (!device_may_wakeup(dev) && ddata->enable_after_suspend) - gpio_keys_open(ddata->input); + if (!device_may_wakeup(dev) && ddata->enable_after_suspend + && ddata->enable) + ddata->enable(dev); input_sync(ddata->input); -- cgit v1.2.3 From f54aef5a94a5e2f4eebcc13841eb8c742ab73553 Mon Sep 17 00:00:00 2001 From: Avinash A Date: Fri, 25 Nov 2011 15:25:31 +0530 Subject: input: touchscreen: cypress: Add button touch support Add the button touch support to the cypress touchscreen ST-Ericsson ID: 373830 ST-Ericsson Linux next: NA ST-Ericsson FOSS-OUT ID: Trivial Change-Id: If5466feeaf2be9949e186bf094e0cc9a0a5f3d6d Signed-off-by: Avinash A Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/39809 Reviewed-by: Avinash A Tested-by: Avinash A Reviewed-by: Srinidhi KASAGAR --- drivers/input/touchscreen/cyttsp_core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/input/touchscreen/cyttsp_core.c b/drivers/input/touchscreen/cyttsp_core.c index fe15cc51a67..57f23b3667b 100755 --- a/drivers/input/touchscreen/cyttsp_core.c +++ b/drivers/input/touchscreen/cyttsp_core.c @@ -795,6 +795,7 @@ no_track_id: t->cur_mt_pos[t->snd_trk[id]][CY_XPOS]); input_report_abs(ts->input, ABS_MT_POSITION_Y, t->cur_mt_pos[t->snd_trk[id]][CY_YPOS]); + input_report_key(ts->input, BTN_TOUCH, 1); if (mt_sync_func) mt_sync_func(ts->input); @@ -815,6 +816,7 @@ no_track_id: ts->prv_mt_pos[ts->prv_mt_tch[id]][CY_XPOS]); input_report_abs(ts->input, ABS_MT_POSITION_Y, ts->prv_mt_pos[ts->prv_mt_tch[id]][CY_YPOS]); + input_report_key(ts->input, BTN_TOUCH, 0); if (mt_sync_func) mt_sync_func(ts->input); -- cgit v1.2.3 From 88a4b6ffb31d50f76a741e9a047d364ed342f2e1 Mon Sep 17 00:00:00 2001 From: Avinash A Date: Tue, 6 Dec 2011 10:10:03 +0530 Subject: input_touchscreen: DocBook: Remove warnings in synaptics driver ST-Ericsson ID: 354484 ST-Ericsson Linux next: NA ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I9ace47541a9cff1923ae4a4634b4bedea260dd2e Signed-off-by: Avinash A Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/41100 Reviewed-by: Avinash A Tested-by: Avinash A Reviewed-by: Srinidhi KASAGAR --- drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c b/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c index e8afb75590a..fd7fed743f7 100644 --- a/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c +++ b/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c @@ -1,5 +1,4 @@ -/** - * +/* * Synaptics Register Mapped Interface (RMI4) I2C Physical Layer Driver. * Copyright (c) 2007-2010, Synaptics Incorporated * -- cgit v1.2.3 From 09687839a7e7446d10bd93231cd5df6102bef6d8 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Fri, 13 Jan 2012 10:56:08 +0100 Subject: Documentation:add kernel-doc for keypad Add kernel docs for keypad Signed-off-by: Naga Radhesh --- Documentation/DocBook/ske_keypad.tmpl | 89 +++++++++++++++++++++++++++++ drivers/input/keyboard/nomadik-ske-keypad.c | 7 ++- 2 files changed, 93 insertions(+), 3 deletions(-) create mode 100644 Documentation/DocBook/ske_keypad.tmpl diff --git a/Documentation/DocBook/ske_keypad.tmpl b/Documentation/DocBook/ske_keypad.tmpl new file mode 100644 index 00000000000..030d5201990 --- /dev/null +++ b/Documentation/DocBook/ske_keypad.tmpl @@ -0,0 +1,89 @@ + + + + + + SKE Keypad + + + + NaveenKumar + Gaddipati + +
+ naveen.gaddipati@stericsson.com +
+
+
+
+ + + 2010 + ST-Ericsson + + + + + Linux standard functions + + + + + + License terms: GNU General Public License (GPL) version 2. + + + +
+ + + + + Introduction + + This documentation describes the API provided by the keypad driver for internal keypad. + + + + + Known Bugs And Assumptions + + + + None + + + None. + + + + + + + + + Structures + + This chapter contains the autogenerated documentation of the structures which are + used in the keypad driver. + +!Iarch/arm/plat-nomadik/include/plat/ske.h + + + + Public Functions Provided + + This ske keypad driver doesn't export any functions. + + + + + Internal Functions Provided + + This chapter contains the autogenerated documentation of the internal functions. + +!Idrivers/input/keyboard/nomadik-ske-keypad.c + + +
diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c index 946ec69c03d..f74775f6006 100644 --- a/drivers/input/keyboard/nomadik-ske-keypad.c +++ b/drivers/input/keyboard/nomadik-ske-keypad.c @@ -1,8 +1,8 @@ -/** +/* * Copyright (C) ST-Ericsson SA 2010 * * Author: Naveen Kumar G for ST-Ericsson - * Author: Sundar Iyer for ST-Ericsson + * co-Author: Sundar Iyer for ST-Ericsson * * License terms:GNU General Public License (GPL) version 2 * @@ -66,6 +66,7 @@ * @board: keypad platform device * @keymap: matrix scan code table for keycodes * @clk: clock structure pointer + * @ske_keypad_lock: lock used while writting into registers * @enable: flag to enable the driver event * @enable_on_resume: set if keypad should be enabled on resume * @regulator: pointer to the regulator used for ske kyepad @@ -121,7 +122,7 @@ static void ske_keypad_set_bits(struct ske_keypad *keypad, u16 addr, /** * ske_keypad_chip_init: init keypad controller configuration - * + * @keypad: pointer to device structure * Enable Multi key press detection, auto scan mode */ static int __devinit ske_keypad_chip_init(struct ske_keypad *keypad) -- cgit v1.2.3 From d19c44b8945f8f41744aac9e520a2048187ba9f4 Mon Sep 17 00:00:00 2001 From: Kristoffer KARLSSON Date: Fri, 16 Dec 2011 14:55:33 +0100 Subject: input: ab8500-accdet: Add unsupported hs detection This patch adds support for detection of headsets with gnd and mic pins switched. ST-Ericsson Linux next: NA ST-Ericsson ID: 361921 ST-Ericsson FOSS-OUT ID: Trivial Depends-On: I01171aa4ce57a25237986cdf2f0ca4e01660fb28 Change-Id: I1e20586940121f4d7da5b3e8b6e132527303076d Signed-off-by: Kristoffer KARLSSON Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/42939 Reviewed-by: QATOOLS Reviewed-by: QABUILD Reviewed-by: Jonas ABERG --- arch/arm/mach-ux500/include/mach/abx500-accdet.h | 8 +++ drivers/input/misc/ab5500-accdet.c | 9 +++ drivers/input/misc/ab8500-accdet.c | 46 +++++++++++++- drivers/input/misc/abx500-accdet.c | 81 +++++++++++++++++++++--- 4 files changed, 133 insertions(+), 11 deletions(-) diff --git a/arch/arm/mach-ux500/include/mach/abx500-accdet.h b/arch/arm/mach-ux500/include/mach/abx500-accdet.h index 04082b824ae..cdd78cd7d0c 100644 --- a/arch/arm/mach-ux500/include/mach/abx500-accdet.h +++ b/arch/arm/mach-ux500/include/mach/abx500-accdet.h @@ -119,6 +119,7 @@ struct abx500_accdet_platform_data { * detect the actual type. In this mode, possible button events are reported. * @JACK_TYPE_HEADPHONE Headphone type of accessory (spkrs only) connected * @JACK_TYPE_HEADSET Headset type of accessory (mic+spkrs) connected + * @JACK_TYPE_UNSUPPORTED_HEADSET Unsupported headset of type accessory connected * @JACK_TYPE_CARKIT Carkit type of accessory connected * @JACK_TYPE_OPENCABLE Open cable connected * @JACK_TYPE_CVIDEO CVideo type of accessory connected. @@ -129,6 +130,7 @@ enum accessory_jack_type { JACK_TYPE_CONNECTED, JACK_TYPE_HEADPHONE, JACK_TYPE_HEADSET, + JACK_TYPE_UNSUPPORTED_HEADSET, JACK_TYPE_CARKIT, JACK_TYPE_OPENCABLE, JACK_TYPE_CVIDEO @@ -225,6 +227,8 @@ struct accessory_regu_descriptor { * making the decision or can cached voltage be used instead. * @minvol minimum voltage (mV) for decision * @maxvol maximum voltage (mV) for decision + * @alt_minvol minimum alternative voltage (mV) for decision + * @alt_maxvol maximum alternative voltage (mV) for decision */ struct accessory_detect_task { const char *typename; @@ -233,6 +237,8 @@ struct accessory_detect_task { int meas_mv; int minvol; int maxvol; + int alt_minvol; + int alt_maxvol; }; /** @@ -271,6 +277,7 @@ struct accessory_detect_task { * @config_accdetect1_hw: Callback for configuring accdet1 comparator. * @detect_plugged_in: Callback to detect type of accessory connected. * @meas_voltage_stable: Callback to read present accdet voltage. + * @meas_alt_voltage_stable: Callback to read present alt accdet voltage. * @config_hw_test_basic_carkit: Callback to configure hw for carkit * detect. * @turn_of_accdet_comparator: Call back to turn off comparators. @@ -319,6 +326,7 @@ struct abx500_ad { void (*config_accdetect1_hw)(struct abx500_ad *, int); int (*detect_plugged_in)(struct abx500_ad *); int (*meas_voltage_stable)(struct abx500_ad *); + int (*meas_alt_voltage_stable)(struct abx500_ad *); void (*config_hw_test_basic_carkit)(struct abx500_ad *, int); void (*turn_off_accdet_comparator)(struct platform_device *pdev); void (*turn_on_accdet_comparator)(struct platform_device *pdev); diff --git a/drivers/input/misc/ab5500-accdet.c b/drivers/input/misc/ab5500-accdet.c index 828ff29cb1e..280af8ac8be 100644 --- a/drivers/input/misc/ab5500-accdet.c +++ b/drivers/input/misc/ab5500-accdet.c @@ -186,6 +186,14 @@ static int ab5500_meas_voltage_stable(struct abx500_ad *dd) return v1; } +/* + * not implemented + */ +static int ab5500_meas_alt_voltage_stable(struct abx500_ad *dd) +{ + return -1; +} + /* * configures HW so that it is possible to make decision whether * accessory is connected or not. @@ -262,6 +270,7 @@ struct abx500_ad ab5500_accessory_det_callbacks = { .config_accdetect1_hw = ab5500_config_accdetect1_hw, .detect_plugged_in = ab5500_detect_plugged_in, .meas_voltage_stable = ab5500_meas_voltage_stable, + .meas_alt_voltage_stable = ab5500_meas_alt_voltage_stable, .config_hw_test_basic_carkit = ab5500_config_hw_test_basic_carkit, .turn_off_accdet_comparator = ab5500_turn_off_accdet_comparator, .turn_on_accdet_comparator = ab5500_turn_on_accdet_comparator, diff --git a/drivers/input/misc/ab8500-accdet.c b/drivers/input/misc/ab8500-accdet.c index 2d9c740bc17..0fe60364d54 100644 --- a/drivers/input/misc/ab8500-accdet.c +++ b/drivers/input/misc/ab8500-accdet.c @@ -21,6 +21,9 @@ #include #include #include +#ifdef CONFIG_SND_SOC_UX500_AB8500 +#include +#endif #define MAX_DET_COUNT 10 #define MAX_VOLT_DIFF 30 @@ -228,8 +231,38 @@ static int ab8500_detect_plugged_in(struct abx500_ad *dd) return value & BIT_ITSOURCE5_ACCDET1 ? 0 : 1; } +#ifdef CONFIG_SND_SOC_UX500_AB8500 + /* - * mic_line_voltage_stable - measures a relative stable voltage from spec. input + * meas_voltage_stable - measures relative stable voltage from spec. input + */ +static int ab8500_meas_voltage_stable(struct abx500_ad *dd) +{ + int ret, mv; + + ret = ux500_ab8500_audio_gpadc_measure((struct ab8500_gpadc *)dd->gpadc, + ACC_DETECT2, false, &mv); + + return (ret < 0) ? ret : mv; +} + +/* + * meas_alt_voltage_stable - measures relative stable voltage from spec. input + */ +static int ab8500_meas_alt_voltage_stable(struct abx500_ad *dd) +{ + int ret, mv; + + ret = ux500_ab8500_audio_gpadc_measure((struct ab8500_gpadc *)dd->gpadc, + ACC_DETECT2, true, &mv); + + return (ret < 0) ? ret : mv; +} + +#else + +/* + * meas_voltage_stable - measures relative stable voltage from spec. input */ static int ab8500_meas_voltage_stable(struct abx500_ad *dd) { @@ -250,6 +283,16 @@ static int ab8500_meas_voltage_stable(struct abx500_ad *dd) return v1; } +/* + * not implemented for non soc setups + */ +static int ab8500_meas_alt_voltage_stable(struct abx500_ad *dd) +{ + return -1; +} + +#endif + /* * configures HW so that it is possible to make decision whether * accessory is connected or not. @@ -392,6 +435,7 @@ struct abx500_ad ab8500_accessory_det_callbacks = { .config_accdetect1_hw = ab8500_config_accdetect1_hw, .detect_plugged_in = ab8500_detect_plugged_in, .meas_voltage_stable = ab8500_meas_voltage_stable, + .meas_alt_voltage_stable = ab8500_meas_alt_voltage_stable, .config_hw_test_basic_carkit = ab8500_config_hw_test_basic_carkit, .turn_off_accdet_comparator = ab8500_turn_off_accdet_comparator, .turn_on_accdet_comparator = ab8500_turn_on_accdet_comparator, diff --git a/drivers/input/misc/abx500-accdet.c b/drivers/input/misc/abx500-accdet.c index 92aa69d1176..d716a1d8870 100644 --- a/drivers/input/misc/abx500-accdet.c +++ b/drivers/input/misc/abx500-accdet.c @@ -49,6 +49,10 @@ #define ACCESSORY_DET_VOL_DONTCARE -1 #define ACCESSORY_HEADPHONE_DET_VOL_MIN 0 #define ACCESSORY_HEADPHONE_DET_VOL_MAX 40 +#define ACCESSORY_U_HEADSET_DET_VOL_MIN 47 +#define ACCESSORY_U_HEADSET_DET_VOL_MAX 732 +#define ACCESSORY_U_HEADSET_ALT_DET_VOL_MIN 25 +#define ACCESSORY_U_HEADSET_ALT_DET_VOL_MAX 50 #define ACCESSORY_CVIDEO_DET_VOL_MIN 41 #define ACCESSORY_CVIDEO_DET_VOL_MAX 105 #define ACCESSORY_CARKIT_DET_VOL_MIN 1100 @@ -87,7 +91,9 @@ static struct accessory_detect_task detect_ops[] = { .meas_mv = 1, .req_det_count = 1, .minvol = ACCESSORY_DET_VOL_DONTCARE, - .maxvol = ACCESSORY_DET_VOL_DONTCARE + .maxvol = ACCESSORY_DET_VOL_DONTCARE, + .alt_minvol = ACCESSORY_DET_VOL_DONTCARE, + .alt_maxvol = ACCESSORY_DET_VOL_DONTCARE }, { .type = JACK_TYPE_HEADPHONE, @@ -95,7 +101,19 @@ static struct accessory_detect_task detect_ops[] = { .meas_mv = 1, .req_det_count = 1, .minvol = ACCESSORY_HEADPHONE_DET_VOL_MIN, - .maxvol = ACCESSORY_HEADPHONE_DET_VOL_MAX + .maxvol = ACCESSORY_HEADPHONE_DET_VOL_MAX, + .alt_minvol = ACCESSORY_DET_VOL_DONTCARE, + .alt_maxvol = ACCESSORY_DET_VOL_DONTCARE + }, + { + .type = JACK_TYPE_UNSUPPORTED_HEADSET, + .typename = "UNSUPPORTED HEADSET", + .meas_mv = 1, + .req_det_count = 2, + .minvol = ACCESSORY_U_HEADSET_DET_VOL_MIN, + .maxvol = ACCESSORY_U_HEADSET_DET_VOL_MAX, + .alt_minvol = ACCESSORY_U_HEADSET_ALT_DET_VOL_MIN, + .alt_maxvol = ACCESSORY_U_HEADSET_ALT_DET_VOL_MAX }, { .type = JACK_TYPE_CVIDEO, @@ -103,7 +121,9 @@ static struct accessory_detect_task detect_ops[] = { .meas_mv = 0, .req_det_count = 4, .minvol = ACCESSORY_CVIDEO_DET_VOL_MIN, - .maxvol = ACCESSORY_CVIDEO_DET_VOL_MAX + .maxvol = ACCESSORY_CVIDEO_DET_VOL_MAX, + .alt_minvol = ACCESSORY_DET_VOL_DONTCARE, + .alt_maxvol = ACCESSORY_DET_VOL_DONTCARE }, { .type = JACK_TYPE_OPENCABLE, @@ -111,7 +131,9 @@ static struct accessory_detect_task detect_ops[] = { .meas_mv = 0, .req_det_count = 4, .minvol = ACCESSORY_OPENCABLE_DET_VOL_MIN, - .maxvol = ACCESSORY_OPENCABLE_DET_VOL_MAX + .maxvol = ACCESSORY_OPENCABLE_DET_VOL_MAX, + .alt_minvol = ACCESSORY_DET_VOL_DONTCARE, + .alt_maxvol = ACCESSORY_DET_VOL_DONTCARE }, { .type = JACK_TYPE_CARKIT, @@ -119,7 +141,9 @@ static struct accessory_detect_task detect_ops[] = { .meas_mv = 1, .req_det_count = 1, .minvol = ACCESSORY_CARKIT_DET_VOL_MIN, - .maxvol = ACCESSORY_CARKIT_DET_VOL_MAX + .maxvol = ACCESSORY_CARKIT_DET_VOL_MAX, + .alt_minvol = ACCESSORY_DET_VOL_DONTCARE, + .alt_maxvol = ACCESSORY_DET_VOL_DONTCARE }, { .type = JACK_TYPE_HEADSET, @@ -127,7 +151,9 @@ static struct accessory_detect_task detect_ops[] = { .meas_mv = 0, .req_det_count = 2, .minvol = ACCESSORY_HEADSET_DET_VOL_MIN, - .maxvol = ACCESSORY_HEADSET_DET_VOL_MAX + .maxvol = ACCESSORY_HEADSET_DET_VOL_MAX, + .alt_minvol = ACCESSORY_DET_VOL_DONTCARE, + .alt_maxvol = ACCESSORY_DET_VOL_DONTCARE }, { .type = JACK_TYPE_CONNECTED, @@ -135,7 +161,9 @@ static struct accessory_detect_task detect_ops[] = { .meas_mv = 0, .req_det_count = 4, .minvol = ACCESSORY_DET_VOL_DONTCARE, - .maxvol = ACCESSORY_DET_VOL_DONTCARE + .maxvol = ACCESSORY_DET_VOL_DONTCARE, + .alt_minvol = ACCESSORY_DET_VOL_DONTCARE, + .alt_maxvol = ACCESSORY_DET_VOL_DONTCARE } }; @@ -288,6 +316,10 @@ void report_jack_status(struct abx500_ad *dd) dev_info(&dd->pdev->dev, "Accessory: %s\n", accessory_str(dd->jack_type)); + /* Never report unsupported headset */ + if (dd->jack_type == JACK_TYPE_UNSUPPORTED_HEADSET) + goto out; + if (dd->jack_type != JACK_TYPE_DISCONNECTED && dd->jack_type != JACK_TYPE_UNSPECIFIED) value |= SND_JACK_MECHANICAL; @@ -488,14 +520,28 @@ irqreturn_t button_release_irq_handler(int irq, void *_userdata) * voltage might be re-measured or previously measured voltage is reused. */ static int mic_vol_in_range(struct abx500_ad *dd, - int lo, int hi, int force_read) + int lo, int hi, int alt_lo, int alt_hi, int force_read) { static int mv = MIN_MIC_POWER; + static int alt_mv = MIN_MIC_POWER; - if (mv == -100 || force_read) + if (mv == MIN_MIC_POWER || force_read) mv = dd->meas_voltage_stable(dd); - return (mv >= lo && mv <= hi) ? 1 : 0; + if (mv < lo || mv > hi) + return 0; + + if (ACCESSORY_DET_VOL_DONTCARE == alt_lo && + ACCESSORY_DET_VOL_DONTCARE == alt_hi) + return 1; + + if (alt_mv == MIN_MIC_POWER || force_read) + alt_mv = dd->meas_alt_voltage_stable(dd); + + if (alt_mv < alt_lo || alt_mv > alt_hi) + return 0; + + return 1; } /* @@ -519,10 +565,13 @@ static int detect_hw(struct abx500_ad *dd, case JACK_TYPE_HEADPHONE: case JACK_TYPE_CVIDEO: case JACK_TYPE_HEADSET: + case JACK_TYPE_UNSUPPORTED_HEADSET: case JACK_TYPE_OPENCABLE: status = mic_vol_in_range(dd, task->minvol, task->maxvol, + task->alt_minvol, + task->alt_maxvol, task->meas_mv); break; default: @@ -675,6 +724,18 @@ static void config_accdetect(struct abx500_ad *dd) release_irq(dd, BUTTON_RELEASE_IRQ); break; + case JACK_TYPE_UNSUPPORTED_HEADSET: + dd->config_accdetect1_hw(dd, 1); + dd->config_accdetect2_hw(dd, 1); + + release_irq(dd, PLUG_IRQ); + claim_irq(dd, UNPLUG_IRQ); + release_irq(dd, BUTTON_PRESS_IRQ); + release_irq(dd, BUTTON_RELEASE_IRQ); + if (dd->set_av_switch) + dd->set_av_switch(dd, NOT_SET); + break; + case JACK_TYPE_CONNECTED: case JACK_TYPE_HEADSET: case JACK_TYPE_CARKIT: -- cgit v1.2.3 From 329472f6ce21ac19abf341a9c82a65813e0effc7 Mon Sep 17 00:00:00 2001 From: Avinash A Date: Tue, 13 Dec 2011 11:37:52 +0530 Subject: input: touchscreen: cypress: Proper handling of controller reset This fixes the improper handling of the controller reset call, which caused the issue during quick continuous suspend/resume. This also handles the proper call of regulators. ST-Ericsson ID: 403803 ST-Ericsson Linux next: NA ST-Ericsson FOSS-OUT ID: Trivial Change-Id: Id181f8526d802918042ee3dca9995ba78a92a8b9 Signed-off-by: Avinash A Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/42274 Reviewed-by: Avinash A Tested-by: Avinash A Reviewed-by: Srinidhi KASAGAR --- drivers/input/touchscreen/cyttsp_core.c | 188 ++++++++++++++++++-------------- 1 file changed, 106 insertions(+), 82 deletions(-) diff --git a/drivers/input/touchscreen/cyttsp_core.c b/drivers/input/touchscreen/cyttsp_core.c index 57f23b3667b..a154056caa3 100755 --- a/drivers/input/touchscreen/cyttsp_core.c +++ b/drivers/input/touchscreen/cyttsp_core.c @@ -861,6 +861,37 @@ no_track_id: } } +static int cyttsp_reset_controller(struct cyttsp *ts) +{ + int ret; + + ret = gpio_request(ts->platform_data->rst_gpio, "reset_pin"); + if (ret) { + printk(KERN_ERR "cyttsp_reset_controller: touch gpio fail\n"); + return ret; + } + ret = gpio_direction_output(ts->platform_data->rst_gpio, 1); + if (ret < 0) { + printk(KERN_ERR "cyttsp_reset_controller: reset gpio direction fail\n"); + goto out; + } + /* + * The start up procedure + * Set the RESET pin to low + * Wait for a period of 1 milisecond + * Set the RESET pin to high + * Wait for a period of 5 milisecond + * Start the initial Sequence + */ + gpio_set_value(ts->platform_data->rst_gpio, 0); + usleep_range(10000, 20000); + gpio_set_value(ts->platform_data->rst_gpio, 1); + usleep_range(50000, 60000); +out: + gpio_free(ts->platform_data->rst_gpio); + return ret; +} + static void cyttsp_xy_worker(struct cyttsp *ts) { struct cyttsp_xydata xy_data; @@ -885,6 +916,12 @@ static void cyttsp_xy_worker(struct cyttsp *ts) printk(KERN_ERR "%s: Error, bad mode ps=%d hm=%02X tm=%02X\n", __func__, ts->platform_data->power_state, xy_data.hst_mode, xy_data.tt_mode); + retval = cyttsp_reset_controller(ts); + if (retval < 0) { + printk(KERN_ERR "%s: Error, conroller reset fail\n", + __func__); + goto exit_xy_worker; + } retval = cyttsp_power_on(ts); if (retval < 0) printk(KERN_ERR "%s: Error, power on fail\n", __func__); @@ -952,17 +989,9 @@ static void cyttsp_xy_worker(struct cyttsp *ts) * send no events if there were no * previous touches and no new touches */ - if ((trc.prv_tch == CY_NTCH) && ((trc.cur_tch == CY_NTCH) || - (trc.cur_tch > CY_MAX_TCH))) { + if ((trc.prv_tch == CY_NTCH) && ((trc.cur_tch == CY_NTCH))) { if (++ts->ntch_count > CY_MAX_NTCH) { - /* TTSP device has a stuck operational mode */ - printk(KERN_ERR "%s: Error, stuck no-touch ct=%d\n", - __func__, trc.cur_tch); - retval = cyttsp_power_on(ts); - if (retval < 0) - printk(KERN_ERR "%s: Error, power on fail\n", - __func__); - goto exit_xy_worker; + trc.cur_tch = CY_NTCH; } } else ts->ntch_count = 0; @@ -1992,35 +2021,74 @@ static ssize_t attr_fwloader_store(struct device *dev, return ret == size ? ret : -EINVAL; } -int cyttsp_reset_controller(struct cyttsp *ts) +static void cyttsp_close(struct input_dev *dev) { - int ret; + struct cyttsp *ts = dev_get_drvdata(&dev->dev); +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&ts->early_suspend); +#endif + del_timer_sync(&ts->timer); + cancel_work_sync(&ts->work); + free_irq(ts->irq, ts); + input_unregister_device(ts->input); + input_free_device(ts->input); + if (ts->platform_data->init) + ts->platform_data->init(0); + regulator_disable(ts->regulator); + kfree(ts); +} - ret = gpio_request(ts->platform_data->rst_gpio, "reset_pin"); - if (ret) { - printk(KERN_ERR "touch gpio failed\n"); - return ret; +static int cyttsp_open(struct input_dev *dev) +{ + struct cyttsp *ts = dev_get_drvdata(&dev->dev); + int ret = 0; + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + + ts->regulator = regulator_get(ts->pdev, "vcpin"); + if (IS_ERR(ts->regulator)) { + printk(KERN_ERR "%s: Error, regulator_get failed\n", __func__); + ts->regulator = NULL; + ret = PTR_ERR(ts->regulator); + goto error_regulator_get; } - ret = gpio_direction_output(ts->platform_data->rst_gpio, 1); + ret = regulator_enable(ts->regulator); if (ret < 0) { - printk(KERN_ERR "reset gpio direction failed\n"); - goto out; + printk(KERN_ERR "%s: regulator enable failed\n",__func__); + goto error_regulator; } - /* - * The start up procedure - * Set the RESET pin to low - * Wait for a period of 1 milisecond - * Set the RESET pin to high - * Wait for a period of 5 milisecond - * Start the initial Sequence - */ - gpio_set_value(ts->platform_data->rst_gpio, 0); - mdelay(1); - gpio_set_value(ts->platform_data->rst_gpio, 1); - mdelay(5); -out: - gpio_free(ts->platform_data->rst_gpio); - return 0; + /* enable interrupts */ + ts->irq = gpio_to_irq(ts->platform_data->irq_gpio); + ret = request_threaded_irq(ts->irq, NULL, cyttsp_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + ts->input->name, ts); + if (ret < 0) { + printk(KERN_ERR "%s: IRQ request failed r=%d\n", + __func__, ret); + ts->platform_data->power_state = CY_INVALID_STATE; + goto error_gpio_irq; + } + ret = cyttsp_reset_controller(ts); + if (ret < 0) { + printk(KERN_ERR "controller reset failed\n"); + goto error_reset; + } + DBG(printk(KERN_INFO "%s: call power_on\n", __func__);) + + ret = cyttsp_power_on(ts); + if (ret < 0) + printk(KERN_ERR "%s: Error, power on failed!\n", __func__); + + return ret; + +error_reset: + if (ts->irq >= 0) + free_irq(ts->irq, ts); +error_gpio_irq: + regulator_disable(ts->regulator); +error_regulator: + regulator_put(ts->regulator); +error_regulator_get: + return ret; } static struct device_attribute fwloader = @@ -2030,7 +2098,7 @@ void *cyttsp_core_init(struct cyttsp_bus_ops *bus_ops, struct device *pdev) { struct input_dev *input_device; struct cyttsp *ts; - int retval = 0, ret = 0; + int retval = 0; DBG(printk(KERN_INFO"%s: Enter\n", __func__);) @@ -2051,11 +2119,6 @@ void *cyttsp_core_init(struct cyttsp_bus_ops *bus_ops, struct device *pdev) printk(KERN_ERR "%s: platform init failed!\n", __func__); goto error_init; } - ret = cyttsp_reset_controller(ts); - if (ret < 0) { - printk(KERN_ERR "controller reset failed\n"); - goto error_reset; - } /* Create the input device and register it. */ input_device = input_allocate_device(); @@ -2065,34 +2128,18 @@ void *cyttsp_core_init(struct cyttsp_bus_ops *bus_ops, struct device *pdev) __func__); goto error_input_allocate_device; } - ts->input = input_device; input_device->name = ts->platform_data->name; input_device->phys = ts->phys; input_device->dev.parent = ts->pdev; + input_set_drvdata(ts->input, ts); + input_device->open = cyttsp_open; + input_device->close = cyttsp_close; - /* enable interrupts */ - ts->irq = gpio_to_irq(ts->platform_data->irq_gpio); - retval = request_threaded_irq(ts->irq, NULL, cyttsp_irq, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - ts->input->name, ts); - if (retval < 0) { - printk(KERN_ERR "%s: IRQ request failed r=%d\n", - __func__, retval); - ts->platform_data->power_state = CY_INVALID_STATE; - goto error_set_irq; - } /* setup watchdog */ INIT_WORK(&ts->work, cyttsp_check_bl); setup_timer(&ts->timer, cyttsp_timer, (unsigned long) ts); - DBG(printk(KERN_INFO "%s: call power_on\n", __func__);) - retval = cyttsp_power_on(ts); - if (retval < 0) { - printk(KERN_ERR "%s: Error, power on failed!\n", __func__); - goto error_power_on; - } - set_bit(EV_SYN, input_device->evbit); set_bit(EV_KEY, input_device->evbit); set_bit(EV_ABS, input_device->evbit); @@ -2143,19 +2190,6 @@ void *cyttsp_core_init(struct cyttsp_bus_ops *bus_ops, struct device *pdev) } DBG(printk(KERN_INFO "%s: Registered input device %s\n", __func__, input_device->name);) - ts->regulator = regulator_get(ts->pdev, "vcpin"); - if (IS_ERR(ts->regulator)) { - printk(KERN_ERR "%s: Error, regulator_get failed\n", __func__); - ts->regulator = NULL; - goto error_input_register_device; - } else { - ret = regulator_enable(ts->regulator); - if (ret < 0) { - printk(KERN_ERR "%s: regulator enable failed\n", - __func__); - goto out_regulator_error; - } - } #ifdef CONFIG_HAS_EARLYSUSPEND ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; @@ -2169,27 +2203,17 @@ void *cyttsp_core_init(struct cyttsp_bus_ops *bus_ops, struct device *pdev) __func__); goto device_create_error; } - dev_set_drvdata(pdev, ts); return ts; device_create_error: - regulator_disable(ts->regulator); #ifdef CONFIG_HAS_EARLYSUSPEND unregister_early_suspend(&ts->early_suspend); #endif -out_regulator_error: - regulator_put(ts->regulator); error_input_register_device: input_unregister_device(input_device); -error_power_on: cancel_work_sync(&ts->work); del_timer_sync(&ts->timer); - if (ts->irq >= 0) - free_irq(ts->irq, ts); -error_set_irq: - input_free_device(input_device); -error_reset: error_input_allocate_device: if (ts->platform_data->init) ts->platform_data->init(0); -- cgit v1.2.3 From ce7ece8c4b3ba50b80699659b376d844146156ce Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Mon, 30 Jan 2012 09:58:35 +0100 Subject: input: ab5500 accesory: Add include Signed-off-by: Philippe Langlais --- drivers/input/misc/ab5500-accdet.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/input/misc/ab5500-accdet.c b/drivers/input/misc/ab5500-accdet.c index 280af8ac8be..7622f3b45e9 100644 --- a/drivers/input/misc/ab5500-accdet.c +++ b/drivers/input/misc/ab5500-accdet.c @@ -11,6 +11,7 @@ * by the Free Software Foundation. */ +#include #include #include #include -- cgit v1.2.3 From d8253a3597a9d628ba9ab449005a351dfeb8fdd8 Mon Sep 17 00:00:00 2001 From: Naga Radhesh Date: Thu, 12 Jan 2012 19:20:47 +0530 Subject: skekeypad:Release all acquired resources During remove release the clock,irq's and gpio pins properly. ST-Ericsson ID: 410064 ST-Ericsson Linux next: NA ST-Ericsson FOSS-OUT ID: Trivial Change-Id:I68fdc2fd16ca6476166b8a89e01eed0f02679fd9 Signed-off-by: Naga Radhesh Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/45282 Reviewed-by: QATOOLS Reviewed-by: QABUILD Reviewed-by: Srinidhi KASAGAR --- drivers/input/keyboard/nomadik-ske-keypad.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c index f74775f6006..d6b60b8f2a0 100644 --- a/drivers/input/keyboard/nomadik-ske-keypad.c +++ b/drivers/input/keyboard/nomadik-ske-keypad.c @@ -683,12 +683,10 @@ static int __devinit ske_keypad_probe(struct platform_device *pdev) goto out_unregisterinput; } for (i = 0; i < SKE_KPD_MAX_ROWS; i++) { - keypad->ske_rows[i] = *plat->gpio_input_pins; - keypad->ske_cols[i] = *plat->gpio_output_pins; + keypad->ske_rows[i] = plat->gpio_input_pins[i]; + keypad->ske_cols[i] = plat->gpio_output_pins[i]; keypad->gpio_input_irq[i] = NOMADIK_GPIO_TO_IRQ(keypad->ske_rows[i]); - plat->gpio_input_pins++; - plat->gpio_output_pins++; } for (i = 0; i < keypad->board->krow; i++) { @@ -755,21 +753,33 @@ static int __devexit ske_keypad_remove(struct platform_device *pdev) { struct ske_keypad *keypad = platform_get_drvdata(pdev); struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + int i; cancel_delayed_work_sync(&keypad->gpio_work); cancel_delayed_work_sync(&keypad->work); cancel_delayed_work_sync(&keypad->scan_work); - free_irq(keypad->irq, keypad); input_unregister_device(keypad->input); - sysfs_remove_group(&pdev->dev.kobj, &ske_attr_group); - clk_disable(keypad->clk); + if (keypad->enable) + clk_disable(keypad->clk); clk_put(keypad->clk); - if (keypad->board->exit) + if (keypad->enable && keypad->board->exit) keypad->board->exit(); + else { + for (i = 0; i < keypad->board->krow; i++) { + disable_irq_nosync(keypad->gpio_input_irq[i]); + disable_irq_wake(keypad->gpio_input_irq[i]); + } + } + for (i = 0; i < keypad->board->krow; i++) + free_irq(keypad->gpio_input_irq[i], keypad); + + for (i = 0; i < SKE_KPD_MAX_ROWS; i++) + gpio_free(keypad->ske_rows[i]); + free_irq(keypad->irq, keypad); regulator_put(keypad->regulator); iounmap(keypad->reg_base); -- cgit v1.2.3 From 91c76febb86ee53690576e6c44c7a143633ad042 Mon Sep 17 00:00:00 2001 From: Ashok G Date: Wed, 18 Jan 2012 12:28:52 +0530 Subject: abx500-accdet: fix the kfree bug. Don't free the statically allocated data structure. ST-Ericsson Linux next: NA ST-Ericsson ID: 410063 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I2f58852de2dbdd401c72a873f9568aba83d01e49 Signed-off-by: Ashok G Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/45769 Reviewed-by: QATOOLS Reviewed-by: QABUILD Reviewed-by: Virupax SADASHIVPETIMATH Reviewed-by: Srinidhi KASAGAR --- drivers/input/misc/abx500-accdet.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/input/misc/abx500-accdet.c b/drivers/input/misc/abx500-accdet.c index d716a1d8870..1a743f6e6b9 100644 --- a/drivers/input/misc/abx500-accdet.c +++ b/drivers/input/misc/abx500-accdet.c @@ -871,7 +871,6 @@ static void abx500_accessory_cleanup(struct abx500_ad *dd) flush_workqueue(dd->irq_work_queue); destroy_workqueue(dd->irq_work_queue); - kfree(dd); } static int __devinit abx500_acc_detect_probe(struct platform_device *pdev) -- cgit v1.2.3 From 7d8cde367c2dc8dfa0d51a40bebfc966bed35b61 Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Tue, 28 Feb 2012 16:40:39 +0100 Subject: ux500: rmi4: Align touch with display When hiding display physical orientation in previous patch touch gets out of sync with display. This patch realign touch with display. Signed-off-by: Marcus Lorentzon --- drivers/staging/ste_rmi4/board-mop500-u8500uib-rmi4.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/staging/ste_rmi4/board-mop500-u8500uib-rmi4.c b/drivers/staging/ste_rmi4/board-mop500-u8500uib-rmi4.c index 6f9029d81ab..545e03d31fc 100644 --- a/drivers/staging/ste_rmi4/board-mop500-u8500uib-rmi4.c +++ b/drivers/staging/ste_rmi4/board-mop500-u8500uib-rmi4.c @@ -20,8 +20,8 @@ static struct synaptics_rmi4_platform_data rmi4_i2c_dev_platformdata = { .irq_number = NOMADIK_GPIO_TO_IRQ(84), .irq_type = (IRQF_TRIGGER_FALLING | IRQF_SHARED), - .x_flip = false, - .y_flip = true, + .x_flip = true, + .y_flip = false, .regulator_en = true, }; -- cgit v1.2.3 From 7bc2e7afdc3a618f92caf48561c0072b0eebe6d3 Mon Sep 17 00:00:00 2001 From: Naga Radhesh Date: Thu, 19 Jan 2012 11:58:06 +0530 Subject: nomadik-ske-keypad: dereference ptr before free Pointer dereferenced before it is freed, so free the clk properly and remove duplicate freeing of gpio as it is already freed during exit call. ST-Ericsson ID: 401383 ST-Ericsson Linux next: NA ST-Ericsson FOSS-OUT ID: Trivial Change-Id:I5b1b2c8e6161bad1b813da7cf666de2c59fbb7d9 Signed-off-by: Naga Radhesh Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/41568 Reviewed-by: Anil KUMAR (STE) Reviewed-by: QATOOLS Reviewed-by: QABUILD Reviewed-by: Naveen Kumar GADDIPATI Reviewed-by: Srinidhi KASAGAR --- drivers/input/keyboard/nomadik-ske-keypad.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c index d6b60b8f2a0..321863e77d2 100644 --- a/drivers/input/keyboard/nomadik-ske-keypad.c +++ b/drivers/input/keyboard/nomadik-ske-keypad.c @@ -741,7 +741,7 @@ out_regulator_get: out_freekeypad: kfree(keypad); out_freeclk: - clk_put(keypad->clk); + clk_put(clk); out_freeioremap: iounmap(reg_base); out_freerequest_memregions: @@ -776,9 +776,6 @@ static int __devexit ske_keypad_remove(struct platform_device *pdev) for (i = 0; i < keypad->board->krow; i++) free_irq(keypad->gpio_input_irq[i], keypad); - for (i = 0; i < SKE_KPD_MAX_ROWS; i++) - gpio_free(keypad->ske_rows[i]); - free_irq(keypad->irq, keypad); regulator_put(keypad->regulator); -- cgit v1.2.3 From 14452bae7b94afcef89f5a886d15bb3820643ee9 Mon Sep 17 00:00:00 2001 From: Chris Blair Date: Mon, 5 Dec 2011 10:01:21 +0100 Subject: Nomadik ske: Support keypad smaller than 8x8 Adds support to the driver for keypads with less than 8 rows and 8 columns. ST-Ericsson ID: 373774 ST-Ericsson FOSS-OUT ID: Trivial ST-Ericsson Linux next: NA Signed-off-by: Chris Blair Conflicts: drivers/input/keyboard/nomadik-ske-keypad.c Change-Id: Ib357ed6247c82dd326ce00fe826777fd459088b4 Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/46286 Reviewed-by: QATOOLS Reviewed-by: Michel JAOUEN Tested-by: Michel JAOUEN Reviewed-by: QATEST Reviewed-by: Christopher BLAIR Reviewed-by: Linus WALLEIJ --- arch/arm/plat-nomadik/include/plat/ske.h | 4 + drivers/input/keyboard/nomadik-ske-keypad.c | 143 +++++++++++++++++++++++----- 2 files changed, 122 insertions(+), 25 deletions(-) diff --git a/arch/arm/plat-nomadik/include/plat/ske.h b/arch/arm/plat-nomadik/include/plat/ske.h index 7a4fbdf3c13..834c015c935 100644 --- a/arch/arm/plat-nomadik/include/plat/ske.h +++ b/arch/arm/plat-nomadik/include/plat/ske.h @@ -38,6 +38,8 @@ * @keymap_data: matrix scan code table for keycodes * @krow: maximum number of rows * @kcol: maximum number of columns + * @kconnected_rows: number of rows actually connected + * @kconnected_cols: number of columns actually connected * @debounce_ms: platform specific debounce time * @no_autorepeat: flag for auto repetition * @wakeup_enable: allow waking up the system @@ -51,6 +53,8 @@ struct ske_keypad_platform_data { const struct matrix_keymap_data *keymap_data; u8 krow; u8 kcol; + u8 kconnected_rows; + u8 kconnected_cols; u8 debounce_ms; bool no_autorepeat; bool wakeup_enable; diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c index 321863e77d2..d3e2203d20f 100644 --- a/drivers/input/keyboard/nomadik-ske-keypad.c +++ b/drivers/input/keyboard/nomadik-ske-keypad.c @@ -93,15 +93,15 @@ struct ske_keypad { bool enable; bool enable_on_resume; struct regulator *regulator; - int gpio_input_irq[SKE_KPD_MAX_ROWS]; + int *gpio_input_irq; int key_pressed; struct delayed_work work; - int ske_rows[SKE_KPD_MAX_ROWS]; - int ske_cols[SKE_KPD_MAX_COLS]; + int *ske_rows; + int *ske_cols; int gpio_row; int gpio_col; struct delayed_work gpio_work; - u8 keys[SKE_KPD_MAX_ROWS][SKE_KPD_MAX_COLS]; + u8 **keys; struct delayed_work scan_work; }; @@ -180,7 +180,7 @@ static void ske_mode_enable(struct ske_keypad *keypad, bool enable) writel(0, keypad->reg_base + SKE_CR); if (keypad->board->exit) keypad->board->exit(); - for (i = 0; i < keypad->board->krow; i++) { + for (i = 0; i < keypad->board->kconnected_rows; i++) { enable_irq(keypad->gpio_input_irq[i]); enable_irq_wake(keypad->gpio_input_irq[i]); } @@ -190,7 +190,7 @@ static void ske_mode_enable(struct ske_keypad *keypad, bool enable) dev_dbg(keypad->dev, "%s enable keypad\n", __func__); regulator_enable(keypad->regulator); clk_enable(keypad->clk); - for (i = 0; i < keypad->board->krow; i++) { + for (i = 0; i < keypad->board->kconnected_rows; i++) { disable_irq_nosync(keypad->gpio_input_irq[i]); disable_irq_wake(keypad->gpio_input_irq[i]); } @@ -264,6 +264,10 @@ static void ske_keypad_report(struct ske_keypad *keypad, u8 status, int col) row = pos; status &= ~(1 << pos); + if (row >= keypad->board->krow) + /* no more rows supported by this keypad */ + break; + code = MATRIX_SCAN_CODE(row, col, SKE_KEYPAD_ROW_SHIFT); ske_ris = readl(keypad->reg_base + SKE_RIS); keypad->key_pressed = ske_ris & SKE_KPRISA; @@ -301,11 +305,17 @@ static void ske_keypad_read_data(struct ske_keypad *keypad) status = ske_asr & 0xff; if (status) { col = i * 2; + if (col >= keypad->board->kcol) + /* no more columns supported by this keypad */ + break; ske_keypad_report(keypad, status, col); } status = (ske_asr & 0xff00) >> 8; if (status) { col = (i * 2) + 1; + if (col >= keypad->board->kcol) + /* no more columns supported by this keypad */ + break; ske_keypad_report(keypad, status, col); } } @@ -327,8 +337,8 @@ static void ske_keypad_scan_work(struct work_struct *work) ske_keypad_read_data(keypad); /* Check for key actions */ - for (i = 0; i < SKE_KPD_MAX_ROWS; i++) { - for (j = 0; j < SKE_KPD_MAX_COLS; j++) { + for (i = 0; i < keypad->board->krow; i++) { + for (j = 0; j < keypad->board->kcol; j++) { switch (keypad->keys[i][j]) { case KEY_REPORTED: /** @@ -343,8 +353,9 @@ static void ske_keypad_scan_work(struct work_struct *work) input_sync(input); keypad->keys[i][j] = 0; dev_dbg(keypad->dev, - "%s Key release reported, code:%d\n", - __func__, code); + "%s Key release reported, code:%d " + "(key %d)\n", + __func__, code, keypad->keymap[code]); break; case KEY_PRESSED: /* Key pressed but not yet reported, report */ @@ -355,8 +366,9 @@ static void ske_keypad_scan_work(struct work_struct *work) 1); input_sync(input); dev_dbg(keypad->dev, - "%s Key press reported, code:%d\n", - __func__, code); + "%s Key press reported, code:%d " + "(key %d)\n", + __func__, code, keypad->keymap[code]); /* Intentional fall though */ case (KEY_REPORTED | KEY_PRESSED): /** @@ -424,8 +436,8 @@ static void ske_gpio_release_work(struct work_struct *work) code = MATRIX_SCAN_CODE(keypad->gpio_row, keypad->gpio_col, SKE_KEYPAD_ROW_SHIFT); - dev_dbg(keypad->dev, "%s Key press reported, code:%d\n", - __func__, code); + dev_dbg(keypad->dev, "%s Key press reported, code:%d (key %d)\n", + __func__, code, keypad->keymap[code]); input_event(input, EV_MSC, MSC_SCAN, code); input_report_key(input, keypad->keymap[code], 1); @@ -441,13 +453,13 @@ static int ske_read_get_gpio_row(struct ske_keypad *keypad) int ret; /* read all rows GPIO data register values */ - for (row = 0; row < SKE_KPD_MAX_ROWS ; row++) { + for (row = 0; row < keypad->board->kconnected_rows ; row++) { ret = gpio_get_value(keypad->ske_rows[row]); value += (1 << row) * ret; } /* get the exact row */ - for (row = 0; row < keypad->board->krow; row++) { + for (row = 0; row < keypad->board->kconnected_rows; row++) { if (((1 << row) & value) == 0) return row; } @@ -464,7 +476,7 @@ static void ske_set_cols(struct ske_keypad *keypad, int col) * Set all columns except the requested column * output pin as high */ - for (i = 0; i < SKE_KPD_MAX_COLS; i++) { + for (i = 0; i < keypad->board->kconnected_cols; i++) { if (i == col) value = 0; else @@ -479,7 +491,7 @@ static void ske_free_cols(struct ske_keypad *keypad) { int i ; - for (i = 0; i < SKE_KPD_MAX_COLS; i++) { + for (i = 0; i < keypad->board->kconnected_cols; i++) { gpio_request(keypad->ske_cols[i], "ske-kp"); gpio_direction_output(keypad->ske_cols[i], 0); gpio_free(keypad->ske_cols[i]); @@ -491,7 +503,7 @@ static void ske_manual_scan(struct ske_keypad *keypad) int row; int col; - for (col = 0; col < keypad->board->kcol; col++) { + for (col = 0; col < keypad->board->kconnected_cols; col++) { ske_set_cols(keypad, col); row = ske_read_get_gpio_row(keypad); if (row >= 0) { @@ -682,14 +694,78 @@ static int __devinit ske_keypad_probe(struct platform_device *pdev) ret = -EINVAL; goto out_unregisterinput; } - for (i = 0; i < SKE_KPD_MAX_ROWS; i++) { - keypad->ske_rows[i] = plat->gpio_input_pins[i]; - keypad->ske_cols[i] = plat->gpio_output_pins[i]; + + if (plat->kconnected_rows == 0) { + /* + * Board config data does not specify the number of connected + * rows and columns; assume that it matches the specified max + * values. + */ + plat->kconnected_rows = plat->krow; + plat->kconnected_cols = plat->kcol; + } + + /* this code doesn't currently support non-square keypad */ + if (plat->kconnected_rows != plat->kconnected_cols) { + dev_err(&pdev->dev, + "invalid keypad configuration (not square)\n"), + ret = -EINVAL; + goto out_unregisterinput; + } + + if (plat->kconnected_rows > SKE_KPD_MAX_ROWS || + plat->kconnected_cols > SKE_KPD_MAX_COLS) { + dev_err(&pdev->dev, + "invalid keypad configuration (too many rows/cols)\n"), + ret = -EINVAL; + goto out_unregisterinput; + } + + keypad->gpio_input_irq = kmalloc(sizeof(*keypad->gpio_input_irq) * + plat->kconnected_rows, + GFP_KERNEL); + if (!keypad->gpio_input_irq) { + dev_err(&pdev->dev, "failed to allocate input_irq memory\n"); + goto out_unregisterinput; + } + + keypad->ske_rows = kmalloc(sizeof(*keypad->ske_rows) * + plat->kconnected_rows, GFP_KERNEL); + if (!keypad->ske_rows) { + dev_err(&pdev->dev, "failed to allocate ske_rows memory\n"); + goto out_freemem_input_irq; + } + + keypad->ske_cols = kmalloc(sizeof(*keypad->ske_cols) * + plat->kconnected_cols, GFP_KERNEL); + if (!keypad->ske_cols) { + dev_err(&pdev->dev, "failed to allocate ske_cols memory\n"); + goto out_freemem_rows; + } + + keypad->keys = kzalloc(sizeof(*keypad->keys) * plat->krow, GFP_KERNEL); + if (!keypad->keys) { + dev_err(&pdev->dev, "failed to allocate keys:rows memory\n"); + goto out_freemem_cols; + } + for (i = 0; i < plat->krow; i++) { + keypad->keys[i] = kzalloc(sizeof(*keypad->keys[i]) * + plat->kcol, GFP_KERNEL); + if (!keypad->keys[i]) { + dev_err(&pdev->dev, + "failed to allocate keys:cols memory\n"); + goto out_freemem_keys; + } + } + + for (i = 0; i < plat->kconnected_rows; i++) { + keypad->ske_rows[i] = *plat->gpio_input_pins; + keypad->ske_cols[i] = *plat->gpio_output_pins; keypad->gpio_input_irq[i] = NOMADIK_GPIO_TO_IRQ(keypad->ske_rows[i]); } - for (i = 0; i < keypad->board->krow; i++) { + for (i = 0; i < keypad->board->kconnected_rows; i++) { ret = request_threaded_irq(keypad->gpio_input_irq[i], NULL, ske_keypad_gpio_irq, IRQF_TRIGGER_FALLING | IRQF_NO_SUSPEND, @@ -697,7 +773,7 @@ static int __devinit ske_keypad_probe(struct platform_device *pdev) if (ret) { dev_err(&pdev->dev, "allocate gpio irq %d failed\n", keypad->gpio_input_irq[i]); - goto out_unregisterinput; + goto out_freemem_keys; } enable_irq_wake(keypad->gpio_input_irq[i]); } @@ -706,7 +782,7 @@ static int __devinit ske_keypad_probe(struct platform_device *pdev) IRQF_ONESHOT, "ske-keypad", keypad); if (ret) { dev_err(&pdev->dev, "allocate irq %d failed\n", keypad->irq); - goto out_unregisterinput; + goto out_freemem_keys; } /* sysfs implementation for dynamic enable/disable the input event */ @@ -728,6 +804,16 @@ static int __devinit ske_keypad_probe(struct platform_device *pdev) out_free_irq: free_irq(keypad->irq, keypad); +out_freemem_keys: + for (i = 0; i < plat->krow; i++) + kfree(keypad->keys[i]); + kfree(keypad->keys); +out_freemem_cols: + kfree(keypad->ske_cols); +out_freemem_rows: + kfree(keypad->ske_rows); +out_freemem_input_irq: + kfree(keypad->gpio_input_irq); out_unregisterinput: input_unregister_device(input); input = NULL; @@ -759,6 +845,13 @@ static int __devexit ske_keypad_remove(struct platform_device *pdev) cancel_delayed_work_sync(&keypad->work); cancel_delayed_work_sync(&keypad->scan_work); + for (i = 0; i < keypad->board->krow; i++) + kfree(keypad->keys[i]); + kfree(keypad->keys); + kfree(keypad->ske_cols); + kfree(keypad->ske_rows); + kfree(keypad->gpio_input_irq); + input_unregister_device(keypad->input); sysfs_remove_group(&pdev->dev.kobj, &ske_attr_group); if (keypad->enable) -- cgit v1.2.3 From 676a45212b0cc337bba9d4387a852b7f5588e719 Mon Sep 17 00:00:00 2001 From: Avinash A Date: Fri, 13 Jan 2012 14:49:56 +0530 Subject: input: touchscreen: cypress: fix the kernel crash on lbp In lbp, the open function is not called and hence during the supend/resume in lbp there is a kernel crah.This fixes the issue by handling the suspend/resume only when the device is used. ST-Ericsson ID: - ST-Ericsson Linux next:NA ST-Ericsson FOSS-OUT ID:Trivial Change-Id: Ia496ef85ed55e56e42df7284edc16b6e6eab8cb7 Signed-off-by: Avinash A Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/45556 Reviewed-by: Avinash A Tested-by: Avinash A Reviewed-by: QABUILD Tested-by: Om Prakash PAL Reviewed-by: Rabin VINCENT --- drivers/input/touchscreen/cyttsp_core.c | 50 ++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/drivers/input/touchscreen/cyttsp_core.c b/drivers/input/touchscreen/cyttsp_core.c index a154056caa3..6fd5a409ed9 100755 --- a/drivers/input/touchscreen/cyttsp_core.c +++ b/drivers/input/touchscreen/cyttsp_core.c @@ -291,6 +291,7 @@ struct cyttsp { bool to_timeout; struct completion int_running; bool bl_ready; + bool device_in_use; u8 reg_id; u8 ntch_count; }; @@ -1695,6 +1696,9 @@ static int cyttsp_resume(struct cyttsp *ts) int retval = 0; struct cyttsp_xydata xydata; + if (!ts->device_in_use) + return 0; + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) if (ts->platform_data->use_sleep && (ts->platform_data->power_state != CY_ACTIVE_STATE)) { @@ -1726,6 +1730,9 @@ static int cyttsp_suspend(struct cyttsp *ts) u8 sleep_mode = 0; int retval = 0; + if (!ts->device_in_use) + return 0; + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) if (ts->platform_data->use_sleep && (ts->platform_data->power_state == CY_ACTIVE_STATE)) { @@ -1749,6 +1756,9 @@ static void cyttsp_ts_early_suspend(struct early_suspend *h) { struct cyttsp *ts = container_of(h, struct cyttsp, early_suspend); + if (!ts->device_in_use) + return; + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) LOCK(ts->mutex); if (!ts->fw_loader_mode) { @@ -1767,6 +1777,9 @@ static void cyttsp_ts_late_resume(struct early_suspend *h) { struct cyttsp *ts = container_of(h, struct cyttsp, early_suspend); + if (!ts->device_in_use) + return; + regulator_enable(ts->regulator); DBG(printk(KERN_INFO"%s: Enter\n", __func__);) @@ -2023,19 +2036,13 @@ static ssize_t attr_fwloader_store(struct device *dev, static void cyttsp_close(struct input_dev *dev) { + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + struct cyttsp *ts = dev_get_drvdata(&dev->dev); -#ifdef CONFIG_HAS_EARLYSUSPEND - unregister_early_suspend(&ts->early_suspend); -#endif - del_timer_sync(&ts->timer); cancel_work_sync(&ts->work); free_irq(ts->irq, ts); - input_unregister_device(ts->input); - input_free_device(ts->input); - if (ts->platform_data->init) - ts->platform_data->init(0); regulator_disable(ts->regulator); - kfree(ts); + ts->device_in_use = false; } static int cyttsp_open(struct input_dev *dev) @@ -2044,16 +2051,9 @@ static int cyttsp_open(struct input_dev *dev) int ret = 0; DBG(printk(KERN_INFO"%s: Enter\n", __func__);) - ts->regulator = regulator_get(ts->pdev, "vcpin"); - if (IS_ERR(ts->regulator)) { - printk(KERN_ERR "%s: Error, regulator_get failed\n", __func__); - ts->regulator = NULL; - ret = PTR_ERR(ts->regulator); - goto error_regulator_get; - } ret = regulator_enable(ts->regulator); if (ret < 0) { - printk(KERN_ERR "%s: regulator enable failed\n",__func__); + printk(KERN_ERR "%s: regulator enable failed\n", __func__); goto error_regulator; } /* enable interrupts */ @@ -2075,19 +2075,21 @@ static int cyttsp_open(struct input_dev *dev) DBG(printk(KERN_INFO "%s: call power_on\n", __func__);) ret = cyttsp_power_on(ts); - if (ret < 0) + if (ret < 0) { printk(KERN_ERR "%s: Error, power on failed!\n", __func__); + goto error_power_on; + } + ts->device_in_use = true; return ret; +error_power_on: error_reset: if (ts->irq >= 0) free_irq(ts->irq, ts); error_gpio_irq: regulator_disable(ts->regulator); error_regulator: - regulator_put(ts->regulator); -error_regulator_get: return ret; } @@ -2113,6 +2115,12 @@ void *cyttsp_core_init(struct cyttsp_bus_ops *bus_ops, struct device *pdev) ts->bus_ops = bus_ops; init_completion(&ts->int_running); + ts->regulator = regulator_get(ts->pdev, "vcpin"); + if (IS_ERR(ts->regulator)) { + printk(KERN_ERR "%s: Error, regulator_get failed\n", __func__); + ts->regulator = NULL; + goto error_regulator_get; + } if (ts->platform_data->init) retval = ts->platform_data->init(1); if (retval) { @@ -2218,6 +2226,8 @@ error_input_allocate_device: if (ts->platform_data->init) ts->platform_data->init(0); error_init: + regulator_put(ts->regulator); +error_regulator_get: kfree(ts); error_alloc_data_failed: return NULL; -- cgit v1.2.3 From 818cdcfc52daf6b6d878371c7a0c9f9691507af0 Mon Sep 17 00:00:00 2001 From: Jonas Aaberg Date: Mon, 6 Feb 2012 12:03:48 +0100 Subject: input: misc: abx500-accdet: Change info print to dbg Change dev_info print to dev_dbg ST-Ericsson Linux next: - ST-Ericsson ID: 370799 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: Ifc89b414eec3eee6c2d8a6625dfa8ae512025184 Signed-off-by: Jonas Aaberg Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/47965 Reviewed-by: QABUILD Reviewed-by: Kristoffer KARLSSON --- drivers/input/misc/abx500-accdet.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/misc/abx500-accdet.c b/drivers/input/misc/abx500-accdet.c index 1a743f6e6b9..f9df95cb2a5 100644 --- a/drivers/input/misc/abx500-accdet.c +++ b/drivers/input/misc/abx500-accdet.c @@ -313,7 +313,7 @@ void report_jack_status(struct abx500_ad *dd) goto out; dd->reported_jack_type = dd->jack_type; - dev_info(&dd->pdev->dev, "Accessory: %s\n", + dev_dbg(&dd->pdev->dev, "Accessory: %s\n", accessory_str(dd->jack_type)); /* Never report unsupported headset */ -- cgit v1.2.3 From f7672f6d83dd6f2caf2d8697ee54f73ea35fc06b Mon Sep 17 00:00:00 2001 From: Jonas Aaberg Date: Mon, 6 Feb 2012 12:11:05 +0100 Subject: [ANDROID]: input: touchscreen: cyttsp: Fix build warning ST-Ericsson Linux next: - ST-Ericsson ID: 370799 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I965417cf6bd15de1a4ee8d3d0b20dc44dfc5ba2c Signed-off-by: Jonas Aaberg Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/47968 Reviewed-by: QABUILD --- drivers/input/touchscreen/cyttsp_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/cyttsp_core.c b/drivers/input/touchscreen/cyttsp_core.c index 6fd5a409ed9..59d6c091783 100755 --- a/drivers/input/touchscreen/cyttsp_core.c +++ b/drivers/input/touchscreen/cyttsp_core.c @@ -1725,6 +1725,7 @@ static int cyttsp_resume(struct cyttsp *ts) return retval; } +#ifdef CONFIG_HAS_EARLYSUSPEND static int cyttsp_suspend(struct cyttsp *ts) { u8 sleep_mode = 0; @@ -1751,7 +1752,6 @@ static int cyttsp_suspend(struct cyttsp *ts) return retval; } -#ifdef CONFIG_HAS_EARLYSUSPEND static void cyttsp_ts_early_suspend(struct early_suspend *h) { struct cyttsp *ts = container_of(h, struct cyttsp, early_suspend); -- cgit v1.2.3 From b93f1acca131d84f3b5380c0d7ee30a1b25d62eb Mon Sep 17 00:00:00 2001 From: Magnus Smith Date: Thu, 22 Dec 2011 15:38:11 +0100 Subject: input: Removed CVIDEO from accesory detection. Removed support for CVIDEO (TV-out) from accesory detection. ST-Ericsson ID:404443 ST-Ericsson Linux next:NA ST-Ericsson FOSS-OUT ID:Trivial Change-Id: I7770843c0ab33fb5e3fc8bfcfe968834b0b626da Signed-off-by: Magnus Smith Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/43394 Reviewed-by: QATOOLS Reviewed-by: QABUILD Reviewed-by: Kristoffer KARLSSON Reviewed-by: Jonas ABERG --- drivers/input/misc/ab8500-accdet.c | 2 -- drivers/input/misc/abx500-accdet.c | 19 ------------------- 2 files changed, 21 deletions(-) diff --git a/drivers/input/misc/ab8500-accdet.c b/drivers/input/misc/ab8500-accdet.c index 0fe60364d54..8faa7c425a5 100644 --- a/drivers/input/misc/ab8500-accdet.c +++ b/drivers/input/misc/ab8500-accdet.c @@ -63,8 +63,6 @@ #define ACCESSORY_DET_VOL_DONTCARE -1 #define ACCESSORY_HEADPHONE_DET_VOL_MIN 0 #define ACCESSORY_HEADPHONE_DET_VOL_MAX 40 -#define ACCESSORY_CVIDEO_DET_VOL_MIN 41 -#define ACCESSORY_CVIDEO_DET_VOL_MAX 105 #define ACCESSORY_CARKIT_DET_VOL_MIN 1100 #define ACCESSORY_CARKIT_DET_VOL_MAX 1300 #define ACCESSORY_HEADSET_DET_VOL_MIN 0 diff --git a/drivers/input/misc/abx500-accdet.c b/drivers/input/misc/abx500-accdet.c index f9df95cb2a5..c0d1cb04a0e 100644 --- a/drivers/input/misc/abx500-accdet.c +++ b/drivers/input/misc/abx500-accdet.c @@ -53,8 +53,6 @@ #define ACCESSORY_U_HEADSET_DET_VOL_MAX 732 #define ACCESSORY_U_HEADSET_ALT_DET_VOL_MIN 25 #define ACCESSORY_U_HEADSET_ALT_DET_VOL_MAX 50 -#define ACCESSORY_CVIDEO_DET_VOL_MIN 41 -#define ACCESSORY_CVIDEO_DET_VOL_MAX 105 #define ACCESSORY_CARKIT_DET_VOL_MIN 1100 #define ACCESSORY_CARKIT_DET_VOL_MAX 1300 #define ACCESSORY_HEADSET_DET_VOL_MIN 1301 @@ -115,16 +113,6 @@ static struct accessory_detect_task detect_ops[] = { .alt_minvol = ACCESSORY_U_HEADSET_ALT_DET_VOL_MIN, .alt_maxvol = ACCESSORY_U_HEADSET_ALT_DET_VOL_MAX }, - { - .type = JACK_TYPE_CVIDEO, - .typename = "CVIDEO", - .meas_mv = 0, - .req_det_count = 4, - .minvol = ACCESSORY_CVIDEO_DET_VOL_MIN, - .maxvol = ACCESSORY_CVIDEO_DET_VOL_MAX, - .alt_minvol = ACCESSORY_DET_VOL_DONTCARE, - .alt_maxvol = ACCESSORY_DET_VOL_DONTCARE - }, { .type = JACK_TYPE_OPENCABLE, .typename = "OPENCABLE", @@ -327,11 +315,6 @@ void report_jack_status(struct abx500_ad *dd) value |= SND_JACK_MICROPHONE; if (jack_supports_spkr(dd->jack_type)) value |= (SND_JACK_HEADPHONE | SND_JACK_LINEOUT); - if (dd->jack_type == JACK_TYPE_CVIDEO) { - value |= SND_JACK_VIDEOOUT; - if (dd->set_av_switch) - dd->set_av_switch(dd, VIDEO_OUT); - } ux500_ab8500_jack_report(value); out: return; @@ -563,7 +546,6 @@ static int detect_hw(struct abx500_ad *dd, break; case JACK_TYPE_CARKIT: case JACK_TYPE_HEADPHONE: - case JACK_TYPE_CVIDEO: case JACK_TYPE_HEADSET: case JACK_TYPE_UNSUPPORTED_HEADSET: case JACK_TYPE_OPENCABLE: @@ -714,7 +696,6 @@ static void config_accdetect(struct abx500_ad *dd) if (dd->set_av_switch) dd->set_av_switch(dd, NOT_SET); case JACK_TYPE_HEADPHONE: - case JACK_TYPE_CVIDEO: dd->config_accdetect1_hw(dd, 1); dd->config_accdetect2_hw(dd, 0); -- cgit v1.2.3 From a806017de55f26af5cd32cc5bef008f79080d80d Mon Sep 17 00:00:00 2001 From: Ashok G Date: Mon, 27 Feb 2012 12:36:12 +0530 Subject: nomadik-ske-keypad: Stop using already free'd pointer St-Ericsson Linux next: NA St-Ericsson Id: 419847 St-Ericsson FOSS-OUT ID: Trivial Change-Id: I18ee7489264da7a84081486f95e07a5be29a987a Signed-off-by: Ashok G Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/50529 Reviewed-by: Naga RADHESH Y Reviewed-by: Jonas ABERG --- drivers/input/keyboard/nomadik-ske-keypad.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c index d3e2203d20f..037358e28f6 100644 --- a/drivers/input/keyboard/nomadik-ske-keypad.c +++ b/drivers/input/keyboard/nomadik-ske-keypad.c @@ -850,7 +850,6 @@ static int __devexit ske_keypad_remove(struct platform_device *pdev) kfree(keypad->keys); kfree(keypad->ske_cols); kfree(keypad->ske_rows); - kfree(keypad->gpio_input_irq); input_unregister_device(keypad->input); sysfs_remove_group(&pdev->dev.kobj, &ske_attr_group); @@ -869,6 +868,7 @@ static int __devexit ske_keypad_remove(struct platform_device *pdev) for (i = 0; i < keypad->board->krow; i++) free_irq(keypad->gpio_input_irq[i], keypad); + kfree(keypad->gpio_input_irq); free_irq(keypad->irq, keypad); regulator_put(keypad->regulator); -- cgit v1.2.3 From 252cbfa827d9bd859be12fc6acfdf6ed5903cb58 Mon Sep 17 00:00:00 2001 From: Virupax Sadashivpetimath Date: Wed, 29 Feb 2012 18:00:21 +0530 Subject: input:misc:accdet: Add ab8505 accessory detect support Add the new mic control gpio found in the 8520 board. Gpio is used to configure the mux, so that the 3.5mm jack gets connected to either the audio or video part of the board. ST-Ericsson ID: 366316 Signed-off-by: Virupax Sadashivpetimath --- arch/arm/mach-ux500/include/mach/abx500-accdet.h | 2 ++ drivers/input/misc/ab8500-accdet.c | 25 ++++++++++++++++---- drivers/input/misc/abx500-accdet.c | 30 ++++++++++++++++++++++-- 3 files changed, 50 insertions(+), 7 deletions(-) diff --git a/arch/arm/mach-ux500/include/mach/abx500-accdet.h b/arch/arm/mach-ux500/include/mach/abx500-accdet.h index cdd78cd7d0c..9871ae11530 100644 --- a/arch/arm/mach-ux500/include/mach/abx500-accdet.h +++ b/arch/arm/mach-ux500/include/mach/abx500-accdet.h @@ -101,6 +101,7 @@ * @accdet2122_th Voltage thresholds for accdet21 and accdet22 inputs. * @is_detection_inverted Whether the accessory insert/removal, button * press/release irq's are inverted. + * @mic_ctrl Gpio to select between CVBS and MIC. */ struct abx500_accdet_platform_data { int btn_keycode; @@ -108,6 +109,7 @@ struct abx500_accdet_platform_data { u8 accdet2122_th; unsigned int video_ctrl_gpio; bool is_detection_inverted; + unsigned int mic_ctrl; }; /* Enumerations */ diff --git a/drivers/input/misc/ab8500-accdet.c b/drivers/input/misc/ab8500-accdet.c index 8faa7c425a5..59ad959e909 100644 --- a/drivers/input/misc/ab8500-accdet.c +++ b/drivers/input/misc/ab8500-accdet.c @@ -351,18 +351,33 @@ static void ab8500_set_av_switch(struct abx500_ad *dd, ret = gpio_direction_input(dd->pdata->video_ctrl_gpio); dd->gpio35_dir_set = 0; ret = gpio_direction_output(dd->pdata->video_ctrl_gpio, 0); + if (dd->pdata->mic_ctrl) + gpio_direction_output(dd->pdata->mic_ctrl, 0); } else if (!dd->gpio35_dir_set) { ret = gpio_direction_output(dd->pdata->video_ctrl_gpio, dir == AUDIO_IN ? 1 : 0); if (ret < 0) { dev_err(&dd->pdev->dev, - "%s: Output video ctrl signal failed (%d).\n", + "%s: video_ctrl pin output config failed (%d).\n", __func__, ret); - } else { - dd->gpio35_dir_set = 1; - dev_dbg(&dd->pdev->dev, "AV-SWITCH: %s\n", - dir == AUDIO_IN ? "AUDIO_IN" : "VIDEO_OUT"); + return; + } + + if (dd->pdata->mic_ctrl) { + ret = gpio_direction_output(dd->pdata->mic_ctrl, + dir == AUDIO_IN ? 1 : 0); + if (ret < 0) { + dev_err(&dd->pdev->dev, + "%s: mic_ctrl pin output" + "config failed (%d).\n", + __func__, ret); + return; + } } + + dd->gpio35_dir_set = 1; + dev_dbg(&dd->pdev->dev, "AV-SWITCH: %s\n", + dir == AUDIO_IN ? "AUDIO_IN" : "VIDEO_OUT"); } else { gpio_set_value(dd->pdata->video_ctrl_gpio, dir == AUDIO_IN ? 1 : 0); diff --git a/drivers/input/misc/abx500-accdet.c b/drivers/input/misc/abx500-accdet.c index c0d1cb04a0e..5c4e96c73d6 100644 --- a/drivers/input/misc/abx500-accdet.c +++ b/drivers/input/misc/abx500-accdet.c @@ -786,7 +786,25 @@ static int abx500_accessory_init(struct platform_device *pdev) } } - (ret = create_btn_input_dev(dd)); + if (dd->pdata->mic_ctrl) { + ret = gpio_is_valid(dd->pdata->mic_ctrl); + if (!ret) { + dev_err(&pdev->dev, + "%s: Mic ctrl GPIO invalid (%d).\n", __func__, + dd->pdata->mic_ctrl); + + goto mic_ctrl_fail; + } + ret = gpio_request(dd->pdata->mic_ctrl, + "Mic Control"); + if (ret) { + dev_err(&pdev->dev, "%s: Get mic ctrl GPIO" + "failed.\n", __func__); + goto mic_ctrl_fail; + } + } + + ret = create_btn_input_dev(dd); if (ret < 0) { dev_err(&pdev->dev, "%s: create_button_input_dev failed.\n", __func__); @@ -827,6 +845,9 @@ fail_no_mem_for_wq: fail_no_regulators: input_unregister_device(dd->btn_input_dev); fail_no_btn_input_dev: + if (dd->pdata->mic_ctrl) + gpio_free(dd->pdata->mic_ctrl); +mic_ctrl_fail: if (dd->pdata->video_ctrl_gpio) gpio_free(dd->pdata->video_ctrl_gpio); return ret; @@ -842,7 +863,12 @@ static void abx500_accessory_cleanup(struct abx500_ad *dd) dd->jack_type = JACK_TYPE_UNSPECIFIED; config_accdetect(dd); - gpio_free(dd->pdata->video_ctrl_gpio); + if (dd->pdata->mic_ctrl) + gpio_free(dd->pdata->mic_ctrl); + + if (dd->pdata->video_ctrl_gpio) + gpio_free(dd->pdata->video_ctrl_gpio); + input_unregister_device(dd->btn_input_dev); free_regulators(dd); -- cgit v1.2.3 From 248e54469c0f3513d0184c95c46cd6ce24582d64 Mon Sep 17 00:00:00 2001 From: Jonas Aaberg Date: Wed, 29 Feb 2012 14:28:42 +0100 Subject: ARM: ux500: input: abx500-accdet: Move header file Move header file from include/mach to include/linux/input since it belongs to a driver not to a certain mach. ST-Ericsson ID: 370799 Signed-off-by: Jonas Aaberg --- arch/arm/mach-ux500/include/mach/abx500-accdet.h | 362 ----------------------- drivers/input/misc/ab5500-accdet.c | 2 +- drivers/input/misc/ab8500-accdet.c | 2 +- drivers/input/misc/abx500-accdet.c | 10 +- include/linux/input/abx500-accdet.h | 362 +++++++++++++++++++++++ 5 files changed, 370 insertions(+), 368 deletions(-) delete mode 100644 arch/arm/mach-ux500/include/mach/abx500-accdet.h create mode 100644 include/linux/input/abx500-accdet.h diff --git a/arch/arm/mach-ux500/include/mach/abx500-accdet.h b/arch/arm/mach-ux500/include/mach/abx500-accdet.h deleted file mode 100644 index 9871ae11530..00000000000 --- a/arch/arm/mach-ux500/include/mach/abx500-accdet.h +++ /dev/null @@ -1,362 +0,0 @@ -/* - * Copyright ST-Ericsson 2011. - * - * Author: Jarmo K. Kuronen for ST Ericsson. - * Licensed under GPLv2. - */ - -#ifndef _ABx500_ACCDET_H -#define _ABx500_ACCDET_H - -/* -* Debounce times for AccDet1 input -* @0x880 [2:0] -*/ -#define ACCDET1_DB_0ms 0x00 -#define ACCDET1_DB_10ms 0x01 -#define ACCDET1_DB_20ms 0x02 -#define ACCDET1_DB_30ms 0x03 -#define ACCDET1_DB_40ms 0x04 -#define ACCDET1_DB_50ms 0x05 -#define ACCDET1_DB_60ms 0x06 -#define ACCDET1_DB_70ms 0x07 - -/* -* Voltage threshold for AccDet1 input -* @0x880 [6:3] -*/ -#define ACCDET1_TH_1100mV 0x40 -#define ACCDET1_TH_1200mV 0x48 -#define ACCDET1_TH_1300mV 0x50 -#define ACCDET1_TH_1400mV 0x58 -#define ACCDET1_TH_1500mV 0x60 -#define ACCDET1_TH_1600mV 0x68 -#define ACCDET1_TH_1700mV 0x70 -#define ACCDET1_TH_1800mV 0x78 - -/* -* Voltage threshold for AccDet21 input -* @0x881 [3:0] -*/ -#define ACCDET21_TH_300mV 0x00 -#define ACCDET21_TH_400mV 0x01 -#define ACCDET21_TH_500mV 0x02 -#define ACCDET21_TH_600mV 0x03 -#define ACCDET21_TH_700mV 0x04 -#define ACCDET21_TH_800mV 0x05 -#define ACCDET21_TH_900mV 0x06 -#define ACCDET21_TH_1000mV 0x07 -#define ACCDET21_TH_1100mV 0x08 -#define ACCDET21_TH_1200mV 0x09 -#define ACCDET21_TH_1300mV 0x0a -#define ACCDET21_TH_1400mV 0x0b -#define ACCDET21_TH_1500mV 0x0c -#define ACCDET21_TH_1600mV 0x0d -#define ACCDET21_TH_1700mV 0x0e -#define ACCDET21_TH_1800mV 0x0f - -/* -* Voltage threshold for AccDet22 input -* @0x881 [7:4] -*/ -#define ACCDET22_TH_300mV 0x00 -#define ACCDET22_TH_400mV 0x10 -#define ACCDET22_TH_500mV 0x20 -#define ACCDET22_TH_600mV 0x30 -#define ACCDET22_TH_700mV 0x40 -#define ACCDET22_TH_800mV 0x50 -#define ACCDET22_TH_900mV 0x60 -#define ACCDET22_TH_1000mV 0x70 -#define ACCDET22_TH_1100mV 0x80 -#define ACCDET22_TH_1200mV 0x90 -#define ACCDET22_TH_1300mV 0xa0 -#define ACCDET22_TH_1400mV 0xb0 -#define ACCDET22_TH_1500mV 0xc0 -#define ACCDET22_TH_1600mV 0xd0 -#define ACCDET22_TH_1700mV 0xe0 -#define ACCDET22_TH_1800mV 0xf0 - -/* -* Voltage threshold for AccDet1 input -* @0x880 [6:3] -*/ -#define ACCDET1_TH_300mV 0x00 -#define ACCDET1_TH_400mV 0x01 -#define ACCDET1_TH_500mV 0x02 -#define ACCDET1_TH_600mV 0x03 -#define ACCDET1_TH_700mV 0x04 -#define ACCDET1_TH_800mV 0x05 -#define ACCDET1_TH_900mV 0x06 -#define ACCDET1_TH_1000mV 0x07 - -#define MAX_DET_COUNT 10 -#define MAX_VOLT_DIFF 30 -#define MIN_MIC_POWER -100 - -/** - * struct abx500_accdet_platform_data - AV Accessory detection specific - * platform data - * @btn_keycode Keycode to be sent when accessory button is pressed. - * @accdet1_dbth Debounce time + voltage threshold for accdet 1 input. - * @accdet2122_th Voltage thresholds for accdet21 and accdet22 inputs. - * @is_detection_inverted Whether the accessory insert/removal, button - * press/release irq's are inverted. - * @mic_ctrl Gpio to select between CVBS and MIC. - */ -struct abx500_accdet_platform_data { - int btn_keycode; - u8 accdet1_dbth; - u8 accdet2122_th; - unsigned int video_ctrl_gpio; - bool is_detection_inverted; - unsigned int mic_ctrl; -}; - -/* Enumerations */ - -/** - * @JACK_TYPE_UNSPECIFIED Not known whether any accessories are connected. - * @JACK_TYPE_DISCONNECTED No accessories connected. - * @JACK_TYPE_CONNECTED Accessory is connected but functionality was unable to - * detect the actual type. In this mode, possible button events are reported. - * @JACK_TYPE_HEADPHONE Headphone type of accessory (spkrs only) connected - * @JACK_TYPE_HEADSET Headset type of accessory (mic+spkrs) connected - * @JACK_TYPE_UNSUPPORTED_HEADSET Unsupported headset of type accessory connected - * @JACK_TYPE_CARKIT Carkit type of accessory connected - * @JACK_TYPE_OPENCABLE Open cable connected - * @JACK_TYPE_CVIDEO CVideo type of accessory connected. - */ -enum accessory_jack_type { - JACK_TYPE_UNSPECIFIED, - JACK_TYPE_DISCONNECTED, - JACK_TYPE_CONNECTED, - JACK_TYPE_HEADPHONE, - JACK_TYPE_HEADSET, - JACK_TYPE_UNSUPPORTED_HEADSET, - JACK_TYPE_CARKIT, - JACK_TYPE_OPENCABLE, - JACK_TYPE_CVIDEO -}; - -/** - * @BUTTON_UNK Button state not known - * @BUTTON_PRESSED Button "down" - * @BUTTON_RELEASED Button "up" - */ -enum accessory_button_state { - BUTTON_UNK, - BUTTON_PRESSED, - BUTTON_RELEASED -}; - -/** - * @PLUG_IRQ Interrupt gen. when accessory plugged in - * @UNPLUG_IRQ Interrupt gen. when accessory plugged out - * @BUTTON_PRESS_IRQ Interrupt gen. when accessory button pressed. - * @BUTTON_RELEASE_IRQ Interrupt gen. when accessory button released. - */ -enum accessory_irq { - PLUG_IRQ, - UNPLUG_IRQ, - BUTTON_PRESS_IRQ, - BUTTON_RELEASE_IRQ, -}; - -/** - * Enumerates the op. modes of the avcontrol switch - * @AUDIO_IN Audio input is selected - * @VIDEO_OUT Video output is selected - * @NOT_SET The av-switch control signal is disconnected. - */ -enum accessory_avcontrol_dir { - AUDIO_IN, - VIDEO_OUT, - NOT_SET, -}; - -/** - * @REGULATOR_VAUDIO v-audio regulator - * @REGULATOR_VAMIC1 v-amic1 regulator - * @REGULATOR_AVSWITCH Audio/Video select switch regulator - * @REGULATOR_ALL All regulators combined - */ -enum accessory_regulator { - REGULATOR_NONE = 0x0, - REGULATOR_VAUDIO = 0x1, - REGULATOR_VAMIC1 = 0x2, - REGULATOR_AVSWITCH = 0x4, - REGULATOR_ALL = 0xFF -}; - -/* Structures */ - -/** - * Describes an interrupt - * @irq interrupt identifier - * @name name of the irq in platform data - * @isr interrupt service routine - * @register are we currently registered to receive interrupts from this source. - */ -struct accessory_irq_descriptor { - enum accessory_irq irq; - const char *name; - irq_handler_t isr; - int registered; -}; - -/** - * Encapsulates info of single regulator. - * @id regulator identifier - * @name name of the regulator - * @enabled flag indicating whether regu is currently enabled. - * @handle regulator handle - */ -struct accessory_regu_descriptor { - enum accessory_regulator id; - const char *name; - int enabled; - struct regulator *handle; -}; - -/** - * Defines attributes for accessory detection operation. - * @typename type as string - * @type Type of accessory this task tests - * @req_det_count How many times this particular type of accessory - * needs to be detected in sequence in order to accept. Multidetection - * implemented to avoid false detections during plug-in. - * @meas_mv Should ACCDETECT2 input voltage be measured just before - * making the decision or can cached voltage be used instead. - * @minvol minimum voltage (mV) for decision - * @maxvol maximum voltage (mV) for decision - * @alt_minvol minimum alternative voltage (mV) for decision - * @alt_maxvol maximum alternative voltage (mV) for decision - */ -struct accessory_detect_task { - const char *typename; - enum accessory_jack_type type; - int req_det_count; - int meas_mv; - int minvol; - int maxvol; - int alt_minvol; - int alt_maxvol; -}; - -/** - * Device data, capsulates all relevant device data structures. - * - * @pdev: pointer to platform device - * @pdata: Platform data - * @gpadc: interface for ADC data - * @irq_work_queue: Work queue for deferred interrupt processing - * @detect_work: work item to perform detection work - * @unplug_irq_work: work item to process unplug event - * @init_work: work item to process initialization work. - * @btn_input_dev: button input device used to report btn presses - * @btn_state: Current state of accessory button - * @jack_type: type of currently connected accessory - * @reported_jack_type: previously reported jack type. - * @jack_type_temp: temporary storage for currently connected accessory - * @jack_det_count: counter how many times in sequence the accessory - * type detection has produced same result. - * @total_jack_det_count: after plug-in irq, how many times detection - * has totally been made in order to detect the accessory type - * @detect_jiffies: Used to save timestamp when detection was made. Timestamp - * used to filter out spurious button presses that might occur during the - * plug-in procedure. - * @accdet1_th_set: flag to indicate whether accdet1 threshold and debounce - * times are configured - * @accdet2_th_set: flag to indicate whether accdet2 thresholds are configured - * @gpio35_dir_set: flag to indicate whether GPIO35 (VIDEOCTRL) direction - * has been configured. - * @irq_desc_norm: irq's as specified in the initial versions of ab - * @irq_desc_inverted: irq's inverted as seen in the latest versions of ab - * @no_irqs: Total number of irq's - * @regu_desc: Pointer to the regulator descriptors. - * @no_of_regu_desc: Total nummber of descriptors. - * @config_accdetect2_hw: Callback for configuring accdet2 comparator. - * @config_accdetect1_hw: Callback for configuring accdet1 comparator. - * @detect_plugged_in: Callback to detect type of accessory connected. - * @meas_voltage_stable: Callback to read present accdet voltage. - * @meas_alt_voltage_stable: Callback to read present alt accdet voltage. - * @config_hw_test_basic_carkit: Callback to configure hw for carkit - * detect. - * @turn_of_accdet_comparator: Call back to turn off comparators. - * @turn_on_accdet_comparator: Call back to turn ON comparators. - * @accdet_abx500_gpadc_get Call back to get a instance of the - * GPADC convertor. - * @config_hw_test_plug_connected: Call back to configure the hw for - * accessory detection. - * @set_av_switch: Call back to configure the switch for tvout or audioout. - * @get_platform_data: call to get platform specific data. - */ -struct abx500_ad { - struct platform_device *pdev; - struct abx500_accdet_platform_data *pdata; - void *gpadc; - struct workqueue_struct *irq_work_queue; - - struct delayed_work detect_work; - struct delayed_work unplug_irq_work; - struct delayed_work init_work; - - struct input_dev *btn_input_dev; - enum accessory_button_state btn_state; - - enum accessory_jack_type jack_type; - enum accessory_jack_type reported_jack_type; - enum accessory_jack_type jack_type_temp; - - int jack_det_count; - int total_jack_det_count; - - unsigned long detect_jiffies; - - int accdet1_th_set; - int accdet2_th_set; - int gpio35_dir_set; - - struct accessory_irq_descriptor *irq_desc_norm; - struct accessory_irq_descriptor *irq_desc_inverted; - int no_irqs; - - struct accessory_regu_descriptor *regu_desc; - int no_of_regu_desc; - - void (*config_accdetect2_hw)(struct abx500_ad *, int); - void (*config_accdetect1_hw)(struct abx500_ad *, int); - int (*detect_plugged_in)(struct abx500_ad *); - int (*meas_voltage_stable)(struct abx500_ad *); - int (*meas_alt_voltage_stable)(struct abx500_ad *); - void (*config_hw_test_basic_carkit)(struct abx500_ad *, int); - void (*turn_off_accdet_comparator)(struct platform_device *pdev); - void (*turn_on_accdet_comparator)(struct platform_device *pdev); - void* (*accdet_abx500_gpadc_get)(void); - void (*config_hw_test_plug_connected)(struct abx500_ad *dd, int enable); - void (*set_av_switch)(struct abx500_ad *dd, - enum accessory_avcontrol_dir dir); - struct abx500_accdet_platform_data * - (*get_platform_data)(struct platform_device *pdev); -}; - -/* Forward declarations */ -extern irqreturn_t unplug_irq_handler(int irq, void *_userdata); -extern irqreturn_t plug_irq_handler(int irq, void *_userdata); -extern irqreturn_t button_press_irq_handler(int irq, void *_userdata); -extern irqreturn_t button_release_irq_handler(int irq, void *_userdata); -extern void accessory_regulator_enable(struct abx500_ad *dd, - enum accessory_regulator reg); -extern void accessory_regulator_disable(struct abx500_ad *dd, - enum accessory_regulator reg); -extern void report_jack_status(struct abx500_ad *dd); - -#ifdef CONFIG_INPUT_AB5500_ACCDET -extern struct abx500_ad ab5500_accessory_det_callbacks; -#endif - -#ifdef CONFIG_INPUT_AB8500_ACCDET -extern struct abx500_ad ab8500_accessory_det_callbacks; -#endif - -#endif /* _ABx500_ACCDET_H */ diff --git a/drivers/input/misc/ab5500-accdet.c b/drivers/input/misc/ab5500-accdet.c index 7622f3b45e9..fa8e2523126 100644 --- a/drivers/input/misc/ab5500-accdet.c +++ b/drivers/input/misc/ab5500-accdet.c @@ -18,7 +18,7 @@ #include #include #include -#include +#include /* * Register definition for accessory detection. diff --git a/drivers/input/misc/ab8500-accdet.c b/drivers/input/misc/ab8500-accdet.c index 59ad959e909..5786558bec3 100644 --- a/drivers/input/misc/ab8500-accdet.c +++ b/drivers/input/misc/ab8500-accdet.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include #ifdef CONFIG_SND_SOC_UX500_AB8500 #include #endif diff --git a/drivers/input/misc/abx500-accdet.c b/drivers/input/misc/abx500-accdet.c index 5c4e96c73d6..4b5017a6e60 100644 --- a/drivers/input/misc/abx500-accdet.c +++ b/drivers/input/misc/abx500-accdet.c @@ -19,15 +19,17 @@ #include #include #include -#include -#include +#include +#include #include #include #include #include -#include + +#include +#include #include -#include + #ifdef CONFIG_SND_SOC_UX500_AB8500 #include #else diff --git a/include/linux/input/abx500-accdet.h b/include/linux/input/abx500-accdet.h new file mode 100644 index 00000000000..9871ae11530 --- /dev/null +++ b/include/linux/input/abx500-accdet.h @@ -0,0 +1,362 @@ +/* + * Copyright ST-Ericsson 2011. + * + * Author: Jarmo K. Kuronen for ST Ericsson. + * Licensed under GPLv2. + */ + +#ifndef _ABx500_ACCDET_H +#define _ABx500_ACCDET_H + +/* +* Debounce times for AccDet1 input +* @0x880 [2:0] +*/ +#define ACCDET1_DB_0ms 0x00 +#define ACCDET1_DB_10ms 0x01 +#define ACCDET1_DB_20ms 0x02 +#define ACCDET1_DB_30ms 0x03 +#define ACCDET1_DB_40ms 0x04 +#define ACCDET1_DB_50ms 0x05 +#define ACCDET1_DB_60ms 0x06 +#define ACCDET1_DB_70ms 0x07 + +/* +* Voltage threshold for AccDet1 input +* @0x880 [6:3] +*/ +#define ACCDET1_TH_1100mV 0x40 +#define ACCDET1_TH_1200mV 0x48 +#define ACCDET1_TH_1300mV 0x50 +#define ACCDET1_TH_1400mV 0x58 +#define ACCDET1_TH_1500mV 0x60 +#define ACCDET1_TH_1600mV 0x68 +#define ACCDET1_TH_1700mV 0x70 +#define ACCDET1_TH_1800mV 0x78 + +/* +* Voltage threshold for AccDet21 input +* @0x881 [3:0] +*/ +#define ACCDET21_TH_300mV 0x00 +#define ACCDET21_TH_400mV 0x01 +#define ACCDET21_TH_500mV 0x02 +#define ACCDET21_TH_600mV 0x03 +#define ACCDET21_TH_700mV 0x04 +#define ACCDET21_TH_800mV 0x05 +#define ACCDET21_TH_900mV 0x06 +#define ACCDET21_TH_1000mV 0x07 +#define ACCDET21_TH_1100mV 0x08 +#define ACCDET21_TH_1200mV 0x09 +#define ACCDET21_TH_1300mV 0x0a +#define ACCDET21_TH_1400mV 0x0b +#define ACCDET21_TH_1500mV 0x0c +#define ACCDET21_TH_1600mV 0x0d +#define ACCDET21_TH_1700mV 0x0e +#define ACCDET21_TH_1800mV 0x0f + +/* +* Voltage threshold for AccDet22 input +* @0x881 [7:4] +*/ +#define ACCDET22_TH_300mV 0x00 +#define ACCDET22_TH_400mV 0x10 +#define ACCDET22_TH_500mV 0x20 +#define ACCDET22_TH_600mV 0x30 +#define ACCDET22_TH_700mV 0x40 +#define ACCDET22_TH_800mV 0x50 +#define ACCDET22_TH_900mV 0x60 +#define ACCDET22_TH_1000mV 0x70 +#define ACCDET22_TH_1100mV 0x80 +#define ACCDET22_TH_1200mV 0x90 +#define ACCDET22_TH_1300mV 0xa0 +#define ACCDET22_TH_1400mV 0xb0 +#define ACCDET22_TH_1500mV 0xc0 +#define ACCDET22_TH_1600mV 0xd0 +#define ACCDET22_TH_1700mV 0xe0 +#define ACCDET22_TH_1800mV 0xf0 + +/* +* Voltage threshold for AccDet1 input +* @0x880 [6:3] +*/ +#define ACCDET1_TH_300mV 0x00 +#define ACCDET1_TH_400mV 0x01 +#define ACCDET1_TH_500mV 0x02 +#define ACCDET1_TH_600mV 0x03 +#define ACCDET1_TH_700mV 0x04 +#define ACCDET1_TH_800mV 0x05 +#define ACCDET1_TH_900mV 0x06 +#define ACCDET1_TH_1000mV 0x07 + +#define MAX_DET_COUNT 10 +#define MAX_VOLT_DIFF 30 +#define MIN_MIC_POWER -100 + +/** + * struct abx500_accdet_platform_data - AV Accessory detection specific + * platform data + * @btn_keycode Keycode to be sent when accessory button is pressed. + * @accdet1_dbth Debounce time + voltage threshold for accdet 1 input. + * @accdet2122_th Voltage thresholds for accdet21 and accdet22 inputs. + * @is_detection_inverted Whether the accessory insert/removal, button + * press/release irq's are inverted. + * @mic_ctrl Gpio to select between CVBS and MIC. + */ +struct abx500_accdet_platform_data { + int btn_keycode; + u8 accdet1_dbth; + u8 accdet2122_th; + unsigned int video_ctrl_gpio; + bool is_detection_inverted; + unsigned int mic_ctrl; +}; + +/* Enumerations */ + +/** + * @JACK_TYPE_UNSPECIFIED Not known whether any accessories are connected. + * @JACK_TYPE_DISCONNECTED No accessories connected. + * @JACK_TYPE_CONNECTED Accessory is connected but functionality was unable to + * detect the actual type. In this mode, possible button events are reported. + * @JACK_TYPE_HEADPHONE Headphone type of accessory (spkrs only) connected + * @JACK_TYPE_HEADSET Headset type of accessory (mic+spkrs) connected + * @JACK_TYPE_UNSUPPORTED_HEADSET Unsupported headset of type accessory connected + * @JACK_TYPE_CARKIT Carkit type of accessory connected + * @JACK_TYPE_OPENCABLE Open cable connected + * @JACK_TYPE_CVIDEO CVideo type of accessory connected. + */ +enum accessory_jack_type { + JACK_TYPE_UNSPECIFIED, + JACK_TYPE_DISCONNECTED, + JACK_TYPE_CONNECTED, + JACK_TYPE_HEADPHONE, + JACK_TYPE_HEADSET, + JACK_TYPE_UNSUPPORTED_HEADSET, + JACK_TYPE_CARKIT, + JACK_TYPE_OPENCABLE, + JACK_TYPE_CVIDEO +}; + +/** + * @BUTTON_UNK Button state not known + * @BUTTON_PRESSED Button "down" + * @BUTTON_RELEASED Button "up" + */ +enum accessory_button_state { + BUTTON_UNK, + BUTTON_PRESSED, + BUTTON_RELEASED +}; + +/** + * @PLUG_IRQ Interrupt gen. when accessory plugged in + * @UNPLUG_IRQ Interrupt gen. when accessory plugged out + * @BUTTON_PRESS_IRQ Interrupt gen. when accessory button pressed. + * @BUTTON_RELEASE_IRQ Interrupt gen. when accessory button released. + */ +enum accessory_irq { + PLUG_IRQ, + UNPLUG_IRQ, + BUTTON_PRESS_IRQ, + BUTTON_RELEASE_IRQ, +}; + +/** + * Enumerates the op. modes of the avcontrol switch + * @AUDIO_IN Audio input is selected + * @VIDEO_OUT Video output is selected + * @NOT_SET The av-switch control signal is disconnected. + */ +enum accessory_avcontrol_dir { + AUDIO_IN, + VIDEO_OUT, + NOT_SET, +}; + +/** + * @REGULATOR_VAUDIO v-audio regulator + * @REGULATOR_VAMIC1 v-amic1 regulator + * @REGULATOR_AVSWITCH Audio/Video select switch regulator + * @REGULATOR_ALL All regulators combined + */ +enum accessory_regulator { + REGULATOR_NONE = 0x0, + REGULATOR_VAUDIO = 0x1, + REGULATOR_VAMIC1 = 0x2, + REGULATOR_AVSWITCH = 0x4, + REGULATOR_ALL = 0xFF +}; + +/* Structures */ + +/** + * Describes an interrupt + * @irq interrupt identifier + * @name name of the irq in platform data + * @isr interrupt service routine + * @register are we currently registered to receive interrupts from this source. + */ +struct accessory_irq_descriptor { + enum accessory_irq irq; + const char *name; + irq_handler_t isr; + int registered; +}; + +/** + * Encapsulates info of single regulator. + * @id regulator identifier + * @name name of the regulator + * @enabled flag indicating whether regu is currently enabled. + * @handle regulator handle + */ +struct accessory_regu_descriptor { + enum accessory_regulator id; + const char *name; + int enabled; + struct regulator *handle; +}; + +/** + * Defines attributes for accessory detection operation. + * @typename type as string + * @type Type of accessory this task tests + * @req_det_count How many times this particular type of accessory + * needs to be detected in sequence in order to accept. Multidetection + * implemented to avoid false detections during plug-in. + * @meas_mv Should ACCDETECT2 input voltage be measured just before + * making the decision or can cached voltage be used instead. + * @minvol minimum voltage (mV) for decision + * @maxvol maximum voltage (mV) for decision + * @alt_minvol minimum alternative voltage (mV) for decision + * @alt_maxvol maximum alternative voltage (mV) for decision + */ +struct accessory_detect_task { + const char *typename; + enum accessory_jack_type type; + int req_det_count; + int meas_mv; + int minvol; + int maxvol; + int alt_minvol; + int alt_maxvol; +}; + +/** + * Device data, capsulates all relevant device data structures. + * + * @pdev: pointer to platform device + * @pdata: Platform data + * @gpadc: interface for ADC data + * @irq_work_queue: Work queue for deferred interrupt processing + * @detect_work: work item to perform detection work + * @unplug_irq_work: work item to process unplug event + * @init_work: work item to process initialization work. + * @btn_input_dev: button input device used to report btn presses + * @btn_state: Current state of accessory button + * @jack_type: type of currently connected accessory + * @reported_jack_type: previously reported jack type. + * @jack_type_temp: temporary storage for currently connected accessory + * @jack_det_count: counter how many times in sequence the accessory + * type detection has produced same result. + * @total_jack_det_count: after plug-in irq, how many times detection + * has totally been made in order to detect the accessory type + * @detect_jiffies: Used to save timestamp when detection was made. Timestamp + * used to filter out spurious button presses that might occur during the + * plug-in procedure. + * @accdet1_th_set: flag to indicate whether accdet1 threshold and debounce + * times are configured + * @accdet2_th_set: flag to indicate whether accdet2 thresholds are configured + * @gpio35_dir_set: flag to indicate whether GPIO35 (VIDEOCTRL) direction + * has been configured. + * @irq_desc_norm: irq's as specified in the initial versions of ab + * @irq_desc_inverted: irq's inverted as seen in the latest versions of ab + * @no_irqs: Total number of irq's + * @regu_desc: Pointer to the regulator descriptors. + * @no_of_regu_desc: Total nummber of descriptors. + * @config_accdetect2_hw: Callback for configuring accdet2 comparator. + * @config_accdetect1_hw: Callback for configuring accdet1 comparator. + * @detect_plugged_in: Callback to detect type of accessory connected. + * @meas_voltage_stable: Callback to read present accdet voltage. + * @meas_alt_voltage_stable: Callback to read present alt accdet voltage. + * @config_hw_test_basic_carkit: Callback to configure hw for carkit + * detect. + * @turn_of_accdet_comparator: Call back to turn off comparators. + * @turn_on_accdet_comparator: Call back to turn ON comparators. + * @accdet_abx500_gpadc_get Call back to get a instance of the + * GPADC convertor. + * @config_hw_test_plug_connected: Call back to configure the hw for + * accessory detection. + * @set_av_switch: Call back to configure the switch for tvout or audioout. + * @get_platform_data: call to get platform specific data. + */ +struct abx500_ad { + struct platform_device *pdev; + struct abx500_accdet_platform_data *pdata; + void *gpadc; + struct workqueue_struct *irq_work_queue; + + struct delayed_work detect_work; + struct delayed_work unplug_irq_work; + struct delayed_work init_work; + + struct input_dev *btn_input_dev; + enum accessory_button_state btn_state; + + enum accessory_jack_type jack_type; + enum accessory_jack_type reported_jack_type; + enum accessory_jack_type jack_type_temp; + + int jack_det_count; + int total_jack_det_count; + + unsigned long detect_jiffies; + + int accdet1_th_set; + int accdet2_th_set; + int gpio35_dir_set; + + struct accessory_irq_descriptor *irq_desc_norm; + struct accessory_irq_descriptor *irq_desc_inverted; + int no_irqs; + + struct accessory_regu_descriptor *regu_desc; + int no_of_regu_desc; + + void (*config_accdetect2_hw)(struct abx500_ad *, int); + void (*config_accdetect1_hw)(struct abx500_ad *, int); + int (*detect_plugged_in)(struct abx500_ad *); + int (*meas_voltage_stable)(struct abx500_ad *); + int (*meas_alt_voltage_stable)(struct abx500_ad *); + void (*config_hw_test_basic_carkit)(struct abx500_ad *, int); + void (*turn_off_accdet_comparator)(struct platform_device *pdev); + void (*turn_on_accdet_comparator)(struct platform_device *pdev); + void* (*accdet_abx500_gpadc_get)(void); + void (*config_hw_test_plug_connected)(struct abx500_ad *dd, int enable); + void (*set_av_switch)(struct abx500_ad *dd, + enum accessory_avcontrol_dir dir); + struct abx500_accdet_platform_data * + (*get_platform_data)(struct platform_device *pdev); +}; + +/* Forward declarations */ +extern irqreturn_t unplug_irq_handler(int irq, void *_userdata); +extern irqreturn_t plug_irq_handler(int irq, void *_userdata); +extern irqreturn_t button_press_irq_handler(int irq, void *_userdata); +extern irqreturn_t button_release_irq_handler(int irq, void *_userdata); +extern void accessory_regulator_enable(struct abx500_ad *dd, + enum accessory_regulator reg); +extern void accessory_regulator_disable(struct abx500_ad *dd, + enum accessory_regulator reg); +extern void report_jack_status(struct abx500_ad *dd); + +#ifdef CONFIG_INPUT_AB5500_ACCDET +extern struct abx500_ad ab5500_accessory_det_callbacks; +#endif + +#ifdef CONFIG_INPUT_AB8500_ACCDET +extern struct abx500_ad ab8500_accessory_det_callbacks; +#endif + +#endif /* _ABx500_ACCDET_H */ -- cgit v1.2.3