Skip to content

Commit

Permalink
drm/color: Add Color transfer functions for HDR/SDR
Browse files Browse the repository at this point in the history
Due to the way displays and human vision work it is most effective to
encode luminance information in a non-linear space.

For SDR this non-linear mapping is assumed to roughly use a gamma 2.2 curve.
This was due to the way CRTs worked and was fine for SDR content with a
low luminance range.

The large luminance range (0-10,000 nits) for HDR exposes some
short-comings of a simple gamma curve that have been addressed
through various Electro-Optical Transfer Functions (EOTFs).

Rather than assuming how framebuffer content is encoded we want to
make sure userspace presenting HDR content is explicit about the
EOTF of the content, so a driver can decide whether the content
can be supported or not.

This Patch adds common color transfer functions for SDR/HDR. These can
be used to communicate with the driver regarding the transformation to
use for a given plane.

enums added:
	DRM_COLOR_TF_SRGB
		roughly 2.4 gamma with initial linear section
	DRM_COLOR_TF_BT709
		Similar to Gamma 2.2-2.8
	DRM_COLOR_TF_PQ2084
		most common tf used for HDR video (HDR10/Dolby). Can support up
		to 10,000 nits

The usage is similar to color_encoding and color_range where the driver
can specify the default and supported tfs and pass it into
drm_plane_create_color_properties().

Signed-off-by: Bhawanpreet Lakha <Bhawanpreet.Lakha@amd.com>
Signed-off-by: Harry Wentland <harry.wentland@amd.com>
  • Loading branch information
Bhawanpreet Lakha authored and intel-lab-lkp committed Apr 26, 2021
1 parent cd35fe3 commit ecfbc41
Show file tree
Hide file tree
Showing 14 changed files with 124 additions and 16 deletions.
4 changes: 3 additions & 1 deletion drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
Expand Up @@ -7039,7 +7039,9 @@ static int amdgpu_dm_plane_init(struct amdgpu_display_manager *dm,
BIT(DRM_COLOR_YCBCR_BT2020),
BIT(DRM_COLOR_YCBCR_LIMITED_RANGE) |
BIT(DRM_COLOR_YCBCR_FULL_RANGE),
DRM_COLOR_YCBCR_BT709, DRM_COLOR_YCBCR_LIMITED_RANGE);
BIT(DRM_COLOR_TF_SRGB),
DRM_COLOR_YCBCR_BT709, DRM_COLOR_YCBCR_LIMITED_RANGE,
DRM_COLOR_TF_SRGB);
}

supported_rotations =
Expand Down
4 changes: 3 additions & 1 deletion drivers/gpu/drm/arm/display/komeda/komeda_plane.c
Expand Up @@ -302,8 +302,10 @@ static int komeda_plane_add(struct komeda_kms_dev *kms,
BIT(DRM_COLOR_YCBCR_BT2020),
BIT(DRM_COLOR_YCBCR_LIMITED_RANGE) |
BIT(DRM_COLOR_YCBCR_FULL_RANGE),
BIT(DRM_COLOR_TF_SRGB),
DRM_COLOR_YCBCR_BT601,
DRM_COLOR_YCBCR_LIMITED_RANGE);
DRM_COLOR_YCBCR_LIMITED_RANGE,
DRM_COLOR_TF_SRGB);
if (err)
goto cleanup;

Expand Down
4 changes: 3 additions & 1 deletion drivers/gpu/drm/arm/malidp_planes.c
Expand Up @@ -1025,7 +1025,9 @@ int malidp_de_planes_init(struct drm_device *drm)
BIT(DRM_COLOR_YCBCR_BT2020),
BIT(DRM_COLOR_YCBCR_LIMITED_RANGE) | \
BIT(DRM_COLOR_YCBCR_FULL_RANGE),
enc, range);
BIT(DRM_COLOR_SRGB),
enc, range,
DRM_COLOR_SRGB);
if (!ret)
/* program the HW registers */
malidp_de_set_color_encoding(plane, enc, range);
Expand Down
4 changes: 3 additions & 1 deletion drivers/gpu/drm/armada/armada_overlay.c
Expand Up @@ -596,8 +596,10 @@ int armada_overlay_plane_create(struct drm_device *dev, unsigned long crtcs)
BIT(DRM_COLOR_YCBCR_BT601) |
BIT(DRM_COLOR_YCBCR_BT709),
BIT(DRM_COLOR_YCBCR_LIMITED_RANGE),
BIT(DRM_COLOR_TF_SRGB),
DEFAULT_ENCODING,
DRM_COLOR_YCBCR_LIMITED_RANGE);
DRM_COLOR_YCBCR_LIMITED_RANGE,
DRM_COLOR_TF_SRGB);

