Skip to content

Instantly share code, notes, and snippets.

@darksylinc
Created August 3, 2021 15:32
Show Gist options
  • Save darksylinc/8dc3fd06627970a90d8477c9b25f3093 to your computer and use it in GitHub Desktop.
Save darksylinc/8dc3fd06627970a90d8477c9b25f3093 to your computer and use it in GitHub Desktop.
Full RGB Kernel 5.13.7 patch
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
index 2a4cd7d377bf..62c6934a6244 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
@@ -1215,6 +1215,33 @@ static const struct drm_prop_enum_list amdgpu_dither_enum_list[] =
{ AMDGPU_FMT_DITHER_ENABLE, "on" },
};
+static const struct drm_prop_enum_list amdgpu_user_pixenc_list[] = {
+ { 0, "auto" },
+ { DRM_COLOR_FORMAT_RGB444, "rgb" },
+ { DRM_COLOR_FORMAT_YCRCB444, "ycbcr444" },
+ { DRM_COLOR_FORMAT_YCRCB420, "ycbcr420" },
+};
+
+bool amdgpu_user_pixenc_from_name(
+ unsigned int *user_pixenc,
+ const char *pixenc_name)
+{
+ bool found = false;
+
+ if (pixenc_name && (*pixenc_name != '\0')) {
+ const int sz = ARRAY_SIZE(amdgpu_user_pixenc_list);
+ int i;
+
+ for (i = 0; !found && i < sz; ++i) {
+ if (strcmp(pixenc_name, amdgpu_user_pixenc_list[i].name) == 0) {
+ *user_pixenc = amdgpu_user_pixenc_list[i].type;
+ found = true;
+ }
+ }
+ }
+ return found;
+}
+
int amdgpu_display_modeset_create_props(struct amdgpu_device *adev)
{
int sz;
@@ -1267,6 +1294,14 @@ int amdgpu_display_modeset_create_props(struct amdgpu_device *adev)
"abm level", 0, 4);
if (!adev->mode_info.abm_level_property)
return -ENOMEM;
+
+ sz = ARRAY_SIZE(amdgpu_user_pixenc_list);
+ adev->mode_info.pixel_encoding_property =
+ drm_property_create_enum(adev_to_drm(adev), 0,
+ "pixel encoding",
+ amdgpu_user_pixenc_list, sz);
+ if (!adev->mode_info.pixel_encoding_property)
+ return -ENOMEM;
}
return 0;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.h
index 7b6d83e2b13c..f67ae02fb438 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.h
@@ -50,4 +50,7 @@ amdgpu_lookup_format_info(u32 format, uint64_t modifier);
int amdgpu_display_suspend_helper(struct amdgpu_device *adev);
int amdgpu_display_resume_helper(struct amdgpu_device *adev);
+bool amdgpu_user_pixenc_from_name(unsigned int *user_pixenc,
+ const char *pixenc_name);
+
#endif
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h
index cb0b581bbce7..8c6ede86c524 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h
@@ -337,6 +337,8 @@ struct amdgpu_mode_info {
struct drm_property *dither_property;
/* Adaptive Backlight Modulation (power feature) */
struct drm_property *abm_level_property;
+ /* User HDMI pixel encoding override */
+ struct drm_property *pixel_encoding_property;
/* hardcoded DFP edid from BIOS */
struct edid *bios_hardcoded_edid;
int bios_hardcoded_edid_size;
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index eeaae1cf2bc2..287fee85707c 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -5174,6 +5174,115 @@ static bool adjust_colour_depth_from_display_info(
return false;
}
+/* convert an pixel encoding property value to a dc_pixel_encoding */
+static bool drm_prop_to_dc_pixel_encoding(
+ enum dc_pixel_encoding *dc_pixenc,
+ unsigned int propval)
+{
+ bool ret = false;
+
+ switch (propval) {
+ case 0:
+ *dc_pixenc = PIXEL_ENCODING_UNDEFINED;
+ ret = true;
+ break;
+ case DRM_COLOR_FORMAT_RGB444:
+ *dc_pixenc = PIXEL_ENCODING_RGB;
+ ret = true;
+ break;
+ case DRM_COLOR_FORMAT_YCRCB444:
+ *dc_pixenc = PIXEL_ENCODING_YCBCR444;
+ ret = true;
+ break;
+ case DRM_COLOR_FORMAT_YCRCB420:
+ *dc_pixenc = PIXEL_ENCODING_YCBCR420;
+ ret = true;
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+/* convert an dc_pixel_encoding to a pixel encoding property value */
+static unsigned int dc_pixel_encoding_to_drm_prop(
+ enum dc_pixel_encoding pixel_encoding)
+{
+ unsigned int propval = 0;
+
+ switch (pixel_encoding) {
+ case PIXEL_ENCODING_RGB:
+ propval = DRM_COLOR_FORMAT_RGB444;
+ break;
+ case PIXEL_ENCODING_YCBCR444:
+ propval = DRM_COLOR_FORMAT_YCRCB444;
+ break;
+ case PIXEL_ENCODING_YCBCR420:
+ propval = DRM_COLOR_FORMAT_YCRCB420;
+ break;
+ default:
+ break;
+ }
+ return propval;
+}
+
+/*
+ * Tries to read 'pixel_encoding' from the pixel_encoding DRM property on
+ * 'state'. Returns true if a supported, acceptable, non-undefined value is
+ * found; false otherwise. Only modifies 'pixel_encoding' if returning true.
+ */
+bool get_connector_state_pixel_encoding(
+ enum dc_pixel_encoding *pixel_encoding,
+ const struct drm_connector_state *state,
+ const struct drm_display_info *info,
+ const struct drm_display_mode *mode_in)
+{
+ bool ret = false;
+ struct dm_connector_state *dm_state;
+
+ dm_state = to_dm_connector_state(state);
+ if (!dm_state)
+ return false;
+
+ /* check encoding is supported */
+ switch (dm_state->pixel_encoding) {
+ case PIXEL_ENCODING_RGB:
+ ret = (info->color_formats & DRM_COLOR_FORMAT_RGB444);
+ break;
+ case PIXEL_ENCODING_YCBCR444:
+ ret = (info->color_formats & DRM_COLOR_FORMAT_YCRCB444);
+ break;
+ case PIXEL_ENCODING_YCBCR420:
+ ret = drm_mode_is_420(info, mode_in);
+ break;
+ default:
+ break;
+ }
+
+ if (ret)
+ *pixel_encoding = dm_state->pixel_encoding;
+
+ return ret;
+}
+
+/*
+ * Writes 'pixel_encoding' to the pixel_encoding DRM property on 'state', if
+ * the enum value is valid and supported; otherwise writes
+ * PIXEL_ENCODING_UNDEFINED which corresponds to the "auto" property state.
+ */
+void set_connector_state_pixel_encoding(
+ const struct drm_connector_state *state,
+ enum dc_pixel_encoding pixel_encoding)
+{
+ struct dm_connector_state *dm_state;
+
+ dm_state = to_dm_connector_state(state);
+ if (!dm_state)
+ return;
+
+ dm_state->pixel_encoding = pixel_encoding;
+}
+
static void fill_stream_properties_from_drm_display_mode(
struct dc_stream_state *stream,
const struct drm_display_mode *mode_in,
@@ -5195,18 +5304,21 @@ static void fill_stream_properties_from_drm_display_mode(
timing_out->h_border_right = 0;
timing_out->v_border_top = 0;
timing_out->v_border_bottom = 0;
- /* TODO: un-hardcode */
- if (drm_mode_is_420_only(info, mode_in)
- && stream->signal == SIGNAL_TYPE_HDMI_TYPE_A)
- timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420;
- else if (drm_mode_is_420_also(info, mode_in)
- && aconnector->force_yuv420_output)
- timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420;
- else if ((connector->display_info.color_formats & DRM_COLOR_FORMAT_YCRCB444)
- && stream->signal == SIGNAL_TYPE_HDMI_TYPE_A)
- timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR444;
- else
- timing_out->pixel_encoding = PIXEL_ENCODING_RGB;
+
+ if (!get_connector_state_pixel_encoding(&timing_out->pixel_encoding,
+ connector_state, info, mode_in)) {
+ /* auto-select a pixel encoding */
+ if (drm_mode_is_420_only(info, mode_in) &&
+ stream->signal == SIGNAL_TYPE_HDMI_TYPE_A)
+ timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420;
+ else if (drm_mode_is_420_also(info, mode_in) && aconnector->force_yuv420_output)
+ timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420;
+ else if ((connector->display_info.color_formats & DRM_COLOR_FORMAT_YCRCB444)
+ && stream->signal == SIGNAL_TYPE_HDMI_TYPE_A)
+ timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR444;
+ else
+ timing_out->pixel_encoding = PIXEL_ENCODING_RGB;
+ }
timing_out->timing_3d_format = TIMING_3D_FORMAT_NONE;
timing_out->display_color_depth = convert_color_depth_from_display_info(
@@ -5271,6 +5383,9 @@ static void fill_stream_properties_from_drm_display_mode(
adjust_colour_depth_from_display_info(timing_out, info);
}
}
+
+ /* write back final choice of pixel encoding */
+ set_connector_state_pixel_encoding(connector_state, timing_out->pixel_encoding);
}
static void fill_audio_info(struct audio_info *audio_info,
@@ -5946,6 +6061,9 @@ int amdgpu_dm_connector_atomic_set_property(struct drm_connector *connector,
} else if (property == adev->mode_info.abm_level_property) {
dm_new_state->abm_level = val;
ret = 0;
+ } else if (property == adev->mode_info.pixel_encoding_property) {
+ if (drm_prop_to_dc_pixel_encoding(&dm_new_state->pixel_encoding, val))
+ ret = 0;
}
return ret;
@@ -5991,6 +6109,9 @@ int amdgpu_dm_connector_atomic_get_property(struct drm_connector *connector,
} else if (property == adev->mode_info.abm_level_property) {
*val = dm_state->abm_level;
ret = 0;
+ } else if (property == adev->mode_info.pixel_encoding_property) {
+ *val = dc_pixel_encoding_to_drm_prop(dm_state->pixel_encoding);
+ ret = 0;
}
return ret;
@@ -6070,6 +6191,20 @@ void amdgpu_dm_connector_funcs_reset(struct drm_connector *connector)
if (connector->connector_type == DRM_MODE_CONNECTOR_eDP)
state->abm_level = amdgpu_dm_abm_level;
+ switch (connector->cmdline_mode.pixel_encoding) {
+ case DRM_COLOR_FORMAT_RGB444:
+ state->pixel_encoding = PIXEL_ENCODING_RGB;
+ break;
+ case DRM_COLOR_FORMAT_YCRCB444:
+ state->pixel_encoding = PIXEL_ENCODING_YCBCR444;
+ break;
+ case DRM_COLOR_FORMAT_YCRCB420:
+ state->pixel_encoding = PIXEL_ENCODING_YCBCR420;
+ break;
+ default:
+ break;
+ }
+
__drm_atomic_helper_connector_reset(connector, &state->base);
}
}
@@ -6096,6 +6231,7 @@ amdgpu_dm_connector_atomic_duplicate_state(struct drm_connector *connector)
new_state->underscan_vborder = state->underscan_vborder;
new_state->vcpi_slots = state->vcpi_slots;
new_state->pbn = state->pbn;
+ new_state->pixel_encoding = state->pixel_encoding;
return &new_state->base;
}
@@ -7576,6 +7712,12 @@ void amdgpu_dm_connector_init_helper(struct amdgpu_display_manager *dm,
if (adev->dm.hdcp_workqueue)
drm_connector_attach_content_protection_property(&aconnector->base, true);
#endif
+
+ if (adev->mode_info.pixel_encoding_property) {
+ drm_object_attach_property(&aconnector->base.base,
+ adev->mode_info.pixel_encoding_property, 0);
+ DRM_DEBUG_DRIVER("amdgpu: attached pixel encoding drm property");
+ }
}
}
@@ -8636,6 +8778,38 @@ static void amdgpu_dm_crtc_copy_transient_flags(struct drm_crtc_state *crtc_stat
stream_state->mode_changed = drm_atomic_crtc_needs_modeset(crtc_state);
}
+static void update_stream_for_pixel_encoding(
+ struct dc_stream_update *stream_update,
+ struct drm_connector *connector,
+ struct dm_crtc_state *dm_old_crtc_state,
+ struct dm_crtc_state *dm_new_crtc_state,
+ struct dm_connector_state *dm_new_con_state)
+{
+ struct amdgpu_dm_connector *aconnector =
+ to_amdgpu_dm_connector(connector);
+ struct dc_stream_state *new_stream = NULL;
+
+ if (aconnector)
+ new_stream = create_validate_stream_for_sink(
+ aconnector,
+ &dm_new_crtc_state->base.mode,
+ dm_new_con_state,
+ dm_old_crtc_state->stream);
+ if (new_stream) {
+ dm_new_crtc_state->stream->timing =
+ new_stream->timing;
+ stream_update->timing_for_pixel_encoding =
+ &dm_new_crtc_state->stream->timing;
+
+ dm_new_crtc_state->stream->output_color_space =
+ new_stream->output_color_space;
+ stream_update->output_color_space =
+ &dm_new_crtc_state->stream->output_color_space;
+
+ dc_stream_release(new_stream);
+ }
+}
+
/**
* amdgpu_dm_atomic_commit_tail() - AMDgpu DM's commit tail implementation.
* @state: The atomic state to commit
@@ -8848,7 +9022,7 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state)
struct dc_stream_update stream_update;
struct dc_info_packet hdr_packet;
struct dc_stream_status *status = NULL;
- bool abm_changed, hdr_changed, scaling_changed;
+ bool abm_changed, hdr_changed, scaling_changed, pixenc_changed;
memset(&dummy_updates, 0, sizeof(dummy_updates));
memset(&stream_update, 0, sizeof(stream_update));
@@ -8874,7 +9048,10 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state)
hdr_changed =
is_hdr_metadata_different(old_con_state, new_con_state);
- if (!scaling_changed && !abm_changed && !hdr_changed)
+ pixenc_changed = dm_new_con_state->pixel_encoding !=
+ dm_old_con_state->pixel_encoding;
+
+ if (!scaling_changed && !abm_changed && !hdr_changed && !pixenc_changed)
continue;
stream_update.stream = dm_new_crtc_state->stream;
@@ -8897,6 +9074,13 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state)
stream_update.hdr_static_metadata = &hdr_packet;
}
+ if (pixenc_changed) {
+ update_stream_for_pixel_encoding(&stream_update,
+ connector,
+ dm_old_crtc_state, dm_new_crtc_state,
+ dm_new_con_state);
+ }
+
status = dc_stream_get_status(dm_new_crtc_state->stream);
WARN_ON(!status);
WARN_ON(!status->plane_count);
@@ -10044,6 +10228,12 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
if (dm_old_con_state->abm_level !=
dm_new_con_state->abm_level)
new_crtc_state->connectors_changed = true;
+
+ if (dm_old_con_state->pixel_encoding !=
+ dm_new_con_state->pixel_encoding) {
+ new_crtc_state->connectors_changed = true;
+ new_crtc_state->mode_changed = true;
+ }
}
#if defined(CONFIG_DRM_AMD_DC_DCN)
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
index c8e5bbbb8bce..cbbb22f06c32 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
@@ -541,6 +541,7 @@ struct dm_connector_state {
uint8_t abm_level;
int vcpi_slots;
uint64_t pbn;
+ enum dc_pixel_encoding pixel_encoding;
};
struct amdgpu_hdmi_vsdb_info {
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c
index a869702d77af..3a3317dc8de5 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc.c
@@ -2141,6 +2141,11 @@ static enum surface_update_type check_update_surfaces_for_stream(
if (stream_update->output_csc_transform || stream_update->output_color_space)
su_flags->bits.out_csc = 1;
+
+ if (stream_update->timing_for_pixel_encoding) {
+ su_flags->bits.pixel_encoding = 1;
+ elevate_update_type(&overall_type, UPDATE_TYPE_FULL);
+ }
}
for (i = 0 ; i < surface_count; i++) {
@@ -2446,6 +2451,10 @@ static void copy_stream_update_to_stream(struct dc *dc,
update->dsc_config = NULL;
}
}
+
+ if (update->timing_for_pixel_encoding) {
+ stream->timing = *update->timing_for_pixel_encoding;
+ }
}
static void commit_planes_do_stream_update(struct dc *dc,
@@ -2473,7 +2482,8 @@ static void commit_planes_do_stream_update(struct dc *dc,
if ((stream_update->hdr_static_metadata && !stream->use_dynamic_meta) ||
stream_update->vrr_infopacket ||
stream_update->vsc_infopacket ||
- stream_update->vsp_infopacket) {
+ stream_update->vsp_infopacket ||
+ stream_update->timing_for_pixel_encoding) {
resource_build_info_frame(pipe_ctx);
dc->hwss.update_info_frame(pipe_ctx);
}
diff --git a/drivers/gpu/drm/amd/display/dc/dc_stream.h b/drivers/gpu/drm/amd/display/dc/dc_stream.h
index 13dae7238a58..d5f128a69452 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_stream.h
+++ b/drivers/gpu/drm/amd/display/dc/dc_stream.h
@@ -125,6 +125,7 @@ union stream_update_flags {
uint32_t gamut_remap:1;
uint32_t wb_update:1;
uint32_t dsc_changed : 1;
+ uint32_t pixel_encoding:1;
} bits;
uint32_t raw;
@@ -277,6 +278,7 @@ struct dc_stream_update {
struct dc_dsc_config *dsc_config;
struct dc_transfer_func *func_shaper;
struct dc_3dlut *lut3d_func;
+ struct dc_crtc_timing *timing_for_pixel_encoding;
struct test_pattern *pending_test_pattern;
};
diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
index 73e4de3c7f49..82d177a5af79 100644
--- a/drivers/gpu/drm/drm_modes.c
+++ b/drivers/gpu/drm/drm_modes.c
@@ -1590,6 +1590,33 @@ static int drm_mode_parse_panel_orientation(const char *delim,
return 0;
}
+static int drm_mode_parse_pixel_encoding(const char *delim,
+ struct drm_cmdline_mode *mode)
+{
+ const char *value;
+
+ if (*delim != '=')
+ return -EINVAL;
+
+ value = delim + 1;
+ delim = strchr(value, ',');
+ if (!delim)
+ delim = value + strlen(value);
+
+ if (!strncmp(value, "auto", delim - value))
+ mode->pixel_encoding = 0;
+ else if (!strncmp(value, "rgb", delim - value))
+ mode->pixel_encoding = DRM_COLOR_FORMAT_RGB444;
+ else if (!strncmp(value, "ycbcr444", delim - value))
+ mode->pixel_encoding = DRM_COLOR_FORMAT_YCRCB444;
+ else if (!strncmp(value, "ycbcr420", delim - value))
+ mode->pixel_encoding = DRM_COLOR_FORMAT_YCRCB420;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
static int drm_mode_parse_cmdline_options(const char *str,
bool freestanding,
const struct drm_connector *connector,
@@ -1659,6 +1686,9 @@ static int drm_mode_parse_cmdline_options(const char *str,
} else if (!strncmp(option, "panel_orientation", delim - option)) {
if (drm_mode_parse_panel_orientation(delim, mode))
return -EINVAL;
+ } else if (!strncmp(option, "pixel_encoding", delim - option)) {
+ if (drm_mode_parse_pixel_encoding(delim, mode))
+ return -EINVAL;
} else {
return -EINVAL;
}
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index 1922b278ffad..433dc7a440d3 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -1206,6 +1206,13 @@ struct drm_cmdline_mode {
* @tv_margins: TV margins to apply to the mode.
*/
struct drm_connector_tv_margins tv_margins;
+
+ /**
+ * @pixel_encoding:
+ *
+ * Initial pixel encoding.
+ */
+ unsigned int pixel_encoding;
};
/**
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
index 2a4cd7d377bf..62c6934a6244 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
@@ -1215,6 +1215,33 @@ static const struct drm_prop_enum_list amdgpu_dither_enum_list[] =
{ AMDGPU_FMT_DITHER_ENABLE, "on" },
};
+static const struct drm_prop_enum_list amdgpu_user_pixenc_list[] = {
+ { 0, "auto" },
+ { DRM_COLOR_FORMAT_RGB444, "rgb" },
+ { DRM_COLOR_FORMAT_YCRCB444, "ycbcr444" },
+ { DRM_COLOR_FORMAT_YCRCB420, "ycbcr420" },
+};
+
+bool amdgpu_user_pixenc_from_name(
+ unsigned int *user_pixenc,
+ const char *pixenc_name)
+{
+ bool found = false;
+
+ if (pixenc_name && (*pixenc_name != '\0')) {
+ const int sz = ARRAY_SIZE(amdgpu_user_pixenc_list);
+ int i;
+
+ for (i = 0; !found && i < sz; ++i) {
+ if (strcmp(pixenc_name, amdgpu_user_pixenc_list[i].name) == 0) {
+ *user_pixenc = amdgpu_user_pixenc_list[i].type;
+ found = true;
+ }
+ }
+ }
+ return found;
+}
+
int amdgpu_display_modeset_create_props(struct amdgpu_device *adev)
{
int sz;
@@ -1267,6 +1294,14 @@ int amdgpu_display_modeset_create_props(struct amdgpu_device *adev)
"abm level", 0, 4);
if (!adev->mode_info.abm_level_property)
return -ENOMEM;
+
+ sz = ARRAY_SIZE(amdgpu_user_pixenc_list);
+ adev->mode_info.pixel_encoding_property =
+ drm_property_create_enum(adev_to_drm(adev), 0,
+ "pixel encoding",
+ amdgpu_user_pixenc_list, sz);
+ if (!adev->mode_info.pixel_encoding_property)
+ return -ENOMEM;
}
return 0;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.h
index 7b6d83e2b13c..f67ae02fb438 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.h
@@ -50,4 +50,7 @@ amdgpu_lookup_format_info(u32 format, uint64_t modifier);
int amdgpu_display_suspend_helper(struct amdgpu_device *adev);
int amdgpu_display_resume_helper(struct amdgpu_device *adev);
+bool amdgpu_user_pixenc_from_name(unsigned int *user_pixenc,
+ const char *pixenc_name);
+
#endif
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h
index cb0b581bbce7..8c6ede86c524 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h
@@ -337,6 +337,8 @@ struct amdgpu_mode_info {
struct drm_property *dither_property;
/* Adaptive Backlight Modulation (power feature) */
struct drm_property *abm_level_property;
+ /* User HDMI pixel encoding override */
+ struct drm_property *pixel_encoding_property;
/* hardcoded DFP edid from BIOS */
struct edid *bios_hardcoded_edid;
int bios_hardcoded_edid_size;
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index eeaae1cf2bc2..287fee85707c 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -5174,6 +5174,115 @@ static bool adjust_colour_depth_from_display_info(
return false;
}
+/* convert an pixel encoding property value to a dc_pixel_encoding */
+static bool drm_prop_to_dc_pixel_encoding(
+ enum dc_pixel_encoding *dc_pixenc,
+ unsigned int propval)
+{
+ bool ret = false;
+
+ switch (propval) {
+ case 0:
+ *dc_pixenc = PIXEL_ENCODING_UNDEFINED;
+ ret = true;
+ break;
+ case DRM_COLOR_FORMAT_RGB444:
+ *dc_pixenc = PIXEL_ENCODING_RGB;
+ ret = true;
+ break;
+ case DRM_COLOR_FORMAT_YCRCB444:
+ *dc_pixenc = PIXEL_ENCODING_YCBCR444;
+ ret = true;
+ break;
+ case DRM_COLOR_FORMAT_YCRCB420:
+ *dc_pixenc = PIXEL_ENCODING_YCBCR420;
+ ret = true;
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+/* convert an dc_pixel_encoding to a pixel encoding property value */
+static unsigned int dc_pixel_encoding_to_drm_prop(
+ enum dc_pixel_encoding pixel_encoding)
+{
+ unsigned int propval = 0;
+
+ switch (pixel_encoding) {
+ case PIXEL_ENCODING_RGB:
+ propval = DRM_COLOR_FORMAT_RGB444;
+ break;
+ case PIXEL_ENCODING_YCBCR444:
+ propval = DRM_COLOR_FORMAT_YCRCB444;
+ break;
+ case PIXEL_ENCODING_YCBCR420:
+ propval = DRM_COLOR_FORMAT_YCRCB420;
+ break;
+ default:
+ break;
+ }
+ return propval;
+}
+
+/*
+ * Tries to read 'pixel_encoding' from the pixel_encoding DRM property on
+ * 'state'. Returns true if a supported, acceptable, non-undefined value is
+ * found; false otherwise. Only modifies 'pixel_encoding' if returning true.
+ */
+bool get_connector_state_pixel_encoding(
+ enum dc_pixel_encoding *pixel_encoding,
+ const struct drm_connector_state *state,
+ const struct drm_display_info *info,
+ const struct drm_display_mode *mode_in)
+{
+ bool ret = false;
+ struct dm_connector_state *dm_state;
+
+ dm_state = to_dm_connector_state(state);
+ if (!dm_state)
+ return false;
+
+ /* check encoding is supported */
+ switch (dm_state->pixel_encoding) {
+ case PIXEL_ENCODING_RGB:
+ ret = (info->color_formats & DRM_COLOR_FORMAT_RGB444);
+ break;
+ case PIXEL_ENCODING_YCBCR444:
+ ret = (info->color_formats & DRM_COLOR_FORMAT_YCRCB444);
+ break;
+ case PIXEL_ENCODING_YCBCR420:
+ ret = drm_mode_is_420(info, mode_in);
+ break;
+ default:
+ break;
+ }
+
+ if (ret)
+ *pixel_encoding = dm_state->pixel_encoding;
+
+ return ret;
+}
+
+/*
+ * Writes 'pixel_encoding' to the pixel_encoding DRM property on 'state', if
+ * the enum value is valid and supported; otherwise writes
+ * PIXEL_ENCODING_UNDEFINED which corresponds to the "auto" property state.
+ */
+void set_connector_state_pixel_encoding(
+ const struct drm_connector_state *state,
+ enum dc_pixel_encoding pixel_encoding)
+{
+ struct dm_connector_state *dm_state;
+
+ dm_state = to_dm_connector_state(state);
+ if (!dm_state)
+ return;
+
+ dm_state->pixel_encoding = pixel_encoding;
+}
+
static void fill_stream_properties_from_drm_display_mode(
struct dc_stream_state *stream,
const struct drm_display_mode *mode_in,
@@ -5195,18 +5304,21 @@ static void fill_stream_properties_from_drm_display_mode(
timing_out->h_border_right = 0;
timing_out->v_border_top = 0;
timing_out->v_border_bottom = 0;
- /* TODO: un-hardcode */
- if (drm_mode_is_420_only(info, mode_in)
- && stream->signal == SIGNAL_TYPE_HDMI_TYPE_A)
- timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420;
- else if (drm_mode_is_420_also(info, mode_in)
- && aconnector->force_yuv420_output)
- timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420;
- else if ((connector->display_info.color_formats & DRM_COLOR_FORMAT_YCRCB444)
- && stream->signal == SIGNAL_TYPE_HDMI_TYPE_A)
- timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR444;
- else
- timing_out->pixel_encoding = PIXEL_ENCODING_RGB;
+
+ if (!get_connector_state_pixel_encoding(&timing_out->pixel_encoding,
+ connector_state, info, mode_in)) {
+ /* auto-select a pixel encoding */
+ if (drm_mode_is_420_only(info, mode_in) &&
+ stream->signal == SIGNAL_TYPE_HDMI_TYPE_A)
+ timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420;
+ else if (drm_mode_is_420_also(info, mode_in) && aconnector->force_yuv420_output)
+ timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420;
+ else if ((connector->display_info.color_formats & DRM_COLOR_FORMAT_YCRCB444)
+ && stream->signal == SIGNAL_TYPE_HDMI_TYPE_A)
+ timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR444;
+ else
+ timing_out->pixel_encoding = PIXEL_ENCODING_RGB;
+ }
timing_out->timing_3d_format = TIMING_3D_FORMAT_NONE;
timing_out->display_color_depth = convert_color_depth_from_display_info(
@@ -5271,6 +5383,9 @@ static void fill_stream_properties_from_drm_display_mode(
adjust_colour_depth_from_display_info(timing_out, info);
}
}
+
+ /* write back final choice of pixel encoding */
+ set_connector_state_pixel_encoding(connector_state, timing_out->pixel_encoding);
}
static void fill_audio_info(struct audio_info *audio_info,
@@ -5946,6 +6061,9 @@ int amdgpu_dm_connector_atomic_set_property(struct drm_connector *connector,
} else if (property == adev->mode_info.abm_level_property) {
dm_new_state->abm_level = val;
ret = 0;
+ } else if (property == adev->mode_info.pixel_encoding_property) {
+ if (drm_prop_to_dc_pixel_encoding(&dm_new_state->pixel_encoding, val))
+ ret = 0;
}
return ret;
@@ -5991,6 +6109,9 @@ int amdgpu_dm_connector_atomic_get_property(struct drm_connector *connector,
} else if (property == adev->mode_info.abm_level_property) {
*val = dm_state->abm_level;
ret = 0;
+ } else if (property == adev->mode_info.pixel_encoding_property) {
+ *val = dc_pixel_encoding_to_drm_prop(dm_state->pixel_encoding);
+ ret = 0;
}
return ret;
@@ -6070,6 +6191,20 @@ void amdgpu_dm_connector_funcs_reset(struct drm_connector *connector)
if (connector->connector_type == DRM_MODE_CONNECTOR_eDP)
state->abm_level = amdgpu_dm_abm_level;
+ switch (connector->cmdline_mode.pixel_encoding) {
+ case DRM_COLOR_FORMAT_RGB444:
+ state->pixel_encoding = PIXEL_ENCODING_RGB;
+ break;
+ case DRM_COLOR_FORMAT_YCRCB444:
+ state->pixel_encoding = PIXEL_ENCODING_YCBCR444;
+ break;
+ case DRM_COLOR_FORMAT_YCRCB420:
+ state->pixel_encoding = PIXEL_ENCODING_YCBCR420;
+ break;
+ default:
+ break;
+ }
+
__drm_atomic_helper_connector_reset(connector, &state->base);
}
}
@@ -6096,6 +6231,7 @@ amdgpu_dm_connector_atomic_duplicate_state(struct drm_connector *connector)
new_state->underscan_vborder = state->underscan_vborder;
new_state->vcpi_slots = state->vcpi_slots;
new_state->pbn = state->pbn;
+ new_state->pixel_encoding = state->pixel_encoding;
return &new_state->base;
}
@@ -7576,6 +7712,12 @@ void amdgpu_dm_connector_init_helper(struct amdgpu_display_manager *dm,
if (adev->dm.hdcp_workqueue)
drm_connector_attach_content_protection_property(&aconnector->base, true);
#endif
+
+ if (adev->mode_info.pixel_encoding_property) {
+ drm_object_attach_property(&aconnector->base.base,
+ adev->mode_info.pixel_encoding_property, 0);
+ DRM_DEBUG_DRIVER("amdgpu: attached pixel encoding drm property");
+ }
}
}
@@ -8636,6 +8778,38 @@ static void amdgpu_dm_crtc_copy_transient_flags(struct drm_crtc_state *crtc_stat
stream_state->mode_changed = drm_atomic_crtc_needs_modeset(crtc_state);
}
+static void update_stream_for_pixel_encoding(
+ struct dc_stream_update *stream_update,
+ struct drm_connector *connector,
+ struct dm_crtc_state *dm_old_crtc_state,
+ struct dm_crtc_state *dm_new_crtc_state,
+ struct dm_connector_state *dm_new_con_state)
+{
+ struct amdgpu_dm_connector *aconnector =
+ to_amdgpu_dm_connector(connector);
+ struct dc_stream_state *new_stream = NULL;
+
+ if (aconnector)
+ new_stream = create_validate_stream_for_sink(
+ aconnector,
+ &dm_new_crtc_state->base.mode,
+ dm_new_con_state,
+ dm_old_crtc_state->stream);
+ if (new_stream) {
+ dm_new_crtc_state->stream->timing =
+ new_stream->timing;
+ stream_update->timing_for_pixel_encoding =
+ &dm_new_crtc_state->stream->timing;
+
+ dm_new_crtc_state->stream->output_color_space =
+ new_stream->output_color_space;
+ stream_update->output_color_space =
+ &dm_new_crtc_state->stream->output_color_space;
+
+ dc_stream_release(new_stream);
+ }
+}
+
/**
* amdgpu_dm_atomic_commit_tail() - AMDgpu DM's commit tail implementation.
* @state: The atomic state to commit
@@ -8848,7 +9022,7 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state)
struct dc_stream_update stream_update;
struct dc_info_packet hdr_packet;
struct dc_stream_status *status = NULL;
- bool abm_changed, hdr_changed, scaling_changed;
+ bool abm_changed, hdr_changed, scaling_changed, pixenc_changed;
memset(&dummy_updates, 0, sizeof(dummy_updates));
memset(&stream_update, 0, sizeof(stream_update));
@@ -8874,7 +9048,10 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state)
hdr_changed =
is_hdr_metadata_different(old_con_state, new_con_state);
- if (!scaling_changed && !abm_changed && !hdr_changed)
+ pixenc_changed = dm_new_con_state->pixel_encoding !=
+ dm_old_con_state->pixel_encoding;
+
+ if (!scaling_changed && !abm_changed && !hdr_changed && !pixenc_changed)
continue;
stream_update.stream = dm_new_crtc_state->stream;
@@ -8897,6 +9074,13 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state)
stream_update.hdr_static_metadata = &hdr_packet;
}
+ if (pixenc_changed) {
+ update_stream_for_pixel_encoding(&stream_update,
+ connector,
+ dm_old_crtc_state, dm_new_crtc_state,
+ dm_new_con_state);
+ }
+
status = dc_stream_get_status(dm_new_crtc_state->stream);
WARN_ON(!status);
WARN_ON(!status->plane_count);
@@ -10044,6 +10228,12 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
if (dm_old_con_state->abm_level !=
dm_new_con_state->abm_level)
new_crtc_state->connectors_changed = true;
+
+ if (dm_old_con_state->pixel_encoding !=
+ dm_new_con_state->pixel_encoding) {
+ new_crtc_state->connectors_changed = true;
+ new_crtc_state->mode_changed = true;
+ }
}
#if defined(CONFIG_DRM_AMD_DC_DCN)
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
index c8e5bbbb8bce..cbbb22f06c32 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
@@ -541,6 +541,7 @@ struct dm_connector_state {
uint8_t abm_level;
int vcpi_slots;
uint64_t pbn;
+ enum dc_pixel_encoding pixel_encoding;
};
struct amdgpu_hdmi_vsdb_info {
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c
index a869702d77af..3a3317dc8de5 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc.c
@@ -2141,6 +2141,11 @@ static enum surface_update_type check_update_surfaces_for_stream(
if (stream_update->output_csc_transform || stream_update->output_color_space)
su_flags->bits.out_csc = 1;
+
+ if (stream_update->timing_for_pixel_encoding) {
+ su_flags->bits.pixel_encoding = 1;
+ elevate_update_type(&overall_type, UPDATE_TYPE_FULL);
+ }
}
for (i = 0 ; i < surface_count; i++) {
@@ -2446,6 +2451,10 @@ static void copy_stream_update_to_stream(struct dc *dc,
update->dsc_config = NULL;
}
}
+
+ if (update->timing_for_pixel_encoding) {
+ stream->timing = *update->timing_for_pixel_encoding;
+ }
}
static void commit_planes_do_stream_update(struct dc *dc,
@@ -2473,7 +2482,8 @@ static void commit_planes_do_stream_update(struct dc *dc,
if ((stream_update->hdr_static_metadata && !stream->use_dynamic_meta) ||
stream_update->vrr_infopacket ||
stream_update->vsc_infopacket ||
- stream_update->vsp_infopacket) {
+ stream_update->vsp_infopacket ||
+ stream_update->timing_for_pixel_encoding) {
resource_build_info_frame(pipe_ctx);
dc->hwss.update_info_frame(pipe_ctx);
}
diff --git a/drivers/gpu/drm/amd/display/dc/dc_stream.h b/drivers/gpu/drm/amd/display/dc/dc_stream.h
index 13dae7238a58..d5f128a69452 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_stream.h
+++ b/drivers/gpu/drm/amd/display/dc/dc_stream.h
@@ -125,6 +125,7 @@ union stream_update_flags {
uint32_t gamut_remap:1;
uint32_t wb_update:1;
uint32_t dsc_changed : 1;
+ uint32_t pixel_encoding:1;
} bits;
uint32_t raw;
@@ -277,6 +278,7 @@ struct dc_stream_update {
struct dc_dsc_config *dsc_config;
struct dc_transfer_func *func_shaper;
struct dc_3dlut *lut3d_func;
+ struct dc_crtc_timing *timing_for_pixel_encoding;
struct test_pattern *pending_test_pattern;
};
diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
index 73e4de3c7f49..82d177a5af79 100644
--- a/drivers/gpu/drm/drm_modes.c
+++ b/drivers/gpu/drm/drm_modes.c
@@ -1590,6 +1590,33 @@ static int drm_mode_parse_panel_orientation(const char *delim,
return 0;
}
+static int drm_mode_parse_pixel_encoding(const char *delim,
+ struct drm_cmdline_mode *mode)
+{
+ const char *value;
+
+ if (*delim != '=')
+ return -EINVAL;
+
+ value = delim + 1;
+ delim = strchr(value, ',');
+ if (!delim)
+ delim = value + strlen(value);
+
+ if (!strncmp(value, "auto", delim - value))
+ mode->pixel_encoding = 0;
+ else if (!strncmp(value, "rgb", delim - value))
+ mode->pixel_encoding = DRM_COLOR_FORMAT_RGB444;
+ else if (!strncmp(value, "ycbcr444", delim - value))
+ mode->pixel_encoding = DRM_COLOR_FORMAT_YCRCB444;
+ else if (!strncmp(value, "ycbcr420", delim - value))
+ mode->pixel_encoding = DRM_COLOR_FORMAT_YCRCB420;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
static int drm_mode_parse_cmdline_options(const char *str,
bool freestanding,
const struct drm_connector *connector,
@@ -1659,6 +1686,9 @@ static int drm_mode_parse_cmdline_options(const char *str,
} else if (!strncmp(option, "panel_orientation", delim - option)) {
if (drm_mode_parse_panel_orientation(delim, mode))
return -EINVAL;
+ } else if (!strncmp(option, "pixel_encoding", delim - option)) {
+ if (drm_mode_parse_pixel_encoding(delim, mode))
+ return -EINVAL;
} else {
return -EINVAL;
}
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index 1922b278ffad..433dc7a440d3 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -1206,6 +1206,13 @@ struct drm_cmdline_mode {
* @tv_margins: TV margins to apply to the mode.
*/
struct drm_connector_tv_margins tv_margins;
+
+ /**
+ * @pixel_encoding:
+ *
+ * Initial pixel encoding.
+ */
+ unsigned int pixel_encoding;
};
/**
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment