/* * 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" #define DC_LOGGER \ bp->base.ctx->logger #define GET_INDEX_INTO_MASTER_TABLE(MasterOrData, FieldName)\ (((char *)(&((\ struct atom_master_list_of_##MasterOrData##_functions_v2_1 *)0)\ ->FieldName)-(char *)0)/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 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 = NULL; break; } } 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 (EXEC_BIOS_CMD_TABLE(digxencodercontrol, params)) result = BP_RESULT_OK; return result; } /***************************************************************************** ****************************************************************************** ** ** TRANSMITTER CONTROL ** ****************************************************************************** *****************************************************************************/ 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(dig1transmittercontrol, frev, crev) == false) BREAK_TO_DEBUGGER(); switch (crev) { 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_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); } /*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; } /****************************************************************************** ****************************************************************************** ** ** SET PIXEL CLOCK ** ****************************************************************************** *****************************************************************************/ 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 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_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 (EXEC_BIOS_CMD_TABLE(setpixelclock, clk)) 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 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. */ params.v_syncoffset = cpu_to_le16(le16_to_cpu(params.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 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; 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 (EXEC_BIOS_CMD_TABLE(enabledisppowergating, ps.param)) 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; 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; } 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); }