diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index a23a94b..8312b7c 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -868,6 +868,16 @@ config TOUCHSCREEN_PCAP
 	  To compile this driver as a module, choose M here: the
 	  module will be called pcap_ts.
 
+config TOUCHSCREEN_SIRFSOC
+	tristate "SiRFSoC touchscreen"
+	depends on ARCH_SIRF
+	select IIO
+	help
+	  Say Y here if you have a SiRFSoC internal ADC based touchscreen.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called sirfsoc_ts.
+
 config TOUCHSCREEN_ST1232
 	tristate "Sitronix ST1232 touchscreen controllers"
 	depends on I2C
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 126479d..ef438f7 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -52,6 +52,7 @@ obj-$(CONFIG_TOUCHSCREEN_PCAP)		+= pcap_ts.o
 obj-$(CONFIG_TOUCHSCREEN_PENMOUNT)	+= penmount.o
 obj-$(CONFIG_TOUCHSCREEN_PIXCIR)	+= pixcir_i2c_ts.o
 obj-$(CONFIG_TOUCHSCREEN_S3C2410)	+= s3c2410_ts.o
+obj-$(CONFIG_TOUCHSCREEN_SIRFSOC)       += sirfsoc_ts.o
 obj-$(CONFIG_TOUCHSCREEN_ST1232)	+= st1232.o
 obj-$(CONFIG_TOUCHSCREEN_STMPE)		+= stmpe-ts.o
 obj-$(CONFIG_TOUCHSCREEN_SUN4I)		+= sun4i-ts.o
