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

Commit d25de43e authored by xigualu's avatar xigualu Committed by Xin Hua Lu
Browse files

input: touchscreen: cyttsp5: add new touch driver



This is the reference driver source code for cyttsp5 touch driver.

Change-Id: I9b57cac83cfa1ea6cc7f3a655cd65cba499c137c
Signed-off-by: default avatarxigualu <luxh0702@thundersoft.com>
Git-commit: 797867bcbaf7a9c3b7ef9d8652e65e148d90bb07
Git-repo: https://source.codeaurora.org/external/thundersoft/ihvjointlab/sensor-driver/commit/?h=Parade_cyttsp5


Signed-off-by: default avatarFei Mao <feim1@codeaurora.org>
parent 8df0c001
Loading
Loading
Loading
Loading
+88 −0
Original line number Diff line number Diff line
* Cypress cyttsp5 touchscreen controller

Required properties:
 - compatible		: must be "cy,cyttsp5_i2c_adapter"
 - reg			: Device I2C address or SPI chip select number
 - interrupt-parent	: the phandle for the gpio controller
			  (see interrupt binding[0]).
 - interrupts		: (gpio) interrupt to which the chip is connected
			  (see interrupt binding[0]).
 - vcc_i2c-supply	: power supply
 - vdd-supply		: power supply
 - cy,mt		: multi-touch

Optional properties:
 - cy,btn		: button

[0]: Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
[1]: Documentation/devicetree/bindings/gpio/gpio.txt

Example:
	&i2c1 {
		/* ... */
		tsc@24 {
			compatible = "cy,cyttsp5_i2c_adapter";
			reg = <0x24>;
			interrupt-parent = <&msm_gpio>;
			interrupts = <13 0x2008>;
			cy,adapter_id = "cyttsp5_i2c_adapter";
			vcc_i2c-supply = <&pm8916_l6>;
			vdd-supply = <&pm8916_l17>;

			cy,core {
				cy,name = "cyttsp5_core";
				cy,irq_gpio = <13>;
				cy,rst_gpio = <12>;
				cy,hid_desc_register = <1>;
				cy,flags = <4>;
				cy,easy_wakeup_gesture = <1>;
				cy,btn_keys = <172 139 158 217 114 115 212 116>;
				cy,btn_keys-tag = <0>;

				cy,mt {
					cy,name = "cyttsp5_mt";
					cy,inp_dev_name = "cyttsp5_mt";
					cy,flags = <0x28>;
					cy,abs =
						<0x35 0 320 0 0
						0x36 0 360 0 0
						0x3a 0 255 0 0
						0xffff 0 255 0 0
						0x39 0 15 0 0
						0x30 0 255 0 0
						0x31 0 255 0 0
						0x34 0xffffff81 127 0 0
						0x37 0 1 0 0
						0x3b 0 255 0 0>;

					cy,vkeys_x = <320>;
					cy,vkeys_y = <360>;

					cy,virtual_keys =
						/* KEY_BACK */
						<158 1360 90 160 180
						/* KEY_MENU */
						139 1360 270 160 180
						/* KEY_HOMEPAGE */
						172 1360 450 160 180
						/* KEY SEARCH */
						217 1360 630 160 180>;
				};

				cy,btn {
					cy,name = "cyttsp5_btn";

					cy,inp_dev_name = "cyttsp5_btn";
				};

				cy,proximity {
					cy,name = "cyttsp5_proximity";

					cy,inp_dev_name = "cyttsp5_proximity";
					cy,abs = <0x19 0 1 0 0>;
				};
			};
		};

		/* ... */
	};
+49 −0
Original line number Diff line number Diff line
obj-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5) += cyttsp5.o
cyttsp5-y := cyttsp5_core.o cyttsp5_mt_common.o
cyttsp5-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_MT_A) += cyttsp5_mta.o
cyttsp5-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_MT_B) += cyttsp5_mtb.o
cyttsp5-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_BUTTON) += cyttsp5_btn.o
cyttsp5-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PROXIMITY) += cyttsp5_proximity.o
obj-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT) += cyttsp5_devtree.o
ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5
obj-y += cyttsp5_platform.o
endif
obj-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_I2C) += cyttsp5_i2c.o
obj-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_SPI) += cyttsp5_spi.o
obj-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEBUG_MDL) += cyttsp5_debug.o
obj-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_LOADER) += cyttsp5_loader.o
obj-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICE_ACCESS) += cyttsp5_device_access.o
obj-$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_TEST_DEVICE_ACCESS_API) += cyttsp5_test_device_access_api.o

