Root/
1 | /* |
2 | * Wacom W8001 penabled serial touchscreen driver |
3 | * |
4 | * Copyright (c) 2008 Jaya Kumar |
5 | * Copyright (c) 2010 Red Hat, Inc. |
6 | * Copyright (c) 2010 - 2011 Ping Cheng, Wacom. <pingc@wacom.com> |
7 | * |
8 | * This file is subject to the terms and conditions of the GNU General Public |
9 | * License. See the file COPYING in the main directory of this archive for |
10 | * more details. |
11 | * |
12 | * Layout based on Elo serial touchscreen driver by Vojtech Pavlik |
13 | */ |
14 | |
15 | #include <linux/errno.h> |
16 | #include <linux/kernel.h> |
17 | #include <linux/module.h> |
18 | #include <linux/slab.h> |
19 | #include <linux/input/mt.h> |
20 | #include <linux/serio.h> |
21 | #include <linux/init.h> |
22 | #include <linux/ctype.h> |
23 | #include <linux/delay.h> |
24 | |
25 | #define DRIVER_DESC "Wacom W8001 serial touchscreen driver" |
26 | |
27 | MODULE_AUTHOR("Jaya Kumar <jayakumar.lkml@gmail.com>"); |
28 | MODULE_DESCRIPTION(DRIVER_DESC); |
29 | MODULE_LICENSE("GPL"); |
30 | |
31 | #define W8001_MAX_LENGTH 11 |
32 | #define W8001_LEAD_MASK 0x80 |
33 | #define W8001_LEAD_BYTE 0x80 |
34 | #define W8001_TAB_MASK 0x40 |
35 | #define W8001_TAB_BYTE 0x40 |
36 | /* set in first byte of touch data packets */ |
37 | #define W8001_TOUCH_MASK (0x10 | W8001_LEAD_MASK) |
38 | #define W8001_TOUCH_BYTE (0x10 | W8001_LEAD_BYTE) |
39 | |
40 | #define W8001_QUERY_PACKET 0x20 |
41 | |
42 | #define W8001_CMD_STOP '0' |
43 | #define W8001_CMD_START '1' |
44 | #define W8001_CMD_QUERY '*' |
45 | #define W8001_CMD_TOUCHQUERY '%' |
46 | |
47 | /* length of data packets in bytes, depends on device. */ |
48 | #define W8001_PKTLEN_TOUCH93 5 |
49 | #define W8001_PKTLEN_TOUCH9A 7 |
50 | #define W8001_PKTLEN_TPCPEN 9 |
51 | #define W8001_PKTLEN_TPCCTL 11 /* control packet */ |
52 | #define W8001_PKTLEN_TOUCH2FG 13 |
53 | |
54 | /* resolution in points/mm */ |
55 | #define W8001_PEN_RESOLUTION 100 |
56 | #define W8001_TOUCH_RESOLUTION 10 |
57 | |
58 | struct w8001_coord { |
59 | u8 rdy; |
60 | u8 tsw; |
61 | u8 f1; |
62 | u8 f2; |
63 | u16 x; |
64 | u16 y; |
65 | u16 pen_pressure; |
66 | u8 tilt_x; |
67 | u8 tilt_y; |
68 | }; |
69 | |
70 | /* touch query reply packet */ |
71 | struct w8001_touch_query { |
72 | u16 x; |
73 | u16 y; |
74 | u8 panel_res; |
75 | u8 capacity_res; |
76 | u8 sensor_id; |
77 | }; |
78 | |
79 | /* |
80 | * Per-touchscreen data. |
81 | */ |
82 | |
83 | struct w8001 { |
84 | struct input_dev *dev; |
85 | struct serio *serio; |
86 | struct completion cmd_done; |
87 | int id; |
88 | int idx; |
89 | unsigned char response_type; |
90 | unsigned char response[W8001_MAX_LENGTH]; |
91 | unsigned char data[W8001_MAX_LENGTH]; |
92 | char phys[32]; |
93 | int type; |
94 | unsigned int pktlen; |
95 | u16 max_touch_x; |
96 | u16 max_touch_y; |
97 | u16 max_pen_x; |
98 | u16 max_pen_y; |
99 | char name[64]; |
100 | }; |
101 | |
102 | static void parse_pen_data(u8 *data, struct w8001_coord *coord) |
103 | { |
104 | memset(coord, 0, sizeof(*coord)); |
105 | |
106 | coord->rdy = data[0] & 0x20; |
107 | coord->tsw = data[0] & 0x01; |
108 | coord->f1 = data[0] & 0x02; |
109 | coord->f2 = data[0] & 0x04; |
110 | |
111 | coord->x = (data[1] & 0x7F) << 9; |
112 | coord->x |= (data[2] & 0x7F) << 2; |
113 | coord->x |= (data[6] & 0x60) >> 5; |
114 | |
115 | coord->y = (data[3] & 0x7F) << 9; |
116 | coord->y |= (data[4] & 0x7F) << 2; |
117 | coord->y |= (data[6] & 0x18) >> 3; |
118 | |
119 | coord->pen_pressure = data[5] & 0x7F; |
120 | coord->pen_pressure |= (data[6] & 0x07) << 7 ; |
121 | |
122 | coord->tilt_x = data[7] & 0x7F; |
123 | coord->tilt_y = data[8] & 0x7F; |
124 | } |
125 | |
126 | static void parse_single_touch(u8 *data, struct w8001_coord *coord) |
127 | { |
128 | coord->x = (data[1] << 7) | data[2]; |
129 | coord->y = (data[3] << 7) | data[4]; |
130 | coord->tsw = data[0] & 0x01; |
131 | } |
132 | |
133 | static void scale_touch_coordinates(struct w8001 *w8001, |
134 | unsigned int *x, unsigned int *y) |
135 | { |
136 | if (w8001->max_pen_x && w8001->max_touch_x) |
137 | *x = *x * w8001->max_pen_x / w8001->max_touch_x; |
138 | |
139 | if (w8001->max_pen_y && w8001->max_touch_y) |
140 | *y = *y * w8001->max_pen_y / w8001->max_touch_y; |
141 | } |
142 | |
143 | static void parse_multi_touch(struct w8001 *w8001) |
144 | { |
145 | struct input_dev *dev = w8001->dev; |
146 | unsigned char *data = w8001->data; |
147 | unsigned int x, y; |
148 | int i; |
149 | int count = 0; |
150 | |
151 | for (i = 0; i < 2; i++) { |
152 | bool touch = data[0] & (1 << i); |
153 | |
154 | input_mt_slot(dev, i); |
155 | input_mt_report_slot_state(dev, MT_TOOL_FINGER, touch); |
156 | if (touch) { |
157 | x = (data[6 * i + 1] << 7) | data[6 * i + 2]; |
158 | y = (data[6 * i + 3] << 7) | data[6 * i + 4]; |
159 | /* data[5,6] and [11,12] is finger capacity */ |
160 | |
161 | /* scale to pen maximum */ |
162 | scale_touch_coordinates(w8001, &x, &y); |
163 | |
164 | input_report_abs(dev, ABS_MT_POSITION_X, x); |
165 | input_report_abs(dev, ABS_MT_POSITION_Y, y); |
166 | count++; |
167 | } |
168 | } |
169 | |
170 | /* emulate single touch events when stylus is out of proximity. |
171 | * This is to make single touch backward support consistent |
172 | * across all Wacom single touch devices. |
173 | */ |
174 | if (w8001->type != BTN_TOOL_PEN && |
175 | w8001->type != BTN_TOOL_RUBBER) { |
176 | w8001->type = count == 1 ? BTN_TOOL_FINGER : KEY_RESERVED; |
177 | input_mt_report_pointer_emulation(dev, true); |
178 | } |
179 | |
180 | input_sync(dev); |
181 | } |
182 | |
183 | static void parse_touchquery(u8 *data, struct w8001_touch_query *query) |
184 | { |
185 | memset(query, 0, sizeof(*query)); |
186 | |
187 | query->panel_res = data[1]; |
188 | query->sensor_id = data[2] & 0x7; |
189 | query->capacity_res = data[7]; |
190 | |
191 | query->x = data[3] << 9; |
192 | query->x |= data[4] << 2; |
193 | query->x |= (data[2] >> 5) & 0x3; |
194 | |
195 | query->y = data[5] << 9; |
196 | query->y |= data[6] << 2; |
197 | query->y |= (data[2] >> 3) & 0x3; |
198 | |
199 | /* Early days' single-finger touch models need the following defaults */ |
200 | if (!query->x && !query->y) { |
201 | query->x = 1024; |
202 | query->y = 1024; |
203 | if (query->panel_res) |
204 | query->x = query->y = (1 << query->panel_res); |
205 | query->panel_res = W8001_TOUCH_RESOLUTION; |
206 | } |
207 | } |
208 | |
209 | static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord) |
210 | { |
211 | struct input_dev *dev = w8001->dev; |
212 | |
213 | /* |
214 | * We have 1 bit for proximity (rdy) and 3 bits for tip, side, |
215 | * side2/eraser. If rdy && f2 are set, this can be either pen + side2, |
216 | * or eraser. Assume: |
217 | * - if dev is already in proximity and f2 is toggled → pen + side2 |
218 | * - if dev comes into proximity with f2 set → eraser |
219 | * If f2 disappears after assuming eraser, fake proximity out for |
220 | * eraser and in for pen. |
221 | */ |
222 | |
223 | switch (w8001->type) { |
224 | case BTN_TOOL_RUBBER: |
225 | if (!coord->f2) { |
226 | input_report_abs(dev, ABS_PRESSURE, 0); |
227 | input_report_key(dev, BTN_TOUCH, 0); |
228 | input_report_key(dev, BTN_STYLUS, 0); |
229 | input_report_key(dev, BTN_STYLUS2, 0); |
230 | input_report_key(dev, BTN_TOOL_RUBBER, 0); |
231 | input_sync(dev); |
232 | w8001->type = BTN_TOOL_PEN; |
233 | } |
234 | break; |
235 | |
236 | case BTN_TOOL_FINGER: |
237 | input_report_key(dev, BTN_TOUCH, 0); |
238 | input_report_key(dev, BTN_TOOL_FINGER, 0); |
239 | input_sync(dev); |
240 | /* fall through */ |
241 | |
242 | case KEY_RESERVED: |
243 | w8001->type = coord->f2 ? BTN_TOOL_RUBBER : BTN_TOOL_PEN; |
244 | break; |
245 | |
246 | default: |
247 | input_report_key(dev, BTN_STYLUS2, coord->f2); |
248 | break; |
249 | } |
250 | |
251 | input_report_abs(dev, ABS_X, coord->x); |
252 | input_report_abs(dev, ABS_Y, coord->y); |
253 | input_report_abs(dev, ABS_PRESSURE, coord->pen_pressure); |
254 | input_report_key(dev, BTN_TOUCH, coord->tsw); |
255 | input_report_key(dev, BTN_STYLUS, coord->f1); |
256 | input_report_key(dev, w8001->type, coord->rdy); |
257 | input_sync(dev); |
258 | |
259 | if (!coord->rdy) |
260 | w8001->type = KEY_RESERVED; |
261 | } |
262 | |
263 | static void report_single_touch(struct w8001 *w8001, struct w8001_coord *coord) |
264 | { |
265 | struct input_dev *dev = w8001->dev; |
266 | unsigned int x = coord->x; |
267 | unsigned int y = coord->y; |
268 | |
269 | /* scale to pen maximum */ |
270 | scale_touch_coordinates(w8001, &x, &y); |
271 | |
272 | input_report_abs(dev, ABS_X, x); |
273 | input_report_abs(dev, ABS_Y, y); |
274 | input_report_key(dev, BTN_TOUCH, coord->tsw); |
275 | input_report_key(dev, BTN_TOOL_FINGER, coord->tsw); |
276 | |
277 | input_sync(dev); |
278 | |
279 | w8001->type = coord->tsw ? BTN_TOOL_FINGER : KEY_RESERVED; |
280 | } |
281 | |
282 | static irqreturn_t w8001_interrupt(struct serio *serio, |
283 | unsigned char data, unsigned int flags) |
284 | { |
285 | struct w8001 *w8001 = serio_get_drvdata(serio); |
286 | struct w8001_coord coord; |
287 | unsigned char tmp; |
288 | |
289 | w8001->data[w8001->idx] = data; |
290 | switch (w8001->idx++) { |
291 | case 0: |
292 | if ((data & W8001_LEAD_MASK) != W8001_LEAD_BYTE) { |
293 | pr_debug("w8001: unsynchronized data: 0x%02x\n", data); |
294 | w8001->idx = 0; |
295 | } |
296 | break; |
297 | |
298 | case W8001_PKTLEN_TOUCH93 - 1: |
299 | case W8001_PKTLEN_TOUCH9A - 1: |
300 | tmp = w8001->data[0] & W8001_TOUCH_BYTE; |
301 | if (tmp != W8001_TOUCH_BYTE) |
302 | break; |
303 | |
304 | if (w8001->pktlen == w8001->idx) { |
305 | w8001->idx = 0; |
306 | if (w8001->type != BTN_TOOL_PEN && |
307 | w8001->type != BTN_TOOL_RUBBER) { |
308 | parse_single_touch(w8001->data, &coord); |
309 | report_single_touch(w8001, &coord); |
310 | } |
311 | } |
312 | break; |
313 | |
314 | /* Pen coordinates packet */ |
315 | case W8001_PKTLEN_TPCPEN - 1: |
316 | tmp = w8001->data[0] & W8001_TAB_MASK; |
317 | if (unlikely(tmp == W8001_TAB_BYTE)) |
318 | break; |
319 | |
320 | tmp = w8001->data[0] & W8001_TOUCH_BYTE; |
321 | if (tmp == W8001_TOUCH_BYTE) |
322 | break; |
323 | |
324 | w8001->idx = 0; |
325 | parse_pen_data(w8001->data, &coord); |
326 | report_pen_events(w8001, &coord); |
327 | break; |
328 | |
329 | /* control packet */ |
330 | case W8001_PKTLEN_TPCCTL - 1: |
331 | tmp = w8001->data[0] & W8001_TOUCH_MASK; |
332 | if (tmp == W8001_TOUCH_BYTE) |
333 | break; |
334 | |
335 | w8001->idx = 0; |
336 | memcpy(w8001->response, w8001->data, W8001_MAX_LENGTH); |
337 | w8001->response_type = W8001_QUERY_PACKET; |
338 | complete(&w8001->cmd_done); |
339 | break; |
340 | |
341 | /* 2 finger touch packet */ |
342 | case W8001_PKTLEN_TOUCH2FG - 1: |
343 | w8001->idx = 0; |
344 | parse_multi_touch(w8001); |
345 | break; |
346 | } |
347 | |
348 | return IRQ_HANDLED; |
349 | } |
350 | |
351 | static int w8001_command(struct w8001 *w8001, unsigned char command, |
352 | bool wait_response) |
353 | { |
354 | int rc; |
355 | |
356 | w8001->response_type = 0; |
357 | init_completion(&w8001->cmd_done); |
358 | |
359 | rc = serio_write(w8001->serio, command); |
360 | if (rc == 0 && wait_response) { |
361 | |
362 | wait_for_completion_timeout(&w8001->cmd_done, HZ); |
363 | if (w8001->response_type != W8001_QUERY_PACKET) |
364 | rc = -EIO; |
365 | } |
366 | |
367 | return rc; |
368 | } |
369 | |
370 | static int w8001_open(struct input_dev *dev) |
371 | { |
372 | struct w8001 *w8001 = input_get_drvdata(dev); |
373 | |
374 | return w8001_command(w8001, W8001_CMD_START, false); |
375 | } |
376 | |
377 | static void w8001_close(struct input_dev *dev) |
378 | { |
379 | struct w8001 *w8001 = input_get_drvdata(dev); |
380 | |
381 | w8001_command(w8001, W8001_CMD_STOP, false); |
382 | } |
383 | |
384 | static int w8001_setup(struct w8001 *w8001) |
385 | { |
386 | struct input_dev *dev = w8001->dev; |
387 | struct w8001_coord coord; |
388 | struct w8001_touch_query touch; |
389 | int error; |
390 | |
391 | error = w8001_command(w8001, W8001_CMD_STOP, false); |
392 | if (error) |
393 | return error; |
394 | |
395 | msleep(250); /* wait 250ms before querying the device */ |
396 | |
397 | dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); |
398 | strlcat(w8001->name, "Wacom Serial", sizeof(w8001->name)); |
399 | |
400 | __set_bit(INPUT_PROP_DIRECT, dev->propbit); |
401 | |
402 | /* penabled? */ |
403 | error = w8001_command(w8001, W8001_CMD_QUERY, true); |
404 | if (!error) { |
405 | __set_bit(BTN_TOUCH, dev->keybit); |
406 | __set_bit(BTN_TOOL_PEN, dev->keybit); |
407 | __set_bit(BTN_TOOL_RUBBER, dev->keybit); |
408 | __set_bit(BTN_STYLUS, dev->keybit); |
409 | __set_bit(BTN_STYLUS2, dev->keybit); |
410 | |
411 | parse_pen_data(w8001->response, &coord); |
412 | w8001->max_pen_x = coord.x; |
413 | w8001->max_pen_y = coord.y; |
414 | |
415 | input_set_abs_params(dev, ABS_X, 0, coord.x, 0, 0); |
416 | input_set_abs_params(dev, ABS_Y, 0, coord.y, 0, 0); |
417 | input_abs_set_res(dev, ABS_X, W8001_PEN_RESOLUTION); |
418 | input_abs_set_res(dev, ABS_Y, W8001_PEN_RESOLUTION); |
419 | input_set_abs_params(dev, ABS_PRESSURE, 0, coord.pen_pressure, 0, 0); |
420 | if (coord.tilt_x && coord.tilt_y) { |
421 | input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0); |
422 | input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0); |
423 | } |
424 | w8001->id = 0x90; |
425 | strlcat(w8001->name, " Penabled", sizeof(w8001->name)); |
426 | } |
427 | |
428 | /* Touch enabled? */ |
429 | error = w8001_command(w8001, W8001_CMD_TOUCHQUERY, true); |
430 | |
431 | /* |
432 | * Some non-touch devices may reply to the touch query. But their |
433 | * second byte is empty, which indicates touch is not supported. |
434 | */ |
435 | if (!error && w8001->response[1]) { |
436 | __set_bit(BTN_TOUCH, dev->keybit); |
437 | __set_bit(BTN_TOOL_FINGER, dev->keybit); |
438 | |
439 | parse_touchquery(w8001->response, &touch); |
440 | w8001->max_touch_x = touch.x; |
441 | w8001->max_touch_y = touch.y; |
442 | |
443 | if (w8001->max_pen_x && w8001->max_pen_y) { |
444 | /* if pen is supported scale to pen maximum */ |
445 | touch.x = w8001->max_pen_x; |
446 | touch.y = w8001->max_pen_y; |
447 | touch.panel_res = W8001_PEN_RESOLUTION; |
448 | } |
449 | |
450 | input_set_abs_params(dev, ABS_X, 0, touch.x, 0, 0); |
451 | input_set_abs_params(dev, ABS_Y, 0, touch.y, 0, 0); |
452 | input_abs_set_res(dev, ABS_X, touch.panel_res); |
453 | input_abs_set_res(dev, ABS_Y, touch.panel_res); |
454 | |
455 | switch (touch.sensor_id) { |
456 | case 0: |
457 | case 2: |
458 | w8001->pktlen = W8001_PKTLEN_TOUCH93; |
459 | w8001->id = 0x93; |
460 | strlcat(w8001->name, " 1FG", sizeof(w8001->name)); |
461 | break; |
462 | |
463 | case 1: |
464 | case 3: |
465 | case 4: |
466 | w8001->pktlen = W8001_PKTLEN_TOUCH9A; |
467 | strlcat(w8001->name, " 1FG", sizeof(w8001->name)); |
468 | w8001->id = 0x9a; |
469 | break; |
470 | |
471 | case 5: |
472 | w8001->pktlen = W8001_PKTLEN_TOUCH2FG; |
473 | |
474 | input_mt_init_slots(dev, 2, 0); |
475 | input_set_abs_params(dev, ABS_MT_POSITION_X, |
476 | 0, touch.x, 0, 0); |
477 | input_set_abs_params(dev, ABS_MT_POSITION_Y, |
478 | 0, touch.y, 0, 0); |
479 | input_set_abs_params(dev, ABS_MT_TOOL_TYPE, |
480 | 0, MT_TOOL_MAX, 0, 0); |
481 | |
482 | strlcat(w8001->name, " 2FG", sizeof(w8001->name)); |
483 | if (w8001->max_pen_x && w8001->max_pen_y) |
484 | w8001->id = 0xE3; |
485 | else |
486 | w8001->id = 0xE2; |
487 | break; |
488 | } |
489 | } |
490 | |
491 | strlcat(w8001->name, " Touchscreen", sizeof(w8001->name)); |
492 | |
493 | return 0; |
494 | } |
495 | |
496 | /* |
497 | * w8001_disconnect() is the opposite of w8001_connect() |
498 | */ |
499 | |
500 | static void w8001_disconnect(struct serio *serio) |
501 | { |
502 | struct w8001 *w8001 = serio_get_drvdata(serio); |
503 | |
504 | serio_close(serio); |
505 | |
506 | input_unregister_device(w8001->dev); |
507 | kfree(w8001); |
508 | |
509 | serio_set_drvdata(serio, NULL); |
510 | } |
511 | |
512 | /* |
513 | * w8001_connect() is the routine that is called when someone adds a |
514 | * new serio device that supports the w8001 protocol and registers it as |
515 | * an input device. |
516 | */ |
517 | |
518 | static int w8001_connect(struct serio *serio, struct serio_driver *drv) |
519 | { |
520 | struct w8001 *w8001; |
521 | struct input_dev *input_dev; |
522 | int err; |
523 | |
524 | w8001 = kzalloc(sizeof(struct w8001), GFP_KERNEL); |
525 | input_dev = input_allocate_device(); |
526 | if (!w8001 || !input_dev) { |
527 | err = -ENOMEM; |
528 | goto fail1; |
529 | } |
530 | |
531 | w8001->serio = serio; |
532 | w8001->dev = input_dev; |
533 | init_completion(&w8001->cmd_done); |
534 | snprintf(w8001->phys, sizeof(w8001->phys), "%s/input0", serio->phys); |
535 | |
536 | serio_set_drvdata(serio, w8001); |
537 | err = serio_open(serio, drv); |
538 | if (err) |
539 | goto fail2; |
540 | |
541 | err = w8001_setup(w8001); |
542 | if (err) |
543 | goto fail3; |
544 | |
545 | input_dev->name = w8001->name; |
546 | input_dev->phys = w8001->phys; |
547 | input_dev->id.product = w8001->id; |
548 | input_dev->id.bustype = BUS_RS232; |
549 | input_dev->id.vendor = 0x056a; |
550 | input_dev->id.version = 0x0100; |
551 | input_dev->dev.parent = &serio->dev; |
552 | |
553 | input_dev->open = w8001_open; |
554 | input_dev->close = w8001_close; |
555 | |
556 | input_set_drvdata(input_dev, w8001); |
557 | |
558 | err = input_register_device(w8001->dev); |
559 | if (err) |
560 | goto fail3; |
561 | |
562 | return 0; |
563 | |
564 | fail3: |
565 | serio_close(serio); |
566 | fail2: |
567 | serio_set_drvdata(serio, NULL); |
568 | fail1: |
569 | input_free_device(input_dev); |
570 | kfree(w8001); |
571 | return err; |
572 | } |
573 | |
574 | static struct serio_device_id w8001_serio_ids[] = { |
575 | { |
576 | .type = SERIO_RS232, |
577 | .proto = SERIO_W8001, |
578 | .id = SERIO_ANY, |
579 | .extra = SERIO_ANY, |
580 | }, |
581 | { 0 } |
582 | }; |
583 | |
584 | MODULE_DEVICE_TABLE(serio, w8001_serio_ids); |
585 | |
586 | static struct serio_driver w8001_drv = { |
587 | .driver = { |
588 | .name = "w8001", |
589 | }, |
590 | .description = DRIVER_DESC, |
591 | .id_table = w8001_serio_ids, |
592 | .interrupt = w8001_interrupt, |
593 | .connect = w8001_connect, |
594 | .disconnect = w8001_disconnect, |
595 | }; |
596 | |
597 | module_serio_driver(w8001_drv); |
598 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9