/* * 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); 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); 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 * 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 */ 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) /* 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 (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 (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); 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. */ params.usV_SyncOffset = cpu_to_le16(le16_to_cpu(params.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 Inpute!! */ 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; }