xref: /openbmc/linux/drivers/input/touchscreen/pcap_ts.c (revision 75bf465f0bc33e9b776a46d6a1b9b990f5fb7c37)
1*d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
20387e107SDaniel Ribeiro /*
30387e107SDaniel Ribeiro  * Driver for Motorola PCAP2 touchscreen as found in the EZX phone platform.
40387e107SDaniel Ribeiro  *
50387e107SDaniel Ribeiro  *  Copyright (C) 2006 Harald Welte <laforge@openezx.org>
60387e107SDaniel Ribeiro  *  Copyright (C) 2009 Daniel Ribeiro <drwyrm@gmail.com>
70387e107SDaniel Ribeiro  */
80387e107SDaniel Ribeiro 
90387e107SDaniel Ribeiro #include <linux/module.h>
100387e107SDaniel Ribeiro #include <linux/fs.h>
110387e107SDaniel Ribeiro #include <linux/string.h>
125a0e3ad6STejun Heo #include <linux/slab.h>
130387e107SDaniel Ribeiro #include <linux/pm.h>
140387e107SDaniel Ribeiro #include <linux/timer.h>
150387e107SDaniel Ribeiro #include <linux/interrupt.h>
160387e107SDaniel Ribeiro #include <linux/platform_device.h>
170387e107SDaniel Ribeiro #include <linux/input.h>
180387e107SDaniel Ribeiro #include <linux/mfd/ezx-pcap.h>
190387e107SDaniel Ribeiro 
200387e107SDaniel Ribeiro struct pcap_ts {
210387e107SDaniel Ribeiro 	struct pcap_chip *pcap;
220387e107SDaniel Ribeiro 	struct input_dev *input;
230387e107SDaniel Ribeiro 	struct delayed_work work;
240387e107SDaniel Ribeiro 	u16 x, y;
250387e107SDaniel Ribeiro 	u16 pressure;
260387e107SDaniel Ribeiro 	u8 read_state;
270387e107SDaniel Ribeiro };
280387e107SDaniel Ribeiro 
290387e107SDaniel Ribeiro #define SAMPLE_DELAY	20 /* msecs */
300387e107SDaniel Ribeiro 
310387e107SDaniel Ribeiro #define X_AXIS_MIN	0
320387e107SDaniel Ribeiro #define X_AXIS_MAX	1023
330387e107SDaniel Ribeiro #define Y_AXIS_MAX	X_AXIS_MAX
340387e107SDaniel Ribeiro #define Y_AXIS_MIN	X_AXIS_MIN
350387e107SDaniel Ribeiro #define PRESSURE_MAX	X_AXIS_MAX
360387e107SDaniel Ribeiro #define PRESSURE_MIN	X_AXIS_MIN
370387e107SDaniel Ribeiro 
pcap_ts_read_xy(void * data,u16 res[2])380387e107SDaniel Ribeiro static void pcap_ts_read_xy(void *data, u16 res[2])
390387e107SDaniel Ribeiro {
400387e107SDaniel Ribeiro 	struct pcap_ts *pcap_ts = data;
410387e107SDaniel Ribeiro 
420387e107SDaniel Ribeiro 	switch (pcap_ts->read_state) {
430387e107SDaniel Ribeiro 	case PCAP_ADC_TS_M_PRESSURE:
440387e107SDaniel Ribeiro 		/* pressure reading is unreliable */
450387e107SDaniel Ribeiro 		if (res[0] > PRESSURE_MIN && res[0] < PRESSURE_MAX)
460387e107SDaniel Ribeiro 			pcap_ts->pressure = res[0];
470387e107SDaniel Ribeiro 		pcap_ts->read_state = PCAP_ADC_TS_M_XY;
480387e107SDaniel Ribeiro 		schedule_delayed_work(&pcap_ts->work, 0);
490387e107SDaniel Ribeiro 		break;
500387e107SDaniel Ribeiro 	case PCAP_ADC_TS_M_XY:
510387e107SDaniel Ribeiro 		pcap_ts->y = res[0];
520387e107SDaniel Ribeiro 		pcap_ts->x = res[1];
530387e107SDaniel Ribeiro 		if (pcap_ts->x <= X_AXIS_MIN || pcap_ts->x >= X_AXIS_MAX ||
540387e107SDaniel Ribeiro 		    pcap_ts->y <= Y_AXIS_MIN || pcap_ts->y >= Y_AXIS_MAX) {
550387e107SDaniel Ribeiro 			/* pen has been released */
560387e107SDaniel Ribeiro 			input_report_abs(pcap_ts->input, ABS_PRESSURE, 0);
570387e107SDaniel Ribeiro 			input_report_key(pcap_ts->input, BTN_TOUCH, 0);
580387e107SDaniel Ribeiro 
590387e107SDaniel Ribeiro 			pcap_ts->read_state = PCAP_ADC_TS_M_STANDBY;
600387e107SDaniel Ribeiro 			schedule_delayed_work(&pcap_ts->work, 0);
610387e107SDaniel Ribeiro 		} else {
620387e107SDaniel Ribeiro 			/* pen is touching the screen */
630387e107SDaniel Ribeiro 			input_report_abs(pcap_ts->input, ABS_X, pcap_ts->x);
640387e107SDaniel Ribeiro 			input_report_abs(pcap_ts->input, ABS_Y, pcap_ts->y);
650387e107SDaniel Ribeiro 			input_report_key(pcap_ts->input, BTN_TOUCH, 1);
660387e107SDaniel Ribeiro 			input_report_abs(pcap_ts->input, ABS_PRESSURE,
670387e107SDaniel Ribeiro 						pcap_ts->pressure);
680387e107SDaniel Ribeiro 
690387e107SDaniel Ribeiro 			/* switch back to pressure read mode */
700387e107SDaniel Ribeiro 			pcap_ts->read_state = PCAP_ADC_TS_M_PRESSURE;
710387e107SDaniel Ribeiro 			schedule_delayed_work(&pcap_ts->work,
720387e107SDaniel Ribeiro 					msecs_to_jiffies(SAMPLE_DELAY));
730387e107SDaniel Ribeiro 		}
740387e107SDaniel Ribeiro 		input_sync(pcap_ts->input);
750387e107SDaniel Ribeiro 		break;
760387e107SDaniel Ribeiro 	default:
770387e107SDaniel Ribeiro 		dev_warn(&pcap_ts->input->dev,
780387e107SDaniel Ribeiro 				"pcap_ts: Warning, unhandled read_state %d\n",
790387e107SDaniel Ribeiro 				pcap_ts->read_state);
800387e107SDaniel Ribeiro 		break;
810387e107SDaniel Ribeiro 	}
820387e107SDaniel Ribeiro }
830387e107SDaniel Ribeiro 
pcap_ts_work(struct work_struct * work)840387e107SDaniel Ribeiro static void pcap_ts_work(struct work_struct *work)
850387e107SDaniel Ribeiro {
86fa68e277SGeliang Tang 	struct delayed_work *dw = to_delayed_work(work);
870387e107SDaniel Ribeiro 	struct pcap_ts *pcap_ts = container_of(dw, struct pcap_ts, work);
880387e107SDaniel Ribeiro 	u8 ch[2];
890387e107SDaniel Ribeiro 
900387e107SDaniel Ribeiro 	pcap_set_ts_bits(pcap_ts->pcap,
910387e107SDaniel Ribeiro 			pcap_ts->read_state << PCAP_ADC_TS_M_SHIFT);
920387e107SDaniel Ribeiro 
930387e107SDaniel Ribeiro 	if (pcap_ts->read_state == PCAP_ADC_TS_M_STANDBY)
940387e107SDaniel Ribeiro 		return;
950387e107SDaniel Ribeiro 
960387e107SDaniel Ribeiro 	/* start adc conversion */
970387e107SDaniel Ribeiro 	ch[0] = PCAP_ADC_CH_TS_X1;
980387e107SDaniel Ribeiro 	ch[1] = PCAP_ADC_CH_TS_Y1;
990387e107SDaniel Ribeiro 	pcap_adc_async(pcap_ts->pcap, PCAP_ADC_BANK_1, 0, ch,
1000387e107SDaniel Ribeiro 						pcap_ts_read_xy, pcap_ts);
1010387e107SDaniel Ribeiro }
1020387e107SDaniel Ribeiro 
pcap_ts_event_touch(int pirq,void * data)1030387e107SDaniel Ribeiro static irqreturn_t pcap_ts_event_touch(int pirq, void *data)
1040387e107SDaniel Ribeiro {
1050387e107SDaniel Ribeiro 	struct pcap_ts *pcap_ts = data;
1060387e107SDaniel Ribeiro 
1070387e107SDaniel Ribeiro 	if (pcap_ts->read_state == PCAP_ADC_TS_M_STANDBY) {
1080387e107SDaniel Ribeiro 		pcap_ts->read_state = PCAP_ADC_TS_M_PRESSURE;
1090387e107SDaniel Ribeiro 		schedule_delayed_work(&pcap_ts->work, 0);
1100387e107SDaniel Ribeiro 	}
1110387e107SDaniel Ribeiro 	return IRQ_HANDLED;
1120387e107SDaniel Ribeiro }
1130387e107SDaniel Ribeiro 
pcap_ts_open(struct input_dev * dev)1140387e107SDaniel Ribeiro static int pcap_ts_open(struct input_dev *dev)
1150387e107SDaniel Ribeiro {
1160387e107SDaniel Ribeiro 	struct pcap_ts *pcap_ts = input_get_drvdata(dev);
1170387e107SDaniel Ribeiro 
1180387e107SDaniel Ribeiro 	pcap_ts->read_state = PCAP_ADC_TS_M_STANDBY;
1190387e107SDaniel Ribeiro 	schedule_delayed_work(&pcap_ts->work, 0);
1200387e107SDaniel Ribeiro 
1210387e107SDaniel Ribeiro 	return 0;
1220387e107SDaniel Ribeiro }
1230387e107SDaniel Ribeiro 
pcap_ts_close(struct input_dev * dev)1240387e107SDaniel Ribeiro static void pcap_ts_close(struct input_dev *dev)
1250387e107SDaniel Ribeiro {
1260387e107SDaniel Ribeiro 	struct pcap_ts *pcap_ts = input_get_drvdata(dev);
1270387e107SDaniel Ribeiro 
1280387e107SDaniel Ribeiro 	cancel_delayed_work_sync(&pcap_ts->work);
1290387e107SDaniel Ribeiro 
1300387e107SDaniel Ribeiro 	pcap_ts->read_state = PCAP_ADC_TS_M_NONTS;
1310387e107SDaniel Ribeiro 	pcap_set_ts_bits(pcap_ts->pcap,
1320387e107SDaniel Ribeiro 				pcap_ts->read_state << PCAP_ADC_TS_M_SHIFT);
1330387e107SDaniel Ribeiro }
1340387e107SDaniel Ribeiro 
pcap_ts_probe(struct platform_device * pdev)1355298cc4cSBill Pemberton static int pcap_ts_probe(struct platform_device *pdev)
1360387e107SDaniel Ribeiro {
1370387e107SDaniel Ribeiro 	struct input_dev *input_dev;
1380387e107SDaniel Ribeiro 	struct pcap_ts *pcap_ts;
1390387e107SDaniel Ribeiro 	int err = -ENOMEM;
1400387e107SDaniel Ribeiro 
1410387e107SDaniel Ribeiro 	pcap_ts = kzalloc(sizeof(*pcap_ts), GFP_KERNEL);
1420387e107SDaniel Ribeiro 	if (!pcap_ts)
1430387e107SDaniel Ribeiro 		return err;
1440387e107SDaniel Ribeiro 
1450387e107SDaniel Ribeiro 	pcap_ts->pcap = dev_get_drvdata(pdev->dev.parent);
1460387e107SDaniel Ribeiro 	platform_set_drvdata(pdev, pcap_ts);
1470387e107SDaniel Ribeiro 
1480387e107SDaniel Ribeiro 	input_dev = input_allocate_device();
1490387e107SDaniel Ribeiro 	if (!input_dev)
1500387e107SDaniel Ribeiro 		goto fail;
1510387e107SDaniel Ribeiro 
1520387e107SDaniel Ribeiro 	INIT_DELAYED_WORK(&pcap_ts->work, pcap_ts_work);
1530387e107SDaniel Ribeiro 
1540387e107SDaniel Ribeiro 	pcap_ts->read_state = PCAP_ADC_TS_M_NONTS;
1550387e107SDaniel Ribeiro 	pcap_set_ts_bits(pcap_ts->pcap,
1560387e107SDaniel Ribeiro 				pcap_ts->read_state << PCAP_ADC_TS_M_SHIFT);
1570387e107SDaniel Ribeiro 
1580387e107SDaniel Ribeiro 	pcap_ts->input = input_dev;
1590387e107SDaniel Ribeiro 	input_set_drvdata(input_dev, pcap_ts);
1600387e107SDaniel Ribeiro 
1610387e107SDaniel Ribeiro 	input_dev->name = "pcap-touchscreen";
1620387e107SDaniel Ribeiro 	input_dev->phys = "pcap_ts/input0";
1630387e107SDaniel Ribeiro 	input_dev->id.bustype = BUS_HOST;
1640387e107SDaniel Ribeiro 	input_dev->id.vendor = 0x0001;
1650387e107SDaniel Ribeiro 	input_dev->id.product = 0x0002;
1660387e107SDaniel Ribeiro 	input_dev->id.version = 0x0100;
1670387e107SDaniel Ribeiro 	input_dev->dev.parent = &pdev->dev;
1680387e107SDaniel Ribeiro 	input_dev->open = pcap_ts_open;
1690387e107SDaniel Ribeiro 	input_dev->close = pcap_ts_close;
1700387e107SDaniel Ribeiro 
1710387e107SDaniel Ribeiro 	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
1720387e107SDaniel Ribeiro 	input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
1730387e107SDaniel Ribeiro 	input_set_abs_params(input_dev, ABS_X, X_AXIS_MIN, X_AXIS_MAX, 0, 0);
1740387e107SDaniel Ribeiro 	input_set_abs_params(input_dev, ABS_Y, Y_AXIS_MIN, Y_AXIS_MAX, 0, 0);
1750387e107SDaniel Ribeiro 	input_set_abs_params(input_dev, ABS_PRESSURE, PRESSURE_MIN,
1760387e107SDaniel Ribeiro 			     PRESSURE_MAX, 0, 0);
1770387e107SDaniel Ribeiro 
1780387e107SDaniel Ribeiro 	err = input_register_device(pcap_ts->input);
1790387e107SDaniel Ribeiro 	if (err)
1800387e107SDaniel Ribeiro 		goto fail_allocate;
1810387e107SDaniel Ribeiro 
1820387e107SDaniel Ribeiro 	err = request_irq(pcap_to_irq(pcap_ts->pcap, PCAP_IRQ_TS),
1830387e107SDaniel Ribeiro 			pcap_ts_event_touch, 0, "Touch Screen", pcap_ts);
1840387e107SDaniel Ribeiro 	if (err)
1850387e107SDaniel Ribeiro 		goto fail_register;
1860387e107SDaniel Ribeiro 
1870387e107SDaniel Ribeiro 	return 0;
1880387e107SDaniel Ribeiro 
1890387e107SDaniel Ribeiro fail_register:
1900387e107SDaniel Ribeiro 	input_unregister_device(input_dev);
1910387e107SDaniel Ribeiro 	goto fail;
1920387e107SDaniel Ribeiro fail_allocate:
1930387e107SDaniel Ribeiro 	input_free_device(input_dev);
1940387e107SDaniel Ribeiro fail:
1950387e107SDaniel Ribeiro 	kfree(pcap_ts);
1960387e107SDaniel Ribeiro 
1970387e107SDaniel Ribeiro 	return err;
1980387e107SDaniel Ribeiro }
1990387e107SDaniel Ribeiro 
pcap_ts_remove(struct platform_device * pdev)200e2619cf7SBill Pemberton static int pcap_ts_remove(struct platform_device *pdev)
2010387e107SDaniel Ribeiro {
2020387e107SDaniel Ribeiro 	struct pcap_ts *pcap_ts = platform_get_drvdata(pdev);
2030387e107SDaniel Ribeiro 
2040387e107SDaniel Ribeiro 	free_irq(pcap_to_irq(pcap_ts->pcap, PCAP_IRQ_TS), pcap_ts);
2050387e107SDaniel Ribeiro 	cancel_delayed_work_sync(&pcap_ts->work);
2060387e107SDaniel Ribeiro 
2070387e107SDaniel Ribeiro 	input_unregister_device(pcap_ts->input);
2080387e107SDaniel Ribeiro 
2090387e107SDaniel Ribeiro 	kfree(pcap_ts);
2100387e107SDaniel Ribeiro 
2110387e107SDaniel Ribeiro 	return 0;
2120387e107SDaniel Ribeiro }
2130387e107SDaniel Ribeiro 
2140387e107SDaniel Ribeiro #ifdef CONFIG_PM
pcap_ts_suspend(struct device * dev)2150387e107SDaniel Ribeiro static int pcap_ts_suspend(struct device *dev)
2160387e107SDaniel Ribeiro {
2170387e107SDaniel Ribeiro 	struct pcap_ts *pcap_ts = dev_get_drvdata(dev);
2180387e107SDaniel Ribeiro 
2190387e107SDaniel Ribeiro 	pcap_set_ts_bits(pcap_ts->pcap, PCAP_ADC_TS_REF_LOWPWR);
2200387e107SDaniel Ribeiro 	return 0;
2210387e107SDaniel Ribeiro }
2220387e107SDaniel Ribeiro 
pcap_ts_resume(struct device * dev)2230387e107SDaniel Ribeiro static int pcap_ts_resume(struct device *dev)
2240387e107SDaniel Ribeiro {
2250387e107SDaniel Ribeiro 	struct pcap_ts *pcap_ts = dev_get_drvdata(dev);
2260387e107SDaniel Ribeiro 
2270387e107SDaniel Ribeiro 	pcap_set_ts_bits(pcap_ts->pcap,
2280387e107SDaniel Ribeiro 				pcap_ts->read_state << PCAP_ADC_TS_M_SHIFT);
2290387e107SDaniel Ribeiro 	return 0;
2300387e107SDaniel Ribeiro }
2310387e107SDaniel Ribeiro 
23247145210SAlexey Dobriyan static const struct dev_pm_ops pcap_ts_pm_ops = {
2330387e107SDaniel Ribeiro 	.suspend	= pcap_ts_suspend,
2340387e107SDaniel Ribeiro 	.resume		= pcap_ts_resume,
2350387e107SDaniel Ribeiro };
2360387e107SDaniel Ribeiro #define PCAP_TS_PM_OPS (&pcap_ts_pm_ops)
2370387e107SDaniel Ribeiro #else
2380387e107SDaniel Ribeiro #define PCAP_TS_PM_OPS NULL
2390387e107SDaniel Ribeiro #endif
2400387e107SDaniel Ribeiro 
2410387e107SDaniel Ribeiro static struct platform_driver pcap_ts_driver = {
2420387e107SDaniel Ribeiro 	.probe		= pcap_ts_probe,
2431cb0aa88SBill Pemberton 	.remove		= pcap_ts_remove,
2440387e107SDaniel Ribeiro 	.driver		= {
2450387e107SDaniel Ribeiro 		.name	= "pcap-ts",
2460387e107SDaniel Ribeiro 		.pm	= PCAP_TS_PM_OPS,
2470387e107SDaniel Ribeiro 	},
2480387e107SDaniel Ribeiro };
249cdcc96e2SJJ Ding module_platform_driver(pcap_ts_driver);
2500387e107SDaniel Ribeiro 
2510387e107SDaniel Ribeiro MODULE_DESCRIPTION("Motorola PCAP2 touchscreen driver");
2520387e107SDaniel Ribeiro MODULE_AUTHOR("Daniel Ribeiro / Harald Welte");
2530387e107SDaniel Ribeiro MODULE_LICENSE("GPL");
2540387e107SDaniel Ribeiro MODULE_ALIAS("platform:pcap_ts");
255