blob: f67c33f727cbf940f3d8ce3f5bd4ce73f6656eee [file] [log] [blame]
/*
* drivers/input/touchscreen/nt11001.c
*
* Copyright (C) 2004-2010, Ambarella, Inc.
* Zhenwu Xue <zwxue@ambarella.com>
*
*
* 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 <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/i2c/nt11001.h>
#ifdef CONFIG_DEBUG_TOUCHSCREEN
#define NT11001_DEBUG(format, arg...) printk(format , ## arg)
#else
#define NT11001_DEBUG(format, arg...)
#endif
#define MAX_X 3930
#define MAX_Y 2384
#define MAX_Z 16
typedef enum {
NT_GESTURE_CODE = 0x00,
NT_Y1_HIGH,
NT_Y1_LOW,
NT_X1_HIGH,
NT_X1_LOW,
NT_Y2_HIGH,
NT_Y2_LOW,
NT_X2_HIGH,
NT_X2_LOW,
NT_OPERATION_CNTL1 = 0x0c,
NT_OPERATION_CNTL2,
NT_CHIP_ID,
NT_SW_VERSION,
} nt11001_sub_addr_t;
#define NUM_DATA 17
struct nt11001 {
char phys[32];
struct input_dev *input;
struct i2c_client *client;
struct workqueue_struct *workqueue;
struct work_struct report_worker;
u8 reg_data[NUM_DATA];
int irq;
struct nt11001_fix_data fix;
int (*get_pendown_state)(void);
void (*clear_penirq)(void);
};
static inline int nt11001_read_all(struct nt11001 *nt)
{
struct i2c_msg msg;
int errorCode;
u8 buf[] = {NT_GESTURE_CODE};
msg.addr = nt->client->addr;
msg.flags = nt->client->flags;
msg.buf = buf;
msg.len = 1;
errorCode = i2c_transfer(nt->client->adapter, &msg, 1);
if (errorCode != 1) {
printk("NT11001: Can't write reg data.\n");
return -EIO;
}
msg.addr = nt->client->addr;
msg.flags = nt->client->flags | I2C_M_RD;
msg.buf = nt->reg_data;
msg.len = NUM_DATA;
errorCode = i2c_transfer(nt->client->adapter, &msg, 1);
if (errorCode != 1) {
printk("NT11001: Can't read reg data.\n");
return -EIO;
}
return 0;
}
static void nt11001_send_event(struct nt11001 *nt)
{
struct input_dev *input = nt->input;
u8 finger_state[2];
static int prev_touch = 0;
static int curr_touch = 0;
int event = 0;
finger_state[0] = (nt->reg_data[NT_GESTURE_CODE] & 0x10) >> 4;
finger_state[1] = (nt->reg_data[NT_GESTURE_CODE] & 0x20) >> 5;
curr_touch = finger_state[0] + finger_state[1];
/* Button Pressed */
if (!prev_touch && curr_touch) {
input_report_abs(input, BTN_TOUCH, curr_touch);
NT11001_DEBUG("Finger Pressed\n");
}
/* Button Released */
if (prev_touch && !curr_touch) {
event = 1;
input_report_abs(input, ABS_PRESSURE, 0);
input_report_abs(input, BTN_TOUCH, 0);
input_report_abs(input, ABS_MT_TOUCH_MAJOR, 0);
input_mt_sync(input);
NT11001_DEBUG("Finger Released\n\n\n");
}
if (curr_touch) {
u8 x1h, x1l, y1h, y1l;
u32 x1, y1;
x1h = nt->reg_data[NT_X1_HIGH] & 0x01;
x1l = nt->reg_data[NT_X1_LOW] & 0xff;
y1h = nt->reg_data[NT_Y1_HIGH] & 0x01;
y1l = nt->reg_data[NT_Y1_LOW] & 0xff;
x1 = (x1h << 8) | x1l;
y1 = (y1h << 8) | y1l;
NT11001_DEBUG("Finger1 Raw: (%d, %d)\n", x1, y1);
if (x1 < nt->fix.x_min) {
x1 = nt->fix.x_min;
}
if (x1 > nt->fix.x_max) {
x1 = nt->fix.x_max;
}
if (y1 < nt->fix.y_min) {
y1 = nt->fix.y_min;
}
if (y1 > nt->fix.y_max) {
y1 = nt->fix.y_max;
}
if (nt->fix.x_invert)
x1 = nt->fix.x_max - x1 + nt->fix.x_min;
if (nt->fix.y_invert)
y1 = nt->fix.y_max - y1 + nt->fix.y_min;
event = 1;
input_report_abs(input, ABS_PRESSURE, MAX_Z);
input_report_abs(input, ABS_X, x1);
input_report_abs(input, ABS_Y, y1);
input_report_abs(input, ABS_MT_TOUCH_MAJOR, MAX_Z);
input_report_abs(input, ABS_MT_POSITION_X, x1);
input_report_abs(input, ABS_MT_POSITION_Y, y1);
input_mt_sync(input);
NT11001_DEBUG("Finger1 Calibrated: (%d, %d)\n", x1, y1);
}
if (curr_touch >= 2) {
u8 x2h, x2l, y2h, y2l;
u32 x2, y2;
x2h = nt->reg_data[NT_X2_HIGH] & 0x01;
x2l = nt->reg_data[NT_X2_LOW] & 0xff;
y2h = nt->reg_data[NT_Y2_HIGH] & 0x01;
y2l = nt->reg_data[NT_Y2_LOW] & 0xff;
x2 = (x2h << 8) | x2l;
y2 = (y2h << 8) | y2l;
NT11001_DEBUG("Finger2 Raw: (%d, %d)\n", x2, y2);
if (x2 < nt->fix.x_min) {
x2 = nt->fix.x_min;
}
if (x2 > nt->fix.x_max) {
x2 = nt->fix.x_max;
}
if (y2 < nt->fix.y_min) {
y2 = nt->fix.y_min;
}
if (y2 > nt->fix.y_max) {
y2 = nt->fix.y_max;
}
if (nt->fix.x_invert)
x2 = nt->fix.x_max - x2 + nt->fix.x_min;
if (nt->fix.y_invert)
y2 = nt->fix.y_max - y2 + nt->fix.y_min;
event = 1;
input_report_abs(input, ABS_MT_TOUCH_MAJOR, MAX_Z);
input_report_abs(input, ABS_MT_POSITION_X, x2);
input_report_abs(input, ABS_MT_POSITION_Y, y2);
input_mt_sync(input);
NT11001_DEBUG("Finger2 Calibrated: (%d, %d)\n", x2, y2);
}
if (event)
input_sync(input);
prev_touch = curr_touch;
}
static irqreturn_t nt11001_irq(int irq, void *handle)
{
struct nt11001 *nt = handle;
if (nt->clear_penirq) {
nt->clear_penirq();
}
queue_work(nt->workqueue, &nt->report_worker);
return IRQ_HANDLED;
}
static void nt11001_report_worker(struct work_struct *work)
{
struct nt11001 *nt;
nt = container_of(work, struct nt11001, report_worker);
nt11001_read_all(nt);
nt11001_send_event(nt);
}
static int nt11001_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct input_dev *input_dev;
struct nt11001 *nt;
struct nt11001_platform_data *pdata;
int err;
pdata = client->dev.platform_data;
if (!pdata) {
dev_err(&client->dev, "platform data is required!\n");
return -EINVAL;
}
pdata->init_platform_hw();
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_WRITE_BYTE_DATA | I2C_FUNC_SMBUS_WRITE_BYTE |
I2C_FUNC_SMBUS_READ_BYTE))
return -EIO;
nt = kzalloc(sizeof(struct nt11001), GFP_KERNEL);
input_dev = input_allocate_device();
if (!nt || !input_dev) {
err = -ENOMEM;
goto err_free_mem;
}
nt->client = client;
i2c_set_clientdata(client, nt);
nt->input = input_dev;
nt->get_pendown_state = pdata->get_pendown_state;
nt->clear_penirq = pdata->clear_penirq;
snprintf(nt->phys, sizeof(nt->phys),
"%s/input0", dev_name(&client->dev));
nt->fix = pdata->fix;
err = nt11001_read_all(nt);
if (err)
goto err_free_mem;
input_dev->name = "Novatek NT11001 Touchscreen";
input_dev->phys = nt->phys;
input_dev->id.bustype = BUS_I2C;
set_bit(EV_SYN, input_dev->evbit);
set_bit(EV_KEY, input_dev->evbit);
set_bit(BTN_TOUCH, input_dev->keybit);
set_bit(EV_ABS, input_dev->evbit);
input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_Z, 0, 0);
input_set_abs_params(input_dev, ABS_X, nt->fix.x_min, nt->fix.x_max, 0, 0);
input_set_abs_params(input_dev, ABS_Y, nt->fix.y_min, nt->fix.y_max, 0, 0);
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, MAX_Z, 0, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_X, nt->fix.x_min, nt->fix.x_max, 0, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_Y, nt->fix.y_min, nt->fix.y_max, 0, 0);
nt->workqueue = create_singlethread_workqueue("nt11001");
INIT_WORK(&nt->report_worker, nt11001_report_worker);
nt->irq = client->irq;
err = request_irq(nt->irq, nt11001_irq, IRQF_TRIGGER_FALLING,
client->dev.driver->name, nt);
if (err < 0) {
dev_err(&client->dev, "irq %d busy?\n", nt->irq);
goto err_free_mem;
}
err = input_register_device(input_dev);
if (err)
goto err_free_irq;
dev_info(&client->dev, "chip id: %d, software version: %d", nt->reg_data[NT_CHIP_ID], nt->reg_data[NT_SW_VERSION]);
return 0;
err_free_irq:
free_irq(nt->irq, nt);
err_free_mem:
input_free_device(input_dev);
kfree(nt);
return err;
}
static int nt11001_remove(struct i2c_client *client)
{
struct nt11001 *nt = i2c_get_clientdata(client);
struct nt11001_platform_data *pdata = client->dev.platform_data;
pdata->exit_platform_hw();
destroy_workqueue(nt->workqueue);
free_irq(nt->irq, nt);
input_unregister_device(nt->input);
kfree(nt);
return 0;
}
static struct i2c_device_id nt11001_idtable[] = {
{ "nt11001", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, nt11001_idtable);
static struct i2c_driver nt11001_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "nt11001"
},
.id_table = nt11001_idtable,
.probe = nt11001_probe,
.remove = nt11001_remove,
};
static int __init nt11001_init(void)
{
return i2c_add_driver(&nt11001_driver);
}
static void __exit nt11001_exit(void)
{
i2c_del_driver(&nt11001_driver);
}
module_init(nt11001_init);
module_exit(nt11001_exit);
MODULE_AUTHOR("Zhenwu Xue <zwxue@ambarella.com>");
MODULE_DESCRIPTION("Novatek NT11001 TouchScreen Driver");
MODULE_LICENSE("GPL");