atom.c revision 282199
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 <sys/cdefs.h>
26__FBSDID("$FreeBSD: stable/10/sys/dev/drm2/radeon/atom.c 282199 2015-04-28 19:35:05Z dumbbell $");
27
28#define ATOM_DEBUG
29
30#include "atom.h"
31#include "atom-names.h"
32#include "atom-bits.h"
33#include "radeon.h"
34
35#define ATOM_COND_ABOVE		0
36#define ATOM_COND_ABOVEOREQUAL	1
37#define ATOM_COND_ALWAYS	2
38#define ATOM_COND_BELOW		3
39#define ATOM_COND_BELOWOREQUAL	4
40#define ATOM_COND_EQUAL		5
41#define ATOM_COND_NOTEQUAL	6
42
43#define ATOM_PORT_ATI	0
44#define ATOM_PORT_PCI	1
45#define ATOM_PORT_SYSIO	2
46
47#define ATOM_UNIT_MICROSEC	0
48#define ATOM_UNIT_MILLISEC	1
49
50#define PLL_INDEX	2
51#define PLL_DATA	3
52
53typedef struct {
54	struct atom_context *ctx;
55	uint32_t *ps, *ws;
56	int ps_shift;
57	uint16_t start;
58	unsigned last_jump;
59	unsigned long last_jump_jiffies;
60	bool abort;
61} atom_exec_context;
62
63int atom_debug = 0;
64static int atom_execute_table_locked(struct atom_context *ctx, int index, uint32_t * params);
65
66static uint32_t atom_arg_mask[8] =
67    { 0xFFFFFFFF, 0xFFFF, 0xFFFF00, 0xFFFF0000, 0xFF, 0xFF00, 0xFF0000,
680xFF000000 };
69static int atom_arg_shift[8] = { 0, 0, 8, 16, 0, 8, 16, 24 };
70
71static int atom_dst_to_src[8][4] = {
72	/* translate destination alignment field to the source alignment encoding */
73	{0, 0, 0, 0},
74	{1, 2, 3, 0},
75	{1, 2, 3, 0},
76	{1, 2, 3, 0},
77	{4, 5, 6, 7},
78	{4, 5, 6, 7},
79	{4, 5, 6, 7},
80	{4, 5, 6, 7},
81};
82static int atom_def_dst[8] = { 0, 0, 1, 2, 0, 1, 2, 3 };
83
84static int debug_depth = 0;
85#ifdef ATOM_DEBUG
86static void debug_print_spaces(int n)
87{
88	while (n--)
89		printf("   ");
90}
91
92#define ATOM_DEBUG_PRINT(...) do if (atom_debug) { printf(__FILE__ __VA_ARGS__); } while (0)
93#define ATOM_SDEBUG_PRINT(...) do if (atom_debug) { printf(__FILE__); debug_print_spaces(debug_depth); printf(__VA_ARGS__); } while (0)
94#else
95#define ATOM_DEBUG_PRINT(...) do { } while (0)
96#define ATOM_SDEBUG_PRINT(...) do { } while (0)
97#endif
98
99static uint32_t atom_iio_execute(struct atom_context *ctx, int base,
100				 uint32_t index, uint32_t data)
101{
102	struct radeon_device *rdev = ctx->card->dev->dev_private;
103	uint32_t temp = 0xCDCDCDCD;
104
105	while (1)
106		switch (CU8(base)) {
107		case ATOM_IIO_NOP:
108			base++;
109			break;
110		case ATOM_IIO_READ:
111			temp = ctx->card->ioreg_read(ctx->card, CU16(base + 1));
112			base += 3;
113			break;
114		case ATOM_IIO_WRITE:
115			if (rdev->family == CHIP_RV515)
116				(void)ctx->card->ioreg_read(ctx->card, CU16(base + 1));
117			ctx->card->ioreg_write(ctx->card, CU16(base + 1), temp);
118			base += 3;
119			break;
120		case ATOM_IIO_CLEAR:
121			temp &=
122			    ~((0xFFFFFFFF >> (32 - CU8(base + 1))) <<
123			      CU8(base + 2));
124			base += 3;
125			break;
126		case ATOM_IIO_SET:
127			temp |=
128			    (0xFFFFFFFF >> (32 - CU8(base + 1))) << CU8(base +
129									2);
130			base += 3;
131			break;
132		case ATOM_IIO_MOVE_INDEX:
133			temp &=
134			    ~((0xFFFFFFFF >> (32 - CU8(base + 1))) <<
135			      CU8(base + 3));
136			temp |=
137			    ((index >> CU8(base + 2)) &
138			     (0xFFFFFFFF >> (32 - CU8(base + 1)))) << CU8(base +
139									  3);
140			base += 4;
141			break;
142		case ATOM_IIO_MOVE_DATA:
143			temp &=
144			    ~((0xFFFFFFFF >> (32 - CU8(base + 1))) <<
145			      CU8(base + 3));
146			temp |=
147			    ((data >> CU8(base + 2)) &
148			     (0xFFFFFFFF >> (32 - CU8(base + 1)))) << CU8(base +
149									  3);
150			base += 4;
151			break;
152		case ATOM_IIO_MOVE_ATTR:
153			temp &=
154			    ~((0xFFFFFFFF >> (32 - CU8(base + 1))) <<
155			      CU8(base + 3));
156			temp |=
157			    ((ctx->
158			      io_attr >> CU8(base + 2)) & (0xFFFFFFFF >> (32 -
159									  CU8
160									  (base
161									   +
162									   1))))
163			    << CU8(base + 3);
164			base += 4;
165			break;
166		case ATOM_IIO_END:
167			return temp;
168		default:
169			DRM_INFO("Unknown IIO opcode.\n");
170			return 0;
171		}
172}
173
174static uint32_t atom_get_src_int(atom_exec_context *ctx, uint8_t attr,
175				 int *ptr, uint32_t *saved, int print)
176{
177	uint32_t idx, val = 0xCDCDCDCD, align, arg;
178	struct atom_context *gctx = ctx->ctx;
179	arg = attr & 7;
180	align = (attr >> 3) & 7;
181	switch (arg) {
182	case ATOM_ARG_REG:
183		idx = U16(*ptr);
184		(*ptr) += 2;
185		if (print)
186			ATOM_DEBUG_PRINT("REG[0x%04X]", idx);
187		idx += gctx->reg_block;
188		switch (gctx->io_mode) {
189		case ATOM_IO_MM:
190			val = gctx->card->reg_read(gctx->card, idx);
191			break;
192		case ATOM_IO_PCI:
193			DRM_INFO(
194			       "PCI registers are not implemented.\n");
195			return 0;
196		case ATOM_IO_SYSIO:
197			DRM_INFO(
198			       "SYSIO registers are not implemented.\n");
199			return 0;
200		default:
201			if (!(gctx->io_mode & 0x80)) {
202				DRM_INFO("Bad IO mode.\n");
203				return 0;
204			}
205			if (!gctx->iio[gctx->io_mode & 0x7F]) {
206				DRM_INFO(
207				       "Undefined indirect IO read method %d.\n",
208				       gctx->io_mode & 0x7F);
209				return 0;
210			}
211			val =
212			    atom_iio_execute(gctx,
213					     gctx->iio[gctx->io_mode & 0x7F],
214					     idx, 0);
215		}
216		break;
217	case ATOM_ARG_PS:
218		idx = U8(*ptr);
219		(*ptr)++;
220		/* get_unaligned_le32 avoids unaligned accesses from atombios
221		 * tables, noticed on a DEC Alpha. */
222		val = get_unaligned_le32((u32 *)&ctx->ps[idx]);
223		if (print)
224			ATOM_DEBUG_PRINT("PS[0x%02X,0x%04X]", idx, val);
225		break;
226	case ATOM_ARG_WS:
227		idx = U8(*ptr);
228		(*ptr)++;
229		if (print)
230			ATOM_DEBUG_PRINT("WS[0x%02X]", idx);
231		switch (idx) {
232		case ATOM_WS_QUOTIENT:
233			val = gctx->divmul[0];
234			break;
235		case ATOM_WS_REMAINDER:
236			val = gctx->divmul[1];
237			break;
238		case ATOM_WS_DATAPTR:
239			val = gctx->data_block;
240			break;
241		case ATOM_WS_SHIFT:
242			val = gctx->shift;
243			break;
244		case ATOM_WS_OR_MASK:
245			val = 1 << gctx->shift;
246			break;
247		case ATOM_WS_AND_MASK:
248			val = ~(1 << gctx->shift);
249			break;
250		case ATOM_WS_FB_WINDOW:
251			val = gctx->fb_base;
252			break;
253		case ATOM_WS_ATTRIBUTES:
254			val = gctx->io_attr;
255			break;
256		case ATOM_WS_REGPTR:
257			val = gctx->reg_block;
258			break;
259		default:
260			val = ctx->ws[idx];
261		}
262		break;
263	case ATOM_ARG_ID:
264		idx = U16(*ptr);
265		(*ptr) += 2;
266		if (print) {
267			if (gctx->data_block)
268				ATOM_DEBUG_PRINT("ID[0x%04X+%04X]", idx, gctx->data_block);
269			else
270				ATOM_DEBUG_PRINT("ID[0x%04X]", idx);
271		}
272		val = U32(idx + gctx->data_block);
273		break;
274	case ATOM_ARG_FB:
275		idx = U8(*ptr);
276		(*ptr)++;
277		if ((gctx->fb_base + (idx * 4)) > gctx->scratch_size_bytes) {
278			DRM_ERROR("ATOM: fb read beyond scratch region: %d vs. %d\n",
279				  gctx->fb_base + (idx * 4), gctx->scratch_size_bytes);
280			val = 0;
281		} else
282			val = gctx->scratch[(gctx->fb_base / 4) + idx];
283		if (print)
284			ATOM_DEBUG_PRINT("FB[0x%02X]", idx);
285		break;
286	case ATOM_ARG_IMM:
287		switch (align) {
288		case ATOM_SRC_DWORD:
289			val = U32(*ptr);
290			(*ptr) += 4;
291			if (print)
292				ATOM_DEBUG_PRINT("IMM 0x%08X\n", val);
293			return val;
294		case ATOM_SRC_WORD0:
295		case ATOM_SRC_WORD8:
296		case ATOM_SRC_WORD16:
297			val = U16(*ptr);
298			(*ptr) += 2;
299			if (print)
300				ATOM_DEBUG_PRINT("IMM 0x%04X\n", val);
301			return val;
302		case ATOM_SRC_BYTE0:
303		case ATOM_SRC_BYTE8:
304		case ATOM_SRC_BYTE16:
305		case ATOM_SRC_BYTE24:
306			val = U8(*ptr);
307			(*ptr)++;
308			if (print)
309				ATOM_DEBUG_PRINT("IMM 0x%02X\n", val);
310			return val;
311		}
312		return 0;
313	case ATOM_ARG_PLL:
314		idx = U8(*ptr);
315		(*ptr)++;
316		if (print)
317			ATOM_DEBUG_PRINT("PLL[0x%02X]", idx);
318		val = gctx->card->pll_read(gctx->card, idx);
319		break;
320	case ATOM_ARG_MC:
321		idx = U8(*ptr);
322		(*ptr)++;
323		if (print)
324			ATOM_DEBUG_PRINT("MC[0x%02X]", idx);
325		val = gctx->card->mc_read(gctx->card, idx);
326		break;
327	}
328	if (saved)
329		*saved = val;
330	val &= atom_arg_mask[align];
331	val >>= atom_arg_shift[align];
332	if (print)
333		switch (align) {
334		case ATOM_SRC_DWORD:
335			ATOM_DEBUG_PRINT(".[31:0] -> 0x%08X\n", val);
336			break;
337		case ATOM_SRC_WORD0:
338			ATOM_DEBUG_PRINT(".[15:0] -> 0x%04X\n", val);
339			break;
340		case ATOM_SRC_WORD8:
341			ATOM_DEBUG_PRINT(".[23:8] -> 0x%04X\n", val);
342			break;
343		case ATOM_SRC_WORD16:
344			ATOM_DEBUG_PRINT(".[31:16] -> 0x%04X\n", val);
345			break;
346		case ATOM_SRC_BYTE0:
347			ATOM_DEBUG_PRINT(".[7:0] -> 0x%02X\n", val);
348			break;
349		case ATOM_SRC_BYTE8:
350			ATOM_DEBUG_PRINT(".[15:8] -> 0x%02X\n", val);
351			break;
352		case ATOM_SRC_BYTE16:
353			ATOM_DEBUG_PRINT(".[23:16] -> 0x%02X\n", val);
354			break;
355		case ATOM_SRC_BYTE24:
356			ATOM_DEBUG_PRINT(".[31:24] -> 0x%02X\n", val);
357			break;
358		}
359	return val;
360}
361
362static void atom_skip_src_int(atom_exec_context *ctx, uint8_t attr, int *ptr)
363{
364	uint32_t align = (attr >> 3) & 7, arg = attr & 7;
365	switch (arg) {
366	case ATOM_ARG_REG:
367	case ATOM_ARG_ID:
368		(*ptr) += 2;
369		break;
370	case ATOM_ARG_PLL:
371	case ATOM_ARG_MC:
372	case ATOM_ARG_PS:
373	case ATOM_ARG_WS:
374	case ATOM_ARG_FB:
375		(*ptr)++;
376		break;
377	case ATOM_ARG_IMM:
378		switch (align) {
379		case ATOM_SRC_DWORD:
380			(*ptr) += 4;
381			return;
382		case ATOM_SRC_WORD0:
383		case ATOM_SRC_WORD8:
384		case ATOM_SRC_WORD16:
385			(*ptr) += 2;
386			return;
387		case ATOM_SRC_BYTE0:
388		case ATOM_SRC_BYTE8:
389		case ATOM_SRC_BYTE16:
390		case ATOM_SRC_BYTE24:
391			(*ptr)++;
392			return;
393		}
394		return;
395	}
396}
397
398static uint32_t atom_get_src(atom_exec_context *ctx, uint8_t attr, int *ptr)
399{
400	return atom_get_src_int(ctx, attr, ptr, NULL, 1);
401}
402
403static uint32_t atom_get_src_direct(atom_exec_context *ctx, uint8_t align, int *ptr)
404{
405	uint32_t val = 0xCDCDCDCD;
406
407	switch (align) {
408	case ATOM_SRC_DWORD:
409		val = U32(*ptr);
410		(*ptr) += 4;
411		break;
412	case ATOM_SRC_WORD0:
413	case ATOM_SRC_WORD8:
414	case ATOM_SRC_WORD16:
415		val = U16(*ptr);
416		(*ptr) += 2;
417		break;
418	case ATOM_SRC_BYTE0:
419	case ATOM_SRC_BYTE8:
420	case ATOM_SRC_BYTE16:
421	case ATOM_SRC_BYTE24:
422		val = U8(*ptr);
423		(*ptr)++;
424		break;
425	}
426	return val;
427}
428
429static uint32_t atom_get_dst(atom_exec_context *ctx, int arg, uint8_t attr,
430			     int *ptr, uint32_t *saved, int print)
431{
432	return atom_get_src_int(ctx,
433				arg | atom_dst_to_src[(attr >> 3) &
434						      7][(attr >> 6) & 3] << 3,
435				ptr, saved, print);
436}
437
438static void atom_skip_dst(atom_exec_context *ctx, int arg, uint8_t attr, int *ptr)
439{
440	atom_skip_src_int(ctx,
441			  arg | atom_dst_to_src[(attr >> 3) & 7][(attr >> 6) &
442								 3] << 3, ptr);
443}
444
445static void atom_put_dst(atom_exec_context *ctx, int arg, uint8_t attr,
446			 int *ptr, uint32_t val, uint32_t saved)
447{
448	uint32_t align =
449	    atom_dst_to_src[(attr >> 3) & 7][(attr >> 6) & 3], old_val =
450	    val, idx;
451	struct atom_context *gctx = ctx->ctx;
452	old_val &= atom_arg_mask[align] >> atom_arg_shift[align];
453	val <<= atom_arg_shift[align];
454	val &= atom_arg_mask[align];
455	saved &= ~atom_arg_mask[align];
456	val |= saved;
457	switch (arg) {
458	case ATOM_ARG_REG:
459		idx = U16(*ptr);
460		(*ptr) += 2;
461		ATOM_DEBUG_PRINT("REG[0x%04X]", idx);
462		idx += gctx->reg_block;
463		switch (gctx->io_mode) {
464		case ATOM_IO_MM:
465			if (idx == 0)
466				gctx->card->reg_write(gctx->card, idx,
467						      val << 2);
468			else
469				gctx->card->reg_write(gctx->card, idx, val);
470			break;
471		case ATOM_IO_PCI:
472			DRM_INFO(
473			       "PCI registers are not implemented.\n");
474			return;
475		case ATOM_IO_SYSIO:
476			DRM_INFO(
477			       "SYSIO registers are not implemented.\n");
478			return;
479		default:
480			if (!(gctx->io_mode & 0x80)) {
481				DRM_INFO("Bad IO mode.\n");
482				return;
483			}
484			if (!gctx->iio[gctx->io_mode & 0xFF]) {
485				DRM_INFO(
486				       "Undefined indirect IO write method %d.\n",
487				       gctx->io_mode & 0x7F);
488				return;
489			}
490			atom_iio_execute(gctx, gctx->iio[gctx->io_mode & 0xFF],
491					 idx, val);
492		}
493		break;
494	case ATOM_ARG_PS:
495		idx = U8(*ptr);
496		(*ptr)++;
497		ATOM_DEBUG_PRINT("PS[0x%02X]", idx);
498		ctx->ps[idx] = cpu_to_le32(val);
499		break;
500	case ATOM_ARG_WS:
501		idx = U8(*ptr);
502		(*ptr)++;
503		ATOM_DEBUG_PRINT("WS[0x%02X]", idx);
504		switch (idx) {
505		case ATOM_WS_QUOTIENT:
506			gctx->divmul[0] = val;
507			break;
508		case ATOM_WS_REMAINDER:
509			gctx->divmul[1] = val;
510			break;
511		case ATOM_WS_DATAPTR:
512			gctx->data_block = val;
513			break;
514		case ATOM_WS_SHIFT:
515			gctx->shift = val;
516			break;
517		case ATOM_WS_OR_MASK:
518		case ATOM_WS_AND_MASK:
519			break;
520		case ATOM_WS_FB_WINDOW:
521			gctx->fb_base = val;
522			break;
523		case ATOM_WS_ATTRIBUTES:
524			gctx->io_attr = val;
525			break;
526		case ATOM_WS_REGPTR:
527			gctx->reg_block = val;
528			break;
529		default:
530			ctx->ws[idx] = val;
531		}
532		break;
533	case ATOM_ARG_FB:
534		idx = U8(*ptr);
535		(*ptr)++;
536		if ((gctx->fb_base + (idx * 4)) > gctx->scratch_size_bytes) {
537			DRM_ERROR("ATOM: fb write beyond scratch region: %d vs. %d\n",
538				  gctx->fb_base + (idx * 4), gctx->scratch_size_bytes);
539		} else
540			gctx->scratch[(gctx->fb_base / 4) + idx] = val;
541		ATOM_DEBUG_PRINT("FB[0x%02X]", idx);
542		break;
543	case ATOM_ARG_PLL:
544		idx = U8(*ptr);
545		(*ptr)++;
546		ATOM_DEBUG_PRINT("PLL[0x%02X]", idx);
547		gctx->card->pll_write(gctx->card, idx, val);
548		break;
549	case ATOM_ARG_MC:
550		idx = U8(*ptr);
551		(*ptr)++;
552		ATOM_DEBUG_PRINT("MC[0x%02X]", idx);
553		gctx->card->mc_write(gctx->card, idx, val);
554		return;
555	}
556	switch (align) {
557	case ATOM_SRC_DWORD:
558		ATOM_DEBUG_PRINT(".[31:0] <- 0x%08X\n", old_val);
559		break;
560	case ATOM_SRC_WORD0:
561		ATOM_DEBUG_PRINT(".[15:0] <- 0x%04X\n", old_val);
562		break;
563	case ATOM_SRC_WORD8:
564		ATOM_DEBUG_PRINT(".[23:8] <- 0x%04X\n", old_val);
565		break;
566	case ATOM_SRC_WORD16:
567		ATOM_DEBUG_PRINT(".[31:16] <- 0x%04X\n", old_val);
568		break;
569	case ATOM_SRC_BYTE0:
570		ATOM_DEBUG_PRINT(".[7:0] <- 0x%02X\n", old_val);
571		break;
572	case ATOM_SRC_BYTE8:
573		ATOM_DEBUG_PRINT(".[15:8] <- 0x%02X\n", old_val);
574		break;
575	case ATOM_SRC_BYTE16:
576		ATOM_DEBUG_PRINT(".[23:16] <- 0x%02X\n", old_val);
577		break;
578	case ATOM_SRC_BYTE24:
579		ATOM_DEBUG_PRINT(".[31:24] <- 0x%02X\n", old_val);
580		break;
581	}
582}
583
584static void atom_op_add(atom_exec_context *ctx, int *ptr, int arg)
585{
586	uint8_t attr = U8((*ptr)++);
587	uint32_t dst, src, saved;
588	int dptr = *ptr;
589	ATOM_SDEBUG_PRINT("   dst: ");
590	dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
591	ATOM_SDEBUG_PRINT("   src: ");
592	src = atom_get_src(ctx, attr, ptr);
593	dst += src;
594	ATOM_SDEBUG_PRINT("   dst: ");
595	atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
596}
597
598static void atom_op_and(atom_exec_context *ctx, int *ptr, int arg)
599{
600	uint8_t attr = U8((*ptr)++);
601	uint32_t dst, src, saved;
602	int dptr = *ptr;
603	ATOM_SDEBUG_PRINT("   dst: ");
604	dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
605	ATOM_SDEBUG_PRINT("   src: ");
606	src = atom_get_src(ctx, attr, ptr);
607	dst &= src;
608	ATOM_SDEBUG_PRINT("   dst: ");
609	atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
610}
611
612static void atom_op_beep(atom_exec_context *ctx, int *ptr, int arg)
613{
614	DRM_INFO("ATOM BIOS beeped!\n");
615}
616
617static void atom_op_calltable(atom_exec_context *ctx, int *ptr, int arg)
618{
619	int idx = U8((*ptr)++);
620	int r = 0;
621
622	if (idx < ATOM_TABLE_NAMES_CNT)
623		ATOM_SDEBUG_PRINT("   table: %d (%s)\n", idx, atom_table_names[idx]);
624	else
625		ATOM_SDEBUG_PRINT("   table: %d\n", idx);
626	if (U16(ctx->ctx->cmd_table + 4 + 2 * idx))
627		r = atom_execute_table_locked(ctx->ctx, idx, ctx->ps + ctx->ps_shift);
628	if (r) {
629		ctx->abort = true;
630	}
631}
632
633static void atom_op_clear(atom_exec_context *ctx, int *ptr, int arg)
634{
635	uint8_t attr = U8((*ptr)++);
636	uint32_t saved;
637	int dptr = *ptr;
638	attr &= 0x38;
639	attr |= atom_def_dst[attr >> 3] << 6;
640	atom_get_dst(ctx, arg, attr, ptr, &saved, 0);
641	ATOM_SDEBUG_PRINT("   dst: ");
642	atom_put_dst(ctx, arg, attr, &dptr, 0, saved);
643}
644
645static void atom_op_compare(atom_exec_context *ctx, int *ptr, int arg)
646{
647	uint8_t attr = U8((*ptr)++);
648	uint32_t dst, src;
649	ATOM_SDEBUG_PRINT("   src1: ");
650	dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1);
651	ATOM_SDEBUG_PRINT("   src2: ");
652	src = atom_get_src(ctx, attr, ptr);
653	ctx->ctx->cs_equal = (dst == src);
654	ctx->ctx->cs_above = (dst > src);
655	ATOM_SDEBUG_PRINT("   result: %s %s\n", ctx->ctx->cs_equal ? "EQ" : "NE",
656	       ctx->ctx->cs_above ? "GT" : "LE");
657}
658
659static void atom_op_delay(atom_exec_context *ctx, int *ptr, int arg)
660{
661	unsigned count = U8((*ptr)++);
662	ATOM_SDEBUG_PRINT("   count: %d\n", count);
663	if (arg == ATOM_UNIT_MICROSEC)
664		udelay(count);
665	else if (!drm_can_sleep())
666		mdelay(count);
667	else
668		DRM_MSLEEP(count);
669}
670
671static void atom_op_div(atom_exec_context *ctx, int *ptr, int arg)
672{
673	uint8_t attr = U8((*ptr)++);
674	uint32_t dst, src;
675	ATOM_SDEBUG_PRINT("   src1: ");
676	dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1);
677	ATOM_SDEBUG_PRINT("   src2: ");
678	src = atom_get_src(ctx, attr, ptr);
679	if (src != 0) {
680		ctx->ctx->divmul[0] = dst / src;
681		ctx->ctx->divmul[1] = dst % src;
682	} else {
683		ctx->ctx->divmul[0] = 0;
684		ctx->ctx->divmul[1] = 0;
685	}
686}
687
688static void atom_op_eot(atom_exec_context *ctx, int *ptr, int arg)
689{
690	/* functionally, a nop */
691}
692
693static void atom_op_jump(atom_exec_context *ctx, int *ptr, int arg)
694{
695	int execute = 0, target = U16(*ptr);
696	unsigned long cjiffies;
697
698	(*ptr) += 2;
699	switch (arg) {
700	case ATOM_COND_ABOVE:
701		execute = ctx->ctx->cs_above;
702		break;
703	case ATOM_COND_ABOVEOREQUAL:
704		execute = ctx->ctx->cs_above || ctx->ctx->cs_equal;
705		break;
706	case ATOM_COND_ALWAYS:
707		execute = 1;
708		break;
709	case ATOM_COND_BELOW:
710		execute = !(ctx->ctx->cs_above || ctx->ctx->cs_equal);
711		break;
712	case ATOM_COND_BELOWOREQUAL:
713		execute = !ctx->ctx->cs_above;
714		break;
715	case ATOM_COND_EQUAL:
716		execute = ctx->ctx->cs_equal;
717		break;
718	case ATOM_COND_NOTEQUAL:
719		execute = !ctx->ctx->cs_equal;
720		break;
721	}
722	if (arg != ATOM_COND_ALWAYS)
723		ATOM_SDEBUG_PRINT("   taken: %s\n", execute ? "yes" : "no");
724	ATOM_SDEBUG_PRINT("   target: 0x%04X\n", target);
725	if (execute) {
726		if (ctx->last_jump == (ctx->start + target)) {
727			cjiffies = jiffies;
728			if (time_after(cjiffies, ctx->last_jump_jiffies)) {
729				cjiffies -= ctx->last_jump_jiffies;
730				if ((jiffies_to_msecs(cjiffies) > 5000)) {
731					DRM_ERROR("atombios stuck in loop for more than 5secs aborting\n");
732					ctx->abort = true;
733				}
734			} else {
735				/* jiffies wrap around we will just wait a little longer */
736				ctx->last_jump_jiffies = jiffies;
737			}
738		} else {
739			ctx->last_jump = ctx->start + target;
740			ctx->last_jump_jiffies = jiffies;
741		}
742		*ptr = ctx->start + target;
743	}
744}
745
746static void atom_op_mask(atom_exec_context *ctx, int *ptr, int arg)
747{
748	uint8_t attr = U8((*ptr)++);
749	uint32_t dst, mask, src, saved;
750	int dptr = *ptr;
751	ATOM_SDEBUG_PRINT("   dst: ");
752	dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
753	mask = atom_get_src_direct(ctx, ((attr >> 3) & 7), ptr);
754	ATOM_SDEBUG_PRINT("   mask: 0x%08x", mask);
755	ATOM_SDEBUG_PRINT("   src: ");
756	src = atom_get_src(ctx, attr, ptr);
757	dst &= mask;
758	dst |= src;
759	ATOM_SDEBUG_PRINT("   dst: ");
760	atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
761}
762
763static void atom_op_move(atom_exec_context *ctx, int *ptr, int arg)
764{
765	uint8_t attr = U8((*ptr)++);
766	uint32_t src, saved;
767	int dptr = *ptr;
768	if (((attr >> 3) & 7) != ATOM_SRC_DWORD)
769		atom_get_dst(ctx, arg, attr, ptr, &saved, 0);
770	else {
771		atom_skip_dst(ctx, arg, attr, ptr);
772		saved = 0xCDCDCDCD;
773	}
774	ATOM_SDEBUG_PRINT("   src: ");
775	src = atom_get_src(ctx, attr, ptr);
776	ATOM_SDEBUG_PRINT("   dst: ");
777	atom_put_dst(ctx, arg, attr, &dptr, src, saved);
778}
779
780static void atom_op_mul(atom_exec_context *ctx, int *ptr, int arg)
781{
782	uint8_t attr = U8((*ptr)++);
783	uint32_t dst, src;
784	ATOM_SDEBUG_PRINT("   src1: ");
785	dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1);
786	ATOM_SDEBUG_PRINT("   src2: ");
787	src = atom_get_src(ctx, attr, ptr);
788	ctx->ctx->divmul[0] = dst * src;
789}
790
791static void atom_op_nop(atom_exec_context *ctx, int *ptr, int arg)
792{
793	/* nothing */
794}
795
796static void atom_op_or(atom_exec_context *ctx, int *ptr, int arg)
797{
798	uint8_t attr = U8((*ptr)++);
799	uint32_t dst, src, saved;
800	int dptr = *ptr;
801	ATOM_SDEBUG_PRINT("   dst: ");
802	dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
803	ATOM_SDEBUG_PRINT("   src: ");
804	src = atom_get_src(ctx, attr, ptr);
805	dst |= src;
806	ATOM_SDEBUG_PRINT("   dst: ");
807	atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
808}
809
810static void atom_op_postcard(atom_exec_context *ctx, int *ptr, int arg)
811{
812	uint8_t val = U8((*ptr)++);
813	ATOM_SDEBUG_PRINT("POST card output: 0x%02X\n", val);
814}
815
816static void atom_op_repeat(atom_exec_context *ctx, int *ptr, int arg)
817{
818	DRM_INFO("unimplemented!\n");
819}
820
821static void atom_op_restorereg(atom_exec_context *ctx, int *ptr, int arg)
822{
823	DRM_INFO("unimplemented!\n");
824}
825
826static void atom_op_savereg(atom_exec_context *ctx, int *ptr, int arg)
827{
828	DRM_INFO("unimplemented!\n");
829}
830
831static void atom_op_setdatablock(atom_exec_context *ctx, int *ptr, int arg)
832{
833	int idx = U8(*ptr);
834	(*ptr)++;
835	ATOM_SDEBUG_PRINT("   block: %d\n", idx);
836	if (!idx)
837		ctx->ctx->data_block = 0;
838	else if (idx == 255)
839		ctx->ctx->data_block = ctx->start;
840	else
841		ctx->ctx->data_block = U16(ctx->ctx->data_table + 4 + 2 * idx);
842	ATOM_SDEBUG_PRINT("   base: 0x%04X\n", ctx->ctx->data_block);
843}
844
845static void atom_op_setfbbase(atom_exec_context *ctx, int *ptr, int arg)
846{
847	uint8_t attr = U8((*ptr)++);
848	ATOM_SDEBUG_PRINT("   fb_base: ");
849	ctx->ctx->fb_base = atom_get_src(ctx, attr, ptr);
850}
851
852static void atom_op_setport(atom_exec_context *ctx, int *ptr, int arg)
853{
854	int port;
855	switch (arg) {
856	case ATOM_PORT_ATI:
857		port = U16(*ptr);
858		if (port < ATOM_IO_NAMES_CNT)
859			ATOM_SDEBUG_PRINT("   port: %d (%s)\n", port, atom_io_names[port]);
860		else
861			ATOM_SDEBUG_PRINT("   port: %d\n", port);
862		if (!port)
863			ctx->ctx->io_mode = ATOM_IO_MM;
864		else
865			ctx->ctx->io_mode = ATOM_IO_IIO | port;
866		(*ptr) += 2;
867		break;
868	case ATOM_PORT_PCI:
869		ctx->ctx->io_mode = ATOM_IO_PCI;
870		(*ptr)++;
871		break;
872	case ATOM_PORT_SYSIO:
873		ctx->ctx->io_mode = ATOM_IO_SYSIO;
874		(*ptr)++;
875		break;
876	}
877}
878
879static void atom_op_setregblock(atom_exec_context *ctx, int *ptr, int arg)
880{
881	ctx->ctx->reg_block = U16(*ptr);
882	(*ptr) += 2;
883	ATOM_SDEBUG_PRINT("   base: 0x%04X\n", ctx->ctx->reg_block);
884}
885
886static void atom_op_shift_left(atom_exec_context *ctx, int *ptr, int arg)
887{
888	uint8_t attr = U8((*ptr)++), shift;
889	uint32_t saved, dst;
890	int dptr = *ptr;
891	attr &= 0x38;
892	attr |= atom_def_dst[attr >> 3] << 6;
893	ATOM_SDEBUG_PRINT("   dst: ");
894	dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
895	shift = atom_get_src_direct(ctx, ATOM_SRC_BYTE0, ptr);
896	ATOM_SDEBUG_PRINT("   shift: %d\n", shift);
897	dst <<= shift;
898	ATOM_SDEBUG_PRINT("   dst: ");
899	atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
900}
901
902static void atom_op_shift_right(atom_exec_context *ctx, int *ptr, int arg)
903{
904	uint8_t attr = U8((*ptr)++), shift;
905	uint32_t saved, dst;
906	int dptr = *ptr;
907	attr &= 0x38;
908	attr |= atom_def_dst[attr >> 3] << 6;
909	ATOM_SDEBUG_PRINT("   dst: ");
910	dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
911	shift = atom_get_src_direct(ctx, ATOM_SRC_BYTE0, ptr);
912	ATOM_SDEBUG_PRINT("   shift: %d\n", shift);
913	dst >>= shift;
914	ATOM_SDEBUG_PRINT("   dst: ");
915	atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
916}
917
918static void atom_op_shl(atom_exec_context *ctx, int *ptr, int arg)
919{
920	uint8_t attr = U8((*ptr)++), shift;
921	uint32_t saved, dst;
922	int dptr = *ptr;
923	uint32_t dst_align = atom_dst_to_src[(attr >> 3) & 7][(attr >> 6) & 3];
924	ATOM_SDEBUG_PRINT("   dst: ");
925	dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
926	/* op needs to full dst value */
927	dst = saved;
928	shift = atom_get_src(ctx, attr, ptr);
929	ATOM_SDEBUG_PRINT("   shift: %d\n", shift);
930	dst <<= shift;
931	dst &= atom_arg_mask[dst_align];
932	dst >>= atom_arg_shift[dst_align];
933	ATOM_SDEBUG_PRINT("   dst: ");
934	atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
935}
936
937static void atom_op_shr(atom_exec_context *ctx, int *ptr, int arg)
938{
939	uint8_t attr = U8((*ptr)++), shift;
940	uint32_t saved, dst;
941	int dptr = *ptr;
942	uint32_t dst_align = atom_dst_to_src[(attr >> 3) & 7][(attr >> 6) & 3];
943	ATOM_SDEBUG_PRINT("   dst: ");
944	dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
945	/* op needs to full dst value */
946	dst = saved;
947	shift = atom_get_src(ctx, attr, ptr);
948	ATOM_SDEBUG_PRINT("   shift: %d\n", shift);
949	dst >>= shift;
950	dst &= atom_arg_mask[dst_align];
951	dst >>= atom_arg_shift[dst_align];
952	ATOM_SDEBUG_PRINT("   dst: ");
953	atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
954}
955
956static void atom_op_sub(atom_exec_context *ctx, int *ptr, int arg)
957{
958	uint8_t attr = U8((*ptr)++);
959	uint32_t dst, src, saved;
960	int dptr = *ptr;
961	ATOM_SDEBUG_PRINT("   dst: ");
962	dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
963	ATOM_SDEBUG_PRINT("   src: ");
964	src = atom_get_src(ctx, attr, ptr);
965	dst -= src;
966	ATOM_SDEBUG_PRINT("   dst: ");
967	atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
968}
969
970static void atom_op_switch(atom_exec_context *ctx, int *ptr, int arg)
971{
972	uint8_t attr = U8((*ptr)++);
973	uint32_t src, val, target;
974	ATOM_SDEBUG_PRINT("   switch: ");
975	src = atom_get_src(ctx, attr, ptr);
976	while (U16(*ptr) != ATOM_CASE_END)
977		if (U8(*ptr) == ATOM_CASE_MAGIC) {
978			(*ptr)++;
979			ATOM_SDEBUG_PRINT("   case: ");
980			val =
981			    atom_get_src(ctx, (attr & 0x38) | ATOM_ARG_IMM,
982					 ptr);
983			target = U16(*ptr);
984			if (val == src) {
985				ATOM_SDEBUG_PRINT("   target: %04X\n", target);
986				*ptr = ctx->start + target;
987				return;
988			}
989			(*ptr) += 2;
990		} else {
991			DRM_INFO("Bad case.\n");
992			return;
993		}
994	(*ptr) += 2;
995}
996
997static void atom_op_test(atom_exec_context *ctx, int *ptr, int arg)
998{
999	uint8_t attr = U8((*ptr)++);
1000	uint32_t dst, src;
1001	ATOM_SDEBUG_PRINT("   src1: ");
1002	dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1);
1003	ATOM_SDEBUG_PRINT("   src2: ");
1004	src = atom_get_src(ctx, attr, ptr);
1005	ctx->ctx->cs_equal = ((dst & src) == 0);
1006	ATOM_SDEBUG_PRINT("   result: %s\n", ctx->ctx->cs_equal ? "EQ" : "NE");
1007}
1008
1009static void atom_op_xor(atom_exec_context *ctx, int *ptr, int arg)
1010{
1011	uint8_t attr = U8((*ptr)++);
1012	uint32_t dst, src, saved;
1013	int dptr = *ptr;
1014	ATOM_SDEBUG_PRINT("   dst: ");
1015	dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
1016	ATOM_SDEBUG_PRINT("   src: ");
1017	src = atom_get_src(ctx, attr, ptr);
1018	dst ^= src;
1019	ATOM_SDEBUG_PRINT("   dst: ");
1020	atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
1021}
1022
1023static void atom_op_debug(atom_exec_context *ctx, int *ptr, int arg)
1024{
1025	DRM_INFO("unimplemented!\n");
1026}
1027
1028static struct {
1029	void (*func) (atom_exec_context *, int *, int);
1030	int arg;
1031} opcode_table[ATOM_OP_CNT] = {
1032	{
1033	NULL, 0}, {
1034	atom_op_move, ATOM_ARG_REG}, {
1035	atom_op_move, ATOM_ARG_PS}, {
1036	atom_op_move, ATOM_ARG_WS}, {
1037	atom_op_move, ATOM_ARG_FB}, {
1038	atom_op_move, ATOM_ARG_PLL}, {
1039	atom_op_move, ATOM_ARG_MC}, {
1040	atom_op_and, ATOM_ARG_REG}, {
1041	atom_op_and, ATOM_ARG_PS}, {
1042	atom_op_and, ATOM_ARG_WS}, {
1043	atom_op_and, ATOM_ARG_FB}, {
1044	atom_op_and, ATOM_ARG_PLL}, {
1045	atom_op_and, ATOM_ARG_MC}, {
1046	atom_op_or, ATOM_ARG_REG}, {
1047	atom_op_or, ATOM_ARG_PS}, {
1048	atom_op_or, ATOM_ARG_WS}, {
1049	atom_op_or, ATOM_ARG_FB}, {
1050	atom_op_or, ATOM_ARG_PLL}, {
1051	atom_op_or, ATOM_ARG_MC}, {
1052	atom_op_shift_left, ATOM_ARG_REG}, {
1053	atom_op_shift_left, ATOM_ARG_PS}, {
1054	atom_op_shift_left, ATOM_ARG_WS}, {
1055	atom_op_shift_left, ATOM_ARG_FB}, {
1056	atom_op_shift_left, ATOM_ARG_PLL}, {
1057	atom_op_shift_left, ATOM_ARG_MC}, {
1058	atom_op_shift_right, ATOM_ARG_REG}, {
1059	atom_op_shift_right, ATOM_ARG_PS}, {
1060	atom_op_shift_right, ATOM_ARG_WS}, {
1061	atom_op_shift_right, ATOM_ARG_FB}, {
1062	atom_op_shift_right, ATOM_ARG_PLL}, {
1063	atom_op_shift_right, ATOM_ARG_MC}, {
1064	atom_op_mul, ATOM_ARG_REG}, {
1065	atom_op_mul, ATOM_ARG_PS}, {
1066	atom_op_mul, ATOM_ARG_WS}, {
1067	atom_op_mul, ATOM_ARG_FB}, {
1068	atom_op_mul, ATOM_ARG_PLL}, {
1069	atom_op_mul, ATOM_ARG_MC}, {
1070	atom_op_div, ATOM_ARG_REG}, {
1071	atom_op_div, ATOM_ARG_PS}, {
1072	atom_op_div, ATOM_ARG_WS}, {
1073	atom_op_div, ATOM_ARG_FB}, {
1074	atom_op_div, ATOM_ARG_PLL}, {
1075	atom_op_div, ATOM_ARG_MC}, {
1076	atom_op_add, ATOM_ARG_REG}, {
1077	atom_op_add, ATOM_ARG_PS}, {
1078	atom_op_add, ATOM_ARG_WS}, {
1079	atom_op_add, ATOM_ARG_FB}, {
1080	atom_op_add, ATOM_ARG_PLL}, {
1081	atom_op_add, ATOM_ARG_MC}, {
1082	atom_op_sub, ATOM_ARG_REG}, {
1083	atom_op_sub, ATOM_ARG_PS}, {
1084	atom_op_sub, ATOM_ARG_WS}, {
1085	atom_op_sub, ATOM_ARG_FB}, {
1086	atom_op_sub, ATOM_ARG_PLL}, {
1087	atom_op_sub, ATOM_ARG_MC}, {
1088	atom_op_setport, ATOM_PORT_ATI}, {
1089	atom_op_setport, ATOM_PORT_PCI}, {
1090	atom_op_setport, ATOM_PORT_SYSIO}, {
1091	atom_op_setregblock, 0}, {
1092	atom_op_setfbbase, 0}, {
1093	atom_op_compare, ATOM_ARG_REG}, {
1094	atom_op_compare, ATOM_ARG_PS}, {
1095	atom_op_compare, ATOM_ARG_WS}, {
1096	atom_op_compare, ATOM_ARG_FB}, {
1097	atom_op_compare, ATOM_ARG_PLL}, {
1098	atom_op_compare, ATOM_ARG_MC}, {
1099	atom_op_switch, 0}, {
1100	atom_op_jump, ATOM_COND_ALWAYS}, {
1101	atom_op_jump, ATOM_COND_EQUAL}, {
1102	atom_op_jump, ATOM_COND_BELOW}, {
1103	atom_op_jump, ATOM_COND_ABOVE}, {
1104	atom_op_jump, ATOM_COND_BELOWOREQUAL}, {
1105	atom_op_jump, ATOM_COND_ABOVEOREQUAL}, {
1106	atom_op_jump, ATOM_COND_NOTEQUAL}, {
1107	atom_op_test, ATOM_ARG_REG}, {
1108	atom_op_test, ATOM_ARG_PS}, {
1109	atom_op_test, ATOM_ARG_WS}, {
1110	atom_op_test, ATOM_ARG_FB}, {
1111	atom_op_test, ATOM_ARG_PLL}, {
1112	atom_op_test, ATOM_ARG_MC}, {
1113	atom_op_delay, ATOM_UNIT_MILLISEC}, {
1114	atom_op_delay, ATOM_UNIT_MICROSEC}, {
1115	atom_op_calltable, 0}, {
1116	atom_op_repeat, 0}, {
1117	atom_op_clear, ATOM_ARG_REG}, {
1118	atom_op_clear, ATOM_ARG_PS}, {
1119	atom_op_clear, ATOM_ARG_WS}, {
1120	atom_op_clear, ATOM_ARG_FB}, {
1121	atom_op_clear, ATOM_ARG_PLL}, {
1122	atom_op_clear, ATOM_ARG_MC}, {
1123	atom_op_nop, 0}, {
1124	atom_op_eot, 0}, {
1125	atom_op_mask, ATOM_ARG_REG}, {
1126	atom_op_mask, ATOM_ARG_PS}, {
1127	atom_op_mask, ATOM_ARG_WS}, {
1128	atom_op_mask, ATOM_ARG_FB}, {
1129	atom_op_mask, ATOM_ARG_PLL}, {
1130	atom_op_mask, ATOM_ARG_MC}, {
1131	atom_op_postcard, 0}, {
1132	atom_op_beep, 0}, {
1133	atom_op_savereg, 0}, {
1134	atom_op_restorereg, 0}, {
1135	atom_op_setdatablock, 0}, {
1136	atom_op_xor, ATOM_ARG_REG}, {
1137	atom_op_xor, ATOM_ARG_PS}, {
1138	atom_op_xor, ATOM_ARG_WS}, {
1139	atom_op_xor, ATOM_ARG_FB}, {
1140	atom_op_xor, ATOM_ARG_PLL}, {
1141	atom_op_xor, ATOM_ARG_MC}, {
1142	atom_op_shl, ATOM_ARG_REG}, {
1143	atom_op_shl, ATOM_ARG_PS}, {
1144	atom_op_shl, ATOM_ARG_WS}, {
1145	atom_op_shl, ATOM_ARG_FB}, {
1146	atom_op_shl, ATOM_ARG_PLL}, {
1147	atom_op_shl, ATOM_ARG_MC}, {
1148	atom_op_shr, ATOM_ARG_REG}, {
1149	atom_op_shr, ATOM_ARG_PS}, {
1150	atom_op_shr, ATOM_ARG_WS}, {
1151	atom_op_shr, ATOM_ARG_FB}, {
1152	atom_op_shr, ATOM_ARG_PLL}, {
1153	atom_op_shr, ATOM_ARG_MC}, {
1154atom_op_debug, 0},};
1155
1156static int atom_execute_table_locked(struct atom_context *ctx, int index, uint32_t * params)
1157{
1158	int base = CU16(ctx->cmd_table + 4 + 2 * index);
1159	int len, ws, ps, ptr;
1160	unsigned char op;
1161	atom_exec_context ectx;
1162	int ret = 0;
1163
1164	if (!base)
1165		return -EINVAL;
1166
1167	len = CU16(base + ATOM_CT_SIZE_PTR);
1168	ws = CU8(base + ATOM_CT_WS_PTR);
1169	ps = CU8(base + ATOM_CT_PS_PTR) & ATOM_CT_PS_MASK;
1170	ptr = base + ATOM_CT_CODE_PTR;
1171
1172	ATOM_SDEBUG_PRINT(">> execute %04X (len %d, WS %d, PS %d)\n", base, len, ws, ps);
1173
1174	ectx.ctx = ctx;
1175	ectx.ps_shift = ps / 4;
1176	ectx.start = base;
1177	ectx.ps = params;
1178	ectx.abort = false;
1179	ectx.last_jump = 0;
1180	if (ws)
1181		ectx.ws = malloc(4 * ws, DRM_MEM_DRIVER, M_NOWAIT | M_ZERO);
1182	else
1183		ectx.ws = NULL;
1184
1185	debug_depth++;
1186	while (1) {
1187		op = CU8(ptr++);
1188		if (op < ATOM_OP_NAMES_CNT)
1189			ATOM_SDEBUG_PRINT("%s @ 0x%04X\n", atom_op_names[op], ptr - 1);
1190		else
1191			ATOM_SDEBUG_PRINT("[%d] @ 0x%04X\n", op, ptr - 1);
1192		if (ectx.abort) {
1193			DRM_ERROR("atombios stuck executing %04X (len %d, WS %d, PS %d) @ 0x%04X\n",
1194				base, len, ws, ps, ptr - 1);
1195			ret = -EINVAL;
1196			goto free;
1197		}
1198
1199		if (op < ATOM_OP_CNT && op > 0)
1200			opcode_table[op].func(&ectx, &ptr,
1201					      opcode_table[op].arg);
1202		else
1203			break;
1204
1205		if (op == ATOM_OP_EOT)
1206			break;
1207	}
1208	debug_depth--;
1209	ATOM_SDEBUG_PRINT("<<\n");
1210
1211free:
1212	if (ws)
1213		free(ectx.ws, DRM_MEM_DRIVER);
1214	return ret;
1215}
1216
1217int atom_execute_table(struct atom_context *ctx, int index, uint32_t * params)
1218{
1219	int r;
1220
1221	sx_xlock(&ctx->mutex);
1222	/* reset reg block */
1223	ctx->reg_block = 0;
1224	/* reset fb window */
1225	ctx->fb_base = 0;
1226	/* reset io mode */
1227	ctx->io_mode = ATOM_IO_MM;
1228	r = atom_execute_table_locked(ctx, index, params);
1229	sx_xunlock(&ctx->mutex);
1230	return r;
1231}
1232
1233static int atom_iio_len[] = { 1, 2, 3, 3, 3, 3, 4, 4, 4, 3 };
1234
1235static void atom_index_iio(struct atom_context *ctx, int base)
1236{
1237	ctx->iio = malloc(2 * 256, DRM_MEM_DRIVER, M_NOWAIT | M_ZERO);
1238	while (CU8(base) == ATOM_IIO_START) {
1239		ctx->iio[CU8(base + 1)] = base + 2;
1240		base += 2;
1241		while (CU8(base) != ATOM_IIO_END)
1242			base += atom_iio_len[CU8(base)];
1243		base += 3;
1244	}
1245}
1246
1247struct atom_context *atom_parse(struct card_info *card, void *bios)
1248{
1249	int base;
1250	struct atom_context *ctx =
1251	    malloc(sizeof(struct atom_context), DRM_MEM_DRIVER, M_NOWAIT | M_ZERO);
1252	char *str;
1253	char name[512];
1254	int i;
1255
1256	if (!ctx)
1257		return NULL;
1258
1259	ctx->card = card;
1260	ctx->bios = bios;
1261
1262	if (CU16(0) != ATOM_BIOS_MAGIC) {
1263		DRM_INFO("Invalid BIOS magic.\n");
1264		free(ctx, DRM_MEM_DRIVER);
1265		return NULL;
1266	}
1267	if (strncmp
1268	    (CSTR(ATOM_ATI_MAGIC_PTR), ATOM_ATI_MAGIC,
1269	     strlen(ATOM_ATI_MAGIC))) {
1270		DRM_INFO("Invalid ATI magic.\n");
1271		free(ctx, DRM_MEM_DRIVER);
1272		return NULL;
1273	}
1274
1275	base = CU16(ATOM_ROM_TABLE_PTR);
1276	if (strncmp
1277	    (CSTR(base + ATOM_ROM_MAGIC_PTR), ATOM_ROM_MAGIC,
1278	     strlen(ATOM_ROM_MAGIC))) {
1279		DRM_INFO("Invalid ATOM magic.\n");
1280		free(ctx, DRM_MEM_DRIVER);
1281		return NULL;
1282	}
1283
1284	ctx->cmd_table = CU16(base + ATOM_ROM_CMD_PTR);
1285	ctx->data_table = CU16(base + ATOM_ROM_DATA_PTR);
1286	atom_index_iio(ctx, CU16(ctx->data_table + ATOM_DATA_IIO_PTR) + 4);
1287
1288	str = CSTR(CU16(base + ATOM_ROM_MSG_PTR));
1289	while (*str && ((*str == '\n') || (*str == '\r')))
1290		str++;
1291	/* name string isn't always 0 terminated */
1292	for (i = 0; i < 511; i++) {
1293		name[i] = str[i];
1294		if (name[i] < '.' || name[i] > 'z') {
1295			name[i] = 0;
1296			break;
1297		}
1298	}
1299	DRM_INFO("ATOM BIOS: %s\n", name);
1300
1301	return ctx;
1302}
1303
1304int atom_asic_init(struct atom_context *ctx)
1305{
1306	struct radeon_device *rdev = ctx->card->dev->dev_private;
1307	int hwi = CU16(ctx->data_table + ATOM_DATA_FWI_PTR);
1308	uint32_t ps[16];
1309	int ret;
1310
1311	memset(ps, 0, 64);
1312
1313	ps[0] = cpu_to_le32(CU32(hwi + ATOM_FWI_DEFSCLK_PTR));
1314	ps[1] = cpu_to_le32(CU32(hwi + ATOM_FWI_DEFMCLK_PTR));
1315	if (!ps[0] || !ps[1])
1316		return 1;
1317
1318	if (!CU16(ctx->cmd_table + 4 + 2 * ATOM_CMD_INIT))
1319		return 1;
1320	ret = atom_execute_table(ctx, ATOM_CMD_INIT, ps);
1321	if (ret)
1322		return ret;
1323
1324	memset(ps, 0, 64);
1325
1326	if (rdev->family < CHIP_R600) {
1327		if (CU16(ctx->cmd_table + 4 + 2 * ATOM_CMD_SPDFANCNTL))
1328			atom_execute_table(ctx, ATOM_CMD_SPDFANCNTL, ps);
1329	}
1330	return ret;
1331}
1332
1333void atom_destroy(struct atom_context *ctx)
1334{
1335	if (ctx->iio)
1336		free(ctx->iio, DRM_MEM_DRIVER);
1337	free(ctx, DRM_MEM_DRIVER);
1338}
1339
1340bool atom_parse_data_header(struct atom_context *ctx, int index,
1341			    uint16_t * size, uint8_t * frev, uint8_t * crev,
1342			    uint16_t * data_start)
1343{
1344	int offset = index * 2 + 4;
1345	int idx = CU16(ctx->data_table + offset);
1346	u16 *mdt = (u16 *)((char *)ctx->bios + ctx->data_table + 4);
1347
1348	if (!mdt[index])
1349		return false;
1350
1351	if (size)
1352		*size = CU16(idx);
1353	if (frev)
1354		*frev = CU8(idx + 2);
1355	if (crev)
1356		*crev = CU8(idx + 3);
1357	*data_start = idx;
1358	return true;
1359}
1360
1361bool atom_parse_cmd_header(struct atom_context *ctx, int index, uint8_t * frev,
1362			   uint8_t * crev)
1363{
1364	int offset = index * 2 + 4;
1365	int idx = CU16(ctx->cmd_table + offset);
1366	u16 *mct = (u16 *)((char *)ctx->bios + ctx->cmd_table + 4);
1367
1368	if (!mct[index])
1369		return false;
1370
1371	if (frev)
1372		*frev = CU8(idx + 2);
1373	if (crev)
1374		*crev = CU8(idx + 3);
1375	return true;
1376}
1377
1378int atom_allocate_fb_scratch(struct atom_context *ctx)
1379{
1380	int index = GetIndexIntoMasterTable(DATA, VRAM_UsageByFirmware);
1381	uint16_t data_offset;
1382	int usage_bytes = 0;
1383	struct _ATOM_VRAM_USAGE_BY_FIRMWARE *firmware_usage;
1384
1385	if (atom_parse_data_header(ctx, index, NULL, NULL, NULL, &data_offset)) {
1386		firmware_usage = (struct _ATOM_VRAM_USAGE_BY_FIRMWARE *)((char *)ctx->bios + data_offset);
1387
1388		DRM_DEBUG("atom firmware requested %08x %dkb\n",
1389			  le32_to_cpu(firmware_usage->asFirmwareVramReserveInfo[0].ulStartAddrUsedByFirmware),
1390			  le16_to_cpu(firmware_usage->asFirmwareVramReserveInfo[0].usFirmwareUseInKb));
1391
1392		usage_bytes = le16_to_cpu(firmware_usage->asFirmwareVramReserveInfo[0].usFirmwareUseInKb) * 1024;
1393	}
1394	ctx->scratch_size_bytes = 0;
1395	if (usage_bytes == 0)
1396		usage_bytes = 20 * 1024;
1397	/* allocate some scratch memory */
1398	ctx->scratch = malloc(usage_bytes, DRM_MEM_DRIVER, M_NOWAIT | M_ZERO);
1399	if (!ctx->scratch)
1400		return -ENOMEM;
1401	ctx->scratch_size_bytes = usage_bytes;
1402	return 0;
1403}
1404