projects/linux·blob master · refs

1720 lines · 41.8 KiB

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#include <linux/slab.h>
28#include <linux/string_helpers.h>
29
30#include <linux/unaligned.h>
31
32#include <drm/drm_util.h>
33
34#define ATOM_DEBUG
35
36#include "atomfirmware.h"
37#include "atom.h"
38#include "atom-names.h"
39#include "atom-bits.h"
40#include "amdgpu.h"
41
42#define ATOM_COND_ABOVE 0
43#define ATOM_COND_ABOVEOREQUAL 1
44#define ATOM_COND_ALWAYS 2
45#define ATOM_COND_BELOW 3
46#define ATOM_COND_BELOWOREQUAL 4
47#define ATOM_COND_EQUAL 5
48#define ATOM_COND_NOTEQUAL 6
49
50#define ATOM_PORT_ATI 0
51#define ATOM_PORT_PCI 1
52#define ATOM_PORT_SYSIO 2
53
54#define ATOM_UNIT_MICROSEC 0
55#define ATOM_UNIT_MILLISEC 1
56
57#define PLL_INDEX 2
58#define PLL_DATA 3
59
60#define ATOM_CMD_TIMEOUT_SEC 20
61
62/* Limit ATOM command table recursion (calltable) to avoid kernel stack overflow. */
63#define ATOM_EXECUTE_MAX_DEPTH 32
64
65typedef struct {
66 struct atom_context *ctx;
67 uint32_t *ps, *ws;
68 int ps_size, ws_size;
69 int ps_shift;
70 uint16_t start;
71 unsigned last_jump;
72 unsigned long last_jump_jiffies;
73 bool abort;
74} atom_exec_context;
75
76int amdgpu_atom_debug;
77static int amdgpu_atom_execute_table_locked(struct atom_context *ctx, int index, uint32_t *params, int params_size);
78int amdgpu_atom_execute_table(struct atom_context *ctx, int index, uint32_t *params, int params_size);
79
80static uint32_t atom_arg_mask[8] =
81 { 0xFFFFFFFF, 0xFFFF, 0xFFFF00, 0xFFFF0000, 0xFF, 0xFF00, 0xFF0000,
82 0xFF000000 };
83static int atom_arg_shift[8] = { 0, 0, 8, 16, 0, 8, 16, 24 };
84
85static int atom_dst_to_src[8][4] = {
86 /* translate destination alignment field to the source alignment encoding */
87 {0, 0, 0, 0},
88 {1, 2, 3, 0},
89 {1, 2, 3, 0},
90 {1, 2, 3, 0},
91 {4, 5, 6, 7},
92 {4, 5, 6, 7},
93 {4, 5, 6, 7},
94 {4, 5, 6, 7},
95};
96static int atom_def_dst[8] = { 0, 0, 1, 2, 0, 1, 2, 3 };
97
98static int debug_depth;
99#ifdef ATOM_DEBUG
100static void debug_print_spaces(int n)
101{
102 while (n--)
103 printk(" ");
104}
105
106#define DEBUG(...) do if (amdgpu_atom_debug) { printk(KERN_DEBUG __VA_ARGS__); } while (0)
107#define SDEBUG(...) do if (amdgpu_atom_debug) { printk(KERN_DEBUG); debug_print_spaces(debug_depth); printk(__VA_ARGS__); } while (0)
108#else
109#define DEBUG(...) do { } while (0)
110#define SDEBUG(...) do { } while (0)
111#endif
112
113static uint32_t atom_iio_execute(struct atom_context *ctx, int base,
114 uint32_t index, uint32_t data)
115{
116 uint32_t temp = 0xCDCDCDCD;
117 int start = base;
118
119 /* IIO opcodes read up to base+3; keep within the BIOS image */
120 while (base + 3 < ctx->bios_size)
121 switch (CU8(base)) {
122 case ATOM_IIO_NOP:
123 base++;
124 break;
125 case ATOM_IIO_READ:
126 temp = ctx->card->reg_read(ctx->card, CU16(base + 1));
127 base += 3;
128 break;
129 case ATOM_IIO_WRITE:
130 ctx->card->reg_write(ctx->card, CU16(base + 1), temp);
131 base += 3;
132 break;
133 case ATOM_IIO_CLEAR:
134 temp &=
135 ~((0xFFFFFFFF >> (32 - CU8(base + 1))) <<
136 CU8(base + 2));
137 base += 3;
138 break;
139 case ATOM_IIO_SET:
140 temp |=
141 (0xFFFFFFFF >> (32 - CU8(base + 1))) << CU8(base +
142 2);
143 base += 3;
144 break;
145 case ATOM_IIO_MOVE_INDEX:
146 temp &=
147 ~((0xFFFFFFFF >> (32 - CU8(base + 1))) <<
148 CU8(base + 3));
149 temp |=
150 ((index >> CU8(base + 2)) &
151 (0xFFFFFFFF >> (32 - CU8(base + 1)))) << CU8(base +
152 3);
153 base += 4;
154 break;
155 case ATOM_IIO_MOVE_DATA:
156 temp &=
157 ~((0xFFFFFFFF >> (32 - CU8(base + 1))) <<
158 CU8(base + 3));
159 temp |=
160 ((data >> CU8(base + 2)) &
161 (0xFFFFFFFF >> (32 - CU8(base + 1)))) << CU8(base +
162 3);
163 base += 4;
164 break;
165 case ATOM_IIO_MOVE_ATTR:
166 temp &=
167 ~((0xFFFFFFFF >> (32 - CU8(base + 1))) <<
168 CU8(base + 3));
169 temp |=
170 ((ctx->
171 io_attr >> CU8(base + 2)) & (0xFFFFFFFF >> (32 -
172 CU8
173 (base
174 +
175 1))))
176 << CU8(base + 3);
177 base += 4;
178 break;
179 case ATOM_IIO_END:
180 return temp;
181 default:
182 pr_info("Unknown IIO opcode\n");
183 return 0;
184 }
185
186 pr_info("IIO method starting at offset %d runs past BIOS image\n", start);
187 return 0;
188}
189
190static uint32_t atom_get_src_int(atom_exec_context *ctx, uint8_t attr,
191 int *ptr, uint32_t *saved, int print)
192{
193 uint32_t idx, val = 0xCDCDCDCD, align, arg;
194 struct atom_context *gctx = ctx->ctx;
195 arg = attr & 7;
196 align = (attr >> 3) & 7;
197 switch (arg) {
198 case ATOM_ARG_REG:
199 idx = U16(*ptr);
200 (*ptr) += 2;
201 if (print)
202 DEBUG("REG[0x%04X]", idx);
203 idx += gctx->reg_block;
204 switch (gctx->io_mode) {
205 case ATOM_IO_MM:
206 val = gctx->card->reg_read(gctx->card, idx);
207 break;
208 case ATOM_IO_PCI:
209 pr_info("PCI registers are not implemented\n");
210 return 0;
211 case ATOM_IO_SYSIO:
212 pr_info("SYSIO registers are not implemented\n");
213 return 0;
214 default:
215 if (!(gctx->io_mode & 0x80)) {
216 pr_info("Bad IO mode\n");
217 return 0;
218 }
219 if (!gctx->iio[gctx->io_mode & 0x7F]) {
220 pr_info("Undefined indirect IO read method %d\n",
221 gctx->io_mode & 0x7F);
222 return 0;
223 }
224 val =
225 atom_iio_execute(gctx,
226 gctx->iio[gctx->io_mode & 0x7F],
227 idx, 0);
228 }
229 break;
230 case ATOM_ARG_PS:
231 idx = U8(*ptr);
232 (*ptr)++;
233 /* get_unaligned_le32 avoids unaligned accesses from atombios
234 * tables, noticed on a DEC Alpha. */
235 if (idx < ctx->ps_size)
236 val = get_unaligned_le32((u32 *)&ctx->ps[idx]);
237 else
238 pr_info("PS index out of range: %i > %i\n", idx, ctx->ps_size);
239 if (print)
240 DEBUG("PS[0x%02X,0x%04X]", idx, val);
241 break;
242 case ATOM_ARG_WS:
243 idx = U8(*ptr);
244 (*ptr)++;
245 if (print)
246 DEBUG("WS[0x%02X]", idx);
247 switch (idx) {
248 case ATOM_WS_QUOTIENT:
249 val = gctx->divmul[0];
250 break;
251 case ATOM_WS_REMAINDER:
252 val = gctx->divmul[1];
253 break;
254 case ATOM_WS_DATAPTR:
255 val = gctx->data_block;
256 break;
257 case ATOM_WS_SHIFT:
258 val = gctx->shift;
259 break;
260 case ATOM_WS_OR_MASK:
261 val = 1 << gctx->shift;
262 break;
263 case ATOM_WS_AND_MASK:
264 val = ~(1 << gctx->shift);
265 break;
266 case ATOM_WS_FB_WINDOW:
267 val = gctx->fb_base;
268 break;
269 case ATOM_WS_ATTRIBUTES:
270 val = gctx->io_attr;
271 break;
272 case ATOM_WS_REGPTR:
273 val = gctx->reg_block;
274 break;
275 default:
276 if (idx < ctx->ws_size)
277 val = ctx->ws[idx];
278 else
279 pr_info("WS index out of range: %i > %i\n", idx, ctx->ws_size);
280 }
281 break;
282 case ATOM_ARG_ID:
283 idx = U16(*ptr);
284 (*ptr) += 2;
285 if (print) {
286 if (gctx->data_block)
287 DEBUG("ID[0x%04X+%04X]", idx, gctx->data_block);
288 else
289 DEBUG("ID[0x%04X]", idx);
290 }
291 val = U32(idx + gctx->data_block);
292 break;
293 case ATOM_ARG_FB:
294 idx = U8(*ptr);
295 (*ptr)++;
296 if ((gctx->fb_base + (idx * 4)) > gctx->scratch_size_bytes) {
297 DRM_ERROR("ATOM: fb read beyond scratch region: %d vs. %d\n",
298 gctx->fb_base + (idx * 4), gctx->scratch_size_bytes);
299 val = 0;
300 } else
301 val = gctx->scratch[(gctx->fb_base / 4) + idx];
302 if (print)
303 DEBUG("FB[0x%02X]", idx);
304 break;
305 case ATOM_ARG_IMM:
306 switch (align) {
307 case ATOM_SRC_DWORD:
308 val = U32(*ptr);
309 (*ptr) += 4;
310 if (print)
311 DEBUG("IMM 0x%08X\n", val);
312 break;
313 case ATOM_SRC_WORD0:
314 case ATOM_SRC_WORD8:
315 case ATOM_SRC_WORD16:
316 val = U16(*ptr);
317 (*ptr) += 2;
318 if (print)
319 DEBUG("IMM 0x%04X\n", val);
320 break;
321 case ATOM_SRC_BYTE0:
322 case ATOM_SRC_BYTE8:
323 case ATOM_SRC_BYTE16:
324 case ATOM_SRC_BYTE24:
325 val = U8(*ptr);
326 (*ptr)++;
327 if (print)
328 DEBUG("IMM 0x%02X\n", val);
329 break;
330 }
331 return val;
332 case ATOM_ARG_PLL:
333 idx = U8(*ptr);
334 (*ptr)++;
335 if (print)
336 DEBUG("PLL[0x%02X]", idx);
337 val = gctx->card->pll_read(gctx->card, idx);
338 break;
339 case ATOM_ARG_MC:
340 idx = U8(*ptr);
341 (*ptr)++;
342 if (print)
343 DEBUG("MC[0x%02X]", idx);
344 val = gctx->card->mc_read(gctx->card, idx);
345 break;
346 }
347 if (saved)
348 *saved = val;
349 val &= atom_arg_mask[align];
350 val >>= atom_arg_shift[align];
351 if (print)
352 switch (align) {
353 case ATOM_SRC_DWORD:
354 DEBUG(".[31:0] -> 0x%08X\n", val);
355 break;
356 case ATOM_SRC_WORD0:
357 DEBUG(".[15:0] -> 0x%04X\n", val);
358 break;
359 case ATOM_SRC_WORD8:
360 DEBUG(".[23:8] -> 0x%04X\n", val);
361 break;
362 case ATOM_SRC_WORD16:
363 DEBUG(".[31:16] -> 0x%04X\n", val);
364 break;
365 case ATOM_SRC_BYTE0:
366 DEBUG(".[7:0] -> 0x%02X\n", val);
367 break;
368 case ATOM_SRC_BYTE8:
369 DEBUG(".[15:8] -> 0x%02X\n", val);
370 break;
371 case ATOM_SRC_BYTE16:
372 DEBUG(".[23:16] -> 0x%02X\n", val);
373 break;
374 case ATOM_SRC_BYTE24:
375 DEBUG(".[31:24] -> 0x%02X\n", val);
376 break;
377 }
378 return val;
379}
380
381static void atom_skip_src_int(atom_exec_context *ctx, uint8_t attr, int *ptr)
382{
383 uint32_t align = (attr >> 3) & 7, arg = attr & 7;
384 switch (arg) {
385 case ATOM_ARG_REG:
386 case ATOM_ARG_ID:
387 (*ptr) += 2;
388 break;
389 case ATOM_ARG_PLL:
390 case ATOM_ARG_MC:
391 case ATOM_ARG_PS:
392 case ATOM_ARG_WS:
393 case ATOM_ARG_FB:
394 (*ptr)++;
395 break;
396 case ATOM_ARG_IMM:
397 switch (align) {
398 case ATOM_SRC_DWORD:
399 (*ptr) += 4;
400 return;
401 case ATOM_SRC_WORD0:
402 case ATOM_SRC_WORD8:
403 case ATOM_SRC_WORD16:
404 (*ptr) += 2;
405 return;
406 case ATOM_SRC_BYTE0:
407 case ATOM_SRC_BYTE8:
408 case ATOM_SRC_BYTE16:
409 case ATOM_SRC_BYTE24:
410 (*ptr)++;
411 return;
412 }
413 }
414}
415
416static uint32_t atom_get_src(atom_exec_context *ctx, uint8_t attr, int *ptr)
417{
418 return atom_get_src_int(ctx, attr, ptr, NULL, 1);
419}
420
421static uint32_t atom_get_src_direct(atom_exec_context *ctx, uint8_t align, int *ptr)
422{
423 uint32_t val = 0xCDCDCDCD;
424
425 switch (align) {
426 case ATOM_SRC_DWORD:
427 val = U32(*ptr);
428 (*ptr) += 4;
429 break;
430 case ATOM_SRC_WORD0:
431 case ATOM_SRC_WORD8:
432 case ATOM_SRC_WORD16:
433 val = U16(*ptr);
434 (*ptr) += 2;
435 break;
436 case ATOM_SRC_BYTE0:
437 case ATOM_SRC_BYTE8:
438 case ATOM_SRC_BYTE16:
439 case ATOM_SRC_BYTE24:
440 val = U8(*ptr);
441 (*ptr)++;
442 break;
443 }
444 return val;
445}
446
447static uint32_t atom_get_dst(atom_exec_context *ctx, int arg, uint8_t attr,
448 int *ptr, uint32_t *saved, int print)
449{
450 return atom_get_src_int(ctx,
451 arg | atom_dst_to_src[(attr >> 3) &
452 7][(attr >> 6) & 3] << 3,
453 ptr, saved, print);
454}
455
456static void atom_skip_dst(atom_exec_context *ctx, int arg, uint8_t attr, int *ptr)
457{
458 atom_skip_src_int(ctx,
459 arg | atom_dst_to_src[(attr >> 3) & 7][(attr >> 6) &
460 3] << 3, ptr);
461}
462
463static void atom_put_dst(atom_exec_context *ctx, int arg, uint8_t attr,
464 int *ptr, uint32_t val, uint32_t saved)
465{
466 uint32_t align =
467 atom_dst_to_src[(attr >> 3) & 7][(attr >> 6) & 3], old_val =
468 val, idx;
469 struct atom_context *gctx = ctx->ctx;
470 old_val &= atom_arg_mask[align] >> atom_arg_shift[align];
471 val <<= atom_arg_shift[align];
472 val &= atom_arg_mask[align];
473 saved &= ~atom_arg_mask[align];
474 val |= saved;
475 switch (arg) {
476 case ATOM_ARG_REG:
477 idx = U16(*ptr);
478 (*ptr) += 2;
479 DEBUG("REG[0x%04X]", idx);
480 idx += gctx->reg_block;
481 switch (gctx->io_mode) {
482 case ATOM_IO_MM:
483 if (idx == 0)
484 gctx->card->reg_write(gctx->card, idx,
485 val << 2);
486 else
487 gctx->card->reg_write(gctx->card, idx, val);
488 break;
489 case ATOM_IO_PCI:
490 pr_info("PCI registers are not implemented\n");
491 return;
492 case ATOM_IO_SYSIO:
493 pr_info("SYSIO registers are not implemented\n");
494 return;
495 default:
496 if (!(gctx->io_mode & 0x80)) {
497 pr_info("Bad IO mode\n");
498 return;
499 }
500 if (!gctx->iio[gctx->io_mode & 0xFF]) {
501 pr_info("Undefined indirect IO write method %d\n",
502 gctx->io_mode & 0x7F);
503 return;
504 }
505 atom_iio_execute(gctx, gctx->iio[gctx->io_mode & 0xFF],
506 idx, val);
507 }
508 break;
509 case ATOM_ARG_PS:
510 idx = U8(*ptr);
511 (*ptr)++;
512 DEBUG("PS[0x%02X]", idx);
513 if (idx >= ctx->ps_size) {
514 pr_info("PS index out of range: %i > %i\n", idx, ctx->ps_size);
515 return;
516 }
517 ctx->ps[idx] = cpu_to_le32(val);
518 break;
519 case ATOM_ARG_WS:
520 idx = U8(*ptr);
521 (*ptr)++;
522 DEBUG("WS[0x%02X]", idx);
523 switch (idx) {
524 case ATOM_WS_QUOTIENT:
525 gctx->divmul[0] = val;
526 break;
527 case ATOM_WS_REMAINDER:
528 gctx->divmul[1] = val;
529 break;
530 case ATOM_WS_DATAPTR:
531 gctx->data_block = val;
532 break;
533 case ATOM_WS_SHIFT:
534 gctx->shift = val;
535 break;
536 case ATOM_WS_OR_MASK:
537 case ATOM_WS_AND_MASK:
538 break;
539 case ATOM_WS_FB_WINDOW:
540 gctx->fb_base = val;
541 break;
542 case ATOM_WS_ATTRIBUTES:
543 gctx->io_attr = val;
544 break;
545 case ATOM_WS_REGPTR:
546 gctx->reg_block = val;
547 break;
548 default:
549 if (idx >= ctx->ws_size) {
550 pr_info("WS index out of range: %i > %i\n", idx, ctx->ws_size);
551 return;
552 }
553 ctx->ws[idx] = val;
554 }
555 break;
556 case ATOM_ARG_FB:
557 idx = U8(*ptr);
558 (*ptr)++;
559 if ((gctx->fb_base + (idx * 4)) > gctx->scratch_size_bytes) {
560 DRM_ERROR("ATOM: fb write beyond scratch region: %d vs. %d\n",
561 gctx->fb_base + (idx * 4), gctx->scratch_size_bytes);
562 } else
563 gctx->scratch[(gctx->fb_base / 4) + idx] = val;
564 DEBUG("FB[0x%02X]", idx);
565 break;
566 case ATOM_ARG_PLL:
567 idx = U8(*ptr);
568 (*ptr)++;
569 DEBUG("PLL[0x%02X]", idx);
570 gctx->card->pll_write(gctx->card, idx, val);
571 break;
572 case ATOM_ARG_MC:
573 idx = U8(*ptr);
574 (*ptr)++;
575 DEBUG("MC[0x%02X]", idx);
576 gctx->card->mc_write(gctx->card, idx, val);
577 return;
578 }
579 switch (align) {
580 case ATOM_SRC_DWORD:
581 DEBUG(".[31:0] <- 0x%08X\n", old_val);
582 break;
583 case ATOM_SRC_WORD0:
584 DEBUG(".[15:0] <- 0x%04X\n", old_val);
585 break;
586 case ATOM_SRC_WORD8:
587 DEBUG(".[23:8] <- 0x%04X\n", old_val);
588 break;
589 case ATOM_SRC_WORD16:
590 DEBUG(".[31:16] <- 0x%04X\n", old_val);
591 break;
592 case ATOM_SRC_BYTE0:
593 DEBUG(".[7:0] <- 0x%02X\n", old_val);
594 break;
595 case ATOM_SRC_BYTE8:
596 DEBUG(".[15:8] <- 0x%02X\n", old_val);
597 break;
598 case ATOM_SRC_BYTE16:
599 DEBUG(".[23:16] <- 0x%02X\n", old_val);
600 break;
601 case ATOM_SRC_BYTE24:
602 DEBUG(".[31:24] <- 0x%02X\n", old_val);
603 break;
604 }
605}
606
607static void atom_op_add(atom_exec_context *ctx, int *ptr, int arg)
608{
609 uint8_t attr = U8((*ptr)++);
610 uint32_t dst, src, saved;
611 int dptr = *ptr;
612 SDEBUG(" dst: ");
613 dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
614 SDEBUG(" src: ");
615 src = atom_get_src(ctx, attr, ptr);
616 dst += src;
617 SDEBUG(" dst: ");
618 atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
619}
620
621static void atom_op_and(atom_exec_context *ctx, int *ptr, int arg)
622{
623 uint8_t attr = U8((*ptr)++);
624 uint32_t dst, src, saved;
625 int dptr = *ptr;
626 SDEBUG(" dst: ");
627 dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
628 SDEBUG(" src: ");
629 src = atom_get_src(ctx, attr, ptr);
630 dst &= src;
631 SDEBUG(" dst: ");
632 atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
633}
634
635static void atom_op_beep(atom_exec_context *ctx, int *ptr, int arg)
636{
637 printk("ATOM BIOS beeped!\n");
638}
639
640static void atom_op_calltable(atom_exec_context *ctx, int *ptr, int arg)
641{
642 int idx = U8((*ptr)++);
643 int r = 0;
644
645 if (idx < ATOM_TABLE_NAMES_CNT)
646 SDEBUG(" table: %d (%s)\n", idx, atom_table_names[idx]);
647 else
648 SDEBUG(" table: %d\n", idx);
649 if (U16(ctx->ctx->cmd_table + 4 + 2 * idx))
650 r = amdgpu_atom_execute_table_locked(ctx->ctx, idx, ctx->ps + ctx->ps_shift, ctx->ps_size - ctx->ps_shift);
651 if (r) {
652 ctx->abort = true;
653 }
654}
655
656static void atom_op_clear(atom_exec_context *ctx, int *ptr, int arg)
657{
658 uint8_t attr = U8((*ptr)++);
659 uint32_t saved;
660 int dptr = *ptr;
661 attr &= 0x38;
662 attr |= atom_def_dst[attr >> 3] << 6;
663 atom_get_dst(ctx, arg, attr, ptr, &saved, 0);
664 SDEBUG(" dst: ");
665 atom_put_dst(ctx, arg, attr, &dptr, 0, saved);
666}
667
668static void atom_op_compare(atom_exec_context *ctx, int *ptr, int arg)
669{
670 uint8_t attr = U8((*ptr)++);
671 uint32_t dst, src;
672 SDEBUG(" src1: ");
673 dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1);
674 SDEBUG(" src2: ");
675 src = atom_get_src(ctx, attr, ptr);
676 ctx->ctx->cs_equal = (dst == src);
677 ctx->ctx->cs_above = (dst > src);
678 SDEBUG(" result: %s %s\n", ctx->ctx->cs_equal ? "EQ" : "NE",
679 ctx->ctx->cs_above ? "GT" : "LE");
680}
681
682static void atom_op_delay(atom_exec_context *ctx, int *ptr, int arg)
683{
684 unsigned count = U8((*ptr)++);
685 SDEBUG(" count: %d\n", count);
686 if (arg == ATOM_UNIT_MICROSEC)
687 udelay(count);
688 else if (!drm_can_sleep())
689 mdelay(count);
690 else
691 msleep(count);
692}
693
694static void atom_op_div(atom_exec_context *ctx, int *ptr, int arg)
695{
696 uint8_t attr = U8((*ptr)++);
697 uint32_t dst, src;
698 SDEBUG(" src1: ");
699 dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1);
700 SDEBUG(" src2: ");
701 src = atom_get_src(ctx, attr, ptr);
702 if (src != 0) {
703 ctx->ctx->divmul[0] = dst / src;
704 ctx->ctx->divmul[1] = dst % src;
705 } else {
706 ctx->ctx->divmul[0] = 0;
707 ctx->ctx->divmul[1] = 0;
708 }
709}
710
711static void atom_op_div32(atom_exec_context *ctx, int *ptr, int arg)
712{
713 uint64_t val64;
714 uint8_t attr = U8((*ptr)++);
715 uint32_t dst, src;
716 SDEBUG(" src1: ");
717 dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1);
718 SDEBUG(" src2: ");
719 src = atom_get_src(ctx, attr, ptr);
720 if (src != 0) {
721 val64 = dst;
722 val64 |= ((uint64_t)ctx->ctx->divmul[1]) << 32;
723 do_div(val64, src);
724 ctx->ctx->divmul[0] = lower_32_bits(val64);
725 ctx->ctx->divmul[1] = upper_32_bits(val64);
726 } else {
727 ctx->ctx->divmul[0] = 0;
728 ctx->ctx->divmul[1] = 0;
729 }
730}
731
732static void atom_op_eot(atom_exec_context *ctx, int *ptr, int arg)
733{
734 /* functionally, a nop */
735}
736
737static void atom_op_jump(atom_exec_context *ctx, int *ptr, int arg)
738{
739 int execute = 0, target = U16(*ptr);
740 unsigned long cjiffies;
741
742 (*ptr) += 2;
743 switch (arg) {
744 case ATOM_COND_ABOVE:
745 execute = ctx->ctx->cs_above;
746 break;
747 case ATOM_COND_ABOVEOREQUAL:
748 execute = ctx->ctx->cs_above || ctx->ctx->cs_equal;
749 break;
750 case ATOM_COND_ALWAYS:
751 execute = 1;
752 break;
753 case ATOM_COND_BELOW:
754 execute = !(ctx->ctx->cs_above || ctx->ctx->cs_equal);
755 break;
756 case ATOM_COND_BELOWOREQUAL:
757 execute = !ctx->ctx->cs_above;
758 break;
759 case ATOM_COND_EQUAL:
760 execute = ctx->ctx->cs_equal;
761 break;
762 case ATOM_COND_NOTEQUAL:
763 execute = !ctx->ctx->cs_equal;
764 break;
765 }
766 if (arg != ATOM_COND_ALWAYS)
767 SDEBUG(" taken: %s\n", str_yes_no(execute));
768 SDEBUG(" target: 0x%04X\n", target);
769 if (execute) {
770 if (ctx->last_jump == (ctx->start + target)) {
771 cjiffies = jiffies;
772 if (time_after(cjiffies, ctx->last_jump_jiffies)) {
773 cjiffies -= ctx->last_jump_jiffies;
774 if ((jiffies_to_msecs(cjiffies) > ATOM_CMD_TIMEOUT_SEC*1000)) {
775 DRM_ERROR("atombios stuck in loop for more than %dsecs aborting\n",
776 ATOM_CMD_TIMEOUT_SEC);
777 ctx->abort = true;
778 }
779 } else {
780 /* jiffies wrap around we will just wait a little longer */
781 ctx->last_jump_jiffies = jiffies;
782 }
783 } else {
784 ctx->last_jump = ctx->start + target;
785 ctx->last_jump_jiffies = jiffies;
786 }
787 *ptr = ctx->start + target;
788 }
789}
790
791static void atom_op_mask(atom_exec_context *ctx, int *ptr, int arg)
792{
793 uint8_t attr = U8((*ptr)++);
794 uint32_t dst, mask, src, saved;
795 int dptr = *ptr;
796 SDEBUG(" dst: ");
797 dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
798 mask = atom_get_src_direct(ctx, ((attr >> 3) & 7), ptr);
799 SDEBUG(" mask: 0x%08x", mask);
800 SDEBUG(" src: ");
801 src = atom_get_src(ctx, attr, ptr);
802 dst &= mask;
803 dst |= src;
804 SDEBUG(" dst: ");
805 atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
806}
807
808static void atom_op_move(atom_exec_context *ctx, int *ptr, int arg)
809{
810 uint8_t attr = U8((*ptr)++);
811 uint32_t src, saved;
812 int dptr = *ptr;
813 if (((attr >> 3) & 7) != ATOM_SRC_DWORD)
814 atom_get_dst(ctx, arg, attr, ptr, &saved, 0);
815 else {
816 atom_skip_dst(ctx, arg, attr, ptr);
817 saved = 0xCDCDCDCD;
818 }
819 SDEBUG(" src: ");
820 src = atom_get_src(ctx, attr, ptr);
821 SDEBUG(" dst: ");
822 atom_put_dst(ctx, arg, attr, &dptr, src, saved);
823}
824
825static void atom_op_mul(atom_exec_context *ctx, int *ptr, int arg)
826{
827 uint8_t attr = U8((*ptr)++);
828 uint32_t dst, src;
829 SDEBUG(" src1: ");
830 dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1);
831 SDEBUG(" src2: ");
832 src = atom_get_src(ctx, attr, ptr);
833 ctx->ctx->divmul[0] = dst * src;
834}
835
836static void atom_op_mul32(atom_exec_context *ctx, int *ptr, int arg)
837{
838 uint64_t val64;
839 uint8_t attr = U8((*ptr)++);
840 uint32_t dst, src;
841 SDEBUG(" src1: ");
842 dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1);
843 SDEBUG(" src2: ");
844 src = atom_get_src(ctx, attr, ptr);
845 val64 = (uint64_t)dst * (uint64_t)src;
846 ctx->ctx->divmul[0] = lower_32_bits(val64);
847 ctx->ctx->divmul[1] = upper_32_bits(val64);
848}
849
850static void atom_op_nop(atom_exec_context *ctx, int *ptr, int arg)
851{
852 /* nothing */
853}
854
855static void atom_op_or(atom_exec_context *ctx, int *ptr, int arg)
856{
857 uint8_t attr = U8((*ptr)++);
858 uint32_t dst, src, saved;
859 int dptr = *ptr;
860 SDEBUG(" dst: ");
861 dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
862 SDEBUG(" src: ");
863 src = atom_get_src(ctx, attr, ptr);
864 dst |= src;
865 SDEBUG(" dst: ");
866 atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
867}
868
869static void atom_op_postcard(atom_exec_context *ctx, int *ptr, int arg)
870{
871 uint8_t val = U8((*ptr)++);
872 SDEBUG("POST card output: 0x%02X\n", val);
873}
874
875static void atom_op_repeat(atom_exec_context *ctx, int *ptr, int arg)
876{
877 pr_info("unimplemented!\n");
878}
879
880static void atom_op_restorereg(atom_exec_context *ctx, int *ptr, int arg)
881{
882 pr_info("unimplemented!\n");
883}
884
885static void atom_op_savereg(atom_exec_context *ctx, int *ptr, int arg)
886{
887 pr_info("unimplemented!\n");
888}
889
890static void atom_op_setdatablock(atom_exec_context *ctx, int *ptr, int arg)
891{
892 int idx = U8(*ptr);
893 (*ptr)++;
894 SDEBUG(" block: %d\n", idx);
895 if (!idx)
896 ctx->ctx->data_block = 0;
897 else if (idx == 255)
898 ctx->ctx->data_block = ctx->start;
899 else
900 ctx->ctx->data_block = U16(ctx->ctx->data_table + 4 + 2 * idx);
901 SDEBUG(" base: 0x%04X\n", ctx->ctx->data_block);
902}
903
904static void atom_op_setfbbase(atom_exec_context *ctx, int *ptr, int arg)
905{
906 uint8_t attr = U8((*ptr)++);
907 SDEBUG(" fb_base: ");
908 ctx->ctx->fb_base = atom_get_src(ctx, attr, ptr);
909}
910
911static void atom_op_setport(atom_exec_context *ctx, int *ptr, int arg)
912{
913 int port;
914 switch (arg) {
915 case ATOM_PORT_ATI:
916 port = U16(*ptr);
917 if (port < ATOM_IO_NAMES_CNT)
918 SDEBUG(" port: %d (%s)\n", port, atom_io_names[port]);
919 else
920 SDEBUG(" port: %d\n", port);
921 if (!port)
922 ctx->ctx->io_mode = ATOM_IO_MM;
923 else
924 ctx->ctx->io_mode = ATOM_IO_IIO | port;
925 (*ptr) += 2;
926 break;
927 case ATOM_PORT_PCI:
928 ctx->ctx->io_mode = ATOM_IO_PCI;
929 (*ptr)++;
930 break;
931 case ATOM_PORT_SYSIO:
932 ctx->ctx->io_mode = ATOM_IO_SYSIO;
933 (*ptr)++;
934 break;
935 }
936}
937
938static void atom_op_setregblock(atom_exec_context *ctx, int *ptr, int arg)
939{
940 ctx->ctx->reg_block = U16(*ptr);
941 (*ptr) += 2;
942 SDEBUG(" base: 0x%04X\n", ctx->ctx->reg_block);
943}
944
945static void atom_op_shift_left(atom_exec_context *ctx, int *ptr, int arg)
946{
947 uint8_t attr = U8((*ptr)++), shift;
948 uint32_t saved, dst;
949 int dptr = *ptr;
950 attr &= 0x38;
951 attr |= atom_def_dst[attr >> 3] << 6;
952 SDEBUG(" dst: ");
953 dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
954 shift = atom_get_src_direct(ctx, ATOM_SRC_BYTE0, ptr);
955 SDEBUG(" shift: %d\n", shift);
956 dst <<= shift;
957 SDEBUG(" dst: ");
958 atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
959}
960
961static void atom_op_shift_right(atom_exec_context *ctx, int *ptr, int arg)
962{
963 uint8_t attr = U8((*ptr)++), shift;
964 uint32_t saved, dst;
965 int dptr = *ptr;
966 attr &= 0x38;
967 attr |= atom_def_dst[attr >> 3] << 6;
968 SDEBUG(" dst: ");
969 dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
970 shift = atom_get_src_direct(ctx, ATOM_SRC_BYTE0, ptr);
971 SDEBUG(" shift: %d\n", shift);
972 dst >>= shift;
973 SDEBUG(" dst: ");
974 atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
975}
976
977static void atom_op_shl(atom_exec_context *ctx, int *ptr, int arg)
978{
979 uint8_t attr = U8((*ptr)++), shift;
980 uint32_t saved, dst;
981 int dptr = *ptr;
982 uint32_t dst_align = atom_dst_to_src[(attr >> 3) & 7][(attr >> 6) & 3];
983 SDEBUG(" dst: ");
984 dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
985 /* op needs to full dst value */
986 dst = saved;
987 shift = atom_get_src(ctx, attr, ptr);
988 SDEBUG(" shift: %d\n", shift);
989 dst <<= shift;
990 dst &= atom_arg_mask[dst_align];
991 dst >>= atom_arg_shift[dst_align];
992 SDEBUG(" dst: ");
993 atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
994}
995
996static void atom_op_shr(atom_exec_context *ctx, int *ptr, int arg)
997{
998 uint8_t attr = U8((*ptr)++), shift;
999 uint32_t saved, dst;
1000 int dptr = *ptr;
1001 uint32_t dst_align = atom_dst_to_src[(attr >> 3) & 7][(attr >> 6) & 3];
1002 SDEBUG(" dst: ");
1003 dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
1004 /* op needs to full dst value */
1005 dst = saved;
1006 shift = atom_get_src(ctx, attr, ptr);
1007 SDEBUG(" shift: %d\n", shift);
1008 dst >>= shift;
1009 dst &= atom_arg_mask[dst_align];
1010 dst >>= atom_arg_shift[dst_align];
1011 SDEBUG(" dst: ");
1012 atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
1013}
1014
1015static void atom_op_sub(atom_exec_context *ctx, int *ptr, int arg)
1016{
1017 uint8_t attr = U8((*ptr)++);
1018 uint32_t dst, src, saved;
1019 int dptr = *ptr;
1020 SDEBUG(" dst: ");
1021 dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
1022 SDEBUG(" src: ");
1023 src = atom_get_src(ctx, attr, ptr);
1024 dst -= src;
1025 SDEBUG(" dst: ");
1026 atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
1027}
1028
1029static void atom_op_switch(atom_exec_context *ctx, int *ptr, int arg)
1030{
1031 uint8_t attr = U8((*ptr)++);
1032 uint32_t src, val, target;
1033 SDEBUG(" switch: ");
1034 src = atom_get_src(ctx, attr, ptr);
1035 while (U16(*ptr) != ATOM_CASE_END)
1036 if (U8(*ptr) == ATOM_CASE_MAGIC) {
1037 (*ptr)++;
1038 SDEBUG(" case: ");
1039 val =
1040 atom_get_src(ctx, (attr & 0x38) | ATOM_ARG_IMM,
1041 ptr);
1042 target = U16(*ptr);
1043 if (val == src) {
1044 SDEBUG(" target: %04X\n", target);
1045 *ptr = ctx->start + target;
1046 return;
1047 }
1048 (*ptr) += 2;
1049 } else {
1050 pr_info("Bad case\n");
1051 return;
1052 }
1053 (*ptr) += 2;
1054}
1055
1056static void atom_op_test(atom_exec_context *ctx, int *ptr, int arg)
1057{
1058 uint8_t attr = U8((*ptr)++);
1059 uint32_t dst, src;
1060 SDEBUG(" src1: ");
1061 dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1);
1062 SDEBUG(" src2: ");
1063 src = atom_get_src(ctx, attr, ptr);
1064 ctx->ctx->cs_equal = ((dst & src) == 0);
1065 SDEBUG(" result: %s\n", ctx->ctx->cs_equal ? "EQ" : "NE");
1066}
1067
1068static void atom_op_xor(atom_exec_context *ctx, int *ptr, int arg)
1069{
1070 uint8_t attr = U8((*ptr)++);
1071 uint32_t dst, src, saved;
1072 int dptr = *ptr;
1073 SDEBUG(" dst: ");
1074 dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
1075 SDEBUG(" src: ");
1076 src = atom_get_src(ctx, attr, ptr);
1077 dst ^= src;
1078 SDEBUG(" dst: ");
1079 atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
1080}
1081
1082static void atom_op_debug(atom_exec_context *ctx, int *ptr, int arg)
1083{
1084 uint8_t val = U8((*ptr)++);
1085 SDEBUG("DEBUG output: 0x%02X\n", val);
1086}
1087
1088static void atom_op_processds(atom_exec_context *ctx, int *ptr, int arg)
1089{
1090 uint16_t val = U16(*ptr);
1091 (*ptr) += val + 2;
1092 SDEBUG("PROCESSDS output: 0x%02X\n", val);
1093}
1094
1095static struct {
1096 void (*func) (atom_exec_context *, int *, int);
1097 int arg;
1098} opcode_table[ATOM_OP_CNT] = {
1099 {
1100 NULL, 0}, {
1101 atom_op_move, ATOM_ARG_REG}, {
1102 atom_op_move, ATOM_ARG_PS}, {
1103 atom_op_move, ATOM_ARG_WS}, {
1104 atom_op_move, ATOM_ARG_FB}, {
1105 atom_op_move, ATOM_ARG_PLL}, {
1106 atom_op_move, ATOM_ARG_MC}, {
1107 atom_op_and, ATOM_ARG_REG}, {
1108 atom_op_and, ATOM_ARG_PS}, {
1109 atom_op_and, ATOM_ARG_WS}, {
1110 atom_op_and, ATOM_ARG_FB}, {
1111 atom_op_and, ATOM_ARG_PLL}, {
1112 atom_op_and, ATOM_ARG_MC}, {
1113 atom_op_or, ATOM_ARG_REG}, {
1114 atom_op_or, ATOM_ARG_PS}, {
1115 atom_op_or, ATOM_ARG_WS}, {
1116 atom_op_or, ATOM_ARG_FB}, {
1117 atom_op_or, ATOM_ARG_PLL}, {
1118 atom_op_or, ATOM_ARG_MC}, {
1119 atom_op_shift_left, ATOM_ARG_REG}, {
1120 atom_op_shift_left, ATOM_ARG_PS}, {
1121 atom_op_shift_left, ATOM_ARG_WS}, {
1122 atom_op_shift_left, ATOM_ARG_FB}, {
1123 atom_op_shift_left, ATOM_ARG_PLL}, {
1124 atom_op_shift_left, ATOM_ARG_MC}, {
1125 atom_op_shift_right, ATOM_ARG_REG}, {
1126 atom_op_shift_right, ATOM_ARG_PS}, {
1127 atom_op_shift_right, ATOM_ARG_WS}, {
1128 atom_op_shift_right, ATOM_ARG_FB}, {
1129 atom_op_shift_right, ATOM_ARG_PLL}, {
1130 atom_op_shift_right, ATOM_ARG_MC}, {
1131 atom_op_mul, ATOM_ARG_REG}, {
1132 atom_op_mul, ATOM_ARG_PS}, {
1133 atom_op_mul, ATOM_ARG_WS}, {
1134 atom_op_mul, ATOM_ARG_FB}, {
1135 atom_op_mul, ATOM_ARG_PLL}, {
1136 atom_op_mul, ATOM_ARG_MC}, {
1137 atom_op_div, ATOM_ARG_REG}, {
1138 atom_op_div, ATOM_ARG_PS}, {
1139 atom_op_div, ATOM_ARG_WS}, {
1140 atom_op_div, ATOM_ARG_FB}, {
1141 atom_op_div, ATOM_ARG_PLL}, {
1142 atom_op_div, ATOM_ARG_MC}, {
1143 atom_op_add, ATOM_ARG_REG}, {
1144 atom_op_add, ATOM_ARG_PS}, {
1145 atom_op_add, ATOM_ARG_WS}, {
1146 atom_op_add, ATOM_ARG_FB}, {
1147 atom_op_add, ATOM_ARG_PLL}, {
1148 atom_op_add, ATOM_ARG_MC}, {
1149 atom_op_sub, ATOM_ARG_REG}, {
1150 atom_op_sub, ATOM_ARG_PS}, {
1151 atom_op_sub, ATOM_ARG_WS}, {
1152 atom_op_sub, ATOM_ARG_FB}, {
1153 atom_op_sub, ATOM_ARG_PLL}, {
1154 atom_op_sub, ATOM_ARG_MC}, {
1155 atom_op_setport, ATOM_PORT_ATI}, {
1156 atom_op_setport, ATOM_PORT_PCI}, {
1157 atom_op_setport, ATOM_PORT_SYSIO}, {
1158 atom_op_setregblock, 0}, {
1159 atom_op_setfbbase, 0}, {
1160 atom_op_compare, ATOM_ARG_REG}, {
1161 atom_op_compare, ATOM_ARG_PS}, {
1162 atom_op_compare, ATOM_ARG_WS}, {
1163 atom_op_compare, ATOM_ARG_FB}, {
1164 atom_op_compare, ATOM_ARG_PLL}, {
1165 atom_op_compare, ATOM_ARG_MC}, {
1166 atom_op_switch, 0}, {
1167 atom_op_jump, ATOM_COND_ALWAYS}, {
1168 atom_op_jump, ATOM_COND_EQUAL}, {
1169 atom_op_jump, ATOM_COND_BELOW}, {
1170 atom_op_jump, ATOM_COND_ABOVE}, {
1171 atom_op_jump, ATOM_COND_BELOWOREQUAL}, {
1172 atom_op_jump, ATOM_COND_ABOVEOREQUAL}, {
1173 atom_op_jump, ATOM_COND_NOTEQUAL}, {
1174 atom_op_test, ATOM_ARG_REG}, {
1175 atom_op_test, ATOM_ARG_PS}, {
1176 atom_op_test, ATOM_ARG_WS}, {
1177 atom_op_test, ATOM_ARG_FB}, {
1178 atom_op_test, ATOM_ARG_PLL}, {
1179 atom_op_test, ATOM_ARG_MC}, {
1180 atom_op_delay, ATOM_UNIT_MILLISEC}, {
1181 atom_op_delay, ATOM_UNIT_MICROSEC}, {
1182 atom_op_calltable, 0}, {
1183 atom_op_repeat, 0}, {
1184 atom_op_clear, ATOM_ARG_REG}, {
1185 atom_op_clear, ATOM_ARG_PS}, {
1186 atom_op_clear, ATOM_ARG_WS}, {
1187 atom_op_clear, ATOM_ARG_FB}, {
1188 atom_op_clear, ATOM_ARG_PLL}, {
1189 atom_op_clear, ATOM_ARG_MC}, {
1190 atom_op_nop, 0}, {
1191 atom_op_eot, 0}, {
1192 atom_op_mask, ATOM_ARG_REG}, {
1193 atom_op_mask, ATOM_ARG_PS}, {
1194 atom_op_mask, ATOM_ARG_WS}, {
1195 atom_op_mask, ATOM_ARG_FB}, {
1196 atom_op_mask, ATOM_ARG_PLL}, {
1197 atom_op_mask, ATOM_ARG_MC}, {
1198 atom_op_postcard, 0}, {
1199 atom_op_beep, 0}, {
1200 atom_op_savereg, 0}, {
1201 atom_op_restorereg, 0}, {
1202 atom_op_setdatablock, 0}, {
1203 atom_op_xor, ATOM_ARG_REG}, {
1204 atom_op_xor, ATOM_ARG_PS}, {
1205 atom_op_xor, ATOM_ARG_WS}, {
1206 atom_op_xor, ATOM_ARG_FB}, {
1207 atom_op_xor, ATOM_ARG_PLL}, {
1208 atom_op_xor, ATOM_ARG_MC}, {
1209 atom_op_shl, ATOM_ARG_REG}, {
1210 atom_op_shl, ATOM_ARG_PS}, {
1211 atom_op_shl, ATOM_ARG_WS}, {
1212 atom_op_shl, ATOM_ARG_FB}, {
1213 atom_op_shl, ATOM_ARG_PLL}, {
1214 atom_op_shl, ATOM_ARG_MC}, {
1215 atom_op_shr, ATOM_ARG_REG}, {
1216 atom_op_shr, ATOM_ARG_PS}, {
1217 atom_op_shr, ATOM_ARG_WS}, {
1218 atom_op_shr, ATOM_ARG_FB}, {
1219 atom_op_shr, ATOM_ARG_PLL}, {
1220 atom_op_shr, ATOM_ARG_MC}, {
1221 atom_op_debug, 0}, {
1222 atom_op_processds, 0}, {
1223 atom_op_mul32, ATOM_ARG_PS}, {
1224 atom_op_mul32, ATOM_ARG_WS}, {
1225 atom_op_div32, ATOM_ARG_PS}, {
1226 atom_op_div32, ATOM_ARG_WS},
1227};
1228
1229static int amdgpu_atom_execute_table_locked(struct atom_context *ctx, int index, uint32_t *params, int params_size)
1230{
1231 int base = CU16(ctx->cmd_table + 4 + 2 * index);
1232 int len, ws, ps, ptr;
1233 unsigned char op;
1234 atom_exec_context ectx;
1235 int ret = 0;
1236
1237 if (!base)
1238 return -EINVAL;
1239
1240 if (ctx->execute_depth >= ATOM_EXECUTE_MAX_DEPTH) {
1241 DRM_ERROR("atombios command table nesting exceeded limit (%u)\n",
1242 ATOM_EXECUTE_MAX_DEPTH);
1243 return -ELOOP;
1244 }
1245 ctx->execute_depth++;
1246
1247 len = CU16(base + ATOM_CT_SIZE_PTR);
1248 ws = CU8(base + ATOM_CT_WS_PTR);
1249 ps = CU8(base + ATOM_CT_PS_PTR) & ATOM_CT_PS_MASK;
1250 ptr = base + ATOM_CT_CODE_PTR;
1251
1252 SDEBUG(">> execute %04X (len %d, WS %d, PS %d)\n", base, len, ws, ps);
1253
1254 ectx.ctx = ctx;
1255 ectx.ps_shift = ps / 4;
1256 ectx.start = base;
1257 ectx.ps = params;
1258 ectx.ps_size = params_size;
1259 ectx.abort = false;
1260 ectx.last_jump = 0;
1261 ectx.last_jump_jiffies = 0;
1262 if (ws) {
1263 ectx.ws = kcalloc(4, ws, GFP_KERNEL);
1264 if (!ectx.ws) {
1265 ret = -ENOMEM;
1266 goto free;
1267 }
1268 ectx.ws_size = ws;
1269 } else {
1270 ectx.ws = NULL;
1271 ectx.ws_size = 0;
1272 }
1273
1274 debug_depth++;
1275 while (1) {
1276 op = CU8(ptr++);
1277 if (op < ATOM_OP_NAMES_CNT)
1278 SDEBUG("%s @ 0x%04X\n", atom_op_names[op], ptr - 1);
1279 else
1280 SDEBUG("[%d] @ 0x%04X\n", op, ptr - 1);
1281 if (ectx.abort) {
1282 DRM_ERROR("atombios stuck executing %04X (len %d, WS %d, PS %d) @ 0x%04X\n",
1283 base, len, ws, ps, ptr - 1);
1284 ret = -EINVAL;
1285 goto free;
1286 }
1287
1288 if (op < ATOM_OP_CNT && op > 0)
1289 opcode_table[op].func(&ectx, &ptr,
1290 opcode_table[op].arg);
1291 else
1292 break;
1293
1294 if (op == ATOM_OP_EOT)
1295 break;
1296 }
1297 debug_depth--;
1298 SDEBUG("<<\n");
1299
1300free:
1301 if (ws)
1302 kfree(ectx.ws);
1303 ctx->execute_depth--;
1304 return ret;
1305}
1306
1307int amdgpu_atom_execute_table(struct atom_context *ctx, int index, uint32_t *params, int params_size)
1308{
1309 int r;
1310
1311 mutex_lock(&ctx->mutex);
1312 /* reset data block */
1313 ctx->data_block = 0;
1314 /* reset reg block */
1315 ctx->reg_block = 0;
1316 /* reset fb window */
1317 ctx->fb_base = 0;
1318 /* reset io mode */
1319 ctx->io_mode = ATOM_IO_MM;
1320 /* reset divmul */
1321 ctx->divmul[0] = 0;
1322 ctx->divmul[1] = 0;
1323 r = amdgpu_atom_execute_table_locked(ctx, index, params, params_size);
1324 mutex_unlock(&ctx->mutex);
1325 return r;
1326}
1327
1328static int atom_iio_len[] = { 1, 2, 3, 3, 3, 3, 4, 4, 4, 3 };
1329
1330static void atom_index_iio(struct atom_context *ctx, int base)
1331{
1332 ctx->iio = kzalloc(2 * 256, GFP_KERNEL);
1333 if (!ctx->iio)
1334 return;
1335 while (base + 1 < ctx->bios_size && CU8(base) == ATOM_IIO_START) {
1336 uint8_t index = CU8(base + 1);
1337 int start = base + 2;
1338 base += 2;
1339 while (base < ctx->bios_size && CU8(base) != ATOM_IIO_END) {
1340 uint8_t op = CU8(base);
1341
1342 /*
1343 * Unknown opcode: its length is unknown so the byte
1344 * stream cannot be resynced reliably.
1345 */
1346 if (op >= ARRAY_SIZE(atom_iio_len))
1347 return;
1348 base += atom_iio_len[op];
1349 }
1350 if (base >= ctx->bios_size)
1351 return;
1352 /* Only index well-formed methods, others stay 0 */
1353 ctx->iio[index] = start;
1354 base += 3;
1355 }
1356}
1357
1358static void atom_get_vbios_name(struct atom_context *ctx)
1359{
1360 unsigned char *p_rom;
1361 unsigned char *p_end;
1362 unsigned char str_num;
1363 unsigned short off_to_vbios_str;
1364 unsigned char *c_ptr;
1365 int name_size;
1366 int i;
1367
1368 const char *na = "--N/A--";
1369 char *back;
1370
1371 p_rom = ctx->bios;
1372 p_end = p_rom + ctx->bios_size;
1373
1374 if (p_rom + OFFSET_TO_GET_ATOMBIOS_STRING_START + 1 >= p_end)
1375 goto no_name;
1376
1377 str_num = *(p_rom + OFFSET_TO_GET_ATOMBIOS_NUMBER_OF_STRINGS);
1378 if (!str_num)
1379 goto no_name;
1380
1381 off_to_vbios_str =
1382 *(unsigned short *)(p_rom + OFFSET_TO_GET_ATOMBIOS_STRING_START);
1383
1384 c_ptr = (unsigned char *)(p_rom + off_to_vbios_str);
1385 if (c_ptr >= p_end)
1386 goto no_name;
1387
1388 /*
1389 * skip the atombios strings, usually 4
1390 * 1st is P/N, 2nd is ASIC, 3rd is PCI type, 4th is Memory type
1391 */
1392 for (i = 0; i < str_num; i++) {
1393 while (c_ptr < p_end && *c_ptr != 0)
1394 c_ptr++;
1395 c_ptr++;
1396 }
1397
1398 /* skip the following 2 chars: 0x0D 0x0A */
1399 c_ptr += 2;
1400 if (c_ptr >= p_end)
1401 goto no_name;
1402
1403 name_size = strnlen(c_ptr, min(STRLEN_LONG - 1, (int)(p_end - c_ptr)));
1404 memcpy(ctx->name, c_ptr, name_size);
1405 back = ctx->name + name_size;
1406 while ((*--back) == ' ')
1407 ;
1408 *(back + 1) = '\0';
1409 return;
1410
1411no_name:
1412 /* do not know where to find name */
1413 strscpy(ctx->name, na, sizeof(ctx->name));
1414}
1415
1416static void atom_get_vbios_date(struct atom_context *ctx)
1417{
1418 unsigned char *p_rom;
1419 unsigned char *date_in_rom;
1420
1421 p_rom = ctx->bios;
1422
1423 date_in_rom = p_rom + OFFSET_TO_VBIOS_DATE;
1424
1425 ctx->date[0] = '2';
1426 ctx->date[1] = '0';
1427 ctx->date[2] = date_in_rom[6];
1428 ctx->date[3] = date_in_rom[7];
1429 ctx->date[4] = '/';
1430 ctx->date[5] = date_in_rom[0];
1431 ctx->date[6] = date_in_rom[1];
1432 ctx->date[7] = '/';
1433 ctx->date[8] = date_in_rom[3];
1434 ctx->date[9] = date_in_rom[4];
1435 ctx->date[10] = ' ';
1436 ctx->date[11] = date_in_rom[9];
1437 ctx->date[12] = date_in_rom[10];
1438 ctx->date[13] = date_in_rom[11];
1439 ctx->date[14] = date_in_rom[12];
1440 ctx->date[15] = date_in_rom[13];
1441 ctx->date[16] = '\0';
1442}
1443
1444static unsigned char *atom_find_str_in_rom(struct atom_context *ctx, char *str, int start,
1445 int end, int maxlen)
1446{
1447 unsigned long str_off;
1448 unsigned char *p_rom;
1449 unsigned short str_len;
1450
1451 str_off = 0;
1452 str_len = strnlen(str, maxlen);
1453 p_rom = ctx->bios;
1454
1455 for (; start <= end; ++start) {
1456 for (str_off = 0; str_off < str_len; ++str_off) {
1457 if (str[str_off] != *(p_rom + start + str_off))
1458 break;
1459 }
1460
1461 if (str_off == str_len || str[str_off] == 0)
1462 return p_rom + start;
1463 }
1464 return NULL;
1465}
1466
1467static void atom_get_vbios_pn(struct atom_context *ctx)
1468{
1469 unsigned char *p_rom;
1470 unsigned short off_to_vbios_str;
1471 unsigned char *vbios_str;
1472 int count;
1473
1474 off_to_vbios_str = 0;
1475 p_rom = ctx->bios;
1476
1477 if (*(p_rom + OFFSET_TO_GET_ATOMBIOS_NUMBER_OF_STRINGS) != 0) {
1478 off_to_vbios_str =
1479 *(unsigned short *)(p_rom + OFFSET_TO_GET_ATOMBIOS_STRING_START);
1480
1481 vbios_str = (unsigned char *)(p_rom + off_to_vbios_str);
1482 } else {
1483 vbios_str = p_rom + OFFSET_TO_VBIOS_PART_NUMBER;
1484 }
1485
1486 if (*vbios_str == 0) {
1487 vbios_str = atom_find_str_in_rom(ctx, BIOS_ATOM_PREFIX, 3, 1024, 64);
1488 if (vbios_str == NULL)
1489 vbios_str += sizeof(BIOS_ATOM_PREFIX) - 1;
1490 }
1491 OPTIMIZER_HIDE_VAR(vbios_str);
1492 if (vbios_str != NULL && *vbios_str == 0)
1493 vbios_str++;
1494
1495 if (vbios_str != NULL) {
1496 count = 0;
1497 while ((count < BIOS_STRING_LENGTH) && vbios_str[count] >= ' ' &&
1498 vbios_str[count] <= 'z') {
1499 ctx->vbios_pn[count] = vbios_str[count];
1500 count++;
1501 }
1502
1503 ctx->vbios_pn[count] = 0;
1504 }
1505}
1506
1507static void atom_get_vbios_version(struct atom_context *ctx)
1508{
1509 unsigned short start = 3, end;
1510 unsigned char *vbios_ver;
1511 unsigned char *p_rom;
1512
1513 p_rom = ctx->bios;
1514 /* Search from strings offset if it's present */
1515 start = *(unsigned short *)(p_rom +
1516 OFFSET_TO_GET_ATOMBIOS_STRING_START);
1517
1518 /* Search till atom rom header start point */
1519 end = *(unsigned short *)(p_rom + OFFSET_TO_ATOM_ROM_HEADER_POINTER);
1520
1521 /* Use hardcoded offsets, if the offsets are not populated */
1522 if (end <= start) {
1523 start = 3;
1524 end = 1024;
1525 }
1526
1527 /* find anchor ATOMBIOSBK-AMD */
1528 vbios_ver =
1529 atom_find_str_in_rom(ctx, BIOS_VERSION_PREFIX, start, end, 64);
1530 if (vbios_ver != NULL) {
1531 /* skip ATOMBIOSBK-AMD VER */
1532 vbios_ver += 18;
1533 memcpy(ctx->vbios_ver_str, vbios_ver, STRLEN_NORMAL);
1534 } else {
1535 ctx->vbios_ver_str[0] = '\0';
1536 }
1537}
1538
1539static void atom_get_vbios_build(struct atom_context *ctx)
1540{
1541 unsigned char *atom_rom_hdr;
1542 unsigned char *str;
1543 uint16_t base, len;
1544
1545 base = CU16(ATOM_ROM_TABLE_PTR);
1546 atom_rom_hdr = CSTR(base);
1547
1548 str = CSTR(CU16(base + ATOM_ROM_CFG_PTR));
1549 /* Skip config string */
1550 while (str < atom_rom_hdr && *str++)
1551 ;
1552 /* Skip change list string */
1553 while (str < atom_rom_hdr && *str++)
1554 ;
1555
1556 len = min(atom_rom_hdr - str, STRLEN_NORMAL);
1557 if (len)
1558 strscpy(ctx->build_num, str, len);
1559}
1560
1561static inline void atom_print_vbios_info(struct atom_context *ctx)
1562{
1563 char vbios_info[256];
1564 int off = 0;
1565
1566 if (ctx->vbios_pn[0])
1567 off += scnprintf(vbios_info + off, sizeof(vbios_info) - off,
1568 "%s", ctx->vbios_pn);
1569 if (ctx->build_num[0])
1570 off += scnprintf(vbios_info + off, sizeof(vbios_info) - off,
1571 "%sbuild: %s", off ? ", " : "",
1572 ctx->build_num);
1573 if (ctx->vbios_ver_str[0])
1574 off += scnprintf(vbios_info + off, sizeof(vbios_info) - off,
1575 "%sver: %s", off ? ", " : "",
1576 ctx->vbios_ver_str);
1577 if (ctx->date[0])
1578 off += scnprintf(vbios_info + off, sizeof(vbios_info) - off,
1579 "%s%.10s", off ? ", " : "",
1580 ctx->date);
1581 if (off)
1582 drm_info(ctx->card->dev, "ATOM BIOS: %s\n", vbios_info);
1583}
1584
1585struct atom_context *amdgpu_atom_parse(struct card_info *card, void *bios, uint32_t bios_size)
1586{
1587 int base;
1588 struct atom_context *ctx =
1589 kzalloc_obj(struct atom_context);
1590 struct _ATOM_ROM_HEADER *atom_rom_header;
1591 struct _ATOM_MASTER_DATA_TABLE *master_table;
1592 struct _ATOM_FIRMWARE_INFO *atom_fw_info;
1593
1594 if (!ctx)
1595 return NULL;
1596
1597 ctx->card = card;
1598 ctx->bios = bios;
1599 ctx->bios_size = bios_size;
1600
1601 if (CU16(0) != ATOM_BIOS_MAGIC) {
1602 pr_info("Invalid BIOS magic\n");
1603 kfree(ctx);
1604 return NULL;
1605 }
1606 if (strncmp
1607 (CSTR(ATOM_ATI_MAGIC_PTR), ATOM_ATI_MAGIC,
1608 strlen(ATOM_ATI_MAGIC))) {
1609 pr_info("Invalid ATI magic\n");
1610 kfree(ctx);
1611 return NULL;
1612 }
1613
1614 base = CU16(ATOM_ROM_TABLE_PTR);
1615 if (strncmp
1616 (CSTR(base + ATOM_ROM_MAGIC_PTR), ATOM_ROM_MAGIC,
1617 strlen(ATOM_ROM_MAGIC))) {
1618 pr_info("Invalid ATOM magic\n");
1619 kfree(ctx);
1620 return NULL;
1621 }
1622
1623 ctx->cmd_table = CU16(base + ATOM_ROM_CMD_PTR);
1624 ctx->data_table = CU16(base + ATOM_ROM_DATA_PTR);
1625 atom_index_iio(ctx, CU16(ctx->data_table + ATOM_DATA_IIO_PTR) + 4);
1626 if (!ctx->iio) {
1627 amdgpu_atom_destroy(ctx);
1628 return NULL;
1629 }
1630
1631 atom_rom_header = (struct _ATOM_ROM_HEADER *)CSTR(base);
1632 if (atom_rom_header->usMasterDataTableOffset != 0) {
1633 master_table = (struct _ATOM_MASTER_DATA_TABLE *)
1634 CSTR(atom_rom_header->usMasterDataTableOffset);
1635 if (master_table->ListOfDataTables.FirmwareInfo != 0) {
1636 atom_fw_info = (struct _ATOM_FIRMWARE_INFO *)
1637 CSTR(master_table->ListOfDataTables.FirmwareInfo);
1638 ctx->version = atom_fw_info->ulFirmwareRevision;
1639 }
1640 }
1641
1642 atom_get_vbios_name(ctx);
1643 atom_get_vbios_pn(ctx);
1644 atom_get_vbios_date(ctx);
1645 atom_get_vbios_version(ctx);
1646 atom_get_vbios_build(ctx);
1647
1648 atom_print_vbios_info(ctx);
1649
1650 return ctx;
1651}
1652
1653int amdgpu_atom_asic_init(struct atom_context *ctx)
1654{
1655 int hwi = CU16(ctx->data_table + ATOM_DATA_FWI_PTR);
1656 uint32_t ps[16];
1657 int ret;
1658
1659 memset(ps, 0, 64);
1660
1661 ps[0] = cpu_to_le32(CU32(hwi + ATOM_FWI_DEFSCLK_PTR));
1662 ps[1] = cpu_to_le32(CU32(hwi + ATOM_FWI_DEFMCLK_PTR));
1663 if (!ps[0] || !ps[1])
1664 return 1;
1665
1666 if (!CU16(ctx->cmd_table + 4 + 2 * ATOM_CMD_INIT))
1667 return 1;
1668 ret = amdgpu_atom_execute_table(ctx, ATOM_CMD_INIT, ps, 16);
1669 if (ret)
1670 return ret;
1671
1672 memset(ps, 0, 64);
1673
1674 return ret;
1675}
1676
1677void amdgpu_atom_destroy(struct atom_context *ctx)
1678{
1679 kfree(ctx->iio);
1680 kfree(ctx);
1681}
1682
1683bool amdgpu_atom_parse_data_header(struct atom_context *ctx, int index,
1684 uint16_t *size, uint8_t *frev, uint8_t *crev,
1685 uint16_t *data_start)
1686{
1687 int offset = index * 2 + 4;
1688 int idx = CU16(ctx->data_table + offset);
1689 u16 *mdt = (u16 *)(ctx->bios + ctx->data_table + 4);
1690
1691 if (!mdt[index])
1692 return false;
1693
1694 if (size)
1695 *size = CU16(idx);
1696 if (frev)
1697 *frev = CU8(idx + 2);
1698 if (crev)
1699 *crev = CU8(idx + 3);
1700 *data_start = idx;
1701 return true;
1702}
1703
1704bool amdgpu_atom_parse_cmd_header(struct atom_context *ctx, int index, uint8_t *frev,
1705 uint8_t *crev)
1706{
1707 int offset = index * 2 + 4;
1708 int idx = CU16(ctx->cmd_table + offset);
1709 u16 *mct = (u16 *)(ctx->bios + ctx->cmd_table + 4);
1710
1711 if (!mct[index])
1712 return false;
1713
1714 if (frev)
1715 *frev = CU8(idx + 2);
1716 if (crev)
1717 *crev = CU8(idx + 3);
1718 return true;
1719}
1720