#include "atom.h"
#include <fcntl.h>

extern unsigned char * volatile fb_mem, *volatile ctrl_mem;
static struct card_info my_card_info;

struct radeon_pll {
	uint16_t reference_freq;
	uint16_t reference_div;
	uint32_t pll_in_min;
	uint32_t pll_in_max;
	uint32_t pll_out_min;
	uint32_t pll_out_max;
	uint16_t xclk;

	uint32_t min_ref_div;
	uint32_t max_ref_div;
	uint32_t min_post_div;
	uint32_t max_post_div;
	uint32_t min_feedback_div;
	uint32_t max_feedback_div;
	uint32_t best_vco;
};

char *object_names[] = { "NONE",
			 "INTERNAL LVDS",
			 "INTERNAL TMDS1",
			 "INTERNAL TMDS2",
			 "INTERNAL DAC1",
			 "INTERNAL DAC2",
			 "INTERNAL SDVOA",
			 "INTERNAL SDVOB",
			 "SI170B",
			 "CH7303",
			 "CH7301",
			 "INTERNAL DVO1",
			 "EXTERNAL SDVOA",
			 "EXTERNAL SDVOB",
			 "TI TFP513",
			 "INTERNAL LVTM1",
			 "VT1623",
			 "HDMI SI1930",
			 "HDMI INTERNAL",
			 "INTERNAL KLDSCP TMDS1",
			 "INTERNAL KLDSCP DVO1",
			 "INTERNAL KLDSCP DAC1",
			 "INTERNAL KLDSCP DAC2",
			 "SI178",
			 "MVPU FPGA",
			 "INTERNAL DDI",
			 "VT1625",
			 "HDMI SI1932",
			 "DP AN9801",
			 "DP DP501",
			 "INTERNAL UNIPHY",
			 "INTERNAL KLDSCP LVTMA",
			 "INTERNAL UNIPHY1",
			 "INTERNAL UNIPHY2"
};
			 
char *connector_names[] = { "NONE",
			    "SL DVI-I",
			    "DL DVI-I",
			    "SL DVI-D",
			    "DL DVI-D",
			    "VGA",
			    "COMPOSITE",
			    "SVIDEO",
			    "YPbPr",
			    "D_CONNECTOR",
			    "9PIN_DIN",
			    "SCART",
			    "HDMI A",
			    "HDMI B",
			    "LVDS",
			    "7PIN_DIN",
			    "PCIE CONNECTOR",
			    "CROSSFIRE",
			    "HARDCODE_DVI",
			    "DISPLAYPORT",
};


union firmware_info {
	ATOM_FIRMWARE_INFO info;
	ATOM_FIRMWARE_INFO_V1_2 info_12;
	ATOM_FIRMWARE_INFO_V1_3 info_13;
	ATOM_FIRMWARE_INFO_V1_4 info_14;
};

static struct radeon_pll p1pll_s, p2pll_s, spll_s, mpll_s;

int radeon_atom_get_clock_info(struct atom_context *ctx)
{

	int index = GetIndexIntoMasterTable(DATA, FirmwareInfo);
	union firmware_info *firmware_info;
	uint8_t frev, crev;
	struct radeon_pll *p1pll = &p1pll_s;
	struct radeon_pll *p2pll = &p2pll_s;
	struct radeon_pll *spll = &spll_s;
	struct radeon_pll *mpll = &mpll_s;
	uint16_t data_offset;

	atom_parse_data_header(ctx, index, NULL, &frev, &crev, &data_offset);

	firmware_info = (union firmware_info *)(ctx->bios + data_offset);

	if (firmware_info) {
		/* pixel clocks */
		p1pll->reference_freq = le16_to_cpu(firmware_info->info.usReferenceClock);
		p1pll->reference_div = 0;

		p1pll->pll_out_min = le16_to_cpu(firmware_info->info.usMinPixelClockPLL_Output);
		p1pll->pll_out_max = le32_to_cpu(firmware_info->info.ulMaxPixelClockPLL_Output);

		if (p1pll->pll_out_min == 0) {
			p1pll->pll_out_min = 64800;
		}

		p1pll->pll_in_min = le16_to_cpu(firmware_info->info.usMinPixelClockPLL_Input);
		p1pll->pll_in_max = le16_to_cpu(firmware_info->info.usMaxPixelClockPLL_Input);

		*p2pll = *p1pll;

		/* system clock */
		spll->reference_freq = le16_to_cpu(firmware_info->info.usReferenceClock);
		spll->reference_div = 0;

		spll->pll_out_min = le16_to_cpu(firmware_info->info.usMinEngineClockPLL_Output);
		spll->pll_out_max = le32_to_cpu(firmware_info->info.ulMaxEngineClockPLL_Output);

		/* ??? */
		if (spll->pll_out_min == 0) {
#if 0
			if (radeon_is_avivo(dev_priv))
				spll->pll_out_min = 64800;
			else
				spll->pll_out_min = 20000;
#endif
		}

		spll->pll_in_min = le16_to_cpu(firmware_info->info.usMinEngineClockPLL_Input);
		spll->pll_in_max = le16_to_cpu(firmware_info->info.usMaxEngineClockPLL_Input);


		/* memory clock */
		mpll->reference_freq = le16_to_cpu(firmware_info->info.usReferenceClock);
		mpll->reference_div = 0;

		mpll->pll_out_min = le16_to_cpu(firmware_info->info.usMinMemoryClockPLL_Output);
		mpll->pll_out_max = le32_to_cpu(firmware_info->info.ulMaxMemoryClockPLL_Output);

		/* ??? */
		if (mpll->pll_out_min == 0) {
			if (0) //radeon_is_avivo(dev_priv))
				mpll->pll_out_min = 64800;
			else
				mpll->pll_out_min = 20000;
		}

		mpll->pll_in_min = le16_to_cpu(firmware_info->info.usMinMemoryClockPLL_Input);
		mpll->pll_in_max = le16_to_cpu(firmware_info->info.usMaxMemoryClockPLL_Input);

	//	mode_info->sclk = le32_to_cpu(firmware_info->info.ulDefaultEngineClock);
	//	mode_info->mclk = le32_to_cpu(firmware_info->info.ulDefaultMemoryClock);

		return true;
	}
	return false;
}

char *object_type_names[] = { "NONE",
			      "GPU",
			      "ENCODER",
			      "CONNECTOR",
			      "ROUTER",
};

char *router_object_names[] = { "NONE",
				"I2C_EXTENDER_CNTL",
};

