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