blob: e21ceca55e9aa30b791d7d1608157977c3c341c2 [file] [log] [blame]
Philip Withnall4f499172013-02-02 12:02:32 +00001/*
2 * Copyright © 2008-2011 Kristian Høgsberg
3 * Copyright © 2011 Intel Corporation
4 * Copyright © 2012 Raspberry Pi Foundation
5 * Copyright © 2013 Philip Withnall
6 *
Bryce Harringtona0bbfea2015-06-11 15:35:43 -07007 * Permission is hereby granted, free of charge, to any person obtaining
8 * a copy of this software and associated documentation files (the
9 * "Software"), to deal in the Software without restriction, including
10 * without limitation the rights to use, copy, modify, merge, publish,
11 * distribute, sublicense, and/or sell copies of the Software, and to
12 * permit persons to whom the Software is furnished to do so, subject to
13 * the following conditions:
Philip Withnall4f499172013-02-02 12:02:32 +000014 *
Bryce Harringtona0bbfea2015-06-11 15:35:43 -070015 * The above copyright notice and this permission notice (including the
16 * next paragraph) shall be included in all copies or substantial
17 * portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
23 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
24 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
25 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 * SOFTWARE.
Philip Withnall4f499172013-02-02 12:02:32 +000027 */
28
Daniel Stonec228e232013-05-22 18:03:19 +030029#include "config.h"
Philip Withnall4f499172013-02-02 12:02:32 +000030
31#include <errno.h>
32#include <stdlib.h>
33#include <stdio.h>
34#include <string.h>
35#include <math.h>
36#include <sys/mman.h>
37#include <sys/types.h>
38#include <fcntl.h>
39#include <unistd.h>
40#include <linux/fb.h>
Kristian Høgsberg7e597f22013-02-18 16:35:26 -050041#include <linux/input.h>
Philip Withnall4f499172013-02-02 12:02:32 +000042
43#include <libudev.h>
44
Jon Cruz35b2eaa2015-06-15 15:37:08 -070045#include "shared/helpers.h"
Philip Withnall4f499172013-02-02 12:02:32 +000046#include "compositor.h"
Benoit Gschwind934e89a2016-04-27 23:56:42 +020047#include "compositor-fbdev.h"
Philip Withnall4f499172013-02-02 12:02:32 +000048#include "launcher-util.h"
49#include "pixman-renderer.h"
Peter Hutterer823ad332014-11-26 07:06:31 +100050#include "libinput-seat.h"
Pekka Paalanenb00c79b2016-02-18 16:53:27 +020051#include "presentation-time-server-protocol.h"
Philip Withnall4f499172013-02-02 12:02:32 +000052
Giulio Camuffo954f1832014-10-11 18:27:30 +030053struct fbdev_backend {
54 struct weston_backend base;
55 struct weston_compositor *compositor;
Philip Withnall4f499172013-02-02 12:02:32 +000056 uint32_t prev_state;
57
58 struct udev *udev;
Rob Bradfordd355b802013-05-31 18:09:55 +010059 struct udev_input input;
Benoit Gschwind934e89a2016-04-27 23:56:42 +020060 uint32_t output_transform;
Kristian Høgsberg61741a22013-09-17 16:02:57 -070061 struct wl_listener session_listener;
Philip Withnall4f499172013-02-02 12:02:32 +000062};
63
64struct fbdev_screeninfo {
65 unsigned int x_resolution; /* pixels, visible area */
66 unsigned int y_resolution; /* pixels, visible area */
67 unsigned int width_mm; /* visible screen width in mm */
68 unsigned int height_mm; /* visible screen height in mm */
69 unsigned int bits_per_pixel;
70
71 size_t buffer_length; /* length of frame buffer memory in bytes */
72 size_t line_length; /* length of a line in bytes */
73 char id[16]; /* screen identifier */
74
75 pixman_format_code_t pixel_format; /* frame buffer pixel format */
76 unsigned int refresh_rate; /* Hertz */
77};
78
79struct fbdev_output {
Giulio Camuffo954f1832014-10-11 18:27:30 +030080 struct fbdev_backend *backend;
Philip Withnall4f499172013-02-02 12:02:32 +000081 struct weston_output base;
82
83 struct weston_mode mode;
84 struct wl_event_source *finish_frame_timer;
85
86 /* Frame buffer details. */
Pekka Paalanene5b56592016-04-28 15:12:25 +030087 char *device;
Philip Withnall4f499172013-02-02 12:02:32 +000088 struct fbdev_screeninfo fb_info;
89 void *fb; /* length is fb_info.buffer_length */
90
91 /* pixman details. */
92 pixman_image_t *hw_surface;
Philip Withnall4f499172013-02-02 12:02:32 +000093 uint8_t depth;
94};
95
Kristian Høgsberg7e597f22013-02-18 16:35:26 -050096static const char default_seat[] = "seat0";
97
Philip Withnall4f499172013-02-02 12:02:32 +000098static inline struct fbdev_output *
99to_fbdev_output(struct weston_output *base)
100{
101 return container_of(base, struct fbdev_output, base);
102}
103
Giulio Camuffo954f1832014-10-11 18:27:30 +0300104static inline struct fbdev_backend *
105to_fbdev_backend(struct weston_compositor *base)
Philip Withnall4f499172013-02-02 12:02:32 +0000106{
Giulio Camuffo954f1832014-10-11 18:27:30 +0300107 return container_of(base->backend, struct fbdev_backend, base);
Philip Withnall4f499172013-02-02 12:02:32 +0000108}
109
110static void
Jonas Ådahle5a12252013-04-05 23:07:11 +0200111fbdev_output_start_repaint_loop(struct weston_output *output)
112{
Pekka Paalanenb5eedad2014-09-23 22:08:45 -0400113 struct timespec ts;
Jonas Ådahle5a12252013-04-05 23:07:11 +0200114
Pekka Paalanen662f3842015-03-18 12:17:26 +0200115 weston_compositor_read_presentation_clock(output->compositor, &ts);
Pekka Paalanenb00c79b2016-02-18 16:53:27 +0200116 weston_output_finish_frame(output, &ts, WP_PRESENTATION_FEEDBACK_INVALID);
Jonas Ådahle5a12252013-04-05 23:07:11 +0200117}
118
Pekka Paalanene77f8ad2016-06-08 17:39:37 +0300119static int
120fbdev_output_repaint(struct weston_output *base, pixman_region32_t *damage)
Philip Withnall4f499172013-02-02 12:02:32 +0000121{
122 struct fbdev_output *output = to_fbdev_output(base);
123 struct weston_compositor *ec = output->base.compositor;
Philip Withnall4f499172013-02-02 12:02:32 +0000124
125 /* Repaint the damaged region onto the back buffer. */
Sjoerd Simonsc112e0c2015-12-04 19:20:12 -0600126 pixman_renderer_output_set_buffer(base, output->hw_surface);
Philip Withnall4f499172013-02-02 12:02:32 +0000127 ec->renderer->repaint_output(base, damage);
128
Philip Withnall4f499172013-02-02 12:02:32 +0000129 /* Update the damage region. */
130 pixman_region32_subtract(&ec->primary_plane.damage,
131 &ec->primary_plane.damage, damage);
132
133 /* Schedule the end of the frame. We do not sync this to the frame
134 * buffer clock because users who want that should be using the DRM
135 * compositor. FBIO_WAITFORVSYNC blocks and FB_ACTIVATE_VBL requires
136 * panning, which is broken in most kernel drivers.
137 *
138 * Finish the frame synchronised to the specified refresh rate. The
139 * refresh rate is given in mHz and the interval in ms. */
140 wl_event_source_timer_update(output->finish_frame_timer,
141 1000000 / output->mode.refresh);
David Herrmann1edf44c2013-10-22 17:11:26 +0200142
143 return 0;
Adrian Negreanu4aa756d2013-09-06 15:16:09 +0300144}
145
Philip Withnall4f499172013-02-02 12:02:32 +0000146static int
147finish_frame_handler(void *data)
148{
149 struct fbdev_output *output = data;
Pekka Paalanen363aa7b2014-12-17 16:20:40 +0200150 struct timespec ts;
Philip Withnall4f499172013-02-02 12:02:32 +0000151
Pekka Paalanen662f3842015-03-18 12:17:26 +0200152 weston_compositor_read_presentation_clock(output->base.compositor, &ts);
Pekka Paalanen363aa7b2014-12-17 16:20:40 +0200153 weston_output_finish_frame(&output->base, &ts, 0);
Philip Withnall4f499172013-02-02 12:02:32 +0000154
155 return 1;
156}
157
158static pixman_format_code_t
159calculate_pixman_format(struct fb_var_screeninfo *vinfo,
160 struct fb_fix_screeninfo *finfo)
161{
162 /* Calculate the pixman format supported by the frame buffer from the
163 * buffer's metadata. Return 0 if no known pixman format is supported
164 * (since this has depth 0 it's guaranteed to not conflict with any
165 * actual pixman format).
166 *
167 * Documentation on the vinfo and finfo structures:
168 * http://www.mjmwired.net/kernel/Documentation/fb/api.txt
169 *
170 * TODO: Try a bit harder to support other formats, including setting
171 * the preferred format in the hardware. */
172 int type;
173
174 weston_log("Calculating pixman format from:\n"
175 STAMP_SPACE " - type: %i (aux: %i)\n"
176 STAMP_SPACE " - visual: %i\n"
177 STAMP_SPACE " - bpp: %i (grayscale: %i)\n"
178 STAMP_SPACE " - red: offset: %i, length: %i, MSB: %i\n"
179 STAMP_SPACE " - green: offset: %i, length: %i, MSB: %i\n"
180 STAMP_SPACE " - blue: offset: %i, length: %i, MSB: %i\n"
181 STAMP_SPACE " - transp: offset: %i, length: %i, MSB: %i\n",
182 finfo->type, finfo->type_aux, finfo->visual,
183 vinfo->bits_per_pixel, vinfo->grayscale,
184 vinfo->red.offset, vinfo->red.length, vinfo->red.msb_right,
185 vinfo->green.offset, vinfo->green.length,
186 vinfo->green.msb_right,
187 vinfo->blue.offset, vinfo->blue.length,
188 vinfo->blue.msb_right,
189 vinfo->transp.offset, vinfo->transp.length,
190 vinfo->transp.msb_right);
191
192 /* We only handle packed formats at the moment. */
193 if (finfo->type != FB_TYPE_PACKED_PIXELS)
194 return 0;
195
196 /* We only handle true-colour frame buffers at the moment. */
Marc Chalainffbddff2013-09-03 16:47:43 +0200197 switch(finfo->visual) {
198 case FB_VISUAL_TRUECOLOR:
199 case FB_VISUAL_DIRECTCOLOR:
200 if (vinfo->grayscale != 0)
201 return 0;
202 break;
203 default:
204 return 0;
205 }
Philip Withnall4f499172013-02-02 12:02:32 +0000206
207 /* We only support formats with MSBs on the left. */
208 if (vinfo->red.msb_right != 0 || vinfo->green.msb_right != 0 ||
209 vinfo->blue.msb_right != 0)
210 return 0;
211
212 /* Work out the format type from the offsets. We only support RGBA and
213 * ARGB at the moment. */
214 type = PIXMAN_TYPE_OTHER;
215
216 if ((vinfo->transp.offset >= vinfo->red.offset ||
217 vinfo->transp.length == 0) &&
218 vinfo->red.offset >= vinfo->green.offset &&
219 vinfo->green.offset >= vinfo->blue.offset)
220 type = PIXMAN_TYPE_ARGB;
221 else if (vinfo->red.offset >= vinfo->green.offset &&
222 vinfo->green.offset >= vinfo->blue.offset &&
223 vinfo->blue.offset >= vinfo->transp.offset)
224 type = PIXMAN_TYPE_RGBA;
225
226 if (type == PIXMAN_TYPE_OTHER)
227 return 0;
228
229 /* Build the format. */
230 return PIXMAN_FORMAT(vinfo->bits_per_pixel, type,
231 vinfo->transp.length,
232 vinfo->red.length,
233 vinfo->green.length,
234 vinfo->blue.length);
235}
236
237static int
238calculate_refresh_rate(struct fb_var_screeninfo *vinfo)
239{
240 uint64_t quot;
241
242 /* Calculate monitor refresh rate. Default is 60 Hz. Units are mHz. */
243 quot = (vinfo->upper_margin + vinfo->lower_margin + vinfo->yres);
244 quot *= (vinfo->left_margin + vinfo->right_margin + vinfo->xres);
245 quot *= vinfo->pixclock;
246
247 if (quot > 0) {
248 uint64_t refresh_rate;
249
250 refresh_rate = 1000000000000000LLU / quot;
251 if (refresh_rate > 200000)
252 refresh_rate = 200000; /* cap at 200 Hz */
253
254 return refresh_rate;
255 }
256
257 return 60 * 1000; /* default to 60 Hz */
258}
259
260static int
261fbdev_query_screen_info(struct fbdev_output *output, int fd,
262 struct fbdev_screeninfo *info)
263{
264 struct fb_var_screeninfo varinfo;
265 struct fb_fix_screeninfo fixinfo;
266
267 /* Probe the device for screen information. */
268 if (ioctl(fd, FBIOGET_FSCREENINFO, &fixinfo) < 0 ||
269 ioctl(fd, FBIOGET_VSCREENINFO, &varinfo) < 0) {
270 return -1;
271 }
272
273 /* Store the pertinent data. */
274 info->x_resolution = varinfo.xres;
275 info->y_resolution = varinfo.yres;
276 info->width_mm = varinfo.width;
277 info->height_mm = varinfo.height;
278 info->bits_per_pixel = varinfo.bits_per_pixel;
279
280 info->buffer_length = fixinfo.smem_len;
281 info->line_length = fixinfo.line_length;
Derek Foreman61793382015-09-02 13:45:20 -0500282 strncpy(info->id, fixinfo.id, sizeof(info->id));
Bryce Harrington44bbdd02015-09-23 17:39:05 -0700283 info->id[sizeof(info->id)-1] = '\0';
Philip Withnall4f499172013-02-02 12:02:32 +0000284
285 info->pixel_format = calculate_pixman_format(&varinfo, &fixinfo);
286 info->refresh_rate = calculate_refresh_rate(&varinfo);
287
288 if (info->pixel_format == 0) {
289 weston_log("Frame buffer uses an unsupported format.\n");
290 return -1;
291 }
292
293 return 1;
294}
295
296static int
297fbdev_set_screen_info(struct fbdev_output *output, int fd,
298 struct fbdev_screeninfo *info)
299{
300 struct fb_var_screeninfo varinfo;
301
302 /* Grab the current screen information. */
303 if (ioctl(fd, FBIOGET_VSCREENINFO, &varinfo) < 0) {
304 return -1;
305 }
306
307 /* Update the information. */
308 varinfo.xres = info->x_resolution;
309 varinfo.yres = info->y_resolution;
310 varinfo.width = info->width_mm;
311 varinfo.height = info->height_mm;
312 varinfo.bits_per_pixel = info->bits_per_pixel;
313
314 /* Try to set up an ARGB (x8r8g8b8) pixel format. */
315 varinfo.grayscale = 0;
316 varinfo.transp.offset = 24;
317 varinfo.transp.length = 0;
318 varinfo.transp.msb_right = 0;
319 varinfo.red.offset = 16;
320 varinfo.red.length = 8;
321 varinfo.red.msb_right = 0;
322 varinfo.green.offset = 8;
323 varinfo.green.length = 8;
324 varinfo.green.msb_right = 0;
325 varinfo.blue.offset = 0;
326 varinfo.blue.length = 8;
327 varinfo.blue.msb_right = 0;
328
329 /* Set the device's screen information. */
330 if (ioctl(fd, FBIOPUT_VSCREENINFO, &varinfo) < 0) {
331 return -1;
332 }
333
334 return 1;
335}
336
337static void fbdev_frame_buffer_destroy(struct fbdev_output *output);
338
339/* Returns an FD for the frame buffer device. */
340static int
341fbdev_frame_buffer_open(struct fbdev_output *output, const char *fb_dev,
342 struct fbdev_screeninfo *screen_info)
343{
344 int fd = -1;
345
346 weston_log("Opening fbdev frame buffer.\n");
347
348 /* Open the frame buffer device. */
349 fd = open(fb_dev, O_RDWR | O_CLOEXEC);
350 if (fd < 0) {
351 weston_log("Failed to open frame buffer device ‘%s’: %s\n",
352 fb_dev, strerror(errno));
353 return -1;
354 }
355
356 /* Grab the screen info. */
357 if (fbdev_query_screen_info(output, fd, screen_info) < 0) {
358 weston_log("Failed to get frame buffer info: %s\n",
359 strerror(errno));
360
361 close(fd);
362 return -1;
363 }
364
365 return fd;
366}
367
368/* Closes the FD on success or failure. */
369static int
370fbdev_frame_buffer_map(struct fbdev_output *output, int fd)
371{
372 int retval = -1;
373
374 weston_log("Mapping fbdev frame buffer.\n");
375
376 /* Map the frame buffer. Write-only mode, since we don't want to read
377 * anything back (because it's slow). */
378 output->fb = mmap(NULL, output->fb_info.buffer_length,
379 PROT_WRITE, MAP_SHARED, fd, 0);
380 if (output->fb == MAP_FAILED) {
381 weston_log("Failed to mmap frame buffer: %s\n",
382 strerror(errno));
383 goto out_close;
384 }
385
386 /* Create a pixman image to wrap the memory mapped frame buffer. */
387 output->hw_surface =
388 pixman_image_create_bits(output->fb_info.pixel_format,
389 output->fb_info.x_resolution,
390 output->fb_info.y_resolution,
391 output->fb,
392 output->fb_info.line_length);
393 if (output->hw_surface == NULL) {
394 weston_log("Failed to create surface for frame buffer.\n");
395 goto out_unmap;
396 }
397
398 /* Success! */
399 retval = 0;
400
401out_unmap:
402 if (retval != 0 && output->fb != NULL)
403 fbdev_frame_buffer_destroy(output);
404
405out_close:
406 if (fd >= 0)
407 close(fd);
408
409 return retval;
410}
411
412static void
413fbdev_frame_buffer_destroy(struct fbdev_output *output)
414{
415 weston_log("Destroying fbdev frame buffer.\n");
416
417 if (munmap(output->fb, output->fb_info.buffer_length) < 0)
418 weston_log("Failed to munmap frame buffer: %s\n",
419 strerror(errno));
420
421 output->fb = NULL;
422}
423
424static void fbdev_output_destroy(struct weston_output *base);
425static void fbdev_output_disable(struct weston_output *base);
426
427static int
Giulio Camuffo954f1832014-10-11 18:27:30 +0300428fbdev_output_create(struct fbdev_backend *backend,
Philip Withnall4f499172013-02-02 12:02:32 +0000429 const char *device)
430{
431 struct fbdev_output *output;
Philip Withnall4f499172013-02-02 12:02:32 +0000432 int fb_fd;
Philip Withnall4f499172013-02-02 12:02:32 +0000433 struct wl_event_loop *loop;
434
435 weston_log("Creating fbdev output.\n");
436
Bryce Harringtonde16d892014-11-20 22:21:57 -0800437 output = zalloc(sizeof *output);
438 if (output == NULL)
Philip Withnall4f499172013-02-02 12:02:32 +0000439 return -1;
440
Giulio Camuffo954f1832014-10-11 18:27:30 +0300441 output->backend = backend;
Pekka Paalanene5b56592016-04-28 15:12:25 +0300442 output->device = strdup(device);
Philip Withnall4f499172013-02-02 12:02:32 +0000443
444 /* Create the frame buffer. */
445 fb_fd = fbdev_frame_buffer_open(output, device, &output->fb_info);
446 if (fb_fd < 0) {
447 weston_log("Creating frame buffer failed.\n");
448 goto out_free;
449 }
Pekka Paalanene77f8ad2016-06-08 17:39:37 +0300450
451 if (fbdev_frame_buffer_map(output, fb_fd) < 0) {
452 weston_log("Mapping frame buffer failed.\n");
453 goto out_free;
Philip Withnall4f499172013-02-02 12:02:32 +0000454 }
455
Jonas Ådahle5a12252013-04-05 23:07:11 +0200456 output->base.start_repaint_loop = fbdev_output_start_repaint_loop;
Philip Withnall4f499172013-02-02 12:02:32 +0000457 output->base.repaint = fbdev_output_repaint;
458 output->base.destroy = fbdev_output_destroy;
Philip Withnall4f499172013-02-02 12:02:32 +0000459
460 /* only one static mode in list */
461 output->mode.flags =
462 WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
463 output->mode.width = output->fb_info.x_resolution;
464 output->mode.height = output->fb_info.y_resolution;
465 output->mode.refresh = output->fb_info.refresh_rate;
466 wl_list_init(&output->base.mode_list);
467 wl_list_insert(&output->base.mode_list, &output->mode.link);
468
Hardeningff39efa2013-09-18 23:56:35 +0200469 output->base.current_mode = &output->mode;
Philip Withnall4f499172013-02-02 12:02:32 +0000470 output->base.subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN;
471 output->base.make = "unknown";
472 output->base.model = output->fb_info.id;
Derek Foremane516c3b2015-03-17 13:22:34 -0500473 output->base.name = strdup("fbdev");
Philip Withnall4f499172013-02-02 12:02:32 +0000474
Giulio Camuffo954f1832014-10-11 18:27:30 +0300475 weston_output_init(&output->base, backend->compositor,
Philip Withnall4f499172013-02-02 12:02:32 +0000476 0, 0, output->fb_info.width_mm,
477 output->fb_info.height_mm,
Benoit Gschwind934e89a2016-04-27 23:56:42 +0200478 backend->output_transform,
Alexander Larsson4ea95522013-05-22 14:41:37 +0200479 1);
Philip Withnall4f499172013-02-02 12:02:32 +0000480
Pekka Paalanene77f8ad2016-06-08 17:39:37 +0300481 if (pixman_renderer_output_create(&output->base) < 0)
482 goto out_hw_surface;
Adrian Negreanu4aa756d2013-09-06 15:16:09 +0300483
Giulio Camuffo954f1832014-10-11 18:27:30 +0300484 loop = wl_display_get_event_loop(backend->compositor->wl_display);
Philip Withnall4f499172013-02-02 12:02:32 +0000485 output->finish_frame_timer =
486 wl_event_loop_add_timer(loop, finish_frame_handler, output);
487
Giulio Camuffo954f1832014-10-11 18:27:30 +0300488 weston_compositor_add_output(backend->compositor, &output->base);
Philip Withnall4f499172013-02-02 12:02:32 +0000489
490 weston_log("fbdev output %d×%d px\n",
491 output->mode.width, output->mode.height);
492 weston_log_continue(STAMP_SPACE "guessing %d Hz and 96 dpi\n",
493 output->mode.refresh / 1000);
494
495 return 0;
496
Philip Withnall4f499172013-02-02 12:02:32 +0000497out_hw_surface:
Philip Withnall4f499172013-02-02 12:02:32 +0000498 pixman_image_unref(output->hw_surface);
499 output->hw_surface = NULL;
500 weston_output_destroy(&output->base);
501 fbdev_frame_buffer_destroy(output);
502out_free:
Pekka Paalanene5b56592016-04-28 15:12:25 +0300503 free(output->device);
Philip Withnall4f499172013-02-02 12:02:32 +0000504 free(output);
505
506 return -1;
507}
508
509static void
510fbdev_output_destroy(struct weston_output *base)
511{
512 struct fbdev_output *output = to_fbdev_output(base);
513
514 weston_log("Destroying fbdev output.\n");
515
516 /* Close the frame buffer. */
517 fbdev_output_disable(base);
518
Pekka Paalanene77f8ad2016-06-08 17:39:37 +0300519 if (base->renderer_state != NULL)
520 pixman_renderer_output_destroy(base);
Philip Withnall4f499172013-02-02 12:02:32 +0000521
522 /* Remove the output. */
Philip Withnall4f499172013-02-02 12:02:32 +0000523 weston_output_destroy(&output->base);
524
Pekka Paalanene5b56592016-04-28 15:12:25 +0300525 free(output->device);
Philip Withnall4f499172013-02-02 12:02:32 +0000526 free(output);
527}
528
529/* strcmp()-style return values. */
530static int
531compare_screen_info (const struct fbdev_screeninfo *a,
532 const struct fbdev_screeninfo *b)
533{
534 if (a->x_resolution == b->x_resolution &&
535 a->y_resolution == b->y_resolution &&
536 a->width_mm == b->width_mm &&
537 a->height_mm == b->height_mm &&
538 a->bits_per_pixel == b->bits_per_pixel &&
539 a->pixel_format == b->pixel_format &&
540 a->refresh_rate == b->refresh_rate)
541 return 0;
542
543 return 1;
544}
545
546static int
Giulio Camuffo954f1832014-10-11 18:27:30 +0300547fbdev_output_reenable(struct fbdev_backend *backend,
Philip Withnall4f499172013-02-02 12:02:32 +0000548 struct weston_output *base)
549{
550 struct fbdev_output *output = to_fbdev_output(base);
551 struct fbdev_screeninfo new_screen_info;
552 int fb_fd;
Pekka Paalanene5b56592016-04-28 15:12:25 +0300553 char *device;
Philip Withnall4f499172013-02-02 12:02:32 +0000554
555 weston_log("Re-enabling fbdev output.\n");
556
557 /* Create the frame buffer. */
558 fb_fd = fbdev_frame_buffer_open(output, output->device,
559 &new_screen_info);
560 if (fb_fd < 0) {
561 weston_log("Creating frame buffer failed.\n");
562 goto err;
563 }
564
565 /* Check whether the frame buffer details have changed since we were
566 * disabled. */
567 if (compare_screen_info (&output->fb_info, &new_screen_info) != 0) {
568 /* Perform a mode-set to restore the old mode. */
569 if (fbdev_set_screen_info(output, fb_fd,
570 &output->fb_info) < 0) {
571 weston_log("Failed to restore mode settings. "
572 "Attempting to re-open output anyway.\n");
573 }
574
Rob Bradford581b3fd2013-07-26 16:29:39 +0100575 close(fb_fd);
576
Philip Withnall4f499172013-02-02 12:02:32 +0000577 /* Remove and re-add the output so that resources depending on
578 * the frame buffer X/Y resolution (such as the shadow buffer)
579 * are re-initialised. */
Pekka Paalanene5b56592016-04-28 15:12:25 +0300580 device = strdup(output->device);
581 fbdev_output_destroy(&output->base);
Giulio Camuffo954f1832014-10-11 18:27:30 +0300582 fbdev_output_create(backend, device);
Pekka Paalanene5b56592016-04-28 15:12:25 +0300583 free(device);
Philip Withnall4f499172013-02-02 12:02:32 +0000584
585 return 0;
586 }
587
588 /* Map the device if it has the same details as before. */
Pekka Paalanene77f8ad2016-06-08 17:39:37 +0300589 if (fbdev_frame_buffer_map(output, fb_fd) < 0) {
590 weston_log("Mapping frame buffer failed.\n");
591 goto err;
Philip Withnall4f499172013-02-02 12:02:32 +0000592 }
593
594 return 0;
595
596err:
597 return -1;
598}
599
600/* NOTE: This leaves output->fb_info populated, caching data so that if
601 * fbdev_output_reenable() is called again, it can determine whether a mode-set
602 * is needed. */
603static void
604fbdev_output_disable(struct weston_output *base)
605{
606 struct fbdev_output *output = to_fbdev_output(base);
607
608 weston_log("Disabling fbdev output.\n");
609
610 if (output->hw_surface != NULL) {
611 pixman_image_unref(output->hw_surface);
612 output->hw_surface = NULL;
613 }
614
615 fbdev_frame_buffer_destroy(output);
616}
617
618static void
Giulio Camuffo954f1832014-10-11 18:27:30 +0300619fbdev_backend_destroy(struct weston_compositor *base)
Philip Withnall4f499172013-02-02 12:02:32 +0000620{
Giulio Camuffo954f1832014-10-11 18:27:30 +0300621 struct fbdev_backend *backend = to_fbdev_backend(base);
Philip Withnall4f499172013-02-02 12:02:32 +0000622
Giulio Camuffo954f1832014-10-11 18:27:30 +0300623 udev_input_destroy(&backend->input);
Philip Withnall4f499172013-02-02 12:02:32 +0000624
625 /* Destroy the output. */
Giulio Camuffo954f1832014-10-11 18:27:30 +0300626 weston_compositor_shutdown(base);
Philip Withnall4f499172013-02-02 12:02:32 +0000627
628 /* Chain up. */
Giulio Camuffo954f1832014-10-11 18:27:30 +0300629 weston_launcher_destroy(base->launcher);
Philip Withnall4f499172013-02-02 12:02:32 +0000630
Giulio Camuffo954f1832014-10-11 18:27:30 +0300631 free(backend);
Philip Withnall4f499172013-02-02 12:02:32 +0000632}
633
634static void
Kristian Høgsberg61741a22013-09-17 16:02:57 -0700635session_notify(struct wl_listener *listener, void *data)
Philip Withnall4f499172013-02-02 12:02:32 +0000636{
Pekka Paalanen3f897942015-08-19 13:52:47 +0300637 struct weston_compositor *compositor = data;
638 struct fbdev_backend *backend = to_fbdev_backend(compositor);
Philip Withnall4f499172013-02-02 12:02:32 +0000639 struct weston_output *output;
640
Giulio Camuffo954f1832014-10-11 18:27:30 +0300641 if (compositor->session_active) {
Philip Withnall4f499172013-02-02 12:02:32 +0000642 weston_log("entering VT\n");
Giulio Camuffo954f1832014-10-11 18:27:30 +0300643 compositor->state = backend->prev_state;
Philip Withnall4f499172013-02-02 12:02:32 +0000644
Giulio Camuffo954f1832014-10-11 18:27:30 +0300645 wl_list_for_each(output, &compositor->output_list, link) {
646 fbdev_output_reenable(backend, output);
Philip Withnall4f499172013-02-02 12:02:32 +0000647 }
648
Giulio Camuffo954f1832014-10-11 18:27:30 +0300649 weston_compositor_damage_all(compositor);
Philip Withnall4f499172013-02-02 12:02:32 +0000650
Giulio Camuffo954f1832014-10-11 18:27:30 +0300651 udev_input_enable(&backend->input);
Kristian Høgsberg61741a22013-09-17 16:02:57 -0700652 } else {
Philip Withnall4f499172013-02-02 12:02:32 +0000653 weston_log("leaving VT\n");
Giulio Camuffo954f1832014-10-11 18:27:30 +0300654 udev_input_disable(&backend->input);
Philip Withnall4f499172013-02-02 12:02:32 +0000655
Giulio Camuffo954f1832014-10-11 18:27:30 +0300656 wl_list_for_each(output, &compositor->output_list, link) {
Philip Withnall4f499172013-02-02 12:02:32 +0000657 fbdev_output_disable(output);
658 }
659
Giulio Camuffo954f1832014-10-11 18:27:30 +0300660 backend->prev_state = compositor->state;
661 weston_compositor_offscreen(compositor);
Philip Withnall4f499172013-02-02 12:02:32 +0000662
663 /* If we have a repaint scheduled (from the idle handler), make
664 * sure we cancel that so we don't try to pageflip when we're
Philipp Brüschweiler57edf7f2013-03-29 13:01:56 +0100665 * vt switched away. The OFFSCREEN state will prevent
Bryce Harringtonc2be8572015-12-11 13:11:37 -0800666 * further attempts at repainting. When we switch
Philip Withnall4f499172013-02-02 12:02:32 +0000667 * back, we schedule a repaint, which will process
668 * pending frame callbacks. */
669
670 wl_list_for_each(output,
Giulio Camuffo954f1832014-10-11 18:27:30 +0300671 &compositor->output_list, link) {
Philip Withnall4f499172013-02-02 12:02:32 +0000672 output->repaint_needed = 0;
673 }
nerdopolis38946702014-12-03 15:53:03 +0000674 }
Philip Withnall4f499172013-02-02 12:02:32 +0000675}
676
677static void
Kristian Høgsberg3f495872013-09-18 23:00:17 -0700678fbdev_restore(struct weston_compositor *compositor)
Philip Withnall4f499172013-02-02 12:02:32 +0000679{
Kristian Høgsberg3f495872013-09-18 23:00:17 -0700680 weston_launcher_restore(compositor->launcher);
Philip Withnall4f499172013-02-02 12:02:32 +0000681}
682
Giulio Camuffo954f1832014-10-11 18:27:30 +0300683static struct fbdev_backend *
Pekka Paalanena256c5e2016-06-03 14:56:18 +0300684fbdev_backend_create(struct weston_compositor *compositor,
Benoit Gschwind934e89a2016-04-27 23:56:42 +0200685 struct weston_fbdev_backend_config *param)
Philip Withnall4f499172013-02-02 12:02:32 +0000686{
Giulio Camuffo954f1832014-10-11 18:27:30 +0300687 struct fbdev_backend *backend;
Rob Bradford2387fde2013-05-31 18:09:57 +0100688 const char *seat_id = default_seat;
Philip Withnall4f499172013-02-02 12:02:32 +0000689
690 weston_log("initializing fbdev backend\n");
691
Giulio Camuffo954f1832014-10-11 18:27:30 +0300692 backend = zalloc(sizeof *backend);
693 if (backend == NULL)
Philip Withnall4f499172013-02-02 12:02:32 +0000694 return NULL;
695
Giulio Camuffo954f1832014-10-11 18:27:30 +0300696 backend->compositor = compositor;
Pekka Paalanenb5eedad2014-09-23 22:08:45 -0400697 if (weston_compositor_set_presentation_clock_software(
Giulio Camuffo954f1832014-10-11 18:27:30 +0300698 compositor) < 0)
Pekka Paalanenb5eedad2014-09-23 22:08:45 -0400699 goto out_compositor;
700
Giulio Camuffo954f1832014-10-11 18:27:30 +0300701 backend->udev = udev_new();
702 if (backend->udev == NULL) {
Philip Withnall4f499172013-02-02 12:02:32 +0000703 weston_log("Failed to initialize udev context.\n");
704 goto out_compositor;
705 }
706
707 /* Set up the TTY. */
Giulio Camuffo954f1832014-10-11 18:27:30 +0300708 backend->session_listener.notify = session_notify;
709 wl_signal_add(&compositor->session_signal,
710 &backend->session_listener);
711 compositor->launcher =
712 weston_launcher_connect(compositor, param->tty, "seat0", false);
713 if (!compositor->launcher) {
David Herrmann1641d142013-10-15 14:29:57 +0200714 weston_log("fatal: fbdev backend should be run "
715 "using weston-launch binary or as root\n");
Philip Withnall4f499172013-02-02 12:02:32 +0000716 goto out_udev;
717 }
718
Giulio Camuffo954f1832014-10-11 18:27:30 +0300719 backend->base.destroy = fbdev_backend_destroy;
720 backend->base.restore = fbdev_restore;
Philip Withnall4f499172013-02-02 12:02:32 +0000721
Giulio Camuffo954f1832014-10-11 18:27:30 +0300722 backend->prev_state = WESTON_COMPOSITOR_ACTIVE;
Benoit Gschwind934e89a2016-04-27 23:56:42 +0200723 backend->output_transform = param->output_transform;
Philip Withnall4f499172013-02-02 12:02:32 +0000724
Bob Ham91880f12016-01-12 10:21:47 +0000725 weston_setup_vt_switch_bindings(compositor);
726
Pekka Paalanene77f8ad2016-06-08 17:39:37 +0300727 if (pixman_renderer_init(compositor) < 0)
728 goto out_launcher;
Philip Withnall4f499172013-02-02 12:02:32 +0000729
Giulio Camuffo954f1832014-10-11 18:27:30 +0300730 if (fbdev_output_create(backend, param->device) < 0)
Dawid Gajownik82d49252015-07-31 00:02:28 -0300731 goto out_launcher;
Philip Withnall4f499172013-02-02 12:02:32 +0000732
Giulio Camuffo8aedf7b2016-06-02 21:48:12 +0300733 udev_input_init(&backend->input, compositor, backend->udev,
734 seat_id, param->configure_device);
Philip Withnall4f499172013-02-02 12:02:32 +0000735
Giulio Camuffo954f1832014-10-11 18:27:30 +0300736 compositor->backend = &backend->base;
737 return backend;
Philip Withnall4f499172013-02-02 12:02:32 +0000738
Kristian Høgsberg3f495872013-09-18 23:00:17 -0700739out_launcher:
Giulio Camuffo954f1832014-10-11 18:27:30 +0300740 weston_launcher_destroy(compositor->launcher);
Philip Withnall4f499172013-02-02 12:02:32 +0000741
742out_udev:
Giulio Camuffo954f1832014-10-11 18:27:30 +0300743 udev_unref(backend->udev);
Philip Withnall4f499172013-02-02 12:02:32 +0000744
745out_compositor:
Giulio Camuffo954f1832014-10-11 18:27:30 +0300746 weston_compositor_shutdown(compositor);
Giulio Camuffo954f1832014-10-11 18:27:30 +0300747 free(backend);
Philip Withnall4f499172013-02-02 12:02:32 +0000748
749 return NULL;
750}
751
Benoit Gschwind934e89a2016-04-27 23:56:42 +0200752static void
753config_init_to_defaults(struct weston_fbdev_backend_config *config)
754{
755 /* TODO: Ideally, available frame buffers should be enumerated using
756 * udev, rather than passing a device node in as a parameter. */
757 config->tty = 0; /* default to current tty */
758 config->device = "/dev/fb0"; /* default frame buffer */
Benoit Gschwind934e89a2016-04-27 23:56:42 +0200759 config->output_transform = WL_OUTPUT_TRANSFORM_NORMAL;
760}
761
Giulio Camuffo954f1832014-10-11 18:27:30 +0300762WL_EXPORT int
Pekka Paalanena256c5e2016-06-03 14:56:18 +0300763backend_init(struct weston_compositor *compositor,
Giulio Camuffo93daabb2015-10-17 19:24:14 +0300764 struct weston_backend_config *config_base)
Philip Withnall4f499172013-02-02 12:02:32 +0000765{
Giulio Camuffo954f1832014-10-11 18:27:30 +0300766 struct fbdev_backend *b;
Benoit Gschwind934e89a2016-04-27 23:56:42 +0200767 struct weston_fbdev_backend_config config = {{ 0, }};
Philip Withnall4f499172013-02-02 12:02:32 +0000768
Benoit Gschwind934e89a2016-04-27 23:56:42 +0200769 if (config_base == NULL ||
770 config_base->struct_version != WESTON_FBDEV_BACKEND_CONFIG_VERSION ||
771 config_base->struct_size > sizeof(struct weston_fbdev_backend_config)) {
772 weston_log("fbdev backend config structure is invalid\n");
773 return -1;
774 }
Philip Withnall4f499172013-02-02 12:02:32 +0000775
Benoit Gschwind934e89a2016-04-27 23:56:42 +0200776 config_init_to_defaults(&config);
777 memcpy(&config, config_base, config_base->struct_size);
Philip Withnall4f499172013-02-02 12:02:32 +0000778
Pekka Paalanena256c5e2016-06-03 14:56:18 +0300779 b = fbdev_backend_create(compositor, &config);
Giulio Camuffo954f1832014-10-11 18:27:30 +0300780 if (b == NULL)
781 return -1;
782 return 0;
Philip Withnall4f499172013-02-02 12:02:32 +0000783}