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