ifeq ($(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEBUG),y)
CFLAGS_cyttsp5_core.o += -DDEBUG
CFLAGS_cyttsp5_i2c.o += -DDEBUG
CFLAGS_cyttsp5_spi.o += -DDEBUG
CFLAGS_cyttsp5_mta.o += -DDEBUG
CFLAGS_cyttsp5_mtb.o += -DDEBUG
CFLAGS_cyttsp5_mt_common.o += -DDEBUG
CFLAGS_cyttsp5_btn.o += -DDEBUG
CFLAGS_cyttsp5_proximity.o += -DDEBUG
CFLAGS_cyttsp5_device_access.o += -DDEBUG
CFLAGS_cyttsp5_loader.o += -DDEBUG
CFLAGS_cyttsp5_debug.o += -DDEBUG
CFLAGS_cyttsp5_devtree.o += -DDEBUG
CFLAGS_cyttsp5_platform.o += -DDEBUG
endif
ifeq ($(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_VDEBUG),y)
CFLAGS_cyttsp5_core.o += -DVERBOSE_DEBUG
CFLAGS_cyttsp5_i2c.o += -DVERBOSE_DEBUG
CFLAGS_cyttsp5_spi.o += -DVERBOSE_DEBUG
CFLAGS_cyttsp5_mta.o += -DVERBOSE_DEBUG
CFLAGS_cyttsp5_mtb.o += -DVERBOSE_DEBUG
CFLAGS_cyttsp5_mt_common.o += -DVERBOSE_DEBUG
CFLAGS_cyttsp5_btn.o += -DVERBOSE_DEBUG
CFLAGS_cyttsp5_proximity.o += -DVERBOSE_DEBUG
CFLAGS_cyttsp5_device_access.o += -DVERBOSE_DEBUG
CFLAGS_cyttsp5_loader.o += -DVERBOSE_DEBUG
CFLAGS_cyttsp5_debug.o += -DVERBOSE_DEBUG
CFLAGS_cyttsp5_devtree.o += -DVERBOSE_DEBUG
CFLAGS_cyttsp5_platform.o += -DVERBOSE_DEBUG
endif

+369 −0
Original line number Diff line number Diff line
/*
 * cyttsp5_btn.c
 * Parade TrueTouch(TM) Standard Product V5 CapSense Reports Module.
 * For use with Parade touchscreen controllers.
 * Supported parts include:
 * CYTMA5XX
 * CYTMA448
 * CYTMA445A
 * CYTT21XXX
 * CYTT31XXX
 *
 * Copyright (C) 2015 Parade Technologies
 * Copyright (C) 2012-2015 Cypress Semiconductor
 *
 * 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.
 *
 * Contact Parade Technologies at www.paradetech.com <ttdrivers@paradetech.com>
 *
 */

#include "cyttsp5_regs.h"

#define CYTTSP5_BTN_NAME "cyttsp5_btn"

static inline void cyttsp5_btn_key_action(struct cyttsp5_btn_data *bd,
	int btn_no, int btn_state)
{
	struct device *dev = bd->dev;
	struct cyttsp5_sysinfo *si = bd->si;

	if (!si->btn[btn_no].enabled ||
			si->btn[btn_no].state == btn_state)
		return;

	si->btn[btn_no].state = btn_state;
	input_report_key(bd->input, si->btn[btn_no].key_code, btn_state);
	input_sync(bd->input);

	parade_debug(dev, DEBUG_LEVEL_1, "%s: btn=%d key_code=%d %s\n",
		__func__, btn_no, si->btn[btn_no].key_code,
		btn_state == CY_BTN_PRESSED ?
			"PRESSED" : "RELEASED");
}