return ret;
}
4 changes: 4 additions & 0 deletions drivers/gpu/drm/drm_atomic_uapi.c
Expand Up @@ -595,6 +595,8 @@ static int drm_atomic_plane_set_property(struct drm_plane *plane,
state->color_encoding = val;
} else if (property == plane->color_range_property) {
state->color_range = val;
} else if (property == plane->color_tf_property) {
state->color_tf = val;
} else if (property == config->prop_fb_damage_clips) {
ret = drm_atomic_replace_property_blob_from_id(dev,
&state->fb_damage_clips,
Expand Down Expand Up @@ -661,6 +663,8 @@ drm_atomic_plane_get_property(struct drm_plane *plane,
*val = state->color_encoding;
} else if (property == plane->color_range_property) {
*val = state->color_range;
} else if (property == plane->color_tf_property) {
*val = state->color_tf;
} else if (property == config->prop_fb_damage_clips) {
*val = (state->fb_damage_clips) ?
state->fb_damage_clips->base.id : 0;
Expand Down
63 changes: 58 additions & 5 deletions drivers/gpu/drm/drm_color_mgmt.c
Expand Up @@ -106,6 +106,11 @@
* Optional plane enum property to support different non RGB
* color parameter ranges. The driver can provide a subset of
* standard enum values supported by the DRM plane.
*
* "COLOR_TRANFER_FUNCTION":
* Optional plane enum property to support different
* color luminance mappings. The driver can provide a subset of
* standard enum values supported by the DRM plane.
*/

/**
Expand Down Expand Up @@ -480,6 +485,10 @@ static const char * const color_range_name[] = {
[DRM_COLOR_YCBCR_LIMITED_RANGE] = "YCbCr limited range",
};

static const char * const color_tf_name[] = {
[DRM_COLOR_TF_SRGB] = "sRGB",
[DRM_COLOR_TF_PQ2084] = "PQ2084",
};
/**
* drm_get_color_encoding_name - return a string for color encoding
* @encoding: color encoding to compute name of
Expand Down Expand Up @@ -510,30 +519,49 @@ const char *drm_get_color_range_name(enum drm_color_range range)
return color_range_name[range];
}

/**
* drm_get_color_transfer_function - return a string for color transfer function
* @tf: transfer function to compute name of
*
* In contrast to the other drm_get_*_name functions this one here returns a
* const pointer and hence is threadsafe.
*/
const char *drm_get_color_transfer_function_name(enum drm_color_transfer_function tf)
{
if (WARN_ON(tf >= ARRAY_SIZE(color_tf_name)))
return "unknown";

return color_tf_name[tf];
}
/**
* drm_plane_create_color_properties - color encoding related plane properties
* @plane: plane object
* @supported_encodings: bitfield indicating supported color encodings
* @supported_ranges: bitfileld indicating supported color ranges
* @supported_tfs: bitfileld indicating supported color transfer functions
* @default_encoding: default color encoding
* @default_range: default color range
* @default_tf: default color transfer function
*
* Create and attach plane specific COLOR_ENCODING and COLOR_RANGE
* properties to @plane. The supported encodings and ranges should
* be provided in supported_encodings and supported_ranges bitmasks.
* Create and attach plane specific COLOR_ENCODING, COLOR_RANGE and COLOR_TRANSFER_FUNCTION
* properties to @plane. The supported encodings, ranges and tfs should
* be provided in supported_encodings, supported_ranges and supported_tfs bitmasks.
* Each bit set in the bitmask indicates that its number as enum
* value is supported.
*/
int drm_plane_create_color_properties(struct drm_plane *plane,
u32 supported_encodings,
u32 supported_ranges,
u32 supported_tfs,
enum drm_color_encoding default_encoding,
enum drm_color_range default_range)
enum drm_color_range default_range,
enum drm_color_transfer_function default_tf)
{
struct drm_device *dev = plane->dev;
struct drm_property *prop;
struct drm_prop_enum_list enum_list[max_t(int, DRM_COLOR_ENCODING_MAX,
DRM_COLOR_RANGE_MAX)];
max_t(int, DRM_COLOR_RANGE_MAX,
DRM_COLOR_TF_MAX))];
int i, len;

if (WARN_ON(supported_encodings == 0 ||
Expand All @@ -546,6 +574,11 @@ int drm_plane_create_color_properties(struct drm_plane *plane,
(supported_ranges & BIT(default_range)) == 0))
return -EINVAL;

if (WARN_ON(supported_tfs == 0 ||
(supported_tfs & -BIT(DRM_COLOR_TF_MAX)) != 0 ||
(supported_tfs & BIT(default_tf)) == 0))
return -EINVAL;

len = 0;
for (i = 0; i < DRM_COLOR_ENCODING_MAX; i++) {
if ((supported_encodings & BIT(i)) == 0)
Expand Down Expand Up @@ -584,6 +617,26 @@ int drm_plane_create_color_properties(struct drm_plane *plane,
if (plane->state)
plane->state->color_range = default_range;


len = 0;
for (i = 0; i < DRM_COLOR_TF_MAX; i++) {
if ((supported_tfs & BIT(i)) == 0)
continue;

enum_list[len].type = i;
enum_list[len].name = color_tf_name[i];
len++;
}

prop = drm_property_create_enum(dev, 0, "COLOR_TRANSFER_FUNCTION",
enum_list, len);
if (!prop)
return -ENOMEM;
plane->color_tf_property = prop;
drm_object_attach_property(&plane->base, prop, default_tf);
if (plane->state)
plane->state->color_tf = default_tf;

return 0;
}
EXPORT_SYMBOL(drm_plane_create_color_properties);
Expand Down
4 changes: 3 additions & 1 deletion drivers/gpu/drm/i915/display/intel_sprite.c
Expand Up @@ -1849,8 +1849,10 @@ intel_sprite_plane_create(struct drm_i915_private *dev_priv,
BIT(DRM_COLOR_YCBCR_BT709),
BIT(DRM_COLOR_YCBCR_LIMITED_RANGE) |
BIT(DRM_COLOR_YCBCR_FULL_RANGE),
BIT(DRM_COLOR_TF_SRGB),
DRM_COLOR_YCBCR_BT709,
DRM_COLOR_YCBCR_LIMITED_RANGE);
DRM_COLOR_YCBCR_LIMITED_RANGE,
DRM_COLOR_TF_SRGB);

zpos = sprite + 1;
drm_plane_create_zpos_immutable_property(&plane->base, zpos);
Expand Down
4 changes: 3 additions & 1 deletion drivers/gpu/drm/i915/display/skl_universal_plane.c
Expand Up @@ -2044,8 +2044,10 @@ skl_universal_plane_create(struct drm_i915_private *dev_priv,
supported_csc,
BIT(DRM_COLOR_YCBCR_LIMITED_RANGE) |
BIT(DRM_COLOR_YCBCR_FULL_RANGE),
BIT(DRM_COLOR_TF_SRGB),
DRM_COLOR_YCBCR_BT709,
DRM_COLOR_YCBCR_LIMITED_RANGE);
DRM_COLOR_YCBCR_LIMITED_RANGE,
DRM_COLOR_TF_SRGB);

drm_plane_create_alpha_property(&plane->base);
drm_plane_create_blend_mode_property(&plane->base,
Expand Down
4 changes: 3 additions & 1 deletion drivers/gpu/drm/nouveau/dispnv04/overlay.c
Expand Up @@ -345,8 +345,10 @@ nv10_overlay_init(struct drm_device *device)
BIT(DRM_COLOR_YCBCR_BT601) |
BIT(DRM_COLOR_YCBCR_BT709),
BIT(DRM_COLOR_YCBCR_LIMITED_RANGE),
BIT(DRM_COLOR_TF_SRGB),
DRM_COLOR_YCBCR_BT601,
DRM_COLOR_YCBCR_LIMITED_RANGE);
DRM_COLOR_YCBCR_LIMITED_RANGE,
DRM_COLOR_TF_SRGB);

