diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:49:45 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:49:45 +0000 |
commit | 2c3c1048746a4622d8c89a29670120dc8fab93c4 (patch) | |
tree | 848558de17fb3008cdf4d861b01ac7781903ce39 /drivers/gpu/drm/amd/display/dc/bios | |
parent | Initial commit. (diff) | |
download | linux-2c3c1048746a4622d8c89a29670120dc8fab93c4.tar.xz linux-2c3c1048746a4622d8c89a29670120dc8fab93c4.zip |
Adding upstream version 6.1.76.upstream/6.1.76
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/gpu/drm/amd/display/dc/bios')
31 files changed, 13853 insertions, 0 deletions
diff --git a/drivers/gpu/drm/amd/display/dc/bios/Makefile b/drivers/gpu/drm/amd/display/dc/bios/Makefile new file mode 100644 index 000000000..ed6b5e976 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/Makefile @@ -0,0 +1,57 @@ +# +# Copyright 2017 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. +# +# +# Makefile for the 'bios' sub-component of DAL. +# It provides the parsing and executing controls for atom bios image. + +BIOS = bios_parser.o bios_parser_interface.o bios_parser_helper.o command_table.o command_table_helper.o bios_parser_common.o + +BIOS += command_table2.o command_table_helper2.o bios_parser2.o + +AMD_DAL_BIOS = $(addprefix $(AMDDALPATH)/dc/bios/,$(BIOS)) + +AMD_DISPLAY_FILES += $(AMD_DAL_BIOS) + +############################################################################### +# DCE 6x +############################################################################### +# All DCE6.x are derived from DCE6.0, so 6.0 MUST be defined if ANY of +# DCE6.x is compiled. +ifdef CONFIG_DRM_AMD_DC_SI +AMD_DISPLAY_FILES += $(AMDDALPATH)/dc/bios/dce60/command_table_helper_dce60.o +endif + +############################################################################### +# DCE 8x +############################################################################### +# All DCE8.x are derived from DCE8.0, so 8.0 MUST be defined if ANY of +# DCE8.x is compiled. +AMD_DISPLAY_FILES += $(AMDDALPATH)/dc/bios/dce80/command_table_helper_dce80.o + +############################################################################### +# DCE 11x +############################################################################### +AMD_DISPLAY_FILES += $(AMDDALPATH)/dc/bios/dce110/command_table_helper_dce110.o + +AMD_DISPLAY_FILES += $(AMDDALPATH)/dc/bios/dce112/command_table_helper_dce112.o + +AMD_DISPLAY_FILES += $(AMDDALPATH)/dc/bios/dce112/command_table_helper2_dce112.o diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c b/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c new file mode 100644 index 000000000..9b8ea6e9a --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c @@ -0,0 +1,2923 @@ +/* + * 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 + * + */ + +#include <linux/slab.h> + +#include "dm_services.h" + +#include "atom.h" + +#include "dc_bios_types.h" +#include "include/gpio_service_interface.h" +#include "include/grph_object_ctrl_defs.h" +#include "include/bios_parser_interface.h" +#include "include/i2caux_interface.h" +#include "include/logger_interface.h" + +#include "command_table.h" +#include "bios_parser_helper.h" +#include "command_table_helper.h" +#include "bios_parser.h" +#include "bios_parser_types_internal.h" +#include "bios_parser_interface.h" + +#include "bios_parser_common.h" + +#include "dc.h" + +#define THREE_PERCENT_OF_10000 300 + +#define LAST_RECORD_TYPE 0xff + +#define DC_LOGGER \ + bp->base.ctx->logger + +#define DATA_TABLES(table) (bp->master_data_tbl->ListOfDataTables.table) + +static void get_atom_data_table_revision( + ATOM_COMMON_TABLE_HEADER *atom_data_tbl, + struct atom_data_revision *tbl_revision); +static uint32_t get_src_obj_list(struct bios_parser *bp, ATOM_OBJECT *object, + uint16_t **id_list); +static ATOM_OBJECT *get_bios_object(struct bios_parser *bp, + struct graphics_object_id id); +static enum bp_result get_gpio_i2c_info(struct bios_parser *bp, + ATOM_I2C_RECORD *record, + struct graphics_object_i2c_info *info); +static ATOM_HPD_INT_RECORD *get_hpd_record(struct bios_parser *bp, + ATOM_OBJECT *object); +static struct device_id device_type_from_device_id(uint16_t device_id); +static uint32_t signal_to_ss_id(enum as_signal_type signal); +static uint32_t get_support_mask_for_device_id(struct device_id device_id); +static ATOM_ENCODER_CAP_RECORD_V2 *get_encoder_cap_record( + struct bios_parser *bp, + ATOM_OBJECT *object); + +#define BIOS_IMAGE_SIZE_OFFSET 2 +#define BIOS_IMAGE_SIZE_UNIT 512 + +/*****************************************************************************/ +static bool bios_parser_construct( + struct bios_parser *bp, + struct bp_init_data *init, + enum dce_version dce_version); + +static uint8_t bios_parser_get_connectors_number( + struct dc_bios *dcb); + +static enum bp_result bios_parser_get_embedded_panel_info( + struct dc_bios *dcb, + struct embedded_panel_info *info); + +/*****************************************************************************/ + +struct dc_bios *bios_parser_create( + struct bp_init_data *init, + enum dce_version dce_version) +{ + struct bios_parser *bp = NULL; + + bp = kzalloc(sizeof(struct bios_parser), GFP_KERNEL); + if (!bp) + return NULL; + + if (bios_parser_construct(bp, init, dce_version)) + return &bp->base; + + kfree(bp); + BREAK_TO_DEBUGGER(); + return NULL; +} + +static void bios_parser_destruct(struct bios_parser *bp) +{ + kfree(bp->base.bios_local_image); + kfree(bp->base.integrated_info); +} + +static void bios_parser_destroy(struct dc_bios **dcb) +{ + struct bios_parser *bp = BP_FROM_DCB(*dcb); + + if (!bp) { + BREAK_TO_DEBUGGER(); + return; + } + + bios_parser_destruct(bp); + + kfree(bp); + *dcb = NULL; +} + +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; +} + +static uint8_t bios_parser_get_connectors_number(struct dc_bios *dcb) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + + return get_number_of_objects(bp, + le16_to_cpu(bp->object_info_tbl.v1_1->usConnectorObjectTableOffset)); +} + +static struct graphics_object_id bios_parser_get_connector_id( + struct dc_bios *dcb, + uint8_t i) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + struct graphics_object_id object_id = dal_graphics_object_id_init( + 0, ENUM_ID_UNKNOWN, OBJECT_TYPE_UNKNOWN); + uint16_t id; + + uint32_t connector_table_offset = bp->object_info_tbl_offset + + le16_to_cpu(bp->object_info_tbl.v1_1->usConnectorObjectTableOffset); + + ATOM_OBJECT_TABLE *tbl = + GET_IMAGE(ATOM_OBJECT_TABLE, connector_table_offset); + + if (!tbl) { + dm_error("Can't get connector table from atom bios.\n"); + return object_id; + } + + if (tbl->ucNumberOfObjects <= i) { + dm_error("Can't find connector id %d in connector table of size %d.\n", + i, tbl->ucNumberOfObjects); + return object_id; + } + + id = le16_to_cpu(tbl->asObjects[i].usObjectID); + object_id = object_id_from_bios_object_id(id); + return object_id; +} + +static enum bp_result bios_parser_get_src_obj(struct dc_bios *dcb, + struct graphics_object_id object_id, uint32_t index, + struct graphics_object_id *src_object_id) +{ + uint32_t number; + uint16_t *id; + ATOM_OBJECT *object; + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!src_object_id) + return BP_RESULT_BADINPUT; + + object = get_bios_object(bp, object_id); + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object id */ + return BP_RESULT_BADINPUT; + } + + number = get_src_obj_list(bp, object, &id); + + if (number <= index) + return BP_RESULT_BADINPUT; + + *src_object_id = object_id_from_bios_object_id(id[index]); + + return BP_RESULT_OK; +} + +static enum bp_result bios_parser_get_i2c_info(struct dc_bios *dcb, + struct graphics_object_id id, + struct graphics_object_i2c_info *info) +{ + uint32_t offset; + ATOM_OBJECT *object; + ATOM_COMMON_RECORD_HEADER *header; + ATOM_I2C_RECORD *record; + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!info) + return BP_RESULT_BADINPUT; + + object = get_bios_object(bp, id); + + if (!object) + return BP_RESULT_BADINPUT; + + offset = le16_to_cpu(object->usRecordOffset) + + bp->object_info_tbl_offset; + + for (;;) { + header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); + + if (!header) + return BP_RESULT_BADBIOSTABLE; + + if (LAST_RECORD_TYPE == header->ucRecordType || + !header->ucRecordSize) + break; + + if (ATOM_I2C_RECORD_TYPE == header->ucRecordType + && sizeof(ATOM_I2C_RECORD) <= header->ucRecordSize) { + /* get the I2C info */ + record = (ATOM_I2C_RECORD *) header; + + if (get_gpio_i2c_info(bp, record, info) == BP_RESULT_OK) + return BP_RESULT_OK; + } + + offset += header->ucRecordSize; + } + + return BP_RESULT_NORECORD; +} + +static enum bp_result bios_parser_get_hpd_info(struct dc_bios *dcb, + struct graphics_object_id id, + struct graphics_object_hpd_info *info) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + ATOM_OBJECT *object; + ATOM_HPD_INT_RECORD *record = NULL; + + if (!info) + return BP_RESULT_BADINPUT; + + object = get_bios_object(bp, id); + + if (!object) + return BP_RESULT_BADINPUT; + + record = get_hpd_record(bp, object); + + if (record != NULL) { + info->hpd_int_gpio_uid = record->ucHPDIntGPIOID; + info->hpd_active = record->ucPlugged_PinState; + return BP_RESULT_OK; + } + + return BP_RESULT_NORECORD; +} + +static enum bp_result bios_parser_get_device_tag_record( + struct bios_parser *bp, + ATOM_OBJECT *object, + ATOM_CONNECTOR_DEVICE_TAG_RECORD **record) +{ + ATOM_COMMON_RECORD_HEADER *header; + uint32_t offset; + + offset = le16_to_cpu(object->usRecordOffset) + + bp->object_info_tbl_offset; + + for (;;) { + header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); + + if (!header) + return BP_RESULT_BADBIOSTABLE; + + offset += header->ucRecordSize; + + if (LAST_RECORD_TYPE == header->ucRecordType || + !header->ucRecordSize) + break; + + if (ATOM_CONNECTOR_DEVICE_TAG_RECORD_TYPE != + header->ucRecordType) + continue; + + if (sizeof(ATOM_CONNECTOR_DEVICE_TAG) > header->ucRecordSize) + continue; + + *record = (ATOM_CONNECTOR_DEVICE_TAG_RECORD *) header; + return BP_RESULT_OK; + } + + return BP_RESULT_NORECORD; +} + +static enum bp_result bios_parser_get_device_tag( + struct dc_bios *dcb, + struct graphics_object_id connector_object_id, + uint32_t device_tag_index, + struct connector_device_tag_info *info) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + ATOM_OBJECT *object; + ATOM_CONNECTOR_DEVICE_TAG_RECORD *record = NULL; + ATOM_CONNECTOR_DEVICE_TAG *device_tag; + + if (!info) + return BP_RESULT_BADINPUT; + + /* getBiosObject will return MXM object */ + object = get_bios_object(bp, connector_object_id); + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object id */ + return BP_RESULT_BADINPUT; + } + + if (bios_parser_get_device_tag_record(bp, object, &record) + != BP_RESULT_OK) + return BP_RESULT_NORECORD; + + if (device_tag_index >= record->ucNumberOfDevice) + return BP_RESULT_NORECORD; + + device_tag = &record->asDeviceTag[device_tag_index]; + + info->acpi_device = le32_to_cpu(device_tag->ulACPIDeviceEnum); + info->dev_id = + device_type_from_device_id(le16_to_cpu(device_tag->usDeviceID)); + + return BP_RESULT_OK; +} + +static enum bp_result get_firmware_info_v1_4( + struct bios_parser *bp, + struct dc_firmware_info *info); +static enum bp_result get_firmware_info_v2_1( + struct bios_parser *bp, + struct dc_firmware_info *info); +static enum bp_result get_firmware_info_v2_2( + struct bios_parser *bp, + struct dc_firmware_info *info); + +static enum bp_result bios_parser_get_firmware_info( + struct dc_bios *dcb, + struct dc_firmware_info *info) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + enum bp_result result = BP_RESULT_BADBIOSTABLE; + ATOM_COMMON_TABLE_HEADER *header; + struct atom_data_revision revision; + + if (info && DATA_TABLES(FirmwareInfo)) { + header = GET_IMAGE(ATOM_COMMON_TABLE_HEADER, + DATA_TABLES(FirmwareInfo)); + get_atom_data_table_revision(header, &revision); + switch (revision.major) { + case 1: + switch (revision.minor) { + case 4: + result = get_firmware_info_v1_4(bp, info); + break; + default: + break; + } + break; + + case 2: + switch (revision.minor) { + case 1: + result = get_firmware_info_v2_1(bp, info); + break; + case 2: + result = get_firmware_info_v2_2(bp, info); + break; + default: + break; + } + break; + default: + break; + } + } + + return result; +} + +static enum bp_result get_firmware_info_v1_4( + struct bios_parser *bp, + struct dc_firmware_info *info) +{ + ATOM_FIRMWARE_INFO_V1_4 *firmware_info = + GET_IMAGE(ATOM_FIRMWARE_INFO_V1_4, + DATA_TABLES(FirmwareInfo)); + + if (!info) + return BP_RESULT_BADINPUT; + + if (!firmware_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->pll_info.crystal_frequency = + le16_to_cpu(firmware_info->usReferenceClock) * 10; + info->pll_info.min_input_pxl_clk_pll_frequency = + le16_to_cpu(firmware_info->usMinPixelClockPLL_Input) * 10; + info->pll_info.max_input_pxl_clk_pll_frequency = + le16_to_cpu(firmware_info->usMaxPixelClockPLL_Input) * 10; + info->pll_info.min_output_pxl_clk_pll_frequency = + le32_to_cpu(firmware_info->ulMinPixelClockPLL_Output) * 10; + info->pll_info.max_output_pxl_clk_pll_frequency = + le32_to_cpu(firmware_info->ulMaxPixelClockPLL_Output) * 10; + + if (firmware_info->usFirmwareCapability.sbfAccess.MemoryClockSS_Support) + /* Since there is no information on the SS, report conservative + * value 3% for bandwidth calculation */ + /* unit of 0.01% */ + info->feature.memory_clk_ss_percentage = THREE_PERCENT_OF_10000; + + if (firmware_info->usFirmwareCapability.sbfAccess.EngineClockSS_Support) + /* Since there is no information on the SS,report conservative + * value 3% for bandwidth calculation */ + /* unit of 0.01% */ + info->feature.engine_clk_ss_percentage = THREE_PERCENT_OF_10000; + + return BP_RESULT_OK; +} + +static enum bp_result get_ss_info_v3_1( + struct bios_parser *bp, + uint32_t id, + uint32_t index, + struct spread_spectrum_info *ss_info); + +static enum bp_result get_firmware_info_v2_1( + struct bios_parser *bp, + struct dc_firmware_info *info) +{ + ATOM_FIRMWARE_INFO_V2_1 *firmwareInfo = + GET_IMAGE(ATOM_FIRMWARE_INFO_V2_1, DATA_TABLES(FirmwareInfo)); + struct spread_spectrum_info internalSS; + uint32_t index; + + if (!info) + return BP_RESULT_BADINPUT; + + if (!firmwareInfo) + return BP_RESULT_BADBIOSTABLE; + + memset(info, 0, sizeof(*info)); + + /* Pixel clock pll information. We need to convert from 10KHz units into + * KHz units */ + info->pll_info.crystal_frequency = + le16_to_cpu(firmwareInfo->usCoreReferenceClock) * 10; + info->pll_info.min_input_pxl_clk_pll_frequency = + le16_to_cpu(firmwareInfo->usMinPixelClockPLL_Input) * 10; + info->pll_info.max_input_pxl_clk_pll_frequency = + le16_to_cpu(firmwareInfo->usMaxPixelClockPLL_Input) * 10; + info->pll_info.min_output_pxl_clk_pll_frequency = + le32_to_cpu(firmwareInfo->ulMinPixelClockPLL_Output) * 10; + info->pll_info.max_output_pxl_clk_pll_frequency = + le32_to_cpu(firmwareInfo->ulMaxPixelClockPLL_Output) * 10; + info->default_display_engine_pll_frequency = + le32_to_cpu(firmwareInfo->ulDefaultDispEngineClkFreq) * 10; + info->external_clock_source_frequency_for_dp = + le16_to_cpu(firmwareInfo->usUniphyDPModeExtClkFreq) * 10; + info->min_allowed_bl_level = firmwareInfo->ucMinAllowedBL_Level; + + /* There should be only one entry in the SS info table for Memory Clock + */ + index = 0; + if (firmwareInfo->usFirmwareCapability.sbfAccess.MemoryClockSS_Support) + /* Since there is no information for external SS, report + * conservative value 3% for bandwidth calculation */ + /* unit of 0.01% */ + info->feature.memory_clk_ss_percentage = THREE_PERCENT_OF_10000; + else if (get_ss_info_v3_1(bp, + ASIC_INTERNAL_MEMORY_SS, index, &internalSS) == BP_RESULT_OK) { + if (internalSS.spread_spectrum_percentage) { + info->feature.memory_clk_ss_percentage = + internalSS.spread_spectrum_percentage; + if (internalSS.type.CENTER_MODE) { + /* if it is centermode, the exact SS Percentage + * will be round up of half of the percentage + * reported in the SS table */ + ++info->feature.memory_clk_ss_percentage; + info->feature.memory_clk_ss_percentage /= 2; + } + } + } + + /* There should be only one entry in the SS info table for Engine Clock + */ + index = 1; + if (firmwareInfo->usFirmwareCapability.sbfAccess.EngineClockSS_Support) + /* Since there is no information for external SS, report + * conservative value 3% for bandwidth calculation */ + /* unit of 0.01% */ + info->feature.engine_clk_ss_percentage = THREE_PERCENT_OF_10000; + else if (get_ss_info_v3_1(bp, + ASIC_INTERNAL_ENGINE_SS, index, &internalSS) == BP_RESULT_OK) { + if (internalSS.spread_spectrum_percentage) { + info->feature.engine_clk_ss_percentage = + internalSS.spread_spectrum_percentage; + if (internalSS.type.CENTER_MODE) { + /* if it is centermode, the exact SS Percentage + * will be round up of half of the percentage + * reported in the SS table */ + ++info->feature.engine_clk_ss_percentage; + info->feature.engine_clk_ss_percentage /= 2; + } + } + } + + return BP_RESULT_OK; +} + +static enum bp_result get_firmware_info_v2_2( + struct bios_parser *bp, + struct dc_firmware_info *info) +{ + ATOM_FIRMWARE_INFO_V2_2 *firmware_info; + struct spread_spectrum_info internal_ss; + uint32_t index; + + if (!info) + return BP_RESULT_BADINPUT; + + firmware_info = GET_IMAGE(ATOM_FIRMWARE_INFO_V2_2, + DATA_TABLES(FirmwareInfo)); + + if (!firmware_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->pll_info.crystal_frequency = + le16_to_cpu(firmware_info->usCoreReferenceClock) * 10; + info->pll_info.min_input_pxl_clk_pll_frequency = + le16_to_cpu(firmware_info->usMinPixelClockPLL_Input) * 10; + info->pll_info.max_input_pxl_clk_pll_frequency = + le16_to_cpu(firmware_info->usMaxPixelClockPLL_Input) * 10; + info->pll_info.min_output_pxl_clk_pll_frequency = + le32_to_cpu(firmware_info->ulMinPixelClockPLL_Output) * 10; + info->pll_info.max_output_pxl_clk_pll_frequency = + le32_to_cpu(firmware_info->ulMaxPixelClockPLL_Output) * 10; + info->default_display_engine_pll_frequency = + le32_to_cpu(firmware_info->ulDefaultDispEngineClkFreq) * 10; + info->external_clock_source_frequency_for_dp = + le16_to_cpu(firmware_info->usUniphyDPModeExtClkFreq) * 10; + + /* There should be only one entry in the SS info table for Memory Clock + */ + index = 0; + if (firmware_info->usFirmwareCapability.sbfAccess.MemoryClockSS_Support) + /* Since there is no information for external SS, report + * conservative value 3% for bandwidth calculation */ + /* unit of 0.01% */ + info->feature.memory_clk_ss_percentage = THREE_PERCENT_OF_10000; + else if (get_ss_info_v3_1(bp, + ASIC_INTERNAL_MEMORY_SS, index, &internal_ss) == BP_RESULT_OK) { + if (internal_ss.spread_spectrum_percentage) { + info->feature.memory_clk_ss_percentage = + internal_ss.spread_spectrum_percentage; + if (internal_ss.type.CENTER_MODE) { + /* if it is centermode, the exact SS Percentage + * will be round up of half of the percentage + * reported in the SS table */ + ++info->feature.memory_clk_ss_percentage; + info->feature.memory_clk_ss_percentage /= 2; + } + } + } + + /* There should be only one entry in the SS info table for Engine Clock + */ + index = 1; + if (firmware_info->usFirmwareCapability.sbfAccess.EngineClockSS_Support) + /* Since there is no information for external SS, report + * conservative value 3% for bandwidth calculation */ + /* unit of 0.01% */ + info->feature.engine_clk_ss_percentage = THREE_PERCENT_OF_10000; + else if (get_ss_info_v3_1(bp, + ASIC_INTERNAL_ENGINE_SS, index, &internal_ss) == BP_RESULT_OK) { + if (internal_ss.spread_spectrum_percentage) { + info->feature.engine_clk_ss_percentage = + internal_ss.spread_spectrum_percentage; + if (internal_ss.type.CENTER_MODE) { + /* if it is centermode, the exact SS Percentage + * will be round up of half of the percentage + * reported in the SS table */ + ++info->feature.engine_clk_ss_percentage; + info->feature.engine_clk_ss_percentage /= 2; + } + } + } + + /* Remote Display */ + info->remote_display_config = firmware_info->ucRemoteDisplayConfig; + + /* Is allowed minimum BL level */ + info->min_allowed_bl_level = firmware_info->ucMinAllowedBL_Level; + /* Used starting from CI */ + info->smu_gpu_pll_output_freq = + (uint32_t) (le32_to_cpu(firmware_info->ulGPUPLL_OutputFreq) * 10); + + return BP_RESULT_OK; +} + +static enum bp_result get_ss_info_v3_1( + struct bios_parser *bp, + uint32_t id, + uint32_t index, + struct spread_spectrum_info *ss_info) +{ + ATOM_ASIC_INTERNAL_SS_INFO_V3 *ss_table_header_include; + ATOM_ASIC_SS_ASSIGNMENT_V3 *tbl; + uint32_t table_size; + uint32_t i; + uint32_t table_index = 0; + + if (!ss_info) + return BP_RESULT_BADINPUT; + + if (!DATA_TABLES(ASIC_InternalSS_Info)) + return BP_RESULT_UNSUPPORTED; + + ss_table_header_include = GET_IMAGE(ATOM_ASIC_INTERNAL_SS_INFO_V3, + DATA_TABLES(ASIC_InternalSS_Info)); + table_size = + (le16_to_cpu(ss_table_header_include->sHeader.usStructureSize) + - sizeof(ATOM_COMMON_TABLE_HEADER)) + / sizeof(ATOM_ASIC_SS_ASSIGNMENT_V3); + + tbl = (ATOM_ASIC_SS_ASSIGNMENT_V3 *) + &ss_table_header_include->asSpreadSpectrum[0]; + + memset(ss_info, 0, sizeof(struct spread_spectrum_info)); + + for (i = 0; i < table_size; i++) { + if (tbl[i].ucClockIndication != (uint8_t) id) + continue; + + if (table_index != index) { + table_index++; + continue; + } + /* VBIOS introduced new defines for Version 3, same values as + * before, so now use these new ones for Version 3. + * Shouldn't affect field VBIOS's V3 as define values are still + * same. + * #define SS_MODE_V3_CENTRE_SPREAD_MASK 0x01 + * #define SS_MODE_V3_EXTERNAL_SS_MASK 0x02 + + * Old VBIOS defines: + * #define ATOM_SS_CENTRE_SPREAD_MODE_MASK 0x00000001 + * #define ATOM_EXTERNAL_SS_MASK 0x00000002 + */ + + if (SS_MODE_V3_EXTERNAL_SS_MASK & tbl[i].ucSpreadSpectrumMode) + ss_info->type.EXTERNAL = true; + + if (SS_MODE_V3_CENTRE_SPREAD_MASK & tbl[i].ucSpreadSpectrumMode) + ss_info->type.CENTER_MODE = true; + + /* Older VBIOS (in field) always provides SS percentage in 0.01% + * units set Divider to 100 */ + ss_info->spread_percentage_divider = 100; + + /* #define SS_MODE_V3_PERCENTAGE_DIV_BY_1000_MASK 0x10 */ + if (SS_MODE_V3_PERCENTAGE_DIV_BY_1000_MASK + & tbl[i].ucSpreadSpectrumMode) + ss_info->spread_percentage_divider = 1000; + + ss_info->type.STEP_AND_DELAY_INFO = false; + /* convert [10KHz] into [KHz] */ + ss_info->target_clock_range = + le32_to_cpu(tbl[i].ulTargetClockRange) * 10; + ss_info->spread_spectrum_percentage = + (uint32_t)le16_to_cpu(tbl[i].usSpreadSpectrumPercentage); + ss_info->spread_spectrum_range = + (uint32_t)(le16_to_cpu(tbl[i].usSpreadRateIn10Hz) * 10); + + return BP_RESULT_OK; + } + return BP_RESULT_NORECORD; +} + +static enum bp_result bios_parser_transmitter_control( + struct dc_bios *dcb, + struct bp_transmitter_control *cntl) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!bp->cmd_tbl.transmitter_control) + return BP_RESULT_FAILURE; + + return bp->cmd_tbl.transmitter_control(bp, cntl); +} + +static enum bp_result bios_parser_encoder_control( + struct dc_bios *dcb, + struct bp_encoder_control *cntl) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!bp->cmd_tbl.dig_encoder_control) + return BP_RESULT_FAILURE; + + return bp->cmd_tbl.dig_encoder_control(bp, cntl); +} + +static enum bp_result bios_parser_adjust_pixel_clock( + struct dc_bios *dcb, + struct bp_adjust_pixel_clock_parameters *bp_params) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!bp->cmd_tbl.adjust_display_pll) + return BP_RESULT_FAILURE; + + return bp->cmd_tbl.adjust_display_pll(bp, bp_params); +} + +static enum bp_result bios_parser_set_pixel_clock( + struct dc_bios *dcb, + struct bp_pixel_clock_parameters *bp_params) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!bp->cmd_tbl.set_pixel_clock) + return BP_RESULT_FAILURE; + + return bp->cmd_tbl.set_pixel_clock(bp, bp_params); +} + +static enum bp_result bios_parser_set_dce_clock( + struct dc_bios *dcb, + struct bp_set_dce_clock_parameters *bp_params) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!bp->cmd_tbl.set_dce_clock) + return BP_RESULT_FAILURE; + + return bp->cmd_tbl.set_dce_clock(bp, bp_params); +} + +static enum bp_result bios_parser_enable_spread_spectrum_on_ppll( + struct dc_bios *dcb, + struct bp_spread_spectrum_parameters *bp_params, + bool enable) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!bp->cmd_tbl.enable_spread_spectrum_on_ppll) + return BP_RESULT_FAILURE; + + return bp->cmd_tbl.enable_spread_spectrum_on_ppll( + bp, bp_params, enable); + +} + +static enum bp_result bios_parser_program_crtc_timing( + struct dc_bios *dcb, + struct bp_hw_crtc_timing_parameters *bp_params) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!bp->cmd_tbl.set_crtc_timing) + return BP_RESULT_FAILURE; + + return bp->cmd_tbl.set_crtc_timing(bp, bp_params); +} + +static enum bp_result bios_parser_program_display_engine_pll( + struct dc_bios *dcb, + struct bp_pixel_clock_parameters *bp_params) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!bp->cmd_tbl.program_clock) + return BP_RESULT_FAILURE; + + return bp->cmd_tbl.program_clock(bp, bp_params); + +} + + +static enum bp_result bios_parser_enable_crtc( + struct dc_bios *dcb, + enum controller_id id, + bool enable) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!bp->cmd_tbl.enable_crtc) + return BP_RESULT_FAILURE; + + return bp->cmd_tbl.enable_crtc(bp, id, enable); +} + +static enum bp_result bios_parser_enable_disp_power_gating( + struct dc_bios *dcb, + enum controller_id controller_id, + enum bp_pipe_control_action action) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!bp->cmd_tbl.enable_disp_power_gating) + return BP_RESULT_FAILURE; + + return bp->cmd_tbl.enable_disp_power_gating(bp, controller_id, + action); +} + +static bool bios_parser_is_device_id_supported( + struct dc_bios *dcb, + struct device_id id) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + + uint32_t mask = get_support_mask_for_device_id(id); + + return (le16_to_cpu(bp->object_info_tbl.v1_1->usDeviceSupport) & mask) != 0; +} + +static ATOM_HPD_INT_RECORD *get_hpd_record(struct bios_parser *bp, + ATOM_OBJECT *object) +{ + ATOM_COMMON_RECORD_HEADER *header; + uint32_t offset; + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object */ + return NULL; + } + + offset = le16_to_cpu(object->usRecordOffset) + + bp->object_info_tbl_offset; + + for (;;) { + header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); + + if (!header) + return NULL; + + if (LAST_RECORD_TYPE == header->ucRecordType || + !header->ucRecordSize) + break; + + if (ATOM_HPD_INT_RECORD_TYPE == header->ucRecordType + && sizeof(ATOM_HPD_INT_RECORD) <= header->ucRecordSize) + return (ATOM_HPD_INT_RECORD *) header; + + offset += header->ucRecordSize; + } + + return NULL; +} + +static enum bp_result get_ss_info_from_ss_info_table( + struct bios_parser *bp, + uint32_t id, + struct spread_spectrum_info *ss_info); +static enum bp_result get_ss_info_from_tbl( + struct bios_parser *bp, + uint32_t id, + struct spread_spectrum_info *ss_info); +/** + * 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 + */ +static enum 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; + uint32_t clk_id_ss = 0; + ATOM_COMMON_TABLE_HEADER *header; + struct atom_data_revision tbl_revision; + + if (!ss_info) /* check for bad input */ + return BP_RESULT_BADINPUT; + /* signal translation */ + clk_id_ss = signal_to_ss_id(signal); + + if (!DATA_TABLES(ASIC_InternalSS_Info)) + if (!index) + return get_ss_info_from_ss_info_table(bp, clk_id_ss, + ss_info); + + header = GET_IMAGE(ATOM_COMMON_TABLE_HEADER, + DATA_TABLES(ASIC_InternalSS_Info)); + get_atom_data_table_revision(header, &tbl_revision); + + switch (tbl_revision.major) { + case 2: + switch (tbl_revision.minor) { + case 1: + /* there can not be more then one entry for Internal + * SS Info table version 2.1 */ + if (!index) + return get_ss_info_from_tbl(bp, clk_id_ss, + ss_info); + break; + default: + break; + } + break; + + case 3: + switch (tbl_revision.minor) { + case 1: + return get_ss_info_v3_1(bp, clk_id_ss, index, ss_info); + default: + break; + } + break; + default: + break; + } + /* there can not be more then one entry for SS Info table */ + return result; +} + +static enum bp_result get_ss_info_from_internal_ss_info_tbl_V2_1( + struct bios_parser *bp, + uint32_t id, + struct spread_spectrum_info *info); + +/** + * get_ss_info_from_tbl + * Get spread sprectrum information from the ASIC_InternalSS_Info Ver 2.1 or + * SS_Info table from the VBIOS + * There can not be more than 1 entry for ASIC_InternalSS_Info Ver 2.1 or + * SS_Info. + * + * @bp: pointer to the BIOS parser + * @id: spread sprectrum info index + * @ss_info: sprectrum information structure, + * return: BIOS parser result code + */ +static enum bp_result get_ss_info_from_tbl( + struct bios_parser *bp, + uint32_t id, + struct spread_spectrum_info *ss_info) +{ + if (!ss_info) /* check for bad input, if ss_info is not NULL */ + return BP_RESULT_BADINPUT; + /* for SS_Info table only support DP and LVDS */ + if (id == ASIC_INTERNAL_SS_ON_DP || id == ASIC_INTERNAL_SS_ON_LVDS) + return get_ss_info_from_ss_info_table(bp, id, ss_info); + else + return get_ss_info_from_internal_ss_info_tbl_V2_1(bp, id, + ss_info); +} + +/** + * get_ss_info_from_internal_ss_info_tbl_V2_1 + * Get spread sprectrum information from the ASIC_InternalSS_Info table Ver 2.1 + * from the VBIOS + * There will not be multiple entry for Ver 2.1 + * + * @bp: pointer to the Bios parser + * @id: spread sprectrum info index + * @info: sprectrum information structure, + * return: Bios parser result code + */ +static enum bp_result get_ss_info_from_internal_ss_info_tbl_V2_1( + struct bios_parser *bp, + uint32_t id, + struct spread_spectrum_info *info) +{ + enum bp_result result = BP_RESULT_UNSUPPORTED; + ATOM_ASIC_INTERNAL_SS_INFO_V2 *header; + ATOM_ASIC_SS_ASSIGNMENT_V2 *tbl; + uint32_t tbl_size, i; + + if (!DATA_TABLES(ASIC_InternalSS_Info)) + return result; + + header = GET_IMAGE(ATOM_ASIC_INTERNAL_SS_INFO_V2, + DATA_TABLES(ASIC_InternalSS_Info)); + + memset(info, 0, sizeof(struct spread_spectrum_info)); + + tbl_size = (le16_to_cpu(header->sHeader.usStructureSize) + - sizeof(ATOM_COMMON_TABLE_HEADER)) + / sizeof(ATOM_ASIC_SS_ASSIGNMENT_V2); + + tbl = (ATOM_ASIC_SS_ASSIGNMENT_V2 *) + &(header->asSpreadSpectrum[0]); + for (i = 0; i < tbl_size; i++) { + result = BP_RESULT_NORECORD; + + if (tbl[i].ucClockIndication != (uint8_t)id) + continue; + + if (ATOM_EXTERNAL_SS_MASK + & tbl[i].ucSpreadSpectrumMode) { + info->type.EXTERNAL = true; + } + if (ATOM_SS_CENTRE_SPREAD_MODE_MASK + & tbl[i].ucSpreadSpectrumMode) { + info->type.CENTER_MODE = true; + } + info->type.STEP_AND_DELAY_INFO = false; + /* convert [10KHz] into [KHz] */ + info->target_clock_range = + le32_to_cpu(tbl[i].ulTargetClockRange) * 10; + info->spread_spectrum_percentage = + (uint32_t)le16_to_cpu(tbl[i].usSpreadSpectrumPercentage); + info->spread_spectrum_range = + (uint32_t)(le16_to_cpu(tbl[i].usSpreadRateIn10Hz) * 10); + result = BP_RESULT_OK; + break; + } + + return result; + +} + +/** + * get_ss_info_from_ss_info_table + * Get spread sprectrum information from the SS_Info table from the VBIOS + * if the pointer to info is NULL, indicate the caller what to know the number + * of entries that matches the id + * for, the SS_Info table, there should not be more than 1 entry match. + * + * @bp: pointer to the Bios parser + * @id: spread sprectrum id + * @ss_info: sprectrum information structure, + * return: Bios parser result code + */ +static enum bp_result get_ss_info_from_ss_info_table( + struct bios_parser *bp, + uint32_t id, + struct spread_spectrum_info *ss_info) +{ + enum bp_result result = BP_RESULT_UNSUPPORTED; + ATOM_SPREAD_SPECTRUM_INFO *tbl; + ATOM_COMMON_TABLE_HEADER *header; + uint32_t table_size; + uint32_t i; + uint32_t id_local = SS_ID_UNKNOWN; + struct atom_data_revision revision; + + /* exist of the SS_Info table */ + /* check for bad input, pSSinfo can not be NULL */ + if (!DATA_TABLES(SS_Info) || !ss_info) + return result; + + header = GET_IMAGE(ATOM_COMMON_TABLE_HEADER, DATA_TABLES(SS_Info)); + get_atom_data_table_revision(header, &revision); + + tbl = GET_IMAGE(ATOM_SPREAD_SPECTRUM_INFO, DATA_TABLES(SS_Info)); + + if (1 != revision.major || 2 > revision.minor) + return result; + + /* have to convert from Internal_SS format to SS_Info format */ + switch (id) { + case ASIC_INTERNAL_SS_ON_DP: + id_local = SS_ID_DP1; + break; + case ASIC_INTERNAL_SS_ON_LVDS: + { + struct embedded_panel_info panel_info; + + if (bios_parser_get_embedded_panel_info(&bp->base, &panel_info) + == BP_RESULT_OK) + id_local = panel_info.ss_id; + break; + } + default: + break; + } + + if (id_local == SS_ID_UNKNOWN) + return result; + + table_size = (le16_to_cpu(tbl->sHeader.usStructureSize) - + sizeof(ATOM_COMMON_TABLE_HEADER)) / + sizeof(ATOM_SPREAD_SPECTRUM_ASSIGNMENT); + + for (i = 0; i < table_size; i++) { + if (id_local != (uint32_t)tbl->asSS_Info[i].ucSS_Id) + continue; + + memset(ss_info, 0, sizeof(struct spread_spectrum_info)); + + if (ATOM_EXTERNAL_SS_MASK & + tbl->asSS_Info[i].ucSpreadSpectrumType) + ss_info->type.EXTERNAL = true; + + if (ATOM_SS_CENTRE_SPREAD_MODE_MASK & + tbl->asSS_Info[i].ucSpreadSpectrumType) + ss_info->type.CENTER_MODE = true; + + ss_info->type.STEP_AND_DELAY_INFO = true; + ss_info->spread_spectrum_percentage = + (uint32_t)le16_to_cpu(tbl->asSS_Info[i].usSpreadSpectrumPercentage); + ss_info->step_and_delay_info.step = tbl->asSS_Info[i].ucSS_Step; + ss_info->step_and_delay_info.delay = + tbl->asSS_Info[i].ucSS_Delay; + ss_info->step_and_delay_info.recommended_ref_div = + tbl->asSS_Info[i].ucRecommendedRef_Div; + ss_info->spread_spectrum_range = + (uint32_t)tbl->asSS_Info[i].ucSS_Range * 10000; + + /* there will be only one entry for each display type in SS_info + * table */ + result = BP_RESULT_OK; + break; + } + + return result; +} +static enum bp_result get_embedded_panel_info_v1_2( + struct bios_parser *bp, + struct embedded_panel_info *info); +static enum bp_result get_embedded_panel_info_v1_3( + struct bios_parser *bp, + struct embedded_panel_info *info); + +static enum bp_result bios_parser_get_embedded_panel_info( + struct dc_bios *dcb, + struct embedded_panel_info *info) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + ATOM_COMMON_TABLE_HEADER *hdr; + + if (!DATA_TABLES(LCD_Info)) + return BP_RESULT_FAILURE; + + hdr = GET_IMAGE(ATOM_COMMON_TABLE_HEADER, DATA_TABLES(LCD_Info)); + + if (!hdr) + return BP_RESULT_BADBIOSTABLE; + + switch (hdr->ucTableFormatRevision) { + case 1: + switch (hdr->ucTableContentRevision) { + case 0: + case 1: + case 2: + return get_embedded_panel_info_v1_2(bp, info); + case 3: + return get_embedded_panel_info_v1_3(bp, info); + default: + break; + } + break; + default: + break; + } + + return BP_RESULT_FAILURE; +} + +static enum bp_result get_embedded_panel_info_v1_2( + struct bios_parser *bp, + struct embedded_panel_info *info) +{ + ATOM_LVDS_INFO_V12 *lvds; + + if (!info) + return BP_RESULT_BADINPUT; + + if (!DATA_TABLES(LVDS_Info)) + return BP_RESULT_UNSUPPORTED; + + lvds = + GET_IMAGE(ATOM_LVDS_INFO_V12, DATA_TABLES(LVDS_Info)); + + if (!lvds) + return BP_RESULT_BADBIOSTABLE; + + if (1 != lvds->sHeader.ucTableFormatRevision + || 2 > lvds->sHeader.ucTableContentRevision) + return BP_RESULT_UNSUPPORTED; + + memset(info, 0, sizeof(struct embedded_panel_info)); + + /* We need to convert from 10KHz units into KHz units*/ + info->lcd_timing.pixel_clk = + le16_to_cpu(lvds->sLCDTiming.usPixClk) * 10; + /* usHActive does not include borders, according to VBIOS team*/ + info->lcd_timing.horizontal_addressable = + le16_to_cpu(lvds->sLCDTiming.usHActive); + /* usHBlanking_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.horizontal_blanking_time = + le16_to_cpu(lvds->sLCDTiming.usHBlanking_Time); + /* usVActive does not include borders, according to VBIOS team*/ + info->lcd_timing.vertical_addressable = + le16_to_cpu(lvds->sLCDTiming.usVActive); + /* 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->sLCDTiming.usVBlanking_Time); + info->lcd_timing.horizontal_sync_offset = + le16_to_cpu(lvds->sLCDTiming.usHSyncOffset); + info->lcd_timing.horizontal_sync_width = + le16_to_cpu(lvds->sLCDTiming.usHSyncWidth); + info->lcd_timing.vertical_sync_offset = + le16_to_cpu(lvds->sLCDTiming.usVSyncOffset); + info->lcd_timing.vertical_sync_width = + le16_to_cpu(lvds->sLCDTiming.usVSyncWidth); + info->lcd_timing.horizontal_border = lvds->sLCDTiming.ucHBorder; + info->lcd_timing.vertical_border = lvds->sLCDTiming.ucVBorder; + info->lcd_timing.misc_info.HORIZONTAL_CUT_OFF = + lvds->sLCDTiming.susModeMiscInfo.sbfAccess.HorizontalCutOff; + info->lcd_timing.misc_info.H_SYNC_POLARITY = + ~(uint32_t) + lvds->sLCDTiming.susModeMiscInfo.sbfAccess.HSyncPolarity; + info->lcd_timing.misc_info.V_SYNC_POLARITY = + ~(uint32_t) + lvds->sLCDTiming.susModeMiscInfo.sbfAccess.VSyncPolarity; + info->lcd_timing.misc_info.VERTICAL_CUT_OFF = + lvds->sLCDTiming.susModeMiscInfo.sbfAccess.VerticalCutOff; + info->lcd_timing.misc_info.H_REPLICATION_BY2 = + lvds->sLCDTiming.susModeMiscInfo.sbfAccess.H_ReplicationBy2; + info->lcd_timing.misc_info.V_REPLICATION_BY2 = + lvds->sLCDTiming.susModeMiscInfo.sbfAccess.V_ReplicationBy2; + info->lcd_timing.misc_info.COMPOSITE_SYNC = + lvds->sLCDTiming.susModeMiscInfo.sbfAccess.CompositeSync; + info->lcd_timing.misc_info.INTERLACE = + lvds->sLCDTiming.susModeMiscInfo.sbfAccess.Interlace; + info->lcd_timing.misc_info.DOUBLE_CLOCK = + lvds->sLCDTiming.susModeMiscInfo.sbfAccess.DoubleClock; + info->ss_id = lvds->ucSS_Id; + + { + uint8_t rr = le16_to_cpu(lvds->usSupportedRefreshRate); + /* Get minimum supported refresh rate*/ + if (SUPPORTED_LCD_REFRESHRATE_30Hz & rr) + info->supported_rr.REFRESH_RATE_30HZ = 1; + else if (SUPPORTED_LCD_REFRESHRATE_40Hz & rr) + info->supported_rr.REFRESH_RATE_40HZ = 1; + else if (SUPPORTED_LCD_REFRESHRATE_48Hz & rr) + info->supported_rr.REFRESH_RATE_48HZ = 1; + else if (SUPPORTED_LCD_REFRESHRATE_50Hz & rr) + info->supported_rr.REFRESH_RATE_50HZ = 1; + else if (SUPPORTED_LCD_REFRESHRATE_60Hz & rr) + info->supported_rr.REFRESH_RATE_60HZ = 1; + } + + /*Drr panel support can be reported by VBIOS*/ + if (LCDPANEL_CAP_DRR_SUPPORTED + & lvds->ucLCDPanel_SpecialHandlingCap) + info->drr_enabled = 1; + + if (ATOM_PANEL_MISC_DUAL & lvds->ucLVDS_Misc) + info->lcd_timing.misc_info.DOUBLE_CLOCK = true; + + if (ATOM_PANEL_MISC_888RGB & lvds->ucLVDS_Misc) + info->lcd_timing.misc_info.RGB888 = true; + + info->lcd_timing.misc_info.GREY_LEVEL = + (uint32_t) (ATOM_PANEL_MISC_GREY_LEVEL & + lvds->ucLVDS_Misc) >> ATOM_PANEL_MISC_GREY_LEVEL_SHIFT; + + if (ATOM_PANEL_MISC_SPATIAL & lvds->ucLVDS_Misc) + info->lcd_timing.misc_info.SPATIAL = true; + + if (ATOM_PANEL_MISC_TEMPORAL & lvds->ucLVDS_Misc) + info->lcd_timing.misc_info.TEMPORAL = true; + + if (ATOM_PANEL_MISC_API_ENABLED & lvds->ucLVDS_Misc) + info->lcd_timing.misc_info.API_ENABLED = true; + + return BP_RESULT_OK; +} + +static enum bp_result get_embedded_panel_info_v1_3( + struct bios_parser *bp, + struct embedded_panel_info *info) +{ + ATOM_LCD_INFO_V13 *lvds; + + if (!info) + return BP_RESULT_BADINPUT; + + if (!DATA_TABLES(LCD_Info)) + return BP_RESULT_UNSUPPORTED; + + lvds = GET_IMAGE(ATOM_LCD_INFO_V13, DATA_TABLES(LCD_Info)); + + if (!lvds) + return BP_RESULT_BADBIOSTABLE; + + if (!((1 == lvds->sHeader.ucTableFormatRevision) + && (3 <= lvds->sHeader.ucTableContentRevision))) + return BP_RESULT_UNSUPPORTED; + + memset(info, 0, sizeof(struct embedded_panel_info)); + + /* We need to convert from 10KHz units into KHz units */ + info->lcd_timing.pixel_clk = + le16_to_cpu(lvds->sLCDTiming.usPixClk) * 10; + /* usHActive does not include borders, according to VBIOS team */ + info->lcd_timing.horizontal_addressable = + le16_to_cpu(lvds->sLCDTiming.usHActive); + /* usHBlanking_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.horizontal_blanking_time = + le16_to_cpu(lvds->sLCDTiming.usHBlanking_Time); + /* usVActive does not include borders, according to VBIOS team*/ + info->lcd_timing.vertical_addressable = + le16_to_cpu(lvds->sLCDTiming.usVActive); + /* 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->sLCDTiming.usVBlanking_Time); + info->lcd_timing.horizontal_sync_offset = + le16_to_cpu(lvds->sLCDTiming.usHSyncOffset); + info->lcd_timing.horizontal_sync_width = + le16_to_cpu(lvds->sLCDTiming.usHSyncWidth); + info->lcd_timing.vertical_sync_offset = + le16_to_cpu(lvds->sLCDTiming.usVSyncOffset); + info->lcd_timing.vertical_sync_width = + le16_to_cpu(lvds->sLCDTiming.usVSyncWidth); + info->lcd_timing.horizontal_border = lvds->sLCDTiming.ucHBorder; + info->lcd_timing.vertical_border = lvds->sLCDTiming.ucVBorder; + info->lcd_timing.misc_info.HORIZONTAL_CUT_OFF = + lvds->sLCDTiming.susModeMiscInfo.sbfAccess.HorizontalCutOff; + info->lcd_timing.misc_info.H_SYNC_POLARITY = + ~(uint32_t) + lvds->sLCDTiming.susModeMiscInfo.sbfAccess.HSyncPolarity; + info->lcd_timing.misc_info.V_SYNC_POLARITY = + ~(uint32_t) + lvds->sLCDTiming.susModeMiscInfo.sbfAccess.VSyncPolarity; + info->lcd_timing.misc_info.VERTICAL_CUT_OFF = + lvds->sLCDTiming.susModeMiscInfo.sbfAccess.VerticalCutOff; + info->lcd_timing.misc_info.H_REPLICATION_BY2 = + lvds->sLCDTiming.susModeMiscInfo.sbfAccess.H_ReplicationBy2; + info->lcd_timing.misc_info.V_REPLICATION_BY2 = + lvds->sLCDTiming.susModeMiscInfo.sbfAccess.V_ReplicationBy2; + info->lcd_timing.misc_info.COMPOSITE_SYNC = + lvds->sLCDTiming.susModeMiscInfo.sbfAccess.CompositeSync; + info->lcd_timing.misc_info.INTERLACE = + lvds->sLCDTiming.susModeMiscInfo.sbfAccess.Interlace; + info->lcd_timing.misc_info.DOUBLE_CLOCK = + lvds->sLCDTiming.susModeMiscInfo.sbfAccess.DoubleClock; + info->ss_id = lvds->ucSS_Id; + + /* Drr panel support can be reported by VBIOS*/ + if (LCDPANEL_CAP_V13_DRR_SUPPORTED + & lvds->ucLCDPanel_SpecialHandlingCap) + info->drr_enabled = 1; + + /* Get supported refresh rate*/ + if (info->drr_enabled == 1) { + uint8_t min_rr = + lvds->sRefreshRateSupport.ucMinRefreshRateForDRR; + uint8_t rr = lvds->sRefreshRateSupport.ucSupportedRefreshRate; + + if (min_rr != 0) { + if (SUPPORTED_LCD_REFRESHRATE_30Hz & min_rr) + info->supported_rr.REFRESH_RATE_30HZ = 1; + else if (SUPPORTED_LCD_REFRESHRATE_40Hz & min_rr) + info->supported_rr.REFRESH_RATE_40HZ = 1; + else if (SUPPORTED_LCD_REFRESHRATE_48Hz & min_rr) + info->supported_rr.REFRESH_RATE_48HZ = 1; + else if (SUPPORTED_LCD_REFRESHRATE_50Hz & min_rr) + info->supported_rr.REFRESH_RATE_50HZ = 1; + else if (SUPPORTED_LCD_REFRESHRATE_60Hz & min_rr) + info->supported_rr.REFRESH_RATE_60HZ = 1; + } else { + if (SUPPORTED_LCD_REFRESHRATE_30Hz & rr) + info->supported_rr.REFRESH_RATE_30HZ = 1; + else if (SUPPORTED_LCD_REFRESHRATE_40Hz & rr) + info->supported_rr.REFRESH_RATE_40HZ = 1; + else if (SUPPORTED_LCD_REFRESHRATE_48Hz & rr) + info->supported_rr.REFRESH_RATE_48HZ = 1; + else if (SUPPORTED_LCD_REFRESHRATE_50Hz & rr) + info->supported_rr.REFRESH_RATE_50HZ = 1; + else if (SUPPORTED_LCD_REFRESHRATE_60Hz & rr) + info->supported_rr.REFRESH_RATE_60HZ = 1; + } + } + + if (ATOM_PANEL_MISC_V13_DUAL & lvds->ucLCD_Misc) + info->lcd_timing.misc_info.DOUBLE_CLOCK = true; + + if (ATOM_PANEL_MISC_V13_8BIT_PER_COLOR & lvds->ucLCD_Misc) + info->lcd_timing.misc_info.RGB888 = true; + + info->lcd_timing.misc_info.GREY_LEVEL = + (uint32_t) (ATOM_PANEL_MISC_V13_GREY_LEVEL & + lvds->ucLCD_Misc) >> ATOM_PANEL_MISC_V13_GREY_LEVEL_SHIFT; + + return BP_RESULT_OK; +} + +/** + * bios_parser_get_encoder_cap_info - get encoder capability + * information of input object id + * + * @dcb: pointer to the DC BIOS + * @object_id: object id + * @info: encoder cap information structure + * + * return: Bios parser result code + */ +static enum bp_result bios_parser_get_encoder_cap_info( + struct dc_bios *dcb, + struct graphics_object_id object_id, + struct bp_encoder_cap_info *info) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + ATOM_OBJECT *object; + ATOM_ENCODER_CAP_RECORD_V2 *record = NULL; + + if (!info) + return BP_RESULT_BADINPUT; + + 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; + + info->DP_HBR2_EN = record->usHBR2En; + info->DP_HBR3_EN = record->usHBR3En; + info->HDMI_6GB_EN = record->usHDMI6GEn; + return BP_RESULT_OK; +} + +/** + * get_encoder_cap_record - Get encoder cap record for the object + * + * @bp: pointer to the BIOS parser + * @object: ATOM object + * return: atom encoder cap record + * note: search all records to find the ATOM_ENCODER_CAP_RECORD_V2 record + */ +static ATOM_ENCODER_CAP_RECORD_V2 *get_encoder_cap_record( + struct bios_parser *bp, + ATOM_OBJECT *object) +{ + ATOM_COMMON_RECORD_HEADER *header; + uint32_t offset; + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object */ + return NULL; + } + + offset = le16_to_cpu(object->usRecordOffset) + + bp->object_info_tbl_offset; + + for (;;) { + header = GET_IMAGE(ATOM_COMMON_RECORD_HEADER, offset); + + if (!header) + return NULL; + + offset += header->ucRecordSize; + + if (LAST_RECORD_TYPE == header->ucRecordType || + !header->ucRecordSize) + break; + + if (ATOM_ENCODER_CAP_RECORD_TYPE != header->ucRecordType) + continue; + + if (sizeof(ATOM_ENCODER_CAP_RECORD_V2) <= header->ucRecordSize) + return (ATOM_ENCODER_CAP_RECORD_V2 *)header; + } + + return NULL; +} + +static uint32_t get_ss_entry_number( + struct bios_parser *bp, + uint32_t id); +static uint32_t get_ss_entry_number_from_internal_ss_info_tbl_v2_1( + struct bios_parser *bp, + uint32_t id); +static uint32_t get_ss_entry_number_from_internal_ss_info_tbl_V3_1( + struct bios_parser *bp, + uint32_t id); +static uint32_t get_ss_entry_number_from_ss_info_tbl( + struct bios_parser *bp, + uint32_t id); + +/** + * bios_parser_get_ss_entry_number + * Get Number of SpreadSpectrum Entry from the ASIC_InternalSS_Info table from + * the VBIOS that match the SSid (to be converted from signal) + * + * @dcb: pointer to the DC BIOS + * @signal: ASSignalType to be converted to SSid + * return: number of SS Entry that match the signal + */ +static uint32_t bios_parser_get_ss_entry_number( + struct dc_bios *dcb, + enum as_signal_type signal) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + uint32_t ss_id = 0; + ATOM_COMMON_TABLE_HEADER *header; + struct atom_data_revision revision; + + ss_id = signal_to_ss_id(signal); + + if (!DATA_TABLES(ASIC_InternalSS_Info)) + return get_ss_entry_number_from_ss_info_tbl(bp, ss_id); + + header = GET_IMAGE(ATOM_COMMON_TABLE_HEADER, + DATA_TABLES(ASIC_InternalSS_Info)); + get_atom_data_table_revision(header, &revision); + + switch (revision.major) { + case 2: + switch (revision.minor) { + case 1: + return get_ss_entry_number(bp, ss_id); + default: + break; + } + break; + case 3: + switch (revision.minor) { + case 1: + return + get_ss_entry_number_from_internal_ss_info_tbl_V3_1( + bp, ss_id); + default: + break; + } + break; + default: + break; + } + + return 0; +} + +/** + * get_ss_entry_number_from_ss_info_tbl + * Get Number of spread spectrum entry from the SS_Info table from the VBIOS. + * + * @bp: pointer to the BIOS parser + * @id: spread spectrum id + * return: number of SS Entry that match the id + * note: There can only be one entry for each id for SS_Info Table + */ +static uint32_t get_ss_entry_number_from_ss_info_tbl( + struct bios_parser *bp, + uint32_t id) +{ + ATOM_SPREAD_SPECTRUM_INFO *tbl; + ATOM_COMMON_TABLE_HEADER *header; + uint32_t table_size; + uint32_t i; + uint32_t number = 0; + uint32_t id_local = SS_ID_UNKNOWN; + struct atom_data_revision revision; + + /* SS_Info table exist */ + if (!DATA_TABLES(SS_Info)) + return number; + + header = GET_IMAGE(ATOM_COMMON_TABLE_HEADER, + DATA_TABLES(SS_Info)); + get_atom_data_table_revision(header, &revision); + + tbl = GET_IMAGE(ATOM_SPREAD_SPECTRUM_INFO, + DATA_TABLES(SS_Info)); + + if (1 != revision.major || 2 > revision.minor) + return number; + + /* have to convert from Internal_SS format to SS_Info format */ + switch (id) { + case ASIC_INTERNAL_SS_ON_DP: + id_local = SS_ID_DP1; + break; + case ASIC_INTERNAL_SS_ON_LVDS: { + struct embedded_panel_info panel_info; + + if (bios_parser_get_embedded_panel_info(&bp->base, &panel_info) + == BP_RESULT_OK) + id_local = panel_info.ss_id; + break; + } + default: + break; + } + + if (id_local == SS_ID_UNKNOWN) + return number; + + table_size = (le16_to_cpu(tbl->sHeader.usStructureSize) - + sizeof(ATOM_COMMON_TABLE_HEADER)) / + sizeof(ATOM_SPREAD_SPECTRUM_ASSIGNMENT); + + for (i = 0; i < table_size; i++) + if (id_local == (uint32_t)tbl->asSS_Info[i].ucSS_Id) { + number = 1; + break; + } + + return number; +} + +/** + * get_ss_entry_number + * Get spread sprectrum information from the ASIC_InternalSS_Info Ver 2.1 or + * SS_Info table from the VBIOS + * There can not be more than 1 entry for ASIC_InternalSS_Info Ver 2.1 or + * SS_Info. + * + * @bp: pointer to the BIOS parser + * @id: spread sprectrum info index + * return: Bios parser result code + */ +static uint32_t get_ss_entry_number(struct bios_parser *bp, uint32_t id) +{ + if (id == ASIC_INTERNAL_SS_ON_DP || id == ASIC_INTERNAL_SS_ON_LVDS) + return get_ss_entry_number_from_ss_info_tbl(bp, id); + + return get_ss_entry_number_from_internal_ss_info_tbl_v2_1(bp, id); +} + +/** + * get_ss_entry_number_from_internal_ss_info_tbl_v2_1 + * Get NUmber of spread sprectrum entry from the ASIC_InternalSS_Info table + * Ver 2.1 from the VBIOS + * There will not be multiple entry for Ver 2.1 + * + * @bp: pointer to the BIOS parser + * @id: spread sprectrum info index + * return: number of SS Entry that match the id + */ +static uint32_t get_ss_entry_number_from_internal_ss_info_tbl_v2_1( + struct bios_parser *bp, + uint32_t id) +{ + ATOM_ASIC_INTERNAL_SS_INFO_V2 *header_include; + ATOM_ASIC_SS_ASSIGNMENT_V2 *tbl; + uint32_t size; + uint32_t i; + + if (!DATA_TABLES(ASIC_InternalSS_Info)) + return 0; + + header_include = GET_IMAGE(ATOM_ASIC_INTERNAL_SS_INFO_V2, + DATA_TABLES(ASIC_InternalSS_Info)); + + size = (le16_to_cpu(header_include->sHeader.usStructureSize) + - sizeof(ATOM_COMMON_TABLE_HEADER)) + / sizeof(ATOM_ASIC_SS_ASSIGNMENT_V2); + + tbl = (ATOM_ASIC_SS_ASSIGNMENT_V2 *) + &header_include->asSpreadSpectrum[0]; + for (i = 0; i < size; i++) + if (tbl[i].ucClockIndication == (uint8_t)id) + return 1; + + return 0; +} +/** + * get_ss_entry_number_from_internal_ss_info_tbl_V3_1 + * Get Number of SpreadSpectrum Entry from the ASIC_InternalSS_Info table of + * the VBIOS that matches id + * + * @bp: pointer to the BIOS parser + * @id: spread sprectrum id + * return: number of SS Entry that match the id + */ +static uint32_t get_ss_entry_number_from_internal_ss_info_tbl_V3_1( + struct bios_parser *bp, + uint32_t id) +{ + uint32_t number = 0; + ATOM_ASIC_INTERNAL_SS_INFO_V3 *header_include; + ATOM_ASIC_SS_ASSIGNMENT_V3 *tbl; + uint32_t size; + uint32_t i; + + if (!DATA_TABLES(ASIC_InternalSS_Info)) + return number; + + header_include = GET_IMAGE(ATOM_ASIC_INTERNAL_SS_INFO_V3, + DATA_TABLES(ASIC_InternalSS_Info)); + size = (le16_to_cpu(header_include->sHeader.usStructureSize) - + sizeof(ATOM_COMMON_TABLE_HEADER)) / + sizeof(ATOM_ASIC_SS_ASSIGNMENT_V3); + + tbl = (ATOM_ASIC_SS_ASSIGNMENT_V3 *) + &header_include->asSpreadSpectrum[0]; + + for (i = 0; i < size; i++) + if (tbl[i].ucClockIndication == (uint8_t)id) + number++; + + return number; +} + +/** + * 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 + */ +static enum 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); + ATOM_GPIO_PIN_LUT *header; + uint32_t count = 0; + uint32_t i = 0; + + if (!DATA_TABLES(GPIO_Pin_LUT)) + return BP_RESULT_BADBIOSTABLE; + + header = GET_IMAGE(ATOM_GPIO_PIN_LUT, DATA_TABLES(GPIO_Pin_LUT)); + if (!header) + return BP_RESULT_BADBIOSTABLE; + + if (sizeof(ATOM_COMMON_TABLE_HEADER) + sizeof(ATOM_GPIO_PIN_LUT) + > le16_to_cpu(header->sHeader.usStructureSize)) + return BP_RESULT_BADBIOSTABLE; + + if (1 != header->sHeader.ucTableContentRevision) + return BP_RESULT_UNSUPPORTED; + + count = (le16_to_cpu(header->sHeader.usStructureSize) + - sizeof(ATOM_COMMON_TABLE_HEADER)) + / sizeof(ATOM_GPIO_PIN_ASSIGNMENT); + for (i = 0; i < count; ++i) { + if (header->asGPIO_Pin[i].ucGPIO_ID != gpio_id) + continue; + + info->offset = + (uint32_t) le16_to_cpu(header->asGPIO_Pin[i].usGpioPin_AIndex); + info->offset_y = info->offset + 2; + info->offset_en = info->offset + 1; + info->offset_mask = info->offset - 1; + + info->mask = (uint32_t) (1 << + header->asGPIO_Pin[i].ucGpioPinBitShift); + info->mask_y = info->mask + 2; + info->mask_en = info->mask + 1; + info->mask_mask = info->mask - 1; + + return BP_RESULT_OK; + } + + return BP_RESULT_NORECORD; +} + +static enum bp_result get_gpio_i2c_info(struct bios_parser *bp, + ATOM_I2C_RECORD *record, + struct graphics_object_i2c_info *info) +{ + ATOM_GPIO_I2C_INFO *header; + uint32_t count = 0; + + if (!info) + return BP_RESULT_BADINPUT; + + /* get the GPIO_I2C info */ + if (!DATA_TABLES(GPIO_I2C_Info)) + return BP_RESULT_BADBIOSTABLE; + + header = GET_IMAGE(ATOM_GPIO_I2C_INFO, DATA_TABLES(GPIO_I2C_Info)); + if (!header) + return BP_RESULT_BADBIOSTABLE; + + if (sizeof(ATOM_COMMON_TABLE_HEADER) + sizeof(ATOM_GPIO_I2C_ASSIGMENT) + > le16_to_cpu(header->sHeader.usStructureSize)) + return BP_RESULT_BADBIOSTABLE; + + if (1 != header->sHeader.ucTableContentRevision) + return BP_RESULT_UNSUPPORTED; + + /* get data count */ + count = (le16_to_cpu(header->sHeader.usStructureSize) + - sizeof(ATOM_COMMON_TABLE_HEADER)) + / sizeof(ATOM_GPIO_I2C_ASSIGMENT); + if (count < record->sucI2cId.bfI2C_LineMux) + return BP_RESULT_BADBIOSTABLE; + + /* get the GPIO_I2C_INFO */ + info->i2c_hw_assist = record->sucI2cId.bfHW_Capable; + info->i2c_line = record->sucI2cId.bfI2C_LineMux; + info->i2c_engine_id = record->sucI2cId.bfHW_EngineID; + info->i2c_slave_address = record->ucI2CAddr; + + info->gpio_info.clk_mask_register_index = + le16_to_cpu(header->asGPIO_Info[info->i2c_line].usClkMaskRegisterIndex); + info->gpio_info.clk_en_register_index = + le16_to_cpu(header->asGPIO_Info[info->i2c_line].usClkEnRegisterIndex); + info->gpio_info.clk_y_register_index = + le16_to_cpu(header->asGPIO_Info[info->i2c_line].usClkY_RegisterIndex); + info->gpio_info.clk_a_register_index = + le16_to_cpu(header->asGPIO_Info[info->i2c_line].usClkA_RegisterIndex); + info->gpio_info.data_mask_register_index = + le16_to_cpu(header->asGPIO_Info[info->i2c_line].usDataMaskRegisterIndex); + info->gpio_info.data_en_register_index = + le16_to_cpu(header->asGPIO_Info[info->i2c_line].usDataEnRegisterIndex); + info->gpio_info.data_y_register_index = + le16_to_cpu(header->asGPIO_Info[info->i2c_line].usDataY_RegisterIndex); + info->gpio_info.data_a_register_index = + le16_to_cpu(header->asGPIO_Info[info->i2c_line].usDataA_RegisterIndex); + + info->gpio_info.clk_mask_shift = + header->asGPIO_Info[info->i2c_line].ucClkMaskShift; + info->gpio_info.clk_en_shift = + header->asGPIO_Info[info->i2c_line].ucClkEnShift; + info->gpio_info.clk_y_shift = + header->asGPIO_Info[info->i2c_line].ucClkY_Shift; + info->gpio_info.clk_a_shift = + header->asGPIO_Info[info->i2c_line].ucClkA_Shift; + info->gpio_info.data_mask_shift = + header->asGPIO_Info[info->i2c_line].ucDataMaskShift; + info->gpio_info.data_en_shift = + header->asGPIO_Info[info->i2c_line].ucDataEnShift; + info->gpio_info.data_y_shift = + header->asGPIO_Info[info->i2c_line].ucDataY_Shift; + info->gpio_info.data_a_shift = + header->asGPIO_Info[info->i2c_line].ucDataA_Shift; + + return BP_RESULT_OK; +} + +static bool dal_graphics_object_id_is_valid(struct graphics_object_id id) +{ + bool rc = true; + + switch (id.type) { + case OBJECT_TYPE_UNKNOWN: + rc = false; + break; + case OBJECT_TYPE_GPU: + case OBJECT_TYPE_ENGINE: + /* do NOT check for id.id == 0 */ + if (id.enum_id == ENUM_ID_UNKNOWN) + rc = false; + break; + default: + if (id.id == 0 || id.enum_id == ENUM_ID_UNKNOWN) + rc = false; + break; + } + + return rc; +} + +static bool dal_graphics_object_id_is_equal( + struct graphics_object_id id1, + struct graphics_object_id id2) +{ + if (false == dal_graphics_object_id_is_valid(id1)) { + dm_output_to_console( + "%s: Warning: comparing invalid object 'id1'!\n", __func__); + return false; + } + + if (false == dal_graphics_object_id_is_valid(id2)) { + dm_output_to_console( + "%s: Warning: comparing invalid object 'id2'!\n", __func__); + return false; + } + + if (id1.id == id2.id && id1.enum_id == id2.enum_id + && id1.type == id2.type) + return true; + + return false; +} + +static ATOM_OBJECT *get_bios_object(struct bios_parser *bp, + struct graphics_object_id id) +{ + uint32_t offset; + ATOM_OBJECT_TABLE *tbl; + uint32_t i; + + switch (id.type) { + case OBJECT_TYPE_ENCODER: + offset = le16_to_cpu(bp->object_info_tbl.v1_1->usEncoderObjectTableOffset); + break; + + case OBJECT_TYPE_CONNECTOR: + offset = le16_to_cpu(bp->object_info_tbl.v1_1->usConnectorObjectTableOffset); + break; + + case OBJECT_TYPE_ROUTER: + offset = le16_to_cpu(bp->object_info_tbl.v1_1->usRouterObjectTableOffset); + break; + + case OBJECT_TYPE_GENERIC: + if (bp->object_info_tbl.revision.minor < 3) + return NULL; + offset = le16_to_cpu(bp->object_info_tbl.v1_3->usMiscObjectTableOffset); + break; + + default: + return NULL; + } + + offset += bp->object_info_tbl_offset; + + tbl = GET_IMAGE(ATOM_OBJECT_TABLE, offset); + if (!tbl) + return NULL; + + for (i = 0; i < tbl->ucNumberOfObjects; i++) + if (dal_graphics_object_id_is_equal(id, + object_id_from_bios_object_id( + le16_to_cpu(tbl->asObjects[i].usObjectID)))) + return &tbl->asObjects[i]; + + return NULL; +} + +static uint32_t get_src_obj_list(struct bios_parser *bp, ATOM_OBJECT *object, + uint16_t **id_list) +{ + uint32_t offset; + uint8_t *number; + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object id */ + return 0; + } + + offset = le16_to_cpu(object->usSrcDstTableOffset) + + bp->object_info_tbl_offset; + + number = GET_IMAGE(uint8_t, offset); + if (!number) + return 0; + + offset += sizeof(uint8_t); + *id_list = (uint16_t *)bios_get_image(&bp->base, offset, *number * sizeof(uint16_t)); + + if (!*id_list) + return 0; + + return *number; +} + +static struct device_id device_type_from_device_id(uint16_t device_id) +{ + + struct device_id result_device_id = {0}; + + switch (device_id) { + case ATOM_DEVICE_LCD1_SUPPORT: + result_device_id.device_type = DEVICE_TYPE_LCD; + result_device_id.enum_id = 1; + break; + + case ATOM_DEVICE_LCD2_SUPPORT: + result_device_id.device_type = DEVICE_TYPE_LCD; + result_device_id.enum_id = 2; + break; + + case ATOM_DEVICE_CRT1_SUPPORT: + result_device_id.device_type = DEVICE_TYPE_CRT; + result_device_id.enum_id = 1; + break; + + case ATOM_DEVICE_CRT2_SUPPORT: + result_device_id.device_type = DEVICE_TYPE_CRT; + result_device_id.enum_id = 2; + break; + + case ATOM_DEVICE_DFP1_SUPPORT: + result_device_id.device_type = DEVICE_TYPE_DFP; + result_device_id.enum_id = 1; + break; + + case ATOM_DEVICE_DFP2_SUPPORT: + result_device_id.device_type = DEVICE_TYPE_DFP; + result_device_id.enum_id = 2; + break; + + case ATOM_DEVICE_DFP3_SUPPORT: + result_device_id.device_type = DEVICE_TYPE_DFP; + result_device_id.enum_id = 3; + break; + + case ATOM_DEVICE_DFP4_SUPPORT: + result_device_id.device_type = DEVICE_TYPE_DFP; + result_device_id.enum_id = 4; + break; + + case ATOM_DEVICE_DFP5_SUPPORT: + result_device_id.device_type = DEVICE_TYPE_DFP; + result_device_id.enum_id = 5; + break; + + case ATOM_DEVICE_DFP6_SUPPORT: + result_device_id.device_type = DEVICE_TYPE_DFP; + result_device_id.enum_id = 6; + break; + + default: + BREAK_TO_DEBUGGER(); /* Invalid device Id */ + result_device_id.device_type = DEVICE_TYPE_UNKNOWN; + result_device_id.enum_id = 0; + } + return result_device_id; +} + +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); +} + +static uint32_t signal_to_ss_id(enum as_signal_type signal) +{ + uint32_t clk_id_ss = 0; + + switch (signal) { + case AS_SIGNAL_TYPE_DVI: + clk_id_ss = ASIC_INTERNAL_SS_ON_TMDS; + break; + case AS_SIGNAL_TYPE_HDMI: + clk_id_ss = ASIC_INTERNAL_SS_ON_HDMI; + break; + case AS_SIGNAL_TYPE_LVDS: + clk_id_ss = ASIC_INTERNAL_SS_ON_LVDS; + break; + case AS_SIGNAL_TYPE_DISPLAY_PORT: + clk_id_ss = ASIC_INTERNAL_SS_ON_DP; + break; + case AS_SIGNAL_TYPE_GPU_PLL: + clk_id_ss = ASIC_INTERNAL_GPUPLL_SS; + break; + default: + break; + } + return clk_id_ss; +} + +static uint32_t get_support_mask_for_device_id(struct device_id device_id) +{ + enum dal_device_type device_type = device_id.device_type; + uint32_t enum_id = device_id.enum_id; + + switch (device_type) { + case DEVICE_TYPE_LCD: + switch (enum_id) { + case 1: + return ATOM_DEVICE_LCD1_SUPPORT; + case 2: + return ATOM_DEVICE_LCD2_SUPPORT; + default: + break; + } + break; + case DEVICE_TYPE_CRT: + switch (enum_id) { + case 1: + return ATOM_DEVICE_CRT1_SUPPORT; + case 2: + return ATOM_DEVICE_CRT2_SUPPORT; + default: + break; + } + break; + case DEVICE_TYPE_DFP: + switch (enum_id) { + case 1: + return ATOM_DEVICE_DFP1_SUPPORT; + case 2: + return ATOM_DEVICE_DFP2_SUPPORT; + case 3: + return ATOM_DEVICE_DFP3_SUPPORT; + case 4: + return ATOM_DEVICE_DFP4_SUPPORT; + case 5: + return ATOM_DEVICE_DFP5_SUPPORT; + case 6: + return ATOM_DEVICE_DFP6_SUPPORT; + default: + break; + } + break; + case DEVICE_TYPE_CV: + switch (enum_id) { + case 1: + return ATOM_DEVICE_CV_SUPPORT; + default: + break; + } + break; + case DEVICE_TYPE_TV: + switch (enum_id) { + case 1: + return ATOM_DEVICE_TV1_SUPPORT; + default: + break; + } + break; + default: + break; + } + + /* Unidentified device ID, return empty support mask. */ + return 0; +} + +/** + * bios_parser_set_scratch_critical_state - update critical state + * bit in VBIOS scratch register + * @dcb: pointer to the DC BIOS + * @state: set or reset state + */ +static void bios_parser_set_scratch_critical_state( + struct dc_bios *dcb, + bool state) +{ + bios_set_scratch_critical_state(dcb, state); +} + +/* + * get_integrated_info_v8 + * + * @brief + * Get V8 integrated BIOS information + * + * @param + * bios_parser *bp - [in]BIOS parser handler to get master data table + * integrated_info *info - [out] store and output integrated info + * + * return: + * enum bp_result - BP_RESULT_OK if information is available, + * BP_RESULT_BADBIOSTABLE otherwise. + */ +static enum bp_result get_integrated_info_v8( + struct bios_parser *bp, + struct integrated_info *info) +{ + ATOM_INTEGRATED_SYSTEM_INFO_V1_8 *info_v8; + uint32_t i; + + info_v8 = GET_IMAGE(ATOM_INTEGRATED_SYSTEM_INFO_V1_8, + bp->master_data_tbl->ListOfDataTables.IntegratedSystemInfo); + + if (info_v8 == NULL) + return BP_RESULT_BADBIOSTABLE; + info->boot_up_engine_clock = le32_to_cpu(info_v8->ulBootUpEngineClock) * 10; + info->dentist_vco_freq = le32_to_cpu(info_v8->ulDentistVCOFreq) * 10; + info->boot_up_uma_clock = le32_to_cpu(info_v8->ulBootUpUMAClock) * 10; + + for (i = 0; i < NUMBER_OF_DISP_CLK_VOLTAGE; ++i) { + /* Convert [10KHz] into [KHz] */ + info->disp_clk_voltage[i].max_supported_clk = + le32_to_cpu(info_v8->sDISPCLK_Voltage[i]. + ulMaximumSupportedCLK) * 10; + info->disp_clk_voltage[i].voltage_index = + le32_to_cpu(info_v8->sDISPCLK_Voltage[i].ulVoltageIndex); + } + + info->boot_up_req_display_vector = + le32_to_cpu(info_v8->ulBootUpReqDisplayVector); + info->gpu_cap_info = + le32_to_cpu(info_v8->ulGPUCapInfo); + + /* + * system_config: Bit[0] = 0 : PCIE power gating disabled + * = 1 : PCIE power gating enabled + * Bit[1] = 0 : DDR-PLL shut down disabled + * = 1 : DDR-PLL shut down enabled + * Bit[2] = 0 : DDR-PLL power down disabled + * = 1 : DDR-PLL power down enabled + */ + info->system_config = le32_to_cpu(info_v8->ulSystemConfig); + info->cpu_cap_info = le32_to_cpu(info_v8->ulCPUCapInfo); + info->boot_up_nb_voltage = + le16_to_cpu(info_v8->usBootUpNBVoltage); + info->ext_disp_conn_info_offset = + le16_to_cpu(info_v8->usExtDispConnInfoOffset); + info->memory_type = info_v8->ucMemoryType; + info->ma_channel_number = info_v8->ucUMAChannelNumber; + info->gmc_restore_reset_time = + le32_to_cpu(info_v8->ulGMCRestoreResetTime); + + info->minimum_n_clk = + le32_to_cpu(info_v8->ulNbpStateNClkFreq[0]); + for (i = 1; i < 4; ++i) + info->minimum_n_clk = + info->minimum_n_clk < le32_to_cpu(info_v8->ulNbpStateNClkFreq[i]) ? + info->minimum_n_clk : le32_to_cpu(info_v8->ulNbpStateNClkFreq[i]); + + info->idle_n_clk = le32_to_cpu(info_v8->ulIdleNClk); + info->ddr_dll_power_up_time = + le32_to_cpu(info_v8->ulDDR_DLL_PowerUpTime); + info->ddr_pll_power_up_time = + le32_to_cpu(info_v8->ulDDR_PLL_PowerUpTime); + info->pcie_clk_ss_type = le16_to_cpu(info_v8->usPCIEClkSSType); + info->lvds_ss_percentage = + le16_to_cpu(info_v8->usLvdsSSPercentage); + info->lvds_sspread_rate_in_10hz = + le16_to_cpu(info_v8->usLvdsSSpreadRateIn10Hz); + info->hdmi_ss_percentage = + le16_to_cpu(info_v8->usHDMISSPercentage); + info->hdmi_sspread_rate_in_10hz = + le16_to_cpu(info_v8->usHDMISSpreadRateIn10Hz); + info->dvi_ss_percentage = + le16_to_cpu(info_v8->usDVISSPercentage); + info->dvi_sspread_rate_in_10_hz = + le16_to_cpu(info_v8->usDVISSpreadRateIn10Hz); + + info->max_lvds_pclk_freq_in_single_link = + le16_to_cpu(info_v8->usMaxLVDSPclkFreqInSingleLink); + info->lvds_misc = info_v8->ucLvdsMisc; + info->lvds_pwr_on_seq_dig_on_to_de_in_4ms = + info_v8->ucLVDSPwrOnSeqDIGONtoDE_in4Ms; + info->lvds_pwr_on_seq_de_to_vary_bl_in_4ms = + info_v8->ucLVDSPwrOnSeqDEtoVARY_BL_in4Ms; + info->lvds_pwr_on_seq_vary_bl_to_blon_in_4ms = + info_v8->ucLVDSPwrOnSeqVARY_BLtoBLON_in4Ms; + info->lvds_pwr_off_seq_vary_bl_to_de_in4ms = + info_v8->ucLVDSPwrOffSeqVARY_BLtoDE_in4Ms; + info->lvds_pwr_off_seq_de_to_dig_on_in4ms = + info_v8->ucLVDSPwrOffSeqDEtoDIGON_in4Ms; + info->lvds_pwr_off_seq_blon_to_vary_bl_in_4ms = + info_v8->ucLVDSPwrOffSeqBLONtoVARY_BL_in4Ms; + info->lvds_off_to_on_delay_in_4ms = + info_v8->ucLVDSOffToOnDelay_in4Ms; + info->lvds_bit_depth_control_val = + le32_to_cpu(info_v8->ulLCDBitDepthControlVal); + + for (i = 0; i < NUMBER_OF_AVAILABLE_SCLK; ++i) { + /* Convert [10KHz] into [KHz] */ + info->avail_s_clk[i].supported_s_clk = + le32_to_cpu(info_v8->sAvail_SCLK[i].ulSupportedSCLK) * 10; + info->avail_s_clk[i].voltage_index = + le16_to_cpu(info_v8->sAvail_SCLK[i].usVoltageIndex); + info->avail_s_clk[i].voltage_id = + le16_to_cpu(info_v8->sAvail_SCLK[i].usVoltageID); + } + + for (i = 0; i < NUMBER_OF_UCHAR_FOR_GUID; ++i) { + info->ext_disp_conn_info.gu_id[i] = + info_v8->sExtDispConnInfo.ucGuid[i]; + } + + for (i = 0; i < MAX_NUMBER_OF_EXT_DISPLAY_PATH; ++i) { + info->ext_disp_conn_info.path[i].device_connector_id = + object_id_from_bios_object_id( + le16_to_cpu(info_v8->sExtDispConnInfo.sPath[i].usDeviceConnector)); + + info->ext_disp_conn_info.path[i].ext_encoder_obj_id = + object_id_from_bios_object_id( + le16_to_cpu(info_v8->sExtDispConnInfo.sPath[i].usExtEncoderObjId)); + + info->ext_disp_conn_info.path[i].device_tag = + le16_to_cpu(info_v8->sExtDispConnInfo.sPath[i].usDeviceTag); + info->ext_disp_conn_info.path[i].device_acpi_enum = + le16_to_cpu(info_v8->sExtDispConnInfo.sPath[i].usDeviceACPIEnum); + info->ext_disp_conn_info.path[i].ext_aux_ddc_lut_index = + info_v8->sExtDispConnInfo.sPath[i].ucExtAUXDDCLutIndex; + info->ext_disp_conn_info.path[i].ext_hpd_pin_lut_index = + info_v8->sExtDispConnInfo.sPath[i].ucExtHPDPINLutIndex; + info->ext_disp_conn_info.path[i].channel_mapping.raw = + info_v8->sExtDispConnInfo.sPath[i].ucChannelMapping; + } + info->ext_disp_conn_info.checksum = + info_v8->sExtDispConnInfo.ucChecksum; + + return BP_RESULT_OK; +} + +/* + * get_integrated_info_v8 + * + * @brief + * Get V8 integrated BIOS information + * + * @param + * bios_parser *bp - [in]BIOS parser handler to get master data table + * integrated_info *info - [out] store and output integrated info + * + * return: + * enum bp_result - BP_RESULT_OK if information is available, + * BP_RESULT_BADBIOSTABLE otherwise. + */ +static enum bp_result get_integrated_info_v9( + struct bios_parser *bp, + struct integrated_info *info) +{ + ATOM_INTEGRATED_SYSTEM_INFO_V1_9 *info_v9; + uint32_t i; + + info_v9 = GET_IMAGE(ATOM_INTEGRATED_SYSTEM_INFO_V1_9, + bp->master_data_tbl->ListOfDataTables.IntegratedSystemInfo); + + if (!info_v9) + return BP_RESULT_BADBIOSTABLE; + + info->boot_up_engine_clock = le32_to_cpu(info_v9->ulBootUpEngineClock) * 10; + info->dentist_vco_freq = le32_to_cpu(info_v9->ulDentistVCOFreq) * 10; + info->boot_up_uma_clock = le32_to_cpu(info_v9->ulBootUpUMAClock) * 10; + + for (i = 0; i < NUMBER_OF_DISP_CLK_VOLTAGE; ++i) { + /* Convert [10KHz] into [KHz] */ + info->disp_clk_voltage[i].max_supported_clk = + le32_to_cpu(info_v9->sDISPCLK_Voltage[i].ulMaximumSupportedCLK) * 10; + info->disp_clk_voltage[i].voltage_index = + le32_to_cpu(info_v9->sDISPCLK_Voltage[i].ulVoltageIndex); + } + + info->boot_up_req_display_vector = + le32_to_cpu(info_v9->ulBootUpReqDisplayVector); + info->gpu_cap_info = le32_to_cpu(info_v9->ulGPUCapInfo); + + /* + * system_config: Bit[0] = 0 : PCIE power gating disabled + * = 1 : PCIE power gating enabled + * Bit[1] = 0 : DDR-PLL shut down disabled + * = 1 : DDR-PLL shut down enabled + * Bit[2] = 0 : DDR-PLL power down disabled + * = 1 : DDR-PLL power down enabled + */ + info->system_config = le32_to_cpu(info_v9->ulSystemConfig); + info->cpu_cap_info = le32_to_cpu(info_v9->ulCPUCapInfo); + info->boot_up_nb_voltage = le16_to_cpu(info_v9->usBootUpNBVoltage); + info->ext_disp_conn_info_offset = le16_to_cpu(info_v9->usExtDispConnInfoOffset); + info->memory_type = info_v9->ucMemoryType; + info->ma_channel_number = info_v9->ucUMAChannelNumber; + info->gmc_restore_reset_time = le32_to_cpu(info_v9->ulGMCRestoreResetTime); + + info->minimum_n_clk = le32_to_cpu(info_v9->ulNbpStateNClkFreq[0]); + for (i = 1; i < 4; ++i) + info->minimum_n_clk = + info->minimum_n_clk < le32_to_cpu(info_v9->ulNbpStateNClkFreq[i]) ? + info->minimum_n_clk : le32_to_cpu(info_v9->ulNbpStateNClkFreq[i]); + + info->idle_n_clk = le32_to_cpu(info_v9->ulIdleNClk); + info->ddr_dll_power_up_time = le32_to_cpu(info_v9->ulDDR_DLL_PowerUpTime); + info->ddr_pll_power_up_time = le32_to_cpu(info_v9->ulDDR_PLL_PowerUpTime); + info->pcie_clk_ss_type = le16_to_cpu(info_v9->usPCIEClkSSType); + info->lvds_ss_percentage = le16_to_cpu(info_v9->usLvdsSSPercentage); + info->lvds_sspread_rate_in_10hz = le16_to_cpu(info_v9->usLvdsSSpreadRateIn10Hz); + info->hdmi_ss_percentage = le16_to_cpu(info_v9->usHDMISSPercentage); + info->hdmi_sspread_rate_in_10hz = le16_to_cpu(info_v9->usHDMISSpreadRateIn10Hz); + info->dvi_ss_percentage = le16_to_cpu(info_v9->usDVISSPercentage); + info->dvi_sspread_rate_in_10_hz = le16_to_cpu(info_v9->usDVISSpreadRateIn10Hz); + + info->max_lvds_pclk_freq_in_single_link = + le16_to_cpu(info_v9->usMaxLVDSPclkFreqInSingleLink); + info->lvds_misc = info_v9->ucLvdsMisc; + info->lvds_pwr_on_seq_dig_on_to_de_in_4ms = + info_v9->ucLVDSPwrOnSeqDIGONtoDE_in4Ms; + info->lvds_pwr_on_seq_de_to_vary_bl_in_4ms = + info_v9->ucLVDSPwrOnSeqDEtoVARY_BL_in4Ms; + info->lvds_pwr_on_seq_vary_bl_to_blon_in_4ms = + info_v9->ucLVDSPwrOnSeqVARY_BLtoBLON_in4Ms; + info->lvds_pwr_off_seq_vary_bl_to_de_in4ms = + info_v9->ucLVDSPwrOffSeqVARY_BLtoDE_in4Ms; + info->lvds_pwr_off_seq_de_to_dig_on_in4ms = + info_v9->ucLVDSPwrOffSeqDEtoDIGON_in4Ms; + info->lvds_pwr_off_seq_blon_to_vary_bl_in_4ms = + info_v9->ucLVDSPwrOffSeqBLONtoVARY_BL_in4Ms; + info->lvds_off_to_on_delay_in_4ms = + info_v9->ucLVDSOffToOnDelay_in4Ms; + info->lvds_bit_depth_control_val = + le32_to_cpu(info_v9->ulLCDBitDepthControlVal); + + for (i = 0; i < NUMBER_OF_AVAILABLE_SCLK; ++i) { + /* Convert [10KHz] into [KHz] */ + info->avail_s_clk[i].supported_s_clk = + le32_to_cpu(info_v9->sAvail_SCLK[i].ulSupportedSCLK) * 10; + info->avail_s_clk[i].voltage_index = + le16_to_cpu(info_v9->sAvail_SCLK[i].usVoltageIndex); + info->avail_s_clk[i].voltage_id = + le16_to_cpu(info_v9->sAvail_SCLK[i].usVoltageID); + } + + for (i = 0; i < NUMBER_OF_UCHAR_FOR_GUID; ++i) { + info->ext_disp_conn_info.gu_id[i] = + info_v9->sExtDispConnInfo.ucGuid[i]; + } + + for (i = 0; i < MAX_NUMBER_OF_EXT_DISPLAY_PATH; ++i) { + info->ext_disp_conn_info.path[i].device_connector_id = + object_id_from_bios_object_id( + le16_to_cpu(info_v9->sExtDispConnInfo.sPath[i].usDeviceConnector)); + + info->ext_disp_conn_info.path[i].ext_encoder_obj_id = + object_id_from_bios_object_id( + le16_to_cpu(info_v9->sExtDispConnInfo.sPath[i].usExtEncoderObjId)); + + info->ext_disp_conn_info.path[i].device_tag = + le16_to_cpu(info_v9->sExtDispConnInfo.sPath[i].usDeviceTag); + info->ext_disp_conn_info.path[i].device_acpi_enum = + le16_to_cpu(info_v9->sExtDispConnInfo.sPath[i].usDeviceACPIEnum); + info->ext_disp_conn_info.path[i].ext_aux_ddc_lut_index = + info_v9->sExtDispConnInfo.sPath[i].ucExtAUXDDCLutIndex; + info->ext_disp_conn_info.path[i].ext_hpd_pin_lut_index = + info_v9->sExtDispConnInfo.sPath[i].ucExtHPDPINLutIndex; + info->ext_disp_conn_info.path[i].channel_mapping.raw = + info_v9->sExtDispConnInfo.sPath[i].ucChannelMapping; + } + info->ext_disp_conn_info.checksum = + info_v9->sExtDispConnInfo.ucChecksum; + + return BP_RESULT_OK; +} + +/* + * construct_integrated_info + * + * @brief + * Get integrated BIOS information based on table revision + * + * @param + * bios_parser *bp - [in]BIOS parser handler to get master data table + * integrated_info *info - [out] store and output integrated info + * + * return: + * enum bp_result - BP_RESULT_OK if information is available, + * BP_RESULT_BADBIOSTABLE otherwise. + */ +static enum bp_result construct_integrated_info( + struct bios_parser *bp, + struct integrated_info *info) +{ + enum bp_result result = BP_RESULT_BADBIOSTABLE; + + ATOM_COMMON_TABLE_HEADER *header; + struct atom_data_revision revision; + + if (bp->master_data_tbl->ListOfDataTables.IntegratedSystemInfo) { + header = GET_IMAGE(ATOM_COMMON_TABLE_HEADER, + bp->master_data_tbl->ListOfDataTables.IntegratedSystemInfo); + + get_atom_data_table_revision(header, &revision); + + /* Don't need to check major revision as they are all 1 */ + switch (revision.minor) { + case 8: + result = get_integrated_info_v8(bp, info); + break; + case 9: + result = get_integrated_info_v9(bp, info); + break; + default: + return result; + + } + } + + /* Sort voltage table from low to high*/ + if (result == BP_RESULT_OK) { + uint32_t i; + uint32_t j; + + for (i = 1; i < NUMBER_OF_DISP_CLK_VOLTAGE; ++i) { + for (j = i; j > 0; --j) { + if ( + info->disp_clk_voltage[j].max_supported_clk < + info->disp_clk_voltage[j-1].max_supported_clk) { + /* swap j and j - 1*/ + swap(info->disp_clk_voltage[j - 1], + info->disp_clk_voltage[j]); + } + } + } + + } + + return result; +} + +static struct integrated_info *bios_parser_create_integrated_info( + struct dc_bios *dcb) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + struct integrated_info *info = NULL; + + info = kzalloc(sizeof(struct integrated_info), GFP_KERNEL); + + if (info == NULL) { + ASSERT_CRITICAL(0); + return NULL; + } + + if (construct_integrated_info(bp, info) == BP_RESULT_OK) + return info; + + kfree(info); + + return NULL; +} + +static enum bp_result update_slot_layout_info( + struct dc_bios *dcb, + unsigned int i, + struct slot_layout_info *slot_layout_info, + unsigned int record_offset) +{ + unsigned int j; + struct bios_parser *bp; + ATOM_BRACKET_LAYOUT_RECORD *record; + ATOM_COMMON_RECORD_HEADER *record_header; + enum bp_result result = BP_RESULT_NORECORD; + + bp = BP_FROM_DCB(dcb); + record = NULL; + record_header = NULL; + + for (;;) { + + record_header = (ATOM_COMMON_RECORD_HEADER *) + GET_IMAGE(ATOM_COMMON_RECORD_HEADER, record_offset); + if (record_header == NULL) { + result = BP_RESULT_BADBIOSTABLE; + break; + } + + /* the end of the list */ + if (record_header->ucRecordType == 0xff || + record_header->ucRecordSize == 0) { + break; + } + + if (record_header->ucRecordType == + ATOM_BRACKET_LAYOUT_RECORD_TYPE && + sizeof(ATOM_BRACKET_LAYOUT_RECORD) + <= record_header->ucRecordSize) { + record = (ATOM_BRACKET_LAYOUT_RECORD *) + (record_header); + result = BP_RESULT_OK; + break; + } + + record_offset += record_header->ucRecordSize; + } + + /* return if the record not found */ + if (result != BP_RESULT_OK) + return result; + + /* get slot sizes */ + slot_layout_info->length = record->ucLength; + slot_layout_info->width = record->ucWidth; + + /* get info for each connector in the slot */ + slot_layout_info->num_of_connectors = record->ucConnNum; + for (j = 0; j < slot_layout_info->num_of_connectors; ++j) { + slot_layout_info->connectors[j].connector_type = + (enum connector_layout_type) + (record->asConnInfo[j].ucConnectorType); + switch (record->asConnInfo[j].ucConnectorType) { + case CONNECTOR_TYPE_DVI_D: + slot_layout_info->connectors[j].connector_type = + CONNECTOR_LAYOUT_TYPE_DVI_D; + slot_layout_info->connectors[j].length = + CONNECTOR_SIZE_DVI; + break; + + case CONNECTOR_TYPE_HDMI: + slot_layout_info->connectors[j].connector_type = + CONNECTOR_LAYOUT_TYPE_HDMI; + slot_layout_info->connectors[j].length = + CONNECTOR_SIZE_HDMI; + break; + + case CONNECTOR_TYPE_DISPLAY_PORT: + slot_layout_info->connectors[j].connector_type = + CONNECTOR_LAYOUT_TYPE_DP; + slot_layout_info->connectors[j].length = + CONNECTOR_SIZE_DP; + break; + + case CONNECTOR_TYPE_MINI_DISPLAY_PORT: + slot_layout_info->connectors[j].connector_type = + CONNECTOR_LAYOUT_TYPE_MINI_DP; + slot_layout_info->connectors[j].length = + CONNECTOR_SIZE_MINI_DP; + break; + + default: + slot_layout_info->connectors[j].connector_type = + CONNECTOR_LAYOUT_TYPE_UNKNOWN; + slot_layout_info->connectors[j].length = + CONNECTOR_SIZE_UNKNOWN; + } + + slot_layout_info->connectors[j].position = + record->asConnInfo[j].ucPosition; + slot_layout_info->connectors[j].connector_id = + object_id_from_bios_object_id( + record->asConnInfo[j].usConnectorObjectId); + } + return result; +} + + +static enum bp_result get_bracket_layout_record( + struct dc_bios *dcb, + unsigned int bracket_layout_id, + struct slot_layout_info *slot_layout_info) +{ + unsigned int i; + unsigned int record_offset; + struct bios_parser *bp; + enum bp_result result; + ATOM_OBJECT *object; + ATOM_OBJECT_TABLE *object_table; + unsigned int genericTableOffset; + + bp = BP_FROM_DCB(dcb); + object = NULL; + if (slot_layout_info == NULL) { + DC_LOG_DETECTION_EDID_PARSER("Invalid slot_layout_info\n"); + return BP_RESULT_BADINPUT; + } + + + genericTableOffset = bp->object_info_tbl_offset + + bp->object_info_tbl.v1_3->usMiscObjectTableOffset; + object_table = (ATOM_OBJECT_TABLE *) + GET_IMAGE(ATOM_OBJECT_TABLE, genericTableOffset); + if (!object_table) + return BP_RESULT_FAILURE; + + result = BP_RESULT_NORECORD; + for (i = 0; i < object_table->ucNumberOfObjects; ++i) { + + if (bracket_layout_id == + object_table->asObjects[i].usObjectID) { + + object = &object_table->asObjects[i]; + record_offset = object->usRecordOffset + + bp->object_info_tbl_offset; + + result = update_slot_layout_info(dcb, i, + slot_layout_info, record_offset); + break; + } + } + return result; +} + +static enum bp_result bios_get_board_layout_info( + struct dc_bios *dcb, + struct board_layout_info *board_layout_info) +{ + unsigned int i; + enum bp_result record_result; + + const unsigned int slot_index_to_vbios_id[MAX_BOARD_SLOTS] = { + GENERICOBJECT_BRACKET_LAYOUT_ENUM_ID1, + GENERICOBJECT_BRACKET_LAYOUT_ENUM_ID2, + 0, 0 + }; + + if (board_layout_info == NULL) { + DC_LOG_DETECTION_EDID_PARSER("Invalid board_layout_info\n"); + return BP_RESULT_BADINPUT; + } + + board_layout_info->num_of_slots = 0; + + for (i = 0; i < MAX_BOARD_SLOTS; ++i) { + record_result = get_bracket_layout_record(dcb, + slot_index_to_vbios_id[i], + &board_layout_info->slots[i]); + + if (record_result == BP_RESULT_NORECORD && i > 0) + break; /* no more slots present in bios */ + else if (record_result != BP_RESULT_OK) + return record_result; /* fail */ + + ++board_layout_info->num_of_slots; + } + + /* all data is valid */ + board_layout_info->is_number_of_slots_valid = 1; + board_layout_info->is_slots_size_valid = 1; + board_layout_info->is_connector_offsets_valid = 1; + board_layout_info->is_connector_lengths_valid = 1; + + return BP_RESULT_OK; +} + +/******************************************************************************/ + +static const struct dc_vbios_funcs vbios_funcs = { + .get_connectors_number = bios_parser_get_connectors_number, + + .get_connector_id = bios_parser_get_connector_id, + + .get_src_obj = bios_parser_get_src_obj, + + .get_i2c_info = bios_parser_get_i2c_info, + + .get_hpd_info = bios_parser_get_hpd_info, + + .get_device_tag = bios_parser_get_device_tag, + + .get_spread_spectrum_info = bios_parser_get_spread_spectrum_info, + + .get_ss_entry_number = bios_parser_get_ss_entry_number, + + .get_embedded_panel_info = bios_parser_get_embedded_panel_info, + + .get_gpio_pin_info = bios_parser_get_gpio_pin_info, + + .get_encoder_cap_info = bios_parser_get_encoder_cap_info, + + /* bios scratch register communication */ + .is_accelerated_mode = bios_is_accelerated_mode, + + .set_scratch_critical_state = bios_parser_set_scratch_critical_state, + + .is_device_id_supported = bios_parser_is_device_id_supported, + + /* COMMANDS */ + .encoder_control = bios_parser_encoder_control, + + .transmitter_control = bios_parser_transmitter_control, + + .enable_crtc = bios_parser_enable_crtc, + + .adjust_pixel_clock = bios_parser_adjust_pixel_clock, + + .set_pixel_clock = bios_parser_set_pixel_clock, + + .set_dce_clock = bios_parser_set_dce_clock, + + .enable_spread_spectrum_on_ppll = bios_parser_enable_spread_spectrum_on_ppll, + + .program_crtc_timing = bios_parser_program_crtc_timing, /* still use. should probably retire and program directly */ + + .program_display_engine_pll = bios_parser_program_display_engine_pll, + + .enable_disp_power_gating = bios_parser_enable_disp_power_gating, + + /* SW init and patch */ + + .bios_parser_destroy = bios_parser_destroy, + + .get_board_layout_info = bios_get_board_layout_info, + + .get_atom_dc_golden_table = NULL +}; + +static bool bios_parser_construct( + struct bios_parser *bp, + struct bp_init_data *init, + enum dce_version dce_version) +{ + 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}; + + 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); + bp->base.fw_info_valid = bios_parser_get_firmware_info(&bp->base, &bp->base.fw_info) == BP_RESULT_OK; + + return true; +} + +/******************************************************************************/ diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser.h b/drivers/gpu/drm/amd/display/dc/bios/bios_parser.h new file mode 100644 index 000000000..d6f162750 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser.h @@ -0,0 +1,33 @@ +/* + * 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 + * + */ + +#ifndef __DAL_BIOS_PARSER_H__ +#define __DAL_BIOS_PARSER_H__ + +struct dc_bios *bios_parser_create( + struct bp_init_data *init, + enum dce_version dce_version); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c new file mode 100644 index 000000000..93e40e0a1 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c @@ -0,0 +1,3711 @@ +/* + * 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 + * + */ + +#include "dm_services.h" +#include "core_types.h" + +#include "ObjectID.h" +#include "atomfirmware.h" + +#include "dc_bios_types.h" +#include "include/grph_object_ctrl_defs.h" +#include "include/bios_parser_interface.h" +#include "include/i2caux_interface.h" +#include "include/logger_interface.h" + +#include "command_table2.h" + +#include "bios_parser_helper.h" +#include "command_table_helper2.h" +#include "bios_parser2.h" +#include "bios_parser_types_internal2.h" +#include "bios_parser_interface.h" + +#include "bios_parser_common.h" + +#define DC_LOGGER \ + bp->base.ctx->logger + +#define LAST_RECORD_TYPE 0xff +#define SMU9_SYSPLL0_ID 0 + +static enum bp_result get_gpio_i2c_info(struct bios_parser *bp, + struct atom_i2c_record *record, + struct graphics_object_i2c_info *info); + +static enum bp_result bios_parser_get_firmware_info( + struct dc_bios *dcb, + struct dc_firmware_info *info); + +static enum bp_result bios_parser_get_encoder_cap_info( + struct dc_bios *dcb, + struct graphics_object_id object_id, + struct bp_encoder_cap_info *info); + +static enum bp_result get_firmware_info_v3_1( + struct bios_parser *bp, + struct dc_firmware_info *info); + +static enum bp_result get_firmware_info_v3_2( + struct bios_parser *bp, + struct dc_firmware_info *info); + +static enum bp_result get_firmware_info_v3_4( + struct bios_parser *bp, + struct dc_firmware_info *info); + +static struct atom_hpd_int_record *get_hpd_record(struct bios_parser *bp, + struct atom_display_object_path_v2 *object); + +static struct atom_encoder_caps_record *get_encoder_cap_record( + struct bios_parser *bp, + struct atom_display_object_path_v2 *object); + +#define BIOS_IMAGE_SIZE_OFFSET 2 +#define BIOS_IMAGE_SIZE_UNIT 512 + +#define DATA_TABLES(table) (bp->master_data_tbl->listOfdatatables.table) + +static void bios_parser2_destruct(struct bios_parser *bp) +{ + kfree(bp->base.bios_local_image); + kfree(bp->base.integrated_info); +} + +static void firmware_parser_destroy(struct dc_bios **dcb) +{ + struct bios_parser *bp = BP_FROM_DCB(*dcb); + + if (!bp) { + BREAK_TO_DEBUGGER(); + return; + } + + bios_parser2_destruct(bp); + + kfree(bp); + *dcb = NULL; +} + +static void get_atom_data_table_revision( + struct 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) atom_data_tbl->format_revision & 0x3f; + tbl_revision->minor = + (uint32_t) atom_data_tbl->content_revision & 0x3f; +} + +/* 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); + unsigned int count = 0; + unsigned int 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++; + + break; + } + return count; +} + +static struct graphics_object_id bios_parser_get_connector_id( + struct dc_bios *dcb, + uint8_t i) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + struct graphics_object_id object_id = dal_graphics_object_id_init( + 0, ENUM_ID_UNKNOWN, OBJECT_TYPE_UNKNOWN); + struct object_info_table *tbl = &bp->object_info_tbl; + struct display_object_info_table_v1_4 *v1_4 = tbl->v1_4; + + struct display_object_info_table_v1_5 *v1_5 = tbl->v1_5; + + 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; +} + +static enum bp_result bios_parser_get_src_obj(struct dc_bios *dcb, + struct graphics_object_id object_id, uint32_t index, + struct graphics_object_id *src_object_id) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + unsigned int i; + enum bp_result bp_result = BP_RESULT_BADINPUT; + struct graphics_object_id obj_id = { 0 }; + struct object_info_table *tbl = &bp->object_info_tbl; + + if (!src_object_id) + return bp_result; + + 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); + + 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_5->display_path[i].encoderobjid); + /* break; */ + } + } + bp_result = BP_RESULT_OK; + break; + + default: + bp_result = BP_RESULT_OK; + break; + } + + return bp_result; +} + +/* from graphics_object_id, find display path which includes the object_id */ +static struct atom_display_object_path_v2 *get_bios_object( + struct bios_parser *bp, + struct graphics_object_id id) +{ + unsigned int 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 */ +static struct atom_display_object_path_v3 *get_bios_object_from_path_v3( + struct bios_parser *bp, + struct graphics_object_id id) +{ + unsigned int 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; + + default: + return NULL; + } + + return NULL; +} + +static enum bp_result bios_parser_get_i2c_info(struct dc_bios *dcb, + struct graphics_object_id id, + struct graphics_object_i2c_info *info) +{ + uint32_t offset; + struct atom_display_object_path_v2 *object; + + struct atom_display_object_path_v3 *object_path_v3; + + struct atom_common_record_header *header; + struct atom_i2c_record *record; + struct atom_i2c_record dummy_record = {0}; + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!info) + return BP_RESULT_BADINPUT; + + if (id.type == OBJECT_TYPE_GENERIC) { + dummy_record.i2c_id = id.id; + + if (get_gpio_i2c_info(bp, &dummy_record, info) == BP_RESULT_OK) + return BP_RESULT_OK; + else + return BP_RESULT_NORECORD; + } + + switch (bp->object_info_tbl.revision.minor) { + case 4: + default: + object = get_bios_object(bp, id); + + if (!object) + return BP_RESULT_BADINPUT; + + offset = object->disp_recordoffset + bp->object_info_tbl_offset; + break; + case 5: + object_path_v3 = get_bios_object_from_path_v3(bp, id); + + if (!object_path_v3) + return BP_RESULT_BADINPUT; + + offset = object_path_v3->disp_recordoffset + bp->object_info_tbl_offset; + 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; + } + + offset += header->record_size; + } + + return BP_RESULT_NORECORD; +} + +static enum bp_result get_gpio_i2c_info( + struct bios_parser *bp, + struct atom_i2c_record *record, + struct graphics_object_i2c_info *info) +{ + struct atom_gpio_pin_lut_v2_1 *header; + uint32_t count = 0; + unsigned int table_index = 0; + bool find_valid = false; + struct atom_gpio_pin_assignment *pin; + + if (!info) + return BP_RESULT_BADINPUT; + + /* get the GPIO_I2C info */ + 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; + + /* TODO: is version change? */ + if (header->table_header.content_revision != 1) + return BP_RESULT_UNSUPPORTED; + + /* get data count */ + count = (le16_to_cpu(header->table_header.structuresize) + - sizeof(struct atom_common_table_header)) + / sizeof(struct atom_gpio_pin_assignment); + + pin = (struct atom_gpio_pin_assignment *) header->gpio_pin; + + for (table_index = 0; table_index < count; table_index++) { + if (((record->i2c_id & I2C_HW_CAP) == (pin->gpio_id & I2C_HW_CAP)) && + ((record->i2c_id & I2C_HW_ENGINE_ID_MASK) == (pin->gpio_id & I2C_HW_ENGINE_ID_MASK)) && + ((record->i2c_id & I2C_HW_LANE_MUX) == (pin->gpio_id & I2C_HW_LANE_MUX))) { + /* still valid */ + find_valid = true; + break; + } + pin = (struct atom_gpio_pin_assignment *)((uint8_t *)pin + sizeof(struct atom_gpio_pin_assignment)); + } + + /* 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; + + /* get the GPIO_I2C_INFO */ + info->i2c_hw_assist = (record->i2c_id & I2C_HW_CAP) ? true : false; + info->i2c_line = record->i2c_id & I2C_HW_LANE_MUX; + info->i2c_engine_id = (record->i2c_id & I2C_HW_ENGINE_ID_MASK) >> 4; + info->i2c_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 struct atom_hpd_int_record *get_hpd_record_for_path_v3( + struct bios_parser *bp, + struct atom_display_object_path_v3 *object) +{ + struct atom_common_record_header *header; + uint32_t offset; + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object */ + return NULL; + } + + offset = object->disp_recordoffset + bp->object_info_tbl_offset; + + for (;;) { + header = GET_IMAGE(struct atom_common_record_header, offset); + + if (!header) + return NULL; + + if (header->record_type == ATOM_RECORD_END_TYPE || + !header->record_size) + break; + + if (header->record_type == ATOM_HPD_INT_RECORD_TYPE + && sizeof(struct atom_hpd_int_record) <= + header->record_size) + return (struct atom_hpd_int_record *) header; + + offset += header->record_size; + } + + return NULL; +} + +static enum bp_result bios_parser_get_hpd_info( + struct dc_bios *dcb, + struct graphics_object_id id, + struct graphics_object_hpd_info *info) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + struct atom_display_object_path_v2 *object; + struct atom_display_object_path_v3 *object_path_v3; + struct atom_hpd_int_record *record = NULL; + + if (!info) + return BP_RESULT_BADINPUT; + + switch (bp->object_info_tbl.revision.minor) { + case 4: + default: + object = get_bios_object(bp, id); + + if (!object) + return BP_RESULT_BADINPUT; + + record = get_hpd_record(bp, object); + + break; + case 5: + object_path_v3 = get_bios_object_from_path_v3(bp, id); + + if (!object_path_v3) + return BP_RESULT_BADINPUT; + + record = get_hpd_record_for_path_v3(bp, object_path_v3); + break; + } + + if (record != NULL) { + info->hpd_int_gpio_uid = record->pin_id; + info->hpd_active = record->plugin_pin_state; + return BP_RESULT_OK; + } + + return BP_RESULT_NORECORD; +} + +static struct atom_hpd_int_record *get_hpd_record( + struct bios_parser *bp, + struct atom_display_object_path_v2 *object) +{ + struct atom_common_record_header *header; + uint32_t offset; + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object */ + return NULL; + } + + offset = le16_to_cpu(object->disp_recordoffset) + + bp->object_info_tbl_offset; + + for (;;) { + header = GET_IMAGE(struct atom_common_record_header, offset); + + if (!header) + return NULL; + + if (header->record_type == LAST_RECORD_TYPE || + !header->record_size) + break; + + if (header->record_type == ATOM_HPD_INT_RECORD_TYPE + && sizeof(struct atom_hpd_int_record) <= + header->record_size) + return (struct atom_hpd_int_record *) header; + + offset += header->record_size; + } + + return NULL; +} + +/** + * 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 + */ +static enum 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; + + info->offset = + (uint32_t) le16_to_cpu( + header->gpio_pin[i].data_a_reg_index); + info->offset_y = info->offset + 2; + info->offset_en = info->offset + 1; + info->offset_mask = info->offset - 1; + + info->mask = (uint32_t) (1 << + header->gpio_pin[i].gpio_bitshift); + info->mask_y = info->mask + 2; + info->mask_en = info->mask + 1; + info->mask_mask = info->mask - 1; + + return BP_RESULT_OK; + } + + return BP_RESULT_NORECORD; +} + +static struct device_id device_type_from_device_id(uint16_t device_id) +{ + + struct device_id result_device_id; + + result_device_id.raw_device_tag = device_id; + + switch (device_id) { + case ATOM_DISPLAY_LCD1_SUPPORT: + result_device_id.device_type = DEVICE_TYPE_LCD; + result_device_id.enum_id = 1; + break; + + case ATOM_DISPLAY_LCD2_SUPPORT: + result_device_id.device_type = DEVICE_TYPE_LCD; + result_device_id.enum_id = 2; + break; + + case ATOM_DISPLAY_DFP1_SUPPORT: + result_device_id.device_type = DEVICE_TYPE_DFP; + result_device_id.enum_id = 1; + break; + + case ATOM_DISPLAY_DFP2_SUPPORT: + result_device_id.device_type = DEVICE_TYPE_DFP; + result_device_id.enum_id = 2; + break; + + case ATOM_DISPLAY_DFP3_SUPPORT: + result_device_id.device_type = DEVICE_TYPE_DFP; + result_device_id.enum_id = 3; + break; + + case ATOM_DISPLAY_DFP4_SUPPORT: + result_device_id.device_type = DEVICE_TYPE_DFP; + result_device_id.enum_id = 4; + break; + + case ATOM_DISPLAY_DFP5_SUPPORT: + result_device_id.device_type = DEVICE_TYPE_DFP; + result_device_id.enum_id = 5; + break; + + case ATOM_DISPLAY_DFP6_SUPPORT: + result_device_id.device_type = DEVICE_TYPE_DFP; + result_device_id.enum_id = 6; + break; + + default: + BREAK_TO_DEBUGGER(); /* Invalid device Id */ + result_device_id.device_type = DEVICE_TYPE_UNKNOWN; + result_device_id.enum_id = 0; + } + return result_device_id; +} + +static enum bp_result bios_parser_get_device_tag( + struct dc_bios *dcb, + struct graphics_object_id connector_object_id, + uint32_t device_tag_index, + struct connector_device_tag_info *info) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + struct atom_display_object_path_v2 *object; + + struct atom_display_object_path_v3 *object_path_v3; + + + if (!info) + return BP_RESULT_BADINPUT; + + switch (bp->object_info_tbl.revision.minor) { + case 4: + default: + /* getBiosObject will return MXM object */ + object = get_bios_object(bp, connector_object_id); + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object id */ + return BP_RESULT_BADINPUT; + } + + info->acpi_device = 0; /* BIOS no longer provides this */ + info->dev_id = device_type_from_device_id(object->device_tag); + break; + case 5: + object_path_v3 = get_bios_object_from_path_v3(bp, connector_object_id); + + if (!object_path_v3) { + BREAK_TO_DEBUGGER(); /* Invalid object id */ + return BP_RESULT_BADINPUT; + } + info->acpi_device = 0; /* BIOS no longer provides this */ + info->dev_id = device_type_from_device_id(object_path_v3->device_tag); + break; + } + + return BP_RESULT_OK; +} + +static enum bp_result get_ss_info_v4_1( + struct bios_parser *bp, + uint32_t id, + uint32_t index, + struct spread_spectrum_info *ss_info) +{ + enum bp_result result = BP_RESULT_OK; + struct atom_display_controller_info_v4_1 *disp_cntl_tbl = NULL; + struct atom_smu_info_v3_3 *smu_info = NULL; + + if (!ss_info) + return BP_RESULT_BADINPUT; + + if (!DATA_TABLES(dce_info)) + return BP_RESULT_BADBIOSTABLE; + + disp_cntl_tbl = GET_IMAGE(struct atom_display_controller_info_v4_1, + DATA_TABLES(dce_info)); + if (!disp_cntl_tbl) + return BP_RESULT_BADBIOSTABLE; + + + ss_info->type.STEP_AND_DELAY_INFO = false; + ss_info->spread_percentage_divider = 1000; + /* BIOS no longer uses target clock. Always enable for now */ + ss_info->target_clock_range = 0xffffffff; + + switch (id) { + case AS_SIGNAL_TYPE_DVI: + ss_info->spread_spectrum_percentage = + disp_cntl_tbl->dvi_ss_percentage; + ss_info->spread_spectrum_range = + disp_cntl_tbl->dvi_ss_rate_10hz * 10; + if (disp_cntl_tbl->dvi_ss_mode & ATOM_SS_CENTRE_SPREAD_MODE) + ss_info->type.CENTER_MODE = true; + + DC_LOG_BIOS("AS_SIGNAL_TYPE_DVI ss_percentage: %d\n", ss_info->spread_spectrum_percentage); + break; + case AS_SIGNAL_TYPE_HDMI: + ss_info->spread_spectrum_percentage = + disp_cntl_tbl->hdmi_ss_percentage; + ss_info->spread_spectrum_range = + disp_cntl_tbl->hdmi_ss_rate_10hz * 10; + if (disp_cntl_tbl->hdmi_ss_mode & ATOM_SS_CENTRE_SPREAD_MODE) + ss_info->type.CENTER_MODE = true; + + 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; + } + + return result; +} + +static enum bp_result get_ss_info_v4_2( + struct bios_parser *bp, + uint32_t id, + uint32_t index, + struct spread_spectrum_info *ss_info) +{ + enum bp_result result = BP_RESULT_OK; + struct atom_display_controller_info_v4_2 *disp_cntl_tbl = NULL; + struct atom_smu_info_v3_1 *smu_info = NULL; + + if (!ss_info) + return BP_RESULT_BADINPUT; + + if (!DATA_TABLES(dce_info)) + return BP_RESULT_BADBIOSTABLE; + + if (!DATA_TABLES(smu_info)) + return BP_RESULT_BADBIOSTABLE; + + disp_cntl_tbl = GET_IMAGE(struct atom_display_controller_info_v4_2, + DATA_TABLES(dce_info)); + if (!disp_cntl_tbl) + return BP_RESULT_BADBIOSTABLE; + + smu_info = GET_IMAGE(struct atom_smu_info_v3_1, 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->type.STEP_AND_DELAY_INFO = false; + ss_info->spread_percentage_divider = 1000; + /* BIOS no longer uses target clock. Always enable for now */ + ss_info->target_clock_range = 0xffffffff; + + switch (id) { + case AS_SIGNAL_TYPE_DVI: + ss_info->spread_spectrum_percentage = + disp_cntl_tbl->dvi_ss_percentage; + ss_info->spread_spectrum_range = + disp_cntl_tbl->dvi_ss_rate_10hz * 10; + if (disp_cntl_tbl->dvi_ss_mode & ATOM_SS_CENTRE_SPREAD_MODE) + ss_info->type.CENTER_MODE = true; + + DC_LOG_BIOS("AS_SIGNAL_TYPE_DVI ss_percentage: %d\n", ss_info->spread_spectrum_percentage); + break; + case AS_SIGNAL_TYPE_HDMI: + ss_info->spread_spectrum_percentage = + disp_cntl_tbl->hdmi_ss_percentage; + ss_info->spread_spectrum_range = + disp_cntl_tbl->hdmi_ss_rate_10hz * 10; + if (disp_cntl_tbl->hdmi_ss_mode & ATOM_SS_CENTRE_SPREAD_MODE) + ss_info->type.CENTER_MODE = true; + + 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; + } + + return result; +} + +static enum bp_result get_ss_info_v4_5( + struct bios_parser *bp, + uint32_t id, + uint32_t index, + struct spread_spectrum_info *ss_info) +{ + enum bp_result result = BP_RESULT_OK; + struct atom_display_controller_info_v4_5 *disp_cntl_tbl = NULL; + + if (!ss_info) + return BP_RESULT_BADINPUT; + + if (!DATA_TABLES(dce_info)) + return BP_RESULT_BADBIOSTABLE; + + disp_cntl_tbl = GET_IMAGE(struct atom_display_controller_info_v4_5, + DATA_TABLES(dce_info)); + if (!disp_cntl_tbl) + return BP_RESULT_BADBIOSTABLE; + + ss_info->type.STEP_AND_DELAY_INFO = false; + ss_info->spread_percentage_divider = 1000; + /* BIOS no longer uses target clock. Always enable for now */ + ss_info->target_clock_range = 0xffffffff; + + switch (id) { + case AS_SIGNAL_TYPE_DVI: + ss_info->spread_spectrum_percentage = + disp_cntl_tbl->dvi_ss_percentage; + ss_info->spread_spectrum_range = + disp_cntl_tbl->dvi_ss_rate_10hz * 10; + if (disp_cntl_tbl->dvi_ss_mode & ATOM_SS_CENTRE_SPREAD_MODE) + ss_info->type.CENTER_MODE = true; + + DC_LOG_BIOS("AS_SIGNAL_TYPE_DVI ss_percentage: %d\n", ss_info->spread_spectrum_percentage); + break; + case AS_SIGNAL_TYPE_HDMI: + ss_info->spread_spectrum_percentage = + disp_cntl_tbl->hdmi_ss_percentage; + ss_info->spread_spectrum_range = + disp_cntl_tbl->hdmi_ss_rate_10hz * 10; + if (disp_cntl_tbl->hdmi_ss_mode & ATOM_SS_CENTRE_SPREAD_MODE) + ss_info->type.CENTER_MODE = true; + + 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 + */ +static enum 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; + + header = GET_IMAGE(struct atom_common_table_header, + DATA_TABLES(dce_info)); + get_atom_data_table_revision(header, &tbl_revision); + + switch (tbl_revision.major) { + case 4: + switch (tbl_revision.minor) { + case 1: + return get_ss_info_v4_1(bp, signal, index, ss_info); + case 2: + case 3: + case 4: + return get_ss_info_v4_2(bp, signal, index, ss_info); + case 5: + return get_ss_info_v4_5(bp, signal, index, ss_info); + + default: + ASSERT(0); + break; + } + break; + default: + break; + } + /* there can not be more then one entry for SS Info table */ + return result; +} + +static enum bp_result get_soc_bb_info_v4_4( + struct bios_parser *bp, + struct bp_soc_bb_info *soc_bb_info) +{ + enum bp_result result = BP_RESULT_OK; + struct atom_display_controller_info_v4_4 *disp_cntl_tbl = NULL; + + if (!soc_bb_info) + return BP_RESULT_BADINPUT; + + if (!DATA_TABLES(dce_info)) + return BP_RESULT_BADBIOSTABLE; + + if (!DATA_TABLES(smu_info)) + return BP_RESULT_BADBIOSTABLE; + + disp_cntl_tbl = GET_IMAGE(struct atom_display_controller_info_v4_4, + DATA_TABLES(dce_info)); + if (!disp_cntl_tbl) + return BP_RESULT_BADBIOSTABLE; + + soc_bb_info->dram_clock_change_latency_100ns = disp_cntl_tbl->max_mclk_chg_lat; + soc_bb_info->dram_sr_enter_exit_latency_100ns = disp_cntl_tbl->max_sr_enter_exit_lat; + soc_bb_info->dram_sr_exit_latency_100ns = disp_cntl_tbl->max_sr_exit_lat; + + return result; +} + +static enum bp_result get_soc_bb_info_v4_5( + struct bios_parser *bp, + struct bp_soc_bb_info *soc_bb_info) +{ + enum bp_result result = BP_RESULT_OK; + struct atom_display_controller_info_v4_5 *disp_cntl_tbl = NULL; + + if (!soc_bb_info) + return BP_RESULT_BADINPUT; + + if (!DATA_TABLES(dce_info)) + return BP_RESULT_BADBIOSTABLE; + + disp_cntl_tbl = GET_IMAGE(struct atom_display_controller_info_v4_5, + DATA_TABLES(dce_info)); + if (!disp_cntl_tbl) + return BP_RESULT_BADBIOSTABLE; + + soc_bb_info->dram_clock_change_latency_100ns = disp_cntl_tbl->max_mclk_chg_lat; + soc_bb_info->dram_sr_enter_exit_latency_100ns = disp_cntl_tbl->max_sr_enter_exit_lat; + soc_bb_info->dram_sr_exit_latency_100ns = disp_cntl_tbl->max_sr_exit_lat; + + return result; +} + +static enum bp_result bios_parser_get_soc_bb_info( + struct dc_bios *dcb, + struct bp_soc_bb_info *soc_bb_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 (!soc_bb_info) /* check for bad input */ + return BP_RESULT_BADINPUT; + + if (!DATA_TABLES(dce_info)) + return BP_RESULT_UNSUPPORTED; + + header = GET_IMAGE(struct atom_common_table_header, + DATA_TABLES(dce_info)); + get_atom_data_table_revision(header, &tbl_revision); + + 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; + } + + return result; +} + +static enum bp_result get_disp_caps_v4_1( + struct bios_parser *bp, + uint8_t *dce_caps) +{ + enum bp_result result = BP_RESULT_OK; + struct atom_display_controller_info_v4_1 *disp_cntl_tbl = NULL; + + if (!dce_caps) + return BP_RESULT_BADINPUT; + + if (!DATA_TABLES(dce_info)) + return BP_RESULT_BADBIOSTABLE; + + disp_cntl_tbl = GET_IMAGE(struct atom_display_controller_info_v4_1, + DATA_TABLES(dce_info)); + + if (!disp_cntl_tbl) + return BP_RESULT_BADBIOSTABLE; + + *dce_caps = disp_cntl_tbl->display_caps; + + return result; +} + +static enum bp_result get_disp_caps_v4_2( + struct bios_parser *bp, + uint8_t *dce_caps) +{ + enum bp_result result = BP_RESULT_OK; + struct atom_display_controller_info_v4_2 *disp_cntl_tbl = NULL; + + if (!dce_caps) + return BP_RESULT_BADINPUT; + + if (!DATA_TABLES(dce_info)) + return BP_RESULT_BADBIOSTABLE; + + disp_cntl_tbl = GET_IMAGE(struct atom_display_controller_info_v4_2, + DATA_TABLES(dce_info)); + + if (!disp_cntl_tbl) + return BP_RESULT_BADBIOSTABLE; + + *dce_caps = disp_cntl_tbl->display_caps; + + return result; +} + +static enum bp_result get_disp_caps_v4_3( + struct bios_parser *bp, + uint8_t *dce_caps) +{ + enum bp_result result = BP_RESULT_OK; + struct atom_display_controller_info_v4_3 *disp_cntl_tbl = NULL; + + if (!dce_caps) + return BP_RESULT_BADINPUT; + + if (!DATA_TABLES(dce_info)) + return BP_RESULT_BADBIOSTABLE; + + disp_cntl_tbl = GET_IMAGE(struct atom_display_controller_info_v4_3, + DATA_TABLES(dce_info)); + + if (!disp_cntl_tbl) + return BP_RESULT_BADBIOSTABLE; + + *dce_caps = disp_cntl_tbl->display_caps; + + return result; +} + +static enum bp_result get_disp_caps_v4_4( + struct bios_parser *bp, + uint8_t *dce_caps) +{ + enum bp_result result = BP_RESULT_OK; + struct atom_display_controller_info_v4_4 *disp_cntl_tbl = NULL; + + if (!dce_caps) + return BP_RESULT_BADINPUT; + + if (!DATA_TABLES(dce_info)) + return BP_RESULT_BADBIOSTABLE; + + disp_cntl_tbl = GET_IMAGE(struct atom_display_controller_info_v4_4, + DATA_TABLES(dce_info)); + + if (!disp_cntl_tbl) + return BP_RESULT_BADBIOSTABLE; + + *dce_caps = disp_cntl_tbl->display_caps; + + return result; +} + +static enum bp_result get_disp_caps_v4_5( + struct bios_parser *bp, + uint8_t *dce_caps) +{ + enum bp_result result = BP_RESULT_OK; + struct atom_display_controller_info_v4_5 *disp_cntl_tbl = NULL; + + if (!dce_caps) + return BP_RESULT_BADINPUT; + + if (!DATA_TABLES(dce_info)) + return BP_RESULT_BADBIOSTABLE; + + disp_cntl_tbl = GET_IMAGE(struct atom_display_controller_info_v4_5, + DATA_TABLES(dce_info)); + + if (!disp_cntl_tbl) + return BP_RESULT_BADBIOSTABLE; + + *dce_caps = disp_cntl_tbl->display_caps; + + return result; +} + +static enum bp_result bios_parser_get_lttpr_interop( + struct dc_bios *dcb, + uint8_t *dce_caps) +{ + 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 (!DATA_TABLES(dce_info)) + return BP_RESULT_UNSUPPORTED; + + header = GET_IMAGE(struct atom_common_table_header, + DATA_TABLES(dce_info)); + get_atom_data_table_revision(header, &tbl_revision); + switch (tbl_revision.major) { + case 4: + switch (tbl_revision.minor) { + case 1: + result = get_disp_caps_v4_1(bp, dce_caps); + *dce_caps = !!(*dce_caps & DCE_INFO_CAPS_VBIOS_LTTPR_TRANSPARENT_ENABLE); + break; + case 2: + result = get_disp_caps_v4_2(bp, dce_caps); + *dce_caps = !!(*dce_caps & DCE_INFO_CAPS_VBIOS_LTTPR_TRANSPARENT_ENABLE); + break; + case 3: + result = get_disp_caps_v4_3(bp, dce_caps); + *dce_caps = !!(*dce_caps & DCE_INFO_CAPS_VBIOS_LTTPR_TRANSPARENT_ENABLE); + break; + case 4: + result = get_disp_caps_v4_4(bp, dce_caps); + *dce_caps = !!(*dce_caps & DCE_INFO_CAPS_VBIOS_LTTPR_TRANSPARENT_ENABLE); + break; + case 5: + result = get_disp_caps_v4_5(bp, dce_caps); + *dce_caps = !!(*dce_caps & DCE_INFO_CAPS_VBIOS_LTTPR_TRANSPARENT_ENABLE); + break; + + default: + break; + } + break; + default: + break; + } + DC_LOG_BIOS("DCE_INFO_CAPS_VBIOS_LTTPR_TRANSPARENT_ENABLE: %d tbl_revision.major = %d tbl_revision.minor = %d\n", *dce_caps, tbl_revision.major, tbl_revision.minor); + return result; +} + +static enum bp_result bios_parser_get_lttpr_caps( + struct dc_bios *dcb, + uint8_t *dce_caps) +{ + 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 (!DATA_TABLES(dce_info)) + return BP_RESULT_UNSUPPORTED; + + *dce_caps = 0; + header = GET_IMAGE(struct atom_common_table_header, + DATA_TABLES(dce_info)); + get_atom_data_table_revision(header, &tbl_revision); + switch (tbl_revision.major) { + case 4: + switch (tbl_revision.minor) { + case 1: + result = get_disp_caps_v4_1(bp, dce_caps); + *dce_caps = !!(*dce_caps & DCE_INFO_CAPS_LTTPR_SUPPORT_ENABLE); + break; + case 2: + result = get_disp_caps_v4_2(bp, dce_caps); + *dce_caps = !!(*dce_caps & DCE_INFO_CAPS_LTTPR_SUPPORT_ENABLE); + break; + case 3: + result = get_disp_caps_v4_3(bp, dce_caps); + *dce_caps = !!(*dce_caps & DCE_INFO_CAPS_LTTPR_SUPPORT_ENABLE); + break; + case 4: + result = get_disp_caps_v4_4(bp, dce_caps); + *dce_caps = !!(*dce_caps & DCE_INFO_CAPS_LTTPR_SUPPORT_ENABLE); + break; + case 5: + result = get_disp_caps_v4_5(bp, dce_caps); + *dce_caps = !!(*dce_caps & DCE_INFO_CAPS_LTTPR_SUPPORT_ENABLE); + break; + default: + break; + } + break; + default: + break; + } + DC_LOG_BIOS("DCE_INFO_CAPS_LTTPR_SUPPORT_ENABLE: %d tbl_revision.major = %d tbl_revision.minor = %d\n", *dce_caps, tbl_revision.major, tbl_revision.minor); + if (dcb->ctx->dc->config.force_bios_enable_lttpr && *dce_caps == 0) { + *dce_caps = 1; + DC_LOG_BIOS("DCE_INFO_CAPS_VBIOS_LTTPR_TRANSPARENT_ENABLE: forced enabled"); + } + return result; +} + +static enum bp_result get_embedded_panel_info_v2_1( + struct bios_parser *bp, + struct embedded_panel_info *info) +{ + struct lcd_info_v2_1 *lvds; + + if (!info) + return BP_RESULT_BADINPUT; + + if (!DATA_TABLES(lcd_info)) + return BP_RESULT_UNSUPPORTED; + + lvds = GET_IMAGE(struct lcd_info_v2_1, DATA_TABLES(lcd_info)); + + if (!lvds) + return BP_RESULT_BADBIOSTABLE; + + /* TODO: previous vv1_3, should v2_1 */ + if (!((lvds->table_header.format_revision == 2) + && (lvds->table_header.content_revision >= 1))) + return BP_RESULT_UNSUPPORTED; + + memset(info, 0, sizeof(struct embedded_panel_info)); + + /* 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; + + info->lcd_timing.misc_info.H_SYNC_POLARITY = ~(uint32_t) (lvds->lcd_timing.miscinfo + & ATOM_HSYNC_POLARITY); + info->lcd_timing.misc_info.V_SYNC_POLARITY = ~(uint32_t) (lvds->lcd_timing.miscinfo + & ATOM_VSYNC_POLARITY); + + /* not provided by VBIOS */ + info->lcd_timing.misc_info.VERTICAL_CUT_OFF = 0; + + info->lcd_timing.misc_info.H_REPLICATION_BY2 = !!(lvds->lcd_timing.miscinfo + & ATOM_H_REPLICATIONBY2); + info->lcd_timing.misc_info.V_REPLICATION_BY2 = !!(lvds->lcd_timing.miscinfo + & ATOM_V_REPLICATIONBY2); + info->lcd_timing.misc_info.COMPOSITE_SYNC = !!(lvds->lcd_timing.miscinfo + & ATOM_COMPOSITESYNC); + info->lcd_timing.misc_info.INTERLACE = !!(lvds->lcd_timing.miscinfo & ATOM_INTERLACE); + + /* not provided by VBIOS*/ + info->lcd_timing.misc_info.DOUBLE_CLOCK = 0; + /* not provided by VBIOS*/ + info->ss_id = 0; + + info->realtek_eDPToLVDS = !!(lvds->dplvdsrxid == eDP_TO_LVDS_REALTEK_ID); + + return BP_RESULT_OK; +} + +static enum bp_result bios_parser_get_embedded_panel_info( + struct dc_bios *dcb, + struct embedded_panel_info *info) +{ + struct bios_parser + *bp = BP_FROM_DCB(dcb); + struct atom_common_table_header *header; + struct atom_data_revision tbl_revision; + + if (!DATA_TABLES(lcd_info)) + return BP_RESULT_FAILURE; + + header = GET_IMAGE(struct atom_common_table_header, DATA_TABLES(lcd_info)); + + if (!header) + return BP_RESULT_BADBIOSTABLE; + + get_atom_data_table_revision(header, &tbl_revision); + + switch (tbl_revision.major) { + case 2: + switch (tbl_revision.minor) { + case 1: + return get_embedded_panel_info_v2_1(bp, info); + default: + break; + } + break; + default: + break; + } + + return BP_RESULT_FAILURE; +} + +static uint32_t get_support_mask_for_device_id(struct device_id device_id) +{ + enum dal_device_type device_type = device_id.device_type; + uint32_t enum_id = device_id.enum_id; + + switch (device_type) { + case DEVICE_TYPE_LCD: + switch (enum_id) { + case 1: + return ATOM_DISPLAY_LCD1_SUPPORT; + default: + break; + } + break; + case DEVICE_TYPE_DFP: + switch (enum_id) { + case 1: + return ATOM_DISPLAY_DFP1_SUPPORT; + case 2: + return ATOM_DISPLAY_DFP2_SUPPORT; + case 3: + return ATOM_DISPLAY_DFP3_SUPPORT; + case 4: + return ATOM_DISPLAY_DFP4_SUPPORT; + case 5: + return ATOM_DISPLAY_DFP5_SUPPORT; + case 6: + return ATOM_DISPLAY_DFP6_SUPPORT; + default: + break; + } + break; + default: + break; + } + + /* Unidentified device ID, return empty support mask. */ + return 0; +} + +static bool bios_parser_is_device_id_supported( + struct dc_bios *dcb, + struct device_id id) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + + uint32_t mask = get_support_mask_for_device_id(id); + + switch (bp->object_info_tbl.revision.minor) { + case 4: + default: + return (le16_to_cpu(bp->object_info_tbl.v1_4->supporteddevices) & mask) != 0; + break; + case 5: + return (le16_to_cpu(bp->object_info_tbl.v1_5->supporteddevices) & mask) != 0; + break; + } + + return false; +} + +static uint32_t bios_parser_get_ss_entry_number( + struct dc_bios *dcb, + enum as_signal_type signal) +{ + /* TODO: DAL2 atomfirmware implementation does not need this. + * why DAL3 need this? + */ + return 1; +} + +static enum bp_result bios_parser_transmitter_control( + struct dc_bios *dcb, + struct bp_transmitter_control *cntl) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!bp->cmd_tbl.transmitter_control) + return BP_RESULT_FAILURE; + + return bp->cmd_tbl.transmitter_control(bp, cntl); +} + +static enum bp_result bios_parser_encoder_control( + struct dc_bios *dcb, + struct bp_encoder_control *cntl) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!bp->cmd_tbl.dig_encoder_control) + return BP_RESULT_FAILURE; + + return bp->cmd_tbl.dig_encoder_control(bp, cntl); +} + +static enum bp_result bios_parser_set_pixel_clock( + struct dc_bios *dcb, + struct bp_pixel_clock_parameters *bp_params) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!bp->cmd_tbl.set_pixel_clock) + return BP_RESULT_FAILURE; + + return bp->cmd_tbl.set_pixel_clock(bp, bp_params); +} + +static enum bp_result bios_parser_set_dce_clock( + struct dc_bios *dcb, + struct bp_set_dce_clock_parameters *bp_params) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!bp->cmd_tbl.set_dce_clock) + return BP_RESULT_FAILURE; + + return bp->cmd_tbl.set_dce_clock(bp, bp_params); +} + +static enum bp_result bios_parser_program_crtc_timing( + struct dc_bios *dcb, + struct bp_hw_crtc_timing_parameters *bp_params) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!bp->cmd_tbl.set_crtc_timing) + return BP_RESULT_FAILURE; + + return bp->cmd_tbl.set_crtc_timing(bp, bp_params); +} + +static enum bp_result bios_parser_enable_crtc( + struct dc_bios *dcb, + enum controller_id id, + bool enable) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!bp->cmd_tbl.enable_crtc) + return BP_RESULT_FAILURE; + + return bp->cmd_tbl.enable_crtc(bp, id, enable); +} + +static enum bp_result bios_parser_enable_disp_power_gating( + struct dc_bios *dcb, + enum controller_id controller_id, + enum bp_pipe_control_action action) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!bp->cmd_tbl.enable_disp_power_gating) + return BP_RESULT_FAILURE; + + return bp->cmd_tbl.enable_disp_power_gating(bp, controller_id, + action); +} + +static enum bp_result bios_parser_enable_lvtma_control( + struct dc_bios *dcb, + uint8_t uc_pwr_on, + uint8_t panel_instance) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + + if (!bp->cmd_tbl.enable_lvtma_control) + return BP_RESULT_FAILURE; + + return bp->cmd_tbl.enable_lvtma_control(bp, uc_pwr_on, panel_instance); +} + +static bool bios_parser_is_accelerated_mode( + struct dc_bios *dcb) +{ + return bios_is_accelerated_mode(dcb); +} + +/** + * 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 + */ +static void bios_parser_set_scratch_critical_state( + struct dc_bios *dcb, + bool state) +{ + bios_set_scratch_critical_state(dcb, state); +} + +struct atom_dig_transmitter_info_header_v5_3 { + struct atom_common_table_header table_header; + uint16_t dpphy_hdmi_settings_offset; + uint16_t dpphy_dvi_settings_offset; + uint16_t dpphy_dp_setting_table_offset; + uint16_t uniphy_xbar_settings_v2_table_offset; + uint16_t dpphy_internal_reg_overide_offset; +}; + +static enum bp_result bios_parser_get_firmware_info( + struct dc_bios *dcb, + struct dc_firmware_info *info) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + static enum bp_result result = BP_RESULT_BADBIOSTABLE; + struct atom_common_table_header *header; + + struct atom_data_revision revision; + + if (info && DATA_TABLES(firmwareinfo)) { + header = GET_IMAGE(struct atom_common_table_header, + DATA_TABLES(firmwareinfo)); + get_atom_data_table_revision(header, &revision); + switch (revision.major) { + case 3: + switch (revision.minor) { + case 1: + result = get_firmware_info_v3_1(bp, info); + break; + case 2: + case 3: + result = get_firmware_info_v3_2(bp, info); + break; + case 4: + result = get_firmware_info_v3_4(bp, info); + break; + default: + break; + } + break; + default: + break; + } + } + + return result; +} + +static enum bp_result get_firmware_info_v3_1( + struct bios_parser *bp, + struct dc_firmware_info *info) +{ + struct atom_firmware_info_v3_1 *firmware_info; + struct atom_display_controller_info_v4_1 *dce_info = NULL; + + if (!info) + return BP_RESULT_BADINPUT; + + firmware_info = GET_IMAGE(struct atom_firmware_info_v3_1, + DATA_TABLES(firmwareinfo)); + + dce_info = GET_IMAGE(struct atom_display_controller_info_v4_1, + DATA_TABLES(dce_info)); + + if (!firmware_info || !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; + } + + info->oem_i2c_present = false; + + return BP_RESULT_OK; +} + +static enum bp_result get_firmware_info_v3_2( + struct bios_parser *bp, + struct dc_firmware_info *info) +{ + struct atom_firmware_info_v3_2 *firmware_info; + struct atom_display_controller_info_v4_1 *dce_info = NULL; + struct atom_common_table_header *header; + struct atom_data_revision revision; + struct atom_smu_info_v3_2 *smu_info_v3_2 = NULL; + struct atom_smu_info_v3_3 *smu_info_v3_3 = NULL; + + if (!info) + return BP_RESULT_BADINPUT; + + firmware_info = GET_IMAGE(struct atom_firmware_info_v3_2, + DATA_TABLES(firmwareinfo)); + + dce_info = GET_IMAGE(struct atom_display_controller_info_v4_1, + DATA_TABLES(dce_info)); + + if (!firmware_info || !dce_info) + return BP_RESULT_BADBIOSTABLE; + + memset(info, 0, sizeof(*info)); + + header = GET_IMAGE(struct atom_common_table_header, + DATA_TABLES(smu_info)); + get_atom_data_table_revision(header, &revision); + + if (revision.minor == 2) { + /* Vega12 */ + smu_info_v3_2 = GET_IMAGE(struct atom_smu_info_v3_2, + DATA_TABLES(smu_info)); + DC_LOG_BIOS("gpuclk_ss_percentage (unit of 0.001 percent): %d\n", smu_info_v3_2->gpuclk_ss_percentage); + if (!smu_info_v3_2) + return BP_RESULT_BADBIOSTABLE; + + info->default_engine_clk = smu_info_v3_2->bootup_dcefclk_10khz * 10; + } else if (revision.minor == 3) { + /* Vega20 */ + smu_info_v3_3 = GET_IMAGE(struct atom_smu_info_v3_3, + DATA_TABLES(smu_info)); + DC_LOG_BIOS("gpuclk_ss_percentage (unit of 0.001 percent): %d\n", smu_info_v3_3->gpuclk_ss_percentage); + if (!smu_info_v3_3) + return BP_RESULT_BADBIOSTABLE; + + info->default_engine_clk = smu_info_v3_3->bootup_dcefclk_10khz * 10; + } + + // We need to convert from 10KHz units into KHz units. + info->default_memory_clk = firmware_info->bootup_mclk_in10khz * 10; + + /* 27MHz for Vega10 & Vega12; 100MHz for Vega20 */ + 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) { + if (revision.minor == 2) + info->pll_info.crystal_frequency = 27000; + else if (revision.minor == 3) + info->pll_info.crystal_frequency = 100000; + } + /*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) { + if (revision.minor == 2) + info->smu_gpu_pll_output_freq = + bp->cmd_tbl.get_smu_clock_info(bp, SMU9_SYSPLL0_ID) * 10; + else if (revision.minor == 3) + info->smu_gpu_pll_output_freq = + bp->cmd_tbl.get_smu_clock_info(bp, SMU11_SYSPLL3_0_ID) * 10; + } + + if (firmware_info->board_i2c_feature_id == 0x2) { + info->oem_i2c_present = true; + info->oem_i2c_obj_id = firmware_info->board_i2c_feature_gpio_id; + } else { + info->oem_i2c_present = false; + } + + return BP_RESULT_OK; +} + +static enum bp_result get_firmware_info_v3_4( + struct bios_parser *bp, + struct dc_firmware_info *info) +{ + struct atom_firmware_info_v3_4 *firmware_info; + struct atom_common_table_header *header; + struct atom_data_revision revision; + struct atom_display_controller_info_v4_1 *dce_info_v4_1 = NULL; + struct atom_display_controller_info_v4_4 *dce_info_v4_4 = NULL; + + struct atom_smu_info_v3_5 *smu_info_v3_5 = NULL; + struct atom_display_controller_info_v4_5 *dce_info_v4_5 = NULL; + struct atom_smu_info_v4_0 *smu_info_v4_0 = NULL; + + if (!info) + return BP_RESULT_BADINPUT; + + firmware_info = GET_IMAGE(struct atom_firmware_info_v3_4, + DATA_TABLES(firmwareinfo)); + + if (!firmware_info) + return BP_RESULT_BADBIOSTABLE; + + memset(info, 0, sizeof(*info)); + + header = GET_IMAGE(struct atom_common_table_header, + DATA_TABLES(dce_info)); + + get_atom_data_table_revision(header, &revision); + + switch (revision.major) { + case 4: + switch (revision.minor) { + case 5: + dce_info_v4_5 = GET_IMAGE(struct atom_display_controller_info_v4_5, + DATA_TABLES(dce_info)); + + if (!dce_info_v4_5) + return BP_RESULT_BADBIOSTABLE; + + /* 100MHz expected */ + info->pll_info.crystal_frequency = dce_info_v4_5->dce_refclk_10khz * 10; + info->dp_phy_ref_clk = dce_info_v4_5->dpphy_refclk_10khz * 10; + /* 50MHz expected */ + info->i2c_engine_ref_clk = dce_info_v4_5->i2c_engine_refclk_10khz * 10; + + /* For DCN32/321 Display PLL VCO Frequency from dce_info_v4_5 may not be reliable */ + break; + + case 4: + dce_info_v4_4 = GET_IMAGE(struct atom_display_controller_info_v4_4, + DATA_TABLES(dce_info)); + + if (!dce_info_v4_4) + return BP_RESULT_BADBIOSTABLE; + + /* 100MHz expected */ + info->pll_info.crystal_frequency = dce_info_v4_4->dce_refclk_10khz * 10; + info->dp_phy_ref_clk = dce_info_v4_4->dpphy_refclk_10khz * 10; + /* 50MHz expected */ + info->i2c_engine_ref_clk = dce_info_v4_4->i2c_engine_refclk_10khz * 10; + + /* 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; + + info->pll_info.crystal_frequency = dce_info_v4_1->dce_refclk_10khz * 10; + info->dp_phy_ref_clk = dce_info_v4_1->dpphy_refclk_10khz * 10; + info->i2c_engine_ref_clk = dce_info_v4_1->i2c_engine_refclk_10khz * 10; + break; + } + break; + + default: + ASSERT(0); + break; + } + + header = GET_IMAGE(struct atom_common_table_header, + DATA_TABLES(smu_info)); + get_atom_data_table_revision(header, &revision); + + switch (revision.major) { + case 3: + switch (revision.minor) { + case 5: + smu_info_v3_5 = GET_IMAGE(struct atom_smu_info_v3_5, + DATA_TABLES(smu_info)); + + if (!smu_info_v3_5) + return BP_RESULT_BADBIOSTABLE; + DC_LOG_BIOS("gpuclk_ss_percentage (unit of 0.001 percent): %d\n", smu_info_v3_5->gpuclk_ss_percentage); + info->default_engine_clk = smu_info_v3_5->bootup_dcefclk_10khz * 10; + break; + + default: + break; + } + break; + + case 4: + switch (revision.minor) { + case 0: + smu_info_v4_0 = GET_IMAGE(struct atom_smu_info_v4_0, + DATA_TABLES(smu_info)); + + if (!smu_info_v4_0) + return BP_RESULT_BADBIOSTABLE; + + /* For DCN32/321 bootup DCFCLK from smu_info_v4_0 may not be reliable */ + break; + + default: + break; + } + break; + + default: + break; + } + + // We need to convert from 10KHz units into KHz units. + info->default_memory_clk = firmware_info->bootup_mclk_in10khz * 10; + + if (firmware_info->board_i2c_feature_id == 0x2) { + info->oem_i2c_present = true; + info->oem_i2c_obj_id = firmware_info->board_i2c_feature_gpio_id; + } else { + info->oem_i2c_present = false; + } + + return BP_RESULT_OK; +} + +static enum bp_result bios_parser_get_encoder_cap_info( + struct dc_bios *dcb, + struct graphics_object_id object_id, + struct bp_encoder_cap_info *info) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + struct atom_display_object_path_v2 *object; + struct atom_encoder_caps_record *record = NULL; + + if (!info) + return BP_RESULT_BADINPUT; + +#if defined(CONFIG_DRM_AMD_DC_DCN) + /* 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); + + info->DP_HBR2_CAP = (record->encodercaps & + ATOM_ENCODER_CAP_RECORD_HBR2) ? 1 : 0; + info->DP_HBR2_EN = (record->encodercaps & + ATOM_ENCODER_CAP_RECORD_HBR2_EN) ? 1 : 0; + info->DP_HBR3_EN = (record->encodercaps & + ATOM_ENCODER_CAP_RECORD_HBR3_EN) ? 1 : 0; + info->HDMI_6GB_EN = (record->encodercaps & + ATOM_ENCODER_CAP_RECORD_HDMI6Gbps_EN) ? 1 : 0; + info->IS_DP2_CAPABLE = (record->encodercaps & + ATOM_ENCODER_CAP_RECORD_DP2) ? 1 : 0; + info->DP_UHBR10_EN = (record->encodercaps & + ATOM_ENCODER_CAP_RECORD_UHBR10_EN) ? 1 : 0; + info->DP_UHBR13_5_EN = (record->encodercaps & + ATOM_ENCODER_CAP_RECORD_UHBR13_5_EN) ? 1 : 0; + info->DP_UHBR20_EN = (record->encodercaps & + ATOM_ENCODER_CAP_RECORD_UHBR20_EN) ? 1 : 0; + info->DP_IS_USB_C = (record->encodercaps & + ATOM_ENCODER_CAP_RECORD_USB_C_TYPE) ? 1 : 0; + DC_LOG_BIOS("\t info->DP_IS_USB_C %d", info->DP_IS_USB_C); + + return BP_RESULT_OK; +} + + +static struct atom_encoder_caps_record *get_encoder_cap_record( + struct bios_parser *bp, + struct atom_display_object_path_v2 *object) +{ + struct atom_common_record_header *header; + uint32_t offset; + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object */ + return NULL; + } + + offset = object->encoder_recordoffset + bp->object_info_tbl_offset; + + for (;;) { + header = GET_IMAGE(struct atom_common_record_header, offset); + + if (!header) + return NULL; + + offset += header->record_size; + + if (header->record_type == LAST_RECORD_TYPE || + !header->record_size) + break; + + if (header->record_type != ATOM_ENCODER_CAP_RECORD_TYPE) + continue; + + if (sizeof(struct atom_encoder_caps_record) <= + header->record_size) + return (struct atom_encoder_caps_record *)header; + } + + return NULL; +} + +static struct atom_disp_connector_caps_record *get_disp_connector_caps_record( + struct bios_parser *bp, + struct atom_display_object_path_v2 *object) +{ + struct atom_common_record_header *header; + uint32_t offset; + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object */ + return NULL; + } + + offset = object->disp_recordoffset + bp->object_info_tbl_offset; + + for (;;) { + header = GET_IMAGE(struct atom_common_record_header, offset); + + if (!header) + return NULL; + + offset += header->record_size; + + if (header->record_type == LAST_RECORD_TYPE || + !header->record_size) + break; + + if (header->record_type != ATOM_DISP_CONNECTOR_CAPS_RECORD_TYPE) + continue; + + if (sizeof(struct atom_disp_connector_caps_record) <= + header->record_size) + return (struct atom_disp_connector_caps_record *)header; + } + + return NULL; +} + +static struct atom_connector_caps_record *get_connector_caps_record( + struct bios_parser *bp, + struct atom_display_object_path_v3 *object) +{ + struct atom_common_record_header *header; + uint32_t offset; + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object */ + return NULL; + } + + offset = object->disp_recordoffset + bp->object_info_tbl_offset; + + for (;;) { + header = GET_IMAGE(struct atom_common_record_header, offset); + + if (!header) + return NULL; + + offset += header->record_size; + + if (header->record_type == ATOM_RECORD_END_TYPE || + !header->record_size) + break; + + if (header->record_type != ATOM_CONNECTOR_CAP_RECORD_TYPE) + continue; + + if (sizeof(struct atom_connector_caps_record) <= header->record_size) + return (struct atom_connector_caps_record *)header; + } + + return NULL; +} + +static enum bp_result bios_parser_get_disp_connector_caps_info( + struct dc_bios *dcb, + struct graphics_object_id object_id, + struct bp_disp_connector_caps_info *info) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + struct atom_display_object_path_v2 *object; + + struct atom_display_object_path_v3 *object_path_v3; + struct atom_connector_caps_record *record_path_v3; + + struct atom_disp_connector_caps_record *record = NULL; + + if (!info) + return BP_RESULT_BADINPUT; + + switch (bp->object_info_tbl.revision.minor) { + case 4: + default: + object = get_bios_object(bp, object_id); + + if (!object) + return BP_RESULT_BADINPUT; + + record = get_disp_connector_caps_record(bp, object); + if (!record) + return BP_RESULT_NORECORD; + + info->INTERNAL_DISPLAY = + (record->connectcaps & ATOM_CONNECTOR_CAP_INTERNAL_DISPLAY) ? 1 : 0; + info->INTERNAL_DISPLAY_BL = + (record->connectcaps & ATOM_CONNECTOR_CAP_INTERNAL_DISPLAY_BL) ? 1 : 0; + break; + case 5: + object_path_v3 = get_bios_object_from_path_v3(bp, object_id); + + if (!object_path_v3) + return BP_RESULT_BADINPUT; + + record_path_v3 = get_connector_caps_record(bp, object_path_v3); + if (!record_path_v3) + return BP_RESULT_NORECORD; + + info->INTERNAL_DISPLAY = (record_path_v3->connector_caps & ATOM_CONNECTOR_CAP_INTERNAL_DISPLAY) + ? 1 : 0; + info->INTERNAL_DISPLAY_BL = (record_path_v3->connector_caps & ATOM_CONNECTOR_CAP_INTERNAL_DISPLAY_BL) + ? 1 : 0; + break; + } + + return BP_RESULT_OK; +} + +static struct atom_connector_speed_record *get_connector_speed_cap_record( + struct bios_parser *bp, + struct atom_display_object_path_v3 *object) +{ + struct atom_common_record_header *header; + uint32_t offset; + + if (!object) { + BREAK_TO_DEBUGGER(); /* Invalid object */ + return NULL; + } + + offset = object->disp_recordoffset + bp->object_info_tbl_offset; + + for (;;) { + header = GET_IMAGE(struct atom_common_record_header, offset); + + if (!header) + return NULL; + + offset += header->record_size; + + if (header->record_type == ATOM_RECORD_END_TYPE || + !header->record_size) + break; + + if (header->record_type != ATOM_CONNECTOR_SPEED_UPTO) + continue; + + if (sizeof(struct atom_connector_speed_record) <= header->record_size) + return (struct atom_connector_speed_record *)header; + } + + return NULL; +} + +static enum bp_result bios_parser_get_connector_speed_cap_info( + struct dc_bios *dcb, + struct graphics_object_id object_id, + struct bp_connector_speed_cap_info *info) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + struct atom_display_object_path_v3 *object_path_v3; + //struct atom_connector_speed_record *record = NULL; + struct atom_connector_speed_record *record; + + if (!info) + return BP_RESULT_BADINPUT; + + object_path_v3 = get_bios_object_from_path_v3(bp, object_id); + + if (!object_path_v3) + return BP_RESULT_BADINPUT; + + record = get_connector_speed_cap_record(bp, object_path_v3); + if (!record) + return BP_RESULT_NORECORD; + + info->DP_HBR2_EN = (record->connector_max_speed >= 5400) ? 1 : 0; + info->DP_HBR3_EN = (record->connector_max_speed >= 8100) ? 1 : 0; + info->HDMI_6GB_EN = (record->connector_max_speed >= 5940) ? 1 : 0; + info->DP_UHBR10_EN = (record->connector_max_speed >= 10000) ? 1 : 0; + info->DP_UHBR13_5_EN = (record->connector_max_speed >= 13500) ? 1 : 0; + info->DP_UHBR20_EN = (record->connector_max_speed >= 20000) ? 1 : 0; + return BP_RESULT_OK; +} + +static enum bp_result get_vram_info_v23( + struct bios_parser *bp, + struct dc_vram_info *info) +{ + struct atom_vram_info_header_v2_3 *info_v23; + static enum bp_result result = BP_RESULT_OK; + + info_v23 = GET_IMAGE(struct atom_vram_info_header_v2_3, + DATA_TABLES(vram_info)); + + if (info_v23 == NULL) + return BP_RESULT_BADBIOSTABLE; + + info->num_chans = info_v23->vram_module[0].channel_num; + info->dram_channel_width_bytes = (1 << info_v23->vram_module[0].channel_width) / 8; + + return result; +} + +static enum bp_result get_vram_info_v24( + struct bios_parser *bp, + struct dc_vram_info *info) +{ + struct atom_vram_info_header_v2_4 *info_v24; + static enum bp_result result = BP_RESULT_OK; + + info_v24 = GET_IMAGE(struct atom_vram_info_header_v2_4, + DATA_TABLES(vram_info)); + + if (info_v24 == NULL) + return BP_RESULT_BADBIOSTABLE; + + info->num_chans = info_v24->vram_module[0].channel_num; + info->dram_channel_width_bytes = (1 << info_v24->vram_module[0].channel_width) / 8; + + return result; +} + +static enum bp_result get_vram_info_v25( + struct bios_parser *bp, + struct dc_vram_info *info) +{ + struct atom_vram_info_header_v2_5 *info_v25; + static enum bp_result result = BP_RESULT_OK; + + info_v25 = GET_IMAGE(struct atom_vram_info_header_v2_5, + DATA_TABLES(vram_info)); + + if (info_v25 == NULL) + return BP_RESULT_BADBIOSTABLE; + + info->num_chans = info_v25->vram_module[0].channel_num; + info->dram_channel_width_bytes = (1 << info_v25->vram_module[0].channel_width) / 8; + + return result; +} + +static enum bp_result get_vram_info_v30( + struct bios_parser *bp, + struct dc_vram_info *info) +{ + struct atom_vram_info_header_v3_0 *info_v30; + enum bp_result result = BP_RESULT_OK; + + info_v30 = GET_IMAGE(struct atom_vram_info_header_v3_0, + DATA_TABLES(vram_info)); + + if (info_v30 == NULL) + return BP_RESULT_BADBIOSTABLE; + + info->num_chans = info_v30->channel_num; + info->dram_channel_width_bytes = (1 << info_v30->channel_width) / 8; + + return result; +} + + +/* + * get_integrated_info_v11 + * + * @brief + * Get V8 integrated BIOS information + * + * @param + * bios_parser *bp - [in]BIOS parser handler to get master data table + * integrated_info *info - [out] store and output integrated info + * + * @return + * static enum bp_result - BP_RESULT_OK if information is available, + * BP_RESULT_BADBIOSTABLE otherwise. + */ +static enum bp_result get_integrated_info_v11( + struct bios_parser *bp, + struct integrated_info *info) +{ + struct atom_integrated_system_info_v1_11 *info_v11; + uint32_t i; + + info_v11 = GET_IMAGE(struct atom_integrated_system_info_v1_11, + DATA_TABLES(integratedsysteminfo)); + + DC_LOG_BIOS("gpuclk_ss_percentage (unit of 0.001 percent): %d\n", info_v11->gpuclk_ss_percentage); + if (info_v11 == NULL) + return BP_RESULT_BADBIOSTABLE; + + info->gpu_cap_info = + le32_to_cpu(info_v11->gpucapinfo); + /* + * system_config: Bit[0] = 0 : PCIE power gating disabled + * = 1 : PCIE power gating enabled + * Bit[1] = 0 : DDR-PLL shut down disabled + * = 1 : DDR-PLL shut down enabled + * Bit[2] = 0 : DDR-PLL power down disabled + * = 1 : DDR-PLL power down enabled + */ + info->system_config = le32_to_cpu(info_v11->system_config); + info->cpu_cap_info = le32_to_cpu(info_v11->cpucapinfo); + info->memory_type = info_v11->memorytype; + info->ma_channel_number = info_v11->umachannelnumber; + info->lvds_ss_percentage = + le16_to_cpu(info_v11->lvds_ss_percentage); + info->dp_ss_control = + le16_to_cpu(info_v11->reserved1); + info->lvds_sspread_rate_in_10hz = + le16_to_cpu(info_v11->lvds_ss_rate_10hz); + info->hdmi_ss_percentage = + le16_to_cpu(info_v11->hdmi_ss_percentage); + info->hdmi_sspread_rate_in_10hz = + le16_to_cpu(info_v11->hdmi_ss_rate_10hz); + info->dvi_ss_percentage = + le16_to_cpu(info_v11->dvi_ss_percentage); + info->dvi_sspread_rate_in_10_hz = + le16_to_cpu(info_v11->dvi_ss_rate_10hz); + info->lvds_misc = info_v11->lvds_misc; + for (i = 0; i < NUMBER_OF_UCHAR_FOR_GUID; ++i) { + info->ext_disp_conn_info.gu_id[i] = + info_v11->extdispconninfo.guid[i]; + } + + for (i = 0; i < MAX_NUMBER_OF_EXT_DISPLAY_PATH; ++i) { + info->ext_disp_conn_info.path[i].device_connector_id = + object_id_from_bios_object_id( + le16_to_cpu(info_v11->extdispconninfo.path[i].connectorobjid)); + + info->ext_disp_conn_info.path[i].ext_encoder_obj_id = + object_id_from_bios_object_id( + le16_to_cpu( + info_v11->extdispconninfo.path[i].ext_encoder_objid)); + + info->ext_disp_conn_info.path[i].device_tag = + le16_to_cpu( + info_v11->extdispconninfo.path[i].device_tag); + info->ext_disp_conn_info.path[i].device_acpi_enum = + le16_to_cpu( + info_v11->extdispconninfo.path[i].device_acpi_enum); + info->ext_disp_conn_info.path[i].ext_aux_ddc_lut_index = + info_v11->extdispconninfo.path[i].auxddclut_index; + info->ext_disp_conn_info.path[i].ext_hpd_pin_lut_index = + info_v11->extdispconninfo.path[i].hpdlut_index; + info->ext_disp_conn_info.path[i].channel_mapping.raw = + info_v11->extdispconninfo.path[i].channelmapping; + info->ext_disp_conn_info.path[i].caps = + le16_to_cpu(info_v11->extdispconninfo.path[i].caps); + } + info->ext_disp_conn_info.checksum = + info_v11->extdispconninfo.checksum; + + info->dp0_ext_hdmi_slv_addr = info_v11->dp0_retimer_set.HdmiSlvAddr; + info->dp0_ext_hdmi_reg_num = info_v11->dp0_retimer_set.HdmiRegNum; + for (i = 0; i < info->dp0_ext_hdmi_reg_num; i++) { + info->dp0_ext_hdmi_reg_settings[i].i2c_reg_index = + info_v11->dp0_retimer_set.HdmiRegSetting[i].ucI2cRegIndex; + info->dp0_ext_hdmi_reg_settings[i].i2c_reg_val = + info_v11->dp0_retimer_set.HdmiRegSetting[i].ucI2cRegVal; + } + info->dp0_ext_hdmi_6g_reg_num = info_v11->dp0_retimer_set.Hdmi6GRegNum; + for (i = 0; i < info->dp0_ext_hdmi_6g_reg_num; i++) { + info->dp0_ext_hdmi_6g_reg_settings[i].i2c_reg_index = + info_v11->dp0_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegIndex; + info->dp0_ext_hdmi_6g_reg_settings[i].i2c_reg_val = + info_v11->dp0_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegVal; + } + + info->dp1_ext_hdmi_slv_addr = info_v11->dp1_retimer_set.HdmiSlvAddr; + info->dp1_ext_hdmi_reg_num = info_v11->dp1_retimer_set.HdmiRegNum; + for (i = 0; i < info->dp1_ext_hdmi_reg_num; i++) { + info->dp1_ext_hdmi_reg_settings[i].i2c_reg_index = + info_v11->dp1_retimer_set.HdmiRegSetting[i].ucI2cRegIndex; + info->dp1_ext_hdmi_reg_settings[i].i2c_reg_val = + info_v11->dp1_retimer_set.HdmiRegSetting[i].ucI2cRegVal; + } + info->dp1_ext_hdmi_6g_reg_num = info_v11->dp1_retimer_set.Hdmi6GRegNum; + for (i = 0; i < info->dp1_ext_hdmi_6g_reg_num; i++) { + info->dp1_ext_hdmi_6g_reg_settings[i].i2c_reg_index = + info_v11->dp1_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegIndex; + info->dp1_ext_hdmi_6g_reg_settings[i].i2c_reg_val = + info_v11->dp1_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegVal; + } + + info->dp2_ext_hdmi_slv_addr = info_v11->dp2_retimer_set.HdmiSlvAddr; + info->dp2_ext_hdmi_reg_num = info_v11->dp2_retimer_set.HdmiRegNum; + for (i = 0; i < info->dp2_ext_hdmi_reg_num; i++) { + info->dp2_ext_hdmi_reg_settings[i].i2c_reg_index = + info_v11->dp2_retimer_set.HdmiRegSetting[i].ucI2cRegIndex; + info->dp2_ext_hdmi_reg_settings[i].i2c_reg_val = + info_v11->dp2_retimer_set.HdmiRegSetting[i].ucI2cRegVal; + } + info->dp2_ext_hdmi_6g_reg_num = info_v11->dp2_retimer_set.Hdmi6GRegNum; + for (i = 0; i < info->dp2_ext_hdmi_6g_reg_num; i++) { + info->dp2_ext_hdmi_6g_reg_settings[i].i2c_reg_index = + info_v11->dp2_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegIndex; + info->dp2_ext_hdmi_6g_reg_settings[i].i2c_reg_val = + info_v11->dp2_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegVal; + } + + info->dp3_ext_hdmi_slv_addr = info_v11->dp3_retimer_set.HdmiSlvAddr; + info->dp3_ext_hdmi_reg_num = info_v11->dp3_retimer_set.HdmiRegNum; + for (i = 0; i < info->dp3_ext_hdmi_reg_num; i++) { + info->dp3_ext_hdmi_reg_settings[i].i2c_reg_index = + info_v11->dp3_retimer_set.HdmiRegSetting[i].ucI2cRegIndex; + info->dp3_ext_hdmi_reg_settings[i].i2c_reg_val = + info_v11->dp3_retimer_set.HdmiRegSetting[i].ucI2cRegVal; + } + info->dp3_ext_hdmi_6g_reg_num = info_v11->dp3_retimer_set.Hdmi6GRegNum; + for (i = 0; i < info->dp3_ext_hdmi_6g_reg_num; i++) { + info->dp3_ext_hdmi_6g_reg_settings[i].i2c_reg_index = + info_v11->dp3_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegIndex; + info->dp3_ext_hdmi_6g_reg_settings[i].i2c_reg_val = + info_v11->dp3_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegVal; + } + + + /** TODO - review **/ + #if 0 + info->boot_up_engine_clock = le32_to_cpu(info_v11->ulBootUpEngineClock) + * 10; + info->dentist_vco_freq = le32_to_cpu(info_v11->ulDentistVCOFreq) * 10; + info->boot_up_uma_clock = le32_to_cpu(info_v8->ulBootUpUMAClock) * 10; + + for (i = 0; i < NUMBER_OF_DISP_CLK_VOLTAGE; ++i) { + /* Convert [10KHz] into [KHz] */ + info->disp_clk_voltage[i].max_supported_clk = + le32_to_cpu(info_v11->sDISPCLK_Voltage[i]. + ulMaximumSupportedCLK) * 10; + info->disp_clk_voltage[i].voltage_index = + le32_to_cpu(info_v11->sDISPCLK_Voltage[i].ulVoltageIndex); + } + + info->boot_up_req_display_vector = + le32_to_cpu(info_v11->ulBootUpReqDisplayVector); + info->boot_up_nb_voltage = + le16_to_cpu(info_v11->usBootUpNBVoltage); + info->ext_disp_conn_info_offset = + le16_to_cpu(info_v11->usExtDispConnInfoOffset); + info->gmc_restore_reset_time = + le32_to_cpu(info_v11->ulGMCRestoreResetTime); + info->minimum_n_clk = + le32_to_cpu(info_v11->ulNbpStateNClkFreq[0]); + for (i = 1; i < 4; ++i) + info->minimum_n_clk = + info->minimum_n_clk < + le32_to_cpu(info_v11->ulNbpStateNClkFreq[i]) ? + info->minimum_n_clk : le32_to_cpu( + info_v11->ulNbpStateNClkFreq[i]); + + info->idle_n_clk = le32_to_cpu(info_v11->ulIdleNClk); + info->ddr_dll_power_up_time = + le32_to_cpu(info_v11->ulDDR_DLL_PowerUpTime); + info->ddr_pll_power_up_time = + le32_to_cpu(info_v11->ulDDR_PLL_PowerUpTime); + info->pcie_clk_ss_type = le16_to_cpu(info_v11->usPCIEClkSSType); + info->max_lvds_pclk_freq_in_single_link = + le16_to_cpu(info_v11->usMaxLVDSPclkFreqInSingleLink); + info->max_lvds_pclk_freq_in_single_link = + le16_to_cpu(info_v11->usMaxLVDSPclkFreqInSingleLink); + info->lvds_pwr_on_seq_dig_on_to_de_in_4ms = + info_v11->ucLVDSPwrOnSeqDIGONtoDE_in4Ms; + info->lvds_pwr_on_seq_de_to_vary_bl_in_4ms = + info_v11->ucLVDSPwrOnSeqDEtoVARY_BL_in4Ms; + info->lvds_pwr_on_seq_vary_bl_to_blon_in_4ms = + info_v11->ucLVDSPwrOnSeqVARY_BLtoBLON_in4Ms; + info->lvds_pwr_off_seq_vary_bl_to_de_in4ms = + info_v11->ucLVDSPwrOffSeqVARY_BLtoDE_in4Ms; + info->lvds_pwr_off_seq_de_to_dig_on_in4ms = + info_v11->ucLVDSPwrOffSeqDEtoDIGON_in4Ms; + info->lvds_pwr_off_seq_blon_to_vary_bl_in_4ms = + info_v11->ucLVDSPwrOffSeqBLONtoVARY_BL_in4Ms; + info->lvds_off_to_on_delay_in_4ms = + info_v11->ucLVDSOffToOnDelay_in4Ms; + info->lvds_bit_depth_control_val = + le32_to_cpu(info_v11->ulLCDBitDepthControlVal); + + for (i = 0; i < NUMBER_OF_AVAILABLE_SCLK; ++i) { + /* Convert [10KHz] into [KHz] */ + info->avail_s_clk[i].supported_s_clk = + le32_to_cpu(info_v11->sAvail_SCLK[i].ulSupportedSCLK) + * 10; + info->avail_s_clk[i].voltage_index = + le16_to_cpu(info_v11->sAvail_SCLK[i].usVoltageIndex); + info->avail_s_clk[i].voltage_id = + le16_to_cpu(info_v11->sAvail_SCLK[i].usVoltageID); + } + #endif /* TODO*/ + + return BP_RESULT_OK; +} + +static enum bp_result get_integrated_info_v2_1( + struct bios_parser *bp, + struct integrated_info *info) +{ + struct atom_integrated_system_info_v2_1 *info_v2_1; + uint32_t i; + + info_v2_1 = GET_IMAGE(struct atom_integrated_system_info_v2_1, + DATA_TABLES(integratedsysteminfo)); + DC_LOG_BIOS("gpuclk_ss_percentage (unit of 0.001 percent): %d\n", info_v2_1->gpuclk_ss_percentage); + + if (info_v2_1 == NULL) + return BP_RESULT_BADBIOSTABLE; + + info->gpu_cap_info = + le32_to_cpu(info_v2_1->gpucapinfo); + /* + * system_config: Bit[0] = 0 : PCIE power gating disabled + * = 1 : PCIE power gating enabled + * Bit[1] = 0 : DDR-PLL shut down disabled + * = 1 : DDR-PLL shut down enabled + * Bit[2] = 0 : DDR-PLL power down disabled + * = 1 : DDR-PLL power down enabled + */ + info->system_config = le32_to_cpu(info_v2_1->system_config); + info->cpu_cap_info = le32_to_cpu(info_v2_1->cpucapinfo); + info->memory_type = info_v2_1->memorytype; + info->ma_channel_number = info_v2_1->umachannelnumber; + info->dp_ss_control = + le16_to_cpu(info_v2_1->reserved1); + + for (i = 0; i < NUMBER_OF_UCHAR_FOR_GUID; ++i) { + info->ext_disp_conn_info.gu_id[i] = + info_v2_1->extdispconninfo.guid[i]; + } + + for (i = 0; i < MAX_NUMBER_OF_EXT_DISPLAY_PATH; ++i) { + info->ext_disp_conn_info.path[i].device_connector_id = + object_id_from_bios_object_id( + le16_to_cpu(info_v2_1->extdispconninfo.path[i].connectorobjid)); + + info->ext_disp_conn_info.path[i].ext_encoder_obj_id = + object_id_from_bios_object_id( + le16_to_cpu( + info_v2_1->extdispconninfo.path[i].ext_encoder_objid)); + + info->ext_disp_conn_info.path[i].device_tag = + le16_to_cpu( + info_v2_1->extdispconninfo.path[i].device_tag); + info->ext_disp_conn_info.path[i].device_acpi_enum = + le16_to_cpu( + info_v2_1->extdispconninfo.path[i].device_acpi_enum); + info->ext_disp_conn_info.path[i].ext_aux_ddc_lut_index = + info_v2_1->extdispconninfo.path[i].auxddclut_index; + info->ext_disp_conn_info.path[i].ext_hpd_pin_lut_index = + info_v2_1->extdispconninfo.path[i].hpdlut_index; + info->ext_disp_conn_info.path[i].channel_mapping.raw = + info_v2_1->extdispconninfo.path[i].channelmapping; + info->ext_disp_conn_info.path[i].caps = + le16_to_cpu(info_v2_1->extdispconninfo.path[i].caps); + } + + info->ext_disp_conn_info.checksum = + info_v2_1->extdispconninfo.checksum; + info->dp0_ext_hdmi_slv_addr = info_v2_1->dp0_retimer_set.HdmiSlvAddr; + info->dp0_ext_hdmi_reg_num = info_v2_1->dp0_retimer_set.HdmiRegNum; + for (i = 0; i < info->dp0_ext_hdmi_reg_num; i++) { + info->dp0_ext_hdmi_reg_settings[i].i2c_reg_index = + info_v2_1->dp0_retimer_set.HdmiRegSetting[i].ucI2cRegIndex; + info->dp0_ext_hdmi_reg_settings[i].i2c_reg_val = + info_v2_1->dp0_retimer_set.HdmiRegSetting[i].ucI2cRegVal; + } + info->dp0_ext_hdmi_6g_reg_num = info_v2_1->dp0_retimer_set.Hdmi6GRegNum; + for (i = 0; i < info->dp0_ext_hdmi_6g_reg_num; i++) { + info->dp0_ext_hdmi_6g_reg_settings[i].i2c_reg_index = + info_v2_1->dp0_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegIndex; + info->dp0_ext_hdmi_6g_reg_settings[i].i2c_reg_val = + info_v2_1->dp0_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegVal; + } + info->dp1_ext_hdmi_slv_addr = info_v2_1->dp1_retimer_set.HdmiSlvAddr; + info->dp1_ext_hdmi_reg_num = info_v2_1->dp1_retimer_set.HdmiRegNum; + for (i = 0; i < info->dp1_ext_hdmi_reg_num; i++) { + info->dp1_ext_hdmi_reg_settings[i].i2c_reg_index = + info_v2_1->dp1_retimer_set.HdmiRegSetting[i].ucI2cRegIndex; + info->dp1_ext_hdmi_reg_settings[i].i2c_reg_val = + info_v2_1->dp1_retimer_set.HdmiRegSetting[i].ucI2cRegVal; + } + info->dp1_ext_hdmi_6g_reg_num = info_v2_1->dp1_retimer_set.Hdmi6GRegNum; + for (i = 0; i < info->dp1_ext_hdmi_6g_reg_num; i++) { + info->dp1_ext_hdmi_6g_reg_settings[i].i2c_reg_index = + info_v2_1->dp1_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegIndex; + info->dp1_ext_hdmi_6g_reg_settings[i].i2c_reg_val = + info_v2_1->dp1_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegVal; + } + info->dp2_ext_hdmi_slv_addr = info_v2_1->dp2_retimer_set.HdmiSlvAddr; + info->dp2_ext_hdmi_reg_num = info_v2_1->dp2_retimer_set.HdmiRegNum; + for (i = 0; i < info->dp2_ext_hdmi_reg_num; i++) { + info->dp2_ext_hdmi_reg_settings[i].i2c_reg_index = + info_v2_1->dp2_retimer_set.HdmiRegSetting[i].ucI2cRegIndex; + info->dp2_ext_hdmi_reg_settings[i].i2c_reg_val = + info_v2_1->dp2_retimer_set.HdmiRegSetting[i].ucI2cRegVal; + } + info->dp2_ext_hdmi_6g_reg_num = info_v2_1->dp2_retimer_set.Hdmi6GRegNum; + for (i = 0; i < info->dp2_ext_hdmi_6g_reg_num; i++) { + info->dp2_ext_hdmi_6g_reg_settings[i].i2c_reg_index = + info_v2_1->dp2_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegIndex; + info->dp2_ext_hdmi_6g_reg_settings[i].i2c_reg_val = + info_v2_1->dp2_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegVal; + } + info->dp3_ext_hdmi_slv_addr = info_v2_1->dp3_retimer_set.HdmiSlvAddr; + info->dp3_ext_hdmi_reg_num = info_v2_1->dp3_retimer_set.HdmiRegNum; + for (i = 0; i < info->dp3_ext_hdmi_reg_num; i++) { + info->dp3_ext_hdmi_reg_settings[i].i2c_reg_index = + info_v2_1->dp3_retimer_set.HdmiRegSetting[i].ucI2cRegIndex; + info->dp3_ext_hdmi_reg_settings[i].i2c_reg_val = + info_v2_1->dp3_retimer_set.HdmiRegSetting[i].ucI2cRegVal; + } + info->dp3_ext_hdmi_6g_reg_num = info_v2_1->dp3_retimer_set.Hdmi6GRegNum; + for (i = 0; i < info->dp3_ext_hdmi_6g_reg_num; i++) { + info->dp3_ext_hdmi_6g_reg_settings[i].i2c_reg_index = + info_v2_1->dp3_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegIndex; + info->dp3_ext_hdmi_6g_reg_settings[i].i2c_reg_val = + info_v2_1->dp3_retimer_set.Hdmi6GhzRegSetting[i].ucI2cRegVal; + } + + info->edp1_info.edp_backlight_pwm_hz = + le16_to_cpu(info_v2_1->edp1_info.edp_backlight_pwm_hz); + info->edp1_info.edp_ss_percentage = + le16_to_cpu(info_v2_1->edp1_info.edp_ss_percentage); + info->edp1_info.edp_ss_rate_10hz = + le16_to_cpu(info_v2_1->edp1_info.edp_ss_rate_10hz); + info->edp1_info.edp_pwr_on_off_delay = + info_v2_1->edp1_info.edp_pwr_on_off_delay; + info->edp1_info.edp_pwr_on_vary_bl_to_blon = + info_v2_1->edp1_info.edp_pwr_on_vary_bl_to_blon; + info->edp1_info.edp_pwr_down_bloff_to_vary_bloff = + info_v2_1->edp1_info.edp_pwr_down_bloff_to_vary_bloff; + info->edp1_info.edp_panel_bpc = + info_v2_1->edp1_info.edp_panel_bpc; + info->edp1_info.edp_bootup_bl_level = info_v2_1->edp1_info.edp_bootup_bl_level; + + info->edp2_info.edp_backlight_pwm_hz = + le16_to_cpu(info_v2_1->edp2_info.edp_backlight_pwm_hz); + info->edp2_info.edp_ss_percentage = + le16_to_cpu(info_v2_1->edp2_info.edp_ss_percentage); + info->edp2_info.edp_ss_rate_10hz = + le16_to_cpu(info_v2_1->edp2_info.edp_ss_rate_10hz); + info->edp2_info.edp_pwr_on_off_delay = + info_v2_1->edp2_info.edp_pwr_on_off_delay; + info->edp2_info.edp_pwr_on_vary_bl_to_blon = + info_v2_1->edp2_info.edp_pwr_on_vary_bl_to_blon; + info->edp2_info.edp_pwr_down_bloff_to_vary_bloff = + info_v2_1->edp2_info.edp_pwr_down_bloff_to_vary_bloff; + info->edp2_info.edp_panel_bpc = + info_v2_1->edp2_info.edp_panel_bpc; + info->edp2_info.edp_bootup_bl_level = + info_v2_1->edp2_info.edp_bootup_bl_level; + + return BP_RESULT_OK; +} + +static enum bp_result get_integrated_info_v2_2( + struct bios_parser *bp, + struct integrated_info *info) +{ + struct atom_integrated_system_info_v2_2 *info_v2_2; + uint32_t i; + + info_v2_2 = GET_IMAGE(struct atom_integrated_system_info_v2_2, + DATA_TABLES(integratedsysteminfo)); + + DC_LOG_BIOS("gpuclk_ss_percentage (unit of 0.001 percent): %d\n", info_v2_2->gpuclk_ss_percentage); + + if (info_v2_2 == NULL) + return BP_RESULT_BADBIOSTABLE; + + info->gpu_cap_info = + le32_to_cpu(info_v2_2->gpucapinfo); + /* + * system_config: Bit[0] = 0 : PCIE power gating disabled + * = 1 : PCIE power gating enabled + * Bit[1] = 0 : DDR-PLL shut down disabled + * = 1 : DDR-PLL shut down enabled + * Bit[2] = 0 : DDR-PLL power down disabled + * = 1 : DDR-PLL power down enabled + */ + info->system_config = le32_to_cpu(info_v2_2->system_config); + info->cpu_cap_info = le32_to_cpu(info_v2_2->cpucapinfo); + info->memory_type = info_v2_2->memorytype; + info->ma_channel_number = info_v2_2->umachannelnumber; + info->dp_ss_control = + le16_to_cpu(info_v2_2->reserved1); + info->gpuclk_ss_percentage = info_v2_2->gpuclk_ss_percentage; + info->gpuclk_ss_type = info_v2_2->gpuclk_ss_type; + + for (i = 0; i < NUMBER_OF_UCHAR_FOR_GUID; ++i) { + info->ext_disp_conn_info.gu_id[i] = + info_v2_2->extdispconninfo.guid[i]; + } + + for (i = 0; i < MAX_NUMBER_OF_EXT_DISPLAY_PATH; ++i) { + info->ext_disp_conn_info.path[i].device_connector_id = + object_id_from_bios_object_id( + le16_to_cpu(info_v2_2->extdispconninfo.path[i].connectorobjid)); + + info->ext_disp_conn_info.path[i].ext_encoder_obj_id = + object_id_from_bios_object_id( + le16_to_cpu( + info_v2_2->extdispconninfo.path[i].ext_encoder_objid)); + + info->ext_disp_conn_info.path[i].device_tag = + le16_to_cpu( + info_v2_2->extdispconninfo.path[i].device_tag); + info->ext_disp_conn_info.path[i].device_acpi_enum = + le16_to_cpu( + info_v2_2->extdispconninfo.path[i].device_acpi_enum); + info->ext_disp_conn_info.path[i].ext_aux_ddc_lut_index = + info_v2_2->extdispconninfo.path[i].auxddclut_index; + info->ext_disp_conn_info.path[i].ext_hpd_pin_lut_index = + info_v2_2->extdispconninfo.path[i].hpdlut_index; + info->ext_disp_conn_info.path[i].channel_mapping.raw = + info_v2_2->extdispconninfo.path[i].channelmapping; + info->ext_disp_conn_info.path[i].caps = + le16_to_cpu(info_v2_2->extdispconninfo.path[i].caps); + } + + info->ext_disp_conn_info.checksum = + info_v2_2->extdispconninfo.checksum; + info->ext_disp_conn_info.fixdpvoltageswing = + info_v2_2->extdispconninfo.fixdpvoltageswing; + + info->edp1_info.edp_backlight_pwm_hz = + le16_to_cpu(info_v2_2->edp1_info.edp_backlight_pwm_hz); + info->edp1_info.edp_ss_percentage = + le16_to_cpu(info_v2_2->edp1_info.edp_ss_percentage); + info->edp1_info.edp_ss_rate_10hz = + le16_to_cpu(info_v2_2->edp1_info.edp_ss_rate_10hz); + info->edp1_info.edp_pwr_on_off_delay = + info_v2_2->edp1_info.edp_pwr_on_off_delay; + info->edp1_info.edp_pwr_on_vary_bl_to_blon = + info_v2_2->edp1_info.edp_pwr_on_vary_bl_to_blon; + info->edp1_info.edp_pwr_down_bloff_to_vary_bloff = + info_v2_2->edp1_info.edp_pwr_down_bloff_to_vary_bloff; + info->edp1_info.edp_panel_bpc = + info_v2_2->edp1_info.edp_panel_bpc; + info->edp1_info.edp_bootup_bl_level = + + info->edp2_info.edp_backlight_pwm_hz = + le16_to_cpu(info_v2_2->edp2_info.edp_backlight_pwm_hz); + info->edp2_info.edp_ss_percentage = + le16_to_cpu(info_v2_2->edp2_info.edp_ss_percentage); + info->edp2_info.edp_ss_rate_10hz = + le16_to_cpu(info_v2_2->edp2_info.edp_ss_rate_10hz); + info->edp2_info.edp_pwr_on_off_delay = + info_v2_2->edp2_info.edp_pwr_on_off_delay; + info->edp2_info.edp_pwr_on_vary_bl_to_blon = + info_v2_2->edp2_info.edp_pwr_on_vary_bl_to_blon; + info->edp2_info.edp_pwr_down_bloff_to_vary_bloff = + info_v2_2->edp2_info.edp_pwr_down_bloff_to_vary_bloff; + info->edp2_info.edp_panel_bpc = + info_v2_2->edp2_info.edp_panel_bpc; + info->edp2_info.edp_bootup_bl_level = + info_v2_2->edp2_info.edp_bootup_bl_level; + + return BP_RESULT_OK; +} + +/* + * construct_integrated_info + * + * @brief + * Get integrated BIOS information based on table revision + * + * @param + * bios_parser *bp - [in]BIOS parser handler to get master data table + * integrated_info *info - [out] store and output integrated info + * + * @return + * static enum bp_result - BP_RESULT_OK if information is available, + * BP_RESULT_BADBIOSTABLE otherwise. + */ +static enum bp_result construct_integrated_info( + struct bios_parser *bp, + struct integrated_info *info) +{ + static enum bp_result result = BP_RESULT_BADBIOSTABLE; + + struct atom_common_table_header *header; + struct atom_data_revision revision; + + struct clock_voltage_caps temp = {0, 0}; + uint32_t i; + uint32_t j; + + if (info && DATA_TABLES(integratedsysteminfo)) { + header = GET_IMAGE(struct atom_common_table_header, + DATA_TABLES(integratedsysteminfo)); + + get_atom_data_table_revision(header, &revision); + + switch (revision.major) { + case 1: + switch (revision.minor) { + case 11: + case 12: + result = get_integrated_info_v11(bp, info); + break; + default: + return result; + } + break; + case 2: + switch (revision.minor) { + case 1: + result = get_integrated_info_v2_1(bp, info); + break; + case 2: + result = get_integrated_info_v2_2(bp, info); + break; + default: + return result; + } + break; + default: + return result; + } + if (result == BP_RESULT_OK) { + + DC_LOG_BIOS("edp1:\n" + "\tedp_pwr_on_off_delay = %d\n" + "\tedp_pwr_on_vary_bl_to_blon = %d\n" + "\tedp_pwr_down_bloff_to_vary_bloff = %d\n" + "\tedp_bootup_bl_level = %d\n", + info->edp1_info.edp_pwr_on_off_delay, + info->edp1_info.edp_pwr_on_vary_bl_to_blon, + info->edp1_info.edp_pwr_down_bloff_to_vary_bloff, + info->edp1_info.edp_bootup_bl_level); + DC_LOG_BIOS("edp2:\n" + "\tedp_pwr_on_off_delayv = %d\n" + "\tedp_pwr_on_vary_bl_to_blon = %d\n" + "\tedp_pwr_down_bloff_to_vary_bloff = %d\n" + "\tedp_bootup_bl_level = %d\n", + info->edp2_info.edp_pwr_on_off_delay, + info->edp2_info.edp_pwr_on_vary_bl_to_blon, + info->edp2_info.edp_pwr_down_bloff_to_vary_bloff, + info->edp2_info.edp_bootup_bl_level); + } + } + + if (result != BP_RESULT_OK) + return result; + else { + // Log each external path + for (i = 0; i < MAX_NUMBER_OF_EXT_DISPLAY_PATH; i++) { + if (info->ext_disp_conn_info.path[i].device_tag != 0) + DC_LOG_BIOS("integrated_info:For EXTERNAL DISPLAY PATH %d --------------\n" + "DEVICE_TAG: 0x%x\n" + "DEVICE_ACPI_ENUM: 0x%x\n" + "DEVICE_CONNECTOR_ID: 0x%x\n" + "EXT_AUX_DDC_LUT_INDEX: %d\n" + "EXT_HPD_PIN_LUT_INDEX: %d\n" + "EXT_ENCODER_OBJ_ID: 0x%x\n" + "Encoder CAPS: 0x%x\n", + i, + info->ext_disp_conn_info.path[i].device_tag, + info->ext_disp_conn_info.path[i].device_acpi_enum, + info->ext_disp_conn_info.path[i].device_connector_id.id, + info->ext_disp_conn_info.path[i].ext_aux_ddc_lut_index, + info->ext_disp_conn_info.path[i].ext_hpd_pin_lut_index, + info->ext_disp_conn_info.path[i].ext_encoder_obj_id.id, + info->ext_disp_conn_info.path[i].caps + ); + if (info->ext_disp_conn_info.path[i].caps & EXT_DISPLAY_PATH_CAPS__DP_FIXED_VS_EN) + DC_LOG_BIOS("BIOS EXT_DISPLAY_PATH_CAPS__DP_FIXED_VS_EN on path %d\n", i); + else if (bp->base.ctx->dc->config.force_bios_fixed_vs) { + info->ext_disp_conn_info.path[i].caps |= EXT_DISPLAY_PATH_CAPS__DP_FIXED_VS_EN; + DC_LOG_BIOS("driver forced EXT_DISPLAY_PATH_CAPS__DP_FIXED_VS_EN on path %d\n", i); + } + } + // Log the Checksum and Voltage Swing + DC_LOG_BIOS("Integrated info table CHECKSUM: %d\n" + "Integrated info table FIX_DP_VOLTAGE_SWING: %d\n", + info->ext_disp_conn_info.checksum, + info->ext_disp_conn_info.fixdpvoltageswing); + if (bp->base.ctx->dc->config.force_bios_fixed_vs && info->ext_disp_conn_info.fixdpvoltageswing == 0) { + info->ext_disp_conn_info.fixdpvoltageswing = bp->base.ctx->dc->config.force_bios_fixed_vs & 0xF; + DC_LOG_BIOS("driver forced fixdpvoltageswing = %d\n", info->ext_disp_conn_info.fixdpvoltageswing); + } + } + /* Sort voltage table from low to high*/ + for (i = 1; i < NUMBER_OF_DISP_CLK_VOLTAGE; ++i) { + for (j = i; j > 0; --j) { + if (info->disp_clk_voltage[j].max_supported_clk < + info->disp_clk_voltage[j-1].max_supported_clk + ) { + /* swap j and j - 1*/ + temp = info->disp_clk_voltage[j-1]; + info->disp_clk_voltage[j-1] = + info->disp_clk_voltage[j]; + info->disp_clk_voltage[j] = temp; + } + } + } + + return result; +} + +static enum bp_result bios_parser_get_vram_info( + struct dc_bios *dcb, + struct dc_vram_info *info) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + static enum bp_result result = BP_RESULT_BADBIOSTABLE; + struct atom_common_table_header *header; + struct atom_data_revision revision; + + if (info && DATA_TABLES(vram_info)) { + header = GET_IMAGE(struct atom_common_table_header, + DATA_TABLES(vram_info)); + + get_atom_data_table_revision(header, &revision); + + switch (revision.major) { + case 2: + switch (revision.minor) { + case 3: + result = get_vram_info_v23(bp, info); + break; + case 4: + result = get_vram_info_v24(bp, info); + break; + case 5: + result = get_vram_info_v25(bp, info); + break; + default: + break; + } + break; + + case 3: + switch (revision.minor) { + case 0: + result = get_vram_info_v30(bp, info); + break; + default: + break; + } + break; + + default: + return result; + } + + } + return result; +} + +static struct integrated_info *bios_parser_create_integrated_info( + struct dc_bios *dcb) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + struct integrated_info *info = NULL; + + info = kzalloc(sizeof(struct integrated_info), GFP_KERNEL); + + if (info == NULL) { + ASSERT_CRITICAL(0); + return NULL; + } + + if (construct_integrated_info(bp, info) == BP_RESULT_OK) + return info; + + kfree(info); + + return NULL; +} + +static enum bp_result update_slot_layout_info( + struct dc_bios *dcb, + unsigned int i, + struct slot_layout_info *slot_layout_info) +{ + unsigned int record_offset; + unsigned int j; + struct atom_display_object_path_v2 *object; + struct atom_bracket_layout_record *record; + struct atom_common_record_header *record_header; + static enum bp_result result; + struct bios_parser *bp; + struct object_info_table *tbl; + struct display_object_info_table_v1_4 *v1_4; + + record = NULL; + record_header = NULL; + result = BP_RESULT_NORECORD; + + bp = BP_FROM_DCB(dcb); + tbl = &bp->object_info_tbl; + v1_4 = tbl->v1_4; + + object = &v1_4->display_path[i]; + record_offset = (unsigned int) + (object->disp_recordoffset) + + (unsigned int)(bp->object_info_tbl_offset); + + for (;;) { + + record_header = (struct atom_common_record_header *) + GET_IMAGE(struct atom_common_record_header, + record_offset); + if (record_header == NULL) { + result = BP_RESULT_BADBIOSTABLE; + break; + } + + /* the end of the list */ + if (record_header->record_type == 0xff || + record_header->record_size == 0) { + break; + } + + if (record_header->record_type == + ATOM_BRACKET_LAYOUT_RECORD_TYPE && + sizeof(struct atom_bracket_layout_record) + <= record_header->record_size) { + record = (struct atom_bracket_layout_record *) + (record_header); + result = BP_RESULT_OK; + break; + } + + record_offset += record_header->record_size; + } + + /* return if the record not found */ + if (result != BP_RESULT_OK) + return result; + + /* get slot sizes */ + slot_layout_info->length = record->bracketlen; + slot_layout_info->width = record->bracketwidth; + + /* get info for each connector in the slot */ + slot_layout_info->num_of_connectors = record->conn_num; + for (j = 0; j < slot_layout_info->num_of_connectors; ++j) { + slot_layout_info->connectors[j].connector_type = + (enum connector_layout_type) + (record->conn_info[j].connector_type); + switch (record->conn_info[j].connector_type) { + case CONNECTOR_TYPE_DVI_D: + slot_layout_info->connectors[j].connector_type = + CONNECTOR_LAYOUT_TYPE_DVI_D; + slot_layout_info->connectors[j].length = + CONNECTOR_SIZE_DVI; + break; + + case CONNECTOR_TYPE_HDMI: + slot_layout_info->connectors[j].connector_type = + CONNECTOR_LAYOUT_TYPE_HDMI; + slot_layout_info->connectors[j].length = + CONNECTOR_SIZE_HDMI; + break; + + case CONNECTOR_TYPE_DISPLAY_PORT: + slot_layout_info->connectors[j].connector_type = + CONNECTOR_LAYOUT_TYPE_DP; + slot_layout_info->connectors[j].length = + CONNECTOR_SIZE_DP; + break; + + case CONNECTOR_TYPE_MINI_DISPLAY_PORT: + slot_layout_info->connectors[j].connector_type = + CONNECTOR_LAYOUT_TYPE_MINI_DP; + slot_layout_info->connectors[j].length = + CONNECTOR_SIZE_MINI_DP; + break; + + default: + slot_layout_info->connectors[j].connector_type = + CONNECTOR_LAYOUT_TYPE_UNKNOWN; + slot_layout_info->connectors[j].length = + CONNECTOR_SIZE_UNKNOWN; + } + + slot_layout_info->connectors[j].position = + record->conn_info[j].position; + slot_layout_info->connectors[j].connector_id = + object_id_from_bios_object_id( + record->conn_info[j].connectorobjid); + } + return result; +} + +static enum bp_result update_slot_layout_info_v2( + struct dc_bios *dcb, + unsigned int i, + struct slot_layout_info *slot_layout_info) +{ + unsigned int record_offset; + struct atom_display_object_path_v3 *object; + struct atom_bracket_layout_record_v2 *record; + struct atom_common_record_header *record_header; + static enum bp_result result; + struct bios_parser *bp; + struct object_info_table *tbl; + struct display_object_info_table_v1_5 *v1_5; + struct graphics_object_id connector_id; + + record = NULL; + record_header = NULL; + result = BP_RESULT_NORECORD; + + bp = BP_FROM_DCB(dcb); + tbl = &bp->object_info_tbl; + v1_5 = tbl->v1_5; + + object = &v1_5->display_path[i]; + record_offset = (unsigned int) + (object->disp_recordoffset) + + (unsigned int)(bp->object_info_tbl_offset); + + for (;;) { + + record_header = (struct atom_common_record_header *) + GET_IMAGE(struct atom_common_record_header, + record_offset); + if (record_header == NULL) { + result = BP_RESULT_BADBIOSTABLE; + break; + } + + /* the end of the list */ + if (record_header->record_type == ATOM_RECORD_END_TYPE || + record_header->record_size == 0) { + break; + } + + if (record_header->record_type == + ATOM_BRACKET_LAYOUT_V2_RECORD_TYPE && + sizeof(struct atom_bracket_layout_record_v2) + <= record_header->record_size) { + record = (struct atom_bracket_layout_record_v2 *) + (record_header); + result = BP_RESULT_OK; + break; + } + + record_offset += record_header->record_size; + } + + /* return if the record not found */ + if (result != BP_RESULT_OK) + return result; + + /* get slot sizes */ + connector_id = object_id_from_bios_object_id(object->display_objid); + + slot_layout_info->length = record->bracketlen; + slot_layout_info->width = record->bracketwidth; + slot_layout_info->num_of_connectors = v1_5->number_of_path; + slot_layout_info->connectors[i].position = record->conn_num; + slot_layout_info->connectors[i].connector_id = connector_id; + + switch (connector_id.id) { + case CONNECTOR_ID_SINGLE_LINK_DVID: + case CONNECTOR_ID_DUAL_LINK_DVID: + slot_layout_info->connectors[i].connector_type = CONNECTOR_LAYOUT_TYPE_DVI_D; + slot_layout_info->connectors[i].length = CONNECTOR_SIZE_DVI; + break; + + case CONNECTOR_ID_HDMI_TYPE_A: + slot_layout_info->connectors[i].connector_type = CONNECTOR_LAYOUT_TYPE_HDMI; + slot_layout_info->connectors[i].length = CONNECTOR_SIZE_HDMI; + break; + + case CONNECTOR_ID_DISPLAY_PORT: + case CONNECTOR_ID_USBC: + if (record->mini_type == MINI_TYPE_NORMAL) { + slot_layout_info->connectors[i].connector_type = CONNECTOR_LAYOUT_TYPE_DP; + slot_layout_info->connectors[i].length = CONNECTOR_SIZE_DP; + } else { + slot_layout_info->connectors[i].connector_type = CONNECTOR_LAYOUT_TYPE_MINI_DP; + slot_layout_info->connectors[i].length = CONNECTOR_SIZE_MINI_DP; + } + break; + + default: + slot_layout_info->connectors[i].connector_type = CONNECTOR_LAYOUT_TYPE_UNKNOWN; + slot_layout_info->connectors[i].length = CONNECTOR_SIZE_UNKNOWN; + } + return result; +} + +static enum bp_result get_bracket_layout_record( + struct dc_bios *dcb, + unsigned int bracket_layout_id, + struct slot_layout_info *slot_layout_info) +{ + unsigned int i; + struct bios_parser *bp = BP_FROM_DCB(dcb); + static enum bp_result result; + struct object_info_table *tbl; + struct display_object_info_table_v1_4 *v1_4; + struct display_object_info_table_v1_5 *v1_5; + + if (slot_layout_info == NULL) { + DC_LOG_DETECTION_EDID_PARSER("Invalid slot_layout_info\n"); + return BP_RESULT_BADINPUT; + } + tbl = &bp->object_info_tbl; + v1_4 = tbl->v1_4; + v1_5 = tbl->v1_5; + + result = BP_RESULT_NORECORD; + switch (bp->object_info_tbl.revision.minor) { + case 4: + default: + for (i = 0; i < v1_4->number_of_path; ++i) { + if (bracket_layout_id == + v1_4->display_path[i].display_objid) { + result = update_slot_layout_info(dcb, i, slot_layout_info); + break; + } + } + break; + case 5: + for (i = 0; i < v1_5->number_of_path; ++i) + result = update_slot_layout_info_v2(dcb, i, slot_layout_info); + break; + } + return result; +} + +static enum bp_result bios_get_board_layout_info( + struct dc_bios *dcb, + struct board_layout_info *board_layout_info) +{ + unsigned int i; + + struct bios_parser *bp; + + static enum bp_result record_result; + unsigned int max_slots; + + const unsigned int slot_index_to_vbios_id[MAX_BOARD_SLOTS] = { + GENERICOBJECT_BRACKET_LAYOUT_ENUM_ID1, + GENERICOBJECT_BRACKET_LAYOUT_ENUM_ID2, + 0, 0 + }; + + + bp = BP_FROM_DCB(dcb); + + if (board_layout_info == NULL) { + DC_LOG_DETECTION_EDID_PARSER("Invalid board_layout_info\n"); + return BP_RESULT_BADINPUT; + } + + board_layout_info->num_of_slots = 0; + max_slots = MAX_BOARD_SLOTS; + + // Assume single slot on v1_5 + if (bp->object_info_tbl.revision.minor == 5) { + max_slots = 1; + } + + for (i = 0; i < max_slots; ++i) { + record_result = get_bracket_layout_record(dcb, + slot_index_to_vbios_id[i], + &board_layout_info->slots[i]); + + if (record_result == BP_RESULT_NORECORD && i > 0) + break; /* no more slots present in bios */ + else if (record_result != BP_RESULT_OK) + return record_result; /* fail */ + + ++board_layout_info->num_of_slots; + } + + /* all data is valid */ + board_layout_info->is_number_of_slots_valid = 1; + board_layout_info->is_slots_size_valid = 1; + board_layout_info->is_connector_offsets_valid = 1; + board_layout_info->is_connector_lengths_valid = 1; + + return BP_RESULT_OK; +} + + +static uint16_t bios_parser_pack_data_tables( + struct dc_bios *dcb, + void *dst) +{ + // TODO: There is data bytes alignment issue, disable it for now. + return 0; +} + +static struct atom_dc_golden_table_v1 *bios_get_golden_table( + struct bios_parser *bp, + uint32_t rev_major, + uint32_t rev_minor, + uint16_t *dc_golden_table_ver) +{ + struct atom_display_controller_info_v4_4 *disp_cntl_tbl_4_4 = NULL; + uint32_t dc_golden_offset = 0; + *dc_golden_table_ver = 0; + + if (!DATA_TABLES(dce_info)) + return NULL; + + /* ver.4.4 or higher */ + switch (rev_major) { + case 4: + switch (rev_minor) { + case 4: + disp_cntl_tbl_4_4 = GET_IMAGE(struct atom_display_controller_info_v4_4, + DATA_TABLES(dce_info)); + if (!disp_cntl_tbl_4_4) + return NULL; + dc_golden_offset = DATA_TABLES(dce_info) + disp_cntl_tbl_4_4->dc_golden_table_offset; + *dc_golden_table_ver = disp_cntl_tbl_4_4->dc_golden_table_ver; + break; + case 5: + default: + /* For atom_display_controller_info_v4_5 there is no need to get golden table from + * dc_golden_table_offset as all these fields previously in golden table used for AUX + * pre-charge settings are now available directly in atom_display_controller_info_v4_5. + */ + break; + } + break; + } + + if (!dc_golden_offset) + return NULL; + + if (*dc_golden_table_ver != 1) + return NULL; + + return GET_IMAGE(struct atom_dc_golden_table_v1, + dc_golden_offset); +} + +static enum bp_result bios_get_atom_dc_golden_table( + struct dc_bios *dcb) +{ + struct bios_parser *bp = BP_FROM_DCB(dcb); + enum bp_result result = BP_RESULT_OK; + struct atom_dc_golden_table_v1 *atom_dc_golden_table = NULL; + struct atom_common_table_header *header; + struct atom_data_revision tbl_revision; + uint16_t dc_golden_table_ver = 0; + + header = GET_IMAGE(struct atom_common_table_header, + DATA_TABLES(dce_info)); + if (!header) + return BP_RESULT_UNSUPPORTED; + + get_atom_data_table_revision(header, &tbl_revision); + + atom_dc_golden_table = bios_get_golden_table(bp, + tbl_revision.major, + tbl_revision.minor, + &dc_golden_table_ver); + + if (!atom_dc_golden_table) + return BP_RESULT_UNSUPPORTED; + + dcb->golden_table.dc_golden_table_ver = dc_golden_table_ver; + dcb->golden_table.aux_dphy_rx_control0_val = atom_dc_golden_table->aux_dphy_rx_control0_val; + dcb->golden_table.aux_dphy_rx_control1_val = atom_dc_golden_table->aux_dphy_rx_control1_val; + dcb->golden_table.aux_dphy_tx_control_val = atom_dc_golden_table->aux_dphy_tx_control_val; + dcb->golden_table.dc_gpio_aux_ctrl_0_val = atom_dc_golden_table->dc_gpio_aux_ctrl_0_val; + dcb->golden_table.dc_gpio_aux_ctrl_1_val = atom_dc_golden_table->dc_gpio_aux_ctrl_1_val; + dcb->golden_table.dc_gpio_aux_ctrl_2_val = atom_dc_golden_table->dc_gpio_aux_ctrl_2_val; + dcb->golden_table.dc_gpio_aux_ctrl_3_val = atom_dc_golden_table->dc_gpio_aux_ctrl_3_val; + dcb->golden_table.dc_gpio_aux_ctrl_4_val = atom_dc_golden_table->dc_gpio_aux_ctrl_4_val; + dcb->golden_table.dc_gpio_aux_ctrl_5_val = atom_dc_golden_table->dc_gpio_aux_ctrl_5_val; + + return result; +} + + +static const struct dc_vbios_funcs vbios_funcs = { + .get_connectors_number = bios_parser_get_connectors_number, + + .get_connector_id = bios_parser_get_connector_id, + + .get_src_obj = bios_parser_get_src_obj, + + .get_i2c_info = bios_parser_get_i2c_info, + + .get_hpd_info = bios_parser_get_hpd_info, + + .get_device_tag = bios_parser_get_device_tag, + + .get_spread_spectrum_info = bios_parser_get_spread_spectrum_info, + + .get_ss_entry_number = bios_parser_get_ss_entry_number, + + .get_embedded_panel_info = bios_parser_get_embedded_panel_info, + + .get_gpio_pin_info = bios_parser_get_gpio_pin_info, + + .get_encoder_cap_info = bios_parser_get_encoder_cap_info, + + .is_device_id_supported = bios_parser_is_device_id_supported, + + .is_accelerated_mode = bios_parser_is_accelerated_mode, + + .set_scratch_critical_state = bios_parser_set_scratch_critical_state, + + +/* COMMANDS */ + .encoder_control = bios_parser_encoder_control, + + .transmitter_control = bios_parser_transmitter_control, + + .enable_crtc = bios_parser_enable_crtc, + + .set_pixel_clock = bios_parser_set_pixel_clock, + + .set_dce_clock = bios_parser_set_dce_clock, + + .program_crtc_timing = bios_parser_program_crtc_timing, + + .enable_disp_power_gating = bios_parser_enable_disp_power_gating, + + .bios_parser_destroy = firmware_parser_destroy, + + .get_board_layout_info = bios_get_board_layout_info, + /* TODO: use this fn in hw init?*/ + .pack_data_tables = bios_parser_pack_data_tables, + + .get_atom_dc_golden_table = bios_get_atom_dc_golden_table, + + .enable_lvtma_control = bios_parser_enable_lvtma_control, + + .get_soc_bb_info = bios_parser_get_soc_bb_info, + + .get_disp_connector_caps_info = bios_parser_get_disp_connector_caps_info, + + .get_lttpr_caps = bios_parser_get_lttpr_caps, + + .get_lttpr_interop = bios_parser_get_lttpr_interop, + + .get_connector_speed_cap_info = bios_parser_get_connector_speed_cap_info, +}; + +static bool bios_parser2_construct( + struct bios_parser *bp, + struct bp_init_data *init, + enum dce_version dce_version) +{ + uint16_t *rom_header_offset = NULL; + struct atom_rom_header_v2_2 *rom_header = NULL; + struct display_object_info_table_v1_4 *object_info_tbl; + struct atom_data_revision tbl_rev = {0}; + + 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[OFFSET_TO_ATOM_ROM_IMAGE_SIZE] * BIOS_IMAGE_SIZE_UNIT; + + bp->base.ctx = init->ctx; + + bp->base.bios_local_image = NULL; + + rom_header_offset = + GET_IMAGE(uint16_t, OFFSET_TO_ATOM_ROM_HEADER_POINTER); + + if (!rom_header_offset) + return false; + + rom_header = GET_IMAGE(struct atom_rom_header_v2_2, *rom_header_offset); + + if (!rom_header) + return false; + + get_atom_data_table_revision(&rom_header->table_header, &tbl_rev); + if (!(tbl_rev.major >= 2 && tbl_rev.minor >= 2)) + return false; + + bp->master_data_tbl = + GET_IMAGE(struct atom_master_data_table_v2_1, + rom_header->masterdatatable_offset); + + if (!bp->master_data_tbl) + return false; + + bp->object_info_tbl_offset = DATA_TABLES(displayobjectinfo); + + if (!bp->object_info_tbl_offset) + return false; + + object_info_tbl = + GET_IMAGE(struct display_object_info_table_v1_4, + bp->object_info_tbl_offset); + + if (!object_info_tbl) + return false; + + get_atom_data_table_revision(&object_info_tbl->table_header, + &bp->object_info_tbl.revision); + + if (bp->object_info_tbl.revision.major == 1 + && bp->object_info_tbl.revision.minor == 4) { + struct display_object_info_table_v1_4 *tbl_v1_4; + + tbl_v1_4 = GET_IMAGE(struct display_object_info_table_v1_4, + bp->object_info_tbl_offset); + if (!tbl_v1_4) + return false; + + bp->object_info_tbl.v1_4 = tbl_v1_4; + } else if (bp->object_info_tbl.revision.major == 1 + && bp->object_info_tbl.revision.minor == 5) { + struct display_object_info_table_v1_5 *tbl_v1_5; + + tbl_v1_5 = GET_IMAGE(struct display_object_info_table_v1_5, + bp->object_info_tbl_offset); + if (!tbl_v1_5) + return false; + + bp->object_info_tbl.v1_5 = tbl_v1_5; + } else { + ASSERT(0); + return false; + } + + dal_firmware_parser_init_cmd_tbl(bp); + dal_bios_parser_init_cmd_tbl_helper2(&bp->cmd_helper, dce_version); + + bp->base.integrated_info = bios_parser_create_integrated_info(&bp->base); + bp->base.fw_info_valid = bios_parser_get_firmware_info(&bp->base, &bp->base.fw_info) == BP_RESULT_OK; + bios_parser_get_vram_info(&bp->base, &bp->base.vram_info); + + return true; +} + +struct dc_bios *firmware_parser_create( + struct bp_init_data *init, + enum dce_version dce_version) +{ + struct bios_parser *bp = NULL; + + bp = kzalloc(sizeof(struct bios_parser), GFP_KERNEL); + if (!bp) + return NULL; + + if (bios_parser2_construct(bp, init, dce_version)) + return &bp->base; + + kfree(bp); + return NULL; +} + + diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.h b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.h new file mode 100644 index 000000000..cb40546cd --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.h @@ -0,0 +1,33 @@ +/* + * 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 + * + */ + +#ifndef __DAL_BIOS_PARSER2_H__ +#define __DAL_BIOS_PARSER2_H__ + +struct dc_bios *firmware_parser_create( + struct bp_init_data *init, + enum dce_version dce_version); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser_common.c b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_common.c new file mode 100644 index 000000000..34e3a64f5 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_common.c @@ -0,0 +1,291 @@ +/* + * 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 + * + */ + +#include "bios_parser_common.h" +#include "include/grph_object_ctrl_defs.h" + +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; + enum object_type object_type; + + switch (bios_object_type) { + case GRAPH_OBJECT_TYPE_GPU: + object_type = OBJECT_TYPE_GPU; + break; + case GRAPH_OBJECT_TYPE_ENCODER: + object_type = OBJECT_TYPE_ENCODER; + break; + case GRAPH_OBJECT_TYPE_CONNECTOR: + object_type = OBJECT_TYPE_CONNECTOR; + break; + case GRAPH_OBJECT_TYPE_ROUTER: + object_type = OBJECT_TYPE_ROUTER; + break; + case GRAPH_OBJECT_TYPE_GENERIC: + object_type = OBJECT_TYPE_GENERIC; + break; + default: + object_type = OBJECT_TYPE_UNKNOWN; + break; + } + + return object_type; +} + +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; + enum object_enum_id id; + + switch (bios_enum_id) { + case GRAPH_OBJECT_ENUM_ID1: + id = ENUM_ID_1; + break; + case GRAPH_OBJECT_ENUM_ID2: + id = ENUM_ID_2; + break; + case GRAPH_OBJECT_ENUM_ID3: + id = ENUM_ID_3; + break; + case GRAPH_OBJECT_ENUM_ID4: + id = ENUM_ID_4; + break; + case GRAPH_OBJECT_ENUM_ID5: + id = ENUM_ID_5; + break; + case GRAPH_OBJECT_ENUM_ID6: + id = ENUM_ID_6; + break; + case GRAPH_OBJECT_ENUM_ID7: + id = ENUM_ID_7; + break; + default: + id = ENUM_ID_UNKNOWN; + break; + } + + return id; +} + +static uint32_t gpu_id_from_bios_object_id(uint32_t bios_object_id) +{ + return (bios_object_id & OBJECT_ID_MASK) >> OBJECT_ID_SHIFT; +} + +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); + enum encoder_id id; + + switch (bios_encoder_id) { + case ENCODER_OBJECT_ID_INTERNAL_LVDS: + id = ENCODER_ID_INTERNAL_LVDS; + break; + case ENCODER_OBJECT_ID_INTERNAL_TMDS1: + id = ENCODER_ID_INTERNAL_TMDS1; + break; + case ENCODER_OBJECT_ID_INTERNAL_TMDS2: + id = ENCODER_ID_INTERNAL_TMDS2; + break; + case ENCODER_OBJECT_ID_INTERNAL_DAC1: + id = ENCODER_ID_INTERNAL_DAC1; + break; + case ENCODER_OBJECT_ID_INTERNAL_DAC2: + id = ENCODER_ID_INTERNAL_DAC2; + break; + case ENCODER_OBJECT_ID_INTERNAL_LVTM1: + id = ENCODER_ID_INTERNAL_LVTM1; + break; + case ENCODER_OBJECT_ID_HDMI_INTERNAL: + id = ENCODER_ID_INTERNAL_HDMI; + break; + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: + id = ENCODER_ID_INTERNAL_KLDSCP_TMDS1; + break; + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1: + id = ENCODER_ID_INTERNAL_KLDSCP_DAC1; + break; + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2: + id = ENCODER_ID_INTERNAL_KLDSCP_DAC2; + break; + case ENCODER_OBJECT_ID_MVPU_FPGA: + id = ENCODER_ID_EXTERNAL_MVPU_FPGA; + break; + case ENCODER_OBJECT_ID_INTERNAL_DDI: + id = ENCODER_ID_INTERNAL_DDI; + break; + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: + id = ENCODER_ID_INTERNAL_UNIPHY; + break; + case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: + id = ENCODER_ID_INTERNAL_KLDSCP_LVTMA; + break; + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: + id = ENCODER_ID_INTERNAL_UNIPHY1; + break; + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: + id = ENCODER_ID_INTERNAL_UNIPHY2; + break; + case ENCODER_OBJECT_ID_ALMOND: /* ENCODER_OBJECT_ID_NUTMEG */ + id = ENCODER_ID_EXTERNAL_NUTMEG; + break; + case ENCODER_OBJECT_ID_TRAVIS: + id = ENCODER_ID_EXTERNAL_TRAVIS; + break; + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3: + id = ENCODER_ID_INTERNAL_UNIPHY3; + break; + default: + id = ENCODER_ID_UNKNOWN; + ASSERT(0); + break; + } + + return id; +} + +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); + + enum connector_id id; + + switch (bios_connector_id) { + case CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I: + id = CONNECTOR_ID_SINGLE_LINK_DVII; + break; + case CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I: + id = CONNECTOR_ID_DUAL_LINK_DVII; + break; + case CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D: + id = CONNECTOR_ID_SINGLE_LINK_DVID; + break; + case CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D: + id = CONNECTOR_ID_DUAL_LINK_DVID; + break; + case CONNECTOR_OBJECT_ID_VGA: + id = CONNECTOR_ID_VGA; + break; + case CONNECTOR_OBJECT_ID_HDMI_TYPE_A: + id = CONNECTOR_ID_HDMI_TYPE_A; + break; + case CONNECTOR_OBJECT_ID_LVDS: + id = CONNECTOR_ID_LVDS; + break; + case CONNECTOR_OBJECT_ID_PCIE_CONNECTOR: + id = CONNECTOR_ID_PCIE; + break; + case CONNECTOR_OBJECT_ID_HARDCODE_DVI: + id = CONNECTOR_ID_HARDCODE_DVI; + break; + case CONNECTOR_OBJECT_ID_DISPLAYPORT: + id = CONNECTOR_ID_DISPLAY_PORT; + break; + case CONNECTOR_OBJECT_ID_eDP: + id = CONNECTOR_ID_EDP; + break; + case CONNECTOR_OBJECT_ID_MXM: + id = CONNECTOR_ID_MXM; + break; + case CONNECTOR_OBJECT_ID_USBC: + id = CONNECTOR_ID_USBC; + break; + default: + id = CONNECTOR_ID_UNKNOWN; + break; + } + + return id; +} + +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); + + enum generic_id id; + + switch (bios_generic_id) { + case GENERIC_OBJECT_ID_MXM_OPM: + id = GENERIC_ID_MXM_OPM; + break; + case GENERIC_OBJECT_ID_GLSYNC: + id = GENERIC_ID_GLSYNC; + break; + case GENERIC_OBJECT_ID_STEREO_PIN: + id = GENERIC_ID_STEREO; + break; + default: + id = GENERIC_ID_UNKNOWN; + break; + } + + return id; +} + +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; + } +} + +struct graphics_object_id object_id_from_bios_object_id(uint32_t bios_object_id) +{ + enum object_type type; + enum object_enum_id enum_id; + struct graphics_object_id go_id = { 0 }; + + type = object_type_from_bios_object_id(bios_object_id); + + if (OBJECT_TYPE_UNKNOWN == type) + return go_id; + + enum_id = enum_id_from_bios_object_id(bios_object_id); + + if (ENUM_ID_UNKNOWN == enum_id) + return go_id; + + go_id = dal_graphics_object_id_init( + id_from_bios_object_id(type, bios_object_id), enum_id, type); + + return go_id; +} + + diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser_common.h b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_common.h new file mode 100644 index 000000000..a076c61df --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_common.h @@ -0,0 +1,33 @@ +/* + * 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 + * + */ + +#ifndef __BIOS_PARSER_COMMON_H__ +#define __BIOS_PARSER_COMMON_H__ + +#include "dm_services.h" +#include "ObjectID.h" + +struct graphics_object_id object_id_from_bios_object_id(uint32_t bios_object_id); +#endif diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.c b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.c new file mode 100644 index 000000000..adc710fe4 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.c @@ -0,0 +1,87 @@ +/* + * 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 + * + */ + +#include "dm_services.h" + +#include "atom.h" + +#include "include/bios_parser_types.h" +#include "bios_parser_helper.h" +#include "command_table_helper.h" +#include "command_table.h" +#include "bios_parser_types_internal.h" + +uint8_t *bios_get_image(struct dc_bios *bp, + uint32_t offset, + uint32_t size) +{ + if (bp->bios && offset + size < bp->bios_size) + return bp->bios + offset; + else + return NULL; +} + +#include "reg_helper.h" + +#define CTX \ + bios->ctx +#define REG(reg)\ + (bios->regs->reg) + +#undef FN +#define FN(reg_name, field_name) \ + ATOM_ ## field_name ## _SHIFT, ATOM_ ## field_name + +bool bios_is_accelerated_mode( + struct dc_bios *bios) +{ + uint32_t acc_mode; + REG_GET(BIOS_SCRATCH_6, S6_ACC_MODE, &acc_mode); + return (acc_mode == 1); +} + + +void bios_set_scratch_acc_mode_change( + struct dc_bios *bios, + uint32_t state) +{ + REG_UPDATE(BIOS_SCRATCH_6, S6_ACC_MODE, state); +} + + +void bios_set_scratch_critical_state( + struct dc_bios *bios, + bool state) +{ + uint32_t critial_state = state ? 1 : 0; + REG_UPDATE(BIOS_SCRATCH_6, S6_CRITICAL_STATE, critial_state); +} + +uint32_t bios_get_vga_enabled_displays( + struct dc_bios *bios) +{ + return REG_READ(BIOS_SCRATCH_3) & 0XFFFF; +} + diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.h b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.h new file mode 100644 index 000000000..e1b4a40a3 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_helper.h @@ -0,0 +1,41 @@ +/* + * Copyright 2012-16 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 + * + */ + +#ifndef __DAL_BIOS_PARSER_HELPER_H__ +#define __DAL_BIOS_PARSER_HELPER_H__ + +struct bios_parser; + +uint8_t *bios_get_image(struct dc_bios *bp, uint32_t offset, + uint32_t size); + +bool bios_is_accelerated_mode(struct dc_bios *bios); +void bios_set_scratch_acc_mode_change(struct dc_bios *bios, uint32_t state); +void bios_set_scratch_critical_state(struct dc_bios *bios, bool state); +uint32_t bios_get_vga_enabled_displays(struct dc_bios *bios); + +#define GET_IMAGE(type, offset) ((type *) bios_get_image(&bp->base, offset, sizeof(type))) + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser_interface.c b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_interface.c new file mode 100644 index 000000000..0079a1e26 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_interface.c @@ -0,0 +1,56 @@ +/* + * 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 + * + */ + +#include "dm_services.h" +#include "include/logger_interface.h" + +#include "bios_parser_interface.h" +#include "bios_parser.h" + +#include "bios_parser2.h" + + +struct dc_bios *dal_bios_parser_create( + struct bp_init_data *init, + enum dce_version dce_version) +{ + struct dc_bios *bios = NULL; + + bios = firmware_parser_create(init, dce_version); + + /* Fall back to old bios parser for older asics */ + if (bios == NULL) + bios = bios_parser_create(init, dce_version); + + return bios; +} + +void dal_bios_parser_destroy(struct dc_bios **dcb) +{ + struct dc_bios *bios = *dcb; + + bios->funcs->bios_parser_destroy(dcb); +} + diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser_types_internal.h b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_types_internal.h new file mode 100644 index 000000000..5918923bf --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_types_internal.h @@ -0,0 +1,72 @@ +/* + * 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 + * + */ + +#ifndef __DAL_BIOS_PARSER_TYPES_BIOS_H__ +#define __DAL_BIOS_PARSER_TYPES_BIOS_H__ + +#include "dc_bios_types.h" +#include "bios_parser_helper.h" + +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; + }; +}; + +enum spread_spectrum_id { + SS_ID_UNKNOWN = 0, + SS_ID_DP1 = 0xf1, + SS_ID_DP2 = 0xf2, + SS_ID_LVLINK_2700MHZ = 0xf3, + SS_ID_LVLINK_1620MHZ = 0xf4 +}; + +struct bios_parser { + struct dc_bios base; + + 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; +}; + +/* Bios Parser from DC Bios */ +#define BP_FROM_DCB(dc_bios) \ + container_of(dc_bios, struct bios_parser, base) + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser_types_internal2.h b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_types_internal2.h new file mode 100644 index 000000000..41d02d473 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser_types_internal2.h @@ -0,0 +1,75 @@ +/* + * 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 + * + */ + +#ifndef __DAL_BIOS_PARSER_TYPES_BIOS2_H__ +#define __DAL_BIOS_PARSER_TYPES_BIOS2_H__ + +#include "dc_bios_types.h" +#include "bios_parser_helper.h" + +/* use atomfirmware_bringup.h only. Not atombios.h anymore */ + +struct atom_data_revision { + uint32_t major; + uint32_t minor; +}; + +struct object_info_table { + struct atom_data_revision revision; + union { + struct display_object_info_table_v1_4 *v1_4; + struct display_object_info_table_v1_5 *v1_5; + }; +}; + +enum spread_spectrum_id { + SS_ID_UNKNOWN = 0, + SS_ID_DP1 = 0xf1, + SS_ID_DP2 = 0xf2, + SS_ID_LVLINK_2700MHZ = 0xf3, + SS_ID_LVLINK_1620MHZ = 0xf4 +}; + +struct bios_parser { + struct dc_bios base; + + struct object_info_table object_info_tbl; + uint32_t object_info_tbl_offset; + struct atom_master_data_table_v2_1 *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; +}; + +/* Bios Parser from DC Bios */ +#define BP_FROM_DCB(dc_bios) \ + container_of(dc_bios, struct bios_parser, base) + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/bios/command_table.c b/drivers/gpu/drm/amd/display/dc/bios/command_table.c new file mode 100644 index 000000000..818a529ca --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/command_table.c @@ -0,0 +1,2432 @@ +/* + * 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 + * + */ + +#include "dm_services.h" +#include "amdgpu.h" +#include "atom.h" + +#include "include/bios_parser_interface.h" + +#include "command_table.h" +#include "command_table_helper.h" +#include "bios_parser_helper.h" +#include "bios_parser_types_internal.h" + +#define EXEC_BIOS_CMD_TABLE(command, params)\ + (amdgpu_atom_execute_table(((struct amdgpu_device *)bp->base.ctx->driver_context)->mode_info.atom_context, \ + GetIndexIntoMasterTable(COMMAND, command), \ + (uint32_t *)¶ms) == 0) + +#define BIOS_CMD_TABLE_REVISION(command, frev, crev)\ + amdgpu_atom_parse_cmd_header(((struct amdgpu_device *)bp->base.ctx->driver_context)->mode_info.atom_context, \ + GetIndexIntoMasterTable(COMMAND, command), &frev, &crev) + +#define BIOS_CMD_TABLE_PARA_REVISION(command)\ + bios_cmd_table_para_revision(bp->base.ctx->driver_context, \ + GetIndexIntoMasterTable(COMMAND, command)) + +static void init_dig_encoder_control(struct bios_parser *bp); +static void init_transmitter_control(struct bios_parser *bp); +static void init_set_pixel_clock(struct bios_parser *bp); +static void init_enable_spread_spectrum_on_ppll(struct bios_parser *bp); +static void init_adjust_display_pll(struct bios_parser *bp); +static void init_dac_encoder_control(struct bios_parser *bp); +static void init_dac_output_control(struct bios_parser *bp); +static void init_set_crtc_timing(struct bios_parser *bp); +static void init_enable_crtc(struct bios_parser *bp); +static void init_enable_crtc_mem_req(struct bios_parser *bp); +static void init_external_encoder_control(struct bios_parser *bp); +static void init_enable_disp_power_gating(struct bios_parser *bp); +static void init_program_clock(struct bios_parser *bp); +static void init_set_dce_clock(struct bios_parser *bp); + +void dal_bios_parser_init_cmd_tbl(struct bios_parser *bp) +{ + init_dig_encoder_control(bp); + init_transmitter_control(bp); + init_set_pixel_clock(bp); + init_enable_spread_spectrum_on_ppll(bp); + init_adjust_display_pll(bp); + init_dac_encoder_control(bp); + init_dac_output_control(bp); + init_set_crtc_timing(bp); + init_enable_crtc(bp); + init_enable_crtc_mem_req(bp); + init_program_clock(bp); + init_external_encoder_control(bp); + init_enable_disp_power_gating(bp); + init_set_dce_clock(bp); +} + +static uint32_t bios_cmd_table_para_revision(void *dev, + uint32_t index) +{ + struct amdgpu_device *adev = dev; + uint8_t frev, crev; + + if (amdgpu_atom_parse_cmd_header(adev->mode_info.atom_context, + index, + &frev, &crev)) + return crev; + else + return 0; +} + +/******************************************************************************* + ******************************************************************************** + ** + ** D I G E N C O D E R C O N T R O L + ** + ******************************************************************************** + *******************************************************************************/ +static enum bp_result encoder_control_digx_v3( + struct bios_parser *bp, + struct bp_encoder_control *cntl); + +static enum bp_result encoder_control_digx_v4( + struct bios_parser *bp, + struct bp_encoder_control *cntl); + +static enum bp_result encoder_control_digx_v5( + struct bios_parser *bp, + struct bp_encoder_control *cntl); + +static void init_encoder_control_dig_v1(struct bios_parser *bp); + +static void init_dig_encoder_control(struct bios_parser *bp) +{ + uint32_t version = + BIOS_CMD_TABLE_PARA_REVISION(DIGxEncoderControl); + + switch (version) { + case 2: + bp->cmd_tbl.dig_encoder_control = encoder_control_digx_v3; + break; + case 4: + bp->cmd_tbl.dig_encoder_control = encoder_control_digx_v4; + break; + + case 5: + bp->cmd_tbl.dig_encoder_control = encoder_control_digx_v5; + break; + + default: + init_encoder_control_dig_v1(bp); + break; + } +} + +static enum bp_result encoder_control_dig_v1( + struct bios_parser *bp, + struct bp_encoder_control *cntl); +static enum bp_result encoder_control_dig1_v1( + struct bios_parser *bp, + struct bp_encoder_control *cntl); +static enum bp_result encoder_control_dig2_v1( + struct bios_parser *bp, + struct bp_encoder_control *cntl); + +static void init_encoder_control_dig_v1(struct bios_parser *bp) +{ + struct cmd_tbl *cmd_tbl = &bp->cmd_tbl; + + if (1 == BIOS_CMD_TABLE_PARA_REVISION(DIG1EncoderControl)) + cmd_tbl->encoder_control_dig1 = encoder_control_dig1_v1; + else + cmd_tbl->encoder_control_dig1 = NULL; + + if (1 == BIOS_CMD_TABLE_PARA_REVISION(DIG2EncoderControl)) + cmd_tbl->encoder_control_dig2 = encoder_control_dig2_v1; + else + cmd_tbl->encoder_control_dig2 = NULL; + + cmd_tbl->dig_encoder_control = encoder_control_dig_v1; +} + +static enum bp_result encoder_control_dig_v1( + struct bios_parser *bp, + struct bp_encoder_control *cntl) +{ + enum bp_result result = BP_RESULT_FAILURE; + struct cmd_tbl *cmd_tbl = &bp->cmd_tbl; + + if (cntl != NULL) + switch (cntl->engine_id) { + case ENGINE_ID_DIGA: + if (cmd_tbl->encoder_control_dig1 != NULL) + result = + cmd_tbl->encoder_control_dig1(bp, cntl); + break; + case ENGINE_ID_DIGB: + if (cmd_tbl->encoder_control_dig2 != NULL) + result = + cmd_tbl->encoder_control_dig2(bp, cntl); + break; + + default: + break; + } + + return result; +} + +static enum bp_result encoder_control_dig1_v1( + struct bios_parser *bp, + struct bp_encoder_control *cntl) +{ + enum bp_result result = BP_RESULT_FAILURE; + DIG_ENCODER_CONTROL_PARAMETERS_V2 params = {0}; + + bp->cmd_helper->assign_control_parameter(bp->cmd_helper, cntl, ¶ms); + + if (EXEC_BIOS_CMD_TABLE(DIG1EncoderControl, params)) + result = BP_RESULT_OK; + + return result; +} + +static enum bp_result encoder_control_dig2_v1( + struct bios_parser *bp, + struct bp_encoder_control *cntl) +{ + enum bp_result result = BP_RESULT_FAILURE; + DIG_ENCODER_CONTROL_PARAMETERS_V2 params = {0}; + + bp->cmd_helper->assign_control_parameter(bp->cmd_helper, cntl, ¶ms); + + if (EXEC_BIOS_CMD_TABLE(DIG2EncoderControl, params)) + result = BP_RESULT_OK; + + return result; +} + +static enum bp_result encoder_control_digx_v3( + struct bios_parser *bp, + struct bp_encoder_control *cntl) +{ + enum bp_result result = BP_RESULT_FAILURE; + DIG_ENCODER_CONTROL_PARAMETERS_V3 params = {0}; + + if (LANE_COUNT_FOUR < cntl->lanes_number) + params.acConfig.ucDPLinkRate = 1; /* dual link 2.7GHz */ + else + params.acConfig.ucDPLinkRate = 0; /* single link 1.62GHz */ + + params.acConfig.ucDigSel = (uint8_t)(cntl->engine_id); + + /* We need to convert from KHz units into 10KHz units */ + params.ucAction = bp->cmd_helper->encoder_action_to_atom(cntl->action); + params.usPixelClock = cpu_to_le16((uint16_t)(cntl->pixel_clock / 10)); + params.ucEncoderMode = + (uint8_t)bp->cmd_helper->encoder_mode_bp_to_atom( + cntl->signal, + cntl->enable_dp_audio); + params.ucLaneNum = (uint8_t)(cntl->lanes_number); + + switch (cntl->color_depth) { + case COLOR_DEPTH_888: + params.ucBitPerColor = PANEL_8BIT_PER_COLOR; + break; + case COLOR_DEPTH_101010: + params.ucBitPerColor = PANEL_10BIT_PER_COLOR; + break; + case COLOR_DEPTH_121212: + params.ucBitPerColor = PANEL_12BIT_PER_COLOR; + break; + case COLOR_DEPTH_161616: + params.ucBitPerColor = PANEL_16BIT_PER_COLOR; + break; + default: + break; + } + + if (EXEC_BIOS_CMD_TABLE(DIGxEncoderControl, params)) + result = BP_RESULT_OK; + + return result; +} + +static enum bp_result encoder_control_digx_v4( + struct bios_parser *bp, + struct bp_encoder_control *cntl) +{ + enum bp_result result = BP_RESULT_FAILURE; + DIG_ENCODER_CONTROL_PARAMETERS_V4 params = {0}; + + if (LANE_COUNT_FOUR < cntl->lanes_number) + params.acConfig.ucDPLinkRate = 1; /* dual link 2.7GHz */ + else + params.acConfig.ucDPLinkRate = 0; /* single link 1.62GHz */ + + params.acConfig.ucDigSel = (uint8_t)(cntl->engine_id); + + /* We need to convert from KHz units into 10KHz units */ + params.ucAction = bp->cmd_helper->encoder_action_to_atom(cntl->action); + params.usPixelClock = cpu_to_le16((uint16_t)(cntl->pixel_clock / 10)); + params.ucEncoderMode = + (uint8_t)(bp->cmd_helper->encoder_mode_bp_to_atom( + cntl->signal, + cntl->enable_dp_audio)); + params.ucLaneNum = (uint8_t)(cntl->lanes_number); + + switch (cntl->color_depth) { + case COLOR_DEPTH_888: + params.ucBitPerColor = PANEL_8BIT_PER_COLOR; + break; + case COLOR_DEPTH_101010: + params.ucBitPerColor = PANEL_10BIT_PER_COLOR; + break; + case COLOR_DEPTH_121212: + params.ucBitPerColor = PANEL_12BIT_PER_COLOR; + break; + case COLOR_DEPTH_161616: + params.ucBitPerColor = PANEL_16BIT_PER_COLOR; + break; + default: + break; + } + + if (EXEC_BIOS_CMD_TABLE(DIGxEncoderControl, params)) + result = BP_RESULT_OK; + + return result; +} + +static enum bp_result encoder_control_digx_v5( + struct bios_parser *bp, + struct bp_encoder_control *cntl) +{ + enum bp_result result = BP_RESULT_FAILURE; + ENCODER_STREAM_SETUP_PARAMETERS_V5 params = {0}; + + params.ucDigId = (uint8_t)(cntl->engine_id); + params.ucAction = bp->cmd_helper->encoder_action_to_atom(cntl->action); + + params.ulPixelClock = cntl->pixel_clock / 10; + params.ucDigMode = + (uint8_t)(bp->cmd_helper->encoder_mode_bp_to_atom( + cntl->signal, + cntl->enable_dp_audio)); + params.ucLaneNum = (uint8_t)(cntl->lanes_number); + + switch (cntl->color_depth) { + case COLOR_DEPTH_888: + params.ucBitPerColor = PANEL_8BIT_PER_COLOR; + break; + case COLOR_DEPTH_101010: + params.ucBitPerColor = PANEL_10BIT_PER_COLOR; + break; + case COLOR_DEPTH_121212: + params.ucBitPerColor = PANEL_12BIT_PER_COLOR; + break; + case COLOR_DEPTH_161616: + params.ucBitPerColor = PANEL_16BIT_PER_COLOR; + break; + default: + break; + } + + if (cntl->signal == SIGNAL_TYPE_HDMI_TYPE_A) + switch (cntl->color_depth) { + case COLOR_DEPTH_101010: + params.ulPixelClock = + (params.ulPixelClock * 30) / 24; + break; + case COLOR_DEPTH_121212: + params.ulPixelClock = + (params.ulPixelClock * 36) / 24; + break; + case COLOR_DEPTH_161616: + params.ulPixelClock = + (params.ulPixelClock * 48) / 24; + break; + default: + break; + } + + if (EXEC_BIOS_CMD_TABLE(DIGxEncoderControl, params)) + result = BP_RESULT_OK; + + return result; +} + +/******************************************************************************* + ******************************************************************************** + ** + ** TRANSMITTER CONTROL + ** + ******************************************************************************** + *******************************************************************************/ + +static enum bp_result transmitter_control_v2( + struct bios_parser *bp, + struct bp_transmitter_control *cntl); +static enum bp_result transmitter_control_v3( + struct bios_parser *bp, + struct bp_transmitter_control *cntl); +static enum bp_result transmitter_control_v4( + struct bios_parser *bp, + struct bp_transmitter_control *cntl); +static enum bp_result transmitter_control_v1_5( + struct bios_parser *bp, + struct bp_transmitter_control *cntl); +static enum bp_result transmitter_control_v1_6( + struct bios_parser *bp, + struct bp_transmitter_control *cntl); + +static void init_transmitter_control(struct bios_parser *bp) +{ + uint8_t frev; + uint8_t crev; + + if (BIOS_CMD_TABLE_REVISION(UNIPHYTransmitterControl, + frev, crev) == false) + BREAK_TO_DEBUGGER(); + switch (crev) { + case 2: + bp->cmd_tbl.transmitter_control = transmitter_control_v2; + break; + case 3: + bp->cmd_tbl.transmitter_control = transmitter_control_v3; + break; + case 4: + bp->cmd_tbl.transmitter_control = transmitter_control_v4; + break; + case 5: + bp->cmd_tbl.transmitter_control = transmitter_control_v1_5; + break; + case 6: + bp->cmd_tbl.transmitter_control = transmitter_control_v1_6; + break; + default: + dm_output_to_console("Don't have transmitter_control for v%d\n", crev); + bp->cmd_tbl.transmitter_control = NULL; + break; + } +} + +static enum bp_result transmitter_control_v2( + struct bios_parser *bp, + struct bp_transmitter_control *cntl) +{ + enum bp_result result = BP_RESULT_FAILURE; + DIG_TRANSMITTER_CONTROL_PARAMETERS_V2 params; + enum connector_id connector_id = + dal_graphics_object_id_get_connector_id(cntl->connector_obj_id); + + memset(¶ms, 0, sizeof(params)); + + switch (cntl->transmitter) { + case TRANSMITTER_UNIPHY_A: + case TRANSMITTER_UNIPHY_B: + case TRANSMITTER_UNIPHY_C: + case TRANSMITTER_UNIPHY_D: + case TRANSMITTER_UNIPHY_E: + case TRANSMITTER_UNIPHY_F: + case TRANSMITTER_TRAVIS_LCD: + break; + default: + return BP_RESULT_BADINPUT; + } + + switch (cntl->action) { + case TRANSMITTER_CONTROL_INIT: + if ((CONNECTOR_ID_DUAL_LINK_DVII == connector_id) || + (CONNECTOR_ID_DUAL_LINK_DVID == connector_id)) + /* on INIT this bit should be set according to the + * physical connector + * Bit0: dual link connector flag + * =0 connector is single link connector + * =1 connector is dual link connector + */ + params.acConfig.fDualLinkConnector = 1; + + /* connector object id */ + params.usInitInfo = + cpu_to_le16((uint8_t)cntl->connector_obj_id.id); + break; + case TRANSMITTER_CONTROL_SET_VOLTAGE_AND_PREEMPASIS: + /* voltage swing and pre-emphsis */ + params.asMode.ucLaneSel = (uint8_t)cntl->lane_select; + params.asMode.ucLaneSet = (uint8_t)cntl->lane_settings; + break; + default: + /* if dual-link */ + if (LANE_COUNT_FOUR < cntl->lanes_number) { + /* on ENABLE/DISABLE this bit should be set according to + * actual timing (number of lanes) + * Bit0: dual link connector flag + * =0 connector is single link connector + * =1 connector is dual link connector + */ + params.acConfig.fDualLinkConnector = 1; + + /* link rate, half for dual link + * We need to convert from KHz units into 20KHz units + */ + params.usPixelClock = + cpu_to_le16((uint16_t)(cntl->pixel_clock / 20)); + } else + /* link rate, half for dual link + * We need to convert from KHz units into 10KHz units + */ + params.usPixelClock = + cpu_to_le16((uint16_t)(cntl->pixel_clock / 10)); + break; + } + + /* 00 - coherent mode + * 01 - incoherent mode + */ + + params.acConfig.fCoherentMode = cntl->coherent; + + if ((TRANSMITTER_UNIPHY_B == cntl->transmitter) + || (TRANSMITTER_UNIPHY_D == cntl->transmitter) + || (TRANSMITTER_UNIPHY_F == cntl->transmitter)) + /* Bit2: Transmitter Link selection + * =0 when bit0=0, single link A/C/E, when bit0=1, + * master link A/C/E + * =1 when bit0=0, single link B/D/F, when bit0=1, + * master link B/D/F + */ + params.acConfig.ucLinkSel = 1; + + if (ENGINE_ID_DIGB == cntl->engine_id) + /* Bit3: Transmitter data source selection + * =0 DIGA is data source. + * =1 DIGB is data source. + * This bit is only useful when ucAction= ATOM_ENABLE + */ + params.acConfig.ucEncoderSel = 1; + + if (CONNECTOR_ID_DISPLAY_PORT == connector_id || + CONNECTOR_ID_USBC == connector_id) + /* Bit4: DP connector flag + * =0 connector is none-DP connector + * =1 connector is DP connector + */ + params.acConfig.fDPConnector = 1; + + /* Bit[7:6]: Transmitter selection + * =0 UNIPHY_ENCODER: UNIPHYA/B + * =1 UNIPHY1_ENCODER: UNIPHYC/D + * =2 UNIPHY2_ENCODER: UNIPHYE/F + * =3 reserved + */ + params.acConfig.ucTransmitterSel = + (uint8_t)bp->cmd_helper->transmitter_bp_to_atom( + cntl->transmitter); + + params.ucAction = (uint8_t)cntl->action; + + if (EXEC_BIOS_CMD_TABLE(UNIPHYTransmitterControl, params)) + result = BP_RESULT_OK; + + return result; +} + +static enum bp_result transmitter_control_v3( + struct bios_parser *bp, + struct bp_transmitter_control *cntl) +{ + enum bp_result result = BP_RESULT_FAILURE; + DIG_TRANSMITTER_CONTROL_PARAMETERS_V3 params; + uint32_t pll_id; + enum connector_id conn_id = + dal_graphics_object_id_get_connector_id(cntl->connector_obj_id); + const struct command_table_helper *cmd = bp->cmd_helper; + bool dual_link_conn = (CONNECTOR_ID_DUAL_LINK_DVII == conn_id) + || (CONNECTOR_ID_DUAL_LINK_DVID == conn_id); + + memset(¶ms, 0, sizeof(params)); + + switch (cntl->transmitter) { + case TRANSMITTER_UNIPHY_A: + case TRANSMITTER_UNIPHY_B: + case TRANSMITTER_UNIPHY_C: + case TRANSMITTER_UNIPHY_D: + case TRANSMITTER_UNIPHY_E: + case TRANSMITTER_UNIPHY_F: + case TRANSMITTER_TRAVIS_LCD: + break; + default: + return BP_RESULT_BADINPUT; + } + + if (!cmd->clock_source_id_to_atom(cntl->pll_id, &pll_id)) + return BP_RESULT_BADINPUT; + + /* fill information based on the action */ + switch (cntl->action) { + case TRANSMITTER_CONTROL_INIT: + if (dual_link_conn) { + /* on INIT this bit should be set according to the + * phisycal connector + * Bit0: dual link connector flag + * =0 connector is single link connector + * =1 connector is dual link connector + */ + params.acConfig.fDualLinkConnector = 1; + } + + /* connector object id */ + params.usInitInfo = + cpu_to_le16((uint8_t)(cntl->connector_obj_id.id)); + break; + case TRANSMITTER_CONTROL_SET_VOLTAGE_AND_PREEMPASIS: + /* votage swing and pre-emphsis */ + params.asMode.ucLaneSel = (uint8_t)cntl->lane_select; + params.asMode.ucLaneSet = (uint8_t)cntl->lane_settings; + break; + default: + if (dual_link_conn && cntl->multi_path) + /* on ENABLE/DISABLE this bit should be set according to + * actual timing (number of lanes) + * Bit0: dual link connector flag + * =0 connector is single link connector + * =1 connector is dual link connector + */ + params.acConfig.fDualLinkConnector = 1; + + /* if dual-link */ + if (LANE_COUNT_FOUR < cntl->lanes_number) { + /* on ENABLE/DISABLE this bit should be set according to + * actual timing (number of lanes) + * Bit0: dual link connector flag + * =0 connector is single link connector + * =1 connector is dual link connector + */ + params.acConfig.fDualLinkConnector = 1; + + /* link rate, half for dual link + * We need to convert from KHz units into 20KHz units + */ + params.usPixelClock = + cpu_to_le16((uint16_t)(cntl->pixel_clock / 20)); + } else { + /* link rate, half for dual link + * We need to convert from KHz units into 10KHz units + */ + params.usPixelClock = + cpu_to_le16((uint16_t)(cntl->pixel_clock / 10)); + } + break; + } + + /* 00 - coherent mode + * 01 - incoherent mode + */ + + params.acConfig.fCoherentMode = cntl->coherent; + + if ((TRANSMITTER_UNIPHY_B == cntl->transmitter) + || (TRANSMITTER_UNIPHY_D == cntl->transmitter) + || (TRANSMITTER_UNIPHY_F == cntl->transmitter)) + /* Bit2: Transmitter Link selection + * =0 when bit0=0, single link A/C/E, when bit0=1, + * master link A/C/E + * =1 when bit0=0, single link B/D/F, when bit0=1, + * master link B/D/F + */ + params.acConfig.ucLinkSel = 1; + + if (ENGINE_ID_DIGB == cntl->engine_id) + /* Bit3: Transmitter data source selection + * =0 DIGA is data source. + * =1 DIGB is data source. + * This bit is only useful when ucAction= ATOM_ENABLE + */ + params.acConfig.ucEncoderSel = 1; + + /* Bit[7:6]: Transmitter selection + * =0 UNIPHY_ENCODER: UNIPHYA/B + * =1 UNIPHY1_ENCODER: UNIPHYC/D + * =2 UNIPHY2_ENCODER: UNIPHYE/F + * =3 reserved + */ + params.acConfig.ucTransmitterSel = + (uint8_t)cmd->transmitter_bp_to_atom(cntl->transmitter); + + params.ucLaneNum = (uint8_t)cntl->lanes_number; + + params.acConfig.ucRefClkSource = (uint8_t)pll_id; + + params.ucAction = (uint8_t)cntl->action; + + if (EXEC_BIOS_CMD_TABLE(UNIPHYTransmitterControl, params)) + result = BP_RESULT_OK; + + return result; +} + +static enum bp_result transmitter_control_v4( + struct bios_parser *bp, + struct bp_transmitter_control *cntl) +{ + enum bp_result result = BP_RESULT_FAILURE; + DIG_TRANSMITTER_CONTROL_PARAMETERS_V4 params; + uint32_t ref_clk_src_id; + enum connector_id conn_id = + dal_graphics_object_id_get_connector_id(cntl->connector_obj_id); + const struct command_table_helper *cmd = bp->cmd_helper; + + memset(¶ms, 0, sizeof(params)); + + switch (cntl->transmitter) { + case TRANSMITTER_UNIPHY_A: + case TRANSMITTER_UNIPHY_B: + case TRANSMITTER_UNIPHY_C: + case TRANSMITTER_UNIPHY_D: + case TRANSMITTER_UNIPHY_E: + case TRANSMITTER_UNIPHY_F: + case TRANSMITTER_TRAVIS_LCD: + break; + default: + return BP_RESULT_BADINPUT; + } + + if (!cmd->clock_source_id_to_ref_clk_src(cntl->pll_id, &ref_clk_src_id)) + return BP_RESULT_BADINPUT; + + switch (cntl->action) { + case TRANSMITTER_CONTROL_INIT: + { + if ((CONNECTOR_ID_DUAL_LINK_DVII == conn_id) || + (CONNECTOR_ID_DUAL_LINK_DVID == conn_id)) + /* on INIT this bit should be set according to the + * phisycal connector + * Bit0: dual link connector flag + * =0 connector is single link connector + * =1 connector is dual link connector + */ + params.acConfig.fDualLinkConnector = 1; + + /* connector object id */ + params.usInitInfo = + cpu_to_le16((uint8_t)(cntl->connector_obj_id.id)); + } + break; + case TRANSMITTER_CONTROL_SET_VOLTAGE_AND_PREEMPASIS: + /* votage swing and pre-emphsis */ + params.asMode.ucLaneSel = (uint8_t)(cntl->lane_select); + params.asMode.ucLaneSet = (uint8_t)(cntl->lane_settings); + break; + default: + if ((CONNECTOR_ID_DUAL_LINK_DVII == conn_id) || + (CONNECTOR_ID_DUAL_LINK_DVID == conn_id)) + /* on ENABLE/DISABLE this bit should be set according to + * actual timing (number of lanes) + * Bit0: dual link connector flag + * =0 connector is single link connector + * =1 connector is dual link connector + */ + params.acConfig.fDualLinkConnector = 1; + + /* if dual-link */ + if (LANE_COUNT_FOUR < cntl->lanes_number) + /* link rate, half for dual link + * We need to convert from KHz units into 20KHz units + */ + params.usPixelClock = + cpu_to_le16((uint16_t)(cntl->pixel_clock / 20)); + else { + /* link rate, half for dual link + * We need to convert from KHz units into 10KHz units + */ + params.usPixelClock = + cpu_to_le16((uint16_t)(cntl->pixel_clock / 10)); + } + break; + } + + /* 00 - coherent mode + * 01 - incoherent mode + */ + + params.acConfig.fCoherentMode = cntl->coherent; + + if ((TRANSMITTER_UNIPHY_B == cntl->transmitter) + || (TRANSMITTER_UNIPHY_D == cntl->transmitter) + || (TRANSMITTER_UNIPHY_F == cntl->transmitter)) + /* Bit2: Transmitter Link selection + * =0 when bit0=0, single link A/C/E, when bit0=1, + * master link A/C/E + * =1 when bit0=0, single link B/D/F, when bit0=1, + * master link B/D/F + */ + params.acConfig.ucLinkSel = 1; + + if (ENGINE_ID_DIGB == cntl->engine_id) + /* Bit3: Transmitter data source selection + * =0 DIGA is data source. + * =1 DIGB is data source. + * This bit is only useful when ucAction= ATOM_ENABLE + */ + params.acConfig.ucEncoderSel = 1; + + /* Bit[7:6]: Transmitter selection + * =0 UNIPHY_ENCODER: UNIPHYA/B + * =1 UNIPHY1_ENCODER: UNIPHYC/D + * =2 UNIPHY2_ENCODER: UNIPHYE/F + * =3 reserved + */ + params.acConfig.ucTransmitterSel = + (uint8_t)(cmd->transmitter_bp_to_atom(cntl->transmitter)); + params.ucLaneNum = (uint8_t)(cntl->lanes_number); + params.acConfig.ucRefClkSource = (uint8_t)(ref_clk_src_id); + params.ucAction = (uint8_t)(cntl->action); + + if (EXEC_BIOS_CMD_TABLE(UNIPHYTransmitterControl, params)) + result = BP_RESULT_OK; + + return result; +} + +static enum bp_result transmitter_control_v1_5( + struct bios_parser *bp, + struct bp_transmitter_control *cntl) +{ + enum bp_result result = BP_RESULT_FAILURE; + const struct command_table_helper *cmd = bp->cmd_helper; + DIG_TRANSMITTER_CONTROL_PARAMETERS_V1_5 params; + + memset(¶ms, 0, sizeof(params)); + params.ucPhyId = cmd->phy_id_to_atom(cntl->transmitter); + params.ucAction = (uint8_t)cntl->action; + params.ucLaneNum = (uint8_t)cntl->lanes_number; + params.ucConnObjId = (uint8_t)cntl->connector_obj_id.id; + + params.ucDigMode = + cmd->signal_type_to_atom_dig_mode(cntl->signal); + params.asConfig.ucPhyClkSrcId = + cmd->clock_source_id_to_atom_phy_clk_src_id(cntl->pll_id); + /* 00 - coherent mode */ + params.asConfig.ucCoherentMode = cntl->coherent; + params.asConfig.ucHPDSel = + cmd->hpd_sel_to_atom(cntl->hpd_sel); + params.ucDigEncoderSel = + cmd->dig_encoder_sel_to_atom(cntl->engine_id); + params.ucDPLaneSet = (uint8_t) cntl->lane_settings; + params.usSymClock = cpu_to_le16((uint16_t) (cntl->pixel_clock / 10)); + /* + * In SI/TN case, caller have to set usPixelClock as following: + * DP mode: usPixelClock = DP_LINK_CLOCK/10 + * (DP_LINK_CLOCK = 1.62GHz, 2.7GHz, 5.4GHz) + * DVI single link mode: usPixelClock = pixel clock + * DVI dual link mode: usPixelClock = pixel clock + * HDMI mode: usPixelClock = pixel clock * deep_color_ratio + * (=1: 8bpp, =1.25: 10bpp, =1.5:12bpp, =2: 16bpp) + * LVDS mode: usPixelClock = pixel clock + */ + if (cntl->signal == SIGNAL_TYPE_HDMI_TYPE_A) { + switch (cntl->color_depth) { + case COLOR_DEPTH_101010: + params.usSymClock = + cpu_to_le16((le16_to_cpu(params.usSymClock) * 30) / 24); + break; + case COLOR_DEPTH_121212: + params.usSymClock = + cpu_to_le16((le16_to_cpu(params.usSymClock) * 36) / 24); + break; + case COLOR_DEPTH_161616: + params.usSymClock = + cpu_to_le16((le16_to_cpu(params.usSymClock) * 48) / 24); + break; + default: + break; + } + } + + if (EXEC_BIOS_CMD_TABLE(UNIPHYTransmitterControl, params)) + result = BP_RESULT_OK; + + return result; +} + +static enum bp_result transmitter_control_v1_6( + struct bios_parser *bp, + struct bp_transmitter_control *cntl) +{ + enum bp_result result = BP_RESULT_FAILURE; + const struct command_table_helper *cmd = bp->cmd_helper; + DIG_TRANSMITTER_CONTROL_PARAMETERS_V1_6 params; + + memset(¶ms, 0, sizeof(params)); + params.ucPhyId = cmd->phy_id_to_atom(cntl->transmitter); + params.ucAction = (uint8_t)cntl->action; + + if (cntl->action == TRANSMITTER_CONTROL_SET_VOLTAGE_AND_PREEMPASIS) + params.ucDPLaneSet = (uint8_t)cntl->lane_settings; + else + params.ucDigMode = cmd->signal_type_to_atom_dig_mode(cntl->signal); + + params.ucLaneNum = (uint8_t)cntl->lanes_number; + params.ucHPDSel = cmd->hpd_sel_to_atom(cntl->hpd_sel); + params.ucDigEncoderSel = cmd->dig_encoder_sel_to_atom(cntl->engine_id); + params.ucConnObjId = (uint8_t)cntl->connector_obj_id.id; + params.ulSymClock = cntl->pixel_clock/10; + + /* + * In SI/TN case, caller have to set usPixelClock as following: + * DP mode: usPixelClock = DP_LINK_CLOCK/10 + * (DP_LINK_CLOCK = 1.62GHz, 2.7GHz, 5.4GHz) + * DVI single link mode: usPixelClock = pixel clock + * DVI dual link mode: usPixelClock = pixel clock + * HDMI mode: usPixelClock = pixel clock * deep_color_ratio + * (=1: 8bpp, =1.25: 10bpp, =1.5:12bpp, =2: 16bpp) + * LVDS mode: usPixelClock = pixel clock + */ + switch (cntl->signal) { + case SIGNAL_TYPE_HDMI_TYPE_A: + switch (cntl->color_depth) { + case COLOR_DEPTH_101010: + params.ulSymClock = + cpu_to_le16((le16_to_cpu(params.ulSymClock) * 30) / 24); + break; + case COLOR_DEPTH_121212: + params.ulSymClock = + cpu_to_le16((le16_to_cpu(params.ulSymClock) * 36) / 24); + break; + case COLOR_DEPTH_161616: + params.ulSymClock = + cpu_to_le16((le16_to_cpu(params.ulSymClock) * 48) / 24); + break; + default: + break; + } + break; + default: + break; + } + + if (EXEC_BIOS_CMD_TABLE(UNIPHYTransmitterControl, params)) + result = BP_RESULT_OK; + return result; +} + +/******************************************************************************* + ******************************************************************************** + ** + ** SET PIXEL CLOCK + ** + ******************************************************************************** + *******************************************************************************/ + +static enum bp_result set_pixel_clock_v3( + struct bios_parser *bp, + struct bp_pixel_clock_parameters *bp_params); +static enum bp_result set_pixel_clock_v5( + struct bios_parser *bp, + struct bp_pixel_clock_parameters *bp_params); +static enum bp_result set_pixel_clock_v6( + struct bios_parser *bp, + struct bp_pixel_clock_parameters *bp_params); +static enum bp_result set_pixel_clock_v7( + struct bios_parser *bp, + struct bp_pixel_clock_parameters *bp_params); + +static void init_set_pixel_clock(struct bios_parser *bp) +{ + switch (BIOS_CMD_TABLE_PARA_REVISION(SetPixelClock)) { + case 3: + bp->cmd_tbl.set_pixel_clock = set_pixel_clock_v3; + break; + case 5: + bp->cmd_tbl.set_pixel_clock = set_pixel_clock_v5; + break; + case 6: + bp->cmd_tbl.set_pixel_clock = set_pixel_clock_v6; + break; + case 7: + bp->cmd_tbl.set_pixel_clock = set_pixel_clock_v7; + break; + default: + dm_output_to_console("Don't have set_pixel_clock for v%d\n", + BIOS_CMD_TABLE_PARA_REVISION(SetPixelClock)); + bp->cmd_tbl.set_pixel_clock = NULL; + break; + } +} + +static enum bp_result set_pixel_clock_v3( + struct bios_parser *bp, + struct bp_pixel_clock_parameters *bp_params) +{ + enum bp_result result = BP_RESULT_FAILURE; + PIXEL_CLOCK_PARAMETERS_V3 *params; + SET_PIXEL_CLOCK_PS_ALLOCATION allocation; + + memset(&allocation, 0, sizeof(allocation)); + + if (CLOCK_SOURCE_ID_PLL1 == bp_params->pll_id) + allocation.sPCLKInput.ucPpll = ATOM_PPLL1; + else if (CLOCK_SOURCE_ID_PLL2 == bp_params->pll_id) + allocation.sPCLKInput.ucPpll = ATOM_PPLL2; + else + return BP_RESULT_BADINPUT; + + allocation.sPCLKInput.usRefDiv = + cpu_to_le16((uint16_t)bp_params->reference_divider); + allocation.sPCLKInput.usFbDiv = + cpu_to_le16((uint16_t)bp_params->feedback_divider); + allocation.sPCLKInput.ucFracFbDiv = + (uint8_t)bp_params->fractional_feedback_divider; + allocation.sPCLKInput.ucPostDiv = + (uint8_t)bp_params->pixel_clock_post_divider; + + /* We need to convert from 100Hz units into 10KHz units */ + allocation.sPCLKInput.usPixelClock = + cpu_to_le16((uint16_t)(bp_params->target_pixel_clock_100hz / 100)); + + params = (PIXEL_CLOCK_PARAMETERS_V3 *)&allocation.sPCLKInput; + params->ucTransmitterId = + bp->cmd_helper->encoder_id_to_atom( + dal_graphics_object_id_get_encoder_id( + bp_params->encoder_object_id)); + params->ucEncoderMode = + (uint8_t)(bp->cmd_helper->encoder_mode_bp_to_atom( + bp_params->signal_type, false)); + + if (bp_params->flags.FORCE_PROGRAMMING_OF_PLL) + params->ucMiscInfo |= PIXEL_CLOCK_MISC_FORCE_PROG_PPLL; + + if (bp_params->flags.USE_E_CLOCK_AS_SOURCE_FOR_D_CLOCK) + params->ucMiscInfo |= PIXEL_CLOCK_MISC_USE_ENGINE_FOR_DISPCLK; + + if (CONTROLLER_ID_D1 != bp_params->controller_id) + params->ucMiscInfo |= PIXEL_CLOCK_MISC_CRTC_SEL_CRTC2; + + if (EXEC_BIOS_CMD_TABLE(SetPixelClock, allocation)) + result = BP_RESULT_OK; + + return result; +} + +#ifndef SET_PIXEL_CLOCK_PS_ALLOCATION_V5 +/* video bios did not define this: */ +typedef struct _SET_PIXEL_CLOCK_PS_ALLOCATION_V5 { + PIXEL_CLOCK_PARAMETERS_V5 sPCLKInput; + /* Caller doesn't need to init this portion */ + ENABLE_SPREAD_SPECTRUM_ON_PPLL sReserved; +} SET_PIXEL_CLOCK_PS_ALLOCATION_V5; +#endif + +#ifndef SET_PIXEL_CLOCK_PS_ALLOCATION_V6 +/* video bios did not define this: */ +typedef struct _SET_PIXEL_CLOCK_PS_ALLOCATION_V6 { + PIXEL_CLOCK_PARAMETERS_V6 sPCLKInput; + /* Caller doesn't need to init this portion */ + ENABLE_SPREAD_SPECTRUM_ON_PPLL sReserved; +} SET_PIXEL_CLOCK_PS_ALLOCATION_V6; +#endif + +static enum bp_result set_pixel_clock_v5( + struct bios_parser *bp, + struct bp_pixel_clock_parameters *bp_params) +{ + enum bp_result result = BP_RESULT_FAILURE; + SET_PIXEL_CLOCK_PS_ALLOCATION_V5 clk; + uint8_t controller_id; + uint32_t pll_id; + + memset(&clk, 0, sizeof(clk)); + + if (bp->cmd_helper->clock_source_id_to_atom(bp_params->pll_id, &pll_id) + && bp->cmd_helper->controller_id_to_atom( + bp_params->controller_id, &controller_id)) { + clk.sPCLKInput.ucCRTC = controller_id; + clk.sPCLKInput.ucPpll = (uint8_t)pll_id; + clk.sPCLKInput.ucRefDiv = + (uint8_t)(bp_params->reference_divider); + clk.sPCLKInput.usFbDiv = + cpu_to_le16((uint16_t)(bp_params->feedback_divider)); + clk.sPCLKInput.ulFbDivDecFrac = + cpu_to_le32(bp_params->fractional_feedback_divider); + clk.sPCLKInput.ucPostDiv = + (uint8_t)(bp_params->pixel_clock_post_divider); + clk.sPCLKInput.ucTransmitterID = + bp->cmd_helper->encoder_id_to_atom( + dal_graphics_object_id_get_encoder_id( + bp_params->encoder_object_id)); + clk.sPCLKInput.ucEncoderMode = + (uint8_t)bp->cmd_helper->encoder_mode_bp_to_atom( + bp_params->signal_type, false); + + /* We need to convert from 100Hz units into 10KHz units */ + clk.sPCLKInput.usPixelClock = + cpu_to_le16((uint16_t)(bp_params->target_pixel_clock_100hz / 100)); + + if (bp_params->flags.FORCE_PROGRAMMING_OF_PLL) + clk.sPCLKInput.ucMiscInfo |= + PIXEL_CLOCK_MISC_FORCE_PROG_PPLL; + + if (bp_params->flags.SET_EXTERNAL_REF_DIV_SRC) + clk.sPCLKInput.ucMiscInfo |= + PIXEL_CLOCK_MISC_REF_DIV_SRC; + + /* clkV5.ucMiscInfo bit[3:2]= HDMI panel bit depth: =0: 24bpp + * =1:30bpp, =2:32bpp + * driver choose program it itself, i.e. here we program it + * to 888 by default. + */ + if (bp_params->signal_type == SIGNAL_TYPE_HDMI_TYPE_A) + switch (bp_params->color_depth) { + case TRANSMITTER_COLOR_DEPTH_30: + /* yes this is correct, the atom define is wrong */ + clk.sPCLKInput.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_HDMI_32BPP; + break; + case TRANSMITTER_COLOR_DEPTH_36: + /* yes this is correct, the atom define is wrong */ + clk.sPCLKInput.ucMiscInfo |= PIXEL_CLOCK_V5_MISC_HDMI_30BPP; + break; + default: + break; + } + + if (EXEC_BIOS_CMD_TABLE(SetPixelClock, clk)) + result = BP_RESULT_OK; + } + + return result; +} + +static enum bp_result set_pixel_clock_v6( + struct bios_parser *bp, + struct bp_pixel_clock_parameters *bp_params) +{ + enum bp_result result = BP_RESULT_FAILURE; + SET_PIXEL_CLOCK_PS_ALLOCATION_V6 clk; + uint8_t controller_id; + uint32_t pll_id; + + memset(&clk, 0, sizeof(clk)); + + if (bp->cmd_helper->clock_source_id_to_atom(bp_params->pll_id, &pll_id) + && bp->cmd_helper->controller_id_to_atom( + bp_params->controller_id, &controller_id)) { + /* Note: VBIOS still wants to use ucCRTC name which is now + * 1 byte in ULONG + *typedef struct _CRTC_PIXEL_CLOCK_FREQ + *{ + * target the pixel clock to drive the CRTC timing. + * ULONG ulPixelClock:24; + * 0 means disable PPLL/DCPLL. Expanded to 24 bits comparing to + * previous version. + * ATOM_CRTC1~6, indicate the CRTC controller to + * ULONG ucCRTC:8; + * drive the pixel clock. not used for DCPLL case. + *}CRTC_PIXEL_CLOCK_FREQ; + *union + *{ + * pixel clock and CRTC id frequency + * CRTC_PIXEL_CLOCK_FREQ ulCrtcPclkFreq; + * ULONG ulDispEngClkFreq; dispclk frequency + *}; + */ + clk.sPCLKInput.ulCrtcPclkFreq.ucCRTC = controller_id; + clk.sPCLKInput.ucPpll = (uint8_t) pll_id; + clk.sPCLKInput.ucRefDiv = + (uint8_t) bp_params->reference_divider; + clk.sPCLKInput.usFbDiv = + cpu_to_le16((uint16_t) bp_params->feedback_divider); + clk.sPCLKInput.ulFbDivDecFrac = + cpu_to_le32(bp_params->fractional_feedback_divider); + clk.sPCLKInput.ucPostDiv = + (uint8_t) bp_params->pixel_clock_post_divider; + clk.sPCLKInput.ucTransmitterID = + bp->cmd_helper->encoder_id_to_atom( + dal_graphics_object_id_get_encoder_id( + bp_params->encoder_object_id)); + clk.sPCLKInput.ucEncoderMode = + (uint8_t) bp->cmd_helper->encoder_mode_bp_to_atom( + bp_params->signal_type, false); + + /* We need to convert from 100 Hz units into 10KHz units */ + clk.sPCLKInput.ulCrtcPclkFreq.ulPixelClock = + cpu_to_le32(bp_params->target_pixel_clock_100hz / 100); + + if (bp_params->flags.FORCE_PROGRAMMING_OF_PLL) { + clk.sPCLKInput.ucMiscInfo |= + PIXEL_CLOCK_V6_MISC_FORCE_PROG_PPLL; + } + + if (bp_params->flags.SET_EXTERNAL_REF_DIV_SRC) { + clk.sPCLKInput.ucMiscInfo |= + PIXEL_CLOCK_V6_MISC_REF_DIV_SRC; + } + + /* clkV6.ucMiscInfo bit[3:2]= HDMI panel bit depth: =0: + * 24bpp =1:30bpp, =2:32bpp + * driver choose program it itself, i.e. here we pass required + * target rate that includes deep color. + */ + if (bp_params->signal_type == SIGNAL_TYPE_HDMI_TYPE_A) + switch (bp_params->color_depth) { + case TRANSMITTER_COLOR_DEPTH_30: + clk.sPCLKInput.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_30BPP_V6; + break; + case TRANSMITTER_COLOR_DEPTH_36: + clk.sPCLKInput.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_36BPP_V6; + break; + case TRANSMITTER_COLOR_DEPTH_48: + clk.sPCLKInput.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_HDMI_48BPP; + break; + default: + break; + } + + if (EXEC_BIOS_CMD_TABLE(SetPixelClock, clk)) + result = BP_RESULT_OK; + } + + return result; +} + +static enum bp_result set_pixel_clock_v7( + struct bios_parser *bp, + struct bp_pixel_clock_parameters *bp_params) +{ + enum bp_result result = BP_RESULT_FAILURE; + PIXEL_CLOCK_PARAMETERS_V7 clk; + uint8_t controller_id; + uint32_t pll_id; + + memset(&clk, 0, sizeof(clk)); + + if (bp->cmd_helper->clock_source_id_to_atom(bp_params->pll_id, &pll_id) + && bp->cmd_helper->controller_id_to_atom(bp_params->controller_id, &controller_id)) { + /* Note: VBIOS still wants to use ucCRTC name which is now + * 1 byte in ULONG + *typedef struct _CRTC_PIXEL_CLOCK_FREQ + *{ + * target the pixel clock to drive the CRTC timing. + * ULONG ulPixelClock:24; + * 0 means disable PPLL/DCPLL. Expanded to 24 bits comparing to + * previous version. + * ATOM_CRTC1~6, indicate the CRTC controller to + * ULONG ucCRTC:8; + * drive the pixel clock. not used for DCPLL case. + *}CRTC_PIXEL_CLOCK_FREQ; + *union + *{ + * pixel clock and CRTC id frequency + * CRTC_PIXEL_CLOCK_FREQ ulCrtcPclkFreq; + * ULONG ulDispEngClkFreq; dispclk frequency + *}; + */ + clk.ucCRTC = controller_id; + clk.ucPpll = (uint8_t) pll_id; + clk.ucTransmitterID = bp->cmd_helper->encoder_id_to_atom(dal_graphics_object_id_get_encoder_id(bp_params->encoder_object_id)); + clk.ucEncoderMode = (uint8_t) bp->cmd_helper->encoder_mode_bp_to_atom(bp_params->signal_type, false); + + clk.ulPixelClock = cpu_to_le32(bp_params->target_pixel_clock_100hz); + + clk.ucDeepColorRatio = (uint8_t) bp->cmd_helper->transmitter_color_depth_to_atom(bp_params->color_depth); + + if (bp_params->flags.FORCE_PROGRAMMING_OF_PLL) + clk.ucMiscInfo |= PIXEL_CLOCK_V7_MISC_FORCE_PROG_PPLL; + + if (bp_params->flags.SET_EXTERNAL_REF_DIV_SRC) + clk.ucMiscInfo |= PIXEL_CLOCK_V7_MISC_REF_DIV_SRC; + + if (bp_params->flags.PROGRAM_PHY_PLL_ONLY) + clk.ucMiscInfo |= PIXEL_CLOCK_V7_MISC_PROG_PHYPLL; + + if (bp_params->flags.SUPPORT_YUV_420) + clk.ucMiscInfo |= PIXEL_CLOCK_V7_MISC_YUV420_MODE; + + if (bp_params->flags.SET_XTALIN_REF_SRC) + clk.ucMiscInfo |= PIXEL_CLOCK_V7_MISC_REF_DIV_SRC_XTALIN; + + if (bp_params->flags.SET_GENLOCK_REF_DIV_SRC) + clk.ucMiscInfo |= PIXEL_CLOCK_V7_MISC_REF_DIV_SRC_GENLK; + + if (bp_params->signal_type == SIGNAL_TYPE_DVI_DUAL_LINK) + clk.ucMiscInfo |= PIXEL_CLOCK_V7_MISC_DVI_DUALLINK_EN; + + if (EXEC_BIOS_CMD_TABLE(SetPixelClock, clk)) + result = BP_RESULT_OK; + } + return result; +} + +/******************************************************************************* + ******************************************************************************** + ** + ** ENABLE PIXEL CLOCK SS + ** + ******************************************************************************** + *******************************************************************************/ +static enum bp_result enable_spread_spectrum_on_ppll_v1( + struct bios_parser *bp, + struct bp_spread_spectrum_parameters *bp_params, + bool enable); +static enum bp_result enable_spread_spectrum_on_ppll_v2( + struct bios_parser *bp, + struct bp_spread_spectrum_parameters *bp_params, + bool enable); +static enum bp_result enable_spread_spectrum_on_ppll_v3( + struct bios_parser *bp, + struct bp_spread_spectrum_parameters *bp_params, + bool enable); + +static void init_enable_spread_spectrum_on_ppll(struct bios_parser *bp) +{ + switch (BIOS_CMD_TABLE_PARA_REVISION(EnableSpreadSpectrumOnPPLL)) { + case 1: + bp->cmd_tbl.enable_spread_spectrum_on_ppll = + enable_spread_spectrum_on_ppll_v1; + break; + case 2: + bp->cmd_tbl.enable_spread_spectrum_on_ppll = + enable_spread_spectrum_on_ppll_v2; + break; + case 3: + bp->cmd_tbl.enable_spread_spectrum_on_ppll = + enable_spread_spectrum_on_ppll_v3; + break; + default: + dm_output_to_console("Don't have enable_spread_spectrum_on_ppll for v%d\n", + BIOS_CMD_TABLE_PARA_REVISION(EnableSpreadSpectrumOnPPLL)); + bp->cmd_tbl.enable_spread_spectrum_on_ppll = NULL; + break; + } +} + +static enum bp_result enable_spread_spectrum_on_ppll_v1( + struct bios_parser *bp, + struct bp_spread_spectrum_parameters *bp_params, + bool enable) +{ + enum bp_result result = BP_RESULT_FAILURE; + ENABLE_SPREAD_SPECTRUM_ON_PPLL params; + + memset(¶ms, 0, sizeof(params)); + + if ((enable == true) && (bp_params->percentage > 0)) + params.ucEnable = ATOM_ENABLE; + else + params.ucEnable = ATOM_DISABLE; + + params.usSpreadSpectrumPercentage = + cpu_to_le16((uint16_t)bp_params->percentage); + params.ucSpreadSpectrumStep = + (uint8_t)bp_params->ver1.step; + params.ucSpreadSpectrumDelay = + (uint8_t)bp_params->ver1.delay; + /* convert back to unit of 10KHz */ + params.ucSpreadSpectrumRange = + (uint8_t)(bp_params->ver1.range / 10000); + + if (bp_params->flags.EXTERNAL_SS) + params.ucSpreadSpectrumType |= ATOM_EXTERNAL_SS_MASK; + + if (bp_params->flags.CENTER_SPREAD) + params.ucSpreadSpectrumType |= ATOM_SS_CENTRE_SPREAD_MODE; + + if (bp_params->pll_id == CLOCK_SOURCE_ID_PLL1) + params.ucPpll = ATOM_PPLL1; + else if (bp_params->pll_id == CLOCK_SOURCE_ID_PLL2) + params.ucPpll = ATOM_PPLL2; + else + BREAK_TO_DEBUGGER(); /* Unexpected PLL value!! */ + + if (EXEC_BIOS_CMD_TABLE(EnableSpreadSpectrumOnPPLL, params)) + result = BP_RESULT_OK; + + return result; +} + +static enum bp_result enable_spread_spectrum_on_ppll_v2( + struct bios_parser *bp, + struct bp_spread_spectrum_parameters *bp_params, + bool enable) +{ + enum bp_result result = BP_RESULT_FAILURE; + ENABLE_SPREAD_SPECTRUM_ON_PPLL_V2 params; + + memset(¶ms, 0, sizeof(params)); + + if (bp_params->pll_id == CLOCK_SOURCE_ID_PLL1) + params.ucSpreadSpectrumType = ATOM_PPLL_SS_TYPE_V2_P1PLL; + else if (bp_params->pll_id == CLOCK_SOURCE_ID_PLL2) + params.ucSpreadSpectrumType = ATOM_PPLL_SS_TYPE_V2_P2PLL; + else + BREAK_TO_DEBUGGER(); /* Unexpected PLL value!! */ + + if ((enable == true) && (bp_params->percentage > 0)) { + params.ucEnable = ATOM_ENABLE; + + params.usSpreadSpectrumPercentage = + cpu_to_le16((uint16_t)(bp_params->percentage)); + params.usSpreadSpectrumStep = + cpu_to_le16((uint16_t)(bp_params->ds.ds_frac_size)); + + if (bp_params->flags.EXTERNAL_SS) + params.ucSpreadSpectrumType |= + ATOM_PPLL_SS_TYPE_V2_EXT_SPREAD; + + if (bp_params->flags.CENTER_SPREAD) + params.ucSpreadSpectrumType |= + ATOM_PPLL_SS_TYPE_V2_CENTRE_SPREAD; + + /* Both amounts need to be left shifted first before bit + * comparison. Otherwise, the result will always be zero here + */ + params.usSpreadSpectrumAmount = cpu_to_le16((uint16_t)( + ((bp_params->ds.feedback_amount << + ATOM_PPLL_SS_AMOUNT_V2_FBDIV_SHIFT) & + ATOM_PPLL_SS_AMOUNT_V2_FBDIV_MASK) | + ((bp_params->ds.nfrac_amount << + ATOM_PPLL_SS_AMOUNT_V2_NFRAC_SHIFT) & + ATOM_PPLL_SS_AMOUNT_V2_NFRAC_MASK))); + } else + params.ucEnable = ATOM_DISABLE; + + if (EXEC_BIOS_CMD_TABLE(EnableSpreadSpectrumOnPPLL, params)) + result = BP_RESULT_OK; + + return result; +} + +static enum bp_result enable_spread_spectrum_on_ppll_v3( + struct bios_parser *bp, + struct bp_spread_spectrum_parameters *bp_params, + bool enable) +{ + enum bp_result result = BP_RESULT_FAILURE; + ENABLE_SPREAD_SPECTRUM_ON_PPLL_V3 params; + + memset(¶ms, 0, sizeof(params)); + + switch (bp_params->pll_id) { + case CLOCK_SOURCE_ID_PLL0: + /* ATOM_PPLL_SS_TYPE_V3_P0PLL; this is pixel clock only, + * not for SI display clock. + */ + params.ucSpreadSpectrumType = ATOM_PPLL_SS_TYPE_V3_DCPLL; + break; + case CLOCK_SOURCE_ID_PLL1: + params.ucSpreadSpectrumType = ATOM_PPLL_SS_TYPE_V3_P1PLL; + break; + + case CLOCK_SOURCE_ID_PLL2: + params.ucSpreadSpectrumType = ATOM_PPLL_SS_TYPE_V3_P2PLL; + break; + + case CLOCK_SOURCE_ID_DCPLL: + params.ucSpreadSpectrumType = ATOM_PPLL_SS_TYPE_V3_DCPLL; + break; + + default: + BREAK_TO_DEBUGGER(); + /* Unexpected PLL value!! */ + return result; + } + + if (enable == true) { + params.ucEnable = ATOM_ENABLE; + + params.usSpreadSpectrumAmountFrac = + cpu_to_le16((uint16_t)(bp_params->ds_frac_amount)); + params.usSpreadSpectrumStep = + cpu_to_le16((uint16_t)(bp_params->ds.ds_frac_size)); + + if (bp_params->flags.EXTERNAL_SS) + params.ucSpreadSpectrumType |= + ATOM_PPLL_SS_TYPE_V3_EXT_SPREAD; + if (bp_params->flags.CENTER_SPREAD) + params.ucSpreadSpectrumType |= + ATOM_PPLL_SS_TYPE_V3_CENTRE_SPREAD; + + /* Both amounts need to be left shifted first before bit + * comparison. Otherwise, the result will always be zero here + */ + params.usSpreadSpectrumAmount = cpu_to_le16((uint16_t)( + ((bp_params->ds.feedback_amount << + ATOM_PPLL_SS_AMOUNT_V3_FBDIV_SHIFT) & + ATOM_PPLL_SS_AMOUNT_V3_FBDIV_MASK) | + ((bp_params->ds.nfrac_amount << + ATOM_PPLL_SS_AMOUNT_V3_NFRAC_SHIFT) & + ATOM_PPLL_SS_AMOUNT_V3_NFRAC_MASK))); + } else + params.ucEnable = ATOM_DISABLE; + + if (EXEC_BIOS_CMD_TABLE(EnableSpreadSpectrumOnPPLL, params)) + result = BP_RESULT_OK; + + return result; +} + +/******************************************************************************* + ******************************************************************************** + ** + ** ADJUST DISPLAY PLL + ** + ******************************************************************************** + *******************************************************************************/ + +static enum bp_result adjust_display_pll_v2( + struct bios_parser *bp, + struct bp_adjust_pixel_clock_parameters *bp_params); +static enum bp_result adjust_display_pll_v3( + struct bios_parser *bp, + struct bp_adjust_pixel_clock_parameters *bp_params); + +static void init_adjust_display_pll(struct bios_parser *bp) +{ + switch (BIOS_CMD_TABLE_PARA_REVISION(AdjustDisplayPll)) { + case 2: + bp->cmd_tbl.adjust_display_pll = adjust_display_pll_v2; + break; + case 3: + bp->cmd_tbl.adjust_display_pll = adjust_display_pll_v3; + break; + default: + dm_output_to_console("Don't have adjust_display_pll for v%d\n", + BIOS_CMD_TABLE_PARA_REVISION(AdjustDisplayPll)); + bp->cmd_tbl.adjust_display_pll = NULL; + break; + } +} + +static enum bp_result adjust_display_pll_v2( + struct bios_parser *bp, + struct bp_adjust_pixel_clock_parameters *bp_params) +{ + enum bp_result result = BP_RESULT_FAILURE; + ADJUST_DISPLAY_PLL_PS_ALLOCATION params = { 0 }; + + /* We need to convert from KHz units into 10KHz units and then convert + * output pixel clock back 10KHz-->KHz */ + uint32_t pixel_clock_10KHz_in = bp_params->pixel_clock / 10; + + params.usPixelClock = cpu_to_le16((uint16_t)(pixel_clock_10KHz_in)); + params.ucTransmitterID = + bp->cmd_helper->encoder_id_to_atom( + dal_graphics_object_id_get_encoder_id( + bp_params->encoder_object_id)); + params.ucEncodeMode = + (uint8_t)bp->cmd_helper->encoder_mode_bp_to_atom( + bp_params->signal_type, false); + + if (EXEC_BIOS_CMD_TABLE(AdjustDisplayPll, params)) { + /* Convert output pixel clock back 10KHz-->KHz: multiply + * original pixel clock in KHz by ratio + * [output pxlClk/input pxlClk] */ + uint64_t pixel_clk_10_khz_out = + (uint64_t)le16_to_cpu(params.usPixelClock); + uint64_t pixel_clk = (uint64_t)bp_params->pixel_clock; + + if (pixel_clock_10KHz_in != 0) { + bp_params->adjusted_pixel_clock = + div_u64(pixel_clk * pixel_clk_10_khz_out, + pixel_clock_10KHz_in); + } else { + bp_params->adjusted_pixel_clock = 0; + BREAK_TO_DEBUGGER(); + } + + result = BP_RESULT_OK; + } + + return result; +} + +static enum bp_result adjust_display_pll_v3( + struct bios_parser *bp, + struct bp_adjust_pixel_clock_parameters *bp_params) +{ + enum bp_result result = BP_RESULT_FAILURE; + ADJUST_DISPLAY_PLL_PS_ALLOCATION_V3 params; + uint32_t pixel_clk_10_kHz_in = bp_params->pixel_clock / 10; + + memset(¶ms, 0, sizeof(params)); + + /* We need to convert from KHz units into 10KHz units and then convert + * output pixel clock back 10KHz-->KHz */ + params.sInput.usPixelClock = cpu_to_le16((uint16_t)pixel_clk_10_kHz_in); + params.sInput.ucTransmitterID = + bp->cmd_helper->encoder_id_to_atom( + dal_graphics_object_id_get_encoder_id( + bp_params->encoder_object_id)); + params.sInput.ucEncodeMode = + (uint8_t)bp->cmd_helper->encoder_mode_bp_to_atom( + bp_params->signal_type, false); + + if (bp_params->ss_enable == true) + params.sInput.ucDispPllConfig |= DISPPLL_CONFIG_SS_ENABLE; + + if (bp_params->signal_type == SIGNAL_TYPE_DVI_DUAL_LINK) + params.sInput.ucDispPllConfig |= DISPPLL_CONFIG_DUAL_LINK; + + if (EXEC_BIOS_CMD_TABLE(AdjustDisplayPll, params)) { + /* Convert output pixel clock back 10KHz-->KHz: multiply + * original pixel clock in KHz by ratio + * [output pxlClk/input pxlClk] */ + uint64_t pixel_clk_10_khz_out = + (uint64_t)le32_to_cpu(params.sOutput.ulDispPllFreq); + uint64_t pixel_clk = (uint64_t)bp_params->pixel_clock; + + if (pixel_clk_10_kHz_in != 0) { + bp_params->adjusted_pixel_clock = + div_u64(pixel_clk * pixel_clk_10_khz_out, + pixel_clk_10_kHz_in); + } else { + bp_params->adjusted_pixel_clock = 0; + BREAK_TO_DEBUGGER(); + } + + bp_params->reference_divider = params.sOutput.ucRefDiv; + bp_params->pixel_clock_post_divider = params.sOutput.ucPostDiv; + + result = BP_RESULT_OK; + } + + return result; +} + +/******************************************************************************* + ******************************************************************************** + ** + ** DAC ENCODER CONTROL + ** + ******************************************************************************** + *******************************************************************************/ + +static enum bp_result dac1_encoder_control_v1( + struct bios_parser *bp, + bool enable, + uint32_t pixel_clock, + uint8_t dac_standard); +static enum bp_result dac2_encoder_control_v1( + struct bios_parser *bp, + bool enable, + uint32_t pixel_clock, + uint8_t dac_standard); + +static void init_dac_encoder_control(struct bios_parser *bp) +{ + switch (BIOS_CMD_TABLE_PARA_REVISION(DAC1EncoderControl)) { + case 1: + bp->cmd_tbl.dac1_encoder_control = dac1_encoder_control_v1; + break; + default: + bp->cmd_tbl.dac1_encoder_control = NULL; + break; + } + switch (BIOS_CMD_TABLE_PARA_REVISION(DAC2EncoderControl)) { + case 1: + bp->cmd_tbl.dac2_encoder_control = dac2_encoder_control_v1; + break; + default: + bp->cmd_tbl.dac2_encoder_control = NULL; + break; + } +} + +static void dac_encoder_control_prepare_params( + DAC_ENCODER_CONTROL_PS_ALLOCATION *params, + bool enable, + uint32_t pixel_clock, + uint8_t dac_standard) +{ + params->ucDacStandard = dac_standard; + if (enable) + params->ucAction = ATOM_ENABLE; + else + params->ucAction = ATOM_DISABLE; + + /* We need to convert from KHz units into 10KHz units + * it looks as if the TvControl do not care about pixel clock + */ + params->usPixelClock = cpu_to_le16((uint16_t)(pixel_clock / 10)); +} + +static enum bp_result dac1_encoder_control_v1( + struct bios_parser *bp, + bool enable, + uint32_t pixel_clock, + uint8_t dac_standard) +{ + enum bp_result result = BP_RESULT_FAILURE; + DAC_ENCODER_CONTROL_PS_ALLOCATION params; + + dac_encoder_control_prepare_params( + ¶ms, + enable, + pixel_clock, + dac_standard); + + if (EXEC_BIOS_CMD_TABLE(DAC1EncoderControl, params)) + result = BP_RESULT_OK; + + return result; +} + +static enum bp_result dac2_encoder_control_v1( + struct bios_parser *bp, + bool enable, + uint32_t pixel_clock, + uint8_t dac_standard) +{ + enum bp_result result = BP_RESULT_FAILURE; + DAC_ENCODER_CONTROL_PS_ALLOCATION params; + + dac_encoder_control_prepare_params( + ¶ms, + enable, + pixel_clock, + dac_standard); + + if (EXEC_BIOS_CMD_TABLE(DAC2EncoderControl, params)) + result = BP_RESULT_OK; + + return result; +} + +/******************************************************************************* + ******************************************************************************** + ** + ** DAC OUTPUT CONTROL + ** + ******************************************************************************** + *******************************************************************************/ +static enum bp_result dac1_output_control_v1( + struct bios_parser *bp, + bool enable); +static enum bp_result dac2_output_control_v1( + struct bios_parser *bp, + bool enable); + +static void init_dac_output_control(struct bios_parser *bp) +{ + switch (BIOS_CMD_TABLE_PARA_REVISION(DAC1OutputControl)) { + case 1: + bp->cmd_tbl.dac1_output_control = dac1_output_control_v1; + break; + default: + bp->cmd_tbl.dac1_output_control = NULL; + break; + } + switch (BIOS_CMD_TABLE_PARA_REVISION(DAC2OutputControl)) { + case 1: + bp->cmd_tbl.dac2_output_control = dac2_output_control_v1; + break; + default: + bp->cmd_tbl.dac2_output_control = NULL; + break; + } +} + +static enum bp_result dac1_output_control_v1( + struct bios_parser *bp, bool enable) +{ + enum bp_result result = BP_RESULT_FAILURE; + DISPLAY_DEVICE_OUTPUT_CONTROL_PS_ALLOCATION params; + + if (enable) + params.ucAction = ATOM_ENABLE; + else + params.ucAction = ATOM_DISABLE; + + if (EXEC_BIOS_CMD_TABLE(DAC1OutputControl, params)) + result = BP_RESULT_OK; + + return result; +} + +static enum bp_result dac2_output_control_v1( + struct bios_parser *bp, bool enable) +{ + enum bp_result result = BP_RESULT_FAILURE; + DISPLAY_DEVICE_OUTPUT_CONTROL_PS_ALLOCATION params; + + if (enable) + params.ucAction = ATOM_ENABLE; + else + params.ucAction = ATOM_DISABLE; + + if (EXEC_BIOS_CMD_TABLE(DAC2OutputControl, params)) + result = BP_RESULT_OK; + + return result; +} + +/******************************************************************************* + ******************************************************************************** + ** + ** SET CRTC TIMING + ** + ******************************************************************************** + *******************************************************************************/ + +static enum bp_result set_crtc_using_dtd_timing_v3( + struct bios_parser *bp, + struct bp_hw_crtc_timing_parameters *bp_params); +static enum bp_result set_crtc_timing_v1( + struct bios_parser *bp, + struct bp_hw_crtc_timing_parameters *bp_params); + +static void init_set_crtc_timing(struct bios_parser *bp) +{ + uint32_t dtd_version = + BIOS_CMD_TABLE_PARA_REVISION(SetCRTC_UsingDTDTiming); + if (dtd_version > 2) + switch (dtd_version) { + case 3: + bp->cmd_tbl.set_crtc_timing = + set_crtc_using_dtd_timing_v3; + break; + default: + dm_output_to_console("Don't have set_crtc_timing for dtd v%d\n", + dtd_version); + bp->cmd_tbl.set_crtc_timing = NULL; + break; + } + else + switch (BIOS_CMD_TABLE_PARA_REVISION(SetCRTC_Timing)) { + case 1: + bp->cmd_tbl.set_crtc_timing = set_crtc_timing_v1; + break; + default: + dm_output_to_console("Don't have set_crtc_timing for v%d\n", + BIOS_CMD_TABLE_PARA_REVISION(SetCRTC_Timing)); + bp->cmd_tbl.set_crtc_timing = NULL; + break; + } +} + +static enum bp_result set_crtc_timing_v1( + struct bios_parser *bp, + struct bp_hw_crtc_timing_parameters *bp_params) +{ + enum bp_result result = BP_RESULT_FAILURE; + SET_CRTC_TIMING_PARAMETERS_PS_ALLOCATION params = {0}; + uint8_t atom_controller_id; + + if (bp->cmd_helper->controller_id_to_atom( + bp_params->controller_id, &atom_controller_id)) + params.ucCRTC = atom_controller_id; + + params.usH_Total = cpu_to_le16((uint16_t)(bp_params->h_total)); + params.usH_Disp = cpu_to_le16((uint16_t)(bp_params->h_addressable)); + params.usH_SyncStart = cpu_to_le16((uint16_t)(bp_params->h_sync_start)); + params.usH_SyncWidth = cpu_to_le16((uint16_t)(bp_params->h_sync_width)); + params.usV_Total = cpu_to_le16((uint16_t)(bp_params->v_total)); + params.usV_Disp = cpu_to_le16((uint16_t)(bp_params->v_addressable)); + params.usV_SyncStart = + cpu_to_le16((uint16_t)(bp_params->v_sync_start)); + params.usV_SyncWidth = + cpu_to_le16((uint16_t)(bp_params->v_sync_width)); + + /* VBIOS does not expect any value except zero into this call, for + * underscan use another entry ProgramOverscan call but when mode + * 1776x1000 with the overscan 72x44 .e.i. 1920x1080 @30 DAL2 is ok, + * but when same ,but 60 Hz there is corruption + * DAL1 does not allow the mode 1776x1000@60 + */ + params.ucOverscanRight = (uint8_t)bp_params->h_overscan_right; + params.ucOverscanLeft = (uint8_t)bp_params->h_overscan_left; + params.ucOverscanBottom = (uint8_t)bp_params->v_overscan_bottom; + params.ucOverscanTop = (uint8_t)bp_params->v_overscan_top; + + if (0 == bp_params->flags.HSYNC_POSITIVE_POLARITY) + params.susModeMiscInfo.usAccess = + cpu_to_le16(le16_to_cpu(params.susModeMiscInfo.usAccess) | ATOM_HSYNC_POLARITY); + + if (0 == bp_params->flags.VSYNC_POSITIVE_POLARITY) + params.susModeMiscInfo.usAccess = + cpu_to_le16(le16_to_cpu(params.susModeMiscInfo.usAccess) | ATOM_VSYNC_POLARITY); + + if (bp_params->flags.INTERLACE) { + params.susModeMiscInfo.usAccess = + cpu_to_le16(le16_to_cpu(params.susModeMiscInfo.usAccess) | ATOM_INTERLACE); + + /* original DAL code has this condition to apply tis for + * non-TV/CV only due to complex MV testing for possible + * impact + * if (pACParameters->signal != SignalType_YPbPr && + * pACParameters->signal != SignalType_Composite && + * pACParameters->signal != SignalType_SVideo) + */ + /* HW will deduct 0.5 line from 2nd feild. + * i.e. for 1080i, it is 2 lines for 1st field, 2.5 + * lines for the 2nd feild. we need input as 5 instead + * of 4, but it is 4 either from Edid data + * (spec CEA 861) or CEA timing table. + */ + params.usV_SyncStart = + cpu_to_le16((uint16_t)(bp_params->v_sync_start + 1)); + } + + if (bp_params->flags.HORZ_COUNT_BY_TWO) + params.susModeMiscInfo.usAccess = + cpu_to_le16(le16_to_cpu(params.susModeMiscInfo.usAccess) | ATOM_DOUBLE_CLOCK_MODE); + + if (EXEC_BIOS_CMD_TABLE(SetCRTC_Timing, params)) + result = BP_RESULT_OK; + + return result; +} + +static enum bp_result set_crtc_using_dtd_timing_v3( + struct bios_parser *bp, + struct bp_hw_crtc_timing_parameters *bp_params) +{ + enum bp_result result = BP_RESULT_FAILURE; + SET_CRTC_USING_DTD_TIMING_PARAMETERS params = {0}; + uint8_t atom_controller_id; + + if (bp->cmd_helper->controller_id_to_atom( + bp_params->controller_id, &atom_controller_id)) + params.ucCRTC = atom_controller_id; + + /* bios usH_Size wants h addressable size */ + params.usH_Size = cpu_to_le16((uint16_t)bp_params->h_addressable); + /* bios usH_Blanking_Time wants borders included in blanking */ + params.usH_Blanking_Time = + cpu_to_le16((uint16_t)(bp_params->h_total - bp_params->h_addressable)); + /* bios usV_Size wants v addressable size */ + params.usV_Size = cpu_to_le16((uint16_t)bp_params->v_addressable); + /* bios usV_Blanking_Time wants borders included in blanking */ + params.usV_Blanking_Time = + cpu_to_le16((uint16_t)(bp_params->v_total - bp_params->v_addressable)); + /* bios usHSyncOffset is the offset from the end of h addressable, + * our horizontalSyncStart is the offset from the beginning + * of h addressable */ + params.usH_SyncOffset = + cpu_to_le16((uint16_t)(bp_params->h_sync_start - bp_params->h_addressable)); + params.usH_SyncWidth = cpu_to_le16((uint16_t)bp_params->h_sync_width); + /* bios usHSyncOffset is the offset from the end of v addressable, + * our verticalSyncStart is the offset from the beginning of + * v addressable */ + params.usV_SyncOffset = + cpu_to_le16((uint16_t)(bp_params->v_sync_start - bp_params->v_addressable)); + params.usV_SyncWidth = cpu_to_le16((uint16_t)bp_params->v_sync_width); + + /* we assume that overscan from original timing does not get bigger + * than 255 + * we will program all the borders in the Set CRTC Overscan call below + */ + + if (0 == bp_params->flags.HSYNC_POSITIVE_POLARITY) + params.susModeMiscInfo.usAccess = + cpu_to_le16(le16_to_cpu(params.susModeMiscInfo.usAccess) | ATOM_HSYNC_POLARITY); + + if (0 == bp_params->flags.VSYNC_POSITIVE_POLARITY) + params.susModeMiscInfo.usAccess = + cpu_to_le16(le16_to_cpu(params.susModeMiscInfo.usAccess) | ATOM_VSYNC_POLARITY); + + if (bp_params->flags.INTERLACE) { + params.susModeMiscInfo.usAccess = + cpu_to_le16(le16_to_cpu(params.susModeMiscInfo.usAccess) | ATOM_INTERLACE); + + /* original DAL code has this condition to apply this + * for non-TV/CV only + * due to complex MV testing for possible impact + * if ( pACParameters->signal != SignalType_YPbPr && + * pACParameters->signal != SignalType_Composite && + * pACParameters->signal != SignalType_SVideo) + */ + { + /* HW will deduct 0.5 line from 2nd feild. + * i.e. for 1080i, it is 2 lines for 1st field, + * 2.5 lines for the 2nd feild. we need input as 5 + * instead of 4. + * but it is 4 either from Edid data (spec CEA 861) + * or CEA timing table. + */ + le16_add_cpu(¶ms.usV_SyncOffset, 1); + } + } + + if (bp_params->flags.HORZ_COUNT_BY_TWO) + params.susModeMiscInfo.usAccess = + cpu_to_le16(le16_to_cpu(params.susModeMiscInfo.usAccess) | ATOM_DOUBLE_CLOCK_MODE); + + if (EXEC_BIOS_CMD_TABLE(SetCRTC_UsingDTDTiming, params)) + result = BP_RESULT_OK; + + return result; +} + +/******************************************************************************* + ******************************************************************************** + ** + ** ENABLE CRTC + ** + ******************************************************************************** + *******************************************************************************/ + +static enum bp_result enable_crtc_v1( + struct bios_parser *bp, + enum controller_id controller_id, + bool enable); + +static void init_enable_crtc(struct bios_parser *bp) +{ + switch (BIOS_CMD_TABLE_PARA_REVISION(EnableCRTC)) { + case 1: + bp->cmd_tbl.enable_crtc = enable_crtc_v1; + break; + default: + dm_output_to_console("Don't have enable_crtc for v%d\n", + BIOS_CMD_TABLE_PARA_REVISION(EnableCRTC)); + bp->cmd_tbl.enable_crtc = NULL; + break; + } +} + +static enum bp_result enable_crtc_v1( + struct bios_parser *bp, + enum controller_id controller_id, + bool enable) +{ + bool result = BP_RESULT_FAILURE; + ENABLE_CRTC_PARAMETERS params = {0}; + uint8_t id; + + if (bp->cmd_helper->controller_id_to_atom(controller_id, &id)) + params.ucCRTC = id; + else + return BP_RESULT_BADINPUT; + + if (enable) + params.ucEnable = ATOM_ENABLE; + else + params.ucEnable = ATOM_DISABLE; + + if (EXEC_BIOS_CMD_TABLE(EnableCRTC, params)) + result = BP_RESULT_OK; + + return result; +} + +/******************************************************************************* + ******************************************************************************** + ** + ** ENABLE CRTC MEM REQ + ** + ******************************************************************************** + *******************************************************************************/ + +static enum bp_result enable_crtc_mem_req_v1( + struct bios_parser *bp, + enum controller_id controller_id, + bool enable); + +static void init_enable_crtc_mem_req(struct bios_parser *bp) +{ + switch (BIOS_CMD_TABLE_PARA_REVISION(EnableCRTCMemReq)) { + case 1: + bp->cmd_tbl.enable_crtc_mem_req = enable_crtc_mem_req_v1; + break; + default: + bp->cmd_tbl.enable_crtc_mem_req = NULL; + break; + } +} + +static enum bp_result enable_crtc_mem_req_v1( + struct bios_parser *bp, + enum controller_id controller_id, + bool enable) +{ + bool result = BP_RESULT_BADINPUT; + ENABLE_CRTC_PARAMETERS params = {0}; + uint8_t id; + + if (bp->cmd_helper->controller_id_to_atom(controller_id, &id)) { + params.ucCRTC = id; + + if (enable) + params.ucEnable = ATOM_ENABLE; + else + params.ucEnable = ATOM_DISABLE; + + if (EXEC_BIOS_CMD_TABLE(EnableCRTCMemReq, params)) + result = BP_RESULT_OK; + else + result = BP_RESULT_FAILURE; + } + + return result; +} + +/******************************************************************************* + ******************************************************************************** + ** + ** DISPLAY PLL + ** + ******************************************************************************** + *******************************************************************************/ + +static enum bp_result program_clock_v5( + struct bios_parser *bp, + struct bp_pixel_clock_parameters *bp_params); +static enum bp_result program_clock_v6( + struct bios_parser *bp, + struct bp_pixel_clock_parameters *bp_params); + +static void init_program_clock(struct bios_parser *bp) +{ + switch (BIOS_CMD_TABLE_PARA_REVISION(SetPixelClock)) { + case 5: + bp->cmd_tbl.program_clock = program_clock_v5; + break; + case 6: + bp->cmd_tbl.program_clock = program_clock_v6; + break; + default: + dm_output_to_console("Don't have program_clock for v%d\n", + BIOS_CMD_TABLE_PARA_REVISION(SetPixelClock)); + bp->cmd_tbl.program_clock = NULL; + break; + } +} + +static enum bp_result program_clock_v5( + struct bios_parser *bp, + struct bp_pixel_clock_parameters *bp_params) +{ + enum bp_result result = BP_RESULT_FAILURE; + + SET_PIXEL_CLOCK_PS_ALLOCATION_V5 params; + uint32_t atom_pll_id; + + memset(¶ms, 0, sizeof(params)); + if (!bp->cmd_helper->clock_source_id_to_atom( + bp_params->pll_id, &atom_pll_id)) { + BREAK_TO_DEBUGGER(); /* Invalid Input!! */ + return BP_RESULT_BADINPUT; + } + + /* We need to convert from KHz units into 10KHz units */ + params.sPCLKInput.ucPpll = (uint8_t) atom_pll_id; + params.sPCLKInput.usPixelClock = + cpu_to_le16((uint16_t) (bp_params->target_pixel_clock_100hz / 100)); + params.sPCLKInput.ucCRTC = (uint8_t) ATOM_CRTC_INVALID; + + if (bp_params->flags.SET_EXTERNAL_REF_DIV_SRC) + params.sPCLKInput.ucMiscInfo |= PIXEL_CLOCK_MISC_REF_DIV_SRC; + + if (EXEC_BIOS_CMD_TABLE(SetPixelClock, params)) + result = BP_RESULT_OK; + + return result; +} + +static enum bp_result program_clock_v6( + struct bios_parser *bp, + struct bp_pixel_clock_parameters *bp_params) +{ + enum bp_result result = BP_RESULT_FAILURE; + + SET_PIXEL_CLOCK_PS_ALLOCATION_V6 params; + uint32_t atom_pll_id; + + memset(¶ms, 0, sizeof(params)); + + if (!bp->cmd_helper->clock_source_id_to_atom( + bp_params->pll_id, &atom_pll_id)) { + BREAK_TO_DEBUGGER(); /*Invalid Input!!*/ + return BP_RESULT_BADINPUT; + } + + /* We need to convert from KHz units into 10KHz units */ + params.sPCLKInput.ucPpll = (uint8_t)atom_pll_id; + params.sPCLKInput.ulDispEngClkFreq = + cpu_to_le32(bp_params->target_pixel_clock_100hz / 100); + + if (bp_params->flags.SET_EXTERNAL_REF_DIV_SRC) + params.sPCLKInput.ucMiscInfo |= PIXEL_CLOCK_MISC_REF_DIV_SRC; + + if (bp_params->flags.SET_DISPCLK_DFS_BYPASS) + params.sPCLKInput.ucMiscInfo |= PIXEL_CLOCK_V6_MISC_DPREFCLK_BYPASS; + + if (EXEC_BIOS_CMD_TABLE(SetPixelClock, params)) { + /* True display clock is returned by VBIOS if DFS bypass + * is enabled. */ + bp_params->dfs_bypass_display_clock = + (uint32_t)(le32_to_cpu(params.sPCLKInput.ulDispEngClkFreq) * 10); + result = BP_RESULT_OK; + } + + return result; +} + +/******************************************************************************* + ******************************************************************************** + ** + ** EXTERNAL ENCODER CONTROL + ** + ******************************************************************************** + *******************************************************************************/ + +static enum bp_result external_encoder_control_v3( + struct bios_parser *bp, + struct bp_external_encoder_control *cntl); + +static void init_external_encoder_control( + struct bios_parser *bp) +{ + switch (BIOS_CMD_TABLE_PARA_REVISION(ExternalEncoderControl)) { + case 3: + bp->cmd_tbl.external_encoder_control = + external_encoder_control_v3; + break; + default: + bp->cmd_tbl.external_encoder_control = NULL; + break; + } +} + +static enum bp_result external_encoder_control_v3( + struct bios_parser *bp, + struct bp_external_encoder_control *cntl) +{ + enum bp_result result = BP_RESULT_FAILURE; + + /* we need use _PS_Alloc struct */ + EXTERNAL_ENCODER_CONTROL_PS_ALLOCATION_V3 params; + EXTERNAL_ENCODER_CONTROL_PARAMETERS_V3 *cntl_params; + struct graphics_object_id encoder; + bool is_input_signal_dp = false; + + memset(¶ms, 0, sizeof(params)); + + cntl_params = ¶ms.sExtEncoder; + + encoder = cntl->encoder_id; + + /* check if encoder supports external encoder control table */ + switch (dal_graphics_object_id_get_encoder_id(encoder)) { + case ENCODER_ID_EXTERNAL_NUTMEG: + case ENCODER_ID_EXTERNAL_TRAVIS: + is_input_signal_dp = true; + break; + + default: + BREAK_TO_DEBUGGER(); + return BP_RESULT_BADINPUT; + } + + /* Fill information based on the action + * + * Bit[6:4]: indicate external encoder, applied to all functions. + * =0: external encoder1, mapped to external encoder enum id1 + * =1: external encoder2, mapped to external encoder enum id2 + * + * enum ObjectEnumId + * { + * EnumId_Unknown = 0, + * EnumId_1, + * EnumId_2, + * }; + */ + cntl_params->ucConfig = (uint8_t)((encoder.enum_id - 1) << 4); + + switch (cntl->action) { + case EXTERNAL_ENCODER_CONTROL_INIT: + /* output display connector type. Only valid in encoder + * initialization */ + cntl_params->usConnectorId = + cpu_to_le16((uint16_t)cntl->connector_obj_id.id); + break; + case EXTERNAL_ENCODER_CONTROL_SETUP: + /* EXTERNAL_ENCODER_CONTROL_PARAMETERS_V3 pixel clock unit in + * 10KHz + * output display device pixel clock frequency in unit of 10KHz. + * Only valid in setup and enableoutput + */ + cntl_params->usPixelClock = + cpu_to_le16((uint16_t)(cntl->pixel_clock / 10)); + /* Indicate display output signal type drive by external + * encoder, only valid in setup and enableoutput */ + cntl_params->ucEncoderMode = + (uint8_t)bp->cmd_helper->encoder_mode_bp_to_atom( + cntl->signal, false); + + if (is_input_signal_dp) { + /* Bit[0]: indicate link rate, =1: 2.7Ghz, =0: 1.62Ghz, + * only valid in encoder setup with DP mode. */ + if (LINK_RATE_HIGH == cntl->link_rate) + cntl_params->ucConfig |= 1; + /* output color depth Indicate encoder data bpc format + * in DP mode, only valid in encoder setup in DP mode. + */ + cntl_params->ucBitPerColor = + (uint8_t)(cntl->color_depth); + } + /* Indicate how many lanes used by external encoder, only valid + * in encoder setup and enableoutput. */ + cntl_params->ucLaneNum = (uint8_t)(cntl->lanes_number); + break; + case EXTERNAL_ENCODER_CONTROL_ENABLE: + cntl_params->usPixelClock = + cpu_to_le16((uint16_t)(cntl->pixel_clock / 10)); + cntl_params->ucEncoderMode = + (uint8_t)bp->cmd_helper->encoder_mode_bp_to_atom( + cntl->signal, false); + cntl_params->ucLaneNum = (uint8_t)cntl->lanes_number; + break; + default: + break; + } + + cntl_params->ucAction = (uint8_t)cntl->action; + + if (EXEC_BIOS_CMD_TABLE(ExternalEncoderControl, params)) + result = BP_RESULT_OK; + + return result; +} + +/******************************************************************************* + ******************************************************************************** + ** + ** ENABLE DISPLAY POWER GATING + ** + ******************************************************************************** + *******************************************************************************/ + +static enum bp_result enable_disp_power_gating_v2_1( + struct bios_parser *bp, + enum controller_id crtc_id, + enum bp_pipe_control_action action); + +static void init_enable_disp_power_gating( + struct bios_parser *bp) +{ + switch (BIOS_CMD_TABLE_PARA_REVISION(EnableDispPowerGating)) { + case 1: + bp->cmd_tbl.enable_disp_power_gating = + enable_disp_power_gating_v2_1; + break; + default: + dm_output_to_console("Don't enable_disp_power_gating enable_crtc for v%d\n", + BIOS_CMD_TABLE_PARA_REVISION(EnableDispPowerGating)); + bp->cmd_tbl.enable_disp_power_gating = NULL; + break; + } +} + +static enum bp_result enable_disp_power_gating_v2_1( + struct bios_parser *bp, + enum controller_id crtc_id, + enum bp_pipe_control_action action) +{ + enum bp_result result = BP_RESULT_FAILURE; + + ENABLE_DISP_POWER_GATING_PS_ALLOCATION params = {0}; + uint8_t atom_crtc_id; + + if (bp->cmd_helper->controller_id_to_atom(crtc_id, &atom_crtc_id)) + params.ucDispPipeId = atom_crtc_id; + else + return BP_RESULT_BADINPUT; + + params.ucEnable = + bp->cmd_helper->disp_power_gating_action_to_atom(action); + + if (EXEC_BIOS_CMD_TABLE(EnableDispPowerGating, params)) + result = BP_RESULT_OK; + + return result; +} + +/******************************************************************************* + ******************************************************************************** + ** + ** SET DCE CLOCK + ** + ******************************************************************************** + *******************************************************************************/ +static enum bp_result set_dce_clock_v2_1( + struct bios_parser *bp, + struct bp_set_dce_clock_parameters *bp_params); + +static void init_set_dce_clock(struct bios_parser *bp) +{ + switch (BIOS_CMD_TABLE_PARA_REVISION(SetDCEClock)) { + case 1: + bp->cmd_tbl.set_dce_clock = set_dce_clock_v2_1; + break; + default: + dm_output_to_console("Don't have set_dce_clock for v%d\n", + BIOS_CMD_TABLE_PARA_REVISION(SetDCEClock)); + bp->cmd_tbl.set_dce_clock = NULL; + break; + } +} + +static enum bp_result set_dce_clock_v2_1( + struct bios_parser *bp, + struct bp_set_dce_clock_parameters *bp_params) +{ + enum bp_result result = BP_RESULT_FAILURE; + + SET_DCE_CLOCK_PS_ALLOCATION_V2_1 params; + uint32_t atom_pll_id; + uint32_t atom_clock_type; + const struct command_table_helper *cmd = bp->cmd_helper; + + memset(¶ms, 0, sizeof(params)); + + if (!cmd->clock_source_id_to_atom(bp_params->pll_id, &atom_pll_id) || + !cmd->dc_clock_type_to_atom(bp_params->clock_type, &atom_clock_type)) + return BP_RESULT_BADINPUT; + + params.asParam.ucDCEClkSrc = atom_pll_id; + params.asParam.ucDCEClkType = atom_clock_type; + + if (bp_params->clock_type == DCECLOCK_TYPE_DPREFCLK) { + if (bp_params->flags.USE_GENLOCK_AS_SOURCE_FOR_DPREFCLK) + params.asParam.ucDCEClkFlag |= DCE_CLOCK_FLAG_PLL_REFCLK_SRC_GENLK; + + if (bp_params->flags.USE_PCIE_AS_SOURCE_FOR_DPREFCLK) + params.asParam.ucDCEClkFlag |= DCE_CLOCK_FLAG_PLL_REFCLK_SRC_PCIE; + + if (bp_params->flags.USE_XTALIN_AS_SOURCE_FOR_DPREFCLK) + params.asParam.ucDCEClkFlag |= DCE_CLOCK_FLAG_PLL_REFCLK_SRC_XTALIN; + + if (bp_params->flags.USE_GENERICA_AS_SOURCE_FOR_DPREFCLK) + params.asParam.ucDCEClkFlag |= DCE_CLOCK_FLAG_PLL_REFCLK_SRC_GENERICA; + } + else + /* only program clock frequency if display clock is used; VBIOS will program DPREFCLK */ + /* We need to convert from KHz units into 10KHz units */ + params.asParam.ulDCEClkFreq = cpu_to_le32(bp_params->target_clock_frequency / 10); + + if (EXEC_BIOS_CMD_TABLE(SetDCEClock, params)) { + /* Convert from 10KHz units back to KHz */ + bp_params->target_clock_frequency = le32_to_cpu(params.asParam.ulDCEClkFreq) * 10; + result = BP_RESULT_OK; + } + + return result; +} diff --git a/drivers/gpu/drm/amd/display/dc/bios/command_table.h b/drivers/gpu/drm/amd/display/dc/bios/command_table.h new file mode 100644 index 000000000..ad533775e --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/command_table.h @@ -0,0 +1,99 @@ +/* + * 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 + * + */ + +#ifndef __DAL_COMMAND_TABLE_H__ +#define __DAL_COMMAND_TABLE_H__ + +struct bios_parser; +struct bp_encoder_control; + +struct cmd_tbl { + enum bp_result (*dig_encoder_control)( + struct bios_parser *bp, + struct bp_encoder_control *control); + enum bp_result (*encoder_control_dig1)( + struct bios_parser *bp, + struct bp_encoder_control *control); + enum bp_result (*encoder_control_dig2)( + struct bios_parser *bp, + struct bp_encoder_control *control); + enum bp_result (*transmitter_control)( + struct bios_parser *bp, + struct bp_transmitter_control *control); + enum bp_result (*set_pixel_clock)( + struct bios_parser *bp, + struct bp_pixel_clock_parameters *bp_params); + enum bp_result (*enable_spread_spectrum_on_ppll)( + struct bios_parser *bp, + struct bp_spread_spectrum_parameters *bp_params, + bool enable); + enum bp_result (*adjust_display_pll)( + struct bios_parser *bp, + struct bp_adjust_pixel_clock_parameters *bp_params); + enum bp_result (*dac1_encoder_control)( + struct bios_parser *bp, + bool enable, + uint32_t pixel_clock, + uint8_t dac_standard); + enum bp_result (*dac2_encoder_control)( + struct bios_parser *bp, + bool enable, + uint32_t pixel_clock, + uint8_t dac_standard); + enum bp_result (*dac1_output_control)( + struct bios_parser *bp, + bool enable); + enum bp_result (*dac2_output_control)( + struct bios_parser *bp, + bool enable); + enum bp_result (*set_crtc_timing)( + struct bios_parser *bp, + struct bp_hw_crtc_timing_parameters *bp_params); + enum bp_result (*enable_crtc)( + struct bios_parser *bp, + enum controller_id controller_id, + bool enable); + enum bp_result (*enable_crtc_mem_req)( + struct bios_parser *bp, + enum controller_id controller_id, + bool enable); + enum bp_result (*program_clock)( + struct bios_parser *bp, + struct bp_pixel_clock_parameters *bp_params); + enum bp_result (*external_encoder_control)( + struct bios_parser *bp, + struct bp_external_encoder_control *cntl); + enum bp_result (*enable_disp_power_gating)( + struct bios_parser *bp, + enum controller_id crtc_id, + enum bp_pipe_control_action action); + enum bp_result (*set_dce_clock)( + struct bios_parser *bp, + struct bp_set_dce_clock_parameters *bp_params); +}; + +void dal_bios_parser_init_cmd_tbl(struct bios_parser *bp); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/bios/command_table2.c b/drivers/gpu/drm/amd/display/dc/bios/command_table2.c new file mode 100644 index 000000000..f52f7ff7e --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/command_table2.c @@ -0,0 +1,1054 @@ +/* + * 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 + * + */ + +#include "dm_services.h" + +#include "ObjectID.h" + +#include "atomfirmware.h" +#include "atom.h" +#include "include/bios_parser_interface.h" + +#include "command_table2.h" +#include "command_table_helper2.h" +#include "bios_parser_helper.h" +#include "bios_parser_types_internal2.h" +#include "amdgpu.h" + +#include "dc_dmub_srv.h" +#include "dc.h" + +#define DC_LOGGER \ + bp->base.ctx->logger + +#define GET_INDEX_INTO_MASTER_TABLE(MasterOrData, FieldName)\ + (offsetof(struct atom_master_list_of_##MasterOrData##_functions_v2_1, FieldName) / sizeof(uint16_t)) + +#define EXEC_BIOS_CMD_TABLE(fname, params)\ + (amdgpu_atom_execute_table(((struct amdgpu_device *)bp->base.ctx->driver_context)->mode_info.atom_context, \ + GET_INDEX_INTO_MASTER_TABLE(command, fname), \ + (uint32_t *)¶ms) == 0) + +#define BIOS_CMD_TABLE_REVISION(fname, frev, crev)\ + amdgpu_atom_parse_cmd_header(((struct amdgpu_device *)bp->base.ctx->driver_context)->mode_info.atom_context, \ + GET_INDEX_INTO_MASTER_TABLE(command, fname), &frev, &crev) + +#define BIOS_CMD_TABLE_PARA_REVISION(fname)\ + bios_cmd_table_para_revision(bp->base.ctx->driver_context, \ + GET_INDEX_INTO_MASTER_TABLE(command, fname)) + + + +static uint32_t bios_cmd_table_para_revision(void *dev, + uint32_t index) +{ + struct amdgpu_device *adev = dev; + uint8_t frev, crev; + + if (amdgpu_atom_parse_cmd_header(adev->mode_info.atom_context, + index, + &frev, &crev)) + return crev; + else + return 0; +} + +/****************************************************************************** + ****************************************************************************** + ** + ** D I G E N C O D E R C O N T R O L + ** + ****************************************************************************** + *****************************************************************************/ + +static enum bp_result encoder_control_digx_v1_5( + struct bios_parser *bp, + struct bp_encoder_control *cntl); + +static enum bp_result encoder_control_fallback( + struct bios_parser *bp, + struct bp_encoder_control *cntl); + +static void init_dig_encoder_control(struct bios_parser *bp) +{ + uint32_t version = + BIOS_CMD_TABLE_PARA_REVISION(digxencodercontrol); + + switch (version) { + case 5: + bp->cmd_tbl.dig_encoder_control = encoder_control_digx_v1_5; + break; + default: + dm_output_to_console("Don't have dig_encoder_control for v%d\n", version); + bp->cmd_tbl.dig_encoder_control = encoder_control_fallback; + break; + } +} + +static void encoder_control_dmcub( + struct dc_dmub_srv *dmcub, + struct dig_encoder_stream_setup_parameters_v1_5 *dig) +{ + union dmub_rb_cmd cmd; + + memset(&cmd, 0, sizeof(cmd)); + + cmd.digx_encoder_control.header.type = DMUB_CMD__VBIOS; + cmd.digx_encoder_control.header.sub_type = + DMUB_CMD__VBIOS_DIGX_ENCODER_CONTROL; + cmd.digx_encoder_control.header.payload_bytes = + sizeof(cmd.digx_encoder_control) - + sizeof(cmd.digx_encoder_control.header); + cmd.digx_encoder_control.encoder_control.dig.stream_param = *dig; + + dc_dmub_srv_cmd_queue(dmcub, &cmd); + dc_dmub_srv_cmd_execute(dmcub); + dc_dmub_srv_wait_idle(dmcub); +} + +static enum bp_result encoder_control_digx_v1_5( + struct bios_parser *bp, + struct bp_encoder_control *cntl) +{ + enum bp_result result = BP_RESULT_FAILURE; + struct dig_encoder_stream_setup_parameters_v1_5 params = {0}; + + params.digid = (uint8_t)(cntl->engine_id); + params.action = bp->cmd_helper->encoder_action_to_atom(cntl->action); + + params.pclk_10khz = cntl->pixel_clock / 10; + params.digmode = + (uint8_t)(bp->cmd_helper->encoder_mode_bp_to_atom( + cntl->signal, + cntl->enable_dp_audio)); + params.lanenum = (uint8_t)(cntl->lanes_number); + + switch (cntl->color_depth) { + case COLOR_DEPTH_888: + params.bitpercolor = PANEL_8BIT_PER_COLOR; + break; + case COLOR_DEPTH_101010: + params.bitpercolor = PANEL_10BIT_PER_COLOR; + break; + case COLOR_DEPTH_121212: + params.bitpercolor = PANEL_12BIT_PER_COLOR; + break; + case COLOR_DEPTH_161616: + params.bitpercolor = PANEL_16BIT_PER_COLOR; + break; + default: + break; + } + + if (cntl->signal == SIGNAL_TYPE_HDMI_TYPE_A) + switch (cntl->color_depth) { + case COLOR_DEPTH_101010: + params.pclk_10khz = + (params.pclk_10khz * 30) / 24; + break; + case COLOR_DEPTH_121212: + params.pclk_10khz = + (params.pclk_10khz * 36) / 24; + break; + case COLOR_DEPTH_161616: + params.pclk_10khz = + (params.pclk_10khz * 48) / 24; + break; + default: + break; + } + + if (bp->base.ctx->dc->ctx->dmub_srv && + bp->base.ctx->dc->debug.dmub_command_table) { + encoder_control_dmcub(bp->base.ctx->dmub_srv, ¶ms); + return BP_RESULT_OK; + } + + if (EXEC_BIOS_CMD_TABLE(digxencodercontrol, params)) + result = BP_RESULT_OK; + + return result; +} + +static enum bp_result encoder_control_fallback( + struct bios_parser *bp, + struct bp_encoder_control *cntl) +{ + if (bp->base.ctx->dc->ctx->dmub_srv && + bp->base.ctx->dc->debug.dmub_command_table) { + return encoder_control_digx_v1_5(bp, cntl); + } + + return BP_RESULT_FAILURE; +} + +/***************************************************************************** + ****************************************************************************** + ** + ** TRANSMITTER CONTROL + ** + ****************************************************************************** + *****************************************************************************/ + +static enum bp_result transmitter_control_v1_6( + struct bios_parser *bp, + struct bp_transmitter_control *cntl); + +static enum bp_result transmitter_control_v1_7( + struct bios_parser *bp, + struct bp_transmitter_control *cntl); + +static enum bp_result transmitter_control_fallback( + struct bios_parser *bp, + struct bp_transmitter_control *cntl); + +static void init_transmitter_control(struct bios_parser *bp) +{ + uint8_t frev; + uint8_t crev; + + BIOS_CMD_TABLE_REVISION(dig1transmittercontrol, frev, crev); + + switch (crev) { + case 6: + bp->cmd_tbl.transmitter_control = transmitter_control_v1_6; + break; + case 7: + bp->cmd_tbl.transmitter_control = transmitter_control_v1_7; + break; + default: + dm_output_to_console("Don't have transmitter_control for v%d\n", crev); + bp->cmd_tbl.transmitter_control = transmitter_control_fallback; + break; + } +} + +static void transmitter_control_dmcub( + struct dc_dmub_srv *dmcub, + struct dig_transmitter_control_parameters_v1_6 *dig) +{ + union dmub_rb_cmd cmd; + + memset(&cmd, 0, sizeof(cmd)); + + cmd.dig1_transmitter_control.header.type = DMUB_CMD__VBIOS; + cmd.dig1_transmitter_control.header.sub_type = + DMUB_CMD__VBIOS_DIG1_TRANSMITTER_CONTROL; + cmd.dig1_transmitter_control.header.payload_bytes = + sizeof(cmd.dig1_transmitter_control) - + sizeof(cmd.dig1_transmitter_control.header); + cmd.dig1_transmitter_control.transmitter_control.dig = *dig; + + dc_dmub_srv_cmd_queue(dmcub, &cmd); + dc_dmub_srv_cmd_execute(dmcub); + dc_dmub_srv_wait_idle(dmcub); +} + +static enum bp_result transmitter_control_v1_6( + struct bios_parser *bp, + struct bp_transmitter_control *cntl) +{ + enum bp_result result = BP_RESULT_FAILURE; + const struct command_table_helper *cmd = bp->cmd_helper; + struct dig_transmitter_control_ps_allocation_v1_6 ps = { { 0 } }; + + ps.param.phyid = cmd->phy_id_to_atom(cntl->transmitter); + ps.param.action = (uint8_t)cntl->action; + + if (cntl->action == TRANSMITTER_CONTROL_SET_VOLTAGE_AND_PREEMPASIS) + ps.param.mode_laneset.dplaneset = (uint8_t)cntl->lane_settings; + else + ps.param.mode_laneset.digmode = + cmd->signal_type_to_atom_dig_mode(cntl->signal); + + ps.param.lanenum = (uint8_t)cntl->lanes_number; + ps.param.hpdsel = cmd->hpd_sel_to_atom(cntl->hpd_sel); + ps.param.digfe_sel = cmd->dig_encoder_sel_to_atom(cntl->engine_id); + ps.param.connobj_id = (uint8_t)cntl->connector_obj_id.id; + ps.param.symclk_10khz = cntl->pixel_clock/10; + + + if (cntl->action == TRANSMITTER_CONTROL_ENABLE || + cntl->action == TRANSMITTER_CONTROL_ACTIAVATE || + cntl->action == TRANSMITTER_CONTROL_DEACTIVATE) { + DC_LOG_BIOS("%s:ps.param.symclk_10khz = %d\n",\ + __func__, ps.param.symclk_10khz); + } + + if (bp->base.ctx->dc->ctx->dmub_srv && + bp->base.ctx->dc->debug.dmub_command_table) { + transmitter_control_dmcub(bp->base.ctx->dmub_srv, &ps.param); + return BP_RESULT_OK; + } + +/*color_depth not used any more, driver has deep color factor in the Phyclk*/ + if (EXEC_BIOS_CMD_TABLE(dig1transmittercontrol, ps)) + result = BP_RESULT_OK; + return result; +} + +static void transmitter_control_dmcub_v1_7( + struct dc_dmub_srv *dmcub, + struct dmub_dig_transmitter_control_data_v1_7 *dig) +{ + union dmub_rb_cmd cmd; + + memset(&cmd, 0, sizeof(cmd)); + + cmd.dig1_transmitter_control.header.type = DMUB_CMD__VBIOS; + cmd.dig1_transmitter_control.header.sub_type = + DMUB_CMD__VBIOS_DIG1_TRANSMITTER_CONTROL; + cmd.dig1_transmitter_control.header.payload_bytes = + sizeof(cmd.dig1_transmitter_control) - + sizeof(cmd.dig1_transmitter_control.header); + cmd.dig1_transmitter_control.transmitter_control.dig_v1_7 = *dig; + + dc_dmub_srv_cmd_queue(dmcub, &cmd); + dc_dmub_srv_cmd_execute(dmcub); + dc_dmub_srv_wait_idle(dmcub); +} + +static enum bp_result transmitter_control_v1_7( + struct bios_parser *bp, + struct bp_transmitter_control *cntl) +{ + enum bp_result result = BP_RESULT_FAILURE; + const struct command_table_helper *cmd = bp->cmd_helper; + struct dmub_dig_transmitter_control_data_v1_7 dig_v1_7 = {0}; + + uint8_t hpo_instance = (uint8_t)cntl->hpo_engine_id - ENGINE_ID_HPO_0; + + if (dc_is_dp_signal(cntl->signal)) + hpo_instance = (uint8_t)cntl->hpo_engine_id - ENGINE_ID_HPO_DP_0; + + dig_v1_7.phyid = cmd->phy_id_to_atom(cntl->transmitter); + dig_v1_7.action = (uint8_t)cntl->action; + + if (cntl->action == TRANSMITTER_CONTROL_SET_VOLTAGE_AND_PREEMPASIS) + dig_v1_7.mode_laneset.dplaneset = (uint8_t)cntl->lane_settings; + else + dig_v1_7.mode_laneset.digmode = + cmd->signal_type_to_atom_dig_mode(cntl->signal); + + dig_v1_7.lanenum = (uint8_t)cntl->lanes_number; + dig_v1_7.hpdsel = cmd->hpd_sel_to_atom(cntl->hpd_sel); + dig_v1_7.digfe_sel = cmd->dig_encoder_sel_to_atom(cntl->engine_id); + dig_v1_7.connobj_id = (uint8_t)cntl->connector_obj_id.id; + dig_v1_7.HPO_instance = hpo_instance; + dig_v1_7.symclk_units.symclk_10khz = cntl->pixel_clock/10; + + if (cntl->action == TRANSMITTER_CONTROL_ENABLE || + cntl->action == TRANSMITTER_CONTROL_ACTIAVATE || + cntl->action == TRANSMITTER_CONTROL_DEACTIVATE) { + DC_LOG_BIOS("%s:dig_v1_7.symclk_units.symclk_10khz = %d\n", + __func__, dig_v1_7.symclk_units.symclk_10khz); + } + + if (bp->base.ctx->dc->ctx->dmub_srv && + bp->base.ctx->dc->debug.dmub_command_table) { + transmitter_control_dmcub_v1_7(bp->base.ctx->dmub_srv, &dig_v1_7); + return BP_RESULT_OK; + } + +/*color_depth not used any more, driver has deep color factor in the Phyclk*/ + if (EXEC_BIOS_CMD_TABLE(dig1transmittercontrol, dig_v1_7)) + result = BP_RESULT_OK; + return result; +} + +static enum bp_result transmitter_control_fallback( + struct bios_parser *bp, + struct bp_transmitter_control *cntl) +{ + if (bp->base.ctx->dc->ctx->dmub_srv && + bp->base.ctx->dc->debug.dmub_command_table) { + return transmitter_control_v1_7(bp, cntl); + } + + return BP_RESULT_FAILURE; +} + +/****************************************************************************** + ****************************************************************************** + ** + ** SET PIXEL CLOCK + ** + ****************************************************************************** + *****************************************************************************/ + +static enum bp_result set_pixel_clock_v7( + struct bios_parser *bp, + struct bp_pixel_clock_parameters *bp_params); + +static enum bp_result set_pixel_clock_fallback( + struct bios_parser *bp, + struct bp_pixel_clock_parameters *bp_params); + +static void init_set_pixel_clock(struct bios_parser *bp) +{ + switch (BIOS_CMD_TABLE_PARA_REVISION(setpixelclock)) { + case 7: + bp->cmd_tbl.set_pixel_clock = set_pixel_clock_v7; + break; + default: + dm_output_to_console("Don't have set_pixel_clock for v%d\n", + BIOS_CMD_TABLE_PARA_REVISION(setpixelclock)); + bp->cmd_tbl.set_pixel_clock = set_pixel_clock_fallback; + break; + } +} + +static void set_pixel_clock_dmcub( + struct dc_dmub_srv *dmcub, + struct set_pixel_clock_parameter_v1_7 *clk) +{ + union dmub_rb_cmd cmd; + + memset(&cmd, 0, sizeof(cmd)); + + cmd.set_pixel_clock.header.type = DMUB_CMD__VBIOS; + cmd.set_pixel_clock.header.sub_type = DMUB_CMD__VBIOS_SET_PIXEL_CLOCK; + cmd.set_pixel_clock.header.payload_bytes = + sizeof(cmd.set_pixel_clock) - + sizeof(cmd.set_pixel_clock.header); + cmd.set_pixel_clock.pixel_clock.clk = *clk; + + dc_dmub_srv_cmd_queue(dmcub, &cmd); + dc_dmub_srv_cmd_execute(dmcub); + dc_dmub_srv_wait_idle(dmcub); +} + +static enum bp_result set_pixel_clock_v7( + struct bios_parser *bp, + struct bp_pixel_clock_parameters *bp_params) +{ + enum bp_result result = BP_RESULT_FAILURE; + struct set_pixel_clock_parameter_v1_7 clk; + uint8_t controller_id; + uint32_t pll_id; + + memset(&clk, 0, sizeof(clk)); + + if (bp->cmd_helper->clock_source_id_to_atom(bp_params->pll_id, &pll_id) + && bp->cmd_helper->controller_id_to_atom(bp_params-> + controller_id, &controller_id)) { + /* Note: VBIOS still wants to use ucCRTC name which is now + * 1 byte in ULONG + *typedef struct _CRTC_PIXEL_CLOCK_FREQ + *{ + * target the pixel clock to drive the CRTC timing. + * ULONG ulPixelClock:24; + * 0 means disable PPLL/DCPLL. Expanded to 24 bits comparing to + * previous version. + * ATOM_CRTC1~6, indicate the CRTC controller to + * ULONG ucCRTC:8; + * drive the pixel clock. not used for DCPLL case. + *}CRTC_PIXEL_CLOCK_FREQ; + *union + *{ + * pixel clock and CRTC id frequency + * CRTC_PIXEL_CLOCK_FREQ ulCrtcPclkFreq; + * ULONG ulDispEngClkFreq; dispclk frequency + *}; + */ + clk.crtc_id = controller_id; + clk.pll_id = (uint8_t) pll_id; + clk.encoderobjid = + bp->cmd_helper->encoder_id_to_atom( + dal_graphics_object_id_get_encoder_id( + bp_params->encoder_object_id)); + + clk.encoder_mode = (uint8_t) bp-> + cmd_helper->encoder_mode_bp_to_atom( + bp_params->signal_type, false); + + clk.pixclk_100hz = cpu_to_le32(bp_params->target_pixel_clock_100hz); + + clk.deep_color_ratio = + (uint8_t) bp->cmd_helper-> + transmitter_color_depth_to_atom( + bp_params->color_depth); + + DC_LOG_BIOS("%s:program display clock = %d, tg = %d, pll = %d, "\ + "colorDepth = %d\n", __func__, + bp_params->target_pixel_clock_100hz, (int)controller_id, + pll_id, bp_params->color_depth); + + if (bp_params->flags.FORCE_PROGRAMMING_OF_PLL) + clk.miscinfo |= PIXEL_CLOCK_V7_MISC_FORCE_PROG_PPLL; + + if (bp_params->flags.PROGRAM_PHY_PLL_ONLY) + clk.miscinfo |= PIXEL_CLOCK_V7_MISC_PROG_PHYPLL; + + if (bp_params->flags.SUPPORT_YUV_420) + clk.miscinfo |= PIXEL_CLOCK_V7_MISC_YUV420_MODE; + + if (bp_params->flags.SET_XTALIN_REF_SRC) + clk.miscinfo |= PIXEL_CLOCK_V7_MISC_REF_DIV_SRC_XTALIN; + + if (bp_params->flags.SET_GENLOCK_REF_DIV_SRC) + clk.miscinfo |= PIXEL_CLOCK_V7_MISC_REF_DIV_SRC_GENLK; + + if (bp_params->signal_type == SIGNAL_TYPE_DVI_DUAL_LINK) + clk.miscinfo |= PIXEL_CLOCK_V7_MISC_DVI_DUALLINK_EN; + + if (bp->base.ctx->dc->ctx->dmub_srv && + bp->base.ctx->dc->debug.dmub_command_table) { + set_pixel_clock_dmcub(bp->base.ctx->dmub_srv, &clk); + return BP_RESULT_OK; + } + + if (EXEC_BIOS_CMD_TABLE(setpixelclock, clk)) + result = BP_RESULT_OK; + } + return result; +} + +static enum bp_result set_pixel_clock_fallback( + struct bios_parser *bp, + struct bp_pixel_clock_parameters *bp_params) +{ + if (bp->base.ctx->dc->ctx->dmub_srv && + bp->base.ctx->dc->debug.dmub_command_table) { + return set_pixel_clock_v7(bp, bp_params); + } + + return BP_RESULT_FAILURE; +} + +/****************************************************************************** + ****************************************************************************** + ** + ** SET CRTC TIMING + ** + ****************************************************************************** + *****************************************************************************/ + +static enum bp_result set_crtc_using_dtd_timing_v3( + struct bios_parser *bp, + struct bp_hw_crtc_timing_parameters *bp_params); + +static void init_set_crtc_timing(struct bios_parser *bp) +{ + uint32_t dtd_version = + BIOS_CMD_TABLE_PARA_REVISION(setcrtc_usingdtdtiming); + + switch (dtd_version) { + case 3: + bp->cmd_tbl.set_crtc_timing = + set_crtc_using_dtd_timing_v3; + break; + default: + dm_output_to_console("Don't have set_crtc_timing for v%d\n", dtd_version); + bp->cmd_tbl.set_crtc_timing = NULL; + break; + } +} + +static enum bp_result set_crtc_using_dtd_timing_v3( + struct bios_parser *bp, + struct bp_hw_crtc_timing_parameters *bp_params) +{ + enum bp_result result = BP_RESULT_FAILURE; + struct set_crtc_using_dtd_timing_parameters params = {0}; + uint8_t atom_controller_id; + + if (bp->cmd_helper->controller_id_to_atom( + bp_params->controller_id, &atom_controller_id)) + params.crtc_id = atom_controller_id; + + /* bios usH_Size wants h addressable size */ + params.h_size = cpu_to_le16((uint16_t)bp_params->h_addressable); + /* bios usH_Blanking_Time wants borders included in blanking */ + params.h_blanking_time = + cpu_to_le16((uint16_t)(bp_params->h_total - + bp_params->h_addressable)); + /* bios usV_Size wants v addressable size */ + params.v_size = cpu_to_le16((uint16_t)bp_params->v_addressable); + /* bios usV_Blanking_Time wants borders included in blanking */ + params.v_blanking_time = + cpu_to_le16((uint16_t)(bp_params->v_total - + bp_params->v_addressable)); + /* bios usHSyncOffset is the offset from the end of h addressable, + * our horizontalSyncStart is the offset from the beginning + * of h addressable + */ + params.h_syncoffset = + cpu_to_le16((uint16_t)(bp_params->h_sync_start - + bp_params->h_addressable)); + params.h_syncwidth = cpu_to_le16((uint16_t)bp_params->h_sync_width); + /* bios usHSyncOffset is the offset from the end of v addressable, + * our verticalSyncStart is the offset from the beginning of + * v addressable + */ + params.v_syncoffset = + cpu_to_le16((uint16_t)(bp_params->v_sync_start - + bp_params->v_addressable)); + params.v_syncwidth = cpu_to_le16((uint16_t)bp_params->v_sync_width); + + /* we assume that overscan from original timing does not get bigger + * than 255 + * we will program all the borders in the Set CRTC Overscan call below + */ + + if (bp_params->flags.HSYNC_POSITIVE_POLARITY == 0) + params.modemiscinfo = + cpu_to_le16(le16_to_cpu(params.modemiscinfo) | + ATOM_HSYNC_POLARITY); + + if (bp_params->flags.VSYNC_POSITIVE_POLARITY == 0) + params.modemiscinfo = + cpu_to_le16(le16_to_cpu(params.modemiscinfo) | + ATOM_VSYNC_POLARITY); + + if (bp_params->flags.INTERLACE) { + params.modemiscinfo = + cpu_to_le16(le16_to_cpu(params.modemiscinfo) | + ATOM_INTERLACE); + + /* original DAL code has this condition to apply this + * for non-TV/CV only + * due to complex MV testing for possible impact + * if ( pACParameters->signal != SignalType_YPbPr && + * pACParameters->signal != SignalType_Composite && + * pACParameters->signal != SignalType_SVideo) + */ + { + /* HW will deduct 0.5 line from 2nd feild. + * i.e. for 1080i, it is 2 lines for 1st field, + * 2.5 lines for the 2nd feild. we need input as 5 + * instead of 4. + * but it is 4 either from Edid data (spec CEA 861) + * or CEA timing table. + */ + le16_add_cpu(¶ms.v_syncoffset, 1); + } + } + + if (bp_params->flags.HORZ_COUNT_BY_TWO) + params.modemiscinfo = + cpu_to_le16(le16_to_cpu(params.modemiscinfo) | + 0x100); /* ATOM_DOUBLE_CLOCK_MODE */ + + if (EXEC_BIOS_CMD_TABLE(setcrtc_usingdtdtiming, params)) + result = BP_RESULT_OK; + + return result; +} + +/****************************************************************************** + ****************************************************************************** + ** + ** ENABLE CRTC + ** + ****************************************************************************** + *****************************************************************************/ + +static enum bp_result enable_crtc_v1( + struct bios_parser *bp, + enum controller_id controller_id, + bool enable); + +static void init_enable_crtc(struct bios_parser *bp) +{ + switch (BIOS_CMD_TABLE_PARA_REVISION(enablecrtc)) { + case 1: + bp->cmd_tbl.enable_crtc = enable_crtc_v1; + break; + default: + dm_output_to_console("Don't have enable_crtc for v%d\n", + BIOS_CMD_TABLE_PARA_REVISION(enablecrtc)); + bp->cmd_tbl.enable_crtc = NULL; + break; + } +} + +static enum bp_result enable_crtc_v1( + struct bios_parser *bp, + enum controller_id controller_id, + bool enable) +{ + bool result = BP_RESULT_FAILURE; + struct enable_crtc_parameters params = {0}; + uint8_t id; + + if (bp->cmd_helper->controller_id_to_atom(controller_id, &id)) + params.crtc_id = id; + else + return BP_RESULT_BADINPUT; + + if (enable) + params.enable = ATOM_ENABLE; + else + params.enable = ATOM_DISABLE; + + if (EXEC_BIOS_CMD_TABLE(enablecrtc, params)) + result = BP_RESULT_OK; + + return result; +} + +/****************************************************************************** + ****************************************************************************** + ** + ** DISPLAY PLL + ** + ****************************************************************************** + *****************************************************************************/ + + + +/****************************************************************************** + ****************************************************************************** + ** + ** EXTERNAL ENCODER CONTROL + ** + ****************************************************************************** + *****************************************************************************/ + +static enum bp_result external_encoder_control_v3( + struct bios_parser *bp, + struct bp_external_encoder_control *cntl); + +static void init_external_encoder_control( + struct bios_parser *bp) +{ + switch (BIOS_CMD_TABLE_PARA_REVISION(externalencodercontrol)) { + case 3: + bp->cmd_tbl.external_encoder_control = + external_encoder_control_v3; + break; + default: + bp->cmd_tbl.external_encoder_control = NULL; + break; + } +} + +static enum bp_result external_encoder_control_v3( + struct bios_parser *bp, + struct bp_external_encoder_control *cntl) +{ + /* TODO */ + return BP_RESULT_OK; +} + +/****************************************************************************** + ****************************************************************************** + ** + ** ENABLE DISPLAY POWER GATING + ** + ****************************************************************************** + *****************************************************************************/ + +static enum bp_result enable_disp_power_gating_v2_1( + struct bios_parser *bp, + enum controller_id crtc_id, + enum bp_pipe_control_action action); + +static enum bp_result enable_disp_power_gating_fallback( + struct bios_parser *bp, + enum controller_id crtc_id, + enum bp_pipe_control_action action); + +static void init_enable_disp_power_gating( + struct bios_parser *bp) +{ + switch (BIOS_CMD_TABLE_PARA_REVISION(enabledisppowergating)) { + case 1: + bp->cmd_tbl.enable_disp_power_gating = + enable_disp_power_gating_v2_1; + break; + default: + dm_output_to_console("Don't enable_disp_power_gating enable_crtc for v%d\n", + BIOS_CMD_TABLE_PARA_REVISION(enabledisppowergating)); + bp->cmd_tbl.enable_disp_power_gating = enable_disp_power_gating_fallback; + break; + } +} + +static void enable_disp_power_gating_dmcub( + struct dc_dmub_srv *dmcub, + struct enable_disp_power_gating_parameters_v2_1 *pwr) +{ + union dmub_rb_cmd cmd; + + memset(&cmd, 0, sizeof(cmd)); + + cmd.enable_disp_power_gating.header.type = DMUB_CMD__VBIOS; + cmd.enable_disp_power_gating.header.sub_type = + DMUB_CMD__VBIOS_ENABLE_DISP_POWER_GATING; + cmd.enable_disp_power_gating.header.payload_bytes = + sizeof(cmd.enable_disp_power_gating) - + sizeof(cmd.enable_disp_power_gating.header); + cmd.enable_disp_power_gating.power_gating.pwr = *pwr; + + dc_dmub_srv_cmd_queue(dmcub, &cmd); + dc_dmub_srv_cmd_execute(dmcub); + dc_dmub_srv_wait_idle(dmcub); +} + +static enum bp_result enable_disp_power_gating_v2_1( + struct bios_parser *bp, + enum controller_id crtc_id, + enum bp_pipe_control_action action) +{ + enum bp_result result = BP_RESULT_FAILURE; + + + struct enable_disp_power_gating_ps_allocation ps = { { 0 } }; + uint8_t atom_crtc_id; + + if (bp->cmd_helper->controller_id_to_atom(crtc_id, &atom_crtc_id)) + ps.param.disp_pipe_id = atom_crtc_id; + else + return BP_RESULT_BADINPUT; + + ps.param.enable = + bp->cmd_helper->disp_power_gating_action_to_atom(action); + + if (bp->base.ctx->dc->ctx->dmub_srv && + bp->base.ctx->dc->debug.dmub_command_table) { + enable_disp_power_gating_dmcub(bp->base.ctx->dmub_srv, + &ps.param); + return BP_RESULT_OK; + } + + if (EXEC_BIOS_CMD_TABLE(enabledisppowergating, ps.param)) + result = BP_RESULT_OK; + + return result; +} + +static enum bp_result enable_disp_power_gating_fallback( + struct bios_parser *bp, + enum controller_id crtc_id, + enum bp_pipe_control_action action) +{ + if (bp->base.ctx->dc->ctx->dmub_srv && + bp->base.ctx->dc->debug.dmub_command_table) { + return enable_disp_power_gating_v2_1(bp, crtc_id, action); + } + + return BP_RESULT_FAILURE; +} + +/****************************************************************************** +******************************************************************************* + ** + ** SET DCE CLOCK + ** +******************************************************************************* +*******************************************************************************/ + +static enum bp_result set_dce_clock_v2_1( + struct bios_parser *bp, + struct bp_set_dce_clock_parameters *bp_params); + +static void init_set_dce_clock(struct bios_parser *bp) +{ + switch (BIOS_CMD_TABLE_PARA_REVISION(setdceclock)) { + case 1: + bp->cmd_tbl.set_dce_clock = set_dce_clock_v2_1; + break; + default: + dm_output_to_console("Don't have set_dce_clock for v%d\n", + BIOS_CMD_TABLE_PARA_REVISION(setdceclock)); + bp->cmd_tbl.set_dce_clock = NULL; + break; + } +} + +static enum bp_result set_dce_clock_v2_1( + struct bios_parser *bp, + struct bp_set_dce_clock_parameters *bp_params) +{ + enum bp_result result = BP_RESULT_FAILURE; + + struct set_dce_clock_ps_allocation_v2_1 params; + uint32_t atom_pll_id; + uint32_t atom_clock_type; + const struct command_table_helper *cmd = bp->cmd_helper; + + memset(¶ms, 0, sizeof(params)); + + if (!cmd->clock_source_id_to_atom(bp_params->pll_id, &atom_pll_id) || + !cmd->dc_clock_type_to_atom(bp_params->clock_type, + &atom_clock_type)) + return BP_RESULT_BADINPUT; + + params.param.dceclksrc = atom_pll_id; + params.param.dceclktype = atom_clock_type; + + if (bp_params->clock_type == DCECLOCK_TYPE_DPREFCLK) { + if (bp_params->flags.USE_GENLOCK_AS_SOURCE_FOR_DPREFCLK) + params.param.dceclkflag |= + DCE_CLOCK_FLAG_PLL_REFCLK_SRC_GENLK; + + if (bp_params->flags.USE_PCIE_AS_SOURCE_FOR_DPREFCLK) + params.param.dceclkflag |= + DCE_CLOCK_FLAG_PLL_REFCLK_SRC_PCIE; + + if (bp_params->flags.USE_XTALIN_AS_SOURCE_FOR_DPREFCLK) + params.param.dceclkflag |= + DCE_CLOCK_FLAG_PLL_REFCLK_SRC_XTALIN; + + if (bp_params->flags.USE_GENERICA_AS_SOURCE_FOR_DPREFCLK) + params.param.dceclkflag |= + DCE_CLOCK_FLAG_PLL_REFCLK_SRC_GENERICA; + } else + /* only program clock frequency if display clock is used; + * VBIOS will program DPREFCLK + * We need to convert from KHz units into 10KHz units + */ + params.param.dceclk_10khz = cpu_to_le32( + bp_params->target_clock_frequency / 10); + DC_LOG_BIOS("%s:target_clock_frequency = %d"\ + "clock_type = %d \n", __func__,\ + bp_params->target_clock_frequency,\ + bp_params->clock_type); + + if (EXEC_BIOS_CMD_TABLE(setdceclock, params)) { + /* Convert from 10KHz units back to KHz */ + bp_params->target_clock_frequency = le32_to_cpu( + params.param.dceclk_10khz) * 10; + result = BP_RESULT_OK; + } + + return result; +} + + +/****************************************************************************** + ****************************************************************************** + ** + ** GET SMU CLOCK INFO + ** + ****************************************************************************** + *****************************************************************************/ + +static unsigned int get_smu_clock_info_v3_1(struct bios_parser *bp, uint8_t id); + +static void init_get_smu_clock_info(struct bios_parser *bp) +{ + /* TODO add switch for table vrsion */ + bp->cmd_tbl.get_smu_clock_info = get_smu_clock_info_v3_1; + +} + +static unsigned int get_smu_clock_info_v3_1(struct bios_parser *bp, uint8_t id) +{ + struct atom_get_smu_clock_info_parameters_v3_1 smu_input = {0}; + struct atom_get_smu_clock_info_output_parameters_v3_1 smu_output; + + smu_input.command = GET_SMU_CLOCK_INFO_V3_1_GET_PLLVCO_FREQ; + smu_input.syspll_id = id; + + /* Get Specific Clock */ + if (EXEC_BIOS_CMD_TABLE(getsmuclockinfo, smu_input)) { + memmove(&smu_output, &smu_input, sizeof( + struct atom_get_smu_clock_info_parameters_v3_1)); + return smu_output.atom_smu_outputclkfreq.syspllvcofreq_10khz; + } + + return 0; +} + +/****************************************************************************** + ****************************************************************************** + ** + ** LVTMA CONTROL + ** + ****************************************************************************** + *****************************************************************************/ + +static enum bp_result enable_lvtma_control( + struct bios_parser *bp, + uint8_t uc_pwr_on, + uint8_t panel_instance); + +static void init_enable_lvtma_control(struct bios_parser *bp) +{ + /* TODO add switch for table vrsion */ + bp->cmd_tbl.enable_lvtma_control = enable_lvtma_control; + +} + +static void enable_lvtma_control_dmcub( + struct dc_dmub_srv *dmcub, + uint8_t uc_pwr_on, + uint8_t panel_instance) +{ + + union dmub_rb_cmd cmd; + + memset(&cmd, 0, sizeof(cmd)); + + cmd.lvtma_control.header.type = DMUB_CMD__VBIOS; + cmd.lvtma_control.header.sub_type = + DMUB_CMD__VBIOS_LVTMA_CONTROL; + cmd.lvtma_control.data.uc_pwr_action = + uc_pwr_on; + cmd.lvtma_control.data.panel_inst = + panel_instance; + dc_dmub_srv_cmd_queue(dmcub, &cmd); + dc_dmub_srv_cmd_execute(dmcub); + dc_dmub_srv_wait_idle(dmcub); + +} + +static enum bp_result enable_lvtma_control( + struct bios_parser *bp, + uint8_t uc_pwr_on, + uint8_t panel_instance) +{ + enum bp_result result = BP_RESULT_FAILURE; + + if (bp->base.ctx->dc->ctx->dmub_srv && + bp->base.ctx->dc->debug.dmub_command_table) { + enable_lvtma_control_dmcub(bp->base.ctx->dmub_srv, + uc_pwr_on, + panel_instance); + return BP_RESULT_OK; + } + return result; +} + +void dal_firmware_parser_init_cmd_tbl(struct bios_parser *bp) +{ + init_dig_encoder_control(bp); + init_transmitter_control(bp); + init_set_pixel_clock(bp); + + init_set_crtc_timing(bp); + + init_enable_crtc(bp); + + init_external_encoder_control(bp); + init_enable_disp_power_gating(bp); + init_set_dce_clock(bp); + init_get_smu_clock_info(bp); + + init_enable_lvtma_control(bp); +} diff --git a/drivers/gpu/drm/amd/display/dc/bios/command_table2.h b/drivers/gpu/drm/amd/display/dc/bios/command_table2.h new file mode 100644 index 000000000..be060b4b8 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/command_table2.h @@ -0,0 +1,104 @@ +/* + * 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 + * + */ + +#ifndef __DAL_COMMAND_TABLE2_H__ +#define __DAL_COMMAND_TABLE2_H__ + +struct bios_parser; +struct bp_encoder_control; + +struct cmd_tbl { + enum bp_result (*dig_encoder_control)( + struct bios_parser *bp, + struct bp_encoder_control *control); + enum bp_result (*encoder_control_dig1)( + struct bios_parser *bp, + struct bp_encoder_control *control); + enum bp_result (*encoder_control_dig2)( + struct bios_parser *bp, + struct bp_encoder_control *control); + enum bp_result (*transmitter_control)( + struct bios_parser *bp, + struct bp_transmitter_control *control); + enum bp_result (*set_pixel_clock)( + struct bios_parser *bp, + struct bp_pixel_clock_parameters *bp_params); + enum bp_result (*enable_spread_spectrum_on_ppll)( + struct bios_parser *bp, + struct bp_spread_spectrum_parameters *bp_params, + bool enable); + enum bp_result (*adjust_display_pll)( + struct bios_parser *bp, + struct bp_adjust_pixel_clock_parameters *bp_params); + enum bp_result (*dac1_encoder_control)( + struct bios_parser *bp, + bool enable, + uint32_t pixel_clock, + uint8_t dac_standard); + enum bp_result (*dac2_encoder_control)( + struct bios_parser *bp, + bool enable, + uint32_t pixel_clock, + uint8_t dac_standard); + enum bp_result (*dac1_output_control)( + struct bios_parser *bp, + bool enable); + enum bp_result (*dac2_output_control)( + struct bios_parser *bp, + bool enable); + enum bp_result (*set_crtc_timing)( + struct bios_parser *bp, + struct bp_hw_crtc_timing_parameters *bp_params); + enum bp_result (*enable_crtc)( + struct bios_parser *bp, + enum controller_id controller_id, + bool enable); + enum bp_result (*enable_crtc_mem_req)( + struct bios_parser *bp, + enum controller_id controller_id, + bool enable); + enum bp_result (*program_clock)( + struct bios_parser *bp, + struct bp_pixel_clock_parameters *bp_params); + enum bp_result (*external_encoder_control)( + struct bios_parser *bp, + struct bp_external_encoder_control *cntl); + enum bp_result (*enable_disp_power_gating)( + struct bios_parser *bp, + enum controller_id crtc_id, + enum bp_pipe_control_action action); + enum bp_result (*set_dce_clock)( + struct bios_parser *bp, + struct bp_set_dce_clock_parameters *bp_params); + unsigned int (*get_smu_clock_info)( + struct bios_parser *bp, uint8_t id); + enum bp_result (*enable_lvtma_control)(struct bios_parser *bp, + uint8_t uc_pwr_on, + uint8_t panel_instance); +}; + +void dal_firmware_parser_init_cmd_tbl(struct bios_parser *bp); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/bios/command_table_helper.c b/drivers/gpu/drm/amd/display/dc/bios/command_table_helper.c new file mode 100644 index 000000000..e317a3615 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/command_table_helper.c @@ -0,0 +1,295 @@ +/* + * 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 + * + */ + +#include "dm_services.h" + +#include "atom.h" + +#include "include/bios_parser_types.h" + +#include "command_table_helper.h" + +bool dal_bios_parser_init_cmd_tbl_helper( + const struct command_table_helper **h, + enum dce_version dce) +{ + switch (dce) { +#if defined(CONFIG_DRM_AMD_DC_SI) + case DCE_VERSION_6_0: + case DCE_VERSION_6_1: + case DCE_VERSION_6_4: + *h = dal_cmd_tbl_helper_dce60_get_table(); + return true; +#endif + + case DCE_VERSION_8_0: + case DCE_VERSION_8_1: + case DCE_VERSION_8_3: + *h = dal_cmd_tbl_helper_dce80_get_table(); + return true; + + case DCE_VERSION_10_0: + *h = dal_cmd_tbl_helper_dce110_get_table(); + return true; + + case DCE_VERSION_11_0: + *h = dal_cmd_tbl_helper_dce110_get_table(); + return true; + + case DCE_VERSION_11_2: + case DCE_VERSION_11_22: + *h = dal_cmd_tbl_helper_dce112_get_table(); + return true; + + default: + /* Unsupported DCE */ + BREAK_TO_DEBUGGER(); + return false; + } +} + +/* real implementations */ + +bool dal_cmd_table_helper_controller_id_to_atom( + enum controller_id id, + uint8_t *atom_id) +{ + if (atom_id == NULL) { + BREAK_TO_DEBUGGER(); + return false; + } + + switch (id) { + case CONTROLLER_ID_D0: + *atom_id = ATOM_CRTC1; + return true; + case CONTROLLER_ID_D1: + *atom_id = ATOM_CRTC2; + return true; + case CONTROLLER_ID_D2: + *atom_id = ATOM_CRTC3; + return true; + case CONTROLLER_ID_D3: + *atom_id = ATOM_CRTC4; + return true; + case CONTROLLER_ID_D4: + *atom_id = ATOM_CRTC5; + return true; + case CONTROLLER_ID_D5: + *atom_id = ATOM_CRTC6; + return true; + case CONTROLLER_ID_UNDERLAY0: + *atom_id = ATOM_UNDERLAY_PIPE0; + return true; + case CONTROLLER_ID_UNDEFINED: + *atom_id = ATOM_CRTC_INVALID; + return true; + default: + /* Wrong controller id */ + BREAK_TO_DEBUGGER(); + return false; + } +} + +/** + * dal_cmd_table_helper_transmitter_bp_to_atom - Translate the Transmitter to the + * corresponding ATOM BIOS value + * @t: transmitter + * returns: output digitalTransmitter + * // =00: Digital Transmitter1 ( UNIPHY linkAB ) + * // =01: Digital Transmitter2 ( UNIPHY linkCD ) + * // =02: Digital Transmitter3 ( UNIPHY linkEF ) + */ +uint8_t dal_cmd_table_helper_transmitter_bp_to_atom( + enum transmitter t) +{ + switch (t) { + case TRANSMITTER_UNIPHY_A: + case TRANSMITTER_UNIPHY_B: + case TRANSMITTER_TRAVIS_LCD: + return 0; + case TRANSMITTER_UNIPHY_C: + case TRANSMITTER_UNIPHY_D: + return 1; + case TRANSMITTER_UNIPHY_E: + case TRANSMITTER_UNIPHY_F: + return 2; + default: + /* Invalid Transmitter Type! */ + BREAK_TO_DEBUGGER(); + return 0; + } +} + +uint32_t dal_cmd_table_helper_encoder_mode_bp_to_atom( + enum signal_type s, + bool enable_dp_audio) +{ + switch (s) { + case SIGNAL_TYPE_DVI_SINGLE_LINK: + case SIGNAL_TYPE_DVI_DUAL_LINK: + return ATOM_ENCODER_MODE_DVI; + case SIGNAL_TYPE_HDMI_TYPE_A: + return ATOM_ENCODER_MODE_HDMI; + case SIGNAL_TYPE_LVDS: + return ATOM_ENCODER_MODE_LVDS; + case SIGNAL_TYPE_EDP: + case SIGNAL_TYPE_DISPLAY_PORT_MST: + case SIGNAL_TYPE_DISPLAY_PORT: + case SIGNAL_TYPE_VIRTUAL: + if (enable_dp_audio) + return ATOM_ENCODER_MODE_DP_AUDIO; + else + return ATOM_ENCODER_MODE_DP; + case SIGNAL_TYPE_RGB: + return ATOM_ENCODER_MODE_CRT; + default: + return ATOM_ENCODER_MODE_CRT; + } +} + +void dal_cmd_table_helper_assign_control_parameter( + const struct command_table_helper *h, + struct bp_encoder_control *control, + DIG_ENCODER_CONTROL_PARAMETERS_V2 *ctrl_param) +{ + /* there are three transmitter blocks, each one has two links 4-lanes + * each, A+B, C+D, E+F, Uniphy A, C and E are enumerated as link 0 in + * each transmitter block B, D and F as link 1, third transmitter block + * has non splitable links (UniphyE and UniphyF can not be configured + * separately to drive two different streams) + */ + if ((control->transmitter == TRANSMITTER_UNIPHY_B) || + (control->transmitter == TRANSMITTER_UNIPHY_D) || + (control->transmitter == TRANSMITTER_UNIPHY_F)) { + /* Bit2: Link Select + * =0: PHY linkA/C/E + * =1: PHY linkB/D/F + */ + ctrl_param->acConfig.ucLinkSel = 1; + } + + /* Bit[4:3]: Transmitter Selection + * =00: Digital Transmitter1 ( UNIPHY linkAB ) + * =01: Digital Transmitter2 ( UNIPHY linkCD ) + * =02: Digital Transmitter3 ( UNIPHY linkEF ) + * =03: Reserved + */ + ctrl_param->acConfig.ucTransmitterSel = + (uint8_t)(h->transmitter_bp_to_atom(control->transmitter)); + + /* We need to convert from KHz units into 10KHz units */ + ctrl_param->ucAction = h->encoder_action_to_atom(control->action); + ctrl_param->usPixelClock = cpu_to_le16((uint16_t)(control->pixel_clock / 10)); + ctrl_param->ucEncoderMode = + (uint8_t)(h->encoder_mode_bp_to_atom( + control->signal, control->enable_dp_audio)); + ctrl_param->ucLaneNum = (uint8_t)(control->lanes_number); +} + +bool dal_cmd_table_helper_clock_source_id_to_ref_clk_src( + enum clock_source_id id, + uint32_t *ref_clk_src_id) +{ + if (ref_clk_src_id == NULL) { + BREAK_TO_DEBUGGER(); + return false; + } + + switch (id) { + case CLOCK_SOURCE_ID_PLL1: + *ref_clk_src_id = ENCODER_REFCLK_SRC_P1PLL; + return true; + case CLOCK_SOURCE_ID_PLL2: + *ref_clk_src_id = ENCODER_REFCLK_SRC_P2PLL; + return true; + case CLOCK_SOURCE_ID_DCPLL: + *ref_clk_src_id = ENCODER_REFCLK_SRC_DCPLL; + return true; + case CLOCK_SOURCE_ID_EXTERNAL: + *ref_clk_src_id = ENCODER_REFCLK_SRC_EXTCLK; + return true; + case CLOCK_SOURCE_ID_UNDEFINED: + *ref_clk_src_id = ENCODER_REFCLK_SRC_INVALID; + return true; + default: + /* Unsupported clock source id */ + BREAK_TO_DEBUGGER(); + return false; + } +} + +uint8_t dal_cmd_table_helper_encoder_id_to_atom( + enum encoder_id id) +{ + switch (id) { + case ENCODER_ID_INTERNAL_LVDS: + return ENCODER_OBJECT_ID_INTERNAL_LVDS; + case ENCODER_ID_INTERNAL_TMDS1: + return ENCODER_OBJECT_ID_INTERNAL_TMDS1; + case ENCODER_ID_INTERNAL_TMDS2: + return ENCODER_OBJECT_ID_INTERNAL_TMDS2; + case ENCODER_ID_INTERNAL_DAC1: + return ENCODER_OBJECT_ID_INTERNAL_DAC1; + case ENCODER_ID_INTERNAL_DAC2: + return ENCODER_OBJECT_ID_INTERNAL_DAC2; + case ENCODER_ID_INTERNAL_LVTM1: + return ENCODER_OBJECT_ID_INTERNAL_LVTM1; + case ENCODER_ID_INTERNAL_HDMI: + return ENCODER_OBJECT_ID_HDMI_INTERNAL; + case ENCODER_ID_EXTERNAL_TRAVIS: + return ENCODER_OBJECT_ID_TRAVIS; + case ENCODER_ID_EXTERNAL_NUTMEG: + return ENCODER_OBJECT_ID_NUTMEG; + case ENCODER_ID_INTERNAL_KLDSCP_TMDS1: + return ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1; + case ENCODER_ID_INTERNAL_KLDSCP_DAC1: + return ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1; + case ENCODER_ID_INTERNAL_KLDSCP_DAC2: + return ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2; + case ENCODER_ID_EXTERNAL_MVPU_FPGA: + return ENCODER_OBJECT_ID_MVPU_FPGA; + case ENCODER_ID_INTERNAL_DDI: + return ENCODER_OBJECT_ID_INTERNAL_DDI; + case ENCODER_ID_INTERNAL_UNIPHY: + return ENCODER_OBJECT_ID_INTERNAL_UNIPHY; + case ENCODER_ID_INTERNAL_KLDSCP_LVTMA: + return ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA; + case ENCODER_ID_INTERNAL_UNIPHY1: + return ENCODER_OBJECT_ID_INTERNAL_UNIPHY1; + case ENCODER_ID_INTERNAL_UNIPHY2: + return ENCODER_OBJECT_ID_INTERNAL_UNIPHY2; + case ENCODER_ID_INTERNAL_UNIPHY3: + return ENCODER_OBJECT_ID_INTERNAL_UNIPHY3; + case ENCODER_ID_INTERNAL_WIRELESS: + return ENCODER_OBJECT_ID_INTERNAL_VCE; + case ENCODER_ID_UNKNOWN: + return ENCODER_OBJECT_ID_NONE; + default: + /* Invalid encoder id */ + BREAK_TO_DEBUGGER(); + return ENCODER_OBJECT_ID_NONE; + } +} diff --git a/drivers/gpu/drm/amd/display/dc/bios/command_table_helper.h b/drivers/gpu/drm/amd/display/dc/bios/command_table_helper.h new file mode 100644 index 000000000..dfd30aaf4 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/command_table_helper.h @@ -0,0 +1,62 @@ +/* + * 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 + * + */ + +#ifndef __DAL_COMMAND_TABLE_HELPER_H__ +#define __DAL_COMMAND_TABLE_HELPER_H__ + +#if defined(CONFIG_DRM_AMD_DC_SI) +#include "dce60/command_table_helper_dce60.h" +#endif +#include "dce80/command_table_helper_dce80.h" +#include "dce110/command_table_helper_dce110.h" +#include "dce112/command_table_helper_dce112.h" +#include "command_table_helper_struct.h" + +bool dal_bios_parser_init_cmd_tbl_helper(const struct command_table_helper **h, + enum dce_version dce); + +bool dal_cmd_table_helper_controller_id_to_atom( + enum controller_id id, + uint8_t *atom_id); + +uint32_t dal_cmd_table_helper_encoder_mode_bp_to_atom( + enum signal_type s, + bool enable_dp_audio); + +void dal_cmd_table_helper_assign_control_parameter( + const struct command_table_helper *h, + struct bp_encoder_control *control, +DIG_ENCODER_CONTROL_PARAMETERS_V2 *ctrl_param); + +bool dal_cmd_table_helper_clock_source_id_to_ref_clk_src( + enum clock_source_id id, + uint32_t *ref_clk_src_id); + +uint8_t dal_cmd_table_helper_transmitter_bp_to_atom( + enum transmitter t); + +uint8_t dal_cmd_table_helper_encoder_id_to_atom( + enum encoder_id id); +#endif diff --git a/drivers/gpu/drm/amd/display/dc/bios/command_table_helper2.c b/drivers/gpu/drm/amd/display/dc/bios/command_table_helper2.c new file mode 100644 index 000000000..8538f13e0 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/command_table_helper2.c @@ -0,0 +1,280 @@ +/* + * 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 + * + */ + +#include "dm_services.h" + +#include "ObjectID.h" +#include "atomfirmware.h" + +#include "include/bios_parser_types.h" + +#include "command_table_helper2.h" + +bool dal_bios_parser_init_cmd_tbl_helper2( + const struct command_table_helper **h, + enum dce_version dce) +{ + switch (dce) { +#if defined(CONFIG_DRM_AMD_DC_SI) + case DCE_VERSION_6_0: + case DCE_VERSION_6_1: + case DCE_VERSION_6_4: + *h = dal_cmd_tbl_helper_dce60_get_table(); + return true; +#endif + + case DCE_VERSION_8_0: + case DCE_VERSION_8_1: + case DCE_VERSION_8_3: + *h = dal_cmd_tbl_helper_dce80_get_table(); + return true; + + case DCE_VERSION_10_0: + *h = dal_cmd_tbl_helper_dce110_get_table(); + return true; + + case DCE_VERSION_11_0: + *h = dal_cmd_tbl_helper_dce110_get_table(); + return true; + + case DCE_VERSION_11_2: + case DCE_VERSION_11_22: + case DCE_VERSION_12_0: + case DCE_VERSION_12_1: + *h = dal_cmd_tbl_helper_dce112_get_table2(); + return true; + case DCN_VERSION_1_0: + case DCN_VERSION_1_01: + case DCN_VERSION_2_0: + case DCN_VERSION_2_1: + case DCN_VERSION_2_01: + case DCN_VERSION_3_0: + case DCN_VERSION_3_01: + case DCN_VERSION_3_02: + case DCN_VERSION_3_03: + case DCN_VERSION_3_1: + case DCN_VERSION_3_14: + case DCN_VERSION_3_15: + case DCN_VERSION_3_16: + case DCN_VERSION_3_2: + case DCN_VERSION_3_21: + *h = dal_cmd_tbl_helper_dce112_get_table2(); + return true; + + default: + /* Unsupported DCE */ + BREAK_TO_DEBUGGER(); + return false; + } +} + +/* real implementations */ + +bool dal_cmd_table_helper_controller_id_to_atom2( + enum controller_id id, + uint8_t *atom_id) +{ + if (atom_id == NULL) { + BREAK_TO_DEBUGGER(); + return false; + } + + switch (id) { + case CONTROLLER_ID_D0: + *atom_id = ATOM_CRTC1; + return true; + case CONTROLLER_ID_D1: + *atom_id = ATOM_CRTC2; + return true; + case CONTROLLER_ID_D2: + *atom_id = ATOM_CRTC3; + return true; + case CONTROLLER_ID_D3: + *atom_id = ATOM_CRTC4; + return true; + case CONTROLLER_ID_D4: + *atom_id = ATOM_CRTC5; + return true; + case CONTROLLER_ID_D5: + *atom_id = ATOM_CRTC6; + return true; + /* TODO :case CONTROLLER_ID_UNDERLAY0: + *atom_id = ATOM_UNDERLAY_PIPE0; + return true; + */ + case CONTROLLER_ID_UNDEFINED: + *atom_id = ATOM_CRTC_INVALID; + return true; + default: + /* Wrong controller id */ + BREAK_TO_DEBUGGER(); + return false; + } +} + +/** + * dal_cmd_table_helper_transmitter_bp_to_atom2 - Translate the Transmitter to the + * corresponding ATOM BIOS value + * @t: transmitter + * returns: digitalTransmitter + * // =00: Digital Transmitter1 ( UNIPHY linkAB ) + * // =01: Digital Transmitter2 ( UNIPHY linkCD ) + * // =02: Digital Transmitter3 ( UNIPHY linkEF ) + */ +uint8_t dal_cmd_table_helper_transmitter_bp_to_atom2( + enum transmitter t) +{ + switch (t) { + case TRANSMITTER_UNIPHY_A: + case TRANSMITTER_UNIPHY_B: + case TRANSMITTER_TRAVIS_LCD: + return 0; + case TRANSMITTER_UNIPHY_C: + case TRANSMITTER_UNIPHY_D: + return 1; + case TRANSMITTER_UNIPHY_E: + case TRANSMITTER_UNIPHY_F: + return 2; + default: + /* Invalid Transmitter Type! */ + BREAK_TO_DEBUGGER(); + return 0; + } +} + +uint32_t dal_cmd_table_helper_encoder_mode_bp_to_atom2( + enum signal_type s, + bool enable_dp_audio) +{ + switch (s) { + case SIGNAL_TYPE_DVI_SINGLE_LINK: + case SIGNAL_TYPE_DVI_DUAL_LINK: + return ATOM_ENCODER_MODE_DVI; + case SIGNAL_TYPE_HDMI_TYPE_A: + return ATOM_ENCODER_MODE_HDMI; + case SIGNAL_TYPE_LVDS: + return ATOM_ENCODER_MODE_LVDS; + case SIGNAL_TYPE_EDP: + case SIGNAL_TYPE_DISPLAY_PORT_MST: + case SIGNAL_TYPE_DISPLAY_PORT: + case SIGNAL_TYPE_VIRTUAL: + if (enable_dp_audio) + return ATOM_ENCODER_MODE_DP_AUDIO; + else + return ATOM_ENCODER_MODE_DP; + case SIGNAL_TYPE_RGB: + return ATOM_ENCODER_MODE_CRT; + default: + return ATOM_ENCODER_MODE_CRT; + } +} + +bool dal_cmd_table_helper_clock_source_id_to_ref_clk_src2( + enum clock_source_id id, + uint32_t *ref_clk_src_id) +{ + if (ref_clk_src_id == NULL) { + BREAK_TO_DEBUGGER(); + return false; + } + + switch (id) { + case CLOCK_SOURCE_ID_PLL1: + *ref_clk_src_id = ENCODER_REFCLK_SRC_P1PLL; + return true; + case CLOCK_SOURCE_ID_PLL2: + *ref_clk_src_id = ENCODER_REFCLK_SRC_P2PLL; + return true; + /*TODO:case CLOCK_SOURCE_ID_DCPLL: + *ref_clk_src_id = ENCODER_REFCLK_SRC_DCPLL; + return true; + */ + case CLOCK_SOURCE_ID_EXTERNAL: + *ref_clk_src_id = ENCODER_REFCLK_SRC_EXTCLK; + return true; + case CLOCK_SOURCE_ID_UNDEFINED: + *ref_clk_src_id = ENCODER_REFCLK_SRC_INVALID; + return true; + default: + /* Unsupported clock source id */ + BREAK_TO_DEBUGGER(); + return false; + } +} + +uint8_t dal_cmd_table_helper_encoder_id_to_atom2( + enum encoder_id id) +{ + switch (id) { + case ENCODER_ID_INTERNAL_LVDS: + return ENCODER_OBJECT_ID_INTERNAL_LVDS; + case ENCODER_ID_INTERNAL_TMDS1: + return ENCODER_OBJECT_ID_INTERNAL_TMDS1; + case ENCODER_ID_INTERNAL_TMDS2: + return ENCODER_OBJECT_ID_INTERNAL_TMDS2; + case ENCODER_ID_INTERNAL_DAC1: + return ENCODER_OBJECT_ID_INTERNAL_DAC1; + case ENCODER_ID_INTERNAL_DAC2: + return ENCODER_OBJECT_ID_INTERNAL_DAC2; + case ENCODER_ID_INTERNAL_LVTM1: + return ENCODER_OBJECT_ID_INTERNAL_LVTM1; + case ENCODER_ID_INTERNAL_HDMI: + return ENCODER_OBJECT_ID_HDMI_INTERNAL; + case ENCODER_ID_EXTERNAL_TRAVIS: + return ENCODER_OBJECT_ID_TRAVIS; + case ENCODER_ID_EXTERNAL_NUTMEG: + return ENCODER_OBJECT_ID_NUTMEG; + case ENCODER_ID_INTERNAL_KLDSCP_TMDS1: + return ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1; + case ENCODER_ID_INTERNAL_KLDSCP_DAC1: + return ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1; + case ENCODER_ID_INTERNAL_KLDSCP_DAC2: + return ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2; + case ENCODER_ID_EXTERNAL_MVPU_FPGA: + return ENCODER_OBJECT_ID_MVPU_FPGA; + case ENCODER_ID_INTERNAL_DDI: + return ENCODER_OBJECT_ID_INTERNAL_DDI; + case ENCODER_ID_INTERNAL_UNIPHY: + return ENCODER_OBJECT_ID_INTERNAL_UNIPHY; + case ENCODER_ID_INTERNAL_KLDSCP_LVTMA: + return ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA; + case ENCODER_ID_INTERNAL_UNIPHY1: + return ENCODER_OBJECT_ID_INTERNAL_UNIPHY1; + case ENCODER_ID_INTERNAL_UNIPHY2: + return ENCODER_OBJECT_ID_INTERNAL_UNIPHY2; + case ENCODER_ID_INTERNAL_UNIPHY3: + return ENCODER_OBJECT_ID_INTERNAL_UNIPHY3; + case ENCODER_ID_INTERNAL_WIRELESS: + return ENCODER_OBJECT_ID_INTERNAL_VCE; + case ENCODER_ID_INTERNAL_VIRTUAL: + return ENCODER_OBJECT_ID_NONE; + case ENCODER_ID_UNKNOWN: + return ENCODER_OBJECT_ID_NONE; + default: + /* Invalid encoder id */ + BREAK_TO_DEBUGGER(); + return ENCODER_OBJECT_ID_NONE; + } +} diff --git a/drivers/gpu/drm/amd/display/dc/bios/command_table_helper2.h b/drivers/gpu/drm/amd/display/dc/bios/command_table_helper2.h new file mode 100644 index 000000000..66e0a3e73 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/command_table_helper2.h @@ -0,0 +1,57 @@ +/* + * 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 + * + */ + +#ifndef __DAL_COMMAND_TABLE_HELPER2_H__ +#define __DAL_COMMAND_TABLE_HELPER2_H__ + +#if defined(CONFIG_DRM_AMD_DC_SI) +#include "dce60/command_table_helper_dce60.h" +#endif +#include "dce80/command_table_helper_dce80.h" +#include "dce110/command_table_helper_dce110.h" +#include "dce112/command_table_helper2_dce112.h" +#include "command_table_helper_struct.h" + +bool dal_bios_parser_init_cmd_tbl_helper2(const struct command_table_helper **h, + enum dce_version dce); + +bool dal_cmd_table_helper_controller_id_to_atom2( + enum controller_id id, + uint8_t *atom_id); + +uint32_t dal_cmd_table_helper_encoder_mode_bp_to_atom2( + enum signal_type s, + bool enable_dp_audio); + +bool dal_cmd_table_helper_clock_source_id_to_ref_clk_src2( + enum clock_source_id id, + uint32_t *ref_clk_src_id); + +uint8_t dal_cmd_table_helper_transmitter_bp_to_atom2( + enum transmitter t); + +uint8_t dal_cmd_table_helper_encoder_id_to_atom2( + enum encoder_id id); +#endif diff --git a/drivers/gpu/drm/amd/display/dc/bios/command_table_helper_struct.h b/drivers/gpu/drm/amd/display/dc/bios/command_table_helper_struct.h new file mode 100644 index 000000000..1f2c0a3f0 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/command_table_helper_struct.h @@ -0,0 +1,66 @@ +/* + * 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 + * + */ + +#ifndef __DAL_COMMAND_TABLE_HELPER_STRUCT_H__ +#define __DAL_COMMAND_TABLE_HELPER_STRUCT_H__ + +#include "dce80/command_table_helper_dce80.h" +#include "dce110/command_table_helper_dce110.h" +#include "dce112/command_table_helper_dce112.h" + +struct _DIG_ENCODER_CONTROL_PARAMETERS_V2; +struct command_table_helper { + bool (*controller_id_to_atom)(enum controller_id id, uint8_t *atom_id); + uint8_t (*encoder_action_to_atom)( + enum bp_encoder_control_action action); + uint32_t (*encoder_mode_bp_to_atom)(enum signal_type s, + bool enable_dp_audio); + bool (*engine_bp_to_atom)(enum engine_id engine_id, + uint32_t *atom_engine_id); + void (*assign_control_parameter)( + const struct command_table_helper *h, + struct bp_encoder_control *control, + struct _DIG_ENCODER_CONTROL_PARAMETERS_V2 *ctrl_param); + bool (*clock_source_id_to_atom)(enum clock_source_id id, + uint32_t *atom_pll_id); + bool (*clock_source_id_to_ref_clk_src)( + enum clock_source_id id, + uint32_t *ref_clk_src_id); + uint8_t (*transmitter_bp_to_atom)(enum transmitter t); + uint8_t (*encoder_id_to_atom)(enum encoder_id id); + uint8_t (*clock_source_id_to_atom_phy_clk_src_id)( + enum clock_source_id id); + uint8_t (*signal_type_to_atom_dig_mode)(enum signal_type s); + uint8_t (*hpd_sel_to_atom)(enum hpd_source_id id); + uint8_t (*dig_encoder_sel_to_atom)(enum engine_id engine_id); + uint8_t (*phy_id_to_atom)(enum transmitter t); + uint8_t (*disp_power_gating_action_to_atom)( + enum bp_pipe_control_action action); + bool (*dc_clock_type_to_atom)(enum bp_dce_clock_type id, + uint32_t *atom_clock_type); + uint8_t (*transmitter_color_depth_to_atom)(enum transmitter_color_depth id); +}; + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/bios/dce110/command_table_helper_dce110.c b/drivers/gpu/drm/amd/display/dc/bios/dce110/command_table_helper_dce110.c new file mode 100644 index 000000000..11bf247bb --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/dce110/command_table_helper_dce110.c @@ -0,0 +1,336 @@ +/* + * 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 + * + */ + +#include "dm_services.h" + +#include "atom.h" + +#include "include/bios_parser_types.h" + +#include "../command_table_helper.h" + +static uint8_t phy_id_to_atom(enum transmitter t) +{ + uint8_t atom_phy_id; + + switch (t) { + case TRANSMITTER_UNIPHY_A: + atom_phy_id = ATOM_PHY_ID_UNIPHYA; + break; + case TRANSMITTER_UNIPHY_B: + atom_phy_id = ATOM_PHY_ID_UNIPHYB; + break; + case TRANSMITTER_UNIPHY_C: + atom_phy_id = ATOM_PHY_ID_UNIPHYC; + break; + case TRANSMITTER_UNIPHY_D: + atom_phy_id = ATOM_PHY_ID_UNIPHYD; + break; + case TRANSMITTER_UNIPHY_E: + atom_phy_id = ATOM_PHY_ID_UNIPHYE; + break; + case TRANSMITTER_UNIPHY_F: + atom_phy_id = ATOM_PHY_ID_UNIPHYF; + break; + case TRANSMITTER_UNIPHY_G: + atom_phy_id = ATOM_PHY_ID_UNIPHYG; + break; + default: + atom_phy_id = ATOM_PHY_ID_UNIPHYA; + break; + } + return atom_phy_id; +} + +static uint8_t signal_type_to_atom_dig_mode(enum signal_type s) +{ + uint8_t atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_DP; + + switch (s) { + case SIGNAL_TYPE_DISPLAY_PORT: + case SIGNAL_TYPE_EDP: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_DP; + break; + case SIGNAL_TYPE_LVDS: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_LVDS; + break; + case SIGNAL_TYPE_DVI_SINGLE_LINK: + case SIGNAL_TYPE_DVI_DUAL_LINK: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_DVI; + break; + case SIGNAL_TYPE_HDMI_TYPE_A: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_HDMI; + break; + case SIGNAL_TYPE_DISPLAY_PORT_MST: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_DP_MST; + break; + default: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_DVI; + break; + } + + return atom_dig_mode; +} + +static uint8_t clock_source_id_to_atom_phy_clk_src_id( + enum clock_source_id id) +{ + uint8_t atom_phy_clk_src_id = 0; + + switch (id) { + case CLOCK_SOURCE_ID_PLL0: + atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_P0PLL; + break; + case CLOCK_SOURCE_ID_PLL1: + atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_P1PLL; + break; + case CLOCK_SOURCE_ID_PLL2: + atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_P2PLL; + break; + case CLOCK_SOURCE_ID_EXTERNAL: + atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_REFCLK_SRC_EXT; + break; + default: + atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_P1PLL; + break; + } + + return atom_phy_clk_src_id >> 2; +} + +static uint8_t hpd_sel_to_atom(enum hpd_source_id id) +{ + uint8_t atom_hpd_sel = 0; + + switch (id) { + case HPD_SOURCEID1: + atom_hpd_sel = ATOM_TRANSMITTER_CONFIG_V5_HPD1_SEL; + break; + case HPD_SOURCEID2: + atom_hpd_sel = ATOM_TRANSMITTER_CONFIG_V5_HPD2_SEL; + break; + case HPD_SOURCEID3: + atom_hpd_sel = ATOM_TRANSMITTER_CONFIG_V5_HPD3_SEL; + break; + case HPD_SOURCEID4: + atom_hpd_sel = ATOM_TRANSMITTER_CONFIG_V5_HPD4_SEL; + break; + case HPD_SOURCEID5: + atom_hpd_sel = ATOM_TRANSMITTER_CONFIG_V5_HPD5_SEL; + break; + case HPD_SOURCEID6: + atom_hpd_sel = ATOM_TRANSMITTER_CONFIG_V5_HPD6_SEL; + break; + case HPD_SOURCEID_UNKNOWN: + default: + atom_hpd_sel = 0; + break; + } + return atom_hpd_sel >> 4; +} + +static uint8_t dig_encoder_sel_to_atom(enum engine_id id) +{ + /* On any ASIC after DCE80, we manually program the DIG_FE + * selection (see connect_dig_be_to_fe function of the link + * encoder), so translation should always return 0 (no FE). + */ + + return 0; +} + +static bool clock_source_id_to_atom( + enum clock_source_id id, + uint32_t *atom_pll_id) +{ + bool result = true; + + if (atom_pll_id != NULL) + switch (id) { + case CLOCK_SOURCE_ID_PLL0: + *atom_pll_id = ATOM_PPLL0; + break; + case CLOCK_SOURCE_ID_PLL1: + *atom_pll_id = ATOM_PPLL1; + break; + case CLOCK_SOURCE_ID_PLL2: + *atom_pll_id = ATOM_PPLL2; + break; + case CLOCK_SOURCE_ID_EXTERNAL: + *atom_pll_id = ATOM_PPLL_INVALID; + break; + case CLOCK_SOURCE_ID_DFS: + *atom_pll_id = ATOM_EXT_PLL1; + break; + case CLOCK_SOURCE_ID_VCE: + /* for VCE encoding, + * we need to pass in ATOM_PPLL_INVALID + */ + *atom_pll_id = ATOM_PPLL_INVALID; + break; + case CLOCK_SOURCE_ID_DP_DTO: + /* When programming DP DTO PLL ID should be invalid */ + *atom_pll_id = ATOM_PPLL_INVALID; + break; + case CLOCK_SOURCE_ID_UNDEFINED: + /* Should not happen */ + *atom_pll_id = ATOM_PPLL_INVALID; + result = false; + break; + default: + result = false; + break; + } + + return result; +} + +static bool engine_bp_to_atom(enum engine_id id, uint32_t *atom_engine_id) +{ + bool result = false; + + if (atom_engine_id != NULL) + switch (id) { + case ENGINE_ID_DIGA: + *atom_engine_id = ASIC_INT_DIG1_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGB: + *atom_engine_id = ASIC_INT_DIG2_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGC: + *atom_engine_id = ASIC_INT_DIG3_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGD: + *atom_engine_id = ASIC_INT_DIG4_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGE: + *atom_engine_id = ASIC_INT_DIG5_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGF: + *atom_engine_id = ASIC_INT_DIG6_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGG: + *atom_engine_id = ASIC_INT_DIG7_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DACA: + *atom_engine_id = ASIC_INT_DAC1_ENCODER_ID; + result = true; + break; + default: + break; + } + + return result; +} + +static uint8_t encoder_action_to_atom(enum bp_encoder_control_action action) +{ + uint8_t atom_action = 0; + + switch (action) { + case ENCODER_CONTROL_ENABLE: + atom_action = ATOM_ENABLE; + break; + case ENCODER_CONTROL_DISABLE: + atom_action = ATOM_DISABLE; + break; + case ENCODER_CONTROL_SETUP: + atom_action = ATOM_ENCODER_CMD_SETUP; + break; + case ENCODER_CONTROL_INIT: + atom_action = ATOM_ENCODER_INIT; + break; + default: + BREAK_TO_DEBUGGER(); /* Unhandle action in driver.!! */ + break; + } + + return atom_action; +} + +static uint8_t disp_power_gating_action_to_atom( + enum bp_pipe_control_action action) +{ + uint8_t atom_pipe_action = 0; + + switch (action) { + case ASIC_PIPE_DISABLE: + atom_pipe_action = ATOM_DISABLE; + break; + case ASIC_PIPE_ENABLE: + atom_pipe_action = ATOM_ENABLE; + break; + case ASIC_PIPE_INIT: + atom_pipe_action = ATOM_INIT; + break; + default: + ASSERT_CRITICAL(false); /* Unhandle action in driver! */ + break; + } + + return atom_pipe_action; +} + +/* function table */ +static const struct command_table_helper command_table_helper_funcs = { + .controller_id_to_atom = dal_cmd_table_helper_controller_id_to_atom, + .encoder_action_to_atom = encoder_action_to_atom, + .engine_bp_to_atom = engine_bp_to_atom, + .clock_source_id_to_atom = clock_source_id_to_atom, + .clock_source_id_to_atom_phy_clk_src_id = + clock_source_id_to_atom_phy_clk_src_id, + .signal_type_to_atom_dig_mode = signal_type_to_atom_dig_mode, + .hpd_sel_to_atom = hpd_sel_to_atom, + .dig_encoder_sel_to_atom = dig_encoder_sel_to_atom, + .phy_id_to_atom = phy_id_to_atom, + .disp_power_gating_action_to_atom = disp_power_gating_action_to_atom, + .assign_control_parameter = NULL, + .clock_source_id_to_ref_clk_src = NULL, + .transmitter_bp_to_atom = NULL, + .encoder_id_to_atom = dal_cmd_table_helper_encoder_id_to_atom, + .encoder_mode_bp_to_atom = dal_cmd_table_helper_encoder_mode_bp_to_atom, +}; + +/* + * dal_cmd_tbl_helper_dce110_get_table + * + * @brief + * Initialize command table helper functions + * + * @param + * const struct command_table_helper **h - [out] struct of functions + * + */ +const struct command_table_helper *dal_cmd_tbl_helper_dce110_get_table(void) +{ + return &command_table_helper_funcs; +} diff --git a/drivers/gpu/drm/amd/display/dc/bios/dce110/command_table_helper_dce110.h b/drivers/gpu/drm/amd/display/dc/bios/dce110/command_table_helper_dce110.h new file mode 100644 index 000000000..eb60c2ead --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/dce110/command_table_helper_dce110.h @@ -0,0 +1,34 @@ +/* + * 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 + * + */ + +#ifndef __DAL_COMMAND_TABLE_HELPER_DCE110_H__ +#define __DAL_COMMAND_TABLE_HELPER_DCE110_H__ + +struct command_table_helper; + +/* Initialize command table helper functions */ +const struct command_table_helper *dal_cmd_tbl_helper_dce110_get_table(void); + +#endif /* __DAL_COMMAND_TABLE_HELPER_DCE110_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/bios/dce112/command_table_helper2_dce112.c b/drivers/gpu/drm/amd/display/dc/bios/dce112/command_table_helper2_dce112.c new file mode 100644 index 000000000..755b6e331 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/dce112/command_table_helper2_dce112.c @@ -0,0 +1,390 @@ +/* + * 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 + * + */ + +#include "dm_services.h" + +#include "atom.h" + +#include "include/bios_parser_types.h" + +#include "../command_table_helper2.h" + +static uint8_t phy_id_to_atom(enum transmitter t) +{ + uint8_t atom_phy_id; + + switch (t) { + case TRANSMITTER_UNIPHY_A: + atom_phy_id = ATOM_PHY_ID_UNIPHYA; + break; + case TRANSMITTER_UNIPHY_B: + atom_phy_id = ATOM_PHY_ID_UNIPHYB; + break; + case TRANSMITTER_UNIPHY_C: + atom_phy_id = ATOM_PHY_ID_UNIPHYC; + break; + case TRANSMITTER_UNIPHY_D: + atom_phy_id = ATOM_PHY_ID_UNIPHYD; + break; + case TRANSMITTER_UNIPHY_E: + atom_phy_id = ATOM_PHY_ID_UNIPHYE; + break; + case TRANSMITTER_UNIPHY_F: + atom_phy_id = ATOM_PHY_ID_UNIPHYF; + break; + case TRANSMITTER_UNIPHY_G: + atom_phy_id = ATOM_PHY_ID_UNIPHYG; + break; + default: + atom_phy_id = ATOM_PHY_ID_UNIPHYA; + break; + } + return atom_phy_id; +} + +static uint8_t signal_type_to_atom_dig_mode(enum signal_type s) +{ + uint8_t atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V6_DP; + + switch (s) { + case SIGNAL_TYPE_DISPLAY_PORT: + case SIGNAL_TYPE_EDP: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V6_DP; + break; + case SIGNAL_TYPE_DVI_SINGLE_LINK: + case SIGNAL_TYPE_DVI_DUAL_LINK: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V6_DVI; + break; + case SIGNAL_TYPE_HDMI_TYPE_A: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V6_HDMI; + break; + case SIGNAL_TYPE_DISPLAY_PORT_MST: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V6_DP_MST; + break; + default: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V6_DVI; + break; + } + + return atom_dig_mode; +} + +static uint8_t clock_source_id_to_atom_phy_clk_src_id( + enum clock_source_id id) +{ + uint8_t atom_phy_clk_src_id = 0; + + switch (id) { + case CLOCK_SOURCE_ID_PLL0: + atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_P0PLL; + break; + case CLOCK_SOURCE_ID_PLL1: + atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_P1PLL; + break; + case CLOCK_SOURCE_ID_PLL2: + atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_P2PLL; + break; + case CLOCK_SOURCE_ID_EXTERNAL: + atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_REFCLK_SRC_EXT; + break; + default: + atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_P1PLL; + break; + } + + return atom_phy_clk_src_id >> 2; +} + +static uint8_t hpd_sel_to_atom(enum hpd_source_id id) +{ + uint8_t atom_hpd_sel = 0; + + switch (id) { + case HPD_SOURCEID1: + atom_hpd_sel = ATOM_TRANSMITTER_V6_HPD1_SEL; + break; + case HPD_SOURCEID2: + atom_hpd_sel = ATOM_TRANSMITTER_V6_HPD2_SEL; + break; + case HPD_SOURCEID3: + atom_hpd_sel = ATOM_TRANSMITTER_V6_HPD3_SEL; + break; + case HPD_SOURCEID4: + atom_hpd_sel = ATOM_TRANSMITTER_V6_HPD4_SEL; + break; + case HPD_SOURCEID5: + atom_hpd_sel = ATOM_TRANSMITTER_V6_HPD5_SEL; + break; + case HPD_SOURCEID6: + atom_hpd_sel = ATOM_TRANSMITTER_V6_HPD6_SEL; + break; + case HPD_SOURCEID_UNKNOWN: + default: + atom_hpd_sel = 0; + break; + } + return atom_hpd_sel; +} + +static uint8_t dig_encoder_sel_to_atom(enum engine_id id) +{ + /* On any ASIC after DCE80, we manually program the DIG_FE + * selection (see connect_dig_be_to_fe function of the link + * encoder), so translation should always return 0 (no FE). + */ + + return 0; +} + +static bool clock_source_id_to_atom( + enum clock_source_id id, + uint32_t *atom_pll_id) +{ + bool result = true; + + if (atom_pll_id != NULL) + switch (id) { + case CLOCK_SOURCE_COMBO_PHY_PLL0: + *atom_pll_id = ATOM_COMBOPHY_PLL0; + break; + case CLOCK_SOURCE_COMBO_PHY_PLL1: + *atom_pll_id = ATOM_COMBOPHY_PLL1; + break; + case CLOCK_SOURCE_COMBO_PHY_PLL2: + *atom_pll_id = ATOM_COMBOPHY_PLL2; + break; + case CLOCK_SOURCE_COMBO_PHY_PLL3: + *atom_pll_id = ATOM_COMBOPHY_PLL3; + break; + case CLOCK_SOURCE_COMBO_PHY_PLL4: + *atom_pll_id = ATOM_COMBOPHY_PLL4; + break; + case CLOCK_SOURCE_COMBO_PHY_PLL5: + *atom_pll_id = ATOM_COMBOPHY_PLL5; + break; + case CLOCK_SOURCE_COMBO_DISPLAY_PLL0: + *atom_pll_id = ATOM_PPLL0; + break; + case CLOCK_SOURCE_ID_DFS: + *atom_pll_id = ATOM_GCK_DFS; + break; + case CLOCK_SOURCE_ID_VCE: + *atom_pll_id = ATOM_DP_DTO; + break; + case CLOCK_SOURCE_ID_DP_DTO: + *atom_pll_id = ATOM_DP_DTO; + break; + case CLOCK_SOURCE_ID_UNDEFINED: + /* Should not happen */ + *atom_pll_id = ATOM_PPLL_INVALID; + result = false; + break; + default: + result = false; + break; + } + + return result; +} + +static bool engine_bp_to_atom(enum engine_id id, uint32_t *atom_engine_id) +{ + bool result = false; + + if (atom_engine_id != NULL) + switch (id) { + case ENGINE_ID_DIGA: + *atom_engine_id = ASIC_INT_DIG1_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGB: + *atom_engine_id = ASIC_INT_DIG2_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGC: + *atom_engine_id = ASIC_INT_DIG3_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGD: + *atom_engine_id = ASIC_INT_DIG4_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGE: + *atom_engine_id = ASIC_INT_DIG5_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGF: + *atom_engine_id = ASIC_INT_DIG6_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGG: + *atom_engine_id = ASIC_INT_DIG7_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DACA: + *atom_engine_id = ASIC_INT_DAC1_ENCODER_ID; + result = true; + break; + default: + break; + } + + return result; +} + +static uint8_t encoder_action_to_atom(enum bp_encoder_control_action action) +{ + uint8_t atom_action = 0; + + switch (action) { + case ENCODER_CONTROL_ENABLE: + atom_action = ATOM_ENABLE; + break; + case ENCODER_CONTROL_DISABLE: + atom_action = ATOM_DISABLE; + break; + case ENCODER_CONTROL_SETUP: + atom_action = ATOM_ENCODER_CMD_STREAM_SETUP; + break; + case ENCODER_CONTROL_INIT: + atom_action = ATOM_ENCODER_INIT; + break; + default: + BREAK_TO_DEBUGGER(); /* Unhandle action in driver.!! */ + break; + } + + return atom_action; +} + +static uint8_t disp_power_gating_action_to_atom( + enum bp_pipe_control_action action) +{ + uint8_t atom_pipe_action = 0; + + switch (action) { + case ASIC_PIPE_DISABLE: + atom_pipe_action = ATOM_DISABLE; + break; + case ASIC_PIPE_ENABLE: + atom_pipe_action = ATOM_ENABLE; + break; + case ASIC_PIPE_INIT: + atom_pipe_action = ATOM_INIT; + break; + default: + ASSERT_CRITICAL(false); /* Unhandle action in driver! */ + break; + } + + return atom_pipe_action; +} + +static bool dc_clock_type_to_atom( + enum bp_dce_clock_type id, + uint32_t *atom_clock_type) +{ + bool retCode = true; + + if (atom_clock_type != NULL) { + switch (id) { + case DCECLOCK_TYPE_DISPLAY_CLOCK: + *atom_clock_type = DCE_CLOCK_TYPE_DISPCLK; + break; + + case DCECLOCK_TYPE_DPREFCLK: + *atom_clock_type = DCE_CLOCK_TYPE_DPREFCLK; + break; + + default: + ASSERT_CRITICAL(false); /* Unhandle action in driver! */ + break; + } + } + + return retCode; +} + +static uint8_t transmitter_color_depth_to_atom(enum transmitter_color_depth id) +{ + uint8_t atomColorDepth = 0; + + switch (id) { + case TRANSMITTER_COLOR_DEPTH_24: + atomColorDepth = PIXEL_CLOCK_V7_DEEPCOLOR_RATIO_DIS; + break; + case TRANSMITTER_COLOR_DEPTH_30: + atomColorDepth = PIXEL_CLOCK_V7_DEEPCOLOR_RATIO_5_4; + break; + case TRANSMITTER_COLOR_DEPTH_36: + atomColorDepth = PIXEL_CLOCK_V7_DEEPCOLOR_RATIO_3_2; + break; + case TRANSMITTER_COLOR_DEPTH_48: + atomColorDepth = PIXEL_CLOCK_V7_DEEPCOLOR_RATIO_2_1; + break; + default: + ASSERT_CRITICAL(false); /* Unhandle action in driver! */ + break; + } + + return atomColorDepth; +} + +/* function table */ +static const struct command_table_helper command_table_helper_funcs = { + .controller_id_to_atom = dal_cmd_table_helper_controller_id_to_atom2, + .encoder_action_to_atom = encoder_action_to_atom, + .engine_bp_to_atom = engine_bp_to_atom, + .clock_source_id_to_atom = clock_source_id_to_atom, + .clock_source_id_to_atom_phy_clk_src_id = + clock_source_id_to_atom_phy_clk_src_id, + .signal_type_to_atom_dig_mode = signal_type_to_atom_dig_mode, + .hpd_sel_to_atom = hpd_sel_to_atom, + .dig_encoder_sel_to_atom = dig_encoder_sel_to_atom, + .phy_id_to_atom = phy_id_to_atom, + .disp_power_gating_action_to_atom = disp_power_gating_action_to_atom, + .clock_source_id_to_ref_clk_src = NULL, + .transmitter_bp_to_atom = NULL, + .encoder_id_to_atom = dal_cmd_table_helper_encoder_id_to_atom2, + .encoder_mode_bp_to_atom = + dal_cmd_table_helper_encoder_mode_bp_to_atom2, + .dc_clock_type_to_atom = dc_clock_type_to_atom, + .transmitter_color_depth_to_atom = transmitter_color_depth_to_atom, +}; + +/* + * dal_cmd_tbl_helper_dce110_get_table + * + * @brief + * Initialize command table helper functions + * + * @param + * const struct command_table_helper **h - [out] struct of functions + * + */ +const struct command_table_helper *dal_cmd_tbl_helper_dce112_get_table2(void) +{ + return &command_table_helper_funcs; +} diff --git a/drivers/gpu/drm/amd/display/dc/bios/dce112/command_table_helper2_dce112.h b/drivers/gpu/drm/amd/display/dc/bios/dce112/command_table_helper2_dce112.h new file mode 100644 index 000000000..abf28a06f --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/dce112/command_table_helper2_dce112.h @@ -0,0 +1,34 @@ +/* + * 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 + * + */ + +#ifndef __DAL_COMMAND_TABLE_HELPER2_DCE112_H__ +#define __DAL_COMMAND_TABLE_HELPER2_DCE112_H__ + +struct command_table_helper; + +/* Initialize command table helper functions */ +const struct command_table_helper *dal_cmd_tbl_helper_dce112_get_table2(void); + +#endif /* __DAL_COMMAND_TABLE_HELPER_DCE110_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/bios/dce112/command_table_helper_dce112.c b/drivers/gpu/drm/amd/display/dc/bios/dce112/command_table_helper_dce112.c new file mode 100644 index 000000000..06b4f7fa4 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/dce112/command_table_helper_dce112.c @@ -0,0 +1,390 @@ +/* + * 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 + * + */ + +#include "dm_services.h" + +#include "atom.h" + +#include "include/bios_parser_types.h" + +#include "../command_table_helper.h" + +static uint8_t phy_id_to_atom(enum transmitter t) +{ + uint8_t atom_phy_id; + + switch (t) { + case TRANSMITTER_UNIPHY_A: + atom_phy_id = ATOM_PHY_ID_UNIPHYA; + break; + case TRANSMITTER_UNIPHY_B: + atom_phy_id = ATOM_PHY_ID_UNIPHYB; + break; + case TRANSMITTER_UNIPHY_C: + atom_phy_id = ATOM_PHY_ID_UNIPHYC; + break; + case TRANSMITTER_UNIPHY_D: + atom_phy_id = ATOM_PHY_ID_UNIPHYD; + break; + case TRANSMITTER_UNIPHY_E: + atom_phy_id = ATOM_PHY_ID_UNIPHYE; + break; + case TRANSMITTER_UNIPHY_F: + atom_phy_id = ATOM_PHY_ID_UNIPHYF; + break; + case TRANSMITTER_UNIPHY_G: + atom_phy_id = ATOM_PHY_ID_UNIPHYG; + break; + default: + atom_phy_id = ATOM_PHY_ID_UNIPHYA; + break; + } + return atom_phy_id; +} + +static uint8_t signal_type_to_atom_dig_mode(enum signal_type s) +{ + uint8_t atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V6_DP; + + switch (s) { + case SIGNAL_TYPE_DISPLAY_PORT: + case SIGNAL_TYPE_EDP: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V6_DP; + break; + case SIGNAL_TYPE_DVI_SINGLE_LINK: + case SIGNAL_TYPE_DVI_DUAL_LINK: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V6_DVI; + break; + case SIGNAL_TYPE_HDMI_TYPE_A: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V6_HDMI; + break; + case SIGNAL_TYPE_DISPLAY_PORT_MST: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V6_DP_MST; + break; + default: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V6_DVI; + break; + } + + return atom_dig_mode; +} + +static uint8_t clock_source_id_to_atom_phy_clk_src_id( + enum clock_source_id id) +{ + uint8_t atom_phy_clk_src_id = 0; + + switch (id) { + case CLOCK_SOURCE_ID_PLL0: + atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_P0PLL; + break; + case CLOCK_SOURCE_ID_PLL1: + atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_P1PLL; + break; + case CLOCK_SOURCE_ID_PLL2: + atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_P2PLL; + break; + case CLOCK_SOURCE_ID_EXTERNAL: + atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_REFCLK_SRC_EXT; + break; + default: + atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_P1PLL; + break; + } + + return atom_phy_clk_src_id >> 2; +} + +static uint8_t hpd_sel_to_atom(enum hpd_source_id id) +{ + uint8_t atom_hpd_sel = 0; + + switch (id) { + case HPD_SOURCEID1: + atom_hpd_sel = ATOM_TRANSMITTER_V6_HPD1_SEL; + break; + case HPD_SOURCEID2: + atom_hpd_sel = ATOM_TRANSMITTER_V6_HPD2_SEL; + break; + case HPD_SOURCEID3: + atom_hpd_sel = ATOM_TRANSMITTER_V6_HPD3_SEL; + break; + case HPD_SOURCEID4: + atom_hpd_sel = ATOM_TRANSMITTER_V6_HPD4_SEL; + break; + case HPD_SOURCEID5: + atom_hpd_sel = ATOM_TRANSMITTER_V6_HPD5_SEL; + break; + case HPD_SOURCEID6: + atom_hpd_sel = ATOM_TRANSMITTER_V6_HPD6_SEL; + break; + case HPD_SOURCEID_UNKNOWN: + default: + atom_hpd_sel = 0; + break; + } + return atom_hpd_sel; +} + +static uint8_t dig_encoder_sel_to_atom(enum engine_id id) +{ + /* On any ASIC after DCE80, we manually program the DIG_FE + * selection (see connect_dig_be_to_fe function of the link + * encoder), so translation should always return 0 (no FE). + */ + + return 0; +} + +static bool clock_source_id_to_atom( + enum clock_source_id id, + uint32_t *atom_pll_id) +{ + bool result = true; + + if (atom_pll_id != NULL) + switch (id) { + case CLOCK_SOURCE_COMBO_PHY_PLL0: + *atom_pll_id = ATOM_COMBOPHY_PLL0; + break; + case CLOCK_SOURCE_COMBO_PHY_PLL1: + *atom_pll_id = ATOM_COMBOPHY_PLL1; + break; + case CLOCK_SOURCE_COMBO_PHY_PLL2: + *atom_pll_id = ATOM_COMBOPHY_PLL2; + break; + case CLOCK_SOURCE_COMBO_PHY_PLL3: + *atom_pll_id = ATOM_COMBOPHY_PLL3; + break; + case CLOCK_SOURCE_COMBO_PHY_PLL4: + *atom_pll_id = ATOM_COMBOPHY_PLL4; + break; + case CLOCK_SOURCE_COMBO_PHY_PLL5: + *atom_pll_id = ATOM_COMBOPHY_PLL5; + break; + case CLOCK_SOURCE_COMBO_DISPLAY_PLL0: + *atom_pll_id = ATOM_PPLL0; + break; + case CLOCK_SOURCE_ID_DFS: + *atom_pll_id = ATOM_GCK_DFS; + break; + case CLOCK_SOURCE_ID_VCE: + *atom_pll_id = ATOM_DP_DTO; + break; + case CLOCK_SOURCE_ID_DP_DTO: + *atom_pll_id = ATOM_DP_DTO; + break; + case CLOCK_SOURCE_ID_UNDEFINED: + /* Should not happen */ + *atom_pll_id = ATOM_PPLL_INVALID; + result = false; + break; + default: + result = false; + break; + } + + return result; +} + +static bool engine_bp_to_atom(enum engine_id id, uint32_t *atom_engine_id) +{ + bool result = false; + + if (atom_engine_id != NULL) + switch (id) { + case ENGINE_ID_DIGA: + *atom_engine_id = ASIC_INT_DIG1_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGB: + *atom_engine_id = ASIC_INT_DIG2_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGC: + *atom_engine_id = ASIC_INT_DIG3_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGD: + *atom_engine_id = ASIC_INT_DIG4_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGE: + *atom_engine_id = ASIC_INT_DIG5_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGF: + *atom_engine_id = ASIC_INT_DIG6_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGG: + *atom_engine_id = ASIC_INT_DIG7_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DACA: + *atom_engine_id = ASIC_INT_DAC1_ENCODER_ID; + result = true; + break; + default: + break; + } + + return result; +} + +static uint8_t encoder_action_to_atom(enum bp_encoder_control_action action) +{ + uint8_t atom_action = 0; + + switch (action) { + case ENCODER_CONTROL_ENABLE: + atom_action = ATOM_ENABLE; + break; + case ENCODER_CONTROL_DISABLE: + atom_action = ATOM_DISABLE; + break; + case ENCODER_CONTROL_SETUP: + atom_action = ATOM_ENCODER_CMD_STREAM_SETUP; + break; + case ENCODER_CONTROL_INIT: + atom_action = ATOM_ENCODER_INIT; + break; + default: + BREAK_TO_DEBUGGER(); /* Unhandle action in driver.!! */ + break; + } + + return atom_action; +} + +static uint8_t disp_power_gating_action_to_atom( + enum bp_pipe_control_action action) +{ + uint8_t atom_pipe_action = 0; + + switch (action) { + case ASIC_PIPE_DISABLE: + atom_pipe_action = ATOM_DISABLE; + break; + case ASIC_PIPE_ENABLE: + atom_pipe_action = ATOM_ENABLE; + break; + case ASIC_PIPE_INIT: + atom_pipe_action = ATOM_INIT; + break; + default: + ASSERT_CRITICAL(false); /* Unhandle action in driver! */ + break; + } + + return atom_pipe_action; +} + +static bool dc_clock_type_to_atom( + enum bp_dce_clock_type id, + uint32_t *atom_clock_type) +{ + bool retCode = true; + + if (atom_clock_type != NULL) { + switch (id) { + case DCECLOCK_TYPE_DISPLAY_CLOCK: + *atom_clock_type = DCE_CLOCK_TYPE_DISPCLK; + break; + + case DCECLOCK_TYPE_DPREFCLK: + *atom_clock_type = DCE_CLOCK_TYPE_DPREFCLK; + break; + + default: + ASSERT_CRITICAL(false); /* Unhandle action in driver! */ + break; + } + } + + return retCode; +} + +static uint8_t transmitter_color_depth_to_atom(enum transmitter_color_depth id) +{ + uint8_t atomColorDepth = 0; + + switch (id) { + case TRANSMITTER_COLOR_DEPTH_24: + atomColorDepth = PIXEL_CLOCK_V7_DEEPCOLOR_RATIO_DIS; + break; + case TRANSMITTER_COLOR_DEPTH_30: + atomColorDepth = PIXEL_CLOCK_V7_DEEPCOLOR_RATIO_5_4; + break; + case TRANSMITTER_COLOR_DEPTH_36: + atomColorDepth = PIXEL_CLOCK_V7_DEEPCOLOR_RATIO_3_2; + break; + case TRANSMITTER_COLOR_DEPTH_48: + atomColorDepth = PIXEL_CLOCK_V7_DEEPCOLOR_RATIO_2_1; + break; + default: + ASSERT_CRITICAL(false); /* Unhandle action in driver! */ + break; + } + + return atomColorDepth; +} + +/* function table */ +static const struct command_table_helper command_table_helper_funcs = { + .controller_id_to_atom = dal_cmd_table_helper_controller_id_to_atom, + .encoder_action_to_atom = encoder_action_to_atom, + .engine_bp_to_atom = engine_bp_to_atom, + .clock_source_id_to_atom = clock_source_id_to_atom, + .clock_source_id_to_atom_phy_clk_src_id = + clock_source_id_to_atom_phy_clk_src_id, + .signal_type_to_atom_dig_mode = signal_type_to_atom_dig_mode, + .hpd_sel_to_atom = hpd_sel_to_atom, + .dig_encoder_sel_to_atom = dig_encoder_sel_to_atom, + .phy_id_to_atom = phy_id_to_atom, + .disp_power_gating_action_to_atom = disp_power_gating_action_to_atom, + .assign_control_parameter = NULL, + .clock_source_id_to_ref_clk_src = NULL, + .transmitter_bp_to_atom = NULL, + .encoder_id_to_atom = dal_cmd_table_helper_encoder_id_to_atom, + .encoder_mode_bp_to_atom = dal_cmd_table_helper_encoder_mode_bp_to_atom, + .dc_clock_type_to_atom = dc_clock_type_to_atom, + .transmitter_color_depth_to_atom = transmitter_color_depth_to_atom, +}; + +/* + * dal_cmd_tbl_helper_dce110_get_table + * + * @brief + * Initialize command table helper functions + * + * @param + * const struct command_table_helper **h - [out] struct of functions + * + */ +const struct command_table_helper *dal_cmd_tbl_helper_dce112_get_table(void) +{ + return &command_table_helper_funcs; +} diff --git a/drivers/gpu/drm/amd/display/dc/bios/dce112/command_table_helper_dce112.h b/drivers/gpu/drm/amd/display/dc/bios/dce112/command_table_helper_dce112.h new file mode 100644 index 000000000..dc3660951 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/dce112/command_table_helper_dce112.h @@ -0,0 +1,34 @@ +/* + * 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 + * + */ + +#ifndef __DAL_COMMAND_TABLE_HELPER_DCE112_H__ +#define __DAL_COMMAND_TABLE_HELPER_DCE112_H__ + +struct command_table_helper; + +/* Initialize command table helper functions */ +const struct command_table_helper *dal_cmd_tbl_helper_dce112_get_table(void); + +#endif /* __DAL_COMMAND_TABLE_HELPER_DCE110_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/bios/dce60/command_table_helper_dce60.c b/drivers/gpu/drm/amd/display/dc/bios/dce60/command_table_helper_dce60.c new file mode 100644 index 000000000..710221b4f --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/dce60/command_table_helper_dce60.c @@ -0,0 +1,354 @@ +/* + * Copyright 2020 Mauro Rossi <issor.oruam@gmail.com> + * + * 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 + * + */ + +#include "dm_services.h" + +#include "atom.h" + +#include "include/grph_object_id.h" +#include "include/grph_object_defs.h" +#include "include/bios_parser_types.h" + +#include "../command_table_helper.h" + +static uint8_t encoder_action_to_atom(enum bp_encoder_control_action action) +{ + uint8_t atom_action = 0; + + switch (action) { + case ENCODER_CONTROL_ENABLE: + atom_action = ATOM_ENABLE; + break; + case ENCODER_CONTROL_DISABLE: + atom_action = ATOM_DISABLE; + break; + case ENCODER_CONTROL_SETUP: + atom_action = ATOM_ENCODER_CMD_SETUP; + break; + case ENCODER_CONTROL_INIT: + atom_action = ATOM_ENCODER_INIT; + break; + default: + BREAK_TO_DEBUGGER(); /* Unhandle action in driver.!! */ + break; + } + + return atom_action; +} + +static bool engine_bp_to_atom(enum engine_id id, uint32_t *atom_engine_id) +{ + bool result = false; + + if (atom_engine_id != NULL) + switch (id) { + case ENGINE_ID_DIGA: + *atom_engine_id = ASIC_INT_DIG1_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGB: + *atom_engine_id = ASIC_INT_DIG2_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGC: + *atom_engine_id = ASIC_INT_DIG3_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGD: + *atom_engine_id = ASIC_INT_DIG4_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGE: + *atom_engine_id = ASIC_INT_DIG5_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGF: + *atom_engine_id = ASIC_INT_DIG6_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGG: + *atom_engine_id = ASIC_INT_DIG7_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DACA: + *atom_engine_id = ASIC_INT_DAC1_ENCODER_ID; + result = true; + break; + default: + break; + } + + return result; +} + +static bool clock_source_id_to_atom( + enum clock_source_id id, + uint32_t *atom_pll_id) +{ + bool result = true; + + if (atom_pll_id != NULL) + switch (id) { + case CLOCK_SOURCE_ID_PLL0: + *atom_pll_id = ATOM_PPLL0; + break; + case CLOCK_SOURCE_ID_PLL1: + *atom_pll_id = ATOM_PPLL1; + break; + case CLOCK_SOURCE_ID_PLL2: + *atom_pll_id = ATOM_PPLL2; + break; + case CLOCK_SOURCE_ID_EXTERNAL: + *atom_pll_id = ATOM_PPLL_INVALID; + break; + case CLOCK_SOURCE_ID_DFS: + *atom_pll_id = ATOM_EXT_PLL1; + break; + case CLOCK_SOURCE_ID_VCE: + /* for VCE encoding, + * we need to pass in ATOM_PPLL_INVALID + */ + *atom_pll_id = ATOM_PPLL_INVALID; + break; + case CLOCK_SOURCE_ID_DP_DTO: + /* When programming DP DTO PLL ID should be invalid */ + *atom_pll_id = ATOM_PPLL_INVALID; + break; + case CLOCK_SOURCE_ID_UNDEFINED: + BREAK_TO_DEBUGGER(); /* check when this will happen! */ + *atom_pll_id = ATOM_PPLL_INVALID; + result = false; + break; + default: + result = false; + break; + } + + return result; +} + +static uint8_t clock_source_id_to_atom_phy_clk_src_id( + enum clock_source_id id) +{ + uint8_t atom_phy_clk_src_id = 0; + + switch (id) { + case CLOCK_SOURCE_ID_PLL0: + atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_P0PLL; + break; + case CLOCK_SOURCE_ID_PLL1: + atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_P1PLL; + break; + case CLOCK_SOURCE_ID_PLL2: + atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_P2PLL; + break; + case CLOCK_SOURCE_ID_EXTERNAL: + atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_REFCLK_SRC_EXT; + break; + default: + atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_P1PLL; + break; + } + + return atom_phy_clk_src_id >> 2; +} + +static uint8_t signal_type_to_atom_dig_mode(enum signal_type s) +{ + uint8_t atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_DP; + + switch (s) { + case SIGNAL_TYPE_DISPLAY_PORT: + case SIGNAL_TYPE_EDP: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_DP; + break; + case SIGNAL_TYPE_LVDS: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_LVDS; + break; + case SIGNAL_TYPE_DVI_SINGLE_LINK: + case SIGNAL_TYPE_DVI_DUAL_LINK: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_DVI; + break; + case SIGNAL_TYPE_HDMI_TYPE_A: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_HDMI; + break; + case SIGNAL_TYPE_DISPLAY_PORT_MST: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_DP_MST; + break; + default: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_DVI; + break; + } + + return atom_dig_mode; +} + +static uint8_t hpd_sel_to_atom(enum hpd_source_id id) +{ + uint8_t atom_hpd_sel = 0; + + switch (id) { + case HPD_SOURCEID1: + atom_hpd_sel = ATOM_TRANSMITTER_CONFIG_V5_HPD1_SEL; + break; + case HPD_SOURCEID2: + atom_hpd_sel = ATOM_TRANSMITTER_CONFIG_V5_HPD2_SEL; + break; + case HPD_SOURCEID3: + atom_hpd_sel = ATOM_TRANSMITTER_CONFIG_V5_HPD3_SEL; + break; + case HPD_SOURCEID4: + atom_hpd_sel = ATOM_TRANSMITTER_CONFIG_V5_HPD4_SEL; + break; + case HPD_SOURCEID5: + atom_hpd_sel = ATOM_TRANSMITTER_CONFIG_V5_HPD5_SEL; + break; + case HPD_SOURCEID6: + atom_hpd_sel = ATOM_TRANSMITTER_CONFIG_V5_HPD6_SEL; + break; + case HPD_SOURCEID_UNKNOWN: + default: + atom_hpd_sel = 0; + break; + } + return atom_hpd_sel >> 4; +} + +static uint8_t dig_encoder_sel_to_atom(enum engine_id id) +{ + uint8_t atom_dig_encoder_sel = 0; + + switch (id) { + case ENGINE_ID_DIGA: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGA_SEL; + break; + case ENGINE_ID_DIGB: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGB_SEL; + break; + case ENGINE_ID_DIGC: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGC_SEL; + break; + case ENGINE_ID_DIGD: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGD_SEL; + break; + case ENGINE_ID_DIGE: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGE_SEL; + break; + case ENGINE_ID_DIGF: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGF_SEL; + break; + case ENGINE_ID_DIGG: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGG_SEL; + break; + default: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGA_SEL; + break; + } + + return atom_dig_encoder_sel; +} + +static uint8_t phy_id_to_atom(enum transmitter t) +{ + uint8_t atom_phy_id; + + switch (t) { + case TRANSMITTER_UNIPHY_A: + atom_phy_id = ATOM_PHY_ID_UNIPHYA; + break; + case TRANSMITTER_UNIPHY_B: + atom_phy_id = ATOM_PHY_ID_UNIPHYB; + break; + case TRANSMITTER_UNIPHY_C: + atom_phy_id = ATOM_PHY_ID_UNIPHYC; + break; + case TRANSMITTER_UNIPHY_D: + atom_phy_id = ATOM_PHY_ID_UNIPHYD; + break; + case TRANSMITTER_UNIPHY_E: + atom_phy_id = ATOM_PHY_ID_UNIPHYE; + break; + case TRANSMITTER_UNIPHY_F: + atom_phy_id = ATOM_PHY_ID_UNIPHYF; + break; + case TRANSMITTER_UNIPHY_G: + atom_phy_id = ATOM_PHY_ID_UNIPHYG; + break; + default: + atom_phy_id = ATOM_PHY_ID_UNIPHYA; + break; + } + return atom_phy_id; +} + +static uint8_t disp_power_gating_action_to_atom( + enum bp_pipe_control_action action) +{ + uint8_t atom_pipe_action = 0; + + switch (action) { + case ASIC_PIPE_DISABLE: + atom_pipe_action = ATOM_DISABLE; + break; + case ASIC_PIPE_ENABLE: + atom_pipe_action = ATOM_ENABLE; + break; + case ASIC_PIPE_INIT: + atom_pipe_action = ATOM_INIT; + break; + default: + BREAK_TO_DEBUGGER(); /* Unhandle action in driver! */ + break; + } + + return atom_pipe_action; +} + +static const struct command_table_helper command_table_helper_funcs = { + .controller_id_to_atom = dal_cmd_table_helper_controller_id_to_atom, + .encoder_action_to_atom = encoder_action_to_atom, + .engine_bp_to_atom = engine_bp_to_atom, + .clock_source_id_to_atom = clock_source_id_to_atom, + .clock_source_id_to_atom_phy_clk_src_id = + clock_source_id_to_atom_phy_clk_src_id, + .signal_type_to_atom_dig_mode = signal_type_to_atom_dig_mode, + .hpd_sel_to_atom = hpd_sel_to_atom, + .dig_encoder_sel_to_atom = dig_encoder_sel_to_atom, + .phy_id_to_atom = phy_id_to_atom, + .disp_power_gating_action_to_atom = disp_power_gating_action_to_atom, + .assign_control_parameter = + dal_cmd_table_helper_assign_control_parameter, + .clock_source_id_to_ref_clk_src = + dal_cmd_table_helper_clock_source_id_to_ref_clk_src, + .transmitter_bp_to_atom = dal_cmd_table_helper_transmitter_bp_to_atom, + .encoder_id_to_atom = dal_cmd_table_helper_encoder_id_to_atom, + .encoder_mode_bp_to_atom = + dal_cmd_table_helper_encoder_mode_bp_to_atom, +}; + +const struct command_table_helper *dal_cmd_tbl_helper_dce60_get_table(void) +{ + return &command_table_helper_funcs; +} diff --git a/drivers/gpu/drm/amd/display/dc/bios/dce60/command_table_helper_dce60.h b/drivers/gpu/drm/amd/display/dc/bios/dce60/command_table_helper_dce60.h new file mode 100644 index 000000000..f733be553 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/dce60/command_table_helper_dce60.h @@ -0,0 +1,33 @@ +/* + * Copyright 2020 Mauro Rossi <issor.oruam@gmail.com> + * + * 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 + * + */ + +#ifndef __DAL_COMMAND_TABLE_HELPER_DCE60_H__ +#define __DAL_COMMAND_TABLE_HELPER_DCE60_H__ + +struct command_table_helper; + +const struct command_table_helper *dal_cmd_tbl_helper_dce60_get_table(void); + +#endif diff --git a/drivers/gpu/drm/amd/display/dc/bios/dce80/command_table_helper_dce80.c b/drivers/gpu/drm/amd/display/dc/bios/dce80/command_table_helper_dce80.c new file mode 100644 index 000000000..8b30b558c --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/dce80/command_table_helper_dce80.c @@ -0,0 +1,354 @@ +/* + * 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 + * + */ + +#include "dm_services.h" + +#include "atom.h" + +#include "include/grph_object_id.h" +#include "include/grph_object_defs.h" +#include "include/bios_parser_types.h" + +#include "../command_table_helper.h" + +static uint8_t encoder_action_to_atom(enum bp_encoder_control_action action) +{ + uint8_t atom_action = 0; + + switch (action) { + case ENCODER_CONTROL_ENABLE: + atom_action = ATOM_ENABLE; + break; + case ENCODER_CONTROL_DISABLE: + atom_action = ATOM_DISABLE; + break; + case ENCODER_CONTROL_SETUP: + atom_action = ATOM_ENCODER_CMD_SETUP; + break; + case ENCODER_CONTROL_INIT: + atom_action = ATOM_ENCODER_INIT; + break; + default: + BREAK_TO_DEBUGGER(); /* Unhandle action in driver.!! */ + break; + } + + return atom_action; +} + +static bool engine_bp_to_atom(enum engine_id id, uint32_t *atom_engine_id) +{ + bool result = false; + + if (atom_engine_id != NULL) + switch (id) { + case ENGINE_ID_DIGA: + *atom_engine_id = ASIC_INT_DIG1_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGB: + *atom_engine_id = ASIC_INT_DIG2_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGC: + *atom_engine_id = ASIC_INT_DIG3_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGD: + *atom_engine_id = ASIC_INT_DIG4_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGE: + *atom_engine_id = ASIC_INT_DIG5_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGF: + *atom_engine_id = ASIC_INT_DIG6_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DIGG: + *atom_engine_id = ASIC_INT_DIG7_ENCODER_ID; + result = true; + break; + case ENGINE_ID_DACA: + *atom_engine_id = ASIC_INT_DAC1_ENCODER_ID; + result = true; + break; + default: + break; + } + + return result; +} + +static bool clock_source_id_to_atom( + enum clock_source_id id, + uint32_t *atom_pll_id) +{ + bool result = true; + + if (atom_pll_id != NULL) + switch (id) { + case CLOCK_SOURCE_ID_PLL0: + *atom_pll_id = ATOM_PPLL0; + break; + case CLOCK_SOURCE_ID_PLL1: + *atom_pll_id = ATOM_PPLL1; + break; + case CLOCK_SOURCE_ID_PLL2: + *atom_pll_id = ATOM_PPLL2; + break; + case CLOCK_SOURCE_ID_EXTERNAL: + *atom_pll_id = ATOM_PPLL_INVALID; + break; + case CLOCK_SOURCE_ID_DFS: + *atom_pll_id = ATOM_EXT_PLL1; + break; + case CLOCK_SOURCE_ID_VCE: + /* for VCE encoding, + * we need to pass in ATOM_PPLL_INVALID + */ + *atom_pll_id = ATOM_PPLL_INVALID; + break; + case CLOCK_SOURCE_ID_DP_DTO: + /* When programming DP DTO PLL ID should be invalid */ + *atom_pll_id = ATOM_PPLL_INVALID; + break; + case CLOCK_SOURCE_ID_UNDEFINED: + BREAK_TO_DEBUGGER(); /* check when this will happen! */ + *atom_pll_id = ATOM_PPLL_INVALID; + result = false; + break; + default: + result = false; + break; + } + + return result; +} + +static uint8_t clock_source_id_to_atom_phy_clk_src_id( + enum clock_source_id id) +{ + uint8_t atom_phy_clk_src_id = 0; + + switch (id) { + case CLOCK_SOURCE_ID_PLL0: + atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_P0PLL; + break; + case CLOCK_SOURCE_ID_PLL1: + atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_P1PLL; + break; + case CLOCK_SOURCE_ID_PLL2: + atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_P2PLL; + break; + case CLOCK_SOURCE_ID_EXTERNAL: + atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_REFCLK_SRC_EXT; + break; + default: + atom_phy_clk_src_id = ATOM_TRANSMITTER_CONFIG_V5_P1PLL; + break; + } + + return atom_phy_clk_src_id >> 2; +} + +static uint8_t signal_type_to_atom_dig_mode(enum signal_type s) +{ + uint8_t atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_DP; + + switch (s) { + case SIGNAL_TYPE_DISPLAY_PORT: + case SIGNAL_TYPE_EDP: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_DP; + break; + case SIGNAL_TYPE_LVDS: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_LVDS; + break; + case SIGNAL_TYPE_DVI_SINGLE_LINK: + case SIGNAL_TYPE_DVI_DUAL_LINK: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_DVI; + break; + case SIGNAL_TYPE_HDMI_TYPE_A: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_HDMI; + break; + case SIGNAL_TYPE_DISPLAY_PORT_MST: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_DP_MST; + break; + default: + atom_dig_mode = ATOM_TRANSMITTER_DIGMODE_V5_DVI; + break; + } + + return atom_dig_mode; +} + +static uint8_t hpd_sel_to_atom(enum hpd_source_id id) +{ + uint8_t atom_hpd_sel = 0; + + switch (id) { + case HPD_SOURCEID1: + atom_hpd_sel = ATOM_TRANSMITTER_CONFIG_V5_HPD1_SEL; + break; + case HPD_SOURCEID2: + atom_hpd_sel = ATOM_TRANSMITTER_CONFIG_V5_HPD2_SEL; + break; + case HPD_SOURCEID3: + atom_hpd_sel = ATOM_TRANSMITTER_CONFIG_V5_HPD3_SEL; + break; + case HPD_SOURCEID4: + atom_hpd_sel = ATOM_TRANSMITTER_CONFIG_V5_HPD4_SEL; + break; + case HPD_SOURCEID5: + atom_hpd_sel = ATOM_TRANSMITTER_CONFIG_V5_HPD5_SEL; + break; + case HPD_SOURCEID6: + atom_hpd_sel = ATOM_TRANSMITTER_CONFIG_V5_HPD6_SEL; + break; + case HPD_SOURCEID_UNKNOWN: + default: + atom_hpd_sel = 0; + break; + } + return atom_hpd_sel >> 4; +} + +static uint8_t dig_encoder_sel_to_atom(enum engine_id id) +{ + uint8_t atom_dig_encoder_sel = 0; + + switch (id) { + case ENGINE_ID_DIGA: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGA_SEL; + break; + case ENGINE_ID_DIGB: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGB_SEL; + break; + case ENGINE_ID_DIGC: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGC_SEL; + break; + case ENGINE_ID_DIGD: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGD_SEL; + break; + case ENGINE_ID_DIGE: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGE_SEL; + break; + case ENGINE_ID_DIGF: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGF_SEL; + break; + case ENGINE_ID_DIGG: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGG_SEL; + break; + default: + atom_dig_encoder_sel = ATOM_TRANMSITTER_V5__DIGA_SEL; + break; + } + + return atom_dig_encoder_sel; +} + +static uint8_t phy_id_to_atom(enum transmitter t) +{ + uint8_t atom_phy_id; + + switch (t) { + case TRANSMITTER_UNIPHY_A: + atom_phy_id = ATOM_PHY_ID_UNIPHYA; + break; + case TRANSMITTER_UNIPHY_B: + atom_phy_id = ATOM_PHY_ID_UNIPHYB; + break; + case TRANSMITTER_UNIPHY_C: + atom_phy_id = ATOM_PHY_ID_UNIPHYC; + break; + case TRANSMITTER_UNIPHY_D: + atom_phy_id = ATOM_PHY_ID_UNIPHYD; + break; + case TRANSMITTER_UNIPHY_E: + atom_phy_id = ATOM_PHY_ID_UNIPHYE; + break; + case TRANSMITTER_UNIPHY_F: + atom_phy_id = ATOM_PHY_ID_UNIPHYF; + break; + case TRANSMITTER_UNIPHY_G: + atom_phy_id = ATOM_PHY_ID_UNIPHYG; + break; + default: + atom_phy_id = ATOM_PHY_ID_UNIPHYA; + break; + } + return atom_phy_id; +} + +static uint8_t disp_power_gating_action_to_atom( + enum bp_pipe_control_action action) +{ + uint8_t atom_pipe_action = 0; + + switch (action) { + case ASIC_PIPE_DISABLE: + atom_pipe_action = ATOM_DISABLE; + break; + case ASIC_PIPE_ENABLE: + atom_pipe_action = ATOM_ENABLE; + break; + case ASIC_PIPE_INIT: + atom_pipe_action = ATOM_INIT; + break; + default: + BREAK_TO_DEBUGGER(); /* Unhandle action in driver! */ + break; + } + + return atom_pipe_action; +} + +static const struct command_table_helper command_table_helper_funcs = { + .controller_id_to_atom = dal_cmd_table_helper_controller_id_to_atom, + .encoder_action_to_atom = encoder_action_to_atom, + .engine_bp_to_atom = engine_bp_to_atom, + .clock_source_id_to_atom = clock_source_id_to_atom, + .clock_source_id_to_atom_phy_clk_src_id = + clock_source_id_to_atom_phy_clk_src_id, + .signal_type_to_atom_dig_mode = signal_type_to_atom_dig_mode, + .hpd_sel_to_atom = hpd_sel_to_atom, + .dig_encoder_sel_to_atom = dig_encoder_sel_to_atom, + .phy_id_to_atom = phy_id_to_atom, + .disp_power_gating_action_to_atom = disp_power_gating_action_to_atom, + .assign_control_parameter = + dal_cmd_table_helper_assign_control_parameter, + .clock_source_id_to_ref_clk_src = + dal_cmd_table_helper_clock_source_id_to_ref_clk_src, + .transmitter_bp_to_atom = dal_cmd_table_helper_transmitter_bp_to_atom, + .encoder_id_to_atom = dal_cmd_table_helper_encoder_id_to_atom, + .encoder_mode_bp_to_atom = + dal_cmd_table_helper_encoder_mode_bp_to_atom, +}; + +const struct command_table_helper *dal_cmd_tbl_helper_dce80_get_table(void) +{ + return &command_table_helper_funcs; +} diff --git a/drivers/gpu/drm/amd/display/dc/bios/dce80/command_table_helper_dce80.h b/drivers/gpu/drm/amd/display/dc/bios/dce80/command_table_helper_dce80.h new file mode 100644 index 000000000..e675c359e --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/bios/dce80/command_table_helper_dce80.h @@ -0,0 +1,33 @@ +/* + * 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 + * + */ + +#ifndef __DAL_COMMAND_TABLE_HELPER_DCE80_H__ +#define __DAL_COMMAND_TABLE_HELPER_DCE80_H__ + +struct command_table_helper; + +const struct command_table_helper *dal_cmd_tbl_helper_dce80_get_table(void); + +#endif |