static void cyttsp5_get_btn_touches(struct cyttsp5_btn_data *bd)
{
	struct cyttsp5_sysinfo *si = bd->si;
	int num_btns = si->num_btns;
	int cur_btn;
	int cur_btn_state;

	for (cur_btn = 0; cur_btn < num_btns; cur_btn++) {
		/* Get current button state */
		cur_btn_state = (si->xy_data[0] >> (cur_btn * CY_BITS_PER_BTN))
				& CY_NUM_BTN_EVENT_ID;

		cyttsp5_btn_key_action(bd, cur_btn, cur_btn_state);
	}
}

static void cyttsp5_btn_lift_all(struct cyttsp5_btn_data *bd)
{
	struct cyttsp5_sysinfo *si = bd->si;
	int i;

	if (!si || si->num_btns == 0)
		return;

	for (i = 0; i < si->num_btns; i++)
		cyttsp5_btn_key_action(bd, i, CY_BTN_RELEASED);
}

#ifdef VERBOSE_DEBUG
static void cyttsp5_log_btn_data(struct cyttsp5_btn_data *bd)
{
	struct device *dev = bd->dev;
	struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
	u8 *pr_buf = cd->pr_buf;
	struct cyttsp5_sysinfo *si = bd->si;
	int cur;
	int value;

	for (cur = 0; cur < si->num_btns; cur++) {
		pr_buf[0] = 0;
		if (si->xy_data[0] & (1 << cur))
			value = 1;
		else
			value = 0;
		snprintf(pr_buf, CY_MAX_PRBUF_SIZE, "btn_rec[%d]=0x", cur);
		snprintf(pr_buf, CY_MAX_PRBUF_SIZE, "%s%X (%02X)",
			pr_buf, value,
			le16_to_cpu(si->xy_data[1 + cur * 2]));

		parade_debug(dev, DEBUG_LEVEL_2, "%s: %s\n", __func__, pr_buf);
	}
}
#endif

/* read xy_data for all current CapSense button touches */
static int cyttsp5_xy_worker(struct cyttsp5_btn_data *bd)
{
	struct cyttsp5_sysinfo *si = bd->si;

	/* extract button press/release touch information */
	if (si->num_btns > 0) {
		cyttsp5_get_btn_touches(bd);
#ifdef VERBOSE_DEBUG
		/* log button press/release touch information */
		cyttsp5_log_btn_data(bd);
#endif
	}

	return 0;
}

static int cyttsp5_btn_attention(struct device *dev)
{
	struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
	struct cyttsp5_btn_data *bd = &cd->bd;
	int rc;

	if (bd->si->xy_mode[2] != bd->si->desc.btn_report_id)
		return 0;

	/* core handles handshake */
	mutex_lock(&bd->btn_lock);
	rc = cyttsp5_xy_worker(bd);
	mutex_unlock(&bd->btn_lock);
	if (rc < 0)
		dev_err(dev, "%s: xy_worker error r=%d\n", __func__, rc);

	return rc;
}

static int cyttsp5_startup_attention(struct device *dev)
{
	struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
	struct cyttsp5_btn_data *bd = &cd->bd;

	mutex_lock(&bd->btn_lock);
	cyttsp5_btn_lift_all(bd);
	mutex_unlock(&bd->btn_lock);

	return 0;
}

static int cyttsp5_btn_suspend_attention(struct device *dev)
{
	struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
	struct cyttsp5_btn_data *bd = &cd->bd;

	mutex_lock(&bd->btn_lock);
	cyttsp5_btn_lift_all(bd);
	bd->is_suspended = true;
	mutex_unlock(&bd->btn_lock);

	pm_runtime_put(dev);

	return 0;
}

static int cyttsp5_btn_resume_attention(struct device *dev)
{
	struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
	struct cyttsp5_btn_data *bd = &cd->bd;

	pm_runtime_get(dev);

	mutex_lock(&bd->btn_lock);
	bd->is_suspended = false;
	mutex_unlock(&bd->btn_lock);

	return 0;
}