void radeon_lookup_gpio(struct atom_context *ctx, uint8_t id)
{
	ATOM_GPIO_I2C_ASSIGMENT *gpio;
	int index = GetIndexIntoMasterTable(DATA, GPIO_I2C_Info);
	struct _ATOM_GPIO_I2C_INFO *i2c_info;
	uint16_t data_offset;
	int i;

	atom_parse_data_header(ctx, index, NULL, NULL, NULL, &data_offset);

	i2c_info = (struct _ATOM_GPIO_I2C_INFO *)(ctx->bios + data_offset);

	for (i = 0; i < ATOM_MAX_SUPPORTED_DEVICE; i++) {
	    gpio = &i2c_info->asGPIO_Info[i];

	    if (gpio->sucI2cId.ucAccess == id)
		printf("    GPIO 0x%02x: 0x%04x\n", id, gpio->usClkMaskRegisterIndex *4);
	}
}

static void radeon_print_object(uint16_t obj)
{
    uint8_t obj_id, num, obj_type;

    obj_id = (obj & OBJECT_ID_MASK) >> OBJECT_ID_SHIFT;
    num = (obj & ENUM_ID_MASK) >> ENUM_ID_SHIFT;
    obj_type = (obj & OBJECT_TYPE_MASK) >> OBJECT_TYPE_SHIFT;

    if (obj_type == GRAPH_OBJECT_TYPE_CONNECTOR)
	printf("%s, %d, %s (0x%04x)\n",
	       object_type_names[obj_type], num, connector_names[obj_id], obj);
    else if (obj_type == GRAPH_OBJECT_TYPE_ENCODER)
	printf("%s, %d, %s (0x%04x)\n",
	       object_type_names[obj_type], num, object_names[obj_id], obj);
    else if (obj_type == GRAPH_OBJECT_TYPE_ROUTER)
	printf("%s, %d, %s (0x%04x)\n",
	       object_type_names[obj_type], num, router_object_names[obj_id], obj);
    else
	printf("%s, %d, 0x%02x (0x%04x)\n",
	       object_type_names[obj_type], num, obj_id, obj);

}

static void radeon_print_supported_devices(uint16_t sd)
{
    if (sd & ATOM_DEVICE_CRT1_SUPPORT)
	printf("CRT1 ");
    if (sd & ATOM_DEVICE_LCD1_SUPPORT)
	printf("LCD1 ");
    if (sd & ATOM_DEVICE_TV1_SUPPORT)
	printf("TV1 ");
    if (sd & ATOM_DEVICE_DFP1_SUPPORT)
	printf("DFP1 ");
    if (sd & ATOM_DEVICE_CRT2_SUPPORT)
	printf("CRT2 ");
    if (sd & ATOM_DEVICE_LCD2_SUPPORT)
	printf("LCD2 ");
    if (sd & ATOM_DEVICE_TV2_SUPPORT)
	printf("TV2 ");
    if (sd & ATOM_DEVICE_DFP2_SUPPORT)
	printf("DFP2 ");
    if (sd & ATOM_DEVICE_CV_SUPPORT)
	printf("CV ");
    if (sd & ATOM_DEVICE_DFP3_SUPPORT)
	printf("DFP3 ");
    if (sd & ATOM_DEVICE_DFP4_SUPPORT)
	printf("DFP4 ");
    if (sd & ATOM_DEVICE_DFP5_SUPPORT)
	printf("DFP5 ");
}

static void radeon_dump_bios_igp_slot_config(struct atom_context *ctx, uint16_t obj)
{
	uint16_t size, data_offset;
	uint8_t frev, crev;
	int index = GetIndexIntoMasterTable(DATA, IntegratedSystemInfo);
	ATOM_INTEGRATED_SYSTEM_INFO_V2 *igp_obj = NULL;
	uint8_t obj_id, num, obj_type;

	obj_id = (obj & OBJECT_ID_MASK) >> OBJECT_ID_SHIFT;
	num = (obj & ENUM_ID_MASK) >> ENUM_ID_SHIFT;
	obj_type = (obj & OBJECT_TYPE_MASK) >> OBJECT_TYPE_SHIFT;

	if (obj_id == CONNECTOR_OBJECT_ID_PCIE_CONNECTOR) {
	    uint32_t slot_config, ct;

	    atom_parse_data_header(ctx, index, &size, &frev, &crev, &data_offset);

	    igp_obj = (ATOM_INTEGRATED_SYSTEM_INFO_V2 *)(ctx->bios + data_offset);

	    if (igp_obj) {
		if (num == 1)
		    slot_config = igp_obj->ulDDISlot1Config;
		else
		    slot_config = igp_obj->ulDDISlot2Config;

		ct = (slot_config  >> 16) & 0xff;
		printf("\tConnector: %s\n", connector_names[ct]);
		printf("\tLane Config: 0x%04x\n", slot_config & 0xffff);
	    }
	}
}

