blob: bb5322c815c8bfc05cbea508db01623e70687538 [file] [log] [blame]
/*
* drivers/input/touchscreen/hx8526.c
*
* Copyright (C) 2004-2012, 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/hx8526.h>
#ifdef CONFIG_DEBUG_TOUCHSCREEN
#define HX8526_DEBUG(format, arg...) printk(format , ## arg)
#else
#define HX8526_DEBUG(format, arg...)
#endif
#define MAX_Z 16
#define MAX_FINGERS 4
typedef enum {
HX_X0_HI = 0x00,
HX_X0_LO,
HX_Y0_HI,
HX_Y0_LO,
HX_X1_HI,
HX_X1_LO,
HX_Y1_HI,
HX_Y1_LO,
HX_X2_HI,
HX_X2_LO,
HX_Y2_HI,
HX_Y2_LO,
HX_X3_HI,
HX_X3_LO,
HX_Y3_HI,
HX_Y3_LO,
HX_SLEEP_OUT = 0x81,
HX_SENSE_ON = 0x83,
HX_READ_ALL_EVENT = 0x86,
} hx8526_sub_addr_t;
#define NUM_DATA 24
struct hx8526 {
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 hx8526_fix_data fix;
int (*get_pendown_state)(void);
void (*clear_penirq)(void);
};
static int hx8526_reg_init(struct hx8526 *hx)
{
u8 data[2];
if(i2c_smbus_write_i2c_block_data(hx->client, HX_SLEEP_OUT, 0, data)) {
printk("I2C Error: %s\n", __func__);
return -EIO;
}
mdelay(1);
data[0] = 0x02;
if(i2c_smbus_write_i2c_block_data(hx->client, 0x42, 1, data)) {
printk("I2C Error: %s\n", __func__);
return -EIO;
}
mdelay(200);
data[0] = 0x02;
if(i2c_smbus_write_i2c_block_data(hx->client, 0x35, 1, data)) {
printk("I2C Error: %s\n", __func__);
return -EIO;
}
mdelay(1);
data[0] = 0x0f;
data[1] = 0x53;
if(i2c_smbus_write_i2c_block_data(hx->client, 0x36, 2, data)) {
printk("I2C Error: %s\n", __func__);
return -EIO;
}
mdelay(1);
data[0] = 0x04;
data[1] = 0x02;
if(i2c_smbus_write_i2c_block_data(hx->client, 0xdd, 2, data)) {
printk("I2C Error: %s\n", __func__);
return -EIO;
}
mdelay(1);
if(i2c_smbus_write_i2c_block_data(hx->client, HX_SENSE_ON, 0, data)) {
printk("I2C Error: %s\n", __func__);
return -EIO;
}
return 0;
}
static inline int hx8526_read_all(struct hx8526 *hx)
{
if (i2c_smbus_read_i2c_block_data(hx->client,
HX_READ_ALL_EVENT, NUM_DATA, hx->reg_data) != NUM_DATA) {
printk("I2C Error: %s\n", __func__);
return -EIO;
} else {
return 0;
}
}
static void hx8526_send_event(struct hx8526 *hx)
{
struct input_dev *input = hx->input;
u8 i, finger;
static int prev_touch = 0;
static int curr_touch = 0;
int event = 0;
switch (hx->reg_data[20]) {
case 0xf1:
case 0xf2:
case 0xf3:
case 0xf4:
curr_touch = hx->reg_data[20] & 0x0f;
break;
default:
curr_touch = 0;
break;
}
/* Button Pressed */
if (!prev_touch && curr_touch) {
input_report_key(input, BTN_TOUCH, curr_touch);
HX8526_DEBUG("Finger Pressed\n");
}
/* Button Released */
if (prev_touch && !curr_touch) {
event = 1;
input_report_abs(input, ABS_PRESSURE, 0);
input_report_key(input, BTN_TOUCH, 0);
input_report_abs(input, ABS_MT_TOUCH_MAJOR, 0);
input_mt_sync(input);
HX8526_DEBUG("Finger Released\n\n\n");
}
for (i = 0, finger = 0; i < MAX_FINGERS; i++) {
u8 xh, xl, yh, yl;
u32 x, y;
xh = hx->reg_data[HX_X0_HI + 4 * i];
xl = hx->reg_data[HX_X0_LO + 4 * i];
yh = hx->reg_data[HX_Y0_HI + 4 * i];
yl = hx->reg_data[HX_Y0_LO + 4 * i];
if (xh == 0xff && xl == 0xff && yh == 0xff && yl == 0xff) {
continue;
} else {
finger++;
}
xh = xh & 0x0f;
yh = yh & 0x0f;
x = (xh << 8) | xl;
y = (yh << 8) | yl;
HX8526_DEBUG("Finger%d Raw: (%d, %d)\n", finger, x, y);
if (x < hx->fix.x_min) {
x = hx->fix.x_min;
}
if (x > hx->fix.x_max) {
x = hx->fix.x_max;
}
if (y < hx->fix.y_min) {
y = hx->fix.y_min;
}
if (y > hx->fix.y_max) {
y = hx->fix.y_max;
}
if (hx->fix.x_invert) {
x = hx->fix.x_max - x + hx->fix.x_min;
}
if (hx->fix.y_invert) {
y = hx->fix.y_max - y + hx->fix.y_min;
}
event = 1;
if (finger == 1) {
input_report_abs(input, ABS_PRESSURE, MAX_Z);
input_report_abs(input, ABS_X, x);
input_report_abs(input, ABS_Y, y);
}
input_report_abs(input, ABS_MT_TOUCH_MAJOR, MAX_Z);
input_report_abs(input, ABS_MT_POSITION_X, x);
input_report_abs(input, ABS_MT_POSITION_Y, y);
input_mt_sync(input);
HX8526_DEBUG("Finger%d Calibrated: (%d, %d)\n", finger, x, y);
}
if (event)
input_sync(input);
prev_touch = curr_touch;
}
static irqreturn_t hx8526_irq(int irq, void *handle)
{
struct hx8526 *hx = handle;
if (hx->get_pendown_state && !hx->get_pendown_state())
goto hx8526_irq_exit;
if (hx->clear_penirq) {
hx->clear_penirq();
}
queue_work(hx->workqueue, &hx->report_worker);
hx8526_irq_exit:
return IRQ_HANDLED;
}
static void hx8526_report_worker(struct work_struct *work)
{
struct hx8526 *hx;
hx = container_of(work, struct hx8526, report_worker);
hx8526_read_all(hx);
hx8526_send_event(hx);
}
static int hx8526_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct input_dev *input_dev;
struct hx8526 *hx;
struct hx8526_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;
hx = kzalloc(sizeof(struct hx8526), GFP_KERNEL);
input_dev = input_allocate_device();
if (!hx || !input_dev) {
err = -ENOMEM;
goto err_free_mem;
}
hx->client = client;
i2c_set_clientdata(client, hx);
hx->input = input_dev;
hx->get_pendown_state = pdata->get_pendown_state;
hx->clear_penirq = pdata->clear_penirq;
snprintf(hx->phys, sizeof(hx->phys),
"%s/input0", dev_name(&client->dev));
err = hx8526_reg_init(hx);
if (err)
goto err_free_mem;
hx->fix = pdata->fix[HX8526_FAMILY_0];
err = hx8526_read_all(hx);
if (err)
goto err_free_mem;
input_dev->name = "Synaptics HX8526 Touchscreen";
input_dev->phys = hx->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, hx->fix.x_min, hx->fix.x_max, 0, 0);
input_set_abs_params(input_dev, ABS_Y, hx->fix.y_min, hx->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, hx->fix.x_min, hx->fix.x_max, 0, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_Y, hx->fix.y_min, hx->fix.y_max, 0, 0);
hx->workqueue = create_singlethread_workqueue("hx8526");
INIT_WORK(&hx->report_worker, hx8526_report_worker);
hx->irq = client->irq;
err = request_irq(hx->irq, hx8526_irq, IRQF_TRIGGER_FALLING,
client->dev.driver->name, hx);
if (err < 0) {
dev_err(&client->dev, "irq %d busy?\n", hx->irq);
goto err_free_mem;
}
err = input_register_device(input_dev);
if (err)
goto err_free_irq;
return 0;
err_free_irq:
free_irq(hx->irq, hx);
err_free_mem:
input_free_device(input_dev);
kfree(hx);
return err;
}
static int hx8526_remove(struct i2c_client *client)
{
struct hx8526 *hx = i2c_get_clientdata(client);
struct hx8526_platform_data *pdata = client->dev.platform_data;
pdata->exit_platform_hw();
destroy_workqueue(hx->workqueue);
free_irq(hx->irq, hx);
input_unregister_device(hx->input);
kfree(hx);
return 0;
}
static struct i2c_device_id hx8526_idtable[] = {
{ "hx8526", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, hx8526_idtable);
static struct i2c_driver hx8526_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "hx8526"
},
.id_table = hx8526_idtable,
.probe = hx8526_probe,
.remove = hx8526_remove,
};
static int __init hx8526_init(void)
{
return i2c_add_driver(&hx8526_driver);
}
static void __exit hx8526_exit(void)
{
i2c_del_driver(&hx8526_driver);
}
module_init(hx8526_init);
module_exit(hx8526_exit);
MODULE_AUTHOR("Zhenwu Xue <zwxue@ambarella.com>");
MODULE_DESCRIPTION("HiMax HX8526 TouchScreen Driver");
MODULE_LICENSE("GPL");