static int cyttsp5_btn_open(struct input_dev *input)
{
	struct device *dev = input->dev.parent;
	struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
	struct cyttsp5_btn_data *bd = &cd->bd;

	pm_runtime_get_sync(dev);

	mutex_lock(&bd->btn_lock);
	bd->is_suspended = false;
	mutex_unlock(&bd->btn_lock);

	parade_debug(dev, DEBUG_LEVEL_2, "%s: setup subscriptions\n", __func__);

	/* set up touch call back */
	_cyttsp5_subscribe_attention(dev, CY_ATTEN_IRQ, CYTTSP5_BTN_NAME,
		cyttsp5_btn_attention, CY_MODE_OPERATIONAL);

	/* set up startup call back */
	_cyttsp5_subscribe_attention(dev, CY_ATTEN_STARTUP, CYTTSP5_BTN_NAME,
		cyttsp5_startup_attention, 0);

	/* set up suspend call back */
	_cyttsp5_subscribe_attention(dev, CY_ATTEN_SUSPEND, CYTTSP5_BTN_NAME,
		cyttsp5_btn_suspend_attention, 0);

	/* set up resume call back */
	_cyttsp5_subscribe_attention(dev, CY_ATTEN_RESUME, CYTTSP5_BTN_NAME,
		cyttsp5_btn_resume_attention, 0);

	return 0;
}

static void cyttsp5_btn_close(struct input_dev *input)
{
	struct device *dev = input->dev.parent;
	struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
	struct cyttsp5_btn_data *bd = &cd->bd;

	_cyttsp5_unsubscribe_attention(dev, CY_ATTEN_IRQ, CYTTSP5_BTN_NAME,
		cyttsp5_btn_attention, CY_MODE_OPERATIONAL);

	_cyttsp5_unsubscribe_attention(dev, CY_ATTEN_STARTUP, CYTTSP5_BTN_NAME,
		cyttsp5_startup_attention, 0);

	_cyttsp5_unsubscribe_attention(dev, CY_ATTEN_SUSPEND, CYTTSP5_BTN_NAME,
		cyttsp5_btn_suspend_attention, 0);

	_cyttsp5_unsubscribe_attention(dev, CY_ATTEN_RESUME, CYTTSP5_BTN_NAME,
		cyttsp5_btn_resume_attention, 0);

	mutex_lock(&bd->btn_lock);
	if (!bd->is_suspended) {
		pm_runtime_put(dev);
		bd->is_suspended = true;
	}
	mutex_unlock(&bd->btn_lock);
}

static int cyttsp5_setup_input_device(struct device *dev)
{
	struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
	struct cyttsp5_btn_data *bd = &cd->bd;
	int i;
	int rc;

	parade_debug(dev, DEBUG_LEVEL_2, "%s: Initialize event signals\n",
		__func__);
	__set_bit(EV_KEY, bd->input->evbit);
	parade_debug(dev, DEBUG_LEVEL_2, "%s: Number of buttons %d\n",
		__func__, bd->si->num_btns);
	for (i = 0; i < bd->si->num_btns; i++) {
		parade_debug(dev, DEBUG_LEVEL_2, "%s: btn:%d keycode:%d\n",
			__func__, i, bd->si->btn[i].key_code);
		__set_bit(bd->si->btn[i].key_code, bd->input->keybit);
	}

	rc = input_register_device(bd->input);
	if (rc < 0)
		dev_err(dev, "%s: Error, failed register input device r=%d\n",
			__func__, rc);
	else
		bd->input_device_registered = true;

	return rc;
}

static int cyttsp5_setup_input_attention(struct device *dev)
{
	struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
	struct cyttsp5_btn_data *bd = &cd->bd;
	int rc;

	bd->si = _cyttsp5_request_sysinfo(dev);
	if (!bd->si)
		return -1;

	rc = cyttsp5_setup_input_device(dev);

	_cyttsp5_unsubscribe_attention(dev, CY_ATTEN_STARTUP, CYTTSP5_BTN_NAME,
		cyttsp5_setup_input_attention, 0);

	return rc;
}

