Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4065d1e7 authored by Javier Martinez Canillas's avatar Javier Martinez Canillas Committed by Dmitry Torokhov
Browse files

Input: add Cypress TTSP capacitive multi-touch screen support



Cypress TrueTouch(tm) Standard Product controllers are found in
a wide range of embedded devices. This driver add support for a
variety of TTSP controllers.

Since the hardware is capable of tracking identifiable contacts, multi-touch
protocol type B (stateful) is used to report contact information.

The driver is composed of a core driver that process the data sent by
the contacts and a set of bus specific interface modules. This patch
adds the base core TTSP driver.

Signed-off-by: default avatarJavier Martinez Canillas <javier@dowhile0.org>
Reviewed-by: default avatarHenrik Rydberg <rydberg@euromail.se>
Signed-off-by: default avatarDmitry Torokhov <dtor@mail.ru>
parent 31175a83
Loading
Loading
Loading
Loading
+31 −1
Original line number Diff line number Diff line
@@ -139,7 +139,6 @@ config TOUCHSCREEN_CY8CTMG110
	tristate "cy8ctmg110 touchscreen"
	depends on I2C
	depends on GPIOLIB

	help
	  Say Y here if you have a cy8ctmg110 capacitive touchscreen on
	  an AAVA device.
@@ -149,6 +148,37 @@ config TOUCHSCREEN_CY8CTMG110
	  To compile this driver as a module, choose M here: the
	  module will be called cy8ctmg110_ts.

config TOUCHSCREEN_CYTTSP_CORE
	tristate "Cypress TTSP touchscreen"
	help
	  Say Y here if you have a touchscreen using controller from
	  the Cypress TrueTouch(tm) Standard Product family connected
	  to your system. You will also need to select appropriate
	  bus connection below.

	  If unsure, say N.

	  To compile this driver as a module, choose M here: the
	  module will be called cyttsp_core.

config TOUCHSCREEN_CYTTSP_I2C
	tristate "support I2C bus connection"
	depends on TOUCHSCREEN_CYTTSP_CORE && I2C
	help
	  Say Y here if the touchscreen is connected via I2C bus.

	  To compile this driver as a module, choose M here: the
	  module will be called cyttsp_i2c.

config TOUCHSCREEN_CYTTSP_SPI
	tristate "support SPI bus connection"
	depends on TOUCHSCREEN_CYTTSP_CORE && SPI_MASTER
	help
	  Say Y here if the touchscreen is connected via SPI bus.

	  To compile this driver as a module, choose M here: the
	  module will be called cyttsp_spi.

config TOUCHSCREEN_DA9034
	tristate "Touchscreen support for Dialog Semiconductor DA9034"
	depends on PMIC_DA903X
+4 −1
Original line number Diff line number Diff line
@@ -18,6 +18,9 @@ obj-$(CONFIG_TOUCHSCREEN_AUO_PIXCIR) += auo-pixcir-ts.o
obj-$(CONFIG_TOUCHSCREEN_BITSY)		+= h3600_ts_input.o
obj-$(CONFIG_TOUCHSCREEN_BU21013)	+= bu21013_ts.o
obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110)	+= cy8ctmg110_ts.o
obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE)	+= cyttsp_core.o
obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C)	+= cyttsp_i2c.o
obj-$(CONFIG_TOUCHSCREEN_CYTTSP_SPI)	+= cyttsp_spi.o
obj-$(CONFIG_TOUCHSCREEN_DA9034)	+= da9034-ts.o
obj-$(CONFIG_TOUCHSCREEN_DYNAPRO)	+= dynapro.o
obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE)	+= hampshire.o
+625 −0
Original line number Diff line number Diff line
/*
 * Core Source for:
 * Cypress TrueTouch(TM) Standard Product (TTSP) touchscreen drivers.
 * For use with Cypress Txx3xx parts.
 * Supported parts include:
 * CY8CTST341
 * CY8CTMA340
 *
 * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc.
 * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * version 2, and only 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.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * Contact Cypress Semiconductor at www.cypress.com <kev@cypress.com>
 *
 */

#include <linux/delay.h>
#include <linux/input.h>
#include <linux/input/mt.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/slab.h>

