cregit-Linux how code gets into the kernel

Release 4.11 drivers/input/touchscreen/cyttsp_core.c

/*
 * 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/input/touchscreen.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/property.h>
#include <linux/gpio/consumer.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_ACT_DIST_MASK		0x0F

#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->dev, ts->xfer_buf, command, length, buf); if (!error) return 0; msleep(CY_DELAY_DFLT); } return -EIO; }

Contributors

PersonTokensPropCommitsCommitProp
Javier Martinez Canillas7692.68%150.00%
Ferruh Yigit67.32%150.00%
Total82100.00%2100.00%


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->dev, ts->xfer_buf, command, length, buf); if (!error) return 0; msleep(CY_DELAY_DFLT); } return -EIO; }

Contributors

PersonTokensPropCommitsCommitProp
Javier Martinez Canillas7692.68%150.00%
Ferruh Yigit67.32%150.00%
Total82100.00%2100.00%


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

Contributors

PersonTokensPropCommitsCommitProp
Javier Martinez Canillas30100.00%1100.00%
Total30100.00%1100.00%


static int cyttsp_handshake(struct cyttsp *ts) { if (ts->use_hndshk) return ttsp_send_command(ts, ts->xy_data.hst_mode ^ CY_HNDSHK_BIT); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Ferruh Yigit34100.00%1100.00%
Total34100.00%1100.00%


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); }

Contributors

PersonTokensPropCommitsCommitProp
Javier Martinez Canillas56100.00%1100.00%
Total56100.00%1100.00%


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->bl_keys) memcpy(&bl_cmd[sizeof(bl_command) - CY_NUM_BL_KEYS], ts->bl_keys, CY_NUM_BL_KEYS); 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; }

Contributors

PersonTokensPropCommitsCommitProp
Javier Martinez Canillas12199.18%150.00%
Ferruh Yigit10.82%150.00%
Total122100.00%2100.00%


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; error = cyttsp_handshake(ts); if (error) return error; return ts->xy_data.act_dist == CY_ACT_DIST_DFLT ? -EIO : 0; }

Contributors

PersonTokensPropCommitsCommitProp
Javier Martinez Canillas7383.91%150.00%
Ferruh Yigit1416.09%150.00%
Total87100.00%2100.00%


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; error = cyttsp_handshake(ts); if (error) return error; if (!ts->sysinfo_data.tts_verh && !ts->sysinfo_data.tts_verl) return -EIO; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Javier Martinez Canillas10588.24%150.00%
Ferruh Yigit1411.76%150.00%
Total119100.00%2100.00%


static int cyttsp_set_sysinfo_regs(struct cyttsp *ts) { int retval = 0; if (ts->act_intrvl != CY_ACT_INTRVL_DFLT || ts->tch_tmout != CY_TCH_TMOUT_DFLT || ts->lp_intrvl != CY_LP_INTRVL_DFLT) { u8 intrvl_ray[] = { ts->act_intrvl, ts->tch_tmout, ts->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; }

Contributors

PersonTokensPropCommitsCommitProp
Javier Martinez Canillas81100.00%1100.00%
Total81100.00%1100.00%


static void cyttsp_hard_reset(struct cyttsp *ts) { if (ts->reset_gpio) { gpiod_set_value_cansleep(ts->reset_gpio, 1); msleep(CY_DELAY_DFLT); gpiod_set_value_cansleep(ts->reset_gpio, 0); msleep(CY_DELAY_DFLT); } }

Contributors

PersonTokensPropCommitsCommitProp
Oreste Salerno47100.00%1100.00%
Total47100.00%1100.00%


static int cyttsp_soft_reset(struct cyttsp *ts) { unsigned long timeout; int retval; /* wait for interrupt to set ready completion */ reinit_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; }

Contributors

PersonTokensPropCommitsCommitProp
Javier Martinez Canillas9898.00%150.00%
Wolfram Sang22.00%150.00%
Total100100.00%2100.00%


static int cyttsp_act_dist_setup(struct cyttsp *ts) { u8 act_dist_setup = ts->act_dist; /* Init gesture; active distance setup */ return ttsp_write_block_data(ts, CY_REG_ACT_DIST, sizeof(act_dist_setup), &act_dist_setup); }

Contributors

PersonTokensPropCommitsCommitProp
Javier Martinez Canillas35100.00%1100.00%
Total35100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Javier Martinez Canillas59100.00%1100.00%
Total59100.00%1100.00%


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; } }

Contributors

PersonTokensPropCommitsCommitProp
Javier Martinez Canillas63100.00%1100.00%
Total63100.00%1100.00%


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); }

Contributors

PersonTokensPropCommitsCommitProp
Javier Martinez Canillas296100.00%1100.00%
Total296100.00%1100.00%


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 */ error = cyttsp_handshake(ts); 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; }

Contributors

