blob: fac0f56b02c5d79f812de73a2fcf358505726ad5 [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"
Tiago Vignattice03ec32011-12-19 01:14:03 +020043#include "evdev.h"
Benjamin Franzkebfeda132012-01-30 14:04:04 +010044#include "launcher-util.h"
Kristian Høgsbergfc783d42010-06-11 12:56:24 -040045
Kristian Høgsberg061c4252012-06-28 11:28:15 -040046static int option_current_mode = 0;
Scott Moreau8ab5d452012-07-30 19:51:08 -060047static char *output_name;
48static char *output_mode;
Scott Moreau1bad5db2012-08-18 01:04:05 -060049static char *output_transform;
Scott Moreau8ab5d452012-07-30 19:51:08 -060050static struct wl_list configured_output_list;
51
52enum output_config {
53 OUTPUT_CONFIG_INVALID = 0,
54 OUTPUT_CONFIG_OFF,
55 OUTPUT_CONFIG_PREFERRED,
56 OUTPUT_CONFIG_CURRENT,
Scott Moreau8f37e0b2012-07-31 15:30:41 -060057 OUTPUT_CONFIG_MODE,
58 OUTPUT_CONFIG_MODELINE
Scott Moreau8ab5d452012-07-30 19:51:08 -060059};
60
61struct drm_configured_output {
62 char *name;
63 char *mode;
Scott Moreau1bad5db2012-08-18 01:04:05 -060064 uint32_t transform;
Scott Moreau8ab5d452012-07-30 19:51:08 -060065 int32_t width, height;
Scott Moreau8f37e0b2012-07-31 15:30:41 -060066 drmModeModeInfo crtc_mode;
Scott Moreau8ab5d452012-07-30 19:51:08 -060067 enum output_config config;
68 struct wl_list link;
69};
Kristian Høgsberg061c4252012-06-28 11:28:15 -040070
Kristian Høgsbergce5325d2010-06-14 11:54:00 -040071struct drm_compositor {
Kristian Høgsberg8334bc12012-01-03 10:29:47 -050072 struct weston_compositor base;
Kristian Høgsbergce5325d2010-06-14 11:54:00 -040073
74 struct udev *udev;
75 struct wl_event_source *drm_source;
Kristian Høgsbergce5325d2010-06-14 11:54:00 -040076
Benjamin Franzke9c26ff32011-03-15 15:08:41 +010077 struct udev_monitor *udev_monitor;
78 struct wl_event_source *udev_drm_source;
79
Benjamin Franzke2af7f102011-03-02 11:14:59 +010080 struct {
David Herrmannd7488c22012-03-11 20:05:21 +010081 int id;
Benjamin Franzke2af7f102011-03-02 11:14:59 +010082 int fd;
83 } drm;
Benjamin Franzke060cf802011-04-30 09:32:11 +020084 struct gbm_device *gbm;
Jesse Barnes58ef3792012-02-23 09:45:49 -050085 uint32_t *crtcs;
86 int num_crtcs;
Marty Jack13d9db22011-02-09 19:01:42 -050087 uint32_t crtc_allocator;
Benjamin Franzke9c26ff32011-03-15 15:08:41 +010088 uint32_t connector_allocator;
Kristian Høgsberge4762a62011-01-14 14:59:13 -050089 struct tty *tty;
Pekka Paalanenbce2d3f2011-12-02 13:07:27 +020090
Rob Clark4339add2012-08-09 14:18:28 -050091 /* we need these parameters in order to not fail drmModeAddFB2()
92 * due to out of bounds dimensions, and then mistakenly set
93 * sprites_are_broken:
94 */
Kristian Høgsberg8a015802012-08-09 17:19:23 -040095 int32_t min_width, max_width;
96 int32_t min_height, max_height;
Rob Clark4339add2012-08-09 14:18:28 -050097
Jesse Barnes58ef3792012-02-23 09:45:49 -050098 struct wl_list sprite_list;
Kristian Høgsberg65bec242012-03-05 19:57:35 -050099 int sprites_are_broken;
Ander Conselvan de Oliveirada1c9082012-10-31 17:55:46 +0200100 int sprites_hidden;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500101
Rob Clarkab5b1e32012-08-09 13:24:45 -0500102 int cursors_are_broken;
103
Pekka Paalanenbce2d3f2011-12-02 13:07:27 +0200104 uint32_t prev_state;
Kristian Høgsbergce5325d2010-06-14 11:54:00 -0400105};
106
Kristian Høgsberg8f0ce052011-06-21 11:16:58 -0400107struct drm_mode {
Kristian Høgsberg8334bc12012-01-03 10:29:47 -0500108 struct weston_mode base;
Kristian Høgsberg8f0ce052011-06-21 11:16:58 -0400109 drmModeModeInfo mode_info;
110};
111
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300112struct drm_output;
113
114struct drm_fb {
115 struct gbm_bo *bo;
116 struct drm_output *output;
117 uint32_t fb_id;
118 int is_client_buffer;
119 struct wl_buffer *buffer;
120 struct wl_listener buffer_destroy_listener;
121};
122
Kristian Høgsbergce5325d2010-06-14 11:54:00 -0400123struct drm_output {
Kristian Høgsberg8334bc12012-01-03 10:29:47 -0500124 struct weston_output base;
Kristian Høgsbergce5325d2010-06-14 11:54:00 -0400125
Kristian Høgsberg2f9ed712012-07-26 17:57:15 -0400126 char *name;
Kristian Høgsbergce5325d2010-06-14 11:54:00 -0400127 uint32_t crtc_id;
Rob Clark5ca1a472012-08-08 20:27:37 -0500128 int pipe;
Kristian Høgsbergce5325d2010-06-14 11:54:00 -0400129 uint32_t connector_id;
Matt Roper361d2ad2011-08-29 13:52:23 -0700130 drmModeCrtcPtr original_crtc;
Benjamin Franzke1178a3c2011-04-10 16:49:52 +0200131
Ander Conselvan de Oliveiraa7326962012-06-26 17:09:13 +0300132 int vblank_pending;
133 int page_flip_pending;
134
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -0400135 struct gbm_surface *surface;
Kristian Høgsberg8e1f77f2012-05-03 11:39:35 -0400136 struct gbm_bo *cursor_bo[2];
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400137 struct weston_plane cursor_plane;
138 struct weston_plane fb_plane;
Kristian Høgsberg5626d342012-08-03 11:50:05 -0400139 struct weston_surface *cursor_surface;
140 int current_cursor;
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300141 struct drm_fb *current, *next;
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +0200142 struct backlight *backlight;
Kristian Høgsbergce5325d2010-06-14 11:54:00 -0400143};
144
Jesse Barnes58ef3792012-02-23 09:45:49 -0500145/*
146 * An output has a primary display plane plus zero or more sprites for
147 * blending display contents.
148 */
149struct drm_sprite {
150 struct wl_list link;
151
152 uint32_t fb_id;
153 uint32_t pending_fb_id;
154 struct weston_surface *surface;
155 struct weston_surface *pending_surface;
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400156 struct weston_plane plane;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500157
Ander Conselvan de Oliveiraa7326962012-06-26 17:09:13 +0300158 struct drm_output *output;
159
Jesse Barnes58ef3792012-02-23 09:45:49 -0500160 struct drm_compositor *compositor;
161
162 struct wl_listener destroy_listener;
163 struct wl_listener pending_destroy_listener;
164
165 uint32_t possible_crtcs;
166 uint32_t plane_id;
167 uint32_t count_formats;
168
169 int32_t src_x, src_y;
170 uint32_t src_w, src_h;
171 uint32_t dest_x, dest_y;
172 uint32_t dest_w, dest_h;
173
174 uint32_t formats[];
175};
176
Pekka Paalanen33156972012-08-03 13:30:30 -0400177struct drm_seat {
178 struct weston_seat base;
179 struct wl_list devices_list;
180 struct udev_monitor *udev_monitor;
181 struct wl_event_source *udev_monitor_source;
182 char *seat_id;
183};
184
Kristian Høgsberg5626d342012-08-03 11:50:05 -0400185static void
186drm_output_set_cursor(struct drm_output *output);
187static void
188drm_disable_unused_sprites(struct weston_output *output_base);
189
Jesse Barnes58ef3792012-02-23 09:45:49 -0500190static int
Jesse Barnes58ef3792012-02-23 09:45:49 -0500191drm_sprite_crtc_supported(struct weston_output *output_base, uint32_t supported)
192{
193 struct weston_compositor *ec = output_base->compositor;
194 struct drm_compositor *c =(struct drm_compositor *) ec;
195 struct drm_output *output = (struct drm_output *) output_base;
196 int crtc;
197
198 for (crtc = 0; crtc < c->num_crtcs; crtc++) {
199 if (c->crtcs[crtc] != output->crtc_id)
200 continue;
201
202 if (supported & (1 << crtc))
203 return -1;
204 }
205
206 return 0;
207}
208
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300209static void
210drm_fb_destroy_callback(struct gbm_bo *bo, void *data)
211{
212 struct drm_fb *fb = data;
213 struct gbm_device *gbm = gbm_bo_get_device(bo);
214
215 if (fb->fb_id)
216 drmModeRmFB(gbm_device_get_fd(gbm), fb->fb_id);
217
218 if (fb->buffer) {
219 weston_buffer_post_release(fb->buffer);
220 wl_list_remove(&fb->buffer_destroy_listener.link);
221 }
222
223 free(data);
224}
225
226static struct drm_fb *
227drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_output *output)
228{
229 struct drm_fb *fb = gbm_bo_get_user_data(bo);
230 struct drm_compositor *compositor =
231 (struct drm_compositor *) output->base.compositor;
232 uint32_t width, height, stride, handle;
233 int ret;
234
235 if (fb)
236 return fb;
237
238 fb = malloc(sizeof *fb);
239
240 fb->bo = bo;
241 fb->output = output;
242 fb->is_client_buffer = 0;
243 fb->buffer = NULL;
244
245 width = gbm_bo_get_width(bo);
246 height = gbm_bo_get_height(bo);
Kristian Høgsberg270a7cb2012-07-16 16:44:16 -0400247 stride = gbm_bo_get_stride(bo);
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300248 handle = gbm_bo_get_handle(bo).u32;
249
250 ret = drmModeAddFB(compositor->drm.fd, width, height, 24, 32,
251 stride, handle, &fb->fb_id);
252 if (ret) {
Martin Minarik6d118362012-06-07 18:01:59 +0200253 weston_log("failed to create kms fb: %m\n");
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300254 free(fb);
255 return NULL;
256 }
257
258 gbm_bo_set_user_data(bo, fb, drm_fb_destroy_callback);
259
260 return fb;
261}
262
263static void
264fb_handle_buffer_destroy(struct wl_listener *listener, void *data)
265{
266 struct drm_fb *fb = container_of(listener, struct drm_fb,
267 buffer_destroy_listener);
268
269 fb->buffer = NULL;
270
271 if (fb == fb->output->next ||
272 (fb == fb->output->current && !fb->output->next))
Kristian Høgsberg49952d12012-06-20 00:35:59 -0400273 weston_output_schedule_repaint(&fb->output->base);
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300274}
275
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400276static struct weston_plane *
Kristian Høgsberg6143f7d2012-07-14 00:31:32 -0400277drm_output_prepare_scanout_surface(struct weston_output *_output,
278 struct weston_surface *es)
Kristian Høgsberg5f5e42e2012-01-25 23:59:42 -0500279{
Kristian Høgsberg6143f7d2012-07-14 00:31:32 -0400280 struct drm_output *output = (struct drm_output *) _output;
Kristian Høgsberg5f5e42e2012-01-25 23:59:42 -0500281 struct drm_compositor *c =
282 (struct drm_compositor *) output->base.compositor;
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300283 struct gbm_bo *bo;
Kristian Høgsberg5f5e42e2012-01-25 23:59:42 -0500284
Kristian Høgsberg101cb652012-02-17 10:45:16 -0500285 if (es->geometry.x != output->base.x ||
Pekka Paalanenba3cf952012-01-25 16:22:05 +0200286 es->geometry.y != output->base.y ||
Pekka Paalanen60921e52012-01-25 15:55:43 +0200287 es->geometry.width != output->base.current->width ||
288 es->geometry.height != output->base.current->height ||
Pekka Paalanenf1f5b362012-01-25 14:33:33 +0200289 es->transform.enabled ||
Kristian Høgsberg2763a2e2012-07-13 22:54:43 -0400290 es->buffer == NULL)
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400291 return NULL;
Kristian Høgsberg5f5e42e2012-01-25 23:59:42 -0500292
Kristian Høgsberg2763a2e2012-07-13 22:54:43 -0400293 bo = gbm_bo_import(c->gbm, GBM_BO_IMPORT_WL_BUFFER,
294 es->buffer, GBM_BO_USE_SCANOUT);
Kristian Høgsberg5f5e42e2012-01-25 23:59:42 -0500295
Rob Bradford9b101872012-09-14 23:25:41 +0100296 /* Unable to use the buffer for scanout */
297 if (!bo)
298 return NULL;
299
Ander Conselvan de Oliveiraa64b15d2012-05-02 16:42:22 +0300300 /* Need to verify output->region contained in surface opaque
301 * region. Or maybe just that format doesn't have alpha.
302 * For now, scanout only if format is XRGB8888. */
303 if (gbm_bo_get_format(bo) != GBM_FORMAT_XRGB8888) {
304 gbm_bo_destroy(bo);
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400305 return NULL;
Ander Conselvan de Oliveiraa64b15d2012-05-02 16:42:22 +0300306 }
307
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300308 output->next = drm_fb_get_from_bo(bo, output);
309 if (!output->next) {
310 gbm_bo_destroy(bo);
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400311 return NULL;
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300312 }
Kristian Høgsberg5f5e42e2012-01-25 23:59:42 -0500313
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300314 output->next->is_client_buffer = 1;
315 output->next->buffer = es->buffer;
316 output->next->buffer->busy_count++;
317 output->next->buffer_destroy_listener.notify = fb_handle_buffer_destroy;
318
319 wl_signal_add(&output->next->buffer->resource.destroy_signal,
320 &output->next->buffer_destroy_listener);
Kristian Høgsberg5f5e42e2012-01-25 23:59:42 -0500321
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400322 return &output->fb_plane;
Kristian Høgsberg5f5e42e2012-01-25 23:59:42 -0500323}
324
Kristian Høgsberg06cf6b02012-01-25 23:47:45 -0500325static void
Kristian Høgsbergd7c17262012-09-05 21:54:15 -0400326drm_output_render(struct drm_output *output, pixman_region32_t *damage)
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -0400327{
Kristian Høgsbergfa1be022012-09-05 22:49:55 -0400328 struct weston_compositor *ec = output->base.compositor;
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300329 struct gbm_bo *bo;
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -0400330
Kristian Høgsbergfa1be022012-09-05 22:49:55 -0400331 ec->renderer->repaint_output(&output->base, damage);
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -0400332
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300333 bo = gbm_surface_lock_front_buffer(output->surface);
334 if (!bo) {
Martin Minarik6d118362012-06-07 18:01:59 +0200335 weston_log("failed to lock front buffer: %m\n");
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -0400336 return;
337 }
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300338
339 output->next = drm_fb_get_from_bo(bo, output);
340 if (!output->next) {
Martin Minarik6d118362012-06-07 18:01:59 +0200341 weston_log("failed to get drm_fb for bo\n");
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300342 gbm_surface_release_buffer(output->surface, bo);
343 return;
344 }
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -0400345}
346
347static void
Kristian Høgsberg6ddcdae2012-02-28 22:31:58 -0500348drm_output_repaint(struct weston_output *output_base,
Kristian Høgsbergd7c17262012-09-05 21:54:15 -0400349 pixman_region32_t *damage)
Benjamin Franzkeeefc36c2011-03-11 16:39:20 +0100350{
351 struct drm_output *output = (struct drm_output *) output_base;
Kristian Høgsberg06cf6b02012-01-25 23:47:45 -0500352 struct drm_compositor *compositor =
353 (struct drm_compositor *) output->base.compositor;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500354 struct drm_sprite *s;
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -0400355 struct drm_mode *mode;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500356 int ret = 0;
Benjamin Franzkeeefc36c2011-03-11 16:39:20 +0100357
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300358 if (!output->next)
Kristian Høgsbergd7c17262012-09-05 21:54:15 -0400359 drm_output_render(output, damage);
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300360 if (!output->next)
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -0400361 return;
Benjamin Franzkeeefc36c2011-03-11 16:39:20 +0100362
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -0400363 mode = container_of(output->base.current, struct drm_mode, base);
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300364 if (!output->current) {
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -0400365 ret = drmModeSetCrtc(compositor->drm.fd, output->crtc_id,
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300366 output->next->fb_id, 0, 0,
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -0400367 &output->connector_id, 1,
368 &mode->mode_info);
369 if (ret) {
Martin Minarik6d118362012-06-07 18:01:59 +0200370 weston_log("set mode failed: %m\n");
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -0400371 return;
372 }
Benjamin Franzke1178a3c2011-04-10 16:49:52 +0200373 }
374
Kristian Høgsberg06cf6b02012-01-25 23:47:45 -0500375 if (drmModePageFlip(compositor->drm.fd, output->crtc_id,
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300376 output->next->fb_id,
Kristian Høgsberg54f14c32012-01-18 11:47:41 -0500377 DRM_MODE_PAGE_FLIP_EVENT, output) < 0) {
Martin Minarik6d118362012-06-07 18:01:59 +0200378 weston_log("queueing pageflip failed: %m\n");
Kristian Høgsberg06cf6b02012-01-25 23:47:45 -0500379 return;
Kristian Høgsberg54f14c32012-01-18 11:47:41 -0500380 }
Benjamin Franzkeec4d3422011-03-14 12:07:26 +0100381
Ander Conselvan de Oliveiraa7326962012-06-26 17:09:13 +0300382 output->page_flip_pending = 1;
383
Kristian Høgsberg5626d342012-08-03 11:50:05 -0400384 drm_output_set_cursor(output);
385
Jesse Barnes58ef3792012-02-23 09:45:49 -0500386 /*
387 * Now, update all the sprite surfaces
388 */
389 wl_list_for_each(s, &compositor->sprite_list, link) {
390 uint32_t flags = 0;
391 drmVBlank vbl = {
392 .request.type = DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT,
393 .request.sequence = 1,
394 };
395
396 if (!drm_sprite_crtc_supported(output_base, s->possible_crtcs))
397 continue;
398
399 ret = drmModeSetPlane(compositor->drm.fd, s->plane_id,
Ander Conselvan de Oliveirada1c9082012-10-31 17:55:46 +0200400 output->crtc_id,
401 compositor->sprites_hidden ?
402 0 : s->pending_fb_id,
403 flags,
Jesse Barnes58ef3792012-02-23 09:45:49 -0500404 s->dest_x, s->dest_y,
405 s->dest_w, s->dest_h,
406 s->src_x, s->src_y,
407 s->src_w, s->src_h);
408 if (ret)
Martin Minarik6d118362012-06-07 18:01:59 +0200409 weston_log("setplane failed: %d: %s\n",
Jesse Barnes58ef3792012-02-23 09:45:49 -0500410 ret, strerror(errno));
411
Rob Clark5ca1a472012-08-08 20:27:37 -0500412 if (output->pipe > 0)
413 vbl.request.type |= DRM_VBLANK_SECONDARY;
414
Jesse Barnes58ef3792012-02-23 09:45:49 -0500415 /*
416 * Queue a vblank signal so we know when the surface
417 * becomes active on the display or has been replaced.
418 */
419 vbl.request.signal = (unsigned long)s;
420 ret = drmWaitVBlank(compositor->drm.fd, &vbl);
421 if (ret) {
Martin Minarik6d118362012-06-07 18:01:59 +0200422 weston_log("vblank event request failed: %d: %s\n",
Jesse Barnes58ef3792012-02-23 09:45:49 -0500423 ret, strerror(errno));
424 }
Ander Conselvan de Oliveiraa7326962012-06-26 17:09:13 +0300425
426 s->output = output;
427 output->vblank_pending = 1;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500428 }
429
Kristian Høgsberg5626d342012-08-03 11:50:05 -0400430 drm_disable_unused_sprites(&output->base);
431
Kristian Høgsberg06cf6b02012-01-25 23:47:45 -0500432 return;
Kristian Høgsbergfc783d42010-06-11 12:56:24 -0400433}
434
435static void
Jesse Barnes58ef3792012-02-23 09:45:49 -0500436vblank_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec,
437 void *data)
438{
439 struct drm_sprite *s = (struct drm_sprite *)data;
440 struct drm_compositor *c = s->compositor;
Ander Conselvan de Oliveiraa7326962012-06-26 17:09:13 +0300441 struct drm_output *output = s->output;
442 uint32_t msecs;
443
444 output->vblank_pending = 0;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500445
446 if (s->surface) {
447 weston_buffer_post_release(s->surface->buffer);
448 wl_list_remove(&s->destroy_listener.link);
449 s->surface = NULL;
450 drmModeRmFB(c->drm.fd, s->fb_id);
451 s->fb_id = 0;
452 }
453
454 if (s->pending_surface) {
455 wl_list_remove(&s->pending_destroy_listener.link);
Kristian Høgsberg27e30522012-04-11 23:18:23 -0400456 wl_signal_add(&s->pending_surface->buffer->resource.destroy_signal,
457 &s->destroy_listener);
Jesse Barnes58ef3792012-02-23 09:45:49 -0500458 s->surface = s->pending_surface;
459 s->pending_surface = NULL;
460 s->fb_id = s->pending_fb_id;
461 s->pending_fb_id = 0;
462 }
Ander Conselvan de Oliveiraa7326962012-06-26 17:09:13 +0300463
464 if (!output->page_flip_pending) {
465 msecs = sec * 1000 + usec / 1000;
466 weston_output_finish_frame(&output->base, msecs);
467 }
Jesse Barnes58ef3792012-02-23 09:45:49 -0500468}
469
470static void
Kristian Høgsbergfc783d42010-06-11 12:56:24 -0400471page_flip_handler(int fd, unsigned int frame,
472 unsigned int sec, unsigned int usec, void *data)
473{
Benjamin Franzke1178a3c2011-04-10 16:49:52 +0200474 struct drm_output *output = (struct drm_output *) data;
Kristian Høgsbergfc783d42010-06-11 12:56:24 -0400475 uint32_t msecs;
476
Ander Conselvan de Oliveiraa7326962012-06-26 17:09:13 +0300477 output->page_flip_pending = 0;
478
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300479 if (output->current) {
480 if (output->current->is_client_buffer)
481 gbm_bo_destroy(output->current->bo);
482 else
483 gbm_surface_release_buffer(output->surface,
484 output->current->bo);
Benjamin Franzke1178a3c2011-04-10 16:49:52 +0200485 }
486
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300487 output->current = output->next;
488 output->next = NULL;
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -0400489
Ander Conselvan de Oliveiraa7326962012-06-26 17:09:13 +0300490 if (!output->vblank_pending) {
491 msecs = sec * 1000 + usec / 1000;
492 weston_output_finish_frame(&output->base, msecs);
493 }
Benjamin Franzke1178a3c2011-04-10 16:49:52 +0200494}
495
496static int
Jesse Barnes58ef3792012-02-23 09:45:49 -0500497drm_surface_format_supported(struct drm_sprite *s, uint32_t format)
498{
Kristian Høgsberg875ab9e2012-03-30 11:52:39 -0400499 uint32_t i;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500500
501 for (i = 0; i < s->count_formats; i++)
502 if (s->formats[i] == format)
503 return 1;
504
505 return 0;
506}
507
508static int
509drm_surface_transform_supported(struct weston_surface *es)
510{
Kristian Høgsberg3b00bae2012-07-13 15:25:07 -0400511 struct weston_matrix *matrix = &es->transform.matrix;
512 int i;
513
514 if (!es->transform.enabled)
515 return 1;
516
517 for (i = 0; i < 16; i++) {
518 switch (i) {
519 case 10:
520 case 15:
521 if (matrix->d[i] != 1.0)
522 return 0;
523 break;
524 case 0:
525 case 5:
526 case 12:
527 case 13:
528 break;
529 default:
530 if (matrix->d[i] != 0.0)
531 return 0;
532 break;
533 }
534 }
Jesse Barnes58ef3792012-02-23 09:45:49 -0500535
536 return 1;
537}
538
Jesse Barnes58ef3792012-02-23 09:45:49 -0500539static void
540drm_disable_unused_sprites(struct weston_output *output_base)
541{
542 struct weston_compositor *ec = output_base->compositor;
543 struct drm_compositor *c =(struct drm_compositor *) ec;
544 struct drm_output *output = (struct drm_output *) output_base;
545 struct drm_sprite *s;
546 int ret;
547
548 wl_list_for_each(s, &c->sprite_list, link) {
549 if (s->pending_fb_id)
550 continue;
551
552 ret = drmModeSetPlane(c->drm.fd, s->plane_id,
553 output->crtc_id, 0, 0,
554 0, 0, 0, 0, 0, 0, 0, 0);
555 if (ret)
Martin Minarik6d118362012-06-07 18:01:59 +0200556 weston_log("failed to disable plane: %d: %s\n",
Jesse Barnes58ef3792012-02-23 09:45:49 -0500557 ret, strerror(errno));
558 drmModeRmFB(c->drm.fd, s->fb_id);
Ander Conselvan de Oliveirafd1f4c62012-06-26 17:09:14 +0300559
560 if (s->surface) {
561 s->surface = NULL;
562 wl_list_remove(&s->destroy_listener.link);
563 }
564
565 assert(!s->pending_surface);
Jesse Barnes58ef3792012-02-23 09:45:49 -0500566 s->fb_id = 0;
567 s->pending_fb_id = 0;
568 }
569}
570
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400571static struct weston_plane *
Jesse Barnes58ef3792012-02-23 09:45:49 -0500572drm_output_prepare_overlay_surface(struct weston_output *output_base,
Kristian Høgsberg6143f7d2012-07-14 00:31:32 -0400573 struct weston_surface *es)
Jesse Barnes58ef3792012-02-23 09:45:49 -0500574{
575 struct weston_compositor *ec = output_base->compositor;
576 struct drm_compositor *c =(struct drm_compositor *) ec;
577 struct drm_sprite *s;
578 int found = 0;
579 EGLint handle, stride;
580 struct gbm_bo *bo;
581 uint32_t fb_id = 0;
582 uint32_t handles[4], pitches[4], offsets[4];
583 int ret = 0;
584 pixman_region32_t dest_rect, src_rect;
585 pixman_box32_t *box;
586 uint32_t format;
Kristian Høgsberg3b00bae2012-07-13 15:25:07 -0400587 wl_fixed_t sx1, sy1, sx2, sy2;
Kristian Høgsberg8a015802012-08-09 17:19:23 -0400588 int32_t width, height;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500589
Kristian Høgsberg65bec242012-03-05 19:57:35 -0500590 if (c->sprites_are_broken)
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400591 return NULL;
Kristian Høgsberg65bec242012-03-05 19:57:35 -0500592
Ander Conselvan de Oliveirad450b192012-06-26 17:09:12 +0300593 if (es->output_mask != (1u << output_base->id))
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400594 return NULL;
Ander Conselvan de Oliveirad450b192012-06-26 17:09:12 +0300595
Kristian Høgsberg2763a2e2012-07-13 22:54:43 -0400596 if (es->buffer == NULL)
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400597 return NULL;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500598
Rob Clark702ffae2012-08-09 14:18:27 -0500599 if (wl_buffer_is_shm(es->buffer))
600 return NULL;
601
Jesse Barnes58ef3792012-02-23 09:45:49 -0500602 if (!drm_surface_transform_supported(es))
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400603 return NULL;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500604
Jesse Barnes58ef3792012-02-23 09:45:49 -0500605 wl_list_for_each(s, &c->sprite_list, link) {
606 if (!drm_sprite_crtc_supported(output_base, s->possible_crtcs))
607 continue;
608
609 if (!s->pending_fb_id) {
610 found = 1;
611 break;
612 }
613 }
614
615 /* No sprites available */
616 if (!found)
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400617 return NULL;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500618
Rob Clark4339add2012-08-09 14:18:28 -0500619 width = es->geometry.width;
620 height = es->geometry.height;
621
622 /* If geometry is out of bounds, don't even bother trying because
623 * we know the AddFB2() call will fail:
624 */
625 if (c->min_width > width || width > c->max_width ||
626 c->min_height > height || height > c->max_height)
Kristian Høgsberg8a015802012-08-09 17:19:23 -0400627 return NULL;
Rob Clark4339add2012-08-09 14:18:28 -0500628
Kristian Høgsberg2763a2e2012-07-13 22:54:43 -0400629 bo = gbm_bo_import(c->gbm, GBM_BO_IMPORT_WL_BUFFER,
630 es->buffer, GBM_BO_USE_SCANOUT);
631 if (!bo)
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400632 return NULL;
Kristian Høgsberg2763a2e2012-07-13 22:54:43 -0400633
Jesse Barnes58ef3792012-02-23 09:45:49 -0500634 format = gbm_bo_get_format(bo);
635 handle = gbm_bo_get_handle(bo).s32;
Kristian Høgsberg270a7cb2012-07-16 16:44:16 -0400636 stride = gbm_bo_get_stride(bo);
Jesse Barnes58ef3792012-02-23 09:45:49 -0500637
638 gbm_bo_destroy(bo);
639
640 if (!drm_surface_format_supported(s, format))
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400641 return NULL;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500642
643 if (!handle)
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400644 return NULL;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500645
646 handles[0] = handle;
647 pitches[0] = stride;
648 offsets[0] = 0;
649
Rob Clark4339add2012-08-09 14:18:28 -0500650 ret = drmModeAddFB2(c->drm.fd, width, height,
Jesse Barnes58ef3792012-02-23 09:45:49 -0500651 format, handles, pitches, offsets,
652 &fb_id, 0);
653 if (ret) {
Martin Minarik6d118362012-06-07 18:01:59 +0200654 weston_log("addfb2 failed: %d\n", ret);
Kristian Høgsberg65bec242012-03-05 19:57:35 -0500655 c->sprites_are_broken = 1;
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400656 return NULL;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500657 }
658
Jesse Barnes58ef3792012-02-23 09:45:49 -0500659 s->pending_fb_id = fb_id;
660 s->pending_surface = es;
661 es->buffer->busy_count++;
662
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400663 box = pixman_region32_extents(&es->transform.boundingbox);
664 s->plane.x = box->x1;
665 s->plane.y = box->y1;
666
Jesse Barnes58ef3792012-02-23 09:45:49 -0500667 /*
668 * Calculate the source & dest rects properly based on actual
Martin Olsson3b132e32012-09-29 15:13:56 +0200669 * position (note the caller has called weston_surface_update_transform()
Jesse Barnes58ef3792012-02-23 09:45:49 -0500670 * for us already).
671 */
672 pixman_region32_init(&dest_rect);
673 pixman_region32_intersect(&dest_rect, &es->transform.boundingbox,
674 &output_base->region);
675 pixman_region32_translate(&dest_rect, -output_base->x, -output_base->y);
676 box = pixman_region32_extents(&dest_rect);
677 s->dest_x = box->x1;
678 s->dest_y = box->y1;
679 s->dest_w = box->x2 - box->x1;
680 s->dest_h = box->y2 - box->y1;
681 pixman_region32_fini(&dest_rect);
682
683 pixman_region32_init(&src_rect);
684 pixman_region32_intersect(&src_rect, &es->transform.boundingbox,
685 &output_base->region);
Jesse Barnes58ef3792012-02-23 09:45:49 -0500686 box = pixman_region32_extents(&src_rect);
Kristian Høgsberg3b00bae2012-07-13 15:25:07 -0400687
688 weston_surface_from_global_fixed(es,
689 wl_fixed_from_int(box->x1),
690 wl_fixed_from_int(box->y1),
691 &sx1, &sy1);
692 weston_surface_from_global_fixed(es,
693 wl_fixed_from_int(box->x2),
694 wl_fixed_from_int(box->y2),
695 &sx2, &sy2);
696
697 if (sx1 < 0)
698 sx1 = 0;
699 if (sy1 < 0)
700 sy1 = 0;
701 if (sx2 > wl_fixed_from_int(es->geometry.width))
702 sx2 = wl_fixed_from_int(es->geometry.width);
703 if (sy2 > wl_fixed_from_int(es->geometry.height))
704 sy2 = wl_fixed_from_int(es->geometry.height);
705
706 s->src_x = sx1 << 8;
707 s->src_y = sy1 << 8;
708 s->src_w = (sx2 - sx1) << 8;
709 s->src_h = (sy2 - sy1) << 8;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500710 pixman_region32_fini(&src_rect);
711
Kristian Høgsberg27e30522012-04-11 23:18:23 -0400712 wl_signal_add(&es->buffer->resource.destroy_signal,
713 &s->pending_destroy_listener);
Kristian Høgsberg3b00bae2012-07-13 15:25:07 -0400714
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400715 return &s->plane;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500716}
717
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400718static struct weston_plane *
Kristian Høgsberg5626d342012-08-03 11:50:05 -0400719drm_output_prepare_cursor_surface(struct weston_output *output_base,
720 struct weston_surface *es)
Kristian Høgsbergd8bf90c2012-02-23 23:03:14 -0500721{
Kristian Høgsberg8a015802012-08-09 17:19:23 -0400722 struct drm_compositor *c =
723 (struct drm_compositor *) output_base->compositor;
Kristian Høgsberga6edab32012-07-14 01:06:28 -0400724 struct drm_output *output = (struct drm_output *) output_base;
Kristian Høgsberg5626d342012-08-03 11:50:05 -0400725
726 if (output->cursor_surface)
727 return NULL;
728 if (es->output_mask != (1u << output_base->id))
729 return NULL;
Rob Clarkab5b1e32012-08-09 13:24:45 -0500730 if (c->cursors_are_broken)
Kristian Høgsberg8a015802012-08-09 17:19:23 -0400731 return NULL;
Kristian Høgsberg5626d342012-08-03 11:50:05 -0400732 if (es->buffer == NULL || !wl_buffer_is_shm(es->buffer) ||
733 es->geometry.width > 64 || es->geometry.height > 64)
734 return NULL;
735
736 output->cursor_surface = es;
737
738 return &output->cursor_plane;
739}
740
741static void
742drm_output_set_cursor(struct drm_output *output)
743{
744 struct weston_surface *es = output->cursor_surface;
Kristian Høgsberga6edab32012-07-14 01:06:28 -0400745 struct drm_compositor *c =
746 (struct drm_compositor *) output->base.compositor;
747 EGLint handle, stride;
748 struct gbm_bo *bo;
749 uint32_t buf[64 * 64];
750 unsigned char *s;
Kristian Høgsberg24e42752012-07-18 12:08:37 -0400751 int i, x, y;
Kristian Høgsbergd8bf90c2012-02-23 23:03:14 -0500752
Kristian Høgsberg79af73e2012-08-03 15:45:23 -0400753 output->cursor_surface = NULL;
Kristian Høgsberg5626d342012-08-03 11:50:05 -0400754 if (es == NULL) {
755 drmModeSetCursor(c->drm.fd, output->crtc_id, 0, 0, 0);
756 return;
757 }
Kristian Høgsbergd8bf90c2012-02-23 23:03:14 -0500758
Kristian Høgsberg5626d342012-08-03 11:50:05 -0400759 if (es->buffer && pixman_region32_not_empty(&output->cursor_plane.damage)) {
760 pixman_region32_fini(&output->cursor_plane.damage);
761 pixman_region32_init(&output->cursor_plane.damage);
Kristian Høgsberg1f5de352012-07-18 12:09:58 -0400762 output->current_cursor ^= 1;
763 bo = output->cursor_bo[output->current_cursor];
764 memset(buf, 0, sizeof buf);
765 stride = wl_shm_buffer_get_stride(es->buffer);
766 s = wl_shm_buffer_get_data(es->buffer);
767 for (i = 0; i < es->geometry.height; i++)
768 memcpy(buf + i * 64, s + i * stride,
769 es->geometry.width * 4);
Kristian Høgsbergd8bf90c2012-02-23 23:03:14 -0500770
Kristian Høgsberg1f5de352012-07-18 12:09:58 -0400771 if (gbm_bo_write(bo, buf, sizeof buf) < 0)
Pekka Paalanenae29da22012-08-06 14:57:05 +0300772 weston_log("failed update cursor: %m\n");
Kristian Høgsberga6edab32012-07-14 01:06:28 -0400773
Kristian Høgsberg1f5de352012-07-18 12:09:58 -0400774 handle = gbm_bo_get_handle(bo).s32;
775 if (drmModeSetCursor(c->drm.fd,
Rob Clarkab5b1e32012-08-09 13:24:45 -0500776 output->crtc_id, handle, 64, 64)) {
Pekka Paalanenae29da22012-08-06 14:57:05 +0300777 weston_log("failed to set cursor: %m\n");
Rob Clarkab5b1e32012-08-09 13:24:45 -0500778 c->cursors_are_broken = 1;
779 }
Kristian Høgsberga6edab32012-07-14 01:06:28 -0400780 }
781
Kristian Høgsberg24e42752012-07-18 12:08:37 -0400782 x = es->geometry.x - output->base.x;
783 y = es->geometry.y - output->base.y;
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400784 if (output->cursor_plane.x != x || output->cursor_plane.y != y) {
Rob Clarkab5b1e32012-08-09 13:24:45 -0500785 if (drmModeMoveCursor(c->drm.fd, output->crtc_id, x, y)) {
Kristian Høgsberg24e42752012-07-18 12:08:37 -0400786 weston_log("failed to move cursor: %m\n");
Rob Clarkab5b1e32012-08-09 13:24:45 -0500787 c->cursors_are_broken = 1;
788 }
789
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400790 output->cursor_plane.x = x;
791 output->cursor_plane.y = y;
Kristian Høgsberga6edab32012-07-14 01:06:28 -0400792 }
Kristian Høgsbergd8bf90c2012-02-23 23:03:14 -0500793}
794
Jesse Barnes58ef3792012-02-23 09:45:49 -0500795static void
796drm_assign_planes(struct weston_output *output)
797{
Kristian Høgsberga6edab32012-07-14 01:06:28 -0400798 struct drm_compositor *c =
799 (struct drm_compositor *) output->compositor;
Ander Conselvan de Oliveira4bcf3a52012-10-31 17:55:45 +0200800 struct drm_output *drm_output = (struct drm_output *) output;
801 struct drm_sprite *s;
Kristian Høgsberg8e1f77f2012-05-03 11:39:35 -0400802 struct weston_surface *es, *next;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500803 pixman_region32_t overlap, surface_overlap;
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400804 struct weston_plane *primary, *next_plane;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500805
Ander Conselvan de Oliveira4bcf3a52012-10-31 17:55:45 +0200806 /* Reset the opaque region of the planes */
807 pixman_region32_fini(&drm_output->cursor_plane.opaque);
808 pixman_region32_init(&drm_output->cursor_plane.opaque);
809 pixman_region32_fini(&drm_output->fb_plane.opaque);
810 pixman_region32_init(&drm_output->fb_plane.opaque);
811
812 wl_list_for_each (s, &c->sprite_list, link) {
813 if (!drm_sprite_crtc_supported(output, s->possible_crtcs))
814 continue;
815
816 pixman_region32_fini(&s->plane.opaque);
817 pixman_region32_init(&s->plane.opaque);
818 }
819
Jesse Barnes58ef3792012-02-23 09:45:49 -0500820 /*
821 * Find a surface for each sprite in the output using some heuristics:
822 * 1) size
823 * 2) frequency of update
824 * 3) opacity (though some hw might support alpha blending)
825 * 4) clipping (this can be fixed with color keys)
826 *
827 * The idea is to save on blitting since this should save power.
828 * If we can get a large video surface on the sprite for example,
829 * the main display surface may not need to update at all, and
830 * the client buffer can be used directly for the sprite surface
831 * as we do for flipping full screen surfaces.
832 */
833 pixman_region32_init(&overlap);
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400834 primary = &c->base.primary_plane;
Kristian Høgsberga6edab32012-07-14 01:06:28 -0400835 wl_list_for_each_safe(es, next, &c->base.surface_list, link) {
Jesse Barnes58ef3792012-02-23 09:45:49 -0500836 pixman_region32_init(&surface_overlap);
837 pixman_region32_intersect(&surface_overlap, &overlap,
838 &es->transform.boundingbox);
839
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400840 next_plane = NULL;
Kristian Høgsberg6143f7d2012-07-14 00:31:32 -0400841 if (pixman_region32_not_empty(&surface_overlap))
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400842 next_plane = primary;
843 if (next_plane == NULL)
Kristian Høgsberg5626d342012-08-03 11:50:05 -0400844 next_plane = drm_output_prepare_cursor_surface(output, es);
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400845 if (next_plane == NULL)
846 next_plane = drm_output_prepare_scanout_surface(output, es);
847 if (next_plane == NULL)
848 next_plane = drm_output_prepare_overlay_surface(output, es);
849 if (next_plane == NULL)
850 next_plane = primary;
851 weston_surface_move_to_plane(es, next_plane);
852 if (next_plane == primary)
Jesse Barnes58ef3792012-02-23 09:45:49 -0500853 pixman_region32_union(&overlap, &overlap,
854 &es->transform.boundingbox);
Kristian Høgsberg6143f7d2012-07-14 00:31:32 -0400855
Jesse Barnes58ef3792012-02-23 09:45:49 -0500856 pixman_region32_fini(&surface_overlap);
857 }
858 pixman_region32_fini(&overlap);
Jesse Barnes58ef3792012-02-23 09:45:49 -0500859}
860
Matt Roper361d2ad2011-08-29 13:52:23 -0700861static void
Kristian Høgsberg8334bc12012-01-03 10:29:47 -0500862drm_output_destroy(struct weston_output *output_base)
Matt Roper361d2ad2011-08-29 13:52:23 -0700863{
864 struct drm_output *output = (struct drm_output *) output_base;
865 struct drm_compositor *c =
Benjamin Franzke117483d2011-08-30 11:38:26 +0200866 (struct drm_compositor *) output->base.compositor;
Matt Roper361d2ad2011-08-29 13:52:23 -0700867 drmModeCrtcPtr origcrtc = output->original_crtc;
Matt Roper361d2ad2011-08-29 13:52:23 -0700868
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +0200869 if (output->backlight)
870 backlight_destroy(output->backlight);
871
Matt Roper361d2ad2011-08-29 13:52:23 -0700872 /* Turn off hardware cursor */
Kristian Høgsberga6edab32012-07-14 01:06:28 -0400873 drmModeSetCursor(c->drm.fd, output->crtc_id, 0, 0, 0);
Matt Roper361d2ad2011-08-29 13:52:23 -0700874
875 /* Restore original CRTC state */
876 drmModeSetCrtc(c->drm.fd, origcrtc->crtc_id, origcrtc->buffer_id,
Benjamin Franzke117483d2011-08-30 11:38:26 +0200877 origcrtc->x, origcrtc->y,
878 &output->connector_id, 1, &origcrtc->mode);
Matt Roper361d2ad2011-08-29 13:52:23 -0700879 drmModeFreeCrtc(origcrtc);
880
Benjamin Franzke48c4ea22011-08-30 11:44:56 +0200881 c->crtc_allocator &= ~(1 << output->crtc_id);
882 c->connector_allocator &= ~(1 << output->connector_id);
883
Kristian Høgsbergd7c17262012-09-05 21:54:15 -0400884 eglDestroySurface(c->base.egl_display, output->base.egl_surface);
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -0400885 gbm_surface_destroy(output->surface);
886
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400887 weston_plane_release(&output->fb_plane);
888 weston_plane_release(&output->cursor_plane);
889
Kristian Høgsberg8334bc12012-01-03 10:29:47 -0500890 weston_output_destroy(&output->base);
Benjamin Franzke48c4ea22011-08-30 11:44:56 +0200891 wl_list_remove(&output->base.link);
892
Kristian Høgsberg148ef012012-07-26 23:03:57 -0400893 free(output->name);
Matt Roper361d2ad2011-08-29 13:52:23 -0700894 free(output);
895}
896
Alex Wub7b8bda2012-04-17 17:20:48 +0800897static struct drm_mode *
898choose_mode (struct drm_output *output, struct weston_mode *target_mode)
899{
900 struct drm_mode *tmp_mode = NULL, *mode;
901
902 if (output->base.current->width == target_mode->width &&
903 output->base.current->height == target_mode->height &&
904 (output->base.current->refresh == target_mode->refresh ||
905 target_mode->refresh == 0))
906 return (struct drm_mode *)output->base.current;
907
908 wl_list_for_each(mode, &output->base.mode_list, base.link) {
909 if (mode->mode_info.hdisplay == target_mode->width &&
910 mode->mode_info.vdisplay == target_mode->height) {
911 if (mode->mode_info.vrefresh == target_mode->refresh ||
912 target_mode->refresh == 0) {
913 return mode;
914 } else if (!tmp_mode)
915 tmp_mode = mode;
916 }
917 }
918
919 return tmp_mode;
920}
921
922static int
923drm_output_switch_mode(struct weston_output *output_base, struct weston_mode *mode)
924{
925 struct drm_output *output;
926 struct drm_mode *drm_mode;
927 int ret;
928 struct drm_compositor *ec;
929 struct gbm_surface *surface;
930 EGLSurface egl_surface;
931
932 if (output_base == NULL) {
Martin Minarik6d118362012-06-07 18:01:59 +0200933 weston_log("output is NULL.\n");
Alex Wub7b8bda2012-04-17 17:20:48 +0800934 return -1;
935 }
936
937 if (mode == NULL) {
Martin Minarik6d118362012-06-07 18:01:59 +0200938 weston_log("mode is NULL.\n");
Alex Wub7b8bda2012-04-17 17:20:48 +0800939 return -1;
940 }
941
942 ec = (struct drm_compositor *)output_base->compositor;
943 output = (struct drm_output *)output_base;
944 drm_mode = choose_mode (output, mode);
945
946 if (!drm_mode) {
Martin Minarik6d118362012-06-07 18:01:59 +0200947 weston_log("%s, invalid resolution:%dx%d\n", __func__, mode->width, mode->height);
Alex Wub7b8bda2012-04-17 17:20:48 +0800948 return -1;
949 } else if (&drm_mode->base == output->base.current) {
950 return 0;
951 } else if (drm_mode->base.width == output->base.current->width &&
952 drm_mode->base.height == output->base.current->height) {
953 /* only change refresh value */
954 ret = drmModeSetCrtc(ec->drm.fd,
955 output->crtc_id,
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300956 output->current->fb_id, 0, 0,
Alex Wub7b8bda2012-04-17 17:20:48 +0800957 &output->connector_id, 1, &drm_mode->mode_info);
958
959 if (ret) {
Martin Minarik6d118362012-06-07 18:01:59 +0200960 weston_log("failed to set mode (%dx%d) %u Hz\n",
Alex Wub7b8bda2012-04-17 17:20:48 +0800961 drm_mode->base.width,
962 drm_mode->base.height,
Kristian Høgsbergc4621b02012-05-10 12:23:53 -0400963 drm_mode->base.refresh / 1000);
Alex Wub7b8bda2012-04-17 17:20:48 +0800964 ret = -1;
965 } else {
966 output->base.current->flags = 0;
967 output->base.current = &drm_mode->base;
968 drm_mode->base.flags =
969 WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
970 ret = 0;
971 }
972
973 return ret;
974 }
975
976 drm_mode->base.flags =
977 WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
978
979 surface = gbm_surface_create(ec->gbm,
980 drm_mode->base.width,
981 drm_mode->base.height,
982 GBM_FORMAT_XRGB8888,
983 GBM_BO_USE_SCANOUT |
984 GBM_BO_USE_RENDERING);
985 if (!surface) {
Martin Minarik6d118362012-06-07 18:01:59 +0200986 weston_log("failed to create gbm surface\n");
Alex Wub7b8bda2012-04-17 17:20:48 +0800987 return -1;
988 }
989
990 egl_surface =
Kristian Høgsberg362b6722012-06-18 15:13:51 -0400991 eglCreateWindowSurface(ec->base.egl_display,
992 ec->base.egl_config,
Alex Wub7b8bda2012-04-17 17:20:48 +0800993 surface, NULL);
994
995 if (egl_surface == EGL_NO_SURFACE) {
Martin Minarik6d118362012-06-07 18:01:59 +0200996 weston_log("failed to create egl surface\n");
Alex Wub7b8bda2012-04-17 17:20:48 +0800997 goto err;
998 }
999
1000 ret = drmModeSetCrtc(ec->drm.fd,
1001 output->crtc_id,
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +03001002 output->current->fb_id, 0, 0,
Alex Wub7b8bda2012-04-17 17:20:48 +08001003 &output->connector_id, 1, &drm_mode->mode_info);
1004 if (ret) {
Martin Minarik6d118362012-06-07 18:01:59 +02001005 weston_log("failed to set mode\n");
Alex Wub7b8bda2012-04-17 17:20:48 +08001006 goto err;
1007 }
1008
1009 /* reset rendering stuff. */
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +03001010 if (output->current) {
1011 if (output->current->is_client_buffer)
1012 gbm_bo_destroy(output->current->bo);
1013 else
1014 gbm_surface_release_buffer(output->surface,
1015 output->current->bo);
1016 }
1017 output->current = NULL;
Alex Wub7b8bda2012-04-17 17:20:48 +08001018
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +03001019 if (output->next) {
1020 if (output->next->is_client_buffer)
1021 gbm_bo_destroy(output->next->bo);
1022 else
1023 gbm_surface_release_buffer(output->surface,
1024 output->next->bo);
1025 }
1026 output->next = NULL;
Alex Wub7b8bda2012-04-17 17:20:48 +08001027
Kristian Høgsbergd7c17262012-09-05 21:54:15 -04001028 eglDestroySurface(ec->base.egl_display, output->base.egl_surface);
Alex Wub7b8bda2012-04-17 17:20:48 +08001029 gbm_surface_destroy(output->surface);
Kristian Høgsbergd7c17262012-09-05 21:54:15 -04001030 output->base.egl_surface = egl_surface;
Alex Wub7b8bda2012-04-17 17:20:48 +08001031 output->surface = surface;
1032
1033 /*update output*/
1034 output->base.current = &drm_mode->base;
1035 output->base.dirty = 1;
1036 weston_output_move(&output->base, output->base.x, output->base.y);
1037 return 0;
1038
1039err:
Kristian Høgsberg362b6722012-06-18 15:13:51 -04001040 eglDestroySurface(ec->base.egl_display, egl_surface);
Alex Wub7b8bda2012-04-17 17:20:48 +08001041 gbm_surface_destroy(surface);
1042 return -1;
1043}
1044
Kristian Høgsbergb1868472011-04-22 12:27:57 -04001045static int
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001046on_drm_input(int fd, uint32_t mask, void *data)
1047{
1048 drmEventContext evctx;
1049
1050 memset(&evctx, 0, sizeof evctx);
1051 evctx.version = DRM_EVENT_CONTEXT_VERSION;
1052 evctx.page_flip_handler = page_flip_handler;
Jesse Barnes58ef3792012-02-23 09:45:49 -05001053 evctx.vblank_handler = vblank_handler;
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001054 drmHandleEvent(fd, &evctx);
Kristian Høgsbergb1868472011-04-22 12:27:57 -04001055
1056 return 1;
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001057}
1058
1059static int
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04001060init_egl(struct drm_compositor *ec, struct udev_device *device)
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001061{
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -04001062 EGLint major, minor, n;
Kristian Høgsbergb5ef5912012-03-28 22:53:49 -04001063 const char *filename, *sysnum;
Kristian Høgsberg5fcd0aa2010-08-09 14:43:33 -04001064 int fd;
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -04001065 static const EGLint config_attribs[] = {
1066 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
1067 EGL_RED_SIZE, 1,
1068 EGL_GREEN_SIZE, 1,
1069 EGL_BLUE_SIZE, 1,
1070 EGL_ALPHA_SIZE, 0,
1071 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
1072 EGL_NONE
1073 };
1074
Kristian Høgsbergb71302e2012-05-10 12:28:35 -04001075 sysnum = udev_device_get_sysnum(device);
1076 if (sysnum)
1077 ec->drm.id = atoi(sysnum);
1078 if (!sysnum || ec->drm.id < 0) {
Martin Minarik6d118362012-06-07 18:01:59 +02001079 weston_log("cannot get device sysnum\n");
Kristian Høgsbergb71302e2012-05-10 12:28:35 -04001080 return -1;
1081 }
1082
Kristian Høgsberg5fcd0aa2010-08-09 14:43:33 -04001083 filename = udev_device_get_devnode(device);
David Herrmann63ff7062011-11-05 18:46:01 +01001084 fd = open(filename, O_RDWR | O_CLOEXEC);
Kristian Høgsberg5fcd0aa2010-08-09 14:43:33 -04001085 if (fd < 0) {
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001086 /* Probably permissions error */
Martin Minarik6d118362012-06-07 18:01:59 +02001087 weston_log("couldn't open %s, skipping\n",
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001088 udev_device_get_devnode(device));
1089 return -1;
1090 }
1091
Kristian Høgsbergfc9c5e02012-06-08 16:45:33 -04001092 weston_log("using %s\n", filename);
1093
Benjamin Franzke2af7f102011-03-02 11:14:59 +01001094 ec->drm.fd = fd;
Benjamin Franzke060cf802011-04-30 09:32:11 +02001095 ec->gbm = gbm_create_device(ec->drm.fd);
Kristian Høgsberg362b6722012-06-18 15:13:51 -04001096 ec->base.egl_display = eglGetDisplay(ec->gbm);
1097 if (ec->base.egl_display == NULL) {
Martin Minarik6d118362012-06-07 18:01:59 +02001098 weston_log("failed to create display\n");
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001099 return -1;
1100 }
1101
Kristian Høgsberg362b6722012-06-18 15:13:51 -04001102 if (!eglInitialize(ec->base.egl_display, &major, &minor)) {
Martin Minarik6d118362012-06-07 18:01:59 +02001103 weston_log("failed to initialize display\n");
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001104 return -1;
1105 }
1106
Kristian Høgsberg362b6722012-06-18 15:13:51 -04001107 if (!eglChooseConfig(ec->base.egl_display, config_attribs,
1108 &ec->base.egl_config, 1, &n) || n != 1) {
Martin Minarik6d118362012-06-07 18:01:59 +02001109 weston_log("failed to choose config: %d\n", n);
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -04001110 return -1;
1111 }
1112
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001113 return 0;
1114}
1115
Ander Conselvan de Oliveira42c46462012-08-09 16:45:00 +03001116static struct drm_mode *
Kristian Høgsberg8f0ce052011-06-21 11:16:58 -04001117drm_output_add_mode(struct drm_output *output, drmModeModeInfo *info)
1118{
1119 struct drm_mode *mode;
Kristian Høgsbergc4621b02012-05-10 12:23:53 -04001120 uint64_t refresh;
Kristian Høgsberg8f0ce052011-06-21 11:16:58 -04001121
1122 mode = malloc(sizeof *mode);
1123 if (mode == NULL)
Ander Conselvan de Oliveira42c46462012-08-09 16:45:00 +03001124 return NULL;
Kristian Høgsberg8f0ce052011-06-21 11:16:58 -04001125
1126 mode->base.flags = 0;
1127 mode->base.width = info->hdisplay;
1128 mode->base.height = info->vdisplay;
Kristian Høgsbergc4621b02012-05-10 12:23:53 -04001129
1130 /* Calculate higher precision (mHz) refresh rate */
1131 refresh = (info->clock * 1000000LL / info->htotal +
1132 info->vtotal / 2) / info->vtotal;
1133
1134 if (info->flags & DRM_MODE_FLAG_INTERLACE)
1135 refresh *= 2;
1136 if (info->flags & DRM_MODE_FLAG_DBLSCAN)
1137 refresh /= 2;
1138 if (info->vscan > 1)
1139 refresh /= info->vscan;
1140
1141 mode->base.refresh = refresh;
Kristian Høgsberg8f0ce052011-06-21 11:16:58 -04001142 mode->mode_info = *info;
Kristian Høgsberg061c4252012-06-28 11:28:15 -04001143
1144 if (info->type & DRM_MODE_TYPE_PREFERRED)
1145 mode->base.flags |= WL_OUTPUT_MODE_PREFERRED;
1146
Kristian Høgsberg8f0ce052011-06-21 11:16:58 -04001147 wl_list_insert(output->base.mode_list.prev, &mode->base.link);
1148
Ander Conselvan de Oliveira42c46462012-08-09 16:45:00 +03001149 return mode;
Kristian Høgsberg8f0ce052011-06-21 11:16:58 -04001150}
1151
1152static int
1153drm_subpixel_to_wayland(int drm_value)
1154{
1155 switch (drm_value) {
1156 default:
1157 case DRM_MODE_SUBPIXEL_UNKNOWN:
1158 return WL_OUTPUT_SUBPIXEL_UNKNOWN;
1159 case DRM_MODE_SUBPIXEL_NONE:
1160 return WL_OUTPUT_SUBPIXEL_NONE;
1161 case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB:
1162 return WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB;
1163 case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR:
1164 return WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR;
1165 case DRM_MODE_SUBPIXEL_VERTICAL_RGB:
1166 return WL_OUTPUT_SUBPIXEL_VERTICAL_RGB;
1167 case DRM_MODE_SUBPIXEL_VERTICAL_BGR:
1168 return WL_OUTPUT_SUBPIXEL_VERTICAL_BGR;
1169 }
1170}
1171
Kristian Høgsberg9f404b72012-01-26 00:11:01 -05001172static void
Kristian Høgsberg27e30522012-04-11 23:18:23 -04001173sprite_handle_buffer_destroy(struct wl_listener *listener, void *data)
Jesse Barnes58ef3792012-02-23 09:45:49 -05001174{
1175 struct drm_sprite *sprite =
1176 container_of(listener, struct drm_sprite,
1177 destroy_listener);
Ander Conselvan de Oliveira01a57ed2012-06-26 17:09:15 +03001178 struct drm_compositor *compositor = sprite->compositor;
Jesse Barnes58ef3792012-02-23 09:45:49 -05001179
1180 sprite->surface = NULL;
Ander Conselvan de Oliveira01a57ed2012-06-26 17:09:15 +03001181 drmModeRmFB(compositor->drm.fd, sprite->fb_id);
1182 sprite->fb_id = 0;
Jesse Barnes58ef3792012-02-23 09:45:49 -05001183}
1184
1185static void
Kristian Høgsberg27e30522012-04-11 23:18:23 -04001186sprite_handle_pending_buffer_destroy(struct wl_listener *listener, void *data)
Jesse Barnes58ef3792012-02-23 09:45:49 -05001187{
1188 struct drm_sprite *sprite =
1189 container_of(listener, struct drm_sprite,
1190 pending_destroy_listener);
Ander Conselvan de Oliveira01a57ed2012-06-26 17:09:15 +03001191 struct drm_compositor *compositor = sprite->compositor;
Jesse Barnes58ef3792012-02-23 09:45:49 -05001192
1193 sprite->pending_surface = NULL;
Ander Conselvan de Oliveira01a57ed2012-06-26 17:09:15 +03001194 drmModeRmFB(compositor->drm.fd, sprite->pending_fb_id);
1195 sprite->pending_fb_id = 0;
Jesse Barnes58ef3792012-02-23 09:45:49 -05001196}
1197
Tiago Vignatti5ab91ad2012-03-12 19:40:09 -03001198/* returns a value between 0-255 range, where higher is brighter */
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +02001199static uint32_t
1200drm_get_backlight(struct drm_output *output)
1201{
1202 long brightness, max_brightness, norm;
1203
1204 brightness = backlight_get_brightness(output->backlight);
1205 max_brightness = backlight_get_max_brightness(output->backlight);
1206
Tiago Vignatti5ab91ad2012-03-12 19:40:09 -03001207 /* convert it on a scale of 0 to 255 */
1208 norm = (brightness * 255)/(max_brightness);
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +02001209
1210 return (uint32_t) norm;
1211}
1212
Tiago Vignatti5ab91ad2012-03-12 19:40:09 -03001213/* values accepted are between 0-255 range */
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +02001214static void
1215drm_set_backlight(struct weston_output *output_base, uint32_t value)
1216{
1217 struct drm_output *output = (struct drm_output *) output_base;
1218 long max_brightness, new_brightness;
1219
1220 if (!output->backlight)
1221 return;
1222
Kristian Høgsberg875ab9e2012-03-30 11:52:39 -04001223 if (value > 255)
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +02001224 return;
1225
1226 max_brightness = backlight_get_max_brightness(output->backlight);
1227
1228 /* get denormalized value */
Tiago Vignatti5ab91ad2012-03-12 19:40:09 -03001229 new_brightness = (value * max_brightness) / 255;
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +02001230
1231 backlight_set_brightness(output->backlight, new_brightness);
1232}
1233
1234static drmModePropertyPtr
1235drm_get_prop(int fd, drmModeConnectorPtr connector, const char *name)
1236{
1237 drmModePropertyPtr props;
1238 int i;
1239
1240 for (i = 0; i < connector->count_props; i++) {
1241 props = drmModeGetProperty(fd, connector->props[i]);
1242 if (!props)
1243 continue;
1244
1245 if (!strcmp(props->name, name))
1246 return props;
1247
1248 drmModeFreeProperty(props);
1249 }
1250
1251 return NULL;
1252}
1253
1254static void
1255drm_set_dpms(struct weston_output *output_base, enum dpms_enum level)
1256{
1257 struct drm_output *output = (struct drm_output *) output_base;
1258 struct weston_compositor *ec = output_base->compositor;
1259 struct drm_compositor *c = (struct drm_compositor *) ec;
1260 drmModeConnectorPtr connector;
1261 drmModePropertyPtr prop;
1262
1263 connector = drmModeGetConnector(c->drm.fd, output->connector_id);
1264 if (!connector)
1265 return;
1266
1267 prop = drm_get_prop(c->drm.fd, connector, "DPMS");
1268 if (!prop) {
1269 drmModeFreeConnector(connector);
1270 return;
1271 }
1272
1273 drmModeConnectorSetProperty(c->drm.fd, connector->connector_id,
1274 prop->prop_id, level);
1275 drmModeFreeProperty(prop);
1276 drmModeFreeConnector(connector);
1277}
1278
Kristian Høgsberg2f9ed712012-07-26 17:57:15 -04001279static const char *connector_type_names[] = {
1280 "None",
1281 "VGA",
1282 "DVI",
1283 "DVI",
1284 "DVI",
1285 "Composite",
1286 "TV",
1287 "LVDS",
1288 "CTV",
1289 "DIN",
1290 "DP",
1291 "HDMI",
1292 "HDMI",
1293 "TV",
1294 "eDP",
1295};
1296
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001297static int
Kristian Høgsberg9ca38462012-07-26 22:44:55 -04001298find_crtc_for_connector(struct drm_compositor *ec,
1299 drmModeRes *resources, drmModeConnector *connector)
1300{
1301 drmModeEncoder *encoder;
1302 uint32_t possible_crtcs;
1303 int i, j;
1304
1305 for (j = 0; j < connector->count_encoders; j++) {
1306 encoder = drmModeGetEncoder(ec->drm.fd, connector->encoders[j]);
1307 if (encoder == NULL) {
1308 weston_log("Failed to get encoder.\n");
1309 return -1;
1310 }
1311 possible_crtcs = encoder->possible_crtcs;
1312 drmModeFreeEncoder(encoder);
1313
1314 for (i = 0; i < resources->count_crtcs; i++) {
1315 if (possible_crtcs & (1 << i) &&
1316 !(ec->crtc_allocator & (1 << resources->crtcs[i])))
1317 return i;
1318 }
1319 }
1320
1321 return -1;
1322}
1323
1324static int
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04001325create_output_for_connector(struct drm_compositor *ec,
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001326 drmModeRes *resources,
Benjamin Franzkeeefc36c2011-03-11 16:39:20 +01001327 drmModeConnector *connector,
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +02001328 int x, int y, struct udev_device *drm_device)
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001329{
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04001330 struct drm_output *output;
Ander Conselvan de Oliveira42c46462012-08-09 16:45:00 +03001331 struct drm_mode *drm_mode, *next, *preferred, *current, *configured;
1332 struct weston_mode *m;
Scott Moreau8ab5d452012-07-30 19:51:08 -06001333 struct drm_configured_output *o = NULL, *temp;
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001334 drmModeEncoder *encoder;
Kristian Høgsberg061c4252012-06-28 11:28:15 -04001335 drmModeModeInfo crtc_mode;
1336 drmModeCrtc *crtc;
Ander Conselvan de Oliveira42c46462012-08-09 16:45:00 +03001337 int i;
Kristian Høgsberg2f9ed712012-07-26 17:57:15 -04001338 char name[32];
1339 const char *type_name;
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001340
Kristian Høgsberg9ca38462012-07-26 22:44:55 -04001341 i = find_crtc_for_connector(ec, resources, connector);
1342 if (i < 0) {
1343 weston_log("No usable crtc/encoder pair for connector.\n");
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001344 return -1;
1345 }
1346
Kristian Høgsberg8f0ce052011-06-21 11:16:58 -04001347 output = malloc(sizeof *output);
Kristian Høgsberg9ca38462012-07-26 22:44:55 -04001348 if (output == NULL)
Kristian Høgsberg8f0ce052011-06-21 11:16:58 -04001349 return -1;
1350
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04001351 memset(output, 0, sizeof *output);
Kristian Høgsberg8f0ce052011-06-21 11:16:58 -04001352 output->base.subpixel = drm_subpixel_to_wayland(connector->subpixel);
1353 output->base.make = "unknown";
1354 output->base.model = "unknown";
1355 wl_list_init(&output->base.mode_list);
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04001356
Kristian Høgsberg2f9ed712012-07-26 17:57:15 -04001357 if (connector->connector_type < ARRAY_LENGTH(connector_type_names))
1358 type_name = connector_type_names[connector->connector_type];
1359 else
1360 type_name = "UNKNOWN";
1361 snprintf(name, 32, "%s%d", type_name, connector->connector_type_id);
1362 output->name = strdup(name);
1363
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001364 output->crtc_id = resources->crtcs[i];
Rob Clark5ca1a472012-08-08 20:27:37 -05001365 output->pipe = i;
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001366 ec->crtc_allocator |= (1 << output->crtc_id);
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001367 output->connector_id = connector->connector_id;
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001368 ec->connector_allocator |= (1 << output->connector_id);
Kristian Høgsberg8f0ce052011-06-21 11:16:58 -04001369
Matt Roper361d2ad2011-08-29 13:52:23 -07001370 output->original_crtc = drmModeGetCrtc(ec->drm.fd, output->crtc_id);
1371
Kristian Høgsberg061c4252012-06-28 11:28:15 -04001372 /* Get the current mode on the crtc that's currently driving
1373 * this connector. */
1374 encoder = drmModeGetEncoder(ec->drm.fd, connector->encoder_id);
Wang Quanxianacb805a2012-07-30 18:09:46 -04001375 memset(&crtc_mode, 0, sizeof crtc_mode);
1376 if (encoder != NULL) {
1377 crtc = drmModeGetCrtc(ec->drm.fd, encoder->crtc_id);
1378 drmModeFreeEncoder(encoder);
1379 if (crtc == NULL)
1380 goto err_free;
1381 if (crtc->mode_valid)
1382 crtc_mode = crtc->mode;
1383 drmModeFreeCrtc(crtc);
1384 }
Kristian Høgsberg061c4252012-06-28 11:28:15 -04001385
David Herrmann0f0d54e2011-12-08 17:05:45 +01001386 for (i = 0; i < connector->count_modes; i++) {
Ander Conselvan de Oliveira42c46462012-08-09 16:45:00 +03001387 drm_mode = drm_output_add_mode(output, &connector->modes[i]);
1388 if (!drm_mode)
David Herrmann0f0d54e2011-12-08 17:05:45 +01001389 goto err_free;
1390 }
1391
Kristian Høgsberg061c4252012-06-28 11:28:15 -04001392 preferred = NULL;
1393 current = NULL;
Scott Moreau8ab5d452012-07-30 19:51:08 -06001394 configured = NULL;
1395
1396 wl_list_for_each(temp, &configured_output_list, link) {
1397 if (strcmp(temp->name, output->name) == 0) {
Scott Moreau1bad5db2012-08-18 01:04:05 -06001398 if (temp->mode)
1399 weston_log("%s mode \"%s\" in config\n",
Scott Moreau8ab5d452012-07-30 19:51:08 -06001400 temp->name, temp->mode);
1401 o = temp;
1402 break;
1403 }
1404 }
1405
Ander Conselvan de Oliveiradc79e6d2012-08-09 16:45:01 +03001406 if (o && o->config == OUTPUT_CONFIG_OFF) {
Scott Moreau8ab5d452012-07-30 19:51:08 -06001407 weston_log("Disabling output %s\n", o->name);
1408
1409 drmModeSetCrtc(ec->drm.fd, output->crtc_id,
1410 0, 0, 0, 0, 0, NULL);
1411 goto err_free;
1412 }
1413
Kristian Høgsberg061c4252012-06-28 11:28:15 -04001414 wl_list_for_each(drm_mode, &output->base.mode_list, base.link) {
Scott Moreau1bad5db2012-08-18 01:04:05 -06001415 if (o && o->config == OUTPUT_CONFIG_MODE &&
1416 o->width == drm_mode->base.width &&
1417 o->height == drm_mode->base.height)
Ander Conselvan de Oliveira42c46462012-08-09 16:45:00 +03001418 configured = drm_mode;
Wang Quanxianacb805a2012-07-30 18:09:46 -04001419 if (!memcmp(&crtc_mode, &drm_mode->mode_info, sizeof crtc_mode))
Ander Conselvan de Oliveira42c46462012-08-09 16:45:00 +03001420 current = drm_mode;
Kristian Høgsberg061c4252012-06-28 11:28:15 -04001421 if (drm_mode->base.flags & WL_OUTPUT_MODE_PREFERRED)
Ander Conselvan de Oliveira42c46462012-08-09 16:45:00 +03001422 preferred = drm_mode;
David Herrmann0f0d54e2011-12-08 17:05:45 +01001423 }
Kristian Høgsberg8f0ce052011-06-21 11:16:58 -04001424
Scott Moreau8f37e0b2012-07-31 15:30:41 -06001425 if (o && o->config == OUTPUT_CONFIG_MODELINE) {
Ander Conselvan de Oliveira42c46462012-08-09 16:45:00 +03001426 configured = drm_output_add_mode(output, &o->crtc_mode);
1427 if (!configured)
Scott Moreau8f37e0b2012-07-31 15:30:41 -06001428 goto err_free;
Scott Moreau8f37e0b2012-07-31 15:30:41 -06001429 current = configured;
1430 }
1431
Wang Quanxianacb805a2012-07-30 18:09:46 -04001432 if (current == NULL && crtc_mode.clock != 0) {
Ander Conselvan de Oliveira42c46462012-08-09 16:45:00 +03001433 current = drm_output_add_mode(output, &crtc_mode);
1434 if (!current)
Kristian Høgsberg061c4252012-06-28 11:28:15 -04001435 goto err_free;
Kristian Høgsberg061c4252012-06-28 11:28:15 -04001436 }
1437
Scott Moreau8ab5d452012-07-30 19:51:08 -06001438 if (o && o->config == OUTPUT_CONFIG_CURRENT)
1439 configured = current;
1440
Wang Quanxianacb805a2012-07-30 18:09:46 -04001441 if (option_current_mode && current)
Ander Conselvan de Oliveira42c46462012-08-09 16:45:00 +03001442 output->base.current = &current->base;
Scott Moreau8ab5d452012-07-30 19:51:08 -06001443 else if (configured)
Ander Conselvan de Oliveira42c46462012-08-09 16:45:00 +03001444 output->base.current = &configured->base;
Wang Quanxianacb805a2012-07-30 18:09:46 -04001445 else if (preferred)
Ander Conselvan de Oliveira42c46462012-08-09 16:45:00 +03001446 output->base.current = &preferred->base;
Wang Quanxianacb805a2012-07-30 18:09:46 -04001447 else if (current)
Ander Conselvan de Oliveira42c46462012-08-09 16:45:00 +03001448 output->base.current = &current->base;
Wang Quanxianacb805a2012-07-30 18:09:46 -04001449
1450 if (output->base.current == NULL) {
1451 weston_log("no available modes for %s\n", output->name);
1452 goto err_free;
Kristian Høgsberg061c4252012-06-28 11:28:15 -04001453 }
Kristian Høgsberg8f0ce052011-06-21 11:16:58 -04001454
Wang Quanxianacb805a2012-07-30 18:09:46 -04001455 output->base.current->flags |= WL_OUTPUT_MODE_CURRENT;
1456
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -04001457 output->surface = gbm_surface_create(ec->gbm,
1458 output->base.current->width,
1459 output->base.current->height,
1460 GBM_FORMAT_XRGB8888,
1461 GBM_BO_USE_SCANOUT |
1462 GBM_BO_USE_RENDERING);
1463 if (!output->surface) {
Martin Minarik6d118362012-06-07 18:01:59 +02001464 weston_log("failed to create gbm surface\n");
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -04001465 goto err_free;
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001466 }
1467
Kristian Høgsbergd7c17262012-09-05 21:54:15 -04001468 output->base.egl_surface =
Kristian Høgsberg362b6722012-06-18 15:13:51 -04001469 eglCreateWindowSurface(ec->base.egl_display,
1470 ec->base.egl_config,
1471 output->surface,
1472 NULL);
Kristian Høgsbergd7c17262012-09-05 21:54:15 -04001473 if (output->base.egl_surface == EGL_NO_SURFACE) {
Martin Minarik6d118362012-06-07 18:01:59 +02001474 weston_log("failed to create egl surface\n");
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -04001475 goto err_surface;
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001476 }
1477
Kristian Høgsberg8e1f77f2012-05-03 11:39:35 -04001478 output->cursor_bo[0] =
1479 gbm_bo_create(ec->gbm, 64, 64, GBM_FORMAT_ARGB8888,
1480 GBM_BO_USE_CURSOR_64X64 | GBM_BO_USE_WRITE);
1481 output->cursor_bo[1] =
1482 gbm_bo_create(ec->gbm, 64, 64, GBM_FORMAT_ARGB8888,
1483 GBM_BO_USE_CURSOR_64X64 | GBM_BO_USE_WRITE);
Kristian Høgsberg1d1e0a52012-10-21 13:29:26 -04001484 if (output->cursor_bo[0] == NULL || output->cursor_bo[1] == NULL) {
1485 weston_log("cursor buffers unavailable, using gl cursors\n");
1486 ec->cursors_are_broken = 1;
1487 }
Kristian Høgsberg8e1f77f2012-05-03 11:39:35 -04001488
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +02001489 output->backlight = backlight_init(drm_device,
1490 connector->connector_type);
1491 if (output->backlight) {
1492 output->base.set_backlight = drm_set_backlight;
1493 output->base.backlight_current = drm_get_backlight(output);
1494 }
1495
Kristian Høgsberg8334bc12012-01-03 10:29:47 -05001496 weston_output_init(&output->base, &ec->base, x, y,
Scott Moreau1bad5db2012-08-18 01:04:05 -06001497 connector->mmWidth, connector->mmHeight,
1498 o ? o->transform : WL_OUTPUT_TRANSFORM_NORMAL);
Kristian Høgsberga4b7e202011-10-24 13:26:32 -04001499
1500 wl_list_insert(ec->base.output_list.prev, &output->base.link);
1501
Alex Wubd3354b2012-04-17 17:20:49 +08001502 output->base.origin = output->base.current;
Kristian Høgsberg68c479a2012-01-25 23:32:28 -05001503 output->base.repaint = drm_output_repaint;
Matt Roper361d2ad2011-08-29 13:52:23 -07001504 output->base.destroy = drm_output_destroy;
Jesse Barnes58ef3792012-02-23 09:45:49 -05001505 output->base.assign_planes = drm_assign_planes;
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +02001506 output->base.set_dpms = drm_set_dpms;
Alex Wub7b8bda2012-04-17 17:20:48 +08001507 output->base.switch_mode = drm_output_switch_mode;
Benjamin Franzkeeefc36c2011-03-11 16:39:20 +01001508
Kristian Høgsberg65a11e12012-08-03 11:30:18 -04001509 weston_plane_init(&output->cursor_plane, 0, 0);
1510 weston_plane_init(&output->fb_plane, 0, 0);
1511
Kristian Høgsberg2f9ed712012-07-26 17:57:15 -04001512 weston_log("Output %s, (connector %d, crtc %d)\n",
1513 output->name, output->connector_id, output->crtc_id);
Kristian Høgsberg061c4252012-06-28 11:28:15 -04001514 wl_list_for_each(m, &output->base.mode_list, link)
1515 weston_log_continue(" mode %dx%d@%.1f%s%s%s\n",
1516 m->width, m->height, m->refresh / 1000.0,
1517 m->flags & WL_OUTPUT_MODE_PREFERRED ?
1518 ", preferred" : "",
1519 m->flags & WL_OUTPUT_MODE_CURRENT ?
1520 ", current" : "",
1521 connector->count_modes == 0 ?
1522 ", built-in" : "");
Kristian Høgsbergfc9c5e02012-06-08 16:45:33 -04001523
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001524 return 0;
David Herrmann0f0d54e2011-12-08 17:05:45 +01001525
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -04001526err_surface:
1527 gbm_surface_destroy(output->surface);
David Herrmann0f0d54e2011-12-08 17:05:45 +01001528err_free:
1529 wl_list_for_each_safe(drm_mode, next, &output->base.mode_list,
1530 base.link) {
1531 wl_list_remove(&drm_mode->base.link);
1532 free(drm_mode);
1533 }
1534
1535 drmModeFreeCrtc(output->original_crtc);
1536 ec->crtc_allocator &= ~(1 << output->crtc_id);
1537 ec->connector_allocator &= ~(1 << output->connector_id);
Kristian Høgsberg148ef012012-07-26 23:03:57 -04001538 free(output->name);
David Herrmann0f0d54e2011-12-08 17:05:45 +01001539 free(output);
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -04001540
David Herrmann0f0d54e2011-12-08 17:05:45 +01001541 return -1;
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001542}
1543
Jesse Barnes58ef3792012-02-23 09:45:49 -05001544static void
1545create_sprites(struct drm_compositor *ec)
1546{
1547 struct drm_sprite *sprite;
1548 drmModePlaneRes *plane_res;
1549 drmModePlane *plane;
Kristian Høgsberg875ab9e2012-03-30 11:52:39 -04001550 uint32_t i;
Jesse Barnes58ef3792012-02-23 09:45:49 -05001551
1552 plane_res = drmModeGetPlaneResources(ec->drm.fd);
1553 if (!plane_res) {
Martin Minarik6d118362012-06-07 18:01:59 +02001554 weston_log("failed to get plane resources: %s\n",
Jesse Barnes58ef3792012-02-23 09:45:49 -05001555 strerror(errno));
1556 return;
1557 }
1558
1559 for (i = 0; i < plane_res->count_planes; i++) {
1560 plane = drmModeGetPlane(ec->drm.fd, plane_res->planes[i]);
1561 if (!plane)
1562 continue;
1563
1564 sprite = malloc(sizeof(*sprite) + ((sizeof(uint32_t)) *
1565 plane->count_formats));
1566 if (!sprite) {
Martin Minarik6d118362012-06-07 18:01:59 +02001567 weston_log("%s: out of memory\n",
Jesse Barnes58ef3792012-02-23 09:45:49 -05001568 __func__);
1569 free(plane);
1570 continue;
1571 }
1572
1573 memset(sprite, 0, sizeof *sprite);
1574
1575 sprite->possible_crtcs = plane->possible_crtcs;
1576 sprite->plane_id = plane->plane_id;
1577 sprite->surface = NULL;
1578 sprite->pending_surface = NULL;
1579 sprite->fb_id = 0;
1580 sprite->pending_fb_id = 0;
Kristian Høgsberg27e30522012-04-11 23:18:23 -04001581 sprite->destroy_listener.notify = sprite_handle_buffer_destroy;
1582 sprite->pending_destroy_listener.notify =
Jesse Barnes58ef3792012-02-23 09:45:49 -05001583 sprite_handle_pending_buffer_destroy;
1584 sprite->compositor = ec;
1585 sprite->count_formats = plane->count_formats;
1586 memcpy(sprite->formats, plane->formats,
Rob Clark8efbc8e2012-03-11 19:48:44 -05001587 plane->count_formats * sizeof(plane->formats[0]));
Jesse Barnes58ef3792012-02-23 09:45:49 -05001588 drmModeFreePlane(plane);
Kristian Høgsberg65a11e12012-08-03 11:30:18 -04001589 weston_plane_init(&sprite->plane, 0, 0);
Jesse Barnes58ef3792012-02-23 09:45:49 -05001590
1591 wl_list_insert(&ec->sprite_list, &sprite->link);
1592 }
1593
1594 free(plane_res->planes);
1595 free(plane_res);
1596}
1597
Kristian Høgsberg85fd3272012-02-23 21:45:32 -05001598static void
1599destroy_sprites(struct drm_compositor *compositor)
1600{
1601 struct drm_sprite *sprite, *next;
1602 struct drm_output *output;
1603
1604 output = container_of(compositor->base.output_list.next,
1605 struct drm_output, base.link);
1606
1607 wl_list_for_each_safe(sprite, next, &compositor->sprite_list, link) {
1608 drmModeSetPlane(compositor->drm.fd,
1609 sprite->plane_id,
1610 output->crtc_id, 0, 0,
1611 0, 0, 0, 0, 0, 0, 0, 0);
1612 drmModeRmFB(compositor->drm.fd, sprite->fb_id);
Kristian Høgsberg65a11e12012-08-03 11:30:18 -04001613 weston_plane_release(&sprite->plane);
Kristian Høgsberg85fd3272012-02-23 21:45:32 -05001614 free(sprite);
1615 }
1616}
Jesse Barnes58ef3792012-02-23 09:45:49 -05001617
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001618static int
Kristian Høgsberg875ab9e2012-03-30 11:52:39 -04001619create_outputs(struct drm_compositor *ec, uint32_t option_connector,
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +02001620 struct udev_device *drm_device)
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001621{
1622 drmModeConnector *connector;
1623 drmModeRes *resources;
1624 int i;
Benjamin Franzkeeefc36c2011-03-11 16:39:20 +01001625 int x = 0, y = 0;
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001626
Benjamin Franzke2af7f102011-03-02 11:14:59 +01001627 resources = drmModeGetResources(ec->drm.fd);
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001628 if (!resources) {
Martin Minarik6d118362012-06-07 18:01:59 +02001629 weston_log("drmModeGetResources failed\n");
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001630 return -1;
1631 }
1632
Jesse Barnes58ef3792012-02-23 09:45:49 -05001633 ec->crtcs = calloc(resources->count_crtcs, sizeof(uint32_t));
Christopher Michaeleb866cd2012-03-07 14:55:21 -05001634 if (!ec->crtcs) {
1635 drmModeFreeResources(resources);
Jesse Barnes58ef3792012-02-23 09:45:49 -05001636 return -1;
Christopher Michaeleb866cd2012-03-07 14:55:21 -05001637 }
Jesse Barnes58ef3792012-02-23 09:45:49 -05001638
Rob Clark4339add2012-08-09 14:18:28 -05001639 ec->min_width = resources->min_width;
1640 ec->max_width = resources->max_width;
1641 ec->min_height = resources->min_height;
1642 ec->max_height = resources->max_height;
1643
Jesse Barnes58ef3792012-02-23 09:45:49 -05001644 ec->num_crtcs = resources->count_crtcs;
1645 memcpy(ec->crtcs, resources->crtcs, sizeof(uint32_t) * ec->num_crtcs);
1646
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001647 for (i = 0; i < resources->count_connectors; i++) {
Benjamin Franzke117483d2011-08-30 11:38:26 +02001648 connector = drmModeGetConnector(ec->drm.fd,
1649 resources->connectors[i]);
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001650 if (connector == NULL)
1651 continue;
1652
1653 if (connector->connection == DRM_MODE_CONNECTED &&
1654 (option_connector == 0 ||
Benjamin Franzke9eaee352011-08-02 13:03:54 +02001655 connector->connector_id == option_connector)) {
Benjamin Franzkeeefc36c2011-03-11 16:39:20 +01001656 if (create_output_for_connector(ec, resources,
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +02001657 connector, x, y,
1658 drm_device) < 0) {
Benjamin Franzke439d9862011-10-07 08:20:53 +02001659 drmModeFreeConnector(connector);
1660 continue;
1661 }
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001662
Benjamin Franzke9eaee352011-08-02 13:03:54 +02001663 x += container_of(ec->base.output_list.prev,
Kristian Høgsberg8334bc12012-01-03 10:29:47 -05001664 struct weston_output,
Scott Moreau1bad5db2012-08-18 01:04:05 -06001665 link)->width;
Benjamin Franzke9eaee352011-08-02 13:03:54 +02001666 }
Benjamin Franzkeeefc36c2011-03-11 16:39:20 +01001667
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001668 drmModeFreeConnector(connector);
1669 }
1670
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04001671 if (wl_list_empty(&ec->base.output_list)) {
Martin Minarik6d118362012-06-07 18:01:59 +02001672 weston_log("No currently active connector found.\n");
Christopher Michaeleb866cd2012-03-07 14:55:21 -05001673 drmModeFreeResources(resources);
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001674 return -1;
1675 }
1676
1677 drmModeFreeResources(resources);
1678
1679 return 0;
1680}
1681
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001682static void
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +02001683update_outputs(struct drm_compositor *ec, struct udev_device *drm_device)
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001684{
1685 drmModeConnector *connector;
1686 drmModeRes *resources;
1687 struct drm_output *output, *next;
1688 int x = 0, y = 0;
1689 int x_offset = 0, y_offset = 0;
1690 uint32_t connected = 0, disconnects = 0;
1691 int i;
1692
1693 resources = drmModeGetResources(ec->drm.fd);
1694 if (!resources) {
Martin Minarik6d118362012-06-07 18:01:59 +02001695 weston_log("drmModeGetResources failed\n");
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001696 return;
1697 }
1698
1699 /* collect new connects */
1700 for (i = 0; i < resources->count_connectors; i++) {
Benjamin Franzke117483d2011-08-30 11:38:26 +02001701 int connector_id = resources->connectors[i];
1702
1703 connector = drmModeGetConnector(ec->drm.fd, connector_id);
David Herrmann7551cff2011-12-08 17:05:43 +01001704 if (connector == NULL)
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001705 continue;
1706
David Herrmann7551cff2011-12-08 17:05:43 +01001707 if (connector->connection != DRM_MODE_CONNECTED) {
1708 drmModeFreeConnector(connector);
1709 continue;
1710 }
1711
Benjamin Franzke117483d2011-08-30 11:38:26 +02001712 connected |= (1 << connector_id);
1713
1714 if (!(ec->connector_allocator & (1 << connector_id))) {
Kristian Høgsberg8334bc12012-01-03 10:29:47 -05001715 struct weston_output *last =
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001716 container_of(ec->base.output_list.prev,
Kristian Høgsberg8334bc12012-01-03 10:29:47 -05001717 struct weston_output, link);
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001718
1719 /* XXX: not yet needed, we die with 0 outputs */
1720 if (!wl_list_empty(&ec->base.output_list))
Scott Moreau1bad5db2012-08-18 01:04:05 -06001721 x = last->x + last->width;
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001722 else
1723 x = 0;
1724 y = 0;
1725 create_output_for_connector(ec, resources,
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +02001726 connector, x, y,
1727 drm_device);
Martin Minarik6d118362012-06-07 18:01:59 +02001728 weston_log("connector %d connected\n", connector_id);
Benjamin Franzke117483d2011-08-30 11:38:26 +02001729
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001730 }
1731 drmModeFreeConnector(connector);
1732 }
1733 drmModeFreeResources(resources);
1734
1735 disconnects = ec->connector_allocator & ~connected;
1736 if (disconnects) {
1737 wl_list_for_each_safe(output, next, &ec->base.output_list,
1738 base.link) {
1739 if (x_offset != 0 || y_offset != 0) {
Kristian Høgsberg8334bc12012-01-03 10:29:47 -05001740 weston_output_move(&output->base,
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001741 output->base.x - x_offset,
1742 output->base.y - y_offset);
1743 }
1744
1745 if (disconnects & (1 << output->connector_id)) {
1746 disconnects &= ~(1 << output->connector_id);
Martin Minarik6d118362012-06-07 18:01:59 +02001747 weston_log("connector %d disconnected\n",
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001748 output->connector_id);
Scott Moreau1bad5db2012-08-18 01:04:05 -06001749 x_offset += output->base.width;
Benjamin Franzke48c4ea22011-08-30 11:44:56 +02001750 drm_output_destroy(&output->base);
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001751 }
1752 }
1753 }
1754
1755 /* FIXME: handle zero outputs, without terminating */
1756 if (ec->connector_allocator == 0)
1757 wl_display_terminate(ec->base.wl_display);
1758}
1759
1760static int
David Herrmannd7488c22012-03-11 20:05:21 +01001761udev_event_is_hotplug(struct drm_compositor *ec, struct udev_device *device)
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001762{
David Herrmannd7488c22012-03-11 20:05:21 +01001763 const char *sysnum;
David Herrmann6ac52db2012-03-11 20:05:22 +01001764 const char *val;
David Herrmannd7488c22012-03-11 20:05:21 +01001765
1766 sysnum = udev_device_get_sysnum(device);
1767 if (!sysnum || atoi(sysnum) != ec->drm.id)
1768 return 0;
Benjamin Franzke117483d2011-08-30 11:38:26 +02001769
David Herrmann6ac52db2012-03-11 20:05:22 +01001770 val = udev_device_get_property_value(device, "HOTPLUG");
1771 if (!val)
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001772 return 0;
1773
David Herrmann6ac52db2012-03-11 20:05:22 +01001774 return strcmp(val, "1") == 0;
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001775}
1776
Kristian Høgsbergb1868472011-04-22 12:27:57 -04001777static int
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001778udev_drm_event(int fd, uint32_t mask, void *data)
1779{
1780 struct drm_compositor *ec = data;
1781 struct udev_device *event;
1782
1783 event = udev_monitor_receive_device(ec->udev_monitor);
Benjamin Franzke117483d2011-08-30 11:38:26 +02001784
David Herrmannd7488c22012-03-11 20:05:21 +01001785 if (udev_event_is_hotplug(ec, event))
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +02001786 update_outputs(ec, event);
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001787
1788 udev_device_unref(event);
Kristian Høgsbergb1868472011-04-22 12:27:57 -04001789
1790 return 1;
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001791}
1792
Kristian Høgsbergcaa64422010-12-01 16:52:15 -05001793static void
Kristian Høgsberg7b884bc2012-07-31 14:32:01 -04001794drm_restore(struct weston_compositor *ec)
1795{
1796 struct drm_compositor *d = (struct drm_compositor *) ec;
1797
1798 if (weston_launcher_drm_set_master(&d->base, d->drm.fd, 0) < 0)
1799 weston_log("failed to drop master: %m\n");
1800 tty_reset(d->tty);
1801}
1802
Pekka Paalanen33156972012-08-03 13:30:30 -04001803static const char default_seat[] = "seat0";
1804
1805static void
1806device_added(struct udev_device *udev_device, struct drm_seat *master)
1807{
1808 struct weston_compositor *c;
Pekka Paalanen3eb47612012-08-06 14:57:08 +03001809 struct evdev_device *device;
Pekka Paalanen33156972012-08-03 13:30:30 -04001810 const char *devnode;
1811 const char *device_seat;
1812 int fd;
1813
1814 device_seat = udev_device_get_property_value(udev_device, "ID_SEAT");
1815 if (!device_seat)
1816 device_seat = default_seat;
1817
1818 if (strcmp(device_seat, master->seat_id))
1819 return;
1820
1821 c = master->base.compositor;
1822 devnode = udev_device_get_devnode(udev_device);
1823
1824 /* Use non-blocking mode so that we can loop on read on
Pekka Paalanen3eb47612012-08-06 14:57:08 +03001825 * evdev_device_data() until all events on the fd are
Pekka Paalanen33156972012-08-03 13:30:30 -04001826 * read. mtdev_get() also expects this. */
1827 fd = weston_launcher_open(c, devnode, O_RDWR | O_NONBLOCK);
1828 if (fd < 0) {
1829 weston_log("opening input device '%s' failed.\n", devnode);
1830 return;
1831 }
1832
Pekka Paalanen3eb47612012-08-06 14:57:08 +03001833 device = evdev_device_create(&master->base, devnode, fd);
Pekka Paalanen33156972012-08-03 13:30:30 -04001834 if (!device) {
1835 close(fd);
1836 weston_log("not using input device '%s'.\n", devnode);
1837 return;
1838 }
1839
1840 wl_list_insert(master->devices_list.prev, &device->link);
1841}
1842
1843static void
1844evdev_add_devices(struct udev *udev, struct weston_seat *seat_base)
1845{
1846 struct drm_seat *seat = (struct drm_seat *) seat_base;
1847 struct udev_enumerate *e;
1848 struct udev_list_entry *entry;
1849 struct udev_device *device;
1850 const char *path, *sysname;
1851
1852 e = udev_enumerate_new(udev);
1853 udev_enumerate_add_match_subsystem(e, "input");
1854 udev_enumerate_scan_devices(e);
1855 udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
1856 path = udev_list_entry_get_name(entry);
1857 device = udev_device_new_from_syspath(udev, path);
1858
1859 sysname = udev_device_get_sysname(device);
1860 if (strncmp("event", sysname, 5) != 0) {
1861 udev_device_unref(device);
1862 continue;
1863 }
1864
1865 device_added(device, seat);
1866
1867 udev_device_unref(device);
1868 }
1869 udev_enumerate_unref(e);
1870
1871 evdev_notify_keyboard_focus(&seat->base, &seat->devices_list);
1872
1873 if (wl_list_empty(&seat->devices_list)) {
1874 weston_log(
1875 "warning: no input devices on entering Weston. "
1876 "Possible causes:\n"
1877 "\t- no permissions to read /dev/input/event*\n"
1878 "\t- seats misconfigured "
1879 "(Weston backend option 'seat', "
1880 "udev device property ID_SEAT)\n");
1881 }
1882}
1883
1884static int
1885evdev_udev_handler(int fd, uint32_t mask, void *data)
1886{
Pekka Paalanend2e69c22012-08-03 14:39:08 +03001887 struct drm_seat *seat = data;
Pekka Paalanen33156972012-08-03 13:30:30 -04001888 struct udev_device *udev_device;
Pekka Paalanen3eb47612012-08-06 14:57:08 +03001889 struct evdev_device *device, *next;
Pekka Paalanen33156972012-08-03 13:30:30 -04001890 const char *action;
1891 const char *devnode;
1892
Pekka Paalanend2e69c22012-08-03 14:39:08 +03001893 udev_device = udev_monitor_receive_device(seat->udev_monitor);
Pekka Paalanen33156972012-08-03 13:30:30 -04001894 if (!udev_device)
1895 return 1;
1896
1897 action = udev_device_get_action(udev_device);
Pekka Paalanend2e69c22012-08-03 14:39:08 +03001898 if (!action)
1899 goto out;
Pekka Paalanen33156972012-08-03 13:30:30 -04001900
Pekka Paalanend2e69c22012-08-03 14:39:08 +03001901 if (strncmp("event", udev_device_get_sysname(udev_device), 5) != 0)
1902 goto out;
1903
1904 if (!strcmp(action, "add")) {
1905 device_added(udev_device, seat);
Pekka Paalanen33156972012-08-03 13:30:30 -04001906 }
Pekka Paalanend2e69c22012-08-03 14:39:08 +03001907 else if (!strcmp(action, "remove")) {
1908 devnode = udev_device_get_devnode(udev_device);
1909 wl_list_for_each_safe(device, next, &seat->devices_list, link)
1910 if (!strcmp(device->devnode, devnode)) {
Pekka Paalanen42b3f6a2012-08-03 14:39:09 +03001911 weston_log("input device %s, %s removed\n",
1912 device->devname, device->devnode);
Pekka Paalanen3eb47612012-08-06 14:57:08 +03001913 evdev_device_destroy(device);
Pekka Paalanend2e69c22012-08-03 14:39:08 +03001914 break;
1915 }
1916 }
1917
1918out:
Pekka Paalanen33156972012-08-03 13:30:30 -04001919 udev_device_unref(udev_device);
1920
1921 return 0;
1922}
1923
1924static int
1925evdev_enable_udev_monitor(struct udev *udev, struct weston_seat *seat_base)
1926{
1927 struct drm_seat *master = (struct drm_seat *) seat_base;
1928 struct wl_event_loop *loop;
1929 struct weston_compositor *c = master->base.compositor;
1930 int fd;
1931
1932 master->udev_monitor = udev_monitor_new_from_netlink(udev, "udev");
1933 if (!master->udev_monitor) {
1934 weston_log("udev: failed to create the udev monitor\n");
1935 return 0;
1936 }
1937
1938 udev_monitor_filter_add_match_subsystem_devtype(master->udev_monitor,
1939 "input", NULL);
1940
1941 if (udev_monitor_enable_receiving(master->udev_monitor)) {
1942 weston_log("udev: failed to bind the udev monitor\n");
1943 udev_monitor_unref(master->udev_monitor);
1944 return 0;
1945 }
1946
1947 loop = wl_display_get_event_loop(c->wl_display);
1948 fd = udev_monitor_get_fd(master->udev_monitor);
1949 master->udev_monitor_source =
1950 wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE,
1951 evdev_udev_handler, master);
1952 if (!master->udev_monitor_source) {
1953 udev_monitor_unref(master->udev_monitor);
1954 return 0;
1955 }
1956
1957 return 1;
1958}
1959
1960static void
1961evdev_disable_udev_monitor(struct weston_seat *seat_base)
1962{
1963 struct drm_seat *seat = (struct drm_seat *) seat_base;
1964
1965 if (!seat->udev_monitor)
1966 return;
1967
1968 udev_monitor_unref(seat->udev_monitor);
1969 seat->udev_monitor = NULL;
1970 wl_event_source_remove(seat->udev_monitor_source);
1971 seat->udev_monitor_source = NULL;
1972}
1973
1974static void
1975drm_led_update(struct weston_seat *seat_base, enum weston_led leds)
1976{
1977 struct drm_seat *seat = (struct drm_seat *) seat_base;
Pekka Paalanen3eb47612012-08-06 14:57:08 +03001978 struct evdev_device *device;
Pekka Paalanen33156972012-08-03 13:30:30 -04001979
Pekka Paalanenb9d38f42012-08-06 14:57:07 +03001980 wl_list_for_each(device, &seat->devices_list, link)
1981 evdev_led_update(device, leds);
Pekka Paalanen33156972012-08-03 13:30:30 -04001982}
1983
1984static void
1985evdev_input_create(struct weston_compositor *c, struct udev *udev,
1986 const char *seat_id)
1987{
1988 struct drm_seat *seat;
1989
1990 seat = malloc(sizeof *seat);
1991 if (seat == NULL)
1992 return;
1993
1994 memset(seat, 0, sizeof *seat);
1995 weston_seat_init(&seat->base, c);
1996 seat->base.led_update = drm_led_update;
1997
1998 wl_list_init(&seat->devices_list);
1999 seat->seat_id = strdup(seat_id);
2000 if (!evdev_enable_udev_monitor(udev, &seat->base)) {
2001 free(seat->seat_id);
2002 free(seat);
2003 return;
2004 }
2005
2006 evdev_add_devices(udev, &seat->base);
Pekka Paalanen33156972012-08-03 13:30:30 -04002007}
2008
2009static void
2010evdev_remove_devices(struct weston_seat *seat_base)
2011{
2012 struct drm_seat *seat = (struct drm_seat *) seat_base;
Pekka Paalanen3eb47612012-08-06 14:57:08 +03002013 struct evdev_device *device, *next;
Pekka Paalanen33156972012-08-03 13:30:30 -04002014
2015 wl_list_for_each_safe(device, next, &seat->devices_list, link)
Pekka Paalanen3eb47612012-08-06 14:57:08 +03002016 evdev_device_destroy(device);
Pekka Paalanen33156972012-08-03 13:30:30 -04002017
Pekka Paalanend8583512012-08-03 14:39:11 +03002018 if (seat->base.seat.keyboard)
Kristian Høgsbergcb3eaae2012-08-10 09:50:11 -04002019 notify_keyboard_focus_out(&seat->base);
Pekka Paalanen33156972012-08-03 13:30:30 -04002020}
2021
2022static void
2023evdev_input_destroy(struct weston_seat *seat_base)
2024{
2025 struct drm_seat *seat = (struct drm_seat *) seat_base;
2026
2027 evdev_remove_devices(seat_base);
2028 evdev_disable_udev_monitor(&seat->base);
2029
2030 weston_seat_release(seat_base);
2031 free(seat->seat_id);
2032 free(seat);
2033}
2034
Kristian Høgsberg7b884bc2012-07-31 14:32:01 -04002035static void
Scott Moreauc50645c2012-07-31 22:29:56 -06002036drm_free_configured_output(struct drm_configured_output *output)
2037{
2038 free(output->name);
2039 free(output->mode);
2040 free(output);
2041}
2042
2043static void
Kristian Høgsberg8334bc12012-01-03 10:29:47 -05002044drm_destroy(struct weston_compositor *ec)
Kristian Høgsbergcaa64422010-12-01 16:52:15 -05002045{
2046 struct drm_compositor *d = (struct drm_compositor *) ec;
Daniel Stone37816df2012-05-16 18:45:18 +01002047 struct weston_seat *seat, *next;
Scott Moreau8ab5d452012-07-30 19:51:08 -06002048 struct drm_configured_output *o, *n;
Kristian Høgsbergcaa64422010-12-01 16:52:15 -05002049
Daniel Stone37816df2012-05-16 18:45:18 +01002050 wl_list_for_each_safe(seat, next, &ec->seat_list, link)
2051 evdev_input_destroy(seat);
Scott Moreau8ab5d452012-07-30 19:51:08 -06002052 wl_list_for_each_safe(o, n, &configured_output_list, link)
Scott Moreauc50645c2012-07-31 22:29:56 -06002053 drm_free_configured_output(o);
Jonas Ådahlc97af922012-03-28 22:36:09 +02002054
2055 wl_event_source_remove(d->udev_drm_source);
2056 wl_event_source_remove(d->drm_source);
2057
Kristian Høgsberg8334bc12012-01-03 10:29:47 -05002058 weston_compositor_shutdown(ec);
Jonas Ådahlc97af922012-03-28 22:36:09 +02002059
Kristian Høgsberg3a0de882012-09-06 21:44:24 -04002060 gles2_renderer_destroy(ec);
2061
Ander Conselvan de Oliveira5f5f3192012-04-30 13:31:28 +03002062 /* Work around crash in egl_dri2.c's dri2_make_current() */
Kristian Høgsberg362b6722012-06-18 15:13:51 -04002063 eglMakeCurrent(ec->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
Ander Conselvan de Oliveira5f5f3192012-04-30 13:31:28 +03002064 EGL_NO_CONTEXT);
Kristian Høgsberg362b6722012-06-18 15:13:51 -04002065 eglTerminate(ec->egl_display);
Ander Conselvan de Oliveira5f5f3192012-04-30 13:31:28 +03002066 eglReleaseThread();
2067
Matt Roper361d2ad2011-08-29 13:52:23 -07002068 gbm_device_destroy(d->gbm);
Kristian Høgsberg85fd3272012-02-23 21:45:32 -05002069 destroy_sprites(d);
Benjamin Franzkebfeda132012-01-30 14:04:04 +01002070 if (weston_launcher_drm_set_master(&d->base, d->drm.fd, 0) < 0)
Martin Minarik6d118362012-06-07 18:01:59 +02002071 weston_log("failed to drop master: %m\n");
Kristian Høgsberge4762a62011-01-14 14:59:13 -05002072 tty_destroy(d->tty);
Kristian Høgsbergcaa64422010-12-01 16:52:15 -05002073
Kristian Høgsberge4762a62011-01-14 14:59:13 -05002074 free(d);
Kristian Høgsbergcaa64422010-12-01 16:52:15 -05002075}
2076
Kristian Høgsberg9396fc52011-05-06 15:15:37 -04002077static void
Kristian Høgsberg835cd492012-01-18 11:48:46 -05002078drm_compositor_set_modes(struct drm_compositor *compositor)
2079{
2080 struct drm_output *output;
2081 struct drm_mode *drm_mode;
2082 int ret;
2083
2084 wl_list_for_each(output, &compositor->base.output_list, base.link) {
2085 drm_mode = (struct drm_mode *) output->base.current;
2086 ret = drmModeSetCrtc(compositor->drm.fd, output->crtc_id,
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +03002087 output->current->fb_id, 0, 0,
Kristian Høgsberg835cd492012-01-18 11:48:46 -05002088 &output->connector_id, 1,
2089 &drm_mode->mode_info);
2090 if (ret < 0) {
Martin Minarik6d118362012-06-07 18:01:59 +02002091 weston_log(
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -04002092 "failed to set mode %dx%d for output at %d,%d: %m\n",
Kristian Høgsberg835cd492012-01-18 11:48:46 -05002093 drm_mode->base.width, drm_mode->base.height,
2094 output->base.x, output->base.y);
2095 }
2096 }
2097}
2098
2099static void
Kristian Høgsberg8334bc12012-01-03 10:29:47 -05002100vt_func(struct weston_compositor *compositor, int event)
Kristian Høgsberg9396fc52011-05-06 15:15:37 -04002101{
2102 struct drm_compositor *ec = (struct drm_compositor *) compositor;
Daniel Stone37816df2012-05-16 18:45:18 +01002103 struct weston_seat *seat;
Kristian Høgsberg85fd3272012-02-23 21:45:32 -05002104 struct drm_sprite *sprite;
Kristian Høgsberga6edab32012-07-14 01:06:28 -04002105 struct drm_output *output;
Kristian Høgsberg9396fc52011-05-06 15:15:37 -04002106
2107 switch (event) {
2108 case TTY_ENTER_VT:
Pekka Paalanen81a13a32012-08-03 14:39:10 +03002109 weston_log("entering VT\n");
Kristian Høgsberg9396fc52011-05-06 15:15:37 -04002110 compositor->focus = 1;
Benjamin Franzkebfeda132012-01-30 14:04:04 +01002111 if (weston_launcher_drm_set_master(&ec->base, ec->drm.fd, 1)) {
Martin Minarik6d118362012-06-07 18:01:59 +02002112 weston_log("failed to set master: %m\n");
Kristian Høgsberga018fb02012-01-16 10:52:52 -05002113 wl_display_terminate(compositor->wl_display);
2114 }
Pekka Paalanenbce2d3f2011-12-02 13:07:27 +02002115 compositor->state = ec->prev_state;
Kristian Høgsberg835cd492012-01-18 11:48:46 -05002116 drm_compositor_set_modes(ec);
Kristian Høgsberg8334bc12012-01-03 10:29:47 -05002117 weston_compositor_damage_all(compositor);
Daniel Stone37816df2012-05-16 18:45:18 +01002118 wl_list_for_each(seat, &compositor->seat_list, link) {
2119 evdev_add_devices(ec->udev, seat);
2120 evdev_enable_udev_monitor(ec->udev, seat);
Benjamin Franzke78d3afe2012-04-09 18:14:58 +02002121 }
Kristian Høgsberg9396fc52011-05-06 15:15:37 -04002122 break;
2123 case TTY_LEAVE_VT:
Pekka Paalanen81a13a32012-08-03 14:39:10 +03002124 weston_log("leaving VT\n");
Daniel Stone37816df2012-05-16 18:45:18 +01002125 wl_list_for_each(seat, &compositor->seat_list, link) {
2126 evdev_disable_udev_monitor(seat);
2127 evdev_remove_devices(seat);
Kristian Høgsberg4014a6b2012-04-10 00:08:45 -04002128 }
2129
Kristian Høgsberg9396fc52011-05-06 15:15:37 -04002130 compositor->focus = 0;
Pekka Paalanenbce2d3f2011-12-02 13:07:27 +02002131 ec->prev_state = compositor->state;
Kristian Høgsberg8334bc12012-01-03 10:29:47 -05002132 compositor->state = WESTON_COMPOSITOR_SLEEPING;
Kristian Høgsbergd8e181b2011-05-06 15:38:28 -04002133
Kristian Høgsberg34f80ff2012-01-18 11:50:31 -05002134 /* If we have a repaint scheduled (either from a
2135 * pending pageflip or the idle handler), make sure we
2136 * cancel that so we don't try to pageflip when we're
2137 * vt switched away. The SLEEPING state will prevent
2138 * further attemps at repainting. When we switch
2139 * back, we schedule a repaint, which will process
2140 * pending frame callbacks. */
2141
Kristian Høgsberga6edab32012-07-14 01:06:28 -04002142 wl_list_for_each(output, &ec->base.output_list, base.link) {
2143 output->base.repaint_needed = 0;
2144 drmModeSetCursor(ec->drm.fd, output->crtc_id, 0, 0, 0);
Kristian Høgsberg34f80ff2012-01-18 11:50:31 -05002145 }
2146
Kristian Høgsberga6edab32012-07-14 01:06:28 -04002147 output = container_of(ec->base.output_list.next,
2148 struct drm_output, base.link);
Kristian Høgsberg85fd3272012-02-23 21:45:32 -05002149
2150 wl_list_for_each(sprite, &ec->sprite_list, link)
2151 drmModeSetPlane(ec->drm.fd,
2152 sprite->plane_id,
Kristian Høgsberga6edab32012-07-14 01:06:28 -04002153 output->crtc_id, 0, 0,
Kristian Høgsberg85fd3272012-02-23 21:45:32 -05002154 0, 0, 0, 0, 0, 0, 0, 0);
2155
Benjamin Franzkebfeda132012-01-30 14:04:04 +01002156 if (weston_launcher_drm_set_master(&ec->base, ec->drm.fd, 0) < 0)
Martin Minarik6d118362012-06-07 18:01:59 +02002157 weston_log("failed to drop master: %m\n");
Kristian Høgsberga018fb02012-01-16 10:52:52 -05002158
Kristian Høgsberg9396fc52011-05-06 15:15:37 -04002159 break;
2160 };
2161}
2162
Kristian Høgsberg5d1c0c52012-04-10 00:11:50 -04002163static void
Daniel Stone325fc2d2012-05-30 16:31:58 +01002164switch_vt_binding(struct wl_seat *seat, uint32_t time, uint32_t key, void *data)
Kristian Høgsberg5d1c0c52012-04-10 00:11:50 -04002165{
2166 struct drm_compositor *ec = data;
2167
Daniel Stone325fc2d2012-05-30 16:31:58 +01002168 tty_activate_vt(ec->tty, key - KEY_F1 + 1);
Kristian Høgsberg5d1c0c52012-04-10 00:11:50 -04002169}
2170
David Herrmann0af066f2012-10-29 19:21:16 +01002171/*
2172 * Find primary GPU
2173 * Some systems may have multiple DRM devices attached to a single seat. This
2174 * function loops over all devices and tries to find a PCI device with the
2175 * boot_vga sysfs attribute set to 1.
2176 * If no such device is found, the first DRM device reported by udev is used.
2177 */
2178static struct udev_device*
2179find_primary_gpu(struct drm_compositor *ec, const char *seat)
2180{
2181 struct udev_enumerate *e;
2182 struct udev_list_entry *entry;
2183 const char *path, *device_seat, *id;
2184 struct udev_device *device, *drm_device, *pci;
2185
2186 e = udev_enumerate_new(ec->udev);
2187 udev_enumerate_add_match_subsystem(e, "drm");
2188 udev_enumerate_add_match_sysname(e, "card[0-9]*");
2189
2190 udev_enumerate_scan_devices(e);
2191 drm_device = NULL;
2192 udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
2193 path = udev_list_entry_get_name(entry);
2194 device = udev_device_new_from_syspath(ec->udev, path);
2195 if (!device)
2196 continue;
2197 device_seat = udev_device_get_property_value(device, "ID_SEAT");
2198 if (!device_seat)
2199 device_seat = default_seat;
2200 if (strcmp(device_seat, seat)) {
2201 udev_device_unref(device);
2202 continue;
2203 }
2204
2205 pci = udev_device_get_parent_with_subsystem_devtype(device,
2206 "pci", NULL);
2207 if (pci) {
2208 id = udev_device_get_sysattr_value(pci, "boot_vga");
2209 if (id && !strcmp(id, "1")) {
2210 if (drm_device)
2211 udev_device_unref(drm_device);
2212 drm_device = device;
2213 break;
2214 }
2215 }
2216
2217 if (!drm_device)
2218 drm_device = device;
2219 else
2220 udev_device_unref(device);
2221 }
2222
2223 udev_enumerate_unref(e);
2224 return drm_device;
2225}
2226
Ander Conselvan de Oliveirada1c9082012-10-31 17:55:46 +02002227static void
2228hide_sprites_binding(struct wl_seat *seat, uint32_t time, uint32_t key,
2229 void *data)
2230{
2231 struct drm_compositor *c = data;
2232
2233 c->sprites_hidden ^= 1;
2234}
2235
Kristian Høgsberg8334bc12012-01-03 10:29:47 -05002236static struct weston_compositor *
Kristian Høgsberg8d51f142011-07-15 21:28:38 -04002237drm_compositor_create(struct wl_display *display,
Daniel Stonebaf43592012-06-01 11:11:10 -04002238 int connector, const char *seat, int tty,
Daniel Stonec1be8e52012-06-01 11:14:02 -04002239 int argc, char *argv[], const char *config_file)
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04002240{
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04002241 struct drm_compositor *ec;
David Herrmann0af066f2012-10-29 19:21:16 +01002242 struct udev_device *drm_device;
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04002243 struct wl_event_loop *loop;
Daniel Stonea96b93c2012-06-22 14:04:37 +01002244 struct weston_seat *weston_seat, *next;
David Herrmann0af066f2012-10-29 19:21:16 +01002245 const char *path;
Kristian Høgsberg5d1c0c52012-04-10 00:11:50 -04002246 uint32_t key;
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04002247
Kristian Høgsbergfc9c5e02012-06-08 16:45:33 -04002248 weston_log("initializing drm backend\n");
2249
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04002250 ec = malloc(sizeof *ec);
2251 if (ec == NULL)
2252 return NULL;
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04002253 memset(ec, 0, sizeof *ec);
Daniel Stone725c2c32012-06-22 14:04:36 +01002254
2255 if (weston_compositor_init(&ec->base, display, argc, argv,
Daniel Stonea96b93c2012-06-22 14:04:37 +01002256 config_file) < 0) {
2257 weston_log("weston_compositor_init failed\n");
2258 goto err_base;
2259 }
Daniel Stone725c2c32012-06-22 14:04:36 +01002260
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04002261 ec->udev = udev_new();
2262 if (ec->udev == NULL) {
Martin Minarik6d118362012-06-07 18:01:59 +02002263 weston_log("failed to initialize udev context\n");
Daniel Stonea96b93c2012-06-22 14:04:37 +01002264 goto err_compositor;
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04002265 }
2266
Kristian Høgsbergc5b9ddb2012-01-15 14:29:09 -05002267 ec->base.wl_display = display;
2268 ec->tty = tty_create(&ec->base, vt_func, tty);
2269 if (!ec->tty) {
Martin Minarik6d118362012-06-07 18:01:59 +02002270 weston_log("failed to initialize tty\n");
Daniel Stonea96b93c2012-06-22 14:04:37 +01002271 goto err_udev;
Kristian Høgsbergc5b9ddb2012-01-15 14:29:09 -05002272 }
2273
David Herrmann0af066f2012-10-29 19:21:16 +01002274 drm_device = find_primary_gpu(ec, seat);
Kristian Høgsberg8d51f142011-07-15 21:28:38 -04002275 if (drm_device == NULL) {
Martin Minarik6d118362012-06-07 18:01:59 +02002276 weston_log("no drm device found\n");
David Herrmann0af066f2012-10-29 19:21:16 +01002277 goto err_tty;
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04002278 }
David Herrmann0af066f2012-10-29 19:21:16 +01002279 path = udev_device_get_syspath(drm_device);
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04002280
Kristian Høgsberg8d51f142011-07-15 21:28:38 -04002281 if (init_egl(ec, drm_device) < 0) {
Martin Minarik6d118362012-06-07 18:01:59 +02002282 weston_log("failed to initialize egl\n");
Daniel Stonea96b93c2012-06-22 14:04:37 +01002283 goto err_udev_dev;
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04002284 }
Kristian Høgsberg8525a502011-01-14 16:20:21 -05002285
2286 ec->base.destroy = drm_destroy;
Kristian Høgsberg7b884bc2012-07-31 14:32:01 -04002287 ec->base.restore = drm_restore;
Benjamin Franzke431da9a2011-04-20 11:02:58 +02002288
Kristian Høgsberg8525a502011-01-14 16:20:21 -05002289 ec->base.focus = 1;
2290
Kristian Høgsberg8334bc12012-01-03 10:29:47 -05002291 ec->prev_state = WESTON_COMPOSITOR_ACTIVE;
Pekka Paalanenbce2d3f2011-12-02 13:07:27 +02002292
Kristian Høgsberg5d1c0c52012-04-10 00:11:50 -04002293 for (key = KEY_F1; key < KEY_F9; key++)
Daniel Stone325fc2d2012-05-30 16:31:58 +01002294 weston_compositor_add_key_binding(&ec->base, key,
2295 MODIFIER_CTRL | MODIFIER_ALT,
2296 switch_vt_binding, ec);
Kristian Høgsberg5d1c0c52012-04-10 00:11:50 -04002297
Jesse Barnes58ef3792012-02-23 09:45:49 -05002298 wl_list_init(&ec->sprite_list);
2299 create_sprites(ec);
2300
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +02002301 if (create_outputs(ec, connector, drm_device) < 0) {
Martin Minarik6d118362012-06-07 18:01:59 +02002302 weston_log("failed to create output for %s\n", path);
Daniel Stonea96b93c2012-06-22 14:04:37 +01002303 goto err_sprite;
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04002304 }
2305
Kristian Høgsberg2bc5e8e2012-09-06 20:51:00 -04002306 if (gles2_renderer_init(&ec->base) < 0)
2307 goto err_egl;
2308
Benjamin Franzke02dee2c2011-10-07 08:27:26 +02002309 path = NULL;
2310
Tiago Vignattice03ec32011-12-19 01:14:03 +02002311 evdev_input_create(&ec->base, ec->udev, seat);
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04002312
2313 loop = wl_display_get_event_loop(ec->base.wl_display);
2314 ec->drm_source =
Benjamin Franzke2af7f102011-03-02 11:14:59 +01002315 wl_event_loop_add_fd(loop, ec->drm.fd,
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04002316 WL_EVENT_READABLE, on_drm_input, ec);
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04002317
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01002318 ec->udev_monitor = udev_monitor_new_from_netlink(ec->udev, "udev");
2319 if (ec->udev_monitor == NULL) {
Martin Minarik6d118362012-06-07 18:01:59 +02002320 weston_log("failed to intialize udev monitor\n");
Daniel Stonea96b93c2012-06-22 14:04:37 +01002321 goto err_drm_source;
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01002322 }
2323 udev_monitor_filter_add_match_subsystem_devtype(ec->udev_monitor,
2324 "drm", NULL);
2325 ec->udev_drm_source =
Benjamin Franzke117483d2011-08-30 11:38:26 +02002326 wl_event_loop_add_fd(loop,
2327 udev_monitor_get_fd(ec->udev_monitor),
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01002328 WL_EVENT_READABLE, udev_drm_event, ec);
2329
2330 if (udev_monitor_enable_receiving(ec->udev_monitor) < 0) {
Martin Minarik6d118362012-06-07 18:01:59 +02002331 weston_log("failed to enable udev-monitor receiving\n");
Daniel Stonea96b93c2012-06-22 14:04:37 +01002332 goto err_udev_monitor;
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01002333 }
2334
Daniel Stonea96b93c2012-06-22 14:04:37 +01002335 udev_device_unref(drm_device);
Daniel Stonea96b93c2012-06-22 14:04:37 +01002336
Ander Conselvan de Oliveirada1c9082012-10-31 17:55:46 +02002337 weston_compositor_add_key_binding(&ec->base, KEY_O,
2338 MODIFIER_CTRL | MODIFIER_ALT,
2339 hide_sprites_binding, ec);
2340
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04002341 return &ec->base;
Daniel Stonea96b93c2012-06-22 14:04:37 +01002342
2343err_udev_monitor:
2344 wl_event_source_remove(ec->udev_drm_source);
2345 udev_monitor_unref(ec->udev_monitor);
2346err_drm_source:
2347 wl_event_source_remove(ec->drm_source);
2348 wl_list_for_each_safe(weston_seat, next, &ec->base.seat_list, link)
2349 evdev_input_destroy(weston_seat);
Daniel Stonea96b93c2012-06-22 14:04:37 +01002350err_egl:
2351 eglMakeCurrent(ec->base.egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
2352 EGL_NO_CONTEXT);
2353 eglTerminate(ec->base.egl_display);
2354 eglReleaseThread();
2355 gbm_device_destroy(ec->gbm);
Kristian Høgsberg2bc5e8e2012-09-06 20:51:00 -04002356err_sprite:
2357 destroy_sprites(ec);
Daniel Stonea96b93c2012-06-22 14:04:37 +01002358err_udev_dev:
2359 udev_device_unref(drm_device);
David Herrmann0af066f2012-10-29 19:21:16 +01002360err_tty:
Daniel Stonea96b93c2012-06-22 14:04:37 +01002361 tty_destroy(ec->tty);
2362err_udev:
2363 udev_unref(ec->udev);
2364err_compositor:
2365 weston_compositor_shutdown(&ec->base);
2366err_base:
2367 free(ec);
2368 return NULL;
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04002369}
Kristian Høgsberg1c562182011-05-02 22:09:20 -04002370
Scott Moreau8f37e0b2012-07-31 15:30:41 -06002371static int
2372set_sync_flags(drmModeModeInfo *mode, char *hsync, char *vsync)
2373{
2374 mode->flags = 0;
2375
2376 if (strcmp(hsync, "+hsync") == 0)
2377 mode->flags |= DRM_MODE_FLAG_PHSYNC;
2378 else if (strcmp(hsync, "-hsync") == 0)
2379 mode->flags |= DRM_MODE_FLAG_NHSYNC;
2380 else
2381 return -1;
2382
2383 if (strcmp(vsync, "+vsync") == 0)
2384 mode->flags |= DRM_MODE_FLAG_PVSYNC;
2385 else if (strcmp(vsync, "-vsync") == 0)
2386 mode->flags |= DRM_MODE_FLAG_NVSYNC;
2387 else
2388 return -1;
2389
2390 return 0;
2391}
2392
2393static int
2394check_for_modeline(struct drm_configured_output *output)
2395{
2396 drmModeModeInfo mode;
2397 char hsync[16];
2398 char vsync[16];
2399 char mode_name[16];
2400 float fclock;
2401
2402 mode.type = DRM_MODE_TYPE_USERDEF;
2403 mode.hskew = 0;
2404 mode.vscan = 0;
2405 mode.vrefresh = 0;
2406
2407 if (sscanf(output_mode, "%f %hd %hd %hd %hd %hd %hd %hd %hd %s %s",
2408 &fclock, &mode.hdisplay,
2409 &mode.hsync_start,
2410 &mode.hsync_end, &mode.htotal,
2411 &mode.vdisplay,
2412 &mode.vsync_start,
2413 &mode.vsync_end, &mode.vtotal,
2414 hsync, vsync) == 11) {
2415 if (set_sync_flags(&mode, hsync, vsync))
2416 return -1;
2417
2418 sprintf(mode_name, "%dx%d", mode.hdisplay, mode.vdisplay);
2419 strcpy(mode.name, mode_name);
2420
2421 mode.clock = fclock * 1000;
2422 } else
2423 return -1;
2424
2425 output->crtc_mode = mode;
2426
2427 return 0;
2428}
2429
Scott Moreau8ab5d452012-07-30 19:51:08 -06002430static void
Scott Moreau1bad5db2012-08-18 01:04:05 -06002431drm_output_set_transform(struct drm_configured_output *output)
2432{
2433 if (!output_transform) {
2434 output->transform = WL_OUTPUT_TRANSFORM_NORMAL;
2435 return;
2436 }
2437
2438 if (!strcmp(output_transform, "normal"))
2439 output->transform = WL_OUTPUT_TRANSFORM_NORMAL;
2440 else if (!strcmp(output_transform, "90"))
2441 output->transform = WL_OUTPUT_TRANSFORM_90;
2442 else if (!strcmp(output_transform, "180"))
2443 output->transform = WL_OUTPUT_TRANSFORM_180;
2444 else if (!strcmp(output_transform, "270"))
2445 output->transform = WL_OUTPUT_TRANSFORM_270;
2446 else if (!strcmp(output_transform, "flipped"))
2447 output->transform = WL_OUTPUT_TRANSFORM_FLIPPED;
2448 else if (!strcmp(output_transform, "flipped-90"))
2449 output->transform = WL_OUTPUT_TRANSFORM_FLIPPED_90;
2450 else if (!strcmp(output_transform, "flipped-180"))
2451 output->transform = WL_OUTPUT_TRANSFORM_FLIPPED_180;
2452 else if (!strcmp(output_transform, "flipped-270"))
2453 output->transform = WL_OUTPUT_TRANSFORM_FLIPPED_270;
2454 else {
2455 weston_log("Invalid transform \"%s\" for output %s\n",
2456 output_transform, output_name);
2457 output->transform = WL_OUTPUT_TRANSFORM_NORMAL;
2458 }
2459
2460 free(output_transform);
2461 output_transform = NULL;
2462}
2463
2464static void
Scott Moreau8ab5d452012-07-30 19:51:08 -06002465output_section_done(void *data)
2466{
2467 struct drm_configured_output *output;
2468
2469 output = malloc(sizeof *output);
2470
Scott Moreau1bad5db2012-08-18 01:04:05 -06002471 if (!output || !output_name || (output_name[0] == 'X') ||
2472 (!output_mode && !output_transform)) {
Kristian Høgsberg8ff69152012-07-31 22:18:28 -04002473 free(output_name);
Kristian Høgsberg8ff69152012-07-31 22:18:28 -04002474 free(output_mode);
Scott Moreau1bad5db2012-08-18 01:04:05 -06002475 free(output_transform);
Rob Bradford6b6795f2012-10-09 18:44:35 +01002476 free(output);
Scott Moreau1bad5db2012-08-18 01:04:05 -06002477 output_name = NULL;
Kristian Høgsberg8ff69152012-07-31 22:18:28 -04002478 output_mode = NULL;
Scott Moreau1bad5db2012-08-18 01:04:05 -06002479 output_transform = NULL;
Scott Moreau8ab5d452012-07-30 19:51:08 -06002480 return;
Kristian Høgsberg8ff69152012-07-31 22:18:28 -04002481 }
Scott Moreau8ab5d452012-07-30 19:51:08 -06002482
2483 output->config = OUTPUT_CONFIG_INVALID;
2484 output->name = output_name;
2485 output->mode = output_mode;
2486
Scott Moreau1bad5db2012-08-18 01:04:05 -06002487 if (output_mode) {
2488 if (strcmp(output_mode, "off") == 0)
2489 output->config = OUTPUT_CONFIG_OFF;
2490 else if (strcmp(output_mode, "preferred") == 0)
2491 output->config = OUTPUT_CONFIG_PREFERRED;
2492 else if (strcmp(output_mode, "current") == 0)
2493 output->config = OUTPUT_CONFIG_CURRENT;
2494 else if (sscanf(output_mode, "%dx%d",
2495 &output->width, &output->height) == 2)
2496 output->config = OUTPUT_CONFIG_MODE;
2497 else if (check_for_modeline(output) == 0)
2498 output->config = OUTPUT_CONFIG_MODELINE;
Scott Moreau8ab5d452012-07-30 19:51:08 -06002499
Scott Moreau1bad5db2012-08-18 01:04:05 -06002500 if (output->config == OUTPUT_CONFIG_INVALID)
2501 weston_log("Invalid mode \"%s\" for output %s\n",
2502 output_mode, output_name);
2503 output_mode = NULL;
Scott Moreau8ab5d452012-07-30 19:51:08 -06002504 }
Scott Moreau1bad5db2012-08-18 01:04:05 -06002505
2506 drm_output_set_transform(output);
2507
2508 wl_list_insert(&configured_output_list, &output->link);
2509
2510 if (output_transform)
2511 free(output_transform);
2512 output_transform = NULL;
Scott Moreau8ab5d452012-07-30 19:51:08 -06002513}
2514
Kristian Høgsberg8334bc12012-01-03 10:29:47 -05002515WL_EXPORT struct weston_compositor *
Daniel Stonec1be8e52012-06-01 11:14:02 -04002516backend_init(struct wl_display *display, int argc, char *argv[],
2517 const char *config_file)
Kristian Høgsberg1c562182011-05-02 22:09:20 -04002518{
Kristian Høgsbergbcacef12012-03-11 21:05:57 -04002519 int connector = 0, tty = 0;
2520 const char *seat = default_seat;
Kristian Høgsberg1c562182011-05-02 22:09:20 -04002521
Kristian Høgsbergbcacef12012-03-11 21:05:57 -04002522 const struct weston_option drm_options[] = {
2523 { WESTON_OPTION_INTEGER, "connector", 0, &connector },
2524 { WESTON_OPTION_STRING, "seat", 0, &seat },
2525 { WESTON_OPTION_INTEGER, "tty", 0, &tty },
Kristian Høgsberg061c4252012-06-28 11:28:15 -04002526 { WESTON_OPTION_BOOLEAN, "current-mode", 0, &option_current_mode },
Kristian Høgsbergbcacef12012-03-11 21:05:57 -04002527 };
Benjamin Franzke117483d2011-08-30 11:38:26 +02002528
Kristian Høgsbergbcacef12012-03-11 21:05:57 -04002529 parse_options(drm_options, ARRAY_LENGTH(drm_options), argc, argv);
Kristian Høgsberg1c562182011-05-02 22:09:20 -04002530
Scott Moreau8ab5d452012-07-30 19:51:08 -06002531 wl_list_init(&configured_output_list);
2532
2533 const struct config_key drm_config_keys[] = {
2534 { "name", CONFIG_KEY_STRING, &output_name },
2535 { "mode", CONFIG_KEY_STRING, &output_mode },
Scott Moreau1bad5db2012-08-18 01:04:05 -06002536 { "transform", CONFIG_KEY_STRING, &output_transform },
Scott Moreau8ab5d452012-07-30 19:51:08 -06002537 };
2538
2539 const struct config_section config_section[] = {
2540 { "output", drm_config_keys,
2541 ARRAY_LENGTH(drm_config_keys), output_section_done },
2542 };
2543
2544 parse_config_file(config_file, config_section,
2545 ARRAY_LENGTH(config_section), NULL);
2546
Daniel Stonec1be8e52012-06-01 11:14:02 -04002547 return drm_compositor_create(display, connector, seat, tty, argc, argv,
2548 config_file);
Kristian Høgsberg1c562182011-05-02 22:09:20 -04002549}