diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..355f568 --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +*.o +*~ +*.d +*core +*.depend +*.*.cmd +*.mod +*.mod.c +*.tmp_versions +*.order +*.symvers +build + +aura/usb +aura/err.h +*dump +drivers/Makefile diff --git a/Makefile b/Makefile index ecdeeff..dd66372 100644 --- a/Makefile +++ b/Makefile @@ -6,8 +6,11 @@ SRCS = \ asic/asic-polaris.c \ asic/asic-vega.c \ asic/asic-navi.c \ + atom/atom.c \ aura-gpu-reg.c \ aura-gpu-i2c.c \ + aura-gpu-bios.c \ + aura-gpu-hw.c \ main.c KERNELDIR = /lib/modules/$(shell uname -r)/build diff --git a/README.md b/README.md index 434cf24..cbad6ce 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ sudo modprobe i2c-dev ``` List all i2c devices with: ``` -sudo i2detect -l +sudo i2cdetect -l ``` It will output something like ``` @@ -59,7 +59,7 @@ i2c-5 i2c dmdc I2C adapter ``` Here we can see this module "AURA GPU adapter" is detected as device number 11. We can now scan that device with: ``` -sudo i2c-detect -y 11 +sudo i2cdetect -y 11 ``` It should output all clients available on that bus: ``` diff --git a/atom/atom-bits.h b/atom/atom-bits.h new file mode 100644 index 0000000..33ccae7 --- /dev/null +++ b/atom/atom-bits.h @@ -0,0 +1,48 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Author: Stanislaw Skowronek + */ + +#ifndef ATOM_BITS_H +#define ATOM_BITS_H + +static inline uint8_t get_u8(void const *bios, int ptr) +{ + return ((unsigned char *)bios)[ptr]; +} +#define U8(ptr) get_u8(ctx->ctx->bios,(ptr)) +#define CU8(ptr) get_u8(ctx->bios,(ptr)) +static inline uint16_t get_u16(void const *bios, int ptr) +{ + return get_u8(bios,ptr)|(((uint16_t)get_u8(bios,ptr+1))<<8); +} +#define U16(ptr) get_u16(ctx->ctx->bios,(ptr)) +#define CU16(ptr) get_u16(ctx->bios,(ptr)) +static inline uint32_t get_u32(void const *bios, int ptr) +{ + return get_u16(bios,ptr)|(((uint32_t)get_u16(bios,ptr+2))<<16); +} +#define U32(ptr) get_u32(ctx->ctx->bios,(ptr)) +#define CU32(ptr) get_u32(ctx->bios,(ptr)) +#define CSTR(ptr) (((char *)(ctx->bios))+(ptr)) + +#endif diff --git a/atom/atom-names.h b/atom/atom-names.h new file mode 100644 index 0000000..2cdc170 --- /dev/null +++ b/atom/atom-names.h @@ -0,0 +1,100 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Author: Stanislaw Skowronek + */ + +#ifndef ATOM_NAMES_H +#define ATOM_NAMES_H + +#include "atom.h" + +#ifdef ATOM_DEBUG + +#define ATOM_OP_NAMES_CNT 123 +static char *atom_op_names[ATOM_OP_NAMES_CNT]={ +"RESERVED", "MOVE_REG", "MOVE_PS", "MOVE_WS", "MOVE_FB", "MOVE_PLL", +"MOVE_MC", "AND_REG", "AND_PS", "AND_WS", "AND_FB", "AND_PLL", "AND_MC", +"OR_REG", "OR_PS", "OR_WS", "OR_FB", "OR_PLL", "OR_MC", "SHIFT_LEFT_REG", +"SHIFT_LEFT_PS", "SHIFT_LEFT_WS", "SHIFT_LEFT_FB", "SHIFT_LEFT_PLL", +"SHIFT_LEFT_MC", "SHIFT_RIGHT_REG", "SHIFT_RIGHT_PS", "SHIFT_RIGHT_WS", +"SHIFT_RIGHT_FB", "SHIFT_RIGHT_PLL", "SHIFT_RIGHT_MC", "MUL_REG", +"MUL_PS", "MUL_WS", "MUL_FB", "MUL_PLL", "MUL_MC", "DIV_REG", "DIV_PS", +"DIV_WS", "DIV_FB", "DIV_PLL", "DIV_MC", "ADD_REG", "ADD_PS", "ADD_WS", +"ADD_FB", "ADD_PLL", "ADD_MC", "SUB_REG", "SUB_PS", "SUB_WS", "SUB_FB", +"SUB_PLL", "SUB_MC", "SET_ATI_PORT", "SET_PCI_PORT", "SET_SYS_IO_PORT", +"SET_REG_BLOCK", "SET_FB_BASE", "COMPARE_REG", "COMPARE_PS", +"COMPARE_WS", "COMPARE_FB", "COMPARE_PLL", "COMPARE_MC", "SWITCH", +"JUMP", "JUMP_EQUAL", "JUMP_BELOW", "JUMP_ABOVE", "JUMP_BELOW_OR_EQUAL", +"JUMP_ABOVE_OR_EQUAL", "JUMP_NOT_EQUAL", "TEST_REG", "TEST_PS", "TEST_WS", +"TEST_FB", "TEST_PLL", "TEST_MC", "DELAY_MILLISEC", "DELAY_MICROSEC", +"CALL_TABLE", "REPEAT", "CLEAR_REG", "CLEAR_PS", "CLEAR_WS", "CLEAR_FB", +"CLEAR_PLL", "CLEAR_MC", "NOP", "EOT", "MASK_REG", "MASK_PS", "MASK_WS", +"MASK_FB", "MASK_PLL", "MASK_MC", "POST_CARD", "BEEP", "SAVE_REG", +"RESTORE_REG", "SET_DATA_BLOCK", "XOR_REG", "XOR_PS", "XOR_WS", "XOR_FB", +"XOR_PLL", "XOR_MC", "SHL_REG", "SHL_PS", "SHL_WS", "SHL_FB", "SHL_PLL", +"SHL_MC", "SHR_REG", "SHR_PS", "SHR_WS", "SHR_FB", "SHR_PLL", "SHR_MC", +"DEBUG", "CTB_DS", +}; + +#define ATOM_TABLE_NAMES_CNT 74 +static char *atom_table_names[ATOM_TABLE_NAMES_CNT]={ +"ASIC_Init", "GetDisplaySurfaceSize", "ASIC_RegistersInit", +"VRAM_BlockVenderDetection", "SetClocksRatio", "MemoryControllerInit", +"GPIO_PinInit", "MemoryParamAdjust", "DVOEncoderControl", +"GPIOPinControl", "SetEngineClock", "SetMemoryClock", "SetPixelClock", +"DynamicClockGating", "ResetMemoryDLL", "ResetMemoryDevice", +"MemoryPLLInit", "EnableMemorySelfRefresh", "AdjustMemoryController", +"EnableASIC_StaticPwrMgt", "ASIC_StaticPwrMgtStatusChange", +"DAC_LoadDetection", "TMDS2EncoderControl", "LCD1OutputControl", +"DAC1EncoderControl", "DAC2EncoderControl", "DVOOutputControl", +"CV1OutputControl", "SetCRTC_DPM_State", "TVEncoderControl", +"TMDS1EncoderControl", "LVDSEncoderControl", "TV1OutputControl", +"EnableScaler", "BlankCRTC", "EnableCRTC", "GetPixelClock", +"EnableVGA_Render", "EnableVGA_Access", "SetCRTC_Timing", +"SetCRTC_OverScan", "SetCRTC_Replication", "SelectCRTC_Source", +"EnableGraphSurfaces", "UpdateCRTC_DoubleBufferRegisters", +"LUT_AutoFill", "EnableHW_IconCursor", "GetMemoryClock", +"GetEngineClock", "SetCRTC_UsingDTDTiming", "TVBootUpStdPinDetection", +"DFP2OutputControl", "VRAM_BlockDetectionByStrap", "MemoryCleanUp", +"ReadEDIDFromHWAssistedI2C", "WriteOneByteToHWAssistedI2C", +"ReadHWAssistedI2CStatus", "SpeedFanControl", "PowerConnectorDetection", +"MC_Synchronization", "ComputeMemoryEnginePLL", "MemoryRefreshConversion", +"VRAM_GetCurrentInfoBlock", "DynamicMemorySettings", "MemoryTraining", +"EnableLVDS_SS", "DFP1OutputControl", "SetVoltage", "CRT1OutputControl", +"CRT2OutputControl", "SetupHWAssistedI2CStatus", "ClockSource", +"MemoryDeviceInit", "EnableYUV", +}; + +#define ATOM_IO_NAMES_CNT 5 +static char *atom_io_names[ATOM_IO_NAMES_CNT]={ +"MM", "PLL", "MC", "PCIE", "PCIE PORT", +}; + +#else + +#define ATOM_OP_NAMES_CNT 0 +#define ATOM_TABLE_NAMES_CNT 0 +#define ATOM_IO_NAMES_CNT 0 + +#endif + +#endif diff --git a/atom/atom.c b/atom/atom.c new file mode 100644 index 0000000..7f1bfb2 --- /dev/null +++ b/atom/atom.c @@ -0,0 +1,1377 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Author: Stanislaw Skowronek + */ + +#include +#include +#include +#include +#include + +#define ATOM_DEBUG 1 + +#include "atom.h" +#include "atom-names.h" +#include "atom-bits.h" + +#define ATOM_COND_ABOVE 0 +#define ATOM_COND_ABOVEOREQUAL 1 +#define ATOM_COND_ALWAYS 2 +#define ATOM_COND_BELOW 3 +#define ATOM_COND_BELOWOREQUAL 4 +#define ATOM_COND_EQUAL 5 +#define ATOM_COND_NOTEQUAL 6 + +#define ATOM_PORT_ATI 0 +#define ATOM_PORT_PCI 1 +#define ATOM_PORT_SYSIO 2 + +#define ATOM_UNIT_MICROSEC 0 +#define ATOM_UNIT_MILLISEC 1 + +#define PLL_INDEX 2 +#define PLL_DATA 3 + +typedef struct { + struct atom_context *ctx; + uint32_t *ps, *ws; + int ps_shift; + uint16_t start; + unsigned last_jump; + unsigned long last_jump_jiffies; + bool abort; +} atom_exec_context; + +int atom_debug = 0; +static int atom_execute_table_locked(struct atom_context *ctx, int index, uint32_t * params); +int atom_execute_table(struct atom_context *ctx, int index, uint32_t * params); + +static uint32_t atom_arg_mask[8] = { 0xFFFFFFFF, 0xFFFF, 0xFFFF00, 0xFFFF0000, 0xFF, 0xFF00, 0xFF0000, 0xFF000000 }; +static int atom_arg_shift[8] = { 0, 0, 8, 16, 0, 8, 16, 24 }; +static int atom_dst_to_src[8][4] = { + /* translate destination alignment field to the source alignment encoding */ + {0, 0, 0, 0}, + {1, 2, 3, 0}, + {1, 2, 3, 0}, + {1, 2, 3, 0}, + {4, 5, 6, 7}, + {4, 5, 6, 7}, + {4, 5, 6, 7}, + {4, 5, 6, 7}, +}; +static int atom_def_dst[8] = { 0, 0, 1, 2, 0, 1, 2, 3 }; +static int debug_depth = 0; + +#ifdef ATOM_DEBUG +static void debug_print_spaces(int n) +{ + while (n--) + printk(" "); +} + +#define ADEBUG(...) do if (atom_debug) { printk(KERN_DEBUG __VA_ARGS__); } while (0) +#define SDEBUG(...) do if (atom_debug) { printk(KERN_DEBUG); debug_print_spaces(debug_depth); printk(__VA_ARGS__); } while (0) +#define _DEBUG(...) do { printk(KERN_DEBUG __VA_ARGS__); } while (0) +#else +#define ADEBUG(...) do { } while (0) +#define SDEBUG(...) do { } while (0) +#define _DEBUG(...) do { } while (0) +#endif + +static uint32_t atom_iio_execute(struct atom_context *ctx, int base, uint32_t index, uint32_t data) +{ + uint32_t temp = 0xCDCDCDCD; + + while (1) + switch (CU8(base)) { + case ATOM_IIO_NOP: + base++; + break; + case ATOM_IIO_READ: + temp = ctx->card->ioreg_read(ctx->card, CU16(base + 1)); + base += 3; + break; + case ATOM_IIO_WRITE: + ctx->card->ioreg_write(ctx->card, CU16(base + 1), temp); + base += 3; + break; + case ATOM_IIO_CLEAR: + temp &= ~((0xFFFFFFFF >> (32 - CU8(base + 1))) << CU8(base + 2)); + base += 3; + break; + case ATOM_IIO_SET: + temp |= (0xFFFFFFFF >> (32 - CU8(base + 1))) << CU8(base + 2); + base += 3; + break; + case ATOM_IIO_MOVE_INDEX: + temp &= ~((0xFFFFFFFF >> (32 - CU8(base + 1))) << CU8(base + 3)); + temp |= ((index >> CU8(base + 2)) & (0xFFFFFFFF >> (32 - CU8(base + 1)))) << CU8(base + 3); + base += 4; + break; + case ATOM_IIO_MOVE_DATA: + temp &= ~((0xFFFFFFFF >> (32 - CU8(base + 1))) << CU8(base + 3)); + temp |= ((data >> CU8(base + 2)) & (0xFFFFFFFF >> (32 - CU8(base + 1)))) << CU8(base + 3); + base += 4; + break; + case ATOM_IIO_MOVE_ATTR: + temp &= ~((0xFFFFFFFF >> (32 - CU8(base + 1))) << CU8(base + 3)); + temp |= ((ctx-> io_attr >> CU8(base + 2)) & (0xFFFFFFFF >> (32 - CU8(base + 1)))) << CU8(base + 3); + base += 4; + break; + case ATOM_IIO_END: + return temp; + default: + pr_info("Unknown IIO opcode\n"); + return 0; + } +} + +static uint32_t atom_get_src_int(atom_exec_context *ctx, uint8_t attr, int *ptr, uint32_t *saved, int print) +{ + uint32_t idx, val = 0xCDCDCDCD, align, arg; + struct atom_context *gctx = ctx->ctx; + arg = attr & 7; + align = (attr >> 3) & 7; + switch (arg) { + case ATOM_ARG_REG: + idx = U16(*ptr); + (*ptr) += 2; + if (print) + ADEBUG("REG[0x%04X]", idx); + idx += gctx->reg_block; + switch (gctx->io_mode) { + case ATOM_IO_MM: + val = gctx->card->reg_read(gctx->card, idx); + break; + case ATOM_IO_PCI: + pr_info("PCI registers are not implemented\n"); + return 0; + case ATOM_IO_SYSIO: + pr_info("SYSIO registers are not implemented\n"); + return 0; + default: + if (!(gctx->io_mode & 0x80)) { + pr_info("Bad IO mode\n"); + return 0; + } + if (!gctx->iio[gctx->io_mode & 0x7F]) { + pr_info("Undefined indirect IO read method %d\n", + gctx->io_mode & 0x7F); + return 0; + } + val = + atom_iio_execute(gctx, + gctx->iio[gctx->io_mode & 0x7F], + idx, 0); + } + break; + case ATOM_ARG_PS: + idx = U8(*ptr); + (*ptr)++; + /* get_unaligned_le32 avoids unaligned accesses from atombios + * tables, noticed on a DEC Alpha. */ + val = get_unaligned_le32((u32 *)&ctx->ps[idx]); + if (print) + ADEBUG("PS[0x%02X,0x%04X]", idx, val); + break; + case ATOM_ARG_WS: + idx = U8(*ptr); + (*ptr)++; + if (print) + ADEBUG("WS[0x%02X]", idx); + switch (idx) { + case ATOM_WS_QUOTIENT: + val = gctx->divmul[0]; + break; + case ATOM_WS_REMAINDER: + val = gctx->divmul[1]; + break; + case ATOM_WS_DATAPTR: + val = gctx->data_block; + break; + case ATOM_WS_SHIFT: + val = gctx->shift; + break; + case ATOM_WS_OR_MASK: + val = 1 << gctx->shift; + break; + case ATOM_WS_AND_MASK: + val = ~(1 << gctx->shift); + break; + case ATOM_WS_FB_WINDOW: + val = gctx->fb_base; + break; + case ATOM_WS_ATTRIBUTES: + val = gctx->io_attr; + break; + case ATOM_WS_REGPTR: + val = gctx->reg_block; + break; + default: + val = ctx->ws[idx]; + } + break; + case ATOM_ARG_ID: + idx = U16(*ptr); + (*ptr) += 2; + if (print) { + if (gctx->data_block) + ADEBUG("ID[0x%04X+%04X]", idx, gctx->data_block); + else + ADEBUG("ID[0x%04X]", idx); + } + val = U32(idx + gctx->data_block); + break; + case ATOM_ARG_FB: + idx = U8(*ptr); + (*ptr)++; + if ((gctx->fb_base + (idx * 4)) > gctx->scratch_size_bytes) { + ADEBUG("ATOM: fb read beyond scratch region: %d vs. %d\n", + gctx->fb_base + (idx * 4), gctx->scratch_size_bytes); + val = 0; + } else + val = gctx->scratch[(gctx->fb_base / 4) + idx]; + if (print) + ADEBUG("FB[0x%02X]", idx); + break; + case ATOM_ARG_IMM: + switch (align) { + case ATOM_SRC_DWORD: + val = U32(*ptr); + (*ptr) += 4; + if (print) + ADEBUG("IMM 0x%08X\n", val); + return val; + case ATOM_SRC_WORD0: + case ATOM_SRC_WORD8: + case ATOM_SRC_WORD16: + val = U16(*ptr); + (*ptr) += 2; + if (print) + ADEBUG("IMM 0x%04X\n", val); + return val; + case ATOM_SRC_BYTE0: + case ATOM_SRC_BYTE8: + case ATOM_SRC_BYTE16: + case ATOM_SRC_BYTE24: + val = U8(*ptr); + (*ptr)++; + if (print) + ADEBUG("IMM 0x%02X\n", val); + return val; + } + return 0; + case ATOM_ARG_PLL: + idx = U8(*ptr); + (*ptr)++; + if (print) + ADEBUG("PLL[0x%02X]", idx); + val = gctx->card->pll_read(gctx->card, idx); + break; + case ATOM_ARG_MC: + idx = U8(*ptr); + (*ptr)++; + if (print) + ADEBUG("MC[0x%02X]", idx); + val = gctx->card->mc_read(gctx->card, idx); + break; + } + if (saved) + *saved = val; + val &= atom_arg_mask[align]; + val >>= atom_arg_shift[align]; + if (print) + switch (align) { + case ATOM_SRC_DWORD: + ADEBUG(".[31:0] -> 0x%08X\n", val); + break; + case ATOM_SRC_WORD0: + ADEBUG(".[15:0] -> 0x%04X\n", val); + break; + case ATOM_SRC_WORD8: + ADEBUG(".[23:8] -> 0x%04X\n", val); + break; + case ATOM_SRC_WORD16: + ADEBUG(".[31:16] -> 0x%04X\n", val); + break; + case ATOM_SRC_BYTE0: + ADEBUG(".[7:0] -> 0x%02X\n", val); + break; + case ATOM_SRC_BYTE8: + ADEBUG(".[15:8] -> 0x%02X\n", val); + break; + case ATOM_SRC_BYTE16: + ADEBUG(".[23:16] -> 0x%02X\n", val); + break; + case ATOM_SRC_BYTE24: + ADEBUG(".[31:24] -> 0x%02X\n", val); + break; + } + return val; +} + +static void atom_skip_src_int(atom_exec_context *ctx, uint8_t attr, int *ptr) +{ + uint32_t align = (attr >> 3) & 7, arg = attr & 7; + switch (arg) { + case ATOM_ARG_REG: + case ATOM_ARG_ID: + (*ptr) += 2; + break; + case ATOM_ARG_PLL: + case ATOM_ARG_MC: + case ATOM_ARG_PS: + case ATOM_ARG_WS: + case ATOM_ARG_FB: + (*ptr)++; + break; + case ATOM_ARG_IMM: + switch (align) { + case ATOM_SRC_DWORD: + (*ptr) += 4; + return; + case ATOM_SRC_WORD0: + case ATOM_SRC_WORD8: + case ATOM_SRC_WORD16: + (*ptr) += 2; + return; + case ATOM_SRC_BYTE0: + case ATOM_SRC_BYTE8: + case ATOM_SRC_BYTE16: + case ATOM_SRC_BYTE24: + (*ptr)++; + return; + } + return; + } +} + +static uint32_t atom_get_src(atom_exec_context *ctx, uint8_t attr, int *ptr) +{ + return atom_get_src_int(ctx, attr, ptr, NULL, 1); +} + +static uint32_t atom_get_src_direct(atom_exec_context *ctx, uint8_t align, int *ptr) +{ + uint32_t val = 0xCDCDCDCD; + + switch (align) { + case ATOM_SRC_DWORD: + val = U32(*ptr); + (*ptr) += 4; + break; + case ATOM_SRC_WORD0: + case ATOM_SRC_WORD8: + case ATOM_SRC_WORD16: + val = U16(*ptr); + (*ptr) += 2; + break; + case ATOM_SRC_BYTE0: + case ATOM_SRC_BYTE8: + case ATOM_SRC_BYTE16: + case ATOM_SRC_BYTE24: + val = U8(*ptr); + (*ptr)++; + break; + } + return val; +} + +static uint32_t atom_get_dst(atom_exec_context *ctx, int arg, uint8_t attr, int *ptr, uint32_t *saved, int print) +{ + return atom_get_src_int(ctx, arg | atom_dst_to_src[(attr >> 3) & 7][(attr >> 6) & 3] << 3, ptr, saved, print); +} + +static void atom_skip_dst(atom_exec_context *ctx, int arg, uint8_t attr, int *ptr) +{ + atom_skip_src_int(ctx, arg | atom_dst_to_src[(attr >> 3) & 7][(attr >> 6) & 3] << 3, ptr); +} + +static void atom_put_dst(atom_exec_context *ctx, int arg, uint8_t attr, int *ptr, uint32_t val, uint32_t saved) +{ + uint32_t align = atom_dst_to_src[(attr >> 3) & 7][(attr >> 6) & 3], old_val = val, idx; + struct atom_context *gctx = ctx->ctx; + old_val &= atom_arg_mask[align] >> atom_arg_shift[align]; + val <<= atom_arg_shift[align]; + val &= atom_arg_mask[align]; + saved &= ~atom_arg_mask[align]; + val |= saved; + switch (arg) { + case ATOM_ARG_REG: + idx = U16(*ptr); + (*ptr) += 2; + ADEBUG("REG[0x%04X]", idx); + idx += gctx->reg_block; + switch (gctx->io_mode) { + case ATOM_IO_MM: + if (idx == 0) + gctx->card->reg_write(gctx->card, idx, val << 2); + else + gctx->card->reg_write(gctx->card, idx, val); + break; + case ATOM_IO_PCI: + pr_info("PCI registers are not implemented\n"); + return; + case ATOM_IO_SYSIO: + pr_info("SYSIO registers are not implemented\n"); + return; + default: + if (!(gctx->io_mode & 0x80)) { + pr_info("Bad IO mode\n"); + return; + } + if (!gctx->iio[gctx->io_mode & 0xFF]) { + pr_info("Undefined indirect IO write method %d\n", + gctx->io_mode & 0x7F); + return; + } + atom_iio_execute(gctx, gctx->iio[gctx->io_mode & 0xFF], + idx, val); + } + break; + case ATOM_ARG_PS: + idx = U8(*ptr); + (*ptr)++; + ADEBUG("PS[0x%02X]", idx); + ctx->ps[idx] = cpu_to_le32(val); + break; + case ATOM_ARG_WS: + idx = U8(*ptr); + (*ptr)++; + ADEBUG("WS[0x%02X]", idx); + switch (idx) { + case ATOM_WS_QUOTIENT: + gctx->divmul[0] = val; + break; + case ATOM_WS_REMAINDER: + gctx->divmul[1] = val; + break; + case ATOM_WS_DATAPTR: + gctx->data_block = val; + break; + case ATOM_WS_SHIFT: + gctx->shift = val; + break; + case ATOM_WS_OR_MASK: + case ATOM_WS_AND_MASK: + break; + case ATOM_WS_FB_WINDOW: + gctx->fb_base = val; + break; + case ATOM_WS_ATTRIBUTES: + gctx->io_attr = val; + break; + case ATOM_WS_REGPTR: + gctx->reg_block = val; + break; + default: + ctx->ws[idx] = val; + } + break; + case ATOM_ARG_FB: + idx = U8(*ptr); + (*ptr)++; + if ((gctx->fb_base + (idx * 4)) > gctx->scratch_size_bytes) { + ADEBUG("ATOM: fb write beyond scratch region: %d vs. %d\n", gctx->fb_base + (idx * 4), gctx->scratch_size_bytes); + } else { + _DEBUG("ATOM: scratch write 0x%x to index %d", val, (gctx->fb_base / 4) + idx); + gctx->scratch[(gctx->fb_base / 4) + idx] = val; + } + ADEBUG("FB[0x%02X]", idx); + break; + case ATOM_ARG_PLL: + idx = U8(*ptr); + (*ptr)++; + ADEBUG("PLL[0x%02X]", idx); + gctx->card->pll_write(gctx->card, idx, val); + break; + case ATOM_ARG_MC: + idx = U8(*ptr); + (*ptr)++; + ADEBUG("MC[0x%02X]", idx); + gctx->card->mc_write(gctx->card, idx, val); + return; + } + switch (align) { + case ATOM_SRC_DWORD: + ADEBUG(".[31:0] <- 0x%08X\n", old_val); + break; + case ATOM_SRC_WORD0: + ADEBUG(".[15:0] <- 0x%04X\n", old_val); + break; + case ATOM_SRC_WORD8: + ADEBUG(".[23:8] <- 0x%04X\n", old_val); + break; + case ATOM_SRC_WORD16: + ADEBUG(".[31:16] <- 0x%04X\n", old_val); + break; + case ATOM_SRC_BYTE0: + ADEBUG(".[7:0] <- 0x%02X\n", old_val); + break; + case ATOM_SRC_BYTE8: + ADEBUG(".[15:8] <- 0x%02X\n", old_val); + break; + case ATOM_SRC_BYTE16: + ADEBUG(".[23:16] <- 0x%02X\n", old_val); + break; + case ATOM_SRC_BYTE24: + ADEBUG(".[31:24] <- 0x%02X\n", old_val); + break; + } +} + +static void atom_op_add(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++); + uint32_t dst, src, saved; + int dptr = *ptr; + SDEBUG(" dst: "); + dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); + SDEBUG(" src: "); + src = atom_get_src(ctx, attr, ptr); + dst += src; + SDEBUG(" dst: "); + atom_put_dst(ctx, arg, attr, &dptr, dst, saved); +} + +static void atom_op_and(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++); + uint32_t dst, src, saved; + int dptr = *ptr; + SDEBUG(" dst: "); + dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); + SDEBUG(" src: "); + src = atom_get_src(ctx, attr, ptr); + dst &= src; + SDEBUG(" dst: "); + atom_put_dst(ctx, arg, attr, &dptr, dst, saved); +} + +static void atom_op_beep(atom_exec_context *ctx, int *ptr, int arg) +{ + printk("ATOM BIOS beeped!\n"); +} + +static void atom_op_calltable(atom_exec_context *ctx, int *ptr, int arg) +{ + int idx = U8((*ptr)++); + int r = 0; + + if (idx < ATOM_TABLE_NAMES_CNT) + SDEBUG(" table: %d (%s)\n", idx, atom_table_names[idx]); + else + SDEBUG(" table: %d\n", idx); + if (U16(ctx->ctx->cmd_table + 4 + 2 * idx)) + r = atom_execute_table_locked(ctx->ctx, idx, ctx->ps + ctx->ps_shift); + if (r) { + ctx->abort = true; + } +} + +static void atom_op_clear(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++); + uint32_t saved; + int dptr = *ptr; + attr &= 0x38; + attr |= atom_def_dst[attr >> 3] << 6; + atom_get_dst(ctx, arg, attr, ptr, &saved, 0); + SDEBUG(" dst: "); + atom_put_dst(ctx, arg, attr, &dptr, 0, saved); +} + +static void atom_op_compare(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++); + uint32_t dst, src; + SDEBUG(" src1: "); + dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1); + SDEBUG(" src2: "); + src = atom_get_src(ctx, attr, ptr); + ctx->ctx->cs_equal = (dst == src); + ctx->ctx->cs_above = (dst > src); + SDEBUG(" result: %s %s\n", ctx->ctx->cs_equal ? "EQ" : "NE", ctx->ctx->cs_above ? "GT" : "LE"); +} + +static void atom_op_delay(atom_exec_context *ctx, int *ptr, int arg) +{ + unsigned count = U8((*ptr)++); + SDEBUG(" count: %d\n", count); + if (arg == ATOM_UNIT_MICROSEC) + udelay(count); + // else if (!drm_can_sleep()) + // mdelay(count); + else + msleep(count); +} + +static void atom_op_div(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++); + uint32_t dst, src; + SDEBUG(" src1: "); + dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1); + SDEBUG(" src2: "); + src = atom_get_src(ctx, attr, ptr); + if (src != 0) { + ctx->ctx->divmul[0] = dst / src; + ctx->ctx->divmul[1] = dst % src; + } else { + ctx->ctx->divmul[0] = 0; + ctx->ctx->divmul[1] = 0; + } +} + +static void atom_op_div32(atom_exec_context *ctx, int *ptr, int arg) +{ + uint64_t val64; + uint8_t attr = U8((*ptr)++); + uint32_t dst, src; + SDEBUG(" src1: "); + dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1); + SDEBUG(" src2: "); + src = atom_get_src(ctx, attr, ptr); + if (src != 0) { + val64 = dst; + val64 |= ((uint64_t)ctx->ctx->divmul[1]) << 32; + do_div(val64, src); + ctx->ctx->divmul[0] = lower_32_bits(val64); + ctx->ctx->divmul[1] = upper_32_bits(val64); + } else { + ctx->ctx->divmul[0] = 0; + ctx->ctx->divmul[1] = 0; + } +} + +static void atom_op_eot(atom_exec_context *ctx, int *ptr, int arg) +{ + /* functionally, a nop */ +} + +static void atom_op_jump(atom_exec_context *ctx, int *ptr, int arg) +{ + int execute = 0, target = U16(*ptr); + unsigned long cjiffies; + + (*ptr) += 2; + switch (arg) { + case ATOM_COND_ABOVE: + execute = ctx->ctx->cs_above; + break; + case ATOM_COND_ABOVEOREQUAL: + execute = ctx->ctx->cs_above || ctx->ctx->cs_equal; + break; + case ATOM_COND_ALWAYS: + execute = 1; + break; + case ATOM_COND_BELOW: + execute = !(ctx->ctx->cs_above || ctx->ctx->cs_equal); + break; + case ATOM_COND_BELOWOREQUAL: + execute = !ctx->ctx->cs_above; + break; + case ATOM_COND_EQUAL: + execute = ctx->ctx->cs_equal; + break; + case ATOM_COND_NOTEQUAL: + execute = !ctx->ctx->cs_equal; + break; + } + if (arg != ATOM_COND_ALWAYS) + SDEBUG(" taken: %s\n", execute ? "yes" : "no"); + SDEBUG(" target: 0x%04X\n", target); + if (execute) { + if (ctx->last_jump == (ctx->start + target)) { + cjiffies = jiffies; + if (time_after(cjiffies, ctx->last_jump_jiffies)) { + cjiffies -= ctx->last_jump_jiffies; + if ((jiffies_to_msecs(cjiffies) > 5000)) { + ADEBUG("atombios stuck in loop for more than 5secs aborting\n"); + ctx->abort = true; + } + } else { + /* jiffies wrap around we will just wait a little longer */ + ctx->last_jump_jiffies = jiffies; + } + } else { + ctx->last_jump = ctx->start + target; + ctx->last_jump_jiffies = jiffies; + } + *ptr = ctx->start + target; + } +} + +static void atom_op_mask(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++); + uint32_t dst, mask, src, saved; + int dptr = *ptr; + SDEBUG(" dst: "); + dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); + mask = atom_get_src_direct(ctx, ((attr >> 3) & 7), ptr); + SDEBUG(" mask: 0x%08x", mask); + SDEBUG(" src: "); + src = atom_get_src(ctx, attr, ptr); + dst &= mask; + dst |= src; + SDEBUG(" dst: "); + atom_put_dst(ctx, arg, attr, &dptr, dst, saved); +} + +static void atom_op_move(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++); + uint32_t src, saved; + int dptr = *ptr; + if (((attr >> 3) & 7) != ATOM_SRC_DWORD) + atom_get_dst(ctx, arg, attr, ptr, &saved, 0); + else { + atom_skip_dst(ctx, arg, attr, ptr); + saved = 0xCDCDCDCD; + } + SDEBUG(" src: "); + src = atom_get_src(ctx, attr, ptr); + SDEBUG(" dst: "); + atom_put_dst(ctx, arg, attr, &dptr, src, saved); +} + +static void atom_op_mul(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++); + uint32_t dst, src; + SDEBUG(" src1: "); + dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1); + SDEBUG(" src2: "); + src = atom_get_src(ctx, attr, ptr); + ctx->ctx->divmul[0] = dst * src; +} + +static void atom_op_mul32(atom_exec_context *ctx, int *ptr, int arg) +{ + uint64_t val64; + uint8_t attr = U8((*ptr)++); + uint32_t dst, src; + SDEBUG(" src1: "); + dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1); + SDEBUG(" src2: "); + src = atom_get_src(ctx, attr, ptr); + val64 = (uint64_t)dst * (uint64_t)src; + ctx->ctx->divmul[0] = lower_32_bits(val64); + ctx->ctx->divmul[1] = upper_32_bits(val64); +} + +static void atom_op_nop(atom_exec_context *ctx, int *ptr, int arg) +{ + /* nothing */ +} + +static void atom_op_or(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++); + uint32_t dst, src, saved; + int dptr = *ptr; + SDEBUG(" dst: "); + dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); + SDEBUG(" src: "); + src = atom_get_src(ctx, attr, ptr); + dst |= src; + SDEBUG(" dst: "); + atom_put_dst(ctx, arg, attr, &dptr, dst, saved); +} + +static void atom_op_postcard(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t val = U8((*ptr)++); + SDEBUG("POST card output: 0x%02X\n", val); +} + +static void atom_op_repeat(atom_exec_context *ctx, int *ptr, int arg) +{ + pr_info("unimplemented!\n"); +} + +static void atom_op_restorereg(atom_exec_context *ctx, int *ptr, int arg) +{ + pr_info("unimplemented!\n"); +} + +static void atom_op_savereg(atom_exec_context *ctx, int *ptr, int arg) +{ + pr_info("unimplemented!\n"); +} + +static void atom_op_setdatablock(atom_exec_context *ctx, int *ptr, int arg) +{ + int idx = U8(*ptr); + (*ptr)++; + SDEBUG(" block: %d\n", idx); + if (!idx) + ctx->ctx->data_block = 0; + else if (idx == 255) + ctx->ctx->data_block = ctx->start; + else + ctx->ctx->data_block = U16(ctx->ctx->data_table + 4 + 2 * idx); + SDEBUG(" base: 0x%04X\n", ctx->ctx->data_block); +} + +static void atom_op_setfbbase(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++); + SDEBUG(" fb_base: "); + ctx->ctx->fb_base = atom_get_src(ctx, attr, ptr); +} + +static void atom_op_setport(atom_exec_context *ctx, int *ptr, int arg) +{ + int port; + switch (arg) { + case ATOM_PORT_ATI: + port = U16(*ptr); + if (port < ATOM_IO_NAMES_CNT) + SDEBUG(" port: %d (%s)\n", port, atom_io_names[port]); + else + SDEBUG(" port: %d\n", port); + if (!port) + ctx->ctx->io_mode = ATOM_IO_MM; + else + ctx->ctx->io_mode = ATOM_IO_IIO | port; + (*ptr) += 2; + break; + case ATOM_PORT_PCI: + ctx->ctx->io_mode = ATOM_IO_PCI; + (*ptr)++; + break; + case ATOM_PORT_SYSIO: + ctx->ctx->io_mode = ATOM_IO_SYSIO; + (*ptr)++; + break; + } +} + +static void atom_op_setregblock(atom_exec_context *ctx, int *ptr, int arg) +{ + ctx->ctx->reg_block = U16(*ptr); + (*ptr) += 2; + SDEBUG(" base: 0x%04X\n", ctx->ctx->reg_block); +} + +static void atom_op_shift_left(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++), shift; + uint32_t saved, dst; + int dptr = *ptr; + attr &= 0x38; + attr |= atom_def_dst[attr >> 3] << 6; + SDEBUG(" dst: "); + dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); + shift = atom_get_src_direct(ctx, ATOM_SRC_BYTE0, ptr); + SDEBUG(" shift: %d\n", shift); + dst <<= shift; + SDEBUG(" dst: "); + atom_put_dst(ctx, arg, attr, &dptr, dst, saved); +} + +static void atom_op_shift_right(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++), shift; + uint32_t saved, dst; + int dptr = *ptr; + attr &= 0x38; + attr |= atom_def_dst[attr >> 3] << 6; + SDEBUG(" dst: "); + dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); + shift = atom_get_src_direct(ctx, ATOM_SRC_BYTE0, ptr); + SDEBUG(" shift: %d\n", shift); + dst >>= shift; + SDEBUG(" dst: "); + atom_put_dst(ctx, arg, attr, &dptr, dst, saved); +} + +static void atom_op_shl(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++), shift; + uint32_t saved, dst; + int dptr = *ptr; + uint32_t dst_align = atom_dst_to_src[(attr >> 3) & 7][(attr >> 6) & 3]; + SDEBUG(" dst: "); + dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); + /* op needs to full dst value */ + dst = saved; + shift = atom_get_src(ctx, attr, ptr); + SDEBUG(" shift: %d\n", shift); + dst <<= shift; + dst &= atom_arg_mask[dst_align]; + dst >>= atom_arg_shift[dst_align]; + SDEBUG(" dst: "); + atom_put_dst(ctx, arg, attr, &dptr, dst, saved); +} + +static void atom_op_shr(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++), shift; + uint32_t saved, dst; + int dptr = *ptr; + uint32_t dst_align = atom_dst_to_src[(attr >> 3) & 7][(attr >> 6) & 3]; + SDEBUG(" dst: "); + dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); + /* op needs to full dst value */ + dst = saved; + shift = atom_get_src(ctx, attr, ptr); + SDEBUG(" shift: %d\n", shift); + dst >>= shift; + dst &= atom_arg_mask[dst_align]; + dst >>= atom_arg_shift[dst_align]; + SDEBUG(" dst: "); + atom_put_dst(ctx, arg, attr, &dptr, dst, saved); +} + +static void atom_op_sub(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++); + uint32_t dst, src, saved; + int dptr = *ptr; + SDEBUG(" dst: "); + dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); + SDEBUG(" src: "); + src = atom_get_src(ctx, attr, ptr); + dst -= src; + SDEBUG(" dst: "); + atom_put_dst(ctx, arg, attr, &dptr, dst, saved); +} + +static void atom_op_switch(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++); + uint32_t src, val, target; + SDEBUG(" switch: "); + src = atom_get_src(ctx, attr, ptr); + while (U16(*ptr) != ATOM_CASE_END) + if (U8(*ptr) == ATOM_CASE_MAGIC) { + (*ptr)++; + SDEBUG(" case: "); + val = + atom_get_src(ctx, (attr & 0x38) | ATOM_ARG_IMM, + ptr); + target = U16(*ptr); + if (val == src) { + SDEBUG(" target: %04X\n", target); + *ptr = ctx->start + target; + return; + } + (*ptr) += 2; + } else { + pr_info("Bad case\n"); + return; + } + (*ptr) += 2; +} + +static void atom_op_test(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++); + uint32_t dst, src; + SDEBUG(" src1: "); + dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1); + SDEBUG(" src2: "); + src = atom_get_src(ctx, attr, ptr); + ctx->ctx->cs_equal = ((dst & src) == 0); + SDEBUG(" result: %s\n", ctx->ctx->cs_equal ? "EQ" : "NE"); +} + +static void atom_op_xor(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t attr = U8((*ptr)++); + uint32_t dst, src, saved; + int dptr = *ptr; + SDEBUG(" dst: "); + dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); + SDEBUG(" src: "); + src = atom_get_src(ctx, attr, ptr); + dst ^= src; + SDEBUG(" dst: "); + atom_put_dst(ctx, arg, attr, &dptr, dst, saved); +} + +static void atom_op_debug(atom_exec_context *ctx, int *ptr, int arg) +{ + uint8_t val = U8((*ptr)++); + SDEBUG("DEBUG output: 0x%02X\n", val); +} + +static void atom_op_processds(atom_exec_context *ctx, int *ptr, int arg) +{ + uint16_t val = U16(*ptr); + (*ptr) += val + 2; + SDEBUG("PROCESSDS output: 0x%02X\n", val); +} + +static struct { + void (*func) (atom_exec_context *, int *, int); + int arg; +} opcode_table[ATOM_OP_CNT] = { + { + NULL, 0}, { + atom_op_move, ATOM_ARG_REG}, { + atom_op_move, ATOM_ARG_PS}, { + atom_op_move, ATOM_ARG_WS}, { + atom_op_move, ATOM_ARG_FB}, { + atom_op_move, ATOM_ARG_PLL}, { + atom_op_move, ATOM_ARG_MC}, { + atom_op_and, ATOM_ARG_REG}, { + atom_op_and, ATOM_ARG_PS}, { + atom_op_and, ATOM_ARG_WS}, { + atom_op_and, ATOM_ARG_FB}, { + atom_op_and, ATOM_ARG_PLL}, { + atom_op_and, ATOM_ARG_MC}, { + atom_op_or, ATOM_ARG_REG}, { + atom_op_or, ATOM_ARG_PS}, { + atom_op_or, ATOM_ARG_WS}, { + atom_op_or, ATOM_ARG_FB}, { + atom_op_or, ATOM_ARG_PLL}, { + atom_op_or, ATOM_ARG_MC}, { + atom_op_shift_left, ATOM_ARG_REG}, { + atom_op_shift_left, ATOM_ARG_PS}, { + atom_op_shift_left, ATOM_ARG_WS}, { + atom_op_shift_left, ATOM_ARG_FB}, { + atom_op_shift_left, ATOM_ARG_PLL}, { + atom_op_shift_left, ATOM_ARG_MC}, { + atom_op_shift_right, ATOM_ARG_REG}, { + atom_op_shift_right, ATOM_ARG_PS}, { + atom_op_shift_right, ATOM_ARG_WS}, { + atom_op_shift_right, ATOM_ARG_FB}, { + atom_op_shift_right, ATOM_ARG_PLL}, { + atom_op_shift_right, ATOM_ARG_MC}, { + atom_op_mul, ATOM_ARG_REG}, { + atom_op_mul, ATOM_ARG_PS}, { + atom_op_mul, ATOM_ARG_WS}, { + atom_op_mul, ATOM_ARG_FB}, { + atom_op_mul, ATOM_ARG_PLL}, { + atom_op_mul, ATOM_ARG_MC}, { + atom_op_div, ATOM_ARG_REG}, { + atom_op_div, ATOM_ARG_PS}, { + atom_op_div, ATOM_ARG_WS}, { + atom_op_div, ATOM_ARG_FB}, { + atom_op_div, ATOM_ARG_PLL}, { + atom_op_div, ATOM_ARG_MC}, { + atom_op_add, ATOM_ARG_REG}, { + atom_op_add, ATOM_ARG_PS}, { + atom_op_add, ATOM_ARG_WS}, { + atom_op_add, ATOM_ARG_FB}, { + atom_op_add, ATOM_ARG_PLL}, { + atom_op_add, ATOM_ARG_MC}, { + atom_op_sub, ATOM_ARG_REG}, { + atom_op_sub, ATOM_ARG_PS}, { + atom_op_sub, ATOM_ARG_WS}, { + atom_op_sub, ATOM_ARG_FB}, { + atom_op_sub, ATOM_ARG_PLL}, { + atom_op_sub, ATOM_ARG_MC}, { + atom_op_setport, ATOM_PORT_ATI}, { + atom_op_setport, ATOM_PORT_PCI}, { + atom_op_setport, ATOM_PORT_SYSIO}, { + atom_op_setregblock, 0}, { + atom_op_setfbbase, 0}, { + atom_op_compare, ATOM_ARG_REG}, { + atom_op_compare, ATOM_ARG_PS}, { + atom_op_compare, ATOM_ARG_WS}, { + atom_op_compare, ATOM_ARG_FB}, { + atom_op_compare, ATOM_ARG_PLL}, { + atom_op_compare, ATOM_ARG_MC}, { + atom_op_switch, 0}, { + atom_op_jump, ATOM_COND_ALWAYS}, { + atom_op_jump, ATOM_COND_EQUAL}, { + atom_op_jump, ATOM_COND_BELOW}, { + atom_op_jump, ATOM_COND_ABOVE}, { + atom_op_jump, ATOM_COND_BELOWOREQUAL}, { + atom_op_jump, ATOM_COND_ABOVEOREQUAL}, { + atom_op_jump, ATOM_COND_NOTEQUAL}, { + atom_op_test, ATOM_ARG_REG}, { + atom_op_test, ATOM_ARG_PS}, { + atom_op_test, ATOM_ARG_WS}, { + atom_op_test, ATOM_ARG_FB}, { + atom_op_test, ATOM_ARG_PLL}, { + atom_op_test, ATOM_ARG_MC}, { + atom_op_delay, ATOM_UNIT_MILLISEC}, { + atom_op_delay, ATOM_UNIT_MICROSEC}, { + atom_op_calltable, 0}, { + atom_op_repeat, 0}, { + atom_op_clear, ATOM_ARG_REG}, { + atom_op_clear, ATOM_ARG_PS}, { + atom_op_clear, ATOM_ARG_WS}, { + atom_op_clear, ATOM_ARG_FB}, { + atom_op_clear, ATOM_ARG_PLL}, { + atom_op_clear, ATOM_ARG_MC}, { + atom_op_nop, 0}, { + atom_op_eot, 0}, { + atom_op_mask, ATOM_ARG_REG}, { + atom_op_mask, ATOM_ARG_PS}, { + atom_op_mask, ATOM_ARG_WS}, { + atom_op_mask, ATOM_ARG_FB}, { + atom_op_mask, ATOM_ARG_PLL}, { + atom_op_mask, ATOM_ARG_MC}, { + atom_op_postcard, 0}, { + atom_op_beep, 0}, { + atom_op_savereg, 0}, { + atom_op_restorereg, 0}, { + atom_op_setdatablock, 0}, { + atom_op_xor, ATOM_ARG_REG}, { + atom_op_xor, ATOM_ARG_PS}, { + atom_op_xor, ATOM_ARG_WS}, { + atom_op_xor, ATOM_ARG_FB}, { + atom_op_xor, ATOM_ARG_PLL}, { + atom_op_xor, ATOM_ARG_MC}, { + atom_op_shl, ATOM_ARG_REG}, { + atom_op_shl, ATOM_ARG_PS}, { + atom_op_shl, ATOM_ARG_WS}, { + atom_op_shl, ATOM_ARG_FB}, { + atom_op_shl, ATOM_ARG_PLL}, { + atom_op_shl, ATOM_ARG_MC}, { + atom_op_shr, ATOM_ARG_REG}, { + atom_op_shr, ATOM_ARG_PS}, { + atom_op_shr, ATOM_ARG_WS}, { + atom_op_shr, ATOM_ARG_FB}, { + atom_op_shr, ATOM_ARG_PLL}, { + atom_op_shr, ATOM_ARG_MC}, { + atom_op_debug, 0}, { + atom_op_processds, 0}, { + atom_op_mul32, ATOM_ARG_PS}, { + atom_op_mul32, ATOM_ARG_WS}, { + atom_op_div32, ATOM_ARG_PS}, { + atom_op_div32, ATOM_ARG_WS}, +}; + +static int atom_execute_table_locked(struct atom_context *ctx, int index, uint32_t * params) +{ + int base = CU16(ctx->cmd_table + 4 + 2 * index); + int len, ws, ps, ptr; + unsigned char op; + atom_exec_context ectx; + int ret = 0; + + if (!base) + return -EINVAL; + + len = CU16(base + ATOM_CT_SIZE_PTR); + ws = CU8(base + ATOM_CT_WS_PTR); + ps = CU8(base + ATOM_CT_PS_PTR) & ATOM_CT_PS_MASK; + ptr = base + ATOM_CT_CODE_PTR; + + SDEBUG(">> execute %04X (len %d, WS %d, PS %d)\n", base, len, ws, ps); + + ectx.ctx = ctx; + ectx.ps_shift = ps / 4; + ectx.start = base; + ectx.ps = params; + ectx.abort = false; + ectx.last_jump = 0; + if (ws) + ectx.ws = kcalloc(4, ws, GFP_KERNEL); + else + ectx.ws = NULL; + + debug_depth++; + while (1) { + op = CU8(ptr++); + if (op < ATOM_OP_NAMES_CNT) + SDEBUG("%s @ 0x%04X\n", atom_op_names[op], ptr - 1); + else + SDEBUG("[%d] @ 0x%04X\n", op, ptr - 1); + if (ectx.abort) { + ADEBUG("atombios stuck executing %04X (len %d, WS %d, PS %d) @ 0x%04X\n", + base, len, ws, ps, ptr - 1); + ret = -EINVAL; + goto free; + } + + if (op < ATOM_OP_CNT && op > 0) + opcode_table[op].func(&ectx, &ptr, opcode_table[op].arg); + else + break; + + if (op == ATOM_OP_EOT) + break; + } + debug_depth--; + SDEBUG("<<\n"); + +free: + if (ws) + kfree(ectx.ws); + return ret; +} + +int atom_execute_table(struct atom_context *ctx, int index, uint32_t * params) +{ + int r; + + mutex_lock(&ctx->mutex); + /* reset data block */ + ctx->data_block = 0; + /* reset reg block */ + ctx->reg_block = 0; + /* reset fb window */ + ctx->fb_base = 0; + /* reset io mode */ + ctx->io_mode = ATOM_IO_MM; + /* reset divmul */ + ctx->divmul[0] = 0; + ctx->divmul[1] = 0; + r = atom_execute_table_locked(ctx, index, params); + mutex_unlock(&ctx->mutex); + return r; +} + +static int atom_iio_len[] = { 1, 2, 3, 3, 3, 3, 4, 4, 4, 3 }; + +static void atom_index_iio(struct atom_context *ctx, int base) +{ + ctx->iio = kzalloc(2 * 256, GFP_KERNEL); + if (!ctx->iio) + return; + while (CU8(base) == ATOM_IIO_START) { + ctx->iio[CU8(base + 1)] = base + 2; + base += 2; + while (CU8(base) != ATOM_IIO_END) + base += atom_iio_len[CU8(base)]; + base += 3; + } +} + +struct atom_context *atom_parse(struct card_info *card, void const *bios) +{ + int base; + struct atom_context *ctx = kzalloc(sizeof(struct atom_context), GFP_KERNEL); + char *str; + u16 idx; + + if (!ctx) + return NULL; + + ctx->card = card; + ctx->bios = bios; + + ADEBUG("ATOM: Parsing BIOS"); + + if (CU16(0) != ATOM_BIOS_MAGIC) { + pr_info("Invalid BIOS magic\n"); + kfree(ctx); + return NULL; + } + if (strncmp (CSTR(ATOM_ATI_MAGIC_PTR), ATOM_ATI_MAGIC, strlen(ATOM_ATI_MAGIC))) { + pr_info("Invalid ATI magic\n"); + kfree(ctx); + return NULL; + } + + base = CU16(ATOM_ROM_TABLE_PTR); + if (strncmp (CSTR(base + ATOM_ROM_MAGIC_PTR), ATOM_ROM_MAGIC, strlen(ATOM_ROM_MAGIC))) { + pr_info("Invalid ATOM magic\n"); + kfree(ctx); + return NULL; + } + + ctx->cmd_table = CU16(base + ATOM_ROM_CMD_PTR); + ctx->data_table = CU16(base + ATOM_ROM_DATA_PTR); + atom_index_iio(ctx, CU16(ctx->data_table + ATOM_DATA_IIO_PTR) + 4); + if (!ctx->iio) { + atom_destroy(ctx); + return NULL; + } + + idx = CU16(ATOM_ROM_PART_NUMBER_PTR); + if (idx == 0) + idx = 0x80; + + str = CSTR(idx); + if (*str != '\0') { + pr_info("ATOM BIOS: %s\n", str); + strlcpy(ctx->vbios_version, str, sizeof(ctx->vbios_version)); + } + + return ctx; +} + +int atom_asic_init(struct atom_context *ctx) +{ + int hwi = CU16(ctx->data_table + ATOM_DATA_FWI_PTR); + uint32_t ps[16]; + int ret; + + memset(ps, 0, 64); + + ps[0] = cpu_to_le32(CU32(hwi + ATOM_FWI_DEFSCLK_PTR)); + ps[1] = cpu_to_le32(CU32(hwi + ATOM_FWI_DEFMCLK_PTR)); + if (!ps[0] || !ps[1]) + return 1; + + if (!CU16(ctx->cmd_table + 4 + 2 * ATOM_CMD_INIT)) + return 1; + ret = atom_execute_table(ctx, ATOM_CMD_INIT, ps); + if (ret) + return ret; + + memset(ps, 0, 64); + + return ret; +} + +void atom_destroy(struct atom_context *ctx) +{ + kfree(ctx->iio); + kfree(ctx); +} + +bool atom_parse_data_header(struct atom_context *ctx, int index, uint16_t * size, uint8_t * frev, uint8_t * crev, uint16_t * data_start) +{ + int offset = index * 2 + 4; + int idx = CU16(ctx->data_table + offset); + u16 *mdt = (u16 *)(ctx->bios + ctx->data_table + 4); + + if (!mdt[index]) + return false; + + if (size) + *size = CU16(idx); + if (frev) + *frev = CU8(idx + 2); + if (crev) + *crev = CU8(idx + 3); + *data_start = idx; + return true; +} + +bool atom_parse_cmd_header(struct atom_context *ctx, int index, uint8_t * frev, uint8_t * crev) +{ + int offset = index * 2 + 4; + int idx = CU16(ctx->cmd_table + offset); + u16 *mct = (u16 *)(ctx->bios + ctx->cmd_table + 4); + + if (!mct[index]) + return false; + + if (frev) + *frev = CU8(idx + 2); + if (crev) + *crev = CU8(idx + 3); + return true; +} diff --git a/atom/atom.h b/atom/atom.h new file mode 100644 index 0000000..8d49264 --- /dev/null +++ b/atom/atom.h @@ -0,0 +1,167 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Author: Stanislaw Skowronek + */ + +#ifndef ATOM_H +#define ATOM_H + +#include +#include +#include "types.h" +#include "atom-bits.h" + +#define ATOM_BIOS_MAGIC 0xAA55 +#define ATOM_ATI_MAGIC_PTR 0x30 +#define ATOM_ATI_MAGIC " 761295520" +#define ATOM_ROM_TABLE_PTR 0x48 +#define ATOM_ROM_PART_NUMBER_PTR 0x6E + +#define ATOM_ROM_MAGIC "ATOM" +#define ATOM_ROM_MAGIC_PTR 4 + +#define ATOM_ROM_HEADER_PTR 0x48 +#define ATOM_IMAGE_SIZE_PTR 2 +#define ATOM_IMAGE_SIZE_UNIT 512 + +#define ATOM_ROM_MSG_PTR 0x10 +#define ATOM_ROM_CMD_PTR 0x1E +#define ATOM_ROM_DATA_PTR 0x20 + +#define ATOM_CMD_INIT 0 +#define ATOM_CMD_SETSCLK 0x0A +#define ATOM_CMD_SETMCLK 0x0B +#define ATOM_CMD_SETPCLK 0x0C +#define ATOM_CMD_SPDFANCNTL 0x39 + +#define ATOM_DATA_FWI_PTR 0xC +#define ATOM_DATA_IIO_PTR 0x32 + +#define ATOM_FWI_DEFSCLK_PTR 8 +#define ATOM_FWI_DEFMCLK_PTR 0xC +#define ATOM_FWI_MAXSCLK_PTR 0x24 +#define ATOM_FWI_MAXMCLK_PTR 0x28 + +#define ATOM_CT_SIZE_PTR 0 +#define ATOM_CT_WS_PTR 4 +#define ATOM_CT_PS_PTR 5 +#define ATOM_CT_PS_MASK 0x7F +#define ATOM_CT_CODE_PTR 6 + +#define ATOM_OP_CNT 127 +#define ATOM_OP_EOT 91 + +#define ATOM_CASE_MAGIC 0x63 +#define ATOM_CASE_END 0x5A5A + +#define ATOM_ARG_REG 0 +#define ATOM_ARG_PS 1 +#define ATOM_ARG_WS 2 +#define ATOM_ARG_FB 3 +#define ATOM_ARG_ID 4 +#define ATOM_ARG_IMM 5 +#define ATOM_ARG_PLL 6 +#define ATOM_ARG_MC 7 + +#define ATOM_SRC_DWORD 0 +#define ATOM_SRC_WORD0 1 +#define ATOM_SRC_WORD8 2 +#define ATOM_SRC_WORD16 3 +#define ATOM_SRC_BYTE0 4 +#define ATOM_SRC_BYTE8 5 +#define ATOM_SRC_BYTE16 6 +#define ATOM_SRC_BYTE24 7 + +#define ATOM_WS_QUOTIENT 0x40 +#define ATOM_WS_REMAINDER 0x41 +#define ATOM_WS_DATAPTR 0x42 +#define ATOM_WS_SHIFT 0x43 +#define ATOM_WS_OR_MASK 0x44 +#define ATOM_WS_AND_MASK 0x45 +#define ATOM_WS_FB_WINDOW 0x46 +#define ATOM_WS_ATTRIBUTES 0x47 +#define ATOM_WS_REGPTR 0x48 + +#define ATOM_IIO_NOP 0 +#define ATOM_IIO_START 1 +#define ATOM_IIO_READ 2 +#define ATOM_IIO_WRITE 3 +#define ATOM_IIO_CLEAR 4 +#define ATOM_IIO_SET 5 +#define ATOM_IIO_MOVE_INDEX 6 +#define ATOM_IIO_MOVE_ATTR 7 +#define ATOM_IIO_MOVE_DATA 8 +#define ATOM_IIO_END 9 + +#define ATOM_IO_MM 0 +#define ATOM_IO_PCI 1 +#define ATOM_IO_SYSIO 2 +#define ATOM_IO_IIO 0x80 + +struct card_info { + void (* reg_write)(struct card_info *, uint32_t, uint32_t); /* filled by driver */ + uint32_t (* reg_read)(struct card_info *, uint32_t); /* filled by driver */ + void (* ioreg_write)(struct card_info *, uint32_t, uint32_t); /* filled by driver */ + uint32_t (* ioreg_read)(struct card_info *, uint32_t); /* filled by driver */ + void (* mc_write)(struct card_info *, uint32_t, uint32_t); /* filled by driver */ + uint32_t (* mc_read)(struct card_info *, uint32_t); /* filled by driver */ + void (* pll_write)(struct card_info *, uint32_t, uint32_t); /* filled by driver */ + uint32_t (* pll_read)(struct card_info *, uint32_t); /* filled by driver */ +}; + +struct atom_context { + struct card_info *card; + struct mutex mutex; + void const *bios; + uint32_t cmd_table, data_table; + uint16_t *iio; + + uint16_t data_block; + uint32_t fb_base; + uint32_t divmul[2]; + uint16_t io_attr; + uint16_t reg_block; + uint8_t shift; + int cs_equal, cs_above; + int io_mode; + uint32_t *scratch; + int scratch_size_bytes; + char vbios_version[20]; + + // uint32_t firmware_start_offset; + // uint32_t firmware_size; + // uint32_t scratch_reg_offset; +}; + +extern int atom_debug; + +struct atom_context *atom_parse(struct card_info *, void const *); +int atom_execute_table(struct atom_context *, int, uint32_t *); +int atom_asic_init(struct atom_context *); +void atom_destroy(struct atom_context *); +bool atom_parse_data_header(struct atom_context *ctx, int index, uint16_t *size, uint8_t *frev, uint8_t *crev, uint16_t *data_start); +bool atom_parse_cmd_header(struct atom_context *ctx, int index, uint8_t *frev, uint8_t *crev); +// #include "atom-types.h" +// #include "atombios.h" +// #include "ObjectID.h" + +#endif diff --git a/atom/parser.c b/atom/parser.c new file mode 100644 index 0000000..e4b9dff --- /dev/null +++ b/atom/parser.c @@ -0,0 +1,152 @@ +#include "atom.h" +#include "atombios.h" + +#define GET_IMAGE(type, offset) ((type*)bios_get_image(bp->bios, offset, sizeof(type))) +#define DATA_TABLES(table) (bp->master_data_tbl->ListOfDataTables.table) + + +uint8_t *bios_get_image(struct atom_bios *bios, uint32_t offset, uint32_t size) +{ + if (offset + size < bios->size) + return bios->data + offset; + else + return NULL; +} + +struct atom_data_revision { + uint32_t major; + uint32_t minor; +}; + +struct object_info_table { + struct atom_data_revision revision; + union { + ATOM_OBJECT_HEADER *v1_1; + ATOM_OBJECT_HEADER_V3 *v1_3; + }; +}; + +struct bios_parser { + bool valid; + // struct dc_bios base; + + struct atom_bios *bios; + + struct object_info_table object_info_tbl; + uint32_t object_info_tbl_offset; + ATOM_MASTER_DATA_TABLE *master_data_tbl; + + // const struct bios_parser_helper *bios_helper; + + // const struct command_table_helper *cmd_helper; + // struct cmd_tbl cmd_tbl; + + // bool remap_device_tags; +}; + +static void get_atom_data_table_revision(ATOM_COMMON_TABLE_HEADER *atom_data_tbl, struct atom_data_revision *tbl_revision) +{ + if (!tbl_revision) + return; + + /* initialize the revision to 0 which is invalid revision */ + tbl_revision->major = 0; + tbl_revision->minor = 0; + + if (!atom_data_tbl) + return; + + tbl_revision->major = (uint32_t) GET_DATA_TABLE_MAJOR_REVISION(atom_data_tbl); + tbl_revision->minor = (uint32_t) GET_DATA_TABLE_MINOR_REVISION(atom_data_tbl); +} + +bool bios_parser_init(struct bios_parser *bp, struct atom_bios *bios) +{ + uint16_t *rom_header_offset = NULL; + ATOM_ROM_HEADER *rom_header = NULL; + ATOM_OBJECT_HEADER *object_info_tbl; + struct atom_data_revision tbl_rev = {0}; + + bp->bios = bios; + + // if (!init) + // return false; + + // if (!init->bios) + // return false; + + // bp->base.funcs = &vbios_funcs; + // bp->base.bios = init->bios; + // bp->base.bios_size = bp->base.bios[BIOS_IMAGE_SIZE_OFFSET] * BIOS_IMAGE_SIZE_UNIT; + // + // bp->base.ctx = init->ctx; + // bp->base.bios_local_image = NULL; + + rom_header_offset = GET_IMAGE(uint16_t, OFFSET_TO_POINTER_TO_ATOM_ROM_HEADER); + if (!rom_header_offset) + return false; + + rom_header = GET_IMAGE(ATOM_ROM_HEADER, *rom_header_offset); + if (!rom_header) + return false; + + get_atom_data_table_revision(&rom_header->sHeader, &tbl_rev); + if (tbl_rev.major >= 2 && tbl_rev.minor >= 2) + return false; + + bp->master_data_tbl = GET_IMAGE(ATOM_MASTER_DATA_TABLE, rom_header->usMasterDataTableOffset); + if (!bp->master_data_tbl) + return false; + + bp->object_info_tbl_offset = DATA_TABLES(Object_Header); + if (!bp->object_info_tbl_offset) + return false; + + object_info_tbl = GET_IMAGE(ATOM_OBJECT_HEADER, bp->object_info_tbl_offset); + if (!object_info_tbl) + return false; + + get_atom_data_table_revision(&object_info_tbl->sHeader, &bp->object_info_tbl.revision); + if (bp->object_info_tbl.revision.major == 1 && bp->object_info_tbl.revision.minor >= 3) { + ATOM_OBJECT_HEADER_V3 *tbl_v3; + + tbl_v3 = GET_IMAGE(ATOM_OBJECT_HEADER_V3, bp->object_info_tbl_offset); + if (!tbl_v3) + return false; + + bp->object_info_tbl.v1_3 = tbl_v3; + } else if (bp->object_info_tbl.revision.major == 1 + && bp->object_info_tbl.revision.minor >= 1) + bp->object_info_tbl.v1_1 = object_info_tbl; + else { + return false; + } + + // dal_bios_parser_init_cmd_tbl(bp); + // dal_bios_parser_init_cmd_tbl_helper(&bp->cmd_helper, dce_version); + + // bp->base.integrated_info = bios_parser_create_integrated_info(&bp->base); + + return true; +} + +static uint8_t get_number_of_objects(struct bios_parser *bp, uint32_t offset) +{ + ATOM_OBJECT_TABLE *table; + + uint32_t object_table_offset = bp->object_info_tbl_offset + offset; + + table = GET_IMAGE(ATOM_OBJECT_TABLE, object_table_offset); + + if (!table) + return 0; + else + return table->ucNumberOfObjects; +} + +uint8_t bios_parser_get_connectors_number(struct bios_parser *bp) +{ + // struct bios_parser *bp = BP_FROM_DCB(dcb); + + return get_number_of_objects(bp, le16_to_cpu(bp->object_info_tbl.v1_1->usConnectorObjectTableOffset)); +} diff --git a/atom/types.h b/atom/types.h new file mode 100644 index 0000000..64e93a7 --- /dev/null +++ b/atom/types.h @@ -0,0 +1,342 @@ +#ifndef _UAPI_AURA_ATOM_TYPES_H +#define _UAPI_AURA_ATOM_TYPES_H + +#include + +// #define AMD_VBIOS_SIGNATURE " 761295520" +// #define AMD_VBIOS_SIGNATURE_OFFSET 0x30 +// #define AMD_VBIOS_SIGNATURE_SIZE sizeof(AMD_VBIOS_SIGNATURE) +// #define AMD_VBIOS_SIGNATURE_END (AMD_VBIOS_SIGNATURE_OFFSET + AMD_VBIOS_SIGNATURE_SIZE) +// #define AMD_IS_VALID_VBIOS(p) ((p)[0] == 0x55 && (p)[1] == 0xAA) +// #define AMD_VBIOS_LENGTH(p) ((p)[2] << 9) +// +// #define ATOM_ROM_PART_NUMBER_PTR 0x6E +// #define ATOM_ROM_TABLE_PTR 0x48 +// #define ATOM_ROM_MSG_PTR 0x10 +// #define ATOM_ROM_CMD_PTR 0x1E +// #define ATOM_ROM_DATA_PTR 0x20 + +// #define ATOM_DEVICE_RESERVEDC_INDEX 0x0000000C +// #define ATOM_DEVICE_RESERVEDD_INDEX 0x0000000D +// #define ATOM_DEVICE_RESERVEDE_INDEX 0x0000000E +#define ATOM_DEVICE_RESERVEDF_INDEX 0x0000000F +// #define ATOM_MAX_SUPPORTED_DEVICE_INFO (ATOM_DEVICE_DFP3_INDEX+1) +// #define ATOM_MAX_SUPPORTED_DEVICE_INFO_2 ATOM_MAX_SUPPORTED_DEVICE_INFO +// #define ATOM_MAX_SUPPORTED_DEVICE_INFO_3 (ATOM_DEVICE_DFP5_INDEX + 1 ) + +#define ATOM_VRAM_BLOCK_SRIOV_MSG_SHARE_RESERVATION 0x2 +#define ATOM_MAX_SUPPORTED_DEVICE (ATOM_DEVICE_RESERVEDF_INDEX+1) + +#define ATOM_VRAM_OPERATION_FLAGS_MASK 0xC0000000L +#define ATOM_VRAM_OPERATION_FLAGS_SHIFT 30 +// #define ATOM_VRAM_BLOCK_NEEDS_NO_RESERVATION 0x1 +// #define ATOM_VRAM_BLOCK_NEEDS_RESERVATION 0x0 +#define ATOM_VRAM_BLOCK_SRIOV_MSG_SHARE_RESERVATION 0x2 + +#define ATOM_MAX_FIRMWARE_VRAM_USAGE_INFO 1 + +#define MasterDataTableIndexOf(FieldName) (((char*)(&((ATOM_MASTER_LIST_OF_DATA_TABLES*)0)->FieldName)-(char*)0)/sizeof(uint16_t)) +#define MasterCommandTableIndexOf(FieldName) (((char*)(&((ATOM_MASTER_LIST_OF_COMMAND_TABLES*)0)->FieldName)-(char*)0)/sizeof(uint16_t)) +#define MasterTableIndexOf(master_table, table_name) (offsetof(master_table, table_name) / sizeof(uint16_t)) + +typedef struct _ATOM_MASTER_LIST_OF_DATA_TABLES_V2_1 { + uint16_t utilitypipeline; /* Offest for the utility to get parser info,Don't change this position!*/ + uint16_t multimedia_info; + uint16_t smc_dpm_info; + uint16_t sw_datatable3; + uint16_t firmwareinfo; /* Shared by various SW components */ + uint16_t sw_datatable5; + uint16_t lcd_info; /* Shared by various SW components */ + uint16_t sw_datatable7; + uint16_t smu_info; + uint16_t sw_datatable9; + uint16_t sw_datatable10; + uint16_t vram_usagebyfirmware; /* Shared by various SW components */ + uint16_t gpio_pin_lut; /* Shared by various SW components */ + uint16_t sw_datatable13; + uint16_t gfx_info; + uint16_t powerplayinfo; /* Shared by various SW components */ + uint16_t sw_datatable16; + uint16_t sw_datatable17; + uint16_t sw_datatable18; + uint16_t sw_datatable19; + uint16_t sw_datatable20; + uint16_t sw_datatable21; + uint16_t displayobjectinfo; /* Shared by various SW components */ + uint16_t indirectioaccess; /* used as an internal one */ + uint16_t umc_info; /* Shared by various SW components */ + uint16_t sw_datatable25; + uint16_t sw_datatable26; + uint16_t dce_info; /* Shared by various SW components */ + uint16_t vram_info; /* Shared by various SW components */ + uint16_t sw_datatable29; + uint16_t integratedsysteminfo; /* Shared by various SW components */ + uint16_t asic_profiling_info; /* Shared by various SW components */ + uint16_t voltageobject_info; /* shared by various SW components */ + uint16_t sw_datatable33; + uint16_t sw_datatable34; +} ATOM_MASTER_LIST_OF_DATA_TABLES_V2_1; + +typedef struct _ATOM_MASTER_LIST_OF_DATA_TABLES { + uint16_t UtilityPipeLine; // Offest for the utility to get parser info,Don't change this position! + uint16_t MultimediaCapabilityInfo; // Only used by MM Lib,latest version 1.1, not configuable from Bios, need to include the table to build Bios + uint16_t MultimediaConfigInfo; // Only used by MM Lib,latest version 2.1, not configuable from Bios, need to include the table to build Bios + uint16_t StandardVESA_Timing; // Only used by Bios + uint16_t FirmwareInfo; // Shared by various SW components,latest version 1.4 + uint16_t PaletteData; // Only used by BIOS + uint16_t LCD_Info; // Shared by various SW components,latest version 1.3, was called LVDS_Info + uint16_t DIGTransmitterInfo; // Internal used by VBIOS only version 3.1 + uint16_t SMU_Info; // Shared by various SW components,latest version 1.1 + uint16_t SupportedDevicesInfo; // Will be obsolete from R600 + uint16_t GPIO_I2C_Info; // Shared by various SW components,latest version 1.2 will be used from R600 + uint16_t VRAM_UsageByFirmware; // Shared by various SW components,latest version 1.3 will be used from R600 + uint16_t GPIO_Pin_LUT; // Shared by various SW components,latest version 1.1 + uint16_t VESA_ToInternalModeLUT; // Only used by Bios + uint16_t GFX_Info; // Shared by various SW components,latest version 2.1 will be used from R600 + uint16_t PowerPlayInfo; // Shared by various SW components,latest version 2.1,new design from R600 + uint16_t GPUVirtualizationInfo; // Will be obsolete from R600 + uint16_t SaveRestoreInfo; // Only used by Bios + uint16_t PPLL_SS_Info; // Shared by various SW components,latest version 1.2, used to call SS_Info, change to new name because of int ASIC SS info + uint16_t OemInfo; // Defined and used by external SW, should be obsolete soon + uint16_t XTMDS_Info; // Will be obsolete from R600 + uint16_t MclkSS_Info; // Shared by various SW components,latest version 1.1, only enabled when ext SS chip is used + uint16_t Object_Header; // Shared by various SW components,latest version 1.1 + uint16_t IndirectIOAccess; // Only used by Bios,this table position can't change at all!! + uint16_t MC_InitParameter; // Only used by command table + uint16_t ASIC_VDDC_Info; // Will be obsolete from R600 + uint16_t ASIC_InternalSS_Info; // New tabel name from R600, used to be called "ASIC_MVDDC_Info" + uint16_t TV_VideoMode; // Only used by command table + uint16_t VRAM_Info; // Only used by command table, latest version 1.3 + uint16_t MemoryTrainingInfo; // Used for VBIOS and Diag utility for memory training purpose since R600. the new table rev start from 2.1 + uint16_t IntegratedSystemInfo; // Shared by various SW components + uint16_t ASIC_ProfilingInfo; // New table name from R600, used to be called "ASIC_VDDCI_Info" for pre-R600 + uint16_t VoltageObjectInfo; // Shared by various SW components, latest version 1.1 + uint16_t PowerSourceInfo; // Shared by various SW components, latest versoin 1.1 + uint16_t ServiceInfo; +} ATOM_MASTER_LIST_OF_DATA_TABLES; + +typedef struct _ATOM_MASTER_LIST_OF_COMMAND_TABLES { + uint16_t ASIC_Init; //Function Table, used by various SW components,latest version 1.1 + uint16_t GetDisplaySurfaceSize; //Atomic Table, Used by Bios when enabling HW ICON + uint16_t ASIC_RegistersInit; //Atomic Table, indirectly used by various SW components,called from ASIC_Init + uint16_t VRAM_BlockVenderDetection; //Atomic Table, used only by Bios + uint16_t DIGxEncoderControl; //Only used by Bios + uint16_t MemoryControllerInit; //Atomic Table, indirectly used by various SW components,called from ASIC_Init + uint16_t EnableCRTCMemReq; //Function Table,directly used by various SW components,latest version 2.1 + uint16_t MemoryParamAdjust; //Atomic Table, indirectly used by various SW components,called from SetMemoryClock if needed + uint16_t DVOEncoderControl; //Function Table,directly used by various SW components,latest version 1.2 + uint16_t GPIOPinControl; //Atomic Table, only used by Bios + uint16_t SetEngineClock; //Function Table,directly used by various SW components,latest version 1.1 + uint16_t SetMemoryClock; //Function Table,directly used by various SW components,latest version 1.1 + uint16_t SetPixelClock; //Function Table,directly used by various SW components,latest version 1.2 + uint16_t EnableDispPowerGating; //Atomic Table, indirectly used by various SW components,called from ASIC_Init + uint16_t ResetMemoryDLL; //Atomic Table, indirectly used by various SW components,called from SetMemoryClock + uint16_t ResetMemoryDevice; //Atomic Table, indirectly used by various SW components,called from SetMemoryClock + uint16_t MemoryPLLInit; //Atomic Table, used only by Bios + uint16_t AdjustDisplayPll; //Atomic Table, used by various SW componentes. + uint16_t AdjustMemoryController; //Atomic Table, indirectly used by various SW components,called from SetMemoryClock + uint16_t EnableASIC_StaticPwrMgt; //Atomic Table, only used by Bios + uint16_t SetUniphyInstance; //Atomic Table, only used by Bios + uint16_t DAC_LoadDetection; //Atomic Table, directly used by various SW components,latest version 1.2 + uint16_t LVTMAEncoderControl; //Atomic Table,directly used by various SW components,latest version 1.3 + uint16_t HW_Misc_Operation; //Atomic Table, directly used by various SW components,latest version 1.1 + uint16_t DAC1EncoderControl; //Atomic Table, directly used by various SW components,latest version 1.1 + uint16_t DAC2EncoderControl; //Atomic Table, directly used by various SW components,latest version 1.1 + uint16_t DVOOutputControl; //Atomic Table, directly used by various SW components,latest version 1.1 + uint16_t CV1OutputControl; //Atomic Table, Atomic Table, Obsolete from Ry6xx, use DAC2 Output instead + uint16_t GetConditionalGoldenSetting; //Only used by Bios + uint16_t SMC_Init; //Function Table,directly used by various SW components,latest version 1.1 + uint16_t PatchMCSetting; //only used by BIOS + uint16_t MC_SEQ_Control; //only used by BIOS + uint16_t Gfx_Harvesting; //Atomic Table, Obsolete from Ry6xx, Now only used by BIOS for GFX harvesting + uint16_t EnableScaler; //Atomic Table, used only by Bios + uint16_t BlankCRTC; //Atomic Table, directly used by various SW components,latest version 1.1 + uint16_t EnableCRTC; //Atomic Table, directly used by various SW components,latest version 1.1 + uint16_t GetPixelClock; //Atomic Table, directly used by various SW components,latest version 1.1 + uint16_t EnableVGA_Render; //Function Table,directly used by various SW components,latest version 1.1 + uint16_t GetSCLKOverMCLKRatio; //Atomic Table, only used by Bios + uint16_t SetCRTC_Timing; //Atomic Table, directly used by various SW components,latest version 1.1 + uint16_t SetCRTC_OverScan; //Atomic Table, used by various SW components,latest version 1.1 + uint16_t GetSMUClockInfo; //Atomic Table, used only by Bios + uint16_t SelectCRTC_Source; //Atomic Table, directly used by various SW components,latest version 1.1 + uint16_t EnableGraphSurfaces; //Atomic Table, used only by Bios + uint16_t UpdateCRTC_DoubleBufferRegisters; //Atomic Table, used only by Bios + uint16_t LUT_AutoFill; //Atomic Table, only used by Bios + uint16_t SetDCEClock; //Atomic Table, start from DCE11.1, shared by driver and VBIOS, change DISPCLK and DPREFCLK + uint16_t GetMemoryClock; //Atomic Table, directly used by various SW components,latest version 1.1 + uint16_t GetEngineClock; //Atomic Table, directly used by various SW components,latest version 1.1 + uint16_t SetCRTC_UsingDTDTiming; //Atomic Table, directly used by various SW components,latest version 1.1 + uint16_t ExternalEncoderControl; //Atomic Table, directly used by various SW components,latest version 2.1 + uint16_t LVTMAOutputControl; //Atomic Table, directly used by various SW components,latest version 1.1 + uint16_t VRAM_BlockDetectionByStrap; //Atomic Table, used only by Bios + uint16_t MemoryCleanUp; //Atomic Table, only used by Bios + uint16_t ProcessI2cChannelTransaction; //Function Table,only used by Bios + uint16_t WriteOneByteToHWAssistedI2C; //Function Table,indirectly used by various SW components + uint16_t ReadHWAssistedI2CStatus; //Atomic Table, indirectly used by various SW components + uint16_t SpeedFanControl; //Function Table,indirectly used by various SW components,called from ASIC_Init + uint16_t PowerConnectorDetection; //Atomic Table, directly used by various SW components,latest version 1.1 + uint16_t MC_Synchronization; //Atomic Table, indirectly used by various SW components,called from SetMemoryClock + uint16_t ComputeMemoryEnginePLL; //Atomic Table, indirectly used by various SW components,called from SetMemory/EngineClock + uint16_t Gfx_Init; //Atomic Table, indirectly used by various SW components,called from SetMemory or SetEngineClock + uint16_t VRAM_GetCurrentInfoBlock; //Atomic Table, used only by Bios + uint16_t DynamicMemorySettings; //Atomic Table, indirectly used by various SW components,called from SetMemoryClock + uint16_t MemoryTraining; //Atomic Table, used only by Bios + uint16_t EnableSpreadSpectrumOnPPLL; //Atomic Table, directly used by various SW components,latest version 1.2 + uint16_t TMDSAOutputControl; //Atomic Table, directly used by various SW components,latest version 1.1 + uint16_t SetVoltage; //Function Table,directly and/or indirectly used by various SW components,latest version 1.1 + uint16_t DAC1OutputControl; //Atomic Table, directly used by various SW components,latest version 1.1 + uint16_t ReadEfuseValue; //Atomic Table, directly used by various SW components,latest version 1.1 + uint16_t ComputeMemoryClockParam; //Function Table,only used by Bios, obsolete soon.Switch to use "ReadEDIDFromHWAssistedI2C" + uint16_t ClockSource; //Atomic Table, indirectly used by various SW components,called from ASIC_Init + uint16_t MemoryDeviceInit; //Atomic Table, indirectly used by various SW components,called from SetMemoryClock + uint16_t GetDispObjectInfo; //Atomic Table, indirectly used by various SW components,called from EnableVGARender + uint16_t DIG1EncoderControl; //Atomic Table,directly used by various SW components,latest version 1.1 + uint16_t DIG2EncoderControl; //Atomic Table,directly used by various SW components,latest version 1.1 + uint16_t DIG1TransmitterControl; //Atomic Table,directly used by various SW components,latest version 1.1 + uint16_t DIG2TransmitterControl; //Atomic Table,directly used by various SW components,latest version 1.1 + uint16_t ProcessAuxChannelTransaction; //Function Table,only used by Bios + uint16_t DPEncoderService; //Function Table,only used by Bios + uint16_t GetVoltageInfo; //Function Table,only used by Bios since SI +} ATOM_MASTER_LIST_OF_COMMAND_TABLES; + +typedef struct _ATOM_COMMON_TABLE_HEADER { + uint16_t usStructureSize; + uint8_t ucTableFormatRevision; //Change it when the Parser is not backward compatible + uint8_t ucTableContentRevision; //Change it only when the table needs to change but the firmware + //Image can't be updated, while Driver needs to carry the new table! +} ATOM_COMMON_TABLE_HEADER; + +typedef struct _ATOM_OBJECT_HEADER +{ + ATOM_COMMON_TABLE_HEADER sHeader; + uint16_t usDeviceSupport; + uint16_t usConnectorObjectTableOffset; + uint16_t usRouterObjectTableOffset; + uint16_t usEncoderObjectTableOffset; + uint16_t usProtectionObjectTableOffset; //only available when Protection block is independent. + uint16_t usDisplayPathTableOffset; +}ATOM_OBJECT_HEADER; + +typedef struct _ATOM_I2C_ID_CONFIG { +#if defined(__BIG_ENDIAN) + uint8_t bfHW_Capable:1; + uint8_t bfHW_EngineID:3; + uint8_t bfI2C_LineMux:4; +#else + uint8_t bfI2C_LineMux:4; + uint8_t bfHW_EngineID:3; + uint8_t bfHW_Capable:1; +#endif +} ATOM_I2C_ID_CONFIG; + +typedef union _ATOM_I2C_ID_CONFIG_ACCESS { + ATOM_I2C_ID_CONFIG sbfAccess; + uint8_t ucAccess; +} ATOM_I2C_ID_CONFIG_ACCESS; + +typedef struct _ATOM_GPIO_I2C_ASSIGMENT { + uint16_t usClkMaskRegisterIndex; + uint16_t usClkEnRegisterIndex; + uint16_t usClkY_RegisterIndex; + uint16_t usClkA_RegisterIndex; + uint16_t usDataMaskRegisterIndex; + uint16_t usDataEnRegisterIndex; + uint16_t usDataY_RegisterIndex; + uint16_t usDataA_RegisterIndex; + ATOM_I2C_ID_CONFIG_ACCESS sucI2cId; + uint8_t ucClkMaskShift; + uint8_t ucClkEnShift; + uint8_t ucClkY_Shift; + uint8_t ucClkA_Shift; + uint8_t ucDataMaskShift; + uint8_t ucDataEnShift; + uint8_t ucDataY_Shift; + uint8_t ucDataA_Shift; + uint8_t ucReserved1; + uint8_t ucReserved2; +} ATOM_GPIO_I2C_ASSIGMENT; + +typedef struct _ATOM_GPIO_I2C_INFO { + ATOM_COMMON_TABLE_HEADER sHeader; + ATOM_GPIO_I2C_ASSIGMENT asGPIO_Info[ATOM_MAX_SUPPORTED_DEVICE]; +} ATOM_GPIO_I2C_INFO; + +typedef struct _ATOM_FIRMWARE_VRAM_RESERVE_INFO { + uint32_t ulStartAddrUsedByFirmware; + uint16_t usFirmwareUseInKb; + uint16_t usReserved; +} ATOM_FIRMWARE_VRAM_RESERVE_INFO; + +typedef struct _ATOM_VRAM_USAGE_BY_FIRMWARE { + ATOM_COMMON_TABLE_HEADER sHeader; + ATOM_FIRMWARE_VRAM_RESERVE_INFO asFirmwareVramReserveInfo[ATOM_MAX_FIRMWARE_VRAM_USAGE_INFO]; +} ATOM_VRAM_USAGE_BY_FIRMWARE; + +typedef struct _VRAM_USAGEBYFIRMWARE_V2_1 { + ATOM_COMMON_TABLE_HEADER table_header; + uint32_t start_address_in_kb; + uint16_t used_by_firmware_in_kb; + uint16_t used_by_driver_in_kb; +} VRAM_USAGEBYFIRMWARE_V2_1; + +typedef struct _PROCESS_I2C_CHANNEL_TRANSACTION_PARAMETERS { + uint8_t ucI2CSpeed; + union + { + uint8_t ucRegIndex; + uint8_t ucStatus; + }; + uint16_t lpI2CDataOut; + uint8_t ucFlag; + uint8_t ucTransBytes; + uint8_t ucSlaveAddr; + uint8_t ucLineNumber; +} PROCESS_I2C_CHANNEL_TRANSACTION_PARAMETERS; + +typedef struct _ATOM_FIRMWARE_INFO_V3_1 { + ATOM_COMMON_TABLE_HEADER table_header; + uint32_t firmware_revision; + uint32_t bootup_sclk_in10khz; + uint32_t bootup_mclk_in10khz; + uint32_t firmware_capability; // enum atombios_firmware_capability + uint32_t main_call_parser_entry; /* direct address of main parser call in VBIOS binary. */ + uint32_t bios_scratch_reg_startaddr; // 1st bios scratch register dword address + uint16_t bootup_vddc_mv; + uint16_t bootup_vddci_mv; + uint16_t bootup_mvddc_mv; + uint16_t bootup_vddgfx_mv; + uint8_t mem_module_id; + uint8_t coolingsolution_id; /*0: Air cooling; 1: Liquid cooling ... */ + uint8_t reserved1[2]; + uint32_t mc_baseaddr_high; + uint32_t mc_baseaddr_low; + uint32_t reserved2[6]; +} ATOM_FIRMWARE_INFO_V3_1; + +typedef struct _PROCESS_AUX_CHANNEL_TRANSACTION_PARAMETERS { + uint16_t lpAuxRequest; + uint16_t lpDataOut; + uint8_t ucChannelID; + union { + uint8_t ucReplyStatus; + uint8_t ucDelay; + }; + uint8_t ucDataOutLen; + uint8_t ucReserved; +} PROCESS_AUX_CHANNEL_TRANSACTION_PARAMETERS; + +typedef struct _PROCESS_AUX_CHANNEL_TRANSACTION_PARAMETERS_V2 { + uint16_t lpAuxRequest; + uint16_t lpDataOut; + uint8_t ucChannelID; + union { + uint8_t ucReplyStatus; + uint8_t ucDelay; + }; + uint8_t ucDataOutLen; + uint8_t ucHPD_ID; //=0: HPD1, =1: HPD2, =2: HPD3, =3: HPD4, =4: HPD5, =5: HPD6 +} PROCESS_AUX_CHANNEL_TRANSACTION_PARAMETERS_V2; + +#define PROCESS_AUX_CHANNEL_TRANSACTION_PS_ALLOCATION PROCESS_AUX_CHANNEL_TRANSACTION_PARAMETERS + +#endif diff --git a/aura-gpu-bios.c b/aura-gpu-bios.c new file mode 100644 index 0000000..c31edf1 --- /dev/null +++ b/aura-gpu-bios.c @@ -0,0 +1,1900 @@ +#include "aura-gpu-bios.h" + +#include "debug.h" + +#define ATOM_BIOS_MAGIC 0xAA55 +#define ATOM_ATI_MAGIC_PTR 0x30 +#define ATOM_ATI_MAGIC " 761295520" + +#define ATOM_ROM_HEADER_PTR 0x48 +#define ATOM_ROM_PART_NUMBER_PTR 0x6E + +#define ATOM_ROM_MAGIC "ATOM" +#define ATOM_ROM_MAGIC_PTR 4 + +#define ATOM_IMAGE_SIZE_PTR 2 +#define ATOM_IMAGE_SIZE_UNIT 512 + +enum bp_result { + BP_RESULT_OK = 0, /* There was no error */ + BP_RESULT_BADINPUT = -EINVAL, /*Bad input parameter */ + BP_RESULT_BADBIOSTABLE = -EBADFD, /* Bad BIOS table */ + BP_RESULT_UNSUPPORTED = -ENOSYS, /* BIOS Table is not supported */ + BP_RESULT_NORECORD = -ENOMSG, /* Record can't be found */ + BP_RESULT_FAILURE = -EFAULT +}; + +/* atom_gpio_pin_assignment.gpio_id definition */ +enum atom_gpio_pin_assignment_gpio_id { + I2C_HW_LANE_MUX = 0x0f, /* only valid when bit7=1 */ + I2C_HW_ENGINE_ID_MASK = 0x70, /* only valid when bit7=1 */ + I2C_HW_CAP = 0x80, /*only when the I2C_HW_CAP is set, the pin ID is assigned to an I2C pin pair, otherwise, it's an generic GPIO pin */ + + /* gpio_id pre-define id for multiple usage */ + /* GPIO use to control PCIE_VDDC in certain SLT board */ + PCIE_VDDC_CONTROL_GPIO_PINID = 56, + /* if PP_AC_DC_SWITCH_GPIO_PINID in Gpio_Pin_LutTable, AC/DC swithing feature is enable */ + PP_AC_DC_SWITCH_GPIO_PINID = 60, + /* VDDC_REGULATOR_VRHOT_GPIO_PINID in Gpio_Pin_LutTable, VRHot feature is enable */ + VDDC_VRHOT_GPIO_PINID = 61, + /*if VDDC_PCC_GPIO_PINID in GPIO_LUTable, Peak Current Control feature is enabled */ + VDDC_PCC_GPIO_PINID = 62, + /* Only used on certain SLT/PA board to allow utility to cut Efuse. */ + EFUSE_CUT_ENABLE_GPIO_PINID = 63, + /* ucGPIO=DRAM_SELF_REFRESH_GPIO_PIND uses for memory self refresh (ucGPIO=0, DRAM self-refresh; ucGPIO= */ + DRAM_SELF_REFRESH_GPIO_PINID = 64, + /* Thermal interrupt output->system thermal chip GPIO pin */ + THERMAL_INT_OUTPUT_GPIO_PINID = 65, +}; + +enum atom_object_record_type_id { + ATOM_I2C_RECORD_TYPE = 1, + ATOM_HPD_INT_RECORD_TYPE = 2, + ATOM_CONNECTOR_DEVICE_TAG_RECORD_TYPE = 4, + ATOM_OBJECT_GPIO_CNTL_RECORD_TYPE = 9, + ATOM_CONNECTOR_HPDPIN_LUT_RECORD_TYPE = 16, + ATOM_CONNECTOR_AUXDDC_LUT_RECORD_TYPE = 17, + ATOM_ENCODER_CAP_RECORD_TYPE = 20, + ATOM_BRACKET_LAYOUT_RECORD_TYPE = 21, + ATOM_CONNECTOR_FORCED_TMDS_CAP_RECORD_TYPE = 22, + ATOM_RECORD_END_TYPE = 0xFF, +}; + +#pragma pack(1) + +struct atom_common_table_header { + uint16_t structuresize; + uint8_t format_revision; //mainly used for a hw function, when the parser is not backward compatible + uint8_t content_revision; //change it when a data table has a structure change, or a hw function has a input/output parameter change +}; + +struct atom_rom_header_v1_1 { + struct atom_common_table_header table_header; + uint8_t atom_bios_string[4]; //enum atom_string_def atom_bios_string; //Signature to distinguish between Atombios and non-atombios, + uint16_t bios_segment_address; + uint16_t protectedmodeoffset; + uint16_t configfilenameoffset; + uint16_t crc_block_offset; + uint16_t vbios_bootupmessageoffset; + uint16_t int10_offset; + uint16_t pcibusdevinitcode; + uint16_t iobaseaddress; + uint16_t subsystem_vendor_id; + uint16_t subsystem_id; + uint16_t pci_info_offset; + uint16_t masterhwfunction_offset; //Offest for SW to get all command function offsets, Don't change the position + uint16_t masterdatatable_offset; //Offest for SW to get all data table offsets, Don't change the position + uint8_t ucExtendedFunctionCode; + uint8_t ucReserved; +}; + +struct atom_rom_header_v2_2 { + struct atom_common_table_header table_header; + uint8_t atom_bios_string[4]; //enum atom_string_def atom_bios_string; //Signature to distinguish between Atombios and non-atombios, + uint16_t bios_segment_address; + uint16_t protectedmodeoffset; + uint16_t configfilenameoffset; + uint16_t crc_block_offset; + uint16_t vbios_bootupmessageoffset; + uint16_t int10_offset; + uint16_t pcibusdevinitcode; + uint16_t iobaseaddress; + uint16_t subsystem_vendor_id; + uint16_t subsystem_id; + uint16_t pci_info_offset; + uint16_t masterhwfunction_offset; //Offest for SW to get all command function offsets, Don't change the position + uint16_t masterdatatable_offset; //Offest for SW to get all data table offsets, Don't change the position + uint16_t reserved; + uint32_t pspdirtableoffset; +}; + +struct atom_display_object_path_v2 { + uint16_t display_objid; //Connector Object ID or Misc Object ID + uint16_t disp_recordoffset; + uint16_t encoderobjid; //first encoder closer to the connector, could be either an external or intenal encoder + uint16_t extencoderobjid; //2nd encoder after the first encoder, from the connector point of view; + uint16_t encoder_recordoffset; + uint16_t extencoder_recordoffset; + uint16_t device_tag; //a supported device vector, each display path starts with this.the paths are enumerated in the way of priority, a path appears first + uint8_t priority_id; + uint8_t reserved; +}; + +struct display_object_info_table_v1_1 { + struct atom_common_table_header table_header; + uint16_t usDeviceSupport; + uint16_t usConnectorObjectTableOffset; + uint16_t usRouterObjectTableOffset; + uint16_t usEncoderObjectTableOffset; + uint16_t usProtectionObjectTableOffset; //only available when Protection block is independent. + uint16_t usDisplayPathTableOffset; +}; + +struct atom_object { + uint16_t usObjectID; + uint16_t usSrcDstTableOffset; + uint16_t usRecordOffset; //this pointing to a bunch of records defined below + uint16_t usReserved; +}; + +struct atom_object_table { + uint8_t ucNumberOfObjects; + uint8_t ucPadding[3]; + struct atom_object asObjects[1]; +}; + +struct display_object_info_table_v1_3 { + struct atom_common_table_header table_header; + uint16_t usDeviceSupport; + uint16_t usConnectorObjectTableOffset; + uint16_t usRouterObjectTableOffset; + uint16_t usEncoderObjectTableOffset; + uint16_t usProtectionObjectTableOffset; //only available when Protection block is independent. + uint16_t usDisplayPathTableOffset; + uint16_t usMiscObjectTableOffset; +}; + +struct display_object_info_table_v1_4 { + struct atom_common_table_header table_header; + uint16_t supporteddevices; + uint8_t number_of_path; + uint8_t reserved; + struct atom_display_object_path_v2 display_path[8]; //the real number of this included in the structure is calculated by using the (whole structure size - the header size- number_of_path)/size of atom_display_object_path +}; + +struct atom_master_list_of_data_tables_v2_1 { + uint16_t utilitypipeline; /* Offest for the utility to get parser info,Don't change this position!*/ + uint16_t multimedia_info; + uint16_t smc_dpm_info; + uint16_t sw_datatable3; + uint16_t firmwareinfo; /* Shared by various SW components */ + uint16_t sw_datatable5; + uint16_t lcd_info; /* Shared by various SW components */ + uint16_t sw_datatable7; + uint16_t smu_info; + uint16_t sw_datatable9; + uint16_t sw_datatable10; + uint16_t vram_usagebyfirmware; /* Shared by various SW components */ + uint16_t gpio_pin_lut; /* Shared by various SW components */ + uint16_t sw_datatable13; + uint16_t gfx_info; + uint16_t powerplayinfo; /* Shared by various SW components */ + uint16_t sw_datatable16; + uint16_t sw_datatable17; + uint16_t sw_datatable18; + uint16_t sw_datatable19; + uint16_t sw_datatable20; + uint16_t sw_datatable21; + uint16_t displayobjectinfo; /* Shared by various SW components */ + uint16_t indirectioaccess; /* used as an internal one */ + uint16_t umc_info; /* Shared by various SW components */ + uint16_t sw_datatable25; + uint16_t sw_datatable26; + uint16_t dce_info; /* Shared by various SW components */ + uint16_t vram_info; /* Shared by various SW components */ + uint16_t sw_datatable29; + uint16_t integratedsysteminfo; /* Shared by various SW components */ + uint16_t asic_profiling_info; /* Shared by various SW components */ + uint16_t voltageobject_info; /* shared by various SW components */ + uint16_t sw_datatable33; + uint16_t sw_datatable34; +}; + +struct atom_master_data_table_v2_1 { + struct atom_common_table_header table_header; + struct atom_master_list_of_data_tables_v2_1 list_of_datatables; +}; + +struct atom_master_list_of_data_tables_v1_1 { + uint16_t utilitypipeline; + uint16_t multimediacapabilityinfo; + uint16_t multimediaconfiginfo; + uint16_t standardvesa_timing; + uint16_t firmwareinfo; + uint16_t palettedata; + uint16_t lcd_info; + uint16_t digtransmitterinfo; + uint16_t smu_info; + uint16_t supporteddevicesinfo; + uint16_t gpio_i2c_info; + uint16_t vram_usagebyfirmware; + uint16_t gpio_pin_lut; + uint16_t vesa_tointernalmodelut; + uint16_t gfx_info; + uint16_t powerplayinfo; + uint16_t gpuvirtualizationinfo; + uint16_t saverestoreinfo; + uint16_t ppll_ss_info; + uint16_t oeminfo; + uint16_t xtmds_info; + uint16_t mclkss_info; + uint16_t object_header; + uint16_t indirectioaccess; + uint16_t mc_initparameter; + uint16_t asic_vddc_info; + uint16_t asic_internalss_info; + uint16_t tv_videomode; + uint16_t vram_info; + uint16_t memorytraininginfo; + uint16_t integratedsysteminfo; + uint16_t asic_profiling_info; + uint16_t voltageobject_info; + uint16_t powersourceinfo; + uint16_t serviceinfo; +}; + +struct atom_master_data_table_v1_1 { + struct atom_common_table_header table_header; + struct atom_master_list_of_data_tables_v1_1 list_of_datatables; +}; + +struct atom_common_record_header { + uint8_t record_type; //An emun to indicate the record type + uint8_t record_size; //The size of the whole record in byte +}; + +struct atom_i2c_record { + struct atom_common_record_header record_header; //record_type = ATOM_I2C_RECORD_TYPE + uint8_t i2c_id; + uint8_t i2c_slave_addr; //The slave address, it's 0 when the record is attached to connector for DDC +}; + +struct atom_i2c_id_config { +#if defined(__BIG_ENDIAN) + uint8_t bfHW_Capable:1; + uint8_t bfHW_EngineID:3; + uint8_t bfI2C_LineMux:4; +#else + uint8_t bfI2C_LineMux:4; + uint8_t bfHW_EngineID:3; + uint8_t bfHW_Capable:1; +#endif +}; + +union atom_i2c_id_config_access { + struct atom_i2c_id_config sbfAccess; + uint8_t ucAccess; +}; + +struct atom_gpio_i2c_assigment { + uint16_t usClkMaskRegisterIndex; + uint16_t usClkEnRegisterIndex; + uint16_t usClkY_RegisterIndex; + uint16_t usClkA_RegisterIndex; + uint16_t usDataMaskRegisterIndex; + uint16_t usDataEnRegisterIndex; + uint16_t usDataY_RegisterIndex; + uint16_t usDataA_RegisterIndex; + union atom_i2c_id_config_access sucI2cId; + uint8_t ucClkMaskShift; + uint8_t ucClkEnShift; + uint8_t ucClkY_Shift; + uint8_t ucClkA_Shift; + uint8_t ucDataMaskShift; + uint8_t ucDataEnShift; + uint8_t ucDataY_Shift; + uint8_t ucDataA_Shift; + uint8_t ucReserved1; + uint8_t ucReserved2; +}; + +#define ATOM_MAX_SUPPORTED_DEVICE 0x10 +struct atom_gpio_i2c_info { + struct atom_common_table_header table_header; + struct atom_gpio_i2c_assigment asGPIO_Info[ATOM_MAX_SUPPORTED_DEVICE]; +}; + +struct atom_gpio_pin_assignment { + uint32_t data_a_reg_index; + uint8_t gpio_bitshift; + uint8_t gpio_mask_bitshift; + uint8_t gpio_id; + uint8_t reserved; +}; + +struct atom_gpio_pin_lut_v2_1 { + struct atom_common_table_header table_header; + /*the real number of this included in the structure is calcualted by using the (whole structure size - the header size)/size of atom_gpio_pin_lut */ + struct atom_gpio_pin_assignment gpio_pin[8]; +}; + +struct atom_pplib_thermalcontroller { + uint8_t ucType; // one of ATOM_PP_THERMALCONTROLLER_* + uint8_t ucI2cLine; // as interpreted by DAL I2C + uint8_t ucI2cAddress; + uint8_t ucFanParameters; // Fan Control Parameters. + uint8_t ucFanMinRPM; // Fan Minimum RPM (hundreds) -- for display purposes only. + uint8_t ucFanMaxRPM; // Fan Maximum RPM (hundreds) -- for display purposes only. + uint8_t ucReserved; // ---- + uint8_t ucFlags; // to be defined +}; + +struct atom_pplib_powerplaytable { + struct atom_common_table_header table_header; + + uint8_t ucDataRevision; + + uint8_t ucNumStates; + uint8_t ucStateEntrySize; + uint8_t ucClockInfoSize; + uint8_t ucNonClockSize; + + // offset from start of this table to array of ucNumStates ATOM_PPLIB_STATE structures + uint16_t usStateArrayOffset; + + // offset from start of this table to array of ASIC-specific structures, + // currently ATOM_PPLIB_CLOCK_INFO. + uint16_t usClockInfoArrayOffset; + + // offset from start of this table to array of ATOM_PPLIB_NONCLOCK_INFO + uint16_t usNonClockInfoArrayOffset; + + uint16_t usBackbiasTime; // in microseconds + uint16_t usVoltageTime; // in microseconds + uint16_t usTableSize; //the size of this structure, or the extended structure + + uint32_t ulPlatformCaps; // See ATOM_PPLIB_CAPS_* + + struct atom_pplib_thermalcontroller sThermalController; + + uint16_t usBootClockInfoOffset; + uint16_t usBootNonClockInfoOffset; +}; + + +struct atom_display_controller_info_v4_1 { + struct atom_common_table_header table_header; + uint32_t display_caps; + uint32_t bootup_dispclk_10khz; + uint16_t dce_refclk_10khz; + uint16_t i2c_engine_refclk_10khz; + uint16_t dvi_ss_percentage; // in unit of 0.001% + uint16_t dvi_ss_rate_10hz; + uint16_t hdmi_ss_percentage; // in unit of 0.001% + uint16_t hdmi_ss_rate_10hz; + uint16_t dp_ss_percentage; // in unit of 0.001% + uint16_t dp_ss_rate_10hz; + uint8_t dvi_ss_mode; // enum of atom_spread_spectrum_mode + uint8_t hdmi_ss_mode; // enum of atom_spread_spectrum_mode + uint8_t dp_ss_mode; // enum of atom_spread_spectrum_mode + uint8_t ss_reserved; + uint8_t hardcode_mode_num; // a hardcode mode number defined in StandardVESA_TimingTable when a CRT or DFP EDID is not available + uint8_t reserved1[3]; + uint16_t dpphy_refclk_10khz; + uint16_t reserved2; + uint8_t dceip_min_ver; + uint8_t dceip_max_ver; + uint8_t max_disp_pipe_num; + uint8_t max_vbios_active_disp_pipe_num; + uint8_t max_ppll_num; + uint8_t max_disp_phy_num; + uint8_t max_aux_pairs; + uint8_t remotedisplayconfig; + uint8_t reserved3[8]; +}; + +struct atom_display_controller_info_v4_2 { + struct atom_common_table_header table_header; + uint32_t display_caps; + uint32_t bootup_dispclk_10khz; + uint16_t dce_refclk_10khz; + uint16_t i2c_engine_refclk_10khz; + uint16_t dvi_ss_percentage; // in unit of 0.001% + uint16_t dvi_ss_rate_10hz; + uint16_t hdmi_ss_percentage; // in unit of 0.001% + uint16_t hdmi_ss_rate_10hz; + uint16_t dp_ss_percentage; // in unit of 0.001% + uint16_t dp_ss_rate_10hz; + uint8_t dvi_ss_mode; // enum of atom_spread_spectrum_mode + uint8_t hdmi_ss_mode; // enum of atom_spread_spectrum_mode + uint8_t dp_ss_mode; // enum of atom_spread_spectrum_mode + uint8_t ss_reserved; + uint8_t dfp_hardcode_mode_num; // DFP hardcode mode number defined in StandardVESA_TimingTable when EDID is not available + uint8_t dfp_hardcode_refreshrate;// DFP hardcode mode refreshrate defined in StandardVESA_TimingTable when EDID is not available + uint8_t vga_hardcode_mode_num; // VGA hardcode mode number defined in StandardVESA_TimingTable when EDID is not avablable + uint8_t vga_hardcode_refreshrate;// VGA hardcode mode number defined in StandardVESA_TimingTable when EDID is not avablable + uint16_t dpphy_refclk_10khz; + uint16_t reserved2; + uint8_t dcnip_min_ver; + uint8_t dcnip_max_ver; + uint8_t max_disp_pipe_num; + uint8_t max_vbios_active_disp_pipe_num; + uint8_t max_ppll_num; + uint8_t max_disp_phy_num; + uint8_t max_aux_pairs; + uint8_t remotedisplayconfig; + uint8_t reserved3[8]; +}; + +struct atom_firmware_info_v1_4 { + struct atom_common_table_header table_header; + uint32_t ulFirmwareRevision; + uint32_t ulDefaultEngineClock; //In 10Khz unit + uint32_t ulDefaultMemoryClock; //In 10Khz unit + uint32_t ulDriverTargetEngineClock; //In 10Khz unit + uint32_t ulDriverTargetMemoryClock; //In 10Khz unit + uint32_t ulMaxEngineClockPLL_Output; //In 10Khz unit + uint32_t ulMaxMemoryClockPLL_Output; //In 10Khz unit + uint32_t ulMaxPixelClockPLL_Output; //In 10Khz unit + uint32_t ulASICMaxEngineClock; //In 10Khz unit + uint32_t ulASICMaxMemoryClock; //In 10Khz unit + uint8_t ucASICMaxTemperature; + uint8_t ucMinAllowedBL_Level; + uint16_t usBootUpVDDCVoltage; //In MV unit + uint16_t usLcdMinPixelClockPLL_Output; // In MHz unit + uint16_t usLcdMaxPixelClockPLL_Output; // In MHz unit + uint32_t ul3DAccelerationEngineClock;//In 10Khz unit + uint32_t ulMinPixelClockPLL_Output; //In 10Khz unit + uint16_t usMinEngineClockPLL_Input; //In 10Khz unit + uint16_t usMaxEngineClockPLL_Input; //In 10Khz unit + uint16_t usMinEngineClockPLL_Output; //In 10Khz unit + uint16_t usMinMemoryClockPLL_Input; //In 10Khz unit + uint16_t usMaxMemoryClockPLL_Input; //In 10Khz unit + uint16_t usMinMemoryClockPLL_Output; //In 10Khz unit + uint16_t usMaxPixelClock; //In 10Khz unit, Max. Pclk + uint16_t usMinPixelClockPLL_Input; //In 10Khz unit + uint16_t usMaxPixelClockPLL_Input; //In 10Khz unit + uint16_t usMinPixelClockPLL_Output; //In 10Khz unit - lower 16bit of ulMinPixelClockPLL_Output + uint16_t usFirmwareCapability; + uint16_t usReferenceClock; //In 10Khz unit + uint16_t usPM_RTS_Location; //RTS PM4 starting location in ROM in 1Kb unit + uint8_t ucPM_RTS_StreamSize; //RTS PM4 packets in Kb unit + uint8_t ucDesign_ID; //Indicate what is the board design + uint8_t ucMemoryModule_ID; //Indicate what is the board design +}; + +struct atom_firmware_info_v2_1 { + struct atom_common_table_header table_header; + uint32_t ulFirmwareRevision; + uint32_t ulDefaultEngineClock; //In 10Khz unit + uint32_t ulDefaultMemoryClock; //In 10Khz unit + uint32_t ulReserved1; + uint32_t ulReserved2; + uint32_t ulMaxEngineClockPLL_Output; //In 10Khz unit + uint32_t ulMaxMemoryClockPLL_Output; //In 10Khz unit + uint32_t ulMaxPixelClockPLL_Output; //In 10Khz unit + uint32_t ulBinaryAlteredInfo; //Was ulASICMaxEngineClock + uint32_t ulDefaultDispEngineClkFreq; //In 10Khz unit + uint8_t ucReserved1; //Was ucASICMaxTemperature; + uint8_t ucMinAllowedBL_Level; + uint16_t usBootUpVDDCVoltage; //In MV unit + uint16_t usLcdMinPixelClockPLL_Output; // In MHz unit + uint16_t usLcdMaxPixelClockPLL_Output; // In MHz unit + uint32_t ulReserved4; //Was ulAsicMaximumVoltage + uint32_t ulMinPixelClockPLL_Output; //In 10Khz unit + uint16_t usMinEngineClockPLL_Input; //In 10Khz unit + uint16_t usMaxEngineClockPLL_Input; //In 10Khz unit + uint16_t usMinEngineClockPLL_Output; //In 10Khz unit + uint16_t usMinMemoryClockPLL_Input; //In 10Khz unit + uint16_t usMaxMemoryClockPLL_Input; //In 10Khz unit + uint16_t usMinMemoryClockPLL_Output; //In 10Khz unit + uint16_t usMaxPixelClock; //In 10Khz unit, Max. Pclk + uint16_t usMinPixelClockPLL_Input; //In 10Khz unit + uint16_t usMaxPixelClockPLL_Input; //In 10Khz unit + uint16_t usMinPixelClockPLL_Output; //In 10Khz unit - lower 16bit of ulMinPixelClockPLL_Output + uint16_t usFirmwareCapability; + uint16_t usCoreReferenceClock; //In 10Khz unit + uint16_t usMemoryReferenceClock; //In 10Khz unit + uint16_t usUniphyDPModeExtClkFreq; //In 10Khz unit, if it is 0, In DP Mode Uniphy Input clock from internal PPLL, otherwise Input clock from external Spread clock + uint8_t ucMemoryModule_ID; //Indicate what is the board design + uint8_t ucReserved4[3]; +}; + +struct product_branding { + uint8_t ucEMBEDDED_CAP:2; // Bit[1:0] Embedded feature level + uint8_t ucReserved:2; // Bit[3:2] Reserved + uint8_t ucBRANDING_ID:4; // Bit[7:4] Branding ID +}; + +struct atom_firmware_info_v2_2 { + struct atom_common_table_header table_header; + uint32_t ulFirmwareRevision; + uint32_t ulDefaultEngineClock; //In 10Khz unit + uint32_t ulDefaultMemoryClock; //In 10Khz unit + uint32_t ulSPLL_OutputFreq; //In 10Khz unit + uint32_t ulGPUPLL_OutputFreq; //In 10Khz unit + uint32_t ulReserved1; //Was ulMaxEngineClockPLL_Output; //In 10Khz unit* + uint32_t ulReserved2; //Was ulMaxMemoryClockPLL_Output; //In 10Khz unit* + uint32_t ulMaxPixelClockPLL_Output; //In 10Khz unit + uint32_t ulBinaryAlteredInfo; //Was ulASICMaxEngineClock ? + uint32_t ulDefaultDispEngineClkFreq; //In 10Khz unit. This is the frequency before DCDTO, corresponding to usBootUpVDDCVoltage. + uint8_t ucReserved3; //Was ucASICMaxTemperature; + uint8_t ucMinAllowedBL_Level; + uint16_t usBootUpVDDCVoltage; //In MV unit + uint16_t usLcdMinPixelClockPLL_Output; // In MHz unit + uint16_t usLcdMaxPixelClockPLL_Output; // In MHz unit + uint32_t ulReserved4; //Was ulAsicMaximumVoltage + uint32_t ulMinPixelClockPLL_Output; //In 10Khz unit + uint8_t ucRemoteDisplayConfig; + uint8_t ucReserved5[3]; //Was usMinEngineClockPLL_Input and usMaxEngineClockPLL_Input + uint32_t ulReserved6; //Was usMinEngineClockPLL_Output and usMinMemoryClockPLL_Input + uint32_t ulReserved7; //Was usMaxMemoryClockPLL_Input and usMinMemoryClockPLL_Output + uint16_t usReserved11; //Was usMaxPixelClock; //In 10Khz unit, Max. Pclk used only for DAC + uint16_t usMinPixelClockPLL_Input; //In 10Khz unit + uint16_t usMaxPixelClockPLL_Input; //In 10Khz unit + uint16_t usBootUpVDDCIVoltage; //In unit of mv; Was usMinPixelClockPLL_Output; + uint16_t usFirmwareCapability; + uint16_t usCoreReferenceClock; //In 10Khz unit + uint16_t usMemoryReferenceClock; //In 10Khz unit + uint16_t usUniphyDPModeExtClkFreq; //In 10Khz unit, if it is 0, In DP Mode Uniphy Input clock from internal PPLL, otherwise Input clock from external Spread clock + uint8_t ucMemoryModule_ID; //Indicate what is the board design + uint8_t ucCoolingSolution_ID; //0: Air cooling; 1: Liquid cooling ... [COOLING_SOLUTION] + struct product_branding ucProductBranding; // Bit[7:4]ucBRANDING_ID: Branding ID, Bit[3:2]ucReserved: Reserved, Bit[1:0]ucEMBEDDED_CAP: Embedded feature level. + uint8_t ucReserved9; + uint16_t usBootUpMVDDCVoltage; //In unit of mv; Was usMinPixelClockPLL_Output; + uint16_t usBootUpVDDGFXVoltage; //In unit of mv; + uint32_t ulReserved10[3]; // New added comparing to previous version +}; + +#pragma pack() + +enum revision { + VERSION_1_1 = 0x0101, + VERSION_1_2 = 0x0102, + VERSION_1_3 = 0x0103, + VERSION_1_4 = 0x0104, + VERSION_2_1 = 0x0201, + VERSION_2_2 = 0x0202, + VERSION_3_1 = 0x0301, + VERSION_3_2 = 0x0302, + VERSION_3_3 = 0x0303, +}; +struct atom_data_revision { + uint16_t major; + uint16_t minor; +}; + +#define revision(rev)\ + ((rev.major << 8) | (uint8_t)rev.minor) +#define table_revision(table) \ + ((table.revision.major << 8) | (uint8_t)table.revision.minor) + +struct object_info_table { + struct atom_data_revision revision; + union { + struct display_object_info_table_v1_1 *v1_1; + struct display_object_info_table_v1_3 *v1_3; + struct display_object_info_table_v1_4 *v1_4; + }; +}; + +struct master_data_table { + struct atom_data_revision revision; + union { + struct atom_master_data_table_v1_1 *v1_1; + struct atom_master_data_table_v2_1 *v2_1; + }; +}; + +struct atom_context { + struct atom_bios bios; + + struct object_info_table object_info_table; + struct master_data_table master_data_table; + + size_t size; + uint8_t data[]; +}; + +#define object_info_field(context, ver, field) \ + le16_to_cpu(context->object_info_table.ver->field) + +#define table_list_field(context, ver, field) \ + le16_to_cpu(context->master_data_table.ver->list_of_datatables.field) + +#define to_atom_context(bios_ptr) \ + container_of(bios_ptr, struct atom_context, bios) + + +static inline uint8_t get_u8(struct atom_context *context, uint32_t offset) +{ + WARN_ON(offset >= context->size); + + return ((unsigned char*)context->data)[offset]; +} + +static inline uint16_t get_u16(struct atom_context *context, uint32_t offset) +{ + return get_u8(context, offset) | (((uint16_t)get_u8(context, offset + 1)) << 8); +} + +static inline uint32_t get_u32(struct atom_context *context, uint32_t offset) +{ + return get_u16(context, offset) | (((uint32_t)get_u16(context, offset + 2)) << 16); +} + +static inline char *get_str(struct atom_context *context, uint32_t offset, size_t size) +{ + if (WARN_ON(offset + size >= context->size)) + return NULL; + + return &(((char *)context->data)[offset]); +} + +static inline void *get_image(struct atom_context *context, uint32_t offset, size_t size) +{ + if (WARN_ON(offset + size >= context->size)) + return NULL; + + if (offset == 0) + return NULL; + + return (((uint8_t *)context->data) + offset); +} +#define GET_IMAGE(context, type, offset) \ + ((type *)get_image(context, offset, sizeof(type))) + + +enum object_id_bit{ + OBJECT_ID_MASK = 0x00FF, + ENUM_ID_MASK = 0x0F00, + OBJECT_TYPE_MASK = 0xF000, + OBJECT_ID_SHIFT = 0x00, + ENUM_ID_SHIFT = 0x08, + OBJECT_TYPE_SHIFT = 0x0C +}; + +enum object_type { + OBJECT_TYPE_UNKNOWN = 0, + + /* Direct ATOM BIOS translation */ + OBJECT_TYPE_GPU, + OBJECT_TYPE_ENCODER, + OBJECT_TYPE_CONNECTOR, + OBJECT_TYPE_ROUTER, + OBJECT_TYPE_GENERIC, + + /* Driver specific */ + OBJECT_TYPE_AUDIO, + OBJECT_TYPE_CONTROLLER, + OBJECT_TYPE_CLOCK_SOURCE, + OBJECT_TYPE_ENGINE, + + OBJECT_TYPE_COUNT +}; +enum _object_types { + GRAPH_OBJECT_TYPE_NONE = 0x0, + GRAPH_OBJECT_TYPE_GPU = 0x1, + GRAPH_OBJECT_TYPE_ENCODER = 0x2, + GRAPH_OBJECT_TYPE_CONNECTOR = 0x3, + GRAPH_OBJECT_TYPE_ROUTER = 0x4, + GRAPH_OBJECT_TYPE_DISPLAY_PATH = 0x6 , + GRAPH_OBJECT_TYPE_GENERIC = 0x7, +}; +static enum object_type object_type_from_bios_object_id( + uint32_t bios_object_id +){ + uint32_t bios_object_type = (bios_object_id & OBJECT_TYPE_MASK) >> OBJECT_TYPE_SHIFT; + + switch (bios_object_type) { + case GRAPH_OBJECT_TYPE_GPU: return OBJECT_TYPE_GPU; + case GRAPH_OBJECT_TYPE_ENCODER: return OBJECT_TYPE_ENCODER; + case GRAPH_OBJECT_TYPE_CONNECTOR: return OBJECT_TYPE_CONNECTOR; + case GRAPH_OBJECT_TYPE_ROUTER: return OBJECT_TYPE_ROUTER; + case GRAPH_OBJECT_TYPE_GENERIC: return OBJECT_TYPE_GENERIC; + default: return OBJECT_TYPE_UNKNOWN; + } +} + +enum object_enum_id { + ENUM_ID_UNKNOWN = 0, + ENUM_ID_1, + ENUM_ID_2, + ENUM_ID_3, + ENUM_ID_4, + ENUM_ID_5, + ENUM_ID_6, + ENUM_ID_7, + + ENUM_ID_COUNT +}; +enum _object_enum_ids { + GRAPH_OBJECT_ENUM_ID1 = 0x01, + GRAPH_OBJECT_ENUM_ID2 = 0x02, + GRAPH_OBJECT_ENUM_ID3 = 0x03, + GRAPH_OBJECT_ENUM_ID4 = 0x04, + GRAPH_OBJECT_ENUM_ID5 = 0x05, + GRAPH_OBJECT_ENUM_ID6 = 0x06, + GRAPH_OBJECT_ENUM_ID7 = 0x07, +}; +static enum object_enum_id enum_id_from_bios_object_id( + uint32_t bios_object_id +){ + uint32_t bios_enum_id = (bios_object_id & ENUM_ID_MASK) >> ENUM_ID_SHIFT; + + switch (bios_enum_id) { + case GRAPH_OBJECT_ENUM_ID1: return ENUM_ID_1; + case GRAPH_OBJECT_ENUM_ID2: return ENUM_ID_2; + case GRAPH_OBJECT_ENUM_ID3: return ENUM_ID_3; + case GRAPH_OBJECT_ENUM_ID4: return ENUM_ID_4; + case GRAPH_OBJECT_ENUM_ID5: return ENUM_ID_5; + case GRAPH_OBJECT_ENUM_ID6: return ENUM_ID_6; + case GRAPH_OBJECT_ENUM_ID7: return ENUM_ID_7; + default: return ENUM_ID_UNKNOWN; + } +} + +static uint32_t gpu_id_from_bios_object_id( + uint32_t bios_object_id +){ + return (bios_object_id & OBJECT_ID_MASK) >> OBJECT_ID_SHIFT; +} + +enum encoder_id { + ENCODER_ID_UNKNOWN = 0, + + /* Radeon Class Display Hardware */ + ENCODER_ID_INTERNAL_LVDS, + ENCODER_ID_INTERNAL_TMDS1, + ENCODER_ID_INTERNAL_TMDS2, + ENCODER_ID_INTERNAL_DAC1, + ENCODER_ID_INTERNAL_DAC2, /* TV/CV DAC */ + + /* External Third Party Encoders */ + ENCODER_ID_INTERNAL_LVTM1, /* not used for Radeon */ + ENCODER_ID_INTERNAL_HDMI, + + /* Kaledisope (KLDSCP) Class Display Hardware */ + ENCODER_ID_INTERNAL_KLDSCP_TMDS1, + ENCODER_ID_INTERNAL_KLDSCP_DAC1, + ENCODER_ID_INTERNAL_KLDSCP_DAC2, /* Shared with CV/TV and CRT */ + /* External TMDS (dual link) */ + ENCODER_ID_EXTERNAL_MVPU_FPGA, /* MVPU FPGA chip */ + ENCODER_ID_INTERNAL_DDI, + ENCODER_ID_INTERNAL_UNIPHY, + ENCODER_ID_INTERNAL_KLDSCP_LVTMA, + ENCODER_ID_INTERNAL_UNIPHY1, + ENCODER_ID_INTERNAL_UNIPHY2, + ENCODER_ID_EXTERNAL_NUTMEG, + ENCODER_ID_EXTERNAL_TRAVIS, + + ENCODER_ID_INTERNAL_WIRELESS, /* Internal wireless display encoder */ + ENCODER_ID_INTERNAL_UNIPHY3, + ENCODER_ID_INTERNAL_VIRTUAL, +}; +enum _encoder_ids { + ENCODER_OBJECT_ID_NONE = 0x00, + ENCODER_OBJECT_ID_INTERNAL_LVDS = 0x01, + ENCODER_OBJECT_ID_INTERNAL_TMDS1 = 0x02, + ENCODER_OBJECT_ID_INTERNAL_TMDS2 = 0x03, + ENCODER_OBJECT_ID_INTERNAL_DAC1 = 0x04, + ENCODER_OBJECT_ID_INTERNAL_DAC2 = 0x05, + ENCODER_OBJECT_ID_INTERNAL_LVTM1 = 0x0F, + ENCODER_OBJECT_ID_HDMI_INTERNAL = 0x12, + ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1 = 0x13, + ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1 = 0x15, + ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2 = 0x16, + ENCODER_OBJECT_ID_MVPU_FPGA = 0x18, + ENCODER_OBJECT_ID_INTERNAL_DDI = 0x19, + ENCODER_OBJECT_ID_INTERNAL_UNIPHY = 0x1E, + ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA = 0x1F, + ENCODER_OBJECT_ID_INTERNAL_UNIPHY1 = 0x20, + ENCODER_OBJECT_ID_INTERNAL_UNIPHY2 = 0x21, + ENCODER_OBJECT_ID_ALMOND = 0x22, + ENCODER_OBJECT_ID_TRAVIS = 0x23, + ENCODER_OBJECT_ID_INTERNAL_UNIPHY3 = 0x25, +}; +static enum encoder_id encoder_id_from_bios_object_id( + uint32_t bios_object_id +){ + uint32_t bios_encoder_id = gpu_id_from_bios_object_id(bios_object_id); + + switch (bios_encoder_id) { + case ENCODER_OBJECT_ID_INTERNAL_LVDS: return ENCODER_ID_INTERNAL_LVDS; + case ENCODER_OBJECT_ID_INTERNAL_TMDS1: return ENCODER_ID_INTERNAL_TMDS1; + case ENCODER_OBJECT_ID_INTERNAL_TMDS2: return ENCODER_ID_INTERNAL_TMDS2; + case ENCODER_OBJECT_ID_INTERNAL_DAC1: return ENCODER_ID_INTERNAL_DAC1; + case ENCODER_OBJECT_ID_INTERNAL_DAC2: return ENCODER_ID_INTERNAL_DAC2; + case ENCODER_OBJECT_ID_INTERNAL_LVTM1: return ENCODER_ID_INTERNAL_LVTM1; + case ENCODER_OBJECT_ID_HDMI_INTERNAL: return ENCODER_ID_INTERNAL_HDMI; + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: return ENCODER_ID_INTERNAL_KLDSCP_TMDS1; + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1: return ENCODER_ID_INTERNAL_KLDSCP_DAC1; + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2: return ENCODER_ID_INTERNAL_KLDSCP_DAC2; + case ENCODER_OBJECT_ID_MVPU_FPGA: return ENCODER_ID_EXTERNAL_MVPU_FPGA; + case ENCODER_OBJECT_ID_INTERNAL_DDI: return ENCODER_ID_INTERNAL_DDI; + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: return ENCODER_ID_INTERNAL_UNIPHY; + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: return ENCODER_ID_INTERNAL_KLDSCP_LVTMA; + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: return ENCODER_ID_INTERNAL_UNIPHY1; + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: return ENCODER_ID_INTERNAL_UNIPHY2; + case ENCODER_OBJECT_ID_ALMOND: return ENCODER_ID_EXTERNAL_NUTMEG; + case ENCODER_OBJECT_ID_TRAVIS: return ENCODER_ID_EXTERNAL_TRAVIS; + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3: return ENCODER_ID_INTERNAL_UNIPHY3; + default: return ENCODER_ID_UNKNOWN; + } +} + +enum connector_id { + CONNECTOR_ID_UNKNOWN = 0, + CONNECTOR_ID_SINGLE_LINK_DVII = 1, + CONNECTOR_ID_DUAL_LINK_DVII = 2, + CONNECTOR_ID_SINGLE_LINK_DVID = 3, + CONNECTOR_ID_DUAL_LINK_DVID = 4, + CONNECTOR_ID_VGA = 5, + CONNECTOR_ID_HDMI_TYPE_A = 12, + CONNECTOR_ID_LVDS = 14, + CONNECTOR_ID_PCIE = 16, + CONNECTOR_ID_HARDCODE_DVI = 18, + CONNECTOR_ID_DISPLAY_PORT = 19, + CONNECTOR_ID_EDP = 20, + CONNECTOR_ID_MXM = 21, + CONNECTOR_ID_WIRELESS = 22, + CONNECTOR_ID_MIRACAST = 23, + + CONNECTOR_ID_VIRTUAL = 100 +}; +enum _connector_ids { + CONNECTOR_OBJECT_ID_NONE = 0x00, + CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I = 0x01, + CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I = 0x02, + CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D = 0x03, + CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D = 0x04, + CONNECTOR_OBJECT_ID_VGA = 0x05, + CONNECTOR_OBJECT_ID_COMPOSITE = 0x06, + CONNECTOR_OBJECT_ID_SVIDEO = 0x07, + CONNECTOR_OBJECT_ID_YPbPr = 0x08, + CONNECTOR_OBJECT_ID_D_CONNECTOR = 0x09, + CONNECTOR_OBJECT_ID_9PIN_DIN = 0x0A, + CONNECTOR_OBJECT_ID_SCART = 0x0B, + CONNECTOR_OBJECT_ID_HDMI_TYPE_A = 0x0C, + CONNECTOR_OBJECT_ID_HDMI_TYPE_B = 0x0D, + CONNECTOR_OBJECT_ID_LVDS = 0x0E, + CONNECTOR_OBJECT_ID_7PIN_DIN = 0x0F, + CONNECTOR_OBJECT_ID_PCIE_CONNECTOR = 0x10, + CONNECTOR_OBJECT_ID_CROSSFIRE = 0x11, + CONNECTOR_OBJECT_ID_HARDCODE_DVI = 0x12, + CONNECTOR_OBJECT_ID_DISPLAYPORT = 0x13, + CONNECTOR_OBJECT_ID_eDP = 0x14, + CONNECTOR_OBJECT_ID_MXM = 0x15, + CONNECTOR_OBJECT_ID_LVDS_eDP = 0x16, +}; +static enum connector_id connector_id_from_bios_object_id( + uint32_t bios_object_id +){ + uint32_t bios_connector_id = gpu_id_from_bios_object_id(bios_object_id); + + switch (bios_connector_id) { + case CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I: return CONNECTOR_ID_SINGLE_LINK_DVII; + case CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I: return CONNECTOR_ID_DUAL_LINK_DVII; + case CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D: return CONNECTOR_ID_SINGLE_LINK_DVID; + case CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D: return CONNECTOR_ID_DUAL_LINK_DVID; + case CONNECTOR_OBJECT_ID_VGA: return CONNECTOR_ID_VGA; + case CONNECTOR_OBJECT_ID_HDMI_TYPE_A: return CONNECTOR_ID_HDMI_TYPE_A; + case CONNECTOR_OBJECT_ID_LVDS: return CONNECTOR_ID_LVDS; + case CONNECTOR_OBJECT_ID_PCIE_CONNECTOR: return CONNECTOR_ID_PCIE; + case CONNECTOR_OBJECT_ID_HARDCODE_DVI: return CONNECTOR_ID_HARDCODE_DVI; + case CONNECTOR_OBJECT_ID_DISPLAYPORT: return CONNECTOR_ID_DISPLAY_PORT; + case CONNECTOR_OBJECT_ID_eDP: return CONNECTOR_ID_EDP; + case CONNECTOR_OBJECT_ID_MXM: return CONNECTOR_ID_MXM; + default: return CONNECTOR_ID_UNKNOWN; + } +} + +enum generic_id { + GENERIC_ID_UNKNOWN = 0, + GENERIC_ID_MXM_OPM, + GENERIC_ID_GLSYNC, + GENERIC_ID_STEREO, + + GENERIC_ID_COUNT +}; +enum _generic_ids { + GENERIC_OBJECT_ID_NONE = 0x00, + GENERIC_OBJECT_ID_GLSYNC = 0x01, + GENERIC_OBJECT_ID_PX2_NON_DRIVABLE = 0x02, + GENERIC_OBJECT_ID_MXM_OPM = 0x03, + GENERIC_OBJECT_ID_STEREO_PIN = 0x04, + GENERIC_OBJECT_ID_BRACKET_LAYOUT = 0x05, +}; +static enum generic_id generic_id_from_bios_object_id( + uint32_t bios_object_id +){ + uint32_t bios_generic_id = gpu_id_from_bios_object_id(bios_object_id); + + switch (bios_generic_id) { + case GENERIC_OBJECT_ID_MXM_OPM: return GENERIC_ID_MXM_OPM; + case GENERIC_OBJECT_ID_GLSYNC: return GENERIC_ID_GLSYNC; + case GENERIC_OBJECT_ID_STEREO_PIN: return GENERIC_ID_STEREO; + default: return GENERIC_ID_UNKNOWN; + } +} + +static uint32_t id_from_bios_object_id( + enum object_type type, + uint32_t bios_object_id +){ + switch (type) { + case OBJECT_TYPE_GPU: + return gpu_id_from_bios_object_id(bios_object_id); + case OBJECT_TYPE_ENCODER: + return (uint32_t)encoder_id_from_bios_object_id(bios_object_id); + case OBJECT_TYPE_CONNECTOR: + return (uint32_t)connector_id_from_bios_object_id( + bios_object_id); + case OBJECT_TYPE_GENERIC: + return generic_id_from_bios_object_id(bios_object_id); + default: + return 0; + } +} + +static inline struct graphics_object_id dal_graphics_object_id_init( + uint32_t id, + enum object_enum_id enum_id, + enum object_type type +){ + struct graphics_object_id result = { + id, enum_id, type, 0 + }; + + return result; +} + +static bool object_id_from_bios_object_id( + uint32_t bios_object_id, + struct graphics_object_id *object_id +){ + enum object_type type; + enum object_enum_id enum_id; + + type = object_type_from_bios_object_id(bios_object_id); + + if (OBJECT_TYPE_UNKNOWN == type) + return false; + + enum_id = enum_id_from_bios_object_id(bios_object_id); + + if (ENUM_ID_UNKNOWN == enum_id) + return false; + + *object_id = dal_graphics_object_id_init(id_from_bios_object_id(type, bios_object_id), enum_id, type); + + return true; +} + +static struct atom_object *get_bios_object_v1_1( + struct atom_context *context, + struct graphics_object_id id +){ + struct atom_object_table *table; + struct graphics_object_id obj_id; + uint32_t offset, i; + + offset = table_list_field(context, v1_1, object_header); + + switch (id.type) { + case OBJECT_TYPE_ENCODER: + offset += object_info_field(context, v1_1, usEncoderObjectTableOffset); + break; + + case OBJECT_TYPE_CONNECTOR: + offset += object_info_field(context, v1_1, usConnectorObjectTableOffset); + break; + + case OBJECT_TYPE_ROUTER: + offset += object_info_field(context, v1_1, usRouterObjectTableOffset); + break; + + case OBJECT_TYPE_GENERIC: + if (context->object_info_table.revision.minor < 3) + return NULL; + offset += object_info_field(context, v1_3, usMiscObjectTableOffset); + break; + + default: + return NULL; + } + + table = GET_IMAGE(context, struct atom_object_table, offset); + if (!table) + return NULL; + + for (i = 0; i < table->ucNumberOfObjects; i++) { + if (!object_id_from_bios_object_id(le16_to_cpu(table->asObjects[i].usObjectID), &obj_id)) + continue; + + if (id.id == obj_id.id && id.enum_id == obj_id.enum_id && id.type == obj_id.type) + return &table->asObjects[i]; + } + + return NULL; +} + +static struct atom_display_object_path_v2 *get_bios_object_v2_1( + struct atom_context *context, + struct graphics_object_id id +){ + struct display_object_info_table_v1_4 *table; + struct graphics_object_id obj_id = {0}; + unsigned int i; + + table = context->object_info_table.v1_4; + + switch (id.type) { + case OBJECT_TYPE_ENCODER: + for (i = 0; i < table->number_of_path; i++) { + if (!object_id_from_bios_object_id(table->display_path[i].encoderobjid, &obj_id)) + continue; + + if (id.type == obj_id.type && id.id == obj_id.id && id.enum_id == obj_id.enum_id) + return &table->display_path[i]; + } + /* fall through */ + case OBJECT_TYPE_CONNECTOR: + case OBJECT_TYPE_GENERIC: + /* Both Generic and Connector Object ID + * will be stored on display_objid + */ + for (i = 0; i < table->number_of_path; i++) { + if (!object_id_from_bios_object_id(table->display_path[i].display_objid, &obj_id)) + continue; + + if (id.type == obj_id.type && id.id == obj_id.id && id.enum_id == obj_id.enum_id) + return &table->display_path[i]; + } + /* fall through */ + default: + return NULL; + } +} + +#define equal_bits(v1, v2, bit) \ + (((v1) & bit) == ((v2) & bit)) + +static enum bp_result get_gpio_i2c_info_v1_1( + struct atom_context *context, + struct atom_i2c_record *record, + struct i2c_info *info +){ + struct atom_gpio_i2c_info *header; + struct atom_gpio_i2c_assigment *pin; + uint32_t table_count; + uint32_t i2c_table_offset; + + if (WARN_ON(info == NULL)) + return BP_RESULT_BADINPUT; + + i2c_table_offset = table_list_field(context, v1_1, gpio_i2c_info); + header = GET_IMAGE(context, struct atom_gpio_i2c_info, i2c_table_offset); + if (!header) + return BP_RESULT_BADBIOSTABLE; + + if (sizeof(struct atom_common_table_header) + + sizeof(struct atom_gpio_i2c_assigment) > + le16_to_cpu(header->table_header.structuresize) + ){ + return BP_RESULT_BADBIOSTABLE; + } + + if (1 != header->table_header.content_revision) + return BP_RESULT_UNSUPPORTED; + + /* get data count */ + table_count = (le16_to_cpu(header->table_header.structuresize) - + sizeof(struct atom_common_table_header)) / + sizeof(struct atom_gpio_i2c_assigment); + + if (table_count < (record->i2c_id & I2C_HW_LANE_MUX)) + return BP_RESULT_BADBIOSTABLE; + + // Lines 4, 6 and 7 are not being used + // for (i = 0; i < table_count; i++) { + // pin = &header->asGPIO_Info[i]; + // + // AURA_DBG("line: %d, hw: %s, eng: %d", + // header->asGPIO_Info[i].sucI2cId.sbfAccess.bfI2C_LineMux, + // header->asGPIO_Info[i].sucI2cId.sbfAccess.bfHW_Capable ? "true" : "false", + // header->asGPIO_Info[i].sucI2cId.sbfAccess.bfHW_EngineID + // ); + // AURA_DBG(" clk_mask %x, clk_en %x, clk_y %x, clk_a %x, data_mask %x, data_en %x, data_y %x, data_a %x", + // le16_to_cpu(pin->usClkMaskRegisterIndex), + // le16_to_cpu(pin->usClkEnRegisterIndex), + // le16_to_cpu(pin->usClkY_RegisterIndex), + // le16_to_cpu(pin->usClkA_RegisterIndex), + // le16_to_cpu(pin->usDataMaskRegisterIndex), + // le16_to_cpu(pin->usDataEnRegisterIndex), + // le16_to_cpu(pin->usDataY_RegisterIndex), + // le16_to_cpu(pin->usDataA_RegisterIndex) + // ); + // } + + /* get the GPIO_I2C_INFO */ + info->hw_assist = (record->i2c_id & I2C_HW_CAP) ? true : false; + info->line = record->i2c_id & I2C_HW_LANE_MUX; + info->engine_id = (record->i2c_id & I2C_HW_ENGINE_ID_MASK) >> 4; + info->slave_address = record->i2c_slave_addr; + + pin = &header->asGPIO_Info[info->line]; + + info->gpio_info.clk_mask_register_index = le16_to_cpu(pin->usClkMaskRegisterIndex); + info->gpio_info.clk_en_register_index = le16_to_cpu(pin->usClkEnRegisterIndex); + info->gpio_info.clk_y_register_index = le16_to_cpu(pin->usClkY_RegisterIndex); + info->gpio_info.clk_a_register_index = le16_to_cpu(pin->usClkA_RegisterIndex); + info->gpio_info.data_mask_register_index = le16_to_cpu(pin->usDataMaskRegisterIndex); + info->gpio_info.data_en_register_index = le16_to_cpu(pin->usDataEnRegisterIndex); + info->gpio_info.data_y_register_index = le16_to_cpu(pin->usDataY_RegisterIndex); + info->gpio_info.data_a_register_index = le16_to_cpu(pin->usDataA_RegisterIndex); + + info->gpio_info.clk_mask_shift = pin->ucClkMaskShift; + info->gpio_info.clk_en_shift = pin->ucClkEnShift; + info->gpio_info.clk_y_shift = pin->ucClkY_Shift; + info->gpio_info.clk_a_shift = pin->ucClkA_Shift; + info->gpio_info.data_mask_shift = pin->ucDataMaskShift; + info->gpio_info.data_en_shift = pin->ucDataEnShift; + info->gpio_info.data_y_shift = pin->ucDataY_Shift; + info->gpio_info.data_a_shift = pin->ucDataA_Shift; + + info->gpio_info.clk_mask_mask = (1 << pin->ucClkMaskShift); + info->gpio_info.clk_en_mask = (1 << pin->ucClkEnShift); + info->gpio_info.clk_y_mask = (1 << pin->ucClkY_Shift); + info->gpio_info.clk_a_mask = (1 << pin->ucClkA_Shift); + info->gpio_info.data_mask_mask = (1 << pin->ucDataMaskShift); + info->gpio_info.data_en_mask = (1 << pin->ucDataEnShift); + info->gpio_info.data_y_mask = (1 << pin->ucDataY_Shift); + info->gpio_info.data_a_mask = (1 << pin->ucDataA_Shift); + + return BP_RESULT_OK; +} + +static enum bp_result get_gpio_i2c_info_v2_1( + struct atom_context *context, + struct atom_i2c_record *record, + struct i2c_info *info +){ + struct atom_gpio_pin_lut_v2_1 *header; + struct atom_gpio_pin_assignment *pin; + uint32_t table_count, table_index; + uint16_t gpio_pin_lut_offset; + // unsigned int table_index = 0; + bool table_found; + + if (WARN_ON(info == NULL)) + return BP_RESULT_BADINPUT; + + /* get the GPIO_I2C info */ + gpio_pin_lut_offset = table_list_field(context, v2_1, gpio_pin_lut); + header = GET_IMAGE(context, struct atom_gpio_pin_lut_v2_1, gpio_pin_lut_offset); + if (!header) + return BP_RESULT_BADBIOSTABLE; + + if (sizeof(struct atom_common_table_header) + + sizeof(struct atom_gpio_pin_assignment) > + le16_to_cpu(header->table_header.structuresize) + ){ + return BP_RESULT_BADBIOSTABLE; + } + + /* TODO: is version change? */ + if (header->table_header.content_revision != 1) + return BP_RESULT_UNSUPPORTED; + + /* get data count */ + table_count = (le16_to_cpu(header->table_header.structuresize) - + sizeof(struct atom_common_table_header)) / + sizeof(struct atom_gpio_pin_assignment); + + table_found = false; + for (table_index = 0; table_index < table_count; table_index++) { + pin = &header->gpio_pin[table_index]; + + if (!equal_bits(record->i2c_id, pin->gpio_id, I2C_HW_CAP)) + continue; + + if (!equal_bits(record->i2c_id, pin->gpio_id, I2C_HW_ENGINE_ID_MASK)) + continue; + + if (!equal_bits(record->i2c_id, pin->gpio_id, I2C_HW_LANE_MUX)) + continue; + + table_found = true; + break; + } + + if (!table_found) + return BP_RESULT_BADBIOSTABLE; + + /* get the GPIO_I2C_INFO */ + info->hw_assist = (record->i2c_id & I2C_HW_CAP) ? true : false; + info->line = record->i2c_id & I2C_HW_LANE_MUX; + info->engine_id = (record->i2c_id & I2C_HW_ENGINE_ID_MASK) >> 4; + info->slave_address = record->i2c_slave_addr; + + /* TODO: check how to get register offset for en, Y, etc. */ + info->gpio_info.clk_a_register_index = le16_to_cpu(pin->data_a_reg_index); + info->gpio_info.clk_a_shift = pin->gpio_bitshift; + + return BP_RESULT_OK; +} + +static void _aura_gpu_bios_read_name( + struct atom_context *context +){ + int name_offset = get_u16(context, ATOM_ROM_PART_NUMBER_PTR); + const char *name_str; + + if (name_offset == 0) + name_offset = 0x80; + + name_str = get_str(context, name_offset, sizeof(context->bios.name) - 1); + if (*name_str != '\0') + strlcpy(context->bios.name, name_str, sizeof(context->bios.name)); +} + +static bool _aura_gpu_bios_is_atom( + struct atom_context *context +){ + uint16_t base; + const char *magic; + + if (get_u16(context, 0) != ATOM_BIOS_MAGIC) + return false; + + magic = get_str(context, ATOM_ATI_MAGIC_PTR, sizeof(ATOM_ATI_MAGIC) - 1); + if (!magic || strncmp(magic, ATOM_ATI_MAGIC, sizeof(ATOM_ATI_MAGIC) - 1)) + return false; + + base = get_u16(context, ATOM_ROM_HEADER_PTR); + magic = get_str(context, (base + ATOM_ROM_MAGIC_PTR), sizeof(ATOM_ROM_MAGIC) - 1); + + if (!magic || strncmp(magic, ATOM_ROM_MAGIC, sizeof(ATOM_ROM_MAGIC) - 1)) + return false; + + return true; +} + +static void _aura_gpu_bios_data_revision_init( + struct atom_data_revision *tbl_revision, + struct atom_common_table_header *atom_data_tbl +){ + tbl_revision->major = (uint32_t)atom_data_tbl->format_revision & 0x3f; + tbl_revision->minor = (uint32_t)atom_data_tbl->content_revision & 0x3f; +} + +static bool _aura_gpu_bios_init( + struct atom_context *context +){ + // Using the latest rom header since the info we need + // is shared amongst versions. + struct atom_rom_header_v2_2 *rom_header; + struct atom_data_revision tbl_rev = {0}; + uint32_t object_info_table_offset; + uint32_t rom_header_offset; + + rom_header_offset = get_u16(context, ATOM_ROM_HEADER_PTR); + rom_header = GET_IMAGE(context, struct atom_rom_header_v2_2, rom_header_offset); + if (!rom_header) + return false; + + _aura_gpu_bios_data_revision_init(&tbl_rev, &rom_header->table_header); + context->master_data_table.revision = tbl_rev; + + // Navi BIOS + if (tbl_rev.major >= 2 && tbl_rev.minor >= 2) { + struct display_object_info_table_v1_4 *object_info_table; + + context->master_data_table.v2_1 = GET_IMAGE(context, struct atom_master_data_table_v2_1, rom_header->masterdatatable_offset); + if (!context->master_data_table.v2_1) + return false; + + object_info_table_offset = table_list_field(context, v2_1, displayobjectinfo); + // object_info_table_offset = MasterDataTableOffset(context, displayobjectinfo); + object_info_table = GET_IMAGE(context, struct display_object_info_table_v1_4, object_info_table_offset); + if (!object_info_table) + return false; + + _aura_gpu_bios_data_revision_init(&tbl_rev, &object_info_table->table_header); + if (tbl_rev.major == 1 && tbl_rev.minor >= 4) { + context->object_info_table.revision = tbl_rev; + context->object_info_table.v1_4 = object_info_table; + } else { + AURA_DBG("Unsupported object_info_table version %d.%d", tbl_rev.major, tbl_rev.minor); + return false; + } + } + + // Polaris/Vega BIOS + else { + struct display_object_info_table_v1_1 *object_info_table; + + context->master_data_table.v1_1 = GET_IMAGE(context, struct atom_master_data_table_v1_1, rom_header->masterdatatable_offset); + if (!context->master_data_table.v1_1) + return false; + + object_info_table_offset = table_list_field(context, v1_1, object_header); + // object_info_table_offset = MasterDataTableOffset(context, Object_Header); + object_info_table = GET_IMAGE(context, struct display_object_info_table_v1_1, object_info_table_offset); + if (!object_info_table) + return false; + + _aura_gpu_bios_data_revision_init(&tbl_rev, &object_info_table->table_header); + context->object_info_table.revision = tbl_rev; + + if (tbl_rev.major == 1 && tbl_rev.minor >= 3) { + struct display_object_info_table_v1_3 *tbl_v3; + + tbl_v3 = GET_IMAGE(context, struct display_object_info_table_v1_3, object_info_table_offset); + if (!tbl_v3) + return false; + + context->object_info_table.v1_3 = tbl_v3; + } else if (tbl_rev.major == 1 && tbl_rev.minor >= 1) { + context->object_info_table.v1_1 = object_info_table; + } else { + AURA_DBG("Unsupported object_info_table version %d.%d", tbl_rev.major, tbl_rev.minor); + return false; + } + } + + return true; +} + + +static uint8_t atom_get_connectors_number_v1_1( + struct atom_context *context +){ + struct atom_object_table *table; + uint32_t object_info_table_offset; + uint32_t connector_table_offset; + + object_info_table_offset = table_list_field(context, v1_1, object_header); + connector_table_offset = object_info_field(context, v1_1, usConnectorObjectTableOffset); + + table = GET_IMAGE(context, struct atom_object_table, object_info_table_offset + connector_table_offset); + if (!table) + return 0; + + return table->ucNumberOfObjects; +} + +static uint8_t atom_get_connectors_number_v2_1( + struct atom_context *context +){ + struct display_object_info_table_v1_4 *table; + uint8_t count, i; + + table = context->object_info_table.v1_4; + for (i = 0, count = 0; i < table->number_of_path; i++) { + // Is this our missing bus? + if (table->display_path[i].encoderobjid != 0) + count++; + } + + AURA_DBG("Counted %d connectors", count); + + return count; +} + + +static bool atom_bios_get_connector_id_v1_1( + struct atom_context *context, + uint8_t index, + struct graphics_object_id *object_id +){ + struct atom_object_table *table; + uint32_t object_info_table_offset; + uint32_t connector_table_offset; + uint16_t id; + + object_info_table_offset = table_list_field(context, v1_1, object_header); + connector_table_offset = object_info_field(context, v1_1, usConnectorObjectTableOffset); + + table = GET_IMAGE(context, struct atom_object_table, object_info_table_offset + connector_table_offset); + if (!table) { + AURA_ERR("Can't get connector table from atom bios."); + return false; + } + + if (table->ucNumberOfObjects <= index) { + AURA_ERR("Can't find connector id %d in connector table of size %d.", index, table->ucNumberOfObjects); + return false; + } + + id = le16_to_cpu(table->asObjects[index].usObjectID); + + if (object_id_from_bios_object_id(id, object_id)) + return true; + + return false; +} + +static bool atom_bios_get_connector_id_v2_1( + struct atom_context *context, + uint8_t index, + struct graphics_object_id *object_id +){ + struct display_object_info_table_v1_4 *table; + struct atom_display_object_path_v2 *object; + + table = context->object_info_table.v1_4; + if (table->number_of_path > index) { + object = &table->display_path[index]; + /* If display_objid is generic object id, the encoderObj + * /extencoderobjId should be 0 + */ + if (object->encoderobjid != 0 && object->display_objid != 0) + return object_id_from_bios_object_id(object->display_objid, object_id); + } + + return false; +} + + +static error_t atom_bios_get_i2c_info_v1_1( + struct atom_context *context, + struct graphics_object_id *object_id, + struct i2c_info *info +){ + uint32_t offset; + struct atom_object *object; + struct atom_common_record_header *header; + struct atom_i2c_record *record; + + object = get_bios_object_v1_1(context, *object_id); + if (!object) + return BP_RESULT_BADINPUT; + + offset = table_list_field(context, v1_1, object_header); + offset += le16_to_cpu(object->usRecordOffset); + + for (;;) { + header = GET_IMAGE(context, struct atom_common_record_header, offset); + if (!header) + return BP_RESULT_BADBIOSTABLE; + + if (ATOM_RECORD_END_TYPE == header->record_type || !header->record_size) + break; + + if (ATOM_I2C_RECORD_TYPE == header->record_type && sizeof(struct atom_i2c_record) <= header->record_size) { + /* get the I2C info */ + record = (struct atom_i2c_record *)header; + + if (get_gpio_i2c_info_v1_1(context, record, info) == BP_RESULT_OK) + return BP_RESULT_OK; + } + + offset += header->record_size; + } + + return BP_RESULT_NORECORD; +} + +static error_t atom_bios_get_i2c_info_v2_1( + struct atom_context *context, + struct graphics_object_id *object_id, + struct i2c_info *info +){ + struct atom_display_object_path_v2 *object; + struct atom_common_record_header *header; + struct atom_i2c_record *record; + uint16_t object_info_table_offset; + uint32_t offset; + + object = get_bios_object_v2_1(context, *object_id); + if (!object) + return BP_RESULT_BADINPUT; + + object_info_table_offset = table_list_field(context, v2_1, displayobjectinfo); + offset = object->disp_recordoffset + object_info_table_offset; + + for (;;) { + header = GET_IMAGE(context, struct atom_common_record_header, offset); + if (!header) + return BP_RESULT_BADBIOSTABLE; + + if (header->record_type == ATOM_RECORD_END_TYPE || !header->record_size) + break; + + if (header->record_type == ATOM_I2C_RECORD_TYPE && sizeof(struct atom_i2c_record) <= header->record_size) { + /* get the I2C info */ + record = (struct atom_i2c_record *)header; + + if (get_gpio_i2c_info_v2_1(context, record, info) == BP_RESULT_OK) + return BP_RESULT_OK; + } + + offset += header->record_size; + } + + return BP_RESULT_NORECORD; +} + + +uint8_t atom_bios_get_connectors_number( + struct atom_bios *bios +){ + struct atom_context *context = to_atom_context(bios); + + if (WARN_ON(bios == NULL)) + return 0; + + switch (table_revision(context->master_data_table)) { + case VERSION_1_1: + case VERSION_1_2: + case VERSION_1_3: + case VERSION_1_4: + return atom_get_connectors_number_v1_1(context); + case VERSION_2_1: + case VERSION_2_2: + return atom_get_connectors_number_v2_1(context); + default: + AURA_ERR("Unexpected master_data_table version, %x", table_revision(context->master_data_table)); + return 0; + } +} + +bool atom_bios_get_connector_id( + struct atom_bios *bios, + uint8_t index, + struct graphics_object_id *object_id +){ + struct atom_context *context = to_atom_context(bios); + + if (WARN_ON(bios == NULL)) + return false; + + if (WARN_ON(object_id == NULL)) + return false; + + switch (table_revision(context->master_data_table)) { + case VERSION_1_1: + case VERSION_1_2: + case VERSION_1_3: + case VERSION_1_4: + return atom_bios_get_connector_id_v1_1(context, index, object_id); + case VERSION_2_1: + case VERSION_2_2: + return atom_bios_get_connector_id_v2_1(context, index, object_id); + default: + *object_id = dal_graphics_object_id_init( + 0, ENUM_ID_UNKNOWN, OBJECT_TYPE_UNKNOWN); + AURA_ERR("Unexpected master_data_table version"); + return false; + } +} + +error_t atom_bios_get_i2c_info( + struct atom_bios *bios, + struct graphics_object_id *object_id, + struct i2c_info *info +){ + struct atom_context *context = to_atom_context(bios); + + if (WARN_ON(bios == NULL)) + return -EINVAL; + + if (WARN_ON(object_id == NULL)) + return -EINVAL; + + if (WARN_ON(info == NULL)) + return -EINVAL; + + switch (table_revision(context->master_data_table)) { + case VERSION_1_1: + case VERSION_1_2: + case VERSION_1_3: + case VERSION_1_4: + return atom_bios_get_i2c_info_v1_1(context, object_id, info); + case VERSION_2_1: + case VERSION_2_2: + return atom_bios_get_i2c_info_v2_1(context, object_id, info); + default: + AURA_ERR("Unexpected master_data_table version"); + return false; + } +} + +struct atom_bios* atom_bios_create( + struct pci_dev *pci_dev +){ + struct atom_context *context; + struct atom_bios *bios; + uint8_t __iomem *buffer; + size_t size; + error_t err; + + buffer = pci_map_rom(pci_dev, &size); + if (!buffer) { + AURA_DBG("pci_map_rom() failed"); + return ERR_PTR(-EIO); + } + + context = kzalloc(sizeof(*context) + size, GFP_KERNEL); + if (context == NULL) { + pci_unmap_rom(pci_dev, buffer); + return ERR_PTR(-ENOMEM); + } + + bios = &context->bios; + memcpy_fromio(context->data, buffer, size); + pci_unmap_rom(pci_dev, buffer); + + // temporarily set size to full memory region + context->size = size; + if (!_aura_gpu_bios_is_atom(context)) { + AURA_DBG("Not an ATOM bios"); + err = -EINVAL; + goto error; + } + + // set size according to bios header + size = get_u8(context, ATOM_IMAGE_SIZE_PTR) * ATOM_IMAGE_SIZE_UNIT; + if (size > context->size) { + AURA_DBG("Bios size (%ld) is greater than memory size (%ld)", size, context->size); + err = -EINVAL; + goto error; + } + context->size = size; + + _aura_gpu_bios_read_name(context); + + AURA_DBG("Detected ATOM bios: %s", context->bios.name); + + if (!_aura_gpu_bios_init(context)) { + err = -EINVAL; + goto error; + } + + bios->size = context->size; + bios->data = context->data; + + return bios; + +error: + kfree(context); + + return ERR_PTR(err); +} + +void atom_bios_release( + struct atom_bios* bios +){ + struct atom_context *context = to_atom_context(bios); + + if (WARN_ON(bios == NULL)) + return; + + kfree(context); +} + +error_t atom_bios_get_gpio_info( + struct atom_bios *bios, + uint8_t index, + struct i2c_info *info +){ + struct atom_context *context = to_atom_context(bios); + struct atom_gpio_i2c_info *header; + struct atom_gpio_i2c_assigment *pin; + uint32_t table_count; + uint32_t i2c_table_offset; + + if (WARN_ON(info == NULL)) + return BP_RESULT_BADINPUT; + + i2c_table_offset = table_list_field(context, v1_1, gpio_i2c_info); + header = GET_IMAGE(context, struct atom_gpio_i2c_info, i2c_table_offset); + if (!header) + return BP_RESULT_BADBIOSTABLE; + + if (sizeof(struct atom_common_table_header) + + sizeof(struct atom_gpio_i2c_assigment) > + le16_to_cpu(header->table_header.structuresize) + ){ + return BP_RESULT_BADBIOSTABLE; + } + + if (1 != header->table_header.content_revision) + return BP_RESULT_UNSUPPORTED; + + /* get data count */ + table_count = (le16_to_cpu(header->table_header.structuresize) - + sizeof(struct atom_common_table_header)) / + sizeof(struct atom_gpio_i2c_assigment); + + if (table_count < index) + return BP_RESULT_BADBIOSTABLE; + + pin = &header->asGPIO_Info[index]; + + /* get the GPIO_I2C_INFO */ + info->hw_assist = pin->sucI2cId.sbfAccess.bfHW_Capable ? true : false; + info->line = pin->sucI2cId.sbfAccess.bfI2C_LineMux; + info->engine_id = pin->sucI2cId.sbfAccess.bfHW_EngineID; + + // info->i2c_slave_address = record->i2c_slave_addr; + + info->gpio_info.clk_mask_register_index = le16_to_cpu(pin->usClkMaskRegisterIndex); + info->gpio_info.clk_en_register_index = le16_to_cpu(pin->usClkEnRegisterIndex); + info->gpio_info.clk_y_register_index = le16_to_cpu(pin->usClkY_RegisterIndex); + info->gpio_info.clk_a_register_index = le16_to_cpu(pin->usClkA_RegisterIndex); + info->gpio_info.data_mask_register_index = le16_to_cpu(pin->usDataMaskRegisterIndex); + info->gpio_info.data_en_register_index = le16_to_cpu(pin->usDataEnRegisterIndex); + info->gpio_info.data_y_register_index = le16_to_cpu(pin->usDataY_RegisterIndex); + info->gpio_info.data_a_register_index = le16_to_cpu(pin->usDataA_RegisterIndex); + + info->gpio_info.clk_mask_shift = pin->ucClkMaskShift; + info->gpio_info.clk_en_shift = pin->ucClkEnShift; + info->gpio_info.clk_y_shift = pin->ucClkY_Shift; + info->gpio_info.clk_a_shift = pin->ucClkA_Shift; + info->gpio_info.data_mask_shift = pin->ucDataMaskShift; + info->gpio_info.data_en_shift = pin->ucDataEnShift; + info->gpio_info.data_y_shift = pin->ucDataY_Shift; + info->gpio_info.data_a_shift = pin->ucDataA_Shift; + + info->gpio_info.clk_mask_mask = (1 << pin->ucClkMaskShift); + info->gpio_info.clk_en_mask = (1 << pin->ucClkEnShift); + info->gpio_info.clk_y_mask = (1 << pin->ucClkY_Shift); + info->gpio_info.clk_a_mask = (1 << pin->ucClkA_Shift); + info->gpio_info.data_mask_mask = (1 << pin->ucDataMaskShift); + info->gpio_info.data_en_mask = (1 << pin->ucDataEnShift); + info->gpio_info.data_y_mask = (1 << pin->ucDataY_Shift); + info->gpio_info.data_a_mask = (1 << pin->ucDataA_Shift); + + + // Lines 4, 6 and 7 are not being used + // for (i = 0; i < table_count; i++) { + // pin = &header->asGPIO_Info[i]; + // + // AURA_DBG("line: %d, hw: %s, eng: %d", + // header->asGPIO_Info[i].sucI2cId.sbfAccess.bfI2C_LineMux, + // header->asGPIO_Info[i].sucI2cId.sbfAccess.bfHW_Capable ? "true" : "false", + // header->asGPIO_Info[i].sucI2cId.sbfAccess.bfHW_EngineID + // ); + // AURA_DBG(" clk_mask %x, clk_en %x, clk_y %x, clk_a %x, data_mask %x, data_en %x, data_y %x, data_a %x", + // le16_to_cpu(pin->usClkMaskRegisterIndex), + // le16_to_cpu(pin->usClkEnRegisterIndex), + // le16_to_cpu(pin->usClkY_RegisterIndex), + // le16_to_cpu(pin->usClkA_RegisterIndex), + // le16_to_cpu(pin->usDataMaskRegisterIndex), + // le16_to_cpu(pin->usDataEnRegisterIndex), + // le16_to_cpu(pin->usDataY_RegisterIndex), + // le16_to_cpu(pin->usDataA_RegisterIndex) + // ); + // } + + return BP_RESULT_OK; +} + + + + +static error_t get_crystal_frequency_v1_4 ( + struct atom_context *context, + uint32_t *frequency +){ + uint32_t offset = table_list_field(context, v1_1, firmwareinfo); + struct atom_firmware_info_v1_4 *firmware_info = GET_IMAGE(context, struct atom_firmware_info_v1_4, offset); + + if (!firmware_info) + return BP_RESULT_BADBIOSTABLE; + + /* Pixel clock pll information. We need to convert from 10KHz units into + * KHz units */ + *frequency = le16_to_cpu(firmware_info->usReferenceClock) * 10; + + return 0; +} + +static error_t get_crystal_frequency_v2_1 ( + struct atom_context *context, + uint32_t *frequency +){ + uint32_t offset = table_list_field(context, v1_1, firmwareinfo); + struct atom_firmware_info_v2_1 *firmware_info = GET_IMAGE(context, struct atom_firmware_info_v2_1, offset); + + if (!firmware_info) + return BP_RESULT_BADBIOSTABLE; + + /* Pixel clock pll information. We need to convert from 10KHz units into + * KHz units */ + *frequency = le16_to_cpu(firmware_info->usCoreReferenceClock) * 10; + + return 0; +} + +static error_t get_crystal_frequency_v2_2 ( + struct atom_context *context, + uint32_t *frequency +){ + uint32_t offset = table_list_field(context, v1_1, firmwareinfo); + struct atom_firmware_info_v2_2 *firmware_info = GET_IMAGE(context, struct atom_firmware_info_v2_2, offset); + + if (!firmware_info) + return BP_RESULT_BADBIOSTABLE; + + /* Pixel clock pll information. We need to convert from 10KHz units into + * KHz units */ + *frequency = le16_to_cpu(firmware_info->usCoreReferenceClock) * 10; + + return 0; +} + +static error_t get_crystal_frequency_v3_1 ( + struct atom_context *context, + uint32_t *frequency +){ + uint32_t offset = table_list_field(context, v2_1, dce_info); + struct atom_display_controller_info_v4_1 *dce_info = NULL; + + dce_info = GET_IMAGE(context, struct atom_display_controller_info_v4_1, offset); + + if (!dce_info) + return BP_RESULT_BADBIOSTABLE; + + /* 27MHz for Vega10 & Vega12; 100MHz for Vega20 */ + *frequency = dce_info->dce_refclk_10khz * 10; + + return 0; +} + +error_t atom_bios_get_crystal_frequency ( + struct atom_bios* bios, + uint32_t *frequency +){ + struct atom_context *context = to_atom_context(bios); + struct atom_common_table_header *header; + struct atom_data_revision firmware_rev = {0}; + uint32_t offset; + + if (WARN_ON(bios == NULL || frequency == NULL)) + return -EINVAL; + + switch (table_revision(context->master_data_table)) { + case VERSION_1_1: + case VERSION_1_2: + case VERSION_1_3: + case VERSION_1_4: + offset = table_list_field(context, v1_1, firmwareinfo); + break; + case VERSION_2_1: + case VERSION_2_2: + offset = table_list_field(context, v2_1, firmwareinfo); + break; + default: + AURA_ERR("Unexpected master_data_table version"); + return BP_RESULT_BADBIOSTABLE; + } + + header = GET_IMAGE(context, struct atom_common_table_header, offset); + if (!header) + return BP_RESULT_BADBIOSTABLE; + + _aura_gpu_bios_data_revision_init(&firmware_rev, header); + + switch (revision(firmware_rev)) { + case VERSION_1_1: + case VERSION_1_2: + case VERSION_1_3: + case VERSION_1_4: + return get_crystal_frequency_v1_4(context, frequency); + case VERSION_2_1: + return get_crystal_frequency_v2_1(context, frequency); + case VERSION_2_2: + return get_crystal_frequency_v2_2(context, frequency); + case VERSION_3_1: + case VERSION_3_2: + case VERSION_3_3: + return get_crystal_frequency_v3_1(context, frequency); + default: + AURA_ERR("Unexpected master_data_table version"); + return BP_RESULT_BADBIOSTABLE; + } +} diff --git a/aura-gpu-bios.h b/aura-gpu-bios.h new file mode 100644 index 0000000..dcc8f38 --- /dev/null +++ b/aura-gpu-bios.h @@ -0,0 +1,94 @@ +#ifndef _UAPI_AURA_GPU_BIOS_H +#define _UAPI_AURA_GPU_BIOS_H + +#include +#include "include/types.h" + +struct atom_bios { + char name[20]; + size_t size; // TODO + const char* data; +}; + +struct gpio_info { + uint32_t clk_mask_register_index; + uint32_t clk_en_register_index; + uint32_t clk_y_register_index; + uint32_t clk_a_register_index; + uint32_t data_mask_register_index; + uint32_t data_en_register_index; + uint32_t data_y_register_index; + uint32_t data_a_register_index; + + uint8_t clk_mask_shift; + uint8_t clk_en_shift; + uint8_t clk_y_shift; + uint8_t clk_a_shift; + uint8_t data_mask_shift; + uint8_t data_en_shift; + uint8_t data_y_shift; + uint8_t data_a_shift; + + uint32_t clk_mask_mask; + uint32_t clk_en_mask; + uint32_t clk_y_mask; + uint32_t clk_a_mask; + uint32_t data_mask_mask; + uint32_t data_en_mask; + uint32_t data_y_mask; + uint32_t data_a_mask; +}; + +struct i2c_info { + struct gpio_info gpio_info; + + bool hw_assist; + + uint32_t line; + uint32_t engine_id; + uint32_t slave_address; +}; + +// #define aura_i2c_info graphics_object_i2c_info + +struct gpio_pin_info { + uint32_t offset; + uint32_t offset_y; + uint32_t offset_en; + uint32_t offset_mask; + + uint32_t mask; + uint32_t mask_y; + uint32_t mask_en; + uint32_t mask_mask; +}; + +struct graphics_object_id { + uint32_t id:8; + uint32_t enum_id:4; + uint32_t type:4; + uint32_t reserved:16; /* for padding. total size should be u32 */ +}; + +error_t +atom_bios_get_gpio_info (struct atom_bios *bios, uint8_t index, struct i2c_info *info); + +uint8_t +atom_bios_get_connectors_number (struct atom_bios *bios); + +bool +atom_bios_get_connector_id (struct atom_bios *bios, uint8_t index, struct graphics_object_id *object_id); + +error_t +atom_bios_get_i2c_info (struct atom_bios *bios, struct graphics_object_id *object_id, struct i2c_info *info); + +error_t +atom_bios_get_crystal_frequency (struct atom_bios* bios, uint32_t *frequency); + +struct atom_bios* +atom_bios_create (struct pci_dev *pci_dev); + +void +atom_bios_release (struct atom_bios* bios); + +#endif diff --git a/aura-gpu-hw.c b/aura-gpu-hw.c new file mode 100644 index 0000000..f064b79 --- /dev/null +++ b/aura-gpu-hw.c @@ -0,0 +1,476 @@ +// SPDX-License-Identifier: GPL-2.0 +#include + +#include "debug.h" +#include "pci_ids.h" +#include "aura-gpu-hw.h" +#include "aura-gpu-bios.h" +#include "aura-gpu-reg.h" +#include "atom/atom.h" + +struct ATOM_MASTER_LIST_OF_COMMAND_TABLES { + uint16_t ASIC_Init; //Function Table, used by various SW components,latest version 1.1 + uint16_t GetDisplaySurfaceSize; //Atomic Table, Used by Bios when enabling HW ICON + uint16_t ASIC_RegistersInit; //Atomic Table, indirectly used by various SW components,called from ASIC_Init + uint16_t VRAM_BlockVenderDetection; //Atomic Table, used only by Bios + uint16_t DIGxEncoderControl; //Only used by Bios + uint16_t MemoryControllerInit; //Atomic Table, indirectly used by various SW components,called from ASIC_Init + uint16_t EnableCRTCMemReq; //Function Table,directly used by various SW components,latest version 2.1 + uint16_t MemoryParamAdjust; //Atomic Table, indirectly used by various SW components,called from SetMemoryClock if needed + uint16_t DVOEncoderControl; //Function Table,directly used by various SW components,latest version 1.2 + uint16_t GPIOPinControl; //Atomic Table, only used by Bios + uint16_t SetEngineClock; //Function Table,directly used by various SW components,latest version 1.1 + uint16_t SetMemoryClock; //Function Table,directly used by various SW components,latest version 1.1 + uint16_t SetPixelClock; //Function Table,directly used by various SW components,latest version 1.2 + uint16_t EnableDispPowerGating; //Atomic Table, indirectly used by various SW components,called from ASIC_Init + uint16_t ResetMemoryDLL; //Atomic Table, indirectly used by various SW components,called from SetMemoryClock + uint16_t ResetMemoryDevice; //Atomic Table, indirectly used by various SW components,called from SetMemoryClock + uint16_t MemoryPLLInit; //Atomic Table, used only by Bios + uint16_t AdjustDisplayPll; //Atomic Table, used by various SW componentes. + uint16_t AdjustMemoryController; //Atomic Table, indirectly used by various SW components,called from SetMemoryClock + uint16_t EnableASIC_StaticPwrMgt; //Atomic Table, only used by Bios + uint16_t SetUniphyInstance; //Atomic Table, only used by Bios + uint16_t DAC_LoadDetection; //Atomic Table, directly used by various SW components,latest version 1.2 + uint16_t LVTMAEncoderControl; //Atomic Table,directly used by various SW components,latest version 1.3 + uint16_t HW_Misc_Operation; //Atomic Table, directly used by various SW components,latest version 1.1 + uint16_t DAC1EncoderControl; //Atomic Table, directly used by various SW components,latest version 1.1 + uint16_t DAC2EncoderControl; //Atomic Table, directly used by various SW components,latest version 1.1 + uint16_t DVOOutputControl; //Atomic Table, directly used by various SW components,latest version 1.1 + uint16_t CV1OutputControl; //Atomic Table, Atomic Table, Obsolete from Ry6xx, use DAC2 Output instead + uint16_t GetConditionalGoldenSetting; //Only used by Bios + uint16_t SMC_Init; //Function Table,directly used by various SW components,latest version 1.1 + uint16_t PatchMCSetting; //only used by BIOS + uint16_t MC_SEQ_Control; //only used by BIOS + uint16_t Gfx_Harvesting; //Atomic Table, Obsolete from Ry6xx, Now only used by BIOS for GFX harvesting + uint16_t EnableScaler; //Atomic Table, used only by Bios + uint16_t BlankCRTC; //Atomic Table, directly used by various SW components,latest version 1.1 + uint16_t EnableCRTC; //Atomic Table, directly used by various SW components,latest version 1.1 + uint16_t GetPixelClock; //Atomic Table, directly used by various SW components,latest version 1.1 + uint16_t EnableVGA_Render; //Function Table,directly used by various SW components,latest version 1.1 + uint16_t GetSCLKOverMCLKRatio; //Atomic Table, only used by Bios + uint16_t SetCRTC_Timing; //Atomic Table, directly used by various SW components,latest version 1.1 + uint16_t SetCRTC_OverScan; //Atomic Table, used by various SW components,latest version 1.1 + uint16_t GetSMUClockInfo; //Atomic Table, used only by Bios + uint16_t SelectCRTC_Source; //Atomic Table, directly used by various SW components,latest version 1.1 + uint16_t EnableGraphSurfaces; //Atomic Table, used only by Bios + uint16_t UpdateCRTC_DoubleBufferRegisters; //Atomic Table, used only by Bios + uint16_t LUT_AutoFill; //Atomic Table, only used by Bios + uint16_t SetDCEClock; //Atomic Table, start from DCE11.1, shared by driver and VBIOS, change DISPCLK and DPREFCLK + uint16_t GetMemoryClock; //Atomic Table, directly used by various SW components,latest version 1.1 + uint16_t GetEngineClock; //Atomic Table, directly used by various SW components,latest version 1.1 + uint16_t SetCRTC_UsingDTDTiming; //Atomic Table, directly used by various SW components,latest version 1.1 + uint16_t ExternalEncoderControl; //Atomic Table, directly used by various SW components,latest version 2.1 + uint16_t LVTMAOutputControl; //Atomic Table, directly used by various SW components,latest version 1.1 + uint16_t VRAM_BlockDetectionByStrap; //Atomic Table, used only by Bios + uint16_t MemoryCleanUp; //Atomic Table, only used by Bios + uint16_t ProcessI2cChannelTransaction; //Function Table,only used by Bios + uint16_t WriteOneByteToHWAssistedI2C; //Function Table,indirectly used by various SW components + uint16_t ReadHWAssistedI2CStatus; //Atomic Table, indirectly used by various SW components + uint16_t SpeedFanControl; //Function Table,indirectly used by various SW components,called from ASIC_Init + uint16_t PowerConnectorDetection; //Atomic Table, directly used by various SW components,latest version 1.1 + uint16_t MC_Synchronization; //Atomic Table, indirectly used by various SW components,called from SetMemoryClock + uint16_t ComputeMemoryEnginePLL; //Atomic Table, indirectly used by various SW components,called from SetMemory/EngineClock + uint16_t Gfx_Init; //Atomic Table, indirectly used by various SW components,called from SetMemory or SetEngineClock + uint16_t VRAM_GetCurrentInfoBlock; //Atomic Table, used only by Bios + uint16_t DynamicMemorySettings; //Atomic Table, indirectly used by various SW components,called from SetMemoryClock + uint16_t MemoryTraining; //Atomic Table, used only by Bios + uint16_t EnableSpreadSpectrumOnPPLL; //Atomic Table, directly used by various SW components,latest version 1.2 + uint16_t TMDSAOutputControl; //Atomic Table, directly used by various SW components,latest version 1.1 + uint16_t SetVoltage; //Function Table,directly and/or indirectly used by various SW components,latest version 1.1 + uint16_t DAC1OutputControl; //Atomic Table, directly used by various SW components,latest version 1.1 + uint16_t ReadEfuseValue; //Atomic Table, directly used by various SW components,latest version 1.1 + uint16_t ComputeMemoryClockParam; //Function Table,only used by Bios, obsolete soon.Switch to use "ReadEDIDFromHWAssistedI2C" + uint16_t ClockSource; //Atomic Table, indirectly used by various SW components,called from ASIC_Init + uint16_t MemoryDeviceInit; //Atomic Table, indirectly used by various SW components,called from SetMemoryClock + uint16_t GetDispObjectInfo; //Atomic Table, indirectly used by various SW components,called from EnableVGARender + uint16_t DIG1EncoderControl; //Atomic Table,directly used by various SW components,latest version 1.1 + uint16_t DIG2EncoderControl; //Atomic Table,directly used by various SW components,latest version 1.1 + uint16_t DIG1TransmitterControl; //Atomic Table,directly used by various SW components,latest version 1.1 + uint16_t DIG2TransmitterControl; //Atomic Table,directly used by various SW components,latest version 1.1 + uint16_t ProcessAuxChannelTransaction; //Function Table,only used by Bios + uint16_t DPEncoderService; //Function Table,only used by Bios + uint16_t GetVoltageInfo; //Function Table,only used by Bios since SI +}; + +#define GetIndexIntoMasterTable(MasterOrData, FieldName) ( \ + (((char*)(&((struct ATOM_MASTER_LIST_OF_##MasterOrData##_TABLES*)0)->FieldName)-(char*)0)/sizeof(uint16_t)) \ +) + +struct hw_i2c_context { + struct card_info atom_card_info; + struct atom_context *atom_context; + struct aura_reg_service *reg_service; + struct atom_bios *bios; + struct i2c_adapter adapter; + bool registered; + + uint8_t scratch[20 * 1024]; +}; +#define context_from_adapter(ptr) ( \ + container_of(ptr, struct hw_i2c_context, adapter) \ +) +#define context_from_card(ptr) ( \ + container_of(ptr, struct hw_i2c_context, atom_card_info) \ +) + +static uint32_t __invalid_read ( + struct card_info *info, + uint32_t reg +){ + AURA_DBG("non MM read called"); + return 0; +} + +static void __invalid_write ( + struct card_info *info, + uint32_t reg, + uint32_t val +){ + AURA_DBG("non MM write called"); +} + +static uint32_t mm_read ( + struct card_info *info, + uint32_t reg +){ + uint32_t res; + struct hw_i2c_context *ctx = context_from_card(info); + + res = reg_read(ctx->reg_service, reg); + AURA_DBG("Read reg %x %x", reg, res); + + return res; +} + +static void mm_write ( + struct card_info *info, + uint32_t reg, + uint32_t val +){ + struct hw_i2c_context *ctx = context_from_card(info); + + AURA_DBG("Writing reg %x val %x", reg, val); + reg_write(ctx->reg_service, reg, val); +} + +#define TARGET_HW_I2C_CLOCK 50 +#define ATOM_MAX_HW_I2C_WRITE 3 +#define ATOM_MAX_HW_I2C_READ 255 +#define HW_I2C_WRITE 1 +#define HW_I2C_READ 0 +#define HW_ASSISTED_I2C_STATUS_FAILURE 2 +#define HW_ASSISTED_I2C_STATUS_SUCCESS 1 + +struct transaction_parameters { + uint8_t ucI2CSpeed; + union { + uint8_t ucRegIndex; + uint8_t ucStatus; + }; + uint16_t lpI2CDataOut; + uint8_t ucFlag; + uint8_t ucTransBytes; + uint8_t ucSlaveAddr; + uint8_t ucLineNumber; +}; + +static error_t aura_gpu_i2c_process_i2c_ch( + struct hw_i2c_context *context, + uint8_t slave_addr, + uint8_t offset, + uint8_t flags, + uint8_t *buf +){ + struct transaction_parameters args; + int index = GetIndexIntoMasterTable(COMMAND, ProcessI2cChannelTransaction); + error_t err = 0; + + memset(&args, 0, sizeof(args)); + + if (flags & HW_I2C_WRITE) { + args.lpI2CDataOut = buf ? cpu_to_le16(*buf) : 0; + } else if (!buf) { + err = -EINVAL; + goto done; + } else { + args.lpI2CDataOut = 0; + } + + args.ucFlag = flags; + args.ucI2CSpeed = TARGET_HW_I2C_CLOCK; + args.ucTransBytes = 1; + args.ucSlaveAddr = slave_addr << 1; + args.ucRegIndex = offset; + args.ucLineNumber = 6; + + AURA_DBG("Pre Transaction: addr = %x, offset = %x, rw = %s, count = %d, out = %x", + args.ucSlaveAddr >> 1, + offset, + args.ucFlag == HW_I2C_READ ? "r" : "w", + args.ucTransBytes, + args.lpI2CDataOut + ); + + atom_execute_table(context->atom_context, index, (uint32_t *)&args); + + AURA_DBG( + "Post Transaction: status = %x, read = %x", + args.ucStatus, + args.lpI2CDataOut + ); + + /* error */ + if (args.ucStatus != HW_ASSISTED_I2C_STATUS_SUCCESS) { + AURA_DBG("hw_i2c error %x", args.ucStatus); + err = -EIO; + goto done; + } + + if (!(flags & HW_I2C_WRITE)) { + *buf = (uint8_t)args.lpI2CDataOut; + } + +done: + return err; +} + +#define dump_i2c_msg(num, msg) ({ \ + AURA_DBG( \ + "Message %d addr = %x, len = %d, flags = %x, data = %x %x", \ + (num), \ + (msg).addr, \ + (msg).len, \ + (msg).flags, \ + (msg).buf[0], \ + (msg).buf[1] \ + ); \ +}) + +static int aura_gpu_i2c_xfer( + struct i2c_adapter *i2c_adap, + struct i2c_msg *msgs, + int num +){ + struct hw_i2c_context *context = i2c_get_adapdata(i2c_adap); + int remaining, buffer_offset; + uint8_t flags; + uint8_t offset; + uint8_t address; + error_t err; + int i; + + /* check for bus probe */ + if ((num == 1) && (msgs[0].len == 0)) { + if (msgs[0].len == 0) { + // AURA_DBG("Bus probe detected %x", msgs[0].addr); + err = aura_gpu_i2c_process_i2c_ch( + context, + msgs[0].addr, + 0, + HW_I2C_WRITE, + NULL + ); + } + + return err ? err : num; + } else if ((num == 1) && (msgs[0].len > 1) && !(msgs[0].flags & I2C_M_RD)) { + // The first data byte should be the offset, remainder are the values + offset = msgs[0].buf[0]; + address = msgs[0].addr; + remaining = msgs[0].len - 1; + buffer_offset = 1; + + while (remaining) { + err = aura_gpu_i2c_process_i2c_ch( + context, + address, + offset, + HW_I2C_WRITE, + &msgs[0].buf[buffer_offset] + ); + + if (err) + return err; + + offset++; + remaining--; + buffer_offset++; + } + } else if (num == 2) { + // The first message should be to set the offset + if (!(msgs[0].flags & I2C_M_RD) && msgs[0].len == 1) { + offset = msgs[0].buf[0]; + address = msgs[0].addr; + } else { + return -EIO; + } + + // The second message should describe how many bytes to read/write + remaining = msgs[1].len; + buffer_offset = 0; + flags = (msgs[1].flags & I2C_M_RD) ? HW_I2C_READ : HW_I2C_WRITE; + + while (remaining) { + err = aura_gpu_i2c_process_i2c_ch( + context, + address, + offset, + flags, + &msgs[1].buf[buffer_offset] + ); + + if (err) + return err; + + offset++; + remaining--; + buffer_offset++; + } + } else { + AURA_DBG("Failed to process messages"); + for (i = 0; i < num; i++) + dump_i2c_msg(i, msgs[i]); + + return -EIO; + } + + return num; +} + +static uint32_t aura_gpu_i2c_func( + struct i2c_adapter *adap +){ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm aura_gpu_i2c_algo = { + .master_xfer = aura_gpu_i2c_xfer, + .functionality = aura_gpu_i2c_func, +}; + + +static void aura_gpu_i2c_destroy ( + struct hw_i2c_context *context +){ + if (context->bios) + atom_bios_release(context->bios); + + if (context->reg_service) + aura_gpu_reg_destroy(context->reg_service); + + if (context->atom_context) + atom_destroy(context->atom_context); + + if (context->registered) { + i2c_set_adapdata(&context->adapter, NULL); + i2c_del_adapter(&context->adapter); + } + + kfree(context); +} + +static struct hw_i2c_context *aura_gpu_i2c_create ( + struct pci_dev *pci_dev +){ + error_t err; + struct hw_i2c_context *context = kzalloc(sizeof(*context), GFP_KERNEL); + + if (!context) + return ERR_PTR(-ENOMEM); + + context->bios = atom_bios_create(pci_dev); + if (IS_ERR_OR_NULL(context->bios)) { + err = CLEAR_ERR(context->bios); + goto error_free_all; + } + + context->reg_service = aura_gpu_reg_create(pci_dev); + if (IS_ERR_OR_NULL(context->reg_service)) { + err = CLEAR_ERR(context->reg_service); + goto error_free_all; + } + + context->atom_card_info.reg_read = mm_read; + context->atom_card_info.reg_write = mm_write; + + context->atom_card_info.ioreg_read = __invalid_read; + context->atom_card_info.ioreg_write = __invalid_write; + context->atom_card_info.mc_read = __invalid_read; + context->atom_card_info.mc_write = __invalid_write; + context->atom_card_info.pll_read = __invalid_read; + context->atom_card_info.pll_write = __invalid_write; + + context->atom_context = atom_parse(&context->atom_card_info, context->bios->data); + if (!context->atom_context) { + kfree(context); + return ERR_PTR(-ENOMEM); + } + + mutex_init(&context->atom_context->mutex); + context->atom_context->scratch = (uint32_t*)context->scratch; + context->atom_context->scratch_size_bytes = sizeof(context->scratch); + context->adapter.owner = THIS_MODULE; + context->adapter.class = I2C_CLASS_DDC; + + i2c_set_adapdata(&context->adapter, context); + + snprintf(context->adapter.name, sizeof(context->adapter.name), "AURA GPU adapter"); + context->adapter.algo = &aura_gpu_i2c_algo; + + err = i2c_add_adapter(&context->adapter); + if (err) + goto error_free_all; + + context->registered = true; + + return context; + +error_free_all: + aura_gpu_i2c_destroy(context); + + return ERR_PTR(err); +} + +static struct pci_dev *find_pci_dev ( + void +){ + struct pci_dev *pci_dev = NULL; + const struct pci_device_id *match; + + while (NULL != (pci_dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pci_dev))) { + match = pci_match_id(pciidlist, pci_dev); + if (match) + return pci_dev; + } + + return NULL; +} + +struct i2c_adapter *aura_i2c_bios_create ( + void +){ + struct pci_dev *pci_dev = find_pci_dev(); + struct hw_i2c_context *context; + + if (!pci_dev) { + AURA_ERR("Failed to find a valid pci device"); + return NULL; + } + + context = aura_gpu_i2c_create(pci_dev); + if (IS_ERR_OR_NULL(context)) + return ERR_PTR(CLEAR_ERR(context)); + + return &context->adapter; +} + +void aura_i2c_bios_destroy ( + struct i2c_adapter *i2c_adapter +){ + struct hw_i2c_context *context = context_from_adapter(i2c_adapter); + + if (IS_NULL(i2c_adapter)) + return; + + aura_gpu_i2c_destroy(context); +} diff --git a/aura-gpu-hw.h b/aura-gpu-hw.h new file mode 100644 index 0000000..5435f4b --- /dev/null +++ b/aura-gpu-hw.h @@ -0,0 +1,14 @@ +#ifndef _UAPI_AURA_GPU_HW_I2C_H +#define _UAPI_AURA_GPU_HW_I2C_H + +#include + +struct i2c_adapter *aura_i2c_bios_create ( + void +); + +void aura_i2c_bios_destroy ( + struct i2c_adapter *i2c_adapter +); + +#endif diff --git a/aura-gpu-i2c.c b/aura-gpu-i2c.c index 6a88f9b..e536f63 100644 --- a/aura-gpu-i2c.c +++ b/aura-gpu-i2c.c @@ -3,6 +3,7 @@ #include #include "debug.h" +#include "pci_ids.h" #include "aura-gpu-reg.h" #include "aura-gpu-i2c.h" #include "asic/asic-registers.h" diff --git a/aura-gpu-i2c.h b/aura-gpu-i2c.h index da702d2..5e85419 100644 --- a/aura-gpu-i2c.h +++ b/aura-gpu-i2c.h @@ -5,30 +5,6 @@ #include "aura-gpu-reg.h" #include "asic/asic-types.h" -/* - AMD devices require an i2c adapter to be created, - NVIDIA devices already have the adapter loaded. - */ -static const struct pci_device_id pciidlist[] = { - {0x1002, 0x67df, 0x1043, 0x0517, 0, 0, CHIP_POLARIS10}, // RX580 (Strix) - {0x1002, 0x687F, 0x1043, 0x0555, 0, 0, CHIP_VEGA10}, // Vega 56 (Strix) - // {0x1002, 0x731f, 0x1043, 0x04e2, 0, 0, CHIP_NAVI10}, // RX5700XT (Strix) - {0, 0, 0}, -}; - -// struct aura_i2c_service { -// // void *private; -// struct i2c_adapter *adapter; -// }; - -/* declared in aura-gpu.h */ -// enum aura_asic_type; - -// struct aura_i2c_service *aura_gpu_i2c_create ( -// struct pci_dev *pci_dev, -// enum aura_asic_type asic_type -// ); - struct i2c_adapter *gpu_adapter_create ( void ); @@ -38,10 +14,6 @@ int gpu_adapters_create ( uint8_t count ); -// void aura_gpu_i2c_destroy ( -// struct aura_i2c_service *service -// ); - void gpu_adapter_destroy ( struct i2c_adapter *i2c_adapter ); diff --git a/main.c b/main.c index 26a8477..0ecad47 100644 --- a/main.c +++ b/main.c @@ -1,18 +1,18 @@ // SPDX-License-Identifier: GPL-2.0 #include +#include #include "debug.h" -#include "aura-gpu-i2c.h" +#include "aura-gpu-hw.h" static struct i2c_adapter *adapter = NULL; static int __init aura_module_init ( void ){ - adapter = gpu_adapter_create(); - if (IS_ERR(adapter)) { + adapter = aura_i2c_bios_create(); + if (IS_ERR_OR_NULL(adapter)) CLEAR_ERR(adapter); - } return 0; } @@ -21,7 +21,7 @@ static void __exit aura_module_exit ( void ){ if (adapter) - gpu_adapter_destroy(adapter); + aura_i2c_bios_destroy(adapter); } module_init(aura_module_init); diff --git a/pci_ids.h b/pci_ids.h new file mode 100644 index 0000000..a94acc8 --- /dev/null +++ b/pci_ids.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _UAPI_AURA_PCI_DEV_I2C_H +#define _UAPI_AURA_PCI_DEV_I2C_H + +#include +#include "asic/asic-types.h" + +/* + AMD devices require an i2c adapter to be created, + NVIDIA devices already have the adapter loaded. + */ +static const struct pci_device_id pciidlist[] = { + {0x1002, 0x67df, 0x1043, 0x0517, 0, 0, CHIP_POLARIS10}, // RX580 (Strix) + {0x1002, 0x687F, 0x1043, 0x0555, 0, 0, CHIP_VEGA10}, // Vega 56 (Strix) + // {0x1002, 0x731f, 0x1043, 0x04e2, 0, 0, CHIP_NAVI10}, // RX5700XT (Strix) + {0, 0, 0}, +}; + +#endif