#include "cyttsp_core.h"

/* Bootloader number of command keys */
#define CY_NUM_BL_KEYS		8

/* helpers */
#define GET_NUM_TOUCHES(x)		((x) & 0x0F)
#define IS_LARGE_AREA(x)		(((x) & 0x10) >> 4)
#define IS_BAD_PKT(x)			((x) & 0x20)
#define IS_VALID_APP(x)			((x) & 0x01)
#define IS_OPERATIONAL_ERR(x)		((x) & 0x3F)
#define GET_HSTMODE(reg)		(((reg) & 0x70) >> 4)
#define GET_BOOTLOADERMODE(reg)		(((reg) & 0x10) >> 4)

#define CY_REG_BASE			0x00
#define CY_REG_ACT_DIST			0x1E
#define CY_REG_ACT_INTRVL		0x1D
#define CY_REG_TCH_TMOUT		(CY_REG_ACT_INTRVL + 1)
#define CY_REG_LP_INTRVL		(CY_REG_TCH_TMOUT + 1)
#define CY_MAXZ				255
#define CY_DELAY_DFLT			20 /* ms */
#define CY_DELAY_MAX			500
#define CY_ACT_DIST_DFLT		0xF8
#define CY_HNDSHK_BIT			0x80
/* device mode bits */
#define CY_OPERATE_MODE			0x00
#define CY_SYSINFO_MODE			0x10
/* power mode select bits */
#define CY_SOFT_RESET_MODE		0x01 /* return to Bootloader mode */
#define CY_DEEP_SLEEP_MODE		0x02
#define CY_LOW_POWER_MODE		0x04

/* Slots management */
#define CY_MAX_FINGER			4
#define CY_MAX_ID			16

static const u8 bl_command[] = {
	0x00,			/* file offset */
	0xFF,			/* command */
	0xA5,			/* exit bootloader command */
	0, 1, 2, 3, 4, 5, 6, 7	/* default keys */
};

static int ttsp_read_block_data(struct cyttsp *ts, u8 command,
				u8 length, void *buf)
{
	int error;
	int tries;

	for (tries = 0; tries < CY_NUM_RETRY; tries++) {
		error = ts->bus_ops->read(ts, command, length, buf);
		if (!error)
			return 0;

		msleep(CY_DELAY_DFLT);
	}

	return -EIO;
}

static int ttsp_write_block_data(struct cyttsp *ts, u8 command,
				 u8 length, void *buf)
{
	int error;
	int tries;

	for (tries = 0; tries < CY_NUM_RETRY; tries++) {
		error = ts->bus_ops->write(ts, command, length, buf);
		if (!error)
			return 0;

		msleep(CY_DELAY_DFLT);
	}

	return -EIO;
}

static int ttsp_send_command(struct cyttsp *ts, u8 cmd)
{
	return ttsp_write_block_data(ts, CY_REG_BASE, sizeof(cmd), &cmd);
}

static int cyttsp_load_bl_regs(struct cyttsp *ts)
{
	memset(&ts->bl_data, 0, sizeof(ts->bl_data));
	ts->bl_data.bl_status = 0x10;

	return ttsp_read_block_data(ts, CY_REG_BASE,
				    sizeof(ts->bl_data), &ts->bl_data);
}

static int cyttsp_exit_bl_mode(struct cyttsp *ts)
{
	int error;
	u8 bl_cmd[sizeof(bl_command)];

	memcpy(bl_cmd, bl_command, sizeof(bl_command));
	if (ts->pdata->bl_keys)
		memcpy(&bl_cmd[sizeof(bl_command) - CY_NUM_BL_KEYS],
			ts->pdata->bl_keys, sizeof(bl_command));

	error = ttsp_write_block_data(ts, CY_REG_BASE,
				      sizeof(bl_cmd), bl_cmd);
	if (error)
		return error;

	/* wait for TTSP Device to complete the operation */
	msleep(CY_DELAY_DFLT);

	error = cyttsp_load_bl_regs(ts);
	if (error)
		return error;

	if (GET_BOOTLOADERMODE(ts->bl_data.bl_status))
		return -EIO;

	return 0;
}