int cyttsp5_btn_probe(struct device *dev)
{
	struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
	struct cyttsp5_btn_data *bd = &cd->bd;
	struct cyttsp5_platform_data *pdata = dev_get_platdata(dev);
	struct cyttsp5_btn_platform_data *btn_pdata;
	int rc = 0;

	if (!pdata || !pdata->btn_pdata) {
		dev_err(dev, "%s: Missing platform data\n", __func__);
		rc = -ENODEV;
		goto error_no_pdata;
	}
	btn_pdata = pdata->btn_pdata;

	mutex_init(&bd->btn_lock);
	bd->dev = dev;
	bd->pdata = btn_pdata;

	/* Create the input device and register it. */
	parade_debug(dev, DEBUG_LEVEL_2, "%s: Create the input device and register it\n",
		__func__);
	bd->input = input_allocate_device();
	if (!bd->input) {
		dev_err(dev, "%s: Error, failed to allocate input device\n",
			__func__);
		rc = -ENODEV;
		goto error_alloc_failed;
	}

	if (bd->pdata->inp_dev_name)
		bd->input->name = bd->pdata->inp_dev_name;
	else
		bd->input->name = CYTTSP5_BTN_NAME;
	scnprintf(bd->phys, sizeof(bd->phys), "%s/input%d", dev_name(dev),
			cd->phys_num++);
	bd->input->phys = bd->phys;
	bd->input->dev.parent = bd->dev;
	bd->input->open = cyttsp5_btn_open;
	bd->input->close = cyttsp5_btn_close;
	input_set_drvdata(bd->input, bd);

	/* get sysinfo */
	bd->si = _cyttsp5_request_sysinfo(dev);

	if (bd->si) {
		rc = cyttsp5_setup_input_device(dev);
		if (rc)
			goto error_init_input;
	} else {
		dev_err(dev, "%s: Fail get sysinfo pointer from core p=%p\n",
			__func__, bd->si);
		_cyttsp5_subscribe_attention(dev, CY_ATTEN_STARTUP,
			CYTTSP5_BTN_NAME, cyttsp5_setup_input_attention, 0);
	}

	return 0;

error_init_input:
	input_free_device(bd->input);
error_alloc_failed:
error_no_pdata:
	dev_err(dev, "%s failed.\n", __func__);
	return rc;
}

int cyttsp5_btn_release(struct device *dev)
{
	struct cyttsp5_core_data *cd = dev_get_drvdata(dev);
	struct cyttsp5_btn_data *bd = &cd->bd;

	if (bd->input_device_registered) {
		input_unregister_device(bd->input);
	} else {
		input_free_device(bd->input);
		_cyttsp5_unsubscribe_attention(dev, CY_ATTEN_STARTUP,
			CYTTSP5_BTN_NAME, cyttsp5_setup_input_attention, 0);
	}

	return 0;
}
+6210 −0

File added.

Preview size limit exceeded, changes collapsed.

+393 −0
Original line number Diff line number Diff line
/*
 * cyttsp5_debug.c
 * Parade TrueTouch(TM) Standard Product V5 Debug Module.
 * For use with Parade touchscreen controllers.
 * Supported parts include:
 * CYTMA5XX
 * CYTMA448
 * CYTMA445A
 * CYTT21XXX
 * CYTT31XXX
 *
 * Copyright (C) 2015 Parade Technologies
 * Copyright (C) 2012-2015 Cypress Semiconductor
 *
 * 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.
 *
 * Contact Parade Technologies at www.paradetech.com <ttdrivers@paradetech.com>
 *
 */

#include "cyttsp5_regs.h"

#define CYTTSP5_DEBUG_NAME "cyttsp5_debug"

struct cyttsp5_debug_data {
	struct device *dev;
	struct cyttsp5_sysinfo *si;
	uint32_t interrupt_count;
	uint32_t formated_output;
	struct mutex sysfs_lock;
	u8 pr_buf[CY_MAX_PRBUF_SIZE];
};

static struct cyttsp5_core_commands *cmd;

static struct cyttsp5_module debug_module;