plane->set_params = nv10_set_params;
nv10_set_params(plane);
Expand Down
4 changes: 3 additions & 1 deletion drivers/gpu/drm/omapdrm/omap_plane.c
Expand Up @@ -325,8 +325,10 @@ struct drm_plane *omap_plane_init(struct drm_device *dev,
BIT(DRM_COLOR_YCBCR_BT709),
BIT(DRM_COLOR_YCBCR_FULL_RANGE) |
BIT(DRM_COLOR_YCBCR_LIMITED_RANGE),
BIT(DRM_COLOR_TF_SRGB),
DRM_COLOR_YCBCR_BT601,
DRM_COLOR_YCBCR_FULL_RANGE);
DRM_COLOR_YCBCR_FULL_RANGE,
DRM_COLOR_TF_SRGB);

return plane;

Expand Down
4 changes: 3 additions & 1 deletion drivers/gpu/drm/sun4i/sun8i_vi_layer.c
Expand Up @@ -594,8 +594,10 @@ struct sun8i_vi_layer *sun8i_vi_layer_init_one(struct drm_device *drm,
ret = drm_plane_create_color_properties(&layer->plane,
supported_encodings,
supported_ranges,
BIT(DRM_COLOR_TF_SRGB),
DRM_COLOR_YCBCR_BT709,
DRM_COLOR_YCBCR_LIMITED_RANGE);
DRM_COLOR_YCBCR_LIMITED_RANGE,
DRM_COLOR_TF_SRGB);
if (ret) {
dev_err(drm->dev, "Couldn't add encoding and range properties!\n");
return ERR_PTR(ret);
Expand Down
6 changes: 5 additions & 1 deletion drivers/gpu/drm/tidss/tidss_plane.c
Expand Up @@ -186,8 +186,10 @@ struct tidss_plane *tidss_plane_create(struct tidss_device *tidss,
BIT(DRM_COLOR_YCBCR_BT709));
u32 color_ranges = (BIT(DRM_COLOR_YCBCR_FULL_RANGE) |
BIT(DRM_COLOR_YCBCR_LIMITED_RANGE));
u32 color_tfs = BIT(DRM_COLOR_TF_SRGB;
u32 default_encoding = DRM_COLOR_YCBCR_BT601;
u32 default_range = DRM_COLOR_YCBCR_FULL_RANGE;
u32 default_tf = DRM_COLOR_TF_SRGB;;
u32 blend_modes = (BIT(DRM_MODE_BLEND_PREMULTI) |
BIT(DRM_MODE_BLEND_COVERAGE));
int ret;
Expand Down Expand Up @@ -217,8 +219,10 @@ struct tidss_plane *tidss_plane_create(struct tidss_device *tidss,
ret = drm_plane_create_color_properties(&tplane->plane,
color_encodings,
color_ranges,
color_tfs,
default_encoding,
default_range);
default_range,
default_tf);
if (ret)
goto err;

Expand Down
15 changes: 14 additions & 1 deletion include/drm/drm_color_mgmt.h
Expand Up @@ -91,11 +91,24 @@ enum drm_color_range {
DRM_COLOR_RANGE_MAX,
};

/**
* enum drm_color_transfer_function - common transfer function used for sdr/hdr formats
*
* DRM_COLOR_TF_SRGB - Based on gamma curve and is used for printer/monitors/web
* DRM_COLOR_TF_PQ2084 - Used for HDR and allows for up to 10,000 nit support.
*/
enum drm_color_transfer_function {
DRM_COLOR_TF_SRGB,
DRM_COLOR_TF_PQ2084,
DRM_COLOR_TF_MAX,
};
int drm_plane_create_color_properties(struct drm_plane *plane,
u32 supported_encodings,
u32 supported_ranges,
u32 supported_tf,
enum drm_color_encoding default_encoding,
enum drm_color_range default_range);
enum drm_color_range default_range,
enum drm_color_transfer_function default_tf);

/**
* enum drm_color_lut_tests - hw-specific LUT tests to perform
Expand Down
16 changes: 16 additions & 0 deletions include/drm/drm_plane.h
Expand Up @@ -179,6 +179,14 @@ struct drm_plane_state {
*/
enum drm_color_range color_range;

/**
* @color_transfer_function:
*
* Transfer function for HDR color/luminance mapping. This will allow the
* driver to know what transfer function should be used to for the current
* format for a proper HDR color/luminance output.
*/
enum drm_color_transfer_function color_tf;
/**
* @fb_damage_clips:
*
Expand Down Expand Up @@ -741,6 +749,14 @@ struct drm_plane {
* See drm_plane_create_color_properties().
*/
struct drm_property *color_range_property;
/**
* @color_tf_property:
*
* Optional "COLOR_TRANSFER_FUNCTION" enum property for specifying
* color transfer function for non RGB formats, mostly used for HDR.
* See drm_plane_create_color_properties().
*/
struct drm_property *color_tf_property;

/**
* @scaling_filter_property: property to apply a particular filter while
Expand Down

0 comments on commit ecfbc41

Please sign in to comment.