static int cyttsp_set_operational_mode(struct cyttsp *ts)
{
	int error;

	error = ttsp_send_command(ts, CY_OPERATE_MODE);
	if (error)
		return error;

	/* wait for TTSP Device to complete switch to Operational mode */
	error = ttsp_read_block_data(ts, CY_REG_BASE,
				     sizeof(ts->xy_data), &ts->xy_data);
	if (error)
		return error;

	return ts->xy_data.act_dist == CY_ACT_DIST_DFLT ? -EIO : 0;
}

static int cyttsp_set_sysinfo_mode(struct cyttsp *ts)
{
	int error;

	memset(&ts->sysinfo_data, 0, sizeof(ts->sysinfo_data));

	/* switch to sysinfo mode */
	error = ttsp_send_command(ts, CY_SYSINFO_MODE);
	if (error)
		return error;

	/* read sysinfo registers */
	msleep(CY_DELAY_DFLT);
	error = ttsp_read_block_data(ts, CY_REG_BASE, sizeof(ts->sysinfo_data),
				      &ts->sysinfo_data);
	if (error)
		return error;

	if (!ts->sysinfo_data.tts_verh && !ts->sysinfo_data.tts_verl)
		return -EIO;

	return 0;
}

static int cyttsp_set_sysinfo_regs(struct cyttsp *ts)
{
	int retval = 0;

	if (ts->pdata->act_intrvl != CY_ACT_INTRVL_DFLT ||
	    ts->pdata->tch_tmout != CY_TCH_TMOUT_DFLT ||
	    ts->pdata->lp_intrvl != CY_LP_INTRVL_DFLT) {

		u8 intrvl_ray[] = {
			ts->pdata->act_intrvl,
			ts->pdata->tch_tmout,
			ts->pdata->lp_intrvl
		};

		/* set intrvl registers */
		retval = ttsp_write_block_data(ts, CY_REG_ACT_INTRVL,
					sizeof(intrvl_ray), intrvl_ray);
		msleep(CY_DELAY_DFLT);
	}

	return retval;
}

static int cyttsp_soft_reset(struct cyttsp *ts)
{
	unsigned long timeout;
	int retval;

	/* wait for interrupt to set ready completion */
	INIT_COMPLETION(ts->bl_ready);
	ts->state = CY_BL_STATE;

	enable_irq(ts->irq);

	retval = ttsp_send_command(ts, CY_SOFT_RESET_MODE);
	if (retval)
		goto out;

	timeout = wait_for_completion_timeout(&ts->bl_ready,
			msecs_to_jiffies(CY_DELAY_DFLT * CY_DELAY_MAX));
	retval = timeout ? 0 : -EIO;

out:
	ts->state = CY_IDLE_STATE;
	disable_irq(ts->irq);
	return retval;
}

static int cyttsp_act_dist_setup(struct cyttsp *ts)
{
	u8 act_dist_setup = ts->pdata->act_dist;

	/* Init gesture; active distance setup */
	return ttsp_write_block_data(ts, CY_REG_ACT_DIST,
				sizeof(act_dist_setup), &act_dist_setup);
}

static void cyttsp_extract_track_ids(struct cyttsp_xydata *xy_data, int *ids)
{
	ids[0] = xy_data->touch12_id >> 4;
	ids[1] = xy_data->touch12_id & 0xF;
	ids[2] = xy_data->touch34_id >> 4;
	ids[3] = xy_data->touch34_id & 0xF;
}

static const struct cyttsp_tch *cyttsp_get_tch(struct cyttsp_xydata *xy_data,
					       int idx)
{
	switch (idx) {
	case 0:
		return &xy_data->tch1;
	case 1:
		return &xy_data->tch2;
	case 2:
		return &xy_data->tch3;
	case 3:
		return &xy_data->tch4;
	default:
		return NULL;
	}
}