static inline struct cyttsp5_debug_data *cyttsp5_get_debug_data(
		struct device *dev)
{
	return cyttsp5_get_module_data(dev, &debug_module);
}

/*
 * This function provide output of combined xy_mode and xy_data.
 * Required by TTHE.
 */
static void cyttsp5_pr_buf_op_mode(struct cyttsp5_debug_data *dd, u8 *pr_buf,
		struct cyttsp5_sysinfo *si, u8 cur_touch)
{
	const char fmt[] = "%02X ";
	int max = (CY_MAX_PRBUF_SIZE - 1) - sizeof(CY_PR_TRUNCATED);
	u8 report_id = si->xy_mode[2];
	int header_size = 0;
	int report_size = 0;
	int total_size = 0;
	int i, k;

	if (report_id == si->desc.tch_report_id) {
		header_size = si->desc.tch_header_size;
		report_size = cur_touch * si->desc.tch_record_size;
	} else if (report_id == si->desc.btn_report_id) {
		header_size = BTN_INPUT_HEADER_SIZE;
		report_size = BTN_REPORT_SIZE;
	}
	total_size = header_size + report_size;

	pr_buf[0] = 0;
	for (i = k = 0; i < header_size && i < max; i++, k += 3)
		scnprintf(pr_buf + k, CY_MAX_PRBUF_SIZE, fmt, si->xy_mode[i]);

	for (i = 0; i < report_size && i < max; i++, k += 3)
		scnprintf(pr_buf + k, CY_MAX_PRBUF_SIZE, fmt, si->xy_data[i]);

	pr_info("%s=%s%s\n", "cyttsp5_OpModeData", pr_buf,
			total_size <= max ? "" : CY_PR_TRUNCATED);
}

static void cyttsp5_debug_print(struct device *dev, u8 *pr_buf, u8 *sptr,
		int size, const char *data_name)
{
	int i, j;
	int elem_size = sizeof("XX ") - 1;
	int max = (CY_MAX_PRBUF_SIZE - 1) / elem_size;
	int limit = size < max ? size : max;

	if (limit < 0)
		limit = 0;

	pr_buf[0] = 0;
	for (i = j = 0; i < limit; i++, j += elem_size)
		scnprintf(pr_buf + j, CY_MAX_PRBUF_SIZE - j, "%02X ", sptr[i]);

	if (size)
		pr_info("%s[0..%d]=%s%s\n", data_name, size - 1, pr_buf,
			size <= max ? "" : CY_PR_TRUNCATED);
	else
		pr_info("%s[]\n", data_name);
}

static void cyttsp5_debug_formated(struct device *dev, u8 *pr_buf,
		struct cyttsp5_sysinfo *si, u8 num_cur_tch)
{
	u8 report_id = si->xy_mode[2];
	int header_size = 0;
	int report_size = 0;
	u8 data_name[] = "touch[99]";
	int max_print_length = 20;
	int i;

	if (report_id == si->desc.tch_report_id) {
		header_size = si->desc.tch_header_size;
		report_size = num_cur_tch * si->desc.tch_record_size;
	} else if (report_id == si->desc.btn_report_id) {
		header_size = BTN_INPUT_HEADER_SIZE;
		report_size = BTN_REPORT_SIZE;
	}

	/* xy_mode */
	cyttsp5_debug_print(dev, pr_buf, si->xy_mode, header_size, "xy_mode");

	/* xy_data */
	if (report_size > max_print_length) {
		pr_info("xy_data[0..%d]:\n", report_size);
		for (i = 0; i < report_size - max_print_length;
				i += max_print_length) {
			cyttsp5_debug_print(dev, pr_buf, si->xy_data + i,
					max_print_length, " ");
		}
		if (report_size - i)
			cyttsp5_debug_print(dev, pr_buf, si->xy_data + i,
					report_size - i, " ");
	} else {
		cyttsp5_debug_print(dev, pr_buf, si->xy_data, report_size,
				"xy_data");
	}

