1254885Sdumbbell/*
2254885Sdumbbell * Copyright 2008 Advanced Micro Devices, Inc.
3254885Sdumbbell *
4254885Sdumbbell * Permission is hereby granted, free of charge, to any person obtaining a
5254885Sdumbbell * copy of this software and associated documentation files (the "Software"),
6254885Sdumbbell * to deal in the Software without restriction, including without limitation
7254885Sdumbbell * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8254885Sdumbbell * and/or sell copies of the Software, and to permit persons to whom the
9254885Sdumbbell * Software is furnished to do so, subject to the following conditions:
10254885Sdumbbell *
11254885Sdumbbell * The above copyright notice and this permission notice shall be included in
12254885Sdumbbell * all copies or substantial portions of the Software.
13254885Sdumbbell *
14254885Sdumbbell * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15254885Sdumbbell * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16254885Sdumbbell * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17254885Sdumbbell * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18254885Sdumbbell * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19254885Sdumbbell * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20254885Sdumbbell * OTHER DEALINGS IN THE SOFTWARE.
21254885Sdumbbell *
22254885Sdumbbell * Author: Stanislaw Skowronek
23254885Sdumbbell */
24254885Sdumbbell
25254885Sdumbbell#include <sys/cdefs.h>
26254885Sdumbbell__FBSDID("$FreeBSD$");
27254885Sdumbbell
28254885Sdumbbell#define ATOM_DEBUG
29254885Sdumbbell
30254885Sdumbbell#include "atom.h"
31254885Sdumbbell#include "atom-names.h"
32254885Sdumbbell#include "atom-bits.h"
33254885Sdumbbell#include "radeon.h"
34254885Sdumbbell
35254885Sdumbbell#define ATOM_COND_ABOVE		0
36254885Sdumbbell#define ATOM_COND_ABOVEOREQUAL	1
37254885Sdumbbell#define ATOM_COND_ALWAYS	2
38254885Sdumbbell#define ATOM_COND_BELOW		3
39254885Sdumbbell#define ATOM_COND_BELOWOREQUAL	4
40254885Sdumbbell#define ATOM_COND_EQUAL		5
41254885Sdumbbell#define ATOM_COND_NOTEQUAL	6
42254885Sdumbbell
43254885Sdumbbell#define ATOM_PORT_ATI	0
44254885Sdumbbell#define ATOM_PORT_PCI	1
45254885Sdumbbell#define ATOM_PORT_SYSIO	2
46254885Sdumbbell
47254885Sdumbbell#define ATOM_UNIT_MICROSEC	0
48254885Sdumbbell#define ATOM_UNIT_MILLISEC	1
49254885Sdumbbell
50254885Sdumbbell#define PLL_INDEX	2
51254885Sdumbbell#define PLL_DATA	3
52254885Sdumbbell
53254885Sdumbbelltypedef struct {
54254885Sdumbbell	struct atom_context *ctx;
55254885Sdumbbell	uint32_t *ps, *ws;
56254885Sdumbbell	int ps_shift;
57254885Sdumbbell	uint16_t start;
58254885Sdumbbell	unsigned last_jump;
59254885Sdumbbell	unsigned long last_jump_jiffies;
60254885Sdumbbell	bool abort;
61254885Sdumbbell} atom_exec_context;
62254885Sdumbbell
63254885Sdumbbellint atom_debug = 0;
64254885Sdumbbellstatic int atom_execute_table_locked(struct atom_context *ctx, int index, uint32_t * params);
65254885Sdumbbell
66254885Sdumbbellstatic uint32_t atom_arg_mask[8] =
67254885Sdumbbell    { 0xFFFFFFFF, 0xFFFF, 0xFFFF00, 0xFFFF0000, 0xFF, 0xFF00, 0xFF0000,
68254885Sdumbbell0xFF000000 };
69254885Sdumbbellstatic int atom_arg_shift[8] = { 0, 0, 8, 16, 0, 8, 16, 24 };
70254885Sdumbbell
71254885Sdumbbellstatic int atom_dst_to_src[8][4] = {
72254885Sdumbbell	/* translate destination alignment field to the source alignment encoding */
73254885Sdumbbell	{0, 0, 0, 0},
74254885Sdumbbell	{1, 2, 3, 0},
75254885Sdumbbell	{1, 2, 3, 0},
76254885Sdumbbell	{1, 2, 3, 0},
77254885Sdumbbell	{4, 5, 6, 7},
78254885Sdumbbell	{4, 5, 6, 7},
79254885Sdumbbell	{4, 5, 6, 7},
80254885Sdumbbell	{4, 5, 6, 7},
81254885Sdumbbell};
82254885Sdumbbellstatic int atom_def_dst[8] = { 0, 0, 1, 2, 0, 1, 2, 3 };
83254885Sdumbbell
84254885Sdumbbellstatic int debug_depth = 0;
85254885Sdumbbell#ifdef ATOM_DEBUG
86254885Sdumbbellstatic void debug_print_spaces(int n)
87254885Sdumbbell{
88254885Sdumbbell	while (n--)
89254885Sdumbbell		printf("   ");
90254885Sdumbbell}
91254885Sdumbbell
92254894Sdumbbell#define ATOM_DEBUG_PRINT(...) do if (atom_debug) { printf(__FILE__ __VA_ARGS__); } while (0)
93254894Sdumbbell#define ATOM_SDEBUG_PRINT(...) do if (atom_debug) { printf(__FILE__); debug_print_spaces(debug_depth); printf(__VA_ARGS__); } while (0)
94254885Sdumbbell#else
95254894Sdumbbell#define ATOM_DEBUG_PRINT(...) do { } while (0)
96254894Sdumbbell#define ATOM_SDEBUG_PRINT(...) do { } while (0)
97254885Sdumbbell#endif
98254885Sdumbbell
99254885Sdumbbellstatic uint32_t atom_iio_execute(struct atom_context *ctx, int base,
100254885Sdumbbell				 uint32_t index, uint32_t data)
101254885Sdumbbell{
102254885Sdumbbell	struct radeon_device *rdev = ctx->card->dev->dev_private;
103254885Sdumbbell	uint32_t temp = 0xCDCDCDCD;
104254885Sdumbbell
105254885Sdumbbell	while (1)
106254885Sdumbbell		switch (CU8(base)) {
107254885Sdumbbell		case ATOM_IIO_NOP:
108254885Sdumbbell			base++;
109254885Sdumbbell			break;
110254885Sdumbbell		case ATOM_IIO_READ:
111254885Sdumbbell			temp = ctx->card->ioreg_read(ctx->card, CU16(base + 1));
112254885Sdumbbell			base += 3;
113254885Sdumbbell			break;
114254885Sdumbbell		case ATOM_IIO_WRITE:
115254885Sdumbbell			if (rdev->family == CHIP_RV515)
116254885Sdumbbell				(void)ctx->card->ioreg_read(ctx->card, CU16(base + 1));
117254885Sdumbbell			ctx->card->ioreg_write(ctx->card, CU16(base + 1), temp);
118254885Sdumbbell			base += 3;
119254885Sdumbbell			break;
120254885Sdumbbell		case ATOM_IIO_CLEAR:
121254885Sdumbbell			temp &=
122254885Sdumbbell			    ~((0xFFFFFFFF >> (32 - CU8(base + 1))) <<
123254885Sdumbbell			      CU8(base + 2));
124254885Sdumbbell			base += 3;
125254885Sdumbbell			break;
126254885Sdumbbell		case ATOM_IIO_SET:
127254885Sdumbbell			temp |=
128254885Sdumbbell			    (0xFFFFFFFF >> (32 - CU8(base + 1))) << CU8(base +
129254885Sdumbbell									2);
130254885Sdumbbell			base += 3;
131254885Sdumbbell			break;
132254885Sdumbbell		case ATOM_IIO_MOVE_INDEX:
133254885Sdumbbell			temp &=
134254885Sdumbbell			    ~((0xFFFFFFFF >> (32 - CU8(base + 1))) <<
135254885Sdumbbell			      CU8(base + 3));
136254885Sdumbbell			temp |=
137254885Sdumbbell			    ((index >> CU8(base + 2)) &
138254885Sdumbbell			     (0xFFFFFFFF >> (32 - CU8(base + 1)))) << CU8(base +
139254885Sdumbbell									  3);
140254885Sdumbbell			base += 4;
141254885Sdumbbell			break;
142254885Sdumbbell		case ATOM_IIO_MOVE_DATA:
143254885Sdumbbell			temp &=
144254885Sdumbbell			    ~((0xFFFFFFFF >> (32 - CU8(base + 1))) <<
145254885Sdumbbell			      CU8(base + 3));
146254885Sdumbbell			temp |=
147254885Sdumbbell			    ((data >> CU8(base + 2)) &
148254885Sdumbbell			     (0xFFFFFFFF >> (32 - CU8(base + 1)))) << CU8(base +
149254885Sdumbbell									  3);
150254885Sdumbbell			base += 4;
151254885Sdumbbell			break;
152254885Sdumbbell		case ATOM_IIO_MOVE_ATTR:
153254885Sdumbbell			temp &=
154254885Sdumbbell			    ~((0xFFFFFFFF >> (32 - CU8(base + 1))) <<
155254885Sdumbbell			      CU8(base + 3));
156254885Sdumbbell			temp |=
157254885Sdumbbell			    ((ctx->
158254885Sdumbbell			      io_attr >> CU8(base + 2)) & (0xFFFFFFFF >> (32 -
159254885Sdumbbell									  CU8
160254885Sdumbbell									  (base
161254885Sdumbbell									   +
162254885Sdumbbell									   1))))
163254885Sdumbbell			    << CU8(base + 3);
164254885Sdumbbell			base += 4;
165254885Sdumbbell			break;
166254885Sdumbbell		case ATOM_IIO_END:
167254885Sdumbbell			return temp;
168254885Sdumbbell		default:
169254885Sdumbbell			DRM_INFO("Unknown IIO opcode.\n");
170254885Sdumbbell			return 0;
171254885Sdumbbell		}
172254885Sdumbbell}
173254885Sdumbbell
174254885Sdumbbellstatic uint32_t atom_get_src_int(atom_exec_context *ctx, uint8_t attr,
175254885Sdumbbell				 int *ptr, uint32_t *saved, int print)
176254885Sdumbbell{
177254885Sdumbbell	uint32_t idx, val = 0xCDCDCDCD, align, arg;
178254885Sdumbbell	struct atom_context *gctx = ctx->ctx;
179254885Sdumbbell	arg = attr & 7;
180254885Sdumbbell	align = (attr >> 3) & 7;
181254885Sdumbbell	switch (arg) {
182254885Sdumbbell	case ATOM_ARG_REG:
183254885Sdumbbell		idx = U16(*ptr);
184254885Sdumbbell		(*ptr) += 2;
185254885Sdumbbell		if (print)
186254894Sdumbbell			ATOM_DEBUG_PRINT("REG[0x%04X]", idx);
187254885Sdumbbell		idx += gctx->reg_block;
188254885Sdumbbell		switch (gctx->io_mode) {
189254885Sdumbbell		case ATOM_IO_MM:
190254885Sdumbbell			val = gctx->card->reg_read(gctx->card, idx);
191254885Sdumbbell			break;
192254885Sdumbbell		case ATOM_IO_PCI:
193254885Sdumbbell			DRM_INFO(
194254885Sdumbbell			       "PCI registers are not implemented.\n");
195254885Sdumbbell			return 0;
196254885Sdumbbell		case ATOM_IO_SYSIO:
197254885Sdumbbell			DRM_INFO(
198254885Sdumbbell			       "SYSIO registers are not implemented.\n");
199254885Sdumbbell			return 0;
200254885Sdumbbell		default:
201254885Sdumbbell			if (!(gctx->io_mode & 0x80)) {
202254885Sdumbbell				DRM_INFO("Bad IO mode.\n");
203254885Sdumbbell				return 0;
204254885Sdumbbell			}
205254885Sdumbbell			if (!gctx->iio[gctx->io_mode & 0x7F]) {
206254885Sdumbbell				DRM_INFO(
207254885Sdumbbell				       "Undefined indirect IO read method %d.\n",
208254885Sdumbbell				       gctx->io_mode & 0x7F);
209254885Sdumbbell				return 0;
210254885Sdumbbell			}
211254885Sdumbbell			val =
212254885Sdumbbell			    atom_iio_execute(gctx,
213254885Sdumbbell					     gctx->iio[gctx->io_mode & 0x7F],
214254885Sdumbbell					     idx, 0);
215254885Sdumbbell		}
216254885Sdumbbell		break;
217254885Sdumbbell	case ATOM_ARG_PS:
218254885Sdumbbell		idx = U8(*ptr);
219254885Sdumbbell		(*ptr)++;
220254885Sdumbbell		/* get_unaligned_le32 avoids unaligned accesses from atombios
221254885Sdumbbell		 * tables, noticed on a DEC Alpha. */
222254885Sdumbbell		val = get_unaligned_le32((u32 *)&ctx->ps[idx]);
223254885Sdumbbell		if (print)
224254894Sdumbbell			ATOM_DEBUG_PRINT("PS[0x%02X,0x%04X]", idx, val);
225254885Sdumbbell		break;
226254885Sdumbbell	case ATOM_ARG_WS:
227254885Sdumbbell		idx = U8(*ptr);
228254885Sdumbbell		(*ptr)++;
229254885Sdumbbell		if (print)
230254894Sdumbbell			ATOM_DEBUG_PRINT("WS[0x%02X]", idx);
231254885Sdumbbell		switch (idx) {
232254885Sdumbbell		case ATOM_WS_QUOTIENT:
233254885Sdumbbell			val = gctx->divmul[0];
234254885Sdumbbell			break;
235254885Sdumbbell		case ATOM_WS_REMAINDER:
236254885Sdumbbell			val = gctx->divmul[1];
237254885Sdumbbell			break;
238254885Sdumbbell		case ATOM_WS_DATAPTR:
239254885Sdumbbell			val = gctx->data_block;
240254885Sdumbbell			break;
241254885Sdumbbell		case ATOM_WS_SHIFT:
242254885Sdumbbell			val = gctx->shift;
243254885Sdumbbell			break;
244254885Sdumbbell		case ATOM_WS_OR_MASK:
245254885Sdumbbell			val = 1 << gctx->shift;
246254885Sdumbbell			break;
247254885Sdumbbell		case ATOM_WS_AND_MASK:
248254885Sdumbbell			val = ~(1 << gctx->shift);
249254885Sdumbbell			break;
250254885Sdumbbell		case ATOM_WS_FB_WINDOW:
251254885Sdumbbell			val = gctx->fb_base;
252254885Sdumbbell			break;
253254885Sdumbbell		case ATOM_WS_ATTRIBUTES:
254254885Sdumbbell			val = gctx->io_attr;
255254885Sdumbbell			break;
256254885Sdumbbell		case ATOM_WS_REGPTR:
257254885Sdumbbell			val = gctx->reg_block;
258254885Sdumbbell			break;
259254885Sdumbbell		default:
260254885Sdumbbell			val = ctx->ws[idx];
261254885Sdumbbell		}
262254885Sdumbbell		break;
263254885Sdumbbell	case ATOM_ARG_ID:
264254885Sdumbbell		idx = U16(*ptr);
265254885Sdumbbell		(*ptr) += 2;
266254885Sdumbbell		if (print) {
267254885Sdumbbell			if (gctx->data_block)
268254894Sdumbbell				ATOM_DEBUG_PRINT("ID[0x%04X+%04X]", idx, gctx->data_block);
269254885Sdumbbell			else
270254894Sdumbbell				ATOM_DEBUG_PRINT("ID[0x%04X]", idx);
271254885Sdumbbell		}
272254885Sdumbbell		val = U32(idx + gctx->data_block);
273254885Sdumbbell		break;
274254885Sdumbbell	case ATOM_ARG_FB:
275254885Sdumbbell		idx = U8(*ptr);
276254885Sdumbbell		(*ptr)++;
277254885Sdumbbell		if ((gctx->fb_base + (idx * 4)) > gctx->scratch_size_bytes) {
278254885Sdumbbell			DRM_ERROR("ATOM: fb read beyond scratch region: %d vs. %d\n",
279254885Sdumbbell				  gctx->fb_base + (idx * 4), gctx->scratch_size_bytes);
280254885Sdumbbell			val = 0;
281254885Sdumbbell		} else
282254885Sdumbbell			val = gctx->scratch[(gctx->fb_base / 4) + idx];
283254885Sdumbbell		if (print)
284254894Sdumbbell			ATOM_DEBUG_PRINT("FB[0x%02X]", idx);
285254885Sdumbbell		break;
286254885Sdumbbell	case ATOM_ARG_IMM:
287254885Sdumbbell		switch (align) {
288254885Sdumbbell		case ATOM_SRC_DWORD:
289254885Sdumbbell			val = U32(*ptr);
290254885Sdumbbell			(*ptr) += 4;
291254885Sdumbbell			if (print)
292254894Sdumbbell				ATOM_DEBUG_PRINT("IMM 0x%08X\n", val);
293254885Sdumbbell			return val;
294254885Sdumbbell		case ATOM_SRC_WORD0:
295254885Sdumbbell		case ATOM_SRC_WORD8:
296254885Sdumbbell		case ATOM_SRC_WORD16:
297254885Sdumbbell			val = U16(*ptr);
298254885Sdumbbell			(*ptr) += 2;
299254885Sdumbbell			if (print)
300254894Sdumbbell				ATOM_DEBUG_PRINT("IMM 0x%04X\n", val);
301254885Sdumbbell			return val;
302254885Sdumbbell		case ATOM_SRC_BYTE0:
303254885Sdumbbell		case ATOM_SRC_BYTE8:
304254885Sdumbbell		case ATOM_SRC_BYTE16:
305254885Sdumbbell		case ATOM_SRC_BYTE24:
306254885Sdumbbell			val = U8(*ptr);
307254885Sdumbbell			(*ptr)++;
308254885Sdumbbell			if (print)
309254894Sdumbbell				ATOM_DEBUG_PRINT("IMM 0x%02X\n", val);
310254885Sdumbbell			return val;
311254885Sdumbbell		}
312254885Sdumbbell		return 0;
313254885Sdumbbell	case ATOM_ARG_PLL:
314254885Sdumbbell		idx = U8(*ptr);
315254885Sdumbbell		(*ptr)++;
316254885Sdumbbell		if (print)
317254894Sdumbbell			ATOM_DEBUG_PRINT("PLL[0x%02X]", idx);
318254885Sdumbbell		val = gctx->card->pll_read(gctx->card, idx);
319254885Sdumbbell		break;
320254885Sdumbbell	case ATOM_ARG_MC:
321254885Sdumbbell		idx = U8(*ptr);
322254885Sdumbbell		(*ptr)++;
323254885Sdumbbell		if (print)
324254894Sdumbbell			ATOM_DEBUG_PRINT("MC[0x%02X]", idx);
325254885Sdumbbell		val = gctx->card->mc_read(gctx->card, idx);
326254885Sdumbbell		break;
327254885Sdumbbell	}
328254885Sdumbbell	if (saved)
329254885Sdumbbell		*saved = val;
330254885Sdumbbell	val &= atom_arg_mask[align];
331254885Sdumbbell	val >>= atom_arg_shift[align];
332254885Sdumbbell	if (print)
333254885Sdumbbell		switch (align) {
334254885Sdumbbell		case ATOM_SRC_DWORD:
335254894Sdumbbell			ATOM_DEBUG_PRINT(".[31:0] -> 0x%08X\n", val);
336254885Sdumbbell			break;
337254885Sdumbbell		case ATOM_SRC_WORD0:
338254894Sdumbbell			ATOM_DEBUG_PRINT(".[15:0] -> 0x%04X\n", val);
339254885Sdumbbell			break;
340254885Sdumbbell		case ATOM_SRC_WORD8:
341254894Sdumbbell			ATOM_DEBUG_PRINT(".[23:8] -> 0x%04X\n", val);
342254885Sdumbbell			break;
343254885Sdumbbell		case ATOM_SRC_WORD16:
344254894Sdumbbell			ATOM_DEBUG_PRINT(".[31:16] -> 0x%04X\n", val);
345254885Sdumbbell			break;
346254885Sdumbbell		case ATOM_SRC_BYTE0:
347254894Sdumbbell			ATOM_DEBUG_PRINT(".[7:0] -> 0x%02X\n", val);
348254885Sdumbbell			break;
349254885Sdumbbell		case ATOM_SRC_BYTE8:
350254894Sdumbbell			ATOM_DEBUG_PRINT(".[15:8] -> 0x%02X\n", val);
351254885Sdumbbell			break;
352254885Sdumbbell		case ATOM_SRC_BYTE16:
353254894Sdumbbell			ATOM_DEBUG_PRINT(".[23:16] -> 0x%02X\n", val);
354254885Sdumbbell			break;
355254885Sdumbbell		case ATOM_SRC_BYTE24:
356254894Sdumbbell			ATOM_DEBUG_PRINT(".[31:24] -> 0x%02X\n", val);
357254885Sdumbbell			break;
358254885Sdumbbell		}
359254885Sdumbbell	return val;
360254885Sdumbbell}
361254885Sdumbbell
362254885Sdumbbellstatic void atom_skip_src_int(atom_exec_context *ctx, uint8_t attr, int *ptr)
363254885Sdumbbell{
364254885Sdumbbell	uint32_t align = (attr >> 3) & 7, arg = attr & 7;
365254885Sdumbbell	switch (arg) {
366254885Sdumbbell	case ATOM_ARG_REG:
367254885Sdumbbell	case ATOM_ARG_ID:
368254885Sdumbbell		(*ptr) += 2;
369254885Sdumbbell		break;
370254885Sdumbbell	case ATOM_ARG_PLL:
371254885Sdumbbell	case ATOM_ARG_MC:
372254885Sdumbbell	case ATOM_ARG_PS:
373254885Sdumbbell	case ATOM_ARG_WS:
374254885Sdumbbell	case ATOM_ARG_FB:
375254885Sdumbbell		(*ptr)++;
376254885Sdumbbell		break;
377254885Sdumbbell	case ATOM_ARG_IMM:
378254885Sdumbbell		switch (align) {
379254885Sdumbbell		case ATOM_SRC_DWORD:
380254885Sdumbbell			(*ptr) += 4;
381254885Sdumbbell			return;
382254885Sdumbbell		case ATOM_SRC_WORD0:
383254885Sdumbbell		case ATOM_SRC_WORD8:
384254885Sdumbbell		case ATOM_SRC_WORD16:
385254885Sdumbbell			(*ptr) += 2;
386254885Sdumbbell			return;
387254885Sdumbbell		case ATOM_SRC_BYTE0:
388254885Sdumbbell		case ATOM_SRC_BYTE8:
389254885Sdumbbell		case ATOM_SRC_BYTE16:
390254885Sdumbbell		case ATOM_SRC_BYTE24:
391254885Sdumbbell			(*ptr)++;
392254885Sdumbbell			return;
393254885Sdumbbell		}
394254885Sdumbbell		return;
395254885Sdumbbell	}
396254885Sdumbbell}
397254885Sdumbbell
398254885Sdumbbellstatic uint32_t atom_get_src(atom_exec_context *ctx, uint8_t attr, int *ptr)
399254885Sdumbbell{
400254885Sdumbbell	return atom_get_src_int(ctx, attr, ptr, NULL, 1);
401254885Sdumbbell}
402254885Sdumbbell
403254885Sdumbbellstatic uint32_t atom_get_src_direct(atom_exec_context *ctx, uint8_t align, int *ptr)
404254885Sdumbbell{
405254885Sdumbbell	uint32_t val = 0xCDCDCDCD;
406254885Sdumbbell
407254885Sdumbbell	switch (align) {
408254885Sdumbbell	case ATOM_SRC_DWORD:
409254885Sdumbbell		val = U32(*ptr);
410254885Sdumbbell		(*ptr) += 4;
411254885Sdumbbell		break;
412254885Sdumbbell	case ATOM_SRC_WORD0:
413254885Sdumbbell	case ATOM_SRC_WORD8:
414254885Sdumbbell	case ATOM_SRC_WORD16:
415254885Sdumbbell		val = U16(*ptr);
416254885Sdumbbell		(*ptr) += 2;
417254885Sdumbbell		break;
418254885Sdumbbell	case ATOM_SRC_BYTE0:
419254885Sdumbbell	case ATOM_SRC_BYTE8:
420254885Sdumbbell	case ATOM_SRC_BYTE16:
421254885Sdumbbell	case ATOM_SRC_BYTE24:
422254885Sdumbbell		val = U8(*ptr);
423254885Sdumbbell		(*ptr)++;
424254885Sdumbbell		break;
425254885Sdumbbell	}
426254885Sdumbbell	return val;
427254885Sdumbbell}
428254885Sdumbbell
429254885Sdumbbellstatic uint32_t atom_get_dst(atom_exec_context *ctx, int arg, uint8_t attr,
430254885Sdumbbell			     int *ptr, uint32_t *saved, int print)
431254885Sdumbbell{
432254885Sdumbbell	return atom_get_src_int(ctx,
433254885Sdumbbell				arg | atom_dst_to_src[(attr >> 3) &
434254885Sdumbbell						      7][(attr >> 6) & 3] << 3,
435254885Sdumbbell				ptr, saved, print);
436254885Sdumbbell}
437254885Sdumbbell
438254885Sdumbbellstatic void atom_skip_dst(atom_exec_context *ctx, int arg, uint8_t attr, int *ptr)
439254885Sdumbbell{
440254885Sdumbbell	atom_skip_src_int(ctx,
441254885Sdumbbell			  arg | atom_dst_to_src[(attr >> 3) & 7][(attr >> 6) &
442254885Sdumbbell								 3] << 3, ptr);
443254885Sdumbbell}
444254885Sdumbbell
445254885Sdumbbellstatic void atom_put_dst(atom_exec_context *ctx, int arg, uint8_t attr,
446254885Sdumbbell			 int *ptr, uint32_t val, uint32_t saved)
447254885Sdumbbell{
448254885Sdumbbell	uint32_t align =
449254885Sdumbbell	    atom_dst_to_src[(attr >> 3) & 7][(attr >> 6) & 3], old_val =
450254885Sdumbbell	    val, idx;
451254885Sdumbbell	struct atom_context *gctx = ctx->ctx;
452254885Sdumbbell	old_val &= atom_arg_mask[align] >> atom_arg_shift[align];
453254885Sdumbbell	val <<= atom_arg_shift[align];
454254885Sdumbbell	val &= atom_arg_mask[align];
455254885Sdumbbell	saved &= ~atom_arg_mask[align];
456254885Sdumbbell	val |= saved;
457254885Sdumbbell	switch (arg) {
458254885Sdumbbell	case ATOM_ARG_REG:
459254885Sdumbbell		idx = U16(*ptr);
460254885Sdumbbell		(*ptr) += 2;
461254894Sdumbbell		ATOM_DEBUG_PRINT("REG[0x%04X]", idx);
462254885Sdumbbell		idx += gctx->reg_block;
463254885Sdumbbell		switch (gctx->io_mode) {
464254885Sdumbbell		case ATOM_IO_MM:
465254885Sdumbbell			if (idx == 0)
466254885Sdumbbell				gctx->card->reg_write(gctx->card, idx,
467254885Sdumbbell						      val << 2);
468254885Sdumbbell			else
469254885Sdumbbell				gctx->card->reg_write(gctx->card, idx, val);
470254885Sdumbbell			break;
471254885Sdumbbell		case ATOM_IO_PCI:
472254885Sdumbbell			DRM_INFO(
473254885Sdumbbell			       "PCI registers are not implemented.\n");
474254885Sdumbbell			return;
475254885Sdumbbell		case ATOM_IO_SYSIO:
476254885Sdumbbell			DRM_INFO(
477254885Sdumbbell			       "SYSIO registers are not implemented.\n");
478254885Sdumbbell			return;
479254885Sdumbbell		default:
480254885Sdumbbell			if (!(gctx->io_mode & 0x80)) {
481254885Sdumbbell				DRM_INFO("Bad IO mode.\n");
482254885Sdumbbell				return;
483254885Sdumbbell			}
484254885Sdumbbell			if (!gctx->iio[gctx->io_mode & 0xFF]) {
485254885Sdumbbell				DRM_INFO(
486254885Sdumbbell				       "Undefined indirect IO write method %d.\n",
487254885Sdumbbell				       gctx->io_mode & 0x7F);
488254885Sdumbbell				return;
489254885Sdumbbell			}
490254885Sdumbbell			atom_iio_execute(gctx, gctx->iio[gctx->io_mode & 0xFF],
491254885Sdumbbell					 idx, val);
492254885Sdumbbell		}
493254885Sdumbbell		break;
494254885Sdumbbell	case ATOM_ARG_PS:
495254885Sdumbbell		idx = U8(*ptr);
496254885Sdumbbell		(*ptr)++;
497254894Sdumbbell		ATOM_DEBUG_PRINT("PS[0x%02X]", idx);
498254885Sdumbbell		ctx->ps[idx] = cpu_to_le32(val);
499254885Sdumbbell		break;
500254885Sdumbbell	case ATOM_ARG_WS:
501254885Sdumbbell		idx = U8(*ptr);
502254885Sdumbbell		(*ptr)++;
503254894Sdumbbell		ATOM_DEBUG_PRINT("WS[0x%02X]", idx);
504254885Sdumbbell		switch (idx) {
505254885Sdumbbell		case ATOM_WS_QUOTIENT:
506254885Sdumbbell			gctx->divmul[0] = val;
507254885Sdumbbell			break;
508254885Sdumbbell		case ATOM_WS_REMAINDER:
509254885Sdumbbell			gctx->divmul[1] = val;
510254885Sdumbbell			break;
511254885Sdumbbell		case ATOM_WS_DATAPTR:
512254885Sdumbbell			gctx->data_block = val;
513254885Sdumbbell			break;
514254885Sdumbbell		case ATOM_WS_SHIFT:
515254885Sdumbbell			gctx->shift = val;
516254885Sdumbbell			break;
517254885Sdumbbell		case ATOM_WS_OR_MASK:
518254885Sdumbbell		case ATOM_WS_AND_MASK:
519254885Sdumbbell			break;
520254885Sdumbbell		case ATOM_WS_FB_WINDOW:
521254885Sdumbbell			gctx->fb_base = val;
522254885Sdumbbell			break;
523254885Sdumbbell		case ATOM_WS_ATTRIBUTES:
524254885Sdumbbell			gctx->io_attr = val;
525254885Sdumbbell			break;
526254885Sdumbbell		case ATOM_WS_REGPTR:
527254885Sdumbbell			gctx->reg_block = val;
528254885Sdumbbell			break;
529254885Sdumbbell		default:
530254885Sdumbbell			ctx->ws[idx] = val;
531254885Sdumbbell		}
532254885Sdumbbell		break;
533254885Sdumbbell	case ATOM_ARG_FB:
534254885Sdumbbell		idx = U8(*ptr);
535254885Sdumbbell		(*ptr)++;
536254885Sdumbbell		if ((gctx->fb_base + (idx * 4)) > gctx->scratch_size_bytes) {
537254885Sdumbbell			DRM_ERROR("ATOM: fb write beyond scratch region: %d vs. %d\n",
538254885Sdumbbell				  gctx->fb_base + (idx * 4), gctx->scratch_size_bytes);
539254885Sdumbbell		} else
540254885Sdumbbell			gctx->scratch[(gctx->fb_base / 4) + idx] = val;
541254894Sdumbbell		ATOM_DEBUG_PRINT("FB[0x%02X]", idx);
542254885Sdumbbell		break;
543254885Sdumbbell	case ATOM_ARG_PLL:
544254885Sdumbbell		idx = U8(*ptr);
545254885Sdumbbell		(*ptr)++;
546254894Sdumbbell		ATOM_DEBUG_PRINT("PLL[0x%02X]", idx);
547254885Sdumbbell		gctx->card->pll_write(gctx->card, idx, val);
548254885Sdumbbell		break;
549254885Sdumbbell	case ATOM_ARG_MC:
550254885Sdumbbell		idx = U8(*ptr);
551254885Sdumbbell		(*ptr)++;
552254894Sdumbbell		ATOM_DEBUG_PRINT("MC[0x%02X]", idx);
553254885Sdumbbell		gctx->card->mc_write(gctx->card, idx, val);
554254885Sdumbbell		return;
555254885Sdumbbell	}
556254885Sdumbbell	switch (align) {
557254885Sdumbbell	case ATOM_SRC_DWORD:
558254894Sdumbbell		ATOM_DEBUG_PRINT(".[31:0] <- 0x%08X\n", old_val);
559254885Sdumbbell		break;
560254885Sdumbbell	case ATOM_SRC_WORD0:
561254894Sdumbbell		ATOM_DEBUG_PRINT(".[15:0] <- 0x%04X\n", old_val);
562254885Sdumbbell		break;
563254885Sdumbbell	case ATOM_SRC_WORD8:
564254894Sdumbbell		ATOM_DEBUG_PRINT(".[23:8] <- 0x%04X\n", old_val);
565254885Sdumbbell		break;
566254885Sdumbbell	case ATOM_SRC_WORD16:
567254894Sdumbbell		ATOM_DEBUG_PRINT(".[31:16] <- 0x%04X\n", old_val);
568254885Sdumbbell		break;
569254885Sdumbbell	case ATOM_SRC_BYTE0:
570254894Sdumbbell		ATOM_DEBUG_PRINT(".[7:0] <- 0x%02X\n", old_val);
571254885Sdumbbell		break;
572254885Sdumbbell	case ATOM_SRC_BYTE8:
573254894Sdumbbell		ATOM_DEBUG_PRINT(".[15:8] <- 0x%02X\n", old_val);
574254885Sdumbbell		break;
575254885Sdumbbell	case ATOM_SRC_BYTE16:
576254894Sdumbbell		ATOM_DEBUG_PRINT(".[23:16] <- 0x%02X\n", old_val);
577254885Sdumbbell		break;
578254885Sdumbbell	case ATOM_SRC_BYTE24:
579254894Sdumbbell		ATOM_DEBUG_PRINT(".[31:24] <- 0x%02X\n", old_val);
580254885Sdumbbell		break;
581254885Sdumbbell	}
582254885Sdumbbell}
583254885Sdumbbell
584254885Sdumbbellstatic void atom_op_add(atom_exec_context *ctx, int *ptr, int arg)
585254885Sdumbbell{
586254885Sdumbbell	uint8_t attr = U8((*ptr)++);
587254885Sdumbbell	uint32_t dst, src, saved;
588254885Sdumbbell	int dptr = *ptr;
589254894Sdumbbell	ATOM_SDEBUG_PRINT("   dst: ");
590254885Sdumbbell	dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
591254894Sdumbbell	ATOM_SDEBUG_PRINT("   src: ");
592254885Sdumbbell	src = atom_get_src(ctx, attr, ptr);
593254885Sdumbbell	dst += src;
594254894Sdumbbell	ATOM_SDEBUG_PRINT("   dst: ");
595254885Sdumbbell	atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
596254885Sdumbbell}
597254885Sdumbbell
598254885Sdumbbellstatic void atom_op_and(atom_exec_context *ctx, int *ptr, int arg)
599254885Sdumbbell{
600254885Sdumbbell	uint8_t attr = U8((*ptr)++);
601254885Sdumbbell	uint32_t dst, src, saved;
602254885Sdumbbell	int dptr = *ptr;
603254894Sdumbbell	ATOM_SDEBUG_PRINT("   dst: ");
604254885Sdumbbell	dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
605254894Sdumbbell	ATOM_SDEBUG_PRINT("   src: ");
606254885Sdumbbell	src = atom_get_src(ctx, attr, ptr);
607254885Sdumbbell	dst &= src;
608254894Sdumbbell	ATOM_SDEBUG_PRINT("   dst: ");
609254885Sdumbbell	atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
610254885Sdumbbell}
611254885Sdumbbell
612254885Sdumbbellstatic void atom_op_beep(atom_exec_context *ctx, int *ptr, int arg)
613254885Sdumbbell{
614254885Sdumbbell	DRM_INFO("ATOM BIOS beeped!\n");
615254885Sdumbbell}
616254885Sdumbbell
617254885Sdumbbellstatic void atom_op_calltable(atom_exec_context *ctx, int *ptr, int arg)
618254885Sdumbbell{
619254885Sdumbbell	int idx = U8((*ptr)++);
620254885Sdumbbell	int r = 0;
621254885Sdumbbell
622254885Sdumbbell	if (idx < ATOM_TABLE_NAMES_CNT)
623254894Sdumbbell		ATOM_SDEBUG_PRINT("   table: %d (%s)\n", idx, atom_table_names[idx]);
624254885Sdumbbell	else
625254894Sdumbbell		ATOM_SDEBUG_PRINT("   table: %d\n", idx);
626254885Sdumbbell	if (U16(ctx->ctx->cmd_table + 4 + 2 * idx))
627254885Sdumbbell		r = atom_execute_table_locked(ctx->ctx, idx, ctx->ps + ctx->ps_shift);
628254885Sdumbbell	if (r) {
629254885Sdumbbell		ctx->abort = true;
630254885Sdumbbell	}
631254885Sdumbbell}
632254885Sdumbbell
633254885Sdumbbellstatic void atom_op_clear(atom_exec_context *ctx, int *ptr, int arg)
634254885Sdumbbell{
635254885Sdumbbell	uint8_t attr = U8((*ptr)++);
636254885Sdumbbell	uint32_t saved;
637254885Sdumbbell	int dptr = *ptr;
638254885Sdumbbell	attr &= 0x38;
639254885Sdumbbell	attr |= atom_def_dst[attr >> 3] << 6;
640254885Sdumbbell	atom_get_dst(ctx, arg, attr, ptr, &saved, 0);
641254894Sdumbbell	ATOM_SDEBUG_PRINT("   dst: ");
642254885Sdumbbell	atom_put_dst(ctx, arg, attr, &dptr, 0, saved);
643254885Sdumbbell}
644254885Sdumbbell
645254885Sdumbbellstatic void atom_op_compare(atom_exec_context *ctx, int *ptr, int arg)
646254885Sdumbbell{
647254885Sdumbbell	uint8_t attr = U8((*ptr)++);
648254885Sdumbbell	uint32_t dst, src;
649254894Sdumbbell	ATOM_SDEBUG_PRINT("   src1: ");
650254885Sdumbbell	dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1);
651254894Sdumbbell	ATOM_SDEBUG_PRINT("   src2: ");
652254885Sdumbbell	src = atom_get_src(ctx, attr, ptr);
653254885Sdumbbell	ctx->ctx->cs_equal = (dst == src);
654254885Sdumbbell	ctx->ctx->cs_above = (dst > src);
655254894Sdumbbell	ATOM_SDEBUG_PRINT("   result: %s %s\n", ctx->ctx->cs_equal ? "EQ" : "NE",
656254885Sdumbbell	       ctx->ctx->cs_above ? "GT" : "LE");
657254885Sdumbbell}
658254885Sdumbbell
659254885Sdumbbellstatic void atom_op_delay(atom_exec_context *ctx, int *ptr, int arg)
660254885Sdumbbell{
661254885Sdumbbell	unsigned count = U8((*ptr)++);
662254894Sdumbbell	ATOM_SDEBUG_PRINT("   count: %d\n", count);
663254885Sdumbbell	if (arg == ATOM_UNIT_MICROSEC)
664254885Sdumbbell		DRM_UDELAY(count);
665254885Sdumbbell	else if (!drm_can_sleep())
666254885Sdumbbell		DRM_MDELAY(count);
667254885Sdumbbell	else
668254885Sdumbbell		DRM_MSLEEP(count);
669254885Sdumbbell}
670254885Sdumbbell
671254885Sdumbbellstatic void atom_op_div(atom_exec_context *ctx, int *ptr, int arg)
672254885Sdumbbell{
673254885Sdumbbell	uint8_t attr = U8((*ptr)++);
674254885Sdumbbell	uint32_t dst, src;
675254894Sdumbbell	ATOM_SDEBUG_PRINT("   src1: ");
676254885Sdumbbell	dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1);
677254894Sdumbbell	ATOM_SDEBUG_PRINT("   src2: ");
678254885Sdumbbell	src = atom_get_src(ctx, attr, ptr);
679254885Sdumbbell	if (src != 0) {
680254885Sdumbbell		ctx->ctx->divmul[0] = dst / src;
681254885Sdumbbell		ctx->ctx->divmul[1] = dst % src;
682254885Sdumbbell	} else {
683254885Sdumbbell		ctx->ctx->divmul[0] = 0;
684254885Sdumbbell		ctx->ctx->divmul[1] = 0;
685254885Sdumbbell	}
686254885Sdumbbell}
687254885Sdumbbell
688254885Sdumbbellstatic void atom_op_eot(atom_exec_context *ctx, int *ptr, int arg)
689254885Sdumbbell{
690254885Sdumbbell	/* functionally, a nop */
691254885Sdumbbell}
692254885Sdumbbell
693254885Sdumbbellstatic void atom_op_jump(atom_exec_context *ctx, int *ptr, int arg)
694254885Sdumbbell{
695254885Sdumbbell	int execute = 0, target = U16(*ptr);
696254885Sdumbbell	unsigned long cjiffies;
697254885Sdumbbell
698254885Sdumbbell	(*ptr) += 2;
699254885Sdumbbell	switch (arg) {
700254885Sdumbbell	case ATOM_COND_ABOVE:
701254885Sdumbbell		execute = ctx->ctx->cs_above;
702254885Sdumbbell		break;
703254885Sdumbbell	case ATOM_COND_ABOVEOREQUAL:
704254885Sdumbbell		execute = ctx->ctx->cs_above || ctx->ctx->cs_equal;
705254885Sdumbbell		break;
706254885Sdumbbell	case ATOM_COND_ALWAYS:
707254885Sdumbbell		execute = 1;
708254885Sdumbbell		break;
709254885Sdumbbell	case ATOM_COND_BELOW:
710254885Sdumbbell		execute = !(ctx->ctx->cs_above || ctx->ctx->cs_equal);
711254885Sdumbbell		break;
712254885Sdumbbell	case ATOM_COND_BELOWOREQUAL:
713254885Sdumbbell		execute = !ctx->ctx->cs_above;
714254885Sdumbbell		break;
715254885Sdumbbell	case ATOM_COND_EQUAL:
716254885Sdumbbell		execute = ctx->ctx->cs_equal;
717254885Sdumbbell		break;
718254885Sdumbbell	case ATOM_COND_NOTEQUAL:
719254885Sdumbbell		execute = !ctx->ctx->cs_equal;
720254885Sdumbbell		break;
721254885Sdumbbell	}
722254885Sdumbbell	if (arg != ATOM_COND_ALWAYS)
723254894Sdumbbell		ATOM_SDEBUG_PRINT("   taken: %s\n", execute ? "yes" : "no");
724254894Sdumbbell	ATOM_SDEBUG_PRINT("   target: 0x%04X\n", target);
725254885Sdumbbell	if (execute) {
726254885Sdumbbell		if (ctx->last_jump == (ctx->start + target)) {
727254885Sdumbbell			cjiffies = jiffies;
728254885Sdumbbell			if (time_after(cjiffies, ctx->last_jump_jiffies)) {
729254885Sdumbbell				cjiffies -= ctx->last_jump_jiffies;
730254885Sdumbbell				if ((jiffies_to_msecs(cjiffies) > 5000)) {
731254885Sdumbbell					DRM_ERROR("atombios stuck in loop for more than 5secs aborting\n");
732254885Sdumbbell					ctx->abort = true;
733254885Sdumbbell				}
734254885Sdumbbell			} else {
735254885Sdumbbell				/* jiffies wrap around we will just wait a little longer */
736254885Sdumbbell				ctx->last_jump_jiffies = jiffies;
737254885Sdumbbell			}
738254885Sdumbbell		} else {
739254885Sdumbbell			ctx->last_jump = ctx->start + target;
740254885Sdumbbell			ctx->last_jump_jiffies = jiffies;
741254885Sdumbbell		}
742254885Sdumbbell		*ptr = ctx->start + target;
743254885Sdumbbell	}
744254885Sdumbbell}
745254885Sdumbbell
746254885Sdumbbellstatic void atom_op_mask(atom_exec_context *ctx, int *ptr, int arg)
747254885Sdumbbell{
748254885Sdumbbell	uint8_t attr = U8((*ptr)++);
749254885Sdumbbell	uint32_t dst, mask, src, saved;
750254885Sdumbbell	int dptr = *ptr;
751254894Sdumbbell	ATOM_SDEBUG_PRINT("   dst: ");
752254885Sdumbbell	dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
753254885Sdumbbell	mask = atom_get_src_direct(ctx, ((attr >> 3) & 7), ptr);
754254894Sdumbbell	ATOM_SDEBUG_PRINT("   mask: 0x%08x", mask);
755254894Sdumbbell	ATOM_SDEBUG_PRINT("   src: ");
756254885Sdumbbell	src = atom_get_src(ctx, attr, ptr);
757254885Sdumbbell	dst &= mask;
758254885Sdumbbell	dst |= src;
759254894Sdumbbell	ATOM_SDEBUG_PRINT("   dst: ");
760254885Sdumbbell	atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
761254885Sdumbbell}
762254885Sdumbbell
763254885Sdumbbellstatic void atom_op_move(atom_exec_context *ctx, int *ptr, int arg)
764254885Sdumbbell{
765254885Sdumbbell	uint8_t attr = U8((*ptr)++);
766254885Sdumbbell	uint32_t src, saved;
767254885Sdumbbell	int dptr = *ptr;
768254885Sdumbbell	if (((attr >> 3) & 7) != ATOM_SRC_DWORD)
769254885Sdumbbell		atom_get_dst(ctx, arg, attr, ptr, &saved, 0);
770254885Sdumbbell	else {
771254885Sdumbbell		atom_skip_dst(ctx, arg, attr, ptr);
772254885Sdumbbell		saved = 0xCDCDCDCD;
773254885Sdumbbell	}
774254894Sdumbbell	ATOM_SDEBUG_PRINT("   src: ");
775254885Sdumbbell	src = atom_get_src(ctx, attr, ptr);
776254894Sdumbbell	ATOM_SDEBUG_PRINT("   dst: ");
777254885Sdumbbell	atom_put_dst(ctx, arg, attr, &dptr, src, saved);
778254885Sdumbbell}
779254885Sdumbbell
780254885Sdumbbellstatic void atom_op_mul(atom_exec_context *ctx, int *ptr, int arg)
781254885Sdumbbell{
782254885Sdumbbell	uint8_t attr = U8((*ptr)++);
783254885Sdumbbell	uint32_t dst, src;
784254894Sdumbbell	ATOM_SDEBUG_PRINT("   src1: ");
785254885Sdumbbell	dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1);
786254894Sdumbbell	ATOM_SDEBUG_PRINT("   src2: ");
787254885Sdumbbell	src = atom_get_src(ctx, attr, ptr);
788254885Sdumbbell	ctx->ctx->divmul[0] = dst * src;
789254885Sdumbbell}
790254885Sdumbbell
791254885Sdumbbellstatic void atom_op_nop(atom_exec_context *ctx, int *ptr, int arg)
792254885Sdumbbell{
793254885Sdumbbell	/* nothing */
794254885Sdumbbell}
795254885Sdumbbell
796254885Sdumbbellstatic void atom_op_or(atom_exec_context *ctx, int *ptr, int arg)
797254885Sdumbbell{
798254885Sdumbbell	uint8_t attr = U8((*ptr)++);
799254885Sdumbbell	uint32_t dst, src, saved;
800254885Sdumbbell	int dptr = *ptr;
801254894Sdumbbell	ATOM_SDEBUG_PRINT("   dst: ");
802254885Sdumbbell	dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
803254894Sdumbbell	ATOM_SDEBUG_PRINT("   src: ");
804254885Sdumbbell	src = atom_get_src(ctx, attr, ptr);
805254885Sdumbbell	dst |= src;
806254894Sdumbbell	ATOM_SDEBUG_PRINT("   dst: ");
807254885Sdumbbell	atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
808254885Sdumbbell}
809254885Sdumbbell
810254885Sdumbbellstatic void atom_op_postcard(atom_exec_context *ctx, int *ptr, int arg)
811254885Sdumbbell{
812254885Sdumbbell	uint8_t val = U8((*ptr)++);
813254894Sdumbbell	ATOM_SDEBUG_PRINT("POST card output: 0x%02X\n", val);
814254885Sdumbbell}
815254885Sdumbbell
816254885Sdumbbellstatic void atom_op_repeat(atom_exec_context *ctx, int *ptr, int arg)
817254885Sdumbbell{
818254885Sdumbbell	DRM_INFO("unimplemented!\n");
819254885Sdumbbell}
820254885Sdumbbell
821254885Sdumbbellstatic void atom_op_restorereg(atom_exec_context *ctx, int *ptr, int arg)
822254885Sdumbbell{
823254885Sdumbbell	DRM_INFO("unimplemented!\n");
824254885Sdumbbell}
825254885Sdumbbell
826254885Sdumbbellstatic void atom_op_savereg(atom_exec_context *ctx, int *ptr, int arg)
827254885Sdumbbell{
828254885Sdumbbell	DRM_INFO("unimplemented!\n");
829254885Sdumbbell}
830254885Sdumbbell
831254885Sdumbbellstatic void atom_op_setdatablock(atom_exec_context *ctx, int *ptr, int arg)
832254885Sdumbbell{
833254885Sdumbbell	int idx = U8(*ptr);
834254885Sdumbbell	(*ptr)++;
835254894Sdumbbell	ATOM_SDEBUG_PRINT("   block: %d\n", idx);
836254885Sdumbbell	if (!idx)
837254885Sdumbbell		ctx->ctx->data_block = 0;
838254885Sdumbbell	else if (idx == 255)
839254885Sdumbbell		ctx->ctx->data_block = ctx->start;
840254885Sdumbbell	else
841254885Sdumbbell		ctx->ctx->data_block = U16(ctx->ctx->data_table + 4 + 2 * idx);
842254894Sdumbbell	ATOM_SDEBUG_PRINT("   base: 0x%04X\n", ctx->ctx->data_block);
843254885Sdumbbell}
844254885Sdumbbell
845254885Sdumbbellstatic void atom_op_setfbbase(atom_exec_context *ctx, int *ptr, int arg)
846254885Sdumbbell{
847254885Sdumbbell	uint8_t attr = U8((*ptr)++);
848254894Sdumbbell	ATOM_SDEBUG_PRINT("   fb_base: ");
849254885Sdumbbell	ctx->ctx->fb_base = atom_get_src(ctx, attr, ptr);
850254885Sdumbbell}
851254885Sdumbbell
852254885Sdumbbellstatic void atom_op_setport(atom_exec_context *ctx, int *ptr, int arg)
853254885Sdumbbell{
854254885Sdumbbell	int port;
855254885Sdumbbell	switch (arg) {
856254885Sdumbbell	case ATOM_PORT_ATI:
857254885Sdumbbell		port = U16(*ptr);
858254885Sdumbbell		if (port < ATOM_IO_NAMES_CNT)
859254894Sdumbbell			ATOM_SDEBUG_PRINT("   port: %d (%s)\n", port, atom_io_names[port]);
860254885Sdumbbell		else
861254894Sdumbbell			ATOM_SDEBUG_PRINT("   port: %d\n", port);
862254885Sdumbbell		if (!port)
863254885Sdumbbell			ctx->ctx->io_mode = ATOM_IO_MM;
864254885Sdumbbell		else
865254885Sdumbbell			ctx->ctx->io_mode = ATOM_IO_IIO | port;
866254885Sdumbbell		(*ptr) += 2;
867254885Sdumbbell		break;
868254885Sdumbbell	case ATOM_PORT_PCI:
869254885Sdumbbell		ctx->ctx->io_mode = ATOM_IO_PCI;
870254885Sdumbbell		(*ptr)++;
871254885Sdumbbell		break;
872254885Sdumbbell	case ATOM_PORT_SYSIO:
873254885Sdumbbell		ctx->ctx->io_mode = ATOM_IO_SYSIO;
874254885Sdumbbell		(*ptr)++;
875254885Sdumbbell		break;
876254885Sdumbbell	}
877254885Sdumbbell}
878254885Sdumbbell
879254885Sdumbbellstatic void atom_op_setregblock(atom_exec_context *ctx, int *ptr, int arg)
880254885Sdumbbell{
881254885Sdumbbell	ctx->ctx->reg_block = U16(*ptr);
882254885Sdumbbell	(*ptr) += 2;
883254894Sdumbbell	ATOM_SDEBUG_PRINT("   base: 0x%04X\n", ctx->ctx->reg_block);
884254885Sdumbbell}
885254885Sdumbbell
886254885Sdumbbellstatic void atom_op_shift_left(atom_exec_context *ctx, int *ptr, int arg)
887254885Sdumbbell{
888254885Sdumbbell	uint8_t attr = U8((*ptr)++), shift;
889254885Sdumbbell	uint32_t saved, dst;
890254885Sdumbbell	int dptr = *ptr;
891254885Sdumbbell	attr &= 0x38;
892254885Sdumbbell	attr |= atom_def_dst[attr >> 3] << 6;
893254894Sdumbbell	ATOM_SDEBUG_PRINT("   dst: ");
894254885Sdumbbell	dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
895254885Sdumbbell	shift = atom_get_src_direct(ctx, ATOM_SRC_BYTE0, ptr);
896254894Sdumbbell	ATOM_SDEBUG_PRINT("   shift: %d\n", shift);
897254885Sdumbbell	dst <<= shift;
898254894Sdumbbell	ATOM_SDEBUG_PRINT("   dst: ");
899254885Sdumbbell	atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
900254885Sdumbbell}
901254885Sdumbbell
902254885Sdumbbellstatic void atom_op_shift_right(atom_exec_context *ctx, int *ptr, int arg)
903254885Sdumbbell{
904254885Sdumbbell	uint8_t attr = U8((*ptr)++), shift;
905254885Sdumbbell	uint32_t saved, dst;
906254885Sdumbbell	int dptr = *ptr;
907254885Sdumbbell	attr &= 0x38;
908254885Sdumbbell	attr |= atom_def_dst[attr >> 3] << 6;
909254894Sdumbbell	ATOM_SDEBUG_PRINT("   dst: ");
910254885Sdumbbell	dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
911254885Sdumbbell	shift = atom_get_src_direct(ctx, ATOM_SRC_BYTE0, ptr);
912254894Sdumbbell	ATOM_SDEBUG_PRINT("   shift: %d\n", shift);
913254885Sdumbbell	dst >>= shift;
914254894Sdumbbell	ATOM_SDEBUG_PRINT("   dst: ");
915254885Sdumbbell	atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
916254885Sdumbbell}
917254885Sdumbbell
918254885Sdumbbellstatic void atom_op_shl(atom_exec_context *ctx, int *ptr, int arg)
919254885Sdumbbell{
920254885Sdumbbell	uint8_t attr = U8((*ptr)++), shift;
921254885Sdumbbell	uint32_t saved, dst;
922254885Sdumbbell	int dptr = *ptr;
923254885Sdumbbell	uint32_t dst_align = atom_dst_to_src[(attr >> 3) & 7][(attr >> 6) & 3];
924254894Sdumbbell	ATOM_SDEBUG_PRINT("   dst: ");
925254885Sdumbbell	dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
926254885Sdumbbell	/* op needs to full dst value */
927254885Sdumbbell	dst = saved;
928254885Sdumbbell	shift = atom_get_src(ctx, attr, ptr);
929254894Sdumbbell	ATOM_SDEBUG_PRINT("   shift: %d\n", shift);
930254885Sdumbbell	dst <<= shift;
931254885Sdumbbell	dst &= atom_arg_mask[dst_align];
932254885Sdumbbell	dst >>= atom_arg_shift[dst_align];
933254894Sdumbbell	ATOM_SDEBUG_PRINT("   dst: ");
934254885Sdumbbell	atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
935254885Sdumbbell}
936254885Sdumbbell
937254885Sdumbbellstatic void atom_op_shr(atom_exec_context *ctx, int *ptr, int arg)
938254885Sdumbbell{
939254885Sdumbbell	uint8_t attr = U8((*ptr)++), shift;
940254885Sdumbbell	uint32_t saved, dst;
941254885Sdumbbell	int dptr = *ptr;
942254885Sdumbbell	uint32_t dst_align = atom_dst_to_src[(attr >> 3) & 7][(attr >> 6) & 3];
943254894Sdumbbell	ATOM_SDEBUG_PRINT("   dst: ");
944254885Sdumbbell	dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
945254885Sdumbbell	/* op needs to full dst value */
946254885Sdumbbell	dst = saved;
947254885Sdumbbell	shift = atom_get_src(ctx, attr, ptr);
948254894Sdumbbell	ATOM_SDEBUG_PRINT("   shift: %d\n", shift);
949254885Sdumbbell	dst >>= shift;
950254885Sdumbbell	dst &= atom_arg_mask[dst_align];
951254885Sdumbbell	dst >>= atom_arg_shift[dst_align];
952254894Sdumbbell	ATOM_SDEBUG_PRINT("   dst: ");
953254885Sdumbbell	atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
954254885Sdumbbell}
955254885Sdumbbell
956254885Sdumbbellstatic void atom_op_sub(atom_exec_context *ctx, int *ptr, int arg)
957254885Sdumbbell{
958254885Sdumbbell	uint8_t attr = U8((*ptr)++);
959254885Sdumbbell	uint32_t dst, src, saved;
960254885Sdumbbell	int dptr = *ptr;
961254894Sdumbbell	ATOM_SDEBUG_PRINT("   dst: ");
962254885Sdumbbell	dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
963254894Sdumbbell	ATOM_SDEBUG_PRINT("   src: ");
964254885Sdumbbell	src = atom_get_src(ctx, attr, ptr);
965254885Sdumbbell	dst -= src;
966254894Sdumbbell	ATOM_SDEBUG_PRINT("   dst: ");
967254885Sdumbbell	atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
968254885Sdumbbell}
969254885Sdumbbell
970254885Sdumbbellstatic void atom_op_switch(atom_exec_context *ctx, int *ptr, int arg)
971254885Sdumbbell{
972254885Sdumbbell	uint8_t attr = U8((*ptr)++);
973254885Sdumbbell	uint32_t src, val, target;
974254894Sdumbbell	ATOM_SDEBUG_PRINT("   switch: ");
975254885Sdumbbell	src = atom_get_src(ctx, attr, ptr);
976254885Sdumbbell	while (U16(*ptr) != ATOM_CASE_END)
977254885Sdumbbell		if (U8(*ptr) == ATOM_CASE_MAGIC) {
978254885Sdumbbell			(*ptr)++;
979254894Sdumbbell			ATOM_SDEBUG_PRINT("   case: ");
980254885Sdumbbell			val =
981254885Sdumbbell			    atom_get_src(ctx, (attr & 0x38) | ATOM_ARG_IMM,
982254885Sdumbbell					 ptr);
983254885Sdumbbell			target = U16(*ptr);
984254885Sdumbbell			if (val == src) {
985254894Sdumbbell				ATOM_SDEBUG_PRINT("   target: %04X\n", target);
986254885Sdumbbell				*ptr = ctx->start + target;
987254885Sdumbbell				return;
988254885Sdumbbell			}
989254885Sdumbbell			(*ptr) += 2;
990254885Sdumbbell		} else {
991254885Sdumbbell			DRM_INFO("Bad case.\n");
992254885Sdumbbell			return;
993254885Sdumbbell		}
994254885Sdumbbell	(*ptr) += 2;
995254885Sdumbbell}
996254885Sdumbbell
997254885Sdumbbellstatic void atom_op_test(atom_exec_context *ctx, int *ptr, int arg)
998254885Sdumbbell{
999254885Sdumbbell	uint8_t attr = U8((*ptr)++);
1000254885Sdumbbell	uint32_t dst, src;
1001254894Sdumbbell	ATOM_SDEBUG_PRINT("   src1: ");
1002254885Sdumbbell	dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1);
1003254894Sdumbbell	ATOM_SDEBUG_PRINT("   src2: ");
1004254885Sdumbbell	src = atom_get_src(ctx, attr, ptr);
1005254885Sdumbbell	ctx->ctx->cs_equal = ((dst & src) == 0);
1006254894Sdumbbell	ATOM_SDEBUG_PRINT("   result: %s\n", ctx->ctx->cs_equal ? "EQ" : "NE");
1007254885Sdumbbell}
1008254885Sdumbbell
1009254885Sdumbbellstatic void atom_op_xor(atom_exec_context *ctx, int *ptr, int arg)
1010254885Sdumbbell{
1011254885Sdumbbell	uint8_t attr = U8((*ptr)++);
1012254885Sdumbbell	uint32_t dst, src, saved;
1013254885Sdumbbell	int dptr = *ptr;
1014254894Sdumbbell	ATOM_SDEBUG_PRINT("   dst: ");
1015254885Sdumbbell	dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
1016254894Sdumbbell	ATOM_SDEBUG_PRINT("   src: ");
1017254885Sdumbbell	src = atom_get_src(ctx, attr, ptr);
1018254885Sdumbbell	dst ^= src;
1019254894Sdumbbell	ATOM_SDEBUG_PRINT("   dst: ");
1020254885Sdumbbell	atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
1021254885Sdumbbell}
1022254885Sdumbbell
1023254885Sdumbbellstatic void atom_op_debug(atom_exec_context *ctx, int *ptr, int arg)
1024254885Sdumbbell{
1025254885Sdumbbell	DRM_INFO("unimplemented!\n");
1026254885Sdumbbell}
1027254885Sdumbbell
1028254885Sdumbbellstatic struct {
1029254885Sdumbbell	void (*func) (atom_exec_context *, int *, int);
1030254885Sdumbbell	int arg;
1031254885Sdumbbell} opcode_table[ATOM_OP_CNT] = {
1032254885Sdumbbell	{
1033254885Sdumbbell	NULL, 0}, {
1034254885Sdumbbell	atom_op_move, ATOM_ARG_REG}, {
1035254885Sdumbbell	atom_op_move, ATOM_ARG_PS}, {
1036254885Sdumbbell	atom_op_move, ATOM_ARG_WS}, {
1037254885Sdumbbell	atom_op_move, ATOM_ARG_FB}, {
1038254885Sdumbbell	atom_op_move, ATOM_ARG_PLL}, {
1039254885Sdumbbell	atom_op_move, ATOM_ARG_MC}, {
1040254885Sdumbbell	atom_op_and, ATOM_ARG_REG}, {
1041254885Sdumbbell	atom_op_and, ATOM_ARG_PS}, {
1042254885Sdumbbell	atom_op_and, ATOM_ARG_WS}, {
1043254885Sdumbbell	atom_op_and, ATOM_ARG_FB}, {
1044254885Sdumbbell	atom_op_and, ATOM_ARG_PLL}, {
1045254885Sdumbbell	atom_op_and, ATOM_ARG_MC}, {
1046254885Sdumbbell	atom_op_or, ATOM_ARG_REG}, {
1047254885Sdumbbell	atom_op_or, ATOM_ARG_PS}, {
1048254885Sdumbbell	atom_op_or, ATOM_ARG_WS}, {
1049254885Sdumbbell	atom_op_or, ATOM_ARG_FB}, {
1050254885Sdumbbell	atom_op_or, ATOM_ARG_PLL}, {
1051254885Sdumbbell	atom_op_or, ATOM_ARG_MC}, {
1052254885Sdumbbell	atom_op_shift_left, ATOM_ARG_REG}, {
1053254885Sdumbbell	atom_op_shift_left, ATOM_ARG_PS}, {
1054254885Sdumbbell	atom_op_shift_left, ATOM_ARG_WS}, {
1055254885Sdumbbell	atom_op_shift_left, ATOM_ARG_FB}, {
1056254885Sdumbbell	atom_op_shift_left, ATOM_ARG_PLL}, {
1057254885Sdumbbell	atom_op_shift_left, ATOM_ARG_MC}, {
1058254885Sdumbbell	atom_op_shift_right, ATOM_ARG_REG}, {
1059254885Sdumbbell	atom_op_shift_right, ATOM_ARG_PS}, {
1060254885Sdumbbell	atom_op_shift_right, ATOM_ARG_WS}, {
1061254885Sdumbbell	atom_op_shift_right, ATOM_ARG_FB}, {
1062254885Sdumbbell	atom_op_shift_right, ATOM_ARG_PLL}, {
1063254885Sdumbbell	atom_op_shift_right, ATOM_ARG_MC}, {
1064254885Sdumbbell	atom_op_mul, ATOM_ARG_REG}, {
1065254885Sdumbbell	atom_op_mul, ATOM_ARG_PS}, {
1066254885Sdumbbell	atom_op_mul, ATOM_ARG_WS}, {
1067254885Sdumbbell	atom_op_mul, ATOM_ARG_FB}, {
1068254885Sdumbbell	atom_op_mul, ATOM_ARG_PLL}, {
1069254885Sdumbbell	atom_op_mul, ATOM_ARG_MC}, {
1070254885Sdumbbell	atom_op_div, ATOM_ARG_REG}, {
1071254885Sdumbbell	atom_op_div, ATOM_ARG_PS}, {
1072254885Sdumbbell	atom_op_div, ATOM_ARG_WS}, {
1073254885Sdumbbell	atom_op_div, ATOM_ARG_FB}, {
1074254885Sdumbbell	atom_op_div, ATOM_ARG_PLL}, {
1075254885Sdumbbell	atom_op_div, ATOM_ARG_MC}, {
1076254885Sdumbbell	atom_op_add, ATOM_ARG_REG}, {
1077254885Sdumbbell	atom_op_add, ATOM_ARG_PS}, {
1078254885Sdumbbell	atom_op_add, ATOM_ARG_WS}, {
1079254885Sdumbbell	atom_op_add, ATOM_ARG_FB}, {
1080254885Sdumbbell	atom_op_add, ATOM_ARG_PLL}, {
1081254885Sdumbbell	atom_op_add, ATOM_ARG_MC}, {
1082254885Sdumbbell	atom_op_sub, ATOM_ARG_REG}, {
1083254885Sdumbbell	atom_op_sub, ATOM_ARG_PS}, {
1084254885Sdumbbell	atom_op_sub, ATOM_ARG_WS}, {
1085254885Sdumbbell	atom_op_sub, ATOM_ARG_FB}, {
1086254885Sdumbbell	atom_op_sub, ATOM_ARG_PLL}, {
1087254885Sdumbbell	atom_op_sub, ATOM_ARG_MC}, {
1088254885Sdumbbell	atom_op_setport, ATOM_PORT_ATI}, {
1089254885Sdumbbell	atom_op_setport, ATOM_PORT_PCI}, {
1090254885Sdumbbell	atom_op_setport, ATOM_PORT_SYSIO}, {
1091254885Sdumbbell	atom_op_setregblock, 0}, {
1092254885Sdumbbell	atom_op_setfbbase, 0}, {
1093254885Sdumbbell	atom_op_compare, ATOM_ARG_REG}, {
1094254885Sdumbbell	atom_op_compare, ATOM_ARG_PS}, {
1095254885Sdumbbell	atom_op_compare, ATOM_ARG_WS}, {
1096254885Sdumbbell	atom_op_compare, ATOM_ARG_FB}, {
1097254885Sdumbbell	atom_op_compare, ATOM_ARG_PLL}, {
1098254885Sdumbbell	atom_op_compare, ATOM_ARG_MC}, {
1099254885Sdumbbell	atom_op_switch, 0}, {
1100254885Sdumbbell	atom_op_jump, ATOM_COND_ALWAYS}, {
1101254885Sdumbbell	atom_op_jump, ATOM_COND_EQUAL}, {
1102254885Sdumbbell	atom_op_jump, ATOM_COND_BELOW}, {
1103254885Sdumbbell	atom_op_jump, ATOM_COND_ABOVE}, {
1104254885Sdumbbell	atom_op_jump, ATOM_COND_BELOWOREQUAL}, {
1105254885Sdumbbell	atom_op_jump, ATOM_COND_ABOVEOREQUAL}, {
1106254885Sdumbbell	atom_op_jump, ATOM_COND_NOTEQUAL}, {
1107254885Sdumbbell	atom_op_test, ATOM_ARG_REG}, {
1108254885Sdumbbell	atom_op_test, ATOM_ARG_PS}, {
1109254885Sdumbbell	atom_op_test, ATOM_ARG_WS}, {
1110254885Sdumbbell	atom_op_test, ATOM_ARG_FB}, {
1111254885Sdumbbell	atom_op_test, ATOM_ARG_PLL}, {
1112254885Sdumbbell	atom_op_test, ATOM_ARG_MC}, {
1113254885Sdumbbell	atom_op_delay, ATOM_UNIT_MILLISEC}, {
1114254885Sdumbbell	atom_op_delay, ATOM_UNIT_MICROSEC}, {
1115254885Sdumbbell	atom_op_calltable, 0}, {
1116254885Sdumbbell	atom_op_repeat, 0}, {
1117254885Sdumbbell	atom_op_clear, ATOM_ARG_REG}, {
1118254885Sdumbbell	atom_op_clear, ATOM_ARG_PS}, {
1119254885Sdumbbell	atom_op_clear, ATOM_ARG_WS}, {
1120254885Sdumbbell	atom_op_clear, ATOM_ARG_FB}, {
1121254885Sdumbbell	atom_op_clear, ATOM_ARG_PLL}, {
1122254885Sdumbbell	atom_op_clear, ATOM_ARG_MC}, {
1123254885Sdumbbell	atom_op_nop, 0}, {
1124254885Sdumbbell	atom_op_eot, 0}, {
1125254885Sdumbbell	atom_op_mask, ATOM_ARG_REG}, {
1126254885Sdumbbell	atom_op_mask, ATOM_ARG_PS}, {
1127254885Sdumbbell	atom_op_mask, ATOM_ARG_WS}, {
1128254885Sdumbbell	atom_op_mask, ATOM_ARG_FB}, {
1129254885Sdumbbell	atom_op_mask, ATOM_ARG_PLL}, {
1130254885Sdumbbell	atom_op_mask, ATOM_ARG_MC}, {
1131254885Sdumbbell	atom_op_postcard, 0}, {
1132254885Sdumbbell	atom_op_beep, 0}, {
1133254885Sdumbbell	atom_op_savereg, 0}, {
1134254885Sdumbbell	atom_op_restorereg, 0}, {
1135254885Sdumbbell	atom_op_setdatablock, 0}, {
1136254885Sdumbbell	atom_op_xor, ATOM_ARG_REG}, {
1137254885Sdumbbell	atom_op_xor, ATOM_ARG_PS}, {
1138254885Sdumbbell	atom_op_xor, ATOM_ARG_WS}, {
1139254885Sdumbbell	atom_op_xor, ATOM_ARG_FB}, {
1140254885Sdumbbell	atom_op_xor, ATOM_ARG_PLL}, {
1141254885Sdumbbell	atom_op_xor, ATOM_ARG_MC}, {
1142254885Sdumbbell	atom_op_shl, ATOM_ARG_REG}, {
1143254885Sdumbbell	atom_op_shl, ATOM_ARG_PS}, {
1144254885Sdumbbell	atom_op_shl, ATOM_ARG_WS}, {
1145254885Sdumbbell	atom_op_shl, ATOM_ARG_FB}, {
1146254885Sdumbbell	atom_op_shl, ATOM_ARG_PLL}, {
1147254885Sdumbbell	atom_op_shl, ATOM_ARG_MC}, {
1148254885Sdumbbell	atom_op_shr, ATOM_ARG_REG}, {
1149254885Sdumbbell	atom_op_shr, ATOM_ARG_PS}, {
1150254885Sdumbbell	atom_op_shr, ATOM_ARG_WS}, {
1151254885Sdumbbell	atom_op_shr, ATOM_ARG_FB}, {
1152254885Sdumbbell	atom_op_shr, ATOM_ARG_PLL}, {
1153254885Sdumbbell	atom_op_shr, ATOM_ARG_MC}, {
1154254885Sdumbbellatom_op_debug, 0},};
1155254885Sdumbbell
1156254885Sdumbbellstatic int atom_execute_table_locked(struct atom_context *ctx, int index, uint32_t * params)
1157254885Sdumbbell{
1158254885Sdumbbell	int base = CU16(ctx->cmd_table + 4 + 2 * index);
1159254885Sdumbbell	int len, ws, ps, ptr;
1160254885Sdumbbell	unsigned char op;
1161254885Sdumbbell	atom_exec_context ectx;
1162254885Sdumbbell	int ret = 0;
1163254885Sdumbbell
1164254885Sdumbbell	if (!base)
1165254885Sdumbbell		return -EINVAL;
1166254885Sdumbbell
1167254885Sdumbbell	len = CU16(base + ATOM_CT_SIZE_PTR);
1168254885Sdumbbell	ws = CU8(base + ATOM_CT_WS_PTR);
1169254885Sdumbbell	ps = CU8(base + ATOM_CT_PS_PTR) & ATOM_CT_PS_MASK;
1170254885Sdumbbell	ptr = base + ATOM_CT_CODE_PTR;
1171254885Sdumbbell
1172254894Sdumbbell	ATOM_SDEBUG_PRINT(">> execute %04X (len %d, WS %d, PS %d)\n", base, len, ws, ps);
1173254885Sdumbbell
1174254885Sdumbbell	ectx.ctx = ctx;
1175254885Sdumbbell	ectx.ps_shift = ps / 4;
1176254885Sdumbbell	ectx.start = base;
1177254885Sdumbbell	ectx.ps = params;
1178254885Sdumbbell	ectx.abort = false;
1179254885Sdumbbell	ectx.last_jump = 0;
1180254885Sdumbbell	if (ws)
1181254885Sdumbbell		ectx.ws = malloc(4 * ws, DRM_MEM_DRIVER, M_ZERO | M_WAITOK);
1182254885Sdumbbell	else
1183254885Sdumbbell		ectx.ws = NULL;
1184254885Sdumbbell
1185254885Sdumbbell	debug_depth++;
1186254885Sdumbbell	while (1) {
1187254885Sdumbbell		op = CU8(ptr++);
1188254885Sdumbbell		if (op < ATOM_OP_NAMES_CNT)
1189254894Sdumbbell			ATOM_SDEBUG_PRINT("%s @ 0x%04X\n", atom_op_names[op], ptr - 1);
1190254885Sdumbbell		else
1191254894Sdumbbell			ATOM_SDEBUG_PRINT("[%d] @ 0x%04X\n", op, ptr - 1);
1192254885Sdumbbell		if (ectx.abort) {
1193254885Sdumbbell			DRM_ERROR("atombios stuck executing %04X (len %d, WS %d, PS %d) @ 0x%04X\n",
1194254885Sdumbbell				base, len, ws, ps, ptr - 1);
1195254885Sdumbbell			ret = -EINVAL;
1196254885Sdumbbell			goto free;
1197254885Sdumbbell		}
1198254885Sdumbbell
1199254885Sdumbbell		if (op < ATOM_OP_CNT && op > 0)
1200254885Sdumbbell			opcode_table[op].func(&ectx, &ptr,
1201254885Sdumbbell					      opcode_table[op].arg);
1202254885Sdumbbell		else
1203254885Sdumbbell			break;
1204254885Sdumbbell
1205254885Sdumbbell		if (op == ATOM_OP_EOT)
1206254885Sdumbbell			break;
1207254885Sdumbbell	}
1208254885Sdumbbell	debug_depth--;
1209254894Sdumbbell	ATOM_SDEBUG_PRINT("<<\n");
1210254885Sdumbbell
1211254885Sdumbbellfree:
1212254885Sdumbbell	if (ws)
1213254885Sdumbbell		free(ectx.ws, DRM_MEM_DRIVER);
1214254885Sdumbbell	return ret;
1215254885Sdumbbell}
1216254885Sdumbbell
1217254885Sdumbbellint atom_execute_table(struct atom_context *ctx, int index, uint32_t * params)
1218254885Sdumbbell{
1219254885Sdumbbell	int r;
1220254885Sdumbbell
1221254885Sdumbbell	sx_xlock(&ctx->mutex);
1222254885Sdumbbell	/* reset reg block */
1223254885Sdumbbell	ctx->reg_block = 0;
1224254885Sdumbbell	/* reset fb window */
1225254885Sdumbbell	ctx->fb_base = 0;
1226254885Sdumbbell	/* reset io mode */
1227254885Sdumbbell	ctx->io_mode = ATOM_IO_MM;
1228254885Sdumbbell	r = atom_execute_table_locked(ctx, index, params);
1229254885Sdumbbell	sx_xunlock(&ctx->mutex);
1230254885Sdumbbell	return r;
1231254885Sdumbbell}
1232254885Sdumbbell
1233254885Sdumbbellstatic int atom_iio_len[] = { 1, 2, 3, 3, 3, 3, 4, 4, 4, 3 };
1234254885Sdumbbell
1235254885Sdumbbellstatic void atom_index_iio(struct atom_context *ctx, int base)
1236254885Sdumbbell{
1237254885Sdumbbell	ctx->iio = malloc(2 * 256, DRM_MEM_DRIVER, M_ZERO | M_WAITOK);
1238254885Sdumbbell	while (CU8(base) == ATOM_IIO_START) {
1239254885Sdumbbell		ctx->iio[CU8(base + 1)] = base + 2;
1240254885Sdumbbell		base += 2;
1241254885Sdumbbell		while (CU8(base) != ATOM_IIO_END)
1242254885Sdumbbell			base += atom_iio_len[CU8(base)];
1243254885Sdumbbell		base += 3;
1244254885Sdumbbell	}
1245254885Sdumbbell}
1246254885Sdumbbell
1247254885Sdumbbellstruct atom_context *atom_parse(struct card_info *card, void *bios)
1248254885Sdumbbell{
1249254885Sdumbbell	int base;
1250254885Sdumbbell	struct atom_context *ctx =
1251254885Sdumbbell	    malloc(sizeof(struct atom_context), DRM_MEM_DRIVER, M_ZERO | M_WAITOK);
1252254885Sdumbbell	char *str;
1253254885Sdumbbell	char name[512];
1254254885Sdumbbell	int i;
1255254885Sdumbbell
1256254885Sdumbbell	if (!ctx)
1257254885Sdumbbell		return NULL;
1258254885Sdumbbell
1259254885Sdumbbell	ctx->card = card;
1260254885Sdumbbell	ctx->bios = bios;
1261254885Sdumbbell
1262254885Sdumbbell	if (CU16(0) != ATOM_BIOS_MAGIC) {
1263254885Sdumbbell		DRM_INFO("Invalid BIOS magic.\n");
1264254885Sdumbbell		free(ctx, DRM_MEM_DRIVER);
1265254885Sdumbbell		return NULL;
1266254885Sdumbbell	}
1267254885Sdumbbell	if (strncmp
1268254885Sdumbbell	    (CSTR(ATOM_ATI_MAGIC_PTR), ATOM_ATI_MAGIC,
1269254885Sdumbbell	     strlen(ATOM_ATI_MAGIC))) {
1270254885Sdumbbell		DRM_INFO("Invalid ATI magic.\n");
1271254885Sdumbbell		free(ctx, DRM_MEM_DRIVER);
1272254885Sdumbbell		return NULL;
1273254885Sdumbbell	}
1274254885Sdumbbell
1275254885Sdumbbell	base = CU16(ATOM_ROM_TABLE_PTR);
1276254885Sdumbbell	if (strncmp
1277254885Sdumbbell	    (CSTR(base + ATOM_ROM_MAGIC_PTR), ATOM_ROM_MAGIC,
1278254885Sdumbbell	     strlen(ATOM_ROM_MAGIC))) {
1279254885Sdumbbell		DRM_INFO("Invalid ATOM magic.\n");
1280254885Sdumbbell		free(ctx, DRM_MEM_DRIVER);
1281254885Sdumbbell		return NULL;
1282254885Sdumbbell	}
1283254885Sdumbbell
1284254885Sdumbbell	ctx->cmd_table = CU16(base + ATOM_ROM_CMD_PTR);
1285254885Sdumbbell	ctx->data_table = CU16(base + ATOM_ROM_DATA_PTR);
1286254885Sdumbbell	atom_index_iio(ctx, CU16(ctx->data_table + ATOM_DATA_IIO_PTR) + 4);
1287254885Sdumbbell
1288254885Sdumbbell	str = CSTR(CU16(base + ATOM_ROM_MSG_PTR));
1289254885Sdumbbell	while (*str && ((*str == '\n') || (*str == '\r')))
1290254885Sdumbbell		str++;
1291254885Sdumbbell	/* name string isn't always 0 terminated */
1292254885Sdumbbell	for (i = 0; i < 511; i++) {
1293254885Sdumbbell		name[i] = str[i];
1294254885Sdumbbell		if (name[i] < '.' || name[i] > 'z') {
1295254885Sdumbbell			name[i] = 0;
1296254885Sdumbbell			break;
1297254885Sdumbbell		}
1298254885Sdumbbell	}
1299254885Sdumbbell	DRM_INFO("ATOM BIOS: %s\n", name);
1300254885Sdumbbell
1301254885Sdumbbell	return ctx;
1302254885Sdumbbell}
1303254885Sdumbbell
1304254885Sdumbbellint atom_asic_init(struct atom_context *ctx)
1305254885Sdumbbell{
1306254885Sdumbbell	struct radeon_device *rdev = ctx->card->dev->dev_private;
1307254885Sdumbbell	int hwi = CU16(ctx->data_table + ATOM_DATA_FWI_PTR);
1308254885Sdumbbell	uint32_t ps[16];
1309254885Sdumbbell	int ret;
1310254885Sdumbbell
1311254885Sdumbbell	memset(ps, 0, 64);
1312254885Sdumbbell
1313254885Sdumbbell	ps[0] = cpu_to_le32(CU32(hwi + ATOM_FWI_DEFSCLK_PTR));
1314254885Sdumbbell	ps[1] = cpu_to_le32(CU32(hwi + ATOM_FWI_DEFMCLK_PTR));
1315254885Sdumbbell	if (!ps[0] || !ps[1])
1316254885Sdumbbell		return 1;
1317254885Sdumbbell
1318254885Sdumbbell	if (!CU16(ctx->cmd_table + 4 + 2 * ATOM_CMD_INIT))
1319254885Sdumbbell		return 1;
1320254885Sdumbbell	ret = atom_execute_table(ctx, ATOM_CMD_INIT, ps);
1321254885Sdumbbell	if (ret)
1322254885Sdumbbell		return ret;
1323254885Sdumbbell
1324254885Sdumbbell	memset(ps, 0, 64);
1325254885Sdumbbell
1326254885Sdumbbell	if (rdev->family < CHIP_R600) {
1327254885Sdumbbell		if (CU16(ctx->cmd_table + 4 + 2 * ATOM_CMD_SPDFANCNTL))
1328254885Sdumbbell			atom_execute_table(ctx, ATOM_CMD_SPDFANCNTL, ps);
1329254885Sdumbbell	}
1330254885Sdumbbell	return ret;
1331254885Sdumbbell}
1332254885Sdumbbell
1333254885Sdumbbellvoid atom_destroy(struct atom_context *ctx)
1334254885Sdumbbell{
1335254885Sdumbbell	if (ctx->iio)
1336254885Sdumbbell		free(ctx->iio, DRM_MEM_DRIVER);
1337254885Sdumbbell	free(ctx, DRM_MEM_DRIVER);
1338254885Sdumbbell}
1339254885Sdumbbell
1340254885Sdumbbellbool atom_parse_data_header(struct atom_context *ctx, int index,
1341254885Sdumbbell			    uint16_t * size, uint8_t * frev, uint8_t * crev,
1342254885Sdumbbell			    uint16_t * data_start)
1343254885Sdumbbell{
1344254885Sdumbbell	int offset = index * 2 + 4;
1345254885Sdumbbell	int idx = CU16(ctx->data_table + offset);
1346254885Sdumbbell	u16 *mdt = (u16 *)((char *)ctx->bios + ctx->data_table + 4);
1347254885Sdumbbell
1348254885Sdumbbell	if (!mdt[index])
1349254885Sdumbbell		return false;
1350254885Sdumbbell
1351254885Sdumbbell	if (size)
1352254885Sdumbbell		*size = CU16(idx);
1353254885Sdumbbell	if (frev)
1354254885Sdumbbell		*frev = CU8(idx + 2);
1355254885Sdumbbell	if (crev)
1356254885Sdumbbell		*crev = CU8(idx + 3);
1357254885Sdumbbell	*data_start = idx;
1358254885Sdumbbell	return true;
1359254885Sdumbbell}
1360254885Sdumbbell
1361254885Sdumbbellbool atom_parse_cmd_header(struct atom_context *ctx, int index, uint8_t * frev,
1362254885Sdumbbell			   uint8_t * crev)
1363254885Sdumbbell{
1364254885Sdumbbell	int offset = index * 2 + 4;
1365254885Sdumbbell	int idx = CU16(ctx->cmd_table + offset);
1366254885Sdumbbell	u16 *mct = (u16 *)((char *)ctx->bios + ctx->cmd_table + 4);
1367254885Sdumbbell
1368254885Sdumbbell	if (!mct[index])
1369254885Sdumbbell		return false;
1370254885Sdumbbell
1371254885Sdumbbell	if (frev)
1372254885Sdumbbell		*frev = CU8(idx + 2);
1373254885Sdumbbell	if (crev)
1374254885Sdumbbell		*crev = CU8(idx + 3);
1375254885Sdumbbell	return true;
1376254885Sdumbbell}
1377254885Sdumbbell
1378254885Sdumbbellint atom_allocate_fb_scratch(struct atom_context *ctx)
1379254885Sdumbbell{
1380254885Sdumbbell	int index = GetIndexIntoMasterTable(DATA, VRAM_UsageByFirmware);
1381254885Sdumbbell	uint16_t data_offset;
1382254885Sdumbbell	int usage_bytes = 0;
1383254885Sdumbbell	struct _ATOM_VRAM_USAGE_BY_FIRMWARE *firmware_usage;
1384254885Sdumbbell
1385254885Sdumbbell	if (atom_parse_data_header(ctx, index, NULL, NULL, NULL, &data_offset)) {
1386254885Sdumbbell		firmware_usage = (struct _ATOM_VRAM_USAGE_BY_FIRMWARE *)((char *)ctx->bios + data_offset);
1387254885Sdumbbell
1388254885Sdumbbell		DRM_DEBUG("atom firmware requested %08x %dkb\n",
1389254885Sdumbbell			  firmware_usage->asFirmwareVramReserveInfo[0].ulStartAddrUsedByFirmware,
1390254885Sdumbbell			  firmware_usage->asFirmwareVramReserveInfo[0].usFirmwareUseInKb);
1391254885Sdumbbell
1392254885Sdumbbell		usage_bytes = firmware_usage->asFirmwareVramReserveInfo[0].usFirmwareUseInKb * 1024;
1393254885Sdumbbell	}
1394254885Sdumbbell	ctx->scratch_size_bytes = 0;
1395254885Sdumbbell	if (usage_bytes == 0)
1396254885Sdumbbell		usage_bytes = 20 * 1024;
1397254885Sdumbbell	/* allocate some scratch memory */
1398254885Sdumbbell	ctx->scratch = malloc(usage_bytes, DRM_MEM_DRIVER, M_ZERO | M_WAITOK);
1399254885Sdumbbell	if (!ctx->scratch)
1400254885Sdumbbell		return -ENOMEM;
1401254885Sdumbbell	ctx->scratch_size_bytes = usage_bytes;
1402254885Sdumbbell	return 0;
1403254885Sdumbbell}
1404