/* * drivers/input/touchscreen/tsc2004.c * * Copyright (C) 2009 Texas Instruments Inc * Author: Vaibhav Hiremath * * Using code from: * - tsc2007.c * * This package 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. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include #include #include #include #include #include #include static int calibration[7]; module_param_array(calibration, int, NULL, S_IRUGO | S_IWUSR); static void translate(u16 *px, u16 *py) { int x, y, x1, y1; if (calibration[6]) { x1 = *px; y1 = *py; x = calibration[0] * x1 + calibration[1] * y1 + calibration[2]; x /= calibration[6]; if (x < 0) x = 0; y = calibration[3] * x1 + calibration[4] * y1 + calibration[5]; y /= calibration[6]; if (y < 0) y = 0; *px = x ; *py = y ; } } #define TS_PENUP_TIMEOUT_MS 40 /* Control byte 0 */ #define TSC2004_CMD0(addr, pnd, rw) ((addr<<3)|(pnd<<1)|rw) /* Control byte 1 */ #define TSC2004_CMD1(cmd, mode, rst) ((1<<7)|(cmd<<4)|(mode<<2)|(rst<<1)) /* Command Bits */ #define READ_REG 1 #define WRITE_REG 0 #define SWRST_TRUE 1 #define SWRST_FALSE 0 #define PND0_TRUE 1 #define PND0_FALSE 0 /* Converter function mapping */ enum convertor_function { MEAS_X_Y_Z1_Z2, /* Measure X,Y,z1 and Z2: 0x0 */ MEAS_X_Y, /* Measure X and Y only: 0x1 */ MEAS_X, /* Measure X only: 0x2 */ MEAS_Y, /* Measure Y only: 0x3 */ MEAS_Z1_Z2, /* Measure Z1 and Z2 only: 0x4 */ MEAS_AUX, /* Measure Auxillary input: 0x5 */ MEAS_TEMP1, /* Measure Temparature1: 0x6 */ MEAS_TEMP2, /* Measure Temparature2: 0x7 */ MEAS_AUX_CONT, /* Continuously measure Auxillary input: 0x8 */ X_DRV_TEST, /* X-Axis drivers tested 0x9 */ Y_DRV_TEST, /* Y-Axis drivers tested 0xA */ /*Command Reserved*/ SHORT_CKT_TST = 0xC, /* Short circuit test: 0xC */ XP_XN_DRV_STAT, /* X+,Y- drivers status: 0xD */ YP_YN_DRV_STAT, /* X+,Y- drivers status: 0xE */ YP_XN_DRV_STAT /* Y+,X- drivers status: 0xF */ }; /* Register address mapping */ enum register_address { X_REG, /* X register: 0x0 */ Y_REG, /* Y register: 0x1 */ Z1_REG, /* Z1 register: 0x2 */ Z2_REG, /* Z2 register: 0x3 */ AUX_REG, /* AUX register: 0x4 */ TEMP1_REG, /* Temp1 register: 0x5 */ TEMP2_REG, /* Temp2 register: 0x6 */ STAT_REG, /* Status Register: 0x7 */ AUX_HGH_TH_REG, /* AUX high threshold register: 0x8 */ AUX_LOW_TH_REG, /* AUX low threshold register: 0x9 */ TMP_HGH_TH_REG, /* Temp high threshold register:0xA */ TMP_LOW_TH_REG, /* Temp low threshold register: 0xB */ CFR0_REG, /* Configuration register 0: 0xC */ CFR1_REG, /* Configuration register 1: 0xD */ CFR2_REG, /* Configuration register 2: 0xE */ CONV_FN_SEL_STAT /* Convertor function select register: 0xF */ }; /* Supported Resolution modes */ enum resolution_mode { MODE_10BIT, /* 10 bit resolution */ MODE_12BIT /* 12 bit resolution */ }; /* Configuraton register bit fields */ /* CFR0 */ #define PEN_STS_CTRL_MODE (1<<15) #define ADC_STS (1<<14) #define RES_CTRL (1<<13) #define ADC_CLK_4MHZ (0<<11) #define ADC_CLK_2MHZ (1<<11) #define ADC_CLK_1MHZ (2<<11) #define PANEL_VLTG_STB_TIME_0US (0<<8) #define PANEL_VLTG_STB_TIME_100US (1<<8) #define PANEL_VLTG_STB_TIME_500US (2<<8) #define PANEL_VLTG_STB_TIME_1MS (3<<8) #define PANEL_VLTG_STB_TIME_5MS (4<<8) #define PANEL_VLTG_STB_TIME_10MS (5<<8) #define PANEL_VLTG_STB_TIME_50MS (6<<8) #define PANEL_VLTG_STB_TIME_100MS (7<<8) /* CFR2 */ #define PINTS1 (1<<15) #define PINTS0 (1<<14) #define MEDIAN_VAL_FLTR_SIZE_1 (0<<12) #define MEDIAN_VAL_FLTR_SIZE_3 (1<<12) #define MEDIAN_VAL_FLTR_SIZE_7 (2<<12) #define MEDIAN_VAL_FLTR_SIZE_15 (3<<12) #define AVRG_VAL_FLTR_SIZE_1 (0<<10) #define AVRG_VAL_FLTR_SIZE_3_4 (1<<10) #define AVRG_VAL_FLTR_SIZE_7_8 (2<<10) #define AVRG_VAL_FLTR_SIZE_16 (3<<10) #define MAV_FLTR_EN_X (1<<4) #define MAV_FLTR_EN_Y (1<<3) #define MAV_FLTR_EN_Z (1<<2) #define MAX_12BIT ((1 << 12) - 1) #define MEAS_MASK 0xFFF struct ts_event { u16 x; u16 y; u16 z1, z2; }; struct tsc2004 { struct input_dev *input; char phys[32]; struct delayed_work work; struct i2c_client *client; u16 x_plate_ohms; bool pendown; int irq; int reset_gpio; int reset_active_low; int irq_gpio; int use_count; }; static int tsc2004_read_word_data(struct tsc2004 *tsc, u8 cmd) { s32 data; u16 val; data = i2c_smbus_read_word_data(tsc->client, cmd); if (data < 0) { dev_err(&tsc->client->dev, "i2c io (read) error: %d\n", data); return data; } /* * We need to swap byte order for little-endian cpus. */ val = be16_to_cpu(data); dev_dbg(&tsc->client->dev, "cmd: 0x%x, val: 0x%x\n", cmd, val); return val; } static inline int tsc2004_read_xyz_data(struct tsc2004 *tsc, u8 cmd) { int val = tsc2004_read_word_data(tsc, cmd); /* * 12 bit precision, high 4 bits should be zero */ if (val >= 0) val &= 0xfff; return val; } static inline int tsc2004_write_word_data(struct tsc2004 *tsc, u8 cmd, u16 data) { u16 val; val = cpu_to_be16(data); return i2c_smbus_write_word_data(tsc->client, cmd, val); } static inline int tsc2004_write_cmd(struct tsc2004 *tsc, u8 value) { return i2c_smbus_write_byte(tsc->client, value); } static int tsc2004_reset(struct i2c_client *client) { int err; int retries = 0; /* Reset the TSC, configure for 12 bit */ do { /* Reset the TSC, configure for 12 bit */ int cmd = TSC2004_CMD1(MEAS_X_Y_Z1_Z2, MODE_12BIT, SWRST_TRUE); err = i2c_smbus_write_byte(client, cmd); if (err < 0) pr_err("%s: write_cmd %d\n", __func__, err); } while ( (err < 0) && (3 < retries++) ); return err; } static int tsc2004_prepare_for_reading(struct tsc2004 *ts) { int err; int cmd, data; /* Enable interrupt for PENIRQ and DAV */ cmd = TSC2004_CMD0(CFR2_REG, PND0_FALSE, WRITE_REG); data = PINTS1 | PINTS0 | MEDIAN_VAL_FLTR_SIZE_15 | AVRG_VAL_FLTR_SIZE_7_8 | MAV_FLTR_EN_X | MAV_FLTR_EN_Y | MAV_FLTR_EN_Z; err = tsc2004_write_word_data(ts, cmd, data); if (err < 0) return err; /* Configure the TSC in TSMode 1 */ cmd = TSC2004_CMD0(CFR0_REG, PND0_FALSE, WRITE_REG); data = PEN_STS_CTRL_MODE | ADC_CLK_2MHZ | PANEL_VLTG_STB_TIME_1MS; err = tsc2004_write_word_data(ts, cmd, data); if (err < 0) return err; /* Enable x, y, z1 and z2 conversion functions */ cmd = TSC2004_CMD1(MEAS_X_Y_Z1_Z2, MODE_12BIT, SWRST_FALSE); err = tsc2004_write_cmd(ts, cmd); if (err < 0) return err; return 0; } static void tsc2004_read_values(struct tsc2004 *tsc, struct ts_event *tc) { int cmd; int err; /* Read X Measurement */ cmd = TSC2004_CMD0(X_REG, PND0_FALSE, READ_REG); tc->x = tsc2004_read_xyz_data(tsc, cmd); /* Read Y Measurement */ cmd = TSC2004_CMD0(Y_REG, PND0_FALSE, READ_REG); tc->y = tsc2004_read_xyz_data(tsc, cmd); /* Read Z1 Measurement */ cmd = TSC2004_CMD0(Z1_REG, PND0_FALSE, READ_REG); tc->z1 = tsc2004_read_xyz_data(tsc, cmd); /* Read Z2 Measurement */ cmd = TSC2004_CMD0(Z2_REG, PND0_FALSE, READ_REG); tc->z2 = tsc2004_read_xyz_data(tsc, cmd); tc->x &= MEAS_MASK; tc->y &= MEAS_MASK; tc->z1 &= MEAS_MASK; tc->z2 &= MEAS_MASK; /* Prepare for touch readings */ err = tsc2004_reset(tsc->client); if (err < 0) { dev_dbg(&tsc->client->dev, "Failed to reset TSC, %d\n", err); return; } if (tsc2004_prepare_for_reading(tsc) < 0) dev_dbg(&tsc->client->dev, "Failed to prepare TSC for next" "reading\n"); } static u32 tsc2004_calculate_pressure(struct tsc2004 *tsc, struct ts_event *tc) { u32 rt = 0; /* range filtering */ if (tc->x == MAX_12BIT) tc->x = 0; if (likely(tc->x && tc->z1)) { /* compute touch pressure resistance using equation #1 */ rt = tc->z2 - tc->z1; rt *= tc->x; rt *= tsc->x_plate_ohms; rt /= tc->z1; rt = (rt + 2047) >> 12; } return rt; } static void tsc2004_work(struct work_struct *work) { int cmd; int val; struct tsc2004 *ts = container_of(to_delayed_work(work), struct tsc2004, work); if (!ts->pendown) return; cmd = TSC2004_CMD0(CFR0_REG, PND0_FALSE, READ_REG); val = tsc2004_read_word_data(ts, cmd); if (val & 0x8000) { schedule_delayed_work(&ts->work, msecs_to_jiffies(TS_PENUP_TIMEOUT_MS)); } else { struct input_dev *input = ts->input; if (ts->pendown) { dev_dbg(&ts->client->dev, "UP\n"); input_report_key(input, BTN_TOUCH, 0); input_report_abs(input, ABS_PRESSURE, 0); input_sync(input); ts->pendown = false; } } } static irqreturn_t tsc2004_irq(int irq, void *handle) { struct tsc2004 *ts = handle; struct ts_event tc; u32 rt; /* * NOTE: We can't rely on the pressure to determine the pen down * state, even though this controller has a pressure sensor. * The pressure value can fluctuate for quite a while after * lifting the pen and in some cases may not even settle at the * expected value. * */ if (ts->irq_gpio >= 0) if (gpio_get_value(ts->irq_gpio)) return IRQ_NONE; cancel_delayed_work_sync(&ts->work); tsc2004_read_values(ts, &tc); rt = tsc2004_calculate_pressure(ts, &tc); if ((rt > 0) && (rt <= MAX_12BIT)) { struct input_dev *input = ts->input; translate(&tc.x, &tc.y); if (!ts->pendown) { dev_dbg(&ts->client->dev, "DOWN\n"); input_report_key(input, BTN_TOUCH, 1); ts->pendown = true; } input_report_abs(input, ABS_X, tc.x); input_report_abs(input, ABS_Y, tc.y); input_report_abs(input, ABS_PRESSURE, rt); input_sync(input); dev_dbg(&ts->client->dev, "point(%4d,%4d), pressure (%4u)\n", tc.x, tc.y, rt); } schedule_delayed_work(&ts->work, msecs_to_jiffies(TS_PENUP_TIMEOUT_MS)); return IRQ_HANDLED; } static int setup_reset_gpio(struct i2c_client *client, struct tsc2004 *ts) { int err = 0; int gpio; enum of_gpio_flags flags; struct device_node *np = client->dev.of_node; ts->reset_gpio = -1; gpio = of_get_named_gpio_flags(np, "reset-gpios", 0, &flags); pr_info("%s:%d\n", __func__, gpio); if (!gpio_is_valid(gpio)) return 0; ts->reset_active_low = flags & OF_GPIO_ACTIVE_LOW; err = devm_gpio_request_one(&client->dev, gpio, ts->reset_active_low ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW, "tsc2004_reset_gpio"); if (err) dev_err(&client->dev, "can't request reset gpio %d", gpio); ts->reset_gpio = gpio; return err; } static int setup_irq_gpio(struct i2c_client *client, struct tsc2004 *ts) { int err = 0; int gpio; struct device_node *np = client->dev.of_node; ts->irq_gpio = -1; gpio = of_get_named_gpio(np, "interrupts-extended", 0); pr_info("%s:%d\n", __func__, gpio); if (!gpio_is_valid(gpio)) return 0; err = devm_gpio_request_one(&client->dev, gpio, GPIOF_DIR_IN, "tsc2004_irq"); if (err) { dev_err(&client->dev, "can't request irq gpio %d", gpio); return err; } ts->irq_gpio = gpio; return err; } static int ts2004_open(struct input_dev *idev) { struct tsc2004 *ts = input_get_drvdata(idev); int ret; if (!ts) return -EIO; if (ts->use_count++) return 0; ret = request_threaded_irq(ts->irq, NULL, tsc2004_irq, IRQF_TRIGGER_LOW | IRQF_ONESHOT, ts->client->dev.driver->name, ts); if (ret < 0) { dev_err(&ts->client->dev, "irq %d busy(%d)?\n", ts->irq, ret); ts->use_count--; return ret; } return 0; } static void ts2004_close(struct input_dev *idev) { struct tsc2004 *ts = input_get_drvdata(idev); if (ts) { if (--ts->use_count == 0) { free_irq(ts->irq, ts); cancel_delayed_work_sync(&ts->work); } } } static int tsc2004_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct tsc2004 *ts; struct input_dev *input_dev; int err; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_WORD_DATA)) return -EIO; err = tsc2004_reset(client); if (err < 0) { dev_err(&client->dev, "Failed to reset TSC %d\n", err); return err; } ts = kzalloc(sizeof(struct tsc2004), GFP_KERNEL); input_dev = input_allocate_device(); if (!ts || !input_dev) { err = -ENOMEM; goto err_free_mem; } ts->client = client; ts->irq = client->irq; ts->input = input_dev; INIT_DELAYED_WORK(&ts->work, tsc2004_work); input_dev->open = ts2004_open; input_dev->close = ts2004_close; ts->x_plate_ohms = 500; setup_reset_gpio(client, ts); setup_irq_gpio(client, ts); snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&client->dev)); input_dev->name = "tsc2004"; input_dev->phys = ts->phys; input_dev->id.bustype = BUS_I2C; input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0); input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0); input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0); /* Prepare for touch readings */ err = tsc2004_prepare_for_reading(ts); if (err < 0) goto err_free_mem; input_set_drvdata(input_dev, ts); err = input_register_device(input_dev); if (err) goto err_free_mem; i2c_set_clientdata(client, ts); return 0; err_free_mem: input_free_device(input_dev); kfree(ts); return err; } static int tsc2004_remove(struct i2c_client *client) { struct tsc2004 *ts = i2c_get_clientdata(client); input_unregister_device(ts->input); if (gpio_is_valid(ts->reset_gpio)) gpio_set_value(ts->reset_gpio, ts->reset_active_low ? 0 : 1); kfree(ts); return 0; } static struct i2c_device_id tsc2004_idtable[] = { { "tsc2004", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, tsc2004_idtable); static const struct of_device_id tsc2004_dt_ids[] = { { .compatible = "tsc2004,tsc2004-touch", }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, tsc2004_dt_ids); static struct i2c_driver tsc2004_driver = { .driver = { .owner = THIS_MODULE, .name = "tsc2004", .of_match_table = tsc2004_dt_ids, }, .id_table = tsc2004_idtable, .probe = tsc2004_probe, .remove = tsc2004_remove, }; static int __init tsc2004_init(void) { return i2c_add_driver(&tsc2004_driver); } static void __exit tsc2004_exit(void) { i2c_del_driver(&tsc2004_driver); } module_init(tsc2004_init); module_exit(tsc2004_exit); MODULE_AUTHOR("Vaibhav Hiremath "); MODULE_DESCRIPTION("TSC2004 TouchScreen Driver"); MODULE_LICENSE("GPL");