]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - board/netphone/phone_console.c
rename CFG_ macros to CONFIG_SYS
[karo-tx-uboot.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              (CONFIG_SYS_HZ/50)      /* refresh every 20ms */
50 #define BLINK_HZ                (CONFIG_SYS_HZ/2)       /* cursor blink every 500ms */
51
52 /*************************************************************************************************/
53
54 #define DISPLAY_BACKLIT_PORT    ((volatile immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pcdat
55 #define DISPLAY_BACKLIT_MASK    0x0010
56
57 /*************************************************************************************************/
58
59 #define KP_STABLE_HZ            (CONFIG_SYS_HZ/100)     /* stable for 10ms */
60 #define KP_REPEAT_DELAY_HZ      (CONFIG_SYS_HZ/4)       /* delay before repeat 250ms */
61 #define KP_REPEAT_HZ            (CONFIG_SYS_HZ/20)      /* repeat every 50ms */
62 #define KP_FORCE_DELAY_HZ       (CONFIG_SYS_HZ/2)       /* key was force pressed */
63 #define KP_IDLE_DELAY_HZ        (CONFIG_SYS_HZ/2)       /* key was released and idle */
64
65 #if CONFIG_NETPHONE_VERSION == 1
66 #define KP_SPI_RXD_PORT (((volatile immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pcdat)
67 #define KP_SPI_RXD_MASK 0x0008
68
69 #define KP_SPI_TXD_PORT (((volatile immap_t *)CONFIG_SYS_IMMR)->im_ioport.iop_pcdat)
70 #define KP_SPI_TXD_MASK 0x0004
71
72 #define KP_SPI_CLK_PORT (((volatile immap_t *)CONFIG_SYS_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 *)CONFIG_SYS_IMMR)->im_cpm.cp_pbdat)
76 #define KP_SPI_RXD_MASK 0x00000008
77
78 #define KP_SPI_TXD_PORT (((volatile immap_t *)CONFIG_SYS_IMMR)->im_cpm.cp_pbdat)
79 #define KP_SPI_TXD_MASK 0x00000004
80
81 #define KP_SPI_CLK_PORT (((volatile immap_t *)CONFIG_SYS_IMMR)->im_cpm.cp_pbdat)
82 #define KP_SPI_CLK_MASK 0x00000002
83 #endif
84
85 #define KP_CS_PORT      (((volatile immap_t *)CONFIG_SYS_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
330         console_init();
331
332         memset(&console_dev, 0, sizeof(console_dev));
333         strcpy(console_dev.name, "phone");
334         console_dev.ext = DEV_EXT_VIDEO;        /* Video extensions */
335         console_dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM;
336         console_dev.start = phone_start;
337         console_dev.stop = phone_stop;
338         console_dev.putc = phone_putc;  /* 'putc' function */
339         console_dev.puts = phone_puts;  /* 'puts' function */
340         console_dev.tstc = phone_tstc;  /* 'tstc' function */
341         console_dev.getc = phone_getc;  /* 'getc' function */
342
343         if (device_register(&console_dev) == 0)
344                 return 1;
345
346         return 0;
347 }
348
349 static int use_me;
350
351 int drv_phone_use_me(void)
352 {
353         return use_me;
354 }
355
356 static void kp_do_poll(void);
357
358 void phone_console_do_poll(void)
359 {
360         int i, x, y;
361
362         kp_do_poll();
363
364         if (enabled) {
365                 /* do the blink */
366                 blink_time -= PHONE_CONSOLE_POLL_HZ;
367                 if (blink_time <= 0) {
368                         blink_time += BLINK_HZ;
369                         if (last_visible_curs_ptr) {
370                                 i = last_visible_curs_ptr - last_visible_buf;
371                                 x = i % width; y = i / width;
372                                 sed156x_reverse_at(x, y, 1);
373                                 last_visible_curs_rev ^= 1;
374                         }
375                 }
376
377                 /* do the refresh */
378                 refresh_time -= PHONE_CONSOLE_POLL_HZ;
379                 if (refresh_time <= 0) {
380                         refresh_time += REFRESH_HZ;
381                         sed156x_sync();
382                 }
383         }
384
385 }
386
387 static int last_scancode = -1;
388 static int forced_scancode = 0;
389 static int input_state = -1;
390 static int input_scancode = -1;
391 static int input_selected_char = -1;
392 static char input_covered_char;
393
394 static void putchar_at_cursor(char c)
395 {
396         vty_buf[curs_row * COLS + curs_col] = c;
397         ensure_visible(curs_col, curs_row, 1, 1);
398 }
399
400 static char getchar_at_cursor(void)
401 {
402         return vty_buf[curs_row * COLS + curs_col];
403 }
404
405 static void queue_input_char(char c)
406 {
407         if (c <= 0)
408                 return;
409
410         queued_char = c;
411 }
412
413 static void terminate_input(void)
414 {
415         if (input_state < 0)
416                 return;
417
418         if (input_selected_char >= 0)
419                 queue_input_char(input_selected_char);
420
421         input_state = -1;
422         input_selected_char = -1;
423         putchar_at_cursor(input_covered_char);
424
425         curs_disabled = 0;
426         blink_time = BLINK_HZ;
427         update();
428 }
429
430 static void handle_enabled_scancode(int scancode)
431 {
432         char c;
433         int new_disp_col, new_disp_row;
434         const char *sel;
435
436
437         switch (scancode) {
438
439                         /* key was released */
440                 case KP_RELEASE:
441                         forced_scancode = 0;
442                         break;
443
444                         /* key was forced */
445                 case KP_FORCE:
446
447                         switch (last_scancode) {
448                                 case '#':
449                                         if (input_mode == IM_NUMBER) {
450                                                 input_mode = IM_CAPITAL;
451                                                 /* queue backspace to erase # */
452                                                 queue_input_char('\b');
453                                         } else {
454                                                 input_mode = IM_NUMBER;
455                                                 fast_punct = '*';
456                                         }
457                                         update();
458                                         break;
459
460                                 case '0': case '1':
461                                 case '2': case '3': case '4': case '5':
462                                 case '6': case '7': case '8': case '9':
463
464                                         if (input_state < 0)
465                                                 break;
466
467                                         input_selected_char = last_scancode;
468                                         putchar_at_cursor((char)input_selected_char);
469                                         terminate_input();
470
471                                         break;
472
473                                 default:
474                                         break;
475                         }
476
477                         break;
478
479                         /* release and idle */
480                 case KP_IDLE:
481                         input_scancode = -1;
482                         if (input_state < 0)
483                                 break;
484                         terminate_input();
485                         break;
486
487                         /* change input mode */
488                 case '#':
489                         if (last_scancode == '#')       /* no repeat */
490                                 break;
491
492                         if (input_mode == IM_NUMBER) {
493                                 input_scancode = scancode;
494                                 input_state = 0;
495                                 input_selected_char = scancode;
496                                 input_covered_char = getchar_at_cursor();
497                                 putchar_at_cursor((char)input_selected_char);
498                                 terminate_input();
499                                 break;
500                         }
501
502                         if (input_mode == IM_SMALL)
503                                 input_mode = IM_CAPITAL;
504                         else
505                                 input_mode = IM_SMALL;
506
507                         update();
508                         break;
509
510                 case '*':
511                         /* no repeat */
512                         if (last_scancode == scancode)
513                                 break;
514
515                         if (input_state >= 0)
516                                 terminate_input();
517
518                         input_scancode = fast_punct;
519                         input_state = 0;
520                         input_selected_char = input_scancode;
521                         input_covered_char = getchar_at_cursor();
522                         putchar_at_cursor((char)input_selected_char);
523                         terminate_input();
524
525                         break;
526
527                 case '0': case '1':
528                 case '2': case '3': case '4': case '5':
529                 case '6': case '7': case '8': case '9':
530
531                         /* no repeat */
532                         if (last_scancode == scancode)
533                                 break;
534
535                         if (input_mode == IM_NUMBER) {
536                                 input_scancode = scancode;
537                                 input_state = 0;
538                                 input_selected_char = scancode;
539                                 input_covered_char = getchar_at_cursor();
540                                 putchar_at_cursor((char)input_selected_char);
541                                 terminate_input();
542                                 break;
543                         }
544
545                         if (input_state >= 0 && input_scancode != scancode)
546                                 terminate_input();
547
548                         if (input_state < 0) {
549                                 curs_disabled = 1;
550                                 input_scancode = scancode;
551                                 input_state = 0;
552                                 input_covered_char = getchar_at_cursor();
553                         } else
554                                 input_state++;
555
556                         if (scancode == '0')
557                                 sel = whspace;
558                         else if (scancode == '1')
559                                 sel = punct;
560                         else
561                                 sel = digits_sel[input_mode][scancode - '2'];
562                         c = *(sel + input_state);
563                         if (c == '\0') {
564                                 input_state = 0;
565                                 c = *sel;
566                         }
567
568                         input_selected_char = (int)c;
569                         putchar_at_cursor((char)input_selected_char);
570                         update();
571
572                         break;
573
574                         /* move visible display */
575                 case KP_F3: case KP_F8: case KP_F7: case KP_F9:
576
577                         new_disp_col = disp_col;
578                         new_disp_row = disp_row;
579
580                         switch (scancode) {
581                                         /* up */
582                                 case KP_F3:
583                                         if (new_disp_row <= 0)
584                                                 break;
585                                         new_disp_row--;
586                                         break;
587
588                                         /* down */
589                                 case KP_F8:
590                                         if (new_disp_row >= ROWS - height)
591                                                 break;
592                                         new_disp_row++;
593                                         break;
594
595                                         /* left */
596                                 case KP_F7:
597                                         if (new_disp_col <= 0)
598                                                 break;
599                                         new_disp_col--;
600                                         break;
601
602                                         /* right */
603                                 case KP_F9:
604                                         if (new_disp_col >= COLS - width)
605                                                 break;
606                                         new_disp_col++;
607                                         break;
608                         }
609
610                         /* no change? */
611                         if (disp_col == new_disp_col && disp_row == new_disp_row)
612                                 break;
613
614                         disp_col = new_disp_col;
615                         disp_row = new_disp_row;
616                         update();
617
618                         break;
619
620                 case KP_F6:     /* backspace */
621                         /* inputing something; no backspace sent, just cancel input */
622                         if (input_state >= 0) {
623                                 input_selected_char = -1;       /* cancel */
624                                 terminate_input();
625                                 break;
626                         }
627                         queue_input_char('\b');
628                         break;
629
630                 case KP_F10:    /* enter */
631                         /* inputing something; first cancel input */
632                         if (input_state >= 0)
633                                 terminate_input();
634                         queue_input_char('\r');
635                         break;
636
637                 case KP_F11:    /* R -> Ctrl-C (abort) */
638                         if (input_state >= 0)
639                                 terminate_input();
640                         queue_input_char('C' - 'Q');    /* ctrl-c */
641                         break;
642
643                 case KP_F5:     /* F% -> Ctrl-U (clear line) */
644                         if (input_state >= 0)
645                                 terminate_input();
646                         queue_input_char('U' - 'Q');    /* ctrl-c */
647                         break;
648
649
650                 case KP_F1:     /* tab */
651                         /* inputing something; first cancel input */
652                         if (input_state >= 0)
653                                 terminate_input();
654                         queue_input_char('\t');
655                         break;
656
657                 case KP_F2:     /* change fast punct */
658                         sel = strchr(fast_punct_list, fast_punct);
659                         if (sel == NULL)
660                                 sel = &fast_punct_list[0];
661                         sel++;
662                         if (*sel == '\0')
663                                 sel = &fast_punct_list[0];
664                         fast_punct = *sel;
665                         update();
666                         break;
667
668
669         }
670
671         if (scancode != KP_FORCE && scancode != KP_IDLE)        /* don't record forced or idle scancode */
672                 last_scancode = scancode;
673 }
674
675 static void scancode_action(int scancode)
676 {
677 #if 0
678         if (scancode == KP_RELEASE)
679                 printf(" RELEASE\n");
680         else if (scancode == KP_FORCE)
681                 printf(" FORCE\n");
682         else if (scancode == KP_IDLE)
683                 printf(" IDLE\n");
684         else if (scancode < 32)
685                 printf(" F%d", scancode + 1);
686         else
687                 printf(" %c", (char)scancode);
688         printf("\n");
689 #endif
690
691         if (enabled) {
692                 handle_enabled_scancode(scancode);
693                 return;
694         }
695
696         if (scancode == KP_FORCE && last_scancode == '*')
697                 use_me = 1;
698
699         last_scancode = scancode;
700 }
701
702 /**************************************************************************************/
703
704 /* update the display; make sure to update only the differences */
705 static void update(void)
706 {
707         int i;
708         char *s, *e, *t, *r, *b, *cp;
709
710         if (input_mode != last_input_mode)
711                 sed156x_output_at(sed156x_text_width - 3, sed156x_text_height - 1, input_mode_txt[input_mode], 3);
712
713         if (tab_indicator == 0) {
714                 sed156x_output_at(0, sed156x_text_height - 1, "\\t", 2);
715                 tab_indicator = 1;
716         }
717
718         if (fast_punct != last_fast_punct)
719                 sed156x_output_at(4, sed156x_text_height - 1, &fast_punct, 1);
720
721         if (curs_disabled ||
722                 curs_col < disp_col || curs_col >= (disp_col + width) ||
723                 curs_row < disp_row || curs_row >= (disp_row + height)) {
724                 cp = NULL;
725         } else
726                 cp = last_visible_buf + (curs_row - disp_row) * width + (curs_col - disp_col);
727
728
729         /* printf("(%d,%d) (%d,%d) %s\n", curs_col, curs_row, disp_col, disp_row, cp ? "YES" : "no"); */
730
731         /* clear previous cursor */
732         if (last_visible_curs_ptr && last_visible_curs_rev == 0) {
733                 i = last_visible_curs_ptr - last_visible_buf;
734                 sed156x_reverse_at(i % width, i / width, 1);
735         }
736
737         b = vty_buf + disp_row * COLS + disp_col;
738         t = last_visible_buf;
739         for (i = 0; i < height; i++) {
740                 s = b;
741                 e = b + width;
742                 /* update only the differences */
743                 do {
744                         while (s < e && *s == *t) {
745                                 s++;
746                                 t++;
747                         }
748                         if (s == e)     /* no more */
749                                 break;
750
751                         /* find run */
752                         r = s;
753                         while (s < e && *s != *t)
754                                 *t++ = *s++;
755
756                         /* and update */
757                         sed156x_output_at(r - b, i, r, s - r);
758
759                 } while (s < e);
760
761                 b += COLS;
762         }
763
764         /* set cursor */
765         if (cp) {
766                 last_visible_curs_ptr = cp;
767                 i = last_visible_curs_ptr - last_visible_buf;
768                 sed156x_reverse_at(i % width, i / width, 1);
769                 last_visible_curs_rev = 0;
770         } else {
771                 last_visible_curs_ptr = NULL;
772         }
773
774         last_input_mode = input_mode;
775         last_fast_punct = fast_punct;
776 }
777
778 /* ensure visibility; the trick is to minimize the screen movement */
779 static void ensure_visible(int col, int row, int dx, int dy)
780 {
781         int x1, y1, x2, y2, a1, b1, a2, b2;
782
783         /* clamp visible region */
784         if (col < 0) {
785                 dx -= col;
786                 col = 0;
787                 if (dx <= 0)
788                         dx = 1;
789         }
790
791         if (row < 0) {
792                 dy -= row;
793                 row = 0;
794                 if (dy <= 0)
795                         dy = 1;
796         }
797
798         if (col + dx > COLS)
799                 dx = COLS - col;
800
801         if (row + dy > ROWS)
802                 dy = ROWS - row;
803
804
805         /* move to easier to use vars */
806         x1 = disp_col;   y1 = disp_row;
807         x2 = x1 + width; y2 = y1 + height;
808         a1 = col;        b1 = row;
809         a2 = a1 + dx;    b2 = b1 + dy;
810
811         /* printf("(%d,%d) - (%d,%d) : (%d, %d) - (%d, %d)\n", x1, y1, x2, y2, a1, b1, a2, b2); */
812
813         if (a2 > x2) {
814                 /* move to the right */
815                 x2 = a2;
816                 x1 = x2 - width;
817                 if (x1 < 0) {
818                         x1 = 0;
819                         x2 = width;
820                 }
821         } else if (a1 < x1) {
822                 /* move to the left */
823                 x1 = a1;
824                 x2 = x1 + width;
825                 if (x2 > COLS) {
826                         x2 = COLS;
827                         x1 = x2 - width;
828                 }
829         }
830
831         if (b2 > y2) {
832                 /* move down */
833                 y2 = b2;
834                 y1 = y2 - height;
835                 if (y1 < 0) {
836                         y1 = 0;
837                         y2 = height;
838                 }
839         } else if (b1 < y1) {
840                 /* move up */
841                 y1 = b1;
842                 y2 = y1 + width;
843                 if (y2 > ROWS) {
844                         y2 = ROWS;
845                         y1 = y2 - height;
846                 }
847         }
848
849         /* printf("(%d,%d) - (%d,%d) : (%d, %d) - (%d, %d)\n", x1, y1, x2, y2, a1, b1, a2, b2); */
850
851         /* no movement? */
852         if (disp_col == x1 && disp_row == y1)
853                 return;
854
855         disp_col = x1;
856         disp_row = y1;
857 }
858
859 /**************************************************************************************/
860
861 static void newline(void)
862 {
863         curs_col = 0;
864         if (curs_row + 1 < ROWS)
865                 curs_row++;
866         else {
867                 memmove(vty_buf, vty_buf + COLS, COLS * (ROWS - 1));
868                 memset(vty_buf + (ROWS - 1) * COLS, ' ', COLS);
869         }
870 }
871
872 void phone_putc(const char c)
873 {
874         int i;
875
876         if (input_mode != -1) {
877                 input_selected_char = -1;
878                 terminate_input();
879         }
880
881         curs_disabled = 1;
882         update();
883
884         blink_time = BLINK_HZ;
885
886         switch (c) {
887                 case '\a':              /* ignore bell            */
888                 case '\r':              /* ignore carriage return */
889                         break;
890
891                 case '\n':              /* next line */
892                         newline();
893                         ensure_visible(curs_col, curs_row, 1, 1);
894                         break;
895
896                 case 9: /* tab 8 */
897                         /* move to tab */
898                         i = curs_col;
899                         i |=  0x0008;
900                         i &= ~0x0007;
901
902                         if (i < COLS)
903                                 curs_col = i;
904                         else
905                                 newline();
906
907                         ensure_visible(curs_col, curs_row, 1, 1);
908                         break;
909
910                 case 8:         /* backspace */
911                         if (curs_col <= 0)
912                                 break;
913                         curs_col--;
914
915                         /* make sure that we see a couple of characters before */
916                         if (curs_col > 4)
917                                 ensure_visible(curs_col - 4, curs_row, 4, 1);
918                         else
919                                 ensure_visible(curs_col, curs_row, 1, 1);
920
921                         break;
922
923                 default:                /* draw the char */
924                         putchar_at_cursor(c);
925
926                         /*
927                          * check for newline
928                          */
929                         if (curs_col + 1 < COLS)
930                                 curs_col++;
931                         else
932                                 newline();
933
934                         ensure_visible(curs_col, curs_row, 1, 1);
935
936                         break;
937         }
938
939         curs_disabled = 0;
940         blink_time = BLINK_HZ;
941         update();
942 }
943
944 /**************************************************************************************/
945
946 static inline unsigned int kp_transfer(unsigned int val)
947 {
948         unsigned int rx;
949         int b;
950
951         rx = 0; b = 8;
952         while (--b >= 0) {
953                 KP_SPI_TXD(val & 0x80);
954                 val <<= 1;
955                 KP_SPI_CLK_TOGGLE();
956                 KP_SPI_BIT_DELAY();
957                 rx <<= 1;
958                 if (KP_SPI_RXD())
959                         rx |= 1;
960                 KP_SPI_CLK_TOGGLE();
961                 KP_SPI_BIT_DELAY();
962         }
963
964         return rx;
965 }
966
967 unsigned int kp_data_transfer(unsigned int val)
968 {
969         KP_SPI_CLK(1);
970         KP_CS(0);
971         val = kp_transfer(val);
972         KP_CS(1);
973
974         return val;
975 }
976
977 unsigned int kp_get_col_mask(unsigned int row_mask)
978 {
979         unsigned int val, col_mask;
980
981         val = 0x80 | (row_mask & 0x7F);
982         (void)kp_data_transfer(val);
983 #if CONFIG_NETPHONE_VERSION == 1
984         col_mask = kp_data_transfer(val) & 0x0F;
985 #elif CONFIG_NETPHONE_VERSION == 2
986         col_mask = ((volatile immap_t *)CONFIG_SYS_IMMR)->im_cpm.cp_pedat & 0x0f;
987         /* XXX FUCK FUCK FUCK FUCK FUCK!!!! */
988         col_mask = ((col_mask & 0x08) >> 3) |   /* BKBR1 */
989                    ((col_mask & 0x04) << 1) |   /* BKBR2 */
990                     (col_mask & 0x02) |         /* BKBR3 */
991                    ((col_mask & 0x01) << 2);    /* BKBR4 */
992
993 #endif
994         /* printf("col_mask(row_mask = 0x%x) -> col_mask = 0x%x\n", row_mask, col_mask); */
995
996         return col_mask;
997 }
998
999 /**************************************************************************************/
1000
1001 static const int kp_scancodes[KP_ROWS * KP_COLS] = {
1002         KP_F1,   KP_F3,   KP_F4,  KP_F2,
1003         KP_F6,   KP_F8,   KP_F9,  KP_F7,
1004         KP_1,    KP_3,    KP_F11, KP_2,
1005         KP_4,    KP_6,    KP_F12, KP_5,
1006         KP_7,    KP_9,    KP_F13, KP_8,
1007         KP_STAR, KP_HASH, KP_F14, KP_0,
1008         KP_F5,   KP_F15,  KP_F16, KP_F10,
1009 };
1010
1011 static const int kp_repeats[KP_ROWS * KP_COLS] = {
1012         0, 1, 0, 0,
1013         0, 1, 1, 1,
1014         1, 1, 0, 1,
1015         1, 1, 0, 1,
1016         1, 1, 0, 1,
1017         1, 1, 0, 1,
1018         0, 0, 0, 1,
1019 };
1020
1021 static int kp_state = SCAN;
1022 static int kp_last_col_mask;
1023 static int kp_cur_row, kp_cur_col;
1024 static int kp_scancode;
1025 static int kp_stable;
1026 static int kp_repeat;
1027 static int kp_repeat_time;
1028 static int kp_force_time;
1029 static int kp_idle_time;
1030
1031 static void kp_do_poll(void)
1032 {
1033         unsigned int col_mask;
1034         int col;
1035
1036         switch (kp_state) {
1037                 case SCAN:
1038                         if (kp_idle_time > 0) {
1039                                 kp_idle_time -= PHONE_CONSOLE_POLL_HZ;
1040                                 if (kp_idle_time <= 0)
1041                                         scancode_action(KP_IDLE);
1042                         }
1043
1044                         col_mask = kp_get_col_mask(KP_ROWS_MASK);
1045                         if (col_mask == KP_COLS_MASK)
1046                                 break;  /* nothing */
1047                         kp_last_col_mask = col_mask;
1048                         kp_stable = 0;
1049                         kp_state = SCAN_FILTER;
1050                         break;
1051
1052                 case SCAN_FILTER:
1053                         col_mask = kp_get_col_mask(KP_ROWS_MASK);
1054                         if (col_mask != kp_last_col_mask) {
1055                                 kp_state = SCAN;
1056                                 break;
1057                         }
1058
1059                         kp_stable += PHONE_CONSOLE_POLL_HZ;
1060                         if (kp_stable < KP_STABLE_HZ)
1061                                 break;
1062
1063                         kp_cur_row = 0;
1064                         kp_stable = 0;
1065                         kp_state = SCAN_COL;
1066
1067                         (void)kp_get_col_mask(1 << kp_cur_row);
1068                         break;
1069
1070                 case SCAN_COL:
1071                         col_mask = kp_get_col_mask(1 << kp_cur_row);
1072                         if (col_mask == KP_COLS_MASK) {
1073                                 if (++kp_cur_row >= KP_ROWS) {
1074                                         kp_state = SCAN;
1075                                         break;
1076                                 }
1077                                 kp_get_col_mask(1 << kp_cur_row);
1078                                 break;
1079                         }
1080                         kp_last_col_mask = col_mask;
1081                         kp_stable = 0;
1082                         kp_state = SCAN_COL_FILTER;
1083                         break;
1084
1085                 case SCAN_COL_FILTER:
1086                         col_mask = kp_get_col_mask(1 << kp_cur_row);
1087                         if (col_mask != kp_last_col_mask || col_mask == KP_COLS_MASK) {
1088                                 kp_state = SCAN;
1089                                 break;
1090                         }
1091
1092                         kp_stable += PHONE_CONSOLE_POLL_HZ;
1093                         if (kp_stable < KP_STABLE_HZ)
1094                                 break;
1095
1096                         for (col = 0; col < KP_COLS; col++)
1097                                 if ((col_mask & (1 << col)) == 0)
1098                                         break;
1099                         kp_cur_col = col;
1100                         kp_state = PRESSED;
1101                         kp_scancode = kp_scancodes[kp_cur_row * KP_COLS + kp_cur_col];
1102                         kp_repeat = kp_repeats[kp_cur_row * KP_COLS + kp_cur_col];
1103
1104                         if (kp_repeat)
1105                                 kp_repeat_time = KP_REPEAT_DELAY_HZ;
1106                         kp_force_time = KP_FORCE_DELAY_HZ;
1107
1108                         scancode_action(kp_scancode);
1109
1110                         break;
1111
1112                 case PRESSED:
1113                         col_mask = kp_get_col_mask(1 << kp_cur_row);
1114                         if (col_mask != kp_last_col_mask) {
1115                                 kp_state = SCAN;
1116                                 scancode_action(KP_RELEASE);
1117                                 kp_idle_time = KP_IDLE_DELAY_HZ;
1118                                 break;
1119                         }
1120
1121                         if (kp_repeat) {
1122                                 kp_repeat_time -= PHONE_CONSOLE_POLL_HZ;
1123                                 if (kp_repeat_time <= 0) {
1124                                         kp_repeat_time += KP_REPEAT_HZ;
1125                                         scancode_action(kp_scancode);
1126                                 }
1127                         }
1128
1129                         if (kp_force_time > 0) {
1130                                 kp_force_time -= PHONE_CONSOLE_POLL_HZ;
1131                                 if (kp_force_time <= 0)
1132                                         scancode_action(KP_FORCE);
1133                         }
1134
1135                         break;
1136         }
1137 }
1138
1139 /**************************************************************************************/
1140
1141 int drv_phone_is_idle(void)
1142 {
1143         return kp_state == SCAN;
1144 }