Linux Audio

Check our new training course

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