blob: cb9fb3e419597d97c11fc17d031a786b0a1357f8 [file] [log] [blame]
Dominik Behre4726302015-04-27 20:18:26 -07001/*
2 * Copyright 2015 The Chromium OS Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
5 */
6
Zach Reizner3415d062016-03-30 11:14:29 -07007#include "bs_drm.h"
8
Dominik Behre4726302015-04-27 20:18:26 -07009#include <getopt.h>
10#include <math.h>
Dominik Behre4726302015-04-27 20:18:26 -070011
Zach Reizner3415d062016-03-30 11:14:29 -070012#define TABLE_LINEAR 0
13#define TABLE_NEGATIVE 1
14#define TABLE_POW 2
15#define TABLE_STEP 3
Dominik Behre4726302015-04-27 20:18:26 -070016
Zach Reizner3415d062016-03-30 11:14:29 -070017#define FLAG_INTERNAL 'i'
18#define FLAG_EXTERNAL 'e'
19#define FLAG_GAMMA 'g'
20#define FLAG_LINEAR 'l'
21#define FLAG_NEGATIVE 'n'
22#define FLAG_TIME 't'
23#define FLAG_CRTCS 'c'
24#define FLAG_PERSIST 'p'
25#define FLAG_STEP 's'
26#define FLAG_HELP 'h'
Dominik Behre4726302015-04-27 20:18:26 -070027
Zach Reizner3415d062016-03-30 11:14:29 -070028static struct option command_options[] = { { "internal", no_argument, NULL, FLAG_INTERNAL },
29 { "external", no_argument, NULL, FLAG_EXTERNAL },
30 { "gamma", required_argument, NULL, FLAG_GAMMA },
31 { "linear", no_argument, NULL, FLAG_LINEAR },
32 { "negative", no_argument, NULL, FLAG_NEGATIVE },
33 { "time", required_argument, NULL, FLAG_TIME },
34 { "crtcs", required_argument, NULL, FLAG_CRTCS },
35 { "persist", no_argument, NULL, FLAG_PERSIST },
36 { "step", no_argument, NULL, FLAG_STEP },
37 { "help", no_argument, NULL, FLAG_HELP },
38 { NULL, 0, NULL, 0 } };
Dominik Behre4726302015-04-27 20:18:26 -070039
Zach Reizner3415d062016-03-30 11:14:29 -070040static void gamma_linear(uint16_t *table, int size)
Dominik Behre4726302015-04-27 20:18:26 -070041{
42 int i;
43 for (i = 0; i < size; i++) {
44 float v = (float)(i) / (float)(size - 1);
45 v *= 65535.0f;
46 table[i] = (uint16_t)v;
47 }
48}
49
Zach Reizner3415d062016-03-30 11:14:29 -070050static void gamma_inv(uint16_t *table, int size)
Dominik Behre4726302015-04-27 20:18:26 -070051{
52 int i;
53 for (i = 0; i < size; i++) {
54 float v = (float)(size - 1 - i) / (float)(size - 1);
55 v *= 65535.0f;
56 table[i] = (uint16_t)v;
57 }
58}
59
Zach Reizner3415d062016-03-30 11:14:29 -070060static void gamma_pow(uint16_t *table, int size, float p)
Dominik Behre4726302015-04-27 20:18:26 -070061{
62 int i;
63 for (i = 0; i < size; i++) {
64 float v = (float)(i) / (float)(size - 1);
65 v = pow(v, p);
66 v *= 65535.0f;
67 table[i] = (uint16_t)v;
68 }
69}
70
Zach Reizner3415d062016-03-30 11:14:29 -070071static void gamma_step(uint16_t *table, int size)
Dominik Behre4726302015-04-27 20:18:26 -070072{
73 int i;
74 for (i = 0; i < size; i++) {
75 table[i] = (i < size / 2) ? 0 : 65535;
76 }
77}
78
Zach Reizner3415d062016-03-30 11:14:29 -070079static void fsleep(double secs)
Dominik Behre4726302015-04-27 20:18:26 -070080{
81 usleep((useconds_t)(1000000.0f * secs));
82}
83
Zach Reizner3415d062016-03-30 11:14:29 -070084static drmModeModeInfoPtr find_best_mode(int mode_count, drmModeModeInfoPtr modes)
Dominik Behre4726302015-04-27 20:18:26 -070085{
Zach Reizner3415d062016-03-30 11:14:29 -070086 assert(mode_count >= 0);
87 if (mode_count == 0)
88 return NULL;
89
90 assert(modes);
91
92 for (int m = 0; m < mode_count; m++)
93 if (modes[m].type & DRM_MODE_TYPE_PREFERRED)
94 return &modes[m];
95
96 return &modes[0];
Dominik Behre4726302015-04-27 20:18:26 -070097}
98
Dongseong Hwang9093afe2017-03-20 19:16:28 -070099static bool draw_pattern(struct bs_mapper *mapper, struct gbm_bo *bo)
Dominik Behre4726302015-04-27 20:18:26 -0700100{
Satyajit Sahub7e47dd2018-05-07 12:35:50 +0530101 uint32_t stride;
Zach Reizner3415d062016-03-30 11:14:29 -0700102 const uint32_t height = gbm_bo_get_height(bo);
Zach Reizner3415d062016-03-30 11:14:29 -0700103 const uint32_t stripw = gbm_bo_get_width(bo) / 256;
104 const uint32_t striph = height / 4;
105
Dongseong Hwang9093afe2017-03-20 19:16:28 -0700106 void *map_data;
Satyajit Sahub7e47dd2018-05-07 12:35:50 +0530107 uint8_t *bo_ptr = bs_mapper_map(mapper, bo, 0, &map_data, &stride);
Dongseong Hwang9093afe2017-03-20 19:16:28 -0700108 if (bo_ptr == MAP_FAILED) {
Zach Reizner3415d062016-03-30 11:14:29 -0700109 bs_debug_error("failed to mmap buffer while drawing pattern");
110 return false;
Dominik Behre4726302015-04-27 20:18:26 -0700111 }
Satyajit Sahub7e47dd2018-05-07 12:35:50 +0530112 const uint32_t bo_size = stride * height;
Dominik Behre4726302015-04-27 20:18:26 -0700113
Zach Reizner3415d062016-03-30 11:14:29 -0700114 bool success = true;
Dominik Behre4726302015-04-27 20:18:26 -0700115
Zach Reizner3415d062016-03-30 11:14:29 -0700116 memset(bo_ptr, 0, bo_size);
117 for (uint32_t s = 0; s < 4; s++) {
118 uint8_t r = 0, g = 0, b = 0;
119 switch (s) {
120 case 0:
121 r = g = b = 1;
122 break;
123 case 1:
124 r = 1;
125 break;
126 case 2:
127 g = 1;
128 break;
129 case 3:
130 b = 1;
131 break;
132 default:
133 assert("invalid strip" && false);
134 success = false;
135 goto out;
136 }
137 for (uint32_t y = s * striph; y < (s + 1) * striph; y++) {
138 uint8_t *row_ptr = &bo_ptr[y * stride];
139 for (uint32_t i = 0; i < 256; i++) {
140 for (uint32_t x = i * stripw; x < (i + 1) * stripw; x++) {
141 row_ptr[x * 4 + 0] = b * i;
142 row_ptr[x * 4 + 1] = g * i;
143 row_ptr[x * 4 + 2] = r * i;
144 row_ptr[x * 4 + 3] = 0;
Dominik Behre4726302015-04-27 20:18:26 -0700145 }
146 }
147 }
Dominik Behre4726302015-04-27 20:18:26 -0700148 }
149
Zach Reizner3415d062016-03-30 11:14:29 -0700150out:
Dongseong Hwang9093afe2017-03-20 19:16:28 -0700151 bs_mapper_unmap(mapper, bo, map_data);
Zach Reizner3415d062016-03-30 11:14:29 -0700152 return success;
Dominik Behre4726302015-04-27 20:18:26 -0700153}
154
Zach Reizner3415d062016-03-30 11:14:29 -0700155static int set_gamma(int fd, uint32_t crtc_id, int gamma_size, int gamma_table, float gamma)
Dominik Behre4726302015-04-27 20:18:26 -0700156{
157 int res;
158 uint16_t *r, *g, *b;
Zach Reizner3415d062016-03-30 11:14:29 -0700159 r = calloc(gamma_size, sizeof(*r));
160 g = calloc(gamma_size, sizeof(*g));
161 b = calloc(gamma_size, sizeof(*b));
Dominik Behre4726302015-04-27 20:18:26 -0700162
163 printf("Setting gamma table %d\n", gamma_table);
164 switch (gamma_table) {
165 case TABLE_LINEAR:
Zach Reizner3415d062016-03-30 11:14:29 -0700166 gamma_linear(r, gamma_size);
167 gamma_linear(g, gamma_size);
168 gamma_linear(b, gamma_size);
Dominik Behre4726302015-04-27 20:18:26 -0700169 break;
170 case TABLE_NEGATIVE:
Zach Reizner3415d062016-03-30 11:14:29 -0700171 gamma_inv(r, gamma_size);
172 gamma_inv(g, gamma_size);
173 gamma_inv(b, gamma_size);
Dominik Behre4726302015-04-27 20:18:26 -0700174 break;
175 case TABLE_POW:
Zach Reizner3415d062016-03-30 11:14:29 -0700176 gamma_pow(r, gamma_size, gamma);
177 gamma_pow(g, gamma_size, gamma);
178 gamma_pow(b, gamma_size, gamma);
Dominik Behre4726302015-04-27 20:18:26 -0700179 break;
180 case TABLE_STEP:
Zach Reizner3415d062016-03-30 11:14:29 -0700181 gamma_step(r, gamma_size);
182 gamma_step(g, gamma_size);
183 gamma_step(b, gamma_size);
Dominik Behre4726302015-04-27 20:18:26 -0700184 break;
185 }
186
Zach Reizner3415d062016-03-30 11:14:29 -0700187 res = drmModeCrtcSetGamma(fd, crtc_id, gamma_size, r, g, b);
188 if (res)
189 bs_debug_error("drmModeCrtcSetGamma(%d) failed: %s", crtc_id, strerror(errno));
190 free(r);
191 free(g);
192 free(b);
Dominik Behre4726302015-04-27 20:18:26 -0700193 return res;
194}
195
196void help(void)
197{
Zach Reizner3415d062016-03-30 11:14:29 -0700198 printf(
199 "\
Dominik Behre4726302015-04-27 20:18:26 -0700200gamma test\n\
201command line options:\
202\n\
203--help - this\n\
204--linear - set linear gamma table\n\
205--negative - set negative linear gamma table\n\
206--step - set step gamma table\n\
207--gamma=f - set pow(gamma) gamma table with gamma=f\n\
208--time=f - set test time\n\
209--crtcs=n - set mask of crtcs to test\n\
210--persist - do not reset gamma table at the end of the test\n\
211--internal - display tests on internal display\n\
212--external - display tests on external display\n\
213");
214}
215
216int main(int argc, char **argv)
217{
Dominik Behre4726302015-04-27 20:18:26 -0700218 int internal = 1;
219 int persist = 0;
220 float time = 5.0;
221 float gamma = 2.2f;
222 float table = TABLE_LINEAR;
Zach Reizner3415d062016-03-30 11:14:29 -0700223 uint32_t crtcs = 0xFFFF;
Dominik Behre4726302015-04-27 20:18:26 -0700224
225 for (;;) {
Zach Reizner3415d062016-03-30 11:14:29 -0700226 int c = getopt_long(argc, argv, "", command_options, NULL);
Dominik Behre4726302015-04-27 20:18:26 -0700227
228 if (c == -1)
229 break;
230
231 switch (c) {
232 case FLAG_HELP:
233 help();
234 return 0;
235
236 case FLAG_INTERNAL:
237 internal = 1;
238 break;
239
240 case FLAG_EXTERNAL:
241 internal = 0;
242 break;
243
244 case FLAG_GAMMA:
245 gamma = strtof(optarg, NULL);
246 table = TABLE_POW;
247 break;
248
249 case FLAG_LINEAR:
250 table = TABLE_LINEAR;
251 break;
252
253 case FLAG_NEGATIVE:
254 table = TABLE_NEGATIVE;
255 break;
256
257 case FLAG_STEP:
258 table = TABLE_STEP;
259 break;
260
261 case FLAG_TIME:
262 time = strtof(optarg, NULL);
263 break;
264
265 case FLAG_CRTCS:
266 crtcs = strtoul(optarg, NULL, 0);
267 break;
268
269 case FLAG_PERSIST:
270 persist = 1;
271 break;
272 }
273 }
274
Zach Reizner3415d062016-03-30 11:14:29 -0700275 drmModeConnector *connector = NULL;
276 struct bs_drm_pipe pipe = { 0 };
277 struct bs_drm_pipe_plumber *plumber = bs_drm_pipe_plumber_new();
278 bs_drm_pipe_plumber_connector_ptr(plumber, &connector);
279 bs_drm_pipe_plumber_crtc_mask(plumber, crtcs);
280 if (!internal)
281 bs_drm_pipe_plumber_connector_ranks(plumber, bs_drm_connectors_external_rank);
282 if (!bs_drm_pipe_plumber_make(plumber, &pipe)) {
283 bs_debug_error("failed to make pipe");
Dominik Behre4726302015-04-27 20:18:26 -0700284 return 1;
285 }
286
Zach Reizner3415d062016-03-30 11:14:29 -0700287 int fd = pipe.fd;
288 bs_drm_pipe_plumber_fd(plumber, fd);
289 drmModeRes *resources = drmModeGetResources(fd);
Dominik Behre4726302015-04-27 20:18:26 -0700290 if (!resources) {
Zach Reizner3415d062016-03-30 11:14:29 -0700291 bs_debug_error("failed to get drm resources");
Dominik Behre4726302015-04-27 20:18:26 -0700292 return 1;
293 }
294
Zach Reizner3415d062016-03-30 11:14:29 -0700295 struct gbm_device *gbm = gbm_create_device(fd);
296 if (!gbm) {
297 bs_debug_error("failed to create gbm");
298 return 1;
299 }
300
Shirish S99c916e2017-07-07 15:57:57 +0530301 struct bs_mapper *mapper = bs_mapper_gem_new();
Dongseong Hwang9093afe2017-03-20 19:16:28 -0700302 if (mapper == NULL) {
303 bs_debug_error("failed to create mapper object");
304 return 1;
305 }
306
Gurchetan Singh5dd7b702017-02-24 17:56:33 -0800307 uint32_t num_success = 0;
Zach Reizner3415d062016-03-30 11:14:29 -0700308 for (int c = 0; c < resources->count_crtcs && (crtcs >> c); c++) {
Dominik Behre4726302015-04-27 20:18:26 -0700309 int ret;
310 drmModeCrtc *crtc;
Zach Reizner3415d062016-03-30 11:14:29 -0700311 uint32_t crtc_mask = 1u << c;
Dominik Behre4726302015-04-27 20:18:26 -0700312
Zach Reizner3415d062016-03-30 11:14:29 -0700313 if (!(crtcs & crtc_mask))
Dominik Behre4726302015-04-27 20:18:26 -0700314 continue;
Zach Reizner3415d062016-03-30 11:14:29 -0700315
316 if (connector != NULL) {
317 drmModeFreeConnector(connector);
318 connector = NULL;
Dominik Behre4726302015-04-27 20:18:26 -0700319 }
320
Zach Reizner3415d062016-03-30 11:14:29 -0700321 bs_drm_pipe_plumber_crtc_mask(plumber, crtc_mask);
322 if (!bs_drm_pipe_plumber_make(plumber, &pipe)) {
Gurchetan Singh5dd7b702017-02-24 17:56:33 -0800323 printf("unable to make pipe with crtc mask: %x\n", crtc_mask);
324 continue;
Dominik Behre4726302015-04-27 20:18:26 -0700325 }
326
Zach Reizner3415d062016-03-30 11:14:29 -0700327 crtc = drmModeGetCrtc(fd, pipe.crtc_id);
328 if (!crtc) {
329 bs_debug_error("drmModeGetCrtc(%d) failed: %s\n", pipe.crtc_id,
330 strerror(errno));
331 return 1;
Dominik Behre4726302015-04-27 20:18:26 -0700332 }
Zach Reizner3415d062016-03-30 11:14:29 -0700333 int gamma_size = crtc->gamma_size;
334 drmModeFreeCrtc(crtc);
335
336 if (!gamma_size) {
337 bs_debug_error("CRTC %d has no gamma table", crtc->crtc_id);
338 continue;
339 }
340
341 printf("CRTC:%d gamma size:%d\n", pipe.crtc_id, gamma_size);
342
343 printf("Using CRTC:%u ENCODER:%u CONNECTOR:%u\n", pipe.crtc_id, pipe.encoder_id,
344 pipe.connector_id);
345
346 drmModeModeInfoPtr mode = find_best_mode(connector->count_modes, connector->modes);
347 if (!mode) {
348 bs_debug_error("Could not find mode for CRTC %d", pipe.crtc_id);
349 continue;
350 }
351
352 printf("Using mode %s\n", mode->name);
353
354 printf("Creating buffer %ux%u\n", mode->hdisplay, mode->vdisplay);
355 struct gbm_bo *bo =
Zach Reiznera21d9eb2016-09-28 11:40:13 -0700356 gbm_bo_create(gbm, mode->hdisplay, mode->vdisplay, GBM_FORMAT_XRGB8888,
Gurchetan Singh18a05992017-11-08 15:43:57 -0800357 GBM_BO_USE_SCANOUT | GBM_BO_USE_SW_WRITE_RARELY);
Zach Reizner3415d062016-03-30 11:14:29 -0700358 if (!bo) {
359 bs_debug_error("failed to create buffer object");
360 return 1;
361 }
362
363 uint32_t fb_id = bs_drm_fb_create_gbm(bo);
364 if (!fb_id) {
365 bs_debug_error("failed to create frame buffer for buffer object");
366 return 1;
367 }
368
Dongseong Hwang9093afe2017-03-20 19:16:28 -0700369 if (!draw_pattern(mapper, bo)) {
Zach Reizner3415d062016-03-30 11:14:29 -0700370 bs_debug_error("failed to draw pattern on buffer object");
371 return 1;
372 }
373
374 ret = drmModeSetCrtc(fd, pipe.crtc_id, fb_id, 0, 0, &pipe.connector_id, 1, mode);
375 if (ret < 0) {
376 bs_debug_error("Could not set mode on CRTC %d %s", pipe.crtc_id,
377 strerror(errno));
378 return 1;
379 }
380
381 ret = set_gamma(fd, pipe.crtc_id, gamma_size, table, gamma);
382 if (ret)
383 return ret;
Dominik Behre4726302015-04-27 20:18:26 -0700384
385 fsleep(time);
386
387 if (!persist) {
Zach Reizner3415d062016-03-30 11:14:29 -0700388 ret = set_gamma(fd, pipe.crtc_id, gamma_size, TABLE_LINEAR, 0.0f);
389 if (ret)
Dominik Behre4726302015-04-27 20:18:26 -0700390 return ret;
Dominik Behre4726302015-04-27 20:18:26 -0700391 }
392
Zach Reizner3415d062016-03-30 11:14:29 -0700393 ret = drmModeSetCrtc(fd, pipe.crtc_id, 0, 0, 0, NULL, 0, NULL);
Dominik Behre4726302015-04-27 20:18:26 -0700394 if (ret < 0) {
Zach Reizner3415d062016-03-30 11:14:29 -0700395 bs_debug_error("Could disable CRTC %d %s\n", pipe.crtc_id, strerror(errno));
Gurchetan Singh5dd7b702017-02-24 17:56:33 -0800396 return 1;
Dominik Behre4726302015-04-27 20:18:26 -0700397 }
Zach Reizner3415d062016-03-30 11:14:29 -0700398
399 drmModeRmFB(fd, fb_id);
400 gbm_bo_destroy(bo);
Gurchetan Singh5dd7b702017-02-24 17:56:33 -0800401 num_success++;
Zach Reizner3415d062016-03-30 11:14:29 -0700402 }
Dongseong Hwang9093afe2017-03-20 19:16:28 -0700403 bs_mapper_destroy(mapper);
Zach Reizner3415d062016-03-30 11:14:29 -0700404
405 if (connector != NULL) {
406 drmModeFreeConnector(connector);
407 connector = NULL;
Dominik Behre4726302015-04-27 20:18:26 -0700408 }
409
410 drmModeFreeResources(resources);
Zach Reizner3415d062016-03-30 11:14:29 -0700411 bs_drm_pipe_plumber_destroy(&plumber);
Dominik Behre4726302015-04-27 20:18:26 -0700412
Gurchetan Singh5dd7b702017-02-24 17:56:33 -0800413 if (!num_success) {
414 bs_debug_error("unable to set gamma table on any CRTC");
415 return 1;
416 }
417
Dominik Behre4726302015-04-27 20:18:26 -0700418 return 0;
419}