blob: 11d998184b0e9c53fb7ae40631012bc970ed2706 [file] [log] [blame]
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001/*
Kristian Høgsberg96aa7da2011-09-15 15:43:14 -04002 * Copyright © 2008-2011 Kristian Høgsberg
3 * Copyright © 2011 Intel Corporation
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04004 *
Kristian Høgsberg96aa7da2011-09-15 15:43:14 -04005 * Permission to use, copy, modify, distribute, and sell this software and
6 * its documentation for any purpose is hereby granted without fee, provided
7 * that the above copyright notice appear in all copies and that both that
8 * copyright notice and this permission notice appear in supporting
9 * documentation, and that the name of the copyright holders not be used in
10 * advertising or publicity pertaining to distribution of the software
11 * without specific, written prior permission. The copyright holders make
12 * no representations about the suitability of this software for any
13 * purpose. It is provided "as is" without express or implied warranty.
Kristian Høgsbergfc783d42010-06-11 12:56:24 -040014 *
Kristian Høgsberg96aa7da2011-09-15 15:43:14 -040015 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
16 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
17 * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
18 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
19 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
20 * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Kristian Høgsbergfc783d42010-06-11 12:56:24 -040022 */
23
Kristian Høgsberg0b9334a2011-04-12 11:34:32 -040024#define _GNU_SOURCE
25
Jesse Barnes58ef3792012-02-23 09:45:49 -050026#include <errno.h>
Kristian Høgsbergfc783d42010-06-11 12:56:24 -040027#include <stdlib.h>
28#include <string.h>
29#include <fcntl.h>
30#include <unistd.h>
Kristian Høgsberg5d1c0c52012-04-10 00:11:50 -040031#include <linux/input.h>
Ander Conselvan de Oliveirafd1f4c62012-06-26 17:09:14 +030032#include <assert.h>
Kristian Høgsbergfc783d42010-06-11 12:56:24 -040033
Benjamin Franzkec649a922011-03-02 11:56:04 +010034#include <xf86drm.h>
35#include <xf86drmMode.h>
Jesse Barnes58ef3792012-02-23 09:45:49 -050036#include <drm_fourcc.h>
Benjamin Franzkec649a922011-03-02 11:56:04 +010037
Benjamin Franzke060cf802011-04-30 09:32:11 +020038#include <gbm.h>
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +020039#include <libbacklight.h>
Pekka Paalanen33156972012-08-03 13:30:30 -040040#include <libudev.h>
Benjamin Franzke060cf802011-04-30 09:32:11 +020041
Kristian Høgsbergfc783d42010-06-11 12:56:24 -040042#include "compositor.h"
John Kåre Alsaker30d2b1f2012-11-13 19:10:28 +010043#include "gl-renderer.h"
Tiago Vignattice03ec32011-12-19 01:14:03 +020044#include "evdev.h"
Benjamin Franzkebfeda132012-01-30 14:04:04 +010045#include "launcher-util.h"
Kristian Høgsbergfc783d42010-06-11 12:56:24 -040046
Kristian Høgsberg061c4252012-06-28 11:28:15 -040047static int option_current_mode = 0;
Scott Moreau8ab5d452012-07-30 19:51:08 -060048static char *output_name;
49static char *output_mode;
Scott Moreau1bad5db2012-08-18 01:04:05 -060050static char *output_transform;
Scott Moreau8ab5d452012-07-30 19:51:08 -060051static struct wl_list configured_output_list;
52
53enum output_config {
54 OUTPUT_CONFIG_INVALID = 0,
55 OUTPUT_CONFIG_OFF,
56 OUTPUT_CONFIG_PREFERRED,
57 OUTPUT_CONFIG_CURRENT,
Scott Moreau8f37e0b2012-07-31 15:30:41 -060058 OUTPUT_CONFIG_MODE,
59 OUTPUT_CONFIG_MODELINE
Scott Moreau8ab5d452012-07-30 19:51:08 -060060};
61
62struct drm_configured_output {
63 char *name;
64 char *mode;
Scott Moreau1bad5db2012-08-18 01:04:05 -060065 uint32_t transform;
Scott Moreau8ab5d452012-07-30 19:51:08 -060066 int32_t width, height;
Scott Moreau8f37e0b2012-07-31 15:30:41 -060067 drmModeModeInfo crtc_mode;
Scott Moreau8ab5d452012-07-30 19:51:08 -060068 enum output_config config;
69 struct wl_list link;
70};
Kristian Høgsberg061c4252012-06-28 11:28:15 -040071
Kristian Høgsbergce5325d2010-06-14 11:54:00 -040072struct drm_compositor {
Kristian Høgsberg8334bc12012-01-03 10:29:47 -050073 struct weston_compositor base;
Kristian Høgsbergce5325d2010-06-14 11:54:00 -040074
75 struct udev *udev;
76 struct wl_event_source *drm_source;
Kristian Høgsbergce5325d2010-06-14 11:54:00 -040077
Benjamin Franzke9c26ff32011-03-15 15:08:41 +010078 struct udev_monitor *udev_monitor;
79 struct wl_event_source *udev_drm_source;
80
Benjamin Franzke2af7f102011-03-02 11:14:59 +010081 struct {
David Herrmannd7488c22012-03-11 20:05:21 +010082 int id;
Benjamin Franzke2af7f102011-03-02 11:14:59 +010083 int fd;
84 } drm;
Benjamin Franzke060cf802011-04-30 09:32:11 +020085 struct gbm_device *gbm;
Jesse Barnes58ef3792012-02-23 09:45:49 -050086 uint32_t *crtcs;
87 int num_crtcs;
Marty Jack13d9db22011-02-09 19:01:42 -050088 uint32_t crtc_allocator;
Benjamin Franzke9c26ff32011-03-15 15:08:41 +010089 uint32_t connector_allocator;
Kristian Høgsberge4762a62011-01-14 14:59:13 -050090 struct tty *tty;
Pekka Paalanenbce2d3f2011-12-02 13:07:27 +020091
Rob Clark4339add2012-08-09 14:18:28 -050092 /* we need these parameters in order to not fail drmModeAddFB2()
93 * due to out of bounds dimensions, and then mistakenly set
94 * sprites_are_broken:
95 */
Ander Conselvan de Oliveira8d360b42012-11-09 14:19:05 +020096 uint32_t min_width, max_width;
97 uint32_t min_height, max_height;
98 int no_addfb2;
Rob Clark4339add2012-08-09 14:18:28 -050099
Jesse Barnes58ef3792012-02-23 09:45:49 -0500100 struct wl_list sprite_list;
Kristian Høgsberg65bec242012-03-05 19:57:35 -0500101 int sprites_are_broken;
Ander Conselvan de Oliveirada1c9082012-10-31 17:55:46 +0200102 int sprites_hidden;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500103
Rob Clarkab5b1e32012-08-09 13:24:45 -0500104 int cursors_are_broken;
105
Pekka Paalanenbce2d3f2011-12-02 13:07:27 +0200106 uint32_t prev_state;
Kristian Høgsbergce5325d2010-06-14 11:54:00 -0400107};
108
Kristian Høgsberg8f0ce052011-06-21 11:16:58 -0400109struct drm_mode {
Kristian Høgsberg8334bc12012-01-03 10:29:47 -0500110 struct weston_mode base;
Kristian Høgsberg8f0ce052011-06-21 11:16:58 -0400111 drmModeModeInfo mode_info;
112};
113
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300114struct drm_output;
115
116struct drm_fb {
117 struct gbm_bo *bo;
118 struct drm_output *output;
119 uint32_t fb_id;
120 int is_client_buffer;
Pekka Paalanende685b82012-12-04 15:58:12 +0200121 struct weston_buffer_reference buffer_ref;
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300122};
123
Kristian Høgsbergce5325d2010-06-14 11:54:00 -0400124struct drm_output {
Kristian Høgsberg8334bc12012-01-03 10:29:47 -0500125 struct weston_output base;
Kristian Høgsbergce5325d2010-06-14 11:54:00 -0400126
Kristian Høgsberg2f9ed712012-07-26 17:57:15 -0400127 char *name;
Kristian Høgsbergce5325d2010-06-14 11:54:00 -0400128 uint32_t crtc_id;
Rob Clark5ca1a472012-08-08 20:27:37 -0500129 int pipe;
Kristian Høgsbergce5325d2010-06-14 11:54:00 -0400130 uint32_t connector_id;
Matt Roper361d2ad2011-08-29 13:52:23 -0700131 drmModeCrtcPtr original_crtc;
Benjamin Franzke1178a3c2011-04-10 16:49:52 +0200132
Ander Conselvan de Oliveiraa7326962012-06-26 17:09:13 +0300133 int vblank_pending;
134 int page_flip_pending;
135
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -0400136 struct gbm_surface *surface;
Kristian Høgsberg8e1f77f2012-05-03 11:39:35 -0400137 struct gbm_bo *cursor_bo[2];
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400138 struct weston_plane cursor_plane;
139 struct weston_plane fb_plane;
Kristian Høgsberg5626d342012-08-03 11:50:05 -0400140 struct weston_surface *cursor_surface;
141 int current_cursor;
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300142 struct drm_fb *current, *next;
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +0200143 struct backlight *backlight;
Kristian Høgsbergce5325d2010-06-14 11:54:00 -0400144};
145
Jesse Barnes58ef3792012-02-23 09:45:49 -0500146/*
147 * An output has a primary display plane plus zero or more sprites for
148 * blending display contents.
149 */
150struct drm_sprite {
151 struct wl_list link;
152
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400153 struct weston_plane plane;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500154
Ander Conselvan de Oliveira8d360b42012-11-09 14:19:05 +0200155 struct drm_fb *current, *next;
Ander Conselvan de Oliveiraa7326962012-06-26 17:09:13 +0300156 struct drm_output *output;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500157 struct drm_compositor *compositor;
158
Jesse Barnes58ef3792012-02-23 09:45:49 -0500159 uint32_t possible_crtcs;
160 uint32_t plane_id;
161 uint32_t count_formats;
162
163 int32_t src_x, src_y;
164 uint32_t src_w, src_h;
165 uint32_t dest_x, dest_y;
166 uint32_t dest_w, dest_h;
167
168 uint32_t formats[];
169};
170
Pekka Paalanen33156972012-08-03 13:30:30 -0400171struct drm_seat {
172 struct weston_seat base;
173 struct wl_list devices_list;
174 struct udev_monitor *udev_monitor;
175 struct wl_event_source *udev_monitor_source;
176 char *seat_id;
177};
178
Kristian Høgsberg5626d342012-08-03 11:50:05 -0400179static void
180drm_output_set_cursor(struct drm_output *output);
Kristian Høgsberg5626d342012-08-03 11:50:05 -0400181
Jesse Barnes58ef3792012-02-23 09:45:49 -0500182static int
Jesse Barnes58ef3792012-02-23 09:45:49 -0500183drm_sprite_crtc_supported(struct weston_output *output_base, uint32_t supported)
184{
185 struct weston_compositor *ec = output_base->compositor;
186 struct drm_compositor *c =(struct drm_compositor *) ec;
187 struct drm_output *output = (struct drm_output *) output_base;
188 int crtc;
189
190 for (crtc = 0; crtc < c->num_crtcs; crtc++) {
191 if (c->crtcs[crtc] != output->crtc_id)
192 continue;
193
194 if (supported & (1 << crtc))
195 return -1;
196 }
197
198 return 0;
199}
200
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300201static void
202drm_fb_destroy_callback(struct gbm_bo *bo, void *data)
203{
204 struct drm_fb *fb = data;
205 struct gbm_device *gbm = gbm_bo_get_device(bo);
206
207 if (fb->fb_id)
208 drmModeRmFB(gbm_device_get_fd(gbm), fb->fb_id);
209
Pekka Paalanende685b82012-12-04 15:58:12 +0200210 weston_buffer_reference(&fb->buffer_ref, NULL);
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300211
212 free(data);
213}
214
215static struct drm_fb *
Ander Conselvan de Oliveira8d360b42012-11-09 14:19:05 +0200216drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_compositor *compositor)
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300217{
218 struct drm_fb *fb = gbm_bo_get_user_data(bo);
Ander Conselvan de Oliveira8d360b42012-11-09 14:19:05 +0200219 uint32_t width, height, stride, handle, format;
220 uint32_t handles[4], pitches[4], offsets[4];
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300221 int ret;
222
223 if (fb)
224 return fb;
225
226 fb = malloc(sizeof *fb);
227
228 fb->bo = bo;
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300229 fb->is_client_buffer = 0;
Pekka Paalanende685b82012-12-04 15:58:12 +0200230 fb->buffer_ref.buffer = NULL;
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300231
232 width = gbm_bo_get_width(bo);
233 height = gbm_bo_get_height(bo);
Kristian Høgsberg270a7cb2012-07-16 16:44:16 -0400234 stride = gbm_bo_get_stride(bo);
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300235 handle = gbm_bo_get_handle(bo).u32;
236
Ander Conselvan de Oliveira8d360b42012-11-09 14:19:05 +0200237 if (compositor->min_width > width || width > compositor->max_width ||
238 compositor->min_height > height ||
239 height > compositor->max_height) {
240 weston_log("bo geometry out of bounds\n");
241 goto err_free;
242 }
243
244 ret = -1;
245
246 format = gbm_bo_get_format(bo);
247
248 if (format && !compositor->no_addfb2) {
249 handles[0] = handle;
250 pitches[0] = stride;
251 offsets[0] = 0;
252
253 ret = drmModeAddFB2(compositor->drm.fd, width, height,
254 format, handles, pitches, offsets,
255 &fb->fb_id, 0);
256 if (ret) {
257 weston_log("addfb2 failed: %m\n");
258 compositor->no_addfb2 = 1;
259 compositor->sprites_are_broken = 1;
260 }
261 }
262
263 if (ret)
264 ret = drmModeAddFB(compositor->drm.fd, width, height, 24, 32,
265 stride, handle, &fb->fb_id);
266
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300267 if (ret) {
Martin Minarik6d118362012-06-07 18:01:59 +0200268 weston_log("failed to create kms fb: %m\n");
Ander Conselvan de Oliveira8d360b42012-11-09 14:19:05 +0200269 goto err_free;
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300270 }
271
272 gbm_bo_set_user_data(bo, fb, drm_fb_destroy_callback);
273
274 return fb;
Ander Conselvan de Oliveira8d360b42012-11-09 14:19:05 +0200275
276err_free:
277 free(fb);
278 return NULL;
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300279}
280
281static void
Ander Conselvan de Oliveira8d360b42012-11-09 14:19:05 +0200282drm_fb_set_buffer(struct drm_fb *fb, struct wl_buffer *buffer)
283{
Pekka Paalanende685b82012-12-04 15:58:12 +0200284 assert(fb->buffer_ref.buffer == NULL);
Ander Conselvan de Oliveira8d360b42012-11-09 14:19:05 +0200285
286 fb->is_client_buffer = 1;
Ander Conselvan de Oliveira8d360b42012-11-09 14:19:05 +0200287
Pekka Paalanende685b82012-12-04 15:58:12 +0200288 weston_buffer_reference(&fb->buffer_ref, buffer);
Ander Conselvan de Oliveira8d360b42012-11-09 14:19:05 +0200289}
290
Ander Conselvan de Oliveirae9209412012-11-30 17:34:22 +0200291static int
292drm_output_check_scanout_format(struct drm_output *output,
293 struct weston_surface *es, struct gbm_bo *bo)
294{
295 int ret = 0;
296 uint32_t format;
297 pixman_region32_t r;
298
299 format = gbm_bo_get_format(bo);
300
301 if (format == GBM_FORMAT_XRGB8888)
302 ret = 1;
303 else if (format == GBM_FORMAT_ARGB8888) {
304 /* We can only scanout an ARGB buffer if the surface's
305 * opaque region covers the whole output */
306 pixman_region32_init(&r);
307 pixman_region32_subtract(&r, &output->base.region,
308 &es->opaque);
309
310 if (!pixman_region32_not_empty(&r))
311 ret = 1;
312
313 pixman_region32_fini(&r);
314 }
315
316 return ret;
317}
318
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400319static struct weston_plane *
Kristian Høgsberg6143f7d2012-07-14 00:31:32 -0400320drm_output_prepare_scanout_surface(struct weston_output *_output,
321 struct weston_surface *es)
Kristian Høgsberg5f5e42e2012-01-25 23:59:42 -0500322{
Kristian Høgsberg6143f7d2012-07-14 00:31:32 -0400323 struct drm_output *output = (struct drm_output *) _output;
Kristian Høgsberg5f5e42e2012-01-25 23:59:42 -0500324 struct drm_compositor *c =
325 (struct drm_compositor *) output->base.compositor;
Pekka Paalanende685b82012-12-04 15:58:12 +0200326 struct wl_buffer *buffer = es->buffer_ref.buffer;
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300327 struct gbm_bo *bo;
Kristian Høgsberg5f5e42e2012-01-25 23:59:42 -0500328
Kristian Høgsberg101cb652012-02-17 10:45:16 -0500329 if (es->geometry.x != output->base.x ||
Pekka Paalanenba3cf952012-01-25 16:22:05 +0200330 es->geometry.y != output->base.y ||
Pekka Paalanende685b82012-12-04 15:58:12 +0200331 buffer == NULL ||
332 buffer->width != output->base.current->width ||
333 buffer->height != output->base.current->height ||
Ander Conselvan de Oliveira2908a3d2012-11-27 17:03:43 +0200334 output->base.transform != es->buffer_transform ||
335 es->transform.enabled)
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400336 return NULL;
Kristian Høgsberg5f5e42e2012-01-25 23:59:42 -0500337
Kristian Høgsberg2763a2e2012-07-13 22:54:43 -0400338 bo = gbm_bo_import(c->gbm, GBM_BO_IMPORT_WL_BUFFER,
Pekka Paalanende685b82012-12-04 15:58:12 +0200339 buffer, GBM_BO_USE_SCANOUT);
Kristian Høgsberg5f5e42e2012-01-25 23:59:42 -0500340
Rob Bradford9b101872012-09-14 23:25:41 +0100341 /* Unable to use the buffer for scanout */
342 if (!bo)
343 return NULL;
344
Ander Conselvan de Oliveirae9209412012-11-30 17:34:22 +0200345 if (!drm_output_check_scanout_format(output, es, bo)) {
Ander Conselvan de Oliveiraa64b15d2012-05-02 16:42:22 +0300346 gbm_bo_destroy(bo);
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400347 return NULL;
Ander Conselvan de Oliveiraa64b15d2012-05-02 16:42:22 +0300348 }
349
Ander Conselvan de Oliveira8d360b42012-11-09 14:19:05 +0200350 output->next = drm_fb_get_from_bo(bo, c);
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300351 if (!output->next) {
352 gbm_bo_destroy(bo);
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400353 return NULL;
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300354 }
Kristian Høgsberg5f5e42e2012-01-25 23:59:42 -0500355
Pekka Paalanende685b82012-12-04 15:58:12 +0200356 drm_fb_set_buffer(output->next, buffer);
Kristian Høgsberg5f5e42e2012-01-25 23:59:42 -0500357
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400358 return &output->fb_plane;
Kristian Høgsberg5f5e42e2012-01-25 23:59:42 -0500359}
360
Kristian Høgsberg06cf6b02012-01-25 23:47:45 -0500361static void
Kristian Høgsbergd7c17262012-09-05 21:54:15 -0400362drm_output_render(struct drm_output *output, pixman_region32_t *damage)
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -0400363{
Ander Conselvan de Oliveira8d360b42012-11-09 14:19:05 +0200364 struct drm_compositor *c =
365 (struct drm_compositor *) output->base.compositor;
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300366 struct gbm_bo *bo;
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -0400367
Ander Conselvan de Oliveira8d360b42012-11-09 14:19:05 +0200368 c->base.renderer->repaint_output(&output->base, damage);
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -0400369
Ander Conselvan de Oliveira0a887722012-11-22 15:57:00 +0200370 pixman_region32_subtract(&c->base.primary_plane.damage,
371 &c->base.primary_plane.damage, damage);
372
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300373 bo = gbm_surface_lock_front_buffer(output->surface);
374 if (!bo) {
Martin Minarik6d118362012-06-07 18:01:59 +0200375 weston_log("failed to lock front buffer: %m\n");
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -0400376 return;
377 }
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300378
Ander Conselvan de Oliveira8d360b42012-11-09 14:19:05 +0200379 output->next = drm_fb_get_from_bo(bo, c);
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300380 if (!output->next) {
Martin Minarik6d118362012-06-07 18:01:59 +0200381 weston_log("failed to get drm_fb for bo\n");
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300382 gbm_surface_release_buffer(output->surface, bo);
383 return;
384 }
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -0400385}
386
387static void
Kristian Høgsberg6ddcdae2012-02-28 22:31:58 -0500388drm_output_repaint(struct weston_output *output_base,
Kristian Høgsbergd7c17262012-09-05 21:54:15 -0400389 pixman_region32_t *damage)
Benjamin Franzkeeefc36c2011-03-11 16:39:20 +0100390{
391 struct drm_output *output = (struct drm_output *) output_base;
Kristian Høgsberg06cf6b02012-01-25 23:47:45 -0500392 struct drm_compositor *compositor =
393 (struct drm_compositor *) output->base.compositor;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500394 struct drm_sprite *s;
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -0400395 struct drm_mode *mode;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500396 int ret = 0;
Benjamin Franzkeeefc36c2011-03-11 16:39:20 +0100397
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300398 if (!output->next)
Kristian Høgsbergd7c17262012-09-05 21:54:15 -0400399 drm_output_render(output, damage);
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300400 if (!output->next)
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -0400401 return;
Benjamin Franzkeeefc36c2011-03-11 16:39:20 +0100402
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -0400403 mode = container_of(output->base.current, struct drm_mode, base);
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300404 if (!output->current) {
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -0400405 ret = drmModeSetCrtc(compositor->drm.fd, output->crtc_id,
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300406 output->next->fb_id, 0, 0,
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -0400407 &output->connector_id, 1,
408 &mode->mode_info);
409 if (ret) {
Martin Minarik6d118362012-06-07 18:01:59 +0200410 weston_log("set mode failed: %m\n");
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -0400411 return;
412 }
Benjamin Franzke1178a3c2011-04-10 16:49:52 +0200413 }
414
Kristian Høgsberg06cf6b02012-01-25 23:47:45 -0500415 if (drmModePageFlip(compositor->drm.fd, output->crtc_id,
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300416 output->next->fb_id,
Kristian Høgsberg54f14c32012-01-18 11:47:41 -0500417 DRM_MODE_PAGE_FLIP_EVENT, output) < 0) {
Martin Minarik6d118362012-06-07 18:01:59 +0200418 weston_log("queueing pageflip failed: %m\n");
Kristian Høgsberg06cf6b02012-01-25 23:47:45 -0500419 return;
Kristian Høgsberg54f14c32012-01-18 11:47:41 -0500420 }
Benjamin Franzkeec4d3422011-03-14 12:07:26 +0100421
Ander Conselvan de Oliveiraa7326962012-06-26 17:09:13 +0300422 output->page_flip_pending = 1;
423
Kristian Høgsberg5626d342012-08-03 11:50:05 -0400424 drm_output_set_cursor(output);
425
Jesse Barnes58ef3792012-02-23 09:45:49 -0500426 /*
427 * Now, update all the sprite surfaces
428 */
429 wl_list_for_each(s, &compositor->sprite_list, link) {
Ander Conselvan de Oliveira8d360b42012-11-09 14:19:05 +0200430 uint32_t flags = 0, fb_id = 0;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500431 drmVBlank vbl = {
432 .request.type = DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT,
433 .request.sequence = 1,
434 };
435
Ander Conselvan de Oliveira8d360b42012-11-09 14:19:05 +0200436 if ((!s->current && !s->next) ||
Ander Conselvan de Oliveira2f7a30b2012-11-09 14:19:03 +0200437 !drm_sprite_crtc_supported(output_base, s->possible_crtcs))
Jesse Barnes58ef3792012-02-23 09:45:49 -0500438 continue;
439
Ander Conselvan de Oliveira8d360b42012-11-09 14:19:05 +0200440 if (s->next && !compositor->sprites_hidden)
441 fb_id = s->next->fb_id;
442
Jesse Barnes58ef3792012-02-23 09:45:49 -0500443 ret = drmModeSetPlane(compositor->drm.fd, s->plane_id,
Ander Conselvan de Oliveira8d360b42012-11-09 14:19:05 +0200444 output->crtc_id, fb_id, flags,
Jesse Barnes58ef3792012-02-23 09:45:49 -0500445 s->dest_x, s->dest_y,
446 s->dest_w, s->dest_h,
447 s->src_x, s->src_y,
448 s->src_w, s->src_h);
449 if (ret)
Martin Minarik6d118362012-06-07 18:01:59 +0200450 weston_log("setplane failed: %d: %s\n",
Jesse Barnes58ef3792012-02-23 09:45:49 -0500451 ret, strerror(errno));
452
Rob Clark5ca1a472012-08-08 20:27:37 -0500453 if (output->pipe > 0)
454 vbl.request.type |= DRM_VBLANK_SECONDARY;
455
Jesse Barnes58ef3792012-02-23 09:45:49 -0500456 /*
457 * Queue a vblank signal so we know when the surface
458 * becomes active on the display or has been replaced.
459 */
460 vbl.request.signal = (unsigned long)s;
461 ret = drmWaitVBlank(compositor->drm.fd, &vbl);
462 if (ret) {
Martin Minarik6d118362012-06-07 18:01:59 +0200463 weston_log("vblank event request failed: %d: %s\n",
Jesse Barnes58ef3792012-02-23 09:45:49 -0500464 ret, strerror(errno));
465 }
Ander Conselvan de Oliveiraa7326962012-06-26 17:09:13 +0300466
467 s->output = output;
468 output->vblank_pending = 1;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500469 }
470
Kristian Høgsberg06cf6b02012-01-25 23:47:45 -0500471 return;
Kristian Høgsbergfc783d42010-06-11 12:56:24 -0400472}
473
474static void
Jesse Barnes58ef3792012-02-23 09:45:49 -0500475vblank_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec,
476 void *data)
477{
478 struct drm_sprite *s = (struct drm_sprite *)data;
Ander Conselvan de Oliveiraa7326962012-06-26 17:09:13 +0300479 struct drm_output *output = s->output;
480 uint32_t msecs;
481
482 output->vblank_pending = 0;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500483
Ander Conselvan de Oliveira8d360b42012-11-09 14:19:05 +0200484 if (s->current)
485 gbm_bo_destroy(s->current->bo);
Jesse Barnes58ef3792012-02-23 09:45:49 -0500486
Ander Conselvan de Oliveira8d360b42012-11-09 14:19:05 +0200487 s->current = s->next;
488 s->next = NULL;
Ander Conselvan de Oliveiraa7326962012-06-26 17:09:13 +0300489
490 if (!output->page_flip_pending) {
491 msecs = sec * 1000 + usec / 1000;
492 weston_output_finish_frame(&output->base, msecs);
493 }
Jesse Barnes58ef3792012-02-23 09:45:49 -0500494}
495
496static void
Kristian Høgsbergfc783d42010-06-11 12:56:24 -0400497page_flip_handler(int fd, unsigned int frame,
498 unsigned int sec, unsigned int usec, void *data)
499{
Benjamin Franzke1178a3c2011-04-10 16:49:52 +0200500 struct drm_output *output = (struct drm_output *) data;
Kristian Høgsbergfc783d42010-06-11 12:56:24 -0400501 uint32_t msecs;
502
Ander Conselvan de Oliveiraa7326962012-06-26 17:09:13 +0300503 output->page_flip_pending = 0;
504
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300505 if (output->current) {
506 if (output->current->is_client_buffer)
507 gbm_bo_destroy(output->current->bo);
508 else
509 gbm_surface_release_buffer(output->surface,
510 output->current->bo);
Benjamin Franzke1178a3c2011-04-10 16:49:52 +0200511 }
512
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300513 output->current = output->next;
514 output->next = NULL;
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -0400515
Ander Conselvan de Oliveiraa7326962012-06-26 17:09:13 +0300516 if (!output->vblank_pending) {
517 msecs = sec * 1000 + usec / 1000;
518 weston_output_finish_frame(&output->base, msecs);
519 }
Benjamin Franzke1178a3c2011-04-10 16:49:52 +0200520}
521
522static int
Jesse Barnes58ef3792012-02-23 09:45:49 -0500523drm_surface_format_supported(struct drm_sprite *s, uint32_t format)
524{
Kristian Høgsberg875ab9e2012-03-30 11:52:39 -0400525 uint32_t i;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500526
527 for (i = 0; i < s->count_formats; i++)
528 if (s->formats[i] == format)
529 return 1;
530
531 return 0;
532}
533
534static int
535drm_surface_transform_supported(struct weston_surface *es)
536{
Kristian Høgsberg3b00bae2012-07-13 15:25:07 -0400537 struct weston_matrix *matrix = &es->transform.matrix;
538 int i;
539
540 if (!es->transform.enabled)
541 return 1;
542
543 for (i = 0; i < 16; i++) {
544 switch (i) {
545 case 10:
546 case 15:
547 if (matrix->d[i] != 1.0)
548 return 0;
549 break;
550 case 0:
551 case 5:
552 case 12:
553 case 13:
554 break;
555 default:
556 if (matrix->d[i] != 0.0)
557 return 0;
558 break;
559 }
560 }
Jesse Barnes58ef3792012-02-23 09:45:49 -0500561
562 return 1;
563}
564
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400565static struct weston_plane *
Jesse Barnes58ef3792012-02-23 09:45:49 -0500566drm_output_prepare_overlay_surface(struct weston_output *output_base,
Kristian Høgsberg6143f7d2012-07-14 00:31:32 -0400567 struct weston_surface *es)
Jesse Barnes58ef3792012-02-23 09:45:49 -0500568{
569 struct weston_compositor *ec = output_base->compositor;
570 struct drm_compositor *c =(struct drm_compositor *) ec;
571 struct drm_sprite *s;
572 int found = 0;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500573 struct gbm_bo *bo;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500574 pixman_region32_t dest_rect, src_rect;
575 pixman_box32_t *box;
576 uint32_t format;
Kristian Høgsberg3b00bae2012-07-13 15:25:07 -0400577 wl_fixed_t sx1, sy1, sx2, sy2;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500578
Ander Conselvan de Oliveira2908a3d2012-11-27 17:03:43 +0200579 if (output_base->transform != WL_OUTPUT_TRANSFORM_NORMAL)
580 return NULL;
581
Kristian Høgsberg65bec242012-03-05 19:57:35 -0500582 if (c->sprites_are_broken)
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400583 return NULL;
Kristian Høgsberg65bec242012-03-05 19:57:35 -0500584
Ander Conselvan de Oliveirad450b192012-06-26 17:09:12 +0300585 if (es->output_mask != (1u << output_base->id))
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400586 return NULL;
Ander Conselvan de Oliveirad450b192012-06-26 17:09:12 +0300587
Pekka Paalanende685b82012-12-04 15:58:12 +0200588 if (es->buffer_ref.buffer == NULL)
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400589 return NULL;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500590
Ville Syrjälä5a84f312012-11-16 11:48:46 +0200591 if (es->alpha != 1.0f)
592 return NULL;
593
Pekka Paalanende685b82012-12-04 15:58:12 +0200594 if (wl_buffer_is_shm(es->buffer_ref.buffer))
Rob Clark702ffae2012-08-09 14:18:27 -0500595 return NULL;
596
Jesse Barnes58ef3792012-02-23 09:45:49 -0500597 if (!drm_surface_transform_supported(es))
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400598 return NULL;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500599
Jesse Barnes58ef3792012-02-23 09:45:49 -0500600 wl_list_for_each(s, &c->sprite_list, link) {
601 if (!drm_sprite_crtc_supported(output_base, s->possible_crtcs))
602 continue;
603
Ander Conselvan de Oliveira8d360b42012-11-09 14:19:05 +0200604 if (!s->next) {
Jesse Barnes58ef3792012-02-23 09:45:49 -0500605 found = 1;
606 break;
607 }
608 }
609
610 /* No sprites available */
611 if (!found)
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400612 return NULL;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500613
Kristian Høgsberg2763a2e2012-07-13 22:54:43 -0400614 bo = gbm_bo_import(c->gbm, GBM_BO_IMPORT_WL_BUFFER,
Pekka Paalanende685b82012-12-04 15:58:12 +0200615 es->buffer_ref.buffer, GBM_BO_USE_SCANOUT);
Kristian Høgsberg2763a2e2012-07-13 22:54:43 -0400616 if (!bo)
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400617 return NULL;
Kristian Høgsberg2763a2e2012-07-13 22:54:43 -0400618
Jesse Barnes58ef3792012-02-23 09:45:49 -0500619 format = gbm_bo_get_format(bo);
Jesse Barnes58ef3792012-02-23 09:45:49 -0500620
Ander Conselvan de Oliveira8d360b42012-11-09 14:19:05 +0200621 if (!drm_surface_format_supported(s, format)) {
622 gbm_bo_destroy(bo);
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400623 return NULL;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500624 }
625
Ander Conselvan de Oliveira8d360b42012-11-09 14:19:05 +0200626 s->next = drm_fb_get_from_bo(bo, c);
627 if (!s->next) {
628 gbm_bo_destroy(bo);
629 return NULL;
630 }
631
Pekka Paalanende685b82012-12-04 15:58:12 +0200632 drm_fb_set_buffer(s->next, es->buffer_ref.buffer);
Jesse Barnes58ef3792012-02-23 09:45:49 -0500633
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400634 box = pixman_region32_extents(&es->transform.boundingbox);
635 s->plane.x = box->x1;
636 s->plane.y = box->y1;
637
Jesse Barnes58ef3792012-02-23 09:45:49 -0500638 /*
639 * Calculate the source & dest rects properly based on actual
Martin Olsson3b132e32012-09-29 15:13:56 +0200640 * position (note the caller has called weston_surface_update_transform()
Jesse Barnes58ef3792012-02-23 09:45:49 -0500641 * for us already).
642 */
643 pixman_region32_init(&dest_rect);
644 pixman_region32_intersect(&dest_rect, &es->transform.boundingbox,
645 &output_base->region);
646 pixman_region32_translate(&dest_rect, -output_base->x, -output_base->y);
647 box = pixman_region32_extents(&dest_rect);
648 s->dest_x = box->x1;
649 s->dest_y = box->y1;
650 s->dest_w = box->x2 - box->x1;
651 s->dest_h = box->y2 - box->y1;
652 pixman_region32_fini(&dest_rect);
653
654 pixman_region32_init(&src_rect);
655 pixman_region32_intersect(&src_rect, &es->transform.boundingbox,
656 &output_base->region);
Jesse Barnes58ef3792012-02-23 09:45:49 -0500657 box = pixman_region32_extents(&src_rect);
Kristian Høgsberg3b00bae2012-07-13 15:25:07 -0400658
659 weston_surface_from_global_fixed(es,
660 wl_fixed_from_int(box->x1),
661 wl_fixed_from_int(box->y1),
662 &sx1, &sy1);
663 weston_surface_from_global_fixed(es,
664 wl_fixed_from_int(box->x2),
665 wl_fixed_from_int(box->y2),
666 &sx2, &sy2);
667
668 if (sx1 < 0)
669 sx1 = 0;
670 if (sy1 < 0)
671 sy1 = 0;
672 if (sx2 > wl_fixed_from_int(es->geometry.width))
673 sx2 = wl_fixed_from_int(es->geometry.width);
674 if (sy2 > wl_fixed_from_int(es->geometry.height))
675 sy2 = wl_fixed_from_int(es->geometry.height);
676
677 s->src_x = sx1 << 8;
678 s->src_y = sy1 << 8;
679 s->src_w = (sx2 - sx1) << 8;
680 s->src_h = (sy2 - sy1) << 8;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500681 pixman_region32_fini(&src_rect);
682
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400683 return &s->plane;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500684}
685
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400686static struct weston_plane *
Kristian Høgsberg5626d342012-08-03 11:50:05 -0400687drm_output_prepare_cursor_surface(struct weston_output *output_base,
688 struct weston_surface *es)
Kristian Høgsbergd8bf90c2012-02-23 23:03:14 -0500689{
Kristian Høgsberg8a015802012-08-09 17:19:23 -0400690 struct drm_compositor *c =
691 (struct drm_compositor *) output_base->compositor;
Kristian Høgsberga6edab32012-07-14 01:06:28 -0400692 struct drm_output *output = (struct drm_output *) output_base;
Kristian Høgsberg5626d342012-08-03 11:50:05 -0400693
Ander Conselvan de Oliveira2908a3d2012-11-27 17:03:43 +0200694 if (output->base.transform != WL_OUTPUT_TRANSFORM_NORMAL)
695 return NULL;
Kristian Høgsberg5626d342012-08-03 11:50:05 -0400696 if (output->cursor_surface)
697 return NULL;
698 if (es->output_mask != (1u << output_base->id))
699 return NULL;
Rob Clarkab5b1e32012-08-09 13:24:45 -0500700 if (c->cursors_are_broken)
Kristian Høgsberg8a015802012-08-09 17:19:23 -0400701 return NULL;
Pekka Paalanende685b82012-12-04 15:58:12 +0200702 if (es->buffer_ref.buffer == NULL ||
703 !wl_buffer_is_shm(es->buffer_ref.buffer) ||
Kristian Høgsberg5626d342012-08-03 11:50:05 -0400704 es->geometry.width > 64 || es->geometry.height > 64)
705 return NULL;
706
707 output->cursor_surface = es;
708
709 return &output->cursor_plane;
710}
711
712static void
713drm_output_set_cursor(struct drm_output *output)
714{
715 struct weston_surface *es = output->cursor_surface;
Kristian Høgsberga6edab32012-07-14 01:06:28 -0400716 struct drm_compositor *c =
717 (struct drm_compositor *) output->base.compositor;
718 EGLint handle, stride;
719 struct gbm_bo *bo;
720 uint32_t buf[64 * 64];
721 unsigned char *s;
Kristian Høgsberg24e42752012-07-18 12:08:37 -0400722 int i, x, y;
Kristian Høgsbergd8bf90c2012-02-23 23:03:14 -0500723
Kristian Høgsberg79af73e2012-08-03 15:45:23 -0400724 output->cursor_surface = NULL;
Kristian Høgsberg5626d342012-08-03 11:50:05 -0400725 if (es == NULL) {
726 drmModeSetCursor(c->drm.fd, output->crtc_id, 0, 0, 0);
727 return;
728 }
Kristian Høgsbergd8bf90c2012-02-23 23:03:14 -0500729
Pekka Paalanende685b82012-12-04 15:58:12 +0200730 if (es->buffer_ref.buffer &&
731 pixman_region32_not_empty(&output->cursor_plane.damage)) {
Kristian Høgsberg5626d342012-08-03 11:50:05 -0400732 pixman_region32_fini(&output->cursor_plane.damage);
733 pixman_region32_init(&output->cursor_plane.damage);
Kristian Høgsberg1f5de352012-07-18 12:09:58 -0400734 output->current_cursor ^= 1;
735 bo = output->cursor_bo[output->current_cursor];
736 memset(buf, 0, sizeof buf);
Pekka Paalanende685b82012-12-04 15:58:12 +0200737 stride = wl_shm_buffer_get_stride(es->buffer_ref.buffer);
738 s = wl_shm_buffer_get_data(es->buffer_ref.buffer);
Kristian Høgsberg1f5de352012-07-18 12:09:58 -0400739 for (i = 0; i < es->geometry.height; i++)
740 memcpy(buf + i * 64, s + i * stride,
741 es->geometry.width * 4);
Kristian Høgsbergd8bf90c2012-02-23 23:03:14 -0500742
Kristian Høgsberg1f5de352012-07-18 12:09:58 -0400743 if (gbm_bo_write(bo, buf, sizeof buf) < 0)
Pekka Paalanenae29da22012-08-06 14:57:05 +0300744 weston_log("failed update cursor: %m\n");
Kristian Høgsberga6edab32012-07-14 01:06:28 -0400745
Kristian Høgsberg1f5de352012-07-18 12:09:58 -0400746 handle = gbm_bo_get_handle(bo).s32;
747 if (drmModeSetCursor(c->drm.fd,
Rob Clarkab5b1e32012-08-09 13:24:45 -0500748 output->crtc_id, handle, 64, 64)) {
Pekka Paalanenae29da22012-08-06 14:57:05 +0300749 weston_log("failed to set cursor: %m\n");
Rob Clarkab5b1e32012-08-09 13:24:45 -0500750 c->cursors_are_broken = 1;
751 }
Kristian Høgsberga6edab32012-07-14 01:06:28 -0400752 }
753
Kristian Høgsberg24e42752012-07-18 12:08:37 -0400754 x = es->geometry.x - output->base.x;
755 y = es->geometry.y - output->base.y;
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400756 if (output->cursor_plane.x != x || output->cursor_plane.y != y) {
Rob Clarkab5b1e32012-08-09 13:24:45 -0500757 if (drmModeMoveCursor(c->drm.fd, output->crtc_id, x, y)) {
Kristian Høgsberg24e42752012-07-18 12:08:37 -0400758 weston_log("failed to move cursor: %m\n");
Rob Clarkab5b1e32012-08-09 13:24:45 -0500759 c->cursors_are_broken = 1;
760 }
761
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400762 output->cursor_plane.x = x;
763 output->cursor_plane.y = y;
Kristian Høgsberga6edab32012-07-14 01:06:28 -0400764 }
Kristian Høgsbergd8bf90c2012-02-23 23:03:14 -0500765}
766
Jesse Barnes58ef3792012-02-23 09:45:49 -0500767static void
768drm_assign_planes(struct weston_output *output)
769{
Kristian Høgsberga6edab32012-07-14 01:06:28 -0400770 struct drm_compositor *c =
771 (struct drm_compositor *) output->compositor;
Ander Conselvan de Oliveira4bcf3a52012-10-31 17:55:45 +0200772 struct drm_output *drm_output = (struct drm_output *) output;
773 struct drm_sprite *s;
Kristian Høgsberg8e1f77f2012-05-03 11:39:35 -0400774 struct weston_surface *es, *next;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500775 pixman_region32_t overlap, surface_overlap;
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400776 struct weston_plane *primary, *next_plane;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500777
Ander Conselvan de Oliveira4bcf3a52012-10-31 17:55:45 +0200778 /* Reset the opaque region of the planes */
779 pixman_region32_fini(&drm_output->cursor_plane.opaque);
780 pixman_region32_init(&drm_output->cursor_plane.opaque);
781 pixman_region32_fini(&drm_output->fb_plane.opaque);
782 pixman_region32_init(&drm_output->fb_plane.opaque);
783
784 wl_list_for_each (s, &c->sprite_list, link) {
785 if (!drm_sprite_crtc_supported(output, s->possible_crtcs))
786 continue;
787
788 pixman_region32_fini(&s->plane.opaque);
789 pixman_region32_init(&s->plane.opaque);
790 }
791
Jesse Barnes58ef3792012-02-23 09:45:49 -0500792 /*
793 * Find a surface for each sprite in the output using some heuristics:
794 * 1) size
795 * 2) frequency of update
796 * 3) opacity (though some hw might support alpha blending)
797 * 4) clipping (this can be fixed with color keys)
798 *
799 * The idea is to save on blitting since this should save power.
800 * If we can get a large video surface on the sprite for example,
801 * the main display surface may not need to update at all, and
802 * the client buffer can be used directly for the sprite surface
803 * as we do for flipping full screen surfaces.
804 */
805 pixman_region32_init(&overlap);
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400806 primary = &c->base.primary_plane;
Kristian Høgsberga6edab32012-07-14 01:06:28 -0400807 wl_list_for_each_safe(es, next, &c->base.surface_list, link) {
Pekka Paalanenccfeae22012-12-04 15:58:14 +0200808 /* test whether this buffer can ever go into a plane:
809 * non-shm, or small enough to be a cursor
810 */
811 if ((es->buffer_ref.buffer &&
812 !wl_buffer_is_shm(es->buffer_ref.buffer)) ||
813 (es->geometry.width <= 64 && es->geometry.height <= 64))
814 es->keep_buffer = 1;
815 else
816 es->keep_buffer = 0;
817
Jesse Barnes58ef3792012-02-23 09:45:49 -0500818 pixman_region32_init(&surface_overlap);
819 pixman_region32_intersect(&surface_overlap, &overlap,
820 &es->transform.boundingbox);
821
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400822 next_plane = NULL;
Kristian Høgsberg6143f7d2012-07-14 00:31:32 -0400823 if (pixman_region32_not_empty(&surface_overlap))
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400824 next_plane = primary;
825 if (next_plane == NULL)
Kristian Høgsberg5626d342012-08-03 11:50:05 -0400826 next_plane = drm_output_prepare_cursor_surface(output, es);
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400827 if (next_plane == NULL)
828 next_plane = drm_output_prepare_scanout_surface(output, es);
829 if (next_plane == NULL)
830 next_plane = drm_output_prepare_overlay_surface(output, es);
831 if (next_plane == NULL)
832 next_plane = primary;
833 weston_surface_move_to_plane(es, next_plane);
834 if (next_plane == primary)
Jesse Barnes58ef3792012-02-23 09:45:49 -0500835 pixman_region32_union(&overlap, &overlap,
836 &es->transform.boundingbox);
Kristian Høgsberg6143f7d2012-07-14 00:31:32 -0400837
Jesse Barnes58ef3792012-02-23 09:45:49 -0500838 pixman_region32_fini(&surface_overlap);
839 }
840 pixman_region32_fini(&overlap);
Jesse Barnes58ef3792012-02-23 09:45:49 -0500841}
842
Matt Roper361d2ad2011-08-29 13:52:23 -0700843static void
Kristian Høgsberg8334bc12012-01-03 10:29:47 -0500844drm_output_destroy(struct weston_output *output_base)
Matt Roper361d2ad2011-08-29 13:52:23 -0700845{
846 struct drm_output *output = (struct drm_output *) output_base;
847 struct drm_compositor *c =
Benjamin Franzke117483d2011-08-30 11:38:26 +0200848 (struct drm_compositor *) output->base.compositor;
Matt Roper361d2ad2011-08-29 13:52:23 -0700849 drmModeCrtcPtr origcrtc = output->original_crtc;
Matt Roper361d2ad2011-08-29 13:52:23 -0700850
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +0200851 if (output->backlight)
852 backlight_destroy(output->backlight);
853
Matt Roper361d2ad2011-08-29 13:52:23 -0700854 /* Turn off hardware cursor */
Kristian Høgsberga6edab32012-07-14 01:06:28 -0400855 drmModeSetCursor(c->drm.fd, output->crtc_id, 0, 0, 0);
Matt Roper361d2ad2011-08-29 13:52:23 -0700856
857 /* Restore original CRTC state */
858 drmModeSetCrtc(c->drm.fd, origcrtc->crtc_id, origcrtc->buffer_id,
Benjamin Franzke117483d2011-08-30 11:38:26 +0200859 origcrtc->x, origcrtc->y,
860 &output->connector_id, 1, &origcrtc->mode);
Matt Roper361d2ad2011-08-29 13:52:23 -0700861 drmModeFreeCrtc(origcrtc);
862
Benjamin Franzke48c4ea22011-08-30 11:44:56 +0200863 c->crtc_allocator &= ~(1 << output->crtc_id);
864 c->connector_allocator &= ~(1 << output->connector_id);
865
John Kåre Alsaker779b52a2012-11-13 19:10:29 +0100866 gl_renderer_output_destroy(output_base);
John Kåre Alsaker94659272012-11-13 19:10:18 +0100867
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -0400868 gbm_surface_destroy(output->surface);
869
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400870 weston_plane_release(&output->fb_plane);
871 weston_plane_release(&output->cursor_plane);
872
Kristian Høgsberg8334bc12012-01-03 10:29:47 -0500873 weston_output_destroy(&output->base);
Benjamin Franzke48c4ea22011-08-30 11:44:56 +0200874 wl_list_remove(&output->base.link);
875
Kristian Høgsberg148ef012012-07-26 23:03:57 -0400876 free(output->name);
Matt Roper361d2ad2011-08-29 13:52:23 -0700877 free(output);
878}
879
Alex Wub7b8bda2012-04-17 17:20:48 +0800880static struct drm_mode *
881choose_mode (struct drm_output *output, struct weston_mode *target_mode)
882{
883 struct drm_mode *tmp_mode = NULL, *mode;
884
885 if (output->base.current->width == target_mode->width &&
886 output->base.current->height == target_mode->height &&
887 (output->base.current->refresh == target_mode->refresh ||
888 target_mode->refresh == 0))
889 return (struct drm_mode *)output->base.current;
890
891 wl_list_for_each(mode, &output->base.mode_list, base.link) {
892 if (mode->mode_info.hdisplay == target_mode->width &&
893 mode->mode_info.vdisplay == target_mode->height) {
894 if (mode->mode_info.vrefresh == target_mode->refresh ||
895 target_mode->refresh == 0) {
896 return mode;
897 } else if (!tmp_mode)
898 tmp_mode = mode;
899 }
900 }
901
902 return tmp_mode;
903}
904
905static int
906drm_output_switch_mode(struct weston_output *output_base, struct weston_mode *mode)
907{
908 struct drm_output *output;
909 struct drm_mode *drm_mode;
910 int ret;
911 struct drm_compositor *ec;
912 struct gbm_surface *surface;
Alex Wub7b8bda2012-04-17 17:20:48 +0800913
914 if (output_base == NULL) {
Martin Minarik6d118362012-06-07 18:01:59 +0200915 weston_log("output is NULL.\n");
Alex Wub7b8bda2012-04-17 17:20:48 +0800916 return -1;
917 }
918
919 if (mode == NULL) {
Martin Minarik6d118362012-06-07 18:01:59 +0200920 weston_log("mode is NULL.\n");
Alex Wub7b8bda2012-04-17 17:20:48 +0800921 return -1;
922 }
923
924 ec = (struct drm_compositor *)output_base->compositor;
925 output = (struct drm_output *)output_base;
926 drm_mode = choose_mode (output, mode);
927
928 if (!drm_mode) {
Martin Minarik6d118362012-06-07 18:01:59 +0200929 weston_log("%s, invalid resolution:%dx%d\n", __func__, mode->width, mode->height);
Alex Wub7b8bda2012-04-17 17:20:48 +0800930 return -1;
931 } else if (&drm_mode->base == output->base.current) {
932 return 0;
933 } else if (drm_mode->base.width == output->base.current->width &&
934 drm_mode->base.height == output->base.current->height) {
935 /* only change refresh value */
936 ret = drmModeSetCrtc(ec->drm.fd,
937 output->crtc_id,
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300938 output->current->fb_id, 0, 0,
Alex Wub7b8bda2012-04-17 17:20:48 +0800939 &output->connector_id, 1, &drm_mode->mode_info);
940
941 if (ret) {
Martin Minarik6d118362012-06-07 18:01:59 +0200942 weston_log("failed to set mode (%dx%d) %u Hz\n",
Alex Wub7b8bda2012-04-17 17:20:48 +0800943 drm_mode->base.width,
944 drm_mode->base.height,
Kristian Høgsbergc4621b02012-05-10 12:23:53 -0400945 drm_mode->base.refresh / 1000);
Alex Wub7b8bda2012-04-17 17:20:48 +0800946 ret = -1;
947 } else {
948 output->base.current->flags = 0;
949 output->base.current = &drm_mode->base;
950 drm_mode->base.flags =
951 WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
952 ret = 0;
953 }
954
955 return ret;
956 }
957
958 drm_mode->base.flags =
959 WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
960
961 surface = gbm_surface_create(ec->gbm,
962 drm_mode->base.width,
963 drm_mode->base.height,
964 GBM_FORMAT_XRGB8888,
965 GBM_BO_USE_SCANOUT |
966 GBM_BO_USE_RENDERING);
967 if (!surface) {
Martin Minarik6d118362012-06-07 18:01:59 +0200968 weston_log("failed to create gbm surface\n");
Alex Wub7b8bda2012-04-17 17:20:48 +0800969 return -1;
970 }
971
John Kåre Alsaker779b52a2012-11-13 19:10:29 +0100972 gl_renderer_output_destroy(&output->base);
Alex Wub7b8bda2012-04-17 17:20:48 +0800973
John Kåre Alsaker779b52a2012-11-13 19:10:29 +0100974 if (!gl_renderer_output_create(&output->base, surface)) {
John Kåre Alsaker94659272012-11-13 19:10:18 +0100975 weston_log("failed to create renderer output\n");
976 goto err_gbm;
Alex Wub7b8bda2012-04-17 17:20:48 +0800977 }
978
979 ret = drmModeSetCrtc(ec->drm.fd,
980 output->crtc_id,
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300981 output->current->fb_id, 0, 0,
Alex Wub7b8bda2012-04-17 17:20:48 +0800982 &output->connector_id, 1, &drm_mode->mode_info);
983 if (ret) {
Martin Minarik6d118362012-06-07 18:01:59 +0200984 weston_log("failed to set mode\n");
John Kåre Alsaker779b52a2012-11-13 19:10:29 +0100985 goto err_gl;
Alex Wub7b8bda2012-04-17 17:20:48 +0800986 }
987
988 /* reset rendering stuff. */
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300989 if (output->current) {
990 if (output->current->is_client_buffer)
991 gbm_bo_destroy(output->current->bo);
992 else
993 gbm_surface_release_buffer(output->surface,
994 output->current->bo);
995 }
996 output->current = NULL;
Alex Wub7b8bda2012-04-17 17:20:48 +0800997
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300998 if (output->next) {
999 if (output->next->is_client_buffer)
1000 gbm_bo_destroy(output->next->bo);
1001 else
1002 gbm_surface_release_buffer(output->surface,
1003 output->next->bo);
1004 }
1005 output->next = NULL;
Alex Wub7b8bda2012-04-17 17:20:48 +08001006
Alex Wub7b8bda2012-04-17 17:20:48 +08001007 gbm_surface_destroy(output->surface);
Alex Wub7b8bda2012-04-17 17:20:48 +08001008 output->surface = surface;
1009
1010 /*update output*/
1011 output->base.current = &drm_mode->base;
1012 output->base.dirty = 1;
1013 weston_output_move(&output->base, output->base.x, output->base.y);
1014 return 0;
1015
John Kåre Alsaker779b52a2012-11-13 19:10:29 +01001016err_gl:
1017 gl_renderer_output_destroy(&output->base);
John Kåre Alsaker94659272012-11-13 19:10:18 +01001018err_gbm:
Alex Wub7b8bda2012-04-17 17:20:48 +08001019 gbm_surface_destroy(surface);
1020 return -1;
1021}
1022
Kristian Høgsbergb1868472011-04-22 12:27:57 -04001023static int
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001024on_drm_input(int fd, uint32_t mask, void *data)
1025{
1026 drmEventContext evctx;
1027
1028 memset(&evctx, 0, sizeof evctx);
1029 evctx.version = DRM_EVENT_CONTEXT_VERSION;
1030 evctx.page_flip_handler = page_flip_handler;
Jesse Barnes58ef3792012-02-23 09:45:49 -05001031 evctx.vblank_handler = vblank_handler;
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001032 drmHandleEvent(fd, &evctx);
Kristian Høgsbergb1868472011-04-22 12:27:57 -04001033
1034 return 1;
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001035}
1036
1037static int
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04001038init_egl(struct drm_compositor *ec, struct udev_device *device)
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001039{
Kristian Høgsbergb5ef5912012-03-28 22:53:49 -04001040 const char *filename, *sysnum;
Kristian Høgsberg5fcd0aa2010-08-09 14:43:33 -04001041 int fd;
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -04001042
Kristian Høgsbergb71302e2012-05-10 12:28:35 -04001043 sysnum = udev_device_get_sysnum(device);
1044 if (sysnum)
1045 ec->drm.id = atoi(sysnum);
1046 if (!sysnum || ec->drm.id < 0) {
Martin Minarik6d118362012-06-07 18:01:59 +02001047 weston_log("cannot get device sysnum\n");
Kristian Høgsbergb71302e2012-05-10 12:28:35 -04001048 return -1;
1049 }
1050
Kristian Høgsberg5fcd0aa2010-08-09 14:43:33 -04001051 filename = udev_device_get_devnode(device);
David Herrmann63ff7062011-11-05 18:46:01 +01001052 fd = open(filename, O_RDWR | O_CLOEXEC);
Kristian Høgsberg5fcd0aa2010-08-09 14:43:33 -04001053 if (fd < 0) {
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001054 /* Probably permissions error */
Martin Minarik6d118362012-06-07 18:01:59 +02001055 weston_log("couldn't open %s, skipping\n",
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001056 udev_device_get_devnode(device));
1057 return -1;
1058 }
1059
Kristian Høgsbergfc9c5e02012-06-08 16:45:33 -04001060 weston_log("using %s\n", filename);
1061
Benjamin Franzke2af7f102011-03-02 11:14:59 +01001062 ec->drm.fd = fd;
Benjamin Franzke060cf802011-04-30 09:32:11 +02001063 ec->gbm = gbm_create_device(ec->drm.fd);
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001064
John Kåre Alsaker779b52a2012-11-13 19:10:29 +01001065 if (gl_renderer_create(&ec->base, ec->gbm, gl_renderer_opaque_attribs,
John Kåre Alsaker1b7ad162012-11-13 19:10:19 +01001066 NULL) < 0) {
1067 gbm_device_destroy(ec->gbm);
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -04001068 return -1;
1069 }
1070
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001071 return 0;
1072}
1073
Ander Conselvan de Oliveira42c46462012-08-09 16:45:00 +03001074static struct drm_mode *
Kristian Høgsberg8f0ce052011-06-21 11:16:58 -04001075drm_output_add_mode(struct drm_output *output, drmModeModeInfo *info)
1076{
1077 struct drm_mode *mode;
Kristian Høgsbergc4621b02012-05-10 12:23:53 -04001078 uint64_t refresh;
Kristian Høgsberg8f0ce052011-06-21 11:16:58 -04001079
1080 mode = malloc(sizeof *mode);
1081 if (mode == NULL)
Ander Conselvan de Oliveira42c46462012-08-09 16:45:00 +03001082 return NULL;
Kristian Høgsberg8f0ce052011-06-21 11:16:58 -04001083
1084 mode->base.flags = 0;
1085 mode->base.width = info->hdisplay;
1086 mode->base.height = info->vdisplay;
Kristian Høgsbergc4621b02012-05-10 12:23:53 -04001087
1088 /* Calculate higher precision (mHz) refresh rate */
1089 refresh = (info->clock * 1000000LL / info->htotal +
1090 info->vtotal / 2) / info->vtotal;
1091
1092 if (info->flags & DRM_MODE_FLAG_INTERLACE)
1093 refresh *= 2;
1094 if (info->flags & DRM_MODE_FLAG_DBLSCAN)
1095 refresh /= 2;
1096 if (info->vscan > 1)
1097 refresh /= info->vscan;
1098
1099 mode->base.refresh = refresh;
Kristian Høgsberg8f0ce052011-06-21 11:16:58 -04001100 mode->mode_info = *info;
Kristian Høgsberg061c4252012-06-28 11:28:15 -04001101
1102 if (info->type & DRM_MODE_TYPE_PREFERRED)
1103 mode->base.flags |= WL_OUTPUT_MODE_PREFERRED;
1104
Kristian Høgsberg8f0ce052011-06-21 11:16:58 -04001105 wl_list_insert(output->base.mode_list.prev, &mode->base.link);
1106
Ander Conselvan de Oliveira42c46462012-08-09 16:45:00 +03001107 return mode;
Kristian Høgsberg8f0ce052011-06-21 11:16:58 -04001108}
1109
1110static int
1111drm_subpixel_to_wayland(int drm_value)
1112{
1113 switch (drm_value) {
1114 default:
1115 case DRM_MODE_SUBPIXEL_UNKNOWN:
1116 return WL_OUTPUT_SUBPIXEL_UNKNOWN;
1117 case DRM_MODE_SUBPIXEL_NONE:
1118 return WL_OUTPUT_SUBPIXEL_NONE;
1119 case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB:
1120 return WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB;
1121 case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR:
1122 return WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR;
1123 case DRM_MODE_SUBPIXEL_VERTICAL_RGB:
1124 return WL_OUTPUT_SUBPIXEL_VERTICAL_RGB;
1125 case DRM_MODE_SUBPIXEL_VERTICAL_BGR:
1126 return WL_OUTPUT_SUBPIXEL_VERTICAL_BGR;
1127 }
1128}
1129
Tiago Vignatti5ab91ad2012-03-12 19:40:09 -03001130/* returns a value between 0-255 range, where higher is brighter */
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +02001131static uint32_t
1132drm_get_backlight(struct drm_output *output)
1133{
1134 long brightness, max_brightness, norm;
1135
1136 brightness = backlight_get_brightness(output->backlight);
1137 max_brightness = backlight_get_max_brightness(output->backlight);
1138
Tiago Vignatti5ab91ad2012-03-12 19:40:09 -03001139 /* convert it on a scale of 0 to 255 */
1140 norm = (brightness * 255)/(max_brightness);
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +02001141
1142 return (uint32_t) norm;
1143}
1144
Tiago Vignatti5ab91ad2012-03-12 19:40:09 -03001145/* values accepted are between 0-255 range */
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +02001146static void
1147drm_set_backlight(struct weston_output *output_base, uint32_t value)
1148{
1149 struct drm_output *output = (struct drm_output *) output_base;
1150 long max_brightness, new_brightness;
1151
1152 if (!output->backlight)
1153 return;
1154
Kristian Høgsberg875ab9e2012-03-30 11:52:39 -04001155 if (value > 255)
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +02001156 return;
1157
1158 max_brightness = backlight_get_max_brightness(output->backlight);
1159
1160 /* get denormalized value */
Tiago Vignatti5ab91ad2012-03-12 19:40:09 -03001161 new_brightness = (value * max_brightness) / 255;
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +02001162
1163 backlight_set_brightness(output->backlight, new_brightness);
1164}
1165
1166static drmModePropertyPtr
1167drm_get_prop(int fd, drmModeConnectorPtr connector, const char *name)
1168{
1169 drmModePropertyPtr props;
1170 int i;
1171
1172 for (i = 0; i < connector->count_props; i++) {
1173 props = drmModeGetProperty(fd, connector->props[i]);
1174 if (!props)
1175 continue;
1176
1177 if (!strcmp(props->name, name))
1178 return props;
1179
1180 drmModeFreeProperty(props);
1181 }
1182
1183 return NULL;
1184}
1185
1186static void
1187drm_set_dpms(struct weston_output *output_base, enum dpms_enum level)
1188{
1189 struct drm_output *output = (struct drm_output *) output_base;
1190 struct weston_compositor *ec = output_base->compositor;
1191 struct drm_compositor *c = (struct drm_compositor *) ec;
1192 drmModeConnectorPtr connector;
1193 drmModePropertyPtr prop;
1194
1195 connector = drmModeGetConnector(c->drm.fd, output->connector_id);
1196 if (!connector)
1197 return;
1198
1199 prop = drm_get_prop(c->drm.fd, connector, "DPMS");
1200 if (!prop) {
1201 drmModeFreeConnector(connector);
1202 return;
1203 }
1204
1205 drmModeConnectorSetProperty(c->drm.fd, connector->connector_id,
1206 prop->prop_id, level);
1207 drmModeFreeProperty(prop);
1208 drmModeFreeConnector(connector);
1209}
1210
Kristian Høgsberg2f9ed712012-07-26 17:57:15 -04001211static const char *connector_type_names[] = {
1212 "None",
1213 "VGA",
1214 "DVI",
1215 "DVI",
1216 "DVI",
1217 "Composite",
1218 "TV",
1219 "LVDS",
1220 "CTV",
1221 "DIN",
1222 "DP",
1223 "HDMI",
1224 "HDMI",
1225 "TV",
1226 "eDP",
1227};
1228
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001229static int
Kristian Høgsberg9ca38462012-07-26 22:44:55 -04001230find_crtc_for_connector(struct drm_compositor *ec,
1231 drmModeRes *resources, drmModeConnector *connector)
1232{
1233 drmModeEncoder *encoder;
1234 uint32_t possible_crtcs;
1235 int i, j;
1236
1237 for (j = 0; j < connector->count_encoders; j++) {
1238 encoder = drmModeGetEncoder(ec->drm.fd, connector->encoders[j]);
1239 if (encoder == NULL) {
1240 weston_log("Failed to get encoder.\n");
1241 return -1;
1242 }
1243 possible_crtcs = encoder->possible_crtcs;
1244 drmModeFreeEncoder(encoder);
1245
1246 for (i = 0; i < resources->count_crtcs; i++) {
1247 if (possible_crtcs & (1 << i) &&
1248 !(ec->crtc_allocator & (1 << resources->crtcs[i])))
1249 return i;
1250 }
1251 }
1252
1253 return -1;
1254}
1255
1256static int
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04001257create_output_for_connector(struct drm_compositor *ec,
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001258 drmModeRes *resources,
Benjamin Franzkeeefc36c2011-03-11 16:39:20 +01001259 drmModeConnector *connector,
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +02001260 int x, int y, struct udev_device *drm_device)
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001261{
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04001262 struct drm_output *output;
Ander Conselvan de Oliveira42c46462012-08-09 16:45:00 +03001263 struct drm_mode *drm_mode, *next, *preferred, *current, *configured;
1264 struct weston_mode *m;
Scott Moreau8ab5d452012-07-30 19:51:08 -06001265 struct drm_configured_output *o = NULL, *temp;
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001266 drmModeEncoder *encoder;
Kristian Høgsberg061c4252012-06-28 11:28:15 -04001267 drmModeModeInfo crtc_mode;
1268 drmModeCrtc *crtc;
Ander Conselvan de Oliveira42c46462012-08-09 16:45:00 +03001269 int i;
Kristian Høgsberg2f9ed712012-07-26 17:57:15 -04001270 char name[32];
1271 const char *type_name;
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001272
Kristian Høgsberg9ca38462012-07-26 22:44:55 -04001273 i = find_crtc_for_connector(ec, resources, connector);
1274 if (i < 0) {
1275 weston_log("No usable crtc/encoder pair for connector.\n");
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001276 return -1;
1277 }
1278
Kristian Høgsberg8f0ce052011-06-21 11:16:58 -04001279 output = malloc(sizeof *output);
Kristian Høgsberg9ca38462012-07-26 22:44:55 -04001280 if (output == NULL)
Kristian Høgsberg8f0ce052011-06-21 11:16:58 -04001281 return -1;
1282
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04001283 memset(output, 0, sizeof *output);
Kristian Høgsberg8f0ce052011-06-21 11:16:58 -04001284 output->base.subpixel = drm_subpixel_to_wayland(connector->subpixel);
1285 output->base.make = "unknown";
1286 output->base.model = "unknown";
1287 wl_list_init(&output->base.mode_list);
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04001288
Kristian Høgsberg2f9ed712012-07-26 17:57:15 -04001289 if (connector->connector_type < ARRAY_LENGTH(connector_type_names))
1290 type_name = connector_type_names[connector->connector_type];
1291 else
1292 type_name = "UNKNOWN";
1293 snprintf(name, 32, "%s%d", type_name, connector->connector_type_id);
1294 output->name = strdup(name);
1295
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001296 output->crtc_id = resources->crtcs[i];
Rob Clark5ca1a472012-08-08 20:27:37 -05001297 output->pipe = i;
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001298 ec->crtc_allocator |= (1 << output->crtc_id);
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001299 output->connector_id = connector->connector_id;
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001300 ec->connector_allocator |= (1 << output->connector_id);
Kristian Høgsberg8f0ce052011-06-21 11:16:58 -04001301
Matt Roper361d2ad2011-08-29 13:52:23 -07001302 output->original_crtc = drmModeGetCrtc(ec->drm.fd, output->crtc_id);
1303
Kristian Høgsberg061c4252012-06-28 11:28:15 -04001304 /* Get the current mode on the crtc that's currently driving
1305 * this connector. */
1306 encoder = drmModeGetEncoder(ec->drm.fd, connector->encoder_id);
Wang Quanxianacb805a2012-07-30 18:09:46 -04001307 memset(&crtc_mode, 0, sizeof crtc_mode);
1308 if (encoder != NULL) {
1309 crtc = drmModeGetCrtc(ec->drm.fd, encoder->crtc_id);
1310 drmModeFreeEncoder(encoder);
1311 if (crtc == NULL)
1312 goto err_free;
1313 if (crtc->mode_valid)
1314 crtc_mode = crtc->mode;
1315 drmModeFreeCrtc(crtc);
1316 }
Kristian Høgsberg061c4252012-06-28 11:28:15 -04001317
David Herrmann0f0d54e2011-12-08 17:05:45 +01001318 for (i = 0; i < connector->count_modes; i++) {
Ander Conselvan de Oliveira42c46462012-08-09 16:45:00 +03001319 drm_mode = drm_output_add_mode(output, &connector->modes[i]);
1320 if (!drm_mode)
David Herrmann0f0d54e2011-12-08 17:05:45 +01001321 goto err_free;
1322 }
1323
Kristian Høgsberg061c4252012-06-28 11:28:15 -04001324 preferred = NULL;
1325 current = NULL;
Scott Moreau8ab5d452012-07-30 19:51:08 -06001326 configured = NULL;
1327
1328 wl_list_for_each(temp, &configured_output_list, link) {
1329 if (strcmp(temp->name, output->name) == 0) {
Scott Moreau1bad5db2012-08-18 01:04:05 -06001330 if (temp->mode)
1331 weston_log("%s mode \"%s\" in config\n",
Scott Moreau8ab5d452012-07-30 19:51:08 -06001332 temp->name, temp->mode);
1333 o = temp;
1334 break;
1335 }
1336 }
1337
Ander Conselvan de Oliveiradc79e6d2012-08-09 16:45:01 +03001338 if (o && o->config == OUTPUT_CONFIG_OFF) {
Scott Moreau8ab5d452012-07-30 19:51:08 -06001339 weston_log("Disabling output %s\n", o->name);
1340
1341 drmModeSetCrtc(ec->drm.fd, output->crtc_id,
1342 0, 0, 0, 0, 0, NULL);
1343 goto err_free;
1344 }
1345
Kristian Høgsberg061c4252012-06-28 11:28:15 -04001346 wl_list_for_each(drm_mode, &output->base.mode_list, base.link) {
Scott Moreau1bad5db2012-08-18 01:04:05 -06001347 if (o && o->config == OUTPUT_CONFIG_MODE &&
1348 o->width == drm_mode->base.width &&
1349 o->height == drm_mode->base.height)
Ander Conselvan de Oliveira42c46462012-08-09 16:45:00 +03001350 configured = drm_mode;
Wang Quanxianacb805a2012-07-30 18:09:46 -04001351 if (!memcmp(&crtc_mode, &drm_mode->mode_info, sizeof crtc_mode))
Ander Conselvan de Oliveira42c46462012-08-09 16:45:00 +03001352 current = drm_mode;
Kristian Høgsberg061c4252012-06-28 11:28:15 -04001353 if (drm_mode->base.flags & WL_OUTPUT_MODE_PREFERRED)
Ander Conselvan de Oliveira42c46462012-08-09 16:45:00 +03001354 preferred = drm_mode;
David Herrmann0f0d54e2011-12-08 17:05:45 +01001355 }
Kristian Høgsberg8f0ce052011-06-21 11:16:58 -04001356
Scott Moreau8f37e0b2012-07-31 15:30:41 -06001357 if (o && o->config == OUTPUT_CONFIG_MODELINE) {
Ander Conselvan de Oliveira42c46462012-08-09 16:45:00 +03001358 configured = drm_output_add_mode(output, &o->crtc_mode);
1359 if (!configured)
Scott Moreau8f37e0b2012-07-31 15:30:41 -06001360 goto err_free;
Scott Moreau8f37e0b2012-07-31 15:30:41 -06001361 current = configured;
1362 }
1363
Wang Quanxianacb805a2012-07-30 18:09:46 -04001364 if (current == NULL && crtc_mode.clock != 0) {
Ander Conselvan de Oliveira42c46462012-08-09 16:45:00 +03001365 current = drm_output_add_mode(output, &crtc_mode);
1366 if (!current)
Kristian Høgsberg061c4252012-06-28 11:28:15 -04001367 goto err_free;
Kristian Høgsberg061c4252012-06-28 11:28:15 -04001368 }
1369
Scott Moreau8ab5d452012-07-30 19:51:08 -06001370 if (o && o->config == OUTPUT_CONFIG_CURRENT)
1371 configured = current;
1372
Wang Quanxianacb805a2012-07-30 18:09:46 -04001373 if (option_current_mode && current)
Ander Conselvan de Oliveira42c46462012-08-09 16:45:00 +03001374 output->base.current = &current->base;
Scott Moreau8ab5d452012-07-30 19:51:08 -06001375 else if (configured)
Ander Conselvan de Oliveira42c46462012-08-09 16:45:00 +03001376 output->base.current = &configured->base;
Wang Quanxianacb805a2012-07-30 18:09:46 -04001377 else if (preferred)
Ander Conselvan de Oliveira42c46462012-08-09 16:45:00 +03001378 output->base.current = &preferred->base;
Wang Quanxianacb805a2012-07-30 18:09:46 -04001379 else if (current)
Ander Conselvan de Oliveira42c46462012-08-09 16:45:00 +03001380 output->base.current = &current->base;
Wang Quanxianacb805a2012-07-30 18:09:46 -04001381
1382 if (output->base.current == NULL) {
1383 weston_log("no available modes for %s\n", output->name);
1384 goto err_free;
Kristian Høgsberg061c4252012-06-28 11:28:15 -04001385 }
Kristian Høgsberg8f0ce052011-06-21 11:16:58 -04001386
Wang Quanxianacb805a2012-07-30 18:09:46 -04001387 output->base.current->flags |= WL_OUTPUT_MODE_CURRENT;
1388
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -04001389 output->surface = gbm_surface_create(ec->gbm,
1390 output->base.current->width,
1391 output->base.current->height,
1392 GBM_FORMAT_XRGB8888,
1393 GBM_BO_USE_SCANOUT |
1394 GBM_BO_USE_RENDERING);
1395 if (!output->surface) {
Martin Minarik6d118362012-06-07 18:01:59 +02001396 weston_log("failed to create gbm surface\n");
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -04001397 goto err_free;
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001398 }
1399
John Kåre Alsaker94659272012-11-13 19:10:18 +01001400 weston_output_init(&output->base, &ec->base, x, y,
1401 connector->mmWidth, connector->mmHeight,
1402 o ? o->transform : WL_OUTPUT_TRANSFORM_NORMAL);
1403
John Kåre Alsaker779b52a2012-11-13 19:10:29 +01001404 if (gl_renderer_output_create(&output->base, output->surface) < 0)
John Kåre Alsaker94659272012-11-13 19:10:18 +01001405 goto err_output;
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001406
Kristian Høgsberg8e1f77f2012-05-03 11:39:35 -04001407 output->cursor_bo[0] =
1408 gbm_bo_create(ec->gbm, 64, 64, GBM_FORMAT_ARGB8888,
1409 GBM_BO_USE_CURSOR_64X64 | GBM_BO_USE_WRITE);
1410 output->cursor_bo[1] =
1411 gbm_bo_create(ec->gbm, 64, 64, GBM_FORMAT_ARGB8888,
1412 GBM_BO_USE_CURSOR_64X64 | GBM_BO_USE_WRITE);
Kristian Høgsberg1d1e0a52012-10-21 13:29:26 -04001413 if (output->cursor_bo[0] == NULL || output->cursor_bo[1] == NULL) {
1414 weston_log("cursor buffers unavailable, using gl cursors\n");
1415 ec->cursors_are_broken = 1;
1416 }
Kristian Høgsberg8e1f77f2012-05-03 11:39:35 -04001417
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +02001418 output->backlight = backlight_init(drm_device,
1419 connector->connector_type);
1420 if (output->backlight) {
1421 output->base.set_backlight = drm_set_backlight;
1422 output->base.backlight_current = drm_get_backlight(output);
1423 }
1424
Kristian Høgsberga4b7e202011-10-24 13:26:32 -04001425 wl_list_insert(ec->base.output_list.prev, &output->base.link);
1426
Alex Wubd3354b2012-04-17 17:20:49 +08001427 output->base.origin = output->base.current;
Kristian Høgsberg68c479a2012-01-25 23:32:28 -05001428 output->base.repaint = drm_output_repaint;
Matt Roper361d2ad2011-08-29 13:52:23 -07001429 output->base.destroy = drm_output_destroy;
Jesse Barnes58ef3792012-02-23 09:45:49 -05001430 output->base.assign_planes = drm_assign_planes;
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +02001431 output->base.set_dpms = drm_set_dpms;
Alex Wub7b8bda2012-04-17 17:20:48 +08001432 output->base.switch_mode = drm_output_switch_mode;
Benjamin Franzkeeefc36c2011-03-11 16:39:20 +01001433
Kristian Høgsberg65a11e12012-08-03 11:30:18 -04001434 weston_plane_init(&output->cursor_plane, 0, 0);
1435 weston_plane_init(&output->fb_plane, 0, 0);
1436
Kristian Høgsberg2f9ed712012-07-26 17:57:15 -04001437 weston_log("Output %s, (connector %d, crtc %d)\n",
1438 output->name, output->connector_id, output->crtc_id);
Kristian Høgsberg061c4252012-06-28 11:28:15 -04001439 wl_list_for_each(m, &output->base.mode_list, link)
1440 weston_log_continue(" mode %dx%d@%.1f%s%s%s\n",
1441 m->width, m->height, m->refresh / 1000.0,
1442 m->flags & WL_OUTPUT_MODE_PREFERRED ?
1443 ", preferred" : "",
1444 m->flags & WL_OUTPUT_MODE_CURRENT ?
1445 ", current" : "",
1446 connector->count_modes == 0 ?
1447 ", built-in" : "");
Kristian Høgsbergfc9c5e02012-06-08 16:45:33 -04001448
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001449 return 0;
David Herrmann0f0d54e2011-12-08 17:05:45 +01001450
John Kåre Alsaker94659272012-11-13 19:10:18 +01001451err_output:
1452 weston_output_destroy(&output->base);
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -04001453 gbm_surface_destroy(output->surface);
David Herrmann0f0d54e2011-12-08 17:05:45 +01001454err_free:
1455 wl_list_for_each_safe(drm_mode, next, &output->base.mode_list,
1456 base.link) {
1457 wl_list_remove(&drm_mode->base.link);
1458 free(drm_mode);
1459 }
1460
1461 drmModeFreeCrtc(output->original_crtc);
1462 ec->crtc_allocator &= ~(1 << output->crtc_id);
1463 ec->connector_allocator &= ~(1 << output->connector_id);
Kristian Høgsberg148ef012012-07-26 23:03:57 -04001464 free(output->name);
David Herrmann0f0d54e2011-12-08 17:05:45 +01001465 free(output);
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -04001466
David Herrmann0f0d54e2011-12-08 17:05:45 +01001467 return -1;
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001468}
1469
Jesse Barnes58ef3792012-02-23 09:45:49 -05001470static void
1471create_sprites(struct drm_compositor *ec)
1472{
1473 struct drm_sprite *sprite;
1474 drmModePlaneRes *plane_res;
1475 drmModePlane *plane;
Kristian Høgsberg875ab9e2012-03-30 11:52:39 -04001476 uint32_t i;
Jesse Barnes58ef3792012-02-23 09:45:49 -05001477
1478 plane_res = drmModeGetPlaneResources(ec->drm.fd);
1479 if (!plane_res) {
Martin Minarik6d118362012-06-07 18:01:59 +02001480 weston_log("failed to get plane resources: %s\n",
Jesse Barnes58ef3792012-02-23 09:45:49 -05001481 strerror(errno));
1482 return;
1483 }
1484
1485 for (i = 0; i < plane_res->count_planes; i++) {
1486 plane = drmModeGetPlane(ec->drm.fd, plane_res->planes[i]);
1487 if (!plane)
1488 continue;
1489
1490 sprite = malloc(sizeof(*sprite) + ((sizeof(uint32_t)) *
1491 plane->count_formats));
1492 if (!sprite) {
Martin Minarik6d118362012-06-07 18:01:59 +02001493 weston_log("%s: out of memory\n",
Jesse Barnes58ef3792012-02-23 09:45:49 -05001494 __func__);
1495 free(plane);
1496 continue;
1497 }
1498
1499 memset(sprite, 0, sizeof *sprite);
1500
1501 sprite->possible_crtcs = plane->possible_crtcs;
1502 sprite->plane_id = plane->plane_id;
Ander Conselvan de Oliveira8d360b42012-11-09 14:19:05 +02001503 sprite->current = NULL;
1504 sprite->next = NULL;
Jesse Barnes58ef3792012-02-23 09:45:49 -05001505 sprite->compositor = ec;
1506 sprite->count_formats = plane->count_formats;
1507 memcpy(sprite->formats, plane->formats,
Rob Clark8efbc8e2012-03-11 19:48:44 -05001508 plane->count_formats * sizeof(plane->formats[0]));
Jesse Barnes58ef3792012-02-23 09:45:49 -05001509 drmModeFreePlane(plane);
Kristian Høgsberg65a11e12012-08-03 11:30:18 -04001510 weston_plane_init(&sprite->plane, 0, 0);
Jesse Barnes58ef3792012-02-23 09:45:49 -05001511
1512 wl_list_insert(&ec->sprite_list, &sprite->link);
1513 }
1514
1515 free(plane_res->planes);
1516 free(plane_res);
1517}
1518
Kristian Høgsberg85fd3272012-02-23 21:45:32 -05001519static void
1520destroy_sprites(struct drm_compositor *compositor)
1521{
1522 struct drm_sprite *sprite, *next;
1523 struct drm_output *output;
1524
1525 output = container_of(compositor->base.output_list.next,
1526 struct drm_output, base.link);
1527
1528 wl_list_for_each_safe(sprite, next, &compositor->sprite_list, link) {
1529 drmModeSetPlane(compositor->drm.fd,
1530 sprite->plane_id,
1531 output->crtc_id, 0, 0,
1532 0, 0, 0, 0, 0, 0, 0, 0);
Ander Conselvan de Oliveira8d360b42012-11-09 14:19:05 +02001533 if (sprite->current)
1534 gbm_bo_destroy(sprite->current->bo);
1535 if (sprite->next)
1536 gbm_bo_destroy(sprite->next->bo);
Kristian Høgsberg65a11e12012-08-03 11:30:18 -04001537 weston_plane_release(&sprite->plane);
Kristian Høgsberg85fd3272012-02-23 21:45:32 -05001538 free(sprite);
1539 }
1540}
Jesse Barnes58ef3792012-02-23 09:45:49 -05001541
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001542static int
Kristian Høgsberg875ab9e2012-03-30 11:52:39 -04001543create_outputs(struct drm_compositor *ec, uint32_t option_connector,
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +02001544 struct udev_device *drm_device)
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001545{
1546 drmModeConnector *connector;
1547 drmModeRes *resources;
1548 int i;
Benjamin Franzkeeefc36c2011-03-11 16:39:20 +01001549 int x = 0, y = 0;
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001550
Benjamin Franzke2af7f102011-03-02 11:14:59 +01001551 resources = drmModeGetResources(ec->drm.fd);
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001552 if (!resources) {
Martin Minarik6d118362012-06-07 18:01:59 +02001553 weston_log("drmModeGetResources failed\n");
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001554 return -1;
1555 }
1556
Jesse Barnes58ef3792012-02-23 09:45:49 -05001557 ec->crtcs = calloc(resources->count_crtcs, sizeof(uint32_t));
Christopher Michaeleb866cd2012-03-07 14:55:21 -05001558 if (!ec->crtcs) {
1559 drmModeFreeResources(resources);
Jesse Barnes58ef3792012-02-23 09:45:49 -05001560 return -1;
Christopher Michaeleb866cd2012-03-07 14:55:21 -05001561 }
Jesse Barnes58ef3792012-02-23 09:45:49 -05001562
Rob Clark4339add2012-08-09 14:18:28 -05001563 ec->min_width = resources->min_width;
1564 ec->max_width = resources->max_width;
1565 ec->min_height = resources->min_height;
1566 ec->max_height = resources->max_height;
1567
Jesse Barnes58ef3792012-02-23 09:45:49 -05001568 ec->num_crtcs = resources->count_crtcs;
1569 memcpy(ec->crtcs, resources->crtcs, sizeof(uint32_t) * ec->num_crtcs);
1570
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001571 for (i = 0; i < resources->count_connectors; i++) {
Benjamin Franzke117483d2011-08-30 11:38:26 +02001572 connector = drmModeGetConnector(ec->drm.fd,
1573 resources->connectors[i]);
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001574 if (connector == NULL)
1575 continue;
1576
1577 if (connector->connection == DRM_MODE_CONNECTED &&
1578 (option_connector == 0 ||
Benjamin Franzke9eaee352011-08-02 13:03:54 +02001579 connector->connector_id == option_connector)) {
Benjamin Franzkeeefc36c2011-03-11 16:39:20 +01001580 if (create_output_for_connector(ec, resources,
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +02001581 connector, x, y,
1582 drm_device) < 0) {
Benjamin Franzke439d9862011-10-07 08:20:53 +02001583 drmModeFreeConnector(connector);
1584 continue;
1585 }
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001586
Benjamin Franzke9eaee352011-08-02 13:03:54 +02001587 x += container_of(ec->base.output_list.prev,
Kristian Høgsberg8334bc12012-01-03 10:29:47 -05001588 struct weston_output,
Scott Moreau1bad5db2012-08-18 01:04:05 -06001589 link)->width;
Benjamin Franzke9eaee352011-08-02 13:03:54 +02001590 }
Benjamin Franzkeeefc36c2011-03-11 16:39:20 +01001591
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001592 drmModeFreeConnector(connector);
1593 }
1594
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04001595 if (wl_list_empty(&ec->base.output_list)) {
Martin Minarik6d118362012-06-07 18:01:59 +02001596 weston_log("No currently active connector found.\n");
Christopher Michaeleb866cd2012-03-07 14:55:21 -05001597 drmModeFreeResources(resources);
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001598 return -1;
1599 }
1600
1601 drmModeFreeResources(resources);
1602
1603 return 0;
1604}
1605
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001606static void
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +02001607update_outputs(struct drm_compositor *ec, struct udev_device *drm_device)
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001608{
1609 drmModeConnector *connector;
1610 drmModeRes *resources;
1611 struct drm_output *output, *next;
1612 int x = 0, y = 0;
1613 int x_offset = 0, y_offset = 0;
1614 uint32_t connected = 0, disconnects = 0;
1615 int i;
1616
1617 resources = drmModeGetResources(ec->drm.fd);
1618 if (!resources) {
Martin Minarik6d118362012-06-07 18:01:59 +02001619 weston_log("drmModeGetResources failed\n");
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001620 return;
1621 }
1622
1623 /* collect new connects */
1624 for (i = 0; i < resources->count_connectors; i++) {
Benjamin Franzke117483d2011-08-30 11:38:26 +02001625 int connector_id = resources->connectors[i];
1626
1627 connector = drmModeGetConnector(ec->drm.fd, connector_id);
David Herrmann7551cff2011-12-08 17:05:43 +01001628 if (connector == NULL)
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001629 continue;
1630
David Herrmann7551cff2011-12-08 17:05:43 +01001631 if (connector->connection != DRM_MODE_CONNECTED) {
1632 drmModeFreeConnector(connector);
1633 continue;
1634 }
1635
Benjamin Franzke117483d2011-08-30 11:38:26 +02001636 connected |= (1 << connector_id);
1637
1638 if (!(ec->connector_allocator & (1 << connector_id))) {
Kristian Høgsberg8334bc12012-01-03 10:29:47 -05001639 struct weston_output *last =
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001640 container_of(ec->base.output_list.prev,
Kristian Høgsberg8334bc12012-01-03 10:29:47 -05001641 struct weston_output, link);
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001642
1643 /* XXX: not yet needed, we die with 0 outputs */
1644 if (!wl_list_empty(&ec->base.output_list))
Scott Moreau1bad5db2012-08-18 01:04:05 -06001645 x = last->x + last->width;
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001646 else
1647 x = 0;
1648 y = 0;
1649 create_output_for_connector(ec, resources,
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +02001650 connector, x, y,
1651 drm_device);
Martin Minarik6d118362012-06-07 18:01:59 +02001652 weston_log("connector %d connected\n", connector_id);
Benjamin Franzke117483d2011-08-30 11:38:26 +02001653
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001654 }
1655 drmModeFreeConnector(connector);
1656 }
1657 drmModeFreeResources(resources);
1658
1659 disconnects = ec->connector_allocator & ~connected;
1660 if (disconnects) {
1661 wl_list_for_each_safe(output, next, &ec->base.output_list,
1662 base.link) {
1663 if (x_offset != 0 || y_offset != 0) {
Kristian Høgsberg8334bc12012-01-03 10:29:47 -05001664 weston_output_move(&output->base,
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001665 output->base.x - x_offset,
1666 output->base.y - y_offset);
1667 }
1668
1669 if (disconnects & (1 << output->connector_id)) {
1670 disconnects &= ~(1 << output->connector_id);
Martin Minarik6d118362012-06-07 18:01:59 +02001671 weston_log("connector %d disconnected\n",
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001672 output->connector_id);
Scott Moreau1bad5db2012-08-18 01:04:05 -06001673 x_offset += output->base.width;
Benjamin Franzke48c4ea22011-08-30 11:44:56 +02001674 drm_output_destroy(&output->base);
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001675 }
1676 }
1677 }
1678
1679 /* FIXME: handle zero outputs, without terminating */
1680 if (ec->connector_allocator == 0)
1681 wl_display_terminate(ec->base.wl_display);
1682}
1683
1684static int
David Herrmannd7488c22012-03-11 20:05:21 +01001685udev_event_is_hotplug(struct drm_compositor *ec, struct udev_device *device)
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001686{
David Herrmannd7488c22012-03-11 20:05:21 +01001687 const char *sysnum;
David Herrmann6ac52db2012-03-11 20:05:22 +01001688 const char *val;
David Herrmannd7488c22012-03-11 20:05:21 +01001689
1690 sysnum = udev_device_get_sysnum(device);
1691 if (!sysnum || atoi(sysnum) != ec->drm.id)
1692 return 0;
Benjamin Franzke117483d2011-08-30 11:38:26 +02001693
David Herrmann6ac52db2012-03-11 20:05:22 +01001694 val = udev_device_get_property_value(device, "HOTPLUG");
1695 if (!val)
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001696 return 0;
1697
David Herrmann6ac52db2012-03-11 20:05:22 +01001698 return strcmp(val, "1") == 0;
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001699}
1700
Kristian Høgsbergb1868472011-04-22 12:27:57 -04001701static int
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001702udev_drm_event(int fd, uint32_t mask, void *data)
1703{
1704 struct drm_compositor *ec = data;
1705 struct udev_device *event;
1706
1707 event = udev_monitor_receive_device(ec->udev_monitor);
Benjamin Franzke117483d2011-08-30 11:38:26 +02001708
David Herrmannd7488c22012-03-11 20:05:21 +01001709 if (udev_event_is_hotplug(ec, event))
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +02001710 update_outputs(ec, event);
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001711
1712 udev_device_unref(event);
Kristian Høgsbergb1868472011-04-22 12:27:57 -04001713
1714 return 1;
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001715}
1716
Kristian Høgsbergcaa64422010-12-01 16:52:15 -05001717static void
Kristian Høgsberg7b884bc2012-07-31 14:32:01 -04001718drm_restore(struct weston_compositor *ec)
1719{
1720 struct drm_compositor *d = (struct drm_compositor *) ec;
1721
1722 if (weston_launcher_drm_set_master(&d->base, d->drm.fd, 0) < 0)
1723 weston_log("failed to drop master: %m\n");
1724 tty_reset(d->tty);
1725}
1726
Pekka Paalanen33156972012-08-03 13:30:30 -04001727static const char default_seat[] = "seat0";
1728
1729static void
1730device_added(struct udev_device *udev_device, struct drm_seat *master)
1731{
1732 struct weston_compositor *c;
Pekka Paalanen3eb47612012-08-06 14:57:08 +03001733 struct evdev_device *device;
Pekka Paalanen33156972012-08-03 13:30:30 -04001734 const char *devnode;
1735 const char *device_seat;
Rob Bradfordf4f58be2012-12-03 19:44:17 +00001736 const char *calibration_values;
Pekka Paalanen33156972012-08-03 13:30:30 -04001737 int fd;
1738
1739 device_seat = udev_device_get_property_value(udev_device, "ID_SEAT");
1740 if (!device_seat)
1741 device_seat = default_seat;
1742
1743 if (strcmp(device_seat, master->seat_id))
1744 return;
1745
1746 c = master->base.compositor;
1747 devnode = udev_device_get_devnode(udev_device);
1748
1749 /* Use non-blocking mode so that we can loop on read on
Pekka Paalanen3eb47612012-08-06 14:57:08 +03001750 * evdev_device_data() until all events on the fd are
Pekka Paalanen33156972012-08-03 13:30:30 -04001751 * read. mtdev_get() also expects this. */
1752 fd = weston_launcher_open(c, devnode, O_RDWR | O_NONBLOCK);
1753 if (fd < 0) {
1754 weston_log("opening input device '%s' failed.\n", devnode);
1755 return;
1756 }
1757
Pekka Paalanen3eb47612012-08-06 14:57:08 +03001758 device = evdev_device_create(&master->base, devnode, fd);
Pekka Paalanen33156972012-08-03 13:30:30 -04001759 if (!device) {
1760 close(fd);
1761 weston_log("not using input device '%s'.\n", devnode);
1762 return;
1763 }
1764
Rob Bradfordf4f58be2012-12-03 19:44:17 +00001765 calibration_values =
1766 udev_device_get_property_value(udev_device,
1767 "WL_CALIBRATION");
1768
1769 if (calibration_values && sscanf(calibration_values,
1770 "%f %f %f %f %f %f",
1771 &device->abs.calibration[0],
1772 &device->abs.calibration[1],
1773 &device->abs.calibration[2],
1774 &device->abs.calibration[3],
1775 &device->abs.calibration[4],
1776 &device->abs.calibration[5]) == 6) {
1777 device->abs.apply_calibration = 1;
1778 weston_log ("Applying calibration: %f %f %f %f %f %f\n",
1779 device->abs.calibration[0],
1780 device->abs.calibration[1],
1781 device->abs.calibration[2],
1782 device->abs.calibration[3],
1783 device->abs.calibration[4],
1784 device->abs.calibration[5]);
1785 }
1786
Pekka Paalanen33156972012-08-03 13:30:30 -04001787 wl_list_insert(master->devices_list.prev, &device->link);
1788}
1789
1790static void
1791evdev_add_devices(struct udev *udev, struct weston_seat *seat_base)
1792{
1793 struct drm_seat *seat = (struct drm_seat *) seat_base;
1794 struct udev_enumerate *e;
1795 struct udev_list_entry *entry;
1796 struct udev_device *device;
1797 const char *path, *sysname;
1798
1799 e = udev_enumerate_new(udev);
1800 udev_enumerate_add_match_subsystem(e, "input");
1801 udev_enumerate_scan_devices(e);
1802 udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
1803 path = udev_list_entry_get_name(entry);
1804 device = udev_device_new_from_syspath(udev, path);
1805
1806 sysname = udev_device_get_sysname(device);
1807 if (strncmp("event", sysname, 5) != 0) {
1808 udev_device_unref(device);
1809 continue;
1810 }
1811
1812 device_added(device, seat);
1813
1814 udev_device_unref(device);
1815 }
1816 udev_enumerate_unref(e);
1817
1818 evdev_notify_keyboard_focus(&seat->base, &seat->devices_list);
1819
1820 if (wl_list_empty(&seat->devices_list)) {
1821 weston_log(
1822 "warning: no input devices on entering Weston. "
1823 "Possible causes:\n"
1824 "\t- no permissions to read /dev/input/event*\n"
1825 "\t- seats misconfigured "
1826 "(Weston backend option 'seat', "
1827 "udev device property ID_SEAT)\n");
1828 }
1829}
1830
1831static int
1832evdev_udev_handler(int fd, uint32_t mask, void *data)
1833{
Pekka Paalanend2e69c22012-08-03 14:39:08 +03001834 struct drm_seat *seat = data;
Pekka Paalanen33156972012-08-03 13:30:30 -04001835 struct udev_device *udev_device;
Pekka Paalanen3eb47612012-08-06 14:57:08 +03001836 struct evdev_device *device, *next;
Pekka Paalanen33156972012-08-03 13:30:30 -04001837 const char *action;
1838 const char *devnode;
1839
Pekka Paalanend2e69c22012-08-03 14:39:08 +03001840 udev_device = udev_monitor_receive_device(seat->udev_monitor);
Pekka Paalanen33156972012-08-03 13:30:30 -04001841 if (!udev_device)
1842 return 1;
1843
1844 action = udev_device_get_action(udev_device);
Pekka Paalanend2e69c22012-08-03 14:39:08 +03001845 if (!action)
1846 goto out;
Pekka Paalanen33156972012-08-03 13:30:30 -04001847
Pekka Paalanend2e69c22012-08-03 14:39:08 +03001848 if (strncmp("event", udev_device_get_sysname(udev_device), 5) != 0)
1849 goto out;
1850
1851 if (!strcmp(action, "add")) {
1852 device_added(udev_device, seat);
Pekka Paalanen33156972012-08-03 13:30:30 -04001853 }
Pekka Paalanend2e69c22012-08-03 14:39:08 +03001854 else if (!strcmp(action, "remove")) {
1855 devnode = udev_device_get_devnode(udev_device);
1856 wl_list_for_each_safe(device, next, &seat->devices_list, link)
1857 if (!strcmp(device->devnode, devnode)) {
Pekka Paalanen42b3f6a2012-08-03 14:39:09 +03001858 weston_log("input device %s, %s removed\n",
1859 device->devname, device->devnode);
Pekka Paalanen3eb47612012-08-06 14:57:08 +03001860 evdev_device_destroy(device);
Pekka Paalanend2e69c22012-08-03 14:39:08 +03001861 break;
1862 }
1863 }
1864
1865out:
Pekka Paalanen33156972012-08-03 13:30:30 -04001866 udev_device_unref(udev_device);
1867
1868 return 0;
1869}
1870
1871static int
1872evdev_enable_udev_monitor(struct udev *udev, struct weston_seat *seat_base)
1873{
1874 struct drm_seat *master = (struct drm_seat *) seat_base;
1875 struct wl_event_loop *loop;
1876 struct weston_compositor *c = master->base.compositor;
1877 int fd;
1878
1879 master->udev_monitor = udev_monitor_new_from_netlink(udev, "udev");
1880 if (!master->udev_monitor) {
1881 weston_log("udev: failed to create the udev monitor\n");
1882 return 0;
1883 }
1884
1885 udev_monitor_filter_add_match_subsystem_devtype(master->udev_monitor,
1886 "input", NULL);
1887
1888 if (udev_monitor_enable_receiving(master->udev_monitor)) {
1889 weston_log("udev: failed to bind the udev monitor\n");
1890 udev_monitor_unref(master->udev_monitor);
1891 return 0;
1892 }
1893
1894 loop = wl_display_get_event_loop(c->wl_display);
1895 fd = udev_monitor_get_fd(master->udev_monitor);
1896 master->udev_monitor_source =
1897 wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE,
1898 evdev_udev_handler, master);
1899 if (!master->udev_monitor_source) {
1900 udev_monitor_unref(master->udev_monitor);
1901 return 0;
1902 }
1903
1904 return 1;
1905}
1906
1907static void
1908evdev_disable_udev_monitor(struct weston_seat *seat_base)
1909{
1910 struct drm_seat *seat = (struct drm_seat *) seat_base;
1911
1912 if (!seat->udev_monitor)
1913 return;
1914
1915 udev_monitor_unref(seat->udev_monitor);
1916 seat->udev_monitor = NULL;
1917 wl_event_source_remove(seat->udev_monitor_source);
1918 seat->udev_monitor_source = NULL;
1919}
1920
1921static void
1922drm_led_update(struct weston_seat *seat_base, enum weston_led leds)
1923{
1924 struct drm_seat *seat = (struct drm_seat *) seat_base;
Pekka Paalanen3eb47612012-08-06 14:57:08 +03001925 struct evdev_device *device;
Pekka Paalanen33156972012-08-03 13:30:30 -04001926
Pekka Paalanenb9d38f42012-08-06 14:57:07 +03001927 wl_list_for_each(device, &seat->devices_list, link)
1928 evdev_led_update(device, leds);
Pekka Paalanen33156972012-08-03 13:30:30 -04001929}
1930
1931static void
1932evdev_input_create(struct weston_compositor *c, struct udev *udev,
1933 const char *seat_id)
1934{
1935 struct drm_seat *seat;
1936
1937 seat = malloc(sizeof *seat);
1938 if (seat == NULL)
1939 return;
1940
1941 memset(seat, 0, sizeof *seat);
1942 weston_seat_init(&seat->base, c);
1943 seat->base.led_update = drm_led_update;
1944
1945 wl_list_init(&seat->devices_list);
1946 seat->seat_id = strdup(seat_id);
1947 if (!evdev_enable_udev_monitor(udev, &seat->base)) {
1948 free(seat->seat_id);
1949 free(seat);
1950 return;
1951 }
1952
1953 evdev_add_devices(udev, &seat->base);
Pekka Paalanen33156972012-08-03 13:30:30 -04001954}
1955
1956static void
1957evdev_remove_devices(struct weston_seat *seat_base)
1958{
1959 struct drm_seat *seat = (struct drm_seat *) seat_base;
Pekka Paalanen3eb47612012-08-06 14:57:08 +03001960 struct evdev_device *device, *next;
Pekka Paalanen33156972012-08-03 13:30:30 -04001961
1962 wl_list_for_each_safe(device, next, &seat->devices_list, link)
Pekka Paalanen3eb47612012-08-06 14:57:08 +03001963 evdev_device_destroy(device);
Pekka Paalanen33156972012-08-03 13:30:30 -04001964
Pekka Paalanend8583512012-08-03 14:39:11 +03001965 if (seat->base.seat.keyboard)
Kristian Høgsbergcb3eaae2012-08-10 09:50:11 -04001966 notify_keyboard_focus_out(&seat->base);
Pekka Paalanen33156972012-08-03 13:30:30 -04001967}
1968
1969static void
1970evdev_input_destroy(struct weston_seat *seat_base)
1971{
1972 struct drm_seat *seat = (struct drm_seat *) seat_base;
1973
1974 evdev_remove_devices(seat_base);
1975 evdev_disable_udev_monitor(&seat->base);
1976
1977 weston_seat_release(seat_base);
1978 free(seat->seat_id);
1979 free(seat);
1980}
1981
Kristian Høgsberg7b884bc2012-07-31 14:32:01 -04001982static void
Scott Moreauc50645c2012-07-31 22:29:56 -06001983drm_free_configured_output(struct drm_configured_output *output)
1984{
1985 free(output->name);
1986 free(output->mode);
1987 free(output);
1988}
1989
1990static void
Kristian Høgsberg8334bc12012-01-03 10:29:47 -05001991drm_destroy(struct weston_compositor *ec)
Kristian Høgsbergcaa64422010-12-01 16:52:15 -05001992{
1993 struct drm_compositor *d = (struct drm_compositor *) ec;
Daniel Stone37816df2012-05-16 18:45:18 +01001994 struct weston_seat *seat, *next;
Scott Moreau8ab5d452012-07-30 19:51:08 -06001995 struct drm_configured_output *o, *n;
Kristian Høgsbergcaa64422010-12-01 16:52:15 -05001996
Daniel Stone37816df2012-05-16 18:45:18 +01001997 wl_list_for_each_safe(seat, next, &ec->seat_list, link)
1998 evdev_input_destroy(seat);
Scott Moreau8ab5d452012-07-30 19:51:08 -06001999 wl_list_for_each_safe(o, n, &configured_output_list, link)
Scott Moreauc50645c2012-07-31 22:29:56 -06002000 drm_free_configured_output(o);
Jonas Ådahlc97af922012-03-28 22:36:09 +02002001
2002 wl_event_source_remove(d->udev_drm_source);
2003 wl_event_source_remove(d->drm_source);
2004
Kristian Høgsberg8334bc12012-01-03 10:29:47 -05002005 weston_compositor_shutdown(ec);
Jonas Ådahlc97af922012-03-28 22:36:09 +02002006
John Kåre Alsaker779b52a2012-11-13 19:10:29 +01002007 gl_renderer_destroy(ec);
Kristian Høgsberg3a0de882012-09-06 21:44:24 -04002008
Kristian Høgsberg85fd3272012-02-23 21:45:32 -05002009 destroy_sprites(d);
Ander Conselvan de Oliveira8d360b42012-11-09 14:19:05 +02002010 gbm_device_destroy(d->gbm);
Benjamin Franzkebfeda132012-01-30 14:04:04 +01002011 if (weston_launcher_drm_set_master(&d->base, d->drm.fd, 0) < 0)
Martin Minarik6d118362012-06-07 18:01:59 +02002012 weston_log("failed to drop master: %m\n");
Kristian Høgsberge4762a62011-01-14 14:59:13 -05002013 tty_destroy(d->tty);
Kristian Høgsbergcaa64422010-12-01 16:52:15 -05002014
Kristian Høgsberge4762a62011-01-14 14:59:13 -05002015 free(d);
Kristian Høgsbergcaa64422010-12-01 16:52:15 -05002016}
2017
Kristian Høgsberg9396fc52011-05-06 15:15:37 -04002018static void
Kristian Høgsberg835cd492012-01-18 11:48:46 -05002019drm_compositor_set_modes(struct drm_compositor *compositor)
2020{
2021 struct drm_output *output;
2022 struct drm_mode *drm_mode;
2023 int ret;
2024
2025 wl_list_for_each(output, &compositor->base.output_list, base.link) {
2026 drm_mode = (struct drm_mode *) output->base.current;
2027 ret = drmModeSetCrtc(compositor->drm.fd, output->crtc_id,
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +03002028 output->current->fb_id, 0, 0,
Kristian Høgsberg835cd492012-01-18 11:48:46 -05002029 &output->connector_id, 1,
2030 &drm_mode->mode_info);
2031 if (ret < 0) {
Martin Minarik6d118362012-06-07 18:01:59 +02002032 weston_log(
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -04002033 "failed to set mode %dx%d for output at %d,%d: %m\n",
Kristian Høgsberg835cd492012-01-18 11:48:46 -05002034 drm_mode->base.width, drm_mode->base.height,
2035 output->base.x, output->base.y);
2036 }
2037 }
2038}
2039
2040static void
Kristian Høgsberg8334bc12012-01-03 10:29:47 -05002041vt_func(struct weston_compositor *compositor, int event)
Kristian Høgsberg9396fc52011-05-06 15:15:37 -04002042{
2043 struct drm_compositor *ec = (struct drm_compositor *) compositor;
Daniel Stone37816df2012-05-16 18:45:18 +01002044 struct weston_seat *seat;
Kristian Høgsberg85fd3272012-02-23 21:45:32 -05002045 struct drm_sprite *sprite;
Kristian Høgsberga6edab32012-07-14 01:06:28 -04002046 struct drm_output *output;
Kristian Høgsberg9396fc52011-05-06 15:15:37 -04002047
2048 switch (event) {
2049 case TTY_ENTER_VT:
Pekka Paalanen81a13a32012-08-03 14:39:10 +03002050 weston_log("entering VT\n");
Kristian Høgsberg9396fc52011-05-06 15:15:37 -04002051 compositor->focus = 1;
Benjamin Franzkebfeda132012-01-30 14:04:04 +01002052 if (weston_launcher_drm_set_master(&ec->base, ec->drm.fd, 1)) {
Martin Minarik6d118362012-06-07 18:01:59 +02002053 weston_log("failed to set master: %m\n");
Kristian Høgsberga018fb02012-01-16 10:52:52 -05002054 wl_display_terminate(compositor->wl_display);
2055 }
Pekka Paalanenbce2d3f2011-12-02 13:07:27 +02002056 compositor->state = ec->prev_state;
Kristian Høgsberg835cd492012-01-18 11:48:46 -05002057 drm_compositor_set_modes(ec);
Kristian Høgsberg8334bc12012-01-03 10:29:47 -05002058 weston_compositor_damage_all(compositor);
Daniel Stone37816df2012-05-16 18:45:18 +01002059 wl_list_for_each(seat, &compositor->seat_list, link) {
2060 evdev_add_devices(ec->udev, seat);
2061 evdev_enable_udev_monitor(ec->udev, seat);
Benjamin Franzke78d3afe2012-04-09 18:14:58 +02002062 }
Kristian Høgsberg9396fc52011-05-06 15:15:37 -04002063 break;
2064 case TTY_LEAVE_VT:
Pekka Paalanen81a13a32012-08-03 14:39:10 +03002065 weston_log("leaving VT\n");
Daniel Stone37816df2012-05-16 18:45:18 +01002066 wl_list_for_each(seat, &compositor->seat_list, link) {
2067 evdev_disable_udev_monitor(seat);
2068 evdev_remove_devices(seat);
Kristian Høgsberg4014a6b2012-04-10 00:08:45 -04002069 }
2070
Kristian Høgsberg9396fc52011-05-06 15:15:37 -04002071 compositor->focus = 0;
Pekka Paalanenbce2d3f2011-12-02 13:07:27 +02002072 ec->prev_state = compositor->state;
Kristian Høgsberg8334bc12012-01-03 10:29:47 -05002073 compositor->state = WESTON_COMPOSITOR_SLEEPING;
Kristian Høgsbergd8e181b2011-05-06 15:38:28 -04002074
Kristian Høgsberg34f80ff2012-01-18 11:50:31 -05002075 /* If we have a repaint scheduled (either from a
2076 * pending pageflip or the idle handler), make sure we
2077 * cancel that so we don't try to pageflip when we're
2078 * vt switched away. The SLEEPING state will prevent
2079 * further attemps at repainting. When we switch
2080 * back, we schedule a repaint, which will process
2081 * pending frame callbacks. */
2082
Kristian Høgsberga6edab32012-07-14 01:06:28 -04002083 wl_list_for_each(output, &ec->base.output_list, base.link) {
2084 output->base.repaint_needed = 0;
2085 drmModeSetCursor(ec->drm.fd, output->crtc_id, 0, 0, 0);
Kristian Høgsberg34f80ff2012-01-18 11:50:31 -05002086 }
2087
Kristian Høgsberga6edab32012-07-14 01:06:28 -04002088 output = container_of(ec->base.output_list.next,
2089 struct drm_output, base.link);
Kristian Høgsberg85fd3272012-02-23 21:45:32 -05002090
2091 wl_list_for_each(sprite, &ec->sprite_list, link)
2092 drmModeSetPlane(ec->drm.fd,
2093 sprite->plane_id,
Kristian Høgsberga6edab32012-07-14 01:06:28 -04002094 output->crtc_id, 0, 0,
Kristian Høgsberg85fd3272012-02-23 21:45:32 -05002095 0, 0, 0, 0, 0, 0, 0, 0);
2096
Benjamin Franzkebfeda132012-01-30 14:04:04 +01002097 if (weston_launcher_drm_set_master(&ec->base, ec->drm.fd, 0) < 0)
Martin Minarik6d118362012-06-07 18:01:59 +02002098 weston_log("failed to drop master: %m\n");
Kristian Høgsberga018fb02012-01-16 10:52:52 -05002099
Kristian Høgsberg9396fc52011-05-06 15:15:37 -04002100 break;
2101 };
2102}
2103
Kristian Høgsberg5d1c0c52012-04-10 00:11:50 -04002104static void
Daniel Stone325fc2d2012-05-30 16:31:58 +01002105switch_vt_binding(struct wl_seat *seat, uint32_t time, uint32_t key, void *data)
Kristian Høgsberg5d1c0c52012-04-10 00:11:50 -04002106{
2107 struct drm_compositor *ec = data;
2108
Daniel Stone325fc2d2012-05-30 16:31:58 +01002109 tty_activate_vt(ec->tty, key - KEY_F1 + 1);
Kristian Høgsberg5d1c0c52012-04-10 00:11:50 -04002110}
2111
David Herrmann0af066f2012-10-29 19:21:16 +01002112/*
2113 * Find primary GPU
2114 * Some systems may have multiple DRM devices attached to a single seat. This
2115 * function loops over all devices and tries to find a PCI device with the
2116 * boot_vga sysfs attribute set to 1.
2117 * If no such device is found, the first DRM device reported by udev is used.
2118 */
2119static struct udev_device*
2120find_primary_gpu(struct drm_compositor *ec, const char *seat)
2121{
2122 struct udev_enumerate *e;
2123 struct udev_list_entry *entry;
2124 const char *path, *device_seat, *id;
2125 struct udev_device *device, *drm_device, *pci;
2126
2127 e = udev_enumerate_new(ec->udev);
2128 udev_enumerate_add_match_subsystem(e, "drm");
2129 udev_enumerate_add_match_sysname(e, "card[0-9]*");
2130
2131 udev_enumerate_scan_devices(e);
2132 drm_device = NULL;
2133 udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
2134 path = udev_list_entry_get_name(entry);
2135 device = udev_device_new_from_syspath(ec->udev, path);
2136 if (!device)
2137 continue;
2138 device_seat = udev_device_get_property_value(device, "ID_SEAT");
2139 if (!device_seat)
2140 device_seat = default_seat;
2141 if (strcmp(device_seat, seat)) {
2142 udev_device_unref(device);
2143 continue;
2144 }
2145
2146 pci = udev_device_get_parent_with_subsystem_devtype(device,
2147 "pci", NULL);
2148 if (pci) {
2149 id = udev_device_get_sysattr_value(pci, "boot_vga");
2150 if (id && !strcmp(id, "1")) {
2151 if (drm_device)
2152 udev_device_unref(drm_device);
2153 drm_device = device;
2154 break;
2155 }
2156 }
2157
2158 if (!drm_device)
2159 drm_device = device;
2160 else
2161 udev_device_unref(device);
2162 }
2163
2164 udev_enumerate_unref(e);
2165 return drm_device;
2166}
2167
Ander Conselvan de Oliveirada1c9082012-10-31 17:55:46 +02002168static void
Ander Conselvan de Oliveira7e918da2012-11-22 15:56:59 +02002169planes_binding(struct wl_seat *seat, uint32_t time, uint32_t key, void *data)
Ander Conselvan de Oliveirada1c9082012-10-31 17:55:46 +02002170{
2171 struct drm_compositor *c = data;
2172
Ander Conselvan de Oliveira7e918da2012-11-22 15:56:59 +02002173 switch (key) {
2174 case KEY_C:
2175 c->cursors_are_broken ^= 1;
2176 break;
2177 case KEY_V:
2178 c->sprites_are_broken ^= 1;
2179 break;
2180 case KEY_O:
2181 c->sprites_hidden ^= 1;
2182 break;
2183 default:
2184 break;
2185 }
Ander Conselvan de Oliveira180f42a2012-11-21 15:11:37 +02002186}
2187
Kristian Høgsberg8334bc12012-01-03 10:29:47 -05002188static struct weston_compositor *
Kristian Høgsberg8d51f142011-07-15 21:28:38 -04002189drm_compositor_create(struct wl_display *display,
Daniel Stonebaf43592012-06-01 11:11:10 -04002190 int connector, const char *seat, int tty,
Daniel Stonec1be8e52012-06-01 11:14:02 -04002191 int argc, char *argv[], const char *config_file)
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04002192{
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04002193 struct drm_compositor *ec;
David Herrmann0af066f2012-10-29 19:21:16 +01002194 struct udev_device *drm_device;
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04002195 struct wl_event_loop *loop;
Daniel Stonea96b93c2012-06-22 14:04:37 +01002196 struct weston_seat *weston_seat, *next;
David Herrmann0af066f2012-10-29 19:21:16 +01002197 const char *path;
Kristian Høgsberg5d1c0c52012-04-10 00:11:50 -04002198 uint32_t key;
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04002199
Kristian Høgsbergfc9c5e02012-06-08 16:45:33 -04002200 weston_log("initializing drm backend\n");
2201
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04002202 ec = malloc(sizeof *ec);
2203 if (ec == NULL)
2204 return NULL;
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04002205 memset(ec, 0, sizeof *ec);
Daniel Stone725c2c32012-06-22 14:04:36 +01002206
Kristian Høgsbergde37d672012-11-02 10:14:40 -04002207 /* KMS support for sprites is not complete yet, so disable the
2208 * functionality for now. */
2209 ec->sprites_are_broken = 1;
2210
Daniel Stone725c2c32012-06-22 14:04:36 +01002211 if (weston_compositor_init(&ec->base, display, argc, argv,
Daniel Stonea96b93c2012-06-22 14:04:37 +01002212 config_file) < 0) {
2213 weston_log("weston_compositor_init failed\n");
2214 goto err_base;
2215 }
Daniel Stone725c2c32012-06-22 14:04:36 +01002216
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04002217 ec->udev = udev_new();
2218 if (ec->udev == NULL) {
Martin Minarik6d118362012-06-07 18:01:59 +02002219 weston_log("failed to initialize udev context\n");
Daniel Stonea96b93c2012-06-22 14:04:37 +01002220 goto err_compositor;
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04002221 }
2222
Kristian Høgsbergc5b9ddb2012-01-15 14:29:09 -05002223 ec->base.wl_display = display;
2224 ec->tty = tty_create(&ec->base, vt_func, tty);
2225 if (!ec->tty) {
Martin Minarik6d118362012-06-07 18:01:59 +02002226 weston_log("failed to initialize tty\n");
Daniel Stonea96b93c2012-06-22 14:04:37 +01002227 goto err_udev;
Kristian Høgsbergc5b9ddb2012-01-15 14:29:09 -05002228 }
2229
David Herrmann0af066f2012-10-29 19:21:16 +01002230 drm_device = find_primary_gpu(ec, seat);
Kristian Høgsberg8d51f142011-07-15 21:28:38 -04002231 if (drm_device == NULL) {
Martin Minarik6d118362012-06-07 18:01:59 +02002232 weston_log("no drm device found\n");
David Herrmann0af066f2012-10-29 19:21:16 +01002233 goto err_tty;
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04002234 }
David Herrmann0af066f2012-10-29 19:21:16 +01002235 path = udev_device_get_syspath(drm_device);
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04002236
Kristian Høgsberg8d51f142011-07-15 21:28:38 -04002237 if (init_egl(ec, drm_device) < 0) {
Martin Minarik6d118362012-06-07 18:01:59 +02002238 weston_log("failed to initialize egl\n");
Daniel Stonea96b93c2012-06-22 14:04:37 +01002239 goto err_udev_dev;
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04002240 }
Kristian Høgsberg8525a502011-01-14 16:20:21 -05002241
2242 ec->base.destroy = drm_destroy;
Kristian Høgsberg7b884bc2012-07-31 14:32:01 -04002243 ec->base.restore = drm_restore;
Benjamin Franzke431da9a2011-04-20 11:02:58 +02002244
Kristian Høgsberg8525a502011-01-14 16:20:21 -05002245 ec->base.focus = 1;
2246
Kristian Høgsberg8334bc12012-01-03 10:29:47 -05002247 ec->prev_state = WESTON_COMPOSITOR_ACTIVE;
Pekka Paalanenbce2d3f2011-12-02 13:07:27 +02002248
Kristian Høgsberg5d1c0c52012-04-10 00:11:50 -04002249 for (key = KEY_F1; key < KEY_F9; key++)
Daniel Stone325fc2d2012-05-30 16:31:58 +01002250 weston_compositor_add_key_binding(&ec->base, key,
2251 MODIFIER_CTRL | MODIFIER_ALT,
2252 switch_vt_binding, ec);
Kristian Høgsberg5d1c0c52012-04-10 00:11:50 -04002253
Jesse Barnes58ef3792012-02-23 09:45:49 -05002254 wl_list_init(&ec->sprite_list);
2255 create_sprites(ec);
2256
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +02002257 if (create_outputs(ec, connector, drm_device) < 0) {
Martin Minarik6d118362012-06-07 18:01:59 +02002258 weston_log("failed to create output for %s\n", path);
Daniel Stonea96b93c2012-06-22 14:04:37 +01002259 goto err_sprite;
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04002260 }
2261
Benjamin Franzke02dee2c2011-10-07 08:27:26 +02002262 path = NULL;
2263
Tiago Vignattice03ec32011-12-19 01:14:03 +02002264 evdev_input_create(&ec->base, ec->udev, seat);
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04002265
2266 loop = wl_display_get_event_loop(ec->base.wl_display);
2267 ec->drm_source =
Benjamin Franzke2af7f102011-03-02 11:14:59 +01002268 wl_event_loop_add_fd(loop, ec->drm.fd,
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04002269 WL_EVENT_READABLE, on_drm_input, ec);
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04002270
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01002271 ec->udev_monitor = udev_monitor_new_from_netlink(ec->udev, "udev");
2272 if (ec->udev_monitor == NULL) {
Martin Minarik6d118362012-06-07 18:01:59 +02002273 weston_log("failed to intialize udev monitor\n");
Daniel Stonea96b93c2012-06-22 14:04:37 +01002274 goto err_drm_source;
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01002275 }
2276 udev_monitor_filter_add_match_subsystem_devtype(ec->udev_monitor,
2277 "drm", NULL);
2278 ec->udev_drm_source =
Benjamin Franzke117483d2011-08-30 11:38:26 +02002279 wl_event_loop_add_fd(loop,
2280 udev_monitor_get_fd(ec->udev_monitor),
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01002281 WL_EVENT_READABLE, udev_drm_event, ec);
2282
2283 if (udev_monitor_enable_receiving(ec->udev_monitor) < 0) {
Martin Minarik6d118362012-06-07 18:01:59 +02002284 weston_log("failed to enable udev-monitor receiving\n");
Daniel Stonea96b93c2012-06-22 14:04:37 +01002285 goto err_udev_monitor;
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01002286 }
2287
Daniel Stonea96b93c2012-06-22 14:04:37 +01002288 udev_device_unref(drm_device);
Daniel Stonea96b93c2012-06-22 14:04:37 +01002289
Ander Conselvan de Oliveirac509d2b2012-11-08 17:20:45 +02002290 weston_compositor_add_debug_binding(&ec->base, KEY_O,
Ander Conselvan de Oliveira7e918da2012-11-22 15:56:59 +02002291 planes_binding, ec);
Ander Conselvan de Oliveira180f42a2012-11-21 15:11:37 +02002292 weston_compositor_add_debug_binding(&ec->base, KEY_C,
Ander Conselvan de Oliveira7e918da2012-11-22 15:56:59 +02002293 planes_binding, ec);
2294 weston_compositor_add_debug_binding(&ec->base, KEY_V,
2295 planes_binding, ec);
Ander Conselvan de Oliveirada1c9082012-10-31 17:55:46 +02002296
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04002297 return &ec->base;
Daniel Stonea96b93c2012-06-22 14:04:37 +01002298
2299err_udev_monitor:
2300 wl_event_source_remove(ec->udev_drm_source);
2301 udev_monitor_unref(ec->udev_monitor);
2302err_drm_source:
2303 wl_event_source_remove(ec->drm_source);
2304 wl_list_for_each_safe(weston_seat, next, &ec->base.seat_list, link)
2305 evdev_input_destroy(weston_seat);
Kristian Høgsberg2bc5e8e2012-09-06 20:51:00 -04002306err_sprite:
John Kåre Alsaker779b52a2012-11-13 19:10:29 +01002307 gl_renderer_destroy(&ec->base);
John Kåre Alsaker1b7ad162012-11-13 19:10:19 +01002308 gbm_device_destroy(ec->gbm);
Kristian Høgsberg2bc5e8e2012-09-06 20:51:00 -04002309 destroy_sprites(ec);
Daniel Stonea96b93c2012-06-22 14:04:37 +01002310err_udev_dev:
2311 udev_device_unref(drm_device);
David Herrmann0af066f2012-10-29 19:21:16 +01002312err_tty:
Daniel Stonea96b93c2012-06-22 14:04:37 +01002313 tty_destroy(ec->tty);
2314err_udev:
2315 udev_unref(ec->udev);
2316err_compositor:
2317 weston_compositor_shutdown(&ec->base);
2318err_base:
2319 free(ec);
2320 return NULL;
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04002321}
Kristian Høgsberg1c562182011-05-02 22:09:20 -04002322
Scott Moreau8f37e0b2012-07-31 15:30:41 -06002323static int
2324set_sync_flags(drmModeModeInfo *mode, char *hsync, char *vsync)
2325{
2326 mode->flags = 0;
2327
2328 if (strcmp(hsync, "+hsync") == 0)
2329 mode->flags |= DRM_MODE_FLAG_PHSYNC;
2330 else if (strcmp(hsync, "-hsync") == 0)
2331 mode->flags |= DRM_MODE_FLAG_NHSYNC;
2332 else
2333 return -1;
2334
2335 if (strcmp(vsync, "+vsync") == 0)
2336 mode->flags |= DRM_MODE_FLAG_PVSYNC;
2337 else if (strcmp(vsync, "-vsync") == 0)
2338 mode->flags |= DRM_MODE_FLAG_NVSYNC;
2339 else
2340 return -1;
2341
2342 return 0;
2343}
2344
2345static int
2346check_for_modeline(struct drm_configured_output *output)
2347{
2348 drmModeModeInfo mode;
2349 char hsync[16];
2350 char vsync[16];
2351 char mode_name[16];
2352 float fclock;
2353
2354 mode.type = DRM_MODE_TYPE_USERDEF;
2355 mode.hskew = 0;
2356 mode.vscan = 0;
2357 mode.vrefresh = 0;
2358
2359 if (sscanf(output_mode, "%f %hd %hd %hd %hd %hd %hd %hd %hd %s %s",
2360 &fclock, &mode.hdisplay,
2361 &mode.hsync_start,
2362 &mode.hsync_end, &mode.htotal,
2363 &mode.vdisplay,
2364 &mode.vsync_start,
2365 &mode.vsync_end, &mode.vtotal,
2366 hsync, vsync) == 11) {
2367 if (set_sync_flags(&mode, hsync, vsync))
2368 return -1;
2369
2370 sprintf(mode_name, "%dx%d", mode.hdisplay, mode.vdisplay);
2371 strcpy(mode.name, mode_name);
2372
2373 mode.clock = fclock * 1000;
2374 } else
2375 return -1;
2376
2377 output->crtc_mode = mode;
2378
2379 return 0;
2380}
2381
Scott Moreau8ab5d452012-07-30 19:51:08 -06002382static void
Scott Moreau1bad5db2012-08-18 01:04:05 -06002383drm_output_set_transform(struct drm_configured_output *output)
2384{
2385 if (!output_transform) {
2386 output->transform = WL_OUTPUT_TRANSFORM_NORMAL;
2387 return;
2388 }
2389
2390 if (!strcmp(output_transform, "normal"))
2391 output->transform = WL_OUTPUT_TRANSFORM_NORMAL;
2392 else if (!strcmp(output_transform, "90"))
2393 output->transform = WL_OUTPUT_TRANSFORM_90;
2394 else if (!strcmp(output_transform, "180"))
2395 output->transform = WL_OUTPUT_TRANSFORM_180;
2396 else if (!strcmp(output_transform, "270"))
2397 output->transform = WL_OUTPUT_TRANSFORM_270;
2398 else if (!strcmp(output_transform, "flipped"))
2399 output->transform = WL_OUTPUT_TRANSFORM_FLIPPED;
2400 else if (!strcmp(output_transform, "flipped-90"))
2401 output->transform = WL_OUTPUT_TRANSFORM_FLIPPED_90;
2402 else if (!strcmp(output_transform, "flipped-180"))
2403 output->transform = WL_OUTPUT_TRANSFORM_FLIPPED_180;
2404 else if (!strcmp(output_transform, "flipped-270"))
2405 output->transform = WL_OUTPUT_TRANSFORM_FLIPPED_270;
2406 else {
2407 weston_log("Invalid transform \"%s\" for output %s\n",
2408 output_transform, output_name);
2409 output->transform = WL_OUTPUT_TRANSFORM_NORMAL;
2410 }
2411
2412 free(output_transform);
2413 output_transform = NULL;
2414}
2415
2416static void
Scott Moreau8ab5d452012-07-30 19:51:08 -06002417output_section_done(void *data)
2418{
2419 struct drm_configured_output *output;
2420
2421 output = malloc(sizeof *output);
2422
Scott Moreau1bad5db2012-08-18 01:04:05 -06002423 if (!output || !output_name || (output_name[0] == 'X') ||
2424 (!output_mode && !output_transform)) {
Kristian Høgsberg8ff69152012-07-31 22:18:28 -04002425 free(output_name);
Kristian Høgsberg8ff69152012-07-31 22:18:28 -04002426 free(output_mode);
Scott Moreau1bad5db2012-08-18 01:04:05 -06002427 free(output_transform);
Rob Bradford6b6795f2012-10-09 18:44:35 +01002428 free(output);
Scott Moreau1bad5db2012-08-18 01:04:05 -06002429 output_name = NULL;
Kristian Høgsberg8ff69152012-07-31 22:18:28 -04002430 output_mode = NULL;
Scott Moreau1bad5db2012-08-18 01:04:05 -06002431 output_transform = NULL;
Scott Moreau8ab5d452012-07-30 19:51:08 -06002432 return;
Kristian Høgsberg8ff69152012-07-31 22:18:28 -04002433 }
Scott Moreau8ab5d452012-07-30 19:51:08 -06002434
2435 output->config = OUTPUT_CONFIG_INVALID;
2436 output->name = output_name;
2437 output->mode = output_mode;
2438
Scott Moreau1bad5db2012-08-18 01:04:05 -06002439 if (output_mode) {
2440 if (strcmp(output_mode, "off") == 0)
2441 output->config = OUTPUT_CONFIG_OFF;
2442 else if (strcmp(output_mode, "preferred") == 0)
2443 output->config = OUTPUT_CONFIG_PREFERRED;
2444 else if (strcmp(output_mode, "current") == 0)
2445 output->config = OUTPUT_CONFIG_CURRENT;
2446 else if (sscanf(output_mode, "%dx%d",
2447 &output->width, &output->height) == 2)
2448 output->config = OUTPUT_CONFIG_MODE;
2449 else if (check_for_modeline(output) == 0)
2450 output->config = OUTPUT_CONFIG_MODELINE;
Scott Moreau8ab5d452012-07-30 19:51:08 -06002451
Scott Moreau1bad5db2012-08-18 01:04:05 -06002452 if (output->config == OUTPUT_CONFIG_INVALID)
2453 weston_log("Invalid mode \"%s\" for output %s\n",
2454 output_mode, output_name);
2455 output_mode = NULL;
Scott Moreau8ab5d452012-07-30 19:51:08 -06002456 }
Scott Moreau1bad5db2012-08-18 01:04:05 -06002457
2458 drm_output_set_transform(output);
2459
2460 wl_list_insert(&configured_output_list, &output->link);
2461
2462 if (output_transform)
2463 free(output_transform);
2464 output_transform = NULL;
Scott Moreau8ab5d452012-07-30 19:51:08 -06002465}
2466
Kristian Høgsberg8334bc12012-01-03 10:29:47 -05002467WL_EXPORT struct weston_compositor *
Daniel Stonec1be8e52012-06-01 11:14:02 -04002468backend_init(struct wl_display *display, int argc, char *argv[],
2469 const char *config_file)
Kristian Høgsberg1c562182011-05-02 22:09:20 -04002470{
Kristian Høgsbergbcacef12012-03-11 21:05:57 -04002471 int connector = 0, tty = 0;
2472 const char *seat = default_seat;
Kristian Høgsberg1c562182011-05-02 22:09:20 -04002473
Kristian Høgsbergbcacef12012-03-11 21:05:57 -04002474 const struct weston_option drm_options[] = {
2475 { WESTON_OPTION_INTEGER, "connector", 0, &connector },
2476 { WESTON_OPTION_STRING, "seat", 0, &seat },
2477 { WESTON_OPTION_INTEGER, "tty", 0, &tty },
Kristian Høgsberg061c4252012-06-28 11:28:15 -04002478 { WESTON_OPTION_BOOLEAN, "current-mode", 0, &option_current_mode },
Kristian Høgsbergbcacef12012-03-11 21:05:57 -04002479 };
Benjamin Franzke117483d2011-08-30 11:38:26 +02002480
Kristian Høgsbergbcacef12012-03-11 21:05:57 -04002481 parse_options(drm_options, ARRAY_LENGTH(drm_options), argc, argv);
Kristian Høgsberg1c562182011-05-02 22:09:20 -04002482
Scott Moreau8ab5d452012-07-30 19:51:08 -06002483 wl_list_init(&configured_output_list);
2484
2485 const struct config_key drm_config_keys[] = {
2486 { "name", CONFIG_KEY_STRING, &output_name },
2487 { "mode", CONFIG_KEY_STRING, &output_mode },
Scott Moreau1bad5db2012-08-18 01:04:05 -06002488 { "transform", CONFIG_KEY_STRING, &output_transform },
Scott Moreau8ab5d452012-07-30 19:51:08 -06002489 };
2490
2491 const struct config_section config_section[] = {
2492 { "output", drm_config_keys,
2493 ARRAY_LENGTH(drm_config_keys), output_section_done },
2494 };
2495
2496 parse_config_file(config_file, config_section,
2497 ARRAY_LENGTH(config_section), NULL);
2498
Daniel Stonec1be8e52012-06-01 11:14:02 -04002499 return drm_compositor_create(display, connector, seat, tty, argc, argv,
2500 config_file);
Kristian Høgsberg1c562182011-05-02 22:09:20 -04002501}