void radeon_dump_bios_object_table(struct atom_context *ctx)
{
	uint16_t size, data_offset;
	uint8_t frev, crev;
	int index = GetIndexIntoMasterTable(DATA, Object_Header);
	ATOM_CONNECTOR_OBJECT_TABLE *con_obj;
	ATOM_DISPLAY_OBJECT_PATH_TABLE *path_obj;
	ATOM_ENCODER_OBJECT_TABLE *enc_obj;
	ATOM_OBJECT_TABLE *router_obj;
	ATOM_INTEGRATED_SYSTEM_INFO_V2 *igp_obj = NULL;
	ATOM_OBJECT_HEADER *obj_header;
	int i, j, k, path_size;

	atom_parse_data_header(ctx, index, &size, &frev, &crev, &data_offset);

	if (crev < 2)
		return;

	obj_header = (ATOM_OBJECT_HEADER *)(ctx->bios + data_offset);

	printf("\nSupported Devices: ");
	radeon_print_supported_devices(obj_header->usDeviceSupport);
	printf("\n");

	path_obj = (ATOM_DISPLAY_OBJECT_PATH_TABLE *)(ctx->bios + data_offset + obj_header->usDisplayPathTableOffset);
	con_obj = (ATOM_CONNECTOR_OBJECT_TABLE *)(ctx->bios + data_offset + obj_header->usConnectorObjectTableOffset);
	enc_obj = (ATOM_ENCODER_OBJECT_TABLE *)(ctx->bios + data_offset + obj_header->usEncoderObjectTableOffset);
	router_obj = (ATOM_OBJECT_TABLE *)(ctx->bios + data_offset + obj_header->usRouterObjectTableOffset);

	path_size = 0;
	for (i = 0; i < path_obj->ucNumOfDispPath; i++) {
	    uint8_t *addr = (uint8_t *)path_obj->asDispPath;
	    ATOM_DISPLAY_OBJECT_PATH *path;
	    addr += path_size;
	    path = (ATOM_DISPLAY_OBJECT_PATH *)addr;

	    if (obj_header->usDeviceSupport & path->usDeviceTag) {
		printf("\nDevice: ");
		radeon_print_supported_devices(path->usDeviceTag);
		printf("\n");
		// connector object
		printf("==== Connector Info ====\n");
		radeon_print_object(path->usConnObjectId);
		for (j = 0; j < con_obj->ucNumberOfObjects; j++) {
		    ATOM_COMMON_RECORD_HEADER *record;
		    int record_base;
		    uint16_t con_obj_id = le16_to_cpu(con_obj->asObjects[j].usObjectID);

		    if (path->usConnObjectId == con_obj_id) {
			record = (ATOM_COMMON_RECORD_HEADER *)
			    (ctx->bios + data_offset + le16_to_cpu(con_obj->asObjects[j].usRecordOffset));
			record_base = le16_to_cpu(con_obj->asObjects[j].usRecordOffset);

			while (record->ucRecordType > 0 &&
			       record->ucRecordType <= ATOM_MAX_OBJECT_RECORD_NUMBER) {
			    //printf("record type %d\n", record->ucRecordType);

			    switch(record->ucRecordType) {
			    case ATOM_I2C_RECORD_TYPE:
				{
				    ATOM_I2C_RECORD *i2c_record = (ATOM_I2C_RECORD *)record;
				    ATOM_I2C_ID_CONFIG_ACCESS *i2c_config =
					(ATOM_I2C_ID_CONFIG_ACCESS *)&i2c_record->sucI2cId;
				    printf("  ATOM_I2C_RECORD_TYPE\n");
				    printf("    i2c mux:%d engine:%d hw_cap:%d\n",
					   i2c_record->sucI2cId.bfI2C_LineMux,
					   i2c_record->sucI2cId.bfHW_EngineID,
					   i2c_record->sucI2cId.bfHW_Capable);
				    radeon_lookup_gpio(ctx, i2c_config->ucAccess);
				}
				break;
			    case ATOM_HPD_INT_RECORD_TYPE:
				{
				    ATOM_HPD_INT_RECORD *hdp = (ATOM_HPD_INT_RECORD *)record;
				    printf("  ATOM_HPD_INT_RECORD_TYPE\n");
				    printf("    gpioid: %d, plugged pin state: 0x%x\n",
					   hdp->ucHPDIntGPIOID, hdp->ucPluggged_PinState);
				}
				break;
			    case ATOM_CONNECTOR_DEVICE_TAG_RECORD_TYPE:
				{
				    ATOM_CONNECTOR_DEVICE_TAG_RECORD *dev_tag_rec =
					(ATOM_CONNECTOR_DEVICE_TAG_RECORD *)record;
				    printf("  ATOM_CONNECTOR_DEVICE_TAG_RECORD_TYPE\n");
				    printf("    Devices: ");
				    for (k = 0; k < dev_tag_rec->ucNumberOfDevice; k++) {
					ATOM_CONNECTOR_DEVICE_TAG *dev_tag =
					    &dev_tag_rec->asDeviceTag[k];
					radeon_print_supported_devices(dev_tag->usDeviceID);
				    }
				    printk("\n");
				}
				break;
			    case ATOM_CONNECTOR_CVTV_SHARE_DIN_RECORD_TYPE:
				{
				    ATOM_CONNECTOR_CVTV_SHARE_DIN_RECORD *cvtv =
					(ATOM_CONNECTOR_CVTV_SHARE_DIN_RECORD *)record;
				    printf("  ATOM_CONNECTOR_CVTV_SHARE_DIN_RECORD_TYPE\n");
				    printf("    gpioid: %d, tv active state: %d\n",
					   cvtv->ucGPIOID, cvtv->ucTVActiveState);
				}
				break;
			    case ATOM_CONNECTOR_PCIE_SUBCONNECTOR_RECORD_TYPE:
				{
				    ATOM_CONNECTOR_PCIE_SUBCONNECTOR_RECORD *pcie =
					(ATOM_CONNECTOR_PCIE_SUBCONNECTOR_RECORD *)record;
				    printf("  ATOM_CONNECTOR_PCIE_SUBCONNECTOR_RECORD_TYPE\n");
				    printf("    %s\n", connector_names[pcie->ucSubConnectorType]);
				}
				break;
			    case ATOM_OBJECT_GPIO_CNTL_RECORD_TYPE:
				printf("  ATOM_OBJECT_GPIO_CNTL_RECORD_TYPE\n");
				break;
			    case ATOM_CONNECTOR_CF_RECORD_TYPE:
				printf("  ATOM_CONNECTOR_CF_RECORD_TYPE\n");
				break;

			    }
			    record = (ATOM_COMMON_RECORD_HEADER *)((char *)record + record->ucRecordSize);
			}
			break;
		    }
		}
		radeon_dump_bios_igp_slot_config(ctx, path->usConnObjectId);
		printf("==== GPU Info ====\n");
		radeon_print_object(path->usGPUObjectId);
		printf("==== Encoder info ====\n");
		for (k = 0; k < ((path->usSize - 8) / 2); k++) {
		    uint8_t obj_type = (path->usGraphicObjIds[k] & OBJECT_TYPE_MASK) >> OBJECT_TYPE_SHIFT;

		    if (obj_type == GRAPH_OBJECT_TYPE_ENCODER) {
			radeon_print_object(path->usGraphicObjIds[k]);
			for (j = 0; j < enc_obj->ucNumberOfObjects; j++) {
			    ATOM_COMMON_RECORD_HEADER *record;
			    int record_base;
			    uint16_t enc_obj_id = le16_to_cpu(enc_obj->asObjects[j].usObjectID);

			    if (path->usGraphicObjIds[k] == enc_obj_id) {
				record = (ATOM_COMMON_RECORD_HEADER *)
				    (ctx->bios + data_offset + le16_to_cpu(enc_obj->asObjects[j].usRecordOffset));
				record_base = le16_to_cpu(enc_obj->asObjects[j].usRecordOffset);

				while (record->ucRecordType > 0 &&
				       record->ucRecordType <= ATOM_MAX_OBJECT_RECORD_NUMBER) {
				    //printf("record type %d\n", record->ucRecordType);

				    switch(record->ucRecordType) {
				    case ATOM_ENCODER_FPGA_CONTROL_RECORD_TYPE:
					printf("  ATOM_ENCODER_FPGA_CONTROL_RECORD_TYPE\n");
					break;
				    case ATOM_ENCODER_DVO_CF_RECORD_TYPE:
					printf("  ATOM_ENCODER_DVO_CF_RECORD_TYPE\n");
					break;
				    case ATOM_OBJECT_GPIO_CNTL_RECORD_TYPE:
					printf("  ATOM_OBJECT_GPIO_CNTL_RECORD_TYPE\n");
					break;
				    }
				    record = (ATOM_COMMON_RECORD_HEADER *)((char *)record + record->ucRecordSize);
				}
				break;
			    }
			}
		    }
		}

		printf("==== Router Info ====\n");
		for (k = 0; k < ((path->usSize - 8) / 2); k++) {
		    uint8_t obj_type = (path->usGraphicObjIds[k] & OBJECT_TYPE_MASK) >> OBJECT_TYPE_SHIFT;

		    if (obj_type == GRAPH_OBJECT_TYPE_ROUTER) {
			radeon_print_object(path->usGraphicObjIds[k]);
			for (j = 0; j < router_obj->ucNumberOfObjects; j++) {
			    ATOM_COMMON_RECORD_HEADER *record;
			    int record_base;
			    uint16_t router_obj_id = le16_to_cpu(router_obj->asObjects[j].usObjectID);

			    if (path->usGraphicObjIds[k] == router_obj_id) {
				record = (ATOM_COMMON_RECORD_HEADER *)
				    (ctx->bios + data_offset + le16_to_cpu(router_obj->asObjects[j].usRecordOffset));
				record_base = le16_to_cpu(router_obj->asObjects[j].usRecordOffset);

				while (record->ucRecordType > 0 &&
				       record->ucRecordType <= ATOM_MAX_OBJECT_RECORD_NUMBER) {
				    //printf("record type %d\n", record->ucRecordType);

				    switch(record->ucRecordType) {
				    case ATOM_I2C_RECORD_TYPE:
					{
					    ATOM_I2C_RECORD *i2c_record = (ATOM_I2C_RECORD *)record;
					    printf("  ATOM_I2C_RECORD_TYPE\n");
					    printf("    i2c mux:%d engine:%d hw_cap:%d\n",
						   i2c_record->sucI2cId.bfI2C_LineMux,
						   i2c_record->sucI2cId.bfHW_EngineID,
						   i2c_record->sucI2cId.bfHW_Capable);
					    radeon_lookup_gpio(ctx, i2c_record->sucI2cId.bfI2C_LineMux);
					}
					break;
				    case ATOM_ROUTER_DDC_PATH_SELECT_RECORD_TYPE:
					{
					    ATOM_ROUTER_DDC_PATH_SELECT_RECORD *ddc_path =
						(ATOM_ROUTER_DDC_PATH_SELECT_RECORD *)record;
					    printf("  ATOM_ROUTER_DDC_PATH_SELECT_RECORD_TYPE\n");
					    printf("    mux type: %d, mux cntl pin: %d, mux state: %d\n",
						   ddc_path->ucMuxType,
						   ddc_path->ucMuxControlPin,
						   ddc_path->ucMuxState[0]);
					}
					break;
				    case ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD_TYPE:
					{
					    ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD *clk_data_path =
						(ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD *)record;
					    printf("  ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD_TYPE\n");
					    printf("    mux type: %d, mux cntl pin: %d, mux state: %d\n",
						   clk_data_path->ucMuxType,
						   clk_data_path->ucMuxControlPin,
						   clk_data_path->ucMuxState[0]);
					}
					break;
				    case ATOM_OBJECT_GPIO_CNTL_RECORD_TYPE:
					printf("  ATOM_OBJECT_GPIO_CNTL_RECORD_TYPE\n");
					break;
				    }
				    record = (ATOM_COMMON_RECORD_HEADER *)((char *)record + record->ucRecordSize);
				}
				break;
			    }
			}
		    }
		}
	    }
	    path_size += path->usSize;
	}
}

