blob: 587e0a15e4f351ffe5b29caff555fad71eb54f68 [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
Zach Reizner3415d062016-03-30 11:14:29 -070099static bool draw_pattern(struct gbm_bo *bo)
Dominik Behre4726302015-04-27 20:18:26 -0700100{
Zach Reizner3415d062016-03-30 11:14:29 -0700101 const uint32_t stride = gbm_bo_get_stride(bo);
102 const uint32_t height = gbm_bo_get_height(bo);
103 const uint32_t bo_size = stride * height;
104 const uint32_t stripw = gbm_bo_get_width(bo) / 256;
105 const uint32_t striph = height / 4;
106
Dongseong Hwang2193f5c2016-04-22 17:25:58 +0300107 uint8_t *bo_ptr = bs_dma_buf_mmap(bo);
Zach Reizner3415d062016-03-30 11:14:29 -0700108 if (!bo_ptr) {
109 bs_debug_error("failed to mmap buffer while drawing pattern");
110 return false;
Dominik Behre4726302015-04-27 20:18:26 -0700111 }
Dominik Behre4726302015-04-27 20:18:26 -0700112
Zach Reizner3415d062016-03-30 11:14:29 -0700113 bool success = true;
Dominik Behre4726302015-04-27 20:18:26 -0700114
Zach Reizner3415d062016-03-30 11:14:29 -0700115 memset(bo_ptr, 0, bo_size);
116 for (uint32_t s = 0; s < 4; s++) {
117 uint8_t r = 0, g = 0, b = 0;
118 switch (s) {
119 case 0:
120 r = g = b = 1;
121 break;
122 case 1:
123 r = 1;
124 break;
125 case 2:
126 g = 1;
127 break;
128 case 3:
129 b = 1;
130 break;
131 default:
132 assert("invalid strip" && false);
133 success = false;
134 goto out;
135 }
136 for (uint32_t y = s * striph; y < (s + 1) * striph; y++) {
137 uint8_t *row_ptr = &bo_ptr[y * stride];
138 for (uint32_t i = 0; i < 256; i++) {
139 for (uint32_t x = i * stripw; x < (i + 1) * stripw; x++) {
140 row_ptr[x * 4 + 0] = b * i;
141 row_ptr[x * 4 + 1] = g * i;
142 row_ptr[x * 4 + 2] = r * i;
143 row_ptr[x * 4 + 3] = 0;
Dominik Behre4726302015-04-27 20:18:26 -0700144 }
145 }
146 }
Dominik Behre4726302015-04-27 20:18:26 -0700147 }
148
Zach Reizner3415d062016-03-30 11:14:29 -0700149out:
Dongseong Hwang2193f5c2016-04-22 17:25:58 +0300150 bs_dma_buf_unmmap(bo, bo_ptr);
Zach Reizner3415d062016-03-30 11:14:29 -0700151 return success;
Dominik Behre4726302015-04-27 20:18:26 -0700152}
153
Zach Reizner3415d062016-03-30 11:14:29 -0700154static int set_gamma(int fd, uint32_t crtc_id, int gamma_size, int gamma_table, float gamma)
Dominik Behre4726302015-04-27 20:18:26 -0700155{
156 int res;
157 uint16_t *r, *g, *b;
Zach Reizner3415d062016-03-30 11:14:29 -0700158 r = calloc(gamma_size, sizeof(*r));
159 g = calloc(gamma_size, sizeof(*g));
160 b = calloc(gamma_size, sizeof(*b));
Dominik Behre4726302015-04-27 20:18:26 -0700161
162 printf("Setting gamma table %d\n", gamma_table);
163 switch (gamma_table) {
164 case TABLE_LINEAR:
Zach Reizner3415d062016-03-30 11:14:29 -0700165 gamma_linear(r, gamma_size);
166 gamma_linear(g, gamma_size);
167 gamma_linear(b, gamma_size);
Dominik Behre4726302015-04-27 20:18:26 -0700168 break;
169 case TABLE_NEGATIVE:
Zach Reizner3415d062016-03-30 11:14:29 -0700170 gamma_inv(r, gamma_size);
171 gamma_inv(g, gamma_size);
172 gamma_inv(b, gamma_size);
Dominik Behre4726302015-04-27 20:18:26 -0700173 break;
174 case TABLE_POW:
Zach Reizner3415d062016-03-30 11:14:29 -0700175 gamma_pow(r, gamma_size, gamma);
176 gamma_pow(g, gamma_size, gamma);
177 gamma_pow(b, gamma_size, gamma);
Dominik Behre4726302015-04-27 20:18:26 -0700178 break;
179 case TABLE_STEP:
Zach Reizner3415d062016-03-30 11:14:29 -0700180 gamma_step(r, gamma_size);
181 gamma_step(g, gamma_size);
182 gamma_step(b, gamma_size);
Dominik Behre4726302015-04-27 20:18:26 -0700183 break;
184 }
185
Zach Reizner3415d062016-03-30 11:14:29 -0700186 res = drmModeCrtcSetGamma(fd, crtc_id, gamma_size, r, g, b);
187 if (res)
188 bs_debug_error("drmModeCrtcSetGamma(%d) failed: %s", crtc_id, strerror(errno));
189 free(r);
190 free(g);
191 free(b);
Dominik Behre4726302015-04-27 20:18:26 -0700192 return res;
193}
194
195void help(void)
196{
Zach Reizner3415d062016-03-30 11:14:29 -0700197 printf(
198 "\
Dominik Behre4726302015-04-27 20:18:26 -0700199gamma test\n\
200command line options:\
201\n\
202--help - this\n\
203--linear - set linear gamma table\n\
204--negative - set negative linear gamma table\n\
205--step - set step gamma table\n\
206--gamma=f - set pow(gamma) gamma table with gamma=f\n\
207--time=f - set test time\n\
208--crtcs=n - set mask of crtcs to test\n\
209--persist - do not reset gamma table at the end of the test\n\
210--internal - display tests on internal display\n\
211--external - display tests on external display\n\
212");
213}
214
215int main(int argc, char **argv)
216{
Dominik Behre4726302015-04-27 20:18:26 -0700217 int internal = 1;
218 int persist = 0;
219 float time = 5.0;
220 float gamma = 2.2f;
221 float table = TABLE_LINEAR;
Zach Reizner3415d062016-03-30 11:14:29 -0700222 uint32_t crtcs = 0xFFFF;
Dominik Behre4726302015-04-27 20:18:26 -0700223
224 for (;;) {
Zach Reizner3415d062016-03-30 11:14:29 -0700225 int c = getopt_long(argc, argv, "", command_options, NULL);
Dominik Behre4726302015-04-27 20:18:26 -0700226
227 if (c == -1)
228 break;
229
230 switch (c) {
231 case FLAG_HELP:
232 help();
233 return 0;
234
235 case FLAG_INTERNAL:
236 internal = 1;
237 break;
238
239 case FLAG_EXTERNAL:
240 internal = 0;
241 break;
242
243 case FLAG_GAMMA:
244 gamma = strtof(optarg, NULL);
245 table = TABLE_POW;
246 break;
247
248 case FLAG_LINEAR:
249 table = TABLE_LINEAR;
250 break;
251
252 case FLAG_NEGATIVE:
253 table = TABLE_NEGATIVE;
254 break;
255
256 case FLAG_STEP:
257 table = TABLE_STEP;
258 break;
259
260 case FLAG_TIME:
261 time = strtof(optarg, NULL);
262 break;
263
264 case FLAG_CRTCS:
265 crtcs = strtoul(optarg, NULL, 0);
266 break;
267
268 case FLAG_PERSIST:
269 persist = 1;
270 break;
271 }
272 }
273
Zach Reizner3415d062016-03-30 11:14:29 -0700274 drmModeConnector *connector = NULL;
275 struct bs_drm_pipe pipe = { 0 };
276 struct bs_drm_pipe_plumber *plumber = bs_drm_pipe_plumber_new();
277 bs_drm_pipe_plumber_connector_ptr(plumber, &connector);
278 bs_drm_pipe_plumber_crtc_mask(plumber, crtcs);
279 if (!internal)
280 bs_drm_pipe_plumber_connector_ranks(plumber, bs_drm_connectors_external_rank);
281 if (!bs_drm_pipe_plumber_make(plumber, &pipe)) {
282 bs_debug_error("failed to make pipe");
Dominik Behre4726302015-04-27 20:18:26 -0700283 return 1;
284 }
285
Zach Reizner3415d062016-03-30 11:14:29 -0700286 int fd = pipe.fd;
287 bs_drm_pipe_plumber_fd(plumber, fd);
288 drmModeRes *resources = drmModeGetResources(fd);
Dominik Behre4726302015-04-27 20:18:26 -0700289 if (!resources) {
Zach Reizner3415d062016-03-30 11:14:29 -0700290 bs_debug_error("failed to get drm resources");
Dominik Behre4726302015-04-27 20:18:26 -0700291 return 1;
292 }
293
Zach Reizner3415d062016-03-30 11:14:29 -0700294 struct gbm_device *gbm = gbm_create_device(fd);
295 if (!gbm) {
296 bs_debug_error("failed to create gbm");
297 return 1;
298 }
299
300 for (int c = 0; c < resources->count_crtcs && (crtcs >> c); c++) {
Dominik Behre4726302015-04-27 20:18:26 -0700301 int ret;
302 drmModeCrtc *crtc;
Zach Reizner3415d062016-03-30 11:14:29 -0700303 uint32_t crtc_mask = 1u << c;
Dominik Behre4726302015-04-27 20:18:26 -0700304
Zach Reizner3415d062016-03-30 11:14:29 -0700305 if (!(crtcs & crtc_mask))
Dominik Behre4726302015-04-27 20:18:26 -0700306 continue;
Zach Reizner3415d062016-03-30 11:14:29 -0700307
308 if (connector != NULL) {
309 drmModeFreeConnector(connector);
310 connector = NULL;
Dominik Behre4726302015-04-27 20:18:26 -0700311 }
312
Zach Reizner3415d062016-03-30 11:14:29 -0700313 bs_drm_pipe_plumber_crtc_mask(plumber, crtc_mask);
314 if (!bs_drm_pipe_plumber_make(plumber, &pipe)) {
315 bs_debug_error("failed to make pipe with crtc mask: %x", crtc_mask);
Dominik Behre4726302015-04-27 20:18:26 -0700316 return 1;
317 }
318
Zach Reizner3415d062016-03-30 11:14:29 -0700319 crtc = drmModeGetCrtc(fd, pipe.crtc_id);
320 if (!crtc) {
321 bs_debug_error("drmModeGetCrtc(%d) failed: %s\n", pipe.crtc_id,
322 strerror(errno));
323 return 1;
Dominik Behre4726302015-04-27 20:18:26 -0700324 }
Zach Reizner3415d062016-03-30 11:14:29 -0700325 int gamma_size = crtc->gamma_size;
326 drmModeFreeCrtc(crtc);
327
328 if (!gamma_size) {
329 bs_debug_error("CRTC %d has no gamma table", crtc->crtc_id);
330 continue;
331 }
332
333 printf("CRTC:%d gamma size:%d\n", pipe.crtc_id, gamma_size);
334
335 printf("Using CRTC:%u ENCODER:%u CONNECTOR:%u\n", pipe.crtc_id, pipe.encoder_id,
336 pipe.connector_id);
337
338 drmModeModeInfoPtr mode = find_best_mode(connector->count_modes, connector->modes);
339 if (!mode) {
340 bs_debug_error("Could not find mode for CRTC %d", pipe.crtc_id);
341 continue;
342 }
343
344 printf("Using mode %s\n", mode->name);
345
346 printf("Creating buffer %ux%u\n", mode->hdisplay, mode->vdisplay);
347 struct gbm_bo *bo =
Zach Reiznera21d9eb2016-09-28 11:40:13 -0700348 gbm_bo_create(gbm, mode->hdisplay, mode->vdisplay, GBM_FORMAT_XRGB8888,
349 GBM_BO_USE_SCANOUT | GBM_BO_USE_LINEAR);
Zach Reizner3415d062016-03-30 11:14:29 -0700350 if (!bo) {
351 bs_debug_error("failed to create buffer object");
352 return 1;
353 }
354
355 uint32_t fb_id = bs_drm_fb_create_gbm(bo);
356 if (!fb_id) {
357 bs_debug_error("failed to create frame buffer for buffer object");
358 return 1;
359 }
360
361 if (!draw_pattern(bo)) {
362 bs_debug_error("failed to draw pattern on buffer object");
363 return 1;
364 }
365
366 ret = drmModeSetCrtc(fd, pipe.crtc_id, fb_id, 0, 0, &pipe.connector_id, 1, mode);
367 if (ret < 0) {
368 bs_debug_error("Could not set mode on CRTC %d %s", pipe.crtc_id,
369 strerror(errno));
370 return 1;
371 }
372
373 ret = set_gamma(fd, pipe.crtc_id, gamma_size, table, gamma);
374 if (ret)
375 return ret;
Dominik Behre4726302015-04-27 20:18:26 -0700376
377 fsleep(time);
378
379 if (!persist) {
Zach Reizner3415d062016-03-30 11:14:29 -0700380 ret = set_gamma(fd, pipe.crtc_id, gamma_size, TABLE_LINEAR, 0.0f);
381 if (ret)
Dominik Behre4726302015-04-27 20:18:26 -0700382 return ret;
Dominik Behre4726302015-04-27 20:18:26 -0700383 }
384
Zach Reizner3415d062016-03-30 11:14:29 -0700385 ret = drmModeSetCrtc(fd, pipe.crtc_id, 0, 0, 0, NULL, 0, NULL);
Dominik Behre4726302015-04-27 20:18:26 -0700386 if (ret < 0) {
Zach Reizner3415d062016-03-30 11:14:29 -0700387 bs_debug_error("Could disable CRTC %d %s\n", pipe.crtc_id, strerror(errno));
Dominik Behre4726302015-04-27 20:18:26 -0700388 }
Zach Reizner3415d062016-03-30 11:14:29 -0700389
390 drmModeRmFB(fd, fb_id);
391 gbm_bo_destroy(bo);
392 }
393
394 if (connector != NULL) {
395 drmModeFreeConnector(connector);
396 connector = NULL;
Dominik Behre4726302015-04-27 20:18:26 -0700397 }
398
399 drmModeFreeResources(resources);
Zach Reizner3415d062016-03-30 11:14:29 -0700400 bs_drm_pipe_plumber_destroy(&plumber);
Dominik Behre4726302015-04-27 20:18:26 -0700401
402 return 0;
403}