Merge branch 'devel' of master.kernel.org:/home/rmk/linux-2.6-arm
[linux-2.6] / drivers / gpu / drm / radeon / atom.c
1 /*
2  * Copyright 2008 Advanced Micro Devices, Inc.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20  * OTHER DEALINGS IN THE SOFTWARE.
21  *
22  * Author: Stanislaw Skowronek
23  */
24
25 #include <linux/module.h>
26 #include <linux/sched.h>
27
28 #define ATOM_DEBUG
29
30 #include "atom.h"
31 #include "atom-names.h"
32 #include "atom-bits.h"
33
34 #define ATOM_COND_ABOVE         0
35 #define ATOM_COND_ABOVEOREQUAL  1
36 #define ATOM_COND_ALWAYS        2
37 #define ATOM_COND_BELOW         3
38 #define ATOM_COND_BELOWOREQUAL  4
39 #define ATOM_COND_EQUAL         5
40 #define ATOM_COND_NOTEQUAL      6
41
42 #define ATOM_PORT_ATI   0
43 #define ATOM_PORT_PCI   1
44 #define ATOM_PORT_SYSIO 2
45
46 #define ATOM_UNIT_MICROSEC      0
47 #define ATOM_UNIT_MILLISEC      1
48
49 #define PLL_INDEX       2
50 #define PLL_DATA        3
51
52 typedef struct {
53         struct atom_context *ctx;
54
55         uint32_t *ps, *ws;
56         int ps_shift;
57         uint16_t start;
58 } atom_exec_context;
59
60 int atom_debug = 0;
61 void atom_execute_table(struct atom_context *ctx, int index, uint32_t * params);
62
63 static uint32_t atom_arg_mask[8] =
64     { 0xFFFFFFFF, 0xFFFF, 0xFFFF00, 0xFFFF0000, 0xFF, 0xFF00, 0xFF0000,
65 0xFF000000 };
66 static int atom_arg_shift[8] = { 0, 0, 8, 16, 0, 8, 16, 24 };
67
68 static int atom_dst_to_src[8][4] = {
69         /* translate destination alignment field to the source alignment encoding */
70         {0, 0, 0, 0},
71         {1, 2, 3, 0},
72         {1, 2, 3, 0},
73         {1, 2, 3, 0},
74         {4, 5, 6, 7},
75         {4, 5, 6, 7},
76         {4, 5, 6, 7},
77         {4, 5, 6, 7},
78 };
79 static int atom_def_dst[8] = { 0, 0, 1, 2, 0, 1, 2, 3 };
80
81 static int debug_depth = 0;
82 #ifdef ATOM_DEBUG
83 static void debug_print_spaces(int n)
84 {
85         while (n--)
86                 printk("   ");
87 }
88
89 #define DEBUG(...) do if (atom_debug) { printk(KERN_DEBUG __VA_ARGS__); } while (0)
90 #define SDEBUG(...) do if (atom_debug) { printk(KERN_DEBUG); debug_print_spaces(debug_depth); printk(__VA_ARGS__); } while (0)
91 #else
92 #define DEBUG(...) do { } while (0)
93 #define SDEBUG(...) do { } while (0)
94 #endif
95
96 static uint32_t atom_iio_execute(struct atom_context *ctx, int base,
97                                  uint32_t index, uint32_t data)
98 {
99         uint32_t temp = 0xCDCDCDCD;
100         while (1)
101                 switch (CU8(base)) {
102                 case ATOM_IIO_NOP:
103                         base++;
104                         break;
105                 case ATOM_IIO_READ:
106                         temp = ctx->card->reg_read(ctx->card, CU16(base + 1));
107                         base += 3;
108                         break;
109                 case ATOM_IIO_WRITE:
110                         ctx->card->reg_write(ctx->card, CU16(base + 1), temp);
111                         base += 3;
112                         break;
113                 case ATOM_IIO_CLEAR:
114                         temp &=
115                             ~((0xFFFFFFFF >> (32 - CU8(base + 1))) <<
116                               CU8(base + 2));
117                         base += 3;
118                         break;
119                 case ATOM_IIO_SET:
120                         temp |=
121                             (0xFFFFFFFF >> (32 - CU8(base + 1))) << CU8(base +
122                                                                         2);
123                         base += 3;
124                         break;
125                 case ATOM_IIO_MOVE_INDEX:
126                         temp &=
127                             ~((0xFFFFFFFF >> (32 - CU8(base + 1))) <<
128                               CU8(base + 2));
129                         temp |=
130                             ((index >> CU8(base + 2)) &
131                              (0xFFFFFFFF >> (32 - CU8(base + 1)))) << CU8(base +
132                                                                           3);
133                         base += 4;
134                         break;
135                 case ATOM_IIO_MOVE_DATA:
136                         temp &=
137                             ~((0xFFFFFFFF >> (32 - CU8(base + 1))) <<
138                               CU8(base + 2));
139                         temp |=
140                             ((data >> CU8(base + 2)) &
141                              (0xFFFFFFFF >> (32 - CU8(base + 1)))) << CU8(base +
142                                                                           3);
143                         base += 4;
144                         break;
145                 case ATOM_IIO_MOVE_ATTR:
146                         temp &=
147                             ~((0xFFFFFFFF >> (32 - CU8(base + 1))) <<
148                               CU8(base + 2));
149                         temp |=
150                             ((ctx->
151                               io_attr >> CU8(base + 2)) & (0xFFFFFFFF >> (32 -
152                                                                           CU8
153                                                                           (base
154                                                                            +
155                                                                            1))))
156                             << CU8(base + 3);
157                         base += 4;
158                         break;
159                 case ATOM_IIO_END:
160                         return temp;
161                 default:
162                         printk(KERN_INFO "Unknown IIO opcode.\n");
163                         return 0;
164                 }
165 }
166
167 static uint32_t atom_get_src_int(atom_exec_context *ctx, uint8_t attr,
168                                  int *ptr, uint32_t *saved, int print)
169 {
170         uint32_t idx, val = 0xCDCDCDCD, align, arg;
171         struct atom_context *gctx = ctx->ctx;
172         arg = attr & 7;
173         align = (attr >> 3) & 7;
174         switch (arg) {
175         case ATOM_ARG_REG:
176                 idx = U16(*ptr);
177                 (*ptr) += 2;
178                 if (print)
179                         DEBUG("REG[0x%04X]", idx);
180                 idx += gctx->reg_block;
181                 switch (gctx->io_mode) {
182                 case ATOM_IO_MM:
183                         val = gctx->card->reg_read(gctx->card, idx);
184                         break;
185                 case ATOM_IO_PCI:
186                         printk(KERN_INFO
187                                "PCI registers are not implemented.\n");
188                         return 0;
189                 case ATOM_IO_SYSIO:
190                         printk(KERN_INFO
191                                "SYSIO registers are not implemented.\n");
192                         return 0;
193                 default:
194                         if (!(gctx->io_mode & 0x80)) {
195                                 printk(KERN_INFO "Bad IO mode.\n");
196                                 return 0;
197                         }
198                         if (!gctx->iio[gctx->io_mode & 0x7F]) {
199                                 printk(KERN_INFO
200                                        "Undefined indirect IO read method %d.\n",
201                                        gctx->io_mode & 0x7F);
202                                 return 0;
203                         }
204                         val =
205                             atom_iio_execute(gctx,
206                                              gctx->iio[gctx->io_mode & 0x7F],
207                                              idx, 0);
208                 }
209                 break;
210         case ATOM_ARG_PS:
211                 idx = U8(*ptr);
212                 (*ptr)++;
213                 val = le32_to_cpu(ctx->ps[idx]);
214                 if (print)
215                         DEBUG("PS[0x%02X,0x%04X]", idx, val);
216                 break;
217         case ATOM_ARG_WS:
218                 idx = U8(*ptr);
219                 (*ptr)++;
220                 if (print)
221                         DEBUG("WS[0x%02X]", idx);
222                 switch (idx) {
223                 case ATOM_WS_QUOTIENT:
224                         val = gctx->divmul[0];
225                         break;
226                 case ATOM_WS_REMAINDER:
227                         val = gctx->divmul[1];
228                         break;
229                 case ATOM_WS_DATAPTR:
230                         val = gctx->data_block;
231                         break;
232                 case ATOM_WS_SHIFT:
233                         val = gctx->shift;
234                         break;
235                 case ATOM_WS_OR_MASK:
236                         val = 1 << gctx->shift;
237                         break;
238                 case ATOM_WS_AND_MASK:
239                         val = ~(1 << gctx->shift);
240                         break;
241                 case ATOM_WS_FB_WINDOW:
242                         val = gctx->fb_base;
243                         break;
244                 case ATOM_WS_ATTRIBUTES:
245                         val = gctx->io_attr;
246                         break;
247                 default:
248                         val = ctx->ws[idx];
249                 }
250                 break;
251         case ATOM_ARG_ID:
252                 idx = U16(*ptr);
253                 (*ptr) += 2;
254                 if (print) {
255                         if (gctx->data_block)
256                                 DEBUG("ID[0x%04X+%04X]", idx, gctx->data_block);
257                         else
258                                 DEBUG("ID[0x%04X]", idx);
259                 }
260                 val = U32(idx + gctx->data_block);
261                 break;
262         case ATOM_ARG_FB:
263                 idx = U8(*ptr);
264                 (*ptr)++;
265                 if (print)
266                         DEBUG("FB[0x%02X]", idx);
267                 printk(KERN_INFO "FB access is not implemented.\n");
268                 return 0;
269         case ATOM_ARG_IMM:
270                 switch (align) {
271                 case ATOM_SRC_DWORD:
272                         val = U32(*ptr);
273                         (*ptr) += 4;
274                         if (print)
275                                 DEBUG("IMM 0x%08X\n", val);
276                         return val;
277                 case ATOM_SRC_WORD0:
278                 case ATOM_SRC_WORD8:
279                 case ATOM_SRC_WORD16:
280                         val = U16(*ptr);
281                         (*ptr) += 2;
282                         if (print)
283                                 DEBUG("IMM 0x%04X\n", val);
284                         return val;
285                 case ATOM_SRC_BYTE0:
286                 case ATOM_SRC_BYTE8:
287                 case ATOM_SRC_BYTE16:
288                 case ATOM_SRC_BYTE24:
289                         val = U8(*ptr);
290                         (*ptr)++;
291                         if (print)
292                                 DEBUG("IMM 0x%02X\n", val);
293                         return val;
294                 }
295                 return 0;
296         case ATOM_ARG_PLL:
297                 idx = U8(*ptr);
298                 (*ptr)++;
299                 if (print)
300                         DEBUG("PLL[0x%02X]", idx);
301                 val = gctx->card->pll_read(gctx->card, idx);
302                 break;
303         case ATOM_ARG_MC:
304                 idx = U8(*ptr);
305                 (*ptr)++;
306                 if (print)
307                         DEBUG("MC[0x%02X]", idx);
308                 val = gctx->card->mc_read(gctx->card, idx);
309                 break;
310         }
311         if (saved)
312                 *saved = val;
313         val &= atom_arg_mask[align];
314         val >>= atom_arg_shift[align];
315         if (print)
316                 switch (align) {
317                 case ATOM_SRC_DWORD:
318                         DEBUG(".[31:0] -> 0x%08X\n", val);
319                         break;
320                 case ATOM_SRC_WORD0:
321                         DEBUG(".[15:0] -> 0x%04X\n", val);
322                         break;
323                 case ATOM_SRC_WORD8:
324                         DEBUG(".[23:8] -> 0x%04X\n", val);
325                         break;
326                 case ATOM_SRC_WORD16:
327                         DEBUG(".[31:16] -> 0x%04X\n", val);
328                         break;
329                 case ATOM_SRC_BYTE0:
330                         DEBUG(".[7:0] -> 0x%02X\n", val);
331                         break;
332                 case ATOM_SRC_BYTE8:
333                         DEBUG(".[15:8] -> 0x%02X\n", val);
334                         break;
335                 case ATOM_SRC_BYTE16:
336                         DEBUG(".[23:16] -> 0x%02X\n", val);
337                         break;
338                 case ATOM_SRC_BYTE24:
339                         DEBUG(".[31:24] -> 0x%02X\n", val);
340                         break;
341                 }
342         return val;
343 }
344
345 static void atom_skip_src_int(atom_exec_context *ctx, uint8_t attr, int *ptr)
346 {
347         uint32_t align = (attr >> 3) & 7, arg = attr & 7;
348         switch (arg) {
349         case ATOM_ARG_REG:
350         case ATOM_ARG_ID:
351                 (*ptr) += 2;
352                 break;
353         case ATOM_ARG_PLL:
354         case ATOM_ARG_MC:
355         case ATOM_ARG_PS:
356         case ATOM_ARG_WS:
357         case ATOM_ARG_FB:
358                 (*ptr)++;
359                 break;
360         case ATOM_ARG_IMM:
361                 switch (align) {
362                 case ATOM_SRC_DWORD:
363                         (*ptr) += 4;
364                         return;
365                 case ATOM_SRC_WORD0:
366                 case ATOM_SRC_WORD8:
367                 case ATOM_SRC_WORD16:
368                         (*ptr) += 2;
369                         return;
370                 case ATOM_SRC_BYTE0:
371                 case ATOM_SRC_BYTE8:
372                 case ATOM_SRC_BYTE16:
373                 case ATOM_SRC_BYTE24:
374                         (*ptr)++;
375                         return;
376                 }
377                 return;
378         }
379 }
380
381 static uint32_t atom_get_src(atom_exec_context *ctx, uint8_t attr, int *ptr)
382 {
383         return atom_get_src_int(ctx, attr, ptr, NULL, 1);
384 }
385
386 static uint32_t atom_get_dst(atom_exec_context *ctx, int arg, uint8_t attr,
387                              int *ptr, uint32_t *saved, int print)
388 {
389         return atom_get_src_int(ctx,
390                                 arg | atom_dst_to_src[(attr >> 3) &
391                                                       7][(attr >> 6) & 3] << 3,
392                                 ptr, saved, print);
393 }
394
395 static void atom_skip_dst(atom_exec_context *ctx, int arg, uint8_t attr, int *ptr)
396 {
397         atom_skip_src_int(ctx,
398                           arg | atom_dst_to_src[(attr >> 3) & 7][(attr >> 6) &
399                                                                  3] << 3, ptr);
400 }
401
402 static void atom_put_dst(atom_exec_context *ctx, int arg, uint8_t attr,
403                          int *ptr, uint32_t val, uint32_t saved)
404 {
405         uint32_t align =
406             atom_dst_to_src[(attr >> 3) & 7][(attr >> 6) & 3], old_val =
407             val, idx;
408         struct atom_context *gctx = ctx->ctx;
409         old_val &= atom_arg_mask[align] >> atom_arg_shift[align];
410         val <<= atom_arg_shift[align];
411         val &= atom_arg_mask[align];
412         saved &= ~atom_arg_mask[align];
413         val |= saved;
414         switch (arg) {
415         case ATOM_ARG_REG:
416                 idx = U16(*ptr);
417                 (*ptr) += 2;
418                 DEBUG("REG[0x%04X]", idx);
419                 idx += gctx->reg_block;
420                 switch (gctx->io_mode) {
421                 case ATOM_IO_MM:
422                         if (idx == 0)
423                                 gctx->card->reg_write(gctx->card, idx,
424                                                       val << 2);
425                         else
426                                 gctx->card->reg_write(gctx->card, idx, val);
427                         break;
428                 case ATOM_IO_PCI:
429                         printk(KERN_INFO
430                                "PCI registers are not implemented.\n");
431                         return;
432                 case ATOM_IO_SYSIO:
433                         printk(KERN_INFO
434                                "SYSIO registers are not implemented.\n");
435                         return;
436                 default:
437                         if (!(gctx->io_mode & 0x80)) {
438                                 printk(KERN_INFO "Bad IO mode.\n");
439                                 return;
440                         }
441                         if (!gctx->iio[gctx->io_mode & 0xFF]) {
442                                 printk(KERN_INFO
443                                        "Undefined indirect IO write method %d.\n",
444                                        gctx->io_mode & 0x7F);
445                                 return;
446                         }
447                         atom_iio_execute(gctx, gctx->iio[gctx->io_mode & 0xFF],
448                                          idx, val);
449                 }
450                 break;
451         case ATOM_ARG_PS:
452                 idx = U8(*ptr);
453                 (*ptr)++;
454                 DEBUG("PS[0x%02X]", idx);
455                 ctx->ps[idx] = cpu_to_le32(val);
456                 break;
457         case ATOM_ARG_WS:
458                 idx = U8(*ptr);
459                 (*ptr)++;
460                 DEBUG("WS[0x%02X]", idx);
461                 switch (idx) {
462                 case ATOM_WS_QUOTIENT:
463                         gctx->divmul[0] = val;
464                         break;
465                 case ATOM_WS_REMAINDER:
466                         gctx->divmul[1] = val;
467                         break;
468                 case ATOM_WS_DATAPTR:
469                         gctx->data_block = val;
470                         break;
471                 case ATOM_WS_SHIFT:
472                         gctx->shift = val;
473                         break;
474                 case ATOM_WS_OR_MASK:
475                 case ATOM_WS_AND_MASK:
476                         break;
477                 case ATOM_WS_FB_WINDOW:
478                         gctx->fb_base = val;
479                         break;
480                 case ATOM_WS_ATTRIBUTES:
481                         gctx->io_attr = val;
482                         break;
483                 default:
484                         ctx->ws[idx] = val;
485                 }
486                 break;
487         case ATOM_ARG_FB:
488                 idx = U8(*ptr);
489                 (*ptr)++;
490                 DEBUG("FB[0x%02X]", idx);
491                 printk(KERN_INFO "FB access is not implemented.\n");
492                 return;
493         case ATOM_ARG_PLL:
494                 idx = U8(*ptr);
495                 (*ptr)++;
496                 DEBUG("PLL[0x%02X]", idx);
497                 gctx->card->pll_write(gctx->card, idx, val);
498                 break;
499         case ATOM_ARG_MC:
500                 idx = U8(*ptr);
501                 (*ptr)++;
502                 DEBUG("MC[0x%02X]", idx);
503                 gctx->card->mc_write(gctx->card, idx, val);
504                 return;
505         }
506         switch (align) {
507         case ATOM_SRC_DWORD:
508                 DEBUG(".[31:0] <- 0x%08X\n", old_val);
509                 break;
510         case ATOM_SRC_WORD0:
511                 DEBUG(".[15:0] <- 0x%04X\n", old_val);
512                 break;
513         case ATOM_SRC_WORD8:
514                 DEBUG(".[23:8] <- 0x%04X\n", old_val);
515                 break;
516         case ATOM_SRC_WORD16:
517                 DEBUG(".[31:16] <- 0x%04X\n", old_val);
518                 break;
519         case ATOM_SRC_BYTE0:
520                 DEBUG(".[7:0] <- 0x%02X\n", old_val);
521                 break;
522         case ATOM_SRC_BYTE8:
523                 DEBUG(".[15:8] <- 0x%02X\n", old_val);
524                 break;
525         case ATOM_SRC_BYTE16:
526                 DEBUG(".[23:16] <- 0x%02X\n", old_val);
527                 break;
528         case ATOM_SRC_BYTE24:
529                 DEBUG(".[31:24] <- 0x%02X\n", old_val);
530                 break;
531         }
532 }
533
534 static void atom_op_add(atom_exec_context *ctx, int *ptr, int arg)
535 {
536         uint8_t attr = U8((*ptr)++);
537         uint32_t dst, src, saved;
538         int dptr = *ptr;
539         SDEBUG("   dst: ");
540         dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
541         SDEBUG("   src: ");
542         src = atom_get_src(ctx, attr, ptr);
543         dst += src;
544         SDEBUG("   dst: ");
545         atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
546 }
547
548 static void atom_op_and(atom_exec_context *ctx, int *ptr, int arg)
549 {
550         uint8_t attr = U8((*ptr)++);
551         uint32_t dst, src, saved;
552         int dptr = *ptr;
553         SDEBUG("   dst: ");
554         dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
555         SDEBUG("   src: ");
556         src = atom_get_src(ctx, attr, ptr);
557         dst &= src;
558         SDEBUG("   dst: ");
559         atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
560 }
561
562 static void atom_op_beep(atom_exec_context *ctx, int *ptr, int arg)
563 {
564         printk("ATOM BIOS beeped!\n");
565 }
566
567 static void atom_op_calltable(atom_exec_context *ctx, int *ptr, int arg)
568 {
569         int idx = U8((*ptr)++);
570         if (idx < ATOM_TABLE_NAMES_CNT)
571                 SDEBUG("   table: %d (%s)\n", idx, atom_table_names[idx]);
572         else
573                 SDEBUG("   table: %d\n", idx);
574         if (U16(ctx->ctx->cmd_table + 4 + 2 * idx))
575                 atom_execute_table(ctx->ctx, idx, ctx->ps + ctx->ps_shift);
576 }
577
578 static void atom_op_clear(atom_exec_context *ctx, int *ptr, int arg)
579 {
580         uint8_t attr = U8((*ptr)++);
581         uint32_t saved;
582         int dptr = *ptr;
583         attr &= 0x38;
584         attr |= atom_def_dst[attr >> 3] << 6;
585         atom_get_dst(ctx, arg, attr, ptr, &saved, 0);
586         SDEBUG("   dst: ");
587         atom_put_dst(ctx, arg, attr, &dptr, 0, saved);
588 }
589
590 static void atom_op_compare(atom_exec_context *ctx, int *ptr, int arg)
591 {
592         uint8_t attr = U8((*ptr)++);
593         uint32_t dst, src;
594         SDEBUG("   src1: ");
595         dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1);
596         SDEBUG("   src2: ");
597         src = atom_get_src(ctx, attr, ptr);
598         ctx->ctx->cs_equal = (dst == src);
599         ctx->ctx->cs_above = (dst > src);
600         SDEBUG("   result: %s %s\n", ctx->ctx->cs_equal ? "EQ" : "NE",
601                ctx->ctx->cs_above ? "GT" : "LE");
602 }
603
604 static void atom_op_delay(atom_exec_context *ctx, int *ptr, int arg)
605 {
606         uint8_t count = U8((*ptr)++);
607         SDEBUG("   count: %d\n", count);
608         if (arg == ATOM_UNIT_MICROSEC)
609                 schedule_timeout_uninterruptible(usecs_to_jiffies(count));
610         else
611                 schedule_timeout_uninterruptible(msecs_to_jiffies(count));
612 }
613
614 static void atom_op_div(atom_exec_context *ctx, int *ptr, int arg)
615 {
616         uint8_t attr = U8((*ptr)++);
617         uint32_t dst, src;
618         SDEBUG("   src1: ");
619         dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1);
620         SDEBUG("   src2: ");
621         src = atom_get_src(ctx, attr, ptr);
622         if (src != 0) {
623                 ctx->ctx->divmul[0] = dst / src;
624                 ctx->ctx->divmul[1] = dst % src;
625         } else {
626                 ctx->ctx->divmul[0] = 0;
627                 ctx->ctx->divmul[1] = 0;
628         }
629 }
630
631 static void atom_op_eot(atom_exec_context *ctx, int *ptr, int arg)
632 {
633         /* functionally, a nop */
634 }
635
636 static void atom_op_jump(atom_exec_context *ctx, int *ptr, int arg)
637 {
638         int execute = 0, target = U16(*ptr);
639         (*ptr) += 2;
640         switch (arg) {
641         case ATOM_COND_ABOVE:
642                 execute = ctx->ctx->cs_above;
643                 break;
644         case ATOM_COND_ABOVEOREQUAL:
645                 execute = ctx->ctx->cs_above || ctx->ctx->cs_equal;
646                 break;
647         case ATOM_COND_ALWAYS:
648                 execute = 1;
649                 break;
650         case ATOM_COND_BELOW:
651                 execute = !(ctx->ctx->cs_above || ctx->ctx->cs_equal);
652                 break;
653         case ATOM_COND_BELOWOREQUAL:
654                 execute = !ctx->ctx->cs_above;
655                 break;
656         case ATOM_COND_EQUAL:
657                 execute = ctx->ctx->cs_equal;
658                 break;
659         case ATOM_COND_NOTEQUAL:
660                 execute = !ctx->ctx->cs_equal;
661                 break;
662         }
663         if (arg != ATOM_COND_ALWAYS)
664                 SDEBUG("   taken: %s\n", execute ? "yes" : "no");
665         SDEBUG("   target: 0x%04X\n", target);
666         if (execute)
667                 *ptr = ctx->start + target;
668 }
669
670 static void atom_op_mask(atom_exec_context *ctx, int *ptr, int arg)
671 {
672         uint8_t attr = U8((*ptr)++);
673         uint32_t dst, src1, src2, saved;
674         int dptr = *ptr;
675         SDEBUG("   dst: ");
676         dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
677         SDEBUG("   src1: ");
678         src1 = atom_get_src(ctx, attr, ptr);
679         SDEBUG("   src2: ");
680         src2 = atom_get_src(ctx, attr, ptr);
681         dst &= src1;
682         dst |= src2;
683         SDEBUG("   dst: ");
684         atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
685 }
686
687 static void atom_op_move(atom_exec_context *ctx, int *ptr, int arg)
688 {
689         uint8_t attr = U8((*ptr)++);
690         uint32_t src, saved;
691         int dptr = *ptr;
692         if (((attr >> 3) & 7) != ATOM_SRC_DWORD)
693                 atom_get_dst(ctx, arg, attr, ptr, &saved, 0);
694         else {
695                 atom_skip_dst(ctx, arg, attr, ptr);
696                 saved = 0xCDCDCDCD;
697         }
698         SDEBUG("   src: ");
699         src = atom_get_src(ctx, attr, ptr);
700         SDEBUG("   dst: ");
701         atom_put_dst(ctx, arg, attr, &dptr, src, saved);
702 }
703
704 static void atom_op_mul(atom_exec_context *ctx, int *ptr, int arg)
705 {
706         uint8_t attr = U8((*ptr)++);
707         uint32_t dst, src;
708         SDEBUG("   src1: ");
709         dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1);
710         SDEBUG("   src2: ");
711         src = atom_get_src(ctx, attr, ptr);
712         ctx->ctx->divmul[0] = dst * src;
713 }
714
715 static void atom_op_nop(atom_exec_context *ctx, int *ptr, int arg)
716 {
717         /* nothing */
718 }
719
720 static void atom_op_or(atom_exec_context *ctx, int *ptr, int arg)
721 {
722         uint8_t attr = U8((*ptr)++);
723         uint32_t dst, src, saved;
724         int dptr = *ptr;
725         SDEBUG("   dst: ");
726         dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
727         SDEBUG("   src: ");
728         src = atom_get_src(ctx, attr, ptr);
729         dst |= src;
730         SDEBUG("   dst: ");
731         atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
732 }
733
734 static void atom_op_postcard(atom_exec_context *ctx, int *ptr, int arg)
735 {
736         uint8_t val = U8((*ptr)++);
737         SDEBUG("POST card output: 0x%02X\n", val);
738 }
739
740 static void atom_op_repeat(atom_exec_context *ctx, int *ptr, int arg)
741 {
742         printk(KERN_INFO "unimplemented!\n");
743 }
744
745 static void atom_op_restorereg(atom_exec_context *ctx, int *ptr, int arg)
746 {
747         printk(KERN_INFO "unimplemented!\n");
748 }
749
750 static void atom_op_savereg(atom_exec_context *ctx, int *ptr, int arg)
751 {
752         printk(KERN_INFO "unimplemented!\n");
753 }
754
755 static void atom_op_setdatablock(atom_exec_context *ctx, int *ptr, int arg)
756 {
757         int idx = U8(*ptr);
758         (*ptr)++;
759         SDEBUG("   block: %d\n", idx);
760         if (!idx)
761                 ctx->ctx->data_block = 0;
762         else if (idx == 255)
763                 ctx->ctx->data_block = ctx->start;
764         else
765                 ctx->ctx->data_block = U16(ctx->ctx->data_table + 4 + 2 * idx);
766         SDEBUG("   base: 0x%04X\n", ctx->ctx->data_block);
767 }
768
769 static void atom_op_setfbbase(atom_exec_context *ctx, int *ptr, int arg)
770 {
771         uint8_t attr = U8((*ptr)++);
772         SDEBUG("   fb_base: ");
773         ctx->ctx->fb_base = atom_get_src(ctx, attr, ptr);
774 }
775
776 static void atom_op_setport(atom_exec_context *ctx, int *ptr, int arg)
777 {
778         int port;
779         switch (arg) {
780         case ATOM_PORT_ATI:
781                 port = U16(*ptr);
782                 if (port < ATOM_IO_NAMES_CNT)
783                         SDEBUG("   port: %d (%s)\n", port, atom_io_names[port]);
784                 else
785                         SDEBUG("   port: %d\n", port);
786                 if (!port)
787                         ctx->ctx->io_mode = ATOM_IO_MM;
788                 else
789                         ctx->ctx->io_mode = ATOM_IO_IIO | port;
790                 (*ptr) += 2;
791                 break;
792         case ATOM_PORT_PCI:
793                 ctx->ctx->io_mode = ATOM_IO_PCI;
794                 (*ptr)++;
795                 break;
796         case ATOM_PORT_SYSIO:
797                 ctx->ctx->io_mode = ATOM_IO_SYSIO;
798                 (*ptr)++;
799                 break;
800         }
801 }
802
803 static void atom_op_setregblock(atom_exec_context *ctx, int *ptr, int arg)
804 {
805         ctx->ctx->reg_block = U16(*ptr);
806         (*ptr) += 2;
807         SDEBUG("   base: 0x%04X\n", ctx->ctx->reg_block);
808 }
809
810 static void atom_op_shl(atom_exec_context *ctx, int *ptr, int arg)
811 {
812         uint8_t attr = U8((*ptr)++), shift;
813         uint32_t saved, dst;
814         int dptr = *ptr;
815         attr &= 0x38;
816         attr |= atom_def_dst[attr >> 3] << 6;
817         SDEBUG("   dst: ");
818         dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
819         shift = U8((*ptr)++);
820         SDEBUG("   shift: %d\n", shift);
821         dst <<= shift;
822         SDEBUG("   dst: ");
823         atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
824 }
825
826 static void atom_op_shr(atom_exec_context *ctx, int *ptr, int arg)
827 {
828         uint8_t attr = U8((*ptr)++), shift;
829         uint32_t saved, dst;
830         int dptr = *ptr;
831         attr &= 0x38;
832         attr |= atom_def_dst[attr >> 3] << 6;
833         SDEBUG("   dst: ");
834         dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
835         shift = U8((*ptr)++);
836         SDEBUG("   shift: %d\n", shift);
837         dst >>= shift;
838         SDEBUG("   dst: ");
839         atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
840 }
841
842 static void atom_op_sub(atom_exec_context *ctx, int *ptr, int arg)
843 {
844         uint8_t attr = U8((*ptr)++);
845         uint32_t dst, src, saved;
846         int dptr = *ptr;
847         SDEBUG("   dst: ");
848         dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
849         SDEBUG("   src: ");
850         src = atom_get_src(ctx, attr, ptr);
851         dst -= src;
852         SDEBUG("   dst: ");
853         atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
854 }
855
856 static void atom_op_switch(atom_exec_context *ctx, int *ptr, int arg)
857 {
858         uint8_t attr = U8((*ptr)++);
859         uint32_t src, val, target;
860         SDEBUG("   switch: ");
861         src = atom_get_src(ctx, attr, ptr);
862         while (U16(*ptr) != ATOM_CASE_END)
863                 if (U8(*ptr) == ATOM_CASE_MAGIC) {
864                         (*ptr)++;
865                         SDEBUG("   case: ");
866                         val =
867                             atom_get_src(ctx, (attr & 0x38) | ATOM_ARG_IMM,
868                                          ptr);
869                         target = U16(*ptr);
870                         if (val == src) {
871                                 SDEBUG("   target: %04X\n", target);
872                                 *ptr = ctx->start + target;
873                                 return;
874                         }
875                         (*ptr) += 2;
876                 } else {
877                         printk(KERN_INFO "Bad case.\n");
878                         return;
879                 }
880         (*ptr) += 2;
881 }
882
883 static void atom_op_test(atom_exec_context *ctx, int *ptr, int arg)
884 {
885         uint8_t attr = U8((*ptr)++);
886         uint32_t dst, src;
887         SDEBUG("   src1: ");
888         dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1);
889         SDEBUG("   src2: ");
890         src = atom_get_src(ctx, attr, ptr);
891         ctx->ctx->cs_equal = ((dst & src) == 0);
892         SDEBUG("   result: %s\n", ctx->ctx->cs_equal ? "EQ" : "NE");
893 }
894
895 static void atom_op_xor(atom_exec_context *ctx, int *ptr, int arg)
896 {
897         uint8_t attr = U8((*ptr)++);
898         uint32_t dst, src, saved;
899         int dptr = *ptr;
900         SDEBUG("   dst: ");
901         dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
902         SDEBUG("   src: ");
903         src = atom_get_src(ctx, attr, ptr);
904         dst ^= src;
905         SDEBUG("   dst: ");
906         atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
907 }
908
909 static void atom_op_debug(atom_exec_context *ctx, int *ptr, int arg)
910 {
911         printk(KERN_INFO "unimplemented!\n");
912 }
913
914 static struct {
915         void (*func) (atom_exec_context *, int *, int);
916         int arg;
917 } opcode_table[ATOM_OP_CNT] = {
918         {
919         NULL, 0}, {
920         atom_op_move, ATOM_ARG_REG}, {
921         atom_op_move, ATOM_ARG_PS}, {
922         atom_op_move, ATOM_ARG_WS}, {
923         atom_op_move, ATOM_ARG_FB}, {
924         atom_op_move, ATOM_ARG_PLL}, {
925         atom_op_move, ATOM_ARG_MC}, {
926         atom_op_and, ATOM_ARG_REG}, {
927         atom_op_and, ATOM_ARG_PS}, {
928         atom_op_and, ATOM_ARG_WS}, {
929         atom_op_and, ATOM_ARG_FB}, {
930         atom_op_and, ATOM_ARG_PLL}, {
931         atom_op_and, ATOM_ARG_MC}, {
932         atom_op_or, ATOM_ARG_REG}, {
933         atom_op_or, ATOM_ARG_PS}, {
934         atom_op_or, ATOM_ARG_WS}, {
935         atom_op_or, ATOM_ARG_FB}, {
936         atom_op_or, ATOM_ARG_PLL}, {
937         atom_op_or, ATOM_ARG_MC}, {
938         atom_op_shl, ATOM_ARG_REG}, {
939         atom_op_shl, ATOM_ARG_PS}, {
940         atom_op_shl, ATOM_ARG_WS}, {
941         atom_op_shl, ATOM_ARG_FB}, {
942         atom_op_shl, ATOM_ARG_PLL}, {
943         atom_op_shl, ATOM_ARG_MC}, {
944         atom_op_shr, ATOM_ARG_REG}, {
945         atom_op_shr, ATOM_ARG_PS}, {
946         atom_op_shr, ATOM_ARG_WS}, {
947         atom_op_shr, ATOM_ARG_FB}, {
948         atom_op_shr, ATOM_ARG_PLL}, {
949         atom_op_shr, ATOM_ARG_MC}, {
950         atom_op_mul, ATOM_ARG_REG}, {
951         atom_op_mul, ATOM_ARG_PS}, {
952         atom_op_mul, ATOM_ARG_WS}, {
953         atom_op_mul, ATOM_ARG_FB}, {
954         atom_op_mul, ATOM_ARG_PLL}, {
955         atom_op_mul, ATOM_ARG_MC}, {
956         atom_op_div, ATOM_ARG_REG}, {
957         atom_op_div, ATOM_ARG_PS}, {
958         atom_op_div, ATOM_ARG_WS}, {
959         atom_op_div, ATOM_ARG_FB}, {
960         atom_op_div, ATOM_ARG_PLL}, {
961         atom_op_div, ATOM_ARG_MC}, {
962         atom_op_add, ATOM_ARG_REG}, {
963         atom_op_add, ATOM_ARG_PS}, {
964         atom_op_add, ATOM_ARG_WS}, {
965         atom_op_add, ATOM_ARG_FB}, {
966         atom_op_add, ATOM_ARG_PLL}, {
967         atom_op_add, ATOM_ARG_MC}, {
968         atom_op_sub, ATOM_ARG_REG}, {
969         atom_op_sub, ATOM_ARG_PS}, {
970         atom_op_sub, ATOM_ARG_WS}, {
971         atom_op_sub, ATOM_ARG_FB}, {
972         atom_op_sub, ATOM_ARG_PLL}, {
973         atom_op_sub, ATOM_ARG_MC}, {
974         atom_op_setport, ATOM_PORT_ATI}, {
975         atom_op_setport, ATOM_PORT_PCI}, {
976         atom_op_setport, ATOM_PORT_SYSIO}, {
977         atom_op_setregblock, 0}, {
978         atom_op_setfbbase, 0}, {
979         atom_op_compare, ATOM_ARG_REG}, {
980         atom_op_compare, ATOM_ARG_PS}, {
981         atom_op_compare, ATOM_ARG_WS}, {
982         atom_op_compare, ATOM_ARG_FB}, {
983         atom_op_compare, ATOM_ARG_PLL}, {
984         atom_op_compare, ATOM_ARG_MC}, {
985         atom_op_switch, 0}, {
986         atom_op_jump, ATOM_COND_ALWAYS}, {
987         atom_op_jump, ATOM_COND_EQUAL}, {
988         atom_op_jump, ATOM_COND_BELOW}, {
989         atom_op_jump, ATOM_COND_ABOVE}, {
990         atom_op_jump, ATOM_COND_BELOWOREQUAL}, {
991         atom_op_jump, ATOM_COND_ABOVEOREQUAL}, {
992         atom_op_jump, ATOM_COND_NOTEQUAL}, {
993         atom_op_test, ATOM_ARG_REG}, {
994         atom_op_test, ATOM_ARG_PS}, {
995         atom_op_test, ATOM_ARG_WS}, {
996         atom_op_test, ATOM_ARG_FB}, {
997         atom_op_test, ATOM_ARG_PLL}, {
998         atom_op_test, ATOM_ARG_MC}, {
999         atom_op_delay, ATOM_UNIT_MILLISEC}, {
1000         atom_op_delay, ATOM_UNIT_MICROSEC}, {
1001         atom_op_calltable, 0}, {
1002         atom_op_repeat, 0}, {
1003         atom_op_clear, ATOM_ARG_REG}, {
1004         atom_op_clear, ATOM_ARG_PS}, {
1005         atom_op_clear, ATOM_ARG_WS}, {
1006         atom_op_clear, ATOM_ARG_FB}, {
1007         atom_op_clear, ATOM_ARG_PLL}, {
1008         atom_op_clear, ATOM_ARG_MC}, {
1009         atom_op_nop, 0}, {
1010         atom_op_eot, 0}, {
1011         atom_op_mask, ATOM_ARG_REG}, {
1012         atom_op_mask, ATOM_ARG_PS}, {
1013         atom_op_mask, ATOM_ARG_WS}, {
1014         atom_op_mask, ATOM_ARG_FB}, {
1015         atom_op_mask, ATOM_ARG_PLL}, {
1016         atom_op_mask, ATOM_ARG_MC}, {
1017         atom_op_postcard, 0}, {
1018         atom_op_beep, 0}, {
1019         atom_op_savereg, 0}, {
1020         atom_op_restorereg, 0}, {
1021         atom_op_setdatablock, 0}, {
1022         atom_op_xor, ATOM_ARG_REG}, {
1023         atom_op_xor, ATOM_ARG_PS}, {
1024         atom_op_xor, ATOM_ARG_WS}, {
1025         atom_op_xor, ATOM_ARG_FB}, {
1026         atom_op_xor, ATOM_ARG_PLL}, {
1027         atom_op_xor, ATOM_ARG_MC}, {
1028         atom_op_shl, ATOM_ARG_REG}, {
1029         atom_op_shl, ATOM_ARG_PS}, {
1030         atom_op_shl, ATOM_ARG_WS}, {
1031         atom_op_shl, ATOM_ARG_FB}, {
1032         atom_op_shl, ATOM_ARG_PLL}, {
1033         atom_op_shl, ATOM_ARG_MC}, {
1034         atom_op_shr, ATOM_ARG_REG}, {
1035         atom_op_shr, ATOM_ARG_PS}, {
1036         atom_op_shr, ATOM_ARG_WS}, {
1037         atom_op_shr, ATOM_ARG_FB}, {
1038         atom_op_shr, ATOM_ARG_PLL}, {
1039         atom_op_shr, ATOM_ARG_MC}, {
1040 atom_op_debug, 0},};
1041
1042 void atom_execute_table(struct atom_context *ctx, int index, uint32_t * params)
1043 {
1044         int base = CU16(ctx->cmd_table + 4 + 2 * index);
1045         int len, ws, ps, ptr;
1046         unsigned char op;
1047         atom_exec_context ectx;
1048
1049         if (!base)
1050                 return;
1051
1052         len = CU16(base + ATOM_CT_SIZE_PTR);
1053         ws = CU8(base + ATOM_CT_WS_PTR);
1054         ps = CU8(base + ATOM_CT_PS_PTR) & ATOM_CT_PS_MASK;
1055         ptr = base + ATOM_CT_CODE_PTR;
1056
1057         SDEBUG(">> execute %04X (len %d, WS %d, PS %d)\n", base, len, ws, ps);
1058
1059         /* reset reg block */
1060         ctx->reg_block = 0;
1061         ectx.ctx = ctx;
1062         ectx.ps_shift = ps / 4;
1063         ectx.start = base;
1064         ectx.ps = params;
1065         if (ws)
1066                 ectx.ws = kzalloc(4 * ws, GFP_KERNEL);
1067         else
1068                 ectx.ws = NULL;
1069
1070         debug_depth++;
1071         while (1) {
1072                 op = CU8(ptr++);
1073                 if (op < ATOM_OP_NAMES_CNT)
1074                         SDEBUG("%s @ 0x%04X\n", atom_op_names[op], ptr - 1);
1075                 else
1076                         SDEBUG("[%d] @ 0x%04X\n", op, ptr - 1);
1077
1078                 if (op < ATOM_OP_CNT && op > 0)
1079                         opcode_table[op].func(&ectx, &ptr,
1080                                               opcode_table[op].arg);
1081                 else
1082                         break;
1083
1084                 if (op == ATOM_OP_EOT)
1085                         break;
1086         }
1087         debug_depth--;
1088         SDEBUG("<<\n");
1089
1090         if (ws)
1091                 kfree(ectx.ws);
1092 }
1093
1094 static int atom_iio_len[] = { 1, 2, 3, 3, 3, 3, 4, 4, 4, 3 };
1095
1096 static void atom_index_iio(struct atom_context *ctx, int base)
1097 {
1098         ctx->iio = kzalloc(2 * 256, GFP_KERNEL);
1099         while (CU8(base) == ATOM_IIO_START) {
1100                 ctx->iio[CU8(base + 1)] = base + 2;
1101                 base += 2;
1102                 while (CU8(base) != ATOM_IIO_END)
1103                         base += atom_iio_len[CU8(base)];
1104                 base += 3;
1105         }
1106 }
1107
1108 struct atom_context *atom_parse(struct card_info *card, void *bios)
1109 {
1110         int base;
1111         struct atom_context *ctx =
1112             kzalloc(sizeof(struct atom_context), GFP_KERNEL);
1113         char *str;
1114         char name[512];
1115         int i;
1116
1117         ctx->card = card;
1118         ctx->bios = bios;
1119
1120         if (CU16(0) != ATOM_BIOS_MAGIC) {
1121                 printk(KERN_INFO "Invalid BIOS magic.\n");
1122                 kfree(ctx);
1123                 return NULL;
1124         }
1125         if (strncmp
1126             (CSTR(ATOM_ATI_MAGIC_PTR), ATOM_ATI_MAGIC,
1127              strlen(ATOM_ATI_MAGIC))) {
1128                 printk(KERN_INFO "Invalid ATI magic.\n");
1129                 kfree(ctx);
1130                 return NULL;
1131         }
1132
1133         base = CU16(ATOM_ROM_TABLE_PTR);
1134         if (strncmp
1135             (CSTR(base + ATOM_ROM_MAGIC_PTR), ATOM_ROM_MAGIC,
1136              strlen(ATOM_ROM_MAGIC))) {
1137                 printk(KERN_INFO "Invalid ATOM magic.\n");
1138                 kfree(ctx);
1139                 return NULL;
1140         }
1141
1142         ctx->cmd_table = CU16(base + ATOM_ROM_CMD_PTR);
1143         ctx->data_table = CU16(base + ATOM_ROM_DATA_PTR);
1144         atom_index_iio(ctx, CU16(ctx->data_table + ATOM_DATA_IIO_PTR) + 4);
1145
1146         str = CSTR(CU16(base + ATOM_ROM_MSG_PTR));
1147         while (*str && ((*str == '\n') || (*str == '\r')))
1148                 str++;
1149         /* name string isn't always 0 terminated */
1150         for (i = 0; i < 511; i++) {
1151                 name[i] = str[i];
1152                 if (name[i] < '.' || name[i] > 'z') {
1153                         name[i] = 0;
1154                         break;
1155                 }
1156         }
1157         printk(KERN_INFO "ATOM BIOS: %s\n", name);
1158
1159         return ctx;
1160 }
1161
1162 int atom_asic_init(struct atom_context *ctx)
1163 {
1164         int hwi = CU16(ctx->data_table + ATOM_DATA_FWI_PTR);
1165         uint32_t ps[16];
1166         memset(ps, 0, 64);
1167
1168         ps[0] = cpu_to_le32(CU32(hwi + ATOM_FWI_DEFSCLK_PTR));
1169         ps[1] = cpu_to_le32(CU32(hwi + ATOM_FWI_DEFMCLK_PTR));
1170         if (!ps[0] || !ps[1])
1171                 return 1;
1172
1173         if (!CU16(ctx->cmd_table + 4 + 2 * ATOM_CMD_INIT))
1174                 return 1;
1175         atom_execute_table(ctx, ATOM_CMD_INIT, ps);
1176
1177         return 0;
1178 }
1179
1180 void atom_destroy(struct atom_context *ctx)
1181 {
1182         if (ctx->iio)
1183                 kfree(ctx->iio);
1184         kfree(ctx);
1185 }
1186
1187 void atom_parse_data_header(struct atom_context *ctx, int index,
1188                             uint16_t * size, uint8_t * frev, uint8_t * crev,
1189                             uint16_t * data_start)
1190 {
1191         int offset = index * 2 + 4;
1192         int idx = CU16(ctx->data_table + offset);
1193
1194         if (size)
1195                 *size = CU16(idx);
1196         if (frev)
1197                 *frev = CU8(idx + 2);
1198         if (crev)
1199                 *crev = CU8(idx + 3);
1200         *data_start = idx;
1201         return;
1202 }
1203
1204 void atom_parse_cmd_header(struct atom_context *ctx, int index, uint8_t * frev,
1205                            uint8_t * crev)
1206 {
1207         int offset = index * 2 + 4;
1208         int idx = CU16(ctx->cmd_table + offset);
1209
1210         if (frev)
1211                 *frev = CU8(idx + 2);
1212         if (crev)
1213                 *crev = CU8(idx + 3);
1214         return;
1215 }