linux-brain/drivers/gpu/drm/amd/display/dc/bios/command_table.c

2413 lines
72 KiB
C
Raw Normal View History

/*
* 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 *)&params) == 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, &params);
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, &params);
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);
drm/amd/display: Fix HDMI deep color output for DCE 6-11. [ Upstream commit efa18405baa55a864c61d2f3cc6fe4d363818eb3 ] This fixes corrupted display output in HDMI deep color 10/12 bpc mode at least as observed on AMD Mullins, DCE-8.3. It will hopefully also provide fixes for other DCE's up to DCE-11, assuming those will need similar fixes, but i could not test that for HDMI due to lack of suitable hw, so viewer discretion is advised. dce110_stream_encoder_hdmi_set_stream_attribute() is used for HDMI setup on all DCE's and is missing color_depth assignment. dce110_program_pix_clk() is used for pixel clock setup on HDMI for DCE 6-11, and is missing color_depth assignment. Additionally some of the underlying Atombios specific encoder and pixelclock setup functions are missing code which is in the classic amdgpu kms modesetting path and the in the radeon kms driver for DCE6/DCE8. encoder_control_digx_v3() - Was missing setup code wrt. amdgpu and radeon kms classic drivers. Added here, but untested due to lack of suitable test hw. encoder_control_digx_v4() - Added missing setup code. Successfully tested on AMD mullins / DCE-8.3 with HDMI deep color output at 10 bpc and 12 bpc. Note that encoder_control_digx_v5() has proper setup code in place and is used, e.g., by DCE-11.2, but this code wasn't used for deep color setup due to the missing cntl.color_depth setup in the calling function for HDMI. set_pixel_clock_v5() - Missing setup code wrt. classic amdgpu/radeon kms. Added here, but untested due to lack of hw. set_pixel_clock_v6() - Missing setup code added. Successfully tested on AMD mullins DCE-8.3. This fixes corrupted display output at HDMI deep color output with 10 bpc or 12 bpc. Fixes: 4562236b3bc0 ("drm/amd/dc: Add dc display driver (v2)") Reviewed-by: Nicholas Kazlauskas <nicholas.kazlauskas@amd.com> Signed-off-by: Mario Kleiner <mario.kleiner.de@gmail.com> Cc: Harry Wentland <harry.wentland@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
2021-01-21 15:17:03 +09:00
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);
drm/amd/display: Fix HDMI deep color output for DCE 6-11. [ Upstream commit efa18405baa55a864c61d2f3cc6fe4d363818eb3 ] This fixes corrupted display output in HDMI deep color 10/12 bpc mode at least as observed on AMD Mullins, DCE-8.3. It will hopefully also provide fixes for other DCE's up to DCE-11, assuming those will need similar fixes, but i could not test that for HDMI due to lack of suitable hw, so viewer discretion is advised. dce110_stream_encoder_hdmi_set_stream_attribute() is used for HDMI setup on all DCE's and is missing color_depth assignment. dce110_program_pix_clk() is used for pixel clock setup on HDMI for DCE 6-11, and is missing color_depth assignment. Additionally some of the underlying Atombios specific encoder and pixelclock setup functions are missing code which is in the classic amdgpu kms modesetting path and the in the radeon kms driver for DCE6/DCE8. encoder_control_digx_v3() - Was missing setup code wrt. amdgpu and radeon kms classic drivers. Added here, but untested due to lack of suitable test hw. encoder_control_digx_v4() - Added missing setup code. Successfully tested on AMD mullins / DCE-8.3 with HDMI deep color output at 10 bpc and 12 bpc. Note that encoder_control_digx_v5() has proper setup code in place and is used, e.g., by DCE-11.2, but this code wasn't used for deep color setup due to the missing cntl.color_depth setup in the calling function for HDMI. set_pixel_clock_v5() - Missing setup code wrt. classic amdgpu/radeon kms. Added here, but untested due to lack of hw. set_pixel_clock_v6() - Missing setup code added. Successfully tested on AMD mullins DCE-8.3. This fixes corrupted display output at HDMI deep color output with 10 bpc or 12 bpc. Fixes: 4562236b3bc0 ("drm/amd/dc: Add dc display driver (v2)") Reviewed-by: Nicholas Kazlauskas <nicholas.kazlauskas@amd.com> Signed-off-by: Mario Kleiner <mario.kleiner.de@gmail.com> Cc: Harry Wentland <harry.wentland@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
2021-01-21 15:17:03 +09:00
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(&params, 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(&params, 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(&params, 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(&params, 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(&params, 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.
*/
drm/amd/display: Fix HDMI deep color output for DCE 6-11. [ Upstream commit efa18405baa55a864c61d2f3cc6fe4d363818eb3 ] This fixes corrupted display output in HDMI deep color 10/12 bpc mode at least as observed on AMD Mullins, DCE-8.3. It will hopefully also provide fixes for other DCE's up to DCE-11, assuming those will need similar fixes, but i could not test that for HDMI due to lack of suitable hw, so viewer discretion is advised. dce110_stream_encoder_hdmi_set_stream_attribute() is used for HDMI setup on all DCE's and is missing color_depth assignment. dce110_program_pix_clk() is used for pixel clock setup on HDMI for DCE 6-11, and is missing color_depth assignment. Additionally some of the underlying Atombios specific encoder and pixelclock setup functions are missing code which is in the classic amdgpu kms modesetting path and the in the radeon kms driver for DCE6/DCE8. encoder_control_digx_v3() - Was missing setup code wrt. amdgpu and radeon kms classic drivers. Added here, but untested due to lack of suitable test hw. encoder_control_digx_v4() - Added missing setup code. Successfully tested on AMD mullins / DCE-8.3 with HDMI deep color output at 10 bpc and 12 bpc. Note that encoder_control_digx_v5() has proper setup code in place and is used, e.g., by DCE-11.2, but this code wasn't used for deep color setup due to the missing cntl.color_depth setup in the calling function for HDMI. set_pixel_clock_v5() - Missing setup code wrt. classic amdgpu/radeon kms. Added here, but untested due to lack of hw. set_pixel_clock_v6() - Missing setup code added. Successfully tested on AMD mullins DCE-8.3. This fixes corrupted display output at HDMI deep color output with 10 bpc or 12 bpc. Fixes: 4562236b3bc0 ("drm/amd/dc: Add dc display driver (v2)") Reviewed-by: Nicholas Kazlauskas <nicholas.kazlauskas@amd.com> Signed-off-by: Mario Kleiner <mario.kleiner.de@gmail.com> Cc: Harry Wentland <harry.wentland@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
2021-01-21 15:17:03 +09:00
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.
*/
drm/amd/display: Fix HDMI deep color output for DCE 6-11. [ Upstream commit efa18405baa55a864c61d2f3cc6fe4d363818eb3 ] This fixes corrupted display output in HDMI deep color 10/12 bpc mode at least as observed on AMD Mullins, DCE-8.3. It will hopefully also provide fixes for other DCE's up to DCE-11, assuming those will need similar fixes, but i could not test that for HDMI due to lack of suitable hw, so viewer discretion is advised. dce110_stream_encoder_hdmi_set_stream_attribute() is used for HDMI setup on all DCE's and is missing color_depth assignment. dce110_program_pix_clk() is used for pixel clock setup on HDMI for DCE 6-11, and is missing color_depth assignment. Additionally some of the underlying Atombios specific encoder and pixelclock setup functions are missing code which is in the classic amdgpu kms modesetting path and the in the radeon kms driver for DCE6/DCE8. encoder_control_digx_v3() - Was missing setup code wrt. amdgpu and radeon kms classic drivers. Added here, but untested due to lack of suitable test hw. encoder_control_digx_v4() - Added missing setup code. Successfully tested on AMD mullins / DCE-8.3 with HDMI deep color output at 10 bpc and 12 bpc. Note that encoder_control_digx_v5() has proper setup code in place and is used, e.g., by DCE-11.2, but this code wasn't used for deep color setup due to the missing cntl.color_depth setup in the calling function for HDMI. set_pixel_clock_v5() - Missing setup code wrt. classic amdgpu/radeon kms. Added here, but untested due to lack of hw. set_pixel_clock_v6() - Missing setup code added. Successfully tested on AMD mullins DCE-8.3. This fixes corrupted display output at HDMI deep color output with 10 bpc or 12 bpc. Fixes: 4562236b3bc0 ("drm/amd/dc: Add dc display driver (v2)") Reviewed-by: Nicholas Kazlauskas <nicholas.kazlauskas@amd.com> Signed-off-by: Mario Kleiner <mario.kleiner.de@gmail.com> Cc: Harry Wentland <harry.wentland@amd.com> Signed-off-by: Alex Deucher <alexander.deucher@amd.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
2021-01-21 15:17:03 +09:00
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(&params, 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(&params, 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(&params, 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(&params, 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(
&params,
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(
&params,
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(&params, 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(&params, 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(&params, 0, sizeof(params));
cntl_params = &params.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(&params, 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;
}