drm/amd/display: Add DAC_LoadDetection to BIOS parser (v2)

DAC_LoadDetection can be used to determine whether something
is connected to an analog connector by determining if there is
an analog load. This causes visible flickering on displays, so
we only resort to using this when the connected display doesn't
have an EDID.

For reference, see the legacy display code:
amdgpu_atombios_encoder_dac_load_detect

v2:
Only clear corresponding bit from BIOS_SCRATCH_0.

Signed-off-by: Timur Kristóf <timur.kristof@gmail.com>
Reviewed-by: Harry Wentland <harry.wentland@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
Timur Kristóf
2025-09-26 20:01:59 +02:00
committed by Alex Deucher
parent 7f1d1c2f4d
commit d75e45b853
5 changed files with 155 additions and 0 deletions

View File

@@ -780,6 +780,54 @@ static enum bp_result bios_parser_encoder_control(
return bp->cmd_tbl.dig_encoder_control(bp, cntl);
}
static enum bp_result bios_parser_dac_load_detection(
struct dc_bios *dcb,
enum engine_id engine_id,
enum dal_device_type device_type,
uint32_t enum_id)
{
struct bios_parser *bp = BP_FROM_DCB(dcb);
struct dc_context *ctx = dcb->ctx;
struct bp_load_detection_parameters bp_params = {0};
enum bp_result bp_result;
uint32_t bios_0_scratch;
uint32_t device_id_mask = 0;
bp_params.engine_id = engine_id;
bp_params.device_id = get_support_mask_for_device_id(device_type, enum_id);
if (engine_id != ENGINE_ID_DACA &&
engine_id != ENGINE_ID_DACB)
return BP_RESULT_UNSUPPORTED;
if (!bp->cmd_tbl.dac_load_detection)
return BP_RESULT_UNSUPPORTED;
if (bp_params.device_id == ATOM_DEVICE_CRT1_SUPPORT)
device_id_mask = ATOM_S0_CRT1_MASK;
else if (bp_params.device_id == ATOM_DEVICE_CRT1_SUPPORT)
device_id_mask = ATOM_S0_CRT2_MASK;
else
return BP_RESULT_UNSUPPORTED;
/* BIOS will write the detected devices to BIOS_SCRATCH_0, clear corresponding bit */
bios_0_scratch = dm_read_reg(ctx, bp->base.regs->BIOS_SCRATCH_0);
bios_0_scratch &= ~device_id_mask;
dm_write_reg(ctx, bp->base.regs->BIOS_SCRATCH_0, bios_0_scratch);
bp_result = bp->cmd_tbl.dac_load_detection(bp, &bp_params);
if (bp_result != BP_RESULT_OK)
return bp_result;
bios_0_scratch = dm_read_reg(ctx, bp->base.regs->BIOS_SCRATCH_0);
if (bios_0_scratch & device_id_mask)
return BP_RESULT_OK;
return BP_RESULT_FAILURE;
}
static enum bp_result bios_parser_adjust_pixel_clock(
struct dc_bios *dcb,
struct bp_adjust_pixel_clock_parameters *bp_params)
@@ -2864,6 +2912,8 @@ static const struct dc_vbios_funcs vbios_funcs = {
.encoder_control = bios_parser_encoder_control,
.dac_load_detection = bios_parser_dac_load_detection,
.transmitter_control = bios_parser_transmitter_control,
.enable_crtc = bios_parser_enable_crtc,

View File

@@ -54,6 +54,7 @@ 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_select_crtc_source(struct bios_parser *bp);
static void init_dac_encoder_control(struct bios_parser *bp);
static void init_dac_load_detection(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);
@@ -72,6 +73,7 @@ void dal_bios_parser_init_cmd_tbl(struct bios_parser *bp)
init_adjust_display_pll(bp);
init_select_crtc_source(bp);
init_dac_encoder_control(bp);
init_dac_load_detection(bp);
init_dac_output_control(bp);
init_set_crtc_timing(bp);
init_enable_crtc(bp);
@@ -1902,6 +1904,96 @@ static enum bp_result dac2_encoder_control_v1(
return result;
}
/*******************************************************************************
********************************************************************************
**
** DAC LOAD DETECTION
**
********************************************************************************
*******************************************************************************/
static enum bp_result dac_load_detection_v1(
struct bios_parser *bp,
struct bp_load_detection_parameters *bp_params);
static enum bp_result dac_load_detection_v3(
struct bios_parser *bp,
struct bp_load_detection_parameters *bp_params);
static void init_dac_load_detection(struct bios_parser *bp)
{
switch (BIOS_CMD_TABLE_PARA_REVISION(DAC_LoadDetection)) {
case 1:
case 2:
bp->cmd_tbl.dac_load_detection = dac_load_detection_v1;
break;
case 3:
default:
bp->cmd_tbl.dac_load_detection = dac_load_detection_v3;
break;
}
}
static void dac_load_detect_prepare_params(
struct _DAC_LOAD_DETECTION_PS_ALLOCATION *params,
enum engine_id engine_id,
uint16_t device_id,
uint8_t misc)
{
uint8_t dac_type = ENGINE_ID_DACA;
if (engine_id == ENGINE_ID_DACB)
dac_type = ATOM_DAC_B;
params->sDacload.usDeviceID = cpu_to_le16(device_id);
params->sDacload.ucDacType = dac_type;
params->sDacload.ucMisc = misc;
}
static enum bp_result dac_load_detection_v1(
struct bios_parser *bp,
struct bp_load_detection_parameters *bp_params)
{
enum bp_result result = BP_RESULT_FAILURE;
DAC_LOAD_DETECTION_PS_ALLOCATION params;
dac_load_detect_prepare_params(
&params,
bp_params->engine_id,
bp_params->device_id,
0);
if (EXEC_BIOS_CMD_TABLE(DAC_LoadDetection, params))
result = BP_RESULT_OK;
return result;
}
static enum bp_result dac_load_detection_v3(
struct bios_parser *bp,
struct bp_load_detection_parameters *bp_params)
{
enum bp_result result = BP_RESULT_FAILURE;
DAC_LOAD_DETECTION_PS_ALLOCATION params;
uint8_t misc = 0;
if (bp_params->device_id == ATOM_DEVICE_CV_SUPPORT ||
bp_params->device_id == ATOM_DEVICE_TV1_SUPPORT)
misc = DAC_LOAD_MISC_YPrPb;
dac_load_detect_prepare_params(
&params,
bp_params->engine_id,
bp_params->device_id,
misc);
if (EXEC_BIOS_CMD_TABLE(DAC_LoadDetection, params))
result = BP_RESULT_OK;
return result;
}
/*******************************************************************************
********************************************************************************
**

View File

@@ -71,6 +71,9 @@ struct cmd_tbl {
enum bp_result (*dac2_output_control)(
struct bios_parser *bp,
bool enable);
enum bp_result (*dac_load_detection)(
struct bios_parser *bp,
struct bp_load_detection_parameters *bp_params);
enum bp_result (*set_crtc_timing)(
struct bios_parser *bp,
struct bp_hw_crtc_timing_parameters *bp_params);

View File

@@ -97,6 +97,11 @@ struct dc_vbios_funcs {
enum bp_result (*encoder_control)(
struct dc_bios *bios,
struct bp_encoder_control *cntl);
enum bp_result (*dac_load_detection)(
struct dc_bios *bios,
enum engine_id engine_id,
enum dal_device_type device_type,
uint32_t enum_id);
enum bp_result (*transmitter_control)(
struct dc_bios *bios,
struct bp_transmitter_control *cntl);

View File

@@ -162,6 +162,11 @@ struct bp_transmitter_control {
bool single_pll_mode;
};
struct bp_load_detection_parameters {
enum engine_id engine_id;
uint16_t device_id;
};
struct bp_hw_crtc_timing_parameters {
enum controller_id controller_id;
/* horizontal part */