void radeon_dump_bios_connector_table(struct atom_context *ctx)
{


}

void radeon_execute_aux_channel_table(struct atom_context *ctx)
{
	int index = GetIndexIntoMasterTable(COMMAND, ProcessAuxChannelTransaction);
	PROCESS_AUX_CHANNEL_TRANSACTION_PARAMETERS params;

	memset(&params, 0, sizeof(params));

	params.lpAuxRequest = 0;
	params.lpDataOut = 0x100;
	params.ucChannelID = 0;
	
	atom_execute_table(ctx, index, (uint32_t *)&params);

}

void radeon_execute_hw_i2c_table(struct atom_context *ctx)
{
	int index = GetIndexIntoMasterTable(COMMAND, ReadEDIDFromHWAssistedI2C);
	READ_EDID_FROM_HW_I2C_DATA_PS_ALLOCATION edid_data;
	int prescale;
	int i2c_clock = 50;
	int engine_clk = 0; //info->sclk * 100;

//	if (info->ChipFamily == CHIP_FAMILY_R520)
		prescale = (127 << 8) + (engine_clk * 10) / (4 * 127 * i2c_clock);
//	else if (info->ChipFamily < CHIP_FAMILY_R600)
		prescale = (((engine_clk * 10)/(4 * 128 * 100) + 1) << 8) + 128;
//	else
//		prescale = (info->pll.reference_freq * 10) / i2c_clock;

	edid_data.usPrescale = prescale;
	edid_data.usVRAMAddress = 0;
	edid_data.ucSlaveAddr = 0xa0;
	//edid_data.ucLineNumber = radeon_output->ddc_i2c.hw_line;	

	atom_execute_table(ctx, index, (uint32_t *)&edid_data);


}

static char *thermal_controller_names[] = {
    "NONE",
    "LM63",
    "ADM1032",
    "ADM1030",
    "MUA6649",
    "LM64",
    "F75375",
    "ASC7512",
};

static char *pp_lib_thermal_controller_names[] = {
    "NONE",
    "LM63",
    "ADM1032",
    "ADM1030",
    "MUA6649",
    "LM64",
    "F75375",
    "RV6xx",
    "RV770",
    "ADT7473",
};

