xref: /openbsd-src/sys/dev/pci/drm/radeon/atom.c (revision f005ef32267c16bdb134f0e9fa4477dbe07c263a)
11099013bSjsg /*
21099013bSjsg  * Copyright 2008 Advanced Micro Devices, Inc.
31099013bSjsg  *
41099013bSjsg  * Permission is hereby granted, free of charge, to any person obtaining a
51099013bSjsg  * copy of this software and associated documentation files (the "Software"),
61099013bSjsg  * to deal in the Software without restriction, including without limitation
71099013bSjsg  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
81099013bSjsg  * and/or sell copies of the Software, and to permit persons to whom the
91099013bSjsg  * Software is furnished to do so, subject to the following conditions:
101099013bSjsg  *
111099013bSjsg  * The above copyright notice and this permission notice shall be included in
121099013bSjsg  * all copies or substantial portions of the Software.
131099013bSjsg  *
141099013bSjsg  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
151099013bSjsg  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
161099013bSjsg  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
171099013bSjsg  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
181099013bSjsg  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
191099013bSjsg  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
201099013bSjsg  * OTHER DEALINGS IN THE SOFTWARE.
211099013bSjsg  *
221099013bSjsg  * Author: Stanislaw Skowronek
231099013bSjsg  */
241099013bSjsg 
257f4dd379Sjsg #include <linux/module.h>
267f4dd379Sjsg #include <linux/sched.h>
277f4dd379Sjsg #include <linux/slab.h>
281bb76ff1Sjsg #include <linux/string_helpers.h>
29c349dbc7Sjsg 
307f4dd379Sjsg #include <asm/unaligned.h>
317f4dd379Sjsg 
32c349dbc7Sjsg #include <drm/drm_device.h>
33c349dbc7Sjsg #include <drm/drm_util.h>
34c349dbc7Sjsg 
351099013bSjsg #define ATOM_DEBUG
361099013bSjsg 
371099013bSjsg #include "atom.h"
381099013bSjsg #include "atom-names.h"
391099013bSjsg #include "atom-bits.h"
401099013bSjsg #include "radeon.h"
411099013bSjsg 
421099013bSjsg #define ATOM_COND_ABOVE		0
431099013bSjsg #define ATOM_COND_ABOVEOREQUAL	1
441099013bSjsg #define ATOM_COND_ALWAYS	2
451099013bSjsg #define ATOM_COND_BELOW		3
461099013bSjsg #define ATOM_COND_BELOWOREQUAL	4
471099013bSjsg #define ATOM_COND_EQUAL		5
481099013bSjsg #define ATOM_COND_NOTEQUAL	6
491099013bSjsg 
501099013bSjsg #define ATOM_PORT_ATI	0
511099013bSjsg #define ATOM_PORT_PCI	1
521099013bSjsg #define ATOM_PORT_SYSIO	2
531099013bSjsg 
541099013bSjsg #define ATOM_UNIT_MICROSEC	0
551099013bSjsg #define ATOM_UNIT_MILLISEC	1
561099013bSjsg 
571099013bSjsg #define PLL_INDEX	2
581099013bSjsg #define PLL_DATA	3
591099013bSjsg 
601099013bSjsg typedef struct {
611099013bSjsg 	struct atom_context *ctx;
621099013bSjsg 	uint32_t *ps, *ws;
631099013bSjsg 	int ps_shift;
641099013bSjsg 	uint16_t start;
651099013bSjsg 	unsigned last_jump;
661099013bSjsg 	unsigned long last_jump_jiffies;
671099013bSjsg 	bool abort;
681099013bSjsg } atom_exec_context;
691099013bSjsg 
701099013bSjsg int atom_debug = 0;
711099013bSjsg static int atom_execute_table_locked(struct atom_context *ctx, int index, uint32_t *params);
721099013bSjsg int atom_execute_table(struct atom_context *ctx, int index, uint32_t *params);
731099013bSjsg 
747f4dd379Sjsg static uint32_t atom_arg_mask[8] = {
757f4dd379Sjsg 	0xFFFFFFFF, 0x0000FFFF, 0x00FFFF00, 0xFFFF0000,
767f4dd379Sjsg 	0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000
777f4dd379Sjsg };
781099013bSjsg static int atom_arg_shift[8] = { 0, 0, 8, 16, 0, 8, 16, 24 };
791099013bSjsg 
801099013bSjsg static int atom_dst_to_src[8][4] = {
811099013bSjsg 	/* translate destination alignment field to the source alignment encoding */
821099013bSjsg 	{0, 0, 0, 0},
831099013bSjsg 	{1, 2, 3, 0},
841099013bSjsg 	{1, 2, 3, 0},
851099013bSjsg 	{1, 2, 3, 0},
861099013bSjsg 	{4, 5, 6, 7},
871099013bSjsg 	{4, 5, 6, 7},
881099013bSjsg 	{4, 5, 6, 7},
891099013bSjsg 	{4, 5, 6, 7},
901099013bSjsg };
911099013bSjsg static int atom_def_dst[8] = { 0, 0, 1, 2, 0, 1, 2, 3 };
921099013bSjsg 
931099013bSjsg static int debug_depth = 0;
941099013bSjsg #ifdef ATOM_DEBUG
debug_print_spaces(int n)951099013bSjsg static void debug_print_spaces(int n)
961099013bSjsg {
971099013bSjsg 	while (n--)
98d765308cSjsg 		printk("   ");
991099013bSjsg }
1001099013bSjsg 
101f5af14d6Sjsg #ifdef DEBUG
102f5af14d6Sjsg #undef DEBUG
103f5af14d6Sjsg #endif
104f5af14d6Sjsg 
1057ccd5a2cSjsg #define DEBUG(...) do if (atom_debug) { printk(KERN_DEBUG __VA_ARGS__); } while (0)
1067ccd5a2cSjsg #define SDEBUG(...) do if (atom_debug) { printk(KERN_DEBUG); debug_print_spaces(debug_depth); printk(__VA_ARGS__); } while (0)
1071099013bSjsg #else
1081099013bSjsg #define DEBUG(...) do { } while (0)
1091099013bSjsg #define SDEBUG(...) do { } while (0)
1101099013bSjsg #endif
1111099013bSjsg 
atom_iio_execute(struct atom_context * ctx,int base,uint32_t index,uint32_t data)1121099013bSjsg static uint32_t atom_iio_execute(struct atom_context *ctx, int base,
1131099013bSjsg 				 uint32_t index, uint32_t data)
1141099013bSjsg {
1151099013bSjsg 	struct radeon_device *rdev = ctx->card->dev->dev_private;
1161099013bSjsg 	uint32_t temp = 0xCDCDCDCD;
1171099013bSjsg 
1181099013bSjsg 	while (1)
1191099013bSjsg 		switch (CU8(base)) {
1201099013bSjsg 		case ATOM_IIO_NOP:
1211099013bSjsg 			base++;
1221099013bSjsg 			break;
1231099013bSjsg 		case ATOM_IIO_READ:
1241099013bSjsg 			temp = ctx->card->ioreg_read(ctx->card, CU16(base + 1));
1251099013bSjsg 			base += 3;
1261099013bSjsg 			break;
1271099013bSjsg 		case ATOM_IIO_WRITE:
1281099013bSjsg 			if (rdev->family == CHIP_RV515)
1291099013bSjsg 				(void)ctx->card->ioreg_read(ctx->card, CU16(base + 1));
1301099013bSjsg 			ctx->card->ioreg_write(ctx->card, CU16(base + 1), temp);
1311099013bSjsg 			base += 3;
1321099013bSjsg 			break;
1331099013bSjsg 		case ATOM_IIO_CLEAR:
1341099013bSjsg 			temp &=
1351099013bSjsg 			    ~((0xFFFFFFFF >> (32 - CU8(base + 1))) <<
1361099013bSjsg 			      CU8(base + 2));
1371099013bSjsg 			base += 3;
1381099013bSjsg 			break;
1391099013bSjsg 		case ATOM_IIO_SET:
1401099013bSjsg 			temp |=
1411099013bSjsg 			    (0xFFFFFFFF >> (32 - CU8(base + 1))) << CU8(base +
1421099013bSjsg 									2);
1431099013bSjsg 			base += 3;
1441099013bSjsg 			break;
1451099013bSjsg 		case ATOM_IIO_MOVE_INDEX:
1461099013bSjsg 			temp &=
1471099013bSjsg 			    ~((0xFFFFFFFF >> (32 - CU8(base + 1))) <<
1481099013bSjsg 			      CU8(base + 3));
1491099013bSjsg 			temp |=
1501099013bSjsg 			    ((index >> CU8(base + 2)) &
1511099013bSjsg 			     (0xFFFFFFFF >> (32 - CU8(base + 1)))) << CU8(base +
1521099013bSjsg 									  3);
1531099013bSjsg 			base += 4;
1541099013bSjsg 			break;
1551099013bSjsg 		case ATOM_IIO_MOVE_DATA:
1561099013bSjsg 			temp &=
1571099013bSjsg 			    ~((0xFFFFFFFF >> (32 - CU8(base + 1))) <<
1581099013bSjsg 			      CU8(base + 3));
1591099013bSjsg 			temp |=
1601099013bSjsg 			    ((data >> CU8(base + 2)) &
1611099013bSjsg 			     (0xFFFFFFFF >> (32 - CU8(base + 1)))) << CU8(base +
1621099013bSjsg 									  3);
1631099013bSjsg 			base += 4;
1641099013bSjsg 			break;
1651099013bSjsg 		case ATOM_IIO_MOVE_ATTR:
1661099013bSjsg 			temp &=
1671099013bSjsg 			    ~((0xFFFFFFFF >> (32 - CU8(base + 1))) <<
1681099013bSjsg 			      CU8(base + 3));
1691099013bSjsg 			temp |=
170*f005ef32Sjsg 			    ((ctx->io_attr >> CU8(base + 2)) &
171*f005ef32Sjsg 			     (0xFFFFFFFF >> (32 - CU8(base + 1)))) <<
172*f005ef32Sjsg 			     CU8(base + 3);
1731099013bSjsg 			base += 4;
1741099013bSjsg 			break;
1751099013bSjsg 		case ATOM_IIO_END:
1761099013bSjsg 			return temp;
1771099013bSjsg 		default:
1787f4dd379Sjsg 			pr_info("Unknown IIO opcode\n");
1791099013bSjsg 			return 0;
1801099013bSjsg 		}
1811099013bSjsg }
1821099013bSjsg 
atom_get_src_int(atom_exec_context * ctx,uint8_t attr,int * ptr,uint32_t * saved,int print)1831099013bSjsg static uint32_t atom_get_src_int(atom_exec_context *ctx, uint8_t attr,
1841099013bSjsg 				 int *ptr, uint32_t *saved, int print)
1851099013bSjsg {
1861099013bSjsg 	uint32_t idx, val = 0xCDCDCDCD, align, arg;
1871099013bSjsg 	struct atom_context *gctx = ctx->ctx;
1881099013bSjsg 	arg = attr & 7;
1891099013bSjsg 	align = (attr >> 3) & 7;
1901099013bSjsg 	switch (arg) {
1911099013bSjsg 	case ATOM_ARG_REG:
1921099013bSjsg 		idx = U16(*ptr);
1931099013bSjsg 		(*ptr) += 2;
1941099013bSjsg 		if (print)
1951099013bSjsg 			DEBUG("REG[0x%04X]", idx);
1961099013bSjsg 		idx += gctx->reg_block;
1971099013bSjsg 		switch (gctx->io_mode) {
1981099013bSjsg 		case ATOM_IO_MM:
1991099013bSjsg 			val = gctx->card->reg_read(gctx->card, idx);
2001099013bSjsg 			break;
2011099013bSjsg 		case ATOM_IO_PCI:
2027f4dd379Sjsg 			pr_info("PCI registers are not implemented\n");
2031099013bSjsg 			return 0;
2041099013bSjsg 		case ATOM_IO_SYSIO:
2057f4dd379Sjsg 			pr_info("SYSIO registers are not implemented\n");
2061099013bSjsg 			return 0;
2071099013bSjsg 		default:
2081099013bSjsg 			if (!(gctx->io_mode & 0x80)) {
2097f4dd379Sjsg 				pr_info("Bad IO mode\n");
2101099013bSjsg 				return 0;
2111099013bSjsg 			}
2121099013bSjsg 			if (!gctx->iio[gctx->io_mode & 0x7F]) {
2137f4dd379Sjsg 				pr_info("Undefined indirect IO read method %d\n",
2141099013bSjsg 					gctx->io_mode & 0x7F);
2151099013bSjsg 				return 0;
2161099013bSjsg 			}
2171099013bSjsg 			val =
2181099013bSjsg 			    atom_iio_execute(gctx,
2191099013bSjsg 					     gctx->iio[gctx->io_mode & 0x7F],
2201099013bSjsg 					     idx, 0);
2211099013bSjsg 		}
2221099013bSjsg 		break;
2231099013bSjsg 	case ATOM_ARG_PS:
2241099013bSjsg 		idx = U8(*ptr);
2251099013bSjsg 		(*ptr)++;
2261099013bSjsg 		/* get_unaligned_le32 avoids unaligned accesses from atombios
2271099013bSjsg 		 * tables, noticed on a DEC Alpha. */
2281099013bSjsg 		val = get_unaligned_le32((u32 *)&ctx->ps[idx]);
2291099013bSjsg 		if (print)
2301099013bSjsg 			DEBUG("PS[0x%02X,0x%04X]", idx, val);
2311099013bSjsg 		break;
2321099013bSjsg 	case ATOM_ARG_WS:
2331099013bSjsg 		idx = U8(*ptr);
2341099013bSjsg 		(*ptr)++;
2351099013bSjsg 		if (print)
2361099013bSjsg 			DEBUG("WS[0x%02X]", idx);
2371099013bSjsg 		switch (idx) {
2381099013bSjsg 		case ATOM_WS_QUOTIENT:
2391099013bSjsg 			val = gctx->divmul[0];
2401099013bSjsg 			break;
2411099013bSjsg 		case ATOM_WS_REMAINDER:
2421099013bSjsg 			val = gctx->divmul[1];
2431099013bSjsg 			break;
2441099013bSjsg 		case ATOM_WS_DATAPTR:
2451099013bSjsg 			val = gctx->data_block;
2461099013bSjsg 			break;
2471099013bSjsg 		case ATOM_WS_SHIFT:
2481099013bSjsg 			val = gctx->shift;
2491099013bSjsg 			break;
2501099013bSjsg 		case ATOM_WS_OR_MASK:
2511099013bSjsg 			val = 1 << gctx->shift;
2521099013bSjsg 			break;
2531099013bSjsg 		case ATOM_WS_AND_MASK:
2541099013bSjsg 			val = ~(1 << gctx->shift);
2551099013bSjsg 			break;
2561099013bSjsg 		case ATOM_WS_FB_WINDOW:
2571099013bSjsg 			val = gctx->fb_base;
2581099013bSjsg 			break;
2591099013bSjsg 		case ATOM_WS_ATTRIBUTES:
2601099013bSjsg 			val = gctx->io_attr;
2611099013bSjsg 			break;
2621099013bSjsg 		case ATOM_WS_REGPTR:
2631099013bSjsg 			val = gctx->reg_block;
2641099013bSjsg 			break;
2651099013bSjsg 		default:
2661099013bSjsg 			val = ctx->ws[idx];
2671099013bSjsg 		}
2681099013bSjsg 		break;
2691099013bSjsg 	case ATOM_ARG_ID:
2701099013bSjsg 		idx = U16(*ptr);
2711099013bSjsg 		(*ptr) += 2;
2721099013bSjsg 		if (print) {
2731099013bSjsg 			if (gctx->data_block)
2741099013bSjsg 				DEBUG("ID[0x%04X+%04X]", idx, gctx->data_block);
2751099013bSjsg 			else
2761099013bSjsg 				DEBUG("ID[0x%04X]", idx);
2771099013bSjsg 		}
2781099013bSjsg 		val = U32(idx + gctx->data_block);
2791099013bSjsg 		break;
2801099013bSjsg 	case ATOM_ARG_FB:
2811099013bSjsg 		idx = U8(*ptr);
2821099013bSjsg 		(*ptr)++;
2831099013bSjsg 		if ((gctx->fb_base + (idx * 4)) > gctx->scratch_size_bytes) {
2841099013bSjsg 			DRM_ERROR("ATOM: fb read beyond scratch region: %d vs. %d\n",
2851099013bSjsg 				  gctx->fb_base + (idx * 4), gctx->scratch_size_bytes);
2861099013bSjsg 			val = 0;
2871099013bSjsg 		} else
2881099013bSjsg 			val = gctx->scratch[(gctx->fb_base / 4) + idx];
2891099013bSjsg 		if (print)
2901099013bSjsg 			DEBUG("FB[0x%02X]", idx);
2911099013bSjsg 		break;
2921099013bSjsg 	case ATOM_ARG_IMM:
2931099013bSjsg 		switch (align) {
2941099013bSjsg 		case ATOM_SRC_DWORD:
2951099013bSjsg 			val = U32(*ptr);
2961099013bSjsg 			(*ptr) += 4;
2971099013bSjsg 			if (print)
2981099013bSjsg 				DEBUG("IMM 0x%08X\n", val);
2991099013bSjsg 			return val;
3001099013bSjsg 		case ATOM_SRC_WORD0:
3011099013bSjsg 		case ATOM_SRC_WORD8:
3021099013bSjsg 		case ATOM_SRC_WORD16:
3031099013bSjsg 			val = U16(*ptr);
3041099013bSjsg 			(*ptr) += 2;
3051099013bSjsg 			if (print)
3061099013bSjsg 				DEBUG("IMM 0x%04X\n", val);
3071099013bSjsg 			return val;
3081099013bSjsg 		case ATOM_SRC_BYTE0:
3091099013bSjsg 		case ATOM_SRC_BYTE8:
3101099013bSjsg 		case ATOM_SRC_BYTE16:
3111099013bSjsg 		case ATOM_SRC_BYTE24:
3121099013bSjsg 			val = U8(*ptr);
3131099013bSjsg 			(*ptr)++;
3141099013bSjsg 			if (print)
3151099013bSjsg 				DEBUG("IMM 0x%02X\n", val);
3161099013bSjsg 			return val;
3171099013bSjsg 		}
3181099013bSjsg 		return 0;
3191099013bSjsg 	case ATOM_ARG_PLL:
3201099013bSjsg 		idx = U8(*ptr);
3211099013bSjsg 		(*ptr)++;
3221099013bSjsg 		if (print)
3231099013bSjsg 			DEBUG("PLL[0x%02X]", idx);
3241099013bSjsg 		val = gctx->card->pll_read(gctx->card, idx);
3251099013bSjsg 		break;
3261099013bSjsg 	case ATOM_ARG_MC:
3271099013bSjsg 		idx = U8(*ptr);
3281099013bSjsg 		(*ptr)++;
3291099013bSjsg 		if (print)
3301099013bSjsg 			DEBUG("MC[0x%02X]", idx);
3311099013bSjsg 		val = gctx->card->mc_read(gctx->card, idx);
3321099013bSjsg 		break;
3331099013bSjsg 	}
3341099013bSjsg 	if (saved)
3351099013bSjsg 		*saved = val;
3361099013bSjsg 	val &= atom_arg_mask[align];
3371099013bSjsg 	val >>= atom_arg_shift[align];
3381099013bSjsg 	if (print)
3391099013bSjsg 		switch (align) {
3401099013bSjsg 		case ATOM_SRC_DWORD:
3411099013bSjsg 			DEBUG(".[31:0] -> 0x%08X\n", val);
3421099013bSjsg 			break;
3431099013bSjsg 		case ATOM_SRC_WORD0:
3441099013bSjsg 			DEBUG(".[15:0] -> 0x%04X\n", val);
3451099013bSjsg 			break;
3461099013bSjsg 		case ATOM_SRC_WORD8:
3471099013bSjsg 			DEBUG(".[23:8] -> 0x%04X\n", val);
3481099013bSjsg 			break;
3491099013bSjsg 		case ATOM_SRC_WORD16:
3501099013bSjsg 			DEBUG(".[31:16] -> 0x%04X\n", val);
3511099013bSjsg 			break;
3521099013bSjsg 		case ATOM_SRC_BYTE0:
3531099013bSjsg 			DEBUG(".[7:0] -> 0x%02X\n", val);
3541099013bSjsg 			break;
3551099013bSjsg 		case ATOM_SRC_BYTE8:
3561099013bSjsg 			DEBUG(".[15:8] -> 0x%02X\n", val);
3571099013bSjsg 			break;
3581099013bSjsg 		case ATOM_SRC_BYTE16:
3591099013bSjsg 			DEBUG(".[23:16] -> 0x%02X\n", val);
3601099013bSjsg 			break;
3611099013bSjsg 		case ATOM_SRC_BYTE24:
3621099013bSjsg 			DEBUG(".[31:24] -> 0x%02X\n", val);
3631099013bSjsg 			break;
3641099013bSjsg 		}
3651099013bSjsg 	return val;
3661099013bSjsg }
3671099013bSjsg 
atom_skip_src_int(atom_exec_context * ctx,uint8_t attr,int * ptr)3681099013bSjsg static void atom_skip_src_int(atom_exec_context *ctx, uint8_t attr, int *ptr)
3691099013bSjsg {
3701099013bSjsg 	uint32_t align = (attr >> 3) & 7, arg = attr & 7;
3711099013bSjsg 	switch (arg) {
3721099013bSjsg 	case ATOM_ARG_REG:
3731099013bSjsg 	case ATOM_ARG_ID:
3741099013bSjsg 		(*ptr) += 2;
3751099013bSjsg 		break;
3761099013bSjsg 	case ATOM_ARG_PLL:
3771099013bSjsg 	case ATOM_ARG_MC:
3781099013bSjsg 	case ATOM_ARG_PS:
3791099013bSjsg 	case ATOM_ARG_WS:
3801099013bSjsg 	case ATOM_ARG_FB:
3811099013bSjsg 		(*ptr)++;
3821099013bSjsg 		break;
3831099013bSjsg 	case ATOM_ARG_IMM:
3841099013bSjsg 		switch (align) {
3851099013bSjsg 		case ATOM_SRC_DWORD:
3861099013bSjsg 			(*ptr) += 4;
3871099013bSjsg 			return;
3881099013bSjsg 		case ATOM_SRC_WORD0:
3891099013bSjsg 		case ATOM_SRC_WORD8:
3901099013bSjsg 		case ATOM_SRC_WORD16:
3911099013bSjsg 			(*ptr) += 2;
3921099013bSjsg 			return;
3931099013bSjsg 		case ATOM_SRC_BYTE0:
3941099013bSjsg 		case ATOM_SRC_BYTE8:
3951099013bSjsg 		case ATOM_SRC_BYTE16:
3961099013bSjsg 		case ATOM_SRC_BYTE24:
3971099013bSjsg 			(*ptr)++;
3981099013bSjsg 			return;
3991099013bSjsg 		}
4001099013bSjsg 		return;
4011099013bSjsg 	}
4021099013bSjsg }
4031099013bSjsg 
atom_get_src(atom_exec_context * ctx,uint8_t attr,int * ptr)4041099013bSjsg static uint32_t atom_get_src(atom_exec_context *ctx, uint8_t attr, int *ptr)
4051099013bSjsg {
4061099013bSjsg 	return atom_get_src_int(ctx, attr, ptr, NULL, 1);
4071099013bSjsg }
4081099013bSjsg 
atom_get_src_direct(atom_exec_context * ctx,uint8_t align,int * ptr)4091099013bSjsg static uint32_t atom_get_src_direct(atom_exec_context *ctx, uint8_t align, int *ptr)
4101099013bSjsg {
4111099013bSjsg 	uint32_t val = 0xCDCDCDCD;
4121099013bSjsg 
4131099013bSjsg 	switch (align) {
4141099013bSjsg 	case ATOM_SRC_DWORD:
4151099013bSjsg 		val = U32(*ptr);
4161099013bSjsg 		(*ptr) += 4;
4171099013bSjsg 		break;
4181099013bSjsg 	case ATOM_SRC_WORD0:
4191099013bSjsg 	case ATOM_SRC_WORD8:
4201099013bSjsg 	case ATOM_SRC_WORD16:
4211099013bSjsg 		val = U16(*ptr);
4221099013bSjsg 		(*ptr) += 2;
4231099013bSjsg 		break;
4241099013bSjsg 	case ATOM_SRC_BYTE0:
4251099013bSjsg 	case ATOM_SRC_BYTE8:
4261099013bSjsg 	case ATOM_SRC_BYTE16:
4271099013bSjsg 	case ATOM_SRC_BYTE24:
4281099013bSjsg 		val = U8(*ptr);
4291099013bSjsg 		(*ptr)++;
4301099013bSjsg 		break;
4311099013bSjsg 	}
4321099013bSjsg 	return val;
4331099013bSjsg }
4341099013bSjsg 
atom_get_dst(atom_exec_context * ctx,int arg,uint8_t attr,int * ptr,uint32_t * saved,int print)4351099013bSjsg static uint32_t atom_get_dst(atom_exec_context *ctx, int arg, uint8_t attr,
4361099013bSjsg 			     int *ptr, uint32_t *saved, int print)
4371099013bSjsg {
4381099013bSjsg 	return atom_get_src_int(ctx,
4391099013bSjsg 				arg | atom_dst_to_src[(attr >> 3) &
4401099013bSjsg 						      7][(attr >> 6) & 3] << 3,
4411099013bSjsg 				ptr, saved, print);
4421099013bSjsg }
4431099013bSjsg 
atom_skip_dst(atom_exec_context * ctx,int arg,uint8_t attr,int * ptr)4441099013bSjsg static void atom_skip_dst(atom_exec_context *ctx, int arg, uint8_t attr, int *ptr)
4451099013bSjsg {
4461099013bSjsg 	atom_skip_src_int(ctx,
4471099013bSjsg 			  arg | atom_dst_to_src[(attr >> 3) & 7][(attr >> 6) &
4481099013bSjsg 								 3] << 3, ptr);
4491099013bSjsg }
4501099013bSjsg 
atom_put_dst(atom_exec_context * ctx,int arg,uint8_t attr,int * ptr,uint32_t val,uint32_t saved)4511099013bSjsg static void atom_put_dst(atom_exec_context *ctx, int arg, uint8_t attr,
4521099013bSjsg 			 int *ptr, uint32_t val, uint32_t saved)
4531099013bSjsg {
4541099013bSjsg 	uint32_t align =
4551099013bSjsg 	    atom_dst_to_src[(attr >> 3) & 7][(attr >> 6) & 3], old_val =
4561099013bSjsg 	    val, idx;
4571099013bSjsg 	struct atom_context *gctx = ctx->ctx;
4581099013bSjsg 	old_val &= atom_arg_mask[align] >> atom_arg_shift[align];
4591099013bSjsg 	val <<= atom_arg_shift[align];
4601099013bSjsg 	val &= atom_arg_mask[align];
4611099013bSjsg 	saved &= ~atom_arg_mask[align];
4621099013bSjsg 	val |= saved;
4631099013bSjsg 	switch (arg) {
4641099013bSjsg 	case ATOM_ARG_REG:
4651099013bSjsg 		idx = U16(*ptr);
4661099013bSjsg 		(*ptr) += 2;
4671099013bSjsg 		DEBUG("REG[0x%04X]", idx);
4681099013bSjsg 		idx += gctx->reg_block;
4691099013bSjsg 		switch (gctx->io_mode) {
4701099013bSjsg 		case ATOM_IO_MM:
4711099013bSjsg 			if (idx == 0)
4721099013bSjsg 				gctx->card->reg_write(gctx->card, idx,
4731099013bSjsg 						      val << 2);
4741099013bSjsg 			else
4751099013bSjsg 				gctx->card->reg_write(gctx->card, idx, val);
4761099013bSjsg 			break;
4771099013bSjsg 		case ATOM_IO_PCI:
4787f4dd379Sjsg 			pr_info("PCI registers are not implemented\n");
4791099013bSjsg 			return;
4801099013bSjsg 		case ATOM_IO_SYSIO:
4817f4dd379Sjsg 			pr_info("SYSIO registers are not implemented\n");
4821099013bSjsg 			return;
4831099013bSjsg 		default:
4841099013bSjsg 			if (!(gctx->io_mode & 0x80)) {
4857f4dd379Sjsg 				pr_info("Bad IO mode\n");
4861099013bSjsg 				return;
4871099013bSjsg 			}
4881099013bSjsg 			if (!gctx->iio[gctx->io_mode & 0xFF]) {
4897f4dd379Sjsg 				pr_info("Undefined indirect IO write method %d\n",
4901099013bSjsg 					gctx->io_mode & 0x7F);
4911099013bSjsg 				return;
4921099013bSjsg 			}
4931099013bSjsg 			atom_iio_execute(gctx, gctx->iio[gctx->io_mode & 0xFF],
4941099013bSjsg 					 idx, val);
4951099013bSjsg 		}
4961099013bSjsg 		break;
4971099013bSjsg 	case ATOM_ARG_PS:
4981099013bSjsg 		idx = U8(*ptr);
4991099013bSjsg 		(*ptr)++;
5001099013bSjsg 		DEBUG("PS[0x%02X]", idx);
5011099013bSjsg 		ctx->ps[idx] = cpu_to_le32(val);
5021099013bSjsg 		break;
5031099013bSjsg 	case ATOM_ARG_WS:
5041099013bSjsg 		idx = U8(*ptr);
5051099013bSjsg 		(*ptr)++;
5061099013bSjsg 		DEBUG("WS[0x%02X]", idx);
5071099013bSjsg 		switch (idx) {
5081099013bSjsg 		case ATOM_WS_QUOTIENT:
5091099013bSjsg 			gctx->divmul[0] = val;
5101099013bSjsg 			break;
5111099013bSjsg 		case ATOM_WS_REMAINDER:
5121099013bSjsg 			gctx->divmul[1] = val;
5131099013bSjsg 			break;
5141099013bSjsg 		case ATOM_WS_DATAPTR:
5151099013bSjsg 			gctx->data_block = val;
5161099013bSjsg 			break;
5171099013bSjsg 		case ATOM_WS_SHIFT:
5181099013bSjsg 			gctx->shift = val;
5191099013bSjsg 			break;
5201099013bSjsg 		case ATOM_WS_OR_MASK:
5211099013bSjsg 		case ATOM_WS_AND_MASK:
5221099013bSjsg 			break;
5231099013bSjsg 		case ATOM_WS_FB_WINDOW:
5241099013bSjsg 			gctx->fb_base = val;
5251099013bSjsg 			break;
5261099013bSjsg 		case ATOM_WS_ATTRIBUTES:
5271099013bSjsg 			gctx->io_attr = val;
5281099013bSjsg 			break;
5291099013bSjsg 		case ATOM_WS_REGPTR:
5301099013bSjsg 			gctx->reg_block = val;
5311099013bSjsg 			break;
5321099013bSjsg 		default:
5331099013bSjsg 			ctx->ws[idx] = val;
5341099013bSjsg 		}
5351099013bSjsg 		break;
5361099013bSjsg 	case ATOM_ARG_FB:
5371099013bSjsg 		idx = U8(*ptr);
5381099013bSjsg 		(*ptr)++;
5391099013bSjsg 		if ((gctx->fb_base + (idx * 4)) > gctx->scratch_size_bytes) {
5401099013bSjsg 			DRM_ERROR("ATOM: fb write beyond scratch region: %d vs. %d\n",
5411099013bSjsg 				  gctx->fb_base + (idx * 4), gctx->scratch_size_bytes);
5421099013bSjsg 		} else
5431099013bSjsg 			gctx->scratch[(gctx->fb_base / 4) + idx] = val;
5441099013bSjsg 		DEBUG("FB[0x%02X]", idx);
5451099013bSjsg 		break;
5461099013bSjsg 	case ATOM_ARG_PLL:
5471099013bSjsg 		idx = U8(*ptr);
5481099013bSjsg 		(*ptr)++;
5491099013bSjsg 		DEBUG("PLL[0x%02X]", idx);
5501099013bSjsg 		gctx->card->pll_write(gctx->card, idx, val);
5511099013bSjsg 		break;
5521099013bSjsg 	case ATOM_ARG_MC:
5531099013bSjsg 		idx = U8(*ptr);
5541099013bSjsg 		(*ptr)++;
5551099013bSjsg 		DEBUG("MC[0x%02X]", idx);
5561099013bSjsg 		gctx->card->mc_write(gctx->card, idx, val);
5571099013bSjsg 		return;
5581099013bSjsg 	}
5591099013bSjsg 	switch (align) {
5601099013bSjsg 	case ATOM_SRC_DWORD:
5611099013bSjsg 		DEBUG(".[31:0] <- 0x%08X\n", old_val);
5621099013bSjsg 		break;
5631099013bSjsg 	case ATOM_SRC_WORD0:
5641099013bSjsg 		DEBUG(".[15:0] <- 0x%04X\n", old_val);
5651099013bSjsg 		break;
5661099013bSjsg 	case ATOM_SRC_WORD8:
5671099013bSjsg 		DEBUG(".[23:8] <- 0x%04X\n", old_val);
5681099013bSjsg 		break;
5691099013bSjsg 	case ATOM_SRC_WORD16:
5701099013bSjsg 		DEBUG(".[31:16] <- 0x%04X\n", old_val);
5711099013bSjsg 		break;
5721099013bSjsg 	case ATOM_SRC_BYTE0:
5731099013bSjsg 		DEBUG(".[7:0] <- 0x%02X\n", old_val);
5741099013bSjsg 		break;
5751099013bSjsg 	case ATOM_SRC_BYTE8:
5761099013bSjsg 		DEBUG(".[15:8] <- 0x%02X\n", old_val);
5771099013bSjsg 		break;
5781099013bSjsg 	case ATOM_SRC_BYTE16:
5791099013bSjsg 		DEBUG(".[23:16] <- 0x%02X\n", old_val);
5801099013bSjsg 		break;
5811099013bSjsg 	case ATOM_SRC_BYTE24:
5821099013bSjsg 		DEBUG(".[31:24] <- 0x%02X\n", old_val);
5831099013bSjsg 		break;
5841099013bSjsg 	}
5851099013bSjsg }
5861099013bSjsg 
atom_op_add(atom_exec_context * ctx,int * ptr,int arg)5871099013bSjsg static void atom_op_add(atom_exec_context *ctx, int *ptr, int arg)
5881099013bSjsg {
5891099013bSjsg 	uint8_t attr = U8((*ptr)++);
5901099013bSjsg 	uint32_t dst, src, saved;
5911099013bSjsg 	int dptr = *ptr;
5921099013bSjsg 	SDEBUG("   dst: ");
5931099013bSjsg 	dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
5941099013bSjsg 	SDEBUG("   src: ");
5951099013bSjsg 	src = atom_get_src(ctx, attr, ptr);
5961099013bSjsg 	dst += src;
5971099013bSjsg 	SDEBUG("   dst: ");
5981099013bSjsg 	atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
5991099013bSjsg }
6001099013bSjsg 
atom_op_and(atom_exec_context * ctx,int * ptr,int arg)6011099013bSjsg static void atom_op_and(atom_exec_context *ctx, int *ptr, int arg)
6021099013bSjsg {
6031099013bSjsg 	uint8_t attr = U8((*ptr)++);
6041099013bSjsg 	uint32_t dst, src, saved;
6051099013bSjsg 	int dptr = *ptr;
6061099013bSjsg 	SDEBUG("   dst: ");
6071099013bSjsg 	dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
6081099013bSjsg 	SDEBUG("   src: ");
6091099013bSjsg 	src = atom_get_src(ctx, attr, ptr);
6101099013bSjsg 	dst &= src;
6111099013bSjsg 	SDEBUG("   dst: ");
6121099013bSjsg 	atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
6131099013bSjsg }
6141099013bSjsg 
atom_op_beep(atom_exec_context * ctx,int * ptr,int arg)6151099013bSjsg static void atom_op_beep(atom_exec_context *ctx, int *ptr, int arg)
6161099013bSjsg {
617d765308cSjsg 	printk("ATOM BIOS beeped!\n");
6181099013bSjsg }
6191099013bSjsg 
atom_op_calltable(atom_exec_context * ctx,int * ptr,int arg)6201099013bSjsg static void atom_op_calltable(atom_exec_context *ctx, int *ptr, int arg)
6211099013bSjsg {
6221099013bSjsg 	int idx = U8((*ptr)++);
6231099013bSjsg 	int r = 0;
6241099013bSjsg 
6251099013bSjsg 	if (idx < ATOM_TABLE_NAMES_CNT)
6261099013bSjsg 		SDEBUG("   table: %d (%s)\n", idx, atom_table_names[idx]);
6271099013bSjsg 	else
6281099013bSjsg 		SDEBUG("   table: %d\n", idx);
6291099013bSjsg 	if (U16(ctx->ctx->cmd_table + 4 + 2 * idx))
6301099013bSjsg 		r = atom_execute_table_locked(ctx->ctx, idx, ctx->ps + ctx->ps_shift);
6311099013bSjsg 	if (r) {
6321099013bSjsg 		ctx->abort = true;
6331099013bSjsg 	}
6341099013bSjsg }
6351099013bSjsg 
atom_op_clear(atom_exec_context * ctx,int * ptr,int arg)6361099013bSjsg static void atom_op_clear(atom_exec_context *ctx, int *ptr, int arg)
6371099013bSjsg {
6381099013bSjsg 	uint8_t attr = U8((*ptr)++);
6391099013bSjsg 	uint32_t saved;
6401099013bSjsg 	int dptr = *ptr;
6411099013bSjsg 	attr &= 0x38;
6421099013bSjsg 	attr |= atom_def_dst[attr >> 3] << 6;
6431099013bSjsg 	atom_get_dst(ctx, arg, attr, ptr, &saved, 0);
6441099013bSjsg 	SDEBUG("   dst: ");
6451099013bSjsg 	atom_put_dst(ctx, arg, attr, &dptr, 0, saved);
6461099013bSjsg }
6471099013bSjsg 
atom_op_compare(atom_exec_context * ctx,int * ptr,int arg)6481099013bSjsg static void atom_op_compare(atom_exec_context *ctx, int *ptr, int arg)
6491099013bSjsg {
6501099013bSjsg 	uint8_t attr = U8((*ptr)++);
6511099013bSjsg 	uint32_t dst, src;
6521099013bSjsg 	SDEBUG("   src1: ");
6531099013bSjsg 	dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1);
6541099013bSjsg 	SDEBUG("   src2: ");
6551099013bSjsg 	src = atom_get_src(ctx, attr, ptr);
6561099013bSjsg 	ctx->ctx->cs_equal = (dst == src);
6571099013bSjsg 	ctx->ctx->cs_above = (dst > src);
6581099013bSjsg 	SDEBUG("   result: %s %s\n", ctx->ctx->cs_equal ? "EQ" : "NE",
6591099013bSjsg 	       ctx->ctx->cs_above ? "GT" : "LE");
6601099013bSjsg }
6611099013bSjsg 
atom_op_delay(atom_exec_context * ctx,int * ptr,int arg)6621099013bSjsg static void atom_op_delay(atom_exec_context *ctx, int *ptr, int arg)
6631099013bSjsg {
6641099013bSjsg 	unsigned count = U8((*ptr)++);
6651099013bSjsg 	SDEBUG("   count: %d\n", count);
6661099013bSjsg 	if (arg == ATOM_UNIT_MICROSEC)
6671099013bSjsg 		udelay(count);
6681099013bSjsg 	else if (!drm_can_sleep())
6691099013bSjsg 		mdelay(count);
6701099013bSjsg 	else
671e1001332Skettenis 		drm_msleep(count);
6721099013bSjsg }
6731099013bSjsg 
atom_op_div(atom_exec_context * ctx,int * ptr,int arg)6741099013bSjsg static void atom_op_div(atom_exec_context *ctx, int *ptr, int arg)
6751099013bSjsg {
6761099013bSjsg 	uint8_t attr = U8((*ptr)++);
6771099013bSjsg 	uint32_t dst, src;
6781099013bSjsg 	SDEBUG("   src1: ");
6791099013bSjsg 	dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1);
6801099013bSjsg 	SDEBUG("   src2: ");
6811099013bSjsg 	src = atom_get_src(ctx, attr, ptr);
6821099013bSjsg 	if (src != 0) {
6831099013bSjsg 		ctx->ctx->divmul[0] = dst / src;
6841099013bSjsg 		ctx->ctx->divmul[1] = dst % src;
6851099013bSjsg 	} else {
6861099013bSjsg 		ctx->ctx->divmul[0] = 0;
6871099013bSjsg 		ctx->ctx->divmul[1] = 0;
6881099013bSjsg 	}
6891099013bSjsg }
6901099013bSjsg 
atom_op_eot(atom_exec_context * ctx,int * ptr,int arg)6911099013bSjsg static void atom_op_eot(atom_exec_context *ctx, int *ptr, int arg)
6921099013bSjsg {
6931099013bSjsg 	/* functionally, a nop */
6941099013bSjsg }
6951099013bSjsg 
atom_op_jump(atom_exec_context * ctx,int * ptr,int arg)6961099013bSjsg static void atom_op_jump(atom_exec_context *ctx, int *ptr, int arg)
6971099013bSjsg {
6981099013bSjsg 	int execute = 0, target = U16(*ptr);
6991099013bSjsg 	unsigned long cjiffies;
7001099013bSjsg 
7011099013bSjsg 	(*ptr) += 2;
7021099013bSjsg 	switch (arg) {
7031099013bSjsg 	case ATOM_COND_ABOVE:
7041099013bSjsg 		execute = ctx->ctx->cs_above;
7051099013bSjsg 		break;
7061099013bSjsg 	case ATOM_COND_ABOVEOREQUAL:
7071099013bSjsg 		execute = ctx->ctx->cs_above || ctx->ctx->cs_equal;
7081099013bSjsg 		break;
7091099013bSjsg 	case ATOM_COND_ALWAYS:
7101099013bSjsg 		execute = 1;
7111099013bSjsg 		break;
7121099013bSjsg 	case ATOM_COND_BELOW:
7131099013bSjsg 		execute = !(ctx->ctx->cs_above || ctx->ctx->cs_equal);
7141099013bSjsg 		break;
7151099013bSjsg 	case ATOM_COND_BELOWOREQUAL:
7161099013bSjsg 		execute = !ctx->ctx->cs_above;
7171099013bSjsg 		break;
7181099013bSjsg 	case ATOM_COND_EQUAL:
7191099013bSjsg 		execute = ctx->ctx->cs_equal;
7201099013bSjsg 		break;
7211099013bSjsg 	case ATOM_COND_NOTEQUAL:
7221099013bSjsg 		execute = !ctx->ctx->cs_equal;
7231099013bSjsg 		break;
7241099013bSjsg 	}
7251099013bSjsg 	if (arg != ATOM_COND_ALWAYS)
7261bb76ff1Sjsg 		SDEBUG("   taken: %s\n", str_yes_no(execute));
7271099013bSjsg 	SDEBUG("   target: 0x%04X\n", target);
7281099013bSjsg 	if (execute) {
7291099013bSjsg 		if (ctx->last_jump == (ctx->start + target)) {
730eab30e11Sjsg 			cjiffies = jiffies;
7311099013bSjsg 			if (time_after(cjiffies, ctx->last_jump_jiffies)) {
7321099013bSjsg 				cjiffies -= ctx->last_jump_jiffies;
7331099013bSjsg 				if ((jiffies_to_msecs(cjiffies) > 5000)) {
7341099013bSjsg 					DRM_ERROR("atombios stuck in loop for more than 5secs aborting\n");
7351099013bSjsg 					ctx->abort = true;
7361099013bSjsg 				}
7371099013bSjsg 			} else {
7381099013bSjsg 				/* jiffies wrap around we will just wait a little longer */
739eab30e11Sjsg 				ctx->last_jump_jiffies = jiffies;
7401099013bSjsg 			}
7411099013bSjsg 		} else {
7421099013bSjsg 			ctx->last_jump = ctx->start + target;
743eab30e11Sjsg 			ctx->last_jump_jiffies = jiffies;
7441099013bSjsg 		}
7451099013bSjsg 		*ptr = ctx->start + target;
7461099013bSjsg 	}
7471099013bSjsg }
7481099013bSjsg 
atom_op_mask(atom_exec_context * ctx,int * ptr,int arg)7491099013bSjsg static void atom_op_mask(atom_exec_context *ctx, int *ptr, int arg)
7501099013bSjsg {
7511099013bSjsg 	uint8_t attr = U8((*ptr)++);
7521099013bSjsg 	uint32_t dst, mask, src, saved;
7531099013bSjsg 	int dptr = *ptr;
7541099013bSjsg 	SDEBUG("   dst: ");
7551099013bSjsg 	dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
7561099013bSjsg 	mask = atom_get_src_direct(ctx, ((attr >> 3) & 7), ptr);
7571099013bSjsg 	SDEBUG("   mask: 0x%08x", mask);
7581099013bSjsg 	SDEBUG("   src: ");
7591099013bSjsg 	src = atom_get_src(ctx, attr, ptr);
7601099013bSjsg 	dst &= mask;
7611099013bSjsg 	dst |= src;
7621099013bSjsg 	SDEBUG("   dst: ");
7631099013bSjsg 	atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
7641099013bSjsg }
7651099013bSjsg 
atom_op_move(atom_exec_context * ctx,int * ptr,int arg)7661099013bSjsg static void atom_op_move(atom_exec_context *ctx, int *ptr, int arg)
7671099013bSjsg {
7681099013bSjsg 	uint8_t attr = U8((*ptr)++);
7691099013bSjsg 	uint32_t src, saved;
7701099013bSjsg 	int dptr = *ptr;
7711099013bSjsg 	if (((attr >> 3) & 7) != ATOM_SRC_DWORD)
7721099013bSjsg 		atom_get_dst(ctx, arg, attr, ptr, &saved, 0);
7731099013bSjsg 	else {
7741099013bSjsg 		atom_skip_dst(ctx, arg, attr, ptr);
7751099013bSjsg 		saved = 0xCDCDCDCD;
7761099013bSjsg 	}
7771099013bSjsg 	SDEBUG("   src: ");
7781099013bSjsg 	src = atom_get_src(ctx, attr, ptr);
7791099013bSjsg 	SDEBUG("   dst: ");
7801099013bSjsg 	atom_put_dst(ctx, arg, attr, &dptr, src, saved);
7811099013bSjsg }
7821099013bSjsg 
atom_op_mul(atom_exec_context * ctx,int * ptr,int arg)7831099013bSjsg static void atom_op_mul(atom_exec_context *ctx, int *ptr, int arg)
7841099013bSjsg {
7851099013bSjsg 	uint8_t attr = U8((*ptr)++);
7861099013bSjsg 	uint32_t dst, src;
7871099013bSjsg 	SDEBUG("   src1: ");
7881099013bSjsg 	dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1);
7891099013bSjsg 	SDEBUG("   src2: ");
7901099013bSjsg 	src = atom_get_src(ctx, attr, ptr);
7911099013bSjsg 	ctx->ctx->divmul[0] = dst * src;
7921099013bSjsg }
7931099013bSjsg 
atom_op_nop(atom_exec_context * ctx,int * ptr,int arg)7941099013bSjsg static void atom_op_nop(atom_exec_context *ctx, int *ptr, int arg)
7951099013bSjsg {
7961099013bSjsg 	/* nothing */
7971099013bSjsg }
7981099013bSjsg 
atom_op_or(atom_exec_context * ctx,int * ptr,int arg)7991099013bSjsg static void atom_op_or(atom_exec_context *ctx, int *ptr, int arg)
8001099013bSjsg {
8011099013bSjsg 	uint8_t attr = U8((*ptr)++);
8021099013bSjsg 	uint32_t dst, src, saved;
8031099013bSjsg 	int dptr = *ptr;
8041099013bSjsg 	SDEBUG("   dst: ");
8051099013bSjsg 	dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
8061099013bSjsg 	SDEBUG("   src: ");
8071099013bSjsg 	src = atom_get_src(ctx, attr, ptr);
8081099013bSjsg 	dst |= src;
8091099013bSjsg 	SDEBUG("   dst: ");
8101099013bSjsg 	atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
8111099013bSjsg }
8121099013bSjsg 
atom_op_postcard(atom_exec_context * ctx,int * ptr,int arg)8131099013bSjsg static void atom_op_postcard(atom_exec_context *ctx, int *ptr, int arg)
8141099013bSjsg {
8151099013bSjsg 	uint8_t val = U8((*ptr)++);
8161099013bSjsg 	SDEBUG("POST card output: 0x%02X\n", val);
8171099013bSjsg }
8181099013bSjsg 
atom_op_repeat(atom_exec_context * ctx,int * ptr,int arg)8191099013bSjsg static void atom_op_repeat(atom_exec_context *ctx, int *ptr, int arg)
8201099013bSjsg {
8217f4dd379Sjsg 	pr_info("unimplemented!\n");
8221099013bSjsg }
8231099013bSjsg 
atom_op_restorereg(atom_exec_context * ctx,int * ptr,int arg)8241099013bSjsg static void atom_op_restorereg(atom_exec_context *ctx, int *ptr, int arg)
8251099013bSjsg {
8267f4dd379Sjsg 	pr_info("unimplemented!\n");
8271099013bSjsg }
8281099013bSjsg 
atom_op_savereg(atom_exec_context * ctx,int * ptr,int arg)8291099013bSjsg static void atom_op_savereg(atom_exec_context *ctx, int *ptr, int arg)
8301099013bSjsg {
8317f4dd379Sjsg 	pr_info("unimplemented!\n");
8321099013bSjsg }
8331099013bSjsg 
atom_op_setdatablock(atom_exec_context * ctx,int * ptr,int arg)8341099013bSjsg static void atom_op_setdatablock(atom_exec_context *ctx, int *ptr, int arg)
8351099013bSjsg {
8361099013bSjsg 	int idx = U8(*ptr);
8371099013bSjsg 	(*ptr)++;
8381099013bSjsg 	SDEBUG("   block: %d\n", idx);
8391099013bSjsg 	if (!idx)
8401099013bSjsg 		ctx->ctx->data_block = 0;
8411099013bSjsg 	else if (idx == 255)
8421099013bSjsg 		ctx->ctx->data_block = ctx->start;
8431099013bSjsg 	else
8441099013bSjsg 		ctx->ctx->data_block = U16(ctx->ctx->data_table + 4 + 2 * idx);
8451099013bSjsg 	SDEBUG("   base: 0x%04X\n", ctx->ctx->data_block);
8461099013bSjsg }
8471099013bSjsg 
atom_op_setfbbase(atom_exec_context * ctx,int * ptr,int arg)8481099013bSjsg static void atom_op_setfbbase(atom_exec_context *ctx, int *ptr, int arg)
8491099013bSjsg {
8501099013bSjsg 	uint8_t attr = U8((*ptr)++);
8511099013bSjsg 	SDEBUG("   fb_base: ");
8521099013bSjsg 	ctx->ctx->fb_base = atom_get_src(ctx, attr, ptr);
8531099013bSjsg }
8541099013bSjsg 
atom_op_setport(atom_exec_context * ctx,int * ptr,int arg)8551099013bSjsg static void atom_op_setport(atom_exec_context *ctx, int *ptr, int arg)
8561099013bSjsg {
8571099013bSjsg 	int port;
8581099013bSjsg 	switch (arg) {
8591099013bSjsg 	case ATOM_PORT_ATI:
8601099013bSjsg 		port = U16(*ptr);
8611099013bSjsg 		if (port < ATOM_IO_NAMES_CNT)
8621099013bSjsg 			SDEBUG("   port: %d (%s)\n", port, atom_io_names[port]);
8631099013bSjsg 		else
8641099013bSjsg 			SDEBUG("   port: %d\n", port);
8651099013bSjsg 		if (!port)
8661099013bSjsg 			ctx->ctx->io_mode = ATOM_IO_MM;
8671099013bSjsg 		else
8681099013bSjsg 			ctx->ctx->io_mode = ATOM_IO_IIO | port;
8691099013bSjsg 		(*ptr) += 2;
8701099013bSjsg 		break;
8711099013bSjsg 	case ATOM_PORT_PCI:
8721099013bSjsg 		ctx->ctx->io_mode = ATOM_IO_PCI;
8731099013bSjsg 		(*ptr)++;
8741099013bSjsg 		break;
8751099013bSjsg 	case ATOM_PORT_SYSIO:
8761099013bSjsg 		ctx->ctx->io_mode = ATOM_IO_SYSIO;
8771099013bSjsg 		(*ptr)++;
8781099013bSjsg 		break;
8791099013bSjsg 	}
8801099013bSjsg }
8811099013bSjsg 
atom_op_setregblock(atom_exec_context * ctx,int * ptr,int arg)8821099013bSjsg static void atom_op_setregblock(atom_exec_context *ctx, int *ptr, int arg)
8831099013bSjsg {
8841099013bSjsg 	ctx->ctx->reg_block = U16(*ptr);
8851099013bSjsg 	(*ptr) += 2;
8861099013bSjsg 	SDEBUG("   base: 0x%04X\n", ctx->ctx->reg_block);
8871099013bSjsg }
8881099013bSjsg 
atom_op_shift_left(atom_exec_context * ctx,int * ptr,int arg)8891099013bSjsg static void atom_op_shift_left(atom_exec_context *ctx, int *ptr, int arg)
8901099013bSjsg {
8911099013bSjsg 	uint8_t attr = U8((*ptr)++), shift;
8921099013bSjsg 	uint32_t saved, dst;
8931099013bSjsg 	int dptr = *ptr;
8941099013bSjsg 	attr &= 0x38;
8951099013bSjsg 	attr |= atom_def_dst[attr >> 3] << 6;
8961099013bSjsg 	SDEBUG("   dst: ");
8971099013bSjsg 	dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
8981099013bSjsg 	shift = atom_get_src_direct(ctx, ATOM_SRC_BYTE0, ptr);
8991099013bSjsg 	SDEBUG("   shift: %d\n", shift);
9001099013bSjsg 	dst <<= shift;
9011099013bSjsg 	SDEBUG("   dst: ");
9021099013bSjsg 	atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
9031099013bSjsg }
9041099013bSjsg 
atom_op_shift_right(atom_exec_context * ctx,int * ptr,int arg)9051099013bSjsg static void atom_op_shift_right(atom_exec_context *ctx, int *ptr, int arg)
9061099013bSjsg {
9071099013bSjsg 	uint8_t attr = U8((*ptr)++), shift;
9081099013bSjsg 	uint32_t saved, dst;
9091099013bSjsg 	int dptr = *ptr;
9101099013bSjsg 	attr &= 0x38;
9111099013bSjsg 	attr |= atom_def_dst[attr >> 3] << 6;
9121099013bSjsg 	SDEBUG("   dst: ");
9131099013bSjsg 	dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
9141099013bSjsg 	shift = atom_get_src_direct(ctx, ATOM_SRC_BYTE0, ptr);
9151099013bSjsg 	SDEBUG("   shift: %d\n", shift);
9161099013bSjsg 	dst >>= shift;
9171099013bSjsg 	SDEBUG("   dst: ");
9181099013bSjsg 	atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
9191099013bSjsg }
9201099013bSjsg 
atom_op_shl(atom_exec_context * ctx,int * ptr,int arg)9211099013bSjsg static void atom_op_shl(atom_exec_context *ctx, int *ptr, int arg)
9221099013bSjsg {
9231099013bSjsg 	uint8_t attr = U8((*ptr)++), shift;
9241099013bSjsg 	uint32_t saved, dst;
9251099013bSjsg 	int dptr = *ptr;
9261099013bSjsg 	uint32_t dst_align = atom_dst_to_src[(attr >> 3) & 7][(attr >> 6) & 3];
9271099013bSjsg 	SDEBUG("   dst: ");
9281099013bSjsg 	dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
9291099013bSjsg 	/* op needs to full dst value */
9301099013bSjsg 	dst = saved;
9311099013bSjsg 	shift = atom_get_src(ctx, attr, ptr);
9321099013bSjsg 	SDEBUG("   shift: %d\n", shift);
9331099013bSjsg 	dst <<= shift;
9341099013bSjsg 	dst &= atom_arg_mask[dst_align];
9351099013bSjsg 	dst >>= atom_arg_shift[dst_align];
9361099013bSjsg 	SDEBUG("   dst: ");
9371099013bSjsg 	atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
9381099013bSjsg }
9391099013bSjsg 
atom_op_shr(atom_exec_context * ctx,int * ptr,int arg)9401099013bSjsg static void atom_op_shr(atom_exec_context *ctx, int *ptr, int arg)
9411099013bSjsg {
9421099013bSjsg 	uint8_t attr = U8((*ptr)++), shift;
9431099013bSjsg 	uint32_t saved, dst;
9441099013bSjsg 	int dptr = *ptr;
9451099013bSjsg 	uint32_t dst_align = atom_dst_to_src[(attr >> 3) & 7][(attr >> 6) & 3];
9461099013bSjsg 	SDEBUG("   dst: ");
9471099013bSjsg 	dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
9481099013bSjsg 	/* op needs to full dst value */
9491099013bSjsg 	dst = saved;
9501099013bSjsg 	shift = atom_get_src(ctx, attr, ptr);
9511099013bSjsg 	SDEBUG("   shift: %d\n", shift);
9521099013bSjsg 	dst >>= shift;
9531099013bSjsg 	dst &= atom_arg_mask[dst_align];
9541099013bSjsg 	dst >>= atom_arg_shift[dst_align];
9551099013bSjsg 	SDEBUG("   dst: ");
9561099013bSjsg 	atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
9571099013bSjsg }
9581099013bSjsg 
atom_op_sub(atom_exec_context * ctx,int * ptr,int arg)9591099013bSjsg static void atom_op_sub(atom_exec_context *ctx, int *ptr, int arg)
9601099013bSjsg {
9611099013bSjsg 	uint8_t attr = U8((*ptr)++);
9621099013bSjsg 	uint32_t dst, src, saved;
9631099013bSjsg 	int dptr = *ptr;
9641099013bSjsg 	SDEBUG("   dst: ");
9651099013bSjsg 	dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
9661099013bSjsg 	SDEBUG("   src: ");
9671099013bSjsg 	src = atom_get_src(ctx, attr, ptr);
9681099013bSjsg 	dst -= src;
9691099013bSjsg 	SDEBUG("   dst: ");
9701099013bSjsg 	atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
9711099013bSjsg }
9721099013bSjsg 
atom_op_switch(atom_exec_context * ctx,int * ptr,int arg)9731099013bSjsg static void atom_op_switch(atom_exec_context *ctx, int *ptr, int arg)
9741099013bSjsg {
9751099013bSjsg 	uint8_t attr = U8((*ptr)++);
9761099013bSjsg 	uint32_t src, val, target;
9771099013bSjsg 	SDEBUG("   switch: ");
9781099013bSjsg 	src = atom_get_src(ctx, attr, ptr);
9791099013bSjsg 	while (U16(*ptr) != ATOM_CASE_END)
9801099013bSjsg 		if (U8(*ptr) == ATOM_CASE_MAGIC) {
9811099013bSjsg 			(*ptr)++;
9821099013bSjsg 			SDEBUG("   case: ");
9831099013bSjsg 			val =
9841099013bSjsg 			    atom_get_src(ctx, (attr & 0x38) | ATOM_ARG_IMM,
9851099013bSjsg 					 ptr);
9861099013bSjsg 			target = U16(*ptr);
9871099013bSjsg 			if (val == src) {
9881099013bSjsg 				SDEBUG("   target: %04X\n", target);
9891099013bSjsg 				*ptr = ctx->start + target;
9901099013bSjsg 				return;
9911099013bSjsg 			}
9921099013bSjsg 			(*ptr) += 2;
9931099013bSjsg 		} else {
9947f4dd379Sjsg 			pr_info("Bad case\n");
9951099013bSjsg 			return;
9961099013bSjsg 		}
9971099013bSjsg 	(*ptr) += 2;
9981099013bSjsg }
9991099013bSjsg 
atom_op_test(atom_exec_context * ctx,int * ptr,int arg)10001099013bSjsg static void atom_op_test(atom_exec_context *ctx, int *ptr, int arg)
10011099013bSjsg {
10021099013bSjsg 	uint8_t attr = U8((*ptr)++);
10031099013bSjsg 	uint32_t dst, src;
10041099013bSjsg 	SDEBUG("   src1: ");
10051099013bSjsg 	dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1);
10061099013bSjsg 	SDEBUG("   src2: ");
10071099013bSjsg 	src = atom_get_src(ctx, attr, ptr);
10081099013bSjsg 	ctx->ctx->cs_equal = ((dst & src) == 0);
10091099013bSjsg 	SDEBUG("   result: %s\n", ctx->ctx->cs_equal ? "EQ" : "NE");
10101099013bSjsg }
10111099013bSjsg 
atom_op_xor(atom_exec_context * ctx,int * ptr,int arg)10121099013bSjsg static void atom_op_xor(atom_exec_context *ctx, int *ptr, int arg)
10131099013bSjsg {
10141099013bSjsg 	uint8_t attr = U8((*ptr)++);
10151099013bSjsg 	uint32_t dst, src, saved;
10161099013bSjsg 	int dptr = *ptr;
10171099013bSjsg 	SDEBUG("   dst: ");
10181099013bSjsg 	dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
10191099013bSjsg 	SDEBUG("   src: ");
10201099013bSjsg 	src = atom_get_src(ctx, attr, ptr);
10211099013bSjsg 	dst ^= src;
10221099013bSjsg 	SDEBUG("   dst: ");
10231099013bSjsg 	atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
10241099013bSjsg }
10251099013bSjsg 
atom_op_debug(atom_exec_context * ctx,int * ptr,int arg)10261099013bSjsg static void atom_op_debug(atom_exec_context *ctx, int *ptr, int arg)
10271099013bSjsg {
10287f4dd379Sjsg 	pr_info("unimplemented!\n");
10291099013bSjsg }
10301099013bSjsg 
10311099013bSjsg static struct {
10321099013bSjsg 	void (*func) (atom_exec_context *, int *, int);
10331099013bSjsg 	int arg;
10341099013bSjsg } opcode_table[ATOM_OP_CNT] = {
10351099013bSjsg 	{
10361099013bSjsg 	NULL, 0}, {
10371099013bSjsg 	atom_op_move, ATOM_ARG_REG}, {
10381099013bSjsg 	atom_op_move, ATOM_ARG_PS}, {
10391099013bSjsg 	atom_op_move, ATOM_ARG_WS}, {
10401099013bSjsg 	atom_op_move, ATOM_ARG_FB}, {
10411099013bSjsg 	atom_op_move, ATOM_ARG_PLL}, {
10421099013bSjsg 	atom_op_move, ATOM_ARG_MC}, {
10431099013bSjsg 	atom_op_and, ATOM_ARG_REG}, {
10441099013bSjsg 	atom_op_and, ATOM_ARG_PS}, {
10451099013bSjsg 	atom_op_and, ATOM_ARG_WS}, {
10461099013bSjsg 	atom_op_and, ATOM_ARG_FB}, {
10471099013bSjsg 	atom_op_and, ATOM_ARG_PLL}, {
10481099013bSjsg 	atom_op_and, ATOM_ARG_MC}, {
10491099013bSjsg 	atom_op_or, ATOM_ARG_REG}, {
10501099013bSjsg 	atom_op_or, ATOM_ARG_PS}, {
10511099013bSjsg 	atom_op_or, ATOM_ARG_WS}, {
10521099013bSjsg 	atom_op_or, ATOM_ARG_FB}, {
10531099013bSjsg 	atom_op_or, ATOM_ARG_PLL}, {
10541099013bSjsg 	atom_op_or, ATOM_ARG_MC}, {
10551099013bSjsg 	atom_op_shift_left, ATOM_ARG_REG}, {
10561099013bSjsg 	atom_op_shift_left, ATOM_ARG_PS}, {
10571099013bSjsg 	atom_op_shift_left, ATOM_ARG_WS}, {
10581099013bSjsg 	atom_op_shift_left, ATOM_ARG_FB}, {
10591099013bSjsg 	atom_op_shift_left, ATOM_ARG_PLL}, {
10601099013bSjsg 	atom_op_shift_left, ATOM_ARG_MC}, {
10611099013bSjsg 	atom_op_shift_right, ATOM_ARG_REG}, {
10621099013bSjsg 	atom_op_shift_right, ATOM_ARG_PS}, {
10631099013bSjsg 	atom_op_shift_right, ATOM_ARG_WS}, {
10641099013bSjsg 	atom_op_shift_right, ATOM_ARG_FB}, {
10651099013bSjsg 	atom_op_shift_right, ATOM_ARG_PLL}, {
10661099013bSjsg 	atom_op_shift_right, ATOM_ARG_MC}, {
10671099013bSjsg 	atom_op_mul, ATOM_ARG_REG}, {
10681099013bSjsg 	atom_op_mul, ATOM_ARG_PS}, {
10691099013bSjsg 	atom_op_mul, ATOM_ARG_WS}, {
10701099013bSjsg 	atom_op_mul, ATOM_ARG_FB}, {
10711099013bSjsg 	atom_op_mul, ATOM_ARG_PLL}, {
10721099013bSjsg 	atom_op_mul, ATOM_ARG_MC}, {
10731099013bSjsg 	atom_op_div, ATOM_ARG_REG}, {
10741099013bSjsg 	atom_op_div, ATOM_ARG_PS}, {
10751099013bSjsg 	atom_op_div, ATOM_ARG_WS}, {
10761099013bSjsg 	atom_op_div, ATOM_ARG_FB}, {
10771099013bSjsg 	atom_op_div, ATOM_ARG_PLL}, {
10781099013bSjsg 	atom_op_div, ATOM_ARG_MC}, {
10791099013bSjsg 	atom_op_add, ATOM_ARG_REG}, {
10801099013bSjsg 	atom_op_add, ATOM_ARG_PS}, {
10811099013bSjsg 	atom_op_add, ATOM_ARG_WS}, {
10821099013bSjsg 	atom_op_add, ATOM_ARG_FB}, {
10831099013bSjsg 	atom_op_add, ATOM_ARG_PLL}, {
10841099013bSjsg 	atom_op_add, ATOM_ARG_MC}, {
10851099013bSjsg 	atom_op_sub, ATOM_ARG_REG}, {
10861099013bSjsg 	atom_op_sub, ATOM_ARG_PS}, {
10871099013bSjsg 	atom_op_sub, ATOM_ARG_WS}, {
10881099013bSjsg 	atom_op_sub, ATOM_ARG_FB}, {
10891099013bSjsg 	atom_op_sub, ATOM_ARG_PLL}, {
10901099013bSjsg 	atom_op_sub, ATOM_ARG_MC}, {
10911099013bSjsg 	atom_op_setport, ATOM_PORT_ATI}, {
10921099013bSjsg 	atom_op_setport, ATOM_PORT_PCI}, {
10931099013bSjsg 	atom_op_setport, ATOM_PORT_SYSIO}, {
10941099013bSjsg 	atom_op_setregblock, 0}, {
10951099013bSjsg 	atom_op_setfbbase, 0}, {
10961099013bSjsg 	atom_op_compare, ATOM_ARG_REG}, {
10971099013bSjsg 	atom_op_compare, ATOM_ARG_PS}, {
10981099013bSjsg 	atom_op_compare, ATOM_ARG_WS}, {
10991099013bSjsg 	atom_op_compare, ATOM_ARG_FB}, {
11001099013bSjsg 	atom_op_compare, ATOM_ARG_PLL}, {
11011099013bSjsg 	atom_op_compare, ATOM_ARG_MC}, {
11021099013bSjsg 	atom_op_switch, 0}, {
11031099013bSjsg 	atom_op_jump, ATOM_COND_ALWAYS}, {
11041099013bSjsg 	atom_op_jump, ATOM_COND_EQUAL}, {
11051099013bSjsg 	atom_op_jump, ATOM_COND_BELOW}, {
11061099013bSjsg 	atom_op_jump, ATOM_COND_ABOVE}, {
11071099013bSjsg 	atom_op_jump, ATOM_COND_BELOWOREQUAL}, {
11081099013bSjsg 	atom_op_jump, ATOM_COND_ABOVEOREQUAL}, {
11091099013bSjsg 	atom_op_jump, ATOM_COND_NOTEQUAL}, {
11101099013bSjsg 	atom_op_test, ATOM_ARG_REG}, {
11111099013bSjsg 	atom_op_test, ATOM_ARG_PS}, {
11121099013bSjsg 	atom_op_test, ATOM_ARG_WS}, {
11131099013bSjsg 	atom_op_test, ATOM_ARG_FB}, {
11141099013bSjsg 	atom_op_test, ATOM_ARG_PLL}, {
11151099013bSjsg 	atom_op_test, ATOM_ARG_MC}, {
11161099013bSjsg 	atom_op_delay, ATOM_UNIT_MILLISEC}, {
11171099013bSjsg 	atom_op_delay, ATOM_UNIT_MICROSEC}, {
11181099013bSjsg 	atom_op_calltable, 0}, {
11191099013bSjsg 	atom_op_repeat, 0}, {
11201099013bSjsg 	atom_op_clear, ATOM_ARG_REG}, {
11211099013bSjsg 	atom_op_clear, ATOM_ARG_PS}, {
11221099013bSjsg 	atom_op_clear, ATOM_ARG_WS}, {
11231099013bSjsg 	atom_op_clear, ATOM_ARG_FB}, {
11241099013bSjsg 	atom_op_clear, ATOM_ARG_PLL}, {
11251099013bSjsg 	atom_op_clear, ATOM_ARG_MC}, {
11261099013bSjsg 	atom_op_nop, 0}, {
11271099013bSjsg 	atom_op_eot, 0}, {
11281099013bSjsg 	atom_op_mask, ATOM_ARG_REG}, {
11291099013bSjsg 	atom_op_mask, ATOM_ARG_PS}, {
11301099013bSjsg 	atom_op_mask, ATOM_ARG_WS}, {
11311099013bSjsg 	atom_op_mask, ATOM_ARG_FB}, {
11321099013bSjsg 	atom_op_mask, ATOM_ARG_PLL}, {
11331099013bSjsg 	atom_op_mask, ATOM_ARG_MC}, {
11341099013bSjsg 	atom_op_postcard, 0}, {
11351099013bSjsg 	atom_op_beep, 0}, {
11361099013bSjsg 	atom_op_savereg, 0}, {
11371099013bSjsg 	atom_op_restorereg, 0}, {
11381099013bSjsg 	atom_op_setdatablock, 0}, {
11391099013bSjsg 	atom_op_xor, ATOM_ARG_REG}, {
11401099013bSjsg 	atom_op_xor, ATOM_ARG_PS}, {
11411099013bSjsg 	atom_op_xor, ATOM_ARG_WS}, {
11421099013bSjsg 	atom_op_xor, ATOM_ARG_FB}, {
11431099013bSjsg 	atom_op_xor, ATOM_ARG_PLL}, {
11441099013bSjsg 	atom_op_xor, ATOM_ARG_MC}, {
11451099013bSjsg 	atom_op_shl, ATOM_ARG_REG}, {
11461099013bSjsg 	atom_op_shl, ATOM_ARG_PS}, {
11471099013bSjsg 	atom_op_shl, ATOM_ARG_WS}, {
11481099013bSjsg 	atom_op_shl, ATOM_ARG_FB}, {
11491099013bSjsg 	atom_op_shl, ATOM_ARG_PLL}, {
11501099013bSjsg 	atom_op_shl, ATOM_ARG_MC}, {
11511099013bSjsg 	atom_op_shr, ATOM_ARG_REG}, {
11521099013bSjsg 	atom_op_shr, ATOM_ARG_PS}, {
11531099013bSjsg 	atom_op_shr, ATOM_ARG_WS}, {
11541099013bSjsg 	atom_op_shr, ATOM_ARG_FB}, {
11551099013bSjsg 	atom_op_shr, ATOM_ARG_PLL}, {
11561099013bSjsg 	atom_op_shr, ATOM_ARG_MC}, {
11571099013bSjsg atom_op_debug, 0},};
11581099013bSjsg 
atom_execute_table_locked(struct atom_context * ctx,int index,uint32_t * params)11591099013bSjsg static int atom_execute_table_locked(struct atom_context *ctx, int index, uint32_t *params)
11601099013bSjsg {
11611099013bSjsg 	int base = CU16(ctx->cmd_table + 4 + 2 * index);
11621099013bSjsg 	int len, ws, ps, ptr;
11631099013bSjsg 	unsigned char op;
11641099013bSjsg 	atom_exec_context ectx;
11651099013bSjsg 	int ret = 0;
11661099013bSjsg 
11671099013bSjsg 	if (!base)
11681099013bSjsg 		return -EINVAL;
11691099013bSjsg 
11701099013bSjsg 	len = CU16(base + ATOM_CT_SIZE_PTR);
11711099013bSjsg 	ws = CU8(base + ATOM_CT_WS_PTR);
11721099013bSjsg 	ps = CU8(base + ATOM_CT_PS_PTR) & ATOM_CT_PS_MASK;
11731099013bSjsg 	ptr = base + ATOM_CT_CODE_PTR;
11741099013bSjsg 
11751099013bSjsg 	SDEBUG(">> execute %04X (len %d, WS %d, PS %d)\n", base, len, ws, ps);
11761099013bSjsg 
11771099013bSjsg 	ectx.ctx = ctx;
11781099013bSjsg 	ectx.ps_shift = ps / 4;
11791099013bSjsg 	ectx.start = base;
11801099013bSjsg 	ectx.ps = params;
11811099013bSjsg 	ectx.abort = false;
11821099013bSjsg 	ectx.last_jump = 0;
11831099013bSjsg 	if (ws)
11847f4dd379Sjsg 		ectx.ws = kcalloc(4, ws, GFP_KERNEL);
11851099013bSjsg 	else
11861099013bSjsg 		ectx.ws = NULL;
11871099013bSjsg 
11881099013bSjsg 	debug_depth++;
11891099013bSjsg 	while (1) {
11901099013bSjsg 		op = CU8(ptr++);
11911099013bSjsg 		if (op < ATOM_OP_NAMES_CNT)
11921099013bSjsg 			SDEBUG("%s @ 0x%04X\n", atom_op_names[op], ptr - 1);
11931099013bSjsg 		else
11941099013bSjsg 			SDEBUG("[%d] @ 0x%04X\n", op, ptr - 1);
11951099013bSjsg 		if (ectx.abort) {
11961099013bSjsg 			DRM_ERROR("atombios stuck executing %04X (len %d, WS %d, PS %d) @ 0x%04X\n",
11971099013bSjsg 				base, len, ws, ps, ptr - 1);
11981099013bSjsg 			ret = -EINVAL;
11991099013bSjsg 			goto free;
12001099013bSjsg 		}
12011099013bSjsg 
12021099013bSjsg 		if (op < ATOM_OP_CNT && op > 0)
12031099013bSjsg 			opcode_table[op].func(&ectx, &ptr,
12041099013bSjsg 					      opcode_table[op].arg);
12051099013bSjsg 		else
12061099013bSjsg 			break;
12071099013bSjsg 
12081099013bSjsg 		if (op == ATOM_OP_EOT)
12091099013bSjsg 			break;
12101099013bSjsg 	}
12111099013bSjsg 	debug_depth--;
12121099013bSjsg 	SDEBUG("<<\n");
12131099013bSjsg 
12141099013bSjsg free:
1215de5631a0Sjsg 	kfree(ectx.ws);
12161099013bSjsg 	return ret;
12171099013bSjsg }
12181099013bSjsg 
atom_execute_table_scratch_unlocked(struct atom_context * ctx,int index,uint32_t * params)12197ccd5a2cSjsg int atom_execute_table_scratch_unlocked(struct atom_context *ctx, int index, uint32_t *params)
12201099013bSjsg {
12211099013bSjsg 	int r;
12221099013bSjsg 
1223528273cbSjsg 	mutex_lock(&ctx->mutex);
12244d39cd64Sjsg 	/* reset data block */
12254d39cd64Sjsg 	ctx->data_block = 0;
12261099013bSjsg 	/* reset reg block */
12271099013bSjsg 	ctx->reg_block = 0;
12281099013bSjsg 	/* reset fb window */
12291099013bSjsg 	ctx->fb_base = 0;
12301099013bSjsg 	/* reset io mode */
12311099013bSjsg 	ctx->io_mode = ATOM_IO_MM;
12324d39cd64Sjsg 	/* reset divmul */
12334d39cd64Sjsg 	ctx->divmul[0] = 0;
12344d39cd64Sjsg 	ctx->divmul[1] = 0;
12351099013bSjsg 	r = atom_execute_table_locked(ctx, index, params);
1236528273cbSjsg 	mutex_unlock(&ctx->mutex);
12371099013bSjsg 	return r;
12381099013bSjsg }
12391099013bSjsg 
atom_execute_table(struct atom_context * ctx,int index,uint32_t * params)12407ccd5a2cSjsg int atom_execute_table(struct atom_context *ctx, int index, uint32_t *params)
12417ccd5a2cSjsg {
12427ccd5a2cSjsg 	int r;
12437ccd5a2cSjsg 	mutex_lock(&ctx->scratch_mutex);
12447ccd5a2cSjsg 	r = atom_execute_table_scratch_unlocked(ctx, index, params);
12457ccd5a2cSjsg 	mutex_unlock(&ctx->scratch_mutex);
12467ccd5a2cSjsg 	return r;
12477ccd5a2cSjsg }
12487ccd5a2cSjsg 
12491099013bSjsg static int atom_iio_len[] = { 1, 2, 3, 3, 3, 3, 4, 4, 4, 3 };
12501099013bSjsg 
atom_index_iio(struct atom_context * ctx,int base)12511099013bSjsg static void atom_index_iio(struct atom_context *ctx, int base)
12521099013bSjsg {
1253de5631a0Sjsg 	ctx->iio = kzalloc(2 * 256, GFP_KERNEL);
12547ccd5a2cSjsg 	if (!ctx->iio)
12557ccd5a2cSjsg 		return;
12561099013bSjsg 	while (CU8(base) == ATOM_IIO_START) {
12571099013bSjsg 		ctx->iio[CU8(base + 1)] = base + 2;
12581099013bSjsg 		base += 2;
12591099013bSjsg 		while (CU8(base) != ATOM_IIO_END)
12601099013bSjsg 			base += atom_iio_len[CU8(base)];
12611099013bSjsg 		base += 3;
12621099013bSjsg 	}
12631099013bSjsg }
12641099013bSjsg 
atom_parse(struct card_info * card,void * bios)12651099013bSjsg struct atom_context *atom_parse(struct card_info *card, void *bios)
12661099013bSjsg {
12671099013bSjsg 	int base;
12681099013bSjsg 	struct atom_context *ctx =
1269de5631a0Sjsg 	    kzalloc(sizeof(struct atom_context), GFP_KERNEL);
12701099013bSjsg 	char *str;
12711099013bSjsg 	char name[512];
12721099013bSjsg 	int i;
12731099013bSjsg 
12741099013bSjsg 	if (!ctx)
12751099013bSjsg 		return NULL;
12761099013bSjsg 
12771099013bSjsg 	ctx->card = card;
12781099013bSjsg 	ctx->bios = bios;
12791099013bSjsg 
12801099013bSjsg 	if (CU16(0) != ATOM_BIOS_MAGIC) {
12817f4dd379Sjsg 		pr_info("Invalid BIOS magic\n");
1282de5631a0Sjsg 		kfree(ctx);
12831099013bSjsg 		return NULL;
12841099013bSjsg 	}
12851099013bSjsg 	if (strncmp
12861099013bSjsg 	    (CSTR(ATOM_ATI_MAGIC_PTR), ATOM_ATI_MAGIC,
12871099013bSjsg 	     strlen(ATOM_ATI_MAGIC))) {
12887f4dd379Sjsg 		pr_info("Invalid ATI magic\n");
1289de5631a0Sjsg 		kfree(ctx);
12901099013bSjsg 		return NULL;
12911099013bSjsg 	}
12921099013bSjsg 
12931099013bSjsg 	base = CU16(ATOM_ROM_TABLE_PTR);
12941099013bSjsg 	if (strncmp
12951099013bSjsg 	    (CSTR(base + ATOM_ROM_MAGIC_PTR), ATOM_ROM_MAGIC,
12961099013bSjsg 	     strlen(ATOM_ROM_MAGIC))) {
12977f4dd379Sjsg 		pr_info("Invalid ATOM magic\n");
1298de5631a0Sjsg 		kfree(ctx);
12991099013bSjsg 		return NULL;
13001099013bSjsg 	}
13011099013bSjsg 
13021099013bSjsg 	ctx->cmd_table = CU16(base + ATOM_ROM_CMD_PTR);
13031099013bSjsg 	ctx->data_table = CU16(base + ATOM_ROM_DATA_PTR);
13041099013bSjsg 	atom_index_iio(ctx, CU16(ctx->data_table + ATOM_DATA_IIO_PTR) + 4);
13057ccd5a2cSjsg 	if (!ctx->iio) {
13067ccd5a2cSjsg 		atom_destroy(ctx);
13077ccd5a2cSjsg 		return NULL;
13087ccd5a2cSjsg 	}
13091099013bSjsg 
13101099013bSjsg 	str = CSTR(CU16(base + ATOM_ROM_MSG_PTR));
13111099013bSjsg 	while (*str && ((*str == '\n') || (*str == '\r')))
13121099013bSjsg 		str++;
13131099013bSjsg 	/* name string isn't always 0 terminated */
13141099013bSjsg 	for (i = 0; i < 511; i++) {
13151099013bSjsg 		name[i] = str[i];
13161099013bSjsg 		if (name[i] < '.' || name[i] > 'z') {
13171099013bSjsg 			name[i] = 0;
13181099013bSjsg 			break;
13191099013bSjsg 		}
13201099013bSjsg 	}
13217f4dd379Sjsg 	pr_info("ATOM BIOS: %s\n", name);
13221099013bSjsg 
13231099013bSjsg 	return ctx;
13241099013bSjsg }
13251099013bSjsg 
atom_asic_init(struct atom_context * ctx)13261099013bSjsg int atom_asic_init(struct atom_context *ctx)
13271099013bSjsg {
13281099013bSjsg 	struct radeon_device *rdev = ctx->card->dev->dev_private;
13291099013bSjsg 	int hwi = CU16(ctx->data_table + ATOM_DATA_FWI_PTR);
13301099013bSjsg 	uint32_t ps[16];
13311099013bSjsg 	int ret;
13321099013bSjsg 
13331099013bSjsg 	memset(ps, 0, 64);
13341099013bSjsg 
13351099013bSjsg 	ps[0] = cpu_to_le32(CU32(hwi + ATOM_FWI_DEFSCLK_PTR));
13361099013bSjsg 	ps[1] = cpu_to_le32(CU32(hwi + ATOM_FWI_DEFMCLK_PTR));
13371099013bSjsg 	if (!ps[0] || !ps[1])
13381099013bSjsg 		return 1;
13391099013bSjsg 
13401099013bSjsg 	if (!CU16(ctx->cmd_table + 4 + 2 * ATOM_CMD_INIT))
13411099013bSjsg 		return 1;
13421099013bSjsg 	ret = atom_execute_table(ctx, ATOM_CMD_INIT, ps);
13431099013bSjsg 	if (ret)
13441099013bSjsg 		return ret;
13451099013bSjsg 
13461099013bSjsg 	memset(ps, 0, 64);
13471099013bSjsg 
13481099013bSjsg 	if (rdev->family < CHIP_R600) {
13491099013bSjsg 		if (CU16(ctx->cmd_table + 4 + 2 * ATOM_CMD_SPDFANCNTL))
13501099013bSjsg 			atom_execute_table(ctx, ATOM_CMD_SPDFANCNTL, ps);
13511099013bSjsg 	}
13521099013bSjsg 	return ret;
13531099013bSjsg }
13541099013bSjsg 
atom_destroy(struct atom_context * ctx)13551099013bSjsg void atom_destroy(struct atom_context *ctx)
13561099013bSjsg {
1357de5631a0Sjsg 	kfree(ctx->iio);
1358de5631a0Sjsg 	kfree(ctx);
13591099013bSjsg }
13601099013bSjsg 
atom_parse_data_header(struct atom_context * ctx,int index,uint16_t * size,uint8_t * frev,uint8_t * crev,uint16_t * data_start)13611099013bSjsg bool atom_parse_data_header(struct atom_context *ctx, int index,
13621099013bSjsg 			    uint16_t *size, uint8_t *frev, uint8_t *crev,
13631099013bSjsg 			    uint16_t *data_start)
13641099013bSjsg {
13651099013bSjsg 	int offset = index * 2 + 4;
13661099013bSjsg 	int idx = CU16(ctx->data_table + offset);
13671099013bSjsg 	u16 *mdt = (u16 *)(ctx->bios + ctx->data_table + 4);
13681099013bSjsg 
13691099013bSjsg 	if (!mdt[index])
13701099013bSjsg 		return false;
13711099013bSjsg 
13721099013bSjsg 	if (size)
13731099013bSjsg 		*size = CU16(idx);
13741099013bSjsg 	if (frev)
13751099013bSjsg 		*frev = CU8(idx + 2);
13761099013bSjsg 	if (crev)
13771099013bSjsg 		*crev = CU8(idx + 3);
13781099013bSjsg 	*data_start = idx;
13791099013bSjsg 	return true;
13801099013bSjsg }
13811099013bSjsg 
atom_parse_cmd_header(struct atom_context * ctx,int index,uint8_t * frev,uint8_t * crev)13821099013bSjsg bool atom_parse_cmd_header(struct atom_context *ctx, int index, uint8_t *frev,
13831099013bSjsg 			   uint8_t *crev)
13841099013bSjsg {
13851099013bSjsg 	int offset = index * 2 + 4;
13861099013bSjsg 	int idx = CU16(ctx->cmd_table + offset);
13871099013bSjsg 	u16 *mct = (u16 *)(ctx->bios + ctx->cmd_table + 4);
13881099013bSjsg 
13891099013bSjsg 	if (!mct[index])
13901099013bSjsg 		return false;
13911099013bSjsg 
13921099013bSjsg 	if (frev)
13931099013bSjsg 		*frev = CU8(idx + 2);
13941099013bSjsg 	if (crev)
13951099013bSjsg 		*crev = CU8(idx + 3);
13961099013bSjsg 	return true;
13971099013bSjsg }
13981099013bSjsg 
atom_allocate_fb_scratch(struct atom_context * ctx)13991099013bSjsg int atom_allocate_fb_scratch(struct atom_context *ctx)
14001099013bSjsg {
14011099013bSjsg 	int index = GetIndexIntoMasterTable(DATA, VRAM_UsageByFirmware);
14021099013bSjsg 	uint16_t data_offset;
14031099013bSjsg 	int usage_bytes = 0;
14041099013bSjsg 	struct _ATOM_VRAM_USAGE_BY_FIRMWARE *firmware_usage;
14051099013bSjsg 
14061099013bSjsg 	if (atom_parse_data_header(ctx, index, NULL, NULL, NULL, &data_offset)) {
14071099013bSjsg 		firmware_usage = (struct _ATOM_VRAM_USAGE_BY_FIRMWARE *)(ctx->bios + data_offset);
14081099013bSjsg 
14091099013bSjsg 		DRM_DEBUG("atom firmware requested %08x %dkb\n",
14101099013bSjsg 			  le32_to_cpu(firmware_usage->asFirmwareVramReserveInfo[0].ulStartAddrUsedByFirmware),
14111099013bSjsg 			  le16_to_cpu(firmware_usage->asFirmwareVramReserveInfo[0].usFirmwareUseInKb));
14121099013bSjsg 
14131099013bSjsg 		usage_bytes = le16_to_cpu(firmware_usage->asFirmwareVramReserveInfo[0].usFirmwareUseInKb) * 1024;
14141099013bSjsg 	}
14151099013bSjsg 	ctx->scratch_size_bytes = 0;
14161099013bSjsg 	if (usage_bytes == 0)
14171099013bSjsg 		usage_bytes = 20 * 1024;
14181099013bSjsg 	/* allocate some scratch memory */
1419de5631a0Sjsg 	ctx->scratch = kzalloc(usage_bytes, GFP_KERNEL);
14201099013bSjsg 	if (!ctx->scratch)
14211099013bSjsg 		return -ENOMEM;
14221099013bSjsg 	ctx->scratch_size_bytes = usage_bytes;
14231099013bSjsg 	return 0;
14241099013bSjsg }
1425