* Patch by Dan Poirot, 06 Jul 2004:
[platform/kernel/u-boot.git] / board / netphone / phone_console.c
1 /*
2  * (C) Copyright 2004 Intracom S.A.
3  * Pantelis Antoniou <panto@intracom.gr>
4  *
5  * See file CREDITS for list of people who contributed to this
6  * project.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of
11  * the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21  * MA 02111-1307 USA
22  */
23
24 /*
25  * phone_console.c
26  *
27  * A phone based console
28  *
29  * Virtual display of 80x24 characters.
30  * The actual display is much smaller and panned to show the virtual one.
31  * Input is made by a numeric keypad utilizing the input method of
32  * mobile phones. Sorry no T9 lexicons...
33  *
34  */
35
36 #include <common.h>
37
38 #include <version.h>
39 #include <linux/types.h>
40 #include <devices.h>
41
42 #include <sed156x.h>
43
44 /*************************************************************************************************/
45
46 #define ROWS    24
47 #define COLS    80
48
49 #define REFRESH_HZ              (CFG_HZ/50)     /* refresh every 20ms */
50 #define BLINK_HZ                (CFG_HZ/2)      /* cursor blink every 500ms */
51
52 /*************************************************************************************************/
53
54 #define DISPLAY_BACKLIT_PORT    ((volatile immap_t *)CFG_IMMR)->im_ioport.iop_pcdat
55 #define DISPLAY_BACKLIT_MASK    0x0010
56
57 /*************************************************************************************************/
58
59 #define KP_STABLE_HZ            (CFG_HZ/100)    /* stable for 10ms */
60 #define KP_REPEAT_DELAY_HZ      (CFG_HZ/4)      /* delay before repeat 250ms */
61 #define KP_REPEAT_HZ            (CFG_HZ/20)     /* repeat every 50ms */
62 #define KP_FORCE_DELAY_HZ       (CFG_HZ/2)      /* key was force pressed */
63 #define KP_IDLE_DELAY_HZ        (CFG_HZ/2)      /* key was released and idle */
64
65 #if CONFIG_NETPHONE_VERSION == 1
66 #define KP_SPI_RXD_PORT (((volatile immap_t *)CFG_IMMR)->im_ioport.iop_pcdat)
67 #define KP_SPI_RXD_MASK 0x0008
68
69 #define KP_SPI_TXD_PORT (((volatile immap_t *)CFG_IMMR)->im_ioport.iop_pcdat)
70 #define KP_SPI_TXD_MASK 0x0004
71
72 #define KP_SPI_CLK_PORT (((volatile immap_t *)CFG_IMMR)->im_ioport.iop_pcdat)
73 #define KP_SPI_CLK_MASK 0x0001
74 #elif CONFIG_NETPHONE_VERSION == 2
75 #define KP_SPI_RXD_PORT (((volatile immap_t *)CFG_IMMR)->im_cpm.cp_pbdat)
76 #define KP_SPI_RXD_MASK 0x00000008
77
78 #define KP_SPI_TXD_PORT (((volatile immap_t *)CFG_IMMR)->im_cpm.cp_pbdat)
79 #define KP_SPI_TXD_MASK 0x00000004
80
81 #define KP_SPI_CLK_PORT (((volatile immap_t *)CFG_IMMR)->im_cpm.cp_pbdat)
82 #define KP_SPI_CLK_MASK 0x00000002
83 #endif
84
85 #define KP_CS_PORT      (((volatile immap_t *)CFG_IMMR)->im_cpm.cp_pedat)
86 #define KP_CS_MASK      0x00000010
87
88 #define KP_SPI_RXD() (KP_SPI_RXD_PORT & KP_SPI_RXD_MASK)
89
90 #define KP_SPI_TXD(x) \
91         do { \
92                 if (x) \
93                         KP_SPI_TXD_PORT |=  KP_SPI_TXD_MASK; \
94                 else \
95                         KP_SPI_TXD_PORT &= ~KP_SPI_TXD_MASK; \
96         } while(0)
97
98 #define KP_SPI_CLK(x) \
99         do { \
100                 if (x) \
101                         KP_SPI_CLK_PORT |=  KP_SPI_CLK_MASK; \
102                 else \
103                         KP_SPI_CLK_PORT &= ~KP_SPI_CLK_MASK; \
104         } while(0)
105
106 #define KP_SPI_CLK_TOGGLE() (KP_SPI_CLK_PORT ^= KP_SPI_CLK_MASK)
107
108 #define KP_SPI_BIT_DELAY()      /* no delay */
109
110 #define KP_CS(x) \
111         do { \
112                 if (x) \
113                         KP_CS_PORT |=  KP_CS_MASK; \
114                 else \
115                         KP_CS_PORT &= ~KP_CS_MASK; \
116         } while(0)
117
118 #define KP_ROWS 7
119 #define KP_COLS 4
120
121 #define KP_ROWS_MASK    ((1 << KP_ROWS) - 1)
122 #define KP_COLS_MASK    ((1 << KP_COLS) - 1)
123
124 #define SCAN            0
125 #define SCAN_FILTER     1
126 #define SCAN_COL        2
127 #define SCAN_COL_FILTER 3
128 #define PRESSED         4
129
130 #define KP_F1   0       /* leftmost dot (tab)   */
131 #define KP_F2   1       /* middle left dot      */
132 #define KP_F3   2       /* up                   */
133 #define KP_F4   3       /* middle right dot     */
134 #define KP_F5   4       /* rightmost dot        */
135 #define KP_F6   5       /* C                    */
136 #define KP_F7   6       /* left                 */
137 #define KP_F8   7       /* down                 */
138 #define KP_F9   8       /* right                */
139 #define KP_F10  9       /* enter                */
140 #define KP_F11  10      /* R                    */
141 #define KP_F12  11      /* save                 */
142 #define KP_F13  12      /* redial               */
143 #define KP_F14  13      /* speaker              */
144 #define KP_F15  14      /* unused               */
145 #define KP_F16  15      /* unused               */
146
147 #define KP_RELEASE              -1      /* key depressed                                */
148 #define KP_FORCE                -2      /* key was pressed for more than force hz       */
149 #define KP_IDLE                 -3      /* key was released and idle                    */
150
151 #define KP_1    '1'
152 #define KP_2    '2'
153 #define KP_3    '3'
154 #define KP_4    '4'
155 #define KP_5    '5'
156 #define KP_6    '6'
157 #define KP_7    '7'
158 #define KP_8    '8'
159 #define KP_9    '9'
160 #define KP_0    '0'
161 #define KP_STAR '*'
162 #define KP_HASH '#'
163
164 /*************************************************************************************************/
165
166 static int curs_disabled;
167 static int curs_col, curs_row;
168 static int disp_col, disp_row;
169
170 static int width, height;
171
172 /* the simulated vty buffer */
173 static char vty_buf[ROWS * COLS];
174 static char last_visible_buf[ROWS * COLS];      /* worst case */
175 static char *last_visible_curs_ptr;
176 static int last_visible_curs_rev;
177 static int blinked_state;
178 static int last_input_mode;
179 static int refresh_time;
180 static int blink_time;
181 static char last_fast_punct;
182
183 /*************************************************************************************************/
184
185 #define IM_SMALL        0
186 #define IM_CAPITAL      1
187 #define IM_NUMBER       2
188
189 static int input_mode;
190 static char fast_punct;
191 static int tab_indicator;
192 static const char *fast_punct_list = ",.:;*";
193
194 static const char *input_mode_txt[] = { "abc", "ABC", "123" };
195
196 static const char *punct = ".,!;?'\"-()@/:_+&%*=<>$[]{}\\~^#|";
197 static const char *whspace = " 0\n";
198 /* per mode character select (for 2-9) */
199 static const char *digits_sel[2][8] = {
200         {       /* small */
201                 "abc2",                                 /* 2 */
202                 "def3",                                 /* 3 */
203                 "ghi4",                                 /* 4 */
204                 "jkl5",                                 /* 5 */
205                 "mno6",                                 /* 6 */
206                 "pqrs7",                                /* 7 */
207                 "tuv8",                                 /* 8 */
208                 "wxyz9",                                /* 9 */
209         }, {    /* capital */
210                 "ABC2",                                 /* 2 */
211                 "DEF3",                                 /* 3 */
212                 "GHI4",                                 /* 4 */
213                 "JKL5",                                 /* 5 */
214                 "MNO6",                                 /* 6 */
215                 "PQRS7",                                /* 7 */
216                 "TUV8",                                 /* 8 */
217                 "WXYZ9",                                /* 9 */
218         }
219 };
220
221 /*****************************************************************************/
222
223 static void update(void);
224 static void ensure_visible(int col, int row, int dx, int dy);
225
226 static void console_init(void)
227 {
228         curs_disabled = 0;
229         curs_col = 0;
230         curs_row = 0;
231
232         disp_col = 0;
233         disp_row = 0;
234
235         input_mode = IM_SMALL;
236         fast_punct = ',';
237         last_fast_punct = '\0';
238         refresh_time = REFRESH_HZ;
239         blink_time = BLINK_HZ;
240
241         memset(vty_buf, ' ', sizeof(vty_buf));
242
243         memset(last_visible_buf, ' ', sizeof(last_visible_buf));
244         last_visible_curs_ptr = NULL;
245         last_input_mode = -1;
246         last_visible_curs_rev = 0;
247
248         blinked_state = 0;
249
250         sed156x_init();
251         width = sed156x_text_width;
252         height = sed156x_text_height - 1;
253
254         tab_indicator = 0;
255 }
256
257 /*****************************************************************************/
258
259 void phone_putc(const char c);
260
261 /*****************************************************************************/
262
263 static int  queued_char = -1;
264 static int  enabled = 0;
265
266 /*****************************************************************************/
267
268 /* flush buffers */
269 int phone_start(void)
270 {
271         console_init();
272
273         update();
274         sed156x_sync();
275
276         enabled = 1;
277         queued_char = 'U' - '@';
278
279         /* backlit on */
280         DISPLAY_BACKLIT_PORT &= ~DISPLAY_BACKLIT_MASK;
281
282         return 0;
283 }
284
285 int phone_stop(void)
286 {
287         enabled = 0;
288
289         sed156x_clear();
290         sed156x_sync();
291
292         /* backlit off */
293         DISPLAY_BACKLIT_PORT |= DISPLAY_BACKLIT_MASK;
294
295         return 0;
296 }
297
298 void phone_puts(const char *s)
299 {
300         int count = strlen(s);
301
302         while (count--)
303                 phone_putc(*s++);
304 }
305
306 int phone_tstc(void)
307 {
308         return queued_char >= 0 ? 1 : 0;
309 }
310
311 int phone_getc(void)
312 {
313         int r;
314
315         if (queued_char < 0)
316                 return -1;
317
318         r = queued_char;
319         queued_char = -1;
320
321         return r;
322 }
323
324 /*****************************************************************************/
325
326 int drv_phone_init(void)
327 {
328         device_t console_dev;
329         char *penv;
330
331         console_init();
332
333         memset(&console_dev, 0, sizeof(console_dev));
334         strcpy(console_dev.name, "phone");
335         console_dev.ext = DEV_EXT_VIDEO;        /* Video extensions */
336         console_dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM;
337         console_dev.start = phone_start;
338         console_dev.stop = phone_stop;
339         console_dev.putc = phone_putc;  /* 'putc' function */
340         console_dev.puts = phone_puts;  /* 'puts' function */
341         console_dev.tstc = phone_tstc;  /* 'tstc' function */
342         console_dev.getc = phone_getc;  /* 'getc' function */
343
344         if (device_register(&console_dev) == 0)
345                 return 1;
346
347         return 0;
348 }
349
350 static int use_me;
351
352 int drv_phone_use_me(void)
353 {
354         return use_me;
355 }
356
357 static void kp_do_poll(void);
358
359 void phone_console_do_poll(void)
360 {
361         int i, x, y;
362
363         kp_do_poll();
364
365         if (enabled) {
366                 /* do the blink */
367                 blink_time -= PHONE_CONSOLE_POLL_HZ;
368                 if (blink_time <= 0) {
369                         blink_time += BLINK_HZ;
370                         if (last_visible_curs_ptr) {
371                                 i = last_visible_curs_ptr - last_visible_buf;
372                                 x = i % width; y = i / width;
373                                 sed156x_reverse_at(x, y, 1);
374                                 last_visible_curs_rev ^= 1;
375                         }
376                 }
377
378                 /* do the refresh */
379                 refresh_time -= PHONE_CONSOLE_POLL_HZ;
380                 if (refresh_time <= 0) {
381                         refresh_time += REFRESH_HZ;
382                         sed156x_sync();
383                 }
384         }
385
386 }
387
388 static int last_scancode = -1;
389 static int forced_scancode = 0;
390 static int input_state = -1;
391 static int input_scancode = -1;
392 static int input_selected_char = -1;
393 static char input_covered_char;
394
395 static void putchar_at_cursor(char c)
396 {
397         vty_buf[curs_row * COLS + curs_col] = c;
398         ensure_visible(curs_col, curs_row, 1, 1);
399 }
400
401 static char getchar_at_cursor(void)
402 {
403         return vty_buf[curs_row * COLS + curs_col];
404 }
405
406 static void queue_input_char(char c)
407 {
408         if (c <= 0)
409                 return;
410
411         queued_char = c;
412 }
413
414 static void terminate_input(void)
415 {
416         if (input_state < 0)
417                 return;
418
419         if (input_selected_char >= 0)
420                 queue_input_char(input_selected_char);
421
422         input_state = -1;
423         input_selected_char = -1;
424         putchar_at_cursor(input_covered_char);
425
426         curs_disabled = 0;
427         blink_time = BLINK_HZ;
428         update();
429 }
430
431 static void handle_enabled_scancode(int scancode)
432 {
433         char c;
434         int new_disp_col, new_disp_row;
435         const char *sel;
436
437
438         switch (scancode) {
439
440                         /* key was released */
441                 case KP_RELEASE:
442                         forced_scancode = 0;
443                         break;
444
445                         /* key was forced */
446                 case KP_FORCE:
447
448                         switch (last_scancode) {
449                                 case '#':
450                                         if (input_mode == IM_NUMBER) {
451                                                 input_mode = IM_CAPITAL;
452                                                 /* queue backspace to erase # */
453                                                 queue_input_char('\b');
454                                         } else {
455                                                 input_mode = IM_NUMBER;
456                                                 fast_punct = '*';
457                                         }
458                                         update();
459                                         break;
460
461                                 case '0': case '1':
462                                 case '2': case '3': case '4': case '5':
463                                 case '6': case '7': case '8': case '9':
464
465                                         if (input_state < 0)
466                                                 break;
467
468                                         input_selected_char = last_scancode;
469                                         putchar_at_cursor((char)input_selected_char);
470                                         terminate_input();
471
472                                         break;
473
474                                 default:
475                                         break;
476                         }
477
478                         break;
479
480                         /* release and idle */
481                 case KP_IDLE:
482                         input_scancode = -1;
483                         if (input_state < 0)
484                                 break;
485                         terminate_input();
486                         break;
487
488                         /* change input mode */
489                 case '#':
490                         if (last_scancode == '#')       /* no repeat */
491                                 break;
492
493                         if (input_mode == IM_NUMBER) {
494                                 input_scancode = scancode;
495                                 input_state = 0;
496                                 input_selected_char = scancode;
497                                 input_covered_char = getchar_at_cursor();
498                                 putchar_at_cursor((char)input_selected_char);
499                                 terminate_input();
500                                 break;
501                         }
502
503                         if (input_mode == IM_SMALL)
504                                 input_mode = IM_CAPITAL;
505                         else
506                                 input_mode = IM_SMALL;
507
508                         update();
509                         break;
510
511                 case '*':
512                         /* no repeat */
513                         if (last_scancode == scancode)
514                                 break;
515
516                         if (input_state >= 0)
517                                 terminate_input();
518
519                         input_scancode = fast_punct;
520                         input_state = 0;
521                         input_selected_char = input_scancode;
522                         input_covered_char = getchar_at_cursor();
523                         putchar_at_cursor((char)input_selected_char);
524                         terminate_input();
525
526                         break;
527
528                 case '0': case '1':
529                 case '2': case '3': case '4': case '5':
530                 case '6': case '7': case '8': case '9':
531
532                         /* no repeat */
533                         if (last_scancode == scancode)
534                                 break;
535
536                         if (input_mode == IM_NUMBER) {
537                                 input_scancode = scancode;
538                                 input_state = 0;
539                                 input_selected_char = scancode;
540                                 input_covered_char = getchar_at_cursor();
541                                 putchar_at_cursor((char)input_selected_char);
542                                 terminate_input();
543                                 break;
544                         }
545
546                         if (input_state >= 0 && input_scancode != scancode)
547                                 terminate_input();
548
549                         if (input_state < 0) {
550                                 curs_disabled = 1;
551                                 input_scancode = scancode;
552                                 input_state = 0;
553                                 input_covered_char = getchar_at_cursor();
554                         } else
555                                 input_state++;
556
557                         if (scancode == '0')
558                                 sel = whspace;
559                         else if (scancode == '1')
560                                 sel = punct;
561                         else
562                                 sel = digits_sel[input_mode][scancode - '2'];
563                         c = *(sel + input_state);
564                         if (c == '\0') {
565                                 input_state = 0;
566                                 c = *sel;
567                         }
568
569                         input_selected_char = (int)c;
570                         putchar_at_cursor((char)input_selected_char);
571                         update();
572
573                         break;
574
575                         /* move visible display */
576                 case KP_F3: case KP_F8: case KP_F7: case KP_F9:
577
578                         new_disp_col = disp_col;
579                         new_disp_row = disp_row;
580
581                         switch (scancode) {
582                                         /* up */
583                                 case KP_F3:
584                                         if (new_disp_row <= 0)
585                                                 break;
586                                         new_disp_row--;
587                                         break;
588
589                                         /* down */
590                                 case KP_F8:
591                                         if (new_disp_row >= ROWS - height)
592                                                 break;
593                                         new_disp_row++;
594                                         break;
595
596                                         /* left */
597                                 case KP_F7:
598                                         if (new_disp_col <= 0)
599                                                 break;
600                                         new_disp_col--;
601                                         break;
602
603                                         /* right */
604                                 case KP_F9:
605                                         if (new_disp_col >= COLS - width)
606                                                 break;
607                                         new_disp_col++;
608                                         break;
609                         }
610
611                         /* no change? */
612                         if (disp_col == new_disp_col && disp_row == new_disp_row)
613                                 break;
614
615                         disp_col = new_disp_col;
616                         disp_row = new_disp_row;
617                         update();
618
619                         break;
620
621                 case KP_F6:     /* backspace */
622                         /* inputing something; no backspace sent, just cancel input */
623                         if (input_state >= 0) {
624                                 input_selected_char = -1;       /* cancel */
625                                 terminate_input();
626                                 break;
627                         }
628                         queue_input_char('\b');
629                         break;
630
631                 case KP_F10:    /* enter */
632                         /* inputing something; first cancel input */
633                         if (input_state >= 0)
634                                 terminate_input();
635                         queue_input_char('\r');
636                         break;
637
638                 case KP_F11:    /* R -> Ctrl-C (abort) */
639                         if (input_state >= 0)
640                                 terminate_input();
641                         queue_input_char('C' - 'Q');    /* ctrl-c */
642                         break;
643
644                 case KP_F5:     /* F% -> Ctrl-U (clear line) */
645                         if (input_state >= 0)
646                                 terminate_input();
647                         queue_input_char('U' - 'Q');    /* ctrl-c */
648                         break;
649
650
651                 case KP_F1:     /* tab */
652                         /* inputing something; first cancel input */
653                         if (input_state >= 0)
654                                 terminate_input();
655                         queue_input_char('\t');
656                         break;
657
658                 case KP_F2:     /* change fast punct */
659                         sel = strchr(fast_punct_list, fast_punct);
660                         if (sel == NULL)
661                                 sel = &fast_punct_list[0];
662                         sel++;
663                         if (*sel == '\0')
664                                 sel = &fast_punct_list[0];
665                         fast_punct = *sel;
666                         update();
667                         break;
668
669
670         }
671
672         if (scancode != KP_FORCE && scancode != KP_IDLE)        /* don't record forced or idle scancode */
673                 last_scancode = scancode;
674 }
675
676 static void scancode_action(int scancode)
677 {
678 #if 0
679         if (scancode == KP_RELEASE)
680                 printf(" RELEASE\n");
681         else if (scancode == KP_FORCE)
682                 printf(" FORCE\n");
683         else if (scancode == KP_IDLE)
684                 printf(" IDLE\n");
685         else if (scancode < 32)
686                 printf(" F%d", scancode + 1);
687         else
688                 printf(" %c", (char)scancode);
689         printf("\n");
690 #endif
691
692         if (enabled) {
693                 handle_enabled_scancode(scancode);
694                 return;
695         }
696
697         if (scancode == KP_FORCE && last_scancode == '*')
698                 use_me = 1;
699
700         last_scancode = scancode;
701 }
702
703 /**************************************************************************************/
704
705 /* update the display; make sure to update only the differences */
706 static void update(void)
707 {
708         int i;
709         char *s, *e, *t, *r, *b, *cp;
710
711         if (input_mode != last_input_mode)
712                 sed156x_output_at(sed156x_text_width - 3, sed156x_text_height - 1, input_mode_txt[input_mode], 3);
713
714         if (tab_indicator == 0) {
715                 sed156x_output_at(0, sed156x_text_height - 1, "\\t", 2);
716                 tab_indicator = 1;
717         }
718
719         if (fast_punct != last_fast_punct)
720                 sed156x_output_at(4, sed156x_text_height - 1, &fast_punct, 1);
721
722         if (curs_disabled ||
723                 curs_col < disp_col || curs_col >= (disp_col + width) ||
724                 curs_row < disp_row || curs_row >= (disp_row + height)) {
725                 cp = NULL;
726         } else
727                 cp = last_visible_buf + (curs_row - disp_row) * width + (curs_col - disp_col);
728
729
730         /* printf("(%d,%d) (%d,%d) %s\n", curs_col, curs_row, disp_col, disp_row, cp ? "YES" : "no"); */
731
732         /* clear previous cursor */
733         if (last_visible_curs_ptr && last_visible_curs_rev == 0) {
734                 i = last_visible_curs_ptr - last_visible_buf;
735                 sed156x_reverse_at(i % width, i / width, 1);
736         }
737
738         b = vty_buf + disp_row * COLS + disp_col;
739         t = last_visible_buf;
740         for (i = 0; i < height; i++) {
741                 s = b;
742                 e = b + width;
743                 /* update only the differences */
744                 do {
745                         while (s < e && *s == *t) {
746                                 s++;
747                                 t++;
748                         }
749                         if (s == e)     /* no more */
750                                 break;
751
752                         /* find run */
753                         r = s;
754                         while (s < e && *s != *t)
755                                 *t++ = *s++;
756
757                         /* and update */
758                         sed156x_output_at(r - b, i, r, s - r);
759
760                 } while (s < e);
761
762                 b += COLS;
763         }
764
765         /* set cursor */
766         if (cp) {
767                 last_visible_curs_ptr = cp;
768                 i = last_visible_curs_ptr - last_visible_buf;
769                 sed156x_reverse_at(i % width, i / width, 1);
770                 last_visible_curs_rev = 0;
771         } else {
772                 last_visible_curs_ptr = NULL;
773         }
774
775         last_input_mode = input_mode;
776         last_fast_punct = fast_punct;
777 }
778
779 /* ensure visibility; the trick is to minimize the screen movement */
780 static void ensure_visible(int col, int row, int dx, int dy)
781 {
782         int x1, y1, x2, y2, a1, b1, a2, b2;
783
784         /* clamp visible region */
785         if (col < 0) {
786                 dx -= col;
787                 col = 0;
788                 if (dx <= 0)
789                         dx = 1;
790         }
791
792         if (row < 0) {
793                 dy -= row;
794                 row = 0;
795                 if (dy <= 0)
796                         dy = 1;
797         }
798
799         if (col + dx > COLS)
800                 dx = COLS - col;
801
802         if (row + dy > ROWS)
803                 dy = ROWS - row;
804
805
806         /* move to easier to use vars */
807         x1 = disp_col;   y1 = disp_row;
808         x2 = x1 + width; y2 = y1 + height;
809         a1 = col;        b1 = row;
810         a2 = a1 + dx;    b2 = b1 + dy;
811
812         /* printf("(%d,%d) - (%d,%d) : (%d, %d) - (%d, %d)\n", x1, y1, x2, y2, a1, b1, a2, b2); */
813
814         if (a2 > x2) {
815                 /* move to the right */
816                 x2 = a2;
817                 x1 = x2 - width;
818                 if (x1 < 0) {
819                         x1 = 0;
820                         x2 = width;
821                 }
822         } else if (a1 < x1) {
823                 /* move to the left */
824                 x1 = a1;
825                 x2 = x1 + width;
826                 if (x2 > COLS) {
827                         x2 = COLS;
828                         x1 = x2 - width;
829                 }
830         }
831
832         if (b2 > y2) {
833                 /* move down */
834                 y2 = b2;
835                 y1 = y2 - height;
836                 if (y1 < 0) {
837                         y1 = 0;
838                         y2 = height;
839                 }
840         } else if (b1 < y1) {
841                 /* move up */
842                 y1 = b1;
843                 y2 = y1 + width;
844                 if (y2 > ROWS) {
845                         y2 = ROWS;
846                         y1 = y2 - height;
847                 }
848         }
849
850         /* printf("(%d,%d) - (%d,%d) : (%d, %d) - (%d, %d)\n", x1, y1, x2, y2, a1, b1, a2, b2); */
851
852         /* no movement? */
853         if (disp_col == x1 && disp_row == y1)
854                 return;
855
856         disp_col = x1;
857         disp_row = y1;
858 }
859
860 /**************************************************************************************/
861
862 static void newline(void)
863 {
864         curs_col = 0;
865         if (curs_row + 1 < ROWS)
866                 curs_row++;
867         else {
868                 memmove(vty_buf, vty_buf + COLS, COLS * (ROWS - 1));
869                 memset(vty_buf + (ROWS - 1) * COLS, ' ', COLS);
870         }
871 }
872
873 void phone_putc(const char c)
874 {
875         int i;
876
877         if (input_mode != -1) {
878                 input_selected_char = -1;
879                 terminate_input();
880         }
881
882         curs_disabled = 1;
883         update();
884
885         blink_time = BLINK_HZ;
886
887         switch (c) {
888                 case '\a':              /* ignore bell            */
889                 case '\r':              /* ignore carriage return */
890                         break;
891
892                 case '\n':              /* next line */
893                         newline();
894                         ensure_visible(curs_col, curs_row, 1, 1);
895                         break;
896
897                 case 9: /* tab 8 */
898                         /* move to tab */
899                         i = curs_col;
900                         i |=  0x0008;
901                         i &= ~0x0007;
902
903                         if (i < COLS)
904                                 curs_col = i;
905                         else
906                                 newline();
907
908                         ensure_visible(curs_col, curs_row, 1, 1);
909                         break;
910
911                 case 8:         /* backspace */
912                         if (curs_col <= 0)
913                                 break;
914                         curs_col--;
915
916                         /* make sure that we see a couple of characters before */
917                         if (curs_col > 4)
918                                 ensure_visible(curs_col - 4, curs_row, 4, 1);
919                         else
920                                 ensure_visible(curs_col, curs_row, 1, 1);
921
922                         break;
923
924                 default:                /* draw the char */
925                         putchar_at_cursor(c);
926
927                         /*
928                          * check for newline
929                          */
930                         if (curs_col + 1 < COLS)
931                                 curs_col++;
932                         else
933                                 newline();
934
935                         ensure_visible(curs_col, curs_row, 1, 1);
936
937                         break;
938         }
939
940         curs_disabled = 0;
941         blink_time = BLINK_HZ;
942         update();
943 }
944
945 /**************************************************************************************/
946
947 static inline unsigned int kp_transfer(unsigned int val)
948 {
949         unsigned int rx;
950         int b;
951
952         rx = 0; b = 8;
953         while (--b >= 0) {
954                 KP_SPI_TXD(val & 0x80);
955                 val <<= 1;
956                 KP_SPI_CLK_TOGGLE();
957                 KP_SPI_BIT_DELAY();
958                 rx <<= 1;
959                 if (KP_SPI_RXD())
960                         rx |= 1;
961                 KP_SPI_CLK_TOGGLE();
962                 KP_SPI_BIT_DELAY();
963         }
964
965         return rx;
966 }
967
968 unsigned int kp_data_transfer(unsigned int val)
969 {
970         KP_SPI_CLK(1);
971         KP_CS(0);
972         val = kp_transfer(val);
973         KP_CS(1);
974
975         return val;
976 }
977
978 unsigned int kp_get_col_mask(unsigned int row_mask)
979 {
980         unsigned int val, col_mask;
981
982         val = 0x80 | (row_mask & 0x7F);
983         (void)kp_data_transfer(val);
984 #if CONFIG_NETPHONE_VERSION == 1
985         col_mask = kp_data_transfer(val) & 0x0F;
986 #elif CONFIG_NETPHONE_VERSION == 2
987         col_mask = ((volatile immap_t *)CFG_IMMR)->im_cpm.cp_pedat & 0x0f;
988         /* XXX FUCK FUCK FUCK FUCK FUCK!!!! */
989         col_mask = ((col_mask & 0x08) >> 3) |   /* BKBR1 */
990                    ((col_mask & 0x04) << 1) |   /* BKBR2 */
991                     (col_mask & 0x02) |         /* BKBR3 */
992                    ((col_mask & 0x01) << 2);    /* BKBR4 */
993
994 #endif
995         /* printf("col_mask(row_mask = 0x%x) -> col_mask = 0x%x\n", row_mask, col_mask); */
996
997         return col_mask;
998 }
999
1000 /**************************************************************************************/
1001
1002 static const int kp_scancodes[KP_ROWS * KP_COLS] = {
1003         KP_F1,   KP_F3,   KP_F4,  KP_F2,
1004         KP_F6,   KP_F8,   KP_F9,  KP_F7,
1005         KP_1,    KP_3,    KP_F11, KP_2,
1006         KP_4,    KP_6,    KP_F12, KP_5,
1007         KP_7,    KP_9,    KP_F13, KP_8,
1008         KP_STAR, KP_HASH, KP_F14, KP_0,
1009         KP_F5,   KP_F15,  KP_F16, KP_F10,
1010 };
1011
1012 static const int kp_repeats[KP_ROWS * KP_COLS] = {
1013         0, 1, 0, 0,
1014         0, 1, 1, 1,
1015         1, 1, 0, 1,
1016         1, 1, 0, 1,
1017         1, 1, 0, 1,
1018         1, 1, 0, 1,
1019         0, 0, 0, 1,
1020 };
1021
1022 static int kp_state = SCAN;
1023 static int kp_last_col_mask;
1024 static int kp_cur_row, kp_cur_col;
1025 static int kp_scancode;
1026 static int kp_stable;
1027 static int kp_repeat;
1028 static int kp_repeat_time;
1029 static int kp_force_time;
1030 static int kp_idle_time;
1031
1032 static void kp_do_poll(void)
1033 {
1034         unsigned int col_mask;
1035         int col;
1036
1037         switch (kp_state) {
1038                 case SCAN:
1039                         if (kp_idle_time > 0) {
1040                                 kp_idle_time -= PHONE_CONSOLE_POLL_HZ;
1041                                 if (kp_idle_time <= 0)
1042                                         scancode_action(KP_IDLE);
1043                         }
1044
1045                         col_mask = kp_get_col_mask(KP_ROWS_MASK);
1046                         if (col_mask == KP_COLS_MASK)
1047                                 break;  /* nothing */
1048                         kp_last_col_mask = col_mask;
1049                         kp_stable = 0;
1050                         kp_state = SCAN_FILTER;
1051                         break;
1052
1053                 case SCAN_FILTER:
1054                         col_mask = kp_get_col_mask(KP_ROWS_MASK);
1055                         if (col_mask != kp_last_col_mask) {
1056                                 kp_state = SCAN;
1057                                 break;
1058                         }
1059
1060                         kp_stable += PHONE_CONSOLE_POLL_HZ;
1061                         if (kp_stable < KP_STABLE_HZ)
1062                                 break;
1063
1064                         kp_cur_row = 0;
1065                         kp_stable = 0;
1066                         kp_state = SCAN_COL;
1067
1068                         (void)kp_get_col_mask(1 << kp_cur_row);
1069                         break;
1070
1071                 case SCAN_COL:
1072                         col_mask = kp_get_col_mask(1 << kp_cur_row);
1073                         if (col_mask == KP_COLS_MASK) {
1074                                 if (++kp_cur_row >= KP_ROWS) {
1075                                         kp_state = SCAN;
1076                                         break;
1077                                 }
1078                                 kp_get_col_mask(1 << kp_cur_row);
1079                                 break;
1080                         }
1081                         kp_last_col_mask = col_mask;
1082                         kp_stable = 0;
1083                         kp_state = SCAN_COL_FILTER;
1084                         break;
1085
1086                 case SCAN_COL_FILTER:
1087                         col_mask = kp_get_col_mask(1 << kp_cur_row);
1088                         if (col_mask != kp_last_col_mask || col_mask == KP_COLS_MASK) {
1089                                 kp_state = SCAN;
1090                                 break;
1091                         }
1092
1093                         kp_stable += PHONE_CONSOLE_POLL_HZ;
1094                         if (kp_stable < KP_STABLE_HZ)
1095                                 break;
1096
1097                         for (col = 0; col < KP_COLS; col++)
1098                                 if ((col_mask & (1 << col)) == 0)
1099                                         break;
1100                         kp_cur_col = col;
1101                         kp_state = PRESSED;
1102                         kp_scancode = kp_scancodes[kp_cur_row * KP_COLS + kp_cur_col];
1103                         kp_repeat = kp_repeats[kp_cur_row * KP_COLS + kp_cur_col];
1104
1105                         if (kp_repeat)
1106                                 kp_repeat_time = KP_REPEAT_DELAY_HZ;
1107                         kp_force_time = KP_FORCE_DELAY_HZ;
1108
1109                         scancode_action(kp_scancode);
1110
1111                         break;
1112
1113                 case PRESSED:
1114                         col_mask = kp_get_col_mask(1 << kp_cur_row);
1115                         if (col_mask != kp_last_col_mask) {
1116                                 kp_state = SCAN;
1117                                 scancode_action(KP_RELEASE);
1118                                 kp_idle_time = KP_IDLE_DELAY_HZ;
1119                                 break;
1120                         }
1121
1122                         if (kp_repeat) {
1123                                 kp_repeat_time -= PHONE_CONSOLE_POLL_HZ;
1124                                 if (kp_repeat_time <= 0) {
1125                                         kp_repeat_time += KP_REPEAT_HZ;
1126                                         scancode_action(kp_scancode);
1127                                 }
1128                         }
1129
1130                         if (kp_force_time > 0) {
1131                                 kp_force_time -= PHONE_CONSOLE_POLL_HZ;
1132                                 if (kp_force_time <= 0)
1133                                         scancode_action(KP_FORCE);
1134                         }
1135
1136                         break;
1137         }
1138 }
1139
1140 /**************************************************************************************/
1141
1142 int drv_phone_is_idle(void)
1143 {
1144         return kp_state == SCAN;
1145 }