blob: fd87ff45ed8c7cb96aaa2e27a1e6a1e7c350d48e [file] [log] [blame]
/*
* Copyright (C) 2009,2010 Imagination Technologies Limited.
*
* Quantum TouchScreen Controller driver.
*/
#include <linux/types.h>
#include <linux/wait.h>
#include <linux/fs.h>
#include <linux/poll.h>
#include <linux/miscdevice.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/timer.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/input/ts_qt5480.h>
#include "ts_qt5480.h"
/*
* TODO
* - multi-touch support
* - clean up debug messages
* - drop Pure's interface and migrate to just /dev/input
* - figure out unreliability of writes
*/
#ifndef I2C_M_WR
#define I2C_M_WR 0x00
#endif
#define QT5480_NAME "ts_qt5480"
#define QT5480_MAX_I2C_RETRIES 5
#define QT5480_SLAVE_ADDRESS 0x30
/* Polling Frequency */
#define QT5480_POLL_TIME (HZ/25)
/* FIXME this is probably not correct */
#define MAX_PRESSURE_VALUE 0x10
/* touch-screen touch event */
struct ts_qt5480_touch {
struct list_head link;
unsigned char x;
unsigned char y;
unsigned char size;
unsigned char area;
};
/* touch-screen gesture event */
struct ts_qt5480_gesture {
unsigned char event;
unsigned char dir;
unsigned char dist;
unsigned char x;
unsigned char y;
};
/* touch-screen chip info */
struct ts_qt5480_status {
unsigned char update_flags;
unsigned char general_status_1;
unsigned char general_status_2;
unsigned char key[6];
unsigned char slider[6];
struct ts_qt5480_touch touchscr[2];
struct ts_qt5480_gesture gesture[2];
};
/* Driver data */
struct ts_qt5480_data {
char phys[32];
struct input_dev *input;
int (*poll_status)(void);
/* Control data */
unsigned int used;
struct mutex mutex;
/* Chip Connections Configuration */
struct i2c_client *client;
struct i2c_adapter *i2c_adap;
unsigned char dev_addr;
ts_qt5480_mapping_t *phy_map;
ts_qt5480_conf_reg_t *config;
/* Chip Live Data */
struct ts_qt5480_status chip_status;
/* Touch state */
bool touch1_down;
};
static struct ts_qt5480_data *ts_qt5480_data;
static int ts_qt5480_read_change(struct ts_qt5480_data *data,
unsigned char *buf);
static void ts_qt5480_parse_data(struct ts_qt5480_data *data,
unsigned char *buf,
struct ts_qt5480_status *chip_status);
static irqreturn_t ts_qt5480_isr_check(int irq, void *p)
{
return IRQ_WAKE_THREAD;
}
static irqreturn_t ts_qt5480_isr(int irq, void *p)
{
struct ts_qt5480_data *data = p;
unsigned char buf[5] = { };
while (!data->poll_status()) {
mutex_lock(&data->mutex);
if (!ts_qt5480_read_change(data, buf)) {
ts_qt5480_parse_data(data, buf,
&data->chip_status);
}
mutex_unlock(&data->mutex);
}
return IRQ_HANDLED;
}
static int ts_qt5480_read_reg(struct ts_qt5480_data *data, short addr,
unsigned char *value)
{
struct i2c_msg msg = { };
struct ts_qt5480_frame frame = { };
unsigned char buf[5] = { };
unsigned int retries = 0;
unsigned long timeout;
short read_ptr = addr >> 2;
dev_dbg(&data->client->dev, "ts_qt5480_read_reg()\n");
do {
/*
* If we have reach the maximum number of retries,
* lets report error.
*/
if (++retries == QT5480_MAX_I2C_RETRIES) {
dev_err(&data->client->dev,
"ts_qt5480_read_reg() - Error: Setting register %d. Max retries achieved\n",
addr);
return -ENODEV;
}
/* Prepare the address and the data */
msg.addr = data->dev_addr;
msg.flags = I2C_M_WR;
msg.len = 3;
msg.buf = (__u8 *) &frame;
frame.addr = QT_ADDRESS_PTR;
frame.data[0] = read_ptr;
/* write the "read address pointer" */
frame.stat = i2c_transfer(data->i2c_adap, &msg, 1);
if (frame.stat < 0) {
dev_warn(&data->client->dev,
"ts_qt5480_read_reg() - Error (%d): Failed writing the read address pointer\n",
frame.stat);
continue;
}
udelay(500);
/* Wait for roughly 10ms. */
timeout = jiffies + (10 * HZ / 1000);
/*
* Wait, with timeout, for the change line asserted low
* (requested data ready signal).
*/
while (time_before(jiffies, timeout)) {
/* if change line asserted low, lets read the frame */
if (!data->poll_status()) {
/* Read back the data */
frame.stat = ts_qt5480_read_change(data, buf);
if (frame.stat < 0) {
dev_warn(&data->client->dev,
"ts_qt5480_read_reg() - Error (%d): Failed reading reg %d\n",
frame.stat, addr);
}
break;
}
}
if (frame.stat < 0)
continue;
/*
* If we haven't get the data we are asking for, lets
* request it again.
*/
} while (buf[0] != read_ptr);
/* Copy the value back */
(*value) = buf[(addr & 0x03) + 1];
return 0;
}
static int ts_qt5480_write_reg(struct ts_qt5480_data *data, short addr,
unsigned char value)
{
struct i2c_msg msg = { };
struct ts_qt5480_frame frame = { };
dev_dbg(&data->client->dev, "ts_qt5480_write_reg()\n");
/* Prepare the address and the data */
msg.addr = data->dev_addr;
msg.flags = I2C_M_WR;
msg.len = 3;
msg.buf = (__u8 *) &frame;
frame.addr = addr;
frame.data[0] = value;
/* Write the value into the register */
frame.stat = i2c_transfer(data->i2c_adap, &msg, 1);
if (frame.stat < 0) {
dev_warn(&data->client->dev,
"ts_qt5480_write_reg() - Error %d: Failed writing reg %d\n",
frame.stat, addr);
return -ENODEV;
}
/*
* Writing registers too quickly appears to cause the device
* to issues NACKs.
*/
udelay(500);
return 0;
}
static int ts_qt5480_reset(struct ts_qt5480_data *data)
{
unsigned char buf[5] = { };
unsigned long timeout;
int ret;
unsigned char chipid = 0, software = 0;
dev_dbg(&data->client->dev, "ts_qt5480_reset()\n");
/* Writing a non-zero value causes a reset. */
ret = ts_qt5480_write_reg(data, QT_RESET, 1);
if (ret < 0) {
dev_warn(&data->client->dev,
"ts_qt5480_reset() - Error %d: Failed writing reset reg\n",
ret);
return -ENODEV;
}
/*
* Wait for 40ms. This is necessary because the device sometimes
* starts up with the CHANGE line asserted and we have to wait for
* it to be deasserted before looking for the reset response.
*/
msleep(40);
/* Timeout after roughly 15ms. */
timeout = jiffies + (15 * HZ / 1000);
/* Wait for reset to be acknowledged. */
while (data->poll_status() && time_before(jiffies, timeout))
cpu_relax();
if (data->poll_status()) {
dev_warn(&data->client->dev,
"ts_qt5480_reset() - Error: did not respond to reset\n");
}
/* Read response. */
ret = ts_qt5480_read_change(data, buf);
if (ret < 0) {
dev_warn(&data->client->dev,
"ts_qt5480_reset() - Error %d: reading status\n",
ret);
}
/* Test for reset bit. */
if (!(buf[3] & 0x80)) {
dev_warn(&data->client->dev,
"ts_qt5480_reset() - incorrect reset response: %#hhx\n",
buf[3]);
}
ret = ts_qt5480_read_reg(data, QT_CHIP_ID, &chipid);
if (ret < 0) {
dev_warn(&data->client->dev,
"ts_qt5480_reset() - Error %d: reading chip id\n",
ret);
return -ENODEV;
}
ret = ts_qt5480_read_reg(data, QT_CODE_VERSION, &software);
if (ret < 0) {
dev_warn(&data->client->dev,
"ts_qt5480_reset() - Error %d: reading chip id\n",
ret);
return -ENODEV;
}
dev_info(&data->client->dev,
"Found device, chip ID %#hhx, software %d.%d\n",
chipid, (software >> 4), software & 0xf);
return 0;
}
static int ts_qt5480_send_config(struct ts_qt5480_data *data)
{
int reg_addr = 0;
int ret = -1;
ts_qt5480_conf_reg_t *config = data->config;
dev_dbg(&data->client->dev, "ts_qt5480_send_config()");
for (reg_addr = 0; reg_addr < QT_MAX_REG; reg_addr++) {
int i;
if (!config[reg_addr].set)
continue;
for (i = 0; i < QT5480_MAX_I2C_RETRIES; i++) {
/* write the register */
ret = ts_qt5480_write_reg(data, reg_addr,
config[reg_addr].value);
if (!ret)
break;
}
if (ret < 0) {
dev_err(&data->client->dev,
"ts_qt5480_send_config() - Error: Failed write reg %d\n",
reg_addr);
return ret;
}
}
/* Send a Calibration request with the new configuration */
ret = ts_qt5480_write_reg(data, QT_CALIBRATE, 0x1);
if (ret < 0) {
dev_err(&data->client->dev,
"ts_qt5480_send_config() - Error: Calibrating after Config\n");
return ret;
}
return 0;
}
static int ts_qt5480_read_change(struct ts_qt5480_data *data,
unsigned char *buf)
{
struct i2c_msg msg = { };
int ret = -1;
dev_dbg(&data->client->dev, "ts_qt5480_read_change()\n");
/* Read back the data */
msg.addr = data->dev_addr;
msg.flags = I2C_M_RD;
msg.len = 5;
msg.buf = buf;
ret = i2c_transfer(data->i2c_adap, &msg, 1);
dev_dbg(&data->client->dev,
"ts_qt5480_read_change() - i2c_transfer() ret %d - data %#x %#x %#x %#x %#x\n",
ret, buf[0], buf[1], buf[2], buf[3], buf[4]);
if (ret < 0) {
dev_err(&data->client->dev,
"ts_qt5480_read_change() - Warning: Failed read\n");
return -ENODEV;
}
return 0;
}
static void add_finger_release_to_queue(struct ts_qt5480_data *data,
struct ts_qt5480_touch *queue)
{
struct ts_qt5480_touch *touch = kmalloc(sizeof(struct ts_qt5480_touch),
GFP_KERNEL);
if (likely(touch)) {
touch->x = 0;
touch->y = 0;
touch->size = 0;
touch->area = 0;
list_add_tail(&touch->link, &queue->link);
dev_dbg(&data->client->dev,
"Added %p %d %d %d\n", touch, touch->x, touch->y,
touch->area);
}
}
static void ts_qt5480_parse_data(struct ts_qt5480_data *data,
unsigned char *buf,
struct ts_qt5480_status *chip_status)
{
unsigned int icnt;
struct ts_qt5480_frame frame = { };
struct input_dev *input = data->input;
/* Safety check */
if (buf == 0)
return;
/* get packet pkt_address */
frame.addr = (unsigned short)buf[0] << 2;
/* get 4 data bytes */
frame.data[0] = buf[1];
frame.data[1] = buf[2];
frame.data[2] = buf[3];
frame.data[3] = buf[4];
dev_dbg(&data->client->dev, "ts_qt5480_parseData()\n");
switch (frame.addr) {
case QT_EEPROM_CHKSUM - 2:
/* special case: isolate 16-bit checksum */
frame.stat = (((unsigned short)frame.data[3] << 8) +
frame.data[2]);
break;
case QT_KEY_STATUS_0:
for (icnt = 0; icnt < 4; icnt++)
chip_status->key[icnt] = frame.data[icnt];
/* flag key status changed */
chip_status->update_flags |= KEY_0_UPDATE;
break;
case QT_KEY_STATUS_4:
chip_status->key[4] = frame.data[0];
chip_status->key[5] = frame.data[1];
chip_status->general_status_1 = frame.data[2];
chip_status->general_status_2 = frame.data[3];
/* flag key status changed */
chip_status->update_flags |= KEY_4_UPDATE;
if ((!(chip_status->general_status_2 & TS0_DET))
&& (chip_status->touchscr[0].area != 0)) {
chip_status->touchscr[0].x = 0;
chip_status->touchscr[0].y = 0;
chip_status->touchscr[0].size = 0;
chip_status->touchscr[0].area = 0;
input_report_key(input, BTN_TOUCH, 0);
input_report_abs(input, ABS_PRESSURE, 0);
input_sync(input);
data->touch1_down = false;
add_finger_release_to_queue(data,
&chip_status->touchscr[0]);
}
/*
* If touch two is not active and is the first time we
* detect it, set a Finger-Up data.
*/
if ((!(chip_status->general_status_2 & TS1_DET))
&& (chip_status->touchscr[1].area != 0)) {
chip_status->touchscr[1].x = 0;
chip_status->touchscr[1].y = 0;
chip_status->touchscr[1].size = 0;
chip_status->touchscr[1].area = 0;
}
break;
case QT_TOUCHSCR_0_X:
case QT_TOUCHSCR_1_X:
icnt = (frame.addr == QT_TOUCHSCR_0_X) ? 0 : 1;
chip_status->touchscr[icnt].x = frame.data[0];
chip_status->touchscr[icnt].y = frame.data[2];
chip_status->touchscr[icnt].size = frame.data[1] & 0x3f;
chip_status->touchscr[icnt].area = frame.data[3] & 0x3f;
if (frame.addr == QT_TOUCHSCR_0_X
&& (chip_status->general_status_2 & TS0_DET)) {
int listEmpty = list_empty(&chip_status->touchscr[0].link);
int addEntry = 1;
struct ts_qt5480_touch *last = NULL;
if (!data->touch1_down) {
data->touch1_down = true;
input_report_key(input, BTN_TOUCH, 1);
}
input_report_abs(input, ABS_X,
chip_status->touchscr[0].x);
input_report_abs(input, ABS_Y,
chip_status->touchscr[0].y);
input_report_abs(input, ABS_PRESSURE,
chip_status->touchscr[0].size);
input_sync(input);
if (!listEmpty) {
last = list_entry(chip_status->touchscr[0].link.prev,
struct ts_qt5480_touch, link);
addEntry = (last->area == 0);
}
if (!addEntry) {
/* update last unread entry in the list with current coordinates */
last->x = chip_status->touchscr[0].x;
last->y = chip_status->touchscr[0].y;
last->area = chip_status->touchscr[0].area;
last->size = chip_status->touchscr[0].size;
dev_dbg(&data->client->dev,
"Updat %p %d %d %d\n", last,
last->x, last->y, last->area);
} else {
struct ts_qt5480_touch *touch = kmalloc(sizeof(struct ts_qt5480_touch),
GFP_KERNEL);
if (likely(touch)) {
*touch = chip_status->touchscr[0];
list_add_tail(&touch->link,
&chip_status->touchscr[0].link);
dev_dbg(&data->client->dev,
"Added %p %d %d %d\n",
touch, touch->x, touch->y,
touch->area);
}
}
} else if (frame.addr == QT_TOUCHSCR_1_X) {
/* save slider info for Touch 1 packet */
for (icnt = 0; icnt < 4; icnt++)
chip_status->slider[icnt] = frame.data[icnt];
}
break;
case QT_SLIDER_4:
for (icnt = 0; icnt < 2; icnt++)
chip_status->slider[icnt + 4] = frame.data[icnt];
break;
case QT_TOUCH_0_GESTURE:
case QT_TOUCH_1_GESTURE:
icnt = (frame.addr == QT_TOUCH_0_GESTURE) ? 0 : 1;
chip_status->gesture[icnt].event = frame.data[0] & 0x0f;
chip_status->gesture[icnt].dir =
(frame.data[0] & 0x70) >> 4;
chip_status->gesture[icnt].dist =
(frame.data[1] & 0xf0) >> 4;
chip_status->gesture[icnt].x =
(frame.data[2] << 2) +
((frame.data[1] >> 2) & 0x03);
chip_status->gesture[icnt].y = frame.data[3];
/* flag gesture received */
chip_status->update_flags |= GESTURE_0_UPDATE << icnt;
break;
default:
break;
}
}
static void ts_qt5480_mapping(struct ts_qt5480_data *data,
const struct ts_qt5480_touch *touch,
long *pX, long *pY)
{
ts_qt5480_mapping_t *phy_map = data->phy_map;
unsigned short x_sen_res = phy_map->x_sensor_res;
unsigned short x_scr_res = phy_map->x_screen_res;
unsigned short x_flip = phy_map->x_flip;
unsigned short x_sen_size = phy_map->x_sensor_size;
unsigned short x_scr_size = phy_map->x_screen_size;
short x_sen_ofst = phy_map->x_sensor_offset;
unsigned short y_sen_res = phy_map->y_sensor_res;
unsigned short y_scr_res = phy_map->y_screen_res;
unsigned short y_flip = phy_map->y_flip;
unsigned short y_sen_size = phy_map->y_sensor_size;
unsigned short y_scr_size = phy_map->y_screen_size;
short y_sen_ofst = phy_map->y_sensor_offset;
long x = 0;
long y = 0;
x = touch->x;
y = touch->y;
/* Resolution and orientation */
x *= x_scr_res;
x /= x_sen_res;
if (x_flip)
x = x_scr_res - x;
/* Physical Mapping */
x *= x_sen_size;
x /= x_scr_size;
x += (x_sen_ofst * x_scr_res) / x_scr_size;
/* Resolution and orientation */
y *= y_scr_res;
y /= y_sen_res;
if (y_flip)
y = y_scr_res - y;
/* Physical Mapping */
y *= y_sen_size;
y /= y_scr_size;
y += (y_sen_ofst * y_scr_res) / y_scr_size;
/* Return the data */
(*pX) = x;
(*pY) = y;
}
static int ts_qt5480_start_input(struct ts_qt5480_data *data)
{
/* Set Mutex protection */
mutex_lock(&data->mutex);
if (data->used) {
data->used++;
dev_dbg(&data->client->dev,
"ts_qt5480_open() - Warning: It is was already in use!!!\n");
} else {
data->used++;
dev_dbg(&data->client->dev,
"ts_qt5480_open() - Device Opened!!!\n");
}
/* Release Mutex protecction */
mutex_unlock(&data->mutex);
return 0;
}
static int ts_qt5480_open(struct inode *inode, struct file *file)
{
struct ts_qt5480_data *data = ts_qt5480_data;
int ret;
if (!data) {
pr_err("ts_qt5480_open() - Error: Device not attached\n");
return -ENODEV;
}
dev_dbg(&data->client->dev,
"ts_qt5480_open(inode %#x, file %#x)\n",
(unsigned int)inode, (unsigned int)file);
ret = ts_qt5480_start_input(data);
if (!ret)
file->private_data = data;
dev_dbg(&data->client->dev,
"ts_qt5480_open returned %d (inode %#x, file %#x)\n",
ret, (unsigned int)inode, (unsigned int)file);
return ret;
}
static int ts_qt5480_release(struct inode *inode, struct file *file)
{
struct ts_qt5480_data *data = file->private_data;
dev_dbg(&data->client->dev, "ts_qt5480_release(inode %#x, file %#x)\n",
(unsigned int)inode, (unsigned int)file);
mutex_lock(&data->mutex);
if (data->used) {
data->used--;
if (!data->used) {
dev_dbg(&data->client->dev,
"ts_qt5480_release() - Device Closed!!!\n");
} else {
dev_dbg(&data->client->dev,
"ts_qt5480_release() - Warning: It is still in use!!!\n");
}
} else {
dev_err(&data->client->dev,
"ts_qt5480_release() - Error: Device never opened!!!\n");
mutex_unlock(&data->mutex);
return -EFAULT;
}
mutex_unlock(&data->mutex);
return 0;
}
static ssize_t ts_qt5480_read(struct file *file, char *buf, size_t count,
loff_t *ppos)
{
struct ts_qt5480_data *data = file->private_data;
ts_event event = { };
dev_dbg(&data->client->dev,
"ts_qt5480_read(file %#x, buf %#x, count %#x, ppos %#x)\n",
(unsigned int)file, (unsigned int)buf, count,
(unsigned int)ppos);
mutex_lock(&data->mutex);
if (count >= sizeof(event)) {
if (!list_empty(&data->chip_status.touchscr[0].link)) {
struct ts_qt5480_touch *touch =
list_entry(data->chip_status.touchscr[0].link.next,
struct ts_qt5480_touch, link);
event.pressure = touch->area;
if (event.pressure == 0) {
event.x = 0;
event.y = 0;
} else {
unsigned long x, y;
ts_qt5480_mapping(data, touch, &x, &y);
event.x = (unsigned short)x;
event.y = (unsigned short)y;
}
dev_dbg(&data->client->dev,
"Read_ %p %d %d %d\n", touch,
touch->x, touch->y, touch->area);
list_del(&touch->link);
kfree(touch);
/* Copy Data to user */
if (copy_to_user((ts_event *) buf, &event,
sizeof(event))) {
dev_dbg(&data->client->dev,
"ts_qt5480_read() - Error: Copying data to the user area\n");
mutex_unlock(&data->mutex);
return -EFAULT;
}
} else {
/* No data to send */
count = 0;
}
}
mutex_unlock(&data->mutex);
return count;
}
static long ts_qt5480_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
struct ts_qt5480_data *data = file->private_data;
long err = -EINVAL;
dev_dbg(&data->client->dev,
"ts_qt5480_ioctl(file %#x, cmd %i, arg %i)\n",
(unsigned int)file, cmd, (unsigned int)arg);
/*
* extract the type and number bitfields, and don't decode
* wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok()
*/
if (_IOC_TYPE(cmd) != QT5480_IOCTL) {
dev_dbg(&data->client->dev,
"ts_qt5480_ioctl() - Error: Don't do family %#x\n",
_IOC_TYPE(cmd));
return -ENOTTY;
}
/*
* the direction is a bitmask, and VERIFY_WRITE catches R/W
* transfers. `Type' is user-oriented, while
* access_ok is kernel-oriented, so the concept of "read" and
* "write" is reversed
*/
if (_IOC_DIR(cmd) & _IOC_READ) {
dev_dbg(&data->client->dev,
"ts_qt5480_ioctl() - read - arg %#x\n",
(int)arg);
err = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd));
} else if (_IOC_DIR(cmd) & _IOC_WRITE) {
dev_dbg(&data->client->dev,
"ts_qt5480_ioctl() - write - arg %#x\n",
(int)arg);
err = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd));
}
if (err) {
dev_dbg(&data->client->dev,
"ts_qt5480_ioctl() - Error: access error!\n");
return -EFAULT;
} else {
dev_dbg(&data->client->dev,
"ts_qt5480_ioctl() - access OK\n");
}
mutex_lock(&data->mutex);
switch (cmd) {
case QT5480_CALIBRATE:
/* Send a Calibrate Command */
dev_dbg(&data->client->dev,
"ts_qt5480_ioctl() - QT5480_CALIBRATE\n");
err = ts_qt5480_write_reg(data, QT_CALIBRATE, 0x1);
if (err < 0) {
dev_err(&data->client->dev,
"ts_qt5480_ioctl() - Error: QT5480_CALIBRATE - Failed i2c_transfer\n");
goto out;
}
break;
case QT5480_POWER: {
/* Switch ON / OFF the device */
unsigned int power_on;
if (copy_from_user(&power_on, (int *)arg, sizeof(int))) {
dev_dbg(&data->client->dev,
"ts_qt5480_ioctl() - Error: QT5480_POWER - failed copy_from_user\n");
err = -EFAULT;
goto out;
}
err = ts_qt5480_write_reg(data, QT_CALIBRATE,
((power_on) ?
data->config[QT_LP_MODE].value :
0x00));
if (err < 0) {
dev_dbg(&data->client->dev,
"ts_qt5480_ioctl() - Error: QT5480_POWER - Failed i2c_transfer\n");
goto out;
}
break;
}
case QT5480_DEBUG:
/* Enable / Disable Debug tracking in the PDP Memory (TFT) */
/* not implemented */
return -EINVAL;
break;
case QT5480_GETREG: {
/* Read Register */
struct ts_qt5480_frame frame;
if (copy_from_user(&frame, (int *)arg,
sizeof(struct ts_qt5480_frame))) {
dev_dbg(&data->client->dev,
"ts_qt5480_ioctl() - Error: QT5480_GETREG - failed copy_from_user\n");
err = -EFAULT;
goto out;
}
frame.stat = ts_qt5480_read_reg(data, frame.addr, frame.data);
if (frame.stat < 0) {
dev_err(&data->client->dev,
"ts_qt5480_ioctl() - Error: QT5480_GETREG - Failed i2c_transfer\n");
err = frame.stat;
goto out;
}
if (copy_to_user((int *)arg, &frame,
sizeof(struct ts_qt5480_frame))) {
dev_dbg(&data->client->dev,
"ts_qt5480_ioctl() - Error: QT5480_GETREG - failed copy_to_user\n");
err = -EFAULT;
goto out;
}
break;
}
case QT5480_SETREG: {
/* Write Register */
struct ts_qt5480_frame frame;
if (copy_from_user(&frame, (int *)arg,
sizeof(struct ts_qt5480_frame))) {
dev_dbg(&data->client->dev,
"ts_qt5480_ioctl() - Error: QT5480_SETREG - failed copy_from_user\n");
err = -EFAULT;
goto out;
}
frame.stat = ts_qt5480_write_reg(data, frame.addr,
frame.data[0]);
if (frame.stat < 0) {
dev_err(&data->client->dev,
"ts_qt5480_ioctl() - Error: QT5480_SETREG - Failed i2c_transfer\n");
err = frame.stat;
goto out;
}
if (copy_to_user((int *)arg, &frame,
sizeof(struct ts_qt5480_frame))) {
dev_dbg(&data->client->dev,
"ts_qt5480_ioctl() - Error: QT5480_SETREG - failed copy_to_user\n");
err = -EFAULT;
goto out;
}
break;
}
}
out:
/* Clean up */
mutex_unlock(&data->mutex);
return err;
}
static int ts_qt5480_input_open(struct input_dev *dev)
{
struct ts_qt5480_data *data = input_get_drvdata(dev);
return ts_qt5480_start_input(data);
}
/* attach to an instance of the device that was probed on a bus.
* This function is only called if i2c_probe determined that some device
* does actually exist at this address
*/
static int ts_qt5480_probe(struct i2c_client *client,
const struct i2c_device_id *idp)
{
struct qt5480_platform_data *pdata = client->dev.platform_data;
struct ts_qt5480_data *data;
struct input_dev *input_dev;
int err = 0;
if (!pdata || !pdata->poll_status || !pdata->phy_map ||
!pdata->config) {
dev_err(&client->dev, "valid platform data is required!\n");
return -EINVAL;
}
data = kzalloc(sizeof(struct ts_qt5480_data), GFP_KERNEL);
input_dev = input_allocate_device();
if (!data || !input_dev) {
err = -ENOMEM;
goto err_mem_alloc;
}
data->dev_addr = client->addr;
data->i2c_adap = client->adapter;
data->client = client;
data->input = input_dev;
data->poll_status = pdata->poll_status;
data->phy_map = pdata->phy_map;
data->config = pdata->config;
input_set_drvdata(input_dev, data);
mutex_init(&data->mutex);
INIT_LIST_HEAD(&data->chip_status.touchscr[0].link);
/* Not used now, but better keep it initialised */
INIT_LIST_HEAD(&data->chip_status.touchscr[1].link);
snprintf(data->phys, sizeof(data->phys),
"%s/input0", dev_name(&client->dev));
input_dev->name = "QT5480 Touchscreen";
input_dev->phys = data->phys;
input_dev->id.bustype = BUS_I2C;
input_dev->open = ts_qt5480_input_open;
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, data->phy_map->x_sensor_res,
0, 0);
input_set_abs_params(input_dev, ABS_Y, 0, data->phy_map->y_sensor_res,
0, 0);
input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_PRESSURE_VALUE,
0, 0);
/* Reset the controller. */
err = ts_qt5480_reset(data);
if (err < 0) {
dev_warn(&client->dev, "could not reset controller\n");
goto err_mem_alloc;
}
/* Configure and calibrate the controller. */
if (ts_qt5480_send_config(data) < 0) {
dev_warn(&client->dev, "could not configure device\n");
}
if (request_threaded_irq(client->irq, ts_qt5480_isr_check,
ts_qt5480_isr, IRQF_ONESHOT,
"ts_qt5480", data)) {
dev_err(&client->dev, "failed to register irq\n");
goto err_mem_alloc;
}
err = input_register_device(input_dev);
if (err) {
dev_err(&client->dev, "failed to register input device: %d\n",
err);
goto err_request_irq;
}
i2c_set_clientdata(client, data);
return 0;
err_request_irq:
free_irq(client->irq, data);
err_mem_alloc:
input_free_device(input_dev);
kfree(data);
return err;
}
static int ts_qt5480_remove(struct i2c_client *client)
{
struct ts_qt5480_data *data = i2c_get_clientdata(client);
input_unregister_device(data->input);
free_irq(client->irq, data);
kfree(data);
i2c_set_clientdata(client, NULL);
return 0;
}
/* i2c Support Data */
static const unsigned short normal_i2c[] = {
QT5480_SLAVE_ADDRESS, I2C_CLIENT_END
};
static const struct i2c_device_id ts_qt5480_id[] = {
{QT5480_NAME, 0},
{}
};
MODULE_DEVICE_TABLE(i2c, ts_qt5480_id);
static struct i2c_driver i2c_driver_qt5480 = {
.driver = {
.name = QT5480_NAME,
},
.probe = ts_qt5480_probe,
.remove = ts_qt5480_remove,
.id_table = ts_qt5480_id,
.address_list = normal_i2c,
};
/* File Ops Support Data */
static const struct file_operations ts_qt5480_fops = {
.owner = THIS_MODULE,
.open = ts_qt5480_open,
.release = ts_qt5480_release,
.read = ts_qt5480_read,
.unlocked_ioctl = ts_qt5480_ioctl,
};
static struct miscdevice ts_qt5480_misc_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = "qt5480",
.fops = &ts_qt5480_fops,
};
int __init ts_qt5480_init(void)
{
int err;
err = i2c_add_driver(&i2c_driver_qt5480);
err = 0;
if (err) {
pr_err("ts_qt5480: failed to add i2c driver: %d\n", err);
return err;
}
/* Now create the /dev part. */
err = misc_register(&ts_qt5480_misc_device);
if (err < 0) {
i2c_del_driver(&i2c_driver_qt5480);
pr_err("ts_qt5480: cannot register misc device: %d\n", err);
return err;
}
return 0;
}
static void __exit ts_qt5480_exit(void)
{
misc_deregister(&ts_qt5480_misc_device);
i2c_del_driver(&i2c_driver_qt5480);
}
module_init(ts_qt5480_init);
module_exit(ts_qt5480_exit);
MODULE_AUTHOR("Pedro Teixido-Rovira <pedro.teixido-rovira@pure.com>");
MODULE_AUTHOR("Marcin Nowakowski <marcin.nowakowski@pure.com>");
MODULE_DESCRIPTION("Atmel QT5480 Touchscreen driver");
MODULE_LICENSE("GPL");