* preparation for hardware commit. If no lut is specified by user, we default
  * to SRGB degamma.
  *
- * Currently, we only support degamma bypass, or preprogrammed SRGB degamma.
- * Programmable degamma is not supported, and an attempt to do so will return
- * -EINVAL.
+ * We support degamma bypass, predefined SRGB, and custom degamma
  *
  * RETURNS:
- * 0 on success, -EINVAL if custom degamma curve is given.
+ * 0 on success
+ * -EINVAL if crtc_state has a degamma_lut of invalid size
+ * -ENOMEM if gamma allocation fails
  */
 int amdgpu_dm_set_degamma_lut(struct drm_crtc_state *crtc_state,
                              struct dc_plane_state *dc_plane_state)
 {
        struct drm_property_blob *blob = crtc_state->degamma_lut;
        struct drm_color_lut *lut;
+       uint32_t lut_size;
+       struct dc_gamma *gamma;
+       bool ret;
 
        if (!blob) {
                /* Default to SRGB */
                return 0;
        }
 
-       /* Otherwise, assume SRGB, since programmable degamma is not
-        * supported.
-        */
-       dc_plane_state->in_transfer_func->type = TF_TYPE_PREDEFINED;
-       dc_plane_state->in_transfer_func->tf = TRANSFER_FUNCTION_SRGB;
-       return -EINVAL;
+       gamma = dc_create_gamma();
+       if (!gamma)
+               return -ENOMEM;
+
+       lut_size = blob->length / sizeof(struct drm_color_lut);
+       gamma->num_entries = lut_size;
+       if (gamma->num_entries == MAX_COLOR_LUT_ENTRIES)
+               gamma->type = GAMMA_CUSTOM;
+       else {
+               dc_gamma_release(&gamma);
+               return -EINVAL;
+       }
+
+       __drm_lut_to_dc_gamma(lut, gamma, false);
+
+       dc_plane_state->in_transfer_func->type = TF_TYPE_DISTRIBUTED_POINTS;
+       ret = mod_color_calculate_degamma_params(dc_plane_state->in_transfer_func, gamma, true);
+       dc_gamma_release(&gamma);
+       if (!ret) {
+               dc_plane_state->in_transfer_func->type = TF_TYPE_BYPASS;
+               DRM_ERROR("Out of memory when calculating degamma params\n");
+               return -ENOMEM;
+       }
+
+       return 0;
 }
 
 
        GAMMA_RGB_256_ENTRIES = 256,
        GAMMA_RGB_FLOAT_1024_ENTRIES = 1024,
        GAMMA_CS_TFM_1D_ENTRIES = 4096,
+       GAMMA_CUSTOM_ENTRIES = 4096,
        GAMMA_MAX_ENTRIES = 4096
 };
 
        GAMMA_RGB_256 = 1,
        GAMMA_RGB_FLOAT_1024 = 2,
        GAMMA_CS_TFM_1D = 3,
+       GAMMA_CUSTOM = 4,
 };
 
 struct dc_csc_transform {
 
        } else if (tf->type == TF_TYPE_BYPASS) {
                dpp_base->funcs->dpp_set_degamma(dpp_base, IPP_DEGAMMA_MODE_BYPASS);
        } else {
-               /*TF_TYPE_DISTRIBUTED_POINTS*/
-               result = false;
+               cm_helper_translate_curve_to_degamma_hw_format(tf,
+                                       &dpp_base->degamma_params);
+               dpp_base->funcs->dpp_program_degamma_pwl(dpp_base,
+                               &dpp_base->degamma_params);
+               result = true;
        }
 
        return result;
 
  * norm_y = 4095*regamma_y, and index is just truncating to nearest integer
  * lut1 = lut1D[index], lut2 = lut1D[index+1]
  *
- *adjustedY is then linearly interpolating regamma Y between lut1 and lut2
+ * adjustedY is then linearly interpolating regamma Y between lut1 and lut2
+ *
+ * Custom degamma on Linux uses the same interpolation math, so is handled here
  */
 static void apply_lut_1d(
                const struct dc_gamma *ramp,
        struct fixed31_32 delta_lut;
        struct fixed31_32 delta_index;
 
-       if (ramp->type != GAMMA_CS_TFM_1D)
+       if (ramp->type != GAMMA_CS_TFM_1D && ramp->type != GAMMA_CUSTOM)
                return; // this is not expected
 
        for (i = 0; i < num_hw_points; i++) {
        map_regamma_hw_to_x_user(ramp, coeff, rgb_user,
                        coordinates_x, axix_x, curve,
                        MAX_HW_POINTS, tf_pts,
-                       mapUserRamp);
+                       mapUserRamp && ramp->type != GAMMA_CUSTOM);
+       if (ramp->type == GAMMA_CUSTOM)
+               apply_lut_1d(ramp, MAX_HW_POINTS, tf_pts);
 
        ret = true;