summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeo (Sunpeng) Li <sunpeng.li@amd.com>2018-03-22 14:27:13 -0400
committerLeo (Sunpeng) Li <sunpeng.li@amd.com>2018-05-02 10:13:30 -0400
commit523785809d55f8f9e9f6658ab3e7788f31c398f1 (patch)
tree00b8ebbe49607800aadbd9a59da5741cde545ba3
parent26483de56ee0cd483d05a16b136bcab0a5631347 (diff)
Implemented demo app for degamma, ctm, and regamma.
The app currently uses a system call to xrandr to set the blob IDs. Ideally, we should include libxrandr, and use the library to do so instead. Signed-off-by: Leo (Sunpeng) Li <sunpeng.li@amd.com>
-rw-r--r--demo.c508
1 files changed, 434 insertions, 74 deletions
diff --git a/demo.c b/demo.c
index c501e3c..c612394 100644
--- a/demo.c
+++ b/demo.c
@@ -26,14 +26,15 @@
#include <fcntl.h>
#include <math.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+
#include <xf86drm.h>
#include <xf86drmMode.h>
-#define LUT_SIZE 16
-
+#define LUT_SIZE 4096
/**
* The below data structures are identical to the ones used by DRM. They are
@@ -66,6 +67,53 @@ struct color3d {
* Helper functions
*/
+static int __fd_is_device(int fd, const char *expect)
+{
+ char name[5] = "";
+ drm_version_t version = {0};
+
+ version.name_len = 4;
+ version.name = name;
+ if (drmIoctl(fd, DRM_IOCTL_VERSION, &version))
+ return 0;
+
+ return strcmp(expect, version.name) == 0;
+}
+
+static int open_drm_device()
+{
+ char *base = "/dev/dri/card";
+ int i = 0;
+ for (i = 0; i < 16; i++) {
+ char name[80];
+ int fd;
+
+ sprintf(name, "%s%u", base, i);
+ printf("Opening %s... ", name);
+ fd = open(name, O_RDWR);
+ if (fd == -1) {
+ printf("Failed.\n");
+ continue;
+ }
+
+ if (__fd_is_device(fd, "amdg")){
+ printf("Success!\n");
+ return fd;
+ }
+
+ printf("Not an amdgpu device.\n");
+ close(fd);
+ }
+ return -1;
+}
+
+/**
+ * Translate coefficients to a color LUT format that DRM accepts.
+ * @coeffs: Input coefficients
+ * @lut: DRM LUT struct, used to create the blob. The translated values will be
+ * placed here.
+ * @lut_size: Number of entries in the LUT.
+ */
static void coeffs_to_lut(struct color3d *coeffs,
struct _drm_color_lut *lut,
int lut_size)
@@ -80,6 +128,37 @@ static void coeffs_to_lut(struct color3d *coeffs,
}
}
+/**
+ * Translate coefficients to a color CTM format that DRM accepts.
+ *
+ * DRM requres the CTM to be in signed-magnitude, not 2's complement.
+ * It is also in 31.32 fixed-point format.
+ *
+ * @coeffs: Input coefficients
+ * @ctm: DRM CTM struct, used to create the blob. The translated values will be
+ * placed here.
+ */
+static void coeffs_to_ctm(double *coeffs,
+ struct _drm_color_ctm *ctm)
+{
+ int i;
+ for (i = 0; i < 9; i++) {
+ if (coeffs[i] < 0) {
+ ctm->matrix[i] =
+ (int64_t) (-coeffs[i] * ((int64_t) 1L << 32));
+ ctm->matrix[i] |= 1ULL << 63;
+ } else
+ ctm->matrix[i] =
+ (int64_t) (coeffs[i] * ((int64_t) 1L << 32));
+ }
+}
+
+/**
+ * The three functions below contain three different methods of generating a
+ * gamma LUT. They all output it in the intermediary coefficient format.
+ *
+ * Call coeffs_to_lut on the coefficients to translate them.
+ */
static void load_table_max(struct color3d *coeffs, int lut_size)
{
int i;
@@ -95,121 +174,402 @@ static void load_table_zero(struct color3d *coeffs, int lut_size)
coeffs[i].r = coeffs[i].g = coeffs[i].b = 0.0;
}
-static void load_table(struct color3d *coeffs, int lut_size, double exp)
+static void load_table(struct color3d *coeffs, int lut_size, double *exps)
{
int i;
+ double sanitized_exps[3];
+
+ double min = 1.0 / (1 << 10);
+
+ /* Ensure no zero exps. */
+ for (i = 0; i < 3; i++) {
+ if (exps[i] < min)
+ sanitized_exps[i] = min;
+ else
+ sanitized_exps[i] = exps[i];
+ }
+
for (i = 0; i < lut_size; i++) {
- coeffs[i].r = coeffs[i].g = coeffs[i].b =
- pow((double) i * 1.0 / (double) (lut_size - 1), exp);
+ coeffs[i].r = pow((double) i * 1.0 / (double) (lut_size - 1),
+ 1.0 / sanitized_exps[0]);
+ coeffs[i].g = pow((double) i * 1.0 / (double) (lut_size - 1),
+ 1.0 / sanitized_exps[1]);
+ coeffs[i].b = pow((double) i * 1.0 / (double) (lut_size - 1),
+ 1.0 / sanitized_exps[2]);
}
}
-static void print_coeffs(const struct color3d *coeffs, int lut_size)
+/**
+ * Create a DRM color LUT blob using the given coefficients, and set the
+ * output's CRTC to use it. Since setting degamma and regamma follows similar
+ * procedures, a flag is used to determine which one is set. Also note the
+ * special case of setting SRGB gamma, explained further below.
+ *
+ * @drm_fd: The file descriptor of the DRM interface.
+ * @coeffs: Coefficients used to create the DRM color LUT blob.
+ * @is_srgb: True if SRGB gamma is being programmed. This is a special case,
+ * since amdgpu DC defaults to SRGB when no DRM blob (i.e. NULL) is
+ * set. In other words, there is no need to create a blob (just set
+ * the blob id to 0)
+ * @is_degamma: True if degamma is being set. Set regamma otherwise.
+ */
+static int set_gamma(int drm_fd, struct color3d *coeffs, int is_srgb,
+ int is_degamma)
{
- int i;
- for (i = 0; i < lut_size; i++) {
- printf("[%d] R:%.2f G:%.2f B:%.2f\n",
- i, coeffs[i].r, coeffs[i].g, coeffs[i].b);
+ struct _drm_color_lut lut[LUT_SIZE];
+ uint32_t blob_id = 0;
+
+ char randr_cmd[128];
+
+ int ret;
+
+ if (!is_srgb) {
+ /* Using LUT */
+ coeffs_to_lut(coeffs, lut, LUT_SIZE);
+ size_t size = sizeof(struct _drm_color_lut) * LUT_SIZE;
+ ret = drmModeCreatePropertyBlob(drm_fd, lut, size, &blob_id);
+ if (ret) {
+ printf("Failed to create blob. %d\n", ret);
+ return ret;
+ }
+
+ printf("Created property blob with id %d\n",
+ blob_id);
+ }
+ /* Else:
+ * In the special case of SRGB, don't create the blob. We just need to
+ * set a NULL blob id (0) */
+
+ sprintf(randr_cmd, "xrandr --output DisplayPort-0 --set %s %d",
+ is_degamma ? "DEGAMMA_LUT" : "GAMMA_LUT", blob_id);
+
+ printf("# %s\n", randr_cmd);
+ system(randr_cmd);
+
+ if (blob_id) {
+ /* Make sure to destroy the blob if one was created.
+ *
+ * Note that we can destroy the blob immediately after it's set.
+ * The blob property is ref-counted within the kernel, and will
+ * be freed once the CRTC it's attached on is destroyed.
+ */
+ ret = drmModeDestroyPropertyBlob(drm_fd, blob_id);
+ if (ret) {
+ printf("Failed to destroy blob. %d\n", ret);
+ return ret;
+ }
+
+ printf("Destroyed property blob with id %d\n", blob_id);
}
+ return 0;
}
-static void print_lut(const struct _drm_color_lut *lut, int lut_size)
+/**
+ * Create a DRM color LUT blob using the given coefficients, and set the
+ * output's CRTC to use it.
+ *
+ * The process is similar to set_gamma(). The only difference is the type of
+ * blob being created. See set_gamma() for a description of the steps being
+ * done.
+ */
+static int set_ctm(int drm_fd, double *coeffs)
{
- int i;
- for (i = 0; i < lut_size; i++) {
- printf("[%d] R:%4x G:%4x B:%4x\n",
- i, lut[i].red, lut[i].green, lut[i].blue);
+ struct _drm_color_ctm ctm;
+ uint32_t blob_id = 0;
+ size_t blob_size;
+
+ char randr_cmd[128];
+
+ int ret;
+
+ coeffs_to_ctm(coeffs, &ctm);
+ blob_size = sizeof(ctm);
+ ret = drmModeCreatePropertyBlob(drm_fd, &ctm, blob_size, &blob_id);
+ if (ret) {
+ printf("Failed to create blob. %d\n", ret);
+ return ret;
+ }
+ printf("Created property blob with id %d\n", blob_id);
+
+ sprintf(randr_cmd, "xrandr --output DisplayPort-0 --set CTM %d",
+ blob_id);
+
+ printf("# %s\n", randr_cmd);
+ system(randr_cmd);
+
+ ret = drmModeDestroyPropertyBlob(drm_fd, blob_id);
+ if (ret) {
+ printf("Failed to destroy blob. %d\n", ret);
+ return ret;
}
+
+ printf("Destroyed property blob with id %d\n", blob_id);
+
+ return 0;
}
-static int fd_is_device(int fd, const char *expect)
+/*******************************************************************************
+ * main function, and functions to assist in parsing input.
+ */
+
+/**
+ * Parse a list of doubles from the given string. ':' is used as the delimiter.
+ *
+ * @str: String containing doubles, delimited by ':'
+ * @count: Number of doubles expected to be found in the string.
+ *
+ * Return: an array of doubles. If the expected number of doubles is not met,
+ * return NULL.
+ */
+static double *parse_d(const char *str, int count)
{
- char name[5] = "";
- drm_version_t version = {0};
+ char *token;
+ uint32_t len = strlen(str);
+ char *cpy_str;
+ char * const cpy_str_head = malloc(sizeof(char) * len);
+ double *ret = malloc(sizeof(double) * count);
- version.name_len = 4;
- version.name = name;
- if (drmIoctl(fd, DRM_IOCTL_VERSION, &version))
- return 0;
+ cpy_str = cpy_str_head;
+ strcpy(cpy_str, str);
- return strcmp(expect, version.name) == 0;
+ int i;
+ for (i = 0; i < count; i++) {
+ token = strsep(&cpy_str, ":");
+ if (!token)
+ return NULL;
+ ret[i] = strtod(token, NULL);
+ }
+
+ free(cpy_str_head);
+ return ret;
}
-static int open_drm_device()
+/**
+ * Parse user input, and fill the coefficients array with the requested LUT.
+ * Degamma currently only supports srgb, or linear tables.
+ *
+ * @gamma_opt: User input
+ * @coeffs: Array of color3d structs. The requested LUT will be filled in here.
+ * @is_srgb: Will be set to true if user requested SRGB LUT.
+ *
+ * Return: True if user has requested gamma change. False otherwise.
+ */
+int parse_user_degamma(char *gamma_opt, struct color3d *coeffs, int *is_srgb)
{
- char *base = "/dev/dri/card";
- int i = 0;
- for (i = 0; i < 16; i++) {
- char name[80];
- int fd;
+ double linear_exps[3] = { 1.0, 1.0, 1.0 };
- sprintf(name, "%s%u", base, i);
- printf("Opening %s... ", name);
- fd = open(name, O_RDWR);
- if (fd == -1) {
- printf("Failed.\n");
- continue;
- }
+ *is_srgb = 0;
- if (fd_is_device(fd, "i915")){
- printf("Success!\n");
- return fd;
- }
+ if (!gamma_opt)
+ return 0;
- printf("Not an amdgpu device.\n");
- close(fd);
+ if (!strcmp(gamma_opt, "srgb")) {
+ printf("Using srgb degamma curve\n");
+ *is_srgb = 1;
+ return 1;
}
- return -1;
+ if (!strcmp(gamma_opt, "linear")) {
+ printf("Using linear degamma curve\n");
+ load_table(coeffs, LUT_SIZE, linear_exps);
+ return 1;
+ }
+
+ printf("Degamma only supports 'srgb', or 'linear' LUT. Skipping.\n");
+ return 0;
}
-/*******************************************************************************
- * main
+/**
+ * Parse user input, and fill the coefficients array with the requested CTM.
+ *
+ * @ctm_opt: user input
+ * @coeffs: Array of 9 doubles. The requested CTM will be filled in here.
+ *
+ * Return: True if user has requested CTM change. False otherwise.
*/
-
-int main(int argc, char const *argv[])
+int parse_user_ctm(char *ctm_opt, double *coeffs)
{
- struct color3d coeffs[LUT_SIZE];
- struct _drm_color_lut lut[LUT_SIZE];
+ if (!ctm_opt)
+ return 0;
- int ret;
+ if (!strcmp(ctm_opt, "id")) {
+ printf("Using identity CTM\n");
+ double temp[9] = {
+ 1, 0, 0,
+ 0, 1, 0,
+ 0, 0, 1
+ };
+ memcpy(coeffs, temp, sizeof(double) * 9);
+ return 1;
+ }
+ /* CTM is left-multiplied with the input color vector */
+ if (!strcmp(ctm_opt, "rg")) {
+ printf("Using red-to-green CTM\n");
+ double temp[9] = {
+ 0, 0, 0,
+ 1, 1, 0,
+ 0, 0, 1
+ };
+ memcpy(coeffs, temp, sizeof(double) * 9);
+ return 1;
+ }
+ if (!strcmp(ctm_opt, "rb")) {
+ printf("Using red-to-blue CTM\n");
+ double temp[9] = {
+ 0, 0, 0,
+ 0, 1, 0,
+ 1, 0, 1
+ };
+ memcpy(coeffs, temp, sizeof(double) * 9);
+ return 1;
+ }
- int fd = open_drm_device();
- if (fd == -1) {
- printf("No valid devices found\n");
- return -1;
+ double *temp = parse_d(ctm_opt, 9);
+ if (!temp) {
+ printf("%s is not a valid CTM. Skipping.\n",
+ ctm_opt);
+ return 0;
}
- load_table(coeffs, LUT_SIZE, 1);
- coeffs_to_lut(coeffs, lut, LUT_SIZE);
+ printf("Using custom CTM:\n");
+ printf(" %2.4f:%2.4f:%2.4f\n", temp[0], temp[1], temp[2]);
+ printf(" %2.4f:%2.4f:%2.4f\n", temp[3], temp[4], temp[5]);
+ printf(" %2.4f:%2.4f:%2.4f\n", temp[6], temp[7], temp[8]);
- size_t size = sizeof(struct _drm_color_lut) * LUT_SIZE;
- uint32_t blob_id = 0;
+ memcpy(coeffs, temp, sizeof(double) * 9);
+ free(temp);
+ return 1;
- ret = drmModeCreatePropertyBlob(fd, lut, size, &blob_id);
- if (ret) {
- printf("Failed to create blob. %d\n", ret);
- return ret;
+}
+
+/**
+ * Parse user input, and fill the coefficients array with the requested LUT.
+ * If predefined SRGB LUT is requested, the coefficients array is not touched,
+ * and is_srgb is set to true. See set_gamma() for why.
+ *
+ * @gamma_opt: User input
+ * @coeffs: Array of color3d structs. The requested LUT will be filled in here.
+ * @is_srgb: Will be set to true if user requested SRGB LUT.
+ *
+ * Return: True if user has requested gamma change. False otherwise.
+ */
+int parse_user_regamma(char *gamma_opt, struct color3d *coeffs, int *is_srgb)
+{
+ *is_srgb = 0;
+
+ if (!gamma_opt)
+ return 0;
+
+ if (!strcmp(gamma_opt, "max")) {
+ /* Use max gamma curve */
+ printf("Using max regamma curve\n");
+ load_table_max(coeffs, LUT_SIZE);
+ return 1;
+ }
+ if (!strcmp(gamma_opt, "min")) {
+ /* Use min gamma curve */
+ printf("Using zero regamma curve\n");
+ load_table_zero(coeffs, LUT_SIZE);
+ return 1;
+ }
+ if (!strcmp(gamma_opt, "srgb")) {
+ printf("Using srgb regamma curve\n");
+ *is_srgb = 1;
+ return 1;
}
- printf("Successfully created property blob with id %d\n", blob_id);
- drmModePropertyBlobPtr blob = drmModeGetPropertyBlob(fd, blob_id);
- if (!blob) {
- printf("Failed to get blob.\n");
- return -1;
+ /* Custom exponential curve */
+ double *exps = parse_d(gamma_opt, 3);
+ if (!exps) {
+ printf("%s is not a valid regamma exponent triple. Skipping.\n",
+ gamma_opt);
+ return 0;
}
+ printf("Using custom regamma curve %.4f:%.4f:%.4f\n",
+ exps[0], exps[1], exps[2]);
+ load_table(coeffs, LUT_SIZE, exps);
+ free(exps);
+ return 1;
+}
- struct _drm_color_lut *ret_lut = (struct _drm_color_lut *)blob->data;
+int main(int argc, char *const argv[])
+{
+ struct color3d degamma_coeffs[LUT_SIZE];
+ double ctm_coeffs[9];
+ struct color3d regamma_coeffs[LUT_SIZE];
- print_lut(ret_lut, LUT_SIZE);
+ int drm_fd;
+ int ret;
- ret = drmModeDestroyPropertyBlob(fd, blob_id);
- if (ret) {
- printf("Failed to destroy blob. %d\n", ret);
+ char cmd_enable_color_mgmt[] =
+ "xrandr --output DisplayPort-0 --set use_color_mgmt 1";
+
+ /*
+ * Parse arguments
+ */
+
+ int opt;
+ char *degamma_opt = NULL;
+ char *ctm_opt = NULL;
+ char *regamma_opt = NULL;
+
+ int degamma_changed, degamma_is_srgb;
+ int ctm_changed;
+ int regamma_changed, regamma_is_srgb;
+
+ while ((opt = getopt(argc, argv, "d:c:r:")) != -1) {
+ if (opt == 'd')
+ degamma_opt = optarg;
+ else if (opt == 'c')
+ ctm_opt = optarg;
+ else if (opt == 'r')
+ regamma_opt = optarg;
+ else {
+ printf("Invalid use.\n");
+ return -1;
+ }
+ }
+
+ /* Parse the input, and create an intermediate 'coefficient' form of
+ * the blob. Further translation is needed before the blob can be
+ * interpreted by DRM.
+ */
+ degamma_changed = parse_user_degamma(degamma_opt, degamma_coeffs,
+ &degamma_is_srgb);
+ ctm_changed = parse_user_ctm(ctm_opt, ctm_coeffs);
+ regamma_changed = parse_user_regamma(regamma_opt, regamma_coeffs,
+ &regamma_is_srgb);
+
+ if (!degamma_changed && !ctm_changed && !regamma_changed)
+ return 0;
+
+ drm_fd = open_drm_device();
+ if (drm_fd == -1) {
+ printf("No valid devices found\n");
+ printf("Did you run with admin privilege?\n");
return -1;
}
- drmModeFreePropertyBlob(blob);
+ /* Ensure non-legacy color management is enabled in xrandr */
+ printf("# %s\n", cmd_enable_color_mgmt);
+ system(cmd_enable_color_mgmt);
+
+ if (degamma_changed) {
+ ret = set_gamma(drm_fd, degamma_coeffs, degamma_is_srgb, 1);
+ if (ret)
+ return ret;
+ }
+ if (ctm_changed) {
+ ret = set_ctm(drm_fd, ctm_coeffs);
+ if (ret)
+ return ret;
+ }
+ if (regamma_changed) {
+ ret = set_gamma(drm_fd, regamma_coeffs, regamma_is_srgb, 0);
+ if (ret)
+ return ret;
+ }
+ close(drm_fd);
printf("Done!\n");
return 0;
-} \ No newline at end of file
+}