static void cyttsp_report_tchdata(struct cyttsp *ts)
{
	struct cyttsp_xydata *xy_data = &ts->xy_data;
	struct input_dev *input = ts->input;
	int num_tch = GET_NUM_TOUCHES(xy_data->tt_stat);
	const struct cyttsp_tch *tch;
	int ids[CY_MAX_ID];
	int i;
	DECLARE_BITMAP(used, CY_MAX_ID);

	if (IS_LARGE_AREA(xy_data->tt_stat) == 1) {
		/* terminate all active tracks */
		num_tch = 0;
		dev_dbg(ts->dev, "%s: Large area detected\n", __func__);
	} else if (num_tch > CY_MAX_FINGER) {
		/* terminate all active tracks */
		num_tch = 0;
		dev_dbg(ts->dev, "%s: Num touch error detected\n", __func__);
	} else if (IS_BAD_PKT(xy_data->tt_mode)) {
		/* terminate all active tracks */
		num_tch = 0;
		dev_dbg(ts->dev, "%s: Invalid buffer detected\n", __func__);
	}

	cyttsp_extract_track_ids(xy_data, ids);

	bitmap_zero(used, CY_MAX_ID);

	for (i = 0; i < num_tch; i++) {
		tch = cyttsp_get_tch(xy_data, i);

		input_mt_slot(input, ids[i]);
		input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
		input_report_abs(input, ABS_MT_POSITION_X, be16_to_cpu(tch->x));
		input_report_abs(input, ABS_MT_POSITION_Y, be16_to_cpu(tch->y));
		input_report_abs(input, ABS_MT_TOUCH_MAJOR, tch->z);

		__set_bit(ids[i], used);
	}

	for (i = 0; i < CY_MAX_ID; i++) {
		if (test_bit(i, used))
			continue;

		input_mt_slot(input, i);
		input_mt_report_slot_state(input, MT_TOOL_FINGER, false);
	}

	input_sync(input);
}

static irqreturn_t cyttsp_irq(int irq, void *handle)
{
	struct cyttsp *ts = handle;
	int error;

	if (unlikely(ts->state == CY_BL_STATE)) {
		complete(&ts->bl_ready);
		goto out;
	}

	/* Get touch data from CYTTSP device */
	error = ttsp_read_block_data(ts, CY_REG_BASE,
				 sizeof(struct cyttsp_xydata), &ts->xy_data);
	if (error)
		goto out;

	/* provide flow control handshake */
	if (ts->pdata->use_hndshk) {
		error = ttsp_send_command(ts,
				ts->xy_data.hst_mode ^ CY_HNDSHK_BIT);
		if (error)
			goto out;
	}

	if (unlikely(ts->state == CY_IDLE_STATE))
		goto out;

	if (GET_BOOTLOADERMODE(ts->xy_data.tt_mode)) {
		/*
		 * TTSP device has reset back to bootloader mode.
		 * Restore to operational mode.
		 */
		error = cyttsp_exit_bl_mode(ts);
		if (error) {
			dev_err(ts->dev,
				"Could not return to operational mode, err: %d\n",
				error);
			ts->state = CY_IDLE_STATE;
		}
	} else {
		cyttsp_report_tchdata(ts);
	}

out:
	return IRQ_HANDLED;
}

static int cyttsp_power_on(struct cyttsp *ts)
{
	int error;

	error = cyttsp_soft_reset(ts);
	if (error)
		return error;

	error = cyttsp_load_bl_regs(ts);
	if (error)
		return error;

	if (GET_BOOTLOADERMODE(ts->bl_data.bl_status) &&
	    IS_VALID_APP(ts->bl_data.bl_status)) {
		error = cyttsp_exit_bl_mode(ts);
		if (error)
			return error;
	}

	if (GET_HSTMODE(ts->bl_data.bl_file) != CY_OPERATE_MODE ||
	    IS_OPERATIONAL_ERR(ts->bl_data.bl_status)) {
		return -ENODEV;
	}

	error = cyttsp_set_sysinfo_mode(ts);
	if (error)
		return error;

	error = cyttsp_set_sysinfo_regs(ts);
	if (error)
		return error;

	error = cyttsp_set_operational_mode(ts);
	if (error)
		return error;

	/* init active distance */
	error = cyttsp_act_dist_setup(ts);
	if (error)
		return error;

	ts->state = CY_ACTIVE_STATE;

	return 0;
}

