OSDN Git Service

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