brintos

brintos / linux-shallow public Read only

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