cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

da9034-ts.c (8293B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Touchscreen driver for Dialog Semiconductor DA9034
      4 *
      5 * Copyright (C) 2006-2008 Marvell International Ltd.
      6 *	Fengwei Yin <fengwei.yin@marvell.com>
      7 *	Bin Yang  <bin.yang@marvell.com>
      8 *	Eric Miao <eric.miao@marvell.com>
      9 */
     10
     11#include <linux/module.h>
     12#include <linux/kernel.h>
     13#include <linux/delay.h>
     14#include <linux/platform_device.h>
     15#include <linux/input.h>
     16#include <linux/workqueue.h>
     17#include <linux/mfd/da903x.h>
     18#include <linux/slab.h>
     19
     20#define DA9034_MANUAL_CTRL	0x50
     21#define DA9034_LDO_ADC_EN	(1 << 4)
     22
     23#define DA9034_AUTO_CTRL1	0x51
     24
     25#define DA9034_AUTO_CTRL2	0x52
     26#define DA9034_AUTO_TSI_EN	(1 << 3)
     27#define DA9034_PEN_DETECT	(1 << 4)
     28
     29#define DA9034_TSI_CTRL1	0x53
     30#define DA9034_TSI_CTRL2	0x54
     31#define DA9034_TSI_X_MSB	0x6c
     32#define DA9034_TSI_Y_MSB	0x6d
     33#define DA9034_TSI_XY_LSB	0x6e
     34
     35enum {
     36	STATE_IDLE,	/* wait for pendown */
     37	STATE_BUSY,	/* TSI busy sampling */
     38	STATE_STOP,	/* sample available */
     39	STATE_WAIT,	/* Wait to start next sample */
     40};
     41
     42enum {
     43	EVENT_PEN_DOWN,
     44	EVENT_PEN_UP,
     45	EVENT_TSI_READY,
     46	EVENT_TIMEDOUT,
     47};
     48
     49struct da9034_touch {
     50	struct device		*da9034_dev;
     51	struct input_dev	*input_dev;
     52
     53	struct delayed_work	tsi_work;
     54	struct notifier_block	notifier;
     55
     56	int	state;
     57
     58	int	interval_ms;
     59	int	x_inverted;
     60	int	y_inverted;
     61
     62	int	last_x;
     63	int	last_y;
     64};
     65
     66static inline int is_pen_down(struct da9034_touch *touch)
     67{
     68	return da903x_query_status(touch->da9034_dev, DA9034_STATUS_PEN_DOWN);
     69}
     70
     71static inline int detect_pen_down(struct da9034_touch *touch, int on)
     72{
     73	if (on)
     74		return da903x_set_bits(touch->da9034_dev,
     75				DA9034_AUTO_CTRL2, DA9034_PEN_DETECT);
     76	else
     77		return da903x_clr_bits(touch->da9034_dev,
     78				DA9034_AUTO_CTRL2, DA9034_PEN_DETECT);
     79}
     80
     81static int read_tsi(struct da9034_touch *touch)
     82{
     83	uint8_t _x, _y, _v;
     84	int ret;
     85
     86	ret = da903x_read(touch->da9034_dev, DA9034_TSI_X_MSB, &_x);
     87	if (ret)
     88		return ret;
     89
     90	ret = da903x_read(touch->da9034_dev, DA9034_TSI_Y_MSB, &_y);
     91	if (ret)
     92		return ret;
     93
     94	ret = da903x_read(touch->da9034_dev, DA9034_TSI_XY_LSB, &_v);
     95	if (ret)
     96		return ret;
     97
     98	touch->last_x = ((_x << 2) & 0x3fc) | (_v & 0x3);
     99	touch->last_y = ((_y << 2) & 0x3fc) | ((_v & 0xc) >> 2);
    100
    101	return 0;
    102}
    103
    104static inline int start_tsi(struct da9034_touch *touch)
    105{
    106	return da903x_set_bits(touch->da9034_dev,
    107			DA9034_AUTO_CTRL2, DA9034_AUTO_TSI_EN);
    108}
    109
    110static inline int stop_tsi(struct da9034_touch *touch)
    111{
    112	return da903x_clr_bits(touch->da9034_dev,
    113			DA9034_AUTO_CTRL2, DA9034_AUTO_TSI_EN);
    114}
    115
    116static inline void report_pen_down(struct da9034_touch *touch)
    117{
    118	int x = touch->last_x;
    119	int y = touch->last_y;
    120
    121	x &= 0xfff;
    122	if (touch->x_inverted)
    123		x = 1024 - x;
    124	y &= 0xfff;
    125	if (touch->y_inverted)
    126		y = 1024 - y;
    127
    128	input_report_abs(touch->input_dev, ABS_X, x);
    129	input_report_abs(touch->input_dev, ABS_Y, y);
    130	input_report_key(touch->input_dev, BTN_TOUCH, 1);
    131
    132	input_sync(touch->input_dev);
    133}
    134
    135static inline void report_pen_up(struct da9034_touch *touch)
    136{
    137	input_report_key(touch->input_dev, BTN_TOUCH, 0);
    138	input_sync(touch->input_dev);
    139}
    140
    141static void da9034_event_handler(struct da9034_touch *touch, int event)
    142{
    143	int err;
    144
    145	switch (touch->state) {
    146	case STATE_IDLE:
    147		if (event != EVENT_PEN_DOWN)
    148			break;
    149
    150		/* Enable auto measurement of the TSI, this will
    151		 * automatically disable pen down detection
    152		 */
    153		err = start_tsi(touch);
    154		if (err)
    155			goto err_reset;
    156
    157		touch->state = STATE_BUSY;
    158		break;
    159
    160	case STATE_BUSY:
    161		if (event != EVENT_TSI_READY)
    162			break;
    163
    164		err = read_tsi(touch);
    165		if (err)
    166			goto err_reset;
    167
    168		/* Disable auto measurement of the TSI, so that
    169		 * pen down status will be available
    170		 */
    171		err = stop_tsi(touch);
    172		if (err)
    173			goto err_reset;
    174
    175		touch->state = STATE_STOP;
    176
    177		/* FIXME: PEN_{UP/DOWN} events are expected to be
    178		 * available by stopping TSI, but this is found not
    179		 * always true, delay and simulate such an event
    180		 * here is more reliable
    181		 */
    182		mdelay(1);
    183		da9034_event_handler(touch,
    184				     is_pen_down(touch) ? EVENT_PEN_DOWN :
    185							  EVENT_PEN_UP);
    186		break;
    187
    188	case STATE_STOP:
    189		if (event == EVENT_PEN_DOWN) {
    190			report_pen_down(touch);
    191			schedule_delayed_work(&touch->tsi_work,
    192				msecs_to_jiffies(touch->interval_ms));
    193			touch->state = STATE_WAIT;
    194		}
    195
    196		if (event == EVENT_PEN_UP) {
    197			report_pen_up(touch);
    198			touch->state = STATE_IDLE;
    199		}
    200		break;
    201
    202	case STATE_WAIT:
    203		if (event != EVENT_TIMEDOUT)
    204			break;
    205
    206		if (is_pen_down(touch)) {
    207			start_tsi(touch);
    208			touch->state = STATE_BUSY;
    209		} else {
    210			report_pen_up(touch);
    211			touch->state = STATE_IDLE;
    212		}
    213		break;
    214	}
    215	return;
    216
    217err_reset:
    218	touch->state = STATE_IDLE;
    219	stop_tsi(touch);
    220	detect_pen_down(touch, 1);
    221}
    222
    223static void da9034_tsi_work(struct work_struct *work)
    224{
    225	struct da9034_touch *touch =
    226		container_of(work, struct da9034_touch, tsi_work.work);
    227
    228	da9034_event_handler(touch, EVENT_TIMEDOUT);
    229}
    230
    231static int da9034_touch_notifier(struct notifier_block *nb,
    232				 unsigned long event, void *data)
    233{
    234	struct da9034_touch *touch =
    235		container_of(nb, struct da9034_touch, notifier);
    236
    237	if (event & DA9034_EVENT_TSI_READY)
    238		da9034_event_handler(touch, EVENT_TSI_READY);
    239
    240	if ((event & DA9034_EVENT_PEN_DOWN) && touch->state == STATE_IDLE)
    241		da9034_event_handler(touch, EVENT_PEN_DOWN);
    242
    243	return 0;
    244}
    245
    246static int da9034_touch_open(struct input_dev *dev)
    247{
    248	struct da9034_touch *touch = input_get_drvdata(dev);
    249	int ret;
    250
    251	ret = da903x_register_notifier(touch->da9034_dev, &touch->notifier,
    252			DA9034_EVENT_PEN_DOWN | DA9034_EVENT_TSI_READY);
    253	if (ret)
    254		return -EBUSY;
    255
    256	/* Enable ADC LDO */
    257	ret = da903x_set_bits(touch->da9034_dev,
    258			DA9034_MANUAL_CTRL, DA9034_LDO_ADC_EN);
    259	if (ret)
    260		return ret;
    261
    262	/* TSI_DELAY: 3 slots, TSI_SKIP: 3 slots */
    263	ret = da903x_write(touch->da9034_dev, DA9034_TSI_CTRL1, 0x1b);
    264	if (ret)
    265		return ret;
    266
    267	ret = da903x_write(touch->da9034_dev, DA9034_TSI_CTRL2, 0x00);
    268	if (ret)
    269		return ret;
    270
    271	touch->state = STATE_IDLE;
    272	detect_pen_down(touch, 1);
    273
    274	return 0;
    275}
    276
    277static void da9034_touch_close(struct input_dev *dev)
    278{
    279	struct da9034_touch *touch = input_get_drvdata(dev);
    280
    281	da903x_unregister_notifier(touch->da9034_dev, &touch->notifier,
    282			DA9034_EVENT_PEN_DOWN | DA9034_EVENT_TSI_READY);
    283
    284	cancel_delayed_work_sync(&touch->tsi_work);
    285
    286	touch->state = STATE_IDLE;
    287	stop_tsi(touch);
    288	detect_pen_down(touch, 0);
    289
    290	/* Disable ADC LDO */
    291	da903x_clr_bits(touch->da9034_dev,
    292			DA9034_MANUAL_CTRL, DA9034_LDO_ADC_EN);
    293}
    294
    295
    296static int da9034_touch_probe(struct platform_device *pdev)
    297{
    298	struct da9034_touch_pdata *pdata = dev_get_platdata(&pdev->dev);
    299	struct da9034_touch *touch;
    300	struct input_dev *input_dev;
    301	int error;
    302
    303	touch = devm_kzalloc(&pdev->dev, sizeof(struct da9034_touch),
    304			     GFP_KERNEL);
    305	if (!touch) {
    306		dev_err(&pdev->dev, "failed to allocate driver data\n");
    307		return -ENOMEM;
    308	}
    309
    310	touch->da9034_dev = pdev->dev.parent;
    311
    312	if (pdata) {
    313		touch->interval_ms	= pdata->interval_ms;
    314		touch->x_inverted	= pdata->x_inverted;
    315		touch->y_inverted	= pdata->y_inverted;
    316	} else {
    317		/* fallback into default */
    318		touch->interval_ms	= 10;
    319	}
    320
    321	INIT_DELAYED_WORK(&touch->tsi_work, da9034_tsi_work);
    322	touch->notifier.notifier_call = da9034_touch_notifier;
    323
    324	input_dev = devm_input_allocate_device(&pdev->dev);
    325	if (!input_dev) {
    326		dev_err(&pdev->dev, "failed to allocate input device\n");
    327		return -ENOMEM;
    328	}
    329
    330	input_dev->name		= pdev->name;
    331	input_dev->open		= da9034_touch_open;
    332	input_dev->close	= da9034_touch_close;
    333	input_dev->dev.parent	= &pdev->dev;
    334
    335	__set_bit(EV_ABS, input_dev->evbit);
    336	__set_bit(ABS_X, input_dev->absbit);
    337	__set_bit(ABS_Y, input_dev->absbit);
    338	input_set_abs_params(input_dev, ABS_X, 0, 1023, 0, 0);
    339	input_set_abs_params(input_dev, ABS_Y, 0, 1023, 0, 0);
    340
    341	__set_bit(EV_KEY, input_dev->evbit);
    342	__set_bit(BTN_TOUCH, input_dev->keybit);
    343
    344	touch->input_dev = input_dev;
    345	input_set_drvdata(input_dev, touch);
    346
    347	error = input_register_device(input_dev);
    348	if (error)
    349		return error;
    350
    351	return 0;
    352}
    353
    354static struct platform_driver da9034_touch_driver = {
    355	.driver	= {
    356		.name	= "da9034-touch",
    357	},
    358	.probe		= da9034_touch_probe,
    359};
    360module_platform_driver(da9034_touch_driver);
    361
    362MODULE_DESCRIPTION("Touchscreen driver for Dialog Semiconductor DA9034");
    363MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>, Bin Yang <bin.yang@marvell.com>");
    364MODULE_LICENSE("GPL");
    365MODULE_ALIAS("platform:da9034-touch");