#include "amdgpu.h"
#include "amdgpu_mode.h"
#include "amdgpu_dm.h"
#include "dc.h"
#include "modules/color/color_gamma.h"
#define MAX_DRM_LUT_VALUE 0xFFFF
void amdgpu_dm_init_color_mod(void)
{
setup_x_points_distribution();
}
static bool __is_lut_linear(struct drm_color_lut *lut, uint32_t size)
{
int i;
uint32_t expected;
int delta;
for (i = 0; i < size; i++) {
if ((lut[i].red != lut[i].green) || (lut[i].green != lut[i].blue))
return false;
expected = i * MAX_DRM_LUT_VALUE / (size-1);
delta = lut[i].red - expected;
if (delta < -1 || 1 < delta)
return false;
}
return true;
}
static void __drm_lut_to_dc_gamma(struct drm_color_lut *lut,
struct dc_gamma *gamma,
bool is_legacy)
{
uint32_t r, g, b;
int i;
if (is_legacy) {
for (i = 0; i < MAX_COLOR_LEGACY_LUT_ENTRIES; i++) {
r = drm_color_lut_extract(lut[i].red, 16);
g = drm_color_lut_extract(lut[i].green, 16);
b = drm_color_lut_extract(lut[i].blue, 16);
gamma->entries.red[i] = dc_fixpt_from_int(r);
gamma->entries.green[i] = dc_fixpt_from_int(g);
gamma->entries.blue[i] = dc_fixpt_from_int(b);
}
return;
}
for (i = 0; i < MAX_COLOR_LUT_ENTRIES; i++) {
r = drm_color_lut_extract(lut[i].red, 16);
g = drm_color_lut_extract(lut[i].green, 16);
b = drm_color_lut_extract(lut[i].blue, 16);
gamma->entries.red[i] = dc_fixpt_from_fraction(r, MAX_DRM_LUT_VALUE);
gamma->entries.green[i] = dc_fixpt_from_fraction(g, MAX_DRM_LUT_VALUE);
gamma->entries.blue[i] = dc_fixpt_from_fraction(b, MAX_DRM_LUT_VALUE);
}
}
int amdgpu_dm_set_regamma_lut(struct dm_crtc_state *crtc)
{
struct drm_property_blob *blob = crtc->base.gamma_lut;
struct dc_stream_state *stream = crtc->stream;
struct amdgpu_device *adev = (struct amdgpu_device *)
crtc->base.state->dev->dev_private;
struct drm_color_lut *lut;
uint32_t lut_size;
struct dc_gamma *gamma;
enum dc_transfer_func_type old_type = stream->out_transfer_func->type;
bool ret;
if (!blob) {
stream->out_transfer_func->type = TF_TYPE_PREDEFINED;
stream->out_transfer_func->tf = TRANSFER_FUNCTION_SRGB;
return 0;
}
lut = (struct drm_color_lut *)blob->data;
lut_size = blob->length / sizeof(struct drm_color_lut);
gamma = dc_create_gamma();
if (!gamma)
return -ENOMEM;
gamma->num_entries = lut_size;
if (gamma->num_entries == MAX_COLOR_LEGACY_LUT_ENTRIES)
gamma->type = GAMMA_RGB_256;
else if (gamma->num_entries == MAX_COLOR_LUT_ENTRIES)
gamma->type = GAMMA_CS_TFM_1D;
else {
dc_gamma_release(&gamma);
return -EINVAL;
}
__drm_lut_to_dc_gamma(lut, gamma, gamma->type == GAMMA_RGB_256);
stream->out_transfer_func->type = TF_TYPE_DISTRIBUTED_POINTS;
ret = mod_color_calculate_regamma_params(stream->out_transfer_func,
gamma, true, adev->asic_type <= CHIP_RAVEN);
dc_gamma_release(&gamma);
if (!ret) {
stream->out_transfer_func->type = old_type;
DRM_ERROR("Out of memory when calculating regamma params\n");
return -ENOMEM;
}
return 0;
}
void amdgpu_dm_set_ctm(struct dm_crtc_state *crtc)
{
struct drm_property_blob *blob = crtc->base.ctm;
struct dc_stream_state *stream = crtc->stream;
struct drm_color_ctm *ctm;
int64_t val;
int i;
if (!blob) {
stream->gamut_remap_matrix.enable_remap = false;
return;
}
stream->gamut_remap_matrix.enable_remap = true;
ctm = (struct drm_color_ctm *)blob->data;
for (i = 0; i < 12; i++) {
if (i % 4 == 3) {
stream->gamut_remap_matrix.matrix[i] = dc_fixpt_zero;
continue;
}
val = ctm->matrix[i - (i/4)];
if (val & (1ULL << 63))
val = -(val & ~(1ULL << 63));
stream->gamut_remap_matrix.matrix[i].value = val;
}
}
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) {
dc_plane_state->in_transfer_func->type = TF_TYPE_PREDEFINED;
dc_plane_state->in_transfer_func->tf = TRANSFER_FUNCTION_SRGB;
return 0;
}
lut = (struct drm_color_lut *)blob->data;
if (__is_lut_linear(lut, MAX_COLOR_LUT_ENTRIES)) {
dc_plane_state->in_transfer_func->type = TF_TYPE_BYPASS;
dc_plane_state->in_transfer_func->tf = TRANSFER_FUNCTION_LINEAR;
return 0;
}
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;
}