On 2023-10-05 13:15, Melissa Wen wrote: > On AMD HW, 3D LUT always assumes a preceding shaper 1D LUT used for > delinearizing and/or normalizing the color space before applying a 3D > LUT. Add pre-defined transfer function to enable delinearizing content > with or without shaper LUT, where AMD color module calculates the > resulted shaper curve. We apply an inverse EOTF to go from linear > values to encoded values. If we are already in a non-linear space and/or > don't need to normalize values, we can bypass shaper LUT with a linear > transfer function that is also the default TF value. > > There is no shaper ROM. When setting shaper TF (!= Identity) and LUT at > the same time, the color module will combine the pre-defined TF and the > custom LUT values into the LUT that's actually programmed. > > v2: > - squash commits for shaper LUT and shaper TF > - define inverse EOTF as supported shaper TFs > > v3: > - spell out TF+LUT behavior in the commit and comments (Harry) > - replace BT709 EOTF by inv OETF > > Signed-off-by: Melissa Wen <mwen@xxxxxxxxxx> Reviewed-by: Harry Wentland <harry.wentland@xxxxxxx> Harry > --- > drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h | 21 ++++++++++++ > .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h | 11 +++++++ > .../amd/display/amdgpu_dm/amdgpu_dm_color.c | 29 +++++++++++++++++ > .../amd/display/amdgpu_dm/amdgpu_dm_plane.c | 32 +++++++++++++++++++ > 4 files changed, 93 insertions(+) > > diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h > index f7adaa52c23f..af70db4f6b4b 100644 > --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h > +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h > @@ -363,6 +363,27 @@ struct amdgpu_mode_info { > * @plane_hdr_mult_property: > */ > struct drm_property *plane_hdr_mult_property; > + /** > + * @shaper_lut_property: Plane property to set pre-blending shaper LUT > + * that converts color content before 3D LUT. If > + * plane_shaper_tf_property != Identity TF, AMD color module will > + * combine the user LUT values with pre-defined TF into the LUT > + * parameters to be programmed. > + */ > + struct drm_property *plane_shaper_lut_property; > + /** > + * @shaper_lut_size_property: Plane property for the size of > + * pre-blending shaper LUT as supported by the driver (read-only). > + */ > + struct drm_property *plane_shaper_lut_size_property; > + /** > + * @plane_shaper_tf_property: Plane property to set a predefined > + * transfer function for pre-blending shaper (before applying 3D LUT) > + * with or without LUT. There is no shaper ROM, but we can use AMD > + * color modules to program LUT parameters from predefined TF (or > + * from a combination of pre-defined TF and the custom 1D LUT). > + */ > + struct drm_property *plane_shaper_tf_property; > /** > * @plane_lut3d_property: Plane property for color transformation using > * a 3D LUT (pre-blending), a three-dimensional array where each > 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 7a2350c62cf1..0e2a04a3caf3 100644 > --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h > +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h > @@ -784,6 +784,17 @@ struct dm_plane_state { > * TF is needed for any subsequent linear-to-non-linear transforms. > */ > __u64 hdr_mult; > + /** > + * @shaper_lut: shaper lookup table blob. The blob (if not NULL) is an > + * array of &struct drm_color_lut. > + */ > + struct drm_property_blob *shaper_lut; > + /** > + * @shaper_tf: > + * > + * Predefined transfer function to delinearize color space. > + */ > + enum amdgpu_transfer_function shaper_tf; > /** > * @lut3d: 3D lookup table blob. The blob (if not NULL) is an array of > * &struct drm_color_lut. > diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c > index 011f2f9ec890..d3c7f9a13a61 100644 > --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c > +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c > @@ -173,6 +173,14 @@ static const u32 amdgpu_eotf = > BIT(AMDGPU_TRANSFER_FUNCTION_GAMMA24_EOTF) | > BIT(AMDGPU_TRANSFER_FUNCTION_GAMMA26_EOTF); > > +static const u32 amdgpu_inv_eotf = > + BIT(AMDGPU_TRANSFER_FUNCTION_SRGB_INV_EOTF) | > + BIT(AMDGPU_TRANSFER_FUNCTION_BT709_OETF) | > + BIT(AMDGPU_TRANSFER_FUNCTION_PQ_INV_EOTF) | > + BIT(AMDGPU_TRANSFER_FUNCTION_GAMMA22_INV_EOTF) | > + BIT(AMDGPU_TRANSFER_FUNCTION_GAMMA24_INV_EOTF) | > + BIT(AMDGPU_TRANSFER_FUNCTION_GAMMA26_INV_EOTF); > + > static struct drm_property * > amdgpu_create_tf_property(struct drm_device *dev, > const char *name, > @@ -230,6 +238,27 @@ amdgpu_dm_create_color_properties(struct amdgpu_device *adev) > return -ENOMEM; > adev->mode_info.plane_hdr_mult_property = prop; > > + prop = drm_property_create(adev_to_drm(adev), > + DRM_MODE_PROP_BLOB, > + "AMD_PLANE_SHAPER_LUT", 0); > + if (!prop) > + return -ENOMEM; > + adev->mode_info.plane_shaper_lut_property = prop; > + > + prop = drm_property_create_range(adev_to_drm(adev), > + DRM_MODE_PROP_IMMUTABLE, > + "AMD_PLANE_SHAPER_LUT_SIZE", 0, UINT_MAX); > + if (!prop) > + return -ENOMEM; > + adev->mode_info.plane_shaper_lut_size_property = prop; > + > + prop = amdgpu_create_tf_property(adev_to_drm(adev), > + "AMD_PLANE_SHAPER_TF", > + amdgpu_inv_eotf); > + if (!prop) > + return -ENOMEM; > + adev->mode_info.plane_shaper_tf_property = prop; > + > prop = drm_property_create(adev_to_drm(adev), > DRM_MODE_PROP_BLOB, > "AMD_PLANE_LUT3D", 0); > diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c > index 068798ffdd56..a381b3558bd1 100644 > --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c > +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c > @@ -1338,6 +1338,7 @@ static void dm_drm_plane_reset(struct drm_plane *plane) > __drm_atomic_helper_plane_reset(plane, &amdgpu_state->base); > amdgpu_state->degamma_tf = AMDGPU_TRANSFER_FUNCTION_DEFAULT; > amdgpu_state->hdr_mult = AMDGPU_HDR_MULT_DEFAULT; > + amdgpu_state->shaper_tf = AMDGPU_TRANSFER_FUNCTION_DEFAULT; > } > > static struct drm_plane_state * > @@ -1359,11 +1360,14 @@ dm_drm_plane_duplicate_state(struct drm_plane *plane) > > if (dm_plane_state->degamma_lut) > drm_property_blob_get(dm_plane_state->degamma_lut); > + if (dm_plane_state->shaper_lut) > + drm_property_blob_get(dm_plane_state->shaper_lut); > if (dm_plane_state->lut3d) > drm_property_blob_get(dm_plane_state->lut3d); > > dm_plane_state->degamma_tf = old_dm_plane_state->degamma_tf; > dm_plane_state->hdr_mult = old_dm_plane_state->hdr_mult; > + dm_plane_state->shaper_tf = old_dm_plane_state->shaper_tf; > > return &dm_plane_state->base; > } > @@ -1436,6 +1440,8 @@ static void dm_drm_plane_destroy_state(struct drm_plane *plane, > drm_property_blob_put(dm_plane_state->degamma_lut); > if (dm_plane_state->lut3d) > drm_property_blob_put(dm_plane_state->lut3d); > + if (dm_plane_state->shaper_lut) > + drm_property_blob_put(dm_plane_state->shaper_lut); > > if (dm_plane_state->dc_state) > dc_plane_state_release(dm_plane_state->dc_state); > @@ -1468,6 +1474,14 @@ dm_atomic_plane_attach_color_mgmt_properties(struct amdgpu_display_manager *dm, > AMDGPU_HDR_MULT_DEFAULT); > > if (dpp_color_caps.hw_3d_lut) { > + drm_object_attach_property(&plane->base, > + mode_info.plane_shaper_lut_property, 0); > + drm_object_attach_property(&plane->base, > + mode_info.plane_shaper_lut_size_property, > + MAX_COLOR_LUT_ENTRIES); > + drm_object_attach_property(&plane->base, > + mode_info.plane_shaper_tf_property, > + AMDGPU_TRANSFER_FUNCTION_DEFAULT); > drm_object_attach_property(&plane->base, > mode_info.plane_lut3d_property, 0); > drm_object_attach_property(&plane->base, > @@ -1505,6 +1519,19 @@ dm_atomic_plane_set_property(struct drm_plane *plane, > dm_plane_state->hdr_mult = val; > dm_plane_state->base.color_mgmt_changed = 1; > } > + } else if (property == adev->mode_info.plane_shaper_lut_property) { > + ret = drm_property_replace_blob_from_id(plane->dev, > + &dm_plane_state->shaper_lut, > + val, -1, > + sizeof(struct drm_color_lut), > + &replaced); > + dm_plane_state->base.color_mgmt_changed |= replaced; > + return ret; > + } else if (property == adev->mode_info.plane_shaper_tf_property) { > + if (dm_plane_state->shaper_tf != val) { > + dm_plane_state->shaper_tf = val; > + dm_plane_state->base.color_mgmt_changed = 1; > + } > } else if (property == adev->mode_info.plane_lut3d_property) { > ret = drm_property_replace_blob_from_id(plane->dev, > &dm_plane_state->lut3d, > @@ -1540,6 +1567,11 @@ dm_atomic_plane_get_property(struct drm_plane *plane, > *val = dm_plane_state->degamma_tf; > } else if (property == adev->mode_info.plane_hdr_mult_property) { > *val = dm_plane_state->hdr_mult; > + } else if (property == adev->mode_info.plane_shaper_lut_property) { > + *val = (dm_plane_state->shaper_lut) ? > + dm_plane_state->shaper_lut->base.id : 0; > + } else if (property == adev->mode_info.plane_shaper_tf_property) { > + *val = dm_plane_state->shaper_tf; > } else if (property == adev->mode_info.plane_lut3d_property) { > *val = (dm_plane_state->lut3d) ? > dm_plane_state->lut3d->base.id : 0;