static void radeon_print_pp_misc(uint32_t misc)
{
    if (misc & ATOM_PM_MISCINFO_SPLIT_CLOCK)
	printf("\t\tSPLIT_CLOCK\n");
    if (misc & ATOM_PM_MISCINFO_USING_MCLK_SRC)
	printf("\t\tUSING_MCLK_SRC\n");
    if (misc & ATOM_PM_MISCINFO_USING_SCLK_SRC)
	printf("\t\tUSING_SCLK_SRC\n");
    if (misc & ATOM_PM_MISCINFO_VOLTAGE_DROP_SUPPORT)
	printf("\t\tVOLTAGE_DROP_SUPPORT\n");
    if (misc & ATOM_PM_MISCINFO_VOLTAGE_DROP_ACTIVE_HIGH)
	printf("\t\tVOLTAGE_DROP_ACTIVE_HIGH\n");
    if (misc & ATOM_PM_MISCINFO_LOAD_PERFORMANCE_EN)
	printf("\t\tLOAD_PERFORMANCE_EN\n");
    if (misc & ATOM_PM_MISCINFO_ENGINE_CLOCK_CONTRL_EN)
	printf("\t\tENGINE_CLOCK_CONTRL_EN\n");
    if (misc & ATOM_PM_MISCINFO_MEMORY_CLOCK_CONTRL_EN)
	printf("\t\tMEMORY_CLOCK_CONTRL_EN\n");
    if (misc & ATOM_PM_MISCINFO_PROGRAM_VOLTAGE)
	printf("\t\tPROGRAM_VOLTAGE\n");
    if (misc & ATOM_PM_MISCINFO_ASIC_REDUCED_SPEED_SCLK_EN)
	printf("\t\tASIC_REDUCED_SPEED_SCLK_EN\n");
    if (misc & ATOM_PM_MISCINFO_ASIC_DYNAMIC_VOLTAGE_EN)
	printf("\t\tASIC_DYNAMIC_VOLTAGE_EN\n");
    if (misc & ATOM_PM_MISCINFO_ASIC_SLEEP_MODE_EN)
	printf("\t\tASIC_SLEEP_MODE_EN\n");
    if (misc & ATOM_PM_MISCINFO_LOAD_BALANCE_EN)
	printf("\t\tLOAD_BALANCE_EN\n");
    if (misc & ATOM_PM_MISCINFO_DEFAULT_DC_STATE_ENTRY_TRUE)
	printf("\t\tDEFAULT_DC_STATE_ENTRY_TRUE\n");
    if (misc & ATOM_PM_MISCINFO_DEFAULT_LOW_DC_STATE_ENTRY_TRUE)
	printf("\t\tDEFAULT_LOW_DC_STATE_ENTRY_TRUE\n");
    if (misc & ATOM_PM_MISCINFO_LOW_LCD_REFRESH_RATE)
	printf("\t\tLOW_LCD_REFRESH_RATE\n");
    if (misc & ATOM_PM_MISCINFO_DRIVER_DEFAULT_MODE)
	printf("\t\tDRIVER_DEFAULT_MODE\n");
    if (misc & ATOM_PM_MISCINFO_OVER_CLOCK_MODE)
	printf("\t\tOVER_CLOCK_MODE\n");
    if (misc & ATOM_PM_MISCINFO_OVER_DRIVE_MODE)
	printf("\t\tOVER_DRIVE_MODE\n");
    if (misc & ATOM_PM_MISCINFO_POWER_SAVING_MODE)
	printf("\t\tPOWER_SAVING_MODE\n");
    if (misc & ATOM_PM_MISCINFO_THERMAL_DIODE_MODE)
	printf("\t\tTHERMAL_DIODE_MODE\n");
    if (misc & ATOM_PM_MISCINFO_FRAME_MODULATION_MASK)
	printf("\t\tFM: 0x%x\n",
	       (misc & ATOM_PM_MISCINFO_FRAME_MODULATION_MASK) >> ATOM_PM_MISCINFO_FRAME_MODULATION_SHIFT);
    if (misc & ATOM_PM_MISCINFO_DYN_CLK_3D_IDLE)
	printf("\t\tDYN_CLK_3D_IDLE\n");
    if (misc & ATOM_PM_MISCINFO_DYNAMIC_CLOCK_DIVIDER_BY_2)
	printf("\t\tDYNAMIC_CLOCK_DIVIDER_BY_2\n");
    if (misc & ATOM_PM_MISCINFO_DYNAMIC_CLOCK_DIVIDER_BY_4)
	printf("\t\tDYNAMIC_CLOCK_DIVIDER_BY_4\n");
    if (misc & ATOM_PM_MISCINFO_DYNAMIC_HDP_BLOCK_EN)
	printf("\t\tDYNAMIC_HDP_BLOCK_EN\n");
    if (misc & ATOM_PM_MISCINFO_DYNAMIC_MC_HOST_BLOCK_EN)
	printf("\t\tDYNAMIC_MC_HOST_BLOCK_EN\n");
    if (misc & ATOM_PM_MISCINFO_3D_ACCELERATION_EN)
	printf("\t\t3D_ACCELERATION_EN\n");
    switch ((misc & ATOM_PM_MISCINFO_POWERPLAY_SETTINGS_GROUP_MASK) >>
	    ATOM_PM_MISCINFO_POWERPLAY_SETTINGS_GROUP_SHIFT) {
    case 1:
	printf("\t\tOptimal Battery Life\n");
	break;
    case 2:
	printf("\t\tHigh Battery\n");
	break;
    case 3:
	printf("\t\tBalanced\n");
	break;
    case 4:
	printf("\t\tHigh Performance\n");
	break;
    case 5:
	printf("\t\tOptimal Performance (Default state with Default clocks)\n");
	break;
    default:
	break;
    }
    if (misc & ATOM_PM_MISCINFO_ENABLE_BACK_BIAS)
	printf("\t\tENABLE_BACK_BIAS\n");
}

static void radeon_print_pp_misc2(uint32_t misc)
{
    if (misc & ATOM_PM_MISCINFO2_SYSTEM_AC_LITE_MODE)
	printf("\t\tSYSTEM_AC_LITE_MODE\n");
    if (misc & ATOM_PM_MISCINFO2_MULTI_DISPLAY_SUPPORT)
	printf("\t\tMULTI_DISPLAY_SUPPORT\n");
    if (misc & ATOM_PM_MISCINFO2_DYNAMIC_BACK_BIAS_EN)
	printf("\t\tDYNAMIC_BACK_BIAS_EN\n");
    if (misc & ATOM_PM_MISCINFO2_FS3D_OVERDRIVE_INFO)
	printf("\t\tFS3D_OVERDRIVE_INFO\n");
    if (misc & ATOM_PM_MISCINFO2_FORCEDLOWPWR_MODE)
	printf("\t\tFORCEDLOWPWR_MODE\n");
    if (misc & ATOM_PM_MISCINFO2_VDDCI_DYNAMIC_VOLTAGE_EN)
	printf("\t\tVDDCI_DYNAMIC_VOLTAGE_EN\n");
    if (misc & ATOM_PM_MISCINFO2_VIDEO_PLAYBACK_CAPABLE)
	printf("\t\tVIDEO_PLAYBACK_CAPABLE\n");
    if (misc & ATOM_PM_MISCINFO2_NOT_VALID_ON_DC)
	printf("\t\tNOT_VALID_ON_DC\n");
    if (misc & ATOM_PM_MISCINFO2_STUTTER_MODE_EN)
	printf("\t\tSTUTTER_MODE_EN\n");
    if (misc & ATOM_PM_MISCINFO2_UVD_SUPPORT_MODE)
	printf("\t\tUVD_SUPPORT_MODE\n");

}

