// SPDX-License-Identifier: GPL-2.0-only /* * Touchscreen driver for UCB1x00-based touchscreens * * Copyright (C) 2001 Russell King, All Rights Reserved. * Copyright (C) 2005 Pavel Machek * * 21-Jan-2002 <jco@ict.es> : * * Added support for synchronous A/D mode. This mode is useful to * avoid noise induced in the touchpanel by the LCD, provided that * the UCB1x00 has a valid LCD sync signal routed to its ADCSYNC pin. * It is important to note that the signal connected to the ADCSYNC * pin should provide pulses even when the LCD is blanked, otherwise * a pen touch needed to unblank the LCD will never be read.
*/ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/sched.h> #include <linux/spinlock.h> #include <linux/completion.h> #include <linux/delay.h> #include <linux/string.h> #include <linux/input.h> #include <linux/device.h> #include <linux/freezer.h> #include <linux/slab.h> #include <linux/kthread.h> #include <linux/mfd/ucb1x00.h>
/* * Switch to pressure mode, and read pressure. We don't need to wait * here, since both plates are being driven.
*/ staticinlineunsignedint ucb1x00_ts_read_pressure(struct ucb1x00_ts *ts)
{ if (machine_is_collie()) {
ucb1x00_io_write(ts->ucb, COLLIE_TC35143_GPIO_TBL_CHK, 0);
ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
UCB_TS_CR_TSPX_POW | UCB_TS_CR_TSMX_POW |
UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
/* * Switch to X position mode and measure Y plate. We switch the plate * configuration in pressure mode, then switch to position mode. This * gives a faster response time. Even so, we need to wait about 55us * for things to stabilise.
*/ staticinlineunsignedint ucb1x00_ts_read_xpos(struct ucb1x00_ts *ts)
{ if (machine_is_collie())
ucb1x00_io_write(ts->ucb, 0, COLLIE_TC35143_GPIO_TBL_CHK); else {
ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
}
ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
/* * Switch to Y position mode and measure X plate. We switch the plate * configuration in pressure mode, then switch to position mode. This * gives a faster response time. Even so, we need to wait about 55us * for things to stabilise.
*/ staticinlineunsignedint ucb1x00_ts_read_ypos(struct ucb1x00_ts *ts)
{ if (machine_is_collie())
ucb1x00_io_write(ts->ucb, 0, COLLIE_TC35143_GPIO_TBL_CHK); else {
ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
}
/* * This is a RT kernel thread that handles the ADC accesses * (mainly so we can use semaphores in the UCB1200 core code * to serialise accesses to the ADC).
*/ staticint ucb1x00_thread(void *_ts)
{ struct ucb1x00_ts *ts = _ts;
DECLARE_WAITQUEUE(wait, current); bool frozen, ignore = false; int valid = 0;
set_freezable();
add_wait_queue(&ts->irq_wait, &wait); while (!kthread_freezable_should_stop(&frozen)) { unsignedint x, y, p; signedlong timeout;
if (frozen)
ignore = true;
ucb1x00_adc_enable(ts->ucb);
x = ucb1x00_ts_read_xpos(ts);
y = ucb1x00_ts_read_ypos(ts);
p = ucb1x00_ts_read_pressure(ts);
/* * Switch back to interrupt mode.
*/
ucb1x00_ts_mode_int(ts);
ucb1x00_adc_disable(ts->ucb);
msleep(10);
ucb1x00_enable(ts->ucb);
if (ucb1x00_ts_pen_down(ts)) {
set_current_state(TASK_INTERRUPTIBLE);
/* * Filtering is policy. Policy belongs in user * space. We therefore leave it to user space * to do any filtering they please.
*/ if (!ignore) {
ucb1x00_ts_evt_add(ts, p, x, y);
valid = 1;
}
/* * We only detect touch screen _touches_ with this interrupt * handler, and even then we just schedule our task.
*/ static irqreturn_t ucb1x00_ts_irq(int irq, void *id)
{ struct ucb1x00_ts *ts = id;
staticint ucb1x00_ts_open(struct input_dev *idev)
{ struct ucb1x00_ts *ts = input_get_drvdata(idev); unsignedlong flags = 0; int ret = 0;
BUG_ON(ts->rtask);
if (machine_is_collie())
flags = IRQF_TRIGGER_RISING; else
flags = IRQF_TRIGGER_FALLING;
ts->irq_disabled = 0;
init_waitqueue_head(&ts->irq_wait);
ret = request_irq(ts->ucb->irq_base + UCB_IRQ_TSPX, ucb1x00_ts_irq,
flags, "ucb1x00-ts", ts); if (ret < 0) goto out;
/* * If we do this at all, we should allow the user to * measure and read the X and Y resistance at any time.
*/
ucb1x00_adc_enable(ts->ucb);
ts->x_res = ucb1x00_ts_read_xres(ts);
ts->y_res = ucb1x00_ts_read_yres(ts);
ucb1x00_adc_disable(ts->ucb);
ts->rtask = kthread_run(ucb1x00_thread, ts, "ktsd"); if (!IS_ERR(ts->rtask)) {
ret = 0;
} else {
free_irq(ts->ucb->irq_base + UCB_IRQ_TSPX, ts);
ts->rtask = NULL;
ret = -EFAULT;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.