PersonTokensPropCommitsCommitProp
Javier Martinez Canillas16099.38%150.00%
Ferruh Yigit10.62%150.00%
Total161100.00%2100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Javier Martinez Canillas172100.00%1100.00%
Total172100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Javier Martinez Canillas68100.00%1100.00%
Total68100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Javier Martinez Canillas40100.00%1100.00%
Total40100.00%1100.00%


static int __maybe_unused 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; }

Contributors

PersonTokensPropCommitsCommitProp
Javier Martinez Canillas7898.73%150.00%
Jingoo Han11.27%150.00%
Total79100.00%2100.00%


static int __maybe_unused 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; }

Contributors

PersonTokensPropCommitsCommitProp
Javier Martinez Canillas6398.44%150.00%
Jingoo Han11.56%150.00%
Total64100.00%2100.00%

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; }

Contributors

PersonTokensPropCommitsCommitProp
Javier Martinez Canillas43100.00%1100.00%
Total43100.00%1100.00%


static void cyttsp_close(struct input_dev *dev) { struct cyttsp *ts = input_get_drvdata(dev); if (!ts->suspended) cyttsp_disable(ts); }

Contributors

PersonTokensPropCommitsCommitProp
Javier Martinez Canillas33100.00%1100.00%
Total33100.00%1100.00%


static int cyttsp_parse_properties(struct cyttsp *ts) { struct device *dev = ts->dev; u32 dt_value; int ret; ts->bl_keys = devm_kzalloc(dev, CY_NUM_BL_KEYS, GFP_KERNEL); if (!ts->bl_keys) return -ENOMEM; /* Set some default values */ ts->use_hndshk = false; ts->act_dist = CY_ACT_DIST_DFLT; ts->act_intrvl = CY_ACT_INTRVL_DFLT; ts->tch_tmout = CY_TCH_TMOUT_DFLT; ts->lp_intrvl = CY_LP_INTRVL_DFLT; ret = device_property_read_u8_array(dev, "bootloader-key", ts->bl_keys, CY_NUM_BL_KEYS); if (ret) { dev_err(dev, "bootloader-key property could not be retrieved\n"); return ret; } ts->use_hndshk = device_property_present(dev, "use-handshake"); if (!device_property_read_u32(dev, "active-distance", &dt_value)) { if (dt_value > 15) { dev_err(dev, "active-distance (%u) must be [0-15]\n", dt_value); return -EINVAL; } ts->act_dist &= ~CY_ACT_DIST_MASK; ts->act_dist |= dt_value; } if (!device_property_read_u32(dev, "active-interval-ms", &dt_value)) { if (dt_value > 255) { dev_err(dev, "active-interval-ms (%u) must be [0-255]\n", dt_value); return -EINVAL; } ts->act_intrvl = dt_value; } if (!device_property_read_u32(dev, "lowpower-interval-ms", &dt_value)) { if (dt_value > 2550) { dev_err(dev, "lowpower-interval-ms (%u) must be [0-2550]\n", dt_value); return -EINVAL; } /* Register value is expressed in 0.01s / bit */ ts->lp_intrvl = dt_value / 10; } if (!device_property_read_u32(dev, "touch-timeout-ms", &dt_value)) { if (dt_value > 2550) { dev_err(dev, "touch-timeout-ms (%u) must be [0-2550]\n", dt_value); return -EINVAL; } /* Register value is expressed in 0.01s / bit */ ts->tch_tmout = dt_value / 10; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Oreste Salerno307100.00%2100.00%
Total307100.00%2100.00%


struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops, struct device *dev, int irq, size_t xfer_buf_size) { struct cyttsp *ts; struct input_dev *input_dev; int error; ts = devm_kzalloc(dev, sizeof(*ts) + xfer_buf_size, GFP_KERNEL); if (!ts) return ERR_PTR(-ENOMEM); input_dev = devm_input_allocate_device(dev); if (!input_dev) return ERR_PTR(-ENOMEM); ts->dev = dev; ts->input = input_dev; ts->bus_ops = bus_ops; ts->irq = irq; ts->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(ts->reset_gpio)) { error = PTR_ERR(ts->reset_gpio); dev_err(dev, "Failed to request reset gpio, error %d\n", error); return ERR_PTR(error); } error = cyttsp_parse_properties(ts); if (error) return ERR_PTR(error); init_completion(&ts->bl_ready); snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev)); input_dev->name = "Cypress TTSP TouchScreen"; 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); input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_X); input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_Y); touchscreen_parse_properties(input_dev, true, NULL); error = input_mt_init_slots(input_dev, CY_MAX_ID, 0); if (error) { dev_err(dev, "Unable to init MT slots.\n"); return ERR_PTR(error); } error = devm_request_threaded_irq(dev, ts->irq, NULL, cyttsp_irq, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "cyttsp", ts); if (error) { dev_err(ts->dev, "failed to request IRQ %d, err: %d\n", ts->irq, error