blob: 84b6dfec7193a339affae64e55863e975149d9ab [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>
Benjamin Franzke060cf802011-04-30 09:32:11 +020040
Kristian Høgsbergfc783d42010-06-11 12:56:24 -040041#include "compositor.h"
Tiago Vignattice03ec32011-12-19 01:14:03 +020042#include "evdev.h"
Benjamin Franzkebfeda132012-01-30 14:04:04 +010043#include "launcher-util.h"
Kristian Høgsbergfc783d42010-06-11 12:56:24 -040044
Kristian Høgsberg061c4252012-06-28 11:28:15 -040045static int option_current_mode = 0;
Scott Moreau8ab5d452012-07-30 19:51:08 -060046static char *output_name;
47static char *output_mode;
48static struct wl_list configured_output_list;
49
50enum output_config {
51 OUTPUT_CONFIG_INVALID = 0,
52 OUTPUT_CONFIG_OFF,
53 OUTPUT_CONFIG_PREFERRED,
54 OUTPUT_CONFIG_CURRENT,
Scott Moreau8f37e0b2012-07-31 15:30:41 -060055 OUTPUT_CONFIG_MODE,
56 OUTPUT_CONFIG_MODELINE
Scott Moreau8ab5d452012-07-30 19:51:08 -060057};
58
59struct drm_configured_output {
60 char *name;
61 char *mode;
62 int32_t width, height;
Scott Moreau8f37e0b2012-07-31 15:30:41 -060063 drmModeModeInfo crtc_mode;
Scott Moreau8ab5d452012-07-30 19:51:08 -060064 enum output_config config;
65 struct wl_list link;
66};
Kristian Høgsberg061c4252012-06-28 11:28:15 -040067
Kristian Høgsbergce5325d2010-06-14 11:54:00 -040068struct drm_compositor {
Kristian Høgsberg8334bc12012-01-03 10:29:47 -050069 struct weston_compositor base;
Kristian Høgsbergce5325d2010-06-14 11:54:00 -040070
71 struct udev *udev;
72 struct wl_event_source *drm_source;
Kristian Høgsbergce5325d2010-06-14 11:54:00 -040073
Benjamin Franzke9c26ff32011-03-15 15:08:41 +010074 struct udev_monitor *udev_monitor;
75 struct wl_event_source *udev_drm_source;
76
Benjamin Franzke2af7f102011-03-02 11:14:59 +010077 struct {
David Herrmannd7488c22012-03-11 20:05:21 +010078 int id;
Benjamin Franzke2af7f102011-03-02 11:14:59 +010079 int fd;
80 } drm;
Benjamin Franzke060cf802011-04-30 09:32:11 +020081 struct gbm_device *gbm;
Jesse Barnes58ef3792012-02-23 09:45:49 -050082 uint32_t *crtcs;
83 int num_crtcs;
Marty Jack13d9db22011-02-09 19:01:42 -050084 uint32_t crtc_allocator;
Benjamin Franzke9c26ff32011-03-15 15:08:41 +010085 uint32_t connector_allocator;
Kristian Høgsberge4762a62011-01-14 14:59:13 -050086 struct tty *tty;
Pekka Paalanenbce2d3f2011-12-02 13:07:27 +020087
Kristian Høgsbergb5ef5912012-03-28 22:53:49 -040088 struct gbm_surface *dummy_surface;
89 EGLSurface dummy_egl_surface;
90
Jesse Barnes58ef3792012-02-23 09:45:49 -050091 struct wl_list sprite_list;
Kristian Høgsberg65bec242012-03-05 19:57:35 -050092 int sprites_are_broken;
Jesse Barnes58ef3792012-02-23 09:45:49 -050093
Pekka Paalanenbce2d3f2011-12-02 13:07:27 +020094 uint32_t prev_state;
Kristian Høgsbergce5325d2010-06-14 11:54:00 -040095};
96
Kristian Høgsberg8f0ce052011-06-21 11:16:58 -040097struct drm_mode {
Kristian Høgsberg8334bc12012-01-03 10:29:47 -050098 struct weston_mode base;
Kristian Høgsberg8f0ce052011-06-21 11:16:58 -040099 drmModeModeInfo mode_info;
100};
101
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300102struct drm_output;
103
104struct drm_fb {
105 struct gbm_bo *bo;
106 struct drm_output *output;
107 uint32_t fb_id;
108 int is_client_buffer;
109 struct wl_buffer *buffer;
110 struct wl_listener buffer_destroy_listener;
111};
112
Kristian Høgsbergce5325d2010-06-14 11:54:00 -0400113struct drm_output {
Kristian Høgsberg8334bc12012-01-03 10:29:47 -0500114 struct weston_output base;
Kristian Høgsbergce5325d2010-06-14 11:54:00 -0400115
Kristian Høgsberg2f9ed712012-07-26 17:57:15 -0400116 char *name;
Kristian Høgsbergce5325d2010-06-14 11:54:00 -0400117 uint32_t crtc_id;
118 uint32_t connector_id;
Matt Roper361d2ad2011-08-29 13:52:23 -0700119 drmModeCrtcPtr original_crtc;
Benjamin Franzke1178a3c2011-04-10 16:49:52 +0200120
Ander Conselvan de Oliveiraa7326962012-06-26 17:09:13 +0300121 int vblank_pending;
122 int page_flip_pending;
123
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -0400124 struct gbm_surface *surface;
Kristian Høgsberg8e1f77f2012-05-03 11:39:35 -0400125 struct gbm_bo *cursor_bo[2];
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400126 struct weston_plane cursor_plane;
127 struct weston_plane fb_plane;
Kristian Høgsberg5626d342012-08-03 11:50:05 -0400128 struct weston_surface *cursor_surface;
129 int current_cursor;
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -0400130 EGLSurface egl_surface;
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300131 struct drm_fb *current, *next;
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +0200132 struct backlight *backlight;
Kristian Høgsbergce5325d2010-06-14 11:54:00 -0400133};
134
Jesse Barnes58ef3792012-02-23 09:45:49 -0500135/*
136 * An output has a primary display plane plus zero or more sprites for
137 * blending display contents.
138 */
139struct drm_sprite {
140 struct wl_list link;
141
142 uint32_t fb_id;
143 uint32_t pending_fb_id;
144 struct weston_surface *surface;
145 struct weston_surface *pending_surface;
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400146 struct weston_plane plane;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500147
Ander Conselvan de Oliveiraa7326962012-06-26 17:09:13 +0300148 struct drm_output *output;
149
Jesse Barnes58ef3792012-02-23 09:45:49 -0500150 struct drm_compositor *compositor;
151
152 struct wl_listener destroy_listener;
153 struct wl_listener pending_destroy_listener;
154
155 uint32_t possible_crtcs;
156 uint32_t plane_id;
157 uint32_t count_formats;
158
159 int32_t src_x, src_y;
160 uint32_t src_w, src_h;
161 uint32_t dest_x, dest_y;
162 uint32_t dest_w, dest_h;
163
164 uint32_t formats[];
165};
166
Kristian Høgsberg5626d342012-08-03 11:50:05 -0400167static void
168drm_output_set_cursor(struct drm_output *output);
169static void
170drm_disable_unused_sprites(struct weston_output *output_base);
171
Jesse Barnes58ef3792012-02-23 09:45:49 -0500172static int
Jesse Barnes58ef3792012-02-23 09:45:49 -0500173drm_sprite_crtc_supported(struct weston_output *output_base, uint32_t supported)
174{
175 struct weston_compositor *ec = output_base->compositor;
176 struct drm_compositor *c =(struct drm_compositor *) ec;
177 struct drm_output *output = (struct drm_output *) output_base;
178 int crtc;
179
180 for (crtc = 0; crtc < c->num_crtcs; crtc++) {
181 if (c->crtcs[crtc] != output->crtc_id)
182 continue;
183
184 if (supported & (1 << crtc))
185 return -1;
186 }
187
188 return 0;
189}
190
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300191static void
192drm_fb_destroy_callback(struct gbm_bo *bo, void *data)
193{
194 struct drm_fb *fb = data;
195 struct gbm_device *gbm = gbm_bo_get_device(bo);
196
197 if (fb->fb_id)
198 drmModeRmFB(gbm_device_get_fd(gbm), fb->fb_id);
199
200 if (fb->buffer) {
201 weston_buffer_post_release(fb->buffer);
202 wl_list_remove(&fb->buffer_destroy_listener.link);
203 }
204
205 free(data);
206}
207
208static struct drm_fb *
209drm_fb_get_from_bo(struct gbm_bo *bo, struct drm_output *output)
210{
211 struct drm_fb *fb = gbm_bo_get_user_data(bo);
212 struct drm_compositor *compositor =
213 (struct drm_compositor *) output->base.compositor;
214 uint32_t width, height, stride, handle;
215 int ret;
216
217 if (fb)
218 return fb;
219
220 fb = malloc(sizeof *fb);
221
222 fb->bo = bo;
223 fb->output = output;
224 fb->is_client_buffer = 0;
225 fb->buffer = NULL;
226
227 width = gbm_bo_get_width(bo);
228 height = gbm_bo_get_height(bo);
Kristian Høgsberg270a7cb2012-07-16 16:44:16 -0400229 stride = gbm_bo_get_stride(bo);
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300230 handle = gbm_bo_get_handle(bo).u32;
231
232 ret = drmModeAddFB(compositor->drm.fd, width, height, 24, 32,
233 stride, handle, &fb->fb_id);
234 if (ret) {
Martin Minarik6d118362012-06-07 18:01:59 +0200235 weston_log("failed to create kms fb: %m\n");
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300236 free(fb);
237 return NULL;
238 }
239
240 gbm_bo_set_user_data(bo, fb, drm_fb_destroy_callback);
241
242 return fb;
243}
244
245static void
246fb_handle_buffer_destroy(struct wl_listener *listener, void *data)
247{
248 struct drm_fb *fb = container_of(listener, struct drm_fb,
249 buffer_destroy_listener);
250
251 fb->buffer = NULL;
252
253 if (fb == fb->output->next ||
254 (fb == fb->output->current && !fb->output->next))
Kristian Høgsberg49952d12012-06-20 00:35:59 -0400255 weston_output_schedule_repaint(&fb->output->base);
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300256}
257
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400258static struct weston_plane *
Kristian Høgsberg6143f7d2012-07-14 00:31:32 -0400259drm_output_prepare_scanout_surface(struct weston_output *_output,
260 struct weston_surface *es)
Kristian Høgsberg5f5e42e2012-01-25 23:59:42 -0500261{
Kristian Høgsberg6143f7d2012-07-14 00:31:32 -0400262 struct drm_output *output = (struct drm_output *) _output;
Kristian Høgsberg5f5e42e2012-01-25 23:59:42 -0500263 struct drm_compositor *c =
264 (struct drm_compositor *) output->base.compositor;
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300265 struct gbm_bo *bo;
Kristian Høgsberg5f5e42e2012-01-25 23:59:42 -0500266
Kristian Høgsberg101cb652012-02-17 10:45:16 -0500267 if (es->geometry.x != output->base.x ||
Pekka Paalanenba3cf952012-01-25 16:22:05 +0200268 es->geometry.y != output->base.y ||
Pekka Paalanen60921e52012-01-25 15:55:43 +0200269 es->geometry.width != output->base.current->width ||
270 es->geometry.height != output->base.current->height ||
Pekka Paalanenf1f5b362012-01-25 14:33:33 +0200271 es->transform.enabled ||
Kristian Høgsberg2763a2e2012-07-13 22:54:43 -0400272 es->buffer == NULL)
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400273 return NULL;
Kristian Høgsberg5f5e42e2012-01-25 23:59:42 -0500274
Kristian Høgsberg2763a2e2012-07-13 22:54:43 -0400275 bo = gbm_bo_import(c->gbm, GBM_BO_IMPORT_WL_BUFFER,
276 es->buffer, GBM_BO_USE_SCANOUT);
Kristian Høgsberg5f5e42e2012-01-25 23:59:42 -0500277
Ander Conselvan de Oliveiraa64b15d2012-05-02 16:42:22 +0300278 /* Need to verify output->region contained in surface opaque
279 * region. Or maybe just that format doesn't have alpha.
280 * For now, scanout only if format is XRGB8888. */
281 if (gbm_bo_get_format(bo) != GBM_FORMAT_XRGB8888) {
282 gbm_bo_destroy(bo);
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400283 return NULL;
Ander Conselvan de Oliveiraa64b15d2012-05-02 16:42:22 +0300284 }
285
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300286 output->next = drm_fb_get_from_bo(bo, output);
287 if (!output->next) {
288 gbm_bo_destroy(bo);
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400289 return NULL;
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300290 }
Kristian Høgsberg5f5e42e2012-01-25 23:59:42 -0500291
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300292 output->next->is_client_buffer = 1;
293 output->next->buffer = es->buffer;
294 output->next->buffer->busy_count++;
295 output->next->buffer_destroy_listener.notify = fb_handle_buffer_destroy;
296
297 wl_signal_add(&output->next->buffer->resource.destroy_signal,
298 &output->next->buffer_destroy_listener);
Kristian Høgsberg5f5e42e2012-01-25 23:59:42 -0500299
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400300 return &output->fb_plane;
Kristian Høgsberg5f5e42e2012-01-25 23:59:42 -0500301}
302
Kristian Høgsberg06cf6b02012-01-25 23:47:45 -0500303static void
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -0400304drm_output_render(struct drm_output *output, pixman_region32_t *damage)
305{
306 struct drm_compositor *compositor =
307 (struct drm_compositor *) output->base.compositor;
308 struct weston_surface *surface;
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300309 struct gbm_bo *bo;
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -0400310
Kristian Høgsberg362b6722012-06-18 15:13:51 -0400311 if (!eglMakeCurrent(compositor->base.egl_display, output->egl_surface,
312 output->egl_surface,
313 compositor->base.egl_context)) {
Martin Minarik6d118362012-06-07 18:01:59 +0200314 weston_log("failed to make current\n");
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -0400315 return;
316 }
317
318 wl_list_for_each_reverse(surface, &compositor->base.surface_list, link)
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400319 if (surface->plane == &compositor->base.primary_plane)
Kristian Høgsberg6143f7d2012-07-14 00:31:32 -0400320 weston_surface_draw(surface, &output->base, damage);
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -0400321
Kristian Høgsberge0f832b2012-06-20 00:13:18 -0400322 wl_signal_emit(&output->base.frame_signal, output);
Scott Moreau062be7e2012-04-20 13:37:33 -0600323
Kristian Høgsberg362b6722012-06-18 15:13:51 -0400324 eglSwapBuffers(compositor->base.egl_display, output->egl_surface);
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300325 bo = gbm_surface_lock_front_buffer(output->surface);
326 if (!bo) {
Martin Minarik6d118362012-06-07 18:01:59 +0200327 weston_log("failed to lock front buffer: %m\n");
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -0400328 return;
329 }
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300330
331 output->next = drm_fb_get_from_bo(bo, output);
332 if (!output->next) {
Martin Minarik6d118362012-06-07 18:01:59 +0200333 weston_log("failed to get drm_fb for bo\n");
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300334 gbm_surface_release_buffer(output->surface, bo);
335 return;
336 }
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -0400337}
338
339static void
Kristian Høgsberg6ddcdae2012-02-28 22:31:58 -0500340drm_output_repaint(struct weston_output *output_base,
341 pixman_region32_t *damage)
Benjamin Franzkeeefc36c2011-03-11 16:39:20 +0100342{
343 struct drm_output *output = (struct drm_output *) output_base;
Kristian Høgsberg06cf6b02012-01-25 23:47:45 -0500344 struct drm_compositor *compositor =
345 (struct drm_compositor *) output->base.compositor;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500346 struct drm_sprite *s;
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -0400347 struct drm_mode *mode;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500348 int ret = 0;
Benjamin Franzkeeefc36c2011-03-11 16:39:20 +0100349
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300350 if (!output->next)
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -0400351 drm_output_render(output, damage);
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300352 if (!output->next)
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -0400353 return;
Benjamin Franzkeeefc36c2011-03-11 16:39:20 +0100354
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -0400355 mode = container_of(output->base.current, struct drm_mode, base);
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300356 if (!output->current) {
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -0400357 ret = drmModeSetCrtc(compositor->drm.fd, output->crtc_id,
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300358 output->next->fb_id, 0, 0,
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -0400359 &output->connector_id, 1,
360 &mode->mode_info);
361 if (ret) {
Martin Minarik6d118362012-06-07 18:01:59 +0200362 weston_log("set mode failed: %m\n");
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -0400363 return;
364 }
Benjamin Franzke1178a3c2011-04-10 16:49:52 +0200365 }
366
Kristian Høgsberg06cf6b02012-01-25 23:47:45 -0500367 if (drmModePageFlip(compositor->drm.fd, output->crtc_id,
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300368 output->next->fb_id,
Kristian Høgsberg54f14c32012-01-18 11:47:41 -0500369 DRM_MODE_PAGE_FLIP_EVENT, output) < 0) {
Martin Minarik6d118362012-06-07 18:01:59 +0200370 weston_log("queueing pageflip failed: %m\n");
Kristian Høgsberg06cf6b02012-01-25 23:47:45 -0500371 return;
Kristian Høgsberg54f14c32012-01-18 11:47:41 -0500372 }
Benjamin Franzkeec4d3422011-03-14 12:07:26 +0100373
Ander Conselvan de Oliveiraa7326962012-06-26 17:09:13 +0300374 output->page_flip_pending = 1;
375
Kristian Høgsberg5626d342012-08-03 11:50:05 -0400376 drm_output_set_cursor(output);
377
Jesse Barnes58ef3792012-02-23 09:45:49 -0500378 /*
379 * Now, update all the sprite surfaces
380 */
381 wl_list_for_each(s, &compositor->sprite_list, link) {
382 uint32_t flags = 0;
383 drmVBlank vbl = {
384 .request.type = DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT,
385 .request.sequence = 1,
386 };
387
388 if (!drm_sprite_crtc_supported(output_base, s->possible_crtcs))
389 continue;
390
391 ret = drmModeSetPlane(compositor->drm.fd, s->plane_id,
392 output->crtc_id, s->pending_fb_id, flags,
393 s->dest_x, s->dest_y,
394 s->dest_w, s->dest_h,
395 s->src_x, s->src_y,
396 s->src_w, s->src_h);
397 if (ret)
Martin Minarik6d118362012-06-07 18:01:59 +0200398 weston_log("setplane failed: %d: %s\n",
Jesse Barnes58ef3792012-02-23 09:45:49 -0500399 ret, strerror(errno));
400
401 /*
402 * Queue a vblank signal so we know when the surface
403 * becomes active on the display or has been replaced.
404 */
405 vbl.request.signal = (unsigned long)s;
406 ret = drmWaitVBlank(compositor->drm.fd, &vbl);
407 if (ret) {
Martin Minarik6d118362012-06-07 18:01:59 +0200408 weston_log("vblank event request failed: %d: %s\n",
Jesse Barnes58ef3792012-02-23 09:45:49 -0500409 ret, strerror(errno));
410 }
Ander Conselvan de Oliveiraa7326962012-06-26 17:09:13 +0300411
412 s->output = output;
413 output->vblank_pending = 1;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500414 }
415
Kristian Høgsberg5626d342012-08-03 11:50:05 -0400416 drm_disable_unused_sprites(&output->base);
417
Kristian Høgsberg06cf6b02012-01-25 23:47:45 -0500418 return;
Kristian Høgsbergfc783d42010-06-11 12:56:24 -0400419}
420
421static void
Jesse Barnes58ef3792012-02-23 09:45:49 -0500422vblank_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec,
423 void *data)
424{
425 struct drm_sprite *s = (struct drm_sprite *)data;
426 struct drm_compositor *c = s->compositor;
Ander Conselvan de Oliveiraa7326962012-06-26 17:09:13 +0300427 struct drm_output *output = s->output;
428 uint32_t msecs;
429
430 output->vblank_pending = 0;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500431
432 if (s->surface) {
433 weston_buffer_post_release(s->surface->buffer);
434 wl_list_remove(&s->destroy_listener.link);
435 s->surface = NULL;
436 drmModeRmFB(c->drm.fd, s->fb_id);
437 s->fb_id = 0;
438 }
439
440 if (s->pending_surface) {
441 wl_list_remove(&s->pending_destroy_listener.link);
Kristian Høgsberg27e30522012-04-11 23:18:23 -0400442 wl_signal_add(&s->pending_surface->buffer->resource.destroy_signal,
443 &s->destroy_listener);
Jesse Barnes58ef3792012-02-23 09:45:49 -0500444 s->surface = s->pending_surface;
445 s->pending_surface = NULL;
446 s->fb_id = s->pending_fb_id;
447 s->pending_fb_id = 0;
448 }
Ander Conselvan de Oliveiraa7326962012-06-26 17:09:13 +0300449
450 if (!output->page_flip_pending) {
451 msecs = sec * 1000 + usec / 1000;
452 weston_output_finish_frame(&output->base, msecs);
453 }
Jesse Barnes58ef3792012-02-23 09:45:49 -0500454}
455
456static void
Kristian Høgsbergfc783d42010-06-11 12:56:24 -0400457page_flip_handler(int fd, unsigned int frame,
458 unsigned int sec, unsigned int usec, void *data)
459{
Benjamin Franzke1178a3c2011-04-10 16:49:52 +0200460 struct drm_output *output = (struct drm_output *) data;
Kristian Høgsbergfc783d42010-06-11 12:56:24 -0400461 uint32_t msecs;
462
Ander Conselvan de Oliveiraa7326962012-06-26 17:09:13 +0300463 output->page_flip_pending = 0;
464
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300465 if (output->current) {
466 if (output->current->is_client_buffer)
467 gbm_bo_destroy(output->current->bo);
468 else
469 gbm_surface_release_buffer(output->surface,
470 output->current->bo);
Benjamin Franzke1178a3c2011-04-10 16:49:52 +0200471 }
472
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300473 output->current = output->next;
474 output->next = NULL;
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -0400475
Ander Conselvan de Oliveiraa7326962012-06-26 17:09:13 +0300476 if (!output->vblank_pending) {
477 msecs = sec * 1000 + usec / 1000;
478 weston_output_finish_frame(&output->base, msecs);
479 }
Benjamin Franzke1178a3c2011-04-10 16:49:52 +0200480}
481
482static int
Jesse Barnes58ef3792012-02-23 09:45:49 -0500483drm_surface_format_supported(struct drm_sprite *s, uint32_t format)
484{
Kristian Høgsberg875ab9e2012-03-30 11:52:39 -0400485 uint32_t i;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500486
487 for (i = 0; i < s->count_formats; i++)
488 if (s->formats[i] == format)
489 return 1;
490
491 return 0;
492}
493
494static int
495drm_surface_transform_supported(struct weston_surface *es)
496{
Kristian Høgsberg3b00bae2012-07-13 15:25:07 -0400497 struct weston_matrix *matrix = &es->transform.matrix;
498 int i;
499
500 if (!es->transform.enabled)
501 return 1;
502
503 for (i = 0; i < 16; i++) {
504 switch (i) {
505 case 10:
506 case 15:
507 if (matrix->d[i] != 1.0)
508 return 0;
509 break;
510 case 0:
511 case 5:
512 case 12:
513 case 13:
514 break;
515 default:
516 if (matrix->d[i] != 0.0)
517 return 0;
518 break;
519 }
520 }
Jesse Barnes58ef3792012-02-23 09:45:49 -0500521
522 return 1;
523}
524
Jesse Barnes58ef3792012-02-23 09:45:49 -0500525static void
526drm_disable_unused_sprites(struct weston_output *output_base)
527{
528 struct weston_compositor *ec = output_base->compositor;
529 struct drm_compositor *c =(struct drm_compositor *) ec;
530 struct drm_output *output = (struct drm_output *) output_base;
531 struct drm_sprite *s;
532 int ret;
533
534 wl_list_for_each(s, &c->sprite_list, link) {
535 if (s->pending_fb_id)
536 continue;
537
538 ret = drmModeSetPlane(c->drm.fd, s->plane_id,
539 output->crtc_id, 0, 0,
540 0, 0, 0, 0, 0, 0, 0, 0);
541 if (ret)
Martin Minarik6d118362012-06-07 18:01:59 +0200542 weston_log("failed to disable plane: %d: %s\n",
Jesse Barnes58ef3792012-02-23 09:45:49 -0500543 ret, strerror(errno));
544 drmModeRmFB(c->drm.fd, s->fb_id);
Ander Conselvan de Oliveirafd1f4c62012-06-26 17:09:14 +0300545
546 if (s->surface) {
547 s->surface = NULL;
548 wl_list_remove(&s->destroy_listener.link);
549 }
550
551 assert(!s->pending_surface);
Jesse Barnes58ef3792012-02-23 09:45:49 -0500552 s->fb_id = 0;
553 s->pending_fb_id = 0;
554 }
555}
556
557/*
558 * This function must take care to damage any previously assigned surface
559 * if the sprite ends up binding to a different surface than in the
560 * previous frame.
561 */
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400562static struct weston_plane *
Jesse Barnes58ef3792012-02-23 09:45:49 -0500563drm_output_prepare_overlay_surface(struct weston_output *output_base,
Kristian Høgsberg6143f7d2012-07-14 00:31:32 -0400564 struct weston_surface *es)
Jesse Barnes58ef3792012-02-23 09:45:49 -0500565{
566 struct weston_compositor *ec = output_base->compositor;
567 struct drm_compositor *c =(struct drm_compositor *) ec;
568 struct drm_sprite *s;
569 int found = 0;
570 EGLint handle, stride;
571 struct gbm_bo *bo;
572 uint32_t fb_id = 0;
573 uint32_t handles[4], pitches[4], offsets[4];
574 int ret = 0;
575 pixman_region32_t dest_rect, src_rect;
576 pixman_box32_t *box;
577 uint32_t format;
Kristian Høgsberg3b00bae2012-07-13 15:25:07 -0400578 wl_fixed_t sx1, sy1, sx2, sy2;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500579
Kristian Høgsberg65bec242012-03-05 19:57:35 -0500580 if (c->sprites_are_broken)
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400581 return NULL;
Kristian Høgsberg65bec242012-03-05 19:57:35 -0500582
Ander Conselvan de Oliveirad450b192012-06-26 17:09:12 +0300583 if (es->output_mask != (1u << output_base->id))
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400584 return NULL;
Ander Conselvan de Oliveirad450b192012-06-26 17:09:12 +0300585
Kristian Høgsberg2763a2e2012-07-13 22:54:43 -0400586 if (es->buffer == NULL)
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400587 return NULL;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500588
589 if (!drm_surface_transform_supported(es))
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400590 return NULL;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500591
Jesse Barnes58ef3792012-02-23 09:45:49 -0500592 wl_list_for_each(s, &c->sprite_list, link) {
593 if (!drm_sprite_crtc_supported(output_base, s->possible_crtcs))
594 continue;
595
596 if (!s->pending_fb_id) {
597 found = 1;
598 break;
599 }
600 }
601
602 /* No sprites available */
603 if (!found)
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400604 return NULL;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500605
Kristian Høgsberg2763a2e2012-07-13 22:54:43 -0400606 bo = gbm_bo_import(c->gbm, GBM_BO_IMPORT_WL_BUFFER,
607 es->buffer, GBM_BO_USE_SCANOUT);
608 if (!bo)
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400609 return NULL;
Kristian Høgsberg2763a2e2012-07-13 22:54:43 -0400610
Jesse Barnes58ef3792012-02-23 09:45:49 -0500611 format = gbm_bo_get_format(bo);
612 handle = gbm_bo_get_handle(bo).s32;
Kristian Høgsberg270a7cb2012-07-16 16:44:16 -0400613 stride = gbm_bo_get_stride(bo);
Jesse Barnes58ef3792012-02-23 09:45:49 -0500614
615 gbm_bo_destroy(bo);
616
617 if (!drm_surface_format_supported(s, format))
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400618 return NULL;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500619
620 if (!handle)
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400621 return NULL;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500622
623 handles[0] = handle;
624 pitches[0] = stride;
625 offsets[0] = 0;
626
627 ret = drmModeAddFB2(c->drm.fd, es->geometry.width, es->geometry.height,
628 format, handles, pitches, offsets,
629 &fb_id, 0);
630 if (ret) {
Martin Minarik6d118362012-06-07 18:01:59 +0200631 weston_log("addfb2 failed: %d\n", ret);
Kristian Høgsberg65bec242012-03-05 19:57:35 -0500632 c->sprites_are_broken = 1;
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400633 return NULL;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500634 }
635
Jesse Barnes58ef3792012-02-23 09:45:49 -0500636 s->pending_fb_id = fb_id;
637 s->pending_surface = es;
638 es->buffer->busy_count++;
639
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400640 box = pixman_region32_extents(&es->transform.boundingbox);
641 s->plane.x = box->x1;
642 s->plane.y = box->y1;
643
Jesse Barnes58ef3792012-02-23 09:45:49 -0500644 /*
645 * Calculate the source & dest rects properly based on actual
646 * postion (note the caller has called weston_surface_update_transform()
647 * for us already).
648 */
649 pixman_region32_init(&dest_rect);
650 pixman_region32_intersect(&dest_rect, &es->transform.boundingbox,
651 &output_base->region);
652 pixman_region32_translate(&dest_rect, -output_base->x, -output_base->y);
653 box = pixman_region32_extents(&dest_rect);
654 s->dest_x = box->x1;
655 s->dest_y = box->y1;
656 s->dest_w = box->x2 - box->x1;
657 s->dest_h = box->y2 - box->y1;
658 pixman_region32_fini(&dest_rect);
659
660 pixman_region32_init(&src_rect);
661 pixman_region32_intersect(&src_rect, &es->transform.boundingbox,
662 &output_base->region);
Jesse Barnes58ef3792012-02-23 09:45:49 -0500663 box = pixman_region32_extents(&src_rect);
Kristian Høgsberg3b00bae2012-07-13 15:25:07 -0400664
665 weston_surface_from_global_fixed(es,
666 wl_fixed_from_int(box->x1),
667 wl_fixed_from_int(box->y1),
668 &sx1, &sy1);
669 weston_surface_from_global_fixed(es,
670 wl_fixed_from_int(box->x2),
671 wl_fixed_from_int(box->y2),
672 &sx2, &sy2);
673
674 if (sx1 < 0)
675 sx1 = 0;
676 if (sy1 < 0)
677 sy1 = 0;
678 if (sx2 > wl_fixed_from_int(es->geometry.width))
679 sx2 = wl_fixed_from_int(es->geometry.width);
680 if (sy2 > wl_fixed_from_int(es->geometry.height))
681 sy2 = wl_fixed_from_int(es->geometry.height);
682
683 s->src_x = sx1 << 8;
684 s->src_y = sy1 << 8;
685 s->src_w = (sx2 - sx1) << 8;
686 s->src_h = (sy2 - sy1) << 8;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500687 pixman_region32_fini(&src_rect);
688
Kristian Høgsberg27e30522012-04-11 23:18:23 -0400689 wl_signal_add(&es->buffer->resource.destroy_signal,
690 &s->pending_destroy_listener);
Kristian Høgsberg3b00bae2012-07-13 15:25:07 -0400691
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400692 return &s->plane;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500693}
694
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400695static struct weston_plane *
Kristian Høgsberg5626d342012-08-03 11:50:05 -0400696drm_output_prepare_cursor_surface(struct weston_output *output_base,
697 struct weston_surface *es)
Kristian Høgsbergd8bf90c2012-02-23 23:03:14 -0500698{
Kristian Høgsberga6edab32012-07-14 01:06:28 -0400699 struct drm_output *output = (struct drm_output *) output_base;
Kristian Høgsberg5626d342012-08-03 11:50:05 -0400700
701 if (output->cursor_surface)
702 return NULL;
703 if (es->output_mask != (1u << output_base->id))
704 return NULL;
705 if (es->buffer == NULL || !wl_buffer_is_shm(es->buffer) ||
706 es->geometry.width > 64 || es->geometry.height > 64)
707 return NULL;
708
709 output->cursor_surface = es;
710
711 return &output->cursor_plane;
712}
713
714static void
715drm_output_set_cursor(struct drm_output *output)
716{
717 struct weston_surface *es = output->cursor_surface;
Kristian Høgsberga6edab32012-07-14 01:06:28 -0400718 struct drm_compositor *c =
719 (struct drm_compositor *) output->base.compositor;
720 EGLint handle, stride;
721 struct gbm_bo *bo;
722 uint32_t buf[64 * 64];
723 unsigned char *s;
Kristian Høgsberg24e42752012-07-18 12:08:37 -0400724 int i, x, y;
Kristian Høgsbergd8bf90c2012-02-23 23:03:14 -0500725
Kristian Høgsberg5626d342012-08-03 11:50:05 -0400726 if (es == NULL) {
727 drmModeSetCursor(c->drm.fd, output->crtc_id, 0, 0, 0);
728 return;
729 }
Kristian Høgsbergd8bf90c2012-02-23 23:03:14 -0500730
Kristian Høgsberg5626d342012-08-03 11:50:05 -0400731 if (es->buffer && pixman_region32_not_empty(&output->cursor_plane.damage)) {
732 pixman_region32_fini(&output->cursor_plane.damage);
733 pixman_region32_init(&output->cursor_plane.damage);
Kristian Høgsberg1f5de352012-07-18 12:09:58 -0400734 output->current_cursor ^= 1;
735 bo = output->cursor_bo[output->current_cursor];
736 memset(buf, 0, sizeof buf);
737 stride = wl_shm_buffer_get_stride(es->buffer);
738 s = wl_shm_buffer_get_data(es->buffer);
739 for (i = 0; i < es->geometry.height; i++)
740 memcpy(buf + i * 64, s + i * stride,
741 es->geometry.width * 4);
Kristian Høgsbergd8bf90c2012-02-23 23:03:14 -0500742
Kristian Høgsberg1f5de352012-07-18 12:09:58 -0400743 if (gbm_bo_write(bo, buf, sizeof buf) < 0)
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400744 weston_log("failed update cursor: %n\n");
Kristian Høgsberga6edab32012-07-14 01:06:28 -0400745
Kristian Høgsberg1f5de352012-07-18 12:09:58 -0400746 handle = gbm_bo_get_handle(bo).s32;
747 if (drmModeSetCursor(c->drm.fd,
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400748 output->crtc_id, handle, 64, 64))
Kristian Høgsberg1f5de352012-07-18 12:09:58 -0400749 weston_log("failed to set cursor: %n\n");
Kristian Høgsberga6edab32012-07-14 01:06:28 -0400750 }
751
Kristian Høgsberg24e42752012-07-18 12:08:37 -0400752 x = es->geometry.x - output->base.x;
753 y = es->geometry.y - output->base.y;
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400754 if (output->cursor_plane.x != x || output->cursor_plane.y != y) {
755 if (drmModeMoveCursor(c->drm.fd, output->crtc_id, x, y))
Kristian Høgsberg24e42752012-07-18 12:08:37 -0400756 weston_log("failed to move cursor: %m\n");
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400757 output->cursor_plane.x = x;
758 output->cursor_plane.y = y;
Kristian Høgsberga6edab32012-07-14 01:06:28 -0400759 }
Kristian Høgsbergd8bf90c2012-02-23 23:03:14 -0500760}
761
Jesse Barnes58ef3792012-02-23 09:45:49 -0500762static void
763drm_assign_planes(struct weston_output *output)
764{
Kristian Høgsberga6edab32012-07-14 01:06:28 -0400765 struct drm_compositor *c =
766 (struct drm_compositor *) output->compositor;
767 struct drm_output *drm_output = (struct drm_output *) output;
Kristian Høgsberg8e1f77f2012-05-03 11:39:35 -0400768 struct weston_surface *es, *next;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500769 pixman_region32_t overlap, surface_overlap;
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400770 struct weston_plane *primary, *next_plane;
Jesse Barnes58ef3792012-02-23 09:45:49 -0500771
772 /*
773 * Find a surface for each sprite in the output using some heuristics:
774 * 1) size
775 * 2) frequency of update
776 * 3) opacity (though some hw might support alpha blending)
777 * 4) clipping (this can be fixed with color keys)
778 *
779 * The idea is to save on blitting since this should save power.
780 * If we can get a large video surface on the sprite for example,
781 * the main display surface may not need to update at all, and
782 * the client buffer can be used directly for the sprite surface
783 * as we do for flipping full screen surfaces.
784 */
785 pixman_region32_init(&overlap);
Kristian Høgsberg5626d342012-08-03 11:50:05 -0400786 drm_output->cursor_surface = NULL;
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400787 primary = &c->base.primary_plane;
Kristian Høgsberga6edab32012-07-14 01:06:28 -0400788 wl_list_for_each_safe(es, next, &c->base.surface_list, link) {
Jesse Barnes58ef3792012-02-23 09:45:49 -0500789 pixman_region32_init(&surface_overlap);
790 pixman_region32_intersect(&surface_overlap, &overlap,
791 &es->transform.boundingbox);
792
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400793 next_plane = NULL;
Kristian Høgsberg6143f7d2012-07-14 00:31:32 -0400794 if (pixman_region32_not_empty(&surface_overlap))
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400795 next_plane = primary;
796 if (next_plane == NULL)
Kristian Høgsberg5626d342012-08-03 11:50:05 -0400797 next_plane = drm_output_prepare_cursor_surface(output, es);
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400798 if (next_plane == NULL)
799 next_plane = drm_output_prepare_scanout_surface(output, es);
800 if (next_plane == NULL)
801 next_plane = drm_output_prepare_overlay_surface(output, es);
802 if (next_plane == NULL)
803 next_plane = primary;
804 weston_surface_move_to_plane(es, next_plane);
805 if (next_plane == primary)
Jesse Barnes58ef3792012-02-23 09:45:49 -0500806 pixman_region32_union(&overlap, &overlap,
807 &es->transform.boundingbox);
Kristian Høgsberg6143f7d2012-07-14 00:31:32 -0400808
Jesse Barnes58ef3792012-02-23 09:45:49 -0500809 pixman_region32_fini(&surface_overlap);
810 }
811 pixman_region32_fini(&overlap);
Jesse Barnes58ef3792012-02-23 09:45:49 -0500812}
813
Matt Roper361d2ad2011-08-29 13:52:23 -0700814static void
Kristian Høgsberg8334bc12012-01-03 10:29:47 -0500815drm_output_destroy(struct weston_output *output_base)
Matt Roper361d2ad2011-08-29 13:52:23 -0700816{
817 struct drm_output *output = (struct drm_output *) output_base;
818 struct drm_compositor *c =
Benjamin Franzke117483d2011-08-30 11:38:26 +0200819 (struct drm_compositor *) output->base.compositor;
Matt Roper361d2ad2011-08-29 13:52:23 -0700820 drmModeCrtcPtr origcrtc = output->original_crtc;
Matt Roper361d2ad2011-08-29 13:52:23 -0700821
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +0200822 if (output->backlight)
823 backlight_destroy(output->backlight);
824
Matt Roper361d2ad2011-08-29 13:52:23 -0700825 /* Turn off hardware cursor */
Kristian Høgsberga6edab32012-07-14 01:06:28 -0400826 drmModeSetCursor(c->drm.fd, output->crtc_id, 0, 0, 0);
Matt Roper361d2ad2011-08-29 13:52:23 -0700827
828 /* Restore original CRTC state */
829 drmModeSetCrtc(c->drm.fd, origcrtc->crtc_id, origcrtc->buffer_id,
Benjamin Franzke117483d2011-08-30 11:38:26 +0200830 origcrtc->x, origcrtc->y,
831 &output->connector_id, 1, &origcrtc->mode);
Matt Roper361d2ad2011-08-29 13:52:23 -0700832 drmModeFreeCrtc(origcrtc);
833
Benjamin Franzke48c4ea22011-08-30 11:44:56 +0200834 c->crtc_allocator &= ~(1 << output->crtc_id);
835 c->connector_allocator &= ~(1 << output->connector_id);
836
Kristian Høgsberg362b6722012-06-18 15:13:51 -0400837 eglDestroySurface(c->base.egl_display, output->egl_surface);
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -0400838 gbm_surface_destroy(output->surface);
839
Kristian Høgsberg65a11e12012-08-03 11:30:18 -0400840 weston_plane_release(&output->fb_plane);
841 weston_plane_release(&output->cursor_plane);
842
Kristian Høgsberg8334bc12012-01-03 10:29:47 -0500843 weston_output_destroy(&output->base);
Benjamin Franzke48c4ea22011-08-30 11:44:56 +0200844 wl_list_remove(&output->base.link);
845
Kristian Høgsberg148ef012012-07-26 23:03:57 -0400846 free(output->name);
Matt Roper361d2ad2011-08-29 13:52:23 -0700847 free(output);
848}
849
Alex Wub7b8bda2012-04-17 17:20:48 +0800850static struct drm_mode *
851choose_mode (struct drm_output *output, struct weston_mode *target_mode)
852{
853 struct drm_mode *tmp_mode = NULL, *mode;
854
855 if (output->base.current->width == target_mode->width &&
856 output->base.current->height == target_mode->height &&
857 (output->base.current->refresh == target_mode->refresh ||
858 target_mode->refresh == 0))
859 return (struct drm_mode *)output->base.current;
860
861 wl_list_for_each(mode, &output->base.mode_list, base.link) {
862 if (mode->mode_info.hdisplay == target_mode->width &&
863 mode->mode_info.vdisplay == target_mode->height) {
864 if (mode->mode_info.vrefresh == target_mode->refresh ||
865 target_mode->refresh == 0) {
866 return mode;
867 } else if (!tmp_mode)
868 tmp_mode = mode;
869 }
870 }
871
872 return tmp_mode;
873}
874
875static int
876drm_output_switch_mode(struct weston_output *output_base, struct weston_mode *mode)
877{
878 struct drm_output *output;
879 struct drm_mode *drm_mode;
880 int ret;
881 struct drm_compositor *ec;
882 struct gbm_surface *surface;
883 EGLSurface egl_surface;
884
885 if (output_base == NULL) {
Martin Minarik6d118362012-06-07 18:01:59 +0200886 weston_log("output is NULL.\n");
Alex Wub7b8bda2012-04-17 17:20:48 +0800887 return -1;
888 }
889
890 if (mode == NULL) {
Martin Minarik6d118362012-06-07 18:01:59 +0200891 weston_log("mode is NULL.\n");
Alex Wub7b8bda2012-04-17 17:20:48 +0800892 return -1;
893 }
894
895 ec = (struct drm_compositor *)output_base->compositor;
896 output = (struct drm_output *)output_base;
897 drm_mode = choose_mode (output, mode);
898
899 if (!drm_mode) {
Martin Minarik6d118362012-06-07 18:01:59 +0200900 weston_log("%s, invalid resolution:%dx%d\n", __func__, mode->width, mode->height);
Alex Wub7b8bda2012-04-17 17:20:48 +0800901 return -1;
902 } else if (&drm_mode->base == output->base.current) {
903 return 0;
904 } else if (drm_mode->base.width == output->base.current->width &&
905 drm_mode->base.height == output->base.current->height) {
906 /* only change refresh value */
907 ret = drmModeSetCrtc(ec->drm.fd,
908 output->crtc_id,
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300909 output->current->fb_id, 0, 0,
Alex Wub7b8bda2012-04-17 17:20:48 +0800910 &output->connector_id, 1, &drm_mode->mode_info);
911
912 if (ret) {
Martin Minarik6d118362012-06-07 18:01:59 +0200913 weston_log("failed to set mode (%dx%d) %u Hz\n",
Alex Wub7b8bda2012-04-17 17:20:48 +0800914 drm_mode->base.width,
915 drm_mode->base.height,
Kristian Høgsbergc4621b02012-05-10 12:23:53 -0400916 drm_mode->base.refresh / 1000);
Alex Wub7b8bda2012-04-17 17:20:48 +0800917 ret = -1;
918 } else {
919 output->base.current->flags = 0;
920 output->base.current = &drm_mode->base;
921 drm_mode->base.flags =
922 WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
923 ret = 0;
924 }
925
926 return ret;
927 }
928
929 drm_mode->base.flags =
930 WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
931
932 surface = gbm_surface_create(ec->gbm,
933 drm_mode->base.width,
934 drm_mode->base.height,
935 GBM_FORMAT_XRGB8888,
936 GBM_BO_USE_SCANOUT |
937 GBM_BO_USE_RENDERING);
938 if (!surface) {
Martin Minarik6d118362012-06-07 18:01:59 +0200939 weston_log("failed to create gbm surface\n");
Alex Wub7b8bda2012-04-17 17:20:48 +0800940 return -1;
941 }
942
943 egl_surface =
Kristian Høgsberg362b6722012-06-18 15:13:51 -0400944 eglCreateWindowSurface(ec->base.egl_display,
945 ec->base.egl_config,
Alex Wub7b8bda2012-04-17 17:20:48 +0800946 surface, NULL);
947
948 if (egl_surface == EGL_NO_SURFACE) {
Martin Minarik6d118362012-06-07 18:01:59 +0200949 weston_log("failed to create egl surface\n");
Alex Wub7b8bda2012-04-17 17:20:48 +0800950 goto err;
951 }
952
953 ret = drmModeSetCrtc(ec->drm.fd,
954 output->crtc_id,
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300955 output->current->fb_id, 0, 0,
Alex Wub7b8bda2012-04-17 17:20:48 +0800956 &output->connector_id, 1, &drm_mode->mode_info);
957 if (ret) {
Martin Minarik6d118362012-06-07 18:01:59 +0200958 weston_log("failed to set mode\n");
Alex Wub7b8bda2012-04-17 17:20:48 +0800959 goto err;
960 }
961
962 /* reset rendering stuff. */
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300963 if (output->current) {
964 if (output->current->is_client_buffer)
965 gbm_bo_destroy(output->current->bo);
966 else
967 gbm_surface_release_buffer(output->surface,
968 output->current->bo);
969 }
970 output->current = NULL;
Alex Wub7b8bda2012-04-17 17:20:48 +0800971
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +0300972 if (output->next) {
973 if (output->next->is_client_buffer)
974 gbm_bo_destroy(output->next->bo);
975 else
976 gbm_surface_release_buffer(output->surface,
977 output->next->bo);
978 }
979 output->next = NULL;
Alex Wub7b8bda2012-04-17 17:20:48 +0800980
Kristian Høgsberg362b6722012-06-18 15:13:51 -0400981 eglDestroySurface(ec->base.egl_display, output->egl_surface);
Alex Wub7b8bda2012-04-17 17:20:48 +0800982 gbm_surface_destroy(output->surface);
983 output->egl_surface = egl_surface;
984 output->surface = surface;
985
986 /*update output*/
987 output->base.current = &drm_mode->base;
988 output->base.dirty = 1;
989 weston_output_move(&output->base, output->base.x, output->base.y);
990 return 0;
991
992err:
Kristian Høgsberg362b6722012-06-18 15:13:51 -0400993 eglDestroySurface(ec->base.egl_display, egl_surface);
Alex Wub7b8bda2012-04-17 17:20:48 +0800994 gbm_surface_destroy(surface);
995 return -1;
996}
997
Kristian Høgsbergb1868472011-04-22 12:27:57 -0400998static int
Kristian Høgsbergfc783d42010-06-11 12:56:24 -0400999on_drm_input(int fd, uint32_t mask, void *data)
1000{
1001 drmEventContext evctx;
1002
1003 memset(&evctx, 0, sizeof evctx);
1004 evctx.version = DRM_EVENT_CONTEXT_VERSION;
1005 evctx.page_flip_handler = page_flip_handler;
Jesse Barnes58ef3792012-02-23 09:45:49 -05001006 evctx.vblank_handler = vblank_handler;
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001007 drmHandleEvent(fd, &evctx);
Kristian Høgsbergb1868472011-04-22 12:27:57 -04001008
1009 return 1;
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001010}
1011
1012static int
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04001013init_egl(struct drm_compositor *ec, struct udev_device *device)
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001014{
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -04001015 EGLint major, minor, n;
Kristian Høgsbergb5ef5912012-03-28 22:53:49 -04001016 const char *filename, *sysnum;
Kristian Høgsberg5fcd0aa2010-08-09 14:43:33 -04001017 int fd;
Kristian Høgsberg2c28aa52010-07-28 23:47:54 -04001018 static const EGLint context_attribs[] = {
1019 EGL_CONTEXT_CLIENT_VERSION, 2,
1020 EGL_NONE
1021 };
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001022
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -04001023 static const EGLint config_attribs[] = {
1024 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
1025 EGL_RED_SIZE, 1,
1026 EGL_GREEN_SIZE, 1,
1027 EGL_BLUE_SIZE, 1,
1028 EGL_ALPHA_SIZE, 0,
1029 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
1030 EGL_NONE
1031 };
1032
Kristian Høgsbergb71302e2012-05-10 12:28:35 -04001033 sysnum = udev_device_get_sysnum(device);
1034 if (sysnum)
1035 ec->drm.id = atoi(sysnum);
1036 if (!sysnum || ec->drm.id < 0) {
Martin Minarik6d118362012-06-07 18:01:59 +02001037 weston_log("cannot get device sysnum\n");
Kristian Høgsbergb71302e2012-05-10 12:28:35 -04001038 return -1;
1039 }
1040
Kristian Høgsberg5fcd0aa2010-08-09 14:43:33 -04001041 filename = udev_device_get_devnode(device);
David Herrmann63ff7062011-11-05 18:46:01 +01001042 fd = open(filename, O_RDWR | O_CLOEXEC);
Kristian Høgsberg5fcd0aa2010-08-09 14:43:33 -04001043 if (fd < 0) {
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001044 /* Probably permissions error */
Martin Minarik6d118362012-06-07 18:01:59 +02001045 weston_log("couldn't open %s, skipping\n",
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001046 udev_device_get_devnode(device));
1047 return -1;
1048 }
1049
Kristian Høgsbergfc9c5e02012-06-08 16:45:33 -04001050 weston_log("using %s\n", filename);
1051
Benjamin Franzke2af7f102011-03-02 11:14:59 +01001052 ec->drm.fd = fd;
Benjamin Franzke060cf802011-04-30 09:32:11 +02001053 ec->gbm = gbm_create_device(ec->drm.fd);
Kristian Høgsberg362b6722012-06-18 15:13:51 -04001054 ec->base.egl_display = eglGetDisplay(ec->gbm);
1055 if (ec->base.egl_display == NULL) {
Martin Minarik6d118362012-06-07 18:01:59 +02001056 weston_log("failed to create display\n");
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001057 return -1;
1058 }
1059
Kristian Høgsberg362b6722012-06-18 15:13:51 -04001060 if (!eglInitialize(ec->base.egl_display, &major, &minor)) {
Martin Minarik6d118362012-06-07 18:01:59 +02001061 weston_log("failed to initialize display\n");
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001062 return -1;
1063 }
1064
Darxus55973f22010-11-22 21:24:39 -05001065 if (!eglBindAPI(EGL_OPENGL_ES_API)) {
Martin Minarik6d118362012-06-07 18:01:59 +02001066 weston_log("failed to bind api EGL_OPENGL_ES_API\n");
Darxus55973f22010-11-22 21:24:39 -05001067 return -1;
1068 }
1069
Kristian Høgsberg362b6722012-06-18 15:13:51 -04001070 if (!eglChooseConfig(ec->base.egl_display, config_attribs,
1071 &ec->base.egl_config, 1, &n) || n != 1) {
Martin Minarik6d118362012-06-07 18:01:59 +02001072 weston_log("failed to choose config: %d\n", n);
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -04001073 return -1;
1074 }
1075
Kristian Høgsberg362b6722012-06-18 15:13:51 -04001076 ec->base.egl_context =
1077 eglCreateContext(ec->base.egl_display, ec->base.egl_config,
1078 EGL_NO_CONTEXT, context_attribs);
1079 if (ec->base.egl_context == NULL) {
Martin Minarik6d118362012-06-07 18:01:59 +02001080 weston_log("failed to create context\n");
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001081 return -1;
1082 }
1083
Kristian Høgsbergb5ef5912012-03-28 22:53:49 -04001084 ec->dummy_surface = gbm_surface_create(ec->gbm, 10, 10,
1085 GBM_FORMAT_XRGB8888,
1086 GBM_BO_USE_RENDERING);
1087 if (!ec->dummy_surface) {
Martin Minarik6d118362012-06-07 18:01:59 +02001088 weston_log("failed to create dummy gbm surface\n");
Kristian Høgsbergb5ef5912012-03-28 22:53:49 -04001089 return -1;
1090 }
1091
1092 ec->dummy_egl_surface =
Kristian Høgsberg362b6722012-06-18 15:13:51 -04001093 eglCreateWindowSurface(ec->base.egl_display,
1094 ec->base.egl_config,
1095 ec->dummy_surface,
1096 NULL);
Kristian Høgsbergb5ef5912012-03-28 22:53:49 -04001097 if (ec->dummy_egl_surface == EGL_NO_SURFACE) {
Martin Minarik6d118362012-06-07 18:01:59 +02001098 weston_log("failed to create egl surface\n");
Kristian Høgsbergb5ef5912012-03-28 22:53:49 -04001099 return -1;
1100 }
1101
Kristian Høgsberg362b6722012-06-18 15:13:51 -04001102 if (!eglMakeCurrent(ec->base.egl_display, ec->dummy_egl_surface,
1103 ec->dummy_egl_surface, ec->base.egl_context)) {
Martin Minarik6d118362012-06-07 18:01:59 +02001104 weston_log("failed to make context current\n");
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001105 return -1;
1106 }
1107
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001108 return 0;
1109}
1110
Kristian Høgsberg8f0ce052011-06-21 11:16:58 -04001111static int
1112drm_output_add_mode(struct drm_output *output, drmModeModeInfo *info)
1113{
1114 struct drm_mode *mode;
Kristian Høgsbergc4621b02012-05-10 12:23:53 -04001115 uint64_t refresh;
Kristian Høgsberg8f0ce052011-06-21 11:16:58 -04001116
1117 mode = malloc(sizeof *mode);
1118 if (mode == NULL)
1119 return -1;
1120
1121 mode->base.flags = 0;
1122 mode->base.width = info->hdisplay;
1123 mode->base.height = info->vdisplay;
Kristian Høgsbergc4621b02012-05-10 12:23:53 -04001124
1125 /* Calculate higher precision (mHz) refresh rate */
1126 refresh = (info->clock * 1000000LL / info->htotal +
1127 info->vtotal / 2) / info->vtotal;
1128
1129 if (info->flags & DRM_MODE_FLAG_INTERLACE)
1130 refresh *= 2;
1131 if (info->flags & DRM_MODE_FLAG_DBLSCAN)
1132 refresh /= 2;
1133 if (info->vscan > 1)
1134 refresh /= info->vscan;
1135
1136 mode->base.refresh = refresh;
Kristian Høgsberg8f0ce052011-06-21 11:16:58 -04001137 mode->mode_info = *info;
Kristian Høgsberg061c4252012-06-28 11:28:15 -04001138
1139 if (info->type & DRM_MODE_TYPE_PREFERRED)
1140 mode->base.flags |= WL_OUTPUT_MODE_PREFERRED;
1141
Kristian Høgsberg8f0ce052011-06-21 11:16:58 -04001142 wl_list_insert(output->base.mode_list.prev, &mode->base.link);
1143
1144 return 0;
1145}
1146
1147static int
1148drm_subpixel_to_wayland(int drm_value)
1149{
1150 switch (drm_value) {
1151 default:
1152 case DRM_MODE_SUBPIXEL_UNKNOWN:
1153 return WL_OUTPUT_SUBPIXEL_UNKNOWN;
1154 case DRM_MODE_SUBPIXEL_NONE:
1155 return WL_OUTPUT_SUBPIXEL_NONE;
1156 case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB:
1157 return WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB;
1158 case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR:
1159 return WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR;
1160 case DRM_MODE_SUBPIXEL_VERTICAL_RGB:
1161 return WL_OUTPUT_SUBPIXEL_VERTICAL_RGB;
1162 case DRM_MODE_SUBPIXEL_VERTICAL_BGR:
1163 return WL_OUTPUT_SUBPIXEL_VERTICAL_BGR;
1164 }
1165}
1166
Kristian Høgsberg9f404b72012-01-26 00:11:01 -05001167static void
Kristian Høgsberg27e30522012-04-11 23:18:23 -04001168sprite_handle_buffer_destroy(struct wl_listener *listener, void *data)
Jesse Barnes58ef3792012-02-23 09:45:49 -05001169{
1170 struct drm_sprite *sprite =
1171 container_of(listener, struct drm_sprite,
1172 destroy_listener);
Ander Conselvan de Oliveira01a57ed2012-06-26 17:09:15 +03001173 struct drm_compositor *compositor = sprite->compositor;
Jesse Barnes58ef3792012-02-23 09:45:49 -05001174
1175 sprite->surface = NULL;
Ander Conselvan de Oliveira01a57ed2012-06-26 17:09:15 +03001176 drmModeRmFB(compositor->drm.fd, sprite->fb_id);
1177 sprite->fb_id = 0;
Jesse Barnes58ef3792012-02-23 09:45:49 -05001178}
1179
1180static void
Kristian Høgsberg27e30522012-04-11 23:18:23 -04001181sprite_handle_pending_buffer_destroy(struct wl_listener *listener, void *data)
Jesse Barnes58ef3792012-02-23 09:45:49 -05001182{
1183 struct drm_sprite *sprite =
1184 container_of(listener, struct drm_sprite,
1185 pending_destroy_listener);
Ander Conselvan de Oliveira01a57ed2012-06-26 17:09:15 +03001186 struct drm_compositor *compositor = sprite->compositor;
Jesse Barnes58ef3792012-02-23 09:45:49 -05001187
1188 sprite->pending_surface = NULL;
Ander Conselvan de Oliveira01a57ed2012-06-26 17:09:15 +03001189 drmModeRmFB(compositor->drm.fd, sprite->pending_fb_id);
1190 sprite->pending_fb_id = 0;
Jesse Barnes58ef3792012-02-23 09:45:49 -05001191}
1192
Tiago Vignatti5ab91ad2012-03-12 19:40:09 -03001193/* returns a value between 0-255 range, where higher is brighter */
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +02001194static uint32_t
1195drm_get_backlight(struct drm_output *output)
1196{
1197 long brightness, max_brightness, norm;
1198
1199 brightness = backlight_get_brightness(output->backlight);
1200 max_brightness = backlight_get_max_brightness(output->backlight);
1201
Tiago Vignatti5ab91ad2012-03-12 19:40:09 -03001202 /* convert it on a scale of 0 to 255 */
1203 norm = (brightness * 255)/(max_brightness);
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +02001204
1205 return (uint32_t) norm;
1206}
1207
Tiago Vignatti5ab91ad2012-03-12 19:40:09 -03001208/* values accepted are between 0-255 range */
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +02001209static void
1210drm_set_backlight(struct weston_output *output_base, uint32_t value)
1211{
1212 struct drm_output *output = (struct drm_output *) output_base;
1213 long max_brightness, new_brightness;
1214
1215 if (!output->backlight)
1216 return;
1217
Kristian Høgsberg875ab9e2012-03-30 11:52:39 -04001218 if (value > 255)
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +02001219 return;
1220
1221 max_brightness = backlight_get_max_brightness(output->backlight);
1222
1223 /* get denormalized value */
Tiago Vignatti5ab91ad2012-03-12 19:40:09 -03001224 new_brightness = (value * max_brightness) / 255;
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +02001225
1226 backlight_set_brightness(output->backlight, new_brightness);
1227}
1228
1229static drmModePropertyPtr
1230drm_get_prop(int fd, drmModeConnectorPtr connector, const char *name)
1231{
1232 drmModePropertyPtr props;
1233 int i;
1234
1235 for (i = 0; i < connector->count_props; i++) {
1236 props = drmModeGetProperty(fd, connector->props[i]);
1237 if (!props)
1238 continue;
1239
1240 if (!strcmp(props->name, name))
1241 return props;
1242
1243 drmModeFreeProperty(props);
1244 }
1245
1246 return NULL;
1247}
1248
1249static void
1250drm_set_dpms(struct weston_output *output_base, enum dpms_enum level)
1251{
1252 struct drm_output *output = (struct drm_output *) output_base;
1253 struct weston_compositor *ec = output_base->compositor;
1254 struct drm_compositor *c = (struct drm_compositor *) ec;
1255 drmModeConnectorPtr connector;
1256 drmModePropertyPtr prop;
1257
1258 connector = drmModeGetConnector(c->drm.fd, output->connector_id);
1259 if (!connector)
1260 return;
1261
1262 prop = drm_get_prop(c->drm.fd, connector, "DPMS");
1263 if (!prop) {
1264 drmModeFreeConnector(connector);
1265 return;
1266 }
1267
1268 drmModeConnectorSetProperty(c->drm.fd, connector->connector_id,
1269 prop->prop_id, level);
1270 drmModeFreeProperty(prop);
1271 drmModeFreeConnector(connector);
1272}
1273
Kristian Høgsberg2f9ed712012-07-26 17:57:15 -04001274static const char *connector_type_names[] = {
1275 "None",
1276 "VGA",
1277 "DVI",
1278 "DVI",
1279 "DVI",
1280 "Composite",
1281 "TV",
1282 "LVDS",
1283 "CTV",
1284 "DIN",
1285 "DP",
1286 "HDMI",
1287 "HDMI",
1288 "TV",
1289 "eDP",
1290};
1291
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001292static int
Kristian Høgsberg9ca38462012-07-26 22:44:55 -04001293find_crtc_for_connector(struct drm_compositor *ec,
1294 drmModeRes *resources, drmModeConnector *connector)
1295{
1296 drmModeEncoder *encoder;
1297 uint32_t possible_crtcs;
1298 int i, j;
1299
1300 for (j = 0; j < connector->count_encoders; j++) {
1301 encoder = drmModeGetEncoder(ec->drm.fd, connector->encoders[j]);
1302 if (encoder == NULL) {
1303 weston_log("Failed to get encoder.\n");
1304 return -1;
1305 }
1306 possible_crtcs = encoder->possible_crtcs;
1307 drmModeFreeEncoder(encoder);
1308
1309 for (i = 0; i < resources->count_crtcs; i++) {
1310 if (possible_crtcs & (1 << i) &&
1311 !(ec->crtc_allocator & (1 << resources->crtcs[i])))
1312 return i;
1313 }
1314 }
1315
1316 return -1;
1317}
1318
1319static int
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04001320create_output_for_connector(struct drm_compositor *ec,
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001321 drmModeRes *resources,
Benjamin Franzkeeefc36c2011-03-11 16:39:20 +01001322 drmModeConnector *connector,
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +02001323 int x, int y, struct udev_device *drm_device)
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001324{
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04001325 struct drm_output *output;
David Herrmann0f0d54e2011-12-08 17:05:45 +01001326 struct drm_mode *drm_mode, *next;
Scott Moreau8ab5d452012-07-30 19:51:08 -06001327 struct weston_mode *m, *preferred, *current, *configured;
1328 struct drm_configured_output *o = NULL, *temp;
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001329 drmModeEncoder *encoder;
Kristian Høgsberg061c4252012-06-28 11:28:15 -04001330 drmModeModeInfo crtc_mode;
1331 drmModeCrtc *crtc;
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001332 int i, ret;
Kristian Høgsberg2f9ed712012-07-26 17:57:15 -04001333 char name[32];
1334 const char *type_name;
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001335
Kristian Høgsberg9ca38462012-07-26 22:44:55 -04001336 i = find_crtc_for_connector(ec, resources, connector);
1337 if (i < 0) {
1338 weston_log("No usable crtc/encoder pair for connector.\n");
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001339 return -1;
1340 }
1341
Kristian Høgsberg8f0ce052011-06-21 11:16:58 -04001342 output = malloc(sizeof *output);
Kristian Høgsberg9ca38462012-07-26 22:44:55 -04001343 if (output == NULL)
Kristian Høgsberg8f0ce052011-06-21 11:16:58 -04001344 return -1;
1345
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04001346 memset(output, 0, sizeof *output);
Kristian Høgsberg8f0ce052011-06-21 11:16:58 -04001347 output->base.subpixel = drm_subpixel_to_wayland(connector->subpixel);
1348 output->base.make = "unknown";
1349 output->base.model = "unknown";
1350 wl_list_init(&output->base.mode_list);
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04001351
Kristian Høgsberg2f9ed712012-07-26 17:57:15 -04001352 if (connector->connector_type < ARRAY_LENGTH(connector_type_names))
1353 type_name = connector_type_names[connector->connector_type];
1354 else
1355 type_name = "UNKNOWN";
1356 snprintf(name, 32, "%s%d", type_name, connector->connector_type_id);
1357 output->name = strdup(name);
1358
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001359 output->crtc_id = resources->crtcs[i];
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001360 ec->crtc_allocator |= (1 << output->crtc_id);
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001361 output->connector_id = connector->connector_id;
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001362 ec->connector_allocator |= (1 << output->connector_id);
Kristian Høgsberg8f0ce052011-06-21 11:16:58 -04001363
Matt Roper361d2ad2011-08-29 13:52:23 -07001364 output->original_crtc = drmModeGetCrtc(ec->drm.fd, output->crtc_id);
1365
Kristian Høgsberg061c4252012-06-28 11:28:15 -04001366 /* Get the current mode on the crtc that's currently driving
1367 * this connector. */
1368 encoder = drmModeGetEncoder(ec->drm.fd, connector->encoder_id);
Wang Quanxianacb805a2012-07-30 18:09:46 -04001369 memset(&crtc_mode, 0, sizeof crtc_mode);
1370 if (encoder != NULL) {
1371 crtc = drmModeGetCrtc(ec->drm.fd, encoder->crtc_id);
1372 drmModeFreeEncoder(encoder);
1373 if (crtc == NULL)
1374 goto err_free;
1375 if (crtc->mode_valid)
1376 crtc_mode = crtc->mode;
1377 drmModeFreeCrtc(crtc);
1378 }
Kristian Høgsberg061c4252012-06-28 11:28:15 -04001379
David Herrmann0f0d54e2011-12-08 17:05:45 +01001380 for (i = 0; i < connector->count_modes; i++) {
1381 ret = drm_output_add_mode(output, &connector->modes[i]);
1382 if (ret)
1383 goto err_free;
1384 }
1385
Kristian Høgsberg061c4252012-06-28 11:28:15 -04001386 preferred = NULL;
1387 current = NULL;
Scott Moreau8ab5d452012-07-30 19:51:08 -06001388 configured = NULL;
1389
1390 wl_list_for_each(temp, &configured_output_list, link) {
1391 if (strcmp(temp->name, output->name) == 0) {
1392 weston_log("%s mode \"%s\" in config\n",
1393 temp->name, temp->mode);
1394 o = temp;
1395 break;
1396 }
1397 }
1398
1399 if (o && strcmp("off", o->mode) == 0) {
1400 weston_log("Disabling output %s\n", o->name);
1401
1402 drmModeSetCrtc(ec->drm.fd, output->crtc_id,
1403 0, 0, 0, 0, 0, NULL);
1404 goto err_free;
1405 }
1406
Kristian Høgsberg061c4252012-06-28 11:28:15 -04001407 wl_list_for_each(drm_mode, &output->base.mode_list, base.link) {
Scott Moreau8ab5d452012-07-30 19:51:08 -06001408 if (o && o->width == drm_mode->base.width &&
Scott Moreau8f37e0b2012-07-31 15:30:41 -06001409 o->height == drm_mode->base.height &&
1410 o->config == OUTPUT_CONFIG_MODE)
Scott Moreau8ab5d452012-07-30 19:51:08 -06001411 configured = &drm_mode->base;
Wang Quanxianacb805a2012-07-30 18:09:46 -04001412 if (!memcmp(&crtc_mode, &drm_mode->mode_info, sizeof crtc_mode))
Kristian Høgsberg061c4252012-06-28 11:28:15 -04001413 current = &drm_mode->base;
Kristian Høgsberg061c4252012-06-28 11:28:15 -04001414 if (drm_mode->base.flags & WL_OUTPUT_MODE_PREFERRED)
1415 preferred = &drm_mode->base;
David Herrmann0f0d54e2011-12-08 17:05:45 +01001416 }
Kristian Høgsberg8f0ce052011-06-21 11:16:58 -04001417
Scott Moreau8f37e0b2012-07-31 15:30:41 -06001418 if (o && o->config == OUTPUT_CONFIG_MODELINE) {
1419 ret = drm_output_add_mode(output, &o->crtc_mode);
1420 if (ret)
1421 goto err_free;
1422 configured = container_of(output->base.mode_list.prev,
1423 struct weston_mode, link);
1424 current = configured;
1425 }
1426
Wang Quanxianacb805a2012-07-30 18:09:46 -04001427 if (current == NULL && crtc_mode.clock != 0) {
Kristian Høgsberg061c4252012-06-28 11:28:15 -04001428 ret = drm_output_add_mode(output, &crtc_mode);
1429 if (ret)
1430 goto err_free;
1431 current = container_of(output->base.mode_list.prev,
1432 struct weston_mode, link);
Kristian Høgsberg061c4252012-06-28 11:28:15 -04001433 }
1434
Scott Moreau8ab5d452012-07-30 19:51:08 -06001435 if (o && o->config == OUTPUT_CONFIG_CURRENT)
1436 configured = current;
1437
Wang Quanxianacb805a2012-07-30 18:09:46 -04001438 if (option_current_mode && current)
Kristian Høgsberg061c4252012-06-28 11:28:15 -04001439 output->base.current = current;
Scott Moreau8ab5d452012-07-30 19:51:08 -06001440 else if (configured)
1441 output->base.current = configured;
Wang Quanxianacb805a2012-07-30 18:09:46 -04001442 else if (preferred)
Kristian Høgsberg061c4252012-06-28 11:28:15 -04001443 output->base.current = preferred;
Wang Quanxianacb805a2012-07-30 18:09:46 -04001444 else if (current)
1445 output->base.current = current;
1446
1447 if (output->base.current == NULL) {
1448 weston_log("no available modes for %s\n", output->name);
1449 goto err_free;
Kristian Høgsberg061c4252012-06-28 11:28:15 -04001450 }
Kristian Høgsberg8f0ce052011-06-21 11:16:58 -04001451
Wang Quanxianacb805a2012-07-30 18:09:46 -04001452 output->base.current->flags |= WL_OUTPUT_MODE_CURRENT;
1453
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -04001454 output->surface = gbm_surface_create(ec->gbm,
1455 output->base.current->width,
1456 output->base.current->height,
1457 GBM_FORMAT_XRGB8888,
1458 GBM_BO_USE_SCANOUT |
1459 GBM_BO_USE_RENDERING);
1460 if (!output->surface) {
Martin Minarik6d118362012-06-07 18:01:59 +02001461 weston_log("failed to create gbm surface\n");
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -04001462 goto err_free;
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001463 }
1464
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -04001465 output->egl_surface =
Kristian Høgsberg362b6722012-06-18 15:13:51 -04001466 eglCreateWindowSurface(ec->base.egl_display,
1467 ec->base.egl_config,
1468 output->surface,
1469 NULL);
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -04001470 if (output->egl_surface == EGL_NO_SURFACE) {
Martin Minarik6d118362012-06-07 18:01:59 +02001471 weston_log("failed to create egl surface\n");
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -04001472 goto err_surface;
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001473 }
1474
Kristian Høgsberg8e1f77f2012-05-03 11:39:35 -04001475 output->cursor_bo[0] =
1476 gbm_bo_create(ec->gbm, 64, 64, GBM_FORMAT_ARGB8888,
1477 GBM_BO_USE_CURSOR_64X64 | GBM_BO_USE_WRITE);
1478 output->cursor_bo[1] =
1479 gbm_bo_create(ec->gbm, 64, 64, GBM_FORMAT_ARGB8888,
1480 GBM_BO_USE_CURSOR_64X64 | GBM_BO_USE_WRITE);
1481
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +02001482 output->backlight = backlight_init(drm_device,
1483 connector->connector_type);
1484 if (output->backlight) {
1485 output->base.set_backlight = drm_set_backlight;
1486 output->base.backlight_current = drm_get_backlight(output);
1487 }
1488
Kristian Høgsberg8334bc12012-01-03 10:29:47 -05001489 weston_output_init(&output->base, &ec->base, x, y,
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -04001490 connector->mmWidth, connector->mmHeight,
1491 WL_OUTPUT_FLIPPED);
Kristian Høgsberga4b7e202011-10-24 13:26:32 -04001492
1493 wl_list_insert(ec->base.output_list.prev, &output->base.link);
1494
Alex Wubd3354b2012-04-17 17:20:49 +08001495 output->base.origin = output->base.current;
Kristian Høgsberg68c479a2012-01-25 23:32:28 -05001496 output->base.repaint = drm_output_repaint;
Matt Roper361d2ad2011-08-29 13:52:23 -07001497 output->base.destroy = drm_output_destroy;
Jesse Barnes58ef3792012-02-23 09:45:49 -05001498 output->base.assign_planes = drm_assign_planes;
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +02001499 output->base.set_dpms = drm_set_dpms;
Alex Wub7b8bda2012-04-17 17:20:48 +08001500 output->base.switch_mode = drm_output_switch_mode;
Benjamin Franzkeeefc36c2011-03-11 16:39:20 +01001501
Kristian Høgsberg65a11e12012-08-03 11:30:18 -04001502 weston_plane_init(&output->cursor_plane, 0, 0);
1503 weston_plane_init(&output->fb_plane, 0, 0);
1504
Kristian Høgsberg2f9ed712012-07-26 17:57:15 -04001505 weston_log("Output %s, (connector %d, crtc %d)\n",
1506 output->name, output->connector_id, output->crtc_id);
Kristian Høgsberg061c4252012-06-28 11:28:15 -04001507 wl_list_for_each(m, &output->base.mode_list, link)
1508 weston_log_continue(" mode %dx%d@%.1f%s%s%s\n",
1509 m->width, m->height, m->refresh / 1000.0,
1510 m->flags & WL_OUTPUT_MODE_PREFERRED ?
1511 ", preferred" : "",
1512 m->flags & WL_OUTPUT_MODE_CURRENT ?
1513 ", current" : "",
1514 connector->count_modes == 0 ?
1515 ", built-in" : "");
Kristian Høgsbergfc9c5e02012-06-08 16:45:33 -04001516
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001517 return 0;
David Herrmann0f0d54e2011-12-08 17:05:45 +01001518
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -04001519err_surface:
1520 gbm_surface_destroy(output->surface);
David Herrmann0f0d54e2011-12-08 17:05:45 +01001521err_free:
1522 wl_list_for_each_safe(drm_mode, next, &output->base.mode_list,
1523 base.link) {
1524 wl_list_remove(&drm_mode->base.link);
1525 free(drm_mode);
1526 }
1527
1528 drmModeFreeCrtc(output->original_crtc);
1529 ec->crtc_allocator &= ~(1 << output->crtc_id);
1530 ec->connector_allocator &= ~(1 << output->connector_id);
Kristian Høgsberg148ef012012-07-26 23:03:57 -04001531 free(output->name);
David Herrmann0f0d54e2011-12-08 17:05:45 +01001532 free(output);
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -04001533
David Herrmann0f0d54e2011-12-08 17:05:45 +01001534 return -1;
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001535}
1536
Jesse Barnes58ef3792012-02-23 09:45:49 -05001537static void
1538create_sprites(struct drm_compositor *ec)
1539{
1540 struct drm_sprite *sprite;
1541 drmModePlaneRes *plane_res;
1542 drmModePlane *plane;
Kristian Høgsberg875ab9e2012-03-30 11:52:39 -04001543 uint32_t i;
Jesse Barnes58ef3792012-02-23 09:45:49 -05001544
1545 plane_res = drmModeGetPlaneResources(ec->drm.fd);
1546 if (!plane_res) {
Martin Minarik6d118362012-06-07 18:01:59 +02001547 weston_log("failed to get plane resources: %s\n",
Jesse Barnes58ef3792012-02-23 09:45:49 -05001548 strerror(errno));
1549 return;
1550 }
1551
1552 for (i = 0; i < plane_res->count_planes; i++) {
1553 plane = drmModeGetPlane(ec->drm.fd, plane_res->planes[i]);
1554 if (!plane)
1555 continue;
1556
1557 sprite = malloc(sizeof(*sprite) + ((sizeof(uint32_t)) *
1558 plane->count_formats));
1559 if (!sprite) {
Martin Minarik6d118362012-06-07 18:01:59 +02001560 weston_log("%s: out of memory\n",
Jesse Barnes58ef3792012-02-23 09:45:49 -05001561 __func__);
1562 free(plane);
1563 continue;
1564 }
1565
1566 memset(sprite, 0, sizeof *sprite);
1567
1568 sprite->possible_crtcs = plane->possible_crtcs;
1569 sprite->plane_id = plane->plane_id;
1570 sprite->surface = NULL;
1571 sprite->pending_surface = NULL;
1572 sprite->fb_id = 0;
1573 sprite->pending_fb_id = 0;
Kristian Høgsberg27e30522012-04-11 23:18:23 -04001574 sprite->destroy_listener.notify = sprite_handle_buffer_destroy;
1575 sprite->pending_destroy_listener.notify =
Jesse Barnes58ef3792012-02-23 09:45:49 -05001576 sprite_handle_pending_buffer_destroy;
1577 sprite->compositor = ec;
1578 sprite->count_formats = plane->count_formats;
1579 memcpy(sprite->formats, plane->formats,
Rob Clark8efbc8e2012-03-11 19:48:44 -05001580 plane->count_formats * sizeof(plane->formats[0]));
Jesse Barnes58ef3792012-02-23 09:45:49 -05001581 drmModeFreePlane(plane);
Kristian Høgsberg65a11e12012-08-03 11:30:18 -04001582 weston_plane_init(&sprite->plane, 0, 0);
Jesse Barnes58ef3792012-02-23 09:45:49 -05001583
1584 wl_list_insert(&ec->sprite_list, &sprite->link);
1585 }
1586
1587 free(plane_res->planes);
1588 free(plane_res);
1589}
1590
Kristian Høgsberg85fd3272012-02-23 21:45:32 -05001591static void
1592destroy_sprites(struct drm_compositor *compositor)
1593{
1594 struct drm_sprite *sprite, *next;
1595 struct drm_output *output;
1596
1597 output = container_of(compositor->base.output_list.next,
1598 struct drm_output, base.link);
1599
1600 wl_list_for_each_safe(sprite, next, &compositor->sprite_list, link) {
1601 drmModeSetPlane(compositor->drm.fd,
1602 sprite->plane_id,
1603 output->crtc_id, 0, 0,
1604 0, 0, 0, 0, 0, 0, 0, 0);
1605 drmModeRmFB(compositor->drm.fd, sprite->fb_id);
Kristian Høgsberg65a11e12012-08-03 11:30:18 -04001606 weston_plane_release(&sprite->plane);
Kristian Høgsberg85fd3272012-02-23 21:45:32 -05001607 free(sprite);
1608 }
1609}
Jesse Barnes58ef3792012-02-23 09:45:49 -05001610
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001611static int
Kristian Høgsberg875ab9e2012-03-30 11:52:39 -04001612create_outputs(struct drm_compositor *ec, uint32_t option_connector,
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +02001613 struct udev_device *drm_device)
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001614{
1615 drmModeConnector *connector;
1616 drmModeRes *resources;
1617 int i;
Benjamin Franzkeeefc36c2011-03-11 16:39:20 +01001618 int x = 0, y = 0;
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001619
Benjamin Franzke2af7f102011-03-02 11:14:59 +01001620 resources = drmModeGetResources(ec->drm.fd);
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001621 if (!resources) {
Martin Minarik6d118362012-06-07 18:01:59 +02001622 weston_log("drmModeGetResources failed\n");
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001623 return -1;
1624 }
1625
Jesse Barnes58ef3792012-02-23 09:45:49 -05001626 ec->crtcs = calloc(resources->count_crtcs, sizeof(uint32_t));
Christopher Michaeleb866cd2012-03-07 14:55:21 -05001627 if (!ec->crtcs) {
1628 drmModeFreeResources(resources);
Jesse Barnes58ef3792012-02-23 09:45:49 -05001629 return -1;
Christopher Michaeleb866cd2012-03-07 14:55:21 -05001630 }
Jesse Barnes58ef3792012-02-23 09:45:49 -05001631
1632 ec->num_crtcs = resources->count_crtcs;
1633 memcpy(ec->crtcs, resources->crtcs, sizeof(uint32_t) * ec->num_crtcs);
1634
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001635 for (i = 0; i < resources->count_connectors; i++) {
Benjamin Franzke117483d2011-08-30 11:38:26 +02001636 connector = drmModeGetConnector(ec->drm.fd,
1637 resources->connectors[i]);
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001638 if (connector == NULL)
1639 continue;
1640
1641 if (connector->connection == DRM_MODE_CONNECTED &&
1642 (option_connector == 0 ||
Benjamin Franzke9eaee352011-08-02 13:03:54 +02001643 connector->connector_id == option_connector)) {
Benjamin Franzkeeefc36c2011-03-11 16:39:20 +01001644 if (create_output_for_connector(ec, resources,
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +02001645 connector, x, y,
1646 drm_device) < 0) {
Benjamin Franzke439d9862011-10-07 08:20:53 +02001647 drmModeFreeConnector(connector);
1648 continue;
1649 }
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001650
Benjamin Franzke9eaee352011-08-02 13:03:54 +02001651 x += container_of(ec->base.output_list.prev,
Kristian Høgsberg8334bc12012-01-03 10:29:47 -05001652 struct weston_output,
Benjamin Franzke9eaee352011-08-02 13:03:54 +02001653 link)->current->width;
1654 }
Benjamin Franzkeeefc36c2011-03-11 16:39:20 +01001655
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001656 drmModeFreeConnector(connector);
1657 }
1658
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04001659 if (wl_list_empty(&ec->base.output_list)) {
Martin Minarik6d118362012-06-07 18:01:59 +02001660 weston_log("No currently active connector found.\n");
Christopher Michaeleb866cd2012-03-07 14:55:21 -05001661 drmModeFreeResources(resources);
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001662 return -1;
1663 }
1664
1665 drmModeFreeResources(resources);
1666
1667 return 0;
1668}
1669
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001670static void
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +02001671update_outputs(struct drm_compositor *ec, struct udev_device *drm_device)
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001672{
1673 drmModeConnector *connector;
1674 drmModeRes *resources;
1675 struct drm_output *output, *next;
1676 int x = 0, y = 0;
1677 int x_offset = 0, y_offset = 0;
1678 uint32_t connected = 0, disconnects = 0;
1679 int i;
1680
1681 resources = drmModeGetResources(ec->drm.fd);
1682 if (!resources) {
Martin Minarik6d118362012-06-07 18:01:59 +02001683 weston_log("drmModeGetResources failed\n");
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001684 return;
1685 }
1686
1687 /* collect new connects */
1688 for (i = 0; i < resources->count_connectors; i++) {
Benjamin Franzke117483d2011-08-30 11:38:26 +02001689 int connector_id = resources->connectors[i];
1690
1691 connector = drmModeGetConnector(ec->drm.fd, connector_id);
David Herrmann7551cff2011-12-08 17:05:43 +01001692 if (connector == NULL)
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001693 continue;
1694
David Herrmann7551cff2011-12-08 17:05:43 +01001695 if (connector->connection != DRM_MODE_CONNECTED) {
1696 drmModeFreeConnector(connector);
1697 continue;
1698 }
1699
Benjamin Franzke117483d2011-08-30 11:38:26 +02001700 connected |= (1 << connector_id);
1701
1702 if (!(ec->connector_allocator & (1 << connector_id))) {
Kristian Høgsberg8334bc12012-01-03 10:29:47 -05001703 struct weston_output *last =
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001704 container_of(ec->base.output_list.prev,
Kristian Høgsberg8334bc12012-01-03 10:29:47 -05001705 struct weston_output, link);
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001706
1707 /* XXX: not yet needed, we die with 0 outputs */
1708 if (!wl_list_empty(&ec->base.output_list))
Benjamin Franzke117483d2011-08-30 11:38:26 +02001709 x = last->x + last->current->width;
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001710 else
1711 x = 0;
1712 y = 0;
1713 create_output_for_connector(ec, resources,
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +02001714 connector, x, y,
1715 drm_device);
Martin Minarik6d118362012-06-07 18:01:59 +02001716 weston_log("connector %d connected\n", connector_id);
Benjamin Franzke117483d2011-08-30 11:38:26 +02001717
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001718 }
1719 drmModeFreeConnector(connector);
1720 }
1721 drmModeFreeResources(resources);
1722
1723 disconnects = ec->connector_allocator & ~connected;
1724 if (disconnects) {
1725 wl_list_for_each_safe(output, next, &ec->base.output_list,
1726 base.link) {
1727 if (x_offset != 0 || y_offset != 0) {
Kristian Høgsberg8334bc12012-01-03 10:29:47 -05001728 weston_output_move(&output->base,
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001729 output->base.x - x_offset,
1730 output->base.y - y_offset);
1731 }
1732
1733 if (disconnects & (1 << output->connector_id)) {
1734 disconnects &= ~(1 << output->connector_id);
Martin Minarik6d118362012-06-07 18:01:59 +02001735 weston_log("connector %d disconnected\n",
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001736 output->connector_id);
Kristian Høgsberg8f0ce052011-06-21 11:16:58 -04001737 x_offset += output->base.current->width;
Benjamin Franzke48c4ea22011-08-30 11:44:56 +02001738 drm_output_destroy(&output->base);
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001739 }
1740 }
1741 }
1742
1743 /* FIXME: handle zero outputs, without terminating */
1744 if (ec->connector_allocator == 0)
1745 wl_display_terminate(ec->base.wl_display);
1746}
1747
1748static int
David Herrmannd7488c22012-03-11 20:05:21 +01001749udev_event_is_hotplug(struct drm_compositor *ec, struct udev_device *device)
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001750{
David Herrmannd7488c22012-03-11 20:05:21 +01001751 const char *sysnum;
David Herrmann6ac52db2012-03-11 20:05:22 +01001752 const char *val;
David Herrmannd7488c22012-03-11 20:05:21 +01001753
1754 sysnum = udev_device_get_sysnum(device);
1755 if (!sysnum || atoi(sysnum) != ec->drm.id)
1756 return 0;
Benjamin Franzke117483d2011-08-30 11:38:26 +02001757
David Herrmann6ac52db2012-03-11 20:05:22 +01001758 val = udev_device_get_property_value(device, "HOTPLUG");
1759 if (!val)
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001760 return 0;
1761
David Herrmann6ac52db2012-03-11 20:05:22 +01001762 return strcmp(val, "1") == 0;
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001763}
1764
Kristian Høgsbergb1868472011-04-22 12:27:57 -04001765static int
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001766udev_drm_event(int fd, uint32_t mask, void *data)
1767{
1768 struct drm_compositor *ec = data;
1769 struct udev_device *event;
1770
1771 event = udev_monitor_receive_device(ec->udev_monitor);
Benjamin Franzke117483d2011-08-30 11:38:26 +02001772
David Herrmannd7488c22012-03-11 20:05:21 +01001773 if (udev_event_is_hotplug(ec, event))
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +02001774 update_outputs(ec, event);
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001775
1776 udev_device_unref(event);
Kristian Høgsbergb1868472011-04-22 12:27:57 -04001777
1778 return 1;
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01001779}
1780
Kristian Høgsbergcaa64422010-12-01 16:52:15 -05001781static void
Kristian Høgsberg7b884bc2012-07-31 14:32:01 -04001782drm_restore(struct weston_compositor *ec)
1783{
1784 struct drm_compositor *d = (struct drm_compositor *) ec;
1785
1786 if (weston_launcher_drm_set_master(&d->base, d->drm.fd, 0) < 0)
1787 weston_log("failed to drop master: %m\n");
1788 tty_reset(d->tty);
1789}
1790
1791static void
Scott Moreauc50645c2012-07-31 22:29:56 -06001792drm_free_configured_output(struct drm_configured_output *output)
1793{
1794 free(output->name);
1795 free(output->mode);
1796 free(output);
1797}
1798
1799static void
Kristian Høgsberg8334bc12012-01-03 10:29:47 -05001800drm_destroy(struct weston_compositor *ec)
Kristian Høgsbergcaa64422010-12-01 16:52:15 -05001801{
1802 struct drm_compositor *d = (struct drm_compositor *) ec;
Daniel Stone37816df2012-05-16 18:45:18 +01001803 struct weston_seat *seat, *next;
Scott Moreau8ab5d452012-07-30 19:51:08 -06001804 struct drm_configured_output *o, *n;
Kristian Høgsbergcaa64422010-12-01 16:52:15 -05001805
Daniel Stone37816df2012-05-16 18:45:18 +01001806 wl_list_for_each_safe(seat, next, &ec->seat_list, link)
1807 evdev_input_destroy(seat);
Scott Moreau8ab5d452012-07-30 19:51:08 -06001808 wl_list_for_each_safe(o, n, &configured_output_list, link)
Scott Moreauc50645c2012-07-31 22:29:56 -06001809 drm_free_configured_output(o);
Jonas Ådahlc97af922012-03-28 22:36:09 +02001810
1811 wl_event_source_remove(d->udev_drm_source);
1812 wl_event_source_remove(d->drm_source);
1813
Kristian Høgsberg8334bc12012-01-03 10:29:47 -05001814 weston_compositor_shutdown(ec);
Jonas Ådahlc97af922012-03-28 22:36:09 +02001815
Ander Conselvan de Oliveira5f5f3192012-04-30 13:31:28 +03001816 /* Work around crash in egl_dri2.c's dri2_make_current() */
Kristian Høgsberg362b6722012-06-18 15:13:51 -04001817 eglMakeCurrent(ec->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
Ander Conselvan de Oliveira5f5f3192012-04-30 13:31:28 +03001818 EGL_NO_CONTEXT);
Kristian Høgsberg362b6722012-06-18 15:13:51 -04001819 eglTerminate(ec->egl_display);
Ander Conselvan de Oliveira5f5f3192012-04-30 13:31:28 +03001820 eglReleaseThread();
1821
Matt Roper361d2ad2011-08-29 13:52:23 -07001822 gbm_device_destroy(d->gbm);
Kristian Høgsberg85fd3272012-02-23 21:45:32 -05001823 destroy_sprites(d);
Benjamin Franzkebfeda132012-01-30 14:04:04 +01001824 if (weston_launcher_drm_set_master(&d->base, d->drm.fd, 0) < 0)
Martin Minarik6d118362012-06-07 18:01:59 +02001825 weston_log("failed to drop master: %m\n");
Kristian Høgsberge4762a62011-01-14 14:59:13 -05001826 tty_destroy(d->tty);
Kristian Høgsbergcaa64422010-12-01 16:52:15 -05001827
Kristian Høgsberge4762a62011-01-14 14:59:13 -05001828 free(d);
Kristian Høgsbergcaa64422010-12-01 16:52:15 -05001829}
1830
Kristian Høgsberg9396fc52011-05-06 15:15:37 -04001831static void
Kristian Høgsberg835cd492012-01-18 11:48:46 -05001832drm_compositor_set_modes(struct drm_compositor *compositor)
1833{
1834 struct drm_output *output;
1835 struct drm_mode *drm_mode;
1836 int ret;
1837
1838 wl_list_for_each(output, &compositor->base.output_list, base.link) {
1839 drm_mode = (struct drm_mode *) output->base.current;
1840 ret = drmModeSetCrtc(compositor->drm.fd, output->crtc_id,
Ander Conselvan de Oliveira555c17d2012-05-02 16:42:21 +03001841 output->current->fb_id, 0, 0,
Kristian Høgsberg835cd492012-01-18 11:48:46 -05001842 &output->connector_id, 1,
1843 &drm_mode->mode_info);
1844 if (ret < 0) {
Martin Minarik6d118362012-06-07 18:01:59 +02001845 weston_log(
Kristian Høgsbergcbcd0472012-03-11 18:27:41 -04001846 "failed to set mode %dx%d for output at %d,%d: %m\n",
Kristian Høgsberg835cd492012-01-18 11:48:46 -05001847 drm_mode->base.width, drm_mode->base.height,
1848 output->base.x, output->base.y);
1849 }
1850 }
1851}
1852
1853static void
Kristian Høgsberg8334bc12012-01-03 10:29:47 -05001854vt_func(struct weston_compositor *compositor, int event)
Kristian Høgsberg9396fc52011-05-06 15:15:37 -04001855{
1856 struct drm_compositor *ec = (struct drm_compositor *) compositor;
Daniel Stone37816df2012-05-16 18:45:18 +01001857 struct weston_seat *seat;
Kristian Høgsberg85fd3272012-02-23 21:45:32 -05001858 struct drm_sprite *sprite;
Kristian Høgsberga6edab32012-07-14 01:06:28 -04001859 struct drm_output *output;
Kristian Høgsberg9396fc52011-05-06 15:15:37 -04001860
1861 switch (event) {
1862 case TTY_ENTER_VT:
1863 compositor->focus = 1;
Benjamin Franzkebfeda132012-01-30 14:04:04 +01001864 if (weston_launcher_drm_set_master(&ec->base, ec->drm.fd, 1)) {
Martin Minarik6d118362012-06-07 18:01:59 +02001865 weston_log("failed to set master: %m\n");
Kristian Høgsberga018fb02012-01-16 10:52:52 -05001866 wl_display_terminate(compositor->wl_display);
1867 }
Pekka Paalanenbce2d3f2011-12-02 13:07:27 +02001868 compositor->state = ec->prev_state;
Kristian Høgsberg835cd492012-01-18 11:48:46 -05001869 drm_compositor_set_modes(ec);
Kristian Høgsberg8334bc12012-01-03 10:29:47 -05001870 weston_compositor_damage_all(compositor);
Daniel Stone37816df2012-05-16 18:45:18 +01001871 wl_list_for_each(seat, &compositor->seat_list, link) {
1872 evdev_add_devices(ec->udev, seat);
1873 evdev_enable_udev_monitor(ec->udev, seat);
Benjamin Franzke78d3afe2012-04-09 18:14:58 +02001874 }
Kristian Høgsberg9396fc52011-05-06 15:15:37 -04001875 break;
1876 case TTY_LEAVE_VT:
Daniel Stone37816df2012-05-16 18:45:18 +01001877 wl_list_for_each(seat, &compositor->seat_list, link) {
1878 evdev_disable_udev_monitor(seat);
1879 evdev_remove_devices(seat);
Kristian Høgsberg4014a6b2012-04-10 00:08:45 -04001880 }
1881
Kristian Høgsberg9396fc52011-05-06 15:15:37 -04001882 compositor->focus = 0;
Pekka Paalanenbce2d3f2011-12-02 13:07:27 +02001883 ec->prev_state = compositor->state;
Kristian Høgsberg8334bc12012-01-03 10:29:47 -05001884 compositor->state = WESTON_COMPOSITOR_SLEEPING;
Kristian Høgsbergd8e181b2011-05-06 15:38:28 -04001885
Kristian Høgsberg34f80ff2012-01-18 11:50:31 -05001886 /* If we have a repaint scheduled (either from a
1887 * pending pageflip or the idle handler), make sure we
1888 * cancel that so we don't try to pageflip when we're
1889 * vt switched away. The SLEEPING state will prevent
1890 * further attemps at repainting. When we switch
1891 * back, we schedule a repaint, which will process
1892 * pending frame callbacks. */
1893
Kristian Høgsberga6edab32012-07-14 01:06:28 -04001894 wl_list_for_each(output, &ec->base.output_list, base.link) {
1895 output->base.repaint_needed = 0;
1896 drmModeSetCursor(ec->drm.fd, output->crtc_id, 0, 0, 0);
Kristian Høgsberg34f80ff2012-01-18 11:50:31 -05001897 }
1898
Kristian Høgsberga6edab32012-07-14 01:06:28 -04001899 output = container_of(ec->base.output_list.next,
1900 struct drm_output, base.link);
Kristian Høgsberg85fd3272012-02-23 21:45:32 -05001901
1902 wl_list_for_each(sprite, &ec->sprite_list, link)
1903 drmModeSetPlane(ec->drm.fd,
1904 sprite->plane_id,
Kristian Høgsberga6edab32012-07-14 01:06:28 -04001905 output->crtc_id, 0, 0,
Kristian Høgsberg85fd3272012-02-23 21:45:32 -05001906 0, 0, 0, 0, 0, 0, 0, 0);
1907
Benjamin Franzkebfeda132012-01-30 14:04:04 +01001908 if (weston_launcher_drm_set_master(&ec->base, ec->drm.fd, 0) < 0)
Martin Minarik6d118362012-06-07 18:01:59 +02001909 weston_log("failed to drop master: %m\n");
Kristian Høgsberga018fb02012-01-16 10:52:52 -05001910
Kristian Høgsberg9396fc52011-05-06 15:15:37 -04001911 break;
1912 };
1913}
1914
Kristian Høgsberg5d1c0c52012-04-10 00:11:50 -04001915static void
Daniel Stone325fc2d2012-05-30 16:31:58 +01001916switch_vt_binding(struct wl_seat *seat, uint32_t time, uint32_t key, void *data)
Kristian Høgsberg5d1c0c52012-04-10 00:11:50 -04001917{
1918 struct drm_compositor *ec = data;
1919
Daniel Stone325fc2d2012-05-30 16:31:58 +01001920 tty_activate_vt(ec->tty, key - KEY_F1 + 1);
Kristian Høgsberg5d1c0c52012-04-10 00:11:50 -04001921}
1922
Kristian Høgsberg8d51f142011-07-15 21:28:38 -04001923static const char default_seat[] = "seat0";
1924
Kristian Høgsberg8334bc12012-01-03 10:29:47 -05001925static struct weston_compositor *
Kristian Høgsberg8d51f142011-07-15 21:28:38 -04001926drm_compositor_create(struct wl_display *display,
Daniel Stonebaf43592012-06-01 11:11:10 -04001927 int connector, const char *seat, int tty,
Daniel Stonec1be8e52012-06-01 11:14:02 -04001928 int argc, char *argv[], const char *config_file)
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001929{
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04001930 struct drm_compositor *ec;
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001931 struct udev_enumerate *e;
Benjamin Franzke117483d2011-08-30 11:38:26 +02001932 struct udev_list_entry *entry;
Kristian Høgsberg8d51f142011-07-15 21:28:38 -04001933 struct udev_device *device, *drm_device;
1934 const char *path, *device_seat;
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001935 struct wl_event_loop *loop;
Daniel Stonea96b93c2012-06-22 14:04:37 +01001936 struct weston_seat *weston_seat, *next;
Kristian Høgsberg5d1c0c52012-04-10 00:11:50 -04001937 uint32_t key;
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001938
Kristian Høgsbergfc9c5e02012-06-08 16:45:33 -04001939 weston_log("initializing drm backend\n");
1940
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04001941 ec = malloc(sizeof *ec);
1942 if (ec == NULL)
1943 return NULL;
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04001944 memset(ec, 0, sizeof *ec);
Daniel Stone725c2c32012-06-22 14:04:36 +01001945
1946 if (weston_compositor_init(&ec->base, display, argc, argv,
Daniel Stonea96b93c2012-06-22 14:04:37 +01001947 config_file) < 0) {
1948 weston_log("weston_compositor_init failed\n");
1949 goto err_base;
1950 }
Daniel Stone725c2c32012-06-22 14:04:36 +01001951
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001952 ec->udev = udev_new();
1953 if (ec->udev == NULL) {
Martin Minarik6d118362012-06-07 18:01:59 +02001954 weston_log("failed to initialize udev context\n");
Daniel Stonea96b93c2012-06-22 14:04:37 +01001955 goto err_compositor;
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001956 }
1957
Kristian Høgsbergc5b9ddb2012-01-15 14:29:09 -05001958 ec->base.wl_display = display;
1959 ec->tty = tty_create(&ec->base, vt_func, tty);
1960 if (!ec->tty) {
Martin Minarik6d118362012-06-07 18:01:59 +02001961 weston_log("failed to initialize tty\n");
Daniel Stonea96b93c2012-06-22 14:04:37 +01001962 goto err_udev;
Kristian Høgsbergc5b9ddb2012-01-15 14:29:09 -05001963 }
1964
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001965 e = udev_enumerate_new(ec->udev);
1966 udev_enumerate_add_match_subsystem(e, "drm");
Benjamin Franzkea764ee52011-10-07 08:23:22 +02001967 udev_enumerate_add_match_sysname(e, "card[0-9]*");
Kristian Høgsberg8d51f142011-07-15 21:28:38 -04001968
Benjamin Franzke117483d2011-08-30 11:38:26 +02001969 udev_enumerate_scan_devices(e);
Kristian Høgsberg8d51f142011-07-15 21:28:38 -04001970 drm_device = NULL;
Benjamin Franzke117483d2011-08-30 11:38:26 +02001971 udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(e)) {
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001972 path = udev_list_entry_get_name(entry);
1973 device = udev_device_new_from_syspath(ec->udev, path);
Benjamin Franzke117483d2011-08-30 11:38:26 +02001974 device_seat =
Kristian Høgsberg8d51f142011-07-15 21:28:38 -04001975 udev_device_get_property_value(device, "ID_SEAT");
1976 if (!device_seat)
1977 device_seat = default_seat;
1978 if (strcmp(device_seat, seat) == 0) {
1979 drm_device = device;
1980 break;
1981 }
1982 udev_device_unref(device);
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001983 }
Kristian Høgsberg8d51f142011-07-15 21:28:38 -04001984
Kristian Høgsberg8d51f142011-07-15 21:28:38 -04001985 if (drm_device == NULL) {
Martin Minarik6d118362012-06-07 18:01:59 +02001986 weston_log("no drm device found\n");
Daniel Stonea96b93c2012-06-22 14:04:37 +01001987 goto err_udev_enum;
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04001988 }
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04001989
Kristian Høgsberg8d51f142011-07-15 21:28:38 -04001990 if (init_egl(ec, drm_device) < 0) {
Martin Minarik6d118362012-06-07 18:01:59 +02001991 weston_log("failed to initialize egl\n");
Daniel Stonea96b93c2012-06-22 14:04:37 +01001992 goto err_udev_dev;
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04001993 }
Kristian Høgsberg8525a502011-01-14 16:20:21 -05001994
1995 ec->base.destroy = drm_destroy;
Kristian Høgsberg7b884bc2012-07-31 14:32:01 -04001996 ec->base.restore = drm_restore;
Benjamin Franzke431da9a2011-04-20 11:02:58 +02001997
Kristian Høgsberg8525a502011-01-14 16:20:21 -05001998 ec->base.focus = 1;
1999
Kristian Høgsberg8334bc12012-01-03 10:29:47 -05002000 ec->prev_state = WESTON_COMPOSITOR_ACTIVE;
Pekka Paalanenbce2d3f2011-12-02 13:07:27 +02002001
Daniel Stone725c2c32012-06-22 14:04:36 +01002002 if (weston_compositor_init_gl(&ec->base) < 0)
Daniel Stonea96b93c2012-06-22 14:04:37 +01002003 goto err_egl;
Benjamin Franzked59eb1c2011-04-29 22:14:54 +02002004
Kristian Høgsberg5d1c0c52012-04-10 00:11:50 -04002005 for (key = KEY_F1; key < KEY_F9; key++)
Daniel Stone325fc2d2012-05-30 16:31:58 +01002006 weston_compositor_add_key_binding(&ec->base, key,
2007 MODIFIER_CTRL | MODIFIER_ALT,
2008 switch_vt_binding, ec);
Kristian Høgsberg5d1c0c52012-04-10 00:11:50 -04002009
Jesse Barnes58ef3792012-02-23 09:45:49 -05002010 wl_list_init(&ec->sprite_list);
2011 create_sprites(ec);
2012
Tiago Vignatti8e53c7f2012-02-29 19:53:50 +02002013 if (create_outputs(ec, connector, drm_device) < 0) {
Martin Minarik6d118362012-06-07 18:01:59 +02002014 weston_log("failed to create output for %s\n", path);
Daniel Stonea96b93c2012-06-22 14:04:37 +01002015 goto err_sprite;
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04002016 }
2017
Benjamin Franzke02dee2c2011-10-07 08:27:26 +02002018 path = NULL;
2019
Tiago Vignattice03ec32011-12-19 01:14:03 +02002020 evdev_input_create(&ec->base, ec->udev, seat);
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04002021
2022 loop = wl_display_get_event_loop(ec->base.wl_display);
2023 ec->drm_source =
Benjamin Franzke2af7f102011-03-02 11:14:59 +01002024 wl_event_loop_add_fd(loop, ec->drm.fd,
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04002025 WL_EVENT_READABLE, on_drm_input, ec);
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04002026
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01002027 ec->udev_monitor = udev_monitor_new_from_netlink(ec->udev, "udev");
2028 if (ec->udev_monitor == NULL) {
Martin Minarik6d118362012-06-07 18:01:59 +02002029 weston_log("failed to intialize udev monitor\n");
Daniel Stonea96b93c2012-06-22 14:04:37 +01002030 goto err_drm_source;
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01002031 }
2032 udev_monitor_filter_add_match_subsystem_devtype(ec->udev_monitor,
2033 "drm", NULL);
2034 ec->udev_drm_source =
Benjamin Franzke117483d2011-08-30 11:38:26 +02002035 wl_event_loop_add_fd(loop,
2036 udev_monitor_get_fd(ec->udev_monitor),
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01002037 WL_EVENT_READABLE, udev_drm_event, ec);
2038
2039 if (udev_monitor_enable_receiving(ec->udev_monitor) < 0) {
Martin Minarik6d118362012-06-07 18:01:59 +02002040 weston_log("failed to enable udev-monitor receiving\n");
Daniel Stonea96b93c2012-06-22 14:04:37 +01002041 goto err_udev_monitor;
Benjamin Franzke9c26ff32011-03-15 15:08:41 +01002042 }
2043
Daniel Stonea96b93c2012-06-22 14:04:37 +01002044 udev_device_unref(drm_device);
2045 udev_enumerate_unref(e);
2046
Kristian Høgsbergce5325d2010-06-14 11:54:00 -04002047 return &ec->base;
Daniel Stonea96b93c2012-06-22 14:04:37 +01002048
2049err_udev_monitor:
2050 wl_event_source_remove(ec->udev_drm_source);
2051 udev_monitor_unref(ec->udev_monitor);
2052err_drm_source:
2053 wl_event_source_remove(ec->drm_source);
2054 wl_list_for_each_safe(weston_seat, next, &ec->base.seat_list, link)
2055 evdev_input_destroy(weston_seat);
2056err_sprite:
2057 destroy_sprites(ec);
2058err_egl:
2059 eglMakeCurrent(ec->base.egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE,
2060 EGL_NO_CONTEXT);
2061 eglTerminate(ec->base.egl_display);
2062 eglReleaseThread();
2063 gbm_device_destroy(ec->gbm);
2064err_udev_dev:
2065 udev_device_unref(drm_device);
2066err_udev_enum:
2067 udev_enumerate_unref(e);
2068 tty_destroy(ec->tty);
2069err_udev:
2070 udev_unref(ec->udev);
2071err_compositor:
2072 weston_compositor_shutdown(&ec->base);
2073err_base:
2074 free(ec);
2075 return NULL;
Kristian Høgsbergfc783d42010-06-11 12:56:24 -04002076}
Kristian Høgsberg1c562182011-05-02 22:09:20 -04002077
Scott Moreau8f37e0b2012-07-31 15:30:41 -06002078static int
2079set_sync_flags(drmModeModeInfo *mode, char *hsync, char *vsync)
2080{
2081 mode->flags = 0;
2082
2083 if (strcmp(hsync, "+hsync") == 0)
2084 mode->flags |= DRM_MODE_FLAG_PHSYNC;
2085 else if (strcmp(hsync, "-hsync") == 0)
2086 mode->flags |= DRM_MODE_FLAG_NHSYNC;
2087 else
2088 return -1;
2089
2090 if (strcmp(vsync, "+vsync") == 0)
2091 mode->flags |= DRM_MODE_FLAG_PVSYNC;
2092 else if (strcmp(vsync, "-vsync") == 0)
2093 mode->flags |= DRM_MODE_FLAG_NVSYNC;
2094 else
2095 return -1;
2096
2097 return 0;
2098}
2099
2100static int
2101check_for_modeline(struct drm_configured_output *output)
2102{
2103 drmModeModeInfo mode;
2104 char hsync[16];
2105 char vsync[16];
2106 char mode_name[16];
2107 float fclock;
2108
2109 mode.type = DRM_MODE_TYPE_USERDEF;
2110 mode.hskew = 0;
2111 mode.vscan = 0;
2112 mode.vrefresh = 0;
2113
2114 if (sscanf(output_mode, "%f %hd %hd %hd %hd %hd %hd %hd %hd %s %s",
2115 &fclock, &mode.hdisplay,
2116 &mode.hsync_start,
2117 &mode.hsync_end, &mode.htotal,
2118 &mode.vdisplay,
2119 &mode.vsync_start,
2120 &mode.vsync_end, &mode.vtotal,
2121 hsync, vsync) == 11) {
2122 if (set_sync_flags(&mode, hsync, vsync))
2123 return -1;
2124
2125 sprintf(mode_name, "%dx%d", mode.hdisplay, mode.vdisplay);
2126 strcpy(mode.name, mode_name);
2127
2128 mode.clock = fclock * 1000;
2129 } else
2130 return -1;
2131
2132 output->crtc_mode = mode;
2133
2134 return 0;
2135}
2136
Scott Moreau8ab5d452012-07-30 19:51:08 -06002137static void
2138output_section_done(void *data)
2139{
2140 struct drm_configured_output *output;
2141
2142 output = malloc(sizeof *output);
2143
Kristian Høgsberg8ff69152012-07-31 22:18:28 -04002144 if (!output || !output_name || !output_mode) {
2145 free(output_name);
2146 output_name = NULL;
2147 free(output_mode);
2148 output_mode = NULL;
Scott Moreau8ab5d452012-07-30 19:51:08 -06002149 return;
Kristian Høgsberg8ff69152012-07-31 22:18:28 -04002150 }
Scott Moreau8ab5d452012-07-30 19:51:08 -06002151
2152 output->config = OUTPUT_CONFIG_INVALID;
2153 output->name = output_name;
2154 output->mode = output_mode;
2155
2156 if (strcmp(output_mode, "off") == 0)
2157 output->config = OUTPUT_CONFIG_OFF;
2158 else if (strcmp(output_mode, "preferred") == 0)
2159 output->config = OUTPUT_CONFIG_PREFERRED;
2160 else if (strcmp(output_mode, "current") == 0)
2161 output->config = OUTPUT_CONFIG_CURRENT;
2162 else if (sscanf(output_mode, "%dx%d", &output->width, &output->height) == 2)
2163 output->config = OUTPUT_CONFIG_MODE;
Scott Moreau8f37e0b2012-07-31 15:30:41 -06002164 else if (check_for_modeline(output) == 0)
2165 output->config = OUTPUT_CONFIG_MODELINE;
Scott Moreau8ab5d452012-07-30 19:51:08 -06002166
2167 if (output->config != OUTPUT_CONFIG_INVALID)
2168 wl_list_insert(&configured_output_list, &output->link);
2169 else {
Scott Moreau8ab5d452012-07-30 19:51:08 -06002170 weston_log("Invalid mode \"%s\" for output %s\n",
2171 output_mode, output_name);
Scott Moreauc50645c2012-07-31 22:29:56 -06002172 drm_free_configured_output(output);
Scott Moreau8ab5d452012-07-30 19:51:08 -06002173 }
2174}
2175
Kristian Høgsberg8334bc12012-01-03 10:29:47 -05002176WL_EXPORT struct weston_compositor *
Daniel Stonec1be8e52012-06-01 11:14:02 -04002177backend_init(struct wl_display *display, int argc, char *argv[],
2178 const char *config_file)
Kristian Høgsberg1c562182011-05-02 22:09:20 -04002179{
Kristian Høgsbergbcacef12012-03-11 21:05:57 -04002180 int connector = 0, tty = 0;
2181 const char *seat = default_seat;
Kristian Høgsberg1c562182011-05-02 22:09:20 -04002182
Kristian Høgsbergbcacef12012-03-11 21:05:57 -04002183 const struct weston_option drm_options[] = {
2184 { WESTON_OPTION_INTEGER, "connector", 0, &connector },
2185 { WESTON_OPTION_STRING, "seat", 0, &seat },
2186 { WESTON_OPTION_INTEGER, "tty", 0, &tty },
Kristian Høgsberg061c4252012-06-28 11:28:15 -04002187 { WESTON_OPTION_BOOLEAN, "current-mode", 0, &option_current_mode },
Kristian Høgsbergbcacef12012-03-11 21:05:57 -04002188 };
Benjamin Franzke117483d2011-08-30 11:38:26 +02002189
Kristian Høgsbergbcacef12012-03-11 21:05:57 -04002190 parse_options(drm_options, ARRAY_LENGTH(drm_options), argc, argv);
Kristian Høgsberg1c562182011-05-02 22:09:20 -04002191
Scott Moreau8ab5d452012-07-30 19:51:08 -06002192 wl_list_init(&configured_output_list);
2193
2194 const struct config_key drm_config_keys[] = {
2195 { "name", CONFIG_KEY_STRING, &output_name },
2196 { "mode", CONFIG_KEY_STRING, &output_mode },
2197 };
2198
2199 const struct config_section config_section[] = {
2200 { "output", drm_config_keys,
2201 ARRAY_LENGTH(drm_config_keys), output_section_done },
2202 };
2203
2204 parse_config_file(config_file, config_section,
2205 ARRAY_LENGTH(config_section), NULL);
2206
Daniel Stonec1be8e52012-06-01 11:14:02 -04002207 return drm_compositor_create(display, connector, seat, tty, argc, argv,
2208 config_file);
Kristian Høgsberg1c562182011-05-02 22:09:20 -04002209}