mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-03-22 23:46:49 +08:00
drm/amd/display: Implement DCE analog link encoders (v2)
We support two kinds of analog connections: 1. DVI-I, which allows both digital and analog signals: The DC code base only allows 1 encoder per connector, and the preferred engine type is still going to be digital. So, for DVI-I to work, we need to make sure the pre-existing link encoder can also work with analog signals. 1. VGA, which only supports analog signals: For VGA, we need to create a link encoder that only works with the DAC without perturbing any digital transmitter functionality. Since dce110_link_encoder already supports analog DVI-I, just reuse that code for VGA as well. v2: Reduce code churn by reusing same link encoder for VGA and DVI-I. 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:
committed by
Alex Deucher
parent
deb072d241
commit
0fbe321a93
@@ -302,6 +302,10 @@ static void setup_panel_mode(
|
|||||||
if (ctx->dc->caps.psp_setup_panel_mode)
|
if (ctx->dc->caps.psp_setup_panel_mode)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
/* The code below is only applicable to encoders with a digital transmitter. */
|
||||||
|
if (enc110->base.transmitter == TRANSMITTER_UNKNOWN)
|
||||||
|
return;
|
||||||
|
|
||||||
ASSERT(REG(DP_DPHY_INTERNAL_CTRL));
|
ASSERT(REG(DP_DPHY_INTERNAL_CTRL));
|
||||||
value = REG_READ(DP_DPHY_INTERNAL_CTRL);
|
value = REG_READ(DP_DPHY_INTERNAL_CTRL);
|
||||||
|
|
||||||
@@ -804,6 +808,33 @@ bool dce110_link_encoder_validate_dp_output(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool dce110_link_encoder_validate_rgb_output(
|
||||||
|
const struct dce110_link_encoder *enc110,
|
||||||
|
const struct dc_crtc_timing *crtc_timing)
|
||||||
|
{
|
||||||
|
/* When the VBIOS doesn't specify any limits, use 400 MHz.
|
||||||
|
* The value comes from amdgpu_atombios_get_clock_info.
|
||||||
|
*/
|
||||||
|
uint32_t max_pixel_clock_khz = 400000;
|
||||||
|
|
||||||
|
if (enc110->base.ctx->dc_bios->fw_info_valid &&
|
||||||
|
enc110->base.ctx->dc_bios->fw_info.max_pixel_clock) {
|
||||||
|
max_pixel_clock_khz =
|
||||||
|
enc110->base.ctx->dc_bios->fw_info.max_pixel_clock;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (crtc_timing->pix_clk_100hz > max_pixel_clock_khz * 10)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (crtc_timing->display_color_depth != COLOR_DEPTH_888)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (crtc_timing->pixel_encoding != PIXEL_ENCODING_RGB)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void dce110_link_encoder_construct(
|
void dce110_link_encoder_construct(
|
||||||
struct dce110_link_encoder *enc110,
|
struct dce110_link_encoder *enc110,
|
||||||
const struct encoder_init_data *init_data,
|
const struct encoder_init_data *init_data,
|
||||||
@@ -824,6 +855,7 @@ void dce110_link_encoder_construct(
|
|||||||
enc110->base.connector = init_data->connector;
|
enc110->base.connector = init_data->connector;
|
||||||
|
|
||||||
enc110->base.preferred_engine = ENGINE_ID_UNKNOWN;
|
enc110->base.preferred_engine = ENGINE_ID_UNKNOWN;
|
||||||
|
enc110->base.analog_engine = init_data->analog_engine;
|
||||||
|
|
||||||
enc110->base.features = *enc_features;
|
enc110->base.features = *enc_features;
|
||||||
|
|
||||||
@@ -847,6 +879,11 @@ void dce110_link_encoder_construct(
|
|||||||
SIGNAL_TYPE_EDP |
|
SIGNAL_TYPE_EDP |
|
||||||
SIGNAL_TYPE_HDMI_TYPE_A;
|
SIGNAL_TYPE_HDMI_TYPE_A;
|
||||||
|
|
||||||
|
if ((enc110->base.connector.id == CONNECTOR_ID_DUAL_LINK_DVII ||
|
||||||
|
enc110->base.connector.id == CONNECTOR_ID_SINGLE_LINK_DVII) &&
|
||||||
|
enc110->base.analog_engine != ENGINE_ID_UNKNOWN)
|
||||||
|
enc110->base.output_signals |= SIGNAL_TYPE_RGB;
|
||||||
|
|
||||||
/* For DCE 8.0 and 8.1, by design, UNIPHY is hardwired to DIG_BE.
|
/* For DCE 8.0 and 8.1, by design, UNIPHY is hardwired to DIG_BE.
|
||||||
* SW always assign DIG_FE 1:1 mapped to DIG_FE for non-MST UNIPHY.
|
* SW always assign DIG_FE 1:1 mapped to DIG_FE for non-MST UNIPHY.
|
||||||
* SW assign DIG_FE to non-MST UNIPHY first and MST last. So prefer
|
* SW assign DIG_FE to non-MST UNIPHY first and MST last. So prefer
|
||||||
@@ -885,6 +922,13 @@ void dce110_link_encoder_construct(
|
|||||||
enc110->base.preferred_engine = ENGINE_ID_DIGG;
|
enc110->base.preferred_engine = ENGINE_ID_DIGG;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
if (init_data->analog_engine != ENGINE_ID_UNKNOWN) {
|
||||||
|
/* The connector is analog-only, ie. VGA */
|
||||||
|
enc110->base.preferred_engine = init_data->analog_engine;
|
||||||
|
enc110->base.output_signals = SIGNAL_TYPE_RGB;
|
||||||
|
enc110->base.transmitter = TRANSMITTER_UNKNOWN;
|
||||||
|
break;
|
||||||
|
}
|
||||||
ASSERT_CRITICAL(false);
|
ASSERT_CRITICAL(false);
|
||||||
enc110->base.preferred_engine = ENGINE_ID_UNKNOWN;
|
enc110->base.preferred_engine = ENGINE_ID_UNKNOWN;
|
||||||
}
|
}
|
||||||
@@ -939,6 +983,10 @@ bool dce110_link_encoder_validate_output_with_stream(
|
|||||||
is_valid = dce110_link_encoder_validate_dp_output(
|
is_valid = dce110_link_encoder_validate_dp_output(
|
||||||
enc110, &stream->timing);
|
enc110, &stream->timing);
|
||||||
break;
|
break;
|
||||||
|
case SIGNAL_TYPE_RGB:
|
||||||
|
is_valid = dce110_link_encoder_validate_rgb_output(
|
||||||
|
enc110, &stream->timing);
|
||||||
|
break;
|
||||||
case SIGNAL_TYPE_EDP:
|
case SIGNAL_TYPE_EDP:
|
||||||
case SIGNAL_TYPE_LVDS:
|
case SIGNAL_TYPE_LVDS:
|
||||||
is_valid = stream->timing.pixel_encoding == PIXEL_ENCODING_RGB;
|
is_valid = stream->timing.pixel_encoding == PIXEL_ENCODING_RGB;
|
||||||
@@ -969,6 +1017,10 @@ void dce110_link_encoder_hw_init(
|
|||||||
cntl.coherent = false;
|
cntl.coherent = false;
|
||||||
cntl.hpd_sel = enc110->base.hpd_source;
|
cntl.hpd_sel = enc110->base.hpd_source;
|
||||||
|
|
||||||
|
/* The code below is only applicable to encoders with a digital transmitter. */
|
||||||
|
if (enc110->base.transmitter == TRANSMITTER_UNKNOWN)
|
||||||
|
return;
|
||||||
|
|
||||||
if (enc110->base.connector.id == CONNECTOR_ID_EDP)
|
if (enc110->base.connector.id == CONNECTOR_ID_EDP)
|
||||||
cntl.signal = SIGNAL_TYPE_EDP;
|
cntl.signal = SIGNAL_TYPE_EDP;
|
||||||
|
|
||||||
@@ -1034,6 +1086,8 @@ void dce110_link_encoder_setup(
|
|||||||
/* DP MST */
|
/* DP MST */
|
||||||
REG_UPDATE(DIG_BE_CNTL, DIG_MODE, 5);
|
REG_UPDATE(DIG_BE_CNTL, DIG_MODE, 5);
|
||||||
break;
|
break;
|
||||||
|
case SIGNAL_TYPE_RGB:
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
ASSERT_CRITICAL(false);
|
ASSERT_CRITICAL(false);
|
||||||
/* invalid mode ! */
|
/* invalid mode ! */
|
||||||
@@ -1282,6 +1336,24 @@ void dce110_link_encoder_disable_output(
|
|||||||
struct bp_transmitter_control cntl = { 0 };
|
struct bp_transmitter_control cntl = { 0 };
|
||||||
enum bp_result result;
|
enum bp_result result;
|
||||||
|
|
||||||
|
switch (enc->analog_engine) {
|
||||||
|
case ENGINE_ID_DACA:
|
||||||
|
REG_UPDATE(DAC_ENABLE, DAC_ENABLE, 0);
|
||||||
|
break;
|
||||||
|
case ENGINE_ID_DACB:
|
||||||
|
/* DACB doesn't seem to be present on DCE6+,
|
||||||
|
* although there are references to it in the register file.
|
||||||
|
*/
|
||||||
|
DC_LOG_ERROR("%s DACB is unsupported\n", __func__);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The code below only applies to connectors that support digital signals. */
|
||||||
|
if (enc->transmitter == TRANSMITTER_UNKNOWN)
|
||||||
|
return;
|
||||||
|
|
||||||
if (!dce110_is_dig_enabled(enc)) {
|
if (!dce110_is_dig_enabled(enc)) {
|
||||||
/* OF_SKIP_POWER_DOWN_INACTIVE_ENCODER */
|
/* OF_SKIP_POWER_DOWN_INACTIVE_ENCODER */
|
||||||
return;
|
return;
|
||||||
@@ -1726,6 +1798,7 @@ void dce60_link_encoder_construct(
|
|||||||
enc110->base.connector = init_data->connector;
|
enc110->base.connector = init_data->connector;
|
||||||
|
|
||||||
enc110->base.preferred_engine = ENGINE_ID_UNKNOWN;
|
enc110->base.preferred_engine = ENGINE_ID_UNKNOWN;
|
||||||
|
enc110->base.analog_engine = init_data->analog_engine;
|
||||||
|
|
||||||
enc110->base.features = *enc_features;
|
enc110->base.features = *enc_features;
|
||||||
|
|
||||||
@@ -1749,6 +1822,11 @@ void dce60_link_encoder_construct(
|
|||||||
SIGNAL_TYPE_EDP |
|
SIGNAL_TYPE_EDP |
|
||||||
SIGNAL_TYPE_HDMI_TYPE_A;
|
SIGNAL_TYPE_HDMI_TYPE_A;
|
||||||
|
|
||||||
|
if ((enc110->base.connector.id == CONNECTOR_ID_DUAL_LINK_DVII ||
|
||||||
|
enc110->base.connector.id == CONNECTOR_ID_SINGLE_LINK_DVII) &&
|
||||||
|
enc110->base.analog_engine != ENGINE_ID_UNKNOWN)
|
||||||
|
enc110->base.output_signals |= SIGNAL_TYPE_RGB;
|
||||||
|
|
||||||
/* For DCE 8.0 and 8.1, by design, UNIPHY is hardwired to DIG_BE.
|
/* For DCE 8.0 and 8.1, by design, UNIPHY is hardwired to DIG_BE.
|
||||||
* SW always assign DIG_FE 1:1 mapped to DIG_FE for non-MST UNIPHY.
|
* SW always assign DIG_FE 1:1 mapped to DIG_FE for non-MST UNIPHY.
|
||||||
* SW assign DIG_FE to non-MST UNIPHY first and MST last. So prefer
|
* SW assign DIG_FE to non-MST UNIPHY first and MST last. So prefer
|
||||||
@@ -1787,6 +1865,13 @@ void dce60_link_encoder_construct(
|
|||||||
enc110->base.preferred_engine = ENGINE_ID_DIGG;
|
enc110->base.preferred_engine = ENGINE_ID_DIGG;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
if (init_data->analog_engine != ENGINE_ID_UNKNOWN) {
|
||||||
|
/* The connector is analog-only, ie. VGA */
|
||||||
|
enc110->base.preferred_engine = init_data->analog_engine;
|
||||||
|
enc110->base.output_signals = SIGNAL_TYPE_RGB;
|
||||||
|
enc110->base.transmitter = TRANSMITTER_UNKNOWN;
|
||||||
|
break;
|
||||||
|
}
|
||||||
ASSERT_CRITICAL(false);
|
ASSERT_CRITICAL(false);
|
||||||
enc110->base.preferred_engine = ENGINE_ID_UNKNOWN;
|
enc110->base.preferred_engine = ENGINE_ID_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,18 +101,21 @@
|
|||||||
SRI(DP_SEC_CNTL, DP, id), \
|
SRI(DP_SEC_CNTL, DP, id), \
|
||||||
SRI(DP_VID_STREAM_CNTL, DP, id), \
|
SRI(DP_VID_STREAM_CNTL, DP, id), \
|
||||||
SRI(DP_DPHY_FAST_TRAINING, DP, id), \
|
SRI(DP_DPHY_FAST_TRAINING, DP, id), \
|
||||||
SRI(DP_SEC_CNTL1, DP, id)
|
SRI(DP_SEC_CNTL1, DP, id), \
|
||||||
|
SR(DAC_ENABLE)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define LE_DCE80_REG_LIST(id)\
|
#define LE_DCE80_REG_LIST(id)\
|
||||||
SRI(DP_DPHY_INTERNAL_CTRL, DP, id), \
|
SRI(DP_DPHY_INTERNAL_CTRL, DP, id), \
|
||||||
LE_COMMON_REG_LIST_BASE(id)
|
LE_COMMON_REG_LIST_BASE(id), \
|
||||||
|
SR(DAC_ENABLE)
|
||||||
|
|
||||||
#define LE_DCE100_REG_LIST(id)\
|
#define LE_DCE100_REG_LIST(id)\
|
||||||
LE_COMMON_REG_LIST_BASE(id), \
|
LE_COMMON_REG_LIST_BASE(id), \
|
||||||
SRI(DP_DPHY_BS_SR_SWAP_CNTL, DP, id), \
|
SRI(DP_DPHY_BS_SR_SWAP_CNTL, DP, id), \
|
||||||
SRI(DP_DPHY_INTERNAL_CTRL, DP, id), \
|
SRI(DP_DPHY_INTERNAL_CTRL, DP, id), \
|
||||||
SR(DCI_MEM_PWR_STATUS)
|
SR(DCI_MEM_PWR_STATUS), \
|
||||||
|
SR(DAC_ENABLE)
|
||||||
|
|
||||||
#define LE_DCE110_REG_LIST(id)\
|
#define LE_DCE110_REG_LIST(id)\
|
||||||
LE_COMMON_REG_LIST_BASE(id), \
|
LE_COMMON_REG_LIST_BASE(id), \
|
||||||
@@ -181,6 +184,9 @@ struct dce110_link_enc_registers {
|
|||||||
uint32_t DP_DPHY_BS_SR_SWAP_CNTL;
|
uint32_t DP_DPHY_BS_SR_SWAP_CNTL;
|
||||||
uint32_t DP_DPHY_HBR2_PATTERN_CONTROL;
|
uint32_t DP_DPHY_HBR2_PATTERN_CONTROL;
|
||||||
uint32_t DP_SEC_CNTL1;
|
uint32_t DP_SEC_CNTL1;
|
||||||
|
|
||||||
|
/* DAC registers */
|
||||||
|
uint32_t DAC_ENABLE;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct dce110_link_encoder {
|
struct dce110_link_encoder {
|
||||||
@@ -215,10 +221,6 @@ bool dce110_link_encoder_validate_dvi_output(
|
|||||||
enum signal_type signal,
|
enum signal_type signal,
|
||||||
const struct dc_crtc_timing *crtc_timing);
|
const struct dc_crtc_timing *crtc_timing);
|
||||||
|
|
||||||
bool dce110_link_encoder_validate_rgb_output(
|
|
||||||
const struct dce110_link_encoder *enc110,
|
|
||||||
const struct dc_crtc_timing *crtc_timing);
|
|
||||||
|
|
||||||
bool dce110_link_encoder_validate_dp_output(
|
bool dce110_link_encoder_validate_dp_output(
|
||||||
const struct dce110_link_encoder *enc110,
|
const struct dce110_link_encoder *enc110,
|
||||||
const struct dc_crtc_timing *crtc_timing);
|
const struct dc_crtc_timing *crtc_timing);
|
||||||
|
|||||||
@@ -225,6 +225,7 @@ static const struct dce110_link_enc_registers link_enc_regs[] = {
|
|||||||
link_regs(4),
|
link_regs(4),
|
||||||
link_regs(5),
|
link_regs(5),
|
||||||
link_regs(6),
|
link_regs(6),
|
||||||
|
{ .DAC_ENABLE = mmDAC_ENABLE },
|
||||||
};
|
};
|
||||||
|
|
||||||
#define stream_enc_regs(id)\
|
#define stream_enc_regs(id)\
|
||||||
@@ -632,7 +633,20 @@ static struct link_encoder *dce100_link_encoder_create(
|
|||||||
kzalloc(sizeof(struct dce110_link_encoder), GFP_KERNEL);
|
kzalloc(sizeof(struct dce110_link_encoder), GFP_KERNEL);
|
||||||
int link_regs_id;
|
int link_regs_id;
|
||||||
|
|
||||||
if (!enc110 || enc_init_data->hpd_source >= ARRAY_SIZE(link_enc_hpd_regs))
|
if (!enc110)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (enc_init_data->connector.id == CONNECTOR_ID_VGA) {
|
||||||
|
dce110_link_encoder_construct(enc110,
|
||||||
|
enc_init_data,
|
||||||
|
&link_enc_feature,
|
||||||
|
&link_enc_regs[ENGINE_ID_DACA],
|
||||||
|
NULL,
|
||||||
|
NULL);
|
||||||
|
return &enc110->base;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enc_init_data->hpd_source >= ARRAY_SIZE(link_enc_hpd_regs))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
link_regs_id =
|
link_regs_id =
|
||||||
|
|||||||
@@ -240,7 +240,9 @@ static const struct dce110_link_enc_registers link_enc_regs[] = {
|
|||||||
link_regs(2),
|
link_regs(2),
|
||||||
link_regs(3),
|
link_regs(3),
|
||||||
link_regs(4),
|
link_regs(4),
|
||||||
link_regs(5)
|
link_regs(5),
|
||||||
|
{0},
|
||||||
|
{ .DAC_ENABLE = mmDAC_ENABLE },
|
||||||
};
|
};
|
||||||
|
|
||||||
#define stream_enc_regs(id)\
|
#define stream_enc_regs(id)\
|
||||||
@@ -726,7 +728,20 @@ static struct link_encoder *dce60_link_encoder_create(
|
|||||||
kzalloc(sizeof(struct dce110_link_encoder), GFP_KERNEL);
|
kzalloc(sizeof(struct dce110_link_encoder), GFP_KERNEL);
|
||||||
int link_regs_id;
|
int link_regs_id;
|
||||||
|
|
||||||
if (!enc110 || enc_init_data->hpd_source >= ARRAY_SIZE(link_enc_hpd_regs))
|
if (!enc110)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (enc_init_data->connector.id == CONNECTOR_ID_VGA) {
|
||||||
|
dce110_link_encoder_construct(enc110,
|
||||||
|
enc_init_data,
|
||||||
|
&link_enc_feature,
|
||||||
|
&link_enc_regs[ENGINE_ID_DACA],
|
||||||
|
NULL,
|
||||||
|
NULL);
|
||||||
|
return &enc110->base;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enc_init_data->hpd_source >= ARRAY_SIZE(link_enc_hpd_regs))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
link_regs_id =
|
link_regs_id =
|
||||||
|
|||||||
@@ -241,6 +241,7 @@ static const struct dce110_link_enc_registers link_enc_regs[] = {
|
|||||||
link_regs(4),
|
link_regs(4),
|
||||||
link_regs(5),
|
link_regs(5),
|
||||||
link_regs(6),
|
link_regs(6),
|
||||||
|
{ .DAC_ENABLE = mmDAC_ENABLE },
|
||||||
};
|
};
|
||||||
|
|
||||||
#define stream_enc_regs(id)\
|
#define stream_enc_regs(id)\
|
||||||
@@ -734,7 +735,20 @@ static struct link_encoder *dce80_link_encoder_create(
|
|||||||
kzalloc(sizeof(struct dce110_link_encoder), GFP_KERNEL);
|
kzalloc(sizeof(struct dce110_link_encoder), GFP_KERNEL);
|
||||||
int link_regs_id;
|
int link_regs_id;
|
||||||
|
|
||||||
if (!enc110 || enc_init_data->hpd_source >= ARRAY_SIZE(link_enc_hpd_regs))
|
if (!enc110)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (enc_init_data->connector.id == CONNECTOR_ID_VGA) {
|
||||||
|
dce110_link_encoder_construct(enc110,
|
||||||
|
enc_init_data,
|
||||||
|
&link_enc_feature,
|
||||||
|
&link_enc_regs[ENGINE_ID_DACA],
|
||||||
|
NULL,
|
||||||
|
NULL);
|
||||||
|
return &enc110->base;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enc_init_data->hpd_source >= ARRAY_SIZE(link_enc_hpd_regs))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
link_regs_id =
|
link_regs_id =
|
||||||
|
|||||||
Reference in New Issue
Block a user