/* drivers/input/touchscreen/msm_touch.c
*
* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/platform_device.h>
#include <linux/jiffies.h>
#include <linux/io.h>
#include <mach/msm_touch.h>
/* HW register map */
#define TSSC_CTL_REG 0x100
#define TSSC_SI_REG 0x108
#define TSSC_OPN_REG 0x104
#define TSSC_STATUS_REG 0x10C
#define TSSC_AVG12_REG 0x110
/* status bits */
#define TSSC_STS_OPN_SHIFT 0x6
#define TSSC_STS_OPN_BMSK 0x1C0
#define TSSC_STS_NUMSAMP_SHFT 0x1
#define TSSC_STS_NUMSAMP_BMSK 0x3E
/* CTL bits */
#define TSSC_CTL_EN (0x1 << 0)
#define TSSC_CTL_SW_RESET (0x1 << 2)
#define TSSC_CTL_MASTER_MODE (0x3 << 3)
#define TSSC_CTL_AVG_EN (0x1 << 5)
#define TSSC_CTL_DEB_EN (0x1 << 6)
#define TSSC_CTL_DEB_12_MS (0x2 << 7) /* 1.2 ms */
#define TSSC_CTL_DEB_16_MS (0x3 << 7) /* 1.6 ms */
#define TSSC_CTL_DEB_2_MS (0x4 << 7) /* 2 ms */
#define TSSC_CTL_DEB_3_MS (0x5 << 7) /* 3 ms */
#define TSSC_CTL_DEB_4_MS (0x6 << 7) /* 4 ms */
#define TSSC_CTL_DEB_6_MS (0x7 << 7) /* 6 ms */
#define TSSC_CTL_INTR_FLAG1 (0x1 << 10)
#define TSSC_CTL_DATA (0x1 << 11)
#define TSSC_CTL_SSBI_CTRL_EN (0x1 << 13)
/* control reg's default state */
#define TSSC_CTL_STATE ( \
TSSC_CTL_DEB_12_MS | \
TSSC_CTL_DEB_EN | \
TSSC_CTL_AVG_EN | \
TSSC_CTL_MASTER_MODE | \
TSSC_CTL_EN)
#define TSSC_NUMBER_OF_OPERATIONS 2
#define TS_PENUP_TIMEOUT_MS 20
#define TS_DRIVER_NAME "msm_touchscreen"
#define X_MAX 1024
#define Y_MAX 1024
#define P_MAX 256
struct ts {
struct input_dev *input;
struct timer_list timer;
int irq;
unsigned int x_max;
unsigned int y_max;
};
static void __iomem *virt;
#define TSSC_REG(reg) (virt + TSSC_##reg##_REG)
static void ts_update_pen_state(struct ts *ts, int x, int y, int pressure)
{
if (pressure) {
input_report_abs(ts->input, ABS_X, x);
input_report_abs(ts->input, ABS_Y, y);
input_report_abs(ts->input, ABS_PRESSURE, pressure);
input_report_key(ts->input, BTN_TOUCH, !!pressure);
} else {
input_report_abs(ts->input, ABS_PRESSURE, 0);
input_report_key(ts->input, BTN_TOUCH, 0);
}
input_sync(ts->input);
}
static void ts_timer(unsigned long arg)
{
struct ts *ts = (struct ts *)arg;
ts_update_pen_state(ts, 0, 0, 0);
}
static irqreturn_t ts_interrupt(int irq, void *dev_id)
{
u32 avgs, x, y, lx, ly;
u32 num_op, num_samp;
u32 status;
struct ts *ts = dev_id;
status = readl(TSSC_REG(STATUS));
avgs = readl(TSSC_REG(AVG12));
x = avgs & 0xFFFF;
y = avgs >> 16;
/* For pen down make sure that the data just read is still valid.
* The DATA bit will still be set if the ARM9 hasn't clobbered
* the TSSC. If it's not set, then it doesn't need to be cleared
* here, so just return.
*/
if (!(readl(TSSC_REG(CTL)) & TSSC_CTL_DATA))
goto out;
/* Data has been read, OK to clear the data flag */
writel(TSSC_CTL_STATE, TSSC_REG(CTL));
/* Valid samples are indicated by the sample number in the status
* register being the number of expected samples and the number of
* samples collected being zero (this check is due to ADC contention).
*/
num_op = (status & TSSC_STS_OPN_BMSK) >> TSSC_STS_OPN_SHIFT;
num_samp = (status & TSSC_STS_NUMSAMP_BMSK) >> TSSC_STS_NUMSAMP_SHFT;
if ((num_op == TSSC_NUMBER_OF_OPERATIONS) && (num_samp == 0)) {
/* TSSC can do Z axis measurment, but driver doesn't support
* this yet.
*/
/*
* REMOVE THIS:
* These x, y co-ordinates adjustments will be removed once
* Android framework adds calibration framework.
*/
#ifdef CONFIG_ANDROID_TOUCHSCREEN_MSM_HACKS
lx = ts->x_max - x;
ly = ts->y_max - y;
#else
lx = x;
ly = y;
#endif
ts_update_pen_state(ts, lx, ly, 255);
/* kick pen up timer - to make sure it expires again(!) */
mod_timer(&ts->timer,
jiffies + msecs_to_jiffies(TS_PENUP_TIMEOUT_MS));
} else
printk(KERN_INFO "Ignored interrupt: {%3d, %3d},"
" op = %3d samp = %3d\n",
x, y, num_op, num_samp);
out:
return IRQ_HANDLED;
}