Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Input: add driver for Sharp gp2ap002a00f proximity sensor

This driver adds support for Sharp's GP2AP002A00F proximity sensor. The
proximity is measured as a binary switch, i.e. an object is either
detected or not detected. Hence, this driver is implemented as a switch
that reports SW_FRONT_PROXIMITY.

Reviewed-by: Datta Shubhrajyoti <shubhrajyoti@ti.com>
Signed-off-by: Courtney Cavin <courtney.cavin@sonyericsson.com>
Signed-off-by: Oskar Andero <oskar.andero@sonyericsson.com>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>

authored by

Courtney Cavin and committed by
Dmitry Torokhov
ff803ed4 aeec0517

+334 -1
+11
drivers/input/misc/Kconfig
··· 179 179 To compile this driver as a module, choose M here: the module will 180 180 be called apanel. 181 181 182 + config INPUT_GP2A 183 + tristate "Sharp GP2AP002A00F I2C Proximity/Opto sensor driver" 184 + depends on I2C 185 + depends on GENERIC_GPIO 186 + help 187 + Say Y here if you have a Sharp GP2AP002A00F proximity/als combo-chip 188 + hooked to an I2C bus. 189 + 190 + To compile this driver as a module, choose M here: the 191 + module will be called gp2ap002a00f. 192 + 182 193 config INPUT_GPIO_TILT_POLLED 183 194 tristate "Polled GPIO tilt switch" 184 195 depends on GENERIC_GPIO
+2 -1
drivers/input/misc/Makefile
··· 22 22 obj-$(CONFIG_INPUT_CMA3000_I2C) += cma3000_d0x_i2c.o 23 23 obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o 24 24 obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o 25 - obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o 25 + obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o 26 26 obj-$(CONFIG_INPUT_GPIO_TILT_POLLED) += gpio_tilt_polled.o 27 + obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o 27 28 obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o 28 29 obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o 29 30 obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o
+299
drivers/input/misc/gp2ap002a00f.c
··· 1 + /* 2 + * Copyright (C) 2011 Sony Ericsson Mobile Communications Inc. 3 + * 4 + * Author: Courtney Cavin <courtney.cavin@sonyericsson.com> 5 + * Prepared for up-stream by: Oskar Andero <oskar.andero@sonyericsson.com> 6 + * 7 + * This program is free software; you can redistribute it and/or modify 8 + * it under the terms of the GNU General Public License version 2, as 9 + * published by the Free Software Foundation. 10 + */ 11 + 12 + #include <linux/i2c.h> 13 + #include <linux/irq.h> 14 + #include <linux/slab.h> 15 + #include <linux/input.h> 16 + #include <linux/module.h> 17 + #include <linux/interrupt.h> 18 + #include <linux/gpio.h> 19 + #include <linux/delay.h> 20 + #include <linux/input/gp2ap002a00f.h> 21 + 22 + struct gp2a_data { 23 + struct input_dev *input; 24 + const struct gp2a_platform_data *pdata; 25 + struct i2c_client *i2c_client; 26 + }; 27 + 28 + enum gp2a_addr { 29 + GP2A_ADDR_PROX = 0x0, 30 + GP2A_ADDR_GAIN = 0x1, 31 + GP2A_ADDR_HYS = 0x2, 32 + GP2A_ADDR_CYCLE = 0x3, 33 + GP2A_ADDR_OPMOD = 0x4, 34 + GP2A_ADDR_CON = 0x6 35 + }; 36 + 37 + enum gp2a_controls { 38 + /* Software Shutdown control: 0 = shutdown, 1 = normal operation */ 39 + GP2A_CTRL_SSD = 0x01 40 + }; 41 + 42 + static int gp2a_report(struct gp2a_data *dt) 43 + { 44 + int vo = gpio_get_value(dt->pdata->vout_gpio); 45 + 46 + input_report_switch(dt->input, SW_FRONT_PROXIMITY, !vo); 47 + input_sync(dt->input); 48 + 49 + return 0; 50 + } 51 + 52 + static irqreturn_t gp2a_irq(int irq, void *handle) 53 + { 54 + struct gp2a_data *dt = handle; 55 + 56 + gp2a_report(dt); 57 + 58 + return IRQ_HANDLED; 59 + } 60 + 61 + static int gp2a_enable(struct gp2a_data *dt) 62 + { 63 + return i2c_smbus_write_byte_data(dt->i2c_client, GP2A_ADDR_OPMOD, 64 + GP2A_CTRL_SSD); 65 + } 66 + 67 + static int gp2a_disable(struct gp2a_data *dt) 68 + { 69 + return i2c_smbus_write_byte_data(dt->i2c_client, GP2A_ADDR_OPMOD, 70 + 0x00); 71 + } 72 + 73 + static int gp2a_device_open(struct input_dev *dev) 74 + { 75 + struct gp2a_data *dt = input_get_drvdata(dev); 76 + int error; 77 + 78 + error = gp2a_enable(dt); 79 + if (error < 0) { 80 + dev_err(&dt->i2c_client->dev, 81 + "unable to activate, err %d\n", error); 82 + return error; 83 + } 84 + 85 + gp2a_report(dt); 86 + 87 + return 0; 88 + } 89 + 90 + static void gp2a_device_close(struct input_dev *dev) 91 + { 92 + struct gp2a_data *dt = input_get_drvdata(dev); 93 + int error; 94 + 95 + error = gp2a_disable(dt); 96 + if (error < 0) 97 + dev_err(&dt->i2c_client->dev, 98 + "unable to deactivate, err %d\n", error); 99 + } 100 + 101 + static int __devinit gp2a_initialize(struct gp2a_data *dt) 102 + { 103 + int error; 104 + 105 + error = i2c_smbus_write_byte_data(dt->i2c_client, GP2A_ADDR_GAIN, 106 + 0x08); 107 + if (error < 0) 108 + return error; 109 + 110 + error = i2c_smbus_write_byte_data(dt->i2c_client, GP2A_ADDR_HYS, 111 + 0xc2); 112 + if (error < 0) 113 + return error; 114 + 115 + error = i2c_smbus_write_byte_data(dt->i2c_client, GP2A_ADDR_CYCLE, 116 + 0x04); 117 + if (error < 0) 118 + return error; 119 + 120 + error = gp2a_disable(dt); 121 + 122 + return error; 123 + } 124 + 125 + static int __devinit gp2a_probe(struct i2c_client *client, 126 + const struct i2c_device_id *id) 127 + { 128 + const struct gp2a_platform_data *pdata = client->dev.platform_data; 129 + struct gp2a_data *dt; 130 + int error; 131 + 132 + if (!pdata) 133 + return -EINVAL; 134 + 135 + if (pdata->hw_setup) { 136 + error = pdata->hw_setup(client); 137 + if (error < 0) 138 + return error; 139 + } 140 + 141 + error = gpio_request_one(pdata->vout_gpio, GPIOF_IN, GP2A_I2C_NAME); 142 + if (error) 143 + goto err_hw_shutdown; 144 + 145 + dt = kzalloc(sizeof(struct gp2a_data), GFP_KERNEL); 146 + if (!dt) { 147 + error = -ENOMEM; 148 + goto err_free_gpio; 149 + } 150 + 151 + dt->pdata = pdata; 152 + dt->i2c_client = client; 153 + 154 + error = gp2a_initialize(dt); 155 + if (error < 0) 156 + goto err_free_mem; 157 + 158 + dt->input = input_allocate_device(); 159 + if (!dt->input) { 160 + error = -ENOMEM; 161 + goto err_free_mem; 162 + } 163 + 164 + input_set_drvdata(dt->input, dt); 165 + 166 + dt->input->open = gp2a_device_open; 167 + dt->input->close = gp2a_device_close; 168 + dt->input->name = GP2A_I2C_NAME; 169 + dt->input->id.bustype = BUS_I2C; 170 + dt->input->dev.parent = &client->dev; 171 + 172 + input_set_capability(dt->input, EV_SW, SW_FRONT_PROXIMITY); 173 + 174 + error = request_threaded_irq(client->irq, NULL, gp2a_irq, 175 + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | 176 + IRQF_ONESHOT, 177 + GP2A_I2C_NAME, dt); 178 + if (error) { 179 + dev_err(&client->dev, "irq request failed\n"); 180 + goto err_free_input_dev; 181 + } 182 + 183 + error = input_register_device(dt->input); 184 + if (error) { 185 + dev_err(&client->dev, "device registration failed\n"); 186 + goto err_free_irq; 187 + } 188 + 189 + device_init_wakeup(&client->dev, pdata->wakeup); 190 + i2c_set_clientdata(client, dt); 191 + 192 + return 0; 193 + 194 + err_free_irq: 195 + free_irq(client->irq, dt); 196 + err_free_input_dev: 197 + input_free_device(dt->input); 198 + err_free_mem: 199 + kfree(dt); 200 + err_free_gpio: 201 + gpio_free(pdata->vout_gpio); 202 + err_hw_shutdown: 203 + if (pdata->hw_shutdown) 204 + pdata->hw_shutdown(client); 205 + return error; 206 + } 207 + 208 + static int __devexit gp2a_remove(struct i2c_client *client) 209 + { 210 + struct gp2a_data *dt = i2c_get_clientdata(client); 211 + const struct gp2a_platform_data *pdata = dt->pdata; 212 + 213 + device_init_wakeup(&client->dev, false); 214 + 215 + free_irq(client->irq, dt); 216 + 217 + input_unregister_device(dt->input); 218 + kfree(dt); 219 + 220 + gpio_free(pdata->vout_gpio); 221 + 222 + if (pdata->hw_shutdown) 223 + pdata->hw_shutdown(client); 224 + 225 + return 0; 226 + } 227 + 228 + #ifdef CONFIG_PM_SLEEP 229 + static int gp2a_suspend(struct device *dev) 230 + { 231 + struct i2c_client *client = to_i2c_client(dev); 232 + struct gp2a_data *dt = i2c_get_clientdata(client); 233 + int retval = 0; 234 + 235 + if (device_may_wakeup(&client->dev)) { 236 + enable_irq_wake(client->irq); 237 + } else { 238 + mutex_lock(&dt->input->mutex); 239 + if (dt->input->users) 240 + retval = gp2a_disable(dt); 241 + mutex_unlock(&dt->input->mutex); 242 + } 243 + 244 + return retval; 245 + } 246 + 247 + static int gp2a_resume(struct device *dev) 248 + { 249 + struct i2c_client *client = to_i2c_client(dev); 250 + struct gp2a_data *dt = i2c_get_clientdata(client); 251 + int retval = 0; 252 + 253 + if (device_may_wakeup(&client->dev)) { 254 + disable_irq_wake(client->irq); 255 + } else { 256 + mutex_lock(&dt->input->mutex); 257 + if (dt->input->users) 258 + retval = gp2a_enable(dt); 259 + mutex_unlock(&dt->input->mutex); 260 + } 261 + 262 + return retval; 263 + } 264 + #endif 265 + 266 + static SIMPLE_DEV_PM_OPS(gp2a_pm, gp2a_suspend, gp2a_resume); 267 + 268 + static const struct i2c_device_id gp2a_i2c_id[] = { 269 + { GP2A_I2C_NAME, 0 }, 270 + { } 271 + }; 272 + 273 + static struct i2c_driver gp2a_i2c_driver = { 274 + .driver = { 275 + .name = GP2A_I2C_NAME, 276 + .owner = THIS_MODULE, 277 + .pm = &gp2a_pm, 278 + }, 279 + .probe = gp2a_probe, 280 + .remove = __devexit_p(gp2a_remove), 281 + .id_table = gp2a_i2c_id, 282 + }; 283 + 284 + static int __init gp2a_init(void) 285 + { 286 + return i2c_add_driver(&gp2a_i2c_driver); 287 + } 288 + 289 + static void __exit gp2a_exit(void) 290 + { 291 + i2c_del_driver(&gp2a_i2c_driver); 292 + } 293 + 294 + module_init(gp2a_init); 295 + module_exit(gp2a_exit); 296 + 297 + MODULE_AUTHOR("Courtney Cavin <courtney.cavin@sonyericsson.com>"); 298 + MODULE_DESCRIPTION("Sharp GP2AP002A00F I2C Proximity/Opto sensor driver"); 299 + MODULE_LICENSE("GPL v2");
+22
include/linux/input/gp2ap002a00f.h
··· 1 + #ifndef _GP2AP002A00F_H_ 2 + #define _GP2AP002A00F_H_ 3 + 4 + #include <linux/i2c.h> 5 + 6 + #define GP2A_I2C_NAME "gp2ap002a00f" 7 + 8 + /** 9 + * struct gp2a_platform_data - Sharp gp2ap002a00f proximity platform data 10 + * @vout_gpio: The gpio connected to the object detected pin (VOUT) 11 + * @wakeup: Set to true if the proximity can wake the device from suspend 12 + * @hw_setup: Callback for setting up hardware such as gpios and vregs 13 + * @hw_shutdown: Callback for properly shutting down hardware 14 + */ 15 + struct gp2a_platform_data { 16 + int vout_gpio; 17 + bool wakeup; 18 + int (*hw_setup)(struct i2c_client *client); 19 + int (*hw_shutdown)(struct i2c_client *client); 20 + }; 21 + 22 + #endif