	/* touches */
	if (report_id == si->desc.tch_report_id) {
		for (i = 0; i < num_cur_tch; i++) {
			scnprintf(data_name, sizeof(data_name) - 1,
					"touch[%u]", i);
			cyttsp5_debug_print(dev, pr_buf,
				si->xy_data + (i * si->desc.tch_record_size),
				si->desc.tch_record_size, data_name);
		}
	}

	/* buttons */
	if (report_id == si->desc.btn_report_id)
		cyttsp5_debug_print(dev, pr_buf, si->xy_data, report_size,
				"button");
}

/* read xy_data for all touches for debug */
static int cyttsp5_xy_worker(struct cyttsp5_debug_data *dd)
{
	struct device *dev = dd->dev;
	struct cyttsp5_sysinfo *si = dd->si;
	u8 report_reg = si->xy_mode[TOUCH_COUNT_BYTE_OFFSET];
	u8 num_cur_tch = GET_NUM_TOUCHES(report_reg);
	uint32_t formated_output;

	mutex_lock(&dd->sysfs_lock);
	dd->interrupt_count++;
	formated_output = dd->formated_output;
	mutex_unlock(&dd->sysfs_lock);

	/* Interrupt */
	pr_info("Interrupt(%u)\n", dd->interrupt_count);

	if (formated_output)
		cyttsp5_debug_formated(dev, dd->pr_buf, si, num_cur_tch);
	else
		/* print data for TTHE */
		cyttsp5_pr_buf_op_mode(dd, dd->pr_buf, si, num_cur_tch);

	pr_info("\n");

	return 0;
}

static int cyttsp5_debug_attention(struct device *dev)
{
	struct cyttsp5_debug_data *dd = cyttsp5_get_debug_data(dev);
	struct cyttsp5_sysinfo *si = dd->si;
	u8 report_id = si->xy_mode[2];
	int rc = 0;

	if (report_id != si->desc.tch_report_id
			&& report_id != si->desc.btn_report_id)
		return 0;

	/* core handles handshake */
	rc = cyttsp5_xy_worker(dd);
	if (rc < 0)
		dev_err(dev, "%s: xy_worker error r=%d\n", __func__, rc);

	return rc;
}

static ssize_t cyttsp5_interrupt_count_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct cyttsp5_debug_data *dd = cyttsp5_get_debug_data(dev);
	int val;

	mutex_lock(&dd->sysfs_lock);
	val = dd->interrupt_count;
	mutex_unlock(&dd->sysfs_lock);

	return scnprintf(buf, CY_MAX_PRBUF_SIZE, "Interrupt Count: %d\n", val);
}

static ssize_t cyttsp5_interrupt_count_store(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t size)
{
	struct cyttsp5_debug_data *dd = cyttsp5_get_debug_data(dev);

	mutex_lock(&dd->sysfs_lock);
	dd->interrupt_count = 0;
	mutex_unlock(&dd->sysfs_lock);
	return size;
}

static DEVICE_ATTR(int_count, S_IRUSR | S_IWUSR,
	cyttsp5_interrupt_count_show, cyttsp5_interrupt_count_store);

static ssize_t cyttsp5_formated_output_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct cyttsp5_debug_data *dd = cyttsp5_get_debug_data(dev);
	int val;

	mutex_lock(&dd->sysfs_lock);
	val = dd->formated_output;
	mutex_unlock(&dd->sysfs_lock);

	return scnprintf(buf, CY_MAX_PRBUF_SIZE,
			"Formated debug output: %x\n", val);
}

static ssize_t cyttsp5_formated_output_store(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t size)
{
	struct cyttsp5_debug_data *dd = cyttsp5_get_debug_data(dev);
	unsigned long value;
	int rc;

	rc = kstrtoul(buf, 10, &value);
	if (rc < 0) {
		dev_err(dev, "%s: Invalid value\n", __func__);
		return size;
	}

	/* Expecting only 0 or 1 */
	if (value != 0 && value != 1) {
		dev_err(dev, "%s: Invalid value %lu\n", __func__, value);
		return size;
	}

	mutex_lock(&dd->sysfs_lock);
	dd->formated_output = value;
	mutex_unlock(&dd->sysfs_lock);
	return size;
}