static int cyttsp_enable(struct cyttsp *ts)
{
	int error;

	/*
	 * The device firmware can wake on an I2C or SPI memory slave
	 * address match. So just reading a register is sufficient to
	 * wake up the device. The first read attempt will fail but it
	 * will wake it up making the second read attempt successful.
	 */
	error = ttsp_read_block_data(ts, CY_REG_BASE,
				     sizeof(ts->xy_data), &ts->xy_data);
	if (error)
		return error;

	if (GET_HSTMODE(ts->xy_data.hst_mode))
		return -EIO;

	enable_irq(ts->irq);

	return 0;
}

static int cyttsp_disable(struct cyttsp *ts)
{
	int error;

	error = ttsp_send_command(ts, CY_LOW_POWER_MODE);
	if (error)
		return error;

	disable_irq(ts->irq);

	return 0;
}

#ifdef CONFIG_PM_SLEEP
static int cyttsp_suspend(struct device *dev)
{
	struct cyttsp *ts = dev_get_drvdata(dev);
	int retval = 0;

	mutex_lock(&ts->input->mutex);

	if (ts->input->users) {
		retval = cyttsp_disable(ts);
		if (retval == 0)
			ts->suspended = true;
	}

	mutex_unlock(&ts->input->mutex);

	return retval;
}

static int cyttsp_resume(struct device *dev)
{
	struct cyttsp *ts = dev_get_drvdata(dev);

	mutex_lock(&ts->input->mutex);

	if (ts->input->users)
		cyttsp_enable(ts);

	ts->suspended = false;

	mutex_unlock(&ts->input->mutex);

	return 0;
}

#endif

SIMPLE_DEV_PM_OPS(cyttsp_pm_ops, cyttsp_suspend, cyttsp_resume);
EXPORT_SYMBOL_GPL(cyttsp_pm_ops);

static int cyttsp_open(struct input_dev *dev)
{
	struct cyttsp *ts = input_get_drvdata(dev);
	int retval = 0;

	if (!ts->suspended)
		retval = cyttsp_enable(ts);

	return retval;
}

static void cyttsp_close(struct input_dev *dev)
{
	struct cyttsp *ts = input_get_drvdata(dev);

	if (!ts->suspended)
		cyttsp_disable(ts);
}

struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops,
			    struct device *dev, int irq, size_t xfer_buf_size)
{
	const struct cyttsp_platform_data *pdata = dev->platform_data;
	struct cyttsp *ts;
	struct input_dev *input_dev;
	int error;

	if (!dev || !bus_ops || !pdata || !pdata->name || irq <= 0) {
		error = -EINVAL;
		goto err_out;
	}

	ts = kzalloc(sizeof(*ts) + xfer_buf_size, GFP_KERNEL);
	input_dev = input_allocate_device();
	if (!ts || !input_dev) {
		error = -ENOMEM;
		goto err_free_mem;
	}

	ts->dev = dev;
	ts->input = input_dev;
	ts->pdata = dev->platform_data;
	ts->bus_ops = bus_ops;
	ts->irq = irq;

	init_completion(&ts->bl_ready);
	snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev));

	if (pdata->init) {
		error = pdata->init();
		if (error) {
			dev_err(ts->dev, "platform init failed, err: %d\n",
				error);
			goto err_free_mem;
		}
	}

	input_dev->name = pdata->name;
	input_dev->phys = ts->phys;
	input_dev->id.bustype = bus_ops->bustype;
	input_dev->dev.parent = ts->dev;

	input_dev->open = cyttsp_open;
	input_dev->close = cyttsp_close;

	input_set_drvdata(input_dev, ts);

	__set_bit(EV_ABS, input_dev->evbit);
	input_set_abs_params(input_dev, ABS_MT_POSITION_X,
			     0, pdata->maxx, 0, 0);
	input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
			     0, pdata->maxy, 0, 0);
	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
			     0, CY_MAXZ, 0, 0);

	input_mt_init_slots(input_dev, CY_MAX_ID);

	error = request_threaded_irq(ts->irq, NULL, cyttsp_irq,
				     IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
				     pdata->name, ts);
	if (error) {
		dev_err(ts->dev, "failed to request IRQ %d, err: %d\n",
			ts->irq, error);
		goto err_platform_exit;
	}

	disable_irq(ts->irq);

	error = cyttsp_power_on(ts);
	if (error)
		goto err_free_irq;

	error = input_register_device(input_dev);
	if (error) {
		dev_err(ts->dev, "failed to register input device: %d\n",
			error);
		goto err_free_irq;
	}

	return ts;