diff --git a/drivers/input/touchscreen/sirfsoc_ts.c b/drivers/input/touchscreen/sirfsoc_ts.c
new file mode 100644
index 0000000..77cde19
--- /dev/null
+++ b/drivers/input/touchscreen/sirfsoc_ts.c
@@ -0,0 +1,658 @@
+/*
+ * sirfsoc touch controller Driver
+ *
+ * Copyright (c) 2014 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/reset.h>
+#include <linux/iio/consumer.h>
+
+#define DRIVER_NAME		"sirfsoc_tsc"
+
+#define DATA_SHIFT_BITS		14
+#define DATA_XMASK		(0x3FFF << 0)
+#define DATA_YMASK		(0x3FFF << DATA_SHIFT_BITS)
+#define GETX(val)		((val) & DATA_XMASK)
+#define GETY(val)		(((val) & DATA_YMASK) >> DATA_SHIFT_BITS)
+
+/*
+ * If new coordinates are close enough to last ones, last values
+ * should be used to suppress glitches.
+ */
+#define TS_GLITCH_GAP		60
+
+/* Dual touch: Configurable parameters */
+#define TS_PREC_BITS		10	/* Precision, must >= 2 */
+#define TS_DUAL_MIN		15	/* Min UAD/UBC of dual touch points */
+/* Dual touch: Fixed parameter */
+#define TS_V_MAX		(1 << DATA_SHIFT_BITS)	/* Full scale AD */
+/* Dual touch: Screen dependent parameters */
+#define TS_RX			694	/* X-plane resister */
+#define TS_RY			228	/* Y-plane resister */
+#define TS_V			10800	/* Max AD (LeftUpper corner) */
+#define TS_COEF_MIN		((u32)(0.100f * (1 << TS_PREC_BITS)))
+#define TS_COEF_MAX		((u32)(100.0f * (1 << TS_PREC_BITS)))
+#define TS_COEF_DEFAULT		((u32)(2.000f * (1 << TS_PREC_BITS)))
+#define TS_RTOUCH_NORMAL	700	/* Normal touch resister */
+#define TS_RTOUCH_MIN		(70 << TS_PREC_BITS)	/* 70 Ohm */
+#define TS_RTOUCH_MAX		(1700 << TS_PREC_BITS)	/* 1700 Ohm */
+#define TS_RTOUCH_DUAL_UP	350	/* Upper bound of dual touch */
+#define TS_RTOUCH_SINGLE_LOW	450	/* Lower bound of single touch */
+#define TS_SINGLE_MAXGAP_X	100	/* Max UAD of single touch point */
+#define TS_SINGLE_MAXGAP_Y	50	/* Max UBC of single touch point */
+
+/* Select AD samples to read (SEL bits in ADC_CONTROL1 register) */
+#define SIRFSOC_TS_SEL_X	0x01	/* x sample */
+#define SIRFSOC_TS_SEL_Y	0x02	/* y sample */
+#define SIRFSOC_TS_SEL_DUAL	0x0F	/* eight samples for dual touch */
+#define SIRFSOC_TS_CTL1(sel)	(ADC_POLL | ADC_SEL(sel) | ADC_DEL_SET(6) \
+		| ADC_FREQ_6K | ADC_TP_TIME(0) | ADC_SGAIN(0) \
+		| ADC_EXTCM(0) | ADC_RBAT_DISABLE | ADC_MORE_CTL1)
+
+/* AD sample indexes (single touch) */
+enum {
+	X,
+	Y,
+	AD_SAMPLE_COUNT_SINGLE
+};
+
+/* AD sample indexes (dual touch) */
+enum {
+	XPXN_YP,
+	YPYN_XP,
+	XPXN_YN,
+	YPYN_XN,
+	XPYN_YP,
+	XPYN_XN,
+	YPXN_XP,
+	YPXN_YN,
+	AD_SAMPLE_COUNT_DUAL
+};
+
+#define AD_REG_COUNT		(AD_SAMPLE_COUNT_DUAL / 2)
+
+struct sirfsoc_ts {
+	char			phys[32];
+
+	/* AD samples buffer of current detection */
+	u32			samples[AD_SAMPLE_COUNT_DUAL];
+
+	/* Calculated coordinates of current detection */
+	int			sampled_x[2], sampled_y[2];
+	/* Last successfully calculated and issued coordinates */
+	int			issued_x[2], issued_y[2];
+	bool			press_down;
+
+	/* Fingers detected pressing on the screen
+	 * always 1 for single touch driver
+	 * maybe 1 or 2 for dual touch driver, depends on calculation
+	 */
+	int			fingers;
+
+	/* Last touching event is a dual touch */
+	bool			last_is_dual;
+
+	struct input_dev	*input;
+
+	/* Points to touch specific data */
+	const struct sirfsoc_ts_of_data_touch	*touch;
+
+	/* ADC data channel for ts*/
+	struct iio_channel	*chan;
+};
+
+/* Single/dual touch specific data and routines */
+struct sirfsoc_ts_of_data_touch {
+	/* Number of continuous stable samples for debouncing */
+	int	debounce_rep;
+	/* Max deviation of AD values for stable samples */
+	int	debounce_dev;
+	/* Callback routine to read and calculate coordinates */
+	int	(*get_coord_and_pen)(struct sirfsoc_ts *);
+	/* Callback routine to read AD samples */
+	int	(*read_samples)(struct sirfsoc_ts *, u32 *);
+};
+
+/*
+ * Debounce routine shared by single and dual touch
+ * sample_count: how many AD samples needs to take care
+ */
+static int sirfsoc_ts_debounce(struct sirfsoc_ts *ts, int sample_count)
+{
+	int i, j;
+	u32 samples[AD_SAMPLE_COUNT_DUAL];	/* Max of single/dual samples */
+
+	/* First sample */
+	if ((*(ts->touch->read_samples))(ts, ts->samples))
+		return -EBUSY;
+
+	/* Debouncing
+	 * - Condition for a stable reading: Three (debounce_rep) continuous
+	 *   reading with AD samples deviation in bound (debounce_dev)
+	 * - ts->samples[] stores sum of previous readings
+	 * - samples[] stored current reading
+	 */
+	for (i = 1; i < ts->touch->debounce_rep; i++) {
+		/* Get raw AD samples */
+		if ((*(ts->touch->read_samples))(ts, samples))
+			return -EBUSY;
+		/* Verify adjacent samples deviation */
+		for (j = 0; j < sample_count; j++) {
+			if (abs(ts->samples[j] / i - samples[j]) >
+					ts->touch->debounce_dev)
+				return -EINVAL;
+			ts->samples[j] += samples[j];
+		}
+	}
+
+	/* Average samples, store to ts->samples */
+	for (i = 0; i < ts->touch->debounce_rep; i++)
+		ts->samples[i] /= ts->touch->debounce_rep;
+
+	return 0;
+}
+
+static int sirfsoc_ts_read_samples_single(struct sirfsoc_ts *ts, u32 *samples)
+{
+	int raw_sample;
+	int ret;
+
+	ret = iio_read_channel_raw(ts->chan, &raw_sample);
+	if (ret < 0) {
+		ts->press_down = false;
+		return ret;
+	}
+
+	/* x sample */
+	samples[X] = GETX(raw_sample);
+	/* y sample */
+	samples[Y] = GETY(raw_sample);
+
+	ts->press_down = true;
+	return 0;
+}
+
+/*
+ * Calculate single touch coordinate
+ * coord: (ts->sampled_x[0], y[0])
+ */
+static int sirfsoc_ts_get_coord_and_pen_single(struct sirfsoc_ts *ts)
+{
+	int ret;
+
+	ret = sirfsoc_ts_debounce(ts, AD_SAMPLE_COUNT_SINGLE);
+	if (ret < 0)
+		return ret;
+
+	ts->sampled_x[0] = ts->samples[X];
+	ts->sampled_y[0] = ts->samples[Y];
+
+	return 0;
+}
+
+/* Read eight AD samples from adc */
+static int sirfsoc_ts_read_samples_dual(struct sirfsoc_ts *ts, u32 *samples)
+{
+	int i, ret;
+	int raw_samples[AD_REG_COUNT];
+
+	ret = iio_read_channel_raw(ts->chan, raw_samples);
+	if (ret < 0) {
+		ts->press_down = false;
+		return ret;
+	}
+
+	for (i = 0; i < AD_REG_COUNT; i++) {
+		*samples++ = GETX(raw_samples[i]);
+		*samples++ = GETY(raw_samples[i]);
+	}
+
+	ts->press_down = true;
+	return 0;
+}
+
+/*
+ * Calculate dual touch coordinates
+ *
+ * - Schematic
+ *
+ *             RX1     A      RX2     D      RX3
+ *    XP ----/\/\/\----o----/\/\/\----o----/\/\/\---- XN
+ *                     |              |
+ *                     /              /
+ *              Rtouch \       Rtouch \
+ *                     /              /
+ *                     \              \
+ *                     |              |
+ *    YP ----/\/\/\----o----/\/\/\----o----/\/\/\---- YN
+ *             RY1     B      RY2     C      RY3
+ *
+ * coord1: (ts->sampled_x[0], y[0]); coord2: (ts->sampled_x[1], y[1])
+ */
+static int sirfsoc_ts_calculate_dual(struct sirfsoc_ts *ts)
+{
+	u32 tmp;
+	int ubc, uad;		/* |UB-UC|, |UA-UD| */
+	u32 x, y, rx2, ry2;	/* Intermediate results */
+	u64 a64, b64, c64;	/* Equation coefficients */
+	u32 rtouch;		/* Touch resister between X & Y planes */
+	bool neg_slope;		/* Slope of the line connecting dual points,
+				   negative if LeftUpper and RightDown */
+	u32 *samples = ts->samples;
+
+	/*
+	 * Step 1: Calculate Rtouch
+	 *
+	 * Rtouch depends on pressure and single/dual touch,
+	 * Dual touch halves the value approximately.
+	 * Two equivalent approaches:
+	 * 1. TS_RY * ypyn_xp * (xpyn_xn/xpyn_yp - 1) / TS_V
+	 * 2. TS_RX * xpxn_yp * (ypxn_yn/ypxn_xp - 1) / TS_V
+	 *
+	 * CAUTION:
+	 * rtouch is multiplied by 1024 to keep enough precision bits
+	 */
+	a64 = TS_RY * samples[YPYN_XP];
+	a64 <<= TS_PREC_BITS;	/* *1024 */
+	do_div(a64, TS_V);
+	b64 = a64 * samples[XPYN_XN];
+	do_div(b64, samples[XPYN_YP]);
+	rtouch = (u32)(b64 - a64);
+	if (rtouch < TS_RTOUCH_MIN || rtouch > TS_RTOUCH_MAX)
+		return -EINVAL;	/* Unstable reading */
+
+	/*
+	 * Step2: Check single touch
+	 *
+	 * As single touch is the dominant case, it should be handled first.
+	 * Chance is prominent that we may skip all dual touch related messes.
+	 */
+	ubc = samples[XPXN_YP] - samples[XPXN_YN];
+	uad = samples[YPYN_XP] - samples[YPYN_XN];
+	neg_slope = (ubc < 0);
+	ubc = abs(ubc);
+
+	/* UA-UD should follow the same sign as UB-UC */
+	if ((uad < 0) != neg_slope)
+		goto single_touch;
+	uad = abs(uad);
+	/* Very closed samples mean single touch or vertical/horizontal */
+	if (ubc <= TS_DUAL_MIN || uad <= TS_DUAL_MIN)
+		goto single_touch;
+	/* Fast scratching on the screen may cause misdetection */
+	if (rtouch > (TS_RTOUCH_SINGLE_LOW<<TS_PREC_BITS))
+		goto single_touch;
+
+	/*
+	 * Step 3: Calculate intermediate value x (slope)
+	 *
+	 * CAUTION:
+	 * x is multiplied by 1024 to keep enough precision bits
+	 */
+	x = TS_COEF_DEFAULT;	/* Fixed slope at about +/-45 degree */
+
+	/*
+	 * Step4: Calculate RX2 & RY2
+	 *
+	 * Solve equation a*y^2 - b*y - c = 0, where:
+	 * a = |UA-UD| + x * TS_V
+	 * b = (1+x) * |UA-UD| * TS_RY
+	 * c = 2 * |UA-UD| * TS_RY * RTouch
+	 *
+	 * CAUTION:
+	 * RX2,RY2 is multiplied by 256 to keep enough precision bits
+	 */
+	a64 = x;
+	a64 *= TS_V;
+	a64 >>= TS_PREC_BITS;
+	a64 += uad;
+	b64 = uad;
+	b64 *= TS_RY;
+	b64 *= ((1<<(TS_PREC_BITS-1)) + (x>>1));	/* *512 */
+	c64 = 2 * uad;
+	c64 *= TS_RY;
+	c64 *= TS_RTOUCH_NORMAL;	/* Fixed RTouch */
+
+	/* a: normal; b: *512; c: normal */
+	do_div(b64, a64);	/* (b/2a)*1024 */
+	do_div(c64, a64);	/* (c/a) */
+
+	c64 <<= (2*TS_PREC_BITS);
+	a64 = int64_sqrt(b64*b64 + c64);
+	a64 += b64;
+	a64 >>= 2;		/* *256 */
+	y = (u32)a64;
+
+	ry2 = y;
+	rx2 = (x * y) >> TS_PREC_BITS;
+	/* rx2, ry2: Left shifted 8 bits */
+
+	/* Step5: Calculate coordinates
+	 *
+	 * center: x0 = (UB+UC)/2, y0 = (UA+UD)/2
+	 * delta : dx = 0.5 * TS_V * RX2 / TS_RX
+	 *         dy = 0.5 * TS_V * RY2 / TS_RY
+	 * -------------------------
+	 * |points  |x     | y     |
+	 * -------------------------
+	 * |(x1,y1) |x0-dx | y0-dy |
+	 * |(x2,y2) |x0+dx | y0+dy |
+	 * -------------------------
+	 */
+
+	/* x1, x2 */
+	x = (samples[XPXN_YP] + samples[XPXN_YN]) << (TS_PREC_BITS-2);
+	tmp = (TS_V * rx2) / TS_RX;
+	/* x = x0 * 512, tmp = dx * 512 */
+	if (unlikely(x <= tmp))
+		ts->sampled_x[0] = 1;
+	else
+		ts->sampled_x[0] = (x - tmp) >> (TS_PREC_BITS-1);
+	ts->sampled_x[1] = (x + tmp) >> (TS_PREC_BITS-1);
+
+	/* y1, y2 */
+	y = (samples[YPYN_XP] + samples[YPYN_XN]) << (TS_PREC_BITS-2);
+	tmp = (TS_V * ry2) / TS_RY;
+	/* y = y0 * 512, tmp = dy * 512 */
+	if (unlikely(y <= tmp))
+		ts->sampled_y[0] = 1;
+	else
+		ts->sampled_y[0] = (y - tmp) >> (TS_PREC_BITS-1);
+	ts->sampled_y[1] = (y + tmp) >> (TS_PREC_BITS-1);
+
+	/* Verify data */
+	if (unlikely(ts->sampled_x[0] > ts->sampled_x[1] ||
+		     ts->sampled_y[0] > ts->sampled_y[1] ||
+		     ts->sampled_x[1] >= TS_V_MAX ||
+		     ts->sampled_y[1] >= TS_V_MAX))
+		return -EINVAL;	/* Illegal data */
+	if (ts->sampled_x[1] >= TS_V)
+		ts->sampled_x[1] = TS_V - 1;
+	if (ts->sampled_y[1] >= TS_V)
+		ts->sampled_y[1] = TS_V - 1;
+
+	/* Swap x coordinates if negative slope */
+	if (neg_slope) {
+		tmp = ts->sampled_x[0];
+		ts->sampled_x[0] = ts->sampled_x[1];
+		ts->sampled_x[1] = tmp;
+	}
+
+	/* Dual touch detected */
+	ts->fingers = 2;
+	return 0;
+
+	/* Fall back to single touch */
+single_touch:
+	/*
+	 * Make sure it's not a misdetected dual touch:
+	 * - Touch resister must be above upper bound of dual touch
+	 * - |UB-UC| and |UA-UD| are below upper bound of single touch
+	 */
+	if (rtouch >= (TS_RTOUCH_DUAL_UP<<TS_PREC_BITS)
+			&& ubc <= TS_SINGLE_MAXGAP_Y
+			&& uad <= TS_SINGLE_MAXGAP_X) {
+		/* Get single touch coordinate */
+		ts->sampled_x[0] = (samples[XPXN_YP] + samples[XPXN_YN]) / 2;
+		ts->sampled_y[0] = (samples[YPYN_XP] + samples[YPYN_XN]) / 2;
+		/* Single touch detected */
+		ts->fingers = 1;
+		return 0;
+	} else {
+		return -EINVAL;	/* Drop uncertainty */
+	}
+}
+
+static int sirfsoc_ts_get_coord_and_pen_dual(struct sirfsoc_ts *ts)
+{
+	int ret;
+	bool is_dual;
+
+	/* Read samples and do debouncing */
+	ret = sirfsoc_ts_debounce(ts, AD_SAMPLE_COUNT_DUAL);
+	if (ret < 0)
+		return ret;
+
+	/* Calculate coordinates */
+	ret = sirfsoc_ts_calculate_dual(ts);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * First switching from single to dual or dual to single are
+	 * sometimes unstable, drop it.
+	 */
+	is_dual = (ts->fingers == 2);	/* Current is dual touch? */
+	if (is_dual != ts->last_is_dual) {
+		ts->last_is_dual = is_dual;
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* Report touch events to event driver */
+static void sirfsoc_ts_report_coord(struct sirfsoc_ts *ts)
+{
+	int i;
+
+	input_report_abs(ts->input, ABS_PRESSURE, 1);
+	input_report_key(ts->input, BTN_TOUCH, 1);
+
+	for (i = 0; i < ts->fingers; i++) {
+		/* Filter small glitches */
+		if (abs(ts->sampled_x[i] - ts->issued_x[i]) < TS_GLITCH_GAP &&
+		    abs(ts->sampled_y[i] - ts->issued_y[i]) < TS_GLITCH_GAP) {
+			/*
+			 * New coord is very close to last checking,
+			 * adopt old coord instead
+			 */
+			ts->sampled_x[i] = ts->issued_x[i];
+			ts->sampled_y[i] = ts->issued_y[i];
+		} else {
+			/* Save new coord for next time comparison */
+			ts->issued_x[i] = ts->sampled_x[i];
+			ts->issued_y[i] = ts->sampled_y[i];
+		}
+
+		input_report_abs(ts->input, ABS_MT_POSITION_X,
+				ts->sampled_x[i]);
+		input_report_abs(ts->input, ABS_MT_POSITION_Y,
+				ts->sampled_y[i]);
+		input_mt_sync(ts->input);
+	}
+
+	input_sync(ts->input);
+}
+
+static irqreturn_t sirfsoc_ts_thread_irq(int irq, void *handle)
+{
+	struct sirfsoc_ts *ts = (struct sirfsoc_ts *)handle;
+	struct input_dev *input = ts->input;
+	int ret;
+
+	/*
+	 * we have pen down interrupt, but before pen up, no more will come
+	 */
+	do {
+		ret = (*(ts->touch->get_coord_and_pen))(ts);
+		if (!ret)
+			sirfsoc_ts_report_coord(ts);
+
+		usleep_range(5000, 8000);
+	} while (ts->press_down);
+
+	input_report_key(input, BTN_TOUCH, 0);
+	input_report_abs(input, ABS_PRESSURE, 0);
+	input_sync(input);
+
+	return IRQ_HANDLED;
+}
+
+static const struct sirfsoc_ts_of_data_touch sirfsoc_ts_of_data_single = {
+	.debounce_rep		= 3,
+	.debounce_dev		= 200,
+	.get_coord_and_pen	= sirfsoc_ts_get_coord_and_pen_single,
+	.read_samples		= sirfsoc_ts_read_samples_single,
+};
+
+static const struct sirfsoc_ts_of_data_touch sirfsoc_ts_of_data_dual = {
+	.debounce_rep		= 4,
+	.debounce_dev		= 500,
+	.get_coord_and_pen	= sirfsoc_ts_get_coord_and_pen_dual,
+	.read_samples		= sirfsoc_ts_read_samples_dual,
+};
+
+static const struct of_device_id sirfsoc_ts_of_match[] = {
+	{ .compatible = "sirf,prima2-tsc",
+	  .data = &sirfsoc_ts_of_data_single },
+	{ .compatible = "sirf,dualtouch-tsc",
+	  .data = &sirfsoc_ts_of_data_dual },
+	{}
+};
+
+static int sirfsoc_ts_probe(struct platform_device *pdev)
+{
+	struct input_dev		*input_dev;
+	struct sirfsoc_ts		*ts;
+	int				ret;
+	int				i;
+	int				irq;
+	const struct of_device_id	*match;
+
+	const unsigned int codes[] = {
+		KEY_HOME, KEY_MENU, KEY_BACK, KEY_SEARCH,
+	};
+
+	ts = devm_kzalloc(&pdev->dev, sizeof(struct sirfsoc_ts), GFP_KERNEL);
+	if (!ts) {
+		dev_err(&pdev->dev,
+			"sirfsoc ts: Cant allocate driver private data\n");
+		return -ENOMEM;
+	}
+
+	platform_set_drvdata(pdev, ts);
+
+	ts->chan = iio_channel_get(&pdev->dev, NULL);
+	if (IS_ERR(ts->chan)) {
+		dev_err(&pdev->dev, "sirfsoc ts: Unable to get the adc channel\n");
+		ret = PTR_ERR(ts->chan);
+		goto out0;
+	}
+
+	input_dev = input_allocate_device();
+	if (!input_dev) {
+		dev_err(&pdev->dev,
+			"sirfsoc ts: Unable to allocate input device\n");
+		ret = -ENOMEM;
+		goto out1;
+	}
+
+	snprintf(ts->phys, sizeof("SIRFSOC-TS"), "SIRFSOC-TS");
+	input_dev->name = "sirfsoc_touchscreen";
+	input_dev->phys = ts->phys;
+	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS)
+				| BIT_MASK(EV_SYN);
+	input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+	__set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
+
+	input_set_abs_params(input_dev, ABS_PRESSURE, 0, 1, 0, 0);
+	input_set_abs_params(input_dev, ABS_TOOL_WIDTH, 0, 15, 0, 0);
+	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 1, 0, 0);
+	input_set_abs_params(input_dev, ABS_MT_WIDTH_MAJOR, 0, 15, 0, 0);
+
+	for (i = 0; i < ARRAY_SIZE(codes); i++)
+		input_set_capability(input_dev, EV_KEY, codes[i]);
+
+	ret = input_register_device(input_dev);
+	if (ret) {
+		dev_err(&pdev->dev,
+			"sirfsoc ts: Unable to register input device\n");
+		goto out2;
+	}
+	ts->input = input_dev;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "sirfsoc tsc: get irq failed!\n");
+		ret = -ENOMEM;
+		goto out3;
+	}
+
+	ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+		sirfsoc_ts_thread_irq, IRQF_ONESHOT, DRIVER_NAME, ts);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "sirfsoc ts: regist irq handler failed!\n");
+		ret = -ENODEV;
+		goto out3;
+	}
+
+	input_set_abs_params(input_dev, ABS_X, 0, 0x3FFF, 0, 0);
+	input_set_abs_params(input_dev, ABS_Y, 0, 0x3FFF, 0, 0);
+	input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, 0x3FFF, 0, 0);
+	input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, 0x3FFF, 0, 0);
+
+	/* Default to single touch */
+	ts->fingers = 1;
+
+	/* Touch specific data */
+	match = of_match_device(of_match_ptr(sirfsoc_ts_of_match), &pdev->dev);
+	ts->touch = match->data;
+
+	return 0;
+out3:
+	input_unregister_device(input_dev);
+out2:
+	input_free_device(input_dev);
+out1:
+	iio_channel_release(ts->chan);
+out0:
+	return ret;
+}
+
+static int sirfsoc_ts_remove(struct platform_device *pdev)
+{
+	struct sirfsoc_ts *ts = platform_get_drvdata(pdev);
+
+	input_unregister_device(ts->input);
+	iio_channel_release(ts->chan);
+
+	return 0;
+}
+
+static void sirfsoc_ts_shutdown(struct platform_device *dev)
+{
+	sirfsoc_ts_remove(dev);
+}
+
+static struct platform_driver tsc_sirfsoc_driver = {
+	.driver	 = {
+		.name   = DRIVER_NAME,
+		.of_match_table = sirfsoc_ts_of_match,
+	},
+	.probe	  = sirfsoc_ts_probe,
+	.remove	 = sirfsoc_ts_remove,
+	.shutdown       = sirfsoc_ts_shutdown,
+};
+
+module_platform_driver(tsc_sirfsoc_driver);
+
+MODULE_AUTHOR("Guoying Zhang <Guoying.Zhang@csr.com>");
+MODULE_AUTHOR("Yibo Cai <Yibo.Cai@csr.com>");
+MODULE_AUTHOR("Sober Song <Zhiwu.Song@csr.com>");
+MODULE_DESCRIPTION("SiRF SoC On-chip Touch screen driver");
+MODULE_LICENSE("GPL v2");