static void radeon_print_pp_lib_platform_caps(uint32_t caps)
{
    if (caps & ATOM_PP_PLATFORM_CAP_BACKBIAS)
	printf("\tBACKBIAS\n");
    if (caps & ATOM_PP_PLATFORM_CAP_POWERPLAY)
	printf("\tPOWERPLAY\n");
    if (caps & ATOM_PP_PLATFORM_CAP_SBIOSPOWERSOURCE)
	printf("\tSBIOSPOWERSOURCE\n");
    if (caps & ATOM_PP_PLATFORM_CAP_ASPM_L0s)
	printf("\tASPM_L0s\n");
    if (caps & ATOM_PP_PLATFORM_CAP_ASPM_L1)
	printf("\tASPM_L1\n");
    if (caps & ATOM_PP_PLATFORM_CAP_HARDWAREDC)
	printf("\tHARDWAREDC\n");
    if (caps & ATOM_PP_PLATFORM_CAP_GEMINIPRIMARY)
	printf("\tGEMINIPRIMARY\n");
    if (caps & ATOM_PP_PLATFORM_CAP_STEPVDDC)
	printf("\tSTEPVDDC\n");
    if (caps & ATOM_PP_PLATFORM_CAP_VOLTAGECONTROL)
	printf("\tVOLTAGECONTROL\n");
    if (caps & ATOM_PP_PLATFORM_CAP_SIDEPORTCONTROL)
	printf("\tSIDEPORTCONTROL\n");
    if (caps & ATOM_PP_PLATFORM_CAP_TURNOFFPLL_ASPML1)
	printf("\tTURNOFFPLL_ASPML1\n");
    if (caps & ATOM_PP_PLATFORM_CAP_HTLINKCONTROL)
	printf("\tHTLINKCONTROL\n");
}

static void radeon_print_pp_lib_clock_flags_discrete(uint32_t flags)
{
    if (flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2)
	printf("\t\tPCIEGEN2\n");
    if (flags & ATOM_PPLIB_R600_FLAGS_UVDSAFE)
	printf("\t\tUVDSAFE\n");
    if (flags & ATOM_PPLIB_R600_FLAGS_BACKBIASENABLE)
	printf("\t\tBACKBIASENABLE\n");
    if (flags & ATOM_PPLIB_R600_FLAGS_MEMORY_ODT_OFF)
	printf("\t\tMEMORY_ODT_OFF\n");
    if (flags & ATOM_PPLIB_R600_FLAGS_MEMORY_DLL_OFF)
	printf("\t\tMEMORY_DLL_OFF\n");

}

static void radeon_print_pp_lib_clock_flags_igp(uint32_t flags)
{
    printf("\t\t0x%08x\n", flags);

}

static void radeon_print_pp_lib_non_clock_flags(uint32_t flags)
{
    int link_speed = (flags & ATOM_PPLIB_PCIE_LINK_SPEED_MASK) >> ATOM_PPLIB_PCIE_LINK_SPEED_SHIFT;
    int link_width = (flags & ATOM_PPLIB_PCIE_LINK_WIDTH_MASK) >> ATOM_PPLIB_PCIE_LINK_WIDTH_SHIFT;
    if (flags & ATOM_PPLIB_SINGLE_DISPLAY_ONLY)
	printf("\t\tSINGLE_DISPLAY_ONLY\n");
    if (flags & ATOM_PPLIB_SUPPORTS_VIDEO_PLAYBACK)
	printf("\t\tSUPPORTS_VIDEO_PLAYBACK\n");
    if (flags & ATOM_PPLIB_SOFTWARE_DISABLE_LOADBALANCING)
	printf("\t\tDISABLE_LOADBALANCING\n");
    if (flags & ATOM_PPLIB_SOFTWARE_ENABLE_SLEEP_FOR_TIMESTAMPS)
	printf("\t\tENABLE_SLEEP_FOR_TIMESTAMPS\n");
    if (flags & ATOM_PPLIB_ENABLE_VARIBRIGHT)
	printf("\t\tENABLE_VARIBRIGHT\n");
    if (flags & ATOM_PPLIB_DISALLOW_ON_DC)
	printf("\t\tDISALLOW_ON_DC\n");
    printf("\tPCIE link speed: %s\n", link_speed ? "5.0 Gbps" : "2.5 Gbps");
    printf("\tPCIE lanes: %d\n", link_width + 1);
}

static void radeon_print_pp_lib_non_clock_class(uint16_t class)
{
    int class_type = (class & ATOM_PPLIB_CLASSIFICATION_UI_MASK) >> ATOM_PPLIB_CLASSIFICATION_UI_SHIFT;
    switch (class_type) {
    case ATOM_PPLIB_CLASSIFICATION_UI_NONE:
	printf("\t\tNONE\n");
	break;
    case ATOM_PPLIB_CLASSIFICATION_UI_BATTERY:
	printf("\t\tBATTERY\n");
	break;
    case ATOM_PPLIB_CLASSIFICATION_UI_BALANCED:
	printf("\t\tBALANCED\n");
	break;
    case ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE:
	printf("\t\tPERFORMANCE\n");
	break;
    default:
	break;
    }
    if (class & ATOM_PPLIB_CLASSIFICATION_BOOT)
	printf("\t\tBOOT\n");
    if (class & ATOM_PPLIB_CLASSIFICATION_THERMAL)
	printf("\t\tTHERMAL\n");
    if (class & ATOM_PPLIB_CLASSIFICATION_LIMITEDPOWERSOURCE)
	printf("\t\tLIMITEDPOWERSOURCE\n");
    if (class & ATOM_PPLIB_CLASSIFICATION_REST)
	printf("\t\tREST\n");
    if (class & ATOM_PPLIB_CLASSIFICATION_FORCED)
	printf("\t\tFORCED\n");
    if (class & ATOM_PPLIB_CLASSIFICATION_3DPERFORMANCE)
	printf("\t\t3DPERFORMANCE\n");
    if (class & ATOM_PPLIB_CLASSIFICATION_OVERDRIVETEMPLATE)
	printf("\t\tOVERDRIVETEMPLATE\n");
    if (class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE)
	printf("\t\tUVDSTATE\n");
    if (class & ATOM_PPLIB_CLASSIFICATION_3DLOW)
	printf("\t\t3DLOW\n");
    if (class & ATOM_PPLIB_CLASSIFICATION_ACPI)
	printf("\t\tACPI\n");
}

union pp_info {
    struct _ATOM_POWERPLAY_INFO info;
    struct _ATOM_POWERPLAY_INFO_V2 info_2;
    struct _ATOM_POWERPLAY_INFO_V3 info_3;
    struct _ATOM_PPLIB_POWERPLAYTABLE info_4;
};

/* asic specific */
union clock_info {
    struct _ATOM_PPLIB_R600_CLOCK_INFO r600;
    struct _ATOM_PPLIB_RS780_CLOCK_INFO rs780;
};

