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