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