void radeon_dump_bios_pp_table(struct atom_context *ctx)
{
	uint16_t size, data_offset;
	uint8_t frev, crev;
	int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo);
	union pp_info *pp_info;
	union clock_info *clock_info;
	ATOM_PPLIB_NONCLOCK_INFO *non_clock_info;
	ATOM_PPLIB_STATE *power_state;
	int num_modes = 0, i, j;
	uint32_t sclk, mclk;

	atom_parse_data_header(ctx, index, &size, &frev, &crev, &data_offset);

	pp_info = (union pp_info *)(ctx->bios + data_offset);

	printf("\nPOWERPLAY\n");
	printf("pp v %d %d\n", frev, crev);

	if (frev < 4) {
	    printf("thermal controller: %s on 0x%02x at 0x%02x - int bitmap 0x%02x\n",
		   thermal_controller_names[pp_info->info.ucOverdriveThermalController],
		   pp_info->info.ucOverdriveI2cLine,
		   pp_info->info.ucOverdriveControllerAddress,
		   pp_info->info.ucOverdriveIntBitmap);
	    printf("%d power modes (lowest to highest)\n", pp_info->info.ucNumOfPowerModeEntries);
	    num_modes = pp_info->info.ucNumOfPowerModeEntries;
	    if (num_modes > ATOM_MAX_NUMBEROF_POWER_BLOCK)
		num_modes = ATOM_MAX_NUMBEROF_POWER_BLOCK;

	    for (i = 0; i < num_modes; i++) {
		printf("== power mode %d ==\n", i);
		switch (frev) {
		case 1:
		    printf("\tengine: %d, memory: %d\n",
			   pp_info->info.asPowerPlayInfo[i].usEngineClock,
			   pp_info->info.asPowerPlayInfo[i].usMemoryClock);
		    printf("\tpcie lanes: %d\n",
			   pp_info->info.asPowerPlayInfo[i].ucNumPciELanes);
		    printf("\ttemp: %d - %d\n",
			   pp_info->info.asPowerPlayInfo[i].ucMinTemperature,
			   pp_info->info.asPowerPlayInfo[i].ucMaxTemperature);
		    printf("\tpanel refresh: %d\n",
			   pp_info->info.asPowerPlayInfo[i].ucSelectedPanel_RefreshRate);
		    printf("\tvoltage gpio id: 0x%02x\n",
			   pp_info->info.asPowerPlayInfo[i].ucVoltageDropIndex);
		    printf("\tflags:\n");
		    radeon_print_pp_misc(pp_info->info.asPowerPlayInfo[i].ulMiscInfo);
		    break;
		case 2:
		    printf("\tengine: %d, memory: %d\n",
			   pp_info->info_2.asPowerPlayInfo[i].ulEngineClock,
			   pp_info->info_2.asPowerPlayInfo[i].ulMemoryClock);
		    printf("\tpcie lanes: %d\n",
			   pp_info->info_2.asPowerPlayInfo[i].ucNumPciELanes);
		    printf("\ttemp: %d - %d\n",
			   pp_info->info_2.asPowerPlayInfo[i].ucMinTemperature,
			   pp_info->info_2.asPowerPlayInfo[i].ucMaxTemperature);
		    printf("\tpanel refresh: %d\n",
			   pp_info->info_2.asPowerPlayInfo[i].ucSelectedPanel_RefreshRate);
		    printf("\tvoltage gpio id: 0x%02x\n",
			   pp_info->info_2.asPowerPlayInfo[i].ucVoltageDropIndex);
		    printf("\tflags:\n");
		    radeon_print_pp_misc(pp_info->info_2.asPowerPlayInfo[i].ulMiscInfo);
		    radeon_print_pp_misc2(pp_info->info_2.asPowerPlayInfo[i].ulMiscInfo2);
		    break;
		case 3:
		    printf("\tengine: %d, memory: %d\n",
			   pp_info->info_3.asPowerPlayInfo[i].ulEngineClock,
			   pp_info->info_3.asPowerPlayInfo[i].ulMemoryClock);
		    printf("\tpcie lanes: %d\n",
			   pp_info->info_3.asPowerPlayInfo[i].ucNumPciELanes);
		    printf("\ttemp: %d - %d\n",
			   pp_info->info_3.asPowerPlayInfo[i].ucMinTemperature,
			   pp_info->info_3.asPowerPlayInfo[i].ucMaxTemperature);
		    printf("\tpanel refresh: %d\n",
			   pp_info->info_3.asPowerPlayInfo[i].ucSelectedPanel_RefreshRate);
		    printf("\tVDDC index: 0x%02x\n",
			   pp_info->info_3.asPowerPlayInfo[i].ucVoltageDropIndex);
		    printf("\tVDDCI index: 0x%02x\n",
			   pp_info->info_3.asPowerPlayInfo[i].ucVDDCI_VoltageDropIndex);
		    printf("\tflags:\n");
		    radeon_print_pp_misc(pp_info->info_3.asPowerPlayInfo[i].ulMiscInfo);
		    radeon_print_pp_misc2(pp_info->info_3.asPowerPlayInfo[i].ulMiscInfo2);
		    break;
		default:
		    break;
		}
	    }
	} else {
	    printf("thermal controller: %s on 0x%02x at 0x%02x\n",
		   pp_lib_thermal_controller_names[pp_info->info_4.sThermalController.ucType],
		   pp_info->info_4.sThermalController.ucI2cLine,
		   pp_info->info_4.sThermalController.ucI2cAddress);
	    if (pp_info->info_4.sThermalController.ucType != 0) {
		if (pp_info->info_4.sThermalController.ucFanParameters)
		    printf("\tno fan connected\n");
		else {
		    printf("\tfan connected\n");
		    printf("\ttach pulse per rev: %d\n",
			   pp_info->info_4.sThermalController.ucFanParameters &
			   ATOM_PP_FANPARAMETERS_TACHOMETER_PULSES_PER_REVOLUTION_MASK);
		    printf("\trpm: %d - %d\n", pp_info->info_4.sThermalController.ucFanMinRPM,
			   pp_info->info_4.sThermalController.ucFanMaxRPM);
		}
	    }
	    printf("back bias time: %d usec\n",pp_info->info_4.usBackbiasTime);
	    printf("voltage time: %d usec\n",pp_info->info_4.usVoltageTime);
	    printf("platform caps:\n");
	    radeon_print_pp_lib_platform_caps(pp_info->info_4.ulPlatformCaps);
	    printf("%d power modes\n", pp_info->info_4.ucNumStates);
	    for (i = 0; i < pp_info->info_4.ucNumStates; i++) {
		printf("== power mode %d ==\n", i);
		power_state = (ATOM_PPLIB_STATE *)(ctx->bios + data_offset +
						   pp_info->info_4.usStateArrayOffset +
						   i * pp_info->info_4.ucStateEntrySize);
		non_clock_info = (ATOM_PPLIB_NONCLOCK_INFO *)(ctx->bios + data_offset +
							      pp_info->info_4.usNonClockInfoArrayOffset +
							      power_state->ucNonClockStateIndex *
							      pp_info->info_4.ucNonClockSize);
		printf("  non-clock info\n");
		printf("\ttemp: %d - %d\n", non_clock_info->ucMinTemperature,
		       non_clock_info->ucMaxTemperature);
		printf("\trequired power: %d\n", non_clock_info->ucRequiredPower);
		printf("\tflags: \n");
		radeon_print_pp_lib_non_clock_flags(non_clock_info->ulCapsAndSettings);
		printf("\tclass info: \n");
		radeon_print_pp_lib_non_clock_class(non_clock_info->usClassification);

		for (j = 0; j < (pp_info->info_4.ucStateEntrySize - 1); j++) {
		    clock_info = (union clock_info *)(ctx->bios + data_offset +
						      pp_info->info_4.usClockInfoArrayOffset +
						      power_state->ucClockStateIndices[j] *
						      pp_info->info_4.ucClockInfoSize);

		    if (1) {
			//discrete
			printf("  clock info %d\n", j);
			sclk = clock_info->r600.usEngineClockLow;
			sclk |= clock_info->r600.ucEngineClockHigh << 16;
			mclk = clock_info->r600.usMemoryClockLow;
			mclk |= clock_info->r600.ucMemoryClockHigh << 16;
			printf("\tengine %d, memory %d\n", sclk, mclk);
			printf("\tVDDC: %d\n", clock_info->r600.usVDDC);
			printf("\tflags: \n");
			radeon_print_pp_lib_clock_flags_discrete(clock_info->r600.ulFlags);
		    } else {
			// igp
			sclk = clock_info->rs780.usLowEngineClockLow;
			sclk |= clock_info->rs780.ucLowEngineClockHigh << 16;
			mclk = clock_info->rs780.usMemoryClockLow;
			mclk |= clock_info->rs780.ucMemoryClockHigh << 16;
			printf("\tengine %d\n", sclk);
			printf("\tsideport memory %d\n", mclk);
			printf("\tHT links %d - %d\n", clock_info->rs780.ucMinHTLinkWidth,
			       clock_info->rs780.ucMaxHTLinkWidth);
			if (clock_info->rs780.usHTLinkFreq & ATOM_PPLIB_RS780_HTLINKFREQ_HIGH)
			    printf("\tHT freq HIGH\n");
			else if (clock_info->rs780.usHTLinkFreq & ATOM_PPLIB_RS780_HTLINKFREQ_LOW)
			    printf("\tHT freq LOW\n");
			else
			    printf("\tHT freq NONE\n");
			printf("\tVDDC: %d\n", clock_info->rs780.usVDDC);
			printf("\tflags: \n");
			radeon_print_pp_lib_clock_flags_igp(clock_info->rs780.ulFlags);
		    }
		}
	    }
	}
}