err_free_irq:
	free_irq(ts->irq, ts);
err_platform_exit:
	if (pdata->exit)
		pdata->exit();
err_free_mem:
	input_free_device(input_dev);
	kfree(ts);
err_out:
	return ERR_PTR(error);
}
EXPORT_SYMBOL_GPL(cyttsp_probe);

void cyttsp_remove(struct cyttsp *ts)
{
	free_irq(ts->irq, ts);
	input_unregister_device(ts->input);
	if (ts->pdata->exit)
		ts->pdata->exit();
	kfree(ts);
}
EXPORT_SYMBOL_GPL(cyttsp_remove);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard touchscreen driver core");
MODULE_AUTHOR("Cypress");
+149 −0
Original line number Diff line number Diff line
/*
 * Header file for:
 * Cypress TrueTouch(TM) Standard Product (TTSP) touchscreen drivers.
 * For use with Cypress Txx3xx parts.
 * Supported parts include:
 * CY8CTST341
 * CY8CTMA340
 *
 * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc.
 * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * version 2, and only 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.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * Contact Cypress Semiconductor at www.cypress.com <kev@cypress.com>
 *
 */


#ifndef __CYTTSP_CORE_H__
#define __CYTTSP_CORE_H__

#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/device.h>
#include <linux/input/cyttsp.h>

#define CY_NUM_RETRY		16 /* max number of retries for read ops */

struct cyttsp_tch {
	__be16 x, y;
	u8 z;
} __packed;

/* TrueTouch Standard Product Gen3 interface definition */
struct cyttsp_xydata {
	u8 hst_mode;
	u8 tt_mode;
	u8 tt_stat;
	struct cyttsp_tch tch1;
	u8 touch12_id;
	struct cyttsp_tch tch2;
	u8 gest_cnt;
	u8 gest_id;
	struct cyttsp_tch tch3;
	u8 touch34_id;
	struct cyttsp_tch tch4;
	u8 tt_undef[3];
	u8 act_dist;
	u8 tt_reserved;
} __packed;


/* TTSP System Information interface definition */
struct cyttsp_sysinfo_data {
	u8 hst_mode;
	u8 mfg_cmd;
	u8 mfg_stat;
	u8 cid[3];
	u8 tt_undef1;
	u8 uid[8];
	u8 bl_verh;
	u8 bl_verl;
	u8 tts_verh;
	u8 tts_verl;
	u8 app_idh;
	u8 app_idl;
	u8 app_verh;
	u8 app_verl;
	u8 tt_undef[5];
	u8 scn_typ;
	u8 act_intrvl;
	u8 tch_tmout;
	u8 lp_intrvl;
};

/* TTSP Bootloader Register Map interface definition */
#define CY_BL_CHKSUM_OK 0x01
struct cyttsp_bootloader_data {
	u8 bl_file;
	u8 bl_status;
	u8 bl_error;
	u8 blver_hi;
	u8 blver_lo;
	u8 bld_blver_hi;
	u8 bld_blver_lo;
	u8 ttspver_hi;
	u8 ttspver_lo;
	u8 appid_hi;
	u8 appid_lo;
	u8 appver_hi;
	u8 appver_lo;
	u8 cid_0;
	u8 cid_1;
	u8 cid_2;
};

struct cyttsp;

struct cyttsp_bus_ops {
	u16 bustype;
	int (*write)(struct cyttsp *ts,
		     u8 addr, u8 length, const void *values);
	int (*read)(struct cyttsp *ts, u8 addr, u8 length, void *values);
};

enum cyttsp_state {
	CY_IDLE_STATE,
	CY_ACTIVE_STATE,
	CY_BL_STATE,
};

struct cyttsp {
	struct device *dev;
	int irq;
	struct input_dev *input;
	char phys[32];
	const struct cyttsp_platform_data *pdata;
	const struct cyttsp_bus_ops *bus_ops;
	struct cyttsp_bootloader_data bl_data;
	struct cyttsp_sysinfo_data sysinfo_data;
	struct cyttsp_xydata xy_data;
	struct completion bl_ready;
	enum cyttsp_state state;
	bool suspended;

