1 /* Support for the ad7877 touchscreen chip attached to the htcapache
4 * (c) Copyright 2006 Kevin O'Connor <kevin@koconnor.net>
6 * This file may be distributed under the terms of the GNU GPL license.
9 #include <linux/interrupt.h>
10 #include <linux/input.h>
11 #include <linux/platform_device.h>
13 #include <asm/mach-types.h>
14 #include <asm/arch/ssp.h>
15 #include <asm/arch/hardware.h>
16 #include <asm/arch/pxa-regs.h>
17 #include <asm/arch/htcapache-gpio.h>
19 /****************************************************************
20 * AD7877 specific functions
21 ****************************************************************/
40 struct input_dev
*input
;
42 int (*ispendown
)(void);
46 u16 read_results_data
[AD7877_NR_SENSE
];
49 struct timer_list timer
;
61 #define TS_POLL_PERIOD msecs_to_jiffies(10)
63 // Build a command that writes to an ad7877 register
64 static inline int WRITE_REG(int reg
, int val
)
66 return (reg
<< 12) | val
;
69 // Send a command and drain the receive buffer.
71 ssp_putget(struct ad7877
*ts
, u32 data
)
74 ssp_write_word(&ts
->ssp
, data
);
75 ssp_read_word(&ts
->ssp
, &ret
);
79 // Send an event to the input layer.
81 report_event(struct ad7877
*ts
, int pressure
, int x
, int y
)
83 input_report_abs(ts
->input
, ABS_PRESSURE
, pressure
);
84 input_report_abs(ts
->input
, ABS_X
, x
);
85 input_report_abs(ts
->input
, ABS_Y
, y
);
86 input_sync(ts
->input
);
89 // Read the most recently reported values from the chip.
90 static void ad7877_readvals(void *data
)
92 struct ad7877
*ts
= data
;
93 u16
*regs
= ts
->read_results_data
;
94 u16 x
, y
, z1
, z2
, pendown
;
99 // Read sensed values from ad7877.
100 spin_lock_irqsave(&ts
->lock
, flag
);
101 ssp_putget(ts
, WRITE_REG(REG_CR1
, REG_SENSORS
<< CR1_OFF_READADDR
));
102 for (i
=0; i
<AD7877_NR_SENSE
; i
++)
103 ts
->read_results_data
[i
] = ssp_putget(ts
, 0);
105 // Calculate x, y, and pressure.
106 x
= regs
[AD7877_SEQ_XPOS
];
107 y
= regs
[AD7877_SEQ_YPOS
];
108 z1
= regs
[AD7877_SEQ_Z1
];
109 z2
= regs
[AD7877_SEQ_Z2
];
111 // RTOUCH = (RXPlate) x (XPOSITION /4096) x [(Z2/Z1) - 1]
116 Rt
*= ts
->x_plate_ohms
;
121 pendown
= ts
->ispendown();
123 // printk(KERN_NOTICE "TS: pen=%d x=%d y=%d Rt=%d (z1=%d z2=%d)\n"
124 // , pendown, x, y, Rt, z1, z2);
126 if (!pendown
|| Rt
< ts
->min_pressure
) {
127 // Pen no longer down.
128 if (ts
->sent_pen_down
) {
129 // Send pen up event.
130 report_event(ts
, 0, 0, 0);
131 ts
->sent_pen_down
= 0;
134 // Pen down - arrange for events until pen up again.
135 ts
->sent_pen_down
= 1;
136 mod_timer(&ts
->timer
, jiffies
+ TS_POLL_PERIOD
);
137 report_event(ts
, Rt
, x
, y
);
140 spin_unlock_irqrestore(&ts
->lock
, flag
);
143 // Instruct the ad7877 to sense new values.
145 startSense(struct ad7877
*ts
)
147 ssp_putget(ts
, WRITE_REG(REG_CR1
, CR1_SLAVEMODE
));
150 // Periodic callback while pen is down.
151 static void ad7877_timer(unsigned long handle
)
153 struct ad7877
*ts
= (void *)handle
;
154 spin_lock(&ts
->lock
);
156 spin_unlock(&ts
->lock
);
159 // Interrupt handler for when pen is first depressed.
160 static irqreturn_t
ad7877_penirq(int irq
, void *handle
, struct pt_regs
*regs
)
162 struct ad7877
*ts
= handle
;
163 spin_lock(&ts
->lock
);
164 if (! ts
->sent_pen_down
)
166 spin_unlock(&ts
->lock
);
170 // Interrupt handler for when sensing is complete.
171 static irqreturn_t
ad7877_davirq(int irq
, void *handle
, struct pt_regs
*regs
)
173 struct ad7877
*ts
= handle
;
181 static irqreturn_t
ad7877_alertirq(int irq
, void *handle
, struct pt_regs
*regs
)
183 struct ad7877
*ts
= handle
;
185 spin_lock(&ts
->lock
);
186 spin_unlock(&ts
->lock
);
192 /****************************************************************
193 * HTCApache specific functions
194 ****************************************************************/
196 #define GPLR_BIT(n) (GPLR((n)) & GPIO_bit((n)))
198 // Return true if the pen is currently down.
202 return !GPLR_BIT(GPIO_NR_HTCAPACHE_TS_PENDOWN
);
205 #define MAX_12BIT ((1<<12)-1)
211 err
= request_irq(IRQ_GPIO(GPIO_NR_HTCAPACHE_TS_PENDOWN
)
212 , ad7877_penirq
, SA_TRIGGER_FALLING
213 , "ad7877-pendown", d
);
216 err
= request_irq(IRQ_GPIO(GPIO_NR_HTCAPACHE_TS_DAV
)
217 , ad7877_davirq
, SA_TRIGGER_FALLING
220 free_irq(IRQ_GPIO(GPIO_NR_HTCAPACHE_TS_PENDOWN
), d
);
226 err
= request_irq(IRQ_GPIO(GPIO_NR_HTCAPACHE_TS_ALERT
)
227 , ad7877_alertirq
, SA_TRIGGER_FALLING
228 , "ad7877-alert", d
);
230 free_irq(IRQ_GPIO(GPIO_NR_HTCAPACHE_TS_PENDOWN
), d
);
231 free_irq(IRQ_GPIO(GPIO_NR_HTCAPACHE_TS_DAV
), d
);
240 free_irq(IRQ_GPIO(GPIO_NR_HTCAPACHE_TS_PENDOWN
), d
);
241 free_irq(IRQ_GPIO(GPIO_NR_HTCAPACHE_TS_DAV
), d
);
243 free_irq(IRQ_GPIO(GPIO_NR_HTCAPACHE_TS_ALERT
), d
);
246 static int __init
ts_ssp_probe(struct device
*dev
)
249 struct input_dev
*input_dev
;
252 // Initialize ts data structure.
253 ts
= kzalloc(sizeof(*ts
), GFP_KERNEL
);
257 ret
= ts_initirq(ts
);
263 ret
= ssp_init(&ts
->ssp
, 1, 0);
265 printk(KERN_ERR
"Unable to register SSP handler!\n");
270 dev
->driver_data
= ts
;
272 ts
->ispendown
= checkPenDown
;
273 ts
->x_plate_ohms
= 400; // XXX - don't know real value.
274 ts
->min_pressure
= 1; // XXX - don't know real value.
276 init_timer(&ts
->timer
);
277 ts
->timer
.data
= (unsigned long) ts
;
278 ts
->timer
.function
= ad7877_timer
;
280 spin_lock_init(&ts
->lock
);
282 // Initialize input device.
283 input_dev
= input_allocate_device();
284 input_dev
->name
= "AD7877 Touchscreen";
285 set_bit(EV_KEY
, input_dev
->evbit
);
286 set_bit(EV_ABS
, input_dev
->evbit
);
287 set_bit(ABS_X
, input_dev
->absbit
);
288 set_bit(ABS_Y
, input_dev
->absbit
);
289 set_bit(ABS_PRESSURE
, input_dev
->absbit
);
290 input_set_abs_params(input_dev
, ABS_X
, 0, MAX_12BIT
, 0, 0);
291 input_set_abs_params(input_dev
, ABS_Y
, 0, MAX_12BIT
, 0, 0);
292 input_set_abs_params(input_dev
, ABS_PRESSURE
, 0, MAX_12BIT
, 0, 0);
293 ts
->input
= input_dev
;
294 input_register_device(input_dev
);
299 static int ts_ssp_remove(struct device
*dev
)
301 struct ad7877
*ts
= dev
->driver_data
;
303 del_timer_sync(&ts
->timer
);
308 input_unregister_device(ts
->input
);
314 static struct device_driver ts_ssp_driver
= {
315 .name
= "htcapache-ad7877",
316 .bus
= &platform_bus_type
,
317 .probe
= ts_ssp_probe
,
318 .remove
= ts_ssp_remove
,
321 static int __init
ts_ssp_init(void)
323 if (!machine_is_htcapache())
326 return driver_register(&ts_ssp_driver
);
329 static void __exit
ts_ssp_exit(void)
331 driver_unregister(&ts_ssp_driver
);
334 module_init(ts_ssp_init
)
335 module_exit(ts_ssp_exit
)
337 MODULE_LICENSE("GPL");