/* * Copyright 2012-15 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. * * Authors: AMD *
*/
/* BIOS oject table displaypath is per connector. * There is extra path not for connector. BIOS fill its encoderid as 0
*/ static uint8_t bios_parser_get_connectors_number(struct dc_bios *dcb)
{ struct bios_parser *bp = BP_FROM_DCB(dcb); unsignedint count = 0; unsignedint i;
switch (bp->object_info_tbl.revision.minor) { default: case 4: for (i = 0; i < bp->object_info_tbl.v1_4->number_of_path; i++) if (bp->object_info_tbl.v1_4->display_path[i].encoderobjid != 0)
count++;
break;
case 5: for (i = 0; i < bp->object_info_tbl.v1_5->number_of_path; i++) if (bp->object_info_tbl.v1_5->display_path[i].encoderobjid != 0)
count++;
switch (bp->object_info_tbl.revision.minor) { default: case 4: if (v1_4->number_of_path > i) { /* If display_objid is generic object id, the encoderObj * /extencoderobjId should be 0
*/ if (v1_4->display_path[i].encoderobjid != 0 &&
v1_4->display_path[i].display_objid != 0)
object_id = object_id_from_bios_object_id(
v1_4->display_path[i].display_objid);
} break;
case 5: if (v1_5->number_of_path > i) { /* If display_objid is generic object id, the encoderObjId * should be 0
*/ if (v1_5->display_path[i].encoderobjid != 0 &&
v1_5->display_path[i].display_objid != 0)
object_id = object_id_from_bios_object_id(
v1_5->display_path[i].display_objid);
} break;
} return object_id;
}
switch (object_id.type) { /* Encoder's Source is GPU. BIOS does not provide GPU, since all * displaypaths point to same GPU (0x1100). Hardcode GPU object type
*/ case OBJECT_TYPE_ENCODER: /* TODO: since num of src must be less than 2. * If found in for loop, should break. * DAL2 implementation may be changed too
*/ switch (bp->object_info_tbl.revision.minor) { default: case 4: for (i = 0; i < tbl->v1_4->number_of_path; i++) {
obj_id = object_id_from_bios_object_id(
tbl->v1_4->display_path[i].encoderobjid); if (object_id.type == obj_id.type &&
object_id.id == obj_id.id &&
object_id.enum_id == obj_id.enum_id) {
*src_object_id =
object_id_from_bios_object_id(
0x1100); /* break; */
}
}
bp_result = BP_RESULT_OK; break;
case 5: for (i = 0; i < tbl->v1_5->number_of_path; i++) {
obj_id = object_id_from_bios_object_id(
tbl->v1_5->display_path[i].encoderobjid); if (object_id.type == obj_id.type &&
object_id.id == obj_id.id &&
object_id.enum_id == obj_id.enum_id) {
*src_object_id =
object_id_from_bios_object_id(
0x1100); /* break; */
}
}
bp_result = BP_RESULT_OK; break;
} break; case OBJECT_TYPE_CONNECTOR: switch (bp->object_info_tbl.revision.minor) { default: case 4: for (i = 0; i < tbl->v1_4->number_of_path; i++) {
obj_id = object_id_from_bios_object_id(
tbl->v1_4->display_path[i]
.display_objid);
if (object_id.type == obj_id.type &&
object_id.id == obj_id.id &&
object_id.enum_id == obj_id.enum_id) {
*src_object_id =
object_id_from_bios_object_id(
tbl->v1_4
->display_path[i]
.encoderobjid); /* break; */
}
}
bp_result = BP_RESULT_OK; break;
}
bp_result = BP_RESULT_OK; break; case 5: for (i = 0; i < tbl->v1_5->number_of_path; i++) {
obj_id = object_id_from_bios_object_id(
tbl->v1_5->display_path[i].display_objid);
/* from graphics_object_id, find display path which includes the object_id */ staticstruct atom_display_object_path_v2 *get_bios_object( struct bios_parser *bp, struct graphics_object_id id)
{ unsignedint i; struct graphics_object_id obj_id = {0};
switch (id.type) { case OBJECT_TYPE_ENCODER: for (i = 0; i < bp->object_info_tbl.v1_4->number_of_path; i++) {
obj_id = object_id_from_bios_object_id(
bp->object_info_tbl.v1_4->display_path[i].encoderobjid); if (id.type == obj_id.type && id.id == obj_id.id
&& id.enum_id == obj_id.enum_id) return &bp->object_info_tbl.v1_4->display_path[i];
}
fallthrough; case OBJECT_TYPE_CONNECTOR: case OBJECT_TYPE_GENERIC: /* Both Generic and Connector Object ID * will be stored on display_objid
*/ for (i = 0; i < bp->object_info_tbl.v1_4->number_of_path; i++) {
obj_id = object_id_from_bios_object_id(
bp->object_info_tbl.v1_4->display_path[i].display_objid); if (id.type == obj_id.type && id.id == obj_id.id
&& id.enum_id == obj_id.enum_id) return &bp->object_info_tbl.v1_4->display_path[i];
}
fallthrough; default: return NULL;
}
}
/* from graphics_object_id, find display path which includes the object_id */ staticstruct atom_display_object_path_v3 *get_bios_object_from_path_v3(struct bios_parser *bp, struct graphics_object_id id)
{ unsignedint i; struct graphics_object_id obj_id = {0};
switch (id.type) { case OBJECT_TYPE_ENCODER: for (i = 0; i < bp->object_info_tbl.v1_5->number_of_path; i++) {
obj_id = object_id_from_bios_object_id(
bp->object_info_tbl.v1_5->display_path[i].encoderobjid); if (id.type == obj_id.type && id.id == obj_id.id
&& id.enum_id == obj_id.enum_id) return &bp->object_info_tbl.v1_5->display_path[i];
} break;
case OBJECT_TYPE_CONNECTOR: case OBJECT_TYPE_GENERIC: /* Both Generic and Connector Object ID * will be stored on display_objid
*/ for (i = 0; i < bp->object_info_tbl.v1_5->number_of_path; i++) {
obj_id = object_id_from_bios_object_id(
bp->object_info_tbl.v1_5->display_path[i].display_objid); if (id.type == obj_id.type && id.id == obj_id.id
&& id.enum_id == obj_id.enum_id) return &bp->object_info_tbl.v1_5->display_path[i];
} break;
for (;;) {
header = GET_IMAGE(struct atom_common_record_header, offset);
if (!header) return BP_RESULT_BADBIOSTABLE;
if (header->record_type == LAST_RECORD_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(bp, record, info) ==
BP_RESULT_OK) return BP_RESULT_OK;
}
/* If we don't find the entry that we are looking for then * we will return BP_Result_BadBiosTable.
*/ if (find_valid == false) return BP_RESULT_BADBIOSTABLE;
/* 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;
/** * bios_parser_get_gpio_pin_info * Get GpioPin information of input gpio id * * @dcb: pointer to the DC BIOS * @gpio_id: GPIO ID * @info: GpioPin information structure * return: Bios parser result code * note: * to get the GPIO PIN INFO, we need: * 1. get the GPIO_ID from other object table, see GetHPDInfo() * 2. in DATA_TABLE.GPIO_Pin_LUT, search all records, * to get the registerA offset/mask
*/ staticenum bp_result bios_parser_get_gpio_pin_info( struct dc_bios *dcb,
uint32_t gpio_id, struct gpio_pin_info *info)
{ struct bios_parser *bp = BP_FROM_DCB(dcb); struct atom_gpio_pin_lut_v2_1 *header;
uint32_t count = 0;
uint32_t i = 0;
if (!DATA_TABLES(gpio_pin_lut)) return BP_RESULT_BADBIOSTABLE;
header = GET_IMAGE(struct atom_gpio_pin_lut_v2_1,
DATA_TABLES(gpio_pin_lut)); 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;
if (header->table_header.content_revision != 1) return BP_RESULT_UNSUPPORTED;
/* Temporary hard code gpio pin info */
count = (le16_to_cpu(header->table_header.structuresize)
- sizeof(struct atom_common_table_header))
/ sizeof(struct atom_gpio_pin_assignment); for (i = 0; i < count; ++i) { if (header->gpio_pin[i].gpio_id != gpio_id) continue;
DC_LOG_BIOS("AS_SIGNAL_TYPE_HDMI ss_percentage: %d\n", ss_info->spread_spectrum_percentage); break; /* TODO LVDS not support anymore? */ case AS_SIGNAL_TYPE_DISPLAY_PORT:
ss_info->spread_spectrum_percentage =
disp_cntl_tbl->dp_ss_percentage;
ss_info->spread_spectrum_range =
disp_cntl_tbl->dp_ss_rate_10hz * 10; if (disp_cntl_tbl->dp_ss_mode & ATOM_SS_CENTRE_SPREAD_MODE)
ss_info->type.CENTER_MODE = true;
DC_LOG_BIOS("AS_SIGNAL_TYPE_DISPLAY_PORT ss_percentage: %d\n", ss_info->spread_spectrum_percentage); break; case AS_SIGNAL_TYPE_GPU_PLL: /* atom_firmware: DAL only get data from dce_info table. * if data within smu_info is needed for DAL, VBIOS should * copy it into dce_info
*/
result = BP_RESULT_UNSUPPORTED; break; case AS_SIGNAL_TYPE_XGMI:
smu_info = GET_IMAGE(struct atom_smu_info_v3_3,
DATA_TABLES(smu_info)); if (!smu_info) return BP_RESULT_BADBIOSTABLE;
DC_LOG_BIOS("gpuclk_ss_percentage (unit of 0.001 percent): %d\n", smu_info->gpuclk_ss_percentage);
ss_info->spread_spectrum_percentage =
smu_info->waflclk_ss_percentage;
ss_info->spread_spectrum_range =
smu_info->gpuclk_ss_rate_10hz * 10; if (smu_info->waflclk_ss_mode & ATOM_SS_CENTRE_SPREAD_MODE)
ss_info->type.CENTER_MODE = true;
DC_LOG_BIOS("AS_SIGNAL_TYPE_XGMI ss_percentage: %d\n", ss_info->spread_spectrum_percentage); break; default:
result = BP_RESULT_UNSUPPORTED;
}
DC_LOG_BIOS("AS_SIGNAL_TYPE_HDMI ss_percentage: %d\n", ss_info->spread_spectrum_percentage); break; /* TODO LVDS not support anymore? */ case AS_SIGNAL_TYPE_DISPLAY_PORT:
ss_info->spread_spectrum_percentage =
smu_info->gpuclk_ss_percentage;
ss_info->spread_spectrum_range =
smu_info->gpuclk_ss_rate_10hz * 10; if (smu_info->gpuclk_ss_mode & ATOM_SS_CENTRE_SPREAD_MODE)
ss_info->type.CENTER_MODE = true;
DC_LOG_BIOS("AS_SIGNAL_TYPE_DISPLAY_PORT ss_percentage: %d\n", ss_info->spread_spectrum_percentage); break; case AS_SIGNAL_TYPE_GPU_PLL: /* atom_firmware: DAL only get data from dce_info table. * if data within smu_info is needed for DAL, VBIOS should * copy it into dce_info
*/
result = BP_RESULT_UNSUPPORTED; break; default:
result = BP_RESULT_UNSUPPORTED;
}
DC_LOG_BIOS("AS_SIGNAL_TYPE_HDMI ss_percentage: %d\n", ss_info->spread_spectrum_percentage); break; case AS_SIGNAL_TYPE_DISPLAY_PORT: if (bp->base.integrated_info) {
DC_LOG_BIOS("gpuclk_ss_percentage (unit of 0.001 percent): %d\n", bp->base.integrated_info->gpuclk_ss_percentage);
ss_info->spread_spectrum_percentage =
bp->base.integrated_info->gpuclk_ss_percentage;
ss_info->type.CENTER_MODE =
bp->base.integrated_info->gpuclk_ss_type;
} else {
ss_info->spread_spectrum_percentage =
disp_cntl_tbl->dp_ss_percentage;
ss_info->spread_spectrum_range =
disp_cntl_tbl->dp_ss_rate_10hz * 10; if (disp_cntl_tbl->dp_ss_mode & ATOM_SS_CENTRE_SPREAD_MODE)
ss_info->type.CENTER_MODE = true;
}
DC_LOG_BIOS("AS_SIGNAL_TYPE_DISPLAY_PORT ss_percentage: %d\n", ss_info->spread_spectrum_percentage); break; case AS_SIGNAL_TYPE_GPU_PLL: /* atom_smu_info_v4_0 does not have fields for SS for SMU Display PLL anymore. * SMU Display PLL supposed to be without spread. * Better place for it would be in atom_display_controller_info_v4_5 table.
*/
result = BP_RESULT_UNSUPPORTED; break; default:
result = BP_RESULT_UNSUPPORTED; break;
}
return result;
}
/** * bios_parser_get_spread_spectrum_info * Get spread spectrum information from the ASIC_InternalSS_Info(ver 2.1 or * ver 3.1) or SS_Info table from the VBIOS. Currently ASIC_InternalSS_Info * ver 2.1 can co-exist with SS_Info table. Expect ASIC_InternalSS_Info * ver 3.1, * there is only one entry for each signal /ss id. However, there is * no planning of supporting multiple spread Sprectum entry for EverGreen * @dcb: pointer to the DC BIOS * @signal: ASSignalType to be converted to info index * @index: number of entries that match the converted info index * @ss_info: sprectrum information structure, * return: Bios parser result code
*/ staticenum bp_result bios_parser_get_spread_spectrum_info( struct dc_bios *dcb, enum as_signal_type signal,
uint32_t index, struct spread_spectrum_info *ss_info)
{ struct bios_parser *bp = BP_FROM_DCB(dcb); enum bp_result result = BP_RESULT_UNSUPPORTED; struct atom_common_table_header *header; struct atom_data_revision tbl_revision;
if (!ss_info) /* check for bad input */ return BP_RESULT_BADINPUT;
if (!DATA_TABLES(dce_info)) return BP_RESULT_UNSUPPORTED;
switch (tbl_revision.major) { case 4: switch (tbl_revision.minor) { case 1: case 2: case 3: break; case 4:
result = get_soc_bb_info_v4_4(bp, soc_bb_info); break; case 5:
result = get_soc_bb_info_v4_5(bp, soc_bb_info); break; default: break;
} break; default: break;
}
/* We need to convert from 10KHz units into KHz units */
info->lcd_timing.pixel_clk = le16_to_cpu(lvds->lcd_timing.pixclk) * 10; /* usHActive does not include borders, according to VBIOS team */
info->lcd_timing.horizontal_addressable = le16_to_cpu(lvds->lcd_timing.h_active); /* usHBlanking_Time includes borders, so we should really be * subtractingborders duing this translation, but LVDS generally * doesn't have borders, so we should be okay leaving this as is for * now. May need to revisit if we ever have LVDS with borders
*/
info->lcd_timing.horizontal_blanking_time = le16_to_cpu(lvds->lcd_timing.h_blanking_time); /* usVActive does not include borders, according to VBIOS team*/
info->lcd_timing.vertical_addressable = le16_to_cpu(lvds->lcd_timing.v_active); /* usVBlanking_Time includes borders, so we should really be * subtracting borders duing this translation, but LVDS generally * doesn't have borders, so we should be okay leaving this as is for * now. May need to revisit if we ever have LVDS with borders
*/
info->lcd_timing.vertical_blanking_time = le16_to_cpu(lvds->lcd_timing.v_blanking_time);
info->lcd_timing.horizontal_sync_offset = le16_to_cpu(lvds->lcd_timing.h_sync_offset);
info->lcd_timing.horizontal_sync_width = le16_to_cpu(lvds->lcd_timing.h_sync_width);
info->lcd_timing.vertical_sync_offset = le16_to_cpu(lvds->lcd_timing.v_sync_offset);
info->lcd_timing.vertical_sync_width = le16_to_cpu(lvds->lcd_timing.v_syncwidth);
info->lcd_timing.horizontal_border = lvds->lcd_timing.h_border;
info->lcd_timing.vertical_border = lvds->lcd_timing.v_border;
/* not provided by VBIOS */
info->lcd_timing.misc_info.HORIZONTAL_CUT_OFF = 0;
/** * bios_parser_set_scratch_critical_state - update critical state bit * in VBIOS scratch register * * @dcb: pointer to the DC BIO * @state: set or reset state
*/ staticvoid bios_parser_set_scratch_critical_state( struct dc_bios *dcb, bool state)
{
bios_set_scratch_critical_state(dcb, state);
}
if (!firmware_info || !firmware_info32 || !dce_info) return BP_RESULT_BADBIOSTABLE;
memset(info, 0, sizeof(*info));
/* Pixel clock pll information. */ /* We need to convert from 10KHz units into KHz units */
info->default_memory_clk = firmware_info->bootup_mclk_in10khz * 10;
info->default_engine_clk = firmware_info->bootup_sclk_in10khz * 10;
/* 27MHz for Vega10: */
info->pll_info.crystal_frequency = dce_info->dce_refclk_10khz * 10;
/* Hardcode frequency if BIOS gives no DCE Ref Clk */ if (info->pll_info.crystal_frequency == 0)
info->pll_info.crystal_frequency = 27000; /*dp_phy_ref_clk is not correct for atom_display_controller_info_v4_2, but we don't use it*/
info->dp_phy_ref_clk = dce_info->dpphy_refclk_10khz * 10;
info->i2c_engine_ref_clk = dce_info->i2c_engine_refclk_10khz * 10;
/* Get GPU PLL VCO Clock */
if (bp->cmd_tbl.get_smu_clock_info != NULL) { /* VBIOS gives in 10KHz */
info->smu_gpu_pll_output_freq =
bp->cmd_tbl.get_smu_clock_info(bp, SMU9_SYSPLL0_ID) * 10;
}
/* These fields are marked as reserved in v3_1, but they appear to be populated * properly.
*/ if (firmware_info32 && firmware_info32->board_i2c_feature_id == 0x2) {
info->oem_i2c_present = true;
info->oem_i2c_obj_id = firmware_info32->board_i2c_feature_gpio_id;
} else {
info->oem_i2c_present = false;
}
/* Get SMU Display PLL VCO Frequency in KHz*/
info->smu_gpu_pll_output_freq = dce_info_v4_4->dispclk_pll_vco_freq * 10; break;
default: /* should not come here, keep as backup, as was before */
dce_info_v4_1 = GET_IMAGE(struct atom_display_controller_info_v4_1,
DATA_TABLES(dce_info));
if (!dce_info_v4_1) return BP_RESULT_BADBIOSTABLE;
#ifdefined(CONFIG_DRM_AMD_DC_FP) /* encoder cap record not available in v1_5 */ if (bp->object_info_tbl.revision.minor == 5) return BP_RESULT_NORECORD; #endif
object = get_bios_object(bp, object_id);
if (!object) return BP_RESULT_BADINPUT;
record = get_encoder_cap_record(bp, object); if (!record) return BP_RESULT_NORECORD;
DC_LOG_BIOS("record->encodercaps 0x%x for object_id 0x%x", record->encodercaps, object_id.id);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.