blob: 22e3e81bd3cce0e6fae5009d9da3b98c54c3562b [file] [log] [blame]
Jesse Barnes731cd552008-12-17 10:09:49 -08001/*
2 * DRM based mode setting test program
3 * Copyright 2008 Tungsten Graphics
4 * Jakob Bornecrantz <jakob@tungstengraphics.com>
5 * Copyright 2008 Intel Corporation
6 * Jesse Barnes <jesse.barnes@intel.com>
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24 * IN THE SOFTWARE.
25 */
26
27/*
28 * This fairly simple test program dumps output in a similar format to the
29 * "xrandr" tool everyone knows & loves. It's necessarily slightly different
30 * since the kernel separates outputs into encoder and connector structures,
31 * each with their own unique ID. The program also allows test testing of the
32 * memory management and mode setting APIs by allowing the user to specify a
33 * connector and mode to use for mode setting. If all works as expected, a
34 * blue background should be painted on the monitor attached to the specified
35 * connector after the selected mode is set.
36 *
37 * TODO: use cairo to write the mode info on the selected output once
38 * the mode has been programmed, along with possible test patterns.
39 */
Thierry Reding1ec3c442015-12-09 18:37:39 +010040
Emil Velikov8e93afc2014-07-27 14:46:45 +010041#ifdef HAVE_CONFIG_H
Kristian Høgsberg7a389aa2009-02-03 15:03:41 -050042#include "config.h"
Emil Velikov8e93afc2014-07-27 14:46:45 +010043#endif
Kristian Høgsberg7a389aa2009-02-03 15:03:41 -050044
Jesse Barnes731cd552008-12-17 10:09:49 -080045#include <assert.h>
Laurent Pinchart2c5ee842013-03-19 13:48:36 +010046#include <ctype.h>
Laurent Pinchart7badcca2013-02-27 05:35:13 +010047#include <stdbool.h>
Jesse Barnes731cd552008-12-17 10:09:49 -080048#include <stdio.h>
49#include <stdlib.h>
50#include <stdint.h>
Paulo Zanonid72a44c2012-04-21 17:51:53 -030051#include <inttypes.h>
Jesse Barnes731cd552008-12-17 10:09:49 -080052#include <unistd.h>
53#include <string.h>
Greg Hackmann0c8db0a2015-04-16 10:55:40 -070054#include <strings.h>
Jesse Barnes731cd552008-12-17 10:09:49 -080055#include <errno.h>
Kristian Høgsberg1e1b3c02009-11-17 15:32:23 -050056#include <sys/poll.h>
Matthew W. S. Belle4a51962010-01-30 02:14:44 +000057#include <sys/time.h>
Jesse Barnes731cd552008-12-17 10:09:49 -080058
59#include "xf86drm.h"
60#include "xf86drmMode.h"
Rob Clarkd55de742011-12-14 21:06:43 -060061#include "drm_fourcc.h"
Jesse Barnes731cd552008-12-17 10:09:49 -080062
Thierry Reding1ec3c442015-12-09 18:37:39 +010063#include "util/common.h"
64#include "util/format.h"
Thierry Reding4664d652015-12-09 18:37:40 +010065#include "util/kms.h"
Thierry Reding1ec3c442015-12-09 18:37:39 +010066#include "util/pattern.h"
67
Laurent Pinchartdb004ba2012-07-20 16:37:00 +020068#include "buffers.h"
Rob Clark0e512792014-04-22 10:33:12 -040069#include "cursor.h"
Kristian Høgsberg7a389aa2009-02-03 15:03:41 -050070
Laurent Pinchart02fa8f72013-02-27 06:39:36 +010071struct crtc {
72 drmModeCrtc *crtc;
73 drmModeObjectProperties *props;
74 drmModePropertyRes **props_info;
Laurent Pinchart56592682013-02-27 05:35:13 +010075 drmModeModeInfo *mode;
Laurent Pinchart02fa8f72013-02-27 06:39:36 +010076};
77
78struct encoder {
79 drmModeEncoder *encoder;
80};
81
82struct connector {
83 drmModeConnector *connector;
84 drmModeObjectProperties *props;
85 drmModePropertyRes **props_info;
Thierry Redingf05a74f2015-01-23 17:08:21 +010086 char *name;
Laurent Pinchart02fa8f72013-02-27 06:39:36 +010087};
88
89struct fb {
90 drmModeFB *fb;
91};
92
93struct plane {
94 drmModePlane *plane;
95 drmModeObjectProperties *props;
96 drmModePropertyRes **props_info;
97};
98
99struct resources {
100 drmModeRes *res;
101 drmModePlaneRes *plane_res;
102
103 struct crtc *crtcs;
104 struct encoder *encoders;
105 struct connector *connectors;
106 struct fb *fbs;
107 struct plane *planes;
108};
109
Laurent Pinchart549fe0b2013-02-27 05:35:13 +0100110struct device {
111 int fd;
112
113 struct resources *resources;
Laurent Pinchart3813e0f2013-02-27 05:35:13 +0100114
115 struct {
116 unsigned int width;
117 unsigned int height;
118
119 unsigned int fb_id;
Laurent Pinchartd7c0a082014-12-09 22:00:58 +0200120 struct bo *bo;
Joonyoung Shim9915e682015-04-13 17:32:18 +0900121 struct bo *cursor_bo;
Laurent Pinchart3813e0f2013-02-27 05:35:13 +0100122 } mode;
Laurent Pinchart549fe0b2013-02-27 05:35:13 +0100123};
Jesse Barnes731cd552008-12-17 10:09:49 -0800124
Rob Clarkfb417702013-10-12 12:16:44 -0400125static inline int64_t U642I64(uint64_t val)
126{
127 return (int64_t)*((int64_t *)&val);
128}
Jesse Barnes731cd552008-12-17 10:09:49 -0800129
Kristian Høgsbergc0ed9b22012-06-28 10:48:31 -0400130#define bit_name_fn(res) \
Laurent Pinchartca9c8f02013-02-11 21:36:30 +0100131const char * res##_str(int type) { \
132 unsigned int i; \
Kristian Høgsbergc0ed9b22012-06-28 10:48:31 -0400133 const char *sep = ""; \
134 for (i = 0; i < ARRAY_SIZE(res##_names); i++) { \
135 if (type & (1 << i)) { \
136 printf("%s%s", sep, res##_names[i]); \
137 sep = ", "; \
138 } \
139 } \
Tobias Klausmann6fa2b292012-08-12 00:00:40 +0200140 return NULL; \
Kristian Høgsbergc0ed9b22012-06-28 10:48:31 -0400141}
142
143static const char *mode_type_names[] = {
144 "builtin",
145 "clock_c",
146 "crtc_c",
147 "preferred",
148 "default",
149 "userdef",
150 "driver",
151};
152
Laurent Pinchartca9c8f02013-02-11 21:36:30 +0100153static bit_name_fn(mode_type)
Kristian Høgsbergc0ed9b22012-06-28 10:48:31 -0400154
155static const char *mode_flag_names[] = {
156 "phsync",
157 "nhsync",
158 "pvsync",
159 "nvsync",
160 "interlace",
161 "dblscan",
162 "csync",
163 "pcsync",
164 "ncsync",
165 "hskew",
166 "bcast",
167 "pixmux",
168 "dblclk",
169 "clkdiv2"
170};
171
Laurent Pinchartca9c8f02013-02-11 21:36:30 +0100172static bit_name_fn(mode_flag)
Kristian Høgsbergc0ed9b22012-06-28 10:48:31 -0400173
Laurent Pinchart549fe0b2013-02-27 05:35:13 +0100174static void dump_encoders(struct device *dev)
Jesse Barnes731cd552008-12-17 10:09:49 -0800175{
176 drmModeEncoder *encoder;
177 int i;
178
179 printf("Encoders:\n");
180 printf("id\tcrtc\ttype\tpossible crtcs\tpossible clones\t\n");
Laurent Pinchart549fe0b2013-02-27 05:35:13 +0100181 for (i = 0; i < dev->resources->res->count_encoders; i++) {
182 encoder = dev->resources->encoders[i].encoder;
Laurent Pinchart02fa8f72013-02-27 06:39:36 +0100183 if (!encoder)
Jesse Barnes731cd552008-12-17 10:09:49 -0800184 continue;
Laurent Pinchart02fa8f72013-02-27 06:39:36 +0100185
Jesse Barnes731cd552008-12-17 10:09:49 -0800186 printf("%d\t%d\t%s\t0x%08x\t0x%08x\n",
187 encoder->encoder_id,
188 encoder->crtc_id,
Thierry Reding4664d652015-12-09 18:37:40 +0100189 util_lookup_encoder_type_name(encoder->encoder_type),
Jesse Barnes731cd552008-12-17 10:09:49 -0800190 encoder->possible_crtcs,
191 encoder->possible_clones);
Jesse Barnes731cd552008-12-17 10:09:49 -0800192 }
Kristian Høgsberg0243c9f2008-12-18 00:02:43 -0500193 printf("\n");
194}
195
Laurent Pinchartca9c8f02013-02-11 21:36:30 +0100196static void dump_mode(drmModeModeInfo *mode)
Kristian Høgsberg0243c9f2008-12-18 00:02:43 -0500197{
Kristian Høgsbergc0ed9b22012-06-28 10:48:31 -0400198 printf(" %s %d %d %d %d %d %d %d %d %d",
Kristian Høgsberg0243c9f2008-12-18 00:02:43 -0500199 mode->name,
Marcin Kościelnicki694ef592010-02-27 15:04:42 +0000200 mode->vrefresh,
Kristian Høgsberg0243c9f2008-12-18 00:02:43 -0500201 mode->hdisplay,
202 mode->hsync_start,
203 mode->hsync_end,
204 mode->htotal,
205 mode->vdisplay,
206 mode->vsync_start,
207 mode->vsync_end,
208 mode->vtotal);
Kristian Høgsbergc0ed9b22012-06-28 10:48:31 -0400209
210 printf(" flags: ");
211 mode_flag_str(mode->flags);
212 printf("; type: ");
213 mode_type_str(mode->type);
214 printf("\n");
Jesse Barnes731cd552008-12-17 10:09:49 -0800215}
216
Laurent Pinchart549fe0b2013-02-27 05:35:13 +0100217static void dump_blob(struct device *dev, uint32_t blob_id)
Kristian Høgsberg9fc85b42009-02-23 15:08:03 -0500218{
Paulo Zanonid72a44c2012-04-21 17:51:53 -0300219 uint32_t i;
220 unsigned char *blob_data;
221 drmModePropertyBlobPtr blob;
Kristian Høgsberg9fc85b42009-02-23 15:08:03 -0500222
Laurent Pinchart549fe0b2013-02-27 05:35:13 +0100223 blob = drmModeGetPropertyBlob(dev->fd, blob_id);
Ville Syrjäläc2c03462012-06-08 13:28:16 +0300224 if (!blob) {
225 printf("\n");
Paulo Zanonid72a44c2012-04-21 17:51:53 -0300226 return;
Ville Syrjäläc2c03462012-06-08 13:28:16 +0300227 }
Paulo Zanonid72a44c2012-04-21 17:51:53 -0300228
229 blob_data = blob->data;
230
231 for (i = 0; i < blob->length; i++) {
232 if (i % 16 == 0)
233 printf("\n\t\t\t");
234 printf("%.2hhx", blob_data[i]);
Kristian Høgsberg9fc85b42009-02-23 15:08:03 -0500235 }
Paulo Zanonid72a44c2012-04-21 17:51:53 -0300236 printf("\n");
237
238 drmModeFreePropertyBlob(blob);
239}
240
Laurent Pinchart549fe0b2013-02-27 05:35:13 +0100241static void dump_prop(struct device *dev, drmModePropertyPtr prop,
242 uint32_t prop_id, uint64_t value)
Paulo Zanonid72a44c2012-04-21 17:51:53 -0300243{
244 int i;
Paulo Zanonid72a44c2012-04-21 17:51:53 -0300245 printf("\t%d", prop_id);
246 if (!prop) {
247 printf("\n");
248 return;
249 }
250
251 printf(" %s:\n", prop->name);
252
253 printf("\t\tflags:");
254 if (prop->flags & DRM_MODE_PROP_PENDING)
255 printf(" pending");
Paulo Zanonid72a44c2012-04-21 17:51:53 -0300256 if (prop->flags & DRM_MODE_PROP_IMMUTABLE)
257 printf(" immutable");
Rob Clarkfb417702013-10-12 12:16:44 -0400258 if (drm_property_type_is(prop, DRM_MODE_PROP_SIGNED_RANGE))
259 printf(" signed range");
260 if (drm_property_type_is(prop, DRM_MODE_PROP_RANGE))
261 printf(" range");
262 if (drm_property_type_is(prop, DRM_MODE_PROP_ENUM))
Paulo Zanonid72a44c2012-04-21 17:51:53 -0300263 printf(" enum");
Rob Clarkfb417702013-10-12 12:16:44 -0400264 if (drm_property_type_is(prop, DRM_MODE_PROP_BITMASK))
Rob Clark6df9e6a2012-06-05 12:28:38 -0500265 printf(" bitmask");
Rob Clarkfb417702013-10-12 12:16:44 -0400266 if (drm_property_type_is(prop, DRM_MODE_PROP_BLOB))
Paulo Zanonid72a44c2012-04-21 17:51:53 -0300267 printf(" blob");
Rob Clarkfb417702013-10-12 12:16:44 -0400268 if (drm_property_type_is(prop, DRM_MODE_PROP_OBJECT))
269 printf(" object");
Paulo Zanonid72a44c2012-04-21 17:51:53 -0300270 printf("\n");
271
Rob Clarkfb417702013-10-12 12:16:44 -0400272 if (drm_property_type_is(prop, DRM_MODE_PROP_SIGNED_RANGE)) {
273 printf("\t\tvalues:");
274 for (i = 0; i < prop->count_values; i++)
275 printf(" %"PRId64, U642I64(prop->values[i]));
276 printf("\n");
277 }
278
279 if (drm_property_type_is(prop, DRM_MODE_PROP_RANGE)) {
Paulo Zanonid72a44c2012-04-21 17:51:53 -0300280 printf("\t\tvalues:");
281 for (i = 0; i < prop->count_values; i++)
282 printf(" %"PRIu64, prop->values[i]);
283 printf("\n");
284 }
285
Rob Clarkfb417702013-10-12 12:16:44 -0400286 if (drm_property_type_is(prop, DRM_MODE_PROP_ENUM)) {
Paulo Zanonid72a44c2012-04-21 17:51:53 -0300287 printf("\t\tenums:");
288 for (i = 0; i < prop->count_enums; i++)
289 printf(" %s=%llu", prop->enums[i].name,
290 prop->enums[i].value);
291 printf("\n");
Rob Clarkfb417702013-10-12 12:16:44 -0400292 } else if (drm_property_type_is(prop, DRM_MODE_PROP_BITMASK)) {
Rob Clark6df9e6a2012-06-05 12:28:38 -0500293 printf("\t\tvalues:");
294 for (i = 0; i < prop->count_enums; i++)
295 printf(" %s=0x%llx", prop->enums[i].name,
296 (1LL << prop->enums[i].value));
297 printf("\n");
Paulo Zanonid72a44c2012-04-21 17:51:53 -0300298 } else {
299 assert(prop->count_enums == 0);
300 }
301
Rob Clarkfb417702013-10-12 12:16:44 -0400302 if (drm_property_type_is(prop, DRM_MODE_PROP_BLOB)) {
Paulo Zanonid72a44c2012-04-21 17:51:53 -0300303 printf("\t\tblobs:\n");
304 for (i = 0; i < prop->count_blobs; i++)
Laurent Pinchart549fe0b2013-02-27 05:35:13 +0100305 dump_blob(dev, prop->blob_ids[i]);
Paulo Zanonid72a44c2012-04-21 17:51:53 -0300306 printf("\n");
307 } else {
308 assert(prop->count_blobs == 0);
309 }
310
311 printf("\t\tvalue:");
Rob Clarkfb417702013-10-12 12:16:44 -0400312 if (drm_property_type_is(prop, DRM_MODE_PROP_BLOB))
Laurent Pinchart549fe0b2013-02-27 05:35:13 +0100313 dump_blob(dev, value);
Paulo Zanonid72a44c2012-04-21 17:51:53 -0300314 else
315 printf(" %"PRIu64"\n", value);
Kristian Høgsberg9fc85b42009-02-23 15:08:03 -0500316}
317
Laurent Pinchart549fe0b2013-02-27 05:35:13 +0100318static void dump_connectors(struct device *dev)
Jesse Barnes731cd552008-12-17 10:09:49 -0800319{
Jesse Barnes731cd552008-12-17 10:09:49 -0800320 int i, j;
321
322 printf("Connectors:\n");
Thierry Redingf05a74f2015-01-23 17:08:21 +0100323 printf("id\tencoder\tstatus\t\tname\t\tsize (mm)\tmodes\tencoders\n");
Laurent Pinchart549fe0b2013-02-27 05:35:13 +0100324 for (i = 0; i < dev->resources->res->count_connectors; i++) {
325 struct connector *_connector = &dev->resources->connectors[i];
Laurent Pinchart02fa8f72013-02-27 06:39:36 +0100326 drmModeConnector *connector = _connector->connector;
327 if (!connector)
Jesse Barnes731cd552008-12-17 10:09:49 -0800328 continue;
Jesse Barnes731cd552008-12-17 10:09:49 -0800329
Thierry Redingf05a74f2015-01-23 17:08:21 +0100330 printf("%d\t%d\t%s\t%-15s\t%dx%d\t\t%d\t",
Jesse Barnes731cd552008-12-17 10:09:49 -0800331 connector->connector_id,
332 connector->encoder_id,
Thierry Reding4664d652015-12-09 18:37:40 +0100333 util_lookup_connector_status_name(connector->connection),
Thierry Redingf05a74f2015-01-23 17:08:21 +0100334 _connector->name,
Jesse Barnes731cd552008-12-17 10:09:49 -0800335 connector->mmWidth, connector->mmHeight,
336 connector->count_modes);
337
Kristian Høgsberg1e1b3c02009-11-17 15:32:23 -0500338 for (j = 0; j < connector->count_encoders; j++)
339 printf("%s%d", j > 0 ? ", " : "", connector->encoders[j]);
340 printf("\n");
341
Paulo Zanonia10bcaa2012-04-21 17:51:51 -0300342 if (connector->count_modes) {
343 printf(" modes:\n");
Paulo Zanonid72a44c2012-04-21 17:51:53 -0300344 printf("\tname refresh (Hz) hdisp hss hse htot vdisp "
Paulo Zanonia10bcaa2012-04-21 17:51:51 -0300345 "vss vse vtot)\n");
346 for (j = 0; j < connector->count_modes; j++)
347 dump_mode(&connector->modes[j]);
Paulo Zanonia10bcaa2012-04-21 17:51:51 -0300348 }
Marcin Kościelnicki9a374552010-02-27 15:04:40 +0000349
Laurent Pinchart02fa8f72013-02-27 06:39:36 +0100350 if (_connector->props) {
351 printf(" props:\n");
352 for (j = 0; j < (int)_connector->props->count_props; j++)
Laurent Pinchart549fe0b2013-02-27 05:35:13 +0100353 dump_prop(dev, _connector->props_info[j],
Laurent Pinchart02fa8f72013-02-27 06:39:36 +0100354 _connector->props->props[j],
355 _connector->props->prop_values[j]);
356 }
Jesse Barnes731cd552008-12-17 10:09:49 -0800357 }
Kristian Høgsberg0243c9f2008-12-18 00:02:43 -0500358 printf("\n");
Jesse Barnes731cd552008-12-17 10:09:49 -0800359}
360
Laurent Pinchart549fe0b2013-02-27 05:35:13 +0100361static void dump_crtcs(struct device *dev)
Jesse Barnes731cd552008-12-17 10:09:49 -0800362{
Jesse Barnes731cd552008-12-17 10:09:49 -0800363 int i;
Paulo Zanoni86dece42012-05-15 18:38:29 -0300364 uint32_t j;
Jesse Barnes731cd552008-12-17 10:09:49 -0800365
Kristian Høgsberg0243c9f2008-12-18 00:02:43 -0500366 printf("CRTCs:\n");
367 printf("id\tfb\tpos\tsize\n");
Laurent Pinchart549fe0b2013-02-27 05:35:13 +0100368 for (i = 0; i < dev->resources->res->count_crtcs; i++) {
369 struct crtc *_crtc = &dev->resources->crtcs[i];
Laurent Pinchart02fa8f72013-02-27 06:39:36 +0100370 drmModeCrtc *crtc = _crtc->crtc;
371 if (!crtc)
Jesse Barnes731cd552008-12-17 10:09:49 -0800372 continue;
Laurent Pinchart02fa8f72013-02-27 06:39:36 +0100373
Kristian Høgsberg0243c9f2008-12-18 00:02:43 -0500374 printf("%d\t%d\t(%d,%d)\t(%dx%d)\n",
375 crtc->crtc_id,
376 crtc->buffer_id,
377 crtc->x, crtc->y,
378 crtc->width, crtc->height);
379 dump_mode(&crtc->mode);
380
Laurent Pinchart02fa8f72013-02-27 06:39:36 +0100381 if (_crtc->props) {
382 printf(" props:\n");
383 for (j = 0; j < _crtc->props->count_props; j++)
Laurent Pinchart549fe0b2013-02-27 05:35:13 +0100384 dump_prop(dev, _crtc->props_info[j],
Laurent Pinchart02fa8f72013-02-27 06:39:36 +0100385 _crtc->props->props[j],
386 _crtc->props->prop_values[j]);
Paulo Zanoni86dece42012-05-15 18:38:29 -0300387 } else {
Laurent Pinchart02fa8f72013-02-27 06:39:36 +0100388 printf(" no properties found\n");
Paulo Zanoni86dece42012-05-15 18:38:29 -0300389 }
Jesse Barnes731cd552008-12-17 10:09:49 -0800390 }
Kristian Høgsberg0243c9f2008-12-18 00:02:43 -0500391 printf("\n");
Jesse Barnes731cd552008-12-17 10:09:49 -0800392}
393
Laurent Pinchart549fe0b2013-02-27 05:35:13 +0100394static void dump_framebuffers(struct device *dev)
Jesse Barnes731cd552008-12-17 10:09:49 -0800395{
396 drmModeFB *fb;
397 int i;
398
Kristian Høgsberg0243c9f2008-12-18 00:02:43 -0500399 printf("Frame buffers:\n");
400 printf("id\tsize\tpitch\n");
Laurent Pinchart549fe0b2013-02-27 05:35:13 +0100401 for (i = 0; i < dev->resources->res->count_fbs; i++) {
402 fb = dev->resources->fbs[i].fb;
Laurent Pinchart02fa8f72013-02-27 06:39:36 +0100403 if (!fb)
Jesse Barnes731cd552008-12-17 10:09:49 -0800404 continue;
Laurent Pinchart02fa8f72013-02-27 06:39:36 +0100405
Matthew W. S. Belle4a51962010-01-30 02:14:44 +0000406 printf("%u\t(%ux%u)\t%u\n",
Kristian Høgsberg0243c9f2008-12-18 00:02:43 -0500407 fb->fb_id,
Matthew W. S. Belle4a51962010-01-30 02:14:44 +0000408 fb->width, fb->height,
409 fb->pitch);
Jesse Barnes731cd552008-12-17 10:09:49 -0800410 }
Kristian Høgsberg0243c9f2008-12-18 00:02:43 -0500411 printf("\n");
Jesse Barnes731cd552008-12-17 10:09:49 -0800412}
413
Laurent Pinchart549fe0b2013-02-27 05:35:13 +0100414static void dump_planes(struct device *dev)
Rob Clarkd55de742011-12-14 21:06:43 -0600415{
Paulo Zanoni9b44fbd2012-04-21 17:51:50 -0300416 unsigned int i, j;
Rob Clarkd55de742011-12-14 21:06:43 -0600417
Rob Clarkd55de742011-12-14 21:06:43 -0600418 printf("Planes:\n");
Ville Syrjälä8e565792013-04-17 19:18:04 +0000419 printf("id\tcrtc\tfb\tCRTC x,y\tx,y\tgamma size\tpossible crtcs\n");
Laurent Pinchart02fa8f72013-02-27 06:39:36 +0100420
Laurent Pinchart549fe0b2013-02-27 05:35:13 +0100421 if (!dev->resources->plane_res)
Laurent Pinchart02fa8f72013-02-27 06:39:36 +0100422 return;
423
Laurent Pinchart549fe0b2013-02-27 05:35:13 +0100424 for (i = 0; i < dev->resources->plane_res->count_planes; i++) {
425 struct plane *plane = &dev->resources->planes[i];
Laurent Pinchart02fa8f72013-02-27 06:39:36 +0100426 drmModePlane *ovr = plane->plane;
427 if (!ovr)
Rob Clarkd55de742011-12-14 21:06:43 -0600428 continue;
Rob Clarkd55de742011-12-14 21:06:43 -0600429
Ville Syrjälä8e565792013-04-17 19:18:04 +0000430 printf("%d\t%d\t%d\t%d,%d\t\t%d,%d\t%-8d\t0x%08x\n",
Rob Clarkd55de742011-12-14 21:06:43 -0600431 ovr->plane_id, ovr->crtc_id, ovr->fb_id,
432 ovr->crtc_x, ovr->crtc_y, ovr->x, ovr->y,
Ville Syrjälä8e565792013-04-17 19:18:04 +0000433 ovr->gamma_size, ovr->possible_crtcs);
Rob Clarkd55de742011-12-14 21:06:43 -0600434
435 if (!ovr->count_formats)
436 continue;
437
438 printf(" formats:");
439 for (j = 0; j < ovr->count_formats; j++)
440 printf(" %4.4s", (char *)&ovr->formats[j]);
441 printf("\n");
442
Laurent Pinchart02fa8f72013-02-27 06:39:36 +0100443 if (plane->props) {
444 printf(" props:\n");
445 for (j = 0; j < plane->props->count_props; j++)
Laurent Pinchart549fe0b2013-02-27 05:35:13 +0100446 dump_prop(dev, plane->props_info[j],
Laurent Pinchart02fa8f72013-02-27 06:39:36 +0100447 plane->props->props[j],
448 plane->props->prop_values[j]);
Rob Clark25e4cb42012-06-05 12:28:47 -0500449 } else {
Laurent Pinchart02fa8f72013-02-27 06:39:36 +0100450 printf(" no properties found\n");
Rob Clark25e4cb42012-06-05 12:28:47 -0500451 }
Rob Clarkd55de742011-12-14 21:06:43 -0600452 }
453 printf("\n");
454
455 return;
456}
457
Laurent Pinchart02fa8f72013-02-27 06:39:36 +0100458static void free_resources(struct resources *res)
459{
Thierry Redingf05a74f2015-01-23 17:08:21 +0100460 int i;
461
Laurent Pinchart02fa8f72013-02-27 06:39:36 +0100462 if (!res)
463 return;
464
465#define free_resource(_res, __res, type, Type) \
466 do { \
Laurent Pinchart02fa8f72013-02-27 06:39:36 +0100467 if (!(_res)->type##s) \
468 break; \
469 for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) { \
470 if (!(_res)->type##s[i].type) \
471 break; \
472 drmModeFree##Type((_res)->type##s[i].type); \
473 } \
474 free((_res)->type##s); \
475 } while (0)
476
477#define free_properties(_res, __res, type) \
478 do { \
Laurent Pinchart02fa8f72013-02-27 06:39:36 +0100479 for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) { \
480 drmModeFreeObjectProperties(res->type##s[i].props); \
481 free(res->type##s[i].props_info); \
482 } \
483 } while (0)
484
485 if (res->res) {
486 free_properties(res, res, crtc);
487
488 free_resource(res, res, crtc, Crtc);
489 free_resource(res, res, encoder, Encoder);
Thierry Redingf05a74f2015-01-23 17:08:21 +0100490
491 for (i = 0; i < res->res->count_connectors; i++)
492 free(res->connectors[i].name);
493
Laurent Pinchart02fa8f72013-02-27 06:39:36 +0100494 free_resource(res, res, connector, Connector);
495 free_resource(res, res, fb, FB);
496
497 drmModeFreeResources(res->res);
498 }
499
500 if (res->plane_res) {
501 free_properties(res, plane_res, plane);
502
503 free_resource(res, plane_res, plane, Plane);
504
505 drmModeFreePlaneResources(res->plane_res);
506 }
507
508 free(res);
509}
510
Laurent Pinchart549fe0b2013-02-27 05:35:13 +0100511static struct resources *get_resources(struct device *dev)
Laurent Pinchart02fa8f72013-02-27 06:39:36 +0100512{
513 struct resources *res;
Laurent Pinchart56592682013-02-27 05:35:13 +0100514 int i;
Laurent Pinchart02fa8f72013-02-27 06:39:36 +0100515
Emil Velikov128344c2015-04-28 13:25:24 +0100516 res = calloc(1, sizeof(*res));
Laurent Pinchart02fa8f72013-02-27 06:39:36 +0100517 if (res == 0)
518 return NULL;
519
Rob Clark1fec6232014-11-24 13:43:10 -0500520 drmSetClientCap(dev->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
521
Laurent Pinchart549fe0b2013-02-27 05:35:13 +0100522 res->res = drmModeGetResources(dev->fd);
Laurent Pinchart02fa8f72013-02-27 06:39:36 +0100523 if (!res->res) {
524 fprintf(stderr, "drmModeGetResources failed: %s\n",
525 strerror(errno));
526 goto error;
527 }
528
Emil Velikov128344c2015-04-28 13:25:24 +0100529 res->crtcs = calloc(res->res->count_crtcs, sizeof(*res->crtcs));
530 res->encoders = calloc(res->res->count_encoders, sizeof(*res->encoders));
531 res->connectors = calloc(res->res->count_connectors, sizeof(*res->connectors));
532 res->fbs = calloc(res->res->count_fbs, sizeof(*res->fbs));
Laurent Pinchart02fa8f72013-02-27 06:39:36 +0100533
534 if (!res->crtcs || !res->encoders || !res->connectors || !res->fbs)
535 goto error;
536
Laurent Pinchart02fa8f72013-02-27 06:39:36 +0100537#define get_resource(_res, __res, type, Type) \
538 do { \
Laurent Pinchart02fa8f72013-02-27 06:39:36 +0100539 for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) { \
540 (_res)->type##s[i].type = \
Laurent Pinchart549fe0b2013-02-27 05:35:13 +0100541 drmModeGet##Type(dev->fd, (_res)->__res->type##s[i]); \
Laurent Pinchart02fa8f72013-02-27 06:39:36 +0100542 if (!(_res)->type##s[i].type) \
543 fprintf(stderr, "could not get %s %i: %s\n", \
544 #type, (_res)->__res->type##s[i], \
545 strerror(errno)); \
546 } \
547 } while (0)
548
549 get_resource(res, res, crtc, Crtc);
550 get_resource(res, res, encoder, Encoder);
551 get_resource(res, res, connector, Connector);
552 get_resource(res, res, fb, FB);
553
Thierry Redingf05a74f2015-01-23 17:08:21 +0100554 /* Set the name of all connectors based on the type name and the per-type ID. */
555 for (i = 0; i < res->res->count_connectors; i++) {
556 struct connector *connector = &res->connectors[i];
Thierry Reding4664d652015-12-09 18:37:40 +0100557 drmModeConnector *conn = connector->connector;
Thierry Redingf05a74f2015-01-23 17:08:21 +0100558
559 asprintf(&connector->name, "%s-%u",
Thierry Reding4664d652015-12-09 18:37:40 +0100560 util_lookup_connector_type_name(conn->connector_type),
561 conn->connector_type_id);
Thierry Redingf05a74f2015-01-23 17:08:21 +0100562 }
563
Laurent Pinchart02fa8f72013-02-27 06:39:36 +0100564#define get_properties(_res, __res, type, Type) \
565 do { \
Laurent Pinchart02fa8f72013-02-27 06:39:36 +0100566 for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) { \
567 struct type *obj = &res->type##s[i]; \
568 unsigned int j; \
569 obj->props = \
Laurent Pinchart549fe0b2013-02-27 05:35:13 +0100570 drmModeObjectGetProperties(dev->fd, obj->type->type##_id, \
Laurent Pinchart02fa8f72013-02-27 06:39:36 +0100571 DRM_MODE_OBJECT_##Type); \
572 if (!obj->props) { \
573 fprintf(stderr, \
574 "could not get %s %i properties: %s\n", \
575 #type, obj->type->type##_id, \
576 strerror(errno)); \
577 continue; \
578 } \
Emil Velikov128344c2015-04-28 13:25:24 +0100579 obj->props_info = calloc(obj->props->count_props, \
580 sizeof(*obj->props_info)); \
Laurent Pinchart02fa8f72013-02-27 06:39:36 +0100581 if (!obj->props_info) \
582 continue; \
583 for (j = 0; j < obj->props->count_props; ++j) \
584 obj->props_info[j] = \
Laurent Pinchart549fe0b2013-02-27 05:35:13 +0100585 drmModeGetProperty(dev->fd, obj->props->props[j]); \
Laurent Pinchart02fa8f72013-02-27 06:39:36 +0100586 } \
587 } while (0)
588
589 get_properties(res, res, crtc, CRTC);
590 get_properties(res, res, connector, CONNECTOR);
591
Laurent Pinchart56592682013-02-27 05:35:13 +0100592 for (i = 0; i < res->res->count_crtcs; ++i)
593 res->crtcs[i].mode = &res->crtcs[i].crtc->mode;
594
Laurent Pinchart549fe0b2013-02-27 05:35:13 +0100595 res->plane_res = drmModeGetPlaneResources(dev->fd);
Laurent Pinchart02fa8f72013-02-27 06:39:36 +0100596 if (!res->plane_res) {
597 fprintf(stderr, "drmModeGetPlaneResources failed: %s\n",
598 strerror(errno));
599 return res;
600 }
601
Emil Velikov128344c2015-04-28 13:25:24 +0100602 res->planes = calloc(res->plane_res->count_planes, sizeof(*res->planes));
Laurent Pinchart02fa8f72013-02-27 06:39:36 +0100603 if (!res->planes)
604 goto error;
605
Laurent Pinchart02fa8f72013-02-27 06:39:36 +0100606 get_resource(res, plane_res, plane, Plane);
607 get_properties(res, plane_res, plane, PLANE);
608
609 return res;
610
611error:
612 free_resources(res);
613 return NULL;
614}
615
Laurent Pincharta4f2f1b2013-03-19 15:36:09 +0100616static int get_crtc_index(struct device *dev, uint32_t id)
617{
618 int i;
619
620 for (i = 0; i < dev->resources->res->count_crtcs; ++i) {
621 drmModeCrtc *crtc = dev->resources->crtcs[i].crtc;
622 if (crtc && crtc->crtc_id == id)
623 return i;
624 }
625
626 return -1;
627}
628
Thierry Redingf05a74f2015-01-23 17:08:21 +0100629static drmModeConnector *get_connector_by_name(struct device *dev, const char *name)
630{
631 struct connector *connector;
632 int i;
633
634 for (i = 0; i < dev->resources->res->count_connectors; i++) {
635 connector = &dev->resources->connectors[i];
636
637 if (strcmp(connector->name, name) == 0)
638 return connector->connector;
639 }
640
641 return NULL;
642}
643
Laurent Pinchart2c5ee842013-03-19 13:48:36 +0100644static drmModeConnector *get_connector_by_id(struct device *dev, uint32_t id)
645{
646 drmModeConnector *connector;
647 int i;
648
649 for (i = 0; i < dev->resources->res->count_connectors; i++) {
650 connector = dev->resources->connectors[i].connector;
651 if (connector && connector->connector_id == id)
652 return connector;
653 }
654
655 return NULL;
656}
657
658static drmModeEncoder *get_encoder_by_id(struct device *dev, uint32_t id)
659{
660 drmModeEncoder *encoder;
661 int i;
662
663 for (i = 0; i < dev->resources->res->count_encoders; i++) {
664 encoder = dev->resources->encoders[i].encoder;
665 if (encoder && encoder->encoder_id == id)
666 return encoder;
667 }
668
669 return NULL;
670}
671
Laurent Pincharta94ee622012-07-20 14:50:42 +0200672/* -----------------------------------------------------------------------------
Laurent Pinchartb1e0bde2013-03-19 13:47:39 +0100673 * Pipes and planes
Laurent Pincharta94ee622012-07-20 14:50:42 +0200674 */
675
Jesse Barnes731cd552008-12-17 10:09:49 -0800676/*
677 * Mode setting with the kernel interfaces is a bit of a chore.
678 * First you have to find the connector in question and make sure the
679 * requested mode is available.
680 * Then you need to find the encoder attached to that connector so you
681 * can bind it with a free crtc.
682 */
Laurent Pinchartb1e0bde2013-03-19 13:47:39 +0100683struct pipe_arg {
Thierry Redingf05a74f2015-01-23 17:08:21 +0100684 const char **cons;
Laurent Pinchart2c5ee842013-03-19 13:48:36 +0100685 uint32_t *con_ids;
686 unsigned int num_cons;
Laurent Pincharta6349d02013-02-27 05:35:13 +0100687 uint32_t crtc_id;
Kristian Høgsberg669fde32009-02-03 14:00:00 -0500688 char mode_str[64];
Laurent Pinchartcc90ffa2012-07-20 14:50:48 +0200689 char format_str[5];
Vincent ABRIOUde097022014-01-10 11:02:33 +0100690 unsigned int vrefresh;
Laurent Pinchartcc90ffa2012-07-20 14:50:48 +0200691 unsigned int fourcc;
Kristian Høgsberg9fc85b42009-02-23 15:08:03 -0500692 drmModeModeInfo *mode;
Laurent Pincharta6349d02013-02-27 05:35:13 +0100693 struct crtc *crtc;
Kristian Høgsberg1e1b3c02009-11-17 15:32:23 -0500694 unsigned int fb_id[2], current_fb_id;
695 struct timeval start;
696
697 int swap_count;
Rob Clarkd55de742011-12-14 21:06:43 -0600698};
699
Laurent Pinchart02fa8f72013-02-27 06:39:36 +0100700struct plane_arg {
Laurent Pincharteabf1992013-02-27 05:35:13 +0100701 uint32_t crtc_id; /* the id of CRTC to bind to */
Laurent Pinchart7badcca2013-02-27 05:35:13 +0100702 bool has_position;
703 int32_t x, y;
Rob Clarkd55de742011-12-14 21:06:43 -0600704 uint32_t w, h;
Ilia Mirkind8954152013-09-07 21:36:02 -0400705 double scale;
Rob Clarkd55de742011-12-14 21:06:43 -0600706 unsigned int fb_id;
Joonyoung Shim4d760d72015-04-28 11:41:39 +0100707 struct bo *bo;
Rob Clarkb83ad862011-12-14 22:24:14 -0600708 char format_str[5]; /* need to leave room for terminating \0 */
Laurent Pinchart03752222012-07-20 14:50:47 +0200709 unsigned int fourcc;
Rob Clarkd55de742011-12-14 21:06:43 -0600710};
Kristian Høgsberg669fde32009-02-03 14:00:00 -0500711
Laurent Pinchart2c5ee842013-03-19 13:48:36 +0100712static drmModeModeInfo *
Vincent ABRIOUde097022014-01-10 11:02:33 +0100713connector_find_mode(struct device *dev, uint32_t con_id, const char *mode_str,
714 const unsigned int vrefresh)
Jesse Barnes731cd552008-12-17 10:09:49 -0800715{
716 drmModeConnector *connector;
Laurent Pinchart2c5ee842013-03-19 13:48:36 +0100717 drmModeModeInfo *mode;
718 int i;
Jesse Barnes731cd552008-12-17 10:09:49 -0800719
Laurent Pinchart2c5ee842013-03-19 13:48:36 +0100720 connector = get_connector_by_id(dev, con_id);
721 if (!connector || !connector->count_modes)
722 return NULL;
723
724 for (i = 0; i < connector->count_modes; i++) {
725 mode = &connector->modes[i];
Vincent ABRIOUde097022014-01-10 11:02:33 +0100726 if (!strcmp(mode->name, mode_str)) {
727 /* If the vertical refresh frequency is not specified then return the
728 * first mode that match with the name. Else, return the mode that match
729 * the name and the specified vertical refresh frequency.
730 */
731 if (vrefresh == 0)
732 return mode;
733 else if (mode->vrefresh == vrefresh)
734 return mode;
735 }
Laurent Pinchart2c5ee842013-03-19 13:48:36 +0100736 }
737
738 return NULL;
739}
740
741static struct crtc *pipe_find_crtc(struct device *dev, struct pipe_arg *pipe)
742{
743 uint32_t possible_crtcs = ~0;
744 uint32_t active_crtcs = 0;
745 unsigned int crtc_idx;
746 unsigned int i;
747 int j;
748
749 for (i = 0; i < pipe->num_cons; ++i) {
Laurent Pincharta4f2f1b2013-03-19 15:36:09 +0100750 uint32_t crtcs_for_connector = 0;
Laurent Pinchart2c5ee842013-03-19 13:48:36 +0100751 drmModeConnector *connector;
752 drmModeEncoder *encoder;
Laurent Pincharta4f2f1b2013-03-19 15:36:09 +0100753 int idx;
Laurent Pinchart2c5ee842013-03-19 13:48:36 +0100754
755 connector = get_connector_by_id(dev, pipe->con_ids[i]);
Laurent Pinchart02fa8f72013-02-27 06:39:36 +0100756 if (!connector)
Laurent Pinchart2c5ee842013-03-19 13:48:36 +0100757 return NULL;
Jesse Barnes731cd552008-12-17 10:09:49 -0800758
Laurent Pincharta4f2f1b2013-03-19 15:36:09 +0100759 for (j = 0; j < connector->count_encoders; ++j) {
760 encoder = get_encoder_by_id(dev, connector->encoders[j]);
761 if (!encoder)
762 continue;
Jesse Barnes731cd552008-12-17 10:09:49 -0800763
Laurent Pincharta4f2f1b2013-03-19 15:36:09 +0100764 crtcs_for_connector |= encoder->possible_crtcs;
Jesse Barnes731cd552008-12-17 10:09:49 -0800765
Laurent Pincharta4f2f1b2013-03-19 15:36:09 +0100766 idx = get_crtc_index(dev, encoder->crtc_id);
767 if (idx >= 0)
768 active_crtcs |= 1 << idx;
Laurent Pincharta6349d02013-02-27 05:35:13 +0100769 }
Laurent Pincharta4f2f1b2013-03-19 15:36:09 +0100770
771 possible_crtcs &= crtcs_for_connector;
Kristian Høgsberg669fde32009-02-03 14:00:00 -0500772 }
Kristian Høgsberg8b880362009-02-04 12:17:13 -0500773
Laurent Pinchart2c5ee842013-03-19 13:48:36 +0100774 if (!possible_crtcs)
775 return NULL;
Laurent Pincharta6349d02013-02-27 05:35:13 +0100776
Laurent Pinchart2c5ee842013-03-19 13:48:36 +0100777 /* Return the first possible and active CRTC if one exists, or the first
778 * possible CRTC otherwise.
779 */
780 if (possible_crtcs & active_crtcs)
781 crtc_idx = ffs(possible_crtcs & active_crtcs);
782 else
783 crtc_idx = ffs(possible_crtcs);
Laurent Pincharta6349d02013-02-27 05:35:13 +0100784
Laurent Pinchart2c5ee842013-03-19 13:48:36 +0100785 return &dev->resources->crtcs[crtc_idx - 1];
786}
787
788static int pipe_find_crtc_and_mode(struct device *dev, struct pipe_arg *pipe)
789{
Emil Velikov4a8da022013-08-29 21:31:51 +0100790 drmModeModeInfo *mode = NULL;
Laurent Pinchart2c5ee842013-03-19 13:48:36 +0100791 int i;
792
793 pipe->mode = NULL;
794
795 for (i = 0; i < (int)pipe->num_cons; i++) {
796 mode = connector_find_mode(dev, pipe->con_ids[i],
Vincent ABRIOUde097022014-01-10 11:02:33 +0100797 pipe->mode_str, pipe->vrefresh);
Laurent Pinchart2c5ee842013-03-19 13:48:36 +0100798 if (mode == NULL) {
799 fprintf(stderr,
Thierry Redingf05a74f2015-01-23 17:08:21 +0100800 "failed to find mode \"%s\" for connector %s\n",
801 pipe->mode_str, pipe->cons[i]);
Laurent Pinchart2c5ee842013-03-19 13:48:36 +0100802 return -EINVAL;
Laurent Pincharta6349d02013-02-27 05:35:13 +0100803 }
804 }
Laurent Pinchart2c5ee842013-03-19 13:48:36 +0100805
806 /* If the CRTC ID was specified, get the corresponding CRTC. Otherwise
807 * locate a CRTC that can be attached to all the connectors.
808 */
809 if (pipe->crtc_id != (uint32_t)-1) {
810 for (i = 0; i < dev->resources->res->count_crtcs; i++) {
811 struct crtc *crtc = &dev->resources->crtcs[i];
812
813 if (pipe->crtc_id == crtc->crtc->crtc_id) {
814 pipe->crtc = crtc;
815 break;
816 }
817 }
818 } else {
819 pipe->crtc = pipe_find_crtc(dev, pipe);
820 }
821
822 if (!pipe->crtc) {
823 fprintf(stderr, "failed to find CRTC for pipe\n");
824 return -EINVAL;
825 }
826
827 pipe->mode = mode;
828 pipe->crtc->mode = mode;
829
830 return 0;
Kristian Høgsberg669fde32009-02-03 14:00:00 -0500831}
832
Laurent Pinchartd7252272013-02-27 05:35:13 +0100833/* -----------------------------------------------------------------------------
834 * Properties
835 */
836
837struct property_arg {
838 uint32_t obj_id;
839 uint32_t obj_type;
840 char name[DRM_PROP_NAME_LEN+1];
841 uint32_t prop_id;
842 uint64_t value;
843};
844
Laurent Pinchart549fe0b2013-02-27 05:35:13 +0100845static void set_property(struct device *dev, struct property_arg *p)
Laurent Pinchartd7252272013-02-27 05:35:13 +0100846{
Emil Velikov4a8da022013-08-29 21:31:51 +0100847 drmModeObjectProperties *props = NULL;
848 drmModePropertyRes **props_info = NULL;
Laurent Pinchartd7252272013-02-27 05:35:13 +0100849 const char *obj_type;
850 int ret;
851 int i;
852
853 p->obj_type = 0;
854 p->prop_id = 0;
855
856#define find_object(_res, __res, type, Type) \
857 do { \
858 for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) { \
859 struct type *obj = &(_res)->type##s[i]; \
860 if (obj->type->type##_id != p->obj_id) \
861 continue; \
862 p->obj_type = DRM_MODE_OBJECT_##Type; \
863 obj_type = #Type; \
864 props = obj->props; \
865 props_info = obj->props_info; \
866 } \
867 } while(0) \
868
Laurent Pinchart549fe0b2013-02-27 05:35:13 +0100869 find_object(dev->resources, res, crtc, CRTC);
Laurent Pinchartd7252272013-02-27 05:35:13 +0100870 if (p->obj_type == 0)
Laurent Pinchart549fe0b2013-02-27 05:35:13 +0100871 find_object(dev->resources, res, connector, CONNECTOR);
Laurent Pinchartd7252272013-02-27 05:35:13 +0100872 if (p->obj_type == 0)
Laurent Pinchart549fe0b2013-02-27 05:35:13 +0100873 find_object(dev->resources, plane_res, plane, PLANE);
Laurent Pinchartd7252272013-02-27 05:35:13 +0100874 if (p->obj_type == 0) {
875 fprintf(stderr, "Object %i not found, can't set property\n",
876 p->obj_id);
877 return;
878 }
879
880 if (!props) {
881 fprintf(stderr, "%s %i has no properties\n",
882 obj_type, p->obj_id);
883 return;
884 }
885
886 for (i = 0; i < (int)props->count_props; ++i) {
887 if (!props_info[i])
888 continue;
889 if (strcmp(props_info[i]->name, p->name) == 0)
890 break;
891 }
892
893 if (i == (int)props->count_props) {
894 fprintf(stderr, "%s %i has no %s property\n",
895 obj_type, p->obj_id, p->name);
896 return;
897 }
898
899 p->prop_id = props->props[i];
900
Laurent Pinchart549fe0b2013-02-27 05:35:13 +0100901 ret = drmModeObjectSetProperty(dev->fd, p->obj_id, p->obj_type,
902 p->prop_id, p->value);
Laurent Pinchartd7252272013-02-27 05:35:13 +0100903 if (ret < 0)
904 fprintf(stderr, "failed to set %s %i property %s to %" PRIu64 ": %s\n",
905 obj_type, p->obj_id, p->name, p->value, strerror(errno));
906}
907
Laurent Pinchart3fdc1772012-07-20 14:50:41 +0200908/* -------------------------------------------------------------------------- */
909
Laurent Pinchartca9c8f02013-02-11 21:36:30 +0100910static void
Laurent Pinchart3fdc1772012-07-20 14:50:41 +0200911page_flip_handler(int fd, unsigned int frame,
912 unsigned int sec, unsigned int usec, void *data)
913{
Laurent Pinchartb1e0bde2013-03-19 13:47:39 +0100914 struct pipe_arg *pipe;
Laurent Pinchart3fdc1772012-07-20 14:50:41 +0200915 unsigned int new_fb_id;
916 struct timeval end;
917 double t;
918
Laurent Pinchartb1e0bde2013-03-19 13:47:39 +0100919 pipe = data;
920 if (pipe->current_fb_id == pipe->fb_id[0])
921 new_fb_id = pipe->fb_id[1];
Laurent Pinchart3fdc1772012-07-20 14:50:41 +0200922 else
Laurent Pinchartb1e0bde2013-03-19 13:47:39 +0100923 new_fb_id = pipe->fb_id[0];
Laurent Pinchart3fdc1772012-07-20 14:50:41 +0200924
Laurent Pinchart2c5ee842013-03-19 13:48:36 +0100925 drmModePageFlip(fd, pipe->crtc->crtc->crtc_id, new_fb_id,
Laurent Pinchartb1e0bde2013-03-19 13:47:39 +0100926 DRM_MODE_PAGE_FLIP_EVENT, pipe);
927 pipe->current_fb_id = new_fb_id;
928 pipe->swap_count++;
929 if (pipe->swap_count == 60) {
Laurent Pinchart3fdc1772012-07-20 14:50:41 +0200930 gettimeofday(&end, NULL);
931 t = end.tv_sec + end.tv_usec * 1e-6 -
Laurent Pinchartb1e0bde2013-03-19 13:47:39 +0100932 (pipe->start.tv_sec + pipe->start.tv_usec * 1e-6);
933 fprintf(stderr, "freq: %.02fHz\n", pipe->swap_count / t);
934 pipe->swap_count = 0;
935 pipe->start = end;
Laurent Pinchart3fdc1772012-07-20 14:50:41 +0200936 }
937}
938
Tobias Jakobi360a7ea2015-05-06 14:29:33 +0200939static bool format_support(const drmModePlanePtr ovr, uint32_t fmt)
940{
941 unsigned int i;
942
943 for (i = 0; i < ovr->count_formats; ++i) {
944 if (ovr->formats[i] == fmt)
945 return true;
946 }
947
948 return false;
949}
950
Laurent Pincharteabf1992013-02-27 05:35:13 +0100951static int set_plane(struct device *dev, struct plane_arg *p)
Rob Clarkd55de742011-12-14 21:06:43 -0600952{
Rob Clarkd55de742011-12-14 21:06:43 -0600953 drmModePlane *ovr;
Tobias Jakobib1d19de2015-04-20 21:50:45 +0200954 uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0};
Rob Clarkd55de742011-12-14 21:06:43 -0600955 uint32_t plane_id = 0;
Laurent Pinchartd7c0a082014-12-09 22:00:58 +0200956 struct bo *plane_bo;
Laurent Pinchart03752222012-07-20 14:50:47 +0200957 uint32_t plane_flags = 0;
Laurent Pinchartca9c8f02013-02-11 21:36:30 +0100958 int crtc_x, crtc_y, crtc_w, crtc_h;
Laurent Pinchart3813e0f2013-02-27 05:35:13 +0100959 struct crtc *crtc = NULL;
Laurent Pinchart605efd72013-02-27 05:35:13 +0100960 unsigned int pipe;
Paulo Zanoni9b44fbd2012-04-21 17:51:50 -0300961 unsigned int i;
Rob Clarkd55de742011-12-14 21:06:43 -0600962
Laurent Pinchart605efd72013-02-27 05:35:13 +0100963 /* Find an unused plane which can be connected to our CRTC. Find the
964 * CRTC index first, then iterate over available planes.
965 */
966 for (i = 0; i < (unsigned int)dev->resources->res->count_crtcs; i++) {
Laurent Pincharteabf1992013-02-27 05:35:13 +0100967 if (p->crtc_id == dev->resources->res->crtcs[i]) {
968 crtc = &dev->resources->crtcs[i];
Laurent Pinchart605efd72013-02-27 05:35:13 +0100969 pipe = i;
970 break;
971 }
972 }
973
Laurent Pincharteabf1992013-02-27 05:35:13 +0100974 if (!crtc) {
975 fprintf(stderr, "CRTC %u not found\n", p->crtc_id);
Laurent Pinchart605efd72013-02-27 05:35:13 +0100976 return -1;
977 }
978
Laurent Pinchart549fe0b2013-02-27 05:35:13 +0100979 for (i = 0; i < dev->resources->plane_res->count_planes && !plane_id; i++) {
980 ovr = dev->resources->planes[i].plane;
Tobias Jakobi360a7ea2015-05-06 14:29:33 +0200981 if (!ovr || !format_support(ovr, p->fourcc))
Laurent Pinchart02fa8f72013-02-27 06:39:36 +0100982 continue;
Rob Clarkd55de742011-12-14 21:06:43 -0600983
Laurent Pinchart605efd72013-02-27 05:35:13 +0100984 if ((ovr->possible_crtcs & (1 << pipe)) && !ovr->crtc_id)
Rob Clarkd55de742011-12-14 21:06:43 -0600985 plane_id = ovr->plane_id;
Rob Clarkd55de742011-12-14 21:06:43 -0600986 }
987
988 if (!plane_id) {
Laurent Pincharteabf1992013-02-27 05:35:13 +0100989 fprintf(stderr, "no unused plane available for CRTC %u\n",
990 crtc->crtc->crtc_id);
Rob Clarkd55de742011-12-14 21:06:43 -0600991 return -1;
992 }
993
Laurent Pinchart581c7cf2013-02-27 05:35:13 +0100994 fprintf(stderr, "testing %dx%d@%s overlay plane %u\n",
995 p->w, p->h, p->format_str, plane_id);
996
Laurent Pinchartd7c0a082014-12-09 22:00:58 +0200997 plane_bo = bo_create(dev->fd, p->fourcc, p->w, p->h, handles,
Thierry Reding1ec3c442015-12-09 18:37:39 +0100998 pitches, offsets, UTIL_PATTERN_TILES);
Laurent Pinchart3fdc1772012-07-20 14:50:41 +0200999 if (plane_bo == NULL)
1000 return -1;
Rob Clarkd55de742011-12-14 21:06:43 -06001001
Joonyoung Shim4d760d72015-04-28 11:41:39 +01001002 p->bo = plane_bo;
1003
Rob Clarkd55de742011-12-14 21:06:43 -06001004 /* just use single plane format for now.. */
Laurent Pinchart549fe0b2013-02-27 05:35:13 +01001005 if (drmModeAddFB2(dev->fd, p->w, p->h, p->fourcc,
Rob Clarkd55de742011-12-14 21:06:43 -06001006 handles, pitches, offsets, &p->fb_id, plane_flags)) {
1007 fprintf(stderr, "failed to add fb: %s\n", strerror(errno));
1008 return -1;
1009 }
1010
Ilia Mirkind8954152013-09-07 21:36:02 -04001011 crtc_w = p->w * p->scale;
1012 crtc_h = p->h * p->scale;
Laurent Pinchart7badcca2013-02-27 05:35:13 +01001013 if (!p->has_position) {
1014 /* Default to the middle of the screen */
Ilia Mirkind8954152013-09-07 21:36:02 -04001015 crtc_x = (crtc->mode->hdisplay - crtc_w) / 2;
1016 crtc_y = (crtc->mode->vdisplay - crtc_h) / 2;
Laurent Pinchart7badcca2013-02-27 05:35:13 +01001017 } else {
1018 crtc_x = p->x;
1019 crtc_y = p->y;
1020 }
Rob Clarkd55de742011-12-14 21:06:43 -06001021
1022 /* note src coords (last 4 args) are in Q16 format */
Laurent Pincharteabf1992013-02-27 05:35:13 +01001023 if (drmModeSetPlane(dev->fd, plane_id, crtc->crtc->crtc_id, p->fb_id,
Rob Clarkd55de742011-12-14 21:06:43 -06001024 plane_flags, crtc_x, crtc_y, crtc_w, crtc_h,
1025 0, 0, p->w << 16, p->h << 16)) {
1026 fprintf(stderr, "failed to enable plane: %s\n",
1027 strerror(errno));
1028 return -1;
1029 }
1030
Laurent Pincharteabf1992013-02-27 05:35:13 +01001031 ovr->crtc_id = crtc->crtc->crtc_id;
Laurent Pinchart02fa8f72013-02-27 06:39:36 +01001032
Rob Clarkd55de742011-12-14 21:06:43 -06001033 return 0;
1034}
1035
Joonyoung Shim4d760d72015-04-28 11:41:39 +01001036static void clear_planes(struct device *dev, struct plane_arg *p, unsigned int count)
1037{
1038 unsigned int i;
1039
1040 for (i = 0; i < count; i++) {
1041 if (p[i].fb_id)
1042 drmModeRmFB(dev->fd, p[i].fb_id);
1043 if (p[i].bo)
1044 bo_destroy(p[i].bo);
1045 }
1046}
1047
1048
Laurent Pinchartb1e0bde2013-03-19 13:47:39 +01001049static void set_mode(struct device *dev, struct pipe_arg *pipes, unsigned int count)
Kristian Høgsberg669fde32009-02-03 14:00:00 -05001050{
Tobias Jakobib1d19de2015-04-20 21:50:45 +02001051 uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0};
Laurent Pinchart3813e0f2013-02-27 05:35:13 +01001052 unsigned int fb_id;
Laurent Pinchartd7c0a082014-12-09 22:00:58 +02001053 struct bo *bo;
Laurent Pinchart3813e0f2013-02-27 05:35:13 +01001054 unsigned int i;
Laurent Pinchart2c5ee842013-03-19 13:48:36 +01001055 unsigned int j;
Laurent Pinchart3813e0f2013-02-27 05:35:13 +01001056 int ret, x;
Kristian Høgsberg669fde32009-02-03 14:00:00 -05001057
Laurent Pinchart3813e0f2013-02-27 05:35:13 +01001058 dev->mode.width = 0;
1059 dev->mode.height = 0;
Joonyoung Shimbcaaa752015-04-13 17:32:15 +09001060 dev->mode.fb_id = 0;
Laurent Pinchart3813e0f2013-02-27 05:35:13 +01001061
Kristian Høgsberg669fde32009-02-03 14:00:00 -05001062 for (i = 0; i < count; i++) {
Laurent Pinchartb1e0bde2013-03-19 13:47:39 +01001063 struct pipe_arg *pipe = &pipes[i];
1064
Laurent Pinchart2c5ee842013-03-19 13:48:36 +01001065 ret = pipe_find_crtc_and_mode(dev, pipe);
1066 if (ret < 0)
Kristian Høgsberg669fde32009-02-03 14:00:00 -05001067 continue;
Laurent Pinchart2c5ee842013-03-19 13:48:36 +01001068
Laurent Pinchartb1e0bde2013-03-19 13:47:39 +01001069 dev->mode.width += pipe->mode->hdisplay;
1070 if (dev->mode.height < pipe->mode->vdisplay)
1071 dev->mode.height = pipe->mode->vdisplay;
Jesse Barnes731cd552008-12-17 10:09:49 -08001072 }
1073
Thierry Reding1ec3c442015-12-09 18:37:39 +01001074 bo = bo_create(dev->fd, pipes[0].fourcc, dev->mode.width,
1075 dev->mode.height, handles, pitches, offsets,
1076 UTIL_PATTERN_SMPTE);
Laurent Pinchart3fdc1772012-07-20 14:50:41 +02001077 if (bo == NULL)
Jesse Barnes731cd552008-12-17 10:09:49 -08001078 return;
Jesse Barnes731cd552008-12-17 10:09:49 -08001079
Joonyoung Shim21170a82015-04-13 17:32:16 +09001080 dev->mode.bo = bo;
1081
Laurent Pinchartb1e0bde2013-03-19 13:47:39 +01001082 ret = drmModeAddFB2(dev->fd, dev->mode.width, dev->mode.height,
1083 pipes[0].fourcc, handles, pitches, offsets, &fb_id, 0);
Jesse Barnes731cd552008-12-17 10:09:49 -08001084 if (ret) {
Jakob Bornecrantz680b9c42011-10-19 13:32:26 +02001085 fprintf(stderr, "failed to add fb (%ux%u): %s\n",
Laurent Pinchart3813e0f2013-02-27 05:35:13 +01001086 dev->mode.width, dev->mode.height, strerror(errno));
Jesse Barnes731cd552008-12-17 10:09:49 -08001087 return;
1088 }
1089
Joonyoung Shim21170a82015-04-13 17:32:16 +09001090 dev->mode.fb_id = fb_id;
1091
Kristian Høgsberg669fde32009-02-03 14:00:00 -05001092 x = 0;
1093 for (i = 0; i < count; i++) {
Laurent Pinchartb1e0bde2013-03-19 13:47:39 +01001094 struct pipe_arg *pipe = &pipes[i];
1095
1096 if (pipe->mode == NULL)
Kristian Høgsberg669fde32009-02-03 14:00:00 -05001097 continue;
Kristian Høgsberg8b880362009-02-04 12:17:13 -05001098
Vincent ABRIOUde097022014-01-10 11:02:33 +01001099 printf("setting mode %s-%dHz@%s on connectors ",
1100 pipe->mode_str, pipe->mode->vrefresh, pipe->format_str);
Laurent Pinchart2c5ee842013-03-19 13:48:36 +01001101 for (j = 0; j < pipe->num_cons; ++j)
Thierry Redingf05a74f2015-01-23 17:08:21 +01001102 printf("%s, ", pipe->cons[j]);
Laurent Pinchart2c5ee842013-03-19 13:48:36 +01001103 printf("crtc %d\n", pipe->crtc->crtc->crtc_id);
Kristian Høgsberg8b880362009-02-04 12:17:13 -05001104
Laurent Pinchart2c5ee842013-03-19 13:48:36 +01001105 ret = drmModeSetCrtc(dev->fd, pipe->crtc->crtc->crtc_id, fb_id,
1106 x, 0, pipe->con_ids, pipe->num_cons,
1107 pipe->mode);
Jakob Bornecrantzd23146f2011-10-19 13:32:43 +02001108
1109 /* XXX: Actually check if this is needed */
Laurent Pinchart549fe0b2013-02-27 05:35:13 +01001110 drmModeDirtyFB(dev->fd, fb_id, NULL, 0);
Jakob Bornecrantzd23146f2011-10-19 13:32:43 +02001111
Laurent Pinchartb1e0bde2013-03-19 13:47:39 +01001112 x += pipe->mode->hdisplay;
Kristian Høgsberg669fde32009-02-03 14:00:00 -05001113
1114 if (ret) {
1115 fprintf(stderr, "failed to set mode: %s\n", strerror(errno));
1116 return;
1117 }
Jesse Barnes731cd552008-12-17 10:09:49 -08001118 }
Laurent Pinchart3813e0f2013-02-27 05:35:13 +01001119}
Laurent Pinchart549fe0b2013-02-27 05:35:13 +01001120
Joonyoung Shim4e4d79d2015-04-13 17:32:14 +09001121static void clear_mode(struct device *dev)
1122{
Joonyoung Shimbcaaa752015-04-13 17:32:15 +09001123 if (dev->mode.fb_id)
1124 drmModeRmFB(dev->fd, dev->mode.fb_id);
Joonyoung Shim4e4d79d2015-04-13 17:32:14 +09001125 if (dev->mode.bo)
1126 bo_destroy(dev->mode.bo);
1127}
1128
Laurent Pinchart3813e0f2013-02-27 05:35:13 +01001129static void set_planes(struct device *dev, struct plane_arg *p, unsigned int count)
1130{
1131 unsigned int i;
1132
1133 /* set up planes/overlays */
1134 for (i = 0; i < count; i++)
1135 if (set_plane(dev, &p[i]))
1136 return;
1137}
1138
Rob Clark0e512792014-04-22 10:33:12 -04001139static void set_cursors(struct device *dev, struct pipe_arg *pipes, unsigned int count)
1140{
Tobias Jakobib1d19de2015-04-20 21:50:45 +02001141 uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0};
Laurent Pinchartd7c0a082014-12-09 22:00:58 +02001142 struct bo *bo;
Rob Clark0e512792014-04-22 10:33:12 -04001143 unsigned int i;
1144 int ret;
1145
1146 /* maybe make cursor width/height configurable some day */
1147 uint32_t cw = 64;
1148 uint32_t ch = 64;
1149
1150 /* create cursor bo.. just using PATTERN_PLAIN as it has
1151 * translucent alpha
1152 */
Laurent Pinchartd7c0a082014-12-09 22:00:58 +02001153 bo = bo_create(dev->fd, DRM_FORMAT_ARGB8888, cw, ch, handles, pitches,
Thierry Reding1ec3c442015-12-09 18:37:39 +01001154 offsets, UTIL_PATTERN_PLAIN);
Rob Clark0e512792014-04-22 10:33:12 -04001155 if (bo == NULL)
1156 return;
1157
Joonyoung Shim9915e682015-04-13 17:32:18 +09001158 dev->mode.cursor_bo = bo;
1159
Rob Clark0e512792014-04-22 10:33:12 -04001160 for (i = 0; i < count; i++) {
1161 struct pipe_arg *pipe = &pipes[i];
1162 ret = cursor_init(dev->fd, handles[0],
1163 pipe->crtc->crtc->crtc_id,
1164 pipe->mode->hdisplay, pipe->mode->vdisplay,
1165 cw, ch);
1166 if (ret) {
1167 fprintf(stderr, "failed to init cursor for CRTC[%u]\n",
1168 pipe->crtc_id);
1169 return;
1170 }
1171 }
1172
1173 cursor_start();
1174}
1175
1176static void clear_cursors(struct device *dev)
1177{
1178 cursor_stop();
Joonyoung Shim9915e682015-04-13 17:32:18 +09001179
1180 if (dev->mode.cursor_bo)
1181 bo_destroy(dev->mode.cursor_bo);
Rob Clark0e512792014-04-22 10:33:12 -04001182}
1183
Laurent Pinchartb1e0bde2013-03-19 13:47:39 +01001184static void test_page_flip(struct device *dev, struct pipe_arg *pipes, unsigned int count)
Laurent Pinchart3813e0f2013-02-27 05:35:13 +01001185{
Tobias Jakobib1d19de2015-04-20 21:50:45 +02001186 uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0};
Laurent Pinchart3813e0f2013-02-27 05:35:13 +01001187 unsigned int other_fb_id;
Laurent Pinchartd7c0a082014-12-09 22:00:58 +02001188 struct bo *other_bo;
Laurent Pinchart3813e0f2013-02-27 05:35:13 +01001189 drmEventContext evctx;
1190 unsigned int i;
1191 int ret;
1192
Thierry Reding1ec3c442015-12-09 18:37:39 +01001193 other_bo = bo_create(dev->fd, pipes[0].fourcc, dev->mode.width,
1194 dev->mode.height, handles, pitches, offsets,
1195 UTIL_PATTERN_PLAIN);
Laurent Pinchart3fdc1772012-07-20 14:50:41 +02001196 if (other_bo == NULL)
Kristian Høgsberg1e1b3c02009-11-17 15:32:23 -05001197 return;
1198
Laurent Pinchartb1e0bde2013-03-19 13:47:39 +01001199 ret = drmModeAddFB2(dev->fd, dev->mode.width, dev->mode.height,
1200 pipes[0].fourcc, handles, pitches, offsets,
1201 &other_fb_id, 0);
Kristian Høgsberg1e1b3c02009-11-17 15:32:23 -05001202 if (ret) {
1203 fprintf(stderr, "failed to add fb: %s\n", strerror(errno));
Joonyoung Shim21170a82015-04-13 17:32:16 +09001204 goto err;
Kristian Høgsberg1e1b3c02009-11-17 15:32:23 -05001205 }
1206
1207 for (i = 0; i < count; i++) {
Laurent Pinchartb1e0bde2013-03-19 13:47:39 +01001208 struct pipe_arg *pipe = &pipes[i];
1209
1210 if (pipe->mode == NULL)
Kristian Høgsberg1e1b3c02009-11-17 15:32:23 -05001211 continue;
1212
Laurent Pinchart2c5ee842013-03-19 13:48:36 +01001213 ret = drmModePageFlip(dev->fd, pipe->crtc->crtc->crtc_id,
1214 other_fb_id, DRM_MODE_PAGE_FLIP_EVENT,
1215 pipe);
Jakob Bornecrantz3c8adda2011-09-28 23:34:09 +02001216 if (ret) {
1217 fprintf(stderr, "failed to page flip: %s\n", strerror(errno));
Joonyoung Shim21170a82015-04-13 17:32:16 +09001218 goto err_rmfb;
Jakob Bornecrantz3c8adda2011-09-28 23:34:09 +02001219 }
Laurent Pinchartb1e0bde2013-03-19 13:47:39 +01001220 gettimeofday(&pipe->start, NULL);
1221 pipe->swap_count = 0;
1222 pipe->fb_id[0] = dev->mode.fb_id;
1223 pipe->fb_id[1] = other_fb_id;
1224 pipe->current_fb_id = other_fb_id;
Kristian Høgsberg1e1b3c02009-11-17 15:32:23 -05001225 }
1226
1227 memset(&evctx, 0, sizeof evctx);
1228 evctx.version = DRM_EVENT_CONTEXT_VERSION;
1229 evctx.vblank_handler = NULL;
Jesse Barnes6f1eba02009-12-04 09:09:19 -08001230 evctx.page_flip_handler = page_flip_handler;
Hyungwon Hwang6e84ada2015-08-19 09:58:42 +09001231
Kristian Høgsberg1e1b3c02009-11-17 15:32:23 -05001232 while (1) {
Jesse Barnese6b3f902010-03-26 13:13:57 -07001233#if 0
Kristian Høgsberg1e1b3c02009-11-17 15:32:23 -05001234 struct pollfd pfd[2];
1235
1236 pfd[0].fd = 0;
1237 pfd[0].events = POLLIN;
1238 pfd[1].fd = fd;
1239 pfd[1].events = POLLIN;
1240
1241 if (poll(pfd, 2, -1) < 0) {
1242 fprintf(stderr, "poll error\n");
1243 break;
1244 }
1245
1246 if (pfd[0].revents)
1247 break;
Jesse Barnese6b3f902010-03-26 13:13:57 -07001248#else
1249 struct timeval timeout = { .tv_sec = 3, .tv_usec = 0 };
1250 fd_set fds;
Jesse Barnese6b3f902010-03-26 13:13:57 -07001251
1252 FD_ZERO(&fds);
1253 FD_SET(0, &fds);
Laurent Pinchart549fe0b2013-02-27 05:35:13 +01001254 FD_SET(dev->fd, &fds);
1255 ret = select(dev->fd + 1, &fds, NULL, NULL, &timeout);
Jesse Barnese6b3f902010-03-26 13:13:57 -07001256
1257 if (ret <= 0) {
1258 fprintf(stderr, "select timed out or error (ret %d)\n",
1259 ret);
1260 continue;
1261 } else if (FD_ISSET(0, &fds)) {
1262 break;
1263 }
1264#endif
Kristian Høgsberg1e1b3c02009-11-17 15:32:23 -05001265
Laurent Pinchart549fe0b2013-02-27 05:35:13 +01001266 drmHandleEvent(dev->fd, &evctx);
Kristian Høgsberg1e1b3c02009-11-17 15:32:23 -05001267 }
Benjamin Franzke8fef2902011-02-17 10:47:47 +01001268
Joonyoung Shim21170a82015-04-13 17:32:16 +09001269err_rmfb:
Joonyoung Shimbcaaa752015-04-13 17:32:15 +09001270 drmModeRmFB(dev->fd, other_fb_id);
Joonyoung Shim21170a82015-04-13 17:32:16 +09001271err:
Laurent Pinchartd7c0a082014-12-09 22:00:58 +02001272 bo_destroy(other_bo);
Jesse Barnes731cd552008-12-17 10:09:49 -08001273}
1274
Laurent Pinchartcc90ffa2012-07-20 14:50:48 +02001275#define min(a, b) ((a) < (b) ? (a) : (b))
1276
Laurent Pinchartb1e0bde2013-03-19 13:47:39 +01001277static int parse_connector(struct pipe_arg *pipe, const char *arg)
Laurent Pinchart03752222012-07-20 14:50:47 +02001278{
Laurent Pinchartcc90ffa2012-07-20 14:50:48 +02001279 unsigned int len;
Laurent Pinchart2c5ee842013-03-19 13:48:36 +01001280 unsigned int i;
Laurent Pinchartcc90ffa2012-07-20 14:50:48 +02001281 const char *p;
1282 char *endp;
1283
Vincent ABRIOUde097022014-01-10 11:02:33 +01001284 pipe->vrefresh = 0;
Laurent Pinchartb1e0bde2013-03-19 13:47:39 +01001285 pipe->crtc_id = (uint32_t)-1;
1286 strcpy(pipe->format_str, "XR24");
Laurent Pinchart03752222012-07-20 14:50:47 +02001287
Laurent Pinchart2c5ee842013-03-19 13:48:36 +01001288 /* Count the number of connectors and allocate them. */
1289 pipe->num_cons = 1;
Thierry Redingf05a74f2015-01-23 17:08:21 +01001290 for (p = arg; *p && *p != ':' && *p != '@'; ++p) {
Laurent Pinchart2c5ee842013-03-19 13:48:36 +01001291 if (*p == ',')
1292 pipe->num_cons++;
1293 }
1294
Emil Velikov128344c2015-04-28 13:25:24 +01001295 pipe->con_ids = calloc(pipe->num_cons, sizeof(*pipe->con_ids));
Thierry Redingf05a74f2015-01-23 17:08:21 +01001296 pipe->cons = calloc(pipe->num_cons, sizeof(*pipe->cons));
1297 if (pipe->con_ids == NULL || pipe->cons == NULL)
Laurent Pinchart2c5ee842013-03-19 13:48:36 +01001298 return -1;
1299
1300 /* Parse the connectors. */
1301 for (i = 0, p = arg; i < pipe->num_cons; ++i, p = endp + 1) {
Thierry Redingf05a74f2015-01-23 17:08:21 +01001302 endp = strpbrk(p, ",@:");
1303 if (!endp)
1304 break;
1305
1306 pipe->cons[i] = strndup(p, endp - p);
1307
Laurent Pinchart2c5ee842013-03-19 13:48:36 +01001308 if (*endp != ',')
1309 break;
1310 }
1311
1312 if (i != pipe->num_cons - 1)
1313 return -1;
1314
1315 /* Parse the remaining parameters. */
Laurent Pinchartcc90ffa2012-07-20 14:50:48 +02001316 if (*endp == '@') {
1317 arg = endp + 1;
Laurent Pinchartb1e0bde2013-03-19 13:47:39 +01001318 pipe->crtc_id = strtoul(arg, &endp, 10);
Laurent Pinchartcc90ffa2012-07-20 14:50:48 +02001319 }
1320 if (*endp != ':')
1321 return -1;
Laurent Pinchart03752222012-07-20 14:50:47 +02001322
Laurent Pinchartcc90ffa2012-07-20 14:50:48 +02001323 arg = endp + 1;
Laurent Pinchart03752222012-07-20 14:50:47 +02001324
Vincent ABRIOUde097022014-01-10 11:02:33 +01001325 /* Search for the vertical refresh or the format. */
1326 p = strpbrk(arg, "-@");
1327 if (p == NULL)
1328 p = arg + strlen(arg);
Laurent Pinchartb1e0bde2013-03-19 13:47:39 +01001329 len = min(sizeof pipe->mode_str - 1, (unsigned int)(p - arg));
1330 strncpy(pipe->mode_str, arg, len);
1331 pipe->mode_str[len] = '\0';
Laurent Pinchartcc90ffa2012-07-20 14:50:48 +02001332
Vincent ABRIOUde097022014-01-10 11:02:33 +01001333 if (*p == '-') {
1334 pipe->vrefresh = strtoul(p + 1, &endp, 10);
1335 p = endp;
1336 }
1337
Laurent Pinchartcc90ffa2012-07-20 14:50:48 +02001338 if (*p == '@') {
Laurent Pinchartb1e0bde2013-03-19 13:47:39 +01001339 strncpy(pipe->format_str, p + 1, 4);
1340 pipe->format_str[4] = '\0';
Rob Clarkebd79042012-07-23 11:35:06 -05001341 }
Laurent Pinchartcc90ffa2012-07-20 14:50:48 +02001342
Thierry Reding1ec3c442015-12-09 18:37:39 +01001343 pipe->fourcc = util_format_fourcc(pipe->format_str);
Laurent Pinchartb1e0bde2013-03-19 13:47:39 +01001344 if (pipe->fourcc == 0) {
1345 fprintf(stderr, "unknown format %s\n", pipe->format_str);
Rob Clarkebd79042012-07-23 11:35:06 -05001346 return -1;
Laurent Pinchartcc90ffa2012-07-20 14:50:48 +02001347 }
1348
1349 return 0;
Laurent Pinchart03752222012-07-20 14:50:47 +02001350}
1351
Laurent Pinchart7badcca2013-02-27 05:35:13 +01001352static int parse_plane(struct plane_arg *plane, const char *p)
Laurent Pinchart03752222012-07-20 14:50:47 +02001353{
Laurent Pinchart7badcca2013-02-27 05:35:13 +01001354 char *end;
Laurent Pinchart03752222012-07-20 14:50:47 +02001355
Laurent Pincharteabf1992013-02-27 05:35:13 +01001356 plane->crtc_id = strtoul(p, &end, 10);
Laurent Pinchart7badcca2013-02-27 05:35:13 +01001357 if (*end != ':')
1358 return -EINVAL;
1359
1360 p = end + 1;
1361 plane->w = strtoul(p, &end, 10);
1362 if (*end != 'x')
1363 return -EINVAL;
1364
1365 p = end + 1;
1366 plane->h = strtoul(p, &end, 10);
1367
1368 if (*end == '+' || *end == '-') {
1369 plane->x = strtol(end, &end, 10);
1370 if (*end != '+' && *end != '-')
1371 return -EINVAL;
1372 plane->y = strtol(end, &end, 10);
1373
1374 plane->has_position = true;
1375 }
1376
Ilia Mirkind8954152013-09-07 21:36:02 -04001377 if (*end == '*') {
1378 p = end + 1;
1379 plane->scale = strtod(p, &end);
1380 if (plane->scale <= 0.0)
1381 return -EINVAL;
1382 } else {
1383 plane->scale = 1.0;
1384 }
1385
Laurent Pinchart7badcca2013-02-27 05:35:13 +01001386 if (*end == '@') {
1387 p = end + 1;
1388 if (strlen(p) != 4)
1389 return -EINVAL;
1390
1391 strcpy(plane->format_str, p);
1392 } else {
1393 strcpy(plane->format_str, "XR24");
1394 }
1395
Thierry Reding1ec3c442015-12-09 18:37:39 +01001396 plane->fourcc = util_format_fourcc(plane->format_str);
Laurent Pinchart7badcca2013-02-27 05:35:13 +01001397 if (plane->fourcc == 0) {
1398 fprintf(stderr, "unknown format %s\n", plane->format_str);
1399 return -EINVAL;
Laurent Pinchart03752222012-07-20 14:50:47 +02001400 }
1401
1402 return 0;
1403}
1404
Laurent Pinchartd7252272013-02-27 05:35:13 +01001405static int parse_property(struct property_arg *p, const char *arg)
1406{
1407 if (sscanf(arg, "%d:%32[^:]:%" SCNu64, &p->obj_id, p->name, &p->value) != 3)
1408 return -1;
1409
1410 p->obj_type = 0;
1411 p->name[DRM_PROP_NAME_LEN] = '\0';
1412
1413 return 0;
1414}
1415
Laurent Pinchartca9c8f02013-02-11 21:36:30 +01001416static void usage(char *name)
Jesse Barnes731cd552008-12-17 10:09:49 -08001417{
Rob Clark0e512792014-04-22 10:33:12 -04001418 fprintf(stderr, "usage: %s [-cDdefMPpsCvw]\n", name);
Laurent Pinchartef07acf2013-02-08 11:12:03 +01001419
1420 fprintf(stderr, "\n Query options:\n\n");
Jesse Barnes731cd552008-12-17 10:09:49 -08001421 fprintf(stderr, "\t-c\tlist connectors\n");
Laurent Pinchartef07acf2013-02-08 11:12:03 +01001422 fprintf(stderr, "\t-e\tlist encoders\n");
Jesse Barnes731cd552008-12-17 10:09:49 -08001423 fprintf(stderr, "\t-f\tlist framebuffers\n");
Laurent Pinchartef07acf2013-02-08 11:12:03 +01001424 fprintf(stderr, "\t-p\tlist CRTCs and planes (pipes)\n");
1425
1426 fprintf(stderr, "\n Test options:\n\n");
Ilia Mirkind8954152013-09-07 21:36:02 -04001427 fprintf(stderr, "\t-P <crtc_id>:<w>x<h>[+<x>+<y>][*<scale>][@<format>]\tset a plane\n");
Vincent ABRIOUde097022014-01-10 11:02:33 +01001428 fprintf(stderr, "\t-s <connector_id>[,<connector_id>][@<crtc_id>]:<mode>[-<vrefresh>][@<format>]\tset a mode\n");
Rob Clark0e512792014-04-22 10:33:12 -04001429 fprintf(stderr, "\t-C\ttest hw cursor\n");
Laurent Pinchartef07acf2013-02-08 11:12:03 +01001430 fprintf(stderr, "\t-v\ttest vsynced page flipping\n");
Laurent Pinchartd7252272013-02-27 05:35:13 +01001431 fprintf(stderr, "\t-w <obj_id>:<prop_name>:<value>\tset property\n");
Laurent Pinchartef07acf2013-02-08 11:12:03 +01001432
Laurent Pinchart45901fd2013-02-08 13:42:45 +01001433 fprintf(stderr, "\n Generic options:\n\n");
Laurent Pinchartab527562013-02-27 05:19:44 +01001434 fprintf(stderr, "\t-d\tdrop master after mode set\n");
Laurent Pinchart45901fd2013-02-08 13:42:45 +01001435 fprintf(stderr, "\t-M module\tuse the given driver\n");
Ilia Mirkinb50826d2013-09-07 21:36:01 -04001436 fprintf(stderr, "\t-D device\tuse the given device\n");
Laurent Pinchart45901fd2013-02-08 13:42:45 +01001437
Jesse Barnes731cd552008-12-17 10:09:49 -08001438 fprintf(stderr, "\n\tDefault is to dump all info.\n");
1439 exit(0);
1440}
1441
Paulo Zanoni9b44fbd2012-04-21 17:51:50 -03001442static int page_flipping_supported(void)
Kristian Høgsberg59d97e72009-12-09 10:36:53 -05001443{
Benjamin Franzke8fef2902011-02-17 10:47:47 +01001444 /*FIXME: generic ioctl needed? */
1445 return 1;
1446#if 0
Kristian Høgsberg59d97e72009-12-09 10:36:53 -05001447 int ret, value;
1448 struct drm_i915_getparam gp;
1449
1450 gp.param = I915_PARAM_HAS_PAGEFLIPPING;
1451 gp.value = &value;
1452
1453 ret = drmCommandWriteRead(fd, DRM_I915_GETPARAM, &gp, sizeof(gp));
1454 if (ret) {
1455 fprintf(stderr, "drm_i915_getparam: %m\n");
1456 return 0;
1457 }
1458
Matthew W. S. Belle4a51962010-01-30 02:14:44 +00001459 return *gp.value;
Benjamin Franzke8fef2902011-02-17 10:47:47 +01001460#endif
Kristian Høgsberg59d97e72009-12-09 10:36:53 -05001461}
1462
Rob Clark0e512792014-04-22 10:33:12 -04001463static int cursor_supported(void)
1464{
1465 /*FIXME: generic ioctl needed? */
1466 return 1;
1467}
1468
Thierry Redingf05a74f2015-01-23 17:08:21 +01001469static int pipe_resolve_connectors(struct device *dev, struct pipe_arg *pipe)
1470{
1471 drmModeConnector *connector;
1472 unsigned int i;
1473 uint32_t id;
1474 char *endp;
1475
1476 for (i = 0; i < pipe->num_cons; i++) {
1477 id = strtoul(pipe->cons[i], &endp, 10);
1478 if (endp == pipe->cons[i]) {
1479 connector = get_connector_by_name(dev, pipe->cons[i]);
1480 if (!connector) {
1481 fprintf(stderr, "no connector named '%s'\n",
1482 pipe->cons[i]);
1483 return -ENODEV;
1484 }
1485
1486 id = connector->connector_id;
1487 }
1488
1489 pipe->con_ids[i] = id;
1490 }
1491
1492 return 0;
1493}
1494
Rob Clark0e512792014-04-22 10:33:12 -04001495static char optstr[] = "cdD:efM:P:ps:Cvw:";
Laurent Pinchartef07acf2013-02-08 11:12:03 +01001496
Jesse Barnes731cd552008-12-17 10:09:49 -08001497int main(int argc, char **argv)
1498{
Laurent Pinchart549fe0b2013-02-27 05:35:13 +01001499 struct device dev;
1500
Jesse Barnes731cd552008-12-17 10:09:49 -08001501 int c;
Rob Clarkd55de742011-12-14 21:06:43 -06001502 int encoders = 0, connectors = 0, crtcs = 0, planes = 0, framebuffers = 0;
Laurent Pinchartab527562013-02-27 05:19:44 +01001503 int drop_master = 0;
Kristian Høgsberg1e1b3c02009-11-17 15:32:23 -05001504 int test_vsync = 0;
Rob Clark0e512792014-04-22 10:33:12 -04001505 int test_cursor = 0;
Ilia Mirkinb50826d2013-09-07 21:36:01 -04001506 char *device = NULL;
Laurent Pinchart45901fd2013-02-08 13:42:45 +01001507 char *module = NULL;
Paulo Zanoni9b44fbd2012-04-21 17:51:50 -03001508 unsigned int i;
Thierry Redingf05a74f2015-01-23 17:08:21 +01001509 unsigned int count = 0, plane_count = 0;
Laurent Pinchartd7252272013-02-27 05:35:13 +01001510 unsigned int prop_count = 0;
Laurent Pinchartb1e0bde2013-03-19 13:47:39 +01001511 struct pipe_arg *pipe_args = NULL;
Laurent Pinchart6e0c74c2013-03-01 02:28:42 +01001512 struct plane_arg *plane_args = NULL;
Laurent Pinchartd7252272013-02-27 05:35:13 +01001513 struct property_arg *prop_args = NULL;
Laurent Pinchart45901fd2013-02-08 13:42:45 +01001514 unsigned int args = 0;
Laurent Pinchart549fe0b2013-02-27 05:35:13 +01001515 int ret;
1516
Laurent Pinchart3813e0f2013-02-27 05:35:13 +01001517 memset(&dev, 0, sizeof dev);
1518
Jesse Barnes731cd552008-12-17 10:09:49 -08001519 opterr = 0;
1520 while ((c = getopt(argc, argv, optstr)) != -1) {
Laurent Pinchart45901fd2013-02-08 13:42:45 +01001521 args++;
1522
Jesse Barnes731cd552008-12-17 10:09:49 -08001523 switch (c) {
Jesse Barnes731cd552008-12-17 10:09:49 -08001524 case 'c':
1525 connectors = 1;
1526 break;
Ilia Mirkinb50826d2013-09-07 21:36:01 -04001527 case 'D':
1528 device = optarg;
1529 args--;
1530 break;
Laurent Pinchartab527562013-02-27 05:19:44 +01001531 case 'd':
1532 drop_master = 1;
1533 break;
Laurent Pinchartef07acf2013-02-08 11:12:03 +01001534 case 'e':
1535 encoders = 1;
Jesse Barnes731cd552008-12-17 10:09:49 -08001536 break;
1537 case 'f':
1538 framebuffers = 1;
1539 break;
Laurent Pinchart45901fd2013-02-08 13:42:45 +01001540 case 'M':
1541 module = optarg;
1542 /* Preserve the default behaviour of dumping all information. */
1543 args--;
1544 break;
Laurent Pinchartef07acf2013-02-08 11:12:03 +01001545 case 'P':
Laurent Pinchart6e0c74c2013-03-01 02:28:42 +01001546 plane_args = realloc(plane_args,
1547 (plane_count + 1) * sizeof *plane_args);
1548 if (plane_args == NULL) {
1549 fprintf(stderr, "memory allocation failed\n");
1550 return 1;
1551 }
Emil Velikovc78917e2015-04-28 14:20:30 +01001552 memset(&plane_args[plane_count], 0, sizeof(*plane_args));
Laurent Pinchart6e0c74c2013-03-01 02:28:42 +01001553
Laurent Pinchartef07acf2013-02-08 11:12:03 +01001554 if (parse_plane(&plane_args[plane_count], optarg) < 0)
1555 usage(argv[0]);
Laurent Pinchart6e0c74c2013-03-01 02:28:42 +01001556
Laurent Pinchartef07acf2013-02-08 11:12:03 +01001557 plane_count++;
1558 break;
1559 case 'p':
1560 crtcs = 1;
1561 planes = 1;
Kristian Høgsberg1e1b3c02009-11-17 15:32:23 -05001562 break;
Jesse Barnes731cd552008-12-17 10:09:49 -08001563 case 's':
Laurent Pinchartb1e0bde2013-03-19 13:47:39 +01001564 pipe_args = realloc(pipe_args,
1565 (count + 1) * sizeof *pipe_args);
1566 if (pipe_args == NULL) {
Laurent Pinchart6e0c74c2013-03-01 02:28:42 +01001567 fprintf(stderr, "memory allocation failed\n");
1568 return 1;
1569 }
Emil Velikovc78917e2015-04-28 14:20:30 +01001570 memset(&pipe_args[count], 0, sizeof(*pipe_args));
Laurent Pinchart6e0c74c2013-03-01 02:28:42 +01001571
Laurent Pinchartb1e0bde2013-03-19 13:47:39 +01001572 if (parse_connector(&pipe_args[count], optarg) < 0)
Kristian Høgsberg669fde32009-02-03 14:00:00 -05001573 usage(argv[0]);
Laurent Pinchart6e0c74c2013-03-01 02:28:42 +01001574
Hyungwon Hwang6e84ada2015-08-19 09:58:42 +09001575 count++;
Jesse Barnes731cd552008-12-17 10:09:49 -08001576 break;
Rob Clark0e512792014-04-22 10:33:12 -04001577 case 'C':
1578 test_cursor = 1;
1579 break;
Laurent Pinchartef07acf2013-02-08 11:12:03 +01001580 case 'v':
1581 test_vsync = 1;
Rob Clarkd55de742011-12-14 21:06:43 -06001582 break;
Laurent Pinchartd7252272013-02-27 05:35:13 +01001583 case 'w':
1584 prop_args = realloc(prop_args,
1585 (prop_count + 1) * sizeof *prop_args);
1586 if (prop_args == NULL) {
1587 fprintf(stderr, "memory allocation failed\n");
1588 return 1;
1589 }
Emil Velikovc78917e2015-04-28 14:20:30 +01001590 memset(&prop_args[prop_count], 0, sizeof(*prop_args));
Laurent Pinchartd7252272013-02-27 05:35:13 +01001591
1592 if (parse_property(&prop_args[prop_count], optarg) < 0)
1593 usage(argv[0]);
1594
1595 prop_count++;
1596 break;
Jesse Barnes731cd552008-12-17 10:09:49 -08001597 default:
1598 usage(argv[0]);
1599 break;
1600 }
1601 }
1602
Laurent Pinchart45901fd2013-02-08 13:42:45 +01001603 if (!args)
Laurent Pinchartdab3c802013-02-27 05:35:13 +01001604 encoders = connectors = crtcs = planes = framebuffers = 1;
Jesse Barnes731cd552008-12-17 10:09:49 -08001605
Thierry Redingc26266f2015-12-09 18:37:46 +01001606 dev.fd = util_open(module, device);
1607 if (dev.fd < 0)
1608 return -1;
Jesse Barnes731cd552008-12-17 10:09:49 -08001609
Paulo Zanoni9b44fbd2012-04-21 17:51:50 -03001610 if (test_vsync && !page_flipping_supported()) {
Kristian Høgsberg59d97e72009-12-09 10:36:53 -05001611 fprintf(stderr, "page flipping not supported by drm.\n");
1612 return -1;
1613 }
1614
Laurent Pinchart3813e0f2013-02-27 05:35:13 +01001615 if (test_vsync && !count) {
1616 fprintf(stderr, "page flipping requires at least one -s option.\n");
1617 return -1;
1618 }
1619
Rob Clark0e512792014-04-22 10:33:12 -04001620 if (test_cursor && !cursor_supported()) {
1621 fprintf(stderr, "hw cursor not supported by drm.\n");
1622 return -1;
1623 }
1624
Laurent Pinchart549fe0b2013-02-27 05:35:13 +01001625 dev.resources = get_resources(&dev);
1626 if (!dev.resources) {
1627 drmClose(dev.fd);
Jesse Barnes731cd552008-12-17 10:09:49 -08001628 return 1;
1629 }
1630
Thierry Redingf05a74f2015-01-23 17:08:21 +01001631 for (i = 0; i < count; i++) {
1632 if (pipe_resolve_connectors(&dev, &pipe_args[i]) < 0) {
1633 free_resources(dev.resources);
1634 drmClose(dev.fd);
1635 return 1;
1636 }
1637 }
1638
Laurent Pinchart549fe0b2013-02-27 05:35:13 +01001639#define dump_resource(dev, res) if (res) dump_##res(dev)
1640
1641 dump_resource(&dev, encoders);
1642 dump_resource(&dev, connectors);
1643 dump_resource(&dev, crtcs);
1644 dump_resource(&dev, planes);
1645 dump_resource(&dev, framebuffers);
Jesse Barnes731cd552008-12-17 10:09:49 -08001646
Laurent Pinchartd7252272013-02-27 05:35:13 +01001647 for (i = 0; i < prop_count; ++i)
Laurent Pinchart549fe0b2013-02-27 05:35:13 +01001648 set_property(&dev, &prop_args[i]);
Laurent Pinchartd7252272013-02-27 05:35:13 +01001649
Laurent Pinchart3813e0f2013-02-27 05:35:13 +01001650 if (count || plane_count) {
Laurent Pinchartd7c0a082014-12-09 22:00:58 +02001651 uint64_t cap = 0;
1652
1653 ret = drmGetCap(dev.fd, DRM_CAP_DUMB_BUFFER, &cap);
1654 if (ret || cap == 0) {
1655 fprintf(stderr, "driver doesn't support the dumb buffer API\n");
Laurent Pinchart549fe0b2013-02-27 05:35:13 +01001656 return 1;
1657 }
1658
Laurent Pinchart3813e0f2013-02-27 05:35:13 +01001659 if (count)
Laurent Pinchartb1e0bde2013-03-19 13:47:39 +01001660 set_mode(&dev, pipe_args, count);
Laurent Pinchart3813e0f2013-02-27 05:35:13 +01001661
1662 if (plane_count)
1663 set_planes(&dev, plane_args, plane_count);
1664
Rob Clark0e512792014-04-22 10:33:12 -04001665 if (test_cursor)
1666 set_cursors(&dev, pipe_args, count);
1667
Laurent Pinchart3813e0f2013-02-27 05:35:13 +01001668 if (test_vsync)
Laurent Pinchartb1e0bde2013-03-19 13:47:39 +01001669 test_page_flip(&dev, pipe_args, count);
Laurent Pinchart3813e0f2013-02-27 05:35:13 +01001670
Laurent Pinchartab527562013-02-27 05:19:44 +01001671 if (drop_master)
Laurent Pinchart549fe0b2013-02-27 05:35:13 +01001672 drmDropMaster(dev.fd);
1673
Rob Clark0e512792014-04-22 10:33:12 -04001674 getchar();
1675
1676 if (test_cursor)
1677 clear_cursors(&dev);
1678
Joonyoung Shim4d760d72015-04-28 11:41:39 +01001679 if (plane_count)
1680 clear_planes(&dev, plane_args, plane_count);
1681
Joonyoung Shim4e4d79d2015-04-13 17:32:14 +09001682 if (count)
1683 clear_mode(&dev);
Jesse Barnes731cd552008-12-17 10:09:49 -08001684 }
1685
Laurent Pinchart549fe0b2013-02-27 05:35:13 +01001686 free_resources(dev.resources);
Jesse Barnes731cd552008-12-17 10:09:49 -08001687
1688 return 0;
1689}