	u8 xfer_buf[] ____cacheline_aligned;
};

struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops,
			    struct device *dev, int irq, size_t xfer_buf_size);
void cyttsp_remove(struct cyttsp *ts);

extern const struct dev_pm_ops cyttsp_pm_ops;

#endif /* __CYTTSP_CORE_H__ */
+146 −0
Original line number Diff line number Diff line
/*
 * Source for:
 * Cypress TrueTouch(TM) Standard Product (TTSP) I2C touchscreen driver.
 * For use with Cypress Txx3xx parts.
 * Supported parts include:
 * CY8CTST341
 * CY8CTMA340
 *
 * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc.
 * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.org>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * version 2, and only 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.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * Contact Cypress Semiconductor at www.cypress.com <kev@cypress.com>
 *
 */

#include "cyttsp_core.h"

#include <linux/i2c.h>
#include <linux/input.h>

#define CY_I2C_DATA_SIZE	128

static int cyttsp_i2c_read_block_data(struct cyttsp *ts,
				      u8 addr, u8 length, void *values)
{
	struct i2c_client *client = to_i2c_client(ts->dev);
	struct i2c_msg msgs[] = {
		{
			.addr = client->addr,
			.flags = 0,
			.len = 1,
			.buf = &addr,
		},
		{
			.addr = client->addr,
			.flags = I2C_M_RD,
			.len = length,
			.buf = values,
		},
	};
	int retval;

	retval = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
	if (retval < 0)
		return retval;

	return retval != ARRAY_SIZE(msgs) ? -EIO : 0;
}

static int cyttsp_i2c_write_block_data(struct cyttsp *ts,
				       u8 addr, u8 length, const void *values)
{
	struct i2c_client *client = to_i2c_client(ts->dev);
	int retval;

	ts->xfer_buf[0] = addr;
	memcpy(&ts->xfer_buf[1], values, length);

	retval = i2c_master_send(client, ts->xfer_buf, length + 1);

	return retval < 0 ? retval : 0;
}

static const struct cyttsp_bus_ops cyttsp_i2c_bus_ops = {
	.bustype	= BUS_I2C,
	.write		= cyttsp_i2c_write_block_data,
	.read           = cyttsp_i2c_read_block_data,
};

static int __devinit cyttsp_i2c_probe(struct i2c_client *client,
				      const struct i2c_device_id *id)
{
	struct cyttsp *ts;

	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
		dev_err(&client->dev, "I2C functionality not Supported\n");
		return -EIO;
	}

	ts = cyttsp_probe(&cyttsp_i2c_bus_ops, &client->dev, client->irq,
			  CY_I2C_DATA_SIZE);

	if (IS_ERR(ts))
		return PTR_ERR(ts);

	i2c_set_clientdata(client, ts);

	return 0;
}

static int __devexit cyttsp_i2c_remove(struct i2c_client *client)
{
	struct cyttsp *ts = i2c_get_clientdata(client);

	cyttsp_remove(ts);

	return 0;
}

static const struct i2c_device_id cyttsp_i2c_id[] = {
	{ CY_I2C_NAME, 0 },
	{ }
};
MODULE_DEVICE_TABLE(i2c, cyttsp_i2c_id);

static struct i2c_driver cyttsp_i2c_driver = {
	.driver = {
		.name	= CY_I2C_NAME,
		.owner	= THIS_MODULE,
		.pm	= &cyttsp_pm_ops,
	},
	.probe		= cyttsp_i2c_probe,
	.remove		= __devexit_p(cyttsp_i2c_remove),
	.id_table	= cyttsp_i2c_id,
};

static int __init cyttsp_i2c_init(void)
{
	return i2c_add_driver(&cyttsp_i2c_driver);
}
module_init(cyttsp_i2c_init);

static void __exit cyttsp_i2c_exit(void)
{
	return i2c_del_driver(&cyttsp_i2c_driver);
}
module_exit(cyttsp_i2c_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) I2C driver");
MODULE_AUTHOR("Cypress");
MODULE_ALIAS("i2c:cyttsp");
Loading