From 583f8891e55d973b72fab24e45b5a43e720a2a08 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 --- drivers/input/touchscreen/bu21013_ts.c | 489 +++++++++++++------ drivers/input/touchscreen/synaptics_i2c_rmi.c | 675 ++++++++++++++++++++++++++ drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c | 309 +++++++++--- 3 files changed, 1253 insertions(+), 220 deletions(-) create mode 100644 drivers/input/touchscreen/synaptics_i2c_rmi.c (limited to 'drivers') diff --git a/drivers/input/touchscreen/bu21013_ts.c b/drivers/input/touchscreen/bu21013_ts.c index 902c7214e88..62dbc183347 100644 --- a/drivers/input/touchscreen/bu21013_ts.c +++ b/drivers/input/touchscreen/bu21013_ts.c @@ -1,5 +1,5 @@ /* - * Copyright (C) ST-Ericsson SA 2010 + * Copyright (C) ST-Ericsson SA 2009 * Author: Naveen Kumar G for ST-Ericsson * License terms:GNU General Public License (GPL) version 2 */ @@ -12,13 +12,14 @@ #include #include #include +#include #include #include #define PEN_DOWN_INTR 0 -#define MAX_FINGERS 2 #define RESET_DELAY 30 -#define PENUP_TIMEOUT (10) +#define PENUP_TIMEOUT 2 /* 2msecs */ +#define SCALE_FACTOR 1000 #define DELTA_MIN 16 #define MASK_BITS 0x03 #define SHIFT_8 8 @@ -131,7 +132,7 @@ #define BU21013_NUMBER_OF_X_SENSORS (6) #define BU21013_NUMBER_OF_Y_SENSORS (11) -#define DRIVER_TP "bu21013_tp" +#define DRIVER_TP "bu21013_ts" /** * struct bu21013_ts_data - touch panel data structure @@ -142,6 +143,12 @@ * @in_dev: pointer to the input device structure * @intr_pin: interrupt pin value * @regulator: pointer to the Regulator used for touch screen + * @enable: variable to indicate the enable/disable of touch screen + * @ext_clk_enable: true if running on ext clk + * @ext_clk_state: Saved state for suspend/resume of ext clk + * @factor_x: x scale factor + * @factor_y: y scale factor + * @tpclk: pointer to clock structure * * Touch panel device data structure */ @@ -149,12 +156,216 @@ struct bu21013_ts_data { struct i2c_client *client; wait_queue_head_t wait; bool touch_stopped; - const struct bu21013_platform_device *chip; + struct bu21013_platform_device *chip; struct input_dev *in_dev; unsigned int intr_pin; struct regulator *regulator; + bool enable; + bool ext_clk_enable; + bool ext_clk_state; + unsigned int factor_x; + unsigned int factor_y; + struct clk *tpclk; }; +static int bu21013_init_chip(struct bu21013_ts_data *data, bool on_ext_clk); + +/** + * bu21013_ext_clk() - enable/disable the external clock + * @pdata: touch screen data + * @enable: enable external clock + * @reconfig: reconfigure chip upon external clock off. + * + * This function used to enable or disable the external clock and possible + * reconfigure hw. + */ +static int bu21013_ext_clk(struct bu21013_ts_data *pdata, bool enable, + bool reconfig) +{ + int retval = 0; + + if (!pdata->tpclk || pdata->ext_clk_enable == enable) + return retval; + + if (enable) { + pdata->ext_clk_enable = true; + clk_enable(pdata->tpclk); + retval = bu21013_init_chip(pdata, true); + } else { + pdata->ext_clk_enable = false; + if (reconfig) + retval = bu21013_init_chip(pdata, false); + clk_disable(pdata->tpclk); + } + return retval; +} + +/** + * bu21013_enable() - enable the touch driver event + * @pdata: touch screen data + * + * This function used to enable the driver and returns integer + */ +static int bu21013_enable(struct bu21013_ts_data *pdata) +{ + int retval; + + if (pdata->regulator) + regulator_enable(pdata->regulator); + + if (pdata->ext_clk_state) + retval = bu21013_ext_clk(pdata, true, true); + else + retval = bu21013_init_chip(pdata, false); + + if (retval < 0) { + dev_err(&pdata->client->dev, "enable hw failed\n"); + return retval; + } + pdata->touch_stopped = false; + enable_irq(pdata->chip->irq); + + return 0; +} + +/** + * bu21013_disable() - disable the touch driver event + * @pdata: touch screen data + * + * This function used to disable the driver and returns integer + */ +static void bu21013_disable(struct bu21013_ts_data *pdata) +{ + pdata->touch_stopped = true; + + pdata->ext_clk_state = pdata->ext_clk_enable; + (void) bu21013_ext_clk(pdata, false, false); + + disable_irq(pdata->chip->irq); + if (pdata->regulator) + regulator_disable(pdata->regulator); +} + +/** + * bu21013_show_attr_enable() - show the touch screen controller status + * @dev: pointer to device structure + * @attr: pointer to device attribute + * @buf: parameter buffer + * + * This funtion is used to show whether the touch screen is enabled or + * disabled + */ +static ssize_t bu21013_show_attr_enable(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct bu21013_ts_data *pdata = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", pdata->enable); +} + +/** + * bu21013_store_attr_enable() - Enable/Disable the touchscreen. + * @dev: pointer to device structure + * @attr: pointer to device attribute + * @buf: parameter buffer + * @count: number of parameters + * + * This funtion is used to enable or disable the touch screen controller. + */ +static ssize_t bu21013_store_attr_enable(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int ret = 0; + unsigned long val; + + struct bu21013_ts_data *pdata = dev_get_drvdata(dev); + + if (strict_strtoul(buf, 0, &val)) + return -EINVAL; + + if ((val != 0) && (val != 1)) + return -EINVAL; + + if (pdata->enable != val) { + pdata->enable = val ? true : false; + if (pdata->enable) { + ret = bu21013_enable(pdata); + if (ret < 0) + return ret; + } else + bu21013_disable(pdata); + } + return count; +} + +/** + * bu21013_show_attr_extclk() - shows the external clock status + * @dev: pointer to device structure + * @attr: pointer to device attribute + * @buf: parameter buffer + * + * This funtion is used to show whether the external clock for the touch + * screen is enabled or disabled. + */ +static ssize_t bu21013_show_attr_extclk(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct bu21013_ts_data *pdata = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", pdata->ext_clk_enable); +} + +/** + * bu21013_store_attr_extclk() - Enable/Disable the external clock + * for the tocuh screen controller. + * @dev: pointer to device structure + * @attr: pointer to device attribute + * @buf: parameter buffer + * @count: number of parameters + * + * This funtion is used enabled or disable the external clock for the touch + * screen controller. + */ +static ssize_t bu21013_store_attr_extclk(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval = 0; + struct bu21013_ts_data *pdata = dev_get_drvdata(dev); + unsigned long val; + + if (strict_strtoul(buf, 0, &val)) + return -EINVAL; + + if ((val != 0) && (val != 1)) + return -EINVAL; + + if (pdata->chip->has_ext_clk) { + if (pdata->enable) + retval = bu21013_ext_clk(pdata, val, true); + else + pdata->ext_clk_state = val; + if (retval < 0) + return retval; + } + return count; +} + +static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO, + bu21013_show_attr_enable, bu21013_store_attr_enable); + +static DEVICE_ATTR(ext_clk, S_IWUSR | S_IRUGO, + bu21013_show_attr_extclk, bu21013_store_attr_extclk); + + +static struct attribute *bu21013_attribute[] = { + &dev_attr_enable.attr, + &dev_attr_ext_clk.attr, + NULL, +}; + +static struct attribute_group bu21013_attr_group = { + .attrs = bu21013_attribute, +}; + + /** * bu21013_read_block_data(): read the touch co-ordinates * @data: bu21013_ts_data structure pointer @@ -204,12 +415,14 @@ static int bu21013_do_touch_report(struct bu21013_ts_data *data) if (!has_x_sensors || !has_y_sensors) return 0; - for (i = 0; i < MAX_FINGERS; i++) { + for (i = 0; i < 2; i++) { const u8 *p = &buf[4 * i + 3]; unsigned int x = p[0] << SHIFT_2 | (p[1] & MASK_BITS); unsigned int y = p[2] << SHIFT_2 | (p[3] & MASK_BITS); if (x == 0 || y == 0) continue; + x = x * data->factor_x / SCALE_FACTOR; + y = y * data->factor_y / SCALE_FACTOR; pos_x[finger_down_count] = x; pos_y[finger_down_count] = y; finger_down_count++; @@ -217,21 +430,21 @@ static int bu21013_do_touch_report(struct bu21013_ts_data *data) if (finger_down_count) { if (finger_down_count == 2 && - (abs(pos_x[0] - pos_x[1]) < DELTA_MIN || - abs(pos_y[0] - pos_y[1]) < DELTA_MIN)) { + (abs(pos_x[0] - pos_x[1]) < DELTA_MIN || + abs(pos_y[0] - pos_y[1]) < DELTA_MIN)) return 0; - } for (i = 0; i < finger_down_count; i++) { - if (data->chip->x_flip) - pos_x[i] = data->chip->touch_x_max - pos_x[i]; - if (data->chip->y_flip) - pos_y[i] = data->chip->touch_y_max - pos_y[i]; - - input_report_abs(data->in_dev, - ABS_MT_POSITION_X, pos_x[i]); - input_report_abs(data->in_dev, - ABS_MT_POSITION_Y, pos_y[i]); + if (data->chip->portrait && data->chip->x_flip) + pos_x[i] = data->chip->x_max_res - pos_x[i]; + if (data->chip->portrait && data->chip->y_flip) + pos_y[i] = data->chip->y_max_res - pos_y[i]; + input_report_abs(data->in_dev, ABS_MT_TOUCH_MAJOR, + max(pos_x[i], pos_y[i])); + input_report_abs(data->in_dev, ABS_MT_POSITION_X, + pos_x[i]); + input_report_abs(data->in_dev, ABS_MT_POSITION_Y, + pos_y[i]); input_mt_sync(data->in_dev); } } else @@ -261,24 +474,23 @@ static irqreturn_t bu21013_gpio_irq(int irq, void *device_data) dev_err(&i2c->dev, "bu21013_do_touch_report failed\n"); return IRQ_NONE; } - data->intr_pin = data->chip->irq_read_val(); if (data->intr_pin == PEN_DOWN_INTR) wait_event_timeout(data->wait, data->touch_stopped, - msecs_to_jiffies(2)); + msecs_to_jiffies(PENUP_TIMEOUT)); } while (!data->intr_pin && !data->touch_stopped); - return IRQ_HANDLED; } /** * bu21013_init_chip() - power on sequence for the bu21013 controller * @data: device structure pointer + * @on_ext_clk: Run on external clock * * This function is used to power on * the bu21013 controller and returns integer. */ -static int bu21013_init_chip(struct bu21013_ts_data *data) +static int bu21013_init_chip(struct bu21013_ts_data *data, bool on_ext_clk) { int retval; struct i2c_client *i2c = data->client; @@ -297,28 +509,24 @@ static int bu21013_init_chip(struct bu21013_ts_data *data) dev_err(&i2c->dev, "BU21013_SENSOR_0_7 reg write failed\n"); return retval; } - retval = i2c_smbus_write_byte_data(i2c, BU21013_SENSOR_8_15_REG, BU21013_SENSORS_EN_8_15); if (retval < 0) { dev_err(&i2c->dev, "BU21013_SENSOR_8_15 reg write failed\n"); return retval; } - retval = i2c_smbus_write_byte_data(i2c, BU21013_SENSOR_16_23_REG, BU21013_SENSORS_EN_16_23); if (retval < 0) { dev_err(&i2c->dev, "BU21013_SENSOR_16_23 reg write failed\n"); return retval; } - retval = i2c_smbus_write_byte_data(i2c, BU21013_POS_MODE1_REG, (BU21013_POS_MODE1_0 | BU21013_POS_MODE1_1)); if (retval < 0) { dev_err(&i2c->dev, "BU21013_POS_MODE1 reg write failed\n"); return retval; } - retval = i2c_smbus_write_byte_data(i2c, BU21013_POS_MODE2_REG, (BU21013_POS_MODE2_ZERO | BU21013_POS_MODE2_AVG1 | BU21013_POS_MODE2_AVG2 | BU21013_POS_MODE2_EN_RAW | @@ -327,8 +535,7 @@ static int bu21013_init_chip(struct bu21013_ts_data *data) dev_err(&i2c->dev, "BU21013_POS_MODE2 reg write failed\n"); return retval; } - - if (data->chip->ext_clk) + if (on_ext_clk) retval = i2c_smbus_write_byte_data(i2c, BU21013_CLK_MODE_REG, (BU21013_CLK_MODE_EXT | BU21013_CLK_MODE_CALIB)); else @@ -338,21 +545,18 @@ static int bu21013_init_chip(struct bu21013_ts_data *data) dev_err(&i2c->dev, "BU21013_CLK_MODE reg write failed\n"); return retval; } - retval = i2c_smbus_write_byte_data(i2c, BU21013_IDLE_REG, (BU21013_IDLET_0 | BU21013_IDLE_INTERMIT_EN)); if (retval < 0) { dev_err(&i2c->dev, "BU21013_IDLE reg write failed\n"); return retval; } - retval = i2c_smbus_write_byte_data(i2c, BU21013_INT_MODE_REG, BU21013_INT_MODE_LEVEL); if (retval < 0) { dev_err(&i2c->dev, "BU21013_INT_MODE reg write failed\n"); return retval; } - retval = i2c_smbus_write_byte_data(i2c, BU21013_FILTER_REG, (BU21013_DELTA_0_6 | BU21013_FILTER_EN)); @@ -367,14 +571,12 @@ static int bu21013_init_chip(struct bu21013_ts_data *data) dev_err(&i2c->dev, "BU21013_TH_ON reg write failed\n"); return retval; } - retval = i2c_smbus_write_byte_data(i2c, BU21013_TH_OFF_REG, BU21013_TH_OFF_4 | BU21013_TH_OFF_3); if (retval < 0) { dev_err(&i2c->dev, "BU21013_TH_OFF reg write failed\n"); return retval; } - retval = i2c_smbus_write_byte_data(i2c, BU21013_GAIN_REG, (BU21013_GAIN_0 | BU21013_GAIN_1)); if (retval < 0) { @@ -388,7 +590,6 @@ static int bu21013_init_chip(struct bu21013_ts_data *data) dev_err(&i2c->dev, "BU21013_OFFSET_MODE reg write failed\n"); return retval; } - retval = i2c_smbus_write_byte_data(i2c, BU21013_XY_EDGE_REG, (BU21013_X_EDGE_0 | BU21013_X_EDGE_2 | BU21013_Y_EDGE_1 | BU21013_Y_EDGE_3)); @@ -396,7 +597,6 @@ static int bu21013_init_chip(struct bu21013_ts_data *data) dev_err(&i2c->dev, "BU21013_XY_EDGE reg write failed\n"); return retval; } - retval = i2c_smbus_write_byte_data(i2c, BU21013_DONE_REG, BU21013_DONE); if (retval < 0) { @@ -404,25 +604,15 @@ static int bu21013_init_chip(struct bu21013_ts_data *data) return retval; } - return 0; -} - -/** - * bu21013_free_irq() - frees IRQ registered for touchscreen - * @bu21013_data: device structure pointer - * - * This function signals interrupt thread to stop processing and - * frees interrupt. - */ -static void bu21013_free_irq(struct bu21013_ts_data *bu21013_data) -{ - bu21013_data->touch_stopped = true; - wake_up(&bu21013_data->wait); - free_irq(bu21013_data->chip->irq, bu21013_data); + data->factor_x = (data->chip->x_max_res * SCALE_FACTOR / + data->chip->touch_x_max); + data->factor_y = (data->chip->y_max_res * SCALE_FACTOR / + data->chip->touch_y_max); + return retval; } /** - * bu21013_probe() - initializes the i2c-client touchscreen driver + * bu21013_probe() - initialzes the i2c-client touchscreen driver * @client: i2c client structure pointer * @id: i2c device id pointer * @@ -432,11 +622,11 @@ static void bu21013_free_irq(struct bu21013_ts_data *bu21013_data) static int __devinit bu21013_probe(struct i2c_client *client, const struct i2c_device_id *id) { + int retval; struct bu21013_ts_data *bu21013_data; struct input_dev *in_dev; - const struct bu21013_platform_device *pdata = + struct bu21013_platform_device *pdata = client->dev.platform_data; - int error; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { @@ -446,53 +636,72 @@ static int __devinit bu21013_probe(struct i2c_client *client, if (!pdata) { dev_err(&client->dev, "platform data not defined\n"); - return -EINVAL; + retval = -EINVAL; + return retval; } bu21013_data = kzalloc(sizeof(struct bu21013_ts_data), GFP_KERNEL); - in_dev = input_allocate_device(); - if (!bu21013_data || !in_dev) { + if (!bu21013_data) { dev_err(&client->dev, "device memory alloc failed\n"); - error = -ENOMEM; - goto err_free_mem; + retval = -ENOMEM; + return retval; + } + /* allocate input device */ + in_dev = input_allocate_device(); + if (!in_dev) { + dev_err(&client->dev, "input device memory alloc failed\n"); + retval = -ENOMEM; + goto err_alloc; } bu21013_data->in_dev = in_dev; bu21013_data->chip = pdata; bu21013_data->client = client; - bu21013_data->regulator = regulator_get(&client->dev, "V-TOUCH"); + bu21013_data->regulator = regulator_get(&client->dev, "avdd"); if (IS_ERR(bu21013_data->regulator)) { - dev_err(&client->dev, "regulator_get failed\n"); - error = PTR_ERR(bu21013_data->regulator); - goto err_free_mem; + dev_warn(&client->dev, "regulator_get failed\n"); + bu21013_data->regulator = NULL; } - - error = regulator_enable(bu21013_data->regulator); - if (error < 0) { - dev_err(&client->dev, "regulator enable failed\n"); - goto err_put_regulator; - } - - bu21013_data->touch_stopped = false; - init_waitqueue_head(&bu21013_data->wait); + if (bu21013_data->regulator) + regulator_enable(bu21013_data->regulator); /* configure the gpio pins */ if (pdata->cs_en) { - error = pdata->cs_en(pdata->cs_pin); - if (error < 0) { + retval = pdata->cs_en(pdata->cs_pin); + if (retval < 0) { dev_err(&client->dev, "chip init failed\n"); - goto err_disable_regulator; + goto err_init_cs; + } + } + + if (pdata->has_ext_clk) { + bu21013_data->tpclk = clk_get(&client->dev, NULL); + if (IS_ERR(bu21013_data->tpclk)) { + dev_warn(&client->dev, "get extern clock failed\n"); + bu21013_data->tpclk = NULL; + } + } + + if (pdata->enable_ext_clk && bu21013_data->tpclk) { + retval = clk_enable(bu21013_data->tpclk); + if (retval < 0) { + dev_err(&client->dev, "clock enable failed\n"); + goto err_ext_clk; } + bu21013_data->ext_clk_enable = true; } /* configure the touch panel controller */ - error = bu21013_init_chip(bu21013_data); - if (error) { + retval = bu21013_init_chip(bu21013_data, bu21013_data->ext_clk_enable); + if (retval < 0) { dev_err(&client->dev, "error in bu21013 config\n"); - goto err_cs_disable; + goto err_init_config; } + init_waitqueue_head(&bu21013_data->wait); + bu21013_data->touch_stopped = false; + /* register the device to input subsystem */ in_dev->name = DRIVER_TP; in_dev->id.bustype = BUS_I2C; @@ -503,44 +712,63 @@ static int __devinit bu21013_probe(struct i2c_client *client, __set_bit(EV_ABS, in_dev->evbit); input_set_abs_params(in_dev, ABS_MT_POSITION_X, 0, - pdata->touch_x_max, 0, 0); + pdata->x_max_res, 0, 0); input_set_abs_params(in_dev, ABS_MT_POSITION_Y, 0, - pdata->touch_y_max, 0, 0); + pdata->y_max_res, 0, 0); + input_set_abs_params(in_dev, ABS_MT_TOUCH_MAJOR, 0, + max(pdata->x_max_res , pdata->y_max_res), 0, 0); input_set_drvdata(in_dev, bu21013_data); - - error = request_threaded_irq(pdata->irq, NULL, bu21013_gpio_irq, - IRQF_TRIGGER_FALLING | IRQF_SHARED, - DRIVER_TP, bu21013_data); - if (error) { + retval = input_register_device(in_dev); + if (retval) + goto err_input_register; + + retval = request_threaded_irq(pdata->irq, NULL, bu21013_gpio_irq, + (IRQF_TRIGGER_FALLING | IRQF_SHARED), + DRIVER_TP, bu21013_data); + if (retval) { dev_err(&client->dev, "request irq %d failed\n", pdata->irq); - goto err_cs_disable; + goto err_init_irq; } + bu21013_data->enable = true; + i2c_set_clientdata(client, bu21013_data); - error = input_register_device(in_dev); - if (error) { - dev_err(&client->dev, "failed to register input device\n"); - goto err_free_irq; + /* sysfs implementation for dynamic enable/disable the input event */ + retval = sysfs_create_group(&client->dev.kobj, &bu21013_attr_group); + if (retval) { + dev_err(&client->dev, "failed to create sysfs entries\n"); + goto err_sysfs_create; } - device_init_wakeup(&client->dev, pdata->wakeup); - i2c_set_clientdata(client, bu21013_data); - - return 0; + return retval; -err_free_irq: - bu21013_free_irq(bu21013_data); -err_cs_disable: - pdata->cs_dis(pdata->cs_pin); -err_disable_regulator: - regulator_disable(bu21013_data->regulator); -err_put_regulator: - regulator_put(bu21013_data->regulator); -err_free_mem: - input_free_device(in_dev); +err_sysfs_create: + free_irq(pdata->irq, bu21013_data); + i2c_set_clientdata(client, NULL); +err_init_irq: + input_unregister_device(bu21013_data->in_dev); +err_input_register: + wake_up(&bu21013_data->wait); +err_init_config: + if (bu21013_data->tpclk) { + if (bu21013_data->ext_clk_enable) + clk_disable(bu21013_data->tpclk); + clk_put(bu21013_data->tpclk); + } +err_ext_clk: + if (pdata->cs_dis) + pdata->cs_dis(pdata->cs_pin); +err_init_cs: + if (bu21013_data->regulator) { + regulator_disable(bu21013_data->regulator); + regulator_put(bu21013_data->regulator); + } + input_free_device(bu21013_data->in_dev); +err_alloc: kfree(bu21013_data); - return error; + return retval; } + /** * bu21013_remove() - removes the i2c-client touchscreen driver * @client: i2c client structure pointer @@ -552,19 +780,24 @@ static int __devexit bu21013_remove(struct i2c_client *client) { struct bu21013_ts_data *bu21013_data = i2c_get_clientdata(client); - bu21013_free_irq(bu21013_data); - + bu21013_data->touch_stopped = true; + sysfs_remove_group(&client->dev.kobj, &bu21013_attr_group); + wake_up(&bu21013_data->wait); + free_irq(bu21013_data->chip->irq, bu21013_data); bu21013_data->chip->cs_dis(bu21013_data->chip->cs_pin); - input_unregister_device(bu21013_data->in_dev); - regulator_disable(bu21013_data->regulator); - regulator_put(bu21013_data->regulator); - + if (bu21013_data->tpclk) { + if (bu21013_data->ext_clk_enable) + clk_disable(bu21013_data->tpclk); + clk_put(bu21013_data->tpclk); + } + if (bu21013_data->regulator) { + regulator_disable(bu21013_data->regulator); + regulator_put(bu21013_data->regulator); + } kfree(bu21013_data); - device_init_wakeup(&client->dev, false); - return 0; } @@ -579,15 +812,8 @@ static int __devexit bu21013_remove(struct i2c_client *client) static int bu21013_suspend(struct device *dev) { struct bu21013_ts_data *bu21013_data = dev_get_drvdata(dev); - struct i2c_client *client = bu21013_data->client; - bu21013_data->touch_stopped = true; - if (device_may_wakeup(&client->dev)) - enable_irq_wake(bu21013_data->chip->irq); - else - disable_irq(bu21013_data->chip->irq); - - regulator_disable(bu21013_data->regulator); + bu21013_disable(bu21013_data); return 0; } @@ -602,29 +828,8 @@ static int bu21013_suspend(struct device *dev) static int bu21013_resume(struct device *dev) { struct bu21013_ts_data *bu21013_data = dev_get_drvdata(dev); - struct i2c_client *client = bu21013_data->client; - int retval; - retval = regulator_enable(bu21013_data->regulator); - if (retval < 0) { - dev_err(&client->dev, "bu21013 regulator enable failed\n"); - return retval; - } - - retval = bu21013_init_chip(bu21013_data); - if (retval < 0) { - dev_err(&client->dev, "bu21013 controller config failed\n"); - return retval; - } - - bu21013_data->touch_stopped = false; - - if (device_may_wakeup(&client->dev)) - disable_irq_wake(bu21013_data->chip->irq); - else - enable_irq(bu21013_data->chip->irq); - - return 0; + return bu21013_enable(bu21013_data); } static const struct dev_pm_ops bu21013_dev_pm_ops = { diff --git a/drivers/input/touchscreen/synaptics_i2c_rmi.c b/drivers/input/touchscreen/synaptics_i2c_rmi.c new file mode 100644 index 00000000000..5729602cbb6 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_i2c_rmi.c @@ -0,0 +1,675 @@ +/* drivers/input/keyboard/synaptics_i2c_rmi.c + * + * Copyright (C) 2007 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct workqueue_struct *synaptics_wq; + +struct synaptics_ts_data { + uint16_t addr; + struct i2c_client *client; + struct input_dev *input_dev; + int use_irq; + bool has_relative_report; + struct hrtimer timer; + struct work_struct work; + uint16_t max[2]; + int snap_state[2][2]; + int snap_down_on[2]; + int snap_down_off[2]; + int snap_up_on[2]; + int snap_up_off[2]; + int snap_down[2]; + int snap_up[2]; + uint32_t flags; + int reported_finger_count; + int8_t sensitivity_adjust; + int (*power)(int on); + struct early_suspend early_suspend; +}; + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void synaptics_ts_early_suspend(struct early_suspend *h); +static void synaptics_ts_late_resume(struct early_suspend *h); +#endif + +static int synaptics_init_panel(struct synaptics_ts_data *ts) +{ + int ret; + + ret = i2c_smbus_write_byte_data(ts->client, 0xff, 0x10); /* page select = 0x10 */ + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_write_byte_data failed for page select\n"); + goto err_page_select_failed; + } + ret = i2c_smbus_write_byte_data(ts->client, 0x41, 0x04); /* Set "No Clip Z" */ + if (ret < 0) + printk(KERN_ERR "i2c_smbus_write_byte_data failed for No Clip Z\n"); + + ret = i2c_smbus_write_byte_data(ts->client, 0x44, + ts->sensitivity_adjust); + if (ret < 0) + pr_err("synaptics_ts: failed to set Sensitivity Adjust\n"); + +err_page_select_failed: + ret = i2c_smbus_write_byte_data(ts->client, 0xff, 0x04); /* page select = 0x04 */ + if (ret < 0) + printk(KERN_ERR "i2c_smbus_write_byte_data failed for page select\n"); + ret = i2c_smbus_write_byte_data(ts->client, 0xf0, 0x81); /* normal operation, 80 reports per second */ + if (ret < 0) + printk(KERN_ERR "synaptics_ts_resume: i2c_smbus_write_byte_data failed\n"); + return ret; +} + +static void synaptics_ts_work_func(struct work_struct *work) +{ + int i; + int ret; + int bad_data = 0; + struct i2c_msg msg[2]; + uint8_t start_reg; + uint8_t buf[15]; + struct synaptics_ts_data *ts = container_of(work, struct synaptics_ts_data, work); + int buf_len = ts->has_relative_report ? 15 : 13; + + msg[0].addr = ts->client->addr; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = &start_reg; + start_reg = 0x00; + msg[1].addr = ts->client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = buf_len; + msg[1].buf = buf; + + /* printk("synaptics_ts_work_func\n"); */ + for (i = 0; i < ((ts->use_irq && !bad_data) ? 1 : 10); i++) { + ret = i2c_transfer(ts->client->adapter, msg, 2); + if (ret < 0) { + printk(KERN_ERR "synaptics_ts_work_func: i2c_transfer failed\n"); + bad_data = 1; + } else { + /* printk("synaptics_ts_work_func: %x %x %x %x %x %x" */ + /* " %x %x %x %x %x %x %x %x %x, ret %d\n", */ + /* buf[0], buf[1], buf[2], buf[3], */ + /* buf[4], buf[5], buf[6], buf[7], */ + /* buf[8], buf[9], buf[10], buf[11], */ + /* buf[12], buf[13], buf[14], ret); */ + if ((buf[buf_len - 1] & 0xc0) != 0x40) { + printk(KERN_WARNING "synaptics_ts_work_func:" + " bad read %x %x %x %x %x %x %x %x %x" + " %x %x %x %x %x %x, ret %d\n", + buf[0], buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6], buf[7], + buf[8], buf[9], buf[10], buf[11], + buf[12], buf[13], buf[14], ret); + if (bad_data) + synaptics_init_panel(ts); + bad_data = 1; + continue; + } + bad_data = 0; + if ((buf[buf_len - 1] & 1) == 0) { + /* printk("read %d coordinates\n", i); */ + break; + } else { + int pos[2][2]; + int f, a; + int base; + /* int x = buf[3] | (uint16_t)(buf[2] & 0x1f) << 8; */ + /* int y = buf[5] | (uint16_t)(buf[4] & 0x1f) << 8; */ + int z = buf[1]; + int w = buf[0] >> 4; + int finger = buf[0] & 7; + + /* int x2 = buf[3+6] | (uint16_t)(buf[2+6] & 0x1f) << 8; */ + /* int y2 = buf[5+6] | (uint16_t)(buf[4+6] & 0x1f) << 8; */ + /* int z2 = buf[1+6]; */ + /* int w2 = buf[0+6] >> 4; */ + /* int finger2 = buf[0+6] & 7; */ + + /* int dx = (int8_t)buf[12]; */ + /* int dy = (int8_t)buf[13]; */ + int finger2_pressed; + + /* printk("x %4d, y %4d, z %3d, w %2d, F %d, 2nd: x %4d, y %4d, z %3d, w %2d, F %d, dx %4d, dy %4d\n", */ + /* x, y, z, w, finger, */ + /* x2, y2, z2, w2, finger2, */ + /* dx, dy); */ + + base = 2; + for (f = 0; f < 2; f++) { + uint32_t flip_flag = SYNAPTICS_FLIP_X; + for (a = 0; a < 2; a++) { + int p = buf[base + 1]; + p |= (uint16_t)(buf[base] & 0x1f) << 8; + if (ts->flags & flip_flag) + p = ts->max[a] - p; + if (ts->flags & SYNAPTICS_SNAP_TO_INACTIVE_EDGE) { + if (ts->snap_state[f][a]) { + if (p <= ts->snap_down_off[a]) + p = ts->snap_down[a]; + else if (p >= ts->snap_up_off[a]) + p = ts->snap_up[a]; + else + ts->snap_state[f][a] = 0; + } else { + if (p <= ts->snap_down_on[a]) { + p = ts->snap_down[a]; + ts->snap_state[f][a] = 1; + } else if (p >= ts->snap_up_on[a]) { + p = ts->snap_up[a]; + ts->snap_state[f][a] = 1; + } + } + } + pos[f][a] = p; + base += 2; + flip_flag <<= 1; + } + base += 2; + if (ts->flags & SYNAPTICS_SWAP_XY) + swap(pos[f][0], pos[f][1]); + } + if (z) { + input_report_abs(ts->input_dev, ABS_X, pos[0][0]); + input_report_abs(ts->input_dev, ABS_Y, pos[0][1]); + } + input_report_abs(ts->input_dev, ABS_PRESSURE, z); + input_report_abs(ts->input_dev, ABS_TOOL_WIDTH, w); + input_report_key(ts->input_dev, BTN_TOUCH, finger); + finger2_pressed = finger > 1 && finger != 7; + input_report_key(ts->input_dev, BTN_2, finger2_pressed); + if (finger2_pressed) { + input_report_abs(ts->input_dev, ABS_HAT0X, pos[1][0]); + input_report_abs(ts->input_dev, ABS_HAT0Y, pos[1][1]); + } + + if (!finger) + z = 0; + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, z); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, w); + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, pos[0][0]); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, pos[0][1]); + input_mt_sync(ts->input_dev); + if (finger2_pressed) { + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, z); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, w); + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, pos[1][0]); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, pos[1][1]); + input_mt_sync(ts->input_dev); + } else if (ts->reported_finger_count > 1) { + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0); + input_mt_sync(ts->input_dev); + } + ts->reported_finger_count = finger; + input_sync(ts->input_dev); + } + } + } + if (ts->use_irq) + enable_irq(ts->client->irq); +} + +static enum hrtimer_restart synaptics_ts_timer_func(struct hrtimer *timer) +{ + struct synaptics_ts_data *ts = container_of(timer, struct synaptics_ts_data, timer); + /* printk("synaptics_ts_timer_func\n"); */ + + queue_work(synaptics_wq, &ts->work); + + hrtimer_start(&ts->timer, ktime_set(0, 12500000), HRTIMER_MODE_REL); + return HRTIMER_NORESTART; +} + +static irqreturn_t synaptics_ts_irq_handler(int irq, void *dev_id) +{ + struct synaptics_ts_data *ts = dev_id; + + /* printk("synaptics_ts_irq_handler\n"); */ + disable_irq_nosync(ts->client->irq); + queue_work(synaptics_wq, &ts->work); + return IRQ_HANDLED; +} + +static int synaptics_ts_probe( + struct i2c_client *client, const struct i2c_device_id *id) +{ + struct synaptics_ts_data *ts; + uint8_t buf0[4]; + uint8_t buf1[8]; + struct i2c_msg msg[2]; + int ret = 0; + uint16_t max_x, max_y; + int fuzz_x, fuzz_y, fuzz_p, fuzz_w; + struct synaptics_i2c_rmi_platform_data *pdata; + unsigned long irqflags; + int inactive_area_left; + int inactive_area_right; + int inactive_area_top; + int inactive_area_bottom; + int snap_left_on; + int snap_left_off; + int snap_right_on; + int snap_right_off; + int snap_top_on; + int snap_top_off; + int snap_bottom_on; + int snap_bottom_off; + uint32_t panel_version; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + printk(KERN_ERR "synaptics_ts_probe: need I2C_FUNC_I2C\n"); + ret = -ENODEV; + goto err_check_functionality_failed; + } + + ts = kzalloc(sizeof(*ts), GFP_KERNEL); + if (ts == NULL) { + ret = -ENOMEM; + goto err_alloc_data_failed; + } + INIT_WORK(&ts->work, synaptics_ts_work_func); + ts->client = client; + i2c_set_clientdata(client, ts); + pdata = client->dev.platform_data; + if (pdata) + ts->power = pdata->power; + if (ts->power) { + ret = ts->power(1); + if (ret < 0) { + printk(KERN_ERR "synaptics_ts_probe power on failed\n"); + goto err_power_failed; + } + } + + ret = i2c_smbus_write_byte_data(ts->client, 0xf4, 0x01); /* device command = reset */ + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_write_byte_data failed\n"); + /* fail? */ + } + { + int retry = 10; + while (retry-- > 0) { + ret = i2c_smbus_read_byte_data(ts->client, 0xe4); + if (ret >= 0) + break; + msleep(100); + } + } + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_read_byte_data failed\n"); + goto err_detect_failed; + } + printk(KERN_INFO "synaptics_ts_probe: Product Major Version %x\n", ret); + panel_version = ret << 8; + ret = i2c_smbus_read_byte_data(ts->client, 0xe5); + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_read_byte_data failed\n"); + goto err_detect_failed; + } + printk(KERN_INFO "synaptics_ts_probe: Product Minor Version %x\n", ret); + panel_version |= ret; + + ret = i2c_smbus_read_byte_data(ts->client, 0xe3); + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_read_byte_data failed\n"); + goto err_detect_failed; + } + printk(KERN_INFO "synaptics_ts_probe: product property %x\n", ret); + + if (pdata) { + while (pdata->version > panel_version) + pdata++; + ts->flags = pdata->flags; + ts->sensitivity_adjust = pdata->sensitivity_adjust; + irqflags = pdata->irqflags; + inactive_area_left = pdata->inactive_left; + inactive_area_right = pdata->inactive_right; + inactive_area_top = pdata->inactive_top; + inactive_area_bottom = pdata->inactive_bottom; + snap_left_on = pdata->snap_left_on; + snap_left_off = pdata->snap_left_off; + snap_right_on = pdata->snap_right_on; + snap_right_off = pdata->snap_right_off; + snap_top_on = pdata->snap_top_on; + snap_top_off = pdata->snap_top_off; + snap_bottom_on = pdata->snap_bottom_on; + snap_bottom_off = pdata->snap_bottom_off; + fuzz_x = pdata->fuzz_x; + fuzz_y = pdata->fuzz_y; + fuzz_p = pdata->fuzz_p; + fuzz_w = pdata->fuzz_w; + } else { + irqflags = 0; + inactive_area_left = 0; + inactive_area_right = 0; + inactive_area_top = 0; + inactive_area_bottom = 0; + snap_left_on = 0; + snap_left_off = 0; + snap_right_on = 0; + snap_right_off = 0; + snap_top_on = 0; + snap_top_off = 0; + snap_bottom_on = 0; + snap_bottom_off = 0; + fuzz_x = 0; + fuzz_y = 0; + fuzz_p = 0; + fuzz_w = 0; + } + + ret = i2c_smbus_read_byte_data(ts->client, 0xf0); + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_read_byte_data failed\n"); + goto err_detect_failed; + } + printk(KERN_INFO "synaptics_ts_probe: device control %x\n", ret); + + ret = i2c_smbus_read_byte_data(ts->client, 0xf1); + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_read_byte_data failed\n"); + goto err_detect_failed; + } + printk(KERN_INFO "synaptics_ts_probe: interrupt enable %x\n", ret); + + ret = i2c_smbus_write_byte_data(ts->client, 0xf1, 0); /* disable interrupt */ + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_write_byte_data failed\n"); + goto err_detect_failed; + } + + msg[0].addr = ts->client->addr; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = buf0; + buf0[0] = 0xe0; + msg[1].addr = ts->client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = 8; + msg[1].buf = buf1; + ret = i2c_transfer(ts->client->adapter, msg, 2); + if (ret < 0) { + printk(KERN_ERR "i2c_transfer failed\n"); + goto err_detect_failed; + } + printk(KERN_INFO "synaptics_ts_probe: 0xe0: %x %x %x %x %x %x %x %x\n", + buf1[0], buf1[1], buf1[2], buf1[3], + buf1[4], buf1[5], buf1[6], buf1[7]); + + ret = i2c_smbus_write_byte_data(ts->client, 0xff, 0x10); /* page select = 0x10 */ + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_write_byte_data failed for page select\n"); + goto err_detect_failed; + } + ret = i2c_smbus_read_word_data(ts->client, 0x02); + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_read_word_data failed\n"); + goto err_detect_failed; + } + ts->has_relative_report = !(ret & 0x100); + printk(KERN_INFO "synaptics_ts_probe: Sensor properties %x\n", ret); + ret = i2c_smbus_read_word_data(ts->client, 0x04); + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_read_word_data failed\n"); + goto err_detect_failed; + } + ts->max[0] = max_x = (ret >> 8 & 0xff) | ((ret & 0x1f) << 8); + ret = i2c_smbus_read_word_data(ts->client, 0x06); + if (ret < 0) { + printk(KERN_ERR "i2c_smbus_read_word_data failed\n"); + goto err_detect_failed; + } + ts->max[1] = max_y = (ret >> 8 & 0xff) | ((ret & 0x1f) << 8); + if (ts->flags & SYNAPTICS_SWAP_XY) + swap(max_x, max_y); + + ret = synaptics_init_panel(ts); /* will also switch back to page 0x04 */ + if (ret < 0) { + printk(KERN_ERR "synaptics_init_panel failed\n"); + goto err_detect_failed; + } + + ts->input_dev = input_allocate_device(); + if (ts->input_dev == NULL) { + ret = -ENOMEM; + printk(KERN_ERR "synaptics_ts_probe: Failed to allocate input device\n"); + goto err_input_dev_alloc_failed; + } + ts->input_dev->name = "synaptics-rmi-touchscreen"; + set_bit(EV_SYN, ts->input_dev->evbit); + set_bit(EV_KEY, ts->input_dev->evbit); + set_bit(BTN_TOUCH, ts->input_dev->keybit); + set_bit(BTN_2, ts->input_dev->keybit); + set_bit(EV_ABS, ts->input_dev->evbit); + inactive_area_left = inactive_area_left * max_x / 0x10000; + inactive_area_right = inactive_area_right * max_x / 0x10000; + inactive_area_top = inactive_area_top * max_y / 0x10000; + inactive_area_bottom = inactive_area_bottom * max_y / 0x10000; + snap_left_on = snap_left_on * max_x / 0x10000; + snap_left_off = snap_left_off * max_x / 0x10000; + snap_right_on = snap_right_on * max_x / 0x10000; + snap_right_off = snap_right_off * max_x / 0x10000; + snap_top_on = snap_top_on * max_y / 0x10000; + snap_top_off = snap_top_off * max_y / 0x10000; + snap_bottom_on = snap_bottom_on * max_y / 0x10000; + snap_bottom_off = snap_bottom_off * max_y / 0x10000; + fuzz_x = fuzz_x * max_x / 0x10000; + fuzz_y = fuzz_y * max_y / 0x10000; + ts->snap_down[!!(ts->flags & SYNAPTICS_SWAP_XY)] = -inactive_area_left; + ts->snap_up[!!(ts->flags & SYNAPTICS_SWAP_XY)] = max_x + inactive_area_right; + ts->snap_down[!(ts->flags & SYNAPTICS_SWAP_XY)] = -inactive_area_top; + ts->snap_up[!(ts->flags & SYNAPTICS_SWAP_XY)] = max_y + inactive_area_bottom; + ts->snap_down_on[!!(ts->flags & SYNAPTICS_SWAP_XY)] = snap_left_on; + ts->snap_down_off[!!(ts->flags & SYNAPTICS_SWAP_XY)] = snap_left_off; + ts->snap_up_on[!!(ts->flags & SYNAPTICS_SWAP_XY)] = max_x - snap_right_on; + ts->snap_up_off[!!(ts->flags & SYNAPTICS_SWAP_XY)] = max_x - snap_right_off; + ts->snap_down_on[!(ts->flags & SYNAPTICS_SWAP_XY)] = snap_top_on; + ts->snap_down_off[!(ts->flags & SYNAPTICS_SWAP_XY)] = snap_top_off; + ts->snap_up_on[!(ts->flags & SYNAPTICS_SWAP_XY)] = max_y - snap_bottom_on; + ts->snap_up_off[!(ts->flags & SYNAPTICS_SWAP_XY)] = max_y - snap_bottom_off; + printk(KERN_INFO "synaptics_ts_probe: max_x %d, max_y %d\n", max_x, max_y); + printk(KERN_INFO "synaptics_ts_probe: inactive_x %d %d, inactive_y %d %d\n", + inactive_area_left, inactive_area_right, + inactive_area_top, inactive_area_bottom); + printk(KERN_INFO "synaptics_ts_probe: snap_x %d-%d %d-%d, snap_y %d-%d %d-%d\n", + snap_left_on, snap_left_off, snap_right_on, snap_right_off, + snap_top_on, snap_top_off, snap_bottom_on, snap_bottom_off); + input_set_abs_params(ts->input_dev, ABS_X, -inactive_area_left, max_x + inactive_area_right, fuzz_x, 0); + input_set_abs_params(ts->input_dev, ABS_Y, -inactive_area_top, max_y + inactive_area_bottom, fuzz_y, 0); + input_set_abs_params(ts->input_dev, ABS_PRESSURE, 0, 255, fuzz_p, 0); + input_set_abs_params(ts->input_dev, ABS_TOOL_WIDTH, 0, 15, fuzz_w, 0); + input_set_abs_params(ts->input_dev, ABS_HAT0X, -inactive_area_left, max_x + inactive_area_right, fuzz_x, 0); + input_set_abs_params(ts->input_dev, ABS_HAT0Y, -inactive_area_top, max_y + inactive_area_bottom, fuzz_y, 0); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, -inactive_area_left, max_x + inactive_area_right, fuzz_x, 0); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, -inactive_area_top, max_y + inactive_area_bottom, fuzz_y, 0); + input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, fuzz_p, 0); + input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0, 15, fuzz_w, 0); + /* ts->input_dev->name = ts->keypad_info->name; */ + ret = input_register_device(ts->input_dev); + if (ret) { + printk(KERN_ERR "synaptics_ts_probe: Unable to register %s input device\n", ts->input_dev->name); + goto err_input_register_device_failed; + } + if (client->irq) { + ret = request_irq(client->irq, synaptics_ts_irq_handler, irqflags, client->name, ts); + if (ret == 0) { + ret = i2c_smbus_write_byte_data(ts->client, 0xf1, 0x01); /* enable abs int */ + if (ret) + free_irq(client->irq, ts); + } + if (ret == 0) + ts->use_irq = 1; + else + dev_err(&client->dev, "request_irq failed\n"); + } + if (!ts->use_irq) { + hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ts->timer.function = synaptics_ts_timer_func; + hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL); + } +#ifdef CONFIG_HAS_EARLYSUSPEND + ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + ts->early_suspend.suspend = synaptics_ts_early_suspend; + ts->early_suspend.resume = synaptics_ts_late_resume; + register_early_suspend(&ts->early_suspend); +#endif + + printk(KERN_INFO "synaptics_ts_probe: Start touchscreen %s in %s mode\n", ts->input_dev->name, ts->use_irq ? "interrupt" : "polling"); + + return 0; + +err_input_register_device_failed: + input_free_device(ts->input_dev); + +err_input_dev_alloc_failed: +err_detect_failed: +err_power_failed: + kfree(ts); +err_alloc_data_failed: +err_check_functionality_failed: + return ret; +} + +static int synaptics_ts_remove(struct i2c_client *client) +{ + struct synaptics_ts_data *ts = i2c_get_clientdata(client); + unregister_early_suspend(&ts->early_suspend); + if (ts->use_irq) + free_irq(client->irq, ts); + else + hrtimer_cancel(&ts->timer); + input_unregister_device(ts->input_dev); + kfree(ts); + return 0; +} + +static int synaptics_ts_suspend(struct i2c_client *client, pm_message_t mesg) +{ + int ret; + struct synaptics_ts_data *ts = i2c_get_clientdata(client); + + if (ts->use_irq) + disable_irq(client->irq); + else + hrtimer_cancel(&ts->timer); + ret = cancel_work_sync(&ts->work); + if (ret && ts->use_irq) /* if work was pending disable-count is now 2 */ + enable_irq(client->irq); + ret = i2c_smbus_write_byte_data(ts->client, 0xf1, 0); /* disable interrupt */ + if (ret < 0) + printk(KERN_ERR "synaptics_ts_suspend: i2c_smbus_write_byte_data failed\n"); + + ret = i2c_smbus_write_byte_data(client, 0xf0, 0x86); /* deep sleep */ + if (ret < 0) + printk(KERN_ERR "synaptics_ts_suspend: i2c_smbus_write_byte_data failed\n"); + if (ts->power) { + ret = ts->power(0); + if (ret < 0) + printk(KERN_ERR "synaptics_ts_resume power off failed\n"); + } + return 0; +} + +static int synaptics_ts_resume(struct i2c_client *client) +{ + int ret; + struct synaptics_ts_data *ts = i2c_get_clientdata(client); + + if (ts->power) { + ret = ts->power(1); + if (ret < 0) + printk(KERN_ERR "synaptics_ts_resume power on failed\n"); + } + + synaptics_init_panel(ts); + + if (ts->use_irq) + enable_irq(client->irq); + + if (!ts->use_irq) + hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL); + else + i2c_smbus_write_byte_data(ts->client, 0xf1, 0x01); /* enable abs int */ + + return 0; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void synaptics_ts_early_suspend(struct early_suspend *h) +{ + struct synaptics_ts_data *ts; + ts = container_of(h, struct synaptics_ts_data, early_suspend); + synaptics_ts_suspend(ts->client, PMSG_SUSPEND); +} + +static void synaptics_ts_late_resume(struct early_suspend *h) +{ + struct synaptics_ts_data *ts; + ts = container_of(h, struct synaptics_ts_data, early_suspend); + synaptics_ts_resume(ts->client); +} +#endif + +static const struct i2c_device_id synaptics_ts_id[] = { + { SYNAPTICS_I2C_RMI_NAME, 0 }, + { } +}; + +static struct i2c_driver synaptics_ts_driver = { + .probe = synaptics_ts_probe, + .remove = synaptics_ts_remove, +#ifndef CONFIG_HAS_EARLYSUSPEND + .suspend = synaptics_ts_suspend, + .resume = synaptics_ts_resume, +#endif + .id_table = synaptics_ts_id, + .driver = { + .name = SYNAPTICS_I2C_RMI_NAME, + }, +}; + +static int __devinit synaptics_ts_init(void) +{ + synaptics_wq = create_singlethread_workqueue("synaptics_wq"); + if (!synaptics_wq) + return -ENOMEM; + return i2c_add_driver(&synaptics_ts_driver); +} + +static void __exit synaptics_ts_exit(void) +{ + i2c_del_driver(&synaptics_ts_driver); + if (synaptics_wq) + destroy_workqueue(synaptics_wq); +} + +module_init(synaptics_ts_init); +module_exit(synaptics_ts_exit); + +MODULE_DESCRIPTION("Synaptics Touchscreen Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c b/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c index 11728a03f8a..2659ea1d70d 100644 --- a/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c +++ b/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c @@ -5,7 +5,7 @@ * * Author: Js HA for ST-Ericsson * Author: Naveen Kumar G for ST-Ericsson - * Copyright 2010 (c) ST-Ericsson AB + * Copyright 2010 (c) ST-Ericsson SA */ /* * This file is licensed under the GPL2 license. @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -36,8 +37,10 @@ /* TODO: for multiple device support will need a per-device mutex */ #define DRIVER_NAME "synaptics_rmi4_i2c" +#define DELTA 8 #define MAX_ERROR_REPORT 6 -#define MAX_TOUCH_MAJOR 15 +#define TIMEOUT_PERIOD 1 +#define MAX_WIDTH_MAJOR 255 #define MAX_RETRY_COUNT 5 #define STD_QUERY_LEN 21 #define PAGE_LEN 2 @@ -45,6 +48,7 @@ #define BUF_LEN 37 #define QUERY_LEN 9 #define DATA_LEN 12 +#define RESUME_DELAY 100 /* msecs */ #define HAS_TAP 0x01 #define HAS_PALMDETECT 0x01 #define HAS_ROTATE 0x02 @@ -164,6 +168,8 @@ struct synaptics_rmi4_device_info { * @regulator: pointer to the regulator structure * @wait: wait queue structure variable * @touch_stopped: flag to stop the thread function + * @enable: flag to enable/disable the driver event. + * @resume_wq_handler: work queue for resume the device * * This structure gives the device data information. */ @@ -184,6 +190,8 @@ struct synaptics_rmi4_data { struct regulator *regulator; wait_queue_head_t wait; bool touch_stopped; + bool enable; + struct work_struct resume_wq_handler; }; /** @@ -290,6 +298,133 @@ exit: return retval; } +/** + * synaptics_rmi4_enable() - enable the touchpad driver event + * @pdata: pointer to synaptics_rmi4_data structure + * + * This function is to enable the touchpad driver event and returns integer. + */ +static int synaptics_rmi4_enable(struct synaptics_rmi4_data *pdata) +{ + int retval; + unsigned char intr_status; + + if (pdata->board->regulator_en) + regulator_enable(pdata->regulator); + enable_irq(pdata->board->irq_number); + pdata->touch_stopped = false; + + msleep(RESUME_DELAY); + retval = synaptics_rmi4_i2c_block_read(pdata, + pdata->fn01_data_base_addr + 1, + &intr_status, + pdata->number_of_interrupt_register); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_i2c_byte_write(pdata, + pdata->fn01_ctrl_base_addr + 1, + (intr_status | TOUCHPAD_CTRL_INTR)); + if (retval < 0) + return retval; + + return 0; +} + +/** + * synaptics_rmi4_disable() - disable the touchpad driver event + * @pdata: pointer to synaptics_rmi4_data structure + * + * This function is to disable the driver event and returns integer. + */ + +static int synaptics_rmi4_disable(struct synaptics_rmi4_data *pdata) +{ + int retval; + unsigned char intr_status; + + pdata->touch_stopped = true; + disable_irq(pdata->board->irq_number); + + retval = synaptics_rmi4_i2c_block_read(pdata, + pdata->fn01_data_base_addr + 1, + &intr_status, + pdata->number_of_interrupt_register); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_i2c_byte_write(pdata, + pdata->fn01_ctrl_base_addr + 1, + (intr_status & ~TOUCHPAD_CTRL_INTR)); + if (retval < 0) + return retval; + if (pdata->board->regulator_en) + regulator_disable(pdata->regulator); + + return 0; +} + +/** + * synaptics_rmi4_show_attr_enable() - show the touchpad enable value + * @dev: pointer to device data structure + * @attr: pointer to attribute structure + * @buf: pointer to character buffer + * + * This function is to show the touchpad enable value and returns ssize_t. + */ +static ssize_t synaptics_rmi4_show_attr_enable(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *pdata = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", pdata->enable); +} + +/** + * synaptics_rmi4_store_attr_enable() - store the touchpad enable value + * @dev: pointer to device data structure + * @attr: pointer to attribute structure + * @buf: pointer to character buffer + * @count: number fo arguments + * + * This function is to store the touchpad enable value and returns ssize_t. + */ +static ssize_t synaptics_rmi4_store_attr_enable(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct synaptics_rmi4_data *pdata = dev_get_drvdata(dev); + unsigned long val; + int retval = 0; + + if (strict_strtoul(buf, 0, &val)) + return -EINVAL; + + if ((val != 0) && (val != 1)) + return -EINVAL; + + if (pdata->enable != val) { + pdata->enable = val ? true : false; + if (pdata->enable) + retval = synaptics_rmi4_enable(pdata); + else + retval = synaptics_rmi4_disable(pdata); + + } + return ((retval < 0) ? retval : count); +} + +static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO, + synaptics_rmi4_show_attr_enable, synaptics_rmi4_store_attr_enable); + +static struct attribute *synaptics_rmi4_attrs[] = { + &dev_attr_enable.attr, + NULL, +}; + +static struct attribute_group synaptics_rmi4_attr_group = { + .attrs = synaptics_rmi4_attrs, +}; + /** * synpatics_rmi4_touchpad_report() - reports for the rmi4 touchpad device * @pdata: pointer to synaptics_rmi4_data structure @@ -316,8 +451,9 @@ static int synpatics_rmi4_touchpad_report(struct synaptics_rmi4_data *pdata, unsigned char data[DATA_LEN]; int x[RMI4_NUMBER_OF_MAX_FINGERS]; int y[RMI4_NUMBER_OF_MAX_FINGERS]; - int wx[RMI4_NUMBER_OF_MAX_FINGERS]; - int wy[RMI4_NUMBER_OF_MAX_FINGERS]; + int w[RMI4_NUMBER_OF_MAX_FINGERS]; + static int prv_x[RMI4_NUMBER_OF_MAX_FINGERS]; + static int prv_y[RMI4_NUMBER_OF_MAX_FINGERS]; struct i2c_client *client = pdata->i2c_client; /* get 2D sensor finger data */ @@ -376,11 +512,7 @@ static int synpatics_rmi4_touchpad_report(struct synaptics_rmi4_data *pdata, y[touch_count] = (data[1] << 4) | ((data[2] >> 4) & MASK_4BIT); - wy[touch_count] = - (data[3] >> 4) & MASK_4BIT; - wx[touch_count] = - (data[3] & MASK_4BIT); - + w[touch_count] = data[3]; if (pdata->board->x_flip) x[touch_count] = pdata->sensor_max_x - @@ -389,6 +521,25 @@ static int synpatics_rmi4_touchpad_report(struct synaptics_rmi4_data *pdata, y[touch_count] = pdata->sensor_max_y - y[touch_count]; + if (x[touch_count] < 0) + x[touch_count] = 0; + else if (x[touch_count] >= pdata->sensor_max_x) + x[touch_count] = + pdata->sensor_max_x - 1; + + if (y[touch_count] < 0) + y[touch_count] = 0; + else if (y[touch_count] >= pdata->sensor_max_y) + y[touch_count] = + pdata->sensor_max_y - 1; + } + if ((abs(x[finger] - prv_x[finger]) < DELTA) && + (abs(y[finger] - prv_y[finger]) < DELTA)) { + x[finger] = prv_x[finger]; + y[finger] = prv_y[finger]; + } else { + prv_x[finger] = x[finger]; + prv_y[finger] = y[finger]; } /* number of active touch points */ touch_count++; @@ -399,7 +550,9 @@ static int synpatics_rmi4_touchpad_report(struct synaptics_rmi4_data *pdata, if (touch_count) { for (finger = 0; finger < touch_count; finger++) { input_report_abs(pdata->input_dev, ABS_MT_TOUCH_MAJOR, - max(wx[finger] , wy[finger])); + max(x[finger] , y[finger])); + input_report_abs(pdata->input_dev, ABS_MT_WIDTH_MAJOR, + w[finger]); input_report_abs(pdata->input_dev, ABS_MT_POSITION_X, x[finger]); input_report_abs(pdata->input_dev, ABS_MT_POSITION_Y, @@ -502,7 +655,7 @@ static irqreturn_t synaptics_rmi4_irq(int irq, void *data) touch_count = synaptics_rmi4_sensor_report(pdata); if (touch_count) wait_event_timeout(pdata->wait, pdata->touch_stopped, - msecs_to_jiffies(1)); + msecs_to_jiffies(TIMEOUT_PERIOD)); else break; } while (!pdata->touch_stopped); @@ -880,6 +1033,24 @@ static int synaptics_rmi4_i2c_query_device(struct synaptics_rmi4_data *pdata) return 0; } +/** + * synaptics_rmi4_resume_handler() - work queue for resume handler + * @work:work_struct structure pointer + * + * This work queue handler used to resume the device and returns none + */ +static void synaptics_rmi4_resume_handler(struct work_struct *work) +{ + struct synaptics_rmi4_data *prmi4_data = container_of(work, + struct synaptics_rmi4_data, resume_wq_handler); + struct i2c_client *client = prmi4_data->i2c_client; + int retval; + + retval = synaptics_rmi4_enable(prmi4_data); + if (retval < 0) + dev_err(&client->dev, "%s: resume failed\n", __func__); +} + /** * synaptics_rmi4_probe() - Initialze the i2c-client touchscreen driver * @i2c: i2c client structure pointer @@ -927,19 +1098,17 @@ static int __devinit synaptics_rmi4_probe goto err_input; } - rmi4_data->regulator = regulator_get(&client->dev, "vdd"); - if (IS_ERR(rmi4_data->regulator)) { - dev_err(&client->dev, "%s:get regulator failed\n", - __func__); - retval = PTR_ERR(rmi4_data->regulator); - goto err_get_regulator; - } - retval = regulator_enable(rmi4_data->regulator); - if (retval < 0) { - dev_err(&client->dev, "%s:regulator enable failed\n", - __func__); - goto err_regulator_enable; + if (platformdata->regulator_en) { + rmi4_data->regulator = regulator_get(&client->dev, "vdd"); + if (IS_ERR(rmi4_data->regulator)) { + dev_err(&client->dev, "%s:get regulator failed\n", + __func__); + retval = PTR_ERR(rmi4_data->regulator); + goto err_regulator; + } + regulator_enable(rmi4_data->regulator); } + init_waitqueue_head(&rmi4_data->wait); /* * Copy i2c_client pointer into RTID's i2c_client pointer for @@ -987,7 +1156,16 @@ static int __devinit synaptics_rmi4_probe input_set_abs_params(rmi4_data->input_dev, ABS_MT_POSITION_Y, 0, rmi4_data->sensor_max_y, 0, 0); input_set_abs_params(rmi4_data->input_dev, ABS_MT_TOUCH_MAJOR, 0, - MAX_TOUCH_MAJOR, 0, 0); + max(rmi4_data->sensor_max_y, rmi4_data->sensor_max_y), + 0, 0); + input_set_abs_params(rmi4_data->input_dev, ABS_MT_WIDTH_MAJOR, 0, + MAX_WIDTH_MAJOR, 0, 0); + + retval = input_register_device(rmi4_data->input_dev); + if (retval) { + dev_err(&client->dev, "%s:input register failed\n", __func__); + goto err_input_register; + } /* Clear interrupts */ synaptics_rmi4_i2c_block_read(rmi4_data, @@ -1000,24 +1178,34 @@ static int __devinit synaptics_rmi4_probe if (retval) { dev_err(&client->dev, "%s:Unable to get attn irq %d\n", __func__, platformdata->irq_number); - goto err_query_dev; + goto err_request_irq; } - retval = input_register_device(rmi4_data->input_dev); + INIT_WORK(&rmi4_data->resume_wq_handler, synaptics_rmi4_resume_handler); + + /* sysfs implementation for dynamic enable/disable the input event */ + retval = sysfs_create_group(&client->dev.kobj, + &synaptics_rmi4_attr_group); if (retval) { - dev_err(&client->dev, "%s:input register failed\n", __func__); - goto err_free_irq; + dev_err(&client->dev, "failed to create sysfs entries\n"); + goto err_sysfs; } - + rmi4_data->enable = true; return retval; -err_free_irq: +err_sysfs: + cancel_work_sync(&rmi4_data->resume_wq_handler); +err_request_irq: free_irq(platformdata->irq_number, rmi4_data); + input_unregister_device(rmi4_data->input_dev); +err_input_register: + i2c_set_clientdata(client, NULL); err_query_dev: - regulator_disable(rmi4_data->regulator); -err_regulator_enable: - regulator_put(rmi4_data->regulator); -err_get_regulator: + if (platformdata->regulator_en) { + regulator_disable(rmi4_data->regulator); + regulator_put(rmi4_data->regulator); + } +err_regulator: input_free_device(rmi4_data->input_dev); rmi4_data->input_dev = NULL; err_input: @@ -1037,12 +1225,16 @@ static int __devexit synaptics_rmi4_remove(struct i2c_client *client) struct synaptics_rmi4_data *rmi4_data = i2c_get_clientdata(client); const struct synaptics_rmi4_platform_data *pdata = rmi4_data->board; + sysfs_remove_group(&client->dev.kobj, &synaptics_rmi4_attr_group); rmi4_data->touch_stopped = true; wake_up(&rmi4_data->wait); + cancel_work_sync(&rmi4_data->resume_wq_handler); free_irq(pdata->irq_number, rmi4_data); input_unregister_device(rmi4_data->input_dev); - regulator_disable(rmi4_data->regulator); - regulator_put(rmi4_data->regulator); + if (pdata->regulator_en) { + regulator_disable(rmi4_data->regulator); + regulator_put(rmi4_data->regulator); + } kfree(rmi4_data); return 0; @@ -1059,31 +1251,11 @@ static int __devexit synaptics_rmi4_remove(struct i2c_client *client) static int synaptics_rmi4_suspend(struct device *dev) { /* Touch sleep mode */ - int retval; - unsigned char intr_status; struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); - const struct synaptics_rmi4_platform_data *pdata = rmi4_data->board; - - rmi4_data->touch_stopped = true; - disable_irq(pdata->irq_number); - - retval = synaptics_rmi4_i2c_block_read(rmi4_data, - rmi4_data->fn01_data_base_addr + 1, - &intr_status, - rmi4_data->number_of_interrupt_register); - if (retval < 0) - return retval; - - retval = synaptics_rmi4_i2c_byte_write(rmi4_data, - rmi4_data->fn01_ctrl_base_addr + 1, - (intr_status & ~TOUCHPAD_CTRL_INTR)); - if (retval < 0) - return retval; - - regulator_disable(rmi4_data->regulator); - return 0; + return synaptics_rmi4_disable(rmi4_data); } + /** * synaptics_rmi4_resume() - resume the touch screen controller * @dev: pointer to device structure @@ -1093,28 +1265,9 @@ static int synaptics_rmi4_suspend(struct device *dev) */ static int synaptics_rmi4_resume(struct device *dev) { - int retval; - unsigned char intr_status; struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); - const struct synaptics_rmi4_platform_data *pdata = rmi4_data->board; - - regulator_enable(rmi4_data->regulator); - enable_irq(pdata->irq_number); - rmi4_data->touch_stopped = false; - - retval = synaptics_rmi4_i2c_block_read(rmi4_data, - rmi4_data->fn01_data_base_addr + 1, - &intr_status, - rmi4_data->number_of_interrupt_register); - if (retval < 0) - return retval; - - retval = synaptics_rmi4_i2c_byte_write(rmi4_data, - rmi4_data->fn01_ctrl_base_addr + 1, - (intr_status | TOUCHPAD_CTRL_INTR)); - if (retval < 0) - return retval; + schedule_work(&rmi4_data->resume_wq_handler); return 0; } -- cgit v1.2.3 From d88f7ec236cad14e92e0c9e7ff3ded7b431f4d4d 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 (limited to 'drivers') 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 615c21f2a55..910e46c4502 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 ddde0fd476f..c2d3e9c0aeb 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 f42720c9c9803c2432a6366a0921ee073526f450 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(-) (limited to 'drivers') diff --git a/drivers/input/misc/ab8500-ponkey.c b/drivers/input/misc/ab8500-ponkey.c index 3d3288a78fd..1cef5e670ca 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 7743c386ad2ac42ae3a3419dffed4a236ef80b30 Mon Sep 17 00:00:00 2001 From: Naveen Kumar Gaddipati Date: Tue, 5 Oct 2010 10:31:54 +0530 Subject: input: Dynamic enable or disable of SKE keypad events Added support for dynamic enable or disable of SKE keypad events by using sysfs commands. Enable SKE keypad event: echo 1 >/sys/devices/platform/nmk-ske-keypad/enable Disable SKE keypad event: echo 0 >/sys/devices/platform/nmk-ske-keypad/enable ST-Ericsson Id: WP 273474 Change-Id:Ia7c98d67609129ee661eec2b3f036bc24402b166 Signed-off-by: Naveen Kumar Gaddipati Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/6202 Reviewed-by: Linus WALLEIJ --- drivers/input/keyboard/nomadik-ske-keypad.c | 198 ++++++++++++++++++++-------- 1 file changed, 140 insertions(+), 58 deletions(-) (limited to 'drivers') diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c index fcdec5e2b29..42d44b5904c 100644 --- a/drivers/input/keyboard/nomadik-ske-keypad.c +++ b/drivers/input/keyboard/nomadik-ske-keypad.c @@ -57,6 +57,7 @@ * @board: keypad platform device * @keymap: matrix scan code table for keycodes * @clk: clock structure pointer + * @enable: flag to enable the driver event */ struct ske_keypad { int irq; @@ -66,6 +67,7 @@ struct ske_keypad { unsigned short keymap[SKE_KPD_KEYMAP_SIZE]; struct clk *clk; spinlock_t ske_keypad_lock; + bool enable; }; static void ske_keypad_set_bits(struct ske_keypad *keypad, u16 addr, @@ -83,6 +85,54 @@ static void ske_keypad_set_bits(struct ske_keypad *keypad, u16 addr, spin_unlock(&keypad->ske_keypad_lock); } +static ssize_t ske_show_attr_enable(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct ske_keypad *keypad = platform_get_drvdata(pdev); + return sprintf(buf, "%d\n", keypad->enable); +} + +static ssize_t ske_store_attr_enable(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct ske_keypad *keypad = platform_get_drvdata(pdev); + unsigned long val; + + if (strict_strtoul(buf, 0, &val)) + return -EINVAL; + + if ((val != 0) && (val != 1)) + return -EINVAL; + + if (keypad->enable != val) { + keypad->enable = val ? true : false; + if (!keypad->enable) { + disable_irq(keypad->irq); + ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0); + clk_disable(keypad->clk); + } else { + clk_enable(keypad->clk); + enable_irq(keypad->irq); + ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA); + } + } + return count; +} + +static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO, + ske_show_attr_enable, ske_store_attr_enable); + +static struct attribute *ske_keypad_attrs[] = { + &dev_attr_enable.attr, + NULL, +}; + +static struct attribute_group ske_attr_group = { + .attrs = ske_keypad_attrs, +}; + /* * ske_keypad_chip_init: init keypad controller configuration * @@ -91,7 +141,7 @@ static void ske_keypad_set_bits(struct ske_keypad *keypad, u16 addr, static int __devinit ske_keypad_chip_init(struct ske_keypad *keypad) { u32 value; - int timeout = 50; + int timeout = keypad->board->debounce_ms; /* check SKE_RIS to be 0 */ while ((readl(keypad->reg_base + SKE_RIS) != 0x00000000) && timeout--) @@ -200,12 +250,14 @@ static irqreturn_t ske_keypad_irq(int irq, void *dev_id) static int __devinit ske_keypad_probe(struct platform_device *pdev) { - const struct ske_keypad_platform_data *plat = pdev->dev.platform_data; struct ske_keypad *keypad; + struct resource *res = NULL; struct input_dev *input; - struct resource *res; + struct clk *clk; + void __iomem *reg_base; + int ret = 0; int irq; - int error; + struct ske_keypad_platform_data *plat = pdev->dev.platform_data; if (!plat) { dev_err(&pdev->dev, "invalid keypad platform data\n"); @@ -219,42 +271,43 @@ static int __devinit ske_keypad_probe(struct platform_device *pdev) } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { + if (res == NULL) { dev_err(&pdev->dev, "missing platform resources\n"); - return -EINVAL; + return -ENXIO; } - keypad = kzalloc(sizeof(struct ske_keypad), GFP_KERNEL); - input = input_allocate_device(); - if (!keypad || !input) { - dev_err(&pdev->dev, "failed to allocate keypad memory\n"); - error = -ENOMEM; - goto err_free_mem; + res = request_mem_region(res->start, resource_size(res), pdev->name); + if (!res) { + dev_err(&pdev->dev, "failed to request I/O memory\n"); + return -EBUSY; } - keypad->irq = irq; - keypad->board = plat; - keypad->input = input; - spin_lock_init(&keypad->ske_keypad_lock); + reg_base = ioremap(res->start, resource_size(res)); + if (!reg_base) { + dev_err(&pdev->dev, "failed to remap I/O memory\n"); + ret = -ENXIO; + goto out_freerequest_memregions; + } - if (!request_mem_region(res->start, resource_size(res), pdev->name)) { - dev_err(&pdev->dev, "failed to request I/O memory\n"); - error = -EBUSY; - goto err_free_mem; + clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "failed to clk_get\n"); + ret = PTR_ERR(clk); + goto out_freeioremap; } - keypad->reg_base = ioremap(res->start, resource_size(res)); - if (!keypad->reg_base) { - dev_err(&pdev->dev, "failed to remap I/O memory\n"); - error = -ENXIO; - goto err_free_mem_region; + /* resources are sane; we begin allocation */ + keypad = kzalloc(sizeof(struct ske_keypad), GFP_KERNEL); + if (!keypad) { + dev_err(&pdev->dev, "failed to allocate keypad memory\n"); + goto out_freeclk; } - keypad->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(keypad->clk)) { - dev_err(&pdev->dev, "failed to get clk\n"); - error = PTR_ERR(keypad->clk); - goto err_iounmap; + input = input_allocate_device(); + if (!input) { + dev_err(&pdev->dev, "failed to input_allocate_device\n"); + ret = -ENOMEM; + goto out_freekeypad; } input->id.bustype = BUS_HOST; @@ -266,60 +319,88 @@ static int __devinit ske_keypad_probe(struct platform_device *pdev) input->keycodemax = ARRAY_SIZE(keypad->keymap); input_set_capability(input, EV_MSC, MSC_SCAN); + input_set_drvdata(input, keypad); __set_bit(EV_KEY, input->evbit); if (!plat->no_autorepeat) __set_bit(EV_REP, input->evbit); matrix_keypad_build_keymap(plat->keymap_data, SKE_KEYPAD_ROW_SHIFT, - input->keycode, input->keybit); + input->keycode, input->keybit); + ret = input_register_device(input); + if (ret) { + dev_err(&pdev->dev, + "unable to register input device: %d\n", ret); + goto out_freeinput; + } + + keypad->board = plat; + keypad->irq = irq; + keypad->board = plat; + keypad->input = input; + keypad->reg_base = reg_base; + keypad->clk = clk; + keypad->enable = true; + + /* allocations are sane, we begin HW initialization */ clk_enable(keypad->clk); - /* go through board initialization helpers */ - if (keypad->board->init) - keypad->board->init(); + if (!keypad->board->init) { + dev_err(&pdev->dev, "NULL board initialization helper\n"); + ret = -EINVAL; + goto out_unregisterinput; + } + + if (keypad->board->init() < 0) { + dev_err(&pdev->dev, "unable to set keypad board config\n"); + ret = -EINVAL; + goto out_unregisterinput; + } - error = ske_keypad_chip_init(keypad); - if (error) { + ret = ske_keypad_chip_init(keypad); + if (ret < 0) { dev_err(&pdev->dev, "unable to init keypad hardware\n"); - goto err_clk_disable; + goto out_unregisterinput; } - error = request_threaded_irq(keypad->irq, NULL, ske_keypad_irq, - IRQF_ONESHOT, "ske-keypad", keypad); - if (error) { + ret = request_threaded_irq(keypad->irq, NULL, ske_keypad_irq, + IRQF_ONESHOT, "ske-keypad", keypad); + if (ret) { dev_err(&pdev->dev, "allocate irq %d failed\n", keypad->irq); - goto err_clk_disable; + goto out_unregisterinput; } - error = input_register_device(input); - if (error) { - dev_err(&pdev->dev, - "unable to register input device: %d\n", error); - goto err_free_irq; + /* sysfs implementation for dynamic enable/disable the input event */ + ret = sysfs_create_group(&pdev->dev.kobj, &ske_attr_group); + if (ret) { + dev_err(&pdev->dev, "failed to create sysfs entries\n"); + goto out_free_irq; } - if (plat->wakeup_enable) - device_init_wakeup(&pdev->dev, true); + device_init_wakeup(&pdev->dev, true); platform_set_drvdata(pdev, keypad); return 0; -err_free_irq: +out_free_irq: free_irq(keypad->irq, keypad); -err_clk_disable: +out_unregisterinput: + input_unregister_device(input); + input = NULL; clk_disable(keypad->clk); - clk_put(keypad->clk); -err_iounmap: - iounmap(keypad->reg_base); -err_free_mem_region: - release_mem_region(res->start, resource_size(res)); -err_free_mem: +out_freeinput: input_free_device(input); +out_freekeypad: kfree(keypad); - return error; +out_freeclk: + clk_put(keypad->clk); +out_freeioremap: + iounmap(reg_base); +out_freerequest_memregions: + release_mem_region(res->start, resource_size(res)); + return ret; } static int __devexit ske_keypad_remove(struct platform_device *pdev) @@ -331,6 +412,7 @@ static int __devexit ske_keypad_remove(struct platform_device *pdev) input_unregister_device(keypad->input); + sysfs_remove_group(&pdev->dev.kobj, &ske_attr_group); clk_disable(keypad->clk); clk_put(keypad->clk); @@ -393,7 +475,7 @@ struct platform_driver ske_keypad_driver = { static int __init ske_keypad_init(void) { - return platform_driver_probe(&ske_keypad_driver, ske_keypad_probe); + return platform_driver_register(&ske_keypad_driver); } module_init(ske_keypad_init); -- cgit v1.2.3 From 9ffac40a6a01dd753d163a7dfb9bfdd70a421ee7 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(+) (limited to 'drivers') diff --git a/drivers/input/keyboard/stmpe-keypad.c b/drivers/input/keyboard/stmpe-keypad.c index ab7610ca10e..f386e07cf31 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 741b62e056dae50d5e48d432266c9bae12f282c9 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(-) (limited to 'drivers') diff --git a/drivers/input/keyboard/stmpe-keypad.c b/drivers/input/keyboard/stmpe-keypad.c index f386e07cf31..a01a18f5735 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 2ba1c9bc6e04e9c1396e902913476eebb5900199 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(-) (limited to 'drivers') diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index c2d3e9c0aeb..e675356acb2 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -31,7 +31,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 42d44b5904c..381e04d969b 100644 --- a/drivers/input/keyboard/nomadik-ske-keypad.c +++ b/drivers/input/keyboard/nomadik-ske-keypad.c @@ -184,12 +184,37 @@ static int __devinit ske_keypad_chip_init(struct ske_keypad *keypad) return 0; } -static void ske_keypad_read_data(struct ske_keypad *keypad) +static void ske_keypad_report(struct ske_keypad *keypad, u8 status, int col) { + int row = 0, code, pos; struct input_dev *input = keypad->input; - u16 status; - int col = 0, row = 0, code; - int ske_asr, ske_ris, key_pressed, i; + u32 ske_ris; + int key_pressed; + int num_of_rows; + + /* find out the row */ + num_of_rows = hweight8(status); + do { + pos = __ffs(status); + row = pos; + status &= ~(1 << pos); + + code = MATRIX_SCAN_CODE(row, col, SKE_KEYPAD_ROW_SHIFT); + ske_ris = readl(keypad->reg_base + SKE_RIS); + key_pressed = ske_ris & SKE_KPRISA; + + input_event(input, EV_MSC, MSC_SCAN, code); + input_report_key(input, keypad->keymap[code], key_pressed); + input_sync(input); + num_of_rows--; + } while (num_of_rows); +} + +static void ske_keypad_read_data(struct ske_keypad *keypad) +{ + u8 status; + int col = 0; + int ske_asr, i; /* * Read the auto scan registers @@ -203,25 +228,17 @@ static void ske_keypad_read_data(struct ske_keypad *keypad) if (!ske_asr) continue; - /* now that ASRx is zero, find out the column x and row y*/ - if (ske_asr & 0xff) { + /* now that ASRx is zero, find out the coloumn x and row y */ + status = ske_asr & 0xff; + if (status) { col = i * 2; - status = ske_asr & 0xff; - } else { + ske_keypad_report(keypad, status, col); + } + status = (ske_asr & 0xff00) >> 8; + if (status) { col = (i * 2) + 1; - status = (ske_asr & 0xff00) >> 8; + ske_keypad_report(keypad, status, col); } - - /* find out the row */ - row = __ffs(status); - - code = MATRIX_SCAN_CODE(row, col, SKE_KEYPAD_ROW_SHIFT); - ske_ris = readl(keypad->reg_base + SKE_RIS); - key_pressed = ske_ris & SKE_KPRISA; - - input_event(input, EV_MSC, MSC_SCAN, code); - input_report_key(input, keypad->keymap[code], key_pressed); - input_sync(input); } } @@ -235,12 +252,10 @@ static irqreturn_t ske_keypad_irq(int irq, void *dev_id) ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA); while ((readl(keypad->reg_base + SKE_CR) & SKE_KPASON) && --retries) - msleep(5); + cpu_relax(); - if (retries) { - /* SKEx registers are stable and can be read */ - ske_keypad_read_data(keypad); - } + /* SKEx registers are stable and can be read */ + ske_keypad_read_data(keypad); /* enable auto scan interrupts */ ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA); -- cgit v1.2.3 From 33b1b7628ea3db7f4865ce33ae7167cd404698f4 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(-) (limited to 'drivers') diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c index 381e04d969b..589689548ee 100644 --- a/drivers/input/keyboard/nomadik-ske-keypad.c +++ b/drivers/input/keyboard/nomadik-ske-keypad.c @@ -393,7 +393,8 @@ static int __devinit ske_keypad_probe(struct platform_device *pdev) goto out_free_irq; } - device_init_wakeup(&pdev->dev, true); + if (plat->wakeup_enable) + device_init_wakeup(&pdev->dev, true); platform_set_drvdata(pdev, keypad); @@ -450,8 +451,11 @@ static int ske_keypad_suspend(struct device *dev) if (device_may_wakeup(dev)) enable_irq_wake(irq); - else + else { + disable_irq(keypad->irq); ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0); + clk_disable(keypad->clk); + } return 0; } @@ -464,8 +468,11 @@ static int ske_keypad_resume(struct device *dev) if (device_may_wakeup(dev)) disable_irq_wake(irq); - else - ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA); + else { + clk_enable(keypad->clk); + enable_irq(keypad->irq); + ske_keypad_chip_init(keypad); + } return 0; } -- cgit v1.2.3 From 3479453e1479cffa60d803e1879cac50ae53a8ac 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(-) (limited to 'drivers') diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c index 589689548ee..db8f94b2413 100644 --- a/drivers/input/keyboard/nomadik-ske-keypad.c +++ b/drivers/input/keyboard/nomadik-ske-keypad.c @@ -245,18 +245,21 @@ static void ske_keypad_read_data(struct ske_keypad *keypad) static irqreturn_t ske_keypad_irq(int irq, void *dev_id) { struct ske_keypad *keypad = dev_id; - int retries = 20; /* disable auto scan interrupt; mask the interrupt generated */ ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0); ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA); - while ((readl(keypad->reg_base + SKE_CR) & SKE_KPASON) && --retries) + while (readl(keypad->reg_base + SKE_CR) & SKE_KPASON) cpu_relax(); /* SKEx registers are stable and can be read */ ske_keypad_read_data(keypad); + /* wait one debounce ms */ + while (readl(keypad->reg_base + SKE_RIS)) + cpu_relax(); + /* enable auto scan interrupts */ ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA); @@ -350,7 +353,6 @@ static int __devinit ske_keypad_probe(struct platform_device *pdev) goto out_freeinput; } - keypad->board = plat; keypad->irq = irq; keypad->board = plat; keypad->input = input; -- cgit v1.2.3 From 2c005ccfdc8aad177bb82a4205a99d36c36c82bd 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(-) (limited to 'drivers') diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c index db8f94b2413..850b346b01c 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 a7f050c43f00ac2bbb4f29de474c184d891edd49 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(-) (limited to 'drivers') diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c index 850b346b01c..f1743ca87ed 100644 --- a/drivers/input/keyboard/nomadik-ske-keypad.c +++ b/drivers/input/keyboard/nomadik-ske-keypad.c @@ -19,6 +19,7 @@ #include #include #include +#include #include @@ -59,6 +60,7 @@ * @keymap: matrix scan code table for keycodes * @clk: clock structure pointer * @enable: flag to enable the driver event + * @regulator: pointer to the regulator used for ske kyepad */ struct ske_keypad { int irq; @@ -69,6 +71,7 @@ struct ske_keypad { struct clk *clk; spinlock_t ske_keypad_lock; bool enable; + struct regulator *regulator; }; static void ske_keypad_set_bits(struct ske_keypad *keypad, u16 addr, @@ -86,54 +89,6 @@ static void ske_keypad_set_bits(struct ske_keypad *keypad, u16 addr, spin_unlock(&keypad->ske_keypad_lock); } -static ssize_t ske_show_attr_enable(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct platform_device *pdev = to_platform_device(dev); - struct ske_keypad *keypad = platform_get_drvdata(pdev); - return sprintf(buf, "%d\n", keypad->enable); -} - -static ssize_t ske_store_attr_enable(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - struct platform_device *pdev = to_platform_device(dev); - struct ske_keypad *keypad = platform_get_drvdata(pdev); - unsigned long val; - - if (strict_strtoul(buf, 0, &val)) - return -EINVAL; - - if ((val != 0) && (val != 1)) - return -EINVAL; - - if (keypad->enable != val) { - keypad->enable = val ? true : false; - if (!keypad->enable) { - disable_irq(keypad->irq); - ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0); - clk_disable(keypad->clk); - } else { - clk_enable(keypad->clk); - enable_irq(keypad->irq); - ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA); - } - } - return count; -} - -static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO, - ske_show_attr_enable, ske_store_attr_enable); - -static struct attribute *ske_keypad_attrs[] = { - &dev_attr_enable.attr, - NULL, -}; - -static struct attribute_group ske_attr_group = { - .attrs = ske_keypad_attrs, -}; - /* * ske_keypad_chip_init: init keypad controller configuration * @@ -185,6 +140,61 @@ static int __devinit ske_keypad_chip_init(struct ske_keypad *keypad) return 0; } +static void ske_enable(struct ske_keypad *keypad) +{ + if (keypad->enable) { + regulator_enable(keypad->regulator); + clk_enable(keypad->clk); + enable_irq(keypad->irq); + ske_keypad_chip_init(keypad); + } else { + disable_irq(keypad->irq); + ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0); + clk_disable(keypad->clk); + regulator_disable(keypad->regulator); + } +} + +static ssize_t ske_show_attr_enable(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct ske_keypad *keypad = platform_get_drvdata(pdev); + return sprintf(buf, "%d\n", keypad->enable); +} + +static ssize_t ske_store_attr_enable(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct ske_keypad *keypad = platform_get_drvdata(pdev); + unsigned long val; + + if (strict_strtoul(buf, 0, &val)) + return -EINVAL; + + if ((val != 0) && (val != 1)) + return -EINVAL; + + if (keypad->enable != val) { + keypad->enable = val ? true : false; + ske_enable(keypad); + } + return count; +} + +static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO, + ske_show_attr_enable, ske_store_attr_enable); + +static struct attribute *ske_keypad_attrs[] = { + &dev_attr_enable.attr, + NULL, +}; + +static struct attribute_group ske_attr_group = { + .attrs = ske_keypad_attrs, +}; + static void ske_keypad_report(struct ske_keypad *keypad, u8 status, int col) { int row = 0, code, pos; @@ -330,6 +340,19 @@ static int __devinit ske_keypad_probe(struct platform_device *pdev) goto out_freekeypad; } + keypad->regulator = regulator_get(&pdev->dev, "v-ape"); + if (IS_ERR(keypad->regulator)) { + dev_err(&pdev->dev, "regulator_get failed\n"); + keypad->regulator = NULL; + goto out_regulator_get; + } else { + ret = regulator_enable(keypad->regulator); + if (ret < 0) { + dev_err(&pdev->dev, "regulator_enable failed\n"); + goto out_regulator_enable; + } + } + input->id.bustype = BUS_HOST; input->name = "ux500-ske-keypad"; input->dev.parent = &pdev->dev; @@ -411,6 +434,10 @@ out_unregisterinput: input = NULL; clk_disable(keypad->clk); out_freeinput: + regulator_disable(keypad->regulator); +out_regulator_enable: + regulator_put(keypad->regulator); +out_regulator_get: input_free_device(input); out_freekeypad: kfree(keypad); @@ -439,6 +466,9 @@ static int __devexit ske_keypad_remove(struct platform_device *pdev) if (keypad->board->exit) keypad->board->exit(); + regulator_disable(keypad->regulator); + regulator_put(keypad->regulator); + iounmap(keypad->reg_base); release_mem_region(res->start, resource_size(res)); kfree(keypad); @@ -455,10 +485,9 @@ static int ske_keypad_suspend(struct device *dev) if (device_may_wakeup(dev)) enable_irq_wake(irq); - else { - disable_irq(keypad->irq); - ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0); - clk_disable(keypad->clk); + else if (keypad->enable) { + keypad->enable = false; + ske_enable(keypad); } return 0; @@ -472,10 +501,9 @@ static int ske_keypad_resume(struct device *dev) if (device_may_wakeup(dev)) disable_irq_wake(irq); - else { - clk_enable(keypad->clk); - enable_irq(keypad->irq); - ske_keypad_chip_init(keypad); + else if (!keypad->enable) { + keypad->enable = true; + ske_enable(keypad); } return 0; -- cgit v1.2.3 From ca224fcca50035f0fbbbf38c5fadfc2f5559c1c8 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(-) (limited to 'drivers') diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 910e46c4502..27daff7cebe 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -375,7 +375,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 2046336651585f806ab456d28022a2019e3361af 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(-) (limited to 'drivers') diff --git a/drivers/input/keyboard/stmpe-keypad.c b/drivers/input/keyboard/stmpe-keypad.c index a01a18f5735..6b3d2ea44e6 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 b5ae03155bc4eaf155ad636aa21a5b9a69a10d42 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(+) (limited to 'drivers') 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 106abad50bca8abe1ad5bc07bbff040f5b38041f 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 Change-Id: Ie1c68120fc718710ecac2d3dafe4f3e7b1a53ac1 Signed-off-by: Rahul Venkatram Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/16256 Reviewed-by: Mattias WALLIN --- 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 | 1328 ++++++++++++++++++++++ 4 files changed, 1429 insertions(+) create mode 100644 arch/arm/mach-ux500/include/mach/ab8500-accdet.h create mode 100644 drivers/input/misc/ab8500-accdet.c (limited to 'drivers') 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 22d875fde53..50f935bba2a 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 a244fc6a781..e0f9c97830b 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..8057d0e881a --- /dev/null +++ b/drivers/input/misc/ab8500-accdet.c @@ -0,0 +1,1328 @@ +/* + * 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 +#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 37e7a251d1aa298ceff71a042a4cbb46b87eccf7 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(-) (limited to 'drivers') diff --git a/drivers/input/misc/ab8500-accdet.c b/drivers/input/misc/ab8500-accdet.c index 8057d0e881a..2d5aaf3bce7 100644 --- a/drivers/input/misc/ab8500-accdet.c +++ b/drivers/input/misc/ab8500-accdet.c @@ -862,7 +862,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 24bd255425dd406a7c9896e5313ca10a41e1a56f 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(-) (limited to 'drivers') diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 50f935bba2a..ed724827cf7 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 785cfbf8ca27d6cf607023a6fe7a24b1cbf9013c 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(-) (limited to 'drivers') diff --git a/drivers/input/misc/ab8500-accdet.c b/drivers/input/misc/ab8500-accdet.c index 2d5aaf3bce7..28cbc2aed42 100644 --- a/drivers/input/misc/ab8500-accdet.c +++ b/drivers/input/misc/ab8500-accdet.c @@ -150,10 +150,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, }; /** @@ -690,8 +692,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); @@ -749,6 +749,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++; @@ -957,8 +959,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) { @@ -1090,9 +1096,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); @@ -1133,7 +1141,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 302ff875c0f904de2b5a212809cd37490030f743 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(+) (limited to 'drivers') diff --git a/drivers/input/touchscreen/bu21013_ts.c b/drivers/input/touchscreen/bu21013_ts.c index 62dbc183347..857a21db0eb 100644 --- a/drivers/input/touchscreen/bu21013_ts.c +++ b/drivers/input/touchscreen/bu21013_ts.c @@ -213,6 +213,14 @@ static int bu21013_enable(struct bu21013_ts_data *pdata) if (pdata->regulator) regulator_enable(pdata->regulator); + if (pdata->chip->cs_en) { + retval = pdata->chip->cs_en(pdata->chip->cs_pin); + if (retval < 0) { + dev_err(&pdata->client->dev, "enable hw failed\n"); + return retval; + } + } + if (pdata->ext_clk_state) retval = bu21013_ext_clk(pdata, true, true); else @@ -242,6 +250,8 @@ static void bu21013_disable(struct bu21013_ts_data *pdata) (void) bu21013_ext_clk(pdata, false, false); disable_irq(pdata->chip->irq); + if (pdata->chip->cs_dis) + pdata->chip->cs_dis(pdata->chip->cs_pin); if (pdata->regulator) regulator_disable(pdata->regulator); } -- cgit v1.2.3 From 43f16d7c8875ba240fdacce1a5ea5693538e654e 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(-) (limited to 'drivers') 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 f1743ca87ed..3f6b5187d88 100644 --- a/drivers/input/keyboard/nomadik-ske-keypad.c +++ b/drivers/input/keyboard/nomadik-ske-keypad.c @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -22,6 +23,7 @@ #include #include +#include /* SKE_CR bits */ #define SKE_KPMLT (0x1 << 6) @@ -61,6 +63,14 @@ * @clk: clock structure pointer * @enable: flag to enable the driver event * @regulator: pointer to the regulator used for ske kyepad + * @gpio_input_irq: array for gpio irqs + * @key_pressed: hold the key state + * @work: delayed work variable for gpio switch + * @ske_rows: rows gpio array for ske + * @ske_cols: columns gpio array for ske + * @gpio_row: gpio row + * @gpio_col: gpio column + * @gpio_work: delayed work variable for release gpio key */ struct ske_keypad { int irq; @@ -72,6 +82,14 @@ struct ske_keypad { spinlock_t ske_keypad_lock; bool enable; struct regulator *regulator; + int gpio_input_irq[SKE_KPD_MAX_ROWS]; + int key_pressed; + struct delayed_work work; + int ske_rows[SKE_KPD_MAX_ROWS]; + int ske_cols[SKE_KPD_MAX_COLS]; + int gpio_row; + int gpio_col; + struct delayed_work gpio_work; }; static void ske_keypad_set_bits(struct ske_keypad *keypad, u16 addr, @@ -140,18 +158,41 @@ static int __devinit ske_keypad_chip_init(struct ske_keypad *keypad) return 0; } -static void ske_enable(struct ske_keypad *keypad) +static void ske_mode_enable(struct ske_keypad *keypad, bool enable) { - if (keypad->enable) { + int i; + + if (!enable) { + writel(0, keypad->reg_base + SKE_CR); + if (keypad->board->exit) + keypad->board->exit(); + for (i = 0; i < keypad->board->krow; i++) { + enable_irq(keypad->gpio_input_irq[i]); + enable_irq_wake(keypad->gpio_input_irq[i]); + } + clk_disable(keypad->clk); + regulator_disable(keypad->regulator); + } else { regulator_enable(keypad->regulator); clk_enable(keypad->clk); - enable_irq(keypad->irq); + for (i = 0; i < keypad->board->krow; i++) { + disable_irq_nosync(keypad->gpio_input_irq[i]); + disable_irq_wake(keypad->gpio_input_irq[i]); + } + if (keypad->board->init) + keypad->board->init(); ske_keypad_chip_init(keypad); + } +} +static void ske_enable(struct ske_keypad *keypad, bool enable) +{ + keypad->enable = enable; + if (keypad->enable) { + enable_irq(keypad->irq); + ske_mode_enable(keypad, true); } else { + ske_mode_enable(keypad, false); disable_irq(keypad->irq); - ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0); - clk_disable(keypad->clk); - regulator_disable(keypad->regulator); } } @@ -178,7 +219,7 @@ static ssize_t ske_store_attr_enable(struct device *dev, if (keypad->enable != val) { keypad->enable = val ? true : false; - ske_enable(keypad); + ske_enable(keypad, keypad->enable); } return count; } @@ -200,7 +241,6 @@ static void ske_keypad_report(struct ske_keypad *keypad, u8 status, int col) int row = 0, code, pos; struct input_dev *input = keypad->input; u32 ske_ris; - int key_pressed; int num_of_rows; /* find out the row */ @@ -212,10 +252,11 @@ static void ske_keypad_report(struct ske_keypad *keypad, u8 status, int col) code = MATRIX_SCAN_CODE(row, col, SKE_KEYPAD_ROW_SHIFT); ske_ris = readl(keypad->reg_base + SKE_RIS); - key_pressed = ske_ris & SKE_KPRISA; + keypad->key_pressed = ske_ris & SKE_KPRISA; input_event(input, EV_MSC, MSC_SCAN, code); - input_report_key(input, keypad->keymap[code], key_pressed); + input_report_key(input, keypad->keymap[code], + keypad->key_pressed); input_sync(input); num_of_rows--; } while (num_of_rows); @@ -252,10 +293,8 @@ static void ske_keypad_read_data(struct ske_keypad *keypad) } } } - -static irqreturn_t ske_keypad_irq(int irq, void *dev_id) +static void ske_keypad_scan(struct ske_keypad *keypad) { - struct ske_keypad *keypad = dev_id; int timeout = keypad->board->debounce_ms; /* disable auto scan interrupt; mask the interrupt generated */ @@ -274,6 +313,139 @@ static irqreturn_t ske_keypad_irq(int irq, void *dev_id) /* enable auto scan interrupts */ ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA); +} + +static void ske_gpio_switch_work(struct work_struct *work) +{ + struct ske_keypad *keypad = container_of(work, + struct ske_keypad, work.work); + + ske_mode_enable(keypad, false); + keypad->enable = false; +} + +static void ske_gpio_release_work(struct work_struct *work) +{ + int code; + struct ske_keypad *keypad = container_of(work, + struct ske_keypad, gpio_work.work); + struct input_dev *input = keypad->input; + + code = MATRIX_SCAN_CODE(keypad->gpio_row, keypad->gpio_col, + SKE_KEYPAD_ROW_SHIFT); + input_event(input, EV_MSC, MSC_SCAN, code); + input_report_key(input, keypad->keymap[code], 1); + input_sync(input); + input_report_key(input, keypad->keymap[code], 0); + input_sync(input); +} + +static int ske_read_get_gpio_row(struct ske_keypad *keypad) +{ + int row; + int value = 0; + int ret; + + /* read all rows GPIO data register values */ + for (row = 0; row < SKE_KPD_MAX_ROWS ; row++) { + ret = gpio_get_value(keypad->ske_rows[row]); + value += (1 << row) * ret; + } + + /* get the exact row */ + for (row = 0; row < keypad->board->krow; row++) { + if (((1 << row) & value) == 0) + return row; + } + + return -1; +} + +static void ske_set_cols(struct ske_keypad *keypad, int col) +{ + int i ; + int value; + + /* + * Set all columns except the requested column + * output pin as high + */ + for (i = 0; i < SKE_KPD_MAX_COLS; i++) { + if (i == col) + value = 0; + else + value = 1; + gpio_request(keypad->ske_cols[i], "ske-kp"); + gpio_direction_output(keypad->ske_cols[i], value); + gpio_free(keypad->ske_cols[i]); + } +} + +static void ske_free_cols(struct ske_keypad *keypad) +{ + int i ; + + for (i = 0; i < SKE_KPD_MAX_COLS; i++) { + gpio_request(keypad->ske_cols[i], "ske-kp"); + gpio_direction_output(keypad->ske_cols[i], 0); + gpio_free(keypad->ske_cols[i]); + } +} + +static void ske_manual_scan(struct ske_keypad *keypad) +{ + int row; + int col; + + for (col = 0; col < keypad->board->kcol; col++) { + ske_set_cols(keypad, col); + row = ske_read_get_gpio_row(keypad); + if (row >= 0) { + keypad->key_pressed = 1; + keypad->gpio_row = row; + keypad->gpio_col = col; + break; + } + } + ske_free_cols(keypad); +} + +static irqreturn_t ske_keypad_gpio_irq(int irq, void *dev_id) +{ + struct ske_keypad *keypad = dev_id; + + if (!gpio_get_value(NOMADIK_IRQ_TO_GPIO(irq))) { + ske_manual_scan(keypad); + if (!keypad->enable) { + keypad->enable = true; + ske_mode_enable(keypad, true); + } + /* + * Schedule the work queue to change it to + * report the key pressed, if it is not detected in SKE mode. + */ + if (keypad->key_pressed) + schedule_delayed_work(&keypad->gpio_work, + KEY_PRESSED_DELAY); + } + + return IRQ_HANDLED; +} +static irqreturn_t ske_keypad_irq(int irq, void *dev_id) +{ + struct ske_keypad *keypad = dev_id; + + cancel_delayed_work_sync(&keypad->gpio_work); + cancel_delayed_work_sync(&keypad->work); + ske_keypad_scan(keypad); + + /* + * Schedule the work queue to change it to + * GPIO mode, if there is no activity in SKE mode + */ + if (!keypad->key_pressed && keypad->enable) + schedule_delayed_work(&keypad->work, + keypad->board->switch_delay); return IRQ_HANDLED; } @@ -287,6 +459,7 @@ static int __devinit ske_keypad_probe(struct platform_device *pdev) void __iomem *reg_base; int ret = 0; int irq; + int i; struct ske_keypad_platform_data *plat = pdev->dev.platform_data; if (!plat) { @@ -339,7 +512,6 @@ static int __devinit ske_keypad_probe(struct platform_device *pdev) ret = -ENOMEM; goto out_freekeypad; } - keypad->regulator = regulator_get(&pdev->dev, "v-ape"); if (IS_ERR(keypad->regulator)) { dev_err(&pdev->dev, "regulator_get failed\n"); @@ -383,29 +555,57 @@ static int __devinit ske_keypad_probe(struct platform_device *pdev) keypad->input = input; keypad->reg_base = reg_base; keypad->clk = clk; - keypad->enable = true; + INIT_DELAYED_WORK(&keypad->work, ske_gpio_switch_work); + INIT_DELAYED_WORK(&keypad->gpio_work, ske_gpio_release_work); /* allocations are sane, we begin HW initialization */ clk_enable(keypad->clk); if (!keypad->board->init) { - dev_err(&pdev->dev, "NULL board initialization helper\n"); + dev_err(&pdev->dev, "init funtion not defined\n"); ret = -EINVAL; goto out_unregisterinput; } if (keypad->board->init() < 0) { - dev_err(&pdev->dev, "unable to set keypad board config\n"); + dev_err(&pdev->dev, "keyboard init config failed\n"); ret = -EINVAL; goto out_unregisterinput; } - ret = ske_keypad_chip_init(keypad); - if (ret < 0) { - dev_err(&pdev->dev, "unable to init keypad hardware\n"); + if (!keypad->board->exit) { + dev_err(&pdev->dev, "exit funtion not defined\n"); + ret = -EINVAL; goto out_unregisterinput; } + if (keypad->board->exit() < 0) { + dev_err(&pdev->dev, "keyboard exit config failed\n"); + ret = -EINVAL; + goto out_unregisterinput; + } + for (i = 0; i < SKE_KPD_MAX_ROWS; i++) { + keypad->ske_rows[i] = *plat->gpio_input_pins; + keypad->ske_cols[i] = *plat->gpio_output_pins; + keypad->gpio_input_irq[i] = + NOMADIK_GPIO_TO_IRQ(keypad->ske_rows[i]); + plat->gpio_input_pins++; + plat->gpio_output_pins++; + } + + for (i = 0; i < keypad->board->krow; i++) { + ret = request_threaded_irq(keypad->gpio_input_irq[i], + NULL, ske_keypad_gpio_irq, + IRQF_TRIGGER_FALLING | IRQF_NO_SUSPEND, + "ske-keypad-gpio", keypad); + if (ret) { + dev_err(&pdev->dev, "allocate gpio irq %d failed\n", + keypad->gpio_input_irq[i]); + goto out_unregisterinput; + } + enable_irq_wake(keypad->gpio_input_irq[i]); + } + ret = request_threaded_irq(keypad->irq, NULL, ske_keypad_irq, IRQF_ONESHOT, "ske-keypad", keypad); if (ret) { @@ -425,6 +625,9 @@ static int __devinit ske_keypad_probe(struct platform_device *pdev) platform_set_drvdata(pdev, keypad); + clk_disable(keypad->clk); + regulator_disable(keypad->regulator); + return 0; out_free_irq: @@ -455,6 +658,8 @@ static int __devexit ske_keypad_remove(struct platform_device *pdev) struct ske_keypad *keypad = platform_get_drvdata(pdev); struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + cancel_delayed_work_sync(&keypad->gpio_work); + cancel_delayed_work_sync(&keypad->work); free_irq(keypad->irq, keypad); input_unregister_device(keypad->input); @@ -466,7 +671,6 @@ static int __devexit ske_keypad_remove(struct platform_device *pdev) if (keypad->board->exit) keypad->board->exit(); - regulator_disable(keypad->regulator); regulator_put(keypad->regulator); iounmap(keypad->reg_base); @@ -485,9 +689,14 @@ static int ske_keypad_suspend(struct device *dev) if (device_may_wakeup(dev)) enable_irq_wake(irq); - else if (keypad->enable) { - keypad->enable = false; - ske_enable(keypad); + else { + cancel_delayed_work_sync(&keypad->gpio_work); + cancel_delayed_work_sync(&keypad->work); + disable_irq(irq); + if (keypad->enable) { + ske_mode_enable(keypad, false); + keypad->enable = false; + } } return 0; @@ -501,9 +710,12 @@ static int ske_keypad_resume(struct device *dev) if (device_may_wakeup(dev)) disable_irq_wake(irq); - else if (!keypad->enable) { - keypad->enable = true; - ske_enable(keypad); + else { + if (!keypad->enable) { + keypad->enable = true; + ske_mode_enable(keypad, true); + } + enable_irq(irq); } return 0; @@ -540,6 +752,6 @@ static void __exit ske_keypad_exit(void) module_exit(ske_keypad_exit); MODULE_LICENSE("GPL v2"); -MODULE_AUTHOR("Naveen Kumar / Sundar Iyer "); +MODULE_AUTHOR("Naveen Kumar "); MODULE_DESCRIPTION("Nomadik Scroll-Key-Encoder Keypad Driver"); MODULE_ALIAS("platform:nomadik-ske-keypad"); -- cgit v1.2.3 From bedadb29ce44382ed0eb5c154d6891e33afacb80 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 | 44 +++++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/input/misc/ab8500-ponkey.c b/drivers/input/misc/ab8500-ponkey.c index 1cef5e670ca..445a9ad3eab 100644 --- a/drivers/input/misc/ab8500-ponkey.c +++ b/drivers/input/misc/ab8500-ponkey.c @@ -11,19 +11,26 @@ #include #include #include -#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 +52,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 +77,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 +94,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 +102,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 +111,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 +143,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 94f14c7a22a3a4740a56c5ae598498472c6f6a74 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(-) (limited to 'drivers') 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 c84c7f22f1b436c7f919cab41775d8066fe1c18a 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(-) (limited to 'drivers') 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 c32bf8da509eafa8a0b033f7eb9404684157a467 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(-) (limited to 'drivers') diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index ed724827cf7..8c930f093a2 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 445a9ad3eab..39964956de2 100644 --- a/drivers/input/misc/ab8500-ponkey.c +++ b/drivers/input/misc/ab8500-ponkey.c @@ -18,6 +18,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", @@ -144,6 +149,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 6c86446cbd0c08996cc4f8a2d1d8fc740488518a 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(+) (limited to 'drivers') diff --git a/drivers/input/misc/ab8500-accdet.c b/drivers/input/misc/ab8500-accdet.c index 28cbc2aed42..3ffa3132264 100644 --- a/drivers/input/misc/ab8500-accdet.c +++ b/drivers/input/misc/ab8500-accdet.c @@ -1274,6 +1274,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) { @@ -1285,6 +1287,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; } @@ -1294,6 +1307,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 4f84f0644837b24a9f6af015ec2af4d960528026 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(-) (limited to 'drivers') 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 3ffa3132264..a17e5cfce80 100644 --- a/drivers/input/misc/ab8500-accdet.c +++ b/drivers/input/misc/ab8500-accdet.c @@ -380,7 +380,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", @@ -403,6 +403,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 */ @@ -643,6 +668,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, @@ -654,7 +680,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; } /* @@ -1023,6 +1052,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; @@ -1062,6 +1096,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 36647dbfb9f6cb57ca063188ffccbd1c83fdc021 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(+) (limited to 'drivers') diff --git a/drivers/input/misc/ab8500-accdet.c b/drivers/input/misc/ab8500-accdet.c index a17e5cfce80..bdb7a6c1bbf 100644 --- a/drivers/input/misc/ab8500-accdet.c +++ b/drivers/input/misc/ab8500-accdet.c @@ -1319,6 +1319,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__); @@ -1326,6 +1327,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, @@ -1343,6 +1359,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__); @@ -1353,6 +1370,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 36760a72f9ba006c1e44b333e0ed463d4fa49eb3 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(-) (limited to 'drivers') diff --git a/drivers/input/misc/ab8500-accdet.c b/drivers/input/misc/ab8500-accdet.c index bdb7a6c1bbf..3bb898503e7 100644 --- a/drivers/input/misc/ab8500-accdet.c +++ b/drivers/input/misc/ab8500-accdet.c @@ -560,7 +560,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; @@ -629,7 +629,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; @@ -1190,7 +1190,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; @@ -1278,7 +1278,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 80200c5a30618b05c811174f3f550cd5925c9cec 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(-) (limited to 'drivers') 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 444ae985231048b745c8776e0c21566936b744e6 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 (limited to 'drivers') 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 dbf0022cedf87376b30c25487f9739fd8145a684 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(-) (limited to 'drivers') diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c index 3f6b5187d88..c5d169a2b3e 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 43cd218ef2bc65cf068d07121fc315a36a3bdb56 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(-) (limited to 'drivers') diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 8c930f093a2..99d1c954e1c 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 bdf10f8f31d03adca0a554adc5b88ef5b780569f 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(-) (limited to 'drivers') diff --git a/drivers/input/keyboard/stmpe-keypad.c b/drivers/input/keyboard/stmpe-keypad.c index 6b3d2ea44e6..c1de4d77ca1 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 879941f0b08c144cf81665e143e7a4cd57a8a9a1 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(+) (limited to 'drivers') diff --git a/drivers/input/misc/ab8500-ponkey.c b/drivers/input/misc/ab8500-ponkey.c index 39964956de2..251408b95c9 100644 --- a/drivers/input/misc/ab8500-ponkey.c +++ b/drivers/input/misc/ab8500-ponkey.c @@ -12,15 +12,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 = { @@ -40,6 +53,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) { @@ -64,6 +108,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 e071498261a845d045d8de1aadfbbfd4f9324985 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(+) (limited to 'drivers') diff --git a/drivers/input/misc/ab8500-accdet.c b/drivers/input/misc/ab8500-accdet.c index 3bb898503e7..cff9b78847b 100644 --- a/drivers/input/misc/ab8500-accdet.c +++ b/drivers/input/misc/ab8500-accdet.c @@ -1353,6 +1353,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; } @@ -1363,6 +1367,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 e82f2443ad4e9db4d4e2725c3364905254b155ea 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(-) (limited to 'drivers') 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 b602bb45daad0132c61283000d84b52126b51c53 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 (limited to 'drivers') 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 99d1c954e1c..c5df9c00b5e 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -551,4 +551,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 e0f9c97830b..756c8e545fd 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -52,3 +52,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 cc400be761fce79e9803692ede718845d128445b 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(-) (limited to 'drivers') 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 81910091455d083386e9bd1f7b5a0b0c65a9d8a9 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(-) (limited to 'drivers') 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 f78d9fed4c25cef285feddc4d8c951fd1f5763da 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(-) (limited to 'drivers') 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 fe7f48b1243c30ee23bc410464b91f1959243ac6 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(+) (limited to 'drivers') diff --git a/drivers/input/misc/ab8500-ponkey.c b/drivers/input/misc/ab8500-ponkey.c index 251408b95c9..c1bb2128217 100644 --- a/drivers/input/misc/ab8500-ponkey.c +++ b/drivers/input/misc/ab8500-ponkey.c @@ -13,6 +13,7 @@ #include #include #include +#include /* Ponkey time control bits */ #define AB5500_MCB 0x2F -- cgit v1.2.3 From 952b39e39a67c9c9fc77e69d080729f53cef118a Mon Sep 17 00:00:00 2001 From: Avinash A Date: Fri, 9 Sep 2011 12:09:21 +0530 Subject: touchscreen: Add Cypress driver Add the Cypress SPI based touchscreen for ux500. ST-Ericsson ID: CR321190,279760 ST-Ericsson Linux next: NA ST-Ericsson FOSS-OUT ID:10249 Ref: Commit-ids: I0b25b7e97968901ff747be62e571134ebb6ad3de Id: Iac75daa197826d8a0bb4c1d394a834575bf601eb Id: Iec8c5516768d85f9f8b301984618bc5b394d4927 Change-Id: I56a3a6080a90553a2a8099f2dc243d85a8229688 Signed-off-by: Avinash A Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/30533 Reviewed-by: Avinash A Tested-by: Avinash A --- drivers/input/touchscreen/Kconfig | 17 + drivers/input/touchscreen/Makefile | 4 +- drivers/input/touchscreen/cyttsp_core.c | 2215 +++++++++++++++++++++++++++++++ drivers/input/touchscreen/cyttsp_core.h | 44 + drivers/input/touchscreen/cyttsp_ldr.h | 333 +++++ drivers/input/touchscreen/cyttsp_spi.c | 302 +++++ include/linux/cyttsp.h | 114 ++ 7 files changed, 3028 insertions(+), 1 deletion(-) create mode 100755 drivers/input/touchscreen/cyttsp_core.c create mode 100755 drivers/input/touchscreen/cyttsp_core.h create mode 100755 drivers/input/touchscreen/cyttsp_ldr.h create mode 100755 drivers/input/touchscreen/cyttsp_spi.c create mode 100755 include/linux/cyttsp.h (limited to 'drivers') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 3488ffe1fa0..085b45d5cb0 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -122,6 +122,23 @@ config TOUCHSCREEN_BU21013 To compile this driver as a module, choose M here: the module will be called bu21013_ts. +config TOUCHSCREEN_CYTTSP_CORE + tristate "Cypress TTSP touchscreen core" + help + Always activated for Cypress TTSP touchscreen. + +config TOUCHSCREEN_CYTTSP_SPI + tristate "Cypress TTSP spi touchscreen" + depends on SPI_MASTER && TOUCHSCREEN_CYTTSP_CORE + help + Say Y here if you have a Cypress TTSP touchscreen + connected to your with an SPI interface. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called cyttsp-spi. + config TOUCHSCREEN_CY8CTMG110 tristate "cy8ctmg110 touchscreen" depends on I2C diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index f957676035a..3eb61596c0a 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -16,7 +16,9 @@ obj-$(CONFIG_TOUCHSCREEN_ATMEL_MXT) += atmel_mxt_ts.o obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o -obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o +obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE) += cyttsp_core.o +obj-$(CONFIG_TOUCHSCREEN_CYTTSP_SPI) += cyttsp_spi.o +bj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o diff --git a/drivers/input/touchscreen/cyttsp_core.c b/drivers/input/touchscreen/cyttsp_core.c new file mode 100755 index 00000000000..076417439ab --- /dev/null +++ b/drivers/input/touchscreen/cyttsp_core.c @@ -0,0 +1,2215 @@ +/* Source for: + * Cypress TrueTouch(TM) Standard Product touchscreen driver. + * drivers/input/touchscreen/cyttsp_core.c + * + * Copyright (C) 2009-2011 Cypress Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Cypress reserves the right to make changes without further notice + * to the materials described herein. Cypress does not assume any + * liability arising out of the application described herein. + * + * Contact Cypress Semiconductor at www.cypress.com + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cyttsp_core.h" + +#define DBG(x) +#define DBG2(x) +#define DBG3(x) + +/* rely on kernel input.h to define Multi-Touch capability */ +#ifndef ABS_MT_TRACKING_ID +/* define only if not defined already by system; */ +/* value based on linux kernel 2.6.30.10 */ +#define ABS_MT_TRACKING_ID (ABS_MT_BLOB_ID + 1) +#endif /* ABS_MT_TRACKING_ID */ + +#define TOUCHSCREEN_TIMEOUT (msecs_to_jiffies(28)) +/* Bootloader File 0 offset */ +#define CY_BL_FILE0 0x00 +/* Bootloader command directive */ +#define CY_BL_CMD 0xFF +/* Bootloader Enter Loader mode */ +#define CY_BL_ENTER 0x38 +/* Bootloader Write a Block */ +#define CY_BL_WRITE_BLK 0x39 +/* Bootloader Terminate Loader mode */ +#define CY_BL_TERMINATE 0x3B +/* Bootloader Exit and Verify Checksum command */ +#define CY_BL_EXIT 0xA5 +/* Bootloader default keys */ +#define CY_BL_KEY0 0 +#define CY_BL_KEY1 1 +#define CY_BL_KEY2 2 +#define CY_BL_KEY3 3 +#define CY_BL_KEY4 4 +#define CY_BL_KEY5 5 +#define CY_BL_KEY6 6 +#define CY_BL_KEY7 7 + +#define CY_DIFF(m, n) ((m) != (n)) +#define GET_NUM_TOUCHES(x) ((x) & 0x0F) +#define GET_TOUCH1_ID(x) (((x) & 0xF0) >> 4) +#define GET_TOUCH2_ID(x) ((x) & 0x0F) +#define GET_TOUCH3_ID(x) (((x) & 0xF0) >> 4) +#define GET_TOUCH4_ID(x) ((x) & 0x0F) +#define IS_LARGE_AREA(x) (((x) & 0x10) >> 4) +#define IS_BAD_PKT(x) ((x) & 0x20) +#define FLIP_DATA_FLAG 0x01 +#define REVERSE_X_FLAG 0x02 +#define REVERSE_Y_FLAG 0x04 +#define FLIP_DATA(flags) ((flags) & FLIP_DATA_FLAG) +#define REVERSE_X(flags) ((flags) & REVERSE_X_FLAG) +#define REVERSE_Y(flags) ((flags) & REVERSE_Y_FLAG) +#define FLIP_XY(x, y) {typeof(x) tmp; tmp = (x); (x) = (y); (y) = tmp; } +#define INVERT_X(x, xmax) ((xmax) - (x)) +#define INVERT_Y(y, ymax) ((ymax) - (y)) +#define SET_HSTMODE(reg, mode) ((reg) & (mode)) +#define GET_HSTMODE(reg) ((reg & 0x70) >> 4) +#define GET_BOOTLOADERMODE(reg) ((reg & 0x10) >> 4) + +/* Watchdog timeout to check if device is reset and no interrupts running */ +#define CY_WDG_TIMEOUT msecs_to_jiffies(3000) +#define CY_MODE_ERROR(ps, hst_mode, tt_mode) \ + ((ps == CY_ACTIVE_STATE && GET_HSTMODE(hst_mode) != CY_OPERATE_MODE) ||\ + GET_BOOTLOADERMODE(tt_mode)) + +/* maximum number of concurrent ST track IDs */ +#define CY_NUM_ST_TCH_ID 2 +/* maximum number of concurrent MT track IDs */ +#define CY_NUM_MT_TCH_ID 4 +/* maximum number of track IDs */ +#define CY_NUM_TRK_ID 16 +/* + * maximum number of concurrent touches + * (only CY_NUM_MT_TCH_ID have coord data) + */ +#define CY_MAX_TCH 10 +/* + * maximum number of touch reports with + * current touches=0 before performing Driver reset + */ +#define CY_MAX_NTCH 10 + +#define CY_NTCH 0 /* lift off */ +#define CY_TCH 1 /* touch down */ +#define CY_ST_FNGR1_IDX 0 +#define CY_ST_FNGR2_IDX 1 +#define CY_MT_TCH1_IDX 0 +#define CY_MT_TCH2_IDX 1 +#define CY_MT_TCH3_IDX 2 +#define CY_MT_TCH4_IDX 3 +#define CY_XPOS 0 +#define CY_YPOS 1 +#define CY_IGNR_TCH (-1) +#define CY_SMALL_TOOL_WIDTH 10 +#define CY_LARGE_TOOL_WIDTH 255 +#define CY_REG_BASE 0x00 +#define CY_REG_GEST_SET 0x1E +#define CY_REG_SCN_TYP 0x1C +#define CY_REG_ACT_INTRVL 0x1D +#define CY_REG_TCH_TMOUT (CY_REG_ACT_INTRVL+1) +#define CY_REG_LP_INTRVL (CY_REG_TCH_TMOUT+1) +#define CY_SOFT_RESET (1 << 0) +#define CY_DEEP_SLEEP (1 << 1) +#define CY_LOW_POWER (1 << 2) +#define CY_MAXZ 255 +#define CY_OK 0 +#define CY_INIT 1 +#define CY_DELAY_DFLT 20 /* ms */ +#define CY_DELAY_MAX (500/CY_DELAY_DFLT) /* half second */ +#define CY_DELAY_SYSINFO 20 /* ms */ +#define CY_DELAY_BL 300 +#define CY_DELAY_DNLOAD 100 /* ms */ +#define CY_HNDSHK_BIT 0x80 +/* device mode bits */ +#define CY_OPERATE_MODE 0x00 +#define CY_SYSINFO_MODE 0x10 +/* power mode select bits */ +#define CY_SOFT_RESET_MODE 0x01 /* return to Bootloader mode */ +#define CY_DEEP_SLEEP_MODE 0x02 +#define CY_LOW_POWER_MODE 0x04 +#define CY_NUM_KEY 8 + +/* TrueTouch Standard Product Gen3 (Txx3xx) interface definition */ +struct cyttsp_xydata { + u8 hst_mode; + u8 tt_mode; + u8 tt_stat; + u16 x1 __attribute__ ((packed)); + u16 y1 __attribute__ ((packed)); + u8 z1; + u8 touch12_id; + u16 x2 __attribute__ ((packed)); + u16 y2 __attribute__ ((packed)); + u8 z2; + u8 gest_cnt; + u8 gest_id; + u16 x3 __attribute__ ((packed)); + u16 y3 __attribute__ ((packed)); + u8 z3; + u8 touch34_id; + u16 x4 __attribute__ ((packed)); + u16 y4 __attribute__ ((packed)); + u8 z4; + u8 tt_undef[3]; + u8 gest_set; + u8 tt_reserved; +}; + +struct cyttsp_xydata_gen2 { + u8 hst_mode; + u8 tt_mode; + u8 tt_stat; + u16 x1 __attribute__ ((packed)); + u16 y1 __attribute__ ((packed)); + u8 z1; + u8 evnt_idx; + u16 x2 __attribute__ ((packed)); + u16 y2 __attribute__ ((packed)); + u8 tt_undef1; + u8 gest_cnt; + u8 gest_id; + u8 tt_undef[14]; + u8 gest_set; + u8 tt_reserved; +}; + +/* TrueTouch Standard Product Gen2 (Txx2xx) interface definition */ +enum cyttsp_gen2_std { + CY_GEN2_NOTOUCH = 0x03, /* Both touches removed */ + CY_GEN2_GHOST = 0x02, /* ghost */ + CY_GEN2_2TOUCH = 0x03, /* 2 touch; no ghost */ + CY_GEN2_1TOUCH = 0x01, /* 1 touch only */ + CY_GEN2_TOUCH2 = 0x01, /* 1st touch removed; 2nd touch remains */ +}; + +/* TTSP System Information interface definition */ +struct cyttsp_sysinfo_data { + u8 hst_mode; + u8 mfg_stat; + u8 mfg_cmd; + u8 cid[3]; + u8 tt_undef1; + u8 uid[8]; + u8 bl_verh; + u8 bl_verl; + u8 tts_verh; + u8 tts_verl; + u8 app_idh; + u8 app_idl; + u8 app_verh; + u8 app_verl; + u8 tt_undef[5]; + u8 scn_typ; /* Gen3 only: scan type [0:Mutual, 1:Self] */ + u8 act_intrvl; + u8 tch_tmout; + u8 lp_intrvl; +}; + +/* TTSP Bootloader Register Map interface definition */ +#define CY_BL_CHKSUM_OK 0x01 +struct cyttsp_bootloader_data { + u8 bl_file; + u8 bl_status; + u8 bl_error; + u8 blver_hi; + u8 blver_lo; + u8 bld_blver_hi; + u8 bld_blver_lo; + u8 ttspver_hi; + u8 ttspver_lo; + u8 appid_hi; + u8 appid_lo; + u8 appver_hi; + u8 appver_lo; + u8 cid_0; + u8 cid_1; + u8 cid_2; +}; + +#define cyttsp_wake_data cyttsp_xydata + +struct cyttsp { + struct device *pdev; + int irq; + struct input_dev *input; + struct work_struct work; + struct timer_list timer; + struct mutex mutex; + struct early_suspend early_suspend; + char phys[32]; + struct cyttsp_platform_data *platform_data; + struct cyttsp_bootloader_data bl_data; + struct cyttsp_sysinfo_data sysinfo_data; + u8 num_prv_st_tch; + u16 act_trk[CY_NUM_TRK_ID]; + u16 prv_mt_tch[CY_NUM_MT_TCH_ID]; + u16 prv_st_tch[CY_NUM_ST_TCH_ID]; + u16 prv_mt_pos[CY_NUM_TRK_ID][2]; + struct cyttsp_bus_ops *bus_ops; + struct regulator *regulator; + unsigned fw_loader_mode:1; + unsigned suspended:1; + struct timer_list to_timer; + bool to_timeout; + struct completion int_running; + bool bl_ready; + u8 reg_id; + u8 ntch_count; +}; + +struct cyttsp_track_data { + u8 prv_tch; + u8 cur_tch; + u16 tmp_trk[CY_NUM_MT_TCH_ID]; + u16 snd_trk[CY_NUM_MT_TCH_ID]; + u16 cur_trk[CY_NUM_TRK_ID]; + u16 cur_st_tch[CY_NUM_ST_TCH_ID]; + u16 cur_mt_tch[CY_NUM_MT_TCH_ID]; + /* if NOT CY_USE_TRACKING_ID then only */ + /* uses CY_NUM_MT_TCH_ID positions */ + u16 cur_mt_pos[CY_NUM_TRK_ID][2]; + /* if NOT CY_USE_TRACKING_ID then only */ + /* uses CY_NUM_MT_TCH_ID positions */ + u8 cur_mt_z[CY_NUM_TRK_ID]; + u8 tool_width; + u16 st_x1; + u16 st_y1; + u8 st_z1; + u16 st_x2; + u16 st_y2; + u8 st_z2; +}; + +static const u8 bl_cmd[] = { + CY_BL_FILE0, CY_BL_CMD, CY_BL_EXIT, + CY_BL_KEY0, CY_BL_KEY1, CY_BL_KEY2, + CY_BL_KEY3, CY_BL_KEY4, CY_BL_KEY5, + CY_BL_KEY6, CY_BL_KEY7 +}; + +#define LOCK(m) do { \ + DBG(printk(KERN_INFO "%s: lock\n", __func__);) \ + mutex_lock(&(m)); \ +} while (0); + +#define UNLOCK(m) do { \ + DBG(printk(KERN_INFO "%s: unlock\n", __func__);) \ + mutex_unlock(&(m)); \ +} while (0); + +DBG( +static void print_data_block(const char *func, u8 command, + u8 length, void *data) +{ + char buf[1024]; + unsigned buf_len = sizeof(buf); + char *p = buf; + int i; + int l; + + l = snprintf(p, buf_len, "cmd 0x%x: ", command); + buf_len -= l; + p += l; + for (i = 0; i < length && buf_len; i++, p += l, buf_len -= l) + l = snprintf(p, buf_len, "%02x ", *((char *)data + i)); + printk(KERN_DEBUG "%s: %s\n", func, buf); +}) + +static int cyttsp_soft_reset(struct cyttsp *ts); +static int cyttsp_set_operational_mode(struct cyttsp *ts); +static int cyttsp_exit_bl_mode(struct cyttsp *ts); +static int cyttsp_power_on(struct cyttsp *ts); +static void cyttsp_init_tch(struct cyttsp *ts) +{ + /* init the touch structures */ + ts->num_prv_st_tch = CY_NTCH; + memset(ts->act_trk, CY_NTCH, sizeof(ts->act_trk)); + memset(ts->prv_mt_pos, CY_NTCH, sizeof(ts->prv_mt_pos)); + memset(ts->prv_mt_tch, CY_IGNR_TCH, sizeof(ts->prv_mt_tch)); + memset(ts->prv_st_tch, CY_IGNR_TCH, sizeof(ts->prv_st_tch)); + ts->ntch_count = 0; +} + +static u8 ttsp_convert_gen2(u8 cur_tch, struct cyttsp_xydata *pxy_data) +{ + struct cyttsp_xydata_gen2 *pxy_data_gen2; + pxy_data_gen2 = (struct cyttsp_xydata_gen2 *)(pxy_data); + + if (pxy_data_gen2->evnt_idx == CY_GEN2_NOTOUCH) { + cur_tch = 0; + } else if (cur_tch == CY_GEN2_GHOST) { + cur_tch = 0; + } else if (cur_tch == CY_GEN2_2TOUCH) { + /* stuff artificial track ID1 and ID2 */ + pxy_data->touch12_id = 0x12; + pxy_data->z1 = CY_MAXZ; + pxy_data->z2 = CY_MAXZ; + cur_tch--; /* 2 touches */ + } else if (cur_tch == CY_GEN2_1TOUCH) { + /* stuff artificial track ID1 and ID2 */ + pxy_data->touch12_id = 0x12; + pxy_data->z1 = CY_MAXZ; + pxy_data->z2 = CY_NTCH; + if (pxy_data_gen2->evnt_idx == CY_GEN2_TOUCH2) { + /* push touch 2 data into touch1 + * (first finger up; second finger down) */ + /* stuff artificial track ID1 for touch2 info */ + pxy_data->touch12_id = 0x20; + /* stuff touch 1 with touch 2 coordinate data */ + pxy_data->x1 = pxy_data->x2; + pxy_data->y1 = pxy_data->y2; + } + } else { + cur_tch = 0; + } + return cur_tch; +} + +static int ttsp_read_block_data(struct cyttsp *ts, u8 command, + u8 length, void *buf) +{ + int rc; + int tries; + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + + if (!buf || !length) { + printk(KERN_ERR "%s: Error, buf:%s len:%u\n", + __func__, !buf ? "NULL" : "OK", length); + return -EIO; + } + + for (tries = 0, rc = -1; tries < CY_NUM_RETRY && (rc < 0); tries++) { + rc = ts->bus_ops->read(ts->bus_ops, command, length, buf); + if (rc) + msleep(CY_DELAY_DFLT); + } + + if (rc < 0) + printk(KERN_ERR "%s: error %d\n", __func__, rc); + DBG(print_data_block(__func__, command, length, buf);) + return rc; +} + +static int ttsp_write_block_data(struct cyttsp *ts, u8 command, + u8 length, void *buf) +{ + int rc; + int tries; + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + + if (!buf || !length) { + printk(KERN_ERR "%s: Error, buf:%s len:%u\n", + __func__, !buf ? "NULL" : "OK", length); + return -EIO; + } + + for (tries = 0, rc = -1; tries < CY_NUM_RETRY && (rc < 0); tries++) { + rc = ts->bus_ops->write(ts->bus_ops, command, length, buf); + if (rc) + msleep(CY_DELAY_DFLT); + } + + if (rc < 0) + printk(KERN_ERR "%s: error %d\n", __func__, rc); + DBG(print_data_block(__func__, command, length, buf);) + return rc; +} + +static int ttsp_tch_ext(struct cyttsp *ts, void *buf) +{ + int rc; + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + + if (!buf) { + printk(KERN_ERR "%s: Error, buf:%s\n", + __func__, !buf ? "NULL" : "OK"); + return -EIO; + } + rc = ts->bus_ops->ext(ts->bus_ops, buf); + if (rc < 0) + printk(KERN_ERR "%s: error %d\n", __func__, rc); + return rc; +} + +/* ************************************************************************ + * The cyttsp_xy_worker function reads the XY coordinates and sends them to + * the input layer. It is called from the Touch Interrupt. + * *************************************************************************/ +static int cyttsp_inlist(u16 prev_track[], u8 cur_trk_id, u8 *prev_loc, + u8 num_touches) +{ + u8 id = 0; + + DBG(printk(KERN_INFO"%s: IN p[%d]=%d c=%d n=%d loc=%d\n", + __func__, id, prev_track[id], cur_trk_id, + num_touches, *prev_loc);) + + for (*prev_loc = CY_IGNR_TCH; id < num_touches; id++) { + DBG(printk(KERN_INFO"%s: p[%d]=%d c=%d n=%d loc=%d\n", + __func__, id, prev_track[id], cur_trk_id, + num_touches, *prev_loc);) + if (prev_track[id] == cur_trk_id) { + *prev_loc = id; + break; + } + } + DBG(printk(KERN_INFO"%s: OUT p[%d]=%d c=%d n=%d loc=%d\n", __func__, + id, prev_track[id], cur_trk_id, num_touches, *prev_loc);) + + return *prev_loc < CY_NUM_TRK_ID; +} + +static int cyttsp_next_avail_inlist(u16 cur_trk[], u8 *new_loc, + u8 num_touches) +{ + u8 id = 0; + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + + for (*new_loc = CY_IGNR_TCH; id < num_touches; id++) { + if (cur_trk[id] > CY_NUM_TRK_ID) { + *new_loc = id; + break; + } + } + return *new_loc < CY_NUM_TRK_ID; +} + +static void handle_single_touch(struct cyttsp_xydata *xy, + struct cyttsp_track_data *t, struct cyttsp *ts) +{ + u8 id; + u8 use_trk_id = ts->platform_data->use_trk_id; + + DBG(printk(KERN_INFO"%s: ST STEP 0 - ST1 ID=%d ST2 ID=%d\n", + __func__, t->cur_st_tch[CY_ST_FNGR1_IDX], + t->cur_st_tch[CY_ST_FNGR2_IDX]);) + + if (t->cur_st_tch[CY_ST_FNGR1_IDX] > CY_NUM_TRK_ID) { + /* reassign finger 1 and 2 positions to new tracks */ + if (t->cur_tch > 0) { + /* reassign st finger1 */ + if (use_trk_id) { + id = CY_MT_TCH1_IDX; + t->cur_st_tch[CY_ST_FNGR1_IDX] = + t->cur_mt_tch[id]; + } else { + id = GET_TOUCH1_ID(xy->touch12_id); + t->cur_st_tch[CY_ST_FNGR1_IDX] = id; + } + t->st_x1 = t->cur_mt_pos[id][CY_XPOS]; + t->st_y1 = t->cur_mt_pos[id][CY_YPOS]; + t->st_z1 = t->cur_mt_z[id]; + + DBG(printk(KERN_INFO"%s: ST STEP 1 - ST1 ID=%3d\n", + __func__, t->cur_st_tch[CY_ST_FNGR1_IDX]);) + + if ((t->cur_tch > 1) && + (t->cur_st_tch[CY_ST_FNGR2_IDX] > + CY_NUM_TRK_ID)) { + /* reassign st finger2 */ + if (use_trk_id) { + id = CY_MT_TCH2_IDX; + t->cur_st_tch[CY_ST_FNGR2_IDX] = + t->cur_mt_tch[id]; + } else { + id = GET_TOUCH2_ID(xy->touch12_id); + t->cur_st_tch[CY_ST_FNGR2_IDX] = id; + } + t->st_x2 = t->cur_mt_pos[id][CY_XPOS]; + t->st_y2 = t->cur_mt_pos[id][CY_YPOS]; + t->st_z2 = t->cur_mt_z[id]; + + DBG( + printk(KERN_INFO"%s: ST STEP 2 - ST2 ID=%3d\n", + __func__, t->cur_st_tch[CY_ST_FNGR2_IDX]);) + } + } + } else if (t->cur_st_tch[CY_ST_FNGR2_IDX] > CY_NUM_TRK_ID) { + if (t->cur_tch > 1) { + /* reassign st finger2 */ + if (use_trk_id) { + /* reassign st finger2 */ + id = CY_MT_TCH2_IDX; + t->cur_st_tch[CY_ST_FNGR2_IDX] = + t->cur_mt_tch[id]; + } else { + /* reassign st finger2 */ + id = GET_TOUCH2_ID(xy->touch12_id); + t->cur_st_tch[CY_ST_FNGR2_IDX] = id; + } + t->st_x2 = t->cur_mt_pos[id][CY_XPOS]; + t->st_y2 = t->cur_mt_pos[id][CY_YPOS]; + t->st_z2 = t->cur_mt_z[id]; + + DBG(printk(KERN_INFO"%s: ST STEP 3 - ST2 ID=%3d\n", + __func__, t->cur_st_tch[CY_ST_FNGR2_IDX]);) + } + } + /* if the 1st touch is missing and there is a 2nd touch, + * then set the 1st touch to 2nd touch and terminate 2nd touch + */ + if ((t->cur_st_tch[CY_ST_FNGR1_IDX] > CY_NUM_TRK_ID) && + (t->cur_st_tch[CY_ST_FNGR2_IDX] < CY_NUM_TRK_ID)) { + t->st_x1 = t->st_x2; + t->st_y1 = t->st_y2; + t->st_z1 = t->st_z2; + t->cur_st_tch[CY_ST_FNGR1_IDX] = t->cur_st_tch[CY_ST_FNGR2_IDX]; + t->cur_st_tch[CY_ST_FNGR2_IDX] = CY_IGNR_TCH; + } + /* if the 2nd touch ends up equal to the 1st touch, + * then just report a single touch */ + if (t->cur_st_tch[CY_ST_FNGR1_IDX] == t->cur_st_tch[CY_ST_FNGR2_IDX]) + t->cur_st_tch[CY_ST_FNGR2_IDX] = CY_IGNR_TCH; + + /* set Single Touch current event signals */ + if (t->cur_st_tch[CY_ST_FNGR1_IDX] < CY_NUM_TRK_ID) { + input_report_abs(ts->input, ABS_X, t->st_x1); + input_report_abs(ts->input, ABS_Y, t->st_y1); + input_report_abs(ts->input, ABS_PRESSURE, t->st_z1); + input_report_key(ts->input, BTN_TOUCH, CY_TCH); + input_report_abs(ts->input, ABS_TOOL_WIDTH, t->tool_width); + + DBG2(printk(KERN_INFO"%s:ST->F1:%3d X:%3d Y:%3d Z:%3d\n", + __func__, t->cur_st_tch[CY_ST_FNGR1_IDX], + t->st_x1, t->st_y1, t->st_z1);) + + if (t->cur_st_tch[CY_ST_FNGR2_IDX] < CY_NUM_TRK_ID) { + input_report_key(ts->input, BTN_2, CY_TCH); + input_report_abs(ts->input, ABS_HAT0X, t->st_x2); + input_report_abs(ts->input, ABS_HAT0Y, t->st_y2); + + DBG2(printk(KERN_INFO + "%s:ST->F2:%3d X:%3d Y:%3d Z:%3d\n", + __func__, t->cur_st_tch[CY_ST_FNGR2_IDX], + t->st_x2, t->st_y2, t->st_z2);) + } else { + input_report_key(ts->input, BTN_2, CY_NTCH); + } + } else { + input_report_abs(ts->input, ABS_PRESSURE, CY_NTCH); + input_report_key(ts->input, BTN_TOUCH, CY_NTCH); + input_report_key(ts->input, BTN_2, CY_NTCH); + } + /* update platform data for the current single touch info */ + ts->prv_st_tch[CY_ST_FNGR1_IDX] = t->cur_st_tch[CY_ST_FNGR1_IDX]; + ts->prv_st_tch[CY_ST_FNGR2_IDX] = t->cur_st_tch[CY_ST_FNGR2_IDX]; + +} + +static void handle_multi_touch(struct cyttsp_track_data *t, struct cyttsp *ts) +{ + + u8 id; + u8 i, loc; + void (*mt_sync_func)(struct input_dev *) = ts->platform_data->mt_sync; + + if (!ts->platform_data->use_trk_id) + goto no_track_id; + + /* terminate any previous touch where the track + * is missing from the current event */ + for (id = 0; id < CY_NUM_TRK_ID; id++) { + if ((ts->act_trk[id] == CY_NTCH) || (t->cur_trk[id] != CY_NTCH)) + continue; + + input_report_abs(ts->input, ABS_MT_TRACKING_ID, id); + input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, CY_NTCH); + input_report_abs(ts->input, ABS_MT_WIDTH_MAJOR, t->tool_width); + input_report_abs(ts->input, ABS_MT_POSITION_X, + ts->prv_mt_pos[id][CY_XPOS]); + input_report_abs(ts->input, ABS_MT_POSITION_Y, + ts->prv_mt_pos[id][CY_YPOS]); + if (mt_sync_func) + mt_sync_func(ts->input); + ts->act_trk[id] = CY_NTCH; + ts->prv_mt_pos[id][CY_XPOS] = 0; + ts->prv_mt_pos[id][CY_YPOS] = 0; + } + /* set Multi-Touch current event signals */ + for (id = 0; id < CY_NUM_MT_TCH_ID; id++) { + if (t->cur_mt_tch[id] >= CY_NUM_TRK_ID) + continue; + + input_report_abs(ts->input, ABS_MT_TRACKING_ID, + t->cur_mt_tch[id]); + input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, + t->cur_mt_z[id]); + input_report_abs(ts->input, ABS_MT_WIDTH_MAJOR, + t->tool_width); + input_report_abs(ts->input, ABS_MT_POSITION_X, + t->cur_mt_pos[id][CY_XPOS]); + input_report_abs(ts->input, ABS_MT_POSITION_Y, + t->cur_mt_pos[id][CY_YPOS]); + if (mt_sync_func) + mt_sync_func(ts->input); + + ts->act_trk[id] = CY_TCH; + ts->prv_mt_pos[id][CY_XPOS] = t->cur_mt_pos[id][CY_XPOS]; + ts->prv_mt_pos[id][CY_YPOS] = t->cur_mt_pos[id][CY_YPOS]; + } + return; +no_track_id: + + /* set temporary track array elements to voids */ + memset(t->tmp_trk, CY_IGNR_TCH, sizeof(t->tmp_trk)); + memset(t->snd_trk, CY_IGNR_TCH, sizeof(t->snd_trk)); + + /* get what is currently active */ + for (i = id = 0; id < CY_NUM_TRK_ID && i < CY_NUM_MT_TCH_ID; id++) { + if (t->cur_trk[id] == CY_TCH) { + /* only incr counter if track found */ + t->tmp_trk[i] = id; + i++; + } + } + DBG(printk(KERN_INFO"%s: T1: t0=%d, t1=%d, t2=%d, t3=%d\n", __func__, + t->tmp_trk[0], t->tmp_trk[1], + t->tmp_trk[2], t->tmp_trk[3]);) + DBG(printk(KERN_INFO"%s: T1: p0=%d, p1=%d, p2=%d, p3=%d\n", __func__, + ts->prv_mt_tch[0], ts->prv_mt_tch[1], + ts->prv_mt_tch[2], ts->prv_mt_tch[3]);) + + /* pack in still active previous touches */ + for (id = t->prv_tch = 0; id < CY_NUM_MT_TCH_ID; id++) { + if (t->tmp_trk[id] >= CY_NUM_TRK_ID) + continue; + + if (cyttsp_inlist(ts->prv_mt_tch, t->tmp_trk[id], &loc, + CY_NUM_MT_TCH_ID)) { + loc &= CY_NUM_MT_TCH_ID - 1; + t->snd_trk[loc] = t->tmp_trk[id]; + t->prv_tch++; + DBG(printk(KERN_INFO"%s: in list s[%d]=%d " + "t[%d]=%d, loc=%d p=%d\n", __func__, + loc, t->snd_trk[loc], + id, t->tmp_trk[id], + loc, t->prv_tch);) + } else { + DBG(printk(KERN_INFO"%s: is not in list s[%d]=%d" + " t[%d]=%d loc=%d\n", __func__, + id, t->snd_trk[id], + id, t->tmp_trk[id], + loc);) + } + } + DBG(printk(KERN_INFO"%s: S1: s0=%d, s1=%d, s2=%d, s3=%d p=%d\n", + __func__, + t->snd_trk[0], t->snd_trk[1], t->snd_trk[2], + t->snd_trk[3], t->prv_tch);) + + /* pack in new touches */ + for (id = 0; id < CY_NUM_MT_TCH_ID; id++) { + if (t->tmp_trk[id] >= CY_NUM_TRK_ID) + continue; + + if (!cyttsp_inlist(t->snd_trk, t->tmp_trk[id], &loc, + CY_NUM_MT_TCH_ID)) { + + DBG( + printk(KERN_INFO"%s: not in list t[%d]=%d, loc=%d\n", + __func__, + id, t->tmp_trk[id], loc);) + + if (cyttsp_next_avail_inlist(t->snd_trk, &loc, + CY_NUM_MT_TCH_ID)) { + loc &= CY_NUM_MT_TCH_ID - 1; + t->snd_trk[loc] = t->tmp_trk[id]; + DBG(printk(KERN_INFO "%s: put in list s[%d]=%d" + " t[%d]=%d\n", __func__, + loc, + t->snd_trk[loc], id, t->tmp_trk[id]); + ) + } + } else { + DBG(printk(KERN_INFO"%s: is in list s[%d]=%d " + "t[%d]=%d loc=%d\n", __func__, + id, t->snd_trk[id], id, t->tmp_trk[id], loc);) + } + } + DBG(printk(KERN_INFO"%s: S2: s0=%d, s1=%d, s2=%d, s3=%d\n", __func__, + t->snd_trk[0], t->snd_trk[1], + t->snd_trk[2], t->snd_trk[3]);) + + /* sync motion event signals for each current touch */ + for (id = 0; id < CY_NUM_MT_TCH_ID; id++) { + /* z will either be 0 (NOTOUCH) or + * some pressure (TOUCH) + */ + DBG(printk(KERN_INFO "%s: MT0 prev[%d]=%d " + "temp[%d]=%d send[%d]=%d\n", + __func__, id, ts->prv_mt_tch[id], + id, t->tmp_trk[id], id, t->snd_trk[id]);) + + if (ts->platform_data->invert) { + t->cur_mt_pos[t->snd_trk[id]][CY_XPOS] = + INVERT_X(t->cur_mt_pos[t->snd_trk[id]] + [CY_XPOS], ts->platform_data->maxx); + t->cur_mt_pos[t->snd_trk[id]][CY_YPOS] = + INVERT_X(t->cur_mt_pos[t->snd_trk[id]] + [CY_YPOS], ts->platform_data->maxy); + } + if (t->snd_trk[id] < CY_NUM_TRK_ID) { + input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, + t->cur_mt_z[t->snd_trk[id]]); + input_report_abs(ts->input, ABS_MT_WIDTH_MAJOR, + t->tool_width); + input_report_abs(ts->input, ABS_MT_POSITION_X, + t->cur_mt_pos[t->snd_trk[id]][CY_XPOS]); + input_report_abs(ts->input, ABS_MT_POSITION_Y, + t->cur_mt_pos[t->snd_trk[id]][CY_YPOS]); + if (mt_sync_func) + mt_sync_func(ts->input); + + DBG2(printk(KERN_INFO"%s: MT1 -> TID:" + "%3d X:%3d Y:%3d Z:%3d\n", __func__, + t->snd_trk[id], + t->cur_mt_pos[t->snd_trk[id]][CY_XPOS], + t->cur_mt_pos[t->snd_trk[id]][CY_YPOS], + t->cur_mt_z[t->snd_trk[id]]);) + + } else if (ts->prv_mt_tch[id] < CY_NUM_TRK_ID) { + /* void out this touch */ + input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, + CY_NTCH); + input_report_abs(ts->input, ABS_MT_WIDTH_MAJOR, + t->tool_width); + input_report_abs(ts->input, ABS_MT_POSITION_X, + ts->prv_mt_pos[ts->prv_mt_tch[id]][CY_XPOS]); + input_report_abs(ts->input, ABS_MT_POSITION_Y, + ts->prv_mt_pos[ts->prv_mt_tch[id]][CY_YPOS]); + + if (mt_sync_func) + mt_sync_func(ts->input); + + DBG2(printk(KERN_INFO"%s: " + "MT2->TID:%2d X:%3d Y:%3d Z:%3d liftoff-sent\n", + __func__, ts->prv_mt_tch[id], + ts->prv_mt_pos[ts->prv_mt_tch[id]][CY_XPOS], + ts->prv_mt_pos[ts->prv_mt_tch[id]][CY_YPOS], + CY_NTCH);) + } else { + /* do not stuff any signals for this + * previously and currently void touches + */ + DBG(printk(KERN_INFO"%s: " + "MT3->send[%d]=%d - No touch - NOT sent\n", + __func__, id, t->snd_trk[id]);) + } + } + + /* save current posted tracks to + * previous track memory */ + for (id = 0; id < CY_NUM_MT_TCH_ID; id++) { + ts->prv_mt_tch[id] = t->snd_trk[id]; + if (t->snd_trk[id] < CY_NUM_TRK_ID) { + ts->prv_mt_pos[t->snd_trk[id]][CY_XPOS] = + t->cur_mt_pos[t->snd_trk[id]][CY_XPOS]; + ts->prv_mt_pos[t->snd_trk[id]][CY_YPOS] = + t->cur_mt_pos[t->snd_trk[id]][CY_YPOS]; + DBG(printk(KERN_INFO"%s: " + "MT4->TID:%2d X:%3d Y:%3d Z:%3d save for prv\n", + __func__, t->snd_trk[id], + ts->prv_mt_pos[t->snd_trk[id]][CY_XPOS], + ts->prv_mt_pos[t->snd_trk[id]][CY_YPOS], + CY_NTCH);) + } + } + memset(ts->act_trk, CY_NTCH, sizeof(ts->act_trk)); + for (id = 0; id < CY_NUM_MT_TCH_ID; id++) { + if (t->snd_trk[id] < CY_NUM_TRK_ID) + ts->act_trk[t->snd_trk[id]] = CY_TCH; + } +} + +static void cyttsp_xy_worker(struct cyttsp *ts) +{ + struct cyttsp_xydata xy_data; + u8 id, tilt, rev_x, rev_y; + struct cyttsp_track_data trc; + s32 retval; + + DBG(printk(KERN_INFO "%s: Enter\n", __func__);) + /* get event data from CYTTSP device */ + retval = ttsp_read_block_data(ts, CY_REG_BASE, + sizeof(xy_data), &xy_data); + + if (retval < 0) { + printk(KERN_ERR "%s: Error, fail to read device on host interface bus\n", + __func__); + goto exit_xy_worker; + } + + if (CY_MODE_ERROR(ts->platform_data->power_state, + xy_data.hst_mode, xy_data.tt_mode)) { + /* TTSP device has switched to non-operational mode */ + printk(KERN_ERR "%s: Error, bad mode ps=%d hm=%02X tm=%02X\n", + __func__, ts->platform_data->power_state, + xy_data.hst_mode, xy_data.tt_mode); + retval = cyttsp_power_on(ts); + if (retval < 0) + printk(KERN_ERR "%s: Error, power on fail\n", __func__); + goto exit_xy_worker; + } + + /* touch extension handling */ + retval = ttsp_tch_ext(ts, &xy_data); + + if (retval < 0) { + printk(KERN_ERR "%s: Error, touch extension handling\n", + __func__); + goto exit_xy_worker; + } else if (retval > 0) { + DBG(printk(KERN_INFO "%s: Touch extension handled\n", + __func__);) + goto exit_xy_worker; + } + + /* provide flow control handshake */ + if (ts->irq) { + if (ts->platform_data->use_hndshk) { + u8 cmd; + cmd = xy_data.hst_mode & CY_HNDSHK_BIT ? + xy_data.hst_mode & ~CY_HNDSHK_BIT : + xy_data.hst_mode | CY_HNDSHK_BIT; + retval = ttsp_write_block_data(ts, CY_REG_BASE, + sizeof(cmd), (u8 *)&cmd); + } + } + trc.cur_tch = GET_NUM_TOUCHES(xy_data.tt_stat); + + if (IS_LARGE_AREA(xy_data.tt_stat) == 1) { + /* terminate all active tracks */ + trc.cur_tch = CY_NTCH; + DBG(printk(KERN_INFO "%s: Large area detected\n", + __func__);) + } else if (trc.cur_tch > CY_MAX_TCH) { + /* terminate all active tracks */ + trc.cur_tch = CY_NTCH; + DBG(printk(KERN_INFO "%s: Num touch error detected\n", + __func__);) + } else if (IS_BAD_PKT(xy_data.tt_mode)) { + /* terminate all active tracks */ + trc.cur_tch = CY_NTCH; + DBG(printk(KERN_INFO "%s: Invalid buffer detected\n", + __func__);) + } + + /* set tool size */ + trc.tool_width = CY_SMALL_TOOL_WIDTH; + + if (ts->platform_data->gen == CY_GEN2) { + /* translate Gen2 interface data into comparable Gen3 data */ + trc.cur_tch = ttsp_convert_gen2(trc.cur_tch, &xy_data); + } + + /* clear current active track ID array and count previous touches */ + for (id = 0, trc.prv_tch = CY_NTCH; id < CY_NUM_TRK_ID; id++) { + trc.cur_trk[id] = CY_NTCH; + trc.prv_tch += ts->act_trk[id]; + } + + /* + * send no events if there were no + * previous touches and no new touches + */ + if ((trc.prv_tch == CY_NTCH) && ((trc.cur_tch == CY_NTCH) || + (trc.cur_tch > CY_MAX_TCH))) { + if (++ts->ntch_count > CY_MAX_NTCH) { + /* TTSP device has a stuck operational mode */ + printk(KERN_ERR "%s: Error, stuck no-touch ct=%d\n", + __func__, trc.cur_tch); + retval = cyttsp_power_on(ts); + if (retval < 0) + printk(KERN_ERR "%s: Error, power on fail\n", + __func__); + goto exit_xy_worker; + } + } else + ts->ntch_count = 0; + + DBG(printk(KERN_INFO "%s: prev=%d curr=%d\n", __func__, + trc.prv_tch, trc.cur_tch);) + + /* clear current single-touch array */ + memset(trc.cur_st_tch, CY_IGNR_TCH, sizeof(trc.cur_st_tch)); + + /* clear single touch positions */ + trc.st_x1 = trc.st_y1 = trc.st_z1 = + trc.st_x2 = trc.st_y2 = trc.st_z2 = CY_NTCH; + + /* clear current multi-touch arrays */ + memset(trc.cur_mt_tch, CY_IGNR_TCH, sizeof(trc.cur_mt_tch)); + memset(trc.cur_mt_pos, CY_NTCH, sizeof(trc.cur_mt_pos)); + memset(trc.cur_mt_z, CY_NTCH, sizeof(trc.cur_mt_z)); + + DBG( + if (trc.cur_tch) { + unsigned i; + u8 *pdata = (u8 *)&xy_data; + + printk(KERN_INFO "%s: TTSP data_pack: ", __func__); + for (i = 0; i < sizeof(struct cyttsp_xydata); i++) + printk(KERN_INFO "[%d]=0x%x ", i, pdata[i]); + printk(KERN_INFO "\n"); + }) + + /* Determine if display is tilted */ + tilt = !!FLIP_DATA(ts->platform_data->flags); + /* Check for switch in origin */ + rev_x = !!REVERSE_X(ts->platform_data->flags); + rev_y = !!REVERSE_Y(ts->platform_data->flags); + + /* process the touches */ + switch (trc.cur_tch) { + case 4: + xy_data.x4 = be16_to_cpu(xy_data.x4); + xy_data.y4 = be16_to_cpu(xy_data.y4); + if (tilt) + FLIP_XY(xy_data.x4, xy_data.y4); + + if (rev_x) + xy_data.x4 = INVERT_X(xy_data.x4, + ts->platform_data->maxx); + if (rev_y) + xy_data.y4 = INVERT_X(xy_data.y4, + ts->platform_data->maxy); + + id = GET_TOUCH4_ID(xy_data.touch34_id); + if (ts->platform_data->use_trk_id) { + trc.cur_mt_pos[CY_MT_TCH4_IDX][CY_XPOS] = xy_data.x4; + trc.cur_mt_pos[CY_MT_TCH4_IDX][CY_YPOS] = xy_data.y4; + trc.cur_mt_z[CY_MT_TCH4_IDX] = xy_data.z4; + } else { + trc.cur_mt_pos[id][CY_XPOS] = xy_data.x4; + trc.cur_mt_pos[id][CY_YPOS] = xy_data.y4; + trc.cur_mt_z[id] = xy_data.z4; + } + trc.cur_mt_tch[CY_MT_TCH4_IDX] = id; + trc.cur_trk[id] = CY_TCH; + if (ts->prv_st_tch[CY_ST_FNGR1_IDX] < CY_NUM_TRK_ID) { + if (ts->prv_st_tch[CY_ST_FNGR1_IDX] == id) { + trc.st_x1 = xy_data.x4; + trc.st_y1 = xy_data.y4; + trc.st_z1 = xy_data.z4; + trc.cur_st_tch[CY_ST_FNGR1_IDX] = id; + } else if (ts->prv_st_tch[CY_ST_FNGR2_IDX] == id) { + trc.st_x2 = xy_data.x4; + trc.st_y2 = xy_data.y4; + trc.st_z2 = xy_data.z4; + trc.cur_st_tch[CY_ST_FNGR2_IDX] = id; + } + } + DBG(printk(KERN_INFO"%s: 4th XYZ:% 3d,% 3d,% 3d ID:% 2d\n\n", + __func__, xy_data.x4, xy_data.y4, xy_data.z4, + (xy_data.touch34_id & 0x0F));) + + /* do not break */ + case 3: + xy_data.x3 = be16_to_cpu(xy_data.x3); + xy_data.y3 = be16_to_cpu(xy_data.y3); + if (tilt) + FLIP_XY(xy_data.x3, xy_data.y3); + + if (rev_x) + xy_data.x3 = INVERT_X(xy_data.x3, + ts->platform_data->maxx); + if (rev_y) + xy_data.y3 = INVERT_X(xy_data.y3, + ts->platform_data->maxy); + + id = GET_TOUCH3_ID(xy_data.touch34_id); + if (ts->platform_data->use_trk_id) { + trc.cur_mt_pos[CY_MT_TCH3_IDX][CY_XPOS] = xy_data.x3; + trc.cur_mt_pos[CY_MT_TCH3_IDX][CY_YPOS] = xy_data.y3; + trc.cur_mt_z[CY_MT_TCH3_IDX] = xy_data.z3; + } else { + trc.cur_mt_pos[id][CY_XPOS] = xy_data.x3; + trc.cur_mt_pos[id][CY_YPOS] = xy_data.y3; + trc.cur_mt_z[id] = xy_data.z3; + } + trc.cur_mt_tch[CY_MT_TCH3_IDX] = id; + trc.cur_trk[id] = CY_TCH; + if (ts->prv_st_tch[CY_ST_FNGR1_IDX] < CY_NUM_TRK_ID) { + if (ts->prv_st_tch[CY_ST_FNGR1_IDX] == id) { + trc.st_x1 = xy_data.x3; + trc.st_y1 = xy_data.y3; + trc.st_z1 = xy_data.z3; + trc.cur_st_tch[CY_ST_FNGR1_IDX] = id; + } else if (ts->prv_st_tch[CY_ST_FNGR2_IDX] == id) { + trc.st_x2 = xy_data.x3; + trc.st_y2 = xy_data.y3; + trc.st_z2 = xy_data.z3; + trc.cur_st_tch[CY_ST_FNGR2_IDX] = id; + } + } + DBG(printk(KERN_INFO"%s: 3rd XYZ:% 3d,% 3d,% 3d ID:% 2d\n", + __func__, xy_data.x3, xy_data.y3, xy_data.z3, + ((xy_data.touch34_id >> 4) & 0x0F));) + + /* do not break */ + case 2: + xy_data.x2 = be16_to_cpu(xy_data.x2); + xy_data.y2 = be16_to_cpu(xy_data.y2); + if (tilt) + FLIP_XY(xy_data.x2, xy_data.y2); + + if (rev_x) + xy_data.x2 = INVERT_X(xy_data.x2, + ts->platform_data->maxx); + if (rev_y) + xy_data.y2 = INVERT_X(xy_data.y2, + ts->platform_data->maxy); + id = GET_TOUCH2_ID(xy_data.touch12_id); + if (ts->platform_data->use_trk_id) { + trc.cur_mt_pos[CY_MT_TCH2_IDX][CY_XPOS] = xy_data.x2; + trc.cur_mt_pos[CY_MT_TCH2_IDX][CY_YPOS] = xy_data.y2; + trc.cur_mt_z[CY_MT_TCH2_IDX] = xy_data.z2; + } else { + trc.cur_mt_pos[id][CY_XPOS] = xy_data.x2; + trc.cur_mt_pos[id][CY_YPOS] = xy_data.y2; + trc.cur_mt_z[id] = xy_data.z2; + } + trc.cur_mt_tch[CY_MT_TCH2_IDX] = id; + trc.cur_trk[id] = CY_TCH; + if (ts->prv_st_tch[CY_ST_FNGR1_IDX] < CY_NUM_TRK_ID) { + if (ts->prv_st_tch[CY_ST_FNGR1_IDX] == id) { + trc.st_x1 = xy_data.x2; + trc.st_y1 = xy_data.y2; + trc.st_z1 = xy_data.z2; + trc.cur_st_tch[CY_ST_FNGR1_IDX] = id; + } else if (ts->prv_st_tch[CY_ST_FNGR2_IDX] == id) { + trc.st_x2 = xy_data.x2; + trc.st_y2 = xy_data.y2; + trc.st_z2 = xy_data.z2; + trc.cur_st_tch[CY_ST_FNGR2_IDX] = id; + } + } + DBG(printk(KERN_INFO"%s: 2nd XYZ:% 3d,% 3d,% 3d ID:% 2d\n", + __func__, xy_data.x2, xy_data.y2, xy_data.z2, + (xy_data.touch12_id & 0x0F));) + + /* do not break */ + case 1: + xy_data.x1 = be16_to_cpu(xy_data.x1); + xy_data.y1 = be16_to_cpu(xy_data.y1); + if (tilt) + FLIP_XY(xy_data.x1, xy_data.y1); + + if (rev_x) + xy_data.x1 = INVERT_X(xy_data.x1, + ts->platform_data->maxx); + if (rev_y) + xy_data.y1 = INVERT_X(xy_data.y1, + ts->platform_data->maxy); + + id = GET_TOUCH1_ID(xy_data.touch12_id); + if (ts->platform_data->use_trk_id) { + trc.cur_mt_pos[CY_MT_TCH1_IDX][CY_XPOS] = xy_data.x1; + trc.cur_mt_pos[CY_MT_TCH1_IDX][CY_YPOS] = xy_data.y1; + trc.cur_mt_z[CY_MT_TCH1_IDX] = xy_data.z1; + } else { + trc.cur_mt_pos[id][CY_XPOS] = xy_data.x1; + trc.cur_mt_pos[id][CY_YPOS] = xy_data.y1; + trc.cur_mt_z[id] = xy_data.z1; + } + trc.cur_mt_tch[CY_MT_TCH1_IDX] = id; + trc.cur_trk[id] = CY_TCH; + if (ts->prv_st_tch[CY_ST_FNGR1_IDX] < CY_NUM_TRK_ID) { + if (ts->prv_st_tch[CY_ST_FNGR1_IDX] == id) { + trc.st_x1 = xy_data.x1; + trc.st_y1 = xy_data.y1; + trc.st_z1 = xy_data.z1; + trc.cur_st_tch[CY_ST_FNGR1_IDX] = id; + } else if (ts->prv_st_tch[CY_ST_FNGR2_IDX] == id) { + trc.st_x2 = xy_data.x1; + trc.st_y2 = xy_data.y1; + trc.st_z2 = xy_data.z1; + trc.cur_st_tch[CY_ST_FNGR2_IDX] = id; + } + } + DBG(printk(KERN_INFO"%s: S1st XYZ:% 3d,% 3d,% 3d ID:% 2d\n", + __func__, xy_data.x1, xy_data.y1, xy_data.z1, + ((xy_data.touch12_id >> 4) & 0x0F));) + + break; + case 0: + default: + break; + } + + if (ts->platform_data->use_st) + handle_single_touch(&xy_data, &trc, ts); + + if (ts->platform_data->use_mt) + handle_multi_touch(&trc, ts); + + /* handle gestures */ + if (ts->platform_data->use_gestures && xy_data.gest_id) { + input_report_key(ts->input, BTN_3, CY_TCH); + input_report_abs(ts->input, ABS_HAT1X, xy_data.gest_id); + input_report_abs(ts->input, ABS_HAT1Y, xy_data.gest_cnt); + } + + /* signal the view motion event */ + input_sync(ts->input); + + /* update platform data for the current multi-touch information */ + memcpy(ts->act_trk, trc.cur_trk, CY_NUM_TRK_ID); + +exit_xy_worker: + /* reset the watchdog */ + mod_timer(&ts->timer, jiffies + CY_WDG_TIMEOUT); + DBG(printk(KERN_INFO"%s: finished.\n", __func__);) + return; +} + +static irqreturn_t cyttsp_irq(int irq, void *handle) +{ + struct cyttsp *ts = (struct cyttsp *)handle; + + DBG(printk(KERN_INFO"%s: Got IRQ!\n", __func__);) + switch (ts->platform_data->power_state) { + case CY_BL_STATE: + case CY_SYSINFO_STATE: + complete(&ts->int_running); + break; + case CY_LDR_STATE: + ts->bl_ready = true; + break; + case CY_ACTIVE_STATE: + cyttsp_xy_worker(ts); + break; + case CY_READY_STATE: + default: + break; + } + + return IRQ_HANDLED; +} + +/* Timer function used as watchdog */ +static void cyttsp_timer(unsigned long handle) +{ + struct cyttsp *ts = (struct cyttsp *)handle; + + DBG(printk(KERN_INFO"%s: Watchdog timer event\n", __func__);) + if (!work_pending(&ts->work)) + schedule_work(&ts->work); + return; +} +/* + ************************************************************************ + * Probe initialization functions + ************************************************************************ + */ +static int cyttsp_load_bl_regs(struct cyttsp *ts) +{ + int retval; + + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + + memset(&ts->bl_data, 0, sizeof(ts->bl_data)); + + retval = ttsp_read_block_data(ts, CY_REG_BASE, + sizeof(ts->bl_data), &(ts->bl_data)); + + if (retval < 0) { + DBG2(printk(KERN_INFO "%s: Failed reading block data, err:%d\n", + __func__, retval);) + goto fail; + } + + DBG({ + int i; + u8 *bl_data = (u8 *)&(ts->bl_data); + for (i = 0; i < sizeof(struct cyttsp_bootloader_data); i++) + printk(KERN_INFO "%s bl_data[%d]=0x%x\n", + __func__, i, bl_data[i]); + }) + DBG2(printk(KERN_INFO "%s: bl f=%02X s=%02X e=%02X\n", + __func__, + ts->bl_data.bl_file, + ts->bl_data.bl_status, + ts->bl_data.bl_error);) + + return 0; +fail: + return retval; +} + +static bool cyttsp_bl_app_valid(struct cyttsp *ts) +{ + int retval; + + ts->bl_data.bl_status = 0x00; + + retval = cyttsp_load_bl_regs(ts); + + if (retval < 0) + return false; + + if (ts->bl_data.bl_status == 0x11) { + printk(KERN_INFO "%s: App found; normal boot\n", __func__); + return true; + } else if (ts->bl_data.bl_status == 0x10) { + printk(KERN_INFO "%s: NO APP; load firmware!!\n", __func__); + return false; + } else { + if (ts->bl_data.bl_file == CY_OPERATE_MODE) { + int invalid_op_mode_status; + invalid_op_mode_status = ts->bl_data.bl_status & 0x3f; + if (invalid_op_mode_status) + return false; + else { + if (ts->platform_data->power_state == + CY_ACTIVE_STATE) + printk(KERN_INFO "%s: Operational\n", + __func__); + else + printk(KERN_INFO "%s: No bootloader\n", + __func__); + } + } + return true; + } +} + +static int cyttsp_exit_bl_mode(struct cyttsp *ts) +{ + int retval; + int tries = 0; + + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + + /* check if in bootloader mode; + * if in operational mode then just return without fail + */ + cyttsp_load_bl_regs(ts); + if (!GET_BOOTLOADERMODE(ts->bl_data.bl_status)) + return 0; + + retval = ttsp_write_block_data(ts, CY_REG_BASE, sizeof(bl_cmd), + (void *)bl_cmd); + if (retval < 0) { + printk(KERN_ERR "%s: Failed writing block data, err:%d\n", + __func__, retval); + goto fail; + } + do { + msleep(20); + cyttsp_load_bl_regs(ts); + } while (GET_BOOTLOADERMODE(ts->bl_data.bl_status) && tries++ < 10); + + DBG2(printk(KERN_INFO "%s: read tries=%d\n", __func__, tries);) + + DBG(printk(KERN_INFO"%s: Exit " + "f=%02X s=%02X e=%02X\n", + __func__, + ts->bl_data.bl_file, ts->bl_data.bl_status, + ts->bl_data.bl_error);) + + return 0; +fail: + return retval; +} + +static int cyttsp_set_sysinfo_mode(struct cyttsp *ts) +{ + int retval; + u8 cmd = CY_SYSINFO_MODE; + + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + memset(&(ts->sysinfo_data), 0, sizeof(struct cyttsp_sysinfo_data)); + + /* switch to sysinfo mode */ + retval = ttsp_write_block_data(ts, CY_REG_BASE, sizeof(cmd), &cmd); + if (retval < 0) { + printk(KERN_ERR "%s: Failed writing block data, err:%d\n", + __func__, retval); + goto exit_sysinfo_mode; + } + + if (!(retval < 0)) { + /* wait for interrupt to set ready completion */ + ts->platform_data->power_state = CY_SYSINFO_STATE; + INIT_COMPLETION(ts->int_running); + retval = wait_for_completion_interruptible_timeout( + &ts->int_running, + msecs_to_jiffies(CY_DELAY_DFLT * CY_DELAY_MAX)); + ts->platform_data->power_state = CY_READY_STATE; + } + + if (retval < 0) { + ts->platform_data->power_state = CY_IDLE_STATE; + printk(KERN_ERR "%s: reset timeout fail (ret=%d)\n", + __func__, retval); + } + +exit_sysinfo_mode: + /* read sysinfo registers */ + retval = ttsp_read_block_data(ts, CY_REG_BASE, + sizeof(ts->sysinfo_data), &(ts->sysinfo_data)); + + DBG(printk(KERN_INFO"%s:SI2: hst_mode=0x%02X mfg_cmd=0x%02X " + "mfg_stat=0x%02X\n", __func__, ts->sysinfo_data.hst_mode, + ts->sysinfo_data.mfg_cmd, + ts->sysinfo_data.mfg_stat);) + + DBG(printk(KERN_INFO"%s:SI2: bl_ver=0x%02X%02X\n", + __func__, ts->sysinfo_data.bl_verh, ts->sysinfo_data.bl_verl);) + + DBG(printk(KERN_INFO"%s:SI2: sysinfo act_intrvl=0x%02X " + "tch_tmout=0x%02X lp_intrvl=0x%02X\n", + __func__, ts->sysinfo_data.act_intrvl, + ts->sysinfo_data.tch_tmout, + ts->sysinfo_data.lp_intrvl);) + + printk(KERN_INFO"%s:SI2:tts_ver=%02X%02X app_id=%02X%02X " + "app_ver=%02X%02X c_id=%02X%02X%02X " + "u_id=%02X%02X%02X%02X%02X%02X%02X%02X\n", + __func__, + ts->sysinfo_data.tts_verh, ts->sysinfo_data.tts_verl, + ts->sysinfo_data.app_idh, ts->sysinfo_data.app_idl, + ts->sysinfo_data.app_verh, ts->sysinfo_data.app_verl, + ts->sysinfo_data.cid[0], ts->sysinfo_data.cid[1], + ts->sysinfo_data.cid[2], + ts->sysinfo_data.uid[0], ts->sysinfo_data.uid[1], + ts->sysinfo_data.uid[2], ts->sysinfo_data.uid[3], + ts->sysinfo_data.uid[4], ts->sysinfo_data.uid[5], + ts->sysinfo_data.uid[6], ts->sysinfo_data.uid[7]); + + return retval; +} + +static int cyttsp_set_sysinfo_regs(struct cyttsp *ts) +{ + int retval = 0; + + if (ts->platform_data->scn_typ != CY_SCN_TYP_DFLT) { + u8 scn_typ = ts->platform_data->scn_typ; + + retval = ttsp_write_block_data(ts, + CY_REG_SCN_TYP, + sizeof(scn_typ), &scn_typ); + } + + if (retval < 0) + return retval; + + if ((ts->platform_data->act_intrvl != CY_ACT_INTRVL_DFLT) || + (ts->platform_data->tch_tmout != CY_TCH_TMOUT_DFLT) || + (ts->platform_data->lp_intrvl != CY_LP_INTRVL_DFLT)) { + + u8 intrvl_ray[3]; + + intrvl_ray[0] = ts->platform_data->act_intrvl; + intrvl_ray[1] = ts->platform_data->tch_tmout; + intrvl_ray[2] = ts->platform_data->lp_intrvl; + + /* set intrvl registers */ + retval = ttsp_write_block_data(ts, + CY_REG_ACT_INTRVL, + sizeof(intrvl_ray), intrvl_ray); + + msleep(CY_DELAY_SYSINFO); + } + + return retval; +} + +static int cyttsp_set_operational_mode(struct cyttsp *ts) +{ + int retval; + int tries; + u8 cmd = CY_OPERATE_MODE; + u8 gest_default; + struct cyttsp_xydata xy_data; + + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + + retval = ttsp_write_block_data(ts, CY_REG_BASE, sizeof(cmd), &cmd); + if (retval < 0) { + printk(KERN_ERR "%s: Failed writing block data, err:%d\n", + __func__, retval); + goto write_block_data_fail; + } + + /* wait for TTSP Device to complete switch to Operational mode */ + msleep(20); + + tries = 0; + gest_default = + CY_GEST_GRP1 + CY_GEST_GRP2 + + CY_GEST_GRP3 + CY_GEST_GRP4 + + CY_ACT_DIST_DFLT; + tries = 0; + do { + msleep(CY_DELAY_DFLT); + memset(&xy_data, 0, sizeof(xy_data)); + retval = ttsp_read_block_data(ts, CY_REG_BASE, + sizeof(struct cyttsp_xydata), &xy_data); + } while (!((retval == 0) && + ((xy_data.gest_set & CY_ACT_DIST_BITS) == + (CY_ACT_DIST_DFLT & CY_ACT_DIST_BITS))) && + (tries++ < CY_DELAY_MAX)); + + DBG2(printk(KERN_INFO "%s: read tries=%d\n", __func__, tries);) + + return 0; +write_block_data_fail: + return retval; +} + +static int cyttsp_soft_reset(struct cyttsp *ts) +{ + int retval; + u8 cmd = CY_SOFT_RESET_MODE; + + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + + /* reset TTSP Device back to bootloader mode */ + ts->platform_data->power_state = CY_BL_STATE; + retval = ttsp_write_block_data(ts, CY_REG_BASE, sizeof(cmd), &cmd); + + if (!(retval < 0)) { + /* wait for interrupt to set ready completion */ + INIT_COMPLETION(ts->int_running); + retval = wait_for_completion_interruptible_timeout( + &ts->int_running, + msecs_to_jiffies(CY_DELAY_DFLT * CY_DELAY_MAX)); + } + + if (retval < 0) { + ts->platform_data->power_state = CY_IDLE_STATE; + printk(KERN_ERR "%s: reset timeout fail (ret=%d)\n", + __func__, retval); + } + + if (retval > 0) + retval = 0; + + return retval; +} + +static int cyttsp_gesture_setup(struct cyttsp *ts) +{ + int retval; + u8 gesture_setup; + + DBG(printk(KERN_INFO"%s: Init gesture; active distance setup\n", + __func__);) + + gesture_setup = ts->platform_data->gest_set; + retval = ttsp_write_block_data(ts, CY_REG_GEST_SET, + sizeof(gesture_setup), &gesture_setup); + + return retval; +} + +#ifdef CY_INCLUDE_LOAD_RECS +#include "cyttsp_ldr.h" +#else +static bool cyttsp_bl_status(struct cyttsp *ts) +{ + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + return ((ts->bl_data.bl_status == 0x10) || + (ts->bl_data.bl_status == 0x11)); +} + +static int cyttsp_loader(struct cyttsp *ts) +{ + void *fptr = cyttsp_bl_status; /* kill warning */ + + if (ts) { + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + DBG(printk(KERN_INFO"%s: Exit\n", __func__);) + } + + if (!fptr) + return -EIO; + else + return 0; +} +#endif + +static int cyttsp_power_on(struct cyttsp *ts) +{ + int retval = 0; + + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + + /* (re-)start watchdog */ + mod_timer(&ts->timer, jiffies + CY_WDG_TIMEOUT); + + cyttsp_init_tch(ts); + retval = cyttsp_soft_reset(ts); + DBG2(printk(KERN_INFO"%s: after softreset r=%d\n", __func__, retval);) + if (retval < 0) + goto bypass; + + if (ts->platform_data->use_load_file) + retval = cyttsp_loader(ts); + + if (!cyttsp_bl_app_valid(ts)) { + retval = 1; + goto bypass; + } + + retval = cyttsp_exit_bl_mode(ts); + DBG2(printk(KERN_INFO"%s: after exit bl r=%d\n", __func__, retval);) + + if (retval < 0) + goto bypass; + + /* switch to System Information mode to read */ + /* versions and set interval registers */ + ts->platform_data->power_state = CY_READY_STATE; + retval = cyttsp_set_sysinfo_mode(ts); + if (retval < 0) + goto bypass; + + retval = cyttsp_set_sysinfo_regs(ts); + if (retval < 0) + goto bypass; + + /* switch back to Operational mode */ + DBG2(printk(KERN_INFO"%s: switch back to operational mode\n", + __func__);) + retval = cyttsp_set_operational_mode(ts); + if (retval < 0) + goto bypass; + + /* init gesture setup; required for active distance */ + cyttsp_gesture_setup(ts); + +bypass: + if (retval) + ts->platform_data->power_state = CY_IDLE_STATE; + else + ts->platform_data->power_state = CY_ACTIVE_STATE; + + printk(KERN_INFO"%s: Power state is %s\n", + __func__, (ts->platform_data->power_state == CY_ACTIVE_STATE) ? + "ACTIVE" : "IDLE"); + return retval; +} + +static void cyttsp_check_bl(struct work_struct *work) +{ + struct cyttsp *ts = container_of(work, struct cyttsp, work); + s32 retval; + struct cyttsp_xydata xy_data; + + retval = ttsp_read_block_data(ts, CY_REG_BASE, 2, &xy_data); + if (retval < 0) { + printk(KERN_ERR "%s: Error, fail to read device\n", __func__); + goto reserve_next; + } + + if (CY_MODE_ERROR(ts->platform_data->power_state, + xy_data.hst_mode, xy_data.tt_mode)) { + printk(KERN_ERR "%s: Error, mode error detected " + "on watchdog timeout ps=%d mode=%02X bl_mode=%02X\n", + __func__, ts->platform_data->power_state, + xy_data.hst_mode, xy_data.tt_mode); + retval = cyttsp_power_on(ts); + if (retval < 0) + printk(KERN_ERR "%s: Error, power on fail\n", __func__); + } + +reserve_next: + mod_timer(&ts->timer, jiffies + CY_WDG_TIMEOUT); +} + +static int cyttsp_resume(struct cyttsp *ts) +{ + int retval = 0; + struct cyttsp_xydata xydata; + + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + if (ts->platform_data->use_sleep && (ts->platform_data->power_state != + CY_ACTIVE_STATE)) { + if (ts->platform_data->wakeup) { + retval = ts->platform_data->wakeup(); + if (retval < 0) + printk(KERN_ERR "%s: Error, wakeup failed!\n", + __func__); + } else { + printk(KERN_ERR "%s: Error, wakeup not implemented " + "(check board file).\n", __func__); + retval = -ENOSYS; + } + if (!(retval < 0)) { + retval = ttsp_read_block_data(ts, CY_REG_BASE, + sizeof(xydata), &xydata); + if (!(retval < 0) && !GET_HSTMODE(xydata.hst_mode)) + ts->platform_data->power_state = + CY_ACTIVE_STATE; + } + } + DBG(printk(KERN_INFO"%s: Wake Up %s\n", __func__, + (retval < 0) ? "FAIL" : "PASS");) + return retval; +} + +static int cyttsp_suspend(struct cyttsp *ts) +{ + u8 sleep_mode = 0; + int retval = 0; + + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + if (ts->platform_data->use_sleep && + (ts->platform_data->power_state == CY_ACTIVE_STATE)) { + sleep_mode = CY_DEEP_SLEEP_MODE; + retval = ttsp_write_block_data(ts, + CY_REG_BASE, sizeof(sleep_mode), &sleep_mode); + if (!(retval < 0)) + ts->platform_data->power_state = CY_SLEEP_STATE; + } + DBG(printk(KERN_INFO"%s: Sleep Power state is %s\n", __func__, + (ts->platform_data->power_state == CY_ACTIVE_STATE) ? + "ACTIVE" : + ((ts->platform_data->power_state == CY_SLEEP_STATE) ? + "SLEEP" : "LOW POWER"));) + return retval; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void cyttsp_ts_early_suspend(struct early_suspend *h) +{ + struct cyttsp *ts = container_of(h, struct cyttsp, early_suspend); + + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + LOCK(ts->mutex); + if (!ts->fw_loader_mode) { + disable_irq_nosync(ts->irq); + ts->suspended = 1; + /* kill watchdog */ + del_timer(&ts->timer); + cancel_work_sync(&ts->work); + cyttsp_suspend(ts); + } + regulator_disable(ts->regulator); + UNLOCK(ts->mutex); +} + +static void cyttsp_ts_late_resume(struct early_suspend *h) +{ + struct cyttsp *ts = container_of(h, struct cyttsp, early_suspend); + + regulator_enable(ts->regulator); + + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + LOCK(ts->mutex); + if (!ts->fw_loader_mode && ts->suspended) { + ts->suspended = 0; + if (cyttsp_resume(ts) < 0) + printk(KERN_ERR "%s: Error, cyttsp_resume.\n", + __func__); + enable_irq(ts->irq); + /* resume watchdog */ + mod_timer(&ts->timer, jiffies + CY_WDG_TIMEOUT); + } + UNLOCK(ts->mutex); +} +#endif + +static int cyttsp_wr_reg(struct cyttsp *ts, u8 reg_id, u8 reg_data) +{ + + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + + return ttsp_write_block_data(ts, + CY_REG_BASE + reg_id, sizeof(u8), ®_data); +} + +static int cyttsp_rd_reg(struct cyttsp *ts, u8 reg_id, u8 *reg_data) +{ + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + return ttsp_read_block_data(ts, + CY_REG_BASE + reg_id, sizeof(u8), reg_data); +} + +static ssize_t firmware_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t pos, size_t size) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct cyttsp *ts = dev_get_drvdata(dev); + LOCK(ts->mutex); + if (!ts->fw_loader_mode) { + unsigned short val = *(unsigned short *)buf; + u8 reg_data = val & 0xff; + ts->reg_id = (val & 0xff00) >> 8; + if (!(ts->reg_id & 0x80)) { + /* write user specified operational register */ + if (ts->reg_id == 0x00) { + switch (GET_HSTMODE(reg_data)) { + case GET_HSTMODE(CY_OPERATE_MODE): + cyttsp_wr_reg(ts, ts->reg_id, reg_data); + ts->platform_data->power_state = + CY_ACTIVE_STATE; + enable_irq(ts->irq); + mod_timer(&ts->timer, CY_WDG_TIMEOUT); + printk(KERN_INFO "%s: " + "Switch to Operational Mode " + "ps=%d\n", __func__, + ts->platform_data->power_state); + break; + case GET_HSTMODE(CY_SYSINFO_MODE): + ts->platform_data->power_state = + CY_READY_STATE; + disable_irq_nosync(ts->irq); + /* kill watchdog */ + del_timer(&ts->timer); + cancel_work_sync(&ts->work); + cyttsp_wr_reg(ts, ts->reg_id, reg_data); + printk(KERN_INFO "%s: " + "Switch to SysInfo Mode " + "ps=%d\n", __func__, + ts->platform_data->power_state); + break; + default: + cyttsp_wr_reg(ts, ts->reg_id, reg_data); + break; + } + } else + cyttsp_wr_reg(ts, ts->reg_id, reg_data); + printk(KERN_INFO "%s: " + "write(reg=%02X(%d) dat=0x%02X(%d))\n", + __func__, + ts->reg_id & ~0x80, ts->reg_id & ~0x80, + reg_data, reg_data); + } else { + /* save user specified operational read register */ + DBG2(printk(KERN_INFO "%s: read(r=0x%02X)\n", + __func__, ts->reg_id);) + } + } else { + int retval = 0; + int tries = 0; + DBG({ + char str[128]; + char *p = str; + int i; + for (i = 0; i < size; i++, p += 2) + sprintf(p, "%02x", (unsigned char)buf[i]); + printk(KERN_DEBUG "%s: size %d, pos %ld payload %s\n", + __func__, size, (long)pos, str); + }) + do { + retval = ttsp_write_block_data(ts, + CY_REG_BASE, size, buf); + if (retval < 0) + msleep(500); + } while ((retval < 0) && (tries++ < 10)); + } + UNLOCK(ts->mutex); + return size; +} + +static ssize_t firmware_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *ba, + char *buf, loff_t pos, size_t size) +{ + int count = 0; + u8 reg_data; + struct device *dev = container_of(kobj, struct device, kobj); + struct cyttsp *ts = dev_get_drvdata(dev); + + DBG2(printk(KERN_INFO"%s: Enter (mode=%d)\n", + __func__, ts->fw_loader_mode);) + + LOCK(ts->mutex); + if (!ts->fw_loader_mode) { + /* read user specified operational register */ + cyttsp_rd_reg(ts, ts->reg_id & ~0x80, ®_data); + *(unsigned short *)buf = reg_data << 8; + count = sizeof(unsigned short); + printk(KERN_INFO "%s: read(reg=%02X(%d) dat=0x%02X(%d))\n", + __func__, ts->reg_id & ~0x80, ts->reg_id & ~0x80, + reg_data, reg_data); + } else { + int retval = 0; + int tries = 0; + + do { + retval = cyttsp_load_bl_regs(ts); + if (retval < 0) + msleep(500); + } while ((retval < 0) && (tries++ < 10)); + + if (retval < 0) { + printk(KERN_ERR "%s: error reading status\n", __func__); + count = 0; + } else { + *(unsigned short *)buf = ts->bl_data.bl_status << 8 | + ts->bl_data.bl_error; + count = sizeof(unsigned short); + } + + DBG2(printk(KERN_INFO + "%s:bl_f=0x%02X bl_s=0x%02X bl_e=0x%02X\n", + __func__, + ts->bl_data.bl_file, + ts->bl_data.bl_status, + ts->bl_data.bl_error);) + } + UNLOCK(ts->mutex); + return count; +} + +static struct bin_attribute cyttsp_firmware = { + .attr = { + .name = "firmware", + .mode = 0644, + }, + .size = 128, + .read = firmware_read, + .write = firmware_write, +}; + +static ssize_t attr_fwloader_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cyttsp *ts = dev_get_drvdata(dev); + return sprintf(buf, "0x%02X%02X 0x%02X%02X 0x%02X%02X 0x%02X%02X%02X\n", + ts->sysinfo_data.tts_verh, ts->sysinfo_data.tts_verl, + ts->sysinfo_data.app_idh, ts->sysinfo_data.app_idl, + ts->sysinfo_data.app_verh, ts->sysinfo_data.app_verl, + ts->sysinfo_data.cid[0], ts->sysinfo_data.cid[1], + ts->sysinfo_data.cid[2]); +} + +static ssize_t attr_fwloader_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + char *p; + int ret; + struct cyttsp *ts = dev_get_drvdata(dev); + unsigned val = simple_strtoul(buf, &p, 10); + + ret = p - buf; + if (*p && isspace(*p)) + ret++; + DBG2(printk(KERN_DEBUG "%s: %u\n", __func__, val);) + + LOCK(ts->mutex) + if (val == 3) { + sysfs_remove_bin_file(&dev->kobj, &cyttsp_firmware); + DBG2(printk(KERN_INFO "%s: FW loader closed for reg r/w\n", + __func__);) + } else if (val == 2) { + if (sysfs_create_bin_file(&dev->kobj, &cyttsp_firmware)) + printk(KERN_ERR "%s: unable to create file\n", + __func__); + DBG2(printk(KERN_INFO "%s: FW loader opened for reg r/w\n", + __func__);) + if (ts->suspended) { + cyttsp_resume(ts); + ts->suspended = 0; + enable_irq(ts->irq); + /* resume watchdog */ + mod_timer(&ts->timer, jiffies + CY_WDG_TIMEOUT); + } + } else if ((val == 1) && !ts->fw_loader_mode) { + ts->fw_loader_mode = 1; + if (ts->suspended) { + cyttsp_resume(ts); + ts->suspended = 0; + enable_irq(ts->irq); + /* resume watchdog */ + mod_timer(&ts->timer, jiffies + CY_WDG_TIMEOUT); + } + disable_irq_nosync(ts->irq); + /* kill watchdog */ + del_timer(&ts->timer); + cancel_work_sync(&ts->work); + if (sysfs_create_bin_file(&dev->kobj, &cyttsp_firmware)) + printk(KERN_ERR "%s: unable to create file\n", + __func__); + DBG2(printk(KERN_INFO + "%s: FW loader opened for start load: ps=%d mode=%d\n", + __func__, + ts->platform_data->power_state, ts->fw_loader_mode);) + cyttsp_soft_reset(ts); + printk(KERN_INFO "%s: FW loader started.\n", __func__); + ts->platform_data->power_state = CY_LDR_STATE; + } else if (!val && ts->fw_loader_mode) { + sysfs_remove_bin_file(&dev->kobj, &cyttsp_firmware); + ts->fw_loader_mode = 0; + printk(KERN_INFO "%s: FW loader finished.\n", __func__); + enable_irq(ts->irq); + ret = cyttsp_power_on(ts); + if (ret < 0) + printk(KERN_ERR "%s: Error, power on fail\n", __func__); + /* resume watchdog */ + mod_timer(&ts->timer, jiffies + CY_WDG_TIMEOUT); + } + UNLOCK(ts->mutex); + return ret == size ? ret : -EINVAL; +} + +int cyttsp_reset_controller(struct cyttsp *ts) +{ + int ret; + + ret = gpio_request(ts->platform_data->rst_gpio, "reset_pin"); + if (ret) { + printk(KERN_ERR "touch gpio failed\n"); + return ret; + } + ret = gpio_direction_output(ts->platform_data->rst_gpio, 1); + if (ret < 0) { + printk(KERN_ERR "reset gpio direction failed\n"); + goto out; + } + /* + * The start up procedure + * Set the RESET pin to low + * Wait for a period of 1 milisecond + * Set the RESET pin to high + * Wait for a period of 5 milisecond + * Start the initial Sequence + */ + gpio_set_value(ts->platform_data->rst_gpio, 0); + mdelay(1); + gpio_set_value(ts->platform_data->rst_gpio, 1); + mdelay(5); +out: + gpio_free(ts->platform_data->rst_gpio); + return 0; +} + +static struct device_attribute fwloader = + __ATTR(fwloader, 0644, attr_fwloader_show, attr_fwloader_store); + +void *cyttsp_core_init(struct cyttsp_bus_ops *bus_ops, struct device *pdev) +{ + struct input_dev *input_device; + struct cyttsp *ts; + int retval = 0, ret = 0; + + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + + ts = kzalloc(sizeof(*ts), GFP_KERNEL); + if (ts == NULL) { + printk(KERN_ERR "%s: Error, kzalloc\n", __func__); + goto error_alloc_data_failed; + } + mutex_init(&ts->mutex); + ts->pdev = pdev; + ts->platform_data = pdev->platform_data; + ts->bus_ops = bus_ops; + init_completion(&ts->int_running); + + if (ts->platform_data->init) + retval = ts->platform_data->init(1); + if (retval) { + printk(KERN_ERR "%s: platform init failed!\n", __func__); + goto error_init; + } + ret = cyttsp_reset_controller(ts); + if (ret < 0) { + printk(KERN_ERR "controller reset failed\n"); + goto error_reset; + } + + /* Create the input device and register it. */ + input_device = input_allocate_device(); + if (!input_device) { + retval = -ENOMEM; + printk(KERN_ERR "%s: Error, failed to allocate input device\n", + __func__); + goto error_input_allocate_device; + } + + ts->input = input_device; + input_device->name = ts->platform_data->name; + input_device->phys = ts->phys; + input_device->dev.parent = ts->pdev; + + /* enable interrupts */ + ts->irq = gpio_to_irq(ts->platform_data->irq_gpio); + retval = request_threaded_irq(ts->irq, NULL, cyttsp_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + ts->input->name, ts); + if (retval < 0) { + printk(KERN_ERR "%s: IRQ request failed r=%d\n", + __func__, retval); + ts->platform_data->power_state = CY_INVALID_STATE; + goto error_set_irq; + } + /* setup watchdog */ + INIT_WORK(&ts->work, cyttsp_check_bl); + setup_timer(&ts->timer, cyttsp_timer, (unsigned long) ts); + + DBG(printk(KERN_INFO "%s: call power_on\n", __func__);) + retval = cyttsp_power_on(ts); + if (retval < 0) { + printk(KERN_ERR "%s: Error, power on failed!\n", __func__); + goto error_power_on; + } + + set_bit(EV_SYN, input_device->evbit); + set_bit(EV_KEY, input_device->evbit); + set_bit(EV_ABS, input_device->evbit); + set_bit(BTN_TOUCH, input_device->keybit); + set_bit(BTN_2, input_device->keybit); + if (ts->platform_data->use_gestures) + set_bit(BTN_3, input_device->keybit); + + input_set_abs_params(input_device, ABS_X, 0, ts->platform_data->maxx, + 0, 0); + input_set_abs_params(input_device, ABS_Y, 0, ts->platform_data->maxy, + 0, 0); + input_set_abs_params(input_device, ABS_TOOL_WIDTH, 0, + CY_LARGE_TOOL_WIDTH, 0, 0); + input_set_abs_params(input_device, ABS_PRESSURE, 0, CY_MAXZ, 0, 0); + input_set_abs_params(input_device, ABS_HAT0X, 0, + ts->platform_data->maxx, 0, 0); + input_set_abs_params(input_device, ABS_HAT0Y, 0, + ts->platform_data->maxy, 0, 0); + if (ts->platform_data->use_gestures) { + input_set_abs_params(input_device, ABS_HAT1X, 0, CY_MAXZ, + 0, 0); + input_set_abs_params(input_device, ABS_HAT1Y, 0, CY_MAXZ, + 0, 0); + } + if (ts->platform_data->use_mt) { + input_set_abs_params(input_device, ABS_MT_POSITION_X, 0, + ts->platform_data->maxx, 0, 0); + input_set_abs_params(input_device, ABS_MT_POSITION_Y, 0, + ts->platform_data->maxy, 0, 0); + input_set_abs_params(input_device, ABS_MT_TOUCH_MAJOR, 0, + CY_MAXZ, 0, 0); + input_set_abs_params(input_device, ABS_MT_WIDTH_MAJOR, 0, + CY_LARGE_TOOL_WIDTH, 0, 0); + if (ts->platform_data->use_trk_id) + input_set_abs_params(input_device, ABS_MT_TRACKING_ID, + 0, CY_NUM_TRK_ID, 0, 0); + } + + if (ts->platform_data->use_virtual_keys) + input_set_capability(input_device, EV_KEY, KEY_PROG1); + + retval = input_register_device(input_device); + if (retval) { + printk(KERN_ERR "%s: Error, failed to register input device\n", + __func__); + goto error_input_register_device; + } + DBG(printk(KERN_INFO "%s: Registered input device %s\n", + __func__, input_device->name);) + ts->regulator = regulator_get(ts->pdev, "vcpin"); + if (IS_ERR(ts->regulator)) { + printk(KERN_ERR "%s: Error, regulator_get failed\n", __func__); + ts->regulator = NULL; + goto error_input_register_device; + } else { + ret = regulator_enable(ts->regulator); + if (ret < 0) { + printk(KERN_ERR "%s: regulator enable failed\n", + __func__); + goto out_regulator_error; + } + } + +#ifdef CONFIG_HAS_EARLYSUSPEND + ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + ts->early_suspend.suspend = cyttsp_ts_early_suspend; + ts->early_suspend.resume = cyttsp_ts_late_resume; + register_early_suspend(&ts->early_suspend); +#endif + retval = device_create_file(pdev, &fwloader); + if (retval) { + printk(KERN_ERR "%s: Error, could not create attribute\n", + __func__); + goto device_create_error; + } + dev_set_drvdata(pdev, ts); + + return ts; + +device_create_error: + regulator_disable(ts->regulator); +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&ts->early_suspend); +#endif +out_regulator_error: + regulator_put(ts->regulator); +error_input_register_device: + input_unregister_device(input_device); +error_power_on: + cancel_work_sync(&ts->work); + del_timer_sync(&ts->timer); + if (ts->irq >= 0) + free_irq(ts->irq, ts); +error_set_irq: + input_free_device(input_device); +error_reset: +error_input_allocate_device: + if (ts->platform_data->init) + ts->platform_data->init(0); +error_init: + kfree(ts); +error_alloc_data_failed: + return NULL; +} + +/* registered in driver struct */ +void cyttsp_core_release(void *handle) +{ + struct cyttsp *ts = handle; + + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&ts->early_suspend); +#endif + del_timer_sync(&ts->timer); + cancel_work_sync(&ts->work); + free_irq(ts->irq, ts); + input_unregister_device(ts->input); + input_free_device(ts->input); + if (ts->platform_data->init) + ts->platform_data->init(0); + kfree(ts); +} +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard touchscreen driver core"); +MODULE_AUTHOR("Cypress"); + diff --git a/drivers/input/touchscreen/cyttsp_core.h b/drivers/input/touchscreen/cyttsp_core.h new file mode 100755 index 00000000000..6af486177a0 --- /dev/null +++ b/drivers/input/touchscreen/cyttsp_core.h @@ -0,0 +1,44 @@ +/* Header file for: + * Cypress TrueTouch(TM) Standard Product I2C touchscreen driver. + * drivers/input/touchscreen/cyttsp_core.h + * + * Copyright (C) 2009-2011 Cypress Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Cypress reserves the right to make changes without further notice + * to the materials described herein. Cypress does not assume any + * liability arising out of the application described herein. + * + * Contact Cypress Semiconductor at www.cypress.com + * + */ + + +#ifndef __CYTTSP_CORE_H__ +#define __CYTTSP_CORE_H__ + +#include + +struct cyttsp_bus_ops { + s32 (*write)(void *handle, u8 addr, u8 length, const void *values); + s32 (*read)(void *handle, u8 addr, u8 length, void *values); + s32 (*ext)(void *handle, void *values); +}; + +void *cyttsp_core_init(struct cyttsp_bus_ops *bus_ops, struct device *pdev); +void cyttsp_core_release(void *handle); + +#endif /* __CYTTSP_CORE_H__ */ diff --git a/drivers/input/touchscreen/cyttsp_ldr.h b/drivers/input/touchscreen/cyttsp_ldr.h new file mode 100755 index 00000000000..95db89d0d13 --- /dev/null +++ b/drivers/input/touchscreen/cyttsp_ldr.h @@ -0,0 +1,333 @@ +/* + * Source for: + * Cypress TrueTouch(TM) Standard Product touchscreen driver. + * + * Copyright (C) 2009-2011 Cypress Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Cypress reserves the right to make changes without further notice + * to the materials described herein. Cypress does not assume any + * liability arising out of the application described herein. + * + * Contact Cypress Semiconductor at www.cypress.com + * + */ +/* + ************************************************************************ + * Compiled image bootloader functions + ************************************************************************ + */ +#include "cyttsp_fw.h" +#define CY_BL_PAGE_SIZE 16 +#define CY_BL_NUM_PAGES 5 +#define CY_MAX_DATA_LEN (CY_BL_PAGE_SIZE * 2) + +/* Timeout timer */ +static int cyttsp_check_polling(struct cyttsp *ts) +{ + return ts->platform_data->use_timer; +} + +static void cyttsp_to_timer(unsigned long handle) +{ + struct cyttsp *ts = (struct cyttsp *)handle; + + DBG(printk(KERN_INFO"%s: TTSP timeout timer event!\n", __func__);) + ts->to_timeout = true; + return; +} + +static void cyttsp_setup_to_timer(struct cyttsp *ts) +{ + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + setup_timer(&ts->to_timer, cyttsp_to_timer, (unsigned long) ts); +} + +static void cyttsp_kill_to_timer(struct cyttsp *ts) +{ + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + del_timer(&ts->to_timer); +} + +static void cyttsp_start_to_timer(struct cyttsp *ts, int ms) +{ + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + ts->to_timeout = false; + mod_timer(&ts->to_timer, jiffies + ms); +} + +static bool cyttsp_timeout(struct cyttsp *ts) +{ + if (cyttsp_check_polling(ts)) + return false; + else + return ts->to_timeout; +} + +static void cyttsp_set_bl_ready(struct cyttsp *ts, bool set) +{ + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + ts->bl_ready = set; + DBG(printk(KERN_INFO"%s: bl_ready=%d\n", __func__, (int)ts->bl_ready);) +} + +static bool cyttsp_check_bl_ready(struct cyttsp *ts) +{ + if (cyttsp_check_polling(ts)) + return true; + else + return ts->bl_ready; +} + +static bool cyttsp_bl_err_status(struct cyttsp *ts) +{ + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + return (((ts->bl_data.bl_status == 0x10) && + (ts->bl_data.bl_error == 0x20)) || + ((ts->bl_data.bl_status == 0x11) && + (ts->bl_data.bl_error == 0x20))); +} + +static bool cyttsp_wait_bl_ready(struct cyttsp *ts, + int pre_delay, int loop_delay, int max_try, + bool (*done)(struct cyttsp *ts)) +{ + int tries; + bool rdy = false, tmo = false; + + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + DBG(printk(KERN_INFO"%s: pre-dly=%d loop-dly=%d, max-try=%d\n", + __func__, pre_delay, loop_delay, max_try);) + + tries = 0; + ts->bl_data.bl_file = 0; + ts->bl_data.bl_status = 0; + ts->bl_data.bl_error = 0; + if (cyttsp_check_polling(ts)) { + msleep(pre_delay); + do { + msleep(abs(loop_delay)); + cyttsp_load_bl_regs(ts); + } while (!done(ts) && + tries++ < max_try); + DBG(printk(KERN_INFO"%s: polling mode tries=%d\n", + __func__, tries);) + } else { + cyttsp_start_to_timer(ts, abs(loop_delay) * max_try); + while (!rdy && !tmo) { + rdy = cyttsp_check_bl_ready(ts); + tmo = cyttsp_timeout(ts); + if (loop_delay < 0) + udelay(abs(loop_delay)); + else + msleep(abs(loop_delay)); + tries++; + } + DBG2(printk(KERN_INFO"%s: irq mode tries=%d rdy=%d tmo=%d\n", + __func__, tries, (int)rdy, (int)tmo);) + cyttsp_load_bl_regs(ts); + } + + if (tries >= max_try || tmo) + return true; /* timeout */ + else + return false; +} + +static int cyttsp_wr_blk_chunks(struct cyttsp *ts, u8 cmd, + u8 length, const u8 *values) +{ + int retval = 0; + int block = 1; + bool timeout; + + u8 dataray[CY_MAX_DATA_LEN]; + + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + + /* first page already includes the bl page offset */ + memcpy(dataray, values, CY_BL_PAGE_SIZE + 1); + cyttsp_set_bl_ready(ts, false); + retval = ttsp_write_block_data(ts, cmd, CY_BL_PAGE_SIZE + 1, dataray); + values += CY_BL_PAGE_SIZE + 1; + length -= CY_BL_PAGE_SIZE + 1; + if (retval) + return retval; + + /* remaining blocks require bl page offset stuffing */ + while (length && (block < CY_BL_NUM_PAGES) && !(retval < 0)) { + dataray[0] = CY_BL_PAGE_SIZE * block; + timeout = cyttsp_wait_bl_ready(ts, + 1, -100, 100, cyttsp_bl_err_status); + if (timeout) + return -EIO; + memcpy(&dataray[1], values, length >= CY_BL_PAGE_SIZE ? + CY_BL_PAGE_SIZE : length); + cyttsp_set_bl_ready(ts, false); + retval = ttsp_write_block_data(ts, cmd, + length >= CY_BL_PAGE_SIZE ? + CY_BL_PAGE_SIZE + 1 : length + 1, dataray); + values += CY_BL_PAGE_SIZE; + length = length >= CY_BL_PAGE_SIZE ? + length - CY_BL_PAGE_SIZE : 0; + block++; + } + + return retval; +} + +static int cyttsp_load_app(struct cyttsp *ts) +{ + int retval = 0; + int rec; + bool timeout; + + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + + printk(KERN_INFO "%s: " + "load file - tver=0x%02X%02X a_id=0x%02X%02X aver=0x%02X%02X\n", + __func__, + cyttsp_fw_tts_verh, cyttsp_fw_tts_verl, + cyttsp_fw_app_idh, cyttsp_fw_app_idl, + cyttsp_fw_app_verh, cyttsp_fw_app_verl); + + /* download new TTSP Application to the Bootloader */ + rec = 0; + + /* send bootload initiation command */ + printk(KERN_INFO"%s: Send BL Enter\n", __func__); + cyttsp_set_bl_ready(ts, false); + retval = ttsp_write_block_data(ts, CY_REG_BASE, + cyttsp_fw[rec].Length, cyttsp_fw[rec].Block); + rec++; + if (retval) + return retval; + timeout = cyttsp_wait_bl_ready(ts, 1, 100, 100, cyttsp_bl_err_status); + DBG(printk(KERN_INFO "%s: BL ENTER f=%02X s=%02X e=%02X t=%d\n", + __func__, + ts->bl_data.bl_file, ts->bl_data.bl_status, + ts->bl_data.bl_error, timeout);) + if (timeout) + goto loader_exit; + + /* send bootload firmware load blocks */ + printk(KERN_INFO"%s: Send BL Blocks\n", __func__); + while (cyttsp_fw[rec].Command == CY_BL_WRITE_BLK) { + DBG2(printk(KERN_INFO "%s:" + "BL DNLD Rec=% 3d Len=% 3d Addr=%04X\n", + __func__, + cyttsp_fw[rec].Record, cyttsp_fw[rec].Length, + cyttsp_fw[rec].Address); + ) + retval = cyttsp_wr_blk_chunks(ts, CY_REG_BASE, + cyttsp_fw[rec].Length, cyttsp_fw[rec].Block); + if (retval < 0) { + DBG(printk(KERN_INFO "%s:" + "BL fail Rec=%3d retval=%d\n", + __func__, + cyttsp_fw[rec].Record, retval); + ) + break; + } else { + cyttsp_wait_bl_ready(ts, 10, 1, 1000, + cyttsp_bl_err_status); + DBG(printk(KERN_INFO "%s: BL _LOAD " + "f=%02X s=%02X e=%02X\n", + __func__, + ts->bl_data.bl_file, ts->bl_data.bl_status, + ts->bl_data.bl_error);) + } + rec++; + } + if (retval < 0) + goto loader_exit; + + /* send bootload terminate command */ + printk(KERN_INFO"%s: Send BL Terminate\n", __func__); + cyttsp_set_bl_ready(ts, false); + retval = ttsp_write_block_data(ts, CY_REG_BASE, + cyttsp_fw[rec].Length, cyttsp_fw[rec].Block); + if (retval < 0) + goto loader_exit; + else + cyttsp_wait_bl_ready(ts, 1, 100, 100, cyttsp_bl_err_status); + +loader_exit: + /* reset TTSP Device back to bootloader mode */ + retval = cyttsp_soft_reset(ts); + + return retval; +} + +static int cyttsp_loader(struct cyttsp *ts) +{ + int retval; + + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + + retval = cyttsp_load_bl_regs(ts); + if (retval < 0) + return retval; + + printk(KERN_INFO "%s:" + "blttsp=0x%02X%02X flttsp=0x%02X%02X force=%d\n", + __func__, + ts->bl_data.ttspver_hi, ts->bl_data.ttspver_lo, + cyttsp_fw_tts_verh, cyttsp_fw_tts_verl, + ts->platform_data->use_force_fw_update); + printk(KERN_INFO "%s:" + "blappid=0x%02X%02X flappid=0x%02X%02X\n", + __func__, + ts->bl_data.appid_hi, ts->bl_data.appid_lo, + cyttsp_fw_app_idh, cyttsp_fw_app_idl); + printk(KERN_INFO "%s:" + "blappver=0x%02X%02X flappver=0x%02X%02X\n", + __func__, + ts->bl_data.appver_hi, ts->bl_data.appver_lo, + cyttsp_fw_app_verh, cyttsp_fw_app_verl); + printk(KERN_INFO "%s:" + "blcid=0x%02X%02X%02X flcid=0x%02X%02X%02X\n", + __func__, + ts->bl_data.cid_0, ts->bl_data.cid_1, ts->bl_data.cid_2, + cyttsp_fw_cid_0, cyttsp_fw_cid_1, cyttsp_fw_cid_2); + + if (CY_DIFF(ts->bl_data.ttspver_hi, cyttsp_fw_tts_verh) || + CY_DIFF(ts->bl_data.ttspver_lo, cyttsp_fw_tts_verl) || + CY_DIFF(ts->bl_data.appid_hi, cyttsp_fw_app_idh) || + CY_DIFF(ts->bl_data.appid_lo, cyttsp_fw_app_idl) || + CY_DIFF(ts->bl_data.appver_hi, cyttsp_fw_app_verh) || + CY_DIFF(ts->bl_data.appver_lo, cyttsp_fw_app_verl) || + CY_DIFF(ts->bl_data.cid_0, cyttsp_fw_cid_0) || + CY_DIFF(ts->bl_data.cid_1, cyttsp_fw_cid_1) || + CY_DIFF(ts->bl_data.cid_2, cyttsp_fw_cid_2) || + ts->platform_data->use_force_fw_update) { + /* load new app into TTSP Device */ + cyttsp_setup_to_timer(ts); + ts->platform_data->power_state = CY_LDR_STATE; + retval = cyttsp_load_app(ts); + cyttsp_kill_to_timer(ts); + + } else { + /* firmware file is a match with firmware in the TTSP device */ + DBG(printk(KERN_INFO "%s: FW matches - no loader\n", __func__);) + } + + if (retval < 0) + return retval; + + return retval; +} + diff --git a/drivers/input/touchscreen/cyttsp_spi.c b/drivers/input/touchscreen/cyttsp_spi.c new file mode 100755 index 00000000000..d4f7ffeed1b --- /dev/null +++ b/drivers/input/touchscreen/cyttsp_spi.c @@ -0,0 +1,302 @@ +/* Source for: + * Cypress TrueTouch(TM) Standard Product I2C touchscreen driver. + * drivers/input/touchscreen/cyttsp_spi.c + * + * Copyright (C) 2009-2011 Cypress Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Cypress reserves the right to make changes without further notice + * to the materials described herein. Cypress does not assume any + * liability arising out of the application described herein. + * + * Contact Cypress Semiconductor at www.cypress.com + * + */ + +#include +#include +#include +#include +#include +#include "cyttsp_core.h" + +#define DBG(x) + +#define CY_SPI_WR_OP 0x00 /* r/~w */ +#define CY_SPI_RD_OP 0x01 +#define CY_SPI_CMD_BYTES 4 +#define CY_SPI_SYNC_BYTE 2 +#define CY_SPI_SYNC_ACK1 0x62 +#define CY_SPI_SYNC_ACK2 0x9D +#define CY_SPI_DATA_SIZE 128 +#define CY_SPI_DATA_BUF_SIZE (CY_SPI_CMD_BYTES + CY_SPI_DATA_SIZE) +#define CY_SPI_BITS_PER_WORD 8 + +struct cyttsp_spi { + struct cyttsp_bus_ops ops; + struct spi_device *spi_client; + void *ttsp_client; + u8 wr_buf[CY_SPI_DATA_BUF_SIZE]; + u8 rd_buf[CY_SPI_DATA_BUF_SIZE]; +}; + +static int cyttsp_spi_xfer_(u8 op, struct cyttsp_spi *ts_spi, + u8 reg, u8 *buf, int length) +{ + struct spi_message msg; + struct spi_transfer xfer[2]; + u8 *wr_buf = ts_spi->wr_buf; + u8 *rd_buf = ts_spi->rd_buf; + int retval; + DBG(printk(KERN_INFO "%s: Enter\n", __func__);) + if (length > CY_SPI_DATA_SIZE) { + printk(KERN_ERR "%s: length %d is too big.\n", + __func__, length); + return -EINVAL; + } + DBG(printk(KERN_INFO "%s: OP=%s length=%d\n", __func__, + op == CY_SPI_RD_OP ? "Read" : "Write", length);) + + memset(wr_buf, 0, CY_SPI_DATA_BUF_SIZE); + memset(rd_buf, 0, CY_SPI_DATA_BUF_SIZE); + + wr_buf[0] = 0x00; /* header byte 0 */ + wr_buf[1] = 0xFF; /* header byte 1 */ + wr_buf[2] = reg; /* reg index */ + wr_buf[3] = op; /* r/~w */ + if (op == CY_SPI_WR_OP) + memcpy(wr_buf + CY_SPI_CMD_BYTES, buf, length); + DBG( + if (op == CY_SPI_RD_OP) + memset(rd_buf, CY_SPI_SYNC_NACK, CY_SPI_DATA_BUF_SIZE);) + DBG( + for (i = 0; i < (length + CY_SPI_CMD_BYTES); i++) { + if ((op == CY_SPI_RD_OP) && (i < CY_SPI_CMD_BYTES)) + printk(KERN_INFO "%s: read op. wr[%d]:0x%02x\n", + __func__, i, wr_buf[i]); + if (op == CY_SPI_WR_OP) + printk(KERN_INFO "%s: write op. wr[%d]:0x%02x\n", + __func__, i, wr_buf[i]); + }) + + memset((void *)xfer, 0, sizeof(xfer)); + spi_message_init(&msg); + xfer[0].tx_buf = wr_buf; + xfer[0].rx_buf = rd_buf; + if (op == CY_SPI_WR_OP) { + xfer[0].len = length + CY_SPI_CMD_BYTES; + spi_message_add_tail(&xfer[0], &msg); + } else if (op == CY_SPI_RD_OP) { + xfer[0].len = CY_SPI_CMD_BYTES; + spi_message_add_tail(&xfer[0], &msg); + + xfer[1].rx_buf = buf; + xfer[1].len = length; + spi_message_add_tail(&xfer[1], &msg); + } + + retval = spi_sync(ts_spi->spi_client, &msg); + if (retval < 0) { + printk(KERN_ERR "%s: spi sync error %d, len=%d, op=%d\n", + __func__, retval, xfer[1].len, op); + retval = 0; + } + + if ((rd_buf[CY_SPI_SYNC_BYTE] == CY_SPI_SYNC_ACK1) && + (rd_buf[CY_SPI_SYNC_BYTE+1] == CY_SPI_SYNC_ACK2)) + retval = 0; + else { + DBG( + for (i = 0; i < (CY_SPI_CMD_BYTES); i++) + printk(KERN_INFO "%s: test rd_buf[%d]:0x%02x\n", + __func__, i, rd_buf[i]); + for (i = 0; i < (length); i++) + printk(KERN_INFO "%s: test buf[%d]:0x%02x\n", + __func__, i, buf[i]);) + retval = 1; + } + return retval; +} + +static int cyttsp_spi_xfer(u8 op, struct cyttsp_spi *ts, + u8 reg, u8 *buf, int length) +{ + int tries; + int retval; + DBG(printk(KERN_INFO "%s: Enter\n", __func__);) + + if (op == CY_SPI_RD_OP) { + for (tries = CY_NUM_RETRY; tries; tries--) { + retval = cyttsp_spi_xfer_(op, ts, reg, buf, length); + if (retval == 0) + break; + else + msleep(10); + } + } else { + retval = cyttsp_spi_xfer_(op, ts, reg, buf, length); + } + return retval; +} + +static s32 ttsp_spi_read_block_data(void *handle, u8 addr, + u8 length, void *data) +{ + int retval; + struct cyttsp_spi *ts = container_of(handle, struct cyttsp_spi, ops); + + DBG(printk(KERN_INFO "%s: Enter\n", __func__);) + + retval = cyttsp_spi_xfer(CY_SPI_RD_OP, ts, addr, data, length); + if (retval < 0) + printk(KERN_ERR "%s: ttsp_spi_read_block_data failed\n", + __func__); + + /* Do not print the above error if the data sync bytes were not found. + This is a normal condition for the bootloader loader startup and need + to retry until data sync bytes are found. */ + if (retval > 0) + retval = -1; /* now signal fail; so retry can be done */ + + return retval; +} + +static s32 ttsp_spi_write_block_data(void *handle, u8 addr, + u8 length, const void *data) +{ + int retval; + struct cyttsp_spi *ts = container_of(handle, struct cyttsp_spi, ops); + + DBG(printk(KERN_INFO "%s: Enter\n", __func__);) + + retval = cyttsp_spi_xfer(CY_SPI_WR_OP, ts, addr, (void *)data, length); + if (retval < 0) + printk(KERN_ERR "%s: ttsp_spi_write_block_data failed\n", + __func__); + + /* Do not print the above error if the data sync bytes were not found. + This is a normal condition for the bootloader loader startup and need + to retry until data sync bytes are found. */ + if (retval > 0) + retval = -1; /* now signal fail; so retry can be done */ + + return retval; +} + +static s32 ttsp_spi_tch_ext(void *handle, void *values) +{ + int retval = 0; + struct cyttsp_spi *ts = container_of(handle, struct cyttsp_spi, ops); + + DBG(printk(KERN_INFO "%s: Enter\n", __func__);) + + /* Add custom touch extension handling code here */ + /* set: retval < 0 for any returned system errors, + retval = 0 if normal touch handling is required, + retval > 0 if normal touch handling is *not* required */ + if (!ts || !values) + retval = -EIO; + + return retval; +} + +static int __devinit cyttsp_spi_probe(struct spi_device *spi) +{ + struct cyttsp_spi *ts_spi; + int retval; + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + + /* Set up SPI*/ + spi->bits_per_word = CY_SPI_BITS_PER_WORD; + spi->mode = SPI_MODE_0; + retval = spi_setup(spi); + if (retval < 0) { + printk(KERN_ERR "%s: SPI setup error %d\n", __func__, retval); + return retval; + } + ts_spi = kzalloc(sizeof(*ts_spi), GFP_KERNEL); + if (ts_spi == NULL) { + printk(KERN_ERR "%s: Error, kzalloc\n", __func__); + retval = -ENOMEM; + goto error_alloc_data_failed; + } + ts_spi->spi_client = spi; + dev_set_drvdata(&spi->dev, ts_spi); + ts_spi->ops.write = ttsp_spi_write_block_data; + ts_spi->ops.read = ttsp_spi_read_block_data; + ts_spi->ops.ext = ttsp_spi_tch_ext; + + ts_spi->ttsp_client = cyttsp_core_init(&ts_spi->ops, &spi->dev); + if (!ts_spi->ttsp_client) { + retval = -ENODEV; + goto ttsp_core_err; + } + printk(KERN_INFO "%s: Successful registration %s\n", + __func__, CY_SPI_NAME); + + return 0; + +ttsp_core_err: + kfree(ts_spi); +error_alloc_data_failed: + return retval; +} + +/* registered in driver struct */ +static int __devexit cyttsp_spi_remove(struct spi_device *spi) +{ + struct cyttsp_spi *ts_spi = dev_get_drvdata(&spi->dev); + DBG(printk(KERN_INFO"%s: Enter\n", __func__);) + cyttsp_core_release(ts_spi->ttsp_client); + kfree(ts_spi); + return 0; +} + + +static struct spi_driver cyttsp_spi_driver = { + .driver = { + .name = CY_SPI_NAME, + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = cyttsp_spi_probe, + .remove = __devexit_p(cyttsp_spi_remove), +}; + +static int __init cyttsp_spi_init(void) +{ + int err; + + err = spi_register_driver(&cyttsp_spi_driver); + printk(KERN_INFO "%s: Cypress TrueTouch(R) Standard Product SPI " + "Touchscreen Driver (Built %s @ %s) returned %d\n", + __func__, __DATE__, __TIME__, err); + + return err; +} +module_init(cyttsp_spi_init); + +static void __exit cyttsp_spi_exit(void) +{ + spi_unregister_driver(&cyttsp_spi_driver); + printk(KERN_INFO "%s: module exit\n", __func__); +} +module_exit(cyttsp_spi_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product SPI driver"); +MODULE_AUTHOR("Cypress"); + diff --git a/include/linux/cyttsp.h b/include/linux/cyttsp.h new file mode 100755 index 00000000000..38ef1236d7a --- /dev/null +++ b/include/linux/cyttsp.h @@ -0,0 +1,114 @@ +/* Header file for: + * Cypress TrueTouch(TM) Standard Product I2C touchscreen driver. + * include/linux/cyttsp.h + * + * Copyright (C) 2009-2011 Cypress Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2, and only version 2, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Cypress reserves the right to make changes without further notice + * to the materials described herein. Cypress does not assume any + * liability arising out of the application described herein. + * + * Contact Cypress Semiconductor at www.cypress.com + * + */ +#include + +#ifndef _CYTTSP_H_ +#define _CYTTSP_H_ + +#include + +#define CY_SPI_NAME "cyttsp-spi" +#define CY_I2C_NAME "cyttsp-i2c" +/* Scan Type selection for finger and/or stylus activation */ +#define CY_SCN_TYP_DFLT 0x01 /* finger only mutual scan */ +/* Active Power state scanning/processing refresh interval */ +#define CY_ACT_INTRVL_DFLT 0x00 /* ms */ +/* touch timeout for the Active power */ +#define CY_TCH_TMOUT_DFLT 0x64 /* ms */ +/* Low Power state scanning/processing refresh interval */ +#define CY_LP_INTRVL_DFLT 0x32 /* ms */ +/* + *defines for Gen2 (Txx2xx); Gen3 (Txx3xx) + * use these defines to set cyttsp_platform_data.gen in board config file + */ +enum cyttsp_gen { + CY_GEN2, + CY_GEN3, +}; +/* + * Active distance in pixels for a gesture to be reported + * if set to 0, then all gesture movements are reported + * Valid range is 0 - 15 + */ +#define CY_ACT_DIST_DFLT 8 +#define CY_ACT_DIST CY_ACT_DIST_DFLT +#define CY_ACT_DIST_BITS 0x0F +/* max retries for read/write ops */ +#define CY_NUM_RETRY 6 + +enum cyttsp_gest { + CY_GEST_GRP_NONE = 0, + CY_GEST_GRP1 = 0x10, + CY_GEST_GRP2 = 0x20, + CY_GEST_GRP3 = 0x40, + CY_GEST_GRP4 = 0x80, +}; + +enum cyttsp_powerstate { + CY_IDLE_STATE, /* IC cannot be reached */ + CY_READY_STATE, /* pre-operational; ready to go to ACTIVE */ + CY_ACTIVE_STATE, /* app is running, IC is scanning */ + CY_LOW_PWR_STATE, /* not currently used */ + CY_SLEEP_STATE, /* app is running, IC is idle */ + CY_BL_STATE, /* bootloader is running */ + CY_LDR_STATE, /* loader is running */ + CY_SYSINFO_STATE, /* Switching to SysInfo mode */ + CY_INVALID_STATE /* always last in the list */ +}; + +struct cyttsp_platform_data { + u32 maxx; + u32 maxy; + u32 flags; + enum cyttsp_gen gen; + unsigned use_st:1; + unsigned use_mt:1; + unsigned use_trk_id:1; + unsigned use_hndshk:1; + unsigned use_timer:1; + unsigned use_sleep:1; + unsigned use_gestures:1; + unsigned use_load_file:1; + unsigned use_force_fw_update:1; + unsigned use_virtual_keys:1; + enum cyttsp_powerstate power_state; + u8 gest_set; + u8 scn_typ; /* finger and/or stylus scanning */ + u8 act_intrvl; /* Active refresh interval; ms */ + u8 tch_tmout; /* Active touch timeout; ms */ + u8 lp_intrvl; /* Low power refresh interval; ms */ + int (*wakeup)(void); + int (*init)(int on_off); + void (*mt_sync)(struct input_dev *); + char *name; + s16 irq_gpio; + s16 rst_gpio; + bool invert; +}; + +#endif /* _CYTTSP_H_ */ -- cgit v1.2.3 From f6cd9f36ff35e209b081c959050c9e930eb39f74 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. ST Ericsson ID: 353610 ST Ericsson FOSS-OUT ID: Trivial ST Ericsson Linux next: NA Change-Id: Ifb06f9c9dd0dc59cbc071198c9e244a422a63af0 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 (limited to 'drivers') 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 c5df9c00b5e..a5ad6408fe6 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 756c8e545fd..0159d83a7ff 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 cff9b78847b..aa93aa0efc2 100644 --- a/drivers/input/misc/ab8500-accdet.c +++ b/drivers/input/misc/ab8500-accdet.c @@ -11,29 +11,16 @@ * 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 -#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 @@ -82,290 +69,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", @@ -380,7 +86,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", @@ -403,7 +109,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", @@ -426,102 +132,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; @@ -560,7 +174,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; @@ -590,82 +204,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; @@ -689,16 +231,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); @@ -706,188 +250,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; @@ -902,20 +270,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( @@ -929,61 +297,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; @@ -1011,336 +328,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( @@ -1354,21 +346,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( @@ -1377,56 +359,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 40cfd063d82f3704228ce9d66850c5f1b4b99985 Mon Sep 17 00:00:00 2001 From: Avinash A Date: Mon, 26 Sep 2011 19:47:27 +0530 Subject: input: ts-cypress proper timing for each mode change The time interval for the cypress controller in deep sleep state after the last communication should be Tdelay > 2*Trefresh. Trefresh is 14.3ms ST-Ericsson ID: 359690, 357430 ST-Ericsson Linux next: NA ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I86aedb29157ea8a2e3d685747b02f8c596945cdf Signed-off-by: Avinash A Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/31993 Reviewed-by: Avinash A Tested-by: Avinash A Reviewed-by: Rabin VINCENT --- drivers/input/touchscreen/cyttsp_core.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/input/touchscreen/cyttsp_core.c b/drivers/input/touchscreen/cyttsp_core.c index 076417439ab..7a2aa109034 100755 --- a/drivers/input/touchscreen/cyttsp_core.c +++ b/drivers/input/touchscreen/cyttsp_core.c @@ -150,6 +150,7 @@ #define CY_DELAY_DFLT 20 /* ms */ #define CY_DELAY_MAX (500/CY_DELAY_DFLT) /* half second */ #define CY_DELAY_SYSINFO 20 /* ms */ +#define CY_MODE_CHANGE_DELAY 30 /* ms */ #define CY_DELAY_BL 300 #define CY_DELAY_DNLOAD 100 /* ms */ #define CY_HNDSHK_BIT 0x80 @@ -1698,6 +1699,7 @@ static int cyttsp_suspend(struct cyttsp *ts) CY_REG_BASE, sizeof(sleep_mode), &sleep_mode); if (!(retval < 0)) ts->platform_data->power_state = CY_SLEEP_STATE; + msleep(CY_MODE_CHANGE_DELAY); } DBG(printk(KERN_INFO"%s: Sleep Power state is %s\n", __func__, (ts->platform_data->power_state == CY_ACTIVE_STATE) ? -- cgit v1.2.3 From 68af19feaf0957aa94d65fe02774706efc42e959 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(-) (limited to 'drivers') diff --git a/drivers/input/misc/ab8500-accdet.c b/drivers/input/misc/ab8500-accdet.c index aa93aa0efc2..d53e9247cf2 100644 --- a/drivers/input/misc/ab8500-accdet.c +++ b/drivers/input/misc/ab8500-accdet.c @@ -261,7 +261,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 8d177c23f0fdf9537eb67a0f2b27e47515007aba 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(-) (limited to 'drivers') diff --git a/drivers/input/misc/ab8500-accdet.c b/drivers/input/misc/ab8500-accdet.c index d53e9247cf2..80b7ded7f27 100644 --- a/drivers/input/misc/ab8500-accdet.c +++ b/drivers/input/misc/ab8500-accdet.c @@ -82,7 +82,7 @@ static struct accessory_regu_descriptor ab8500_regu_desc[3] = { }, { .id = REGULATOR_AVSWITCH, - .name = "vcc-avswitch", + .name = "vcc-N2158", }, }; -- cgit v1.2.3 From 2b5c0e2e0ee29af65cec2f5e90f4d02bbd96bb56 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(-) (limited to 'drivers') 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 80b7ded7f27..9bd52183a26 100644 --- a/drivers/input/misc/ab8500-accdet.c +++ b/drivers/input/misc/ab8500-accdet.c @@ -397,9 +397,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 190a2eb36d7884438070dce59364558c7b65ed49 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(-) (limited to 'drivers') diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c index c5d169a2b3e..66d4c067b98 100644 --- a/drivers/input/keyboard/nomadik-ske-keypad.c +++ b/drivers/input/keyboard/nomadik-ske-keypad.c @@ -1,4 +1,4 @@ -/* +/** * Copyright (C) ST-Ericsson SA 2010 * * Author: Naveen Kumar G for ST-Ericsson @@ -53,26 +53,34 @@ #define SKE_NUM_ASRX_REGISTERS (4) #define KEY_PRESSED_DELAY 10 + +#define KEY_REPORTED 1 +#define KEY_PRESSED 2 + /** * struct ske_keypad - data structure used by keypad driver - * @irq: irq no - * @reg_base: ske regsiters base address - * @input: pointer to input device object - * @board: keypad platform device - * @keymap: matrix scan code table for keycodes - * @clk: clock structure pointer - * @enable: flag to enable the driver event - * @regulator: pointer to the regulator used for ske kyepad - * @gpio_input_irq: array for gpio irqs - * @key_pressed: hold the key state - * @work: delayed work variable for gpio switch - * @ske_rows: rows gpio array for ske - * @ske_cols: columns gpio array for ske - * @gpio_row: gpio row - * @gpio_col: gpio column - * @gpio_work: delayed work variable for release gpio key + * @dev: Pointer to the structure device + * @irq: irq no + * @reg_base: ske regsiters base address + * @input: pointer to input device object + * @board: keypad platform device + * @keymap: matrix scan code table for keycodes + * @clk: clock structure pointer + * @enable: flag to enable the driver event + * @regulator: pointer to the regulator used for ske kyepad + * @gpio_input_irq: array for gpio irqs + * @key_pressed: hold the key state + * @work: delayed work variable for gpio switch + * @ske_rows: rows gpio array for ske + * @ske_cols: columns gpio array for ske + * @gpio_row: gpio row + * @gpio_col: gpio column + * @gpio_work: delayed work variable for release gpio key + * @keys: matrix holding key status + * @scan_work: delayed work for scaning new key actions */ struct ske_keypad { + struct device *dev; int irq; void __iomem *reg_base; struct input_dev *input; @@ -90,6 +98,8 @@ struct ske_keypad { int gpio_row; int gpio_col; struct delayed_work gpio_work; + u8 keys[SKE_KPD_MAX_ROWS][SKE_KPD_MAX_COLS]; + struct delayed_work scan_work; }; static void ske_keypad_set_bits(struct ske_keypad *keypad, u16 addr, @@ -107,7 +117,7 @@ static void ske_keypad_set_bits(struct ske_keypad *keypad, u16 addr, spin_unlock(&keypad->ske_keypad_lock); } -/* +/** * ske_keypad_chip_init: init keypad controller configuration * * Enable Multi key press detection, auto scan mode @@ -124,7 +134,7 @@ static int __devinit ske_keypad_chip_init(struct ske_keypad *keypad) if (!timeout) return -EINVAL; - /* + /** * set debounce value * keypad dbounce is configured in DBCR[15:8] * dbounce value in steps of 32/32.768 ms @@ -139,7 +149,7 @@ static int __devinit ske_keypad_chip_init(struct ske_keypad *keypad) /* enable multi key detection */ ske_keypad_set_bits(keypad, SKE_CR, 0x0, SKE_KPMLT); - /* + /** * set up the number of columns * KPCN[5:3] defines no. of keypad columns to be auto scanned */ @@ -163,6 +173,7 @@ static void ske_mode_enable(struct ske_keypad *keypad, bool enable) int i; if (!enable) { + dev_dbg(keypad->dev, "%s disable keypad\n", __func__); writel(0, keypad->reg_base + SKE_CR); if (keypad->board->exit) keypad->board->exit(); @@ -173,6 +184,7 @@ static void ske_mode_enable(struct ske_keypad *keypad, bool enable) clk_disable(keypad->clk); regulator_disable(keypad->regulator); } else { + dev_dbg(keypad->dev, "%s enable keypad\n", __func__); regulator_enable(keypad->regulator); clk_enable(keypad->clk); for (i = 0; i < keypad->board->krow; i++) { @@ -239,7 +251,6 @@ static struct attribute_group ske_attr_group = { static void ske_keypad_report(struct ske_keypad *keypad, u8 status, int col) { int row = 0, code, pos; - struct input_dev *input = keypad->input; u32 ske_ris; int num_of_rows; @@ -254,10 +265,13 @@ static void ske_keypad_report(struct ske_keypad *keypad, u8 status, int col) ske_ris = readl(keypad->reg_base + SKE_RIS); keypad->key_pressed = ske_ris & SKE_KPRISA; - input_event(input, EV_MSC, MSC_SCAN, code); - input_report_key(input, keypad->keymap[code], - keypad->key_pressed); - input_sync(input); + dev_dbg(keypad->dev, + "%s key_pressed:%d code:%d row:%d col:%d\n", + __func__, keypad->key_pressed, code, row, col); + + if (keypad->key_pressed) + keypad->keys[row][col] |= KEY_PRESSED; + num_of_rows--; } while (num_of_rows); } @@ -268,7 +282,7 @@ static void ske_keypad_read_data(struct ske_keypad *keypad) int col = 0; int ske_asr, i; - /* + /** * Read the auto scan registers * * Each SKE_ASRx (x=0 to x=3) contains two row values. @@ -293,26 +307,95 @@ static void ske_keypad_read_data(struct ske_keypad *keypad) } } } -static void ske_keypad_scan(struct ske_keypad *keypad) -{ - int timeout = keypad->board->debounce_ms; - /* disable auto scan interrupt; mask the interrupt generated */ - ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0); - ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA); +static void ske_keypad_scan_work(struct work_struct *work) +{ + int timeout = 10; + int i, j, code; + struct ske_keypad *keypad = container_of(work, + struct ske_keypad, scan_work.work); + struct input_dev *input = keypad->input; - while ((readl(keypad->reg_base + SKE_CR) & SKE_KPASON) && --timeout) + /* Wait for autoscan to complete */ + while (readl(keypad->reg_base + SKE_CR) & SKE_KPASON) cpu_relax(); /* SKEx registers are stable and can be read */ ske_keypad_read_data(keypad); - /* wait until raw interrupt is clear */ - while ((readl(keypad->reg_base + SKE_RIS)) && --timeout) - msleep(KEY_PRESSED_DELAY); + /* Check for key actions */ + for (i = 0; i < SKE_KPD_MAX_ROWS; i++) { + for (j = 0; j < SKE_KPD_MAX_COLS; j++) { + switch (keypad->keys[i][j]) { + case KEY_REPORTED: + /** + * Key was reported but is no longer pressed, + * report it as released. + */ + code = MATRIX_SCAN_CODE(i, j, + SKE_KEYPAD_ROW_SHIFT); + input_event(input, EV_MSC, MSC_SCAN, code); + input_report_key(input, keypad->keymap[code], + 0); + input_sync(input); + keypad->keys[i][j] = 0; + dev_dbg(keypad->dev, + "%s Key release reported, code:%d\n", + __func__, code); + break; + case KEY_PRESSED: + /* Key pressed but not yet reported, report */ + code = MATRIX_SCAN_CODE(i, j, + SKE_KEYPAD_ROW_SHIFT); + input_event(input, EV_MSC, MSC_SCAN, code); + input_report_key(input, keypad->keymap[code], + 1); + input_sync(input); + dev_dbg(keypad->dev, + "%s Key press reported, code:%d\n", + __func__, code); + /* Intentional fall though */ + case (KEY_REPORTED | KEY_PRESSED): + /** + * Key pressed and reported, just reset + * KEY_PRESSED for next scan + */ + keypad->keys[i][j] = KEY_REPORTED; + break; + } + } + } + + if (keypad->key_pressed) { + /* Key still pressed, schedule work to poll changes in 50 ms */ + schedule_delayed_work(&keypad->scan_work, + msecs_to_jiffies(50)); + } else { + /* For safty measure, clear interrupt once more */ + ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA); - /* enable auto scan interrupts */ - ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA); + /* Wait for raw interrupt to clear */ + while ((readl(keypad->reg_base + SKE_RIS) & SKE_KPRISA) && + --timeout) { + udelay(10); + } + + if (!timeout) + dev_err(keypad->dev, + "%s Timeed out waiting on irq to clear\n", + __func__); + + /* enable auto scan interrupts */ + ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA); + + /** + * Schedule the work queue to change it to GPIO mode + * if there is no activity in SKE mode + */ + if (!keypad->key_pressed && keypad->enable) + schedule_delayed_work(&keypad->work, + keypad->board->switch_delay); + } } static void ske_gpio_switch_work(struct work_struct *work) @@ -333,6 +416,10 @@ static void ske_gpio_release_work(struct work_struct *work) code = MATRIX_SCAN_CODE(keypad->gpio_row, keypad->gpio_col, SKE_KEYPAD_ROW_SHIFT); + + dev_dbg(keypad->dev, "%s Key press reported, code:%d\n", + __func__, code); + input_event(input, EV_MSC, MSC_SCAN, code); input_report_key(input, keypad->keymap[code], 1); input_sync(input); @@ -366,7 +453,7 @@ static void ske_set_cols(struct ske_keypad *keypad, int col) int i ; int value; - /* + /** * Set all columns except the requested column * output pin as high */ @@ -419,10 +506,16 @@ static irqreturn_t ske_keypad_gpio_irq(int irq, void *dev_id) if (!keypad->enable) { keypad->enable = true; ske_mode_enable(keypad, true); + /** + * Schedule the work queue to change it back to GPIO + * mode if there is no activity in SKE mode + */ + schedule_delayed_work(&keypad->work, + keypad->board->switch_delay); } - /* - * Schedule the work queue to change it to - * report the key pressed, if it is not detected in SKE mode. + /** + * Schedule delayed work to report key press if it is not + * detected in SKE mode. */ if (keypad->key_pressed) schedule_delayed_work(&keypad->gpio_work, @@ -434,18 +527,14 @@ static irqreturn_t ske_keypad_gpio_irq(int irq, void *dev_id) static irqreturn_t ske_keypad_irq(int irq, void *dev_id) { struct ske_keypad *keypad = dev_id; - cancel_delayed_work_sync(&keypad->gpio_work); cancel_delayed_work_sync(&keypad->work); - ske_keypad_scan(keypad); - /* - * Schedule the work queue to change it to - * GPIO mode, if there is no activity in SKE mode - */ - if (!keypad->key_pressed && keypad->enable) - schedule_delayed_work(&keypad->work, - keypad->board->switch_delay); + /* disable auto scan interrupt; mask the interrupt generated */ + ske_keypad_set_bits(keypad, SKE_IMSC, SKE_KPIMA, 0x0); + ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA); + + schedule_delayed_work(&keypad->scan_work, 0); return IRQ_HANDLED; } @@ -505,6 +594,7 @@ static int __devinit ske_keypad_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to allocate keypad memory\n"); goto out_freeclk; } + keypad->dev = &pdev->dev; input = input_allocate_device(); if (!input) { @@ -557,6 +647,7 @@ static int __devinit ske_keypad_probe(struct platform_device *pdev) keypad->clk = clk; INIT_DELAYED_WORK(&keypad->work, ske_gpio_switch_work); INIT_DELAYED_WORK(&keypad->gpio_work, ske_gpio_release_work); + INIT_DELAYED_WORK(&keypad->scan_work, ske_keypad_scan_work); /* allocations are sane, we begin HW initialization */ clk_enable(keypad->clk); @@ -660,6 +751,7 @@ static int __devexit ske_keypad_remove(struct platform_device *pdev) cancel_delayed_work_sync(&keypad->gpio_work); cancel_delayed_work_sync(&keypad->work); + cancel_delayed_work_sync(&keypad->scan_work); free_irq(keypad->irq, keypad); input_unregister_device(keypad->input); @@ -692,6 +784,7 @@ static int ske_keypad_suspend(struct device *dev) else { cancel_delayed_work_sync(&keypad->gpio_work); cancel_delayed_work_sync(&keypad->work); + cancel_delayed_work_sync(&keypad->scan_work); disable_irq(irq); if (keypad->enable) { ske_mode_enable(keypad, false); -- cgit v1.2.3 From da9f54e940bc3f105497234bd214d29a282946ab Mon Sep 17 00:00:00 2001 From: Robert Marklund Date: Tue, 18 Oct 2011 08:16:07 +0200 Subject: touchscreen: Fix build problems in cyttsp driver The cyttsp driver dont have proper ifdefs around EARLYSUSPEND code. Change-Id: I03ae51f6b2593ce5cfa601158477eeb485e9f82b Signed-off-by: Robert Marklund --- drivers/input/touchscreen/cyttsp_core.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/input/touchscreen/cyttsp_core.c b/drivers/input/touchscreen/cyttsp_core.c index 7a2aa109034..2d9ce61bf54 100755 --- a/drivers/input/touchscreen/cyttsp_core.c +++ b/drivers/input/touchscreen/cyttsp_core.c @@ -38,7 +38,9 @@ #include #include #include +#ifdef CONFIG_HAS_EARLYSUSPEND #include +#endif #include #include #include @@ -269,7 +271,9 @@ struct cyttsp { struct work_struct work; struct timer_list timer; struct mutex mutex; +#ifdef CONFIG_HAS_EARLYSUSPEND struct early_suspend early_suspend; +#endif char phys[32]; struct cyttsp_platform_data *platform_data; struct cyttsp_bootloader_data bl_data; -- cgit v1.2.3 From f4b8f34294c3bc1056760ff851bb7063d356bf57 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(+) (limited to 'drivers') diff --git a/drivers/input/misc/ab8500-accdet.c b/drivers/input/misc/ab8500-accdet.c index 9bd52183a26..3cd440a2631 100644 --- a/drivers/input/misc/ab8500-accdet.c +++ b/drivers/input/misc/ab8500-accdet.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include -- cgit v1.2.3