static DEVICE_ATTR(formated_output, S_IRUSR | S_IWUSR,
	cyttsp5_formated_output_show, cyttsp5_formated_output_store);

static int cyttsp5_debug_probe(struct device *dev, void **data)
{
	struct cyttsp5_debug_data *dd;
	int rc;

	/* get context and debug print buffers */
	dd = kzalloc(sizeof(*dd), GFP_KERNEL);
	if (!dd) {
		rc = -ENOMEM;
		goto cyttsp5_debug_probe_alloc_failed;
	}

	rc = device_create_file(dev, &dev_attr_int_count);
	if (rc) {
		dev_err(dev, "%s: Error, could not create int_count\n",
				__func__);
		goto cyttsp5_debug_probe_create_int_count_failed;
	}

	rc = device_create_file(dev, &dev_attr_formated_output);
	if (rc) {
		dev_err(dev, "%s: Error, could not create formated_output\n",
				__func__);
		goto cyttsp5_debug_probe_create_formated_failed;
	}

	mutex_init(&dd->sysfs_lock);
	dd->dev = dev;
	*data = dd;

	dd->si = cmd->request_sysinfo(dev);
	if (!dd->si) {
		dev_err(dev, "%s: Fail get sysinfo pointer from core\n",
				__func__);
		rc = -ENODEV;
		goto cyttsp5_debug_probe_sysinfo_failed;
	}

	rc = cmd->subscribe_attention(dev, CY_ATTEN_IRQ, CYTTSP5_DEBUG_NAME,
		cyttsp5_debug_attention, CY_MODE_OPERATIONAL);
	if (rc < 0) {
		dev_err(dev, "%s: Error, could not subscribe attention cb\n",
				__func__);
		goto cyttsp5_debug_probe_subscribe_failed;
	}

	return 0;

cyttsp5_debug_probe_subscribe_failed:
cyttsp5_debug_probe_sysinfo_failed:
	device_remove_file(dev, &dev_attr_formated_output);
cyttsp5_debug_probe_create_formated_failed:
	device_remove_file(dev, &dev_attr_int_count);
cyttsp5_debug_probe_create_int_count_failed:
	kfree(dd);
cyttsp5_debug_probe_alloc_failed:
	dev_err(dev, "%s failed.\n", __func__);
	return rc;
}

static void cyttsp5_debug_release(struct device *dev, void *data)
{
	struct cyttsp5_debug_data *dd = data;
	int rc;

	rc = cmd->unsubscribe_attention(dev, CY_ATTEN_IRQ, CYTTSP5_DEBUG_NAME,
		cyttsp5_debug_attention, CY_MODE_OPERATIONAL);
	if (rc < 0) {
		dev_err(dev, "%s: Error, could not un-subscribe attention\n",
				__func__);
		goto cyttsp5_debug_release_exit;
	}

cyttsp5_debug_release_exit:
	device_remove_file(dev, &dev_attr_formated_output);
	device_remove_file(dev, &dev_attr_int_count);
	kfree(dd);
}

static struct cyttsp5_module debug_module = {
	.name = CYTTSP5_DEBUG_NAME,
	.probe = cyttsp5_debug_probe,
	.release = cyttsp5_debug_release,
};

static int __init cyttsp5_debug_init(void)
{
	int rc;

	cmd = cyttsp5_get_commands();
	if (!cmd)
		return -EINVAL;

	rc = cyttsp5_register_module(&debug_module);
	if (rc < 0) {
		pr_err("%s: Error, failed registering module\n",
			__func__);
			return rc;
	}

	pr_info("%s: Parade TTSP Debug Driver (Built %s) rc=%d\n",
		 __func__, CY_DRIVER_VERSION, rc);
	return 0;
}
module_init(cyttsp5_debug_init);

static void __exit cyttsp5_debug_exit(void)
{
	cyttsp5_unregister_module(&debug_module);
}
module_exit(cyttsp5_debug_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product Debug Driver");
MODULE_AUTHOR("Parade Technologies <ttdrivers@paradetech.com>");
Loading