blob: 4b3605cf7d0a267434f47b46664f999dd1c1d5ae [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>
Jussi Kukkonen649bbce2016-07-19 14:16:27 +030033#include <stdint.h>
Philip Withnall4f499172013-02-02 12:02:32 +000034#include <stdio.h>
35#include <string.h>
36#include <math.h>
37#include <sys/mman.h>
38#include <sys/types.h>
39#include <fcntl.h>
40#include <unistd.h>
41#include <linux/fb.h>
Kristian Høgsberg7e597f22013-02-18 16:35:26 -050042#include <linux/input.h>
Pekka Paalanena51e71f2017-09-13 17:14:19 +030043#include <assert.h>
Philip Withnall4f499172013-02-02 12:02:32 +000044
45#include <libudev.h>
46
Jon Cruz35b2eaa2015-06-15 15:37:08 -070047#include "shared/helpers.h"
Philip Withnall4f499172013-02-02 12:02:32 +000048#include "compositor.h"
Benoit Gschwind934e89a2016-04-27 23:56:42 +020049#include "compositor-fbdev.h"
Philip Withnall4f499172013-02-02 12:02:32 +000050#include "launcher-util.h"
51#include "pixman-renderer.h"
Peter Hutterer823ad332014-11-26 07:06:31 +100052#include "libinput-seat.h"
Pekka Paalanenb00c79b2016-02-18 16:53:27 +020053#include "presentation-time-server-protocol.h"
Philip Withnall4f499172013-02-02 12:02:32 +000054
Giulio Camuffo954f1832014-10-11 18:27:30 +030055struct fbdev_backend {
56 struct weston_backend base;
57 struct weston_compositor *compositor;
Philip Withnall4f499172013-02-02 12:02:32 +000058 uint32_t prev_state;
59
60 struct udev *udev;
Rob Bradfordd355b802013-05-31 18:09:55 +010061 struct udev_input input;
Benoit Gschwind934e89a2016-04-27 23:56:42 +020062 uint32_t output_transform;
Kristian Høgsberg61741a22013-09-17 16:02:57 -070063 struct wl_listener session_listener;
Philip Withnall4f499172013-02-02 12:02:32 +000064};
65
66struct fbdev_screeninfo {
67 unsigned int x_resolution; /* pixels, visible area */
68 unsigned int y_resolution; /* pixels, visible area */
69 unsigned int width_mm; /* visible screen width in mm */
70 unsigned int height_mm; /* visible screen height in mm */
71 unsigned int bits_per_pixel;
72
73 size_t buffer_length; /* length of frame buffer memory in bytes */
74 size_t line_length; /* length of a line in bytes */
75 char id[16]; /* screen identifier */
76
77 pixman_format_code_t pixel_format; /* frame buffer pixel format */
78 unsigned int refresh_rate; /* Hertz */
79};
80
81struct fbdev_output {
Giulio Camuffo954f1832014-10-11 18:27:30 +030082 struct fbdev_backend *backend;
Philip Withnall4f499172013-02-02 12:02:32 +000083 struct weston_output base;
84
85 struct weston_mode mode;
86 struct wl_event_source *finish_frame_timer;
87
88 /* Frame buffer details. */
Pekka Paalanene5b56592016-04-28 15:12:25 +030089 char *device;
Philip Withnall4f499172013-02-02 12:02:32 +000090 struct fbdev_screeninfo fb_info;
91 void *fb; /* length is fb_info.buffer_length */
92
93 /* pixman details. */
94 pixman_image_t *hw_surface;
Philip Withnall4f499172013-02-02 12:02:32 +000095};
96
Kristian Høgsberg7e597f22013-02-18 16:35:26 -050097static const char default_seat[] = "seat0";
98
Philip Withnall4f499172013-02-02 12:02:32 +000099static inline struct fbdev_output *
100to_fbdev_output(struct weston_output *base)
101{
102 return container_of(base, struct fbdev_output, base);
103}
104
Giulio Camuffo954f1832014-10-11 18:27:30 +0300105static inline struct fbdev_backend *
106to_fbdev_backend(struct weston_compositor *base)
Philip Withnall4f499172013-02-02 12:02:32 +0000107{
Giulio Camuffo954f1832014-10-11 18:27:30 +0300108 return container_of(base->backend, struct fbdev_backend, base);
Philip Withnall4f499172013-02-02 12:02:32 +0000109}
110
111static void
Jonas Ådahle5a12252013-04-05 23:07:11 +0200112fbdev_output_start_repaint_loop(struct weston_output *output)
113{
Pekka Paalanenb5eedad2014-09-23 22:08:45 -0400114 struct timespec ts;
Jonas Ådahle5a12252013-04-05 23:07:11 +0200115
Pekka Paalanen662f3842015-03-18 12:17:26 +0200116 weston_compositor_read_presentation_clock(output->compositor, &ts);
Pekka Paalanenb00c79b2016-02-18 16:53:27 +0200117 weston_output_finish_frame(output, &ts, WP_PRESENTATION_FEEDBACK_INVALID);
Jonas Ådahle5a12252013-04-05 23:07:11 +0200118}
119
Pekka Paalanene77f8ad2016-06-08 17:39:37 +0300120static int
Daniel Stoneb1f166d2017-03-01 11:34:10 +0000121fbdev_output_repaint(struct weston_output *base, pixman_region32_t *damage,
122 void *repaint_data)
Philip Withnall4f499172013-02-02 12:02:32 +0000123{
124 struct fbdev_output *output = to_fbdev_output(base);
125 struct weston_compositor *ec = output->base.compositor;
Philip Withnall4f499172013-02-02 12:02:32 +0000126
127 /* Repaint the damaged region onto the back buffer. */
Sjoerd Simonsc112e0c2015-12-04 19:20:12 -0600128 pixman_renderer_output_set_buffer(base, output->hw_surface);
Philip Withnall4f499172013-02-02 12:02:32 +0000129 ec->renderer->repaint_output(base, damage);
130
Philip Withnall4f499172013-02-02 12:02:32 +0000131 /* Update the damage region. */
132 pixman_region32_subtract(&ec->primary_plane.damage,
133 &ec->primary_plane.damage, damage);
134
135 /* Schedule the end of the frame. We do not sync this to the frame
136 * buffer clock because users who want that should be using the DRM
137 * compositor. FBIO_WAITFORVSYNC blocks and FB_ACTIVATE_VBL requires
138 * panning, which is broken in most kernel drivers.
139 *
140 * Finish the frame synchronised to the specified refresh rate. The
141 * refresh rate is given in mHz and the interval in ms. */
142 wl_event_source_timer_update(output->finish_frame_timer,
143 1000000 / output->mode.refresh);
David Herrmann1edf44c2013-10-22 17:11:26 +0200144
145 return 0;
Adrian Negreanu4aa756d2013-09-06 15:16:09 +0300146}
147
Philip Withnall4f499172013-02-02 12:02:32 +0000148static int
149finish_frame_handler(void *data)
150{
151 struct fbdev_output *output = data;
Pekka Paalanen363aa7b2014-12-17 16:20:40 +0200152 struct timespec ts;
Philip Withnall4f499172013-02-02 12:02:32 +0000153
Pekka Paalanen662f3842015-03-18 12:17:26 +0200154 weston_compositor_read_presentation_clock(output->base.compositor, &ts);
Pekka Paalanen363aa7b2014-12-17 16:20:40 +0200155 weston_output_finish_frame(&output->base, &ts, 0);
Philip Withnall4f499172013-02-02 12:02:32 +0000156
157 return 1;
158}
159
160static pixman_format_code_t
161calculate_pixman_format(struct fb_var_screeninfo *vinfo,
162 struct fb_fix_screeninfo *finfo)
163{
164 /* Calculate the pixman format supported by the frame buffer from the
165 * buffer's metadata. Return 0 if no known pixman format is supported
166 * (since this has depth 0 it's guaranteed to not conflict with any
167 * actual pixman format).
168 *
169 * Documentation on the vinfo and finfo structures:
170 * http://www.mjmwired.net/kernel/Documentation/fb/api.txt
171 *
172 * TODO: Try a bit harder to support other formats, including setting
173 * the preferred format in the hardware. */
174 int type;
175
176 weston_log("Calculating pixman format from:\n"
177 STAMP_SPACE " - type: %i (aux: %i)\n"
178 STAMP_SPACE " - visual: %i\n"
179 STAMP_SPACE " - bpp: %i (grayscale: %i)\n"
180 STAMP_SPACE " - red: offset: %i, length: %i, MSB: %i\n"
181 STAMP_SPACE " - green: offset: %i, length: %i, MSB: %i\n"
182 STAMP_SPACE " - blue: offset: %i, length: %i, MSB: %i\n"
183 STAMP_SPACE " - transp: offset: %i, length: %i, MSB: %i\n",
184 finfo->type, finfo->type_aux, finfo->visual,
185 vinfo->bits_per_pixel, vinfo->grayscale,
186 vinfo->red.offset, vinfo->red.length, vinfo->red.msb_right,
187 vinfo->green.offset, vinfo->green.length,
188 vinfo->green.msb_right,
189 vinfo->blue.offset, vinfo->blue.length,
190 vinfo->blue.msb_right,
191 vinfo->transp.offset, vinfo->transp.length,
192 vinfo->transp.msb_right);
193
194 /* We only handle packed formats at the moment. */
195 if (finfo->type != FB_TYPE_PACKED_PIXELS)
196 return 0;
197
198 /* We only handle true-colour frame buffers at the moment. */
Marc Chalainffbddff2013-09-03 16:47:43 +0200199 switch(finfo->visual) {
200 case FB_VISUAL_TRUECOLOR:
201 case FB_VISUAL_DIRECTCOLOR:
202 if (vinfo->grayscale != 0)
203 return 0;
204 break;
205 default:
206 return 0;
207 }
Philip Withnall4f499172013-02-02 12:02:32 +0000208
209 /* We only support formats with MSBs on the left. */
210 if (vinfo->red.msb_right != 0 || vinfo->green.msb_right != 0 ||
211 vinfo->blue.msb_right != 0)
212 return 0;
213
214 /* Work out the format type from the offsets. We only support RGBA and
215 * ARGB at the moment. */
216 type = PIXMAN_TYPE_OTHER;
217
218 if ((vinfo->transp.offset >= vinfo->red.offset ||
219 vinfo->transp.length == 0) &&
220 vinfo->red.offset >= vinfo->green.offset &&
221 vinfo->green.offset >= vinfo->blue.offset)
222 type = PIXMAN_TYPE_ARGB;
223 else if (vinfo->red.offset >= vinfo->green.offset &&
224 vinfo->green.offset >= vinfo->blue.offset &&
225 vinfo->blue.offset >= vinfo->transp.offset)
226 type = PIXMAN_TYPE_RGBA;
227
228 if (type == PIXMAN_TYPE_OTHER)
229 return 0;
230
231 /* Build the format. */
232 return PIXMAN_FORMAT(vinfo->bits_per_pixel, type,
233 vinfo->transp.length,
234 vinfo->red.length,
235 vinfo->green.length,
236 vinfo->blue.length);
237}
238
239static int
240calculate_refresh_rate(struct fb_var_screeninfo *vinfo)
241{
242 uint64_t quot;
243
244 /* Calculate monitor refresh rate. Default is 60 Hz. Units are mHz. */
245 quot = (vinfo->upper_margin + vinfo->lower_margin + vinfo->yres);
246 quot *= (vinfo->left_margin + vinfo->right_margin + vinfo->xres);
247 quot *= vinfo->pixclock;
248
249 if (quot > 0) {
250 uint64_t refresh_rate;
251
252 refresh_rate = 1000000000000000LLU / quot;
253 if (refresh_rate > 200000)
254 refresh_rate = 200000; /* cap at 200 Hz */
255
Oliver Smitha5066e02017-04-17 11:11:00 +0000256 if (refresh_rate >= 1000) /* at least 1 Hz */
257 return refresh_rate;
Philip Withnall4f499172013-02-02 12:02:32 +0000258 }
259
260 return 60 * 1000; /* default to 60 Hz */
261}
262
263static int
Pekka Paalanen82db6b72017-09-12 17:15:42 +0300264fbdev_query_screen_info(int fd, struct fbdev_screeninfo *info)
Philip Withnall4f499172013-02-02 12:02:32 +0000265{
266 struct fb_var_screeninfo varinfo;
267 struct fb_fix_screeninfo fixinfo;
268
269 /* Probe the device for screen information. */
270 if (ioctl(fd, FBIOGET_FSCREENINFO, &fixinfo) < 0 ||
271 ioctl(fd, FBIOGET_VSCREENINFO, &varinfo) < 0) {
272 return -1;
273 }
274
275 /* Store the pertinent data. */
276 info->x_resolution = varinfo.xres;
277 info->y_resolution = varinfo.yres;
278 info->width_mm = varinfo.width;
279 info->height_mm = varinfo.height;
280 info->bits_per_pixel = varinfo.bits_per_pixel;
281
282 info->buffer_length = fixinfo.smem_len;
283 info->line_length = fixinfo.line_length;
Derek Foreman61793382015-09-02 13:45:20 -0500284 strncpy(info->id, fixinfo.id, sizeof(info->id));
Bryce Harrington44bbdd02015-09-23 17:39:05 -0700285 info->id[sizeof(info->id)-1] = '\0';
Philip Withnall4f499172013-02-02 12:02:32 +0000286
287 info->pixel_format = calculate_pixman_format(&varinfo, &fixinfo);
288 info->refresh_rate = calculate_refresh_rate(&varinfo);
289
290 if (info->pixel_format == 0) {
291 weston_log("Frame buffer uses an unsupported format.\n");
292 return -1;
293 }
294
295 return 1;
296}
297
298static int
Pekka Paalanen82db6b72017-09-12 17:15:42 +0300299fbdev_set_screen_info(int fd, struct fbdev_screeninfo *info)
Philip Withnall4f499172013-02-02 12:02:32 +0000300{
301 struct fb_var_screeninfo varinfo;
302
303 /* Grab the current screen information. */
304 if (ioctl(fd, FBIOGET_VSCREENINFO, &varinfo) < 0) {
305 return -1;
306 }
307
308 /* Update the information. */
309 varinfo.xres = info->x_resolution;
310 varinfo.yres = info->y_resolution;
311 varinfo.width = info->width_mm;
312 varinfo.height = info->height_mm;
313 varinfo.bits_per_pixel = info->bits_per_pixel;
314
315 /* Try to set up an ARGB (x8r8g8b8) pixel format. */
316 varinfo.grayscale = 0;
317 varinfo.transp.offset = 24;
318 varinfo.transp.length = 0;
319 varinfo.transp.msb_right = 0;
320 varinfo.red.offset = 16;
321 varinfo.red.length = 8;
322 varinfo.red.msb_right = 0;
323 varinfo.green.offset = 8;
324 varinfo.green.length = 8;
325 varinfo.green.msb_right = 0;
326 varinfo.blue.offset = 0;
327 varinfo.blue.length = 8;
328 varinfo.blue.msb_right = 0;
329
330 /* Set the device's screen information. */
331 if (ioctl(fd, FBIOPUT_VSCREENINFO, &varinfo) < 0) {
332 return -1;
333 }
334
335 return 1;
336}
337
Philip Withnall4f499172013-02-02 12:02:32 +0000338/* Returns an FD for the frame buffer device. */
339static int
Pekka Paalanen82db6b72017-09-12 17:15:42 +0300340fbdev_frame_buffer_open(const char *fb_dev,
341 struct fbdev_screeninfo *screen_info)
Philip Withnall4f499172013-02-02 12:02:32 +0000342{
343 int fd = -1;
344
345 weston_log("Opening fbdev frame buffer.\n");
346
347 /* Open the frame buffer device. */
348 fd = open(fb_dev, O_RDWR | O_CLOEXEC);
349 if (fd < 0) {
350 weston_log("Failed to open frame buffer device ‘%s’: %s\n",
351 fb_dev, strerror(errno));
352 return -1;
353 }
354
355 /* Grab the screen info. */
Pekka Paalanen82db6b72017-09-12 17:15:42 +0300356 if (fbdev_query_screen_info(fd, screen_info) < 0) {
Philip Withnall4f499172013-02-02 12:02:32 +0000357 weston_log("Failed to get frame buffer info: %s\n",
358 strerror(errno));
359
360 close(fd);
361 return -1;
362 }
363
364 return fd;
365}
366
367/* Closes the FD on success or failure. */
368static int
369fbdev_frame_buffer_map(struct fbdev_output *output, int fd)
370{
371 int retval = -1;
372
373 weston_log("Mapping fbdev frame buffer.\n");
374
375 /* Map the frame buffer. Write-only mode, since we don't want to read
376 * anything back (because it's slow). */
377 output->fb = mmap(NULL, output->fb_info.buffer_length,
378 PROT_WRITE, MAP_SHARED, fd, 0);
379 if (output->fb == MAP_FAILED) {
380 weston_log("Failed to mmap frame buffer: %s\n",
381 strerror(errno));
Pekka Paalanen82b8ddf2017-09-13 11:58:00 +0300382 output->fb = NULL;
Philip Withnall4f499172013-02-02 12:02:32 +0000383 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:
Pekka Paalanena51e71f2017-09-13 17:14:19 +0300402 if (retval != 0 && output->fb != NULL) {
403 munmap(output->fb, output->fb_info.buffer_length);
404 output->fb = NULL;
405 }
Philip Withnall4f499172013-02-02 12:02:32 +0000406
407out_close:
408 if (fd >= 0)
409 close(fd);
410
411 return retval;
412}
413
414static void
Pekka Paalanena51e71f2017-09-13 17:14:19 +0300415fbdev_frame_buffer_unmap(struct fbdev_output *output)
Philip Withnall4f499172013-02-02 12:02:32 +0000416{
Pekka Paalanena51e71f2017-09-13 17:14:19 +0300417 if (!output->fb) {
418 assert(!output->hw_surface);
419 return;
420 }
421
422 weston_log("Unmapping fbdev frame buffer.\n");
423
424 if (output->hw_surface)
425 pixman_image_unref(output->hw_surface);
426 output->hw_surface = NULL;
Philip Withnall4f499172013-02-02 12:02:32 +0000427
428 if (munmap(output->fb, output->fb_info.buffer_length) < 0)
429 weston_log("Failed to munmap frame buffer: %s\n",
430 strerror(errno));
431
432 output->fb = NULL;
433}
434
435static void fbdev_output_destroy(struct weston_output *base);
Philip Withnall4f499172013-02-02 12:02:32 +0000436
437static int
Armin Krezović6ba369d2016-09-30 14:11:06 +0200438fbdev_output_enable(struct weston_output *base)
439{
440 struct fbdev_output *output = to_fbdev_output(base);
441 struct fbdev_backend *backend = to_fbdev_backend(base->compositor);
442 int fb_fd;
443 struct wl_event_loop *loop;
444
445 /* Create the frame buffer. */
Pekka Paalanen82db6b72017-09-12 17:15:42 +0300446 fb_fd = fbdev_frame_buffer_open(output->device, &output->fb_info);
Armin Krezović6ba369d2016-09-30 14:11:06 +0200447 if (fb_fd < 0) {
448 weston_log("Creating frame buffer failed.\n");
449 return -1;
450 }
451
452 if (fbdev_frame_buffer_map(output, fb_fd) < 0) {
453 weston_log("Mapping frame buffer failed.\n");
454 return -1;
455 }
456
457 output->base.start_repaint_loop = fbdev_output_start_repaint_loop;
458 output->base.repaint = fbdev_output_repaint;
459
460 if (pixman_renderer_output_create(&output->base) < 0)
461 goto out_hw_surface;
462
463 loop = wl_display_get_event_loop(backend->compositor->wl_display);
464 output->finish_frame_timer =
465 wl_event_loop_add_timer(loop, finish_frame_handler, output);
466
467 weston_log("fbdev output %d×%d px\n",
468 output->mode.width, output->mode.height);
469 weston_log_continue(STAMP_SPACE "guessing %d Hz and 96 dpi\n",
470 output->mode.refresh / 1000);
471
472 return 0;
473
474out_hw_surface:
Pekka Paalanena51e71f2017-09-13 17:14:19 +0300475 fbdev_frame_buffer_unmap(output);
Armin Krezović6ba369d2016-09-30 14:11:06 +0200476
477 return -1;
478}
479
480static int
Pekka Paalanen82ffe792017-09-13 17:25:41 +0300481fbdev_output_disable(struct weston_output *base)
Pekka Paalanenacd71fb2017-08-15 10:35:09 +0300482{
Pekka Paalanena51e71f2017-09-13 17:14:19 +0300483 struct fbdev_output *output = to_fbdev_output(base);
484
Pekka Paalanenacd71fb2017-08-15 10:35:09 +0300485 if (!base->enabled)
486 return 0;
487
Pekka Paalanencbe7fb02017-09-14 11:43:29 +0300488 wl_event_source_remove(output->finish_frame_timer);
489 output->finish_frame_timer = NULL;
490
Pekka Paalanen61e5a272017-09-13 17:22:38 +0300491 pixman_renderer_output_destroy(&output->base);
Pekka Paalanena51e71f2017-09-13 17:14:19 +0300492 fbdev_frame_buffer_unmap(output);
Pekka Paalanenacd71fb2017-08-15 10:35:09 +0300493
Pekka Paalanenacd71fb2017-08-15 10:35:09 +0300494 return 0;
495}
496
497static int
Giulio Camuffo954f1832014-10-11 18:27:30 +0300498fbdev_output_create(struct fbdev_backend *backend,
Philip Withnall4f499172013-02-02 12:02:32 +0000499 const char *device)
500{
501 struct fbdev_output *output;
Philip Withnall4f499172013-02-02 12:02:32 +0000502 int fb_fd;
Philip Withnall4f499172013-02-02 12:02:32 +0000503
504 weston_log("Creating fbdev output.\n");
505
Bryce Harringtonde16d892014-11-20 22:21:57 -0800506 output = zalloc(sizeof *output);
507 if (output == NULL)
Philip Withnall4f499172013-02-02 12:02:32 +0000508 return -1;
509
Giulio Camuffo954f1832014-10-11 18:27:30 +0300510 output->backend = backend;
Pekka Paalanene5b56592016-04-28 15:12:25 +0300511 output->device = strdup(device);
Philip Withnall4f499172013-02-02 12:02:32 +0000512
513 /* Create the frame buffer. */
Pekka Paalanen82db6b72017-09-12 17:15:42 +0300514 fb_fd = fbdev_frame_buffer_open(device, &output->fb_info);
Philip Withnall4f499172013-02-02 12:02:32 +0000515 if (fb_fd < 0) {
516 weston_log("Creating frame buffer failed.\n");
517 goto out_free;
518 }
Pekka Paalanene77f8ad2016-06-08 17:39:37 +0300519
Pekka Paalanen26ac2e12017-04-03 13:18:13 +0300520 weston_output_init(&output->base, backend->compositor, "fbdev");
521
Philip Withnall4f499172013-02-02 12:02:32 +0000522 output->base.destroy = fbdev_output_destroy;
Pekka Paalanen82ffe792017-09-13 17:25:41 +0300523 output->base.disable = fbdev_output_disable;
Armin Krezović6ba369d2016-09-30 14:11:06 +0200524 output->base.enable = fbdev_output_enable;
525
Philip Withnall4f499172013-02-02 12:02:32 +0000526 /* only one static mode in list */
527 output->mode.flags =
528 WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
529 output->mode.width = output->fb_info.x_resolution;
530 output->mode.height = output->fb_info.y_resolution;
531 output->mode.refresh = output->fb_info.refresh_rate;
Philip Withnall4f499172013-02-02 12:02:32 +0000532 wl_list_insert(&output->base.mode_list, &output->mode.link);
533
Hardeningff39efa2013-09-18 23:56:35 +0200534 output->base.current_mode = &output->mode;
Philip Withnall4f499172013-02-02 12:02:32 +0000535 output->base.subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN;
536 output->base.make = "unknown";
537 output->base.model = output->fb_info.id;
538
Armin Krezović6ba369d2016-09-30 14:11:06 +0200539 output->base.mm_width = output->fb_info.width_mm;
540 output->base.mm_height = output->fb_info.height_mm;
Philip Withnall4f499172013-02-02 12:02:32 +0000541
Armin Krezović6ba369d2016-09-30 14:11:06 +0200542 close(fb_fd);
Adrian Negreanu4aa756d2013-09-06 15:16:09 +0300543
Armin Krezović6ba369d2016-09-30 14:11:06 +0200544 weston_compositor_add_pending_output(&output->base, backend->compositor);
Philip Withnall4f499172013-02-02 12:02:32 +0000545
546 return 0;
547
Philip Withnall4f499172013-02-02 12:02:32 +0000548out_free:
Pekka Paalanene5b56592016-04-28 15:12:25 +0300549 free(output->device);
Philip Withnall4f499172013-02-02 12:02:32 +0000550 free(output);
551
552 return -1;
553}
554
555static void
556fbdev_output_destroy(struct weston_output *base)
557{
558 struct fbdev_output *output = to_fbdev_output(base);
559
560 weston_log("Destroying fbdev output.\n");
561
Pekka Paalanen82ffe792017-09-13 17:25:41 +0300562 fbdev_output_disable(base);
Philip Withnall4f499172013-02-02 12:02:32 +0000563
564 /* Remove the output. */
Pekka Paalanenae6d35d2017-08-16 12:07:14 +0300565 weston_output_release(&output->base);
Philip Withnall4f499172013-02-02 12:02:32 +0000566
Pekka Paalanene5b56592016-04-28 15:12:25 +0300567 free(output->device);
Philip Withnall4f499172013-02-02 12:02:32 +0000568 free(output);
569}
570
571/* strcmp()-style return values. */
572static int
573compare_screen_info (const struct fbdev_screeninfo *a,
574 const struct fbdev_screeninfo *b)
575{
576 if (a->x_resolution == b->x_resolution &&
577 a->y_resolution == b->y_resolution &&
578 a->width_mm == b->width_mm &&
579 a->height_mm == b->height_mm &&
580 a->bits_per_pixel == b->bits_per_pixel &&
581 a->pixel_format == b->pixel_format &&
582 a->refresh_rate == b->refresh_rate)
583 return 0;
584
585 return 1;
586}
587
588static int
Giulio Camuffo954f1832014-10-11 18:27:30 +0300589fbdev_output_reenable(struct fbdev_backend *backend,
Philip Withnall4f499172013-02-02 12:02:32 +0000590 struct weston_output *base)
591{
592 struct fbdev_output *output = to_fbdev_output(base);
593 struct fbdev_screeninfo new_screen_info;
594 int fb_fd;
Pekka Paalanene5b56592016-04-28 15:12:25 +0300595 char *device;
Philip Withnall4f499172013-02-02 12:02:32 +0000596
597 weston_log("Re-enabling fbdev output.\n");
598
599 /* Create the frame buffer. */
Pekka Paalanen82db6b72017-09-12 17:15:42 +0300600 fb_fd = fbdev_frame_buffer_open(output->device, &new_screen_info);
Philip Withnall4f499172013-02-02 12:02:32 +0000601 if (fb_fd < 0) {
602 weston_log("Creating frame buffer failed.\n");
603 goto err;
604 }
605
606 /* Check whether the frame buffer details have changed since we were
607 * disabled. */
608 if (compare_screen_info (&output->fb_info, &new_screen_info) != 0) {
609 /* Perform a mode-set to restore the old mode. */
Pekka Paalanen82db6b72017-09-12 17:15:42 +0300610 if (fbdev_set_screen_info(fb_fd, &output->fb_info) < 0) {
Philip Withnall4f499172013-02-02 12:02:32 +0000611 weston_log("Failed to restore mode settings. "
612 "Attempting to re-open output anyway.\n");
613 }
614
Rob Bradford581b3fd2013-07-26 16:29:39 +0100615 close(fb_fd);
616
Philip Withnall4f499172013-02-02 12:02:32 +0000617 /* Remove and re-add the output so that resources depending on
618 * the frame buffer X/Y resolution (such as the shadow buffer)
619 * are re-initialised. */
Pekka Paalanene5b56592016-04-28 15:12:25 +0300620 device = strdup(output->device);
621 fbdev_output_destroy(&output->base);
Giulio Camuffo954f1832014-10-11 18:27:30 +0300622 fbdev_output_create(backend, device);
Pekka Paalanene5b56592016-04-28 15:12:25 +0300623 free(device);
Philip Withnall4f499172013-02-02 12:02:32 +0000624
625 return 0;
626 }
627
628 /* Map the device if it has the same details as before. */
Pekka Paalanene77f8ad2016-06-08 17:39:37 +0300629 if (fbdev_frame_buffer_map(output, fb_fd) < 0) {
630 weston_log("Mapping frame buffer failed.\n");
631 goto err;
Philip Withnall4f499172013-02-02 12:02:32 +0000632 }
633
634 return 0;
635
636err:
637 return -1;
638}
639
Philip Withnall4f499172013-02-02 12:02:32 +0000640static void
Giulio Camuffo954f1832014-10-11 18:27:30 +0300641fbdev_backend_destroy(struct weston_compositor *base)
Philip Withnall4f499172013-02-02 12:02:32 +0000642{
Giulio Camuffo954f1832014-10-11 18:27:30 +0300643 struct fbdev_backend *backend = to_fbdev_backend(base);
Philip Withnall4f499172013-02-02 12:02:32 +0000644
Giulio Camuffo954f1832014-10-11 18:27:30 +0300645 udev_input_destroy(&backend->input);
Philip Withnall4f499172013-02-02 12:02:32 +0000646
647 /* Destroy the output. */
Giulio Camuffo954f1832014-10-11 18:27:30 +0300648 weston_compositor_shutdown(base);
Philip Withnall4f499172013-02-02 12:02:32 +0000649
650 /* Chain up. */
Giulio Camuffo954f1832014-10-11 18:27:30 +0300651 weston_launcher_destroy(base->launcher);
Philip Withnall4f499172013-02-02 12:02:32 +0000652
Pekka Paalanen513f9a42017-09-13 16:49:02 +0300653 udev_unref(backend->udev);
654
Giulio Camuffo954f1832014-10-11 18:27:30 +0300655 free(backend);
Philip Withnall4f499172013-02-02 12:02:32 +0000656}
657
658static void
Kristian Høgsberg61741a22013-09-17 16:02:57 -0700659session_notify(struct wl_listener *listener, void *data)
Philip Withnall4f499172013-02-02 12:02:32 +0000660{
Pekka Paalanen3f897942015-08-19 13:52:47 +0300661 struct weston_compositor *compositor = data;
662 struct fbdev_backend *backend = to_fbdev_backend(compositor);
Philip Withnall4f499172013-02-02 12:02:32 +0000663 struct weston_output *output;
664
Giulio Camuffo954f1832014-10-11 18:27:30 +0300665 if (compositor->session_active) {
Philip Withnall4f499172013-02-02 12:02:32 +0000666 weston_log("entering VT\n");
Giulio Camuffo954f1832014-10-11 18:27:30 +0300667 compositor->state = backend->prev_state;
Philip Withnall4f499172013-02-02 12:02:32 +0000668
Giulio Camuffo954f1832014-10-11 18:27:30 +0300669 wl_list_for_each(output, &compositor->output_list, link) {
670 fbdev_output_reenable(backend, output);
Philip Withnall4f499172013-02-02 12:02:32 +0000671 }
672
Giulio Camuffo954f1832014-10-11 18:27:30 +0300673 weston_compositor_damage_all(compositor);
Philip Withnall4f499172013-02-02 12:02:32 +0000674
Giulio Camuffo954f1832014-10-11 18:27:30 +0300675 udev_input_enable(&backend->input);
Kristian Høgsberg61741a22013-09-17 16:02:57 -0700676 } else {
Philip Withnall4f499172013-02-02 12:02:32 +0000677 weston_log("leaving VT\n");
Giulio Camuffo954f1832014-10-11 18:27:30 +0300678 udev_input_disable(&backend->input);
Philip Withnall4f499172013-02-02 12:02:32 +0000679
Giulio Camuffo954f1832014-10-11 18:27:30 +0300680 wl_list_for_each(output, &compositor->output_list, link) {
Pekka Paalanena51e71f2017-09-13 17:14:19 +0300681 fbdev_frame_buffer_unmap(to_fbdev_output(output));
Philip Withnall4f499172013-02-02 12:02:32 +0000682 }
683
Giulio Camuffo954f1832014-10-11 18:27:30 +0300684 backend->prev_state = compositor->state;
685 weston_compositor_offscreen(compositor);
Philip Withnall4f499172013-02-02 12:02:32 +0000686
687 /* If we have a repaint scheduled (from the idle handler), make
688 * sure we cancel that so we don't try to pageflip when we're
Philipp Brüschweiler57edf7f2013-03-29 13:01:56 +0100689 * vt switched away. The OFFSCREEN state will prevent
Bryce Harringtonc2be8572015-12-11 13:11:37 -0800690 * further attempts at repainting. When we switch
Philip Withnall4f499172013-02-02 12:02:32 +0000691 * back, we schedule a repaint, which will process
692 * pending frame callbacks. */
693
694 wl_list_for_each(output,
Giulio Camuffo954f1832014-10-11 18:27:30 +0300695 &compositor->output_list, link) {
Daniel Stone09a97e22017-03-01 11:34:06 +0000696 output->repaint_needed = false;
Philip Withnall4f499172013-02-02 12:02:32 +0000697 }
nerdopolis38946702014-12-03 15:53:03 +0000698 }
Philip Withnall4f499172013-02-02 12:02:32 +0000699}
700
701static void
Kristian Høgsberg3f495872013-09-18 23:00:17 -0700702fbdev_restore(struct weston_compositor *compositor)
Philip Withnall4f499172013-02-02 12:02:32 +0000703{
Kristian Høgsberg3f495872013-09-18 23:00:17 -0700704 weston_launcher_restore(compositor->launcher);
Philip Withnall4f499172013-02-02 12:02:32 +0000705}
706
Giulio Camuffo954f1832014-10-11 18:27:30 +0300707static struct fbdev_backend *
Pekka Paalanena256c5e2016-06-03 14:56:18 +0300708fbdev_backend_create(struct weston_compositor *compositor,
Benoit Gschwind934e89a2016-04-27 23:56:42 +0200709 struct weston_fbdev_backend_config *param)
Philip Withnall4f499172013-02-02 12:02:32 +0000710{
Giulio Camuffo954f1832014-10-11 18:27:30 +0300711 struct fbdev_backend *backend;
Rob Bradford2387fde2013-05-31 18:09:57 +0100712 const char *seat_id = default_seat;
Philip Withnall4f499172013-02-02 12:02:32 +0000713
714 weston_log("initializing fbdev backend\n");
715
Giulio Camuffo954f1832014-10-11 18:27:30 +0300716 backend = zalloc(sizeof *backend);
717 if (backend == NULL)
Philip Withnall4f499172013-02-02 12:02:32 +0000718 return NULL;
719
Giulio Camuffo954f1832014-10-11 18:27:30 +0300720 backend->compositor = compositor;
Pekka Paalanen7da9a382017-08-30 11:29:49 +0300721 compositor->backend = &backend->base;
Pekka Paalanenb5eedad2014-09-23 22:08:45 -0400722 if (weston_compositor_set_presentation_clock_software(
Giulio Camuffo954f1832014-10-11 18:27:30 +0300723 compositor) < 0)
Pekka Paalanenb5eedad2014-09-23 22:08:45 -0400724 goto out_compositor;
725
Giulio Camuffo954f1832014-10-11 18:27:30 +0300726 backend->udev = udev_new();
727 if (backend->udev == NULL) {
Philip Withnall4f499172013-02-02 12:02:32 +0000728 weston_log("Failed to initialize udev context.\n");
729 goto out_compositor;
730 }
731
732 /* Set up the TTY. */
Giulio Camuffo954f1832014-10-11 18:27:30 +0300733 backend->session_listener.notify = session_notify;
734 wl_signal_add(&compositor->session_signal,
735 &backend->session_listener);
736 compositor->launcher =
737 weston_launcher_connect(compositor, param->tty, "seat0", false);
738 if (!compositor->launcher) {
David Herrmann1641d142013-10-15 14:29:57 +0200739 weston_log("fatal: fbdev backend should be run "
740 "using weston-launch binary or as root\n");
Philip Withnall4f499172013-02-02 12:02:32 +0000741 goto out_udev;
742 }
743
Giulio Camuffo954f1832014-10-11 18:27:30 +0300744 backend->base.destroy = fbdev_backend_destroy;
745 backend->base.restore = fbdev_restore;
Philip Withnall4f499172013-02-02 12:02:32 +0000746
Giulio Camuffo954f1832014-10-11 18:27:30 +0300747 backend->prev_state = WESTON_COMPOSITOR_ACTIVE;
Philip Withnall4f499172013-02-02 12:02:32 +0000748
Bob Ham91880f12016-01-12 10:21:47 +0000749 weston_setup_vt_switch_bindings(compositor);
750
Pekka Paalanene77f8ad2016-06-08 17:39:37 +0300751 if (pixman_renderer_init(compositor) < 0)
752 goto out_launcher;
Philip Withnall4f499172013-02-02 12:02:32 +0000753
Giulio Camuffo954f1832014-10-11 18:27:30 +0300754 if (fbdev_output_create(backend, param->device) < 0)
Dawid Gajownik82d49252015-07-31 00:02:28 -0300755 goto out_launcher;
Philip Withnall4f499172013-02-02 12:02:32 +0000756
Giulio Camuffo8aedf7b2016-06-02 21:48:12 +0300757 udev_input_init(&backend->input, compositor, backend->udev,
758 seat_id, param->configure_device);
Philip Withnall4f499172013-02-02 12:02:32 +0000759
Giulio Camuffo954f1832014-10-11 18:27:30 +0300760 return backend;
Philip Withnall4f499172013-02-02 12:02:32 +0000761
Kristian Høgsberg3f495872013-09-18 23:00:17 -0700762out_launcher:
Giulio Camuffo954f1832014-10-11 18:27:30 +0300763 weston_launcher_destroy(compositor->launcher);
Philip Withnall4f499172013-02-02 12:02:32 +0000764
765out_udev:
Giulio Camuffo954f1832014-10-11 18:27:30 +0300766 udev_unref(backend->udev);
Philip Withnall4f499172013-02-02 12:02:32 +0000767
768out_compositor:
Giulio Camuffo954f1832014-10-11 18:27:30 +0300769 weston_compositor_shutdown(compositor);
Giulio Camuffo954f1832014-10-11 18:27:30 +0300770 free(backend);
Philip Withnall4f499172013-02-02 12:02:32 +0000771
772 return NULL;
773}
774
Benoit Gschwind934e89a2016-04-27 23:56:42 +0200775static void
776config_init_to_defaults(struct weston_fbdev_backend_config *config)
777{
778 /* TODO: Ideally, available frame buffers should be enumerated using
779 * udev, rather than passing a device node in as a parameter. */
780 config->tty = 0; /* default to current tty */
781 config->device = "/dev/fb0"; /* default frame buffer */
Benoit Gschwind934e89a2016-04-27 23:56:42 +0200782}
783
Giulio Camuffo954f1832014-10-11 18:27:30 +0300784WL_EXPORT int
Quentin Glidic23e1d6f2016-12-02 14:08:44 +0100785weston_backend_init(struct weston_compositor *compositor,
786 struct weston_backend_config *config_base)
Philip Withnall4f499172013-02-02 12:02:32 +0000787{
Giulio Camuffo954f1832014-10-11 18:27:30 +0300788 struct fbdev_backend *b;
Benoit Gschwind934e89a2016-04-27 23:56:42 +0200789 struct weston_fbdev_backend_config config = {{ 0, }};
Philip Withnall4f499172013-02-02 12:02:32 +0000790
Benoit Gschwind934e89a2016-04-27 23:56:42 +0200791 if (config_base == NULL ||
792 config_base->struct_version != WESTON_FBDEV_BACKEND_CONFIG_VERSION ||
793 config_base->struct_size > sizeof(struct weston_fbdev_backend_config)) {
794 weston_log("fbdev backend config structure is invalid\n");
795 return -1;
796 }
Philip Withnall4f499172013-02-02 12:02:32 +0000797
Benoit Gschwind934e89a2016-04-27 23:56:42 +0200798 config_init_to_defaults(&config);
799 memcpy(&config, config_base, config_base->struct_size);
Philip Withnall4f499172013-02-02 12:02:32 +0000800
Pekka Paalanena256c5e2016-06-03 14:56:18 +0300801 b = fbdev_backend_create(compositor, &config);
Giulio Camuffo954f1832014-10-11 18:27:30 +0300802 if (b == NULL)
803 return -1;
804 return 0;
Philip Withnall4f499172013-02-02 12:02:32 +0000805}