void dump_vram_table(struct atom_context *ctx)
{
	int index = GetIndexIntoMasterTable(DATA, VRAM_UsageByFirmware);
	uint16_t data_offset;
	uint8_t crev, frev;
	uint16_t size;
	ATOM_VRAM_USAGE_BY_FIRMWARE *vram_usage;
	uint16_t fw_bytes;
	uint32_t start_offset;
	
	atom_parse_data_header(ctx, index, &size, &frev, &crev, &data_offset);

	vram_usage = (struct _ATOM_VRAM_USAGE_BY_FIRMWARE *)(ctx->bios + data_offset);
	start_offset = vram_usage->asFirmwareVramReserveInfo[0].ulStartAddrUsedByFirmware;
	fw_bytes = vram_usage->asFirmwareVramReserveInfo[0].usFirmwareUseInKb * 1024;
	printf("vram_usage 0x%08x %dkb\n", 
	       vram_usage->asFirmwareVramReserveInfo[0].ulStartAddrUsedByFirmware,
	       vram_usage->asFirmwareVramReserveInfo[0].usFirmwareUseInKb);

	if (!fb_mem)
		printf("No card to dump VRAM from\n");
	else {
		void *backing;
		int fd;

		backing = kzalloc(fw_bytes, GFP_KERNEL);

		start_offset &= 0xfffffff;
		memcpy(backing, fb_mem + start_offset, fw_bytes);

		fd = open("/tmp/vram_usage", O_RDWR | O_CREAT, 0666);

		write(fd, backing, fw_bytes);

		close(fd);

		printf("EDID offsets crt1:0x%x lcd1:0x%x dfp1:0x%x crt2:0x%x df5:0x%x\n", ATOM_CRT1_EDID_ADDR,
                       ATOM_LCD1_EDID_ADDR, ATOM_DFP1_EDID_ADDR,
                       ATOM_CRT2_EDID_ADDR, ATOM_DFP5_EDID_ADDR);
                printf("DP train 0x%x\n", ATOM_DP_TRAINING_TBL_ADDR);

	}

}

static void cail_reg_write(struct card_info *card_info, uint32_t reg, uint32_t val)
{
	printf("reg write %d, %d\n", reg, val);
//	*(unsigned int * volatile)(ctrl_mem + reg) = val;  
}

static uint32_t cail_reg_read(struct card_info *card_info, uint32_t reg)
{
	printf("reg read %d\n", reg);
	return 0; //*(unsigned int * volatile)(ctrl_mem + reg);
}

static void fake_reg_write(struct card_info *card_info, uint32_t reg, uint32_t val)
{
	printf("reg write %d, %d\n", reg, val);
//	*(unsigned int * volatile)(ctrl_mem + reg) = val;  
}

static uint32_t fake_reg_read(struct card_info *card_info, uint32_t reg)
{
	printf("reg read %d\n", reg);
	return 0; //*(unsigned int * volatile)(ctrl_mem + reg);
}

struct atom_context *atom_init(void *bios)
{
  struct atom_context *ctx;

  memset(&my_card_info, 0, sizeof(struct card_info));

  if (ctrl_mem) {
	  my_card_info.reg_read = cail_reg_read;
	  my_card_info.reg_write = cail_reg_write;
  } else {
	  my_card_info.reg_read = fake_reg_read;
	  my_card_info.reg_write = fake_reg_write;
  }

  ctx = atom_parse(&my_card_info, bios);

  //atom_asic_init(ctx);
  return ctx;
}

void atom_dump(struct atom_context *ctx)
{

  radeon_dump_bios_connector_table(ctx);

  radeon_dump_bios_object_table(ctx);

  radeon_dump_bios_pp_table(ctx);

  //dump_vram_table(ctx);

  //radeon_execute_aux_channel_table(ctx);

}
