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