/* * linux/drivers/i2c/chips/tsc2003.c * * Copyright (C) 2005 Bill Gatliff * * 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. * * Driver for TI's TSC2003 I2C Touch Screen Controller */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DRIVER_NAME "tsc2003" enum tsc2003_pd { PD_POWERDOWN = 0, /* penirq */ PD_IREFOFF_ADCON = 1, /* no penirq */ PD_IREFON_ADCOFF = 2, /* penirq */ PD_IREFON_ADCON = 3, /* no penirq */ PD_PENIRQ_ARM = PD_IREFON_ADCOFF, PD_PENIRQ_DISARM = PD_IREFON_ADCON, }; enum tsc2003_m { M_12BIT = 0, M_8BIT = 1 }; enum tsc2003_cmd { MEAS_TEMP0 = 0, MEAS_VBAT1 = 1, MEAS_IN1 = 2, MEAS_TEMP1 = 4, MEAS_VBAT2 = 5, MEAS_IN2 = 6, ACTIVATE_NX_DRIVERS = 8, ACTIVATE_NY_DRIVERS = 9, ACTIVATE_YNX_DRIVERS = 10, MEAS_XPOS = 12, MEAS_YPOS = 13, MEAS_Z1POS = 14, MEAS_Z2POS = 15 }; #define TSC2003_CMD(cn,pdn,m) (((cn) << 4) | ((pdn) << 2) | ((m) << 1)) #define ADC_MAX ((1 << 12) - 1) struct tsc2003_data { struct i2c_client *client; struct input_dev *idev; struct timer_list penirq_timer; struct semaphore sem; struct task_struct *tstask; struct completion tstask_completion; enum tsc2003_pd pd; enum tsc2003_m m; int vbat1; int vbat2; int temp0; int temp1; int in1; int in2; }; static int tsc2003_i2c_detect (struct i2c_adapter *adapter, int address, int kind); static inline int tsc2003_command (struct tsc2003_data *data, enum tsc2003_cmd cmd, enum tsc2003_pd pd) { char c; int ret; down(&data->sem); c = TSC2003_CMD(cmd, pd, data->m); ret = i2c_master_send(data->client, &c, 1); up(&data->sem); return ret; } static int tsc2003_read (struct tsc2003_data *data, enum tsc2003_cmd cmd, enum tsc2003_pd pd, int *val) { char c; char d[2]; int ret; c = TSC2003_CMD(cmd, pd, data->m); ret = i2c_master_send(data->client, &c, 1); udelay(20); ret = i2c_master_recv(data->client, d, data->m == M_12BIT ? 2 : 1); if (val) { *val = d[0]; *val <<= 4; if (data->m == M_12BIT) *val += (d[1] >> 4); } #if defined(CONFIG_I2C_DEBUG_CHIP) printk(KERN_ERR "%s: val[%x] = %d\n", __FUNCTION__, cmd, (((int)d[0]) << 8) + d[1]); #endif return 0; if (!ret) ret = -ENODEV; return ret; } static inline int tsc2003_read_temp0 (struct tsc2003_data *d, enum tsc2003_pd pd, int *t) { return tsc2003_read(d, MEAS_TEMP0, pd, t); } static inline int tsc2003_read_temp1 (struct tsc2003_data *d, enum tsc2003_pd pd, int *t) { return tsc2003_read(d, MEAS_TEMP1, pd, t); } static inline int tsc2003_read_xpos (struct tsc2003_data *d, enum tsc2003_pd pd, int *x) { return tsc2003_read(d, MEAS_XPOS, pd, x); } static inline int tsc2003_read_ypos (struct tsc2003_data *d, enum tsc2003_pd pd, int *y) { return tsc2003_read(d, MEAS_YPOS, pd, y); } static inline int tsc2003_read_pressure (struct tsc2003_data *d, enum tsc2003_pd pd, int *p) { return tsc2003_read(d, MEAS_Z1POS, pd, p); } static inline int tsc2003_read_in1 (struct tsc2003_data *d, enum tsc2003_pd pd, int *t) { return tsc2003_read(d, MEAS_IN1, pd, t); } static inline int tsc2003_read_in2 (struct tsc2003_data *d, enum tsc2003_pd pd, int *t) { return tsc2003_read(d, MEAS_IN2, pd, t); } static inline int tsc2003_read_vbat1 (struct tsc2003_data *d, enum tsc2003_pd pd, int *t) { return tsc2003_read(d, MEAS_VBAT1, pd, t); } static inline int tsc2003_read_vbat2 (struct tsc2003_data *d, enum tsc2003_pd pd, int *t) { return tsc2003_read(d, MEAS_VBAT2, pd, t); } static inline int tsc2003_powerdown (struct tsc2003_data *d) { /* we don't have a distinct powerdown command, so do a benign read with the PD bits cleared */ return tsc2003_read(d, MEAS_IN1, PD_POWERDOWN, 0); } void tsc2003_init_client (struct i2c_client *client) { struct tsc2003_data *data = i2c_get_clientdata(client); data->pd = PD_PENIRQ_DISARM; data->m = M_8BIT; return; } static int tsc2003ts_thread (void *v) { struct tsc2003_data *d = v; struct task_struct *tsk = current; int pendown=0; d->tstask = tsk; daemonize(DRIVER_NAME "tsd"); allow_signal(SIGKILL); complete(&d->tstask_completion); printk(KERN_INFO "%s: address 0x%x\n", __FUNCTION__, d->client->addr); while (!signal_pending(tsk)) { unsigned int x, y, p; tsc2003_read_xpos(d, PD_PENIRQ_DISARM, &x); tsc2003_read_ypos(d, PD_PENIRQ_DISARM, &y); tsc2003_read_pressure(d, PD_PENIRQ_DISARM, &p); /* non-X-Y driver read to avoid glitch in penirq (errata?) */ if (p > 100) { pendown = 1; input_report_abs(d->idev, ABS_X, x); input_report_abs(d->idev, ABS_Y, y); input_report_abs(d->idev, ABS_PRESSURE, p); input_report_key(d->idev, BTN_TOUCH, 1); input_sync(d->idev); } else if (pendown == 1) { pendown = 0; input_report_key(d->idev, BTN_TOUCH, 0); input_report_abs(d->idev, ABS_PRESSURE, 0); input_sync(d->idev); } schedule_timeout_interruptible(msecs_to_jiffies(10)); } d->tstask = NULL; complete_and_exit(&d->tstask_completion, 0); } static int tsc2003_idev_open (struct input_dev *idev) { struct tsc2003_data *d = idev->private; int ret = 0; if (down_interruptible(&d->sem)) return -EINTR; if (d->tstask) panic(DRIVER_NAME "tsd already running (!). abort."); init_completion(&d->tstask_completion); ret = kernel_thread(tsc2003ts_thread, d, CLONE_KERNEL); if (ret >= 0) { wait_for_completion(&d->tstask_completion); ret = 0; } up(&d->sem); return ret; } static void tsc2003_idev_close (struct input_dev *idev) { struct tsc2003_data *d = idev->private; down_interruptible(&d->sem); if (d->tstask) { send_sig(SIGKILL, d->tstask, 1); wait_for_completion(&d->tstask_completion); } up(&d->sem); return; } #if defined(CONFIG_SYSFS) && defined(CONFIG_SENSORS_TSC2003_SYSFS) static ssize_t show_addr (struct device *dev, char *buf) { struct tsc2003_data *d = container_of(dev->driver, struct tsc2003_data, driver); return sprintf(buf, "%d\n", d->client.addr); } static DEVICE_ATTR(addr, S_IRUGO, show_addr, NULL); static ssize_t show_vbat1 (struct device *dev, char *buf) { struct tsc2003_data *d = container_of(dev->driver, struct tsc2003_data, driver); return sprintf(buf, "%d\n", d->vbat1); } static DEVICE_ATTR(vbat1, S_IRUGO, show_vbat1, NULL); static ssize_t show_vbat2 (struct device *dev, char *buf) { struct tsc2003_data *d = container_of(dev->driver, struct tsc2003_data, driver); return sprintf(buf, "%d\n", d->vbat2); } static DEVICE_ATTR(vbat2, S_IRUGO, show_vbat2, NULL); static ssize_t show_in1 (struct device *dev, char *buf) { struct tsc2003_data *d = container_of(dev->driver, struct tsc2003_data, driver); return sprintf(buf, "%d\n", d->in1); } static DEVICE_ATTR(in1, S_IRUGO, show_in1, NULL); static ssize_t show_in2 (struct device *dev, char *buf) { struct tsc2003_data *d = container_of(dev->driver, struct tsc2003_data, driver); return sprintf(buf, "%d\n", d->in2); } static DEVICE_ATTR(in2, S_IRUGO, show_in2, NULL); static ssize_t show_temp0 (struct device *dev, char *buf) { struct tsc2003_data *d = container_of(dev->driver, struct tsc2003_data, driver); return sprintf(buf, "%d\n", d->temp0); } static DEVICE_ATTR(temp0, S_IRUGO, show_temp0, NULL); static ssize_t show_temp1 (struct device *dev, char *buf) { struct tsc2003_data *d = container_of(dev->driver, struct tsc2003_data, driver); return sprintf(buf, "%d\n", d->temp1); } static DEVICE_ATTR(temp1, S_IRUGO, show_temp1, NULL); #warning "TODO: this daemon sometimes hangs the touchscreen daemon" #warning "TODO: under periods of heavy touch screen activity." #warning "TODO: Use with caution until the bug is squashed." static int tsc2003s_thread (void *v) { struct tsc2003_data *d = v; daemonize(DRIVER_NAME "sd"); allow_signal(SIGKILL); printk(KERN_INFO "%s: address 0x%x\n", __FUNCTION__, d->client.addr); while (!signal_pending(current)) { if (!down_interruptible(&d->sem)) { if (!timer_pending(&d->penirq_timer)) { tsc2003_read_vbat1(d, d->pd, &d->vbat1); tsc2003_read_vbat2(d, d->pd, &d->vbat2); tsc2003_read_in1(d, d->pd, &d->in1); tsc2003_read_in2(d, d->pd, &d->in2); tsc2003_read_temp0(d, d->pd, &d->temp0); tsc2003_read_temp1(d, d->pd, &d->temp1); } up(&d->sem); } set_task_state(current, TASK_INTERRUPTIBLE); schedule_timeout(5 * HZ); } do_exit(0); } #endif static int tsc2003_driver_register (struct tsc2003_data *data) { int ret = 0; init_MUTEX(&data->sem); data->idev = input_allocate_device(); if(!data->idev) { return -ENOMEM; } data->idev->private = data; data->idev->name = DRIVER_NAME; data->idev->evbit[0] = BIT(EV_ABS); data->idev->open = tsc2003_idev_open; data->idev->close = tsc2003_idev_close; data->idev->absbit[LONG(ABS_X)] = BIT(ABS_X); data->idev->absbit[LONG(ABS_Y)] = BIT(ABS_Y); data->idev->absbit[LONG(ABS_PRESSURE)] = BIT(ABS_PRESSURE); input_set_abs_params(data->idev, ABS_X, 0, ADC_MAX, 0, 0); input_set_abs_params(data->idev, ABS_Y, 0, ADC_MAX, 0, 0); ret = input_register_device(data->idev); if(ret) { input_free_device(data->idev); return ret; } return ret; } /* Magic definition of all other variables and things */ static unsigned short normal_i2c[] = {0x48, 0x49, 0x4a, 0x48b, I2C_CLIENT_END }; I2C_CLIENT_INSMOD; static int tsc2003_i2c_attach_adapter(struct i2c_adapter *adapter) { return i2c_probe(adapter, &addr_data, tsc2003_i2c_detect); } static int tsc2003_i2c_detach_client(struct i2c_client *client) { struct tsc2003_data *data=i2c_get_clientdata(client); int err; input_unregister_device(data->idev); err = i2c_detach_client(client); if (err) { dev_err(&client->dev, "Client deregistration failed, " "client not detached.\n"); return err; } return 0; } static struct i2c_driver tsc2003_i2c_driver = { .driver = { .owner = THIS_MODULE, .name = DRIVER_NAME, }, .attach_adapter = tsc2003_i2c_attach_adapter, .detach_client = tsc2003_i2c_detach_client, .command = NULL }; static struct i2c_client client_template = { .name = "TSC2003", .driver = &tsc2003_i2c_driver, }; static int tsc2003_i2c_detect (struct i2c_adapter *adap, int addr, int kind) { struct i2c_client *i2c; int ret; struct tsc2003_data *data; client_template.adapter = adap; client_template.addr = addr; printk(KERN_INFO "tsc2003 i2c touch screen controller\n"); printk(KERN_INFO "Bill Gatliff \n"); i2c = kmemdup(&client_template, sizeof(client_template), GFP_KERNEL); if (i2c == NULL){ return -ENOMEM; } data = kcalloc(1, sizeof(*data), GFP_KERNEL); if (!data) { ret = -ENOMEM; goto err; } data->client = i2c; i2c_set_clientdata(i2c, data); ret = i2c_attach_client(i2c); if (ret < 0) { printk("failed to attach codec at addr %x\n", addr); goto err; } tsc2003_init_client(i2c); tsc2003_powerdown(data); ret = tsc2003_driver_register(data); if(ret < 0) { printk("driver_register failed\n"); goto err; } return ret; err: kfree(i2c); return ret; } #define tsc2003_suspend NULL #define tsc2003_resume NULL static int __devinit tsc2003_probe(struct platform_device *dev) { int ret; ret=i2c_add_driver(&tsc2003_i2c_driver); if(ret) return ret; #if defined(CONFIG_SYSFS) && defined(CONFIG_SENSORS_TSC2003_SYSFS) ret = kernel_thread(tsc2003s_thread, d, CLONE_KERNEL); if (ret >= 0) ret = 0; device_create_file(dev, &dev_attr_addr); device_create_file(dev, &dev_attr_vbat1); device_create_file(dev, &dev_attr_vbat2); device_create_file(dev, &dev_attr_in1); device_create_file(dev, &dev_attr_in2); device_create_file(dev, &dev_attr_temp0); device_create_file(dev, &dev_attr_temp1); #endif return 0; } static int __devexit tsc2003_remove(struct platform_device *dev) { i2c_del_driver(&tsc2003_i2c_driver); return 0; } static struct platform_driver tsc2003_driver = { .probe = tsc2003_probe, .remove = __devexit_p(tsc2003_remove), .suspend = tsc2003_suspend, .resume = tsc2003_resume, .driver = { .name = "tsc2003", }, }; static int __init tsc2003_init(void) { return platform_driver_register(&tsc2003_driver); } static void __exit tsc2003_exit(void) { platform_driver_unregister(&tsc2003_driver); } MODULE_AUTHOR("Bill Gatliff "); MODULE_DESCRIPTION("TSC2003 Touch Screen Controller driver"); MODULE_LICENSE("GPL"); module_init(tsc2003_init); module_exit(tsc2003_exit);