/* * Copyright 2007-8 Advanced Micro Devices, Inc. * Copyright 2008 Red Hat 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. * * Authors: Dave Airlie * Alex Deucher */ #include <drm/amdgpu_drm.h>#include "amdgpu.h"#include "amdgpu_atombios.h"#include "amdgpu_atomfirmware.h"#include "amdgpu_i2c.h"#include "amdgpu_display.h" #include "atom.h"#include "atom-bits.h"#include "atombios_encoders.h"#include "bif/bif_4_1_d.h" /* VBIOS-reported table size is unchecked against the image; cap iterations and * adev->i2c_bus[] indexing to AMDGPU_MAX_I2C_BUS. */static int amdgpu_atombios_gpio_i2c_num_entries(uint16_t size){ u32 bytes; if (size < sizeof(ATOM_COMMON_TABLE_HEADER)) return 0; bytes = size - sizeof(ATOM_COMMON_TABLE_HEADER); return (int)min_t(u32, bytes / sizeof(ATOM_GPIO_I2C_ASSIGMENT), AMDGPU_MAX_I2C_BUS);} static struct amdgpu_i2c_bus_rec amdgpu_atombios_get_bus_rec_for_i2c_gpio(ATOM_GPIO_I2C_ASSIGMENT *gpio){ struct amdgpu_i2c_bus_rec i2c; memset(&i2c, 0, sizeof(struct amdgpu_i2c_bus_rec)); i2c.mask_clk_reg = le16_to_cpu(gpio->usClkMaskRegisterIndex); i2c.mask_data_reg = le16_to_cpu(gpio->usDataMaskRegisterIndex); i2c.en_clk_reg = le16_to_cpu(gpio->usClkEnRegisterIndex); i2c.en_data_reg = le16_to_cpu(gpio->usDataEnRegisterIndex); i2c.y_clk_reg = le16_to_cpu(gpio->usClkY_RegisterIndex); i2c.y_data_reg = le16_to_cpu(gpio->usDataY_RegisterIndex); i2c.a_clk_reg = le16_to_cpu(gpio->usClkA_RegisterIndex); i2c.a_data_reg = le16_to_cpu(gpio->usDataA_RegisterIndex); i2c.mask_clk_mask = (1 << gpio->ucClkMaskShift); i2c.mask_data_mask = (1 << gpio->ucDataMaskShift); i2c.en_clk_mask = (1 << gpio->ucClkEnShift); i2c.en_data_mask = (1 << gpio->ucDataEnShift); i2c.y_clk_mask = (1 << gpio->ucClkY_Shift); i2c.y_data_mask = (1 << gpio->ucDataY_Shift); i2c.a_clk_mask = (1 << gpio->ucClkA_Shift); i2c.a_data_mask = (1 << gpio->ucDataA_Shift); if (gpio->sucI2cId.sbfAccess.bfHW_Capable) i2c.hw_capable = true; else i2c.hw_capable = false; if (gpio->sucI2cId.ucAccess == 0xa0) i2c.mm_i2c = true; else i2c.mm_i2c = false; i2c.i2c_id = gpio->sucI2cId.ucAccess; if (i2c.mask_clk_reg) i2c.valid = true; else i2c.valid = false; return i2c;} struct amdgpu_i2c_bus_rec amdgpu_atombios_lookup_i2c_gpio(struct amdgpu_device *adev, uint8_t id){ struct atom_context *ctx = adev->mode_info.atom_context; ATOM_GPIO_I2C_ASSIGMENT *gpio; struct amdgpu_i2c_bus_rec i2c; int index = GetIndexIntoMasterTable(DATA, GPIO_I2C_Info); struct _ATOM_GPIO_I2C_INFO *i2c_info; uint16_t data_offset, size; int i, num_indices; memset(&i2c, 0, sizeof(struct amdgpu_i2c_bus_rec)); i2c.valid = false; if (amdgpu_atom_parse_data_header(ctx, index, &size, NULL, NULL, &data_offset)) { i2c_info = (struct _ATOM_GPIO_I2C_INFO *)(ctx->bios + data_offset); num_indices = amdgpu_atombios_gpio_i2c_num_entries(size); gpio = &i2c_info->asGPIO_Info[0]; for (i = 0; i < num_indices; i++) { if (gpio->sucI2cId.ucAccess == id) { i2c = amdgpu_atombios_get_bus_rec_for_i2c_gpio(gpio); break; } gpio = (ATOM_GPIO_I2C_ASSIGMENT *) ((u8 *)gpio + sizeof(ATOM_GPIO_I2C_ASSIGMENT)); } } return i2c;} void amdgpu_atombios_i2c_init(struct amdgpu_device *adev){ struct atom_context *ctx = adev->mode_info.atom_context; ATOM_GPIO_I2C_ASSIGMENT *gpio; struct amdgpu_i2c_bus_rec i2c; int index = GetIndexIntoMasterTable(DATA, GPIO_I2C_Info); struct _ATOM_GPIO_I2C_INFO *i2c_info; uint16_t data_offset, size; int i, num_indices; char stmp[32]; if (amdgpu_atom_parse_data_header(ctx, index, &size, NULL, NULL, &data_offset)) { i2c_info = (struct _ATOM_GPIO_I2C_INFO *)(ctx->bios + data_offset); num_indices = amdgpu_atombios_gpio_i2c_num_entries(size); gpio = &i2c_info->asGPIO_Info[0]; for (i = 0; i < num_indices; i++) { i2c = amdgpu_atombios_get_bus_rec_for_i2c_gpio(gpio); if (i2c.valid) { sprintf(stmp, "0x%x", i2c.i2c_id); adev->i2c_bus[i] = amdgpu_i2c_create(adev_to_drm(adev), &i2c, stmp); } gpio = (ATOM_GPIO_I2C_ASSIGMENT *) ((u8 *)gpio + sizeof(ATOM_GPIO_I2C_ASSIGMENT)); } }} void amdgpu_atombios_oem_i2c_init(struct amdgpu_device *adev, u8 i2c_id){ struct atom_context *ctx = adev->mode_info.atom_context; ATOM_GPIO_I2C_ASSIGMENT *gpio; struct amdgpu_i2c_bus_rec i2c; int index = GetIndexIntoMasterTable(DATA, GPIO_I2C_Info); struct _ATOM_GPIO_I2C_INFO *i2c_info; uint16_t data_offset, size; int i, num_indices; char stmp[32]; if (amdgpu_atom_parse_data_header(ctx, index, &size, NULL, NULL, &data_offset)) { i2c_info = (struct _ATOM_GPIO_I2C_INFO *)(ctx->bios + data_offset); num_indices = amdgpu_atombios_gpio_i2c_num_entries(size); gpio = &i2c_info->asGPIO_Info[0]; for (i = 0; i < num_indices; i++) { i2c = amdgpu_atombios_get_bus_rec_for_i2c_gpio(gpio); if (i2c.valid && i2c.i2c_id == i2c_id) { sprintf(stmp, "OEM 0x%x", i2c.i2c_id); adev->i2c_bus[i] = amdgpu_i2c_create(adev_to_drm(adev), &i2c, stmp); break; } gpio = (ATOM_GPIO_I2C_ASSIGMENT *) ((u8 *)gpio + sizeof(ATOM_GPIO_I2C_ASSIGMENT)); } }} struct amdgpu_gpio_recamdgpu_atombios_lookup_gpio(struct amdgpu_device *adev, u8 id){ struct atom_context *ctx = adev->mode_info.atom_context; struct amdgpu_gpio_rec gpio; int index = GetIndexIntoMasterTable(DATA, GPIO_Pin_LUT); struct _ATOM_GPIO_PIN_LUT *gpio_info; ATOM_GPIO_PIN_ASSIGNMENT *pin; u16 data_offset, size; int i, num_indices; memset(&gpio, 0, sizeof(struct amdgpu_gpio_rec)); gpio.valid = false; if (amdgpu_atom_parse_data_header(ctx, index, &size, NULL, NULL, &data_offset)) { gpio_info = (struct _ATOM_GPIO_PIN_LUT *)(ctx->bios + data_offset); num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / sizeof(ATOM_GPIO_PIN_ASSIGNMENT); pin = gpio_info->asGPIO_Pin; for (i = 0; i < num_indices; i++) { if (id == pin->ucGPIO_ID) { gpio.id = pin->ucGPIO_ID; gpio.reg = le16_to_cpu(pin->usGpioPin_AIndex); gpio.shift = pin->ucGpioPinBitShift; gpio.mask = (1 << pin->ucGpioPinBitShift); gpio.valid = true; break; } pin = (ATOM_GPIO_PIN_ASSIGNMENT *) ((u8 *)pin + sizeof(ATOM_GPIO_PIN_ASSIGNMENT)); } } return gpio;} static struct amdgpu_hpdamdgpu_atombios_get_hpd_info_from_gpio(struct amdgpu_device *adev, struct amdgpu_gpio_rec *gpio){ struct amdgpu_hpd hpd; u32 reg; memset(&hpd, 0, sizeof(struct amdgpu_hpd)); reg = amdgpu_display_hpd_get_gpio_reg(adev); hpd.gpio = *gpio; if (gpio->reg == reg) { switch(gpio->mask) { case (1 << 0): hpd.hpd = AMDGPU_HPD_1; break; case (1 << 8): hpd.hpd = AMDGPU_HPD_2; break; case (1 << 16): hpd.hpd = AMDGPU_HPD_3; break; case (1 << 24): hpd.hpd = AMDGPU_HPD_4; break; case (1 << 26): hpd.hpd = AMDGPU_HPD_5; break; case (1 << 28): hpd.hpd = AMDGPU_HPD_6; break; default: hpd.hpd = AMDGPU_HPD_NONE; break; } } else hpd.hpd = AMDGPU_HPD_NONE; return hpd;} static const int object_connector_convert[] = { DRM_MODE_CONNECTOR_Unknown, DRM_MODE_CONNECTOR_DVII, DRM_MODE_CONNECTOR_DVII, DRM_MODE_CONNECTOR_DVID, DRM_MODE_CONNECTOR_DVID, DRM_MODE_CONNECTOR_VGA, DRM_MODE_CONNECTOR_Composite, DRM_MODE_CONNECTOR_SVIDEO, DRM_MODE_CONNECTOR_Unknown, DRM_MODE_CONNECTOR_Unknown, DRM_MODE_CONNECTOR_9PinDIN, DRM_MODE_CONNECTOR_Unknown, DRM_MODE_CONNECTOR_HDMIA, DRM_MODE_CONNECTOR_HDMIB, DRM_MODE_CONNECTOR_LVDS, DRM_MODE_CONNECTOR_9PinDIN, DRM_MODE_CONNECTOR_Unknown, DRM_MODE_CONNECTOR_Unknown, DRM_MODE_CONNECTOR_Unknown, DRM_MODE_CONNECTOR_DisplayPort, DRM_MODE_CONNECTOR_eDP, DRM_MODE_CONNECTOR_Unknown}; bool amdgpu_atombios_has_dce_engine_info(struct amdgpu_device *adev){ struct amdgpu_mode_info *mode_info = &adev->mode_info; struct atom_context *ctx = mode_info->atom_context; int index = GetIndexIntoMasterTable(DATA, Object_Header); u16 size, data_offset; u8 frev, crev; ATOM_DISPLAY_OBJECT_PATH_TABLE *path_obj; ATOM_OBJECT_HEADER *obj_header; if (!amdgpu_atom_parse_data_header(ctx, index, &size, &frev, &crev, &data_offset)) return false; if (crev < 2) return false; obj_header = (ATOM_OBJECT_HEADER *) (ctx->bios + data_offset); path_obj = (ATOM_DISPLAY_OBJECT_PATH_TABLE *) (ctx->bios + data_offset + le16_to_cpu(obj_header->usDisplayPathTableOffset)); if (path_obj->ucNumOfDispPath) return true; else return false;} bool amdgpu_atombios_get_connector_info_from_object_table(struct amdgpu_device *adev){ struct amdgpu_mode_info *mode_info = &adev->mode_info; struct atom_context *ctx = mode_info->atom_context; int index = GetIndexIntoMasterTable(DATA, Object_Header); u16 size, data_offset; u8 frev, crev; ATOM_CONNECTOR_OBJECT_TABLE *con_obj; ATOM_ENCODER_OBJECT_TABLE *enc_obj; ATOM_OBJECT_TABLE *router_obj; ATOM_DISPLAY_OBJECT_PATH_TABLE *path_obj; ATOM_OBJECT_HEADER *obj_header; int i, j, k, path_size, device_support; int connector_type; u16 conn_id, connector_object_id; struct amdgpu_i2c_bus_rec ddc_bus; struct amdgpu_router router; struct amdgpu_gpio_rec gpio; struct amdgpu_hpd hpd; if (!amdgpu_atom_parse_data_header(ctx, index, &size, &frev, &crev, &data_offset)) return false; if (crev < 2) return false; obj_header = (ATOM_OBJECT_HEADER *) (ctx->bios + data_offset); path_obj = (ATOM_DISPLAY_OBJECT_PATH_TABLE *) (ctx->bios + data_offset + le16_to_cpu(obj_header->usDisplayPathTableOffset)); con_obj = (ATOM_CONNECTOR_OBJECT_TABLE *) (ctx->bios + data_offset + le16_to_cpu(obj_header->usConnectorObjectTableOffset)); enc_obj = (ATOM_ENCODER_OBJECT_TABLE *) (ctx->bios + data_offset + le16_to_cpu(obj_header->usEncoderObjectTableOffset)); router_obj = (ATOM_OBJECT_TABLE *) (ctx->bios + data_offset + le16_to_cpu(obj_header->usRouterObjectTableOffset)); device_support = le16_to_cpu(obj_header->usDeviceSupport); path_size = 0; for (i = 0; i < path_obj->ucNumOfDispPath; i++) { uint8_t *addr = (uint8_t *) path_obj->asDispPath; ATOM_DISPLAY_OBJECT_PATH *path; addr += path_size; path = (ATOM_DISPLAY_OBJECT_PATH *) addr; path_size += le16_to_cpu(path->usSize); if (device_support & le16_to_cpu(path->usDeviceTag)) { uint8_t con_obj_id = (le16_to_cpu(path->usConnObjectId) & OBJECT_ID_MASK) >> OBJECT_ID_SHIFT; /* Skip TV/CV support */ if ((le16_to_cpu(path->usDeviceTag) == ATOM_DEVICE_TV1_SUPPORT) || (le16_to_cpu(path->usDeviceTag) == ATOM_DEVICE_CV_SUPPORT)) continue; if (con_obj_id >= ARRAY_SIZE(object_connector_convert)) { DRM_ERROR("invalid con_obj_id %d for device tag 0x%04x\n", con_obj_id, le16_to_cpu(path->usDeviceTag)); continue; } connector_type = object_connector_convert[con_obj_id]; connector_object_id = con_obj_id; if (connector_type == DRM_MODE_CONNECTOR_Unknown) continue; router.ddc_valid = false; router.cd_valid = false; for (j = 0; j < ((le16_to_cpu(path->usSize) - 8) / 2); j++) { uint8_t grph_obj_type = (le16_to_cpu(path->usGraphicObjIds[j]) & OBJECT_TYPE_MASK) >> OBJECT_TYPE_SHIFT; if (grph_obj_type == GRAPH_OBJECT_TYPE_ENCODER) { for (k = 0; k < enc_obj->ucNumberOfObjects; k++) { u16 encoder_obj = le16_to_cpu(enc_obj->asObjects[k].usObjectID); if (le16_to_cpu(path->usGraphicObjIds[j]) == encoder_obj) { ATOM_COMMON_RECORD_HEADER *record = (ATOM_COMMON_RECORD_HEADER *) (ctx->bios + data_offset + le16_to_cpu(enc_obj->asObjects[k].usRecordOffset)); ATOM_ENCODER_CAP_RECORD *cap_record; u16 caps = 0; while (record->ucRecordSize > 0 && record->ucRecordType > 0 && record->ucRecordType <= ATOM_MAX_OBJECT_RECORD_NUMBER) { switch (record->ucRecordType) { case ATOM_ENCODER_CAP_RECORD_TYPE: cap_record =(ATOM_ENCODER_CAP_RECORD *) record; caps = le16_to_cpu(cap_record->usEncoderCap); break; } record = (ATOM_COMMON_RECORD_HEADER *) ((char *)record + record->ucRecordSize); } amdgpu_display_add_encoder(adev, encoder_obj, le16_to_cpu(path->usDeviceTag), caps); } } } else if (grph_obj_type == GRAPH_OBJECT_TYPE_ROUTER) { for (k = 0; k < router_obj->ucNumberOfObjects; k++) { u16 router_obj_id = le16_to_cpu(router_obj->asObjects[k].usObjectID); if (le16_to_cpu(path->usGraphicObjIds[j]) == router_obj_id) { ATOM_COMMON_RECORD_HEADER *record = (ATOM_COMMON_RECORD_HEADER *) (ctx->bios + data_offset + le16_to_cpu(router_obj->asObjects[k].usRecordOffset)); ATOM_I2C_RECORD *i2c_record; ATOM_I2C_ID_CONFIG_ACCESS *i2c_config; ATOM_ROUTER_DDC_PATH_SELECT_RECORD *ddc_path; ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD *cd_path; ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT *router_src_dst_table = (ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT *) (ctx->bios + data_offset + le16_to_cpu(router_obj->asObjects[k].usSrcDstTableOffset)); u8 *num_dst_objs = (u8 *) ((u8 *)router_src_dst_table + 1 + (router_src_dst_table->ucNumberOfSrc * 2)); u16 *dst_objs = (u16 *)(num_dst_objs + 1); int enum_id; router.router_id = router_obj_id; for (enum_id = 0; enum_id < (*num_dst_objs); enum_id++) { if (le16_to_cpu(path->usConnObjectId) == le16_to_cpu(dst_objs[enum_id])) break; } while (record->ucRecordSize > 0 && record->ucRecordType > 0 && record->ucRecordType <= ATOM_MAX_OBJECT_RECORD_NUMBER) { switch (record->ucRecordType) { case ATOM_I2C_RECORD_TYPE: i2c_record = (ATOM_I2C_RECORD *) record; i2c_config = (ATOM_I2C_ID_CONFIG_ACCESS *) &i2c_record->sucI2cId; router.i2c_info = amdgpu_atombios_lookup_i2c_gpio(adev, i2c_config-> ucAccess); router.i2c_addr = i2c_record->ucI2CAddr >> 1; break; case ATOM_ROUTER_DDC_PATH_SELECT_RECORD_TYPE: ddc_path = (ATOM_ROUTER_DDC_PATH_SELECT_RECORD *) record; router.ddc_valid = true; router.ddc_mux_type = ddc_path->ucMuxType; router.ddc_mux_control_pin = ddc_path->ucMuxControlPin; router.ddc_mux_state = ddc_path->ucMuxState[enum_id]; break; case ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD_TYPE: cd_path = (ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD *) record; router.cd_valid = true; router.cd_mux_type = cd_path->ucMuxType; router.cd_mux_control_pin = cd_path->ucMuxControlPin; router.cd_mux_state = cd_path->ucMuxState[enum_id]; break; } record = (ATOM_COMMON_RECORD_HEADER *) ((char *)record + record->ucRecordSize); } } } } }Lines 1–500 of 1963. Load more
/* * Copyright 2007-8 Advanced Micro Devices, Inc. * Copyright 2008 Red Hat 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. * * Authors: Dave Airlie * Alex Deucher */ #include <drm/amdgpu_drm.h> #include "amdgpu.h" #include "amdgpu_atombios.h" #include "amdgpu_atomfirmware.h" #include "amdgpu_i2c.h" #include "amdgpu_display.h" #include "atom.h" #include "atom-bits.h" #include "atombios_encoders.h" #include "bif/bif_4_1_d.h" /* VBIOS-reported table size is unchecked against the image; cap iterations and * adev->i2c_bus[] indexing to AMDGPU_MAX_I2C_BUS. */ static int amdgpu_atombios_gpio_i2c_num_entries(uint16_t size) { u32 bytes; if (size < sizeof(ATOM_COMMON_TABLE_HEADER)) return 0; bytes = size - sizeof(ATOM_COMMON_TABLE_HEADER); return (int)min_t(u32, bytes / sizeof(ATOM_GPIO_I2C_ASSIGMENT), AMDGPU_MAX_I2C_BUS); } static struct amdgpu_i2c_bus_rec amdgpu_atombios_get_bus_rec_for_i2c_gpio(ATOM_GPIO_I2C_ASSIGMENT *gpio) { struct amdgpu_i2c_bus_rec i2c; memset(&i2c, 0, sizeof(struct amdgpu_i2c_bus_rec)); i2c.mask_clk_reg = le16_to_cpu(gpio->usClkMaskRegisterIndex); i2c.mask_data_reg = le16_to_cpu(gpio->usDataMaskRegisterIndex); i2c.en_clk_reg = le16_to_cpu(gpio->usClkEnRegisterIndex); i2c.en_data_reg = le16_to_cpu(gpio->usDataEnRegisterIndex); i2c.y_clk_reg = le16_to_cpu(gpio->usClkY_RegisterIndex); i2c.y_data_reg = le16_to_cpu(gpio->usDataY_RegisterIndex); i2c.a_clk_reg = le16_to_cpu(gpio->usClkA_RegisterIndex); i2c.a_data_reg = le16_to_cpu(gpio->usDataA_RegisterIndex); i2c.mask_clk_mask = (1 << gpio->ucClkMaskShift); i2c.mask_data_mask = (1 << gpio->ucDataMaskShift); i2c.en_clk_mask = (1 << gpio->ucClkEnShift); i2c.en_data_mask = (1 << gpio->ucDataEnShift); i2c.y_clk_mask = (1 << gpio->ucClkY_Shift); i2c.y_data_mask = (1 << gpio->ucDataY_Shift); i2c.a_clk_mask = (1 << gpio->ucClkA_Shift); i2c.a_data_mask = (1 << gpio->ucDataA_Shift); if (gpio->sucI2cId.sbfAccess.bfHW_Capable) i2c.hw_capable = true; else i2c.hw_capable = false; if (gpio->sucI2cId.ucAccess == 0xa0) i2c.mm_i2c = true; else i2c.mm_i2c = false; i2c.i2c_id = gpio->sucI2cId.ucAccess; if (i2c.mask_clk_reg) i2c.valid = true; else i2c.valid = false; return i2c; } struct amdgpu_i2c_bus_rec amdgpu_atombios_lookup_i2c_gpio(struct amdgpu_device *adev, uint8_t id) { struct atom_context *ctx = adev->mode_info.atom_context; ATOM_GPIO_I2C_ASSIGMENT *gpio; struct amdgpu_i2c_bus_rec i2c; int index = GetIndexIntoMasterTable(DATA, GPIO_I2C_Info); struct _ATOM_GPIO_I2C_INFO *i2c_info; uint16_t data_offset, size; int i, num_indices; memset(&i2c, 0, sizeof(struct amdgpu_i2c_bus_rec)); i2c.valid = false; if (amdgpu_atom_parse_data_header(ctx, index, &size, NULL, NULL, &data_offset)) { i2c_info = (struct _ATOM_GPIO_I2C_INFO *)(ctx->bios + data_offset); num_indices = amdgpu_atombios_gpio_i2c_num_entries(size); gpio = &i2c_info->asGPIO_Info[0]; for (i = 0; i < num_indices; i++) { if (gpio->sucI2cId.ucAccess == id) { i2c = amdgpu_atombios_get_bus_rec_for_i2c_gpio(gpio); break; } gpio = (ATOM_GPIO_I2C_ASSIGMENT *) ((u8 *)gpio + sizeof(ATOM_GPIO_I2C_ASSIGMENT)); } } return i2c; } void amdgpu_atombios_i2c_init(struct amdgpu_device *adev) { struct atom_context *ctx = adev->mode_info.atom_context; ATOM_GPIO_I2C_ASSIGMENT *gpio; struct amdgpu_i2c_bus_rec i2c; int index = GetIndexIntoMasterTable(DATA, GPIO_I2C_Info); struct _ATOM_GPIO_I2C_INFO *i2c_info; uint16_t data_offset, size; int i, num_indices; char stmp[32]; if (amdgpu_atom_parse_data_header(ctx, index, &size, NULL, NULL, &data_offset)) { i2c_info = (struct _ATOM_GPIO_I2C_INFO *)(ctx->bios + data_offset); num_indices = amdgpu_atombios_gpio_i2c_num_entries(size); gpio = &i2c_info->asGPIO_Info[0]; for (i = 0; i < num_indices; i++) { i2c = amdgpu_atombios_get_bus_rec_for_i2c_gpio(gpio); if (i2c.valid) { sprintf(stmp, "0x%x", i2c.i2c_id); adev->i2c_bus[i] = amdgpu_i2c_create(adev_to_drm(adev), &i2c, stmp); } gpio = (ATOM_GPIO_I2C_ASSIGMENT *) ((u8 *)gpio + sizeof(ATOM_GPIO_I2C_ASSIGMENT)); } } } void amdgpu_atombios_oem_i2c_init(struct amdgpu_device *adev, u8 i2c_id) { struct atom_context *ctx = adev->mode_info.atom_context; ATOM_GPIO_I2C_ASSIGMENT *gpio; struct amdgpu_i2c_bus_rec i2c; int index = GetIndexIntoMasterTable(DATA, GPIO_I2C_Info); struct _ATOM_GPIO_I2C_INFO *i2c_info; uint16_t data_offset, size; int i, num_indices; char stmp[32]; if (amdgpu_atom_parse_data_header(ctx, index, &size, NULL, NULL, &data_offset)) { i2c_info = (struct _ATOM_GPIO_I2C_INFO *)(ctx->bios + data_offset); num_indices = amdgpu_atombios_gpio_i2c_num_entries(size); gpio = &i2c_info->asGPIO_Info[0]; for (i = 0; i < num_indices; i++) { i2c = amdgpu_atombios_get_bus_rec_for_i2c_gpio(gpio); if (i2c.valid && i2c.i2c_id == i2c_id) { sprintf(stmp, "OEM 0x%x", i2c.i2c_id); adev->i2c_bus[i] = amdgpu_i2c_create(adev_to_drm(adev), &i2c, stmp); break; } gpio = (ATOM_GPIO_I2C_ASSIGMENT *) ((u8 *)gpio + sizeof(ATOM_GPIO_I2C_ASSIGMENT)); } } } struct amdgpu_gpio_rec amdgpu_atombios_lookup_gpio(struct amdgpu_device *adev, u8 id) { struct atom_context *ctx = adev->mode_info.atom_context; struct amdgpu_gpio_rec gpio; int index = GetIndexIntoMasterTable(DATA, GPIO_Pin_LUT); struct _ATOM_GPIO_PIN_LUT *gpio_info; ATOM_GPIO_PIN_ASSIGNMENT *pin; u16 data_offset, size; int i, num_indices; memset(&gpio, 0, sizeof(struct amdgpu_gpio_rec)); gpio.valid = false; if (amdgpu_atom_parse_data_header(ctx, index, &size, NULL, NULL, &data_offset)) { gpio_info = (struct _ATOM_GPIO_PIN_LUT *)(ctx->bios + data_offset); num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / sizeof(ATOM_GPIO_PIN_ASSIGNMENT); pin = gpio_info->asGPIO_Pin; for (i = 0; i < num_indices; i++) { if (id == pin->ucGPIO_ID) { gpio.id = pin->ucGPIO_ID; gpio.reg = le16_to_cpu(pin->usGpioPin_AIndex); gpio.shift = pin->ucGpioPinBitShift; gpio.mask = (1 << pin->ucGpioPinBitShift); gpio.valid = true; break; } pin = (ATOM_GPIO_PIN_ASSIGNMENT *) ((u8 *)pin + sizeof(ATOM_GPIO_PIN_ASSIGNMENT)); } } return gpio; } static struct amdgpu_hpd amdgpu_atombios_get_hpd_info_from_gpio(struct amdgpu_device *adev, struct amdgpu_gpio_rec *gpio) { struct amdgpu_hpd hpd; u32 reg; memset(&hpd, 0, sizeof(struct amdgpu_hpd)); reg = amdgpu_display_hpd_get_gpio_reg(adev); hpd.gpio = *gpio; if (gpio->reg == reg) { switch(gpio->mask) { case (1 << 0): hpd.hpd = AMDGPU_HPD_1; break; case (1 << 8): hpd.hpd = AMDGPU_HPD_2; break; case (1 << 16): hpd.hpd = AMDGPU_HPD_3; break; case (1 << 24): hpd.hpd = AMDGPU_HPD_4; break; case (1 << 26): hpd.hpd = AMDGPU_HPD_5; break; case (1 << 28): hpd.hpd = AMDGPU_HPD_6; break; default: hpd.hpd = AMDGPU_HPD_NONE; break; } } else hpd.hpd = AMDGPU_HPD_NONE; return hpd; } static const int object_connector_convert[] = { DRM_MODE_CONNECTOR_Unknown, DRM_MODE_CONNECTOR_DVII, DRM_MODE_CONNECTOR_DVII, DRM_MODE_CONNECTOR_DVID, DRM_MODE_CONNECTOR_DVID, DRM_MODE_CONNECTOR_VGA, DRM_MODE_CONNECTOR_Composite, DRM_MODE_CONNECTOR_SVIDEO, DRM_MODE_CONNECTOR_Unknown, DRM_MODE_CONNECTOR_Unknown, DRM_MODE_CONNECTOR_9PinDIN, DRM_MODE_CONNECTOR_Unknown, DRM_MODE_CONNECTOR_HDMIA, DRM_MODE_CONNECTOR_HDMIB, DRM_MODE_CONNECTOR_LVDS, DRM_MODE_CONNECTOR_9PinDIN, DRM_MODE_CONNECTOR_Unknown, DRM_MODE_CONNECTOR_Unknown, DRM_MODE_CONNECTOR_Unknown, DRM_MODE_CONNECTOR_DisplayPort, DRM_MODE_CONNECTOR_eDP, DRM_MODE_CONNECTOR_Unknown }; bool amdgpu_atombios_has_dce_engine_info(struct amdgpu_device *adev) { struct amdgpu_mode_info *mode_info = &adev->mode_info; struct atom_context *ctx = mode_info->atom_context; int index = GetIndexIntoMasterTable(DATA, Object_Header); u16 size, data_offset; u8 frev, crev; ATOM_DISPLAY_OBJECT_PATH_TABLE *path_obj; ATOM_OBJECT_HEADER *obj_header; if (!amdgpu_atom_parse_data_header(ctx, index, &size, &frev, &crev, &data_offset)) return false; if (crev < 2) return false; obj_header = (ATOM_OBJECT_HEADER *) (ctx->bios + data_offset); path_obj = (ATOM_DISPLAY_OBJECT_PATH_TABLE *) (ctx->bios + data_offset + le16_to_cpu(obj_header->usDisplayPathTableOffset)); if (path_obj->ucNumOfDispPath) return true; else return false; } bool amdgpu_atombios_get_connector_info_from_object_table(struct amdgpu_device *adev) { struct amdgpu_mode_info *mode_info = &adev->mode_info; struct atom_context *ctx = mode_info->atom_context; int index = GetIndexIntoMasterTable(DATA, Object_Header); u16 size, data_offset; u8 frev, crev; ATOM_CONNECTOR_OBJECT_TABLE *con_obj; ATOM_ENCODER_OBJECT_TABLE *enc_obj; ATOM_OBJECT_TABLE *router_obj; ATOM_DISPLAY_OBJECT_PATH_TABLE *path_obj; ATOM_OBJECT_HEADER *obj_header; int i, j, k, path_size, device_support; int connector_type; u16 conn_id, connector_object_id; struct amdgpu_i2c_bus_rec ddc_bus; struct amdgpu_router router; struct amdgpu_gpio_rec gpio; struct amdgpu_hpd hpd; if (!amdgpu_atom_parse_data_header(ctx, index, &size, &frev, &crev, &data_offset)) return false; if (crev < 2) return false; obj_header = (ATOM_OBJECT_HEADER *) (ctx->bios + data_offset); path_obj = (ATOM_DISPLAY_OBJECT_PATH_TABLE *) (ctx->bios + data_offset + le16_to_cpu(obj_header->usDisplayPathTableOffset)); con_obj = (ATOM_CONNECTOR_OBJECT_TABLE *) (ctx->bios + data_offset + le16_to_cpu(obj_header->usConnectorObjectTableOffset)); enc_obj = (ATOM_ENCODER_OBJECT_TABLE *) (ctx->bios + data_offset + le16_to_cpu(obj_header->usEncoderObjectTableOffset)); router_obj = (ATOM_OBJECT_TABLE *) (ctx->bios + data_offset + le16_to_cpu(obj_header->usRouterObjectTableOffset)); device_support = le16_to_cpu(obj_header->usDeviceSupport); path_size = 0; for (i = 0; i < path_obj->ucNumOfDispPath; i++) { uint8_t *addr = (uint8_t *) path_obj->asDispPath; ATOM_DISPLAY_OBJECT_PATH *path; addr += path_size; path = (ATOM_DISPLAY_OBJECT_PATH *) addr; path_size += le16_to_cpu(path->usSize); if (device_support & le16_to_cpu(path->usDeviceTag)) { uint8_t con_obj_id = (le16_to_cpu(path->usConnObjectId) & OBJECT_ID_MASK) >> OBJECT_ID_SHIFT; /* Skip TV/CV support */ if ((le16_to_cpu(path->usDeviceTag) == ATOM_DEVICE_TV1_SUPPORT) || (le16_to_cpu(path->usDeviceTag) == ATOM_DEVICE_CV_SUPPORT)) continue; if (con_obj_id >= ARRAY_SIZE(object_connector_convert)) { DRM_ERROR("invalid con_obj_id %d for device tag 0x%04x\n", con_obj_id, le16_to_cpu(path->usDeviceTag)); continue; } connector_type = object_connector_convert[con_obj_id]; connector_object_id = con_obj_id; if (connector_type == DRM_MODE_CONNECTOR_Unknown) continue; router.ddc_valid = false; router.cd_valid = false; for (j = 0; j < ((le16_to_cpu(path->usSize) - 8) / 2); j++) { uint8_t grph_obj_type = (le16_to_cpu(path->usGraphicObjIds[j]) & OBJECT_TYPE_MASK) >> OBJECT_TYPE_SHIFT; if (grph_obj_type == GRAPH_OBJECT_TYPE_ENCODER) { for (k = 0; k < enc_obj->ucNumberOfObjects; k++) { u16 encoder_obj = le16_to_cpu(enc_obj->asObjects[k].usObjectID); if (le16_to_cpu(path->usGraphicObjIds[j]) == encoder_obj) { ATOM_COMMON_RECORD_HEADER *record = (ATOM_COMMON_RECORD_HEADER *) (ctx->bios + data_offset + le16_to_cpu(enc_obj->asObjects[k].usRecordOffset)); ATOM_ENCODER_CAP_RECORD *cap_record; u16 caps = 0; while (record->ucRecordSize > 0 && record->ucRecordType > 0 && record->ucRecordType <= ATOM_MAX_OBJECT_RECORD_NUMBER) { switch (record->ucRecordType) { case ATOM_ENCODER_CAP_RECORD_TYPE: cap_record =(ATOM_ENCODER_CAP_RECORD *) record; caps = le16_to_cpu(cap_record->usEncoderCap); break; } record = (ATOM_COMMON_RECORD_HEADER *) ((char *)record + record->ucRecordSize); } amdgpu_display_add_encoder(adev, encoder_obj, le16_to_cpu(path->usDeviceTag), caps); } } } else if (grph_obj_type == GRAPH_OBJECT_TYPE_ROUTER) { for (k = 0; k < router_obj->ucNumberOfObjects; k++) { u16 router_obj_id = le16_to_cpu(router_obj->asObjects[k].usObjectID); if (le16_to_cpu(path->usGraphicObjIds[j]) == router_obj_id) { ATOM_COMMON_RECORD_HEADER *record = (ATOM_COMMON_RECORD_HEADER *) (ctx->bios + data_offset + le16_to_cpu(router_obj->asObjects[k].usRecordOffset)); ATOM_I2C_RECORD *i2c_record; ATOM_I2C_ID_CONFIG_ACCESS *i2c_config; ATOM_ROUTER_DDC_PATH_SELECT_RECORD *ddc_path; ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD *cd_path; ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT *router_src_dst_table = (ATOM_SRC_DST_TABLE_FOR_ONE_OBJECT *) (ctx->bios + data_offset + le16_to_cpu(router_obj->asObjects[k].usSrcDstTableOffset)); u8 *num_dst_objs = (u8 *) ((u8 *)router_src_dst_table + 1 + (router_src_dst_table->ucNumberOfSrc * 2)); u16 *dst_objs = (u16 *)(num_dst_objs + 1); int enum_id; router.router_id = router_obj_id; for (enum_id = 0; enum_id < (*num_dst_objs); enum_id++) { if (le16_to_cpu(path->usConnObjectId) == le16_to_cpu(dst_objs[enum_id])) break; } while (record->ucRecordSize > 0 && record->ucRecordType > 0 && record->ucRecordType <= ATOM_MAX_OBJECT_RECORD_NUMBER) { switch (record->ucRecordType) { case ATOM_I2C_RECORD_TYPE: i2c_record = (ATOM_I2C_RECORD *) record; i2c_config = (ATOM_I2C_ID_CONFIG_ACCESS *) &i2c_record->sucI2cId; router.i2c_info = amdgpu_atombios_lookup_i2c_gpio(adev, i2c_config-> ucAccess); router.i2c_addr = i2c_record->ucI2CAddr >> 1; break; case ATOM_ROUTER_DDC_PATH_SELECT_RECORD_TYPE: ddc_path = (ATOM_ROUTER_DDC_PATH_SELECT_RECORD *) record; router.ddc_valid = true; router.ddc_mux_type = ddc_path->ucMuxType; router.ddc_mux_control_pin = ddc_path->ucMuxControlPin; router.ddc_mux_state = ddc_path->ucMuxState[enum_id]; break; case ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD_TYPE: cd_path = (ATOM_ROUTER_DATA_CLOCK_PATH_SELECT_RECORD *) record; router.cd_valid = true; router.cd_mux_type = cd_path->ucMuxType; router.cd_mux_control_pin = cd_path->ucMuxControlPin; router.cd_mux_state = cd_path->ucMuxState[enum_id]; break; } record = (ATOM_COMMON_RECORD_HEADER *) ((char *)record + record->ucRecordSize); } } } } }