linux/drivers/input/touchscreen/ft6236.c
<<
>>
Prefs
   1/*
   2 * FocalTech FT6236 TouchScreen driver.
   3 *
   4 * Copyright (c) 2010  Focal tech Ltd.
   5 *
   6 * This software is licensed under the terms of the GNU General Public
   7 * License version 2, as published by the Free Software Foundation, and
   8 * may be copied, distributed, and modified under those terms.
   9 *
  10 * This program is distributed in the hope that it will be useful,
  11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13 * GNU General Public License for more details.
  14 */
  15
  16#include <linux/delay.h>
  17#include <linux/gpio/consumer.h>
  18#include <linux/i2c.h>
  19#include <linux/input.h>
  20#include <linux/input/mt.h>
  21#include <linux/interrupt.h>
  22#include <linux/module.h>
  23#include <linux/property.h>
  24
  25#define FT6236_MAX_TOUCH_POINTS         2
  26
  27#define FT6236_REG_TH_GROUP             0x80
  28#define FT6236_REG_PERIODACTIVE         0x88
  29#define FT6236_REG_LIB_VER_H            0xa1
  30#define FT6236_REG_LIB_VER_L            0xa2
  31#define FT6236_REG_CIPHER               0xa3
  32#define FT6236_REG_FIRMID               0xa6
  33#define FT6236_REG_FOCALTECH_ID         0xa8
  34#define FT6236_REG_RELEASE_CODE_ID      0xaf
  35
  36#define FT6236_EVENT_PRESS_DOWN         0
  37#define FT6236_EVENT_LIFT_UP            1
  38#define FT6236_EVENT_CONTACT            2
  39#define FT6236_EVENT_NO_EVENT           3
  40
  41struct ft6236_data {
  42        struct i2c_client *client;
  43        struct input_dev *input;
  44        struct gpio_desc *reset_gpio;
  45        u32 max_x;
  46        u32 max_y;
  47        bool invert_x;
  48        bool invert_y;
  49        bool swap_xy;
  50};
  51
  52/*
  53 * This struct is a touchpoint as stored in hardware.  Note that the id,
  54 * as well as the event, are stored in the upper nybble of the hi byte.
  55 */
  56struct ft6236_touchpoint {
  57        union {
  58                u8 xhi;
  59                u8 event;
  60        };
  61        u8 xlo;
  62        union {
  63                u8 yhi;
  64                u8 id;
  65        };
  66        u8 ylo;
  67        u8 weight;
  68        u8 misc;
  69} __packed;
  70
  71/* This packet represents the register map as read from offset 0 */
  72struct ft6236_packet {
  73        u8 dev_mode;
  74        u8 gest_id;
  75        u8 touches;
  76        struct ft6236_touchpoint points[FT6236_MAX_TOUCH_POINTS];
  77} __packed;
  78
  79static int ft6236_read(struct i2c_client *client, u8 reg, u8 len, void *data)
  80{
  81        int error;
  82
  83        error = i2c_smbus_read_i2c_block_data(client, reg, len, data);
  84        if (error < 0)
  85                return error;
  86
  87        if (error != len)
  88                return -EIO;
  89
  90        return 0;
  91}
  92
  93static irqreturn_t ft6236_interrupt(int irq, void *dev_id)
  94{
  95        struct ft6236_data *ft6236 = dev_id;
  96        struct device *dev = &ft6236->client->dev;
  97        struct input_dev *input = ft6236->input;
  98        struct ft6236_packet buf;
  99        u8 touches;
 100        int i, error;
 101
 102        error = ft6236_read(ft6236->client, 0, sizeof(buf), &buf);
 103        if (error) {
 104                dev_err(dev, "read touchdata failed %d\n", error);
 105                return IRQ_HANDLED;
 106        }
 107
 108        touches = buf.touches & 0xf;
 109        if (touches > FT6236_MAX_TOUCH_POINTS) {
 110                dev_dbg(dev,
 111                        "%d touch points reported, only %d are supported\n",
 112                        touches, FT6236_MAX_TOUCH_POINTS);
 113                touches = FT6236_MAX_TOUCH_POINTS;
 114        }
 115
 116        for (i = 0; i < touches; i++) {
 117                struct ft6236_touchpoint *point = &buf.points[i];
 118                u16 x = ((point->xhi & 0xf) << 8) | buf.points[i].xlo;
 119                u16 y = ((point->yhi & 0xf) << 8) | buf.points[i].ylo;
 120                u8 event = point->event >> 6;
 121                u8 id = point->id >> 4;
 122                bool act = (event == FT6236_EVENT_PRESS_DOWN ||
 123                            event == FT6236_EVENT_CONTACT);
 124
 125                input_mt_slot(input, id);
 126                input_mt_report_slot_state(input, MT_TOOL_FINGER, act);
 127                if (!act)
 128                        continue;
 129
 130                if (ft6236->invert_x)
 131                        x = ft6236->max_x - x;
 132
 133                if (ft6236->invert_y)
 134                        y = ft6236->max_y - y;
 135
 136                if (ft6236->swap_xy) {
 137                        input_report_abs(input, ABS_MT_POSITION_X, y);
 138                        input_report_abs(input, ABS_MT_POSITION_Y, x);
 139                } else {
 140                        input_report_abs(input, ABS_MT_POSITION_X, x);
 141                        input_report_abs(input, ABS_MT_POSITION_Y, y);
 142                }
 143        }
 144
 145        input_mt_sync_frame(input);
 146        input_sync(input);
 147
 148        return IRQ_HANDLED;
 149}
 150
 151static u8 ft6236_debug_read_byte(struct ft6236_data *ft6236, u8 reg)
 152{
 153        struct i2c_client *client = ft6236->client;
 154        u8 val = 0;
 155        int error;
 156
 157        error = ft6236_read(client, reg, 1, &val);
 158        if (error)
 159                dev_dbg(&client->dev,
 160                        "error reading register 0x%02x: %d\n", reg, error);
 161
 162        return val;
 163}
 164
 165static void ft6236_debug_info(struct ft6236_data *ft6236)
 166{
 167        struct device *dev = &ft6236->client->dev;
 168
 169        dev_dbg(dev, "Touch threshold is %d\n",
 170                ft6236_debug_read_byte(ft6236, FT6236_REG_TH_GROUP) * 4);
 171        dev_dbg(dev, "Report rate is %dHz\n",
 172                ft6236_debug_read_byte(ft6236, FT6236_REG_PERIODACTIVE) * 10);
 173        dev_dbg(dev, "Firmware library version 0x%02x%02x\n",
 174                ft6236_debug_read_byte(ft6236, FT6236_REG_LIB_VER_H),
 175                ft6236_debug_read_byte(ft6236, FT6236_REG_LIB_VER_L));
 176        dev_dbg(dev, "Firmware version 0x%02x\n",
 177                ft6236_debug_read_byte(ft6236, FT6236_REG_FIRMID));
 178        dev_dbg(dev, "Chip vendor ID 0x%02x\n",
 179                ft6236_debug_read_byte(ft6236, FT6236_REG_CIPHER));
 180        dev_dbg(dev, "CTPM vendor ID 0x%02x\n",
 181                ft6236_debug_read_byte(ft6236, FT6236_REG_FOCALTECH_ID));
 182        dev_dbg(dev, "Release code version 0x%02x\n",
 183                ft6236_debug_read_byte(ft6236, FT6236_REG_RELEASE_CODE_ID));
 184}
 185
 186static void ft6236_reset(struct ft6236_data *ft6236)
 187{
 188        if (!ft6236->reset_gpio)
 189                return;
 190
 191        gpiod_set_value_cansleep(ft6236->reset_gpio, 1);
 192        usleep_range(5000, 20000);
 193        gpiod_set_value_cansleep(ft6236->reset_gpio, 0);
 194        msleep(300);
 195}
 196
 197static int ft6236_probe(struct i2c_client *client,
 198                        const struct i2c_device_id *id)
 199{
 200        struct device *dev = &client->dev;
 201        struct ft6236_data *ft6236;
 202        struct input_dev *input;
 203        u32 fuzz_x = 0, fuzz_y = 0;
 204        u8 val;
 205        int error;
 206
 207        if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
 208                return -ENXIO;
 209
 210        if (!client->irq) {
 211                dev_err(dev, "irq is missing\n");
 212                return -EINVAL;
 213        }
 214
 215        ft6236 = devm_kzalloc(dev, sizeof(*ft6236), GFP_KERNEL);
 216        if (!ft6236)
 217                return -ENOMEM;
 218
 219        ft6236->client = client;
 220        ft6236->reset_gpio = devm_gpiod_get_optional(dev, "reset",
 221                                                     GPIOD_OUT_LOW);
 222        if (IS_ERR(ft6236->reset_gpio)) {
 223                error = PTR_ERR(ft6236->reset_gpio);
 224                if (error != -EPROBE_DEFER)
 225                        dev_err(dev, "error getting reset gpio: %d\n", error);
 226                return error;
 227        }
 228
 229        ft6236_reset(ft6236);
 230
 231        /* verify that the controller is present */
 232        error = ft6236_read(client, 0x00, 1, &val);
 233        if (error) {
 234                dev_err(dev, "failed to read from controller: %d\n", error);
 235                return error;
 236        }
 237
 238        ft6236_debug_info(ft6236);
 239
 240        input = devm_input_allocate_device(dev);
 241        if (!input)
 242                return -ENOMEM;
 243
 244        ft6236->input = input;
 245        input->name = client->name;
 246        input->id.bustype = BUS_I2C;
 247
 248        if (device_property_read_u32(dev, "touchscreen-size-x",
 249                                     &ft6236->max_x) ||
 250            device_property_read_u32(dev, "touchscreen-size-y",
 251                                     &ft6236->max_y)) {
 252                dev_err(dev, "touchscreen-size-x and/or -y missing\n");
 253                return -EINVAL;
 254        }
 255
 256        device_property_read_u32(dev, "touchscreen-fuzz-x", &fuzz_x);
 257        device_property_read_u32(dev, "touchscreen-fuzz-y", &fuzz_y);
 258        ft6236->invert_x = device_property_read_bool(dev,
 259                                                     "touchscreen-inverted-x");
 260        ft6236->invert_y = device_property_read_bool(dev,
 261                                                     "touchscreen-inverted-y");
 262        ft6236->swap_xy = device_property_read_bool(dev,
 263                                                    "touchscreen-swapped-x-y");
 264
 265        if (ft6236->swap_xy) {
 266                input_set_abs_params(input, ABS_MT_POSITION_X, 0,
 267                                     ft6236->max_y, fuzz_y, 0);
 268                input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
 269                                     ft6236->max_x, fuzz_x, 0);
 270        } else {
 271                input_set_abs_params(input, ABS_MT_POSITION_X, 0,
 272                                     ft6236->max_x, fuzz_x, 0);
 273                input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
 274                                     ft6236->max_y, fuzz_y, 0);
 275        }
 276
 277        error = input_mt_init_slots(input, FT6236_MAX_TOUCH_POINTS,
 278                                    INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
 279        if (error)
 280                return error;
 281
 282        error = devm_request_threaded_irq(dev, client->irq, NULL,
 283                                          ft6236_interrupt, IRQF_ONESHOT,
 284                                          client->name, ft6236);
 285        if (error) {
 286                dev_err(dev, "request irq %d failed: %d\n", client->irq, error);
 287                return error;
 288        }
 289
 290        error = input_register_device(input);
 291        if (error) {
 292                dev_err(dev, "failed to register input device: %d\n", error);
 293                return error;
 294        }
 295
 296        return 0;
 297}
 298
 299#ifdef CONFIG_OF
 300static const struct of_device_id ft6236_of_match[] = {
 301        { .compatible = "focaltech,ft6236", },
 302        { }
 303};
 304MODULE_DEVICE_TABLE(of, ft6236_of_match);
 305#endif
 306
 307static const struct i2c_device_id ft6236_id[] = {
 308        { "ft6236", },
 309        { }
 310};
 311MODULE_DEVICE_TABLE(i2c, ft6236_id);
 312
 313static struct i2c_driver ft6236_driver = {
 314        .driver = {
 315                .name = "ft6236",
 316                .of_match_table = of_match_ptr(ft6236_of_match),
 317        },
 318        .probe = ft6236_probe,
 319        .id_table = ft6236_id,
 320};
 321module_i2c_driver(ft6236_driver);
 322
 323MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>");
 324MODULE_AUTHOR("Noralf Trønnes <noralf@tronnes.org>");
 325MODULE_DESCRIPTION("FocalTech FT6236 TouchScreen driver");
 326MODULE_LICENSE("GPL v2");
 327