From 4daffc83189f3ae0d09774a7803258d816a347f7 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 f2d03c06c2d..c803af206cb 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 bfa56fc911ea8f3b70360905b3516b338e2b4996 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 f354813a13e..698d37217bb 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 8a64b820ff4b53586f96d10549f162f0ce568497 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 35ae7302c931a3c84588320f2e017830ee32ba16 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 e39ca192d2595c80ad133e6a6a21ea42e6c2662b 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 | 196 ++++++++++++++++++++-------- 1 file changed, 139 insertions(+), 57 deletions(-) diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c index 101e245944e..f8e4ac00c74 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 __init 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 __init 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 __init 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 __init 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); -- cgit v1.2.3 From feafc677d80d2a286ec8a1d282ab5d0032201f1e 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 d265e775bca1649d2d1c5736e9fd396cdef375d2 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 c7e0614d666c0aa3a3320b652d47e94e8f44b2e4 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 f8e4ac00c74..9bb7f164fb6 100644 --- a/drivers/input/keyboard/nomadik-ske-keypad.c +++ b/drivers/input/keyboard/nomadik-ske-keypad.c @@ -184,12 +184,37 @@ static int __init 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 8a428a3a76531f9a9b1ad6b26a24e61f506f1915 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 9bb7f164fb6..076fb3a2fdf 100644 --- a/drivers/input/keyboard/nomadik-ske-keypad.c +++ b/drivers/input/keyboard/nomadik-ske-keypad.c @@ -393,7 +393,8 @@ static int __init 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 3c4b1ba339ee01e57d6f9048937537f53f4e7478 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 076fb3a2fdf..e822c031427 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 __init 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 eff2db6bf47e837ee3b1dd586850ff74e3b02fb3 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 e822c031427..88f34abae9b 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 122a911bea0615e9c398251f8f65cbe737bb2c14 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 88f34abae9b..6b4c6135311 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 __init 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 __init 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 e44e5d032f0410667a624b614c601c8394378847 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 698d37217bb..8aba6cc2bac 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 2a60c4b9227550fdf630fd43753b68afd1933b48 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 1d97c0e74a2b561d15d08a37029b5e0b24180cca 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 4e1bc0413c8473f023182e8d9b2a45794617ab91 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 f134692ffce1791637e24fc0a921d5948d1c0dd3 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 7faf4a7fcaa..3fb71b23fb5 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 f55cdf4916f..edbaf0d7adb 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 d7d94b3d46ee8a391e83688c5c0305e80a3b1fce 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 a056a794381d3d774ebdcd247c85dd119cdbf9ef 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 3fb71b23fb5..54789297273 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 ea17b3ec968115040e365eed245606426d310df5 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 75b6f30a8dd474f212352f081061566ecbd952f1 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 c803af206cb..d6ae75865e4 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 a965b58036cb7433fff1ac19f7f6a4ed55df01b6 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 6b4c6135311..5600d25123b 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 __init 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 __init 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 __init 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 __init 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 __init 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; @@ -535,6 +747,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 2dcab8618aaf3a2ddabcab3acedf712afb695d30 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 a2db8d3ad080f5e8696c8510568546f5b412028f 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 6fbbfcd700b57d212faf92fab1413a887da7aba7 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 9cfc3a2be1d4ce9cf7e7e488124998bd5696eea7 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 54789297273..69b99e2d400 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 0bb8f1bf104462cbb1749e67013258d1c6bc2df4 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 14586ab7e31c71bee3750118c283aee4fb330c52 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 ae851308d807c9c2f0170732679b6da76f7e2b60 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 5ee7f15ed5a429c085ab8c0083d2fad1cc1ee384 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 f176161e930c7961b4254c5d85168d0ce164d31a 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 f691a3e1c51228cb60e64ca40e4fde24a6e1eee3 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 177839ba88ba97d3d8ea3df3da9d04a552f53bf6 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 5600d25123b..b86d44cd5ad 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 cf1650ba34cd98b4e22a8bccc8dea71faee42fcc 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 69b99e2d400..5f034e1ec05 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 2b236e95ab8c997ed2bce09dac25cf5f7751a1ab 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 a0d47f3a38d5bf1edc679f401084b15e5498bc09 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 01a4f7f188ea3d622212ed0848164a396e4821ae 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 ca05ccfea38ea02998c6392ed3180a720e4f449d 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 adbf88539fcd5fab848b60c7053cec6c0052267c 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 5f034e1ec05..920644f5674 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -597,4 +597,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 edbaf0d7adb..6efdfda19dd 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -56,3 +56,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 dc0c779a8f09e343841063d5f19d9a71b14be402 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 eb43e3a25be22cfbc89e46fa09b75b4b4fd1ceb8 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 92899a5d795c61d5381dbdd835978bb53fb575b1 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 c00ba0f42c76967aea1d18f8425ce714efaf7cdc 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 3e8faf51d325f0b7c52cf29b098da2d77811097d 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 920644f5674..dedd5d6cf7a 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 6efdfda19dd..e9aa2d854bc 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 76f7a002b166add74cfca14870c96ff5865dfddd 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 8e139dc96cffb128073cc996796c3193e22aefa4 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 84b09a06a77caebbf9e367cb02e4c5e53ca1638a 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 44808732559fdd0e684be86c8f8004570a43b237 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 b86d44cd5ad..e791b16c6b1 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 __init 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 __init 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 __init 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 __init 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 25c73da6a114c1002dfc5055fdd2f68d625a9920 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 10706ec965ba358539c26cb5988788eacd8ab160 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 d546fc326ee3e73b36ebdd58157fdfaa83e248aa 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 e791b16c6b1..9a3e7dfb605 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 a515797959524b3ab491e9b677c554910b4971f8 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 62bfce468f9..888703d79c7 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -46,6 +46,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]; @@ -524,6 +526,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; } @@ -533,6 +536,7 @@ static void gpio_keys_close(struct input_dev *input) if (ddata->disable) ddata->disable(input->dev.parent); + ddata->enabled = false; } /* @@ -675,6 +679,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); @@ -790,6 +795,10 @@ static int gpio_keys_suspend(struct device *dev) if (bdata->button->wakeup) enable_irq_wake(bdata->irq); } + } else { + ddata->enable_after_suspend = ddata->enabled; + if (ddata->enabled) + gpio_keys_close(ddata->input); } return 0; @@ -808,6 +817,10 @@ static int gpio_keys_resume(struct device *dev) if (gpio_is_valid(bdata->button->gpio)) gpio_keys_gpio_report_event(bdata); } + + 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 9e6a558425b30cf60707526e2ca371d3f9193ee1 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 9a3e7dfb605..7ab061f0124 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 1af9117c6e212375089ee7595441eb510de200c3 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 6ee4c50a1f8d52b8c153ac4cac512bba3519f9ca 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 5d767f8ec6adf7f1eef4c6f4e5b3808a7987c5f6 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 7ab061f0124..0f9966bfaca 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 e104612fc4bcbbf95d009e1ca0f822cf28241c88 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 aa7a9648269fc983f8c0035f353f83d608c3ed56 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 0f9966bfaca..4a38ca32d16 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 9e9b46f37032b6ac8e49bdf3ef5d9c629290126e 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 8c858cdc4a363b7c9b4989125db01d914fa8fc5d 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 b0fe67b7a7cd0645c43bbca20ea31c8db049db8f 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 4a38ca32d16..056f8bc1b06 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 __init ske_keypad_chip_init(struct ske_keypad *keypad) -- cgit v1.2.3 From 22f6fcb4ace2e99058078a8d5e2c1db3a29eac4d 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 6f632d895387719785c816c64b3d213f6b80278f 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 7695f8c2b610ae299f05fefd6adc13483e6c9289 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 056f8bc1b06..022b474e7ec 100644 --- a/drivers/input/keyboard/nomadik-ske-keypad.c +++ b/drivers/input/keyboard/nomadik-ske-keypad.c @@ -683,12 +683,10 @@ static int __init 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 f278b3586371e6ad98237095c279519995fa696d 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 db59ae830656e3c87f0ace7edbade0a684ca2c7d 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 961a50cea677e23992aa0fc49ed33f50779ea328 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 022b474e7ec..5bc91685f90 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 dec331e62c2d978ebf73b9f0f04b53ace3c24907 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 5bc91685f90..156f64b1339 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 __init 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 __init 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 __init 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 __init 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 1c6d6ea4547067a8b627c2f7ea9533f8eb87695f 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 31d89ec8bed559e5f047e253031e5fd4a2bf6ca1 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 5409adc138caf18a6399a0c69d9a73fc858983c7 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 156f64b1339..6a0707f7f76 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 1127d5036421b97ceb2a396978863f2fbb8ac7b8 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 be170847f6a78a1753831cf36b95d94417eb4eb3 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 From cc2397425aa40cb79d054d0825cc2c67808d9f89 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 888703d79c7..1fdf54bb4f6 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -29,6 +29,7 @@ #include #include #include +#include struct gpio_button_data { const struct gpio_keys_button *button; @@ -526,6 +527,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; } @@ -537,6 +539,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); } /* @@ -696,6 +699,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); @@ -761,6 +766,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); @@ -797,8 +804,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; @@ -818,8 +825,9 @@ static int gpio_keys_resume(struct device *dev) gpio_keys_gpio_report_event(bdata); } - 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 574d17466e4def02f2b3a3be8cffec312dcfc51d Mon Sep 17 00:00:00 2001 From: Philippe Langlais Date: Thu, 19 Apr 2012 14:30:11 +0200 Subject: input: ab8500-accdet: Fix after new name param in ab8500_gpadc_get() Signed-off-by: Philippe Langlais --- 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 5786558bec3..1b96b6b3fef 100644 --- a/drivers/input/misc/ab8500-accdet.c +++ b/drivers/input/misc/ab8500-accdet.c @@ -419,7 +419,7 @@ static void ab8500_turn_on_accdet_comparator(struct platform_device *pdev) static void *ab8500_accdet_abx500_gpadc_get(void) { - return ab8500_gpadc_get(); + return ab8500_gpadc_get("ab8500-gpadc.0"); } struct abx500_accdet_platform_data * -- cgit v1.2.3