blob: 74971c35d58712499bbd228033dba9c9780c407c [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
Pekka Paalanen23058122017-09-14 16:50:44 +030081struct fbdev_head {
82 struct weston_head base;
83
84 /* Frame buffer details. */
85 char *device;
86 struct fbdev_screeninfo fb_info;
87};
88
Philip Withnall4f499172013-02-02 12:02:32 +000089struct fbdev_output {
Giulio Camuffo954f1832014-10-11 18:27:30 +030090 struct fbdev_backend *backend;
Philip Withnall4f499172013-02-02 12:02:32 +000091 struct weston_output base;
92
93 struct weston_mode mode;
94 struct wl_event_source *finish_frame_timer;
95
Pekka Paalanen23058122017-09-14 16:50:44 +030096 /* framebuffer mmap details */
97 size_t buffer_length;
98 void *fb;
Philip Withnall4f499172013-02-02 12:02:32 +000099
100 /* pixman details. */
101 pixman_image_t *hw_surface;
Philip Withnall4f499172013-02-02 12:02:32 +0000102};
103
Kristian Høgsberg7e597f22013-02-18 16:35:26 -0500104static const char default_seat[] = "seat0";
105
Pekka Paalanen23058122017-09-14 16:50:44 +0300106static inline struct fbdev_head *
107to_fbdev_head(struct weston_head *base)
108{
109 return container_of(base, struct fbdev_head, base);
110}
111
Philip Withnall4f499172013-02-02 12:02:32 +0000112static inline struct fbdev_output *
113to_fbdev_output(struct weston_output *base)
114{
115 return container_of(base, struct fbdev_output, base);
116}
117
Giulio Camuffo954f1832014-10-11 18:27:30 +0300118static inline struct fbdev_backend *
119to_fbdev_backend(struct weston_compositor *base)
Philip Withnall4f499172013-02-02 12:02:32 +0000120{
Giulio Camuffo954f1832014-10-11 18:27:30 +0300121 return container_of(base->backend, struct fbdev_backend, base);
Philip Withnall4f499172013-02-02 12:02:32 +0000122}
123
Pekka Paalanen23058122017-09-14 16:50:44 +0300124static struct fbdev_head *
125fbdev_output_get_head(struct fbdev_output *output)
126{
127 if (wl_list_length(&output->base.head_list) != 1)
128 return NULL;
129
130 return container_of(output->base.head_list.next,
131 struct fbdev_head, base.output_link);
132}
133
Philip Withnall4f499172013-02-02 12:02:32 +0000134static void
Jonas Ådahle5a12252013-04-05 23:07:11 +0200135fbdev_output_start_repaint_loop(struct weston_output *output)
136{
Pekka Paalanenb5eedad2014-09-23 22:08:45 -0400137 struct timespec ts;
Jonas Ådahle5a12252013-04-05 23:07:11 +0200138
Pekka Paalanen662f3842015-03-18 12:17:26 +0200139 weston_compositor_read_presentation_clock(output->compositor, &ts);
Pekka Paalanenb00c79b2016-02-18 16:53:27 +0200140 weston_output_finish_frame(output, &ts, WP_PRESENTATION_FEEDBACK_INVALID);
Jonas Ådahle5a12252013-04-05 23:07:11 +0200141}
142
Pekka Paalanene77f8ad2016-06-08 17:39:37 +0300143static int
Daniel Stoneb1f166d2017-03-01 11:34:10 +0000144fbdev_output_repaint(struct weston_output *base, pixman_region32_t *damage,
145 void *repaint_data)
Philip Withnall4f499172013-02-02 12:02:32 +0000146{
147 struct fbdev_output *output = to_fbdev_output(base);
148 struct weston_compositor *ec = output->base.compositor;
Philip Withnall4f499172013-02-02 12:02:32 +0000149
150 /* Repaint the damaged region onto the back buffer. */
Sjoerd Simonsc112e0c2015-12-04 19:20:12 -0600151 pixman_renderer_output_set_buffer(base, output->hw_surface);
Philip Withnall4f499172013-02-02 12:02:32 +0000152 ec->renderer->repaint_output(base, damage);
153
Philip Withnall4f499172013-02-02 12:02:32 +0000154 /* Update the damage region. */
155 pixman_region32_subtract(&ec->primary_plane.damage,
156 &ec->primary_plane.damage, damage);
157
158 /* Schedule the end of the frame. We do not sync this to the frame
159 * buffer clock because users who want that should be using the DRM
160 * compositor. FBIO_WAITFORVSYNC blocks and FB_ACTIVATE_VBL requires
161 * panning, which is broken in most kernel drivers.
162 *
163 * Finish the frame synchronised to the specified refresh rate. The
164 * refresh rate is given in mHz and the interval in ms. */
165 wl_event_source_timer_update(output->finish_frame_timer,
166 1000000 / output->mode.refresh);
David Herrmann1edf44c2013-10-22 17:11:26 +0200167
168 return 0;
Adrian Negreanu4aa756d2013-09-06 15:16:09 +0300169}
170
Philip Withnall4f499172013-02-02 12:02:32 +0000171static int
172finish_frame_handler(void *data)
173{
174 struct fbdev_output *output = data;
Pekka Paalanen363aa7b2014-12-17 16:20:40 +0200175 struct timespec ts;
Philip Withnall4f499172013-02-02 12:02:32 +0000176
Pekka Paalanen662f3842015-03-18 12:17:26 +0200177 weston_compositor_read_presentation_clock(output->base.compositor, &ts);
Pekka Paalanen363aa7b2014-12-17 16:20:40 +0200178 weston_output_finish_frame(&output->base, &ts, 0);
Philip Withnall4f499172013-02-02 12:02:32 +0000179
180 return 1;
181}
182
183static pixman_format_code_t
184calculate_pixman_format(struct fb_var_screeninfo *vinfo,
185 struct fb_fix_screeninfo *finfo)
186{
187 /* Calculate the pixman format supported by the frame buffer from the
188 * buffer's metadata. Return 0 if no known pixman format is supported
189 * (since this has depth 0 it's guaranteed to not conflict with any
190 * actual pixman format).
191 *
192 * Documentation on the vinfo and finfo structures:
193 * http://www.mjmwired.net/kernel/Documentation/fb/api.txt
194 *
195 * TODO: Try a bit harder to support other formats, including setting
196 * the preferred format in the hardware. */
197 int type;
198
199 weston_log("Calculating pixman format from:\n"
200 STAMP_SPACE " - type: %i (aux: %i)\n"
201 STAMP_SPACE " - visual: %i\n"
202 STAMP_SPACE " - bpp: %i (grayscale: %i)\n"
203 STAMP_SPACE " - red: offset: %i, length: %i, MSB: %i\n"
204 STAMP_SPACE " - green: offset: %i, length: %i, MSB: %i\n"
205 STAMP_SPACE " - blue: offset: %i, length: %i, MSB: %i\n"
206 STAMP_SPACE " - transp: offset: %i, length: %i, MSB: %i\n",
207 finfo->type, finfo->type_aux, finfo->visual,
208 vinfo->bits_per_pixel, vinfo->grayscale,
209 vinfo->red.offset, vinfo->red.length, vinfo->red.msb_right,
210 vinfo->green.offset, vinfo->green.length,
211 vinfo->green.msb_right,
212 vinfo->blue.offset, vinfo->blue.length,
213 vinfo->blue.msb_right,
214 vinfo->transp.offset, vinfo->transp.length,
215 vinfo->transp.msb_right);
216
217 /* We only handle packed formats at the moment. */
218 if (finfo->type != FB_TYPE_PACKED_PIXELS)
219 return 0;
220
221 /* We only handle true-colour frame buffers at the moment. */
Marc Chalainffbddff2013-09-03 16:47:43 +0200222 switch(finfo->visual) {
223 case FB_VISUAL_TRUECOLOR:
224 case FB_VISUAL_DIRECTCOLOR:
225 if (vinfo->grayscale != 0)
226 return 0;
227 break;
228 default:
229 return 0;
230 }
Philip Withnall4f499172013-02-02 12:02:32 +0000231
232 /* We only support formats with MSBs on the left. */
233 if (vinfo->red.msb_right != 0 || vinfo->green.msb_right != 0 ||
234 vinfo->blue.msb_right != 0)
235 return 0;
236
237 /* Work out the format type from the offsets. We only support RGBA and
238 * ARGB at the moment. */
239 type = PIXMAN_TYPE_OTHER;
240
241 if ((vinfo->transp.offset >= vinfo->red.offset ||
242 vinfo->transp.length == 0) &&
243 vinfo->red.offset >= vinfo->green.offset &&
244 vinfo->green.offset >= vinfo->blue.offset)
245 type = PIXMAN_TYPE_ARGB;
246 else if (vinfo->red.offset >= vinfo->green.offset &&
247 vinfo->green.offset >= vinfo->blue.offset &&
248 vinfo->blue.offset >= vinfo->transp.offset)
249 type = PIXMAN_TYPE_RGBA;
250
251 if (type == PIXMAN_TYPE_OTHER)
252 return 0;
253
254 /* Build the format. */
255 return PIXMAN_FORMAT(vinfo->bits_per_pixel, type,
256 vinfo->transp.length,
257 vinfo->red.length,
258 vinfo->green.length,
259 vinfo->blue.length);
260}
261
262static int
263calculate_refresh_rate(struct fb_var_screeninfo *vinfo)
264{
265 uint64_t quot;
266
267 /* Calculate monitor refresh rate. Default is 60 Hz. Units are mHz. */
268 quot = (vinfo->upper_margin + vinfo->lower_margin + vinfo->yres);
269 quot *= (vinfo->left_margin + vinfo->right_margin + vinfo->xres);
270 quot *= vinfo->pixclock;
271
272 if (quot > 0) {
273 uint64_t refresh_rate;
274
275 refresh_rate = 1000000000000000LLU / quot;
276 if (refresh_rate > 200000)
277 refresh_rate = 200000; /* cap at 200 Hz */
278
Oliver Smitha5066e02017-04-17 11:11:00 +0000279 if (refresh_rate >= 1000) /* at least 1 Hz */
280 return refresh_rate;
Philip Withnall4f499172013-02-02 12:02:32 +0000281 }
282
283 return 60 * 1000; /* default to 60 Hz */
284}
285
286static int
Pekka Paalanen82db6b72017-09-12 17:15:42 +0300287fbdev_query_screen_info(int fd, struct fbdev_screeninfo *info)
Philip Withnall4f499172013-02-02 12:02:32 +0000288{
289 struct fb_var_screeninfo varinfo;
290 struct fb_fix_screeninfo fixinfo;
291
292 /* Probe the device for screen information. */
293 if (ioctl(fd, FBIOGET_FSCREENINFO, &fixinfo) < 0 ||
294 ioctl(fd, FBIOGET_VSCREENINFO, &varinfo) < 0) {
295 return -1;
296 }
297
298 /* Store the pertinent data. */
299 info->x_resolution = varinfo.xres;
300 info->y_resolution = varinfo.yres;
301 info->width_mm = varinfo.width;
302 info->height_mm = varinfo.height;
303 info->bits_per_pixel = varinfo.bits_per_pixel;
304
305 info->buffer_length = fixinfo.smem_len;
306 info->line_length = fixinfo.line_length;
Derek Foreman61793382015-09-02 13:45:20 -0500307 strncpy(info->id, fixinfo.id, sizeof(info->id));
Bryce Harrington44bbdd02015-09-23 17:39:05 -0700308 info->id[sizeof(info->id)-1] = '\0';
Philip Withnall4f499172013-02-02 12:02:32 +0000309
310 info->pixel_format = calculate_pixman_format(&varinfo, &fixinfo);
311 info->refresh_rate = calculate_refresh_rate(&varinfo);
312
313 if (info->pixel_format == 0) {
314 weston_log("Frame buffer uses an unsupported format.\n");
315 return -1;
316 }
317
318 return 1;
319}
320
321static int
Pekka Paalanen82db6b72017-09-12 17:15:42 +0300322fbdev_set_screen_info(int fd, struct fbdev_screeninfo *info)
Philip Withnall4f499172013-02-02 12:02:32 +0000323{
324 struct fb_var_screeninfo varinfo;
325
326 /* Grab the current screen information. */
327 if (ioctl(fd, FBIOGET_VSCREENINFO, &varinfo) < 0) {
328 return -1;
329 }
330
331 /* Update the information. */
332 varinfo.xres = info->x_resolution;
333 varinfo.yres = info->y_resolution;
334 varinfo.width = info->width_mm;
335 varinfo.height = info->height_mm;
336 varinfo.bits_per_pixel = info->bits_per_pixel;
337
338 /* Try to set up an ARGB (x8r8g8b8) pixel format. */
339 varinfo.grayscale = 0;
340 varinfo.transp.offset = 24;
341 varinfo.transp.length = 0;
342 varinfo.transp.msb_right = 0;
343 varinfo.red.offset = 16;
344 varinfo.red.length = 8;
345 varinfo.red.msb_right = 0;
346 varinfo.green.offset = 8;
347 varinfo.green.length = 8;
348 varinfo.green.msb_right = 0;
349 varinfo.blue.offset = 0;
350 varinfo.blue.length = 8;
351 varinfo.blue.msb_right = 0;
352
353 /* Set the device's screen information. */
354 if (ioctl(fd, FBIOPUT_VSCREENINFO, &varinfo) < 0) {
355 return -1;
356 }
357
358 return 1;
359}
360
Philip Withnall4f499172013-02-02 12:02:32 +0000361/* Returns an FD for the frame buffer device. */
362static int
Pekka Paalanen82db6b72017-09-12 17:15:42 +0300363fbdev_frame_buffer_open(const char *fb_dev,
364 struct fbdev_screeninfo *screen_info)
Philip Withnall4f499172013-02-02 12:02:32 +0000365{
366 int fd = -1;
367
368 weston_log("Opening fbdev frame buffer.\n");
369
370 /* Open the frame buffer device. */
371 fd = open(fb_dev, O_RDWR | O_CLOEXEC);
372 if (fd < 0) {
373 weston_log("Failed to open frame buffer device ‘%s’: %s\n",
374 fb_dev, strerror(errno));
375 return -1;
376 }
377
378 /* Grab the screen info. */
Pekka Paalanen82db6b72017-09-12 17:15:42 +0300379 if (fbdev_query_screen_info(fd, screen_info) < 0) {
Philip Withnall4f499172013-02-02 12:02:32 +0000380 weston_log("Failed to get frame buffer info: %s\n",
381 strerror(errno));
382
383 close(fd);
384 return -1;
385 }
386
nerdopolis92a06a92018-06-29 08:17:49 -0400387 /* Attempt to wake up the framebuffer device, needed for secondary
388 * framebuffer devices */
389 if (fbdev_set_screen_info(fd, screen_info) < 0) {
390 weston_log("Failed to set mode settings. "
391 "Attempting to open output anyway.\n");
392 }
393
394
Philip Withnall4f499172013-02-02 12:02:32 +0000395 return fd;
396}
397
398/* Closes the FD on success or failure. */
399static int
400fbdev_frame_buffer_map(struct fbdev_output *output, int fd)
401{
Pekka Paalanen23058122017-09-14 16:50:44 +0300402 struct fbdev_head *head;
Philip Withnall4f499172013-02-02 12:02:32 +0000403 int retval = -1;
404
Pekka Paalanen23058122017-09-14 16:50:44 +0300405 head = fbdev_output_get_head(output);
406
Philip Withnall4f499172013-02-02 12:02:32 +0000407 weston_log("Mapping fbdev frame buffer.\n");
408
409 /* Map the frame buffer. Write-only mode, since we don't want to read
410 * anything back (because it's slow). */
Pekka Paalanen23058122017-09-14 16:50:44 +0300411 output->buffer_length = head->fb_info.buffer_length;
412 output->fb = mmap(NULL, output->buffer_length,
Philip Withnall4f499172013-02-02 12:02:32 +0000413 PROT_WRITE, MAP_SHARED, fd, 0);
414 if (output->fb == MAP_FAILED) {
415 weston_log("Failed to mmap frame buffer: %s\n",
416 strerror(errno));
Pekka Paalanen82b8ddf2017-09-13 11:58:00 +0300417 output->fb = NULL;
Philip Withnall4f499172013-02-02 12:02:32 +0000418 goto out_close;
419 }
420
421 /* Create a pixman image to wrap the memory mapped frame buffer. */
422 output->hw_surface =
Pekka Paalanen23058122017-09-14 16:50:44 +0300423 pixman_image_create_bits(head->fb_info.pixel_format,
424 head->fb_info.x_resolution,
425 head->fb_info.y_resolution,
Philip Withnall4f499172013-02-02 12:02:32 +0000426 output->fb,
Pekka Paalanen23058122017-09-14 16:50:44 +0300427 head->fb_info.line_length);
Philip Withnall4f499172013-02-02 12:02:32 +0000428 if (output->hw_surface == NULL) {
429 weston_log("Failed to create surface for frame buffer.\n");
430 goto out_unmap;
431 }
432
433 /* Success! */
434 retval = 0;
435
436out_unmap:
Pekka Paalanena51e71f2017-09-13 17:14:19 +0300437 if (retval != 0 && output->fb != NULL) {
Pekka Paalanen23058122017-09-14 16:50:44 +0300438 munmap(output->fb, output->buffer_length);
Pekka Paalanena51e71f2017-09-13 17:14:19 +0300439 output->fb = NULL;
440 }
Philip Withnall4f499172013-02-02 12:02:32 +0000441
442out_close:
443 if (fd >= 0)
444 close(fd);
445
446 return retval;
447}
448
449static void
Pekka Paalanena51e71f2017-09-13 17:14:19 +0300450fbdev_frame_buffer_unmap(struct fbdev_output *output)
Philip Withnall4f499172013-02-02 12:02:32 +0000451{
Pekka Paalanena51e71f2017-09-13 17:14:19 +0300452 if (!output->fb) {
453 assert(!output->hw_surface);
454 return;
455 }
456
457 weston_log("Unmapping fbdev frame buffer.\n");
458
459 if (output->hw_surface)
460 pixman_image_unref(output->hw_surface);
461 output->hw_surface = NULL;
Philip Withnall4f499172013-02-02 12:02:32 +0000462
Pekka Paalanen23058122017-09-14 16:50:44 +0300463 if (munmap(output->fb, output->buffer_length) < 0)
Philip Withnall4f499172013-02-02 12:02:32 +0000464 weston_log("Failed to munmap frame buffer: %s\n",
465 strerror(errno));
466
467 output->fb = NULL;
468}
469
Pekka Paalanen23058122017-09-14 16:50:44 +0300470
471static int
472fbdev_output_attach_head(struct weston_output *output_base,
473 struct weston_head *head_base)
474{
475 struct fbdev_output *output = to_fbdev_output(output_base);
476 struct fbdev_head *head = to_fbdev_head(head_base);
477
478 /* Clones not supported. */
479 if (!wl_list_empty(&output->base.head_list))
480 return -1;
481
482 /* only one static mode in list */
483 output->mode.flags = WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
484 output->mode.width = head->fb_info.x_resolution;
485 output->mode.height = head->fb_info.y_resolution;
486 output->mode.refresh = head->fb_info.refresh_rate;
487 wl_list_init(&output->base.mode_list);
488 wl_list_insert(&output->base.mode_list, &output->mode.link);
489 output->base.current_mode = &output->mode;
490
491 return 0;
492}
493
Philip Withnall4f499172013-02-02 12:02:32 +0000494static void fbdev_output_destroy(struct weston_output *base);
Philip Withnall4f499172013-02-02 12:02:32 +0000495
496static int
Armin Krezović6ba369d2016-09-30 14:11:06 +0200497fbdev_output_enable(struct weston_output *base)
498{
499 struct fbdev_output *output = to_fbdev_output(base);
500 struct fbdev_backend *backend = to_fbdev_backend(base->compositor);
Pekka Paalanen23058122017-09-14 16:50:44 +0300501 struct fbdev_head *head;
Armin Krezović6ba369d2016-09-30 14:11:06 +0200502 int fb_fd;
503 struct wl_event_loop *loop;
504
Pekka Paalanen23058122017-09-14 16:50:44 +0300505 head = fbdev_output_get_head(output);
506
Armin Krezović6ba369d2016-09-30 14:11:06 +0200507 /* Create the frame buffer. */
Pekka Paalanen23058122017-09-14 16:50:44 +0300508 fb_fd = fbdev_frame_buffer_open(head->device, &head->fb_info);
Armin Krezović6ba369d2016-09-30 14:11:06 +0200509 if (fb_fd < 0) {
510 weston_log("Creating frame buffer failed.\n");
511 return -1;
512 }
513
514 if (fbdev_frame_buffer_map(output, fb_fd) < 0) {
515 weston_log("Mapping frame buffer failed.\n");
516 return -1;
517 }
518
519 output->base.start_repaint_loop = fbdev_output_start_repaint_loop;
520 output->base.repaint = fbdev_output_repaint;
521
Pekka Paalanen26ded942018-04-23 11:44:57 +0200522 if (pixman_renderer_output_create(&output->base,
523 PIXMAN_RENDERER_OUTPUT_USE_SHADOW) < 0)
Armin Krezović6ba369d2016-09-30 14:11:06 +0200524 goto out_hw_surface;
525
526 loop = wl_display_get_event_loop(backend->compositor->wl_display);
527 output->finish_frame_timer =
528 wl_event_loop_add_timer(loop, finish_frame_handler, output);
529
530 weston_log("fbdev output %d×%d px\n",
531 output->mode.width, output->mode.height);
532 weston_log_continue(STAMP_SPACE "guessing %d Hz and 96 dpi\n",
533 output->mode.refresh / 1000);
534
535 return 0;
536
537out_hw_surface:
Pekka Paalanena51e71f2017-09-13 17:14:19 +0300538 fbdev_frame_buffer_unmap(output);
Armin Krezović6ba369d2016-09-30 14:11:06 +0200539
540 return -1;
541}
542
543static int
Pekka Paalanen82ffe792017-09-13 17:25:41 +0300544fbdev_output_disable(struct weston_output *base)
Pekka Paalanenacd71fb2017-08-15 10:35:09 +0300545{
Pekka Paalanena51e71f2017-09-13 17:14:19 +0300546 struct fbdev_output *output = to_fbdev_output(base);
547
Pekka Paalanenacd71fb2017-08-15 10:35:09 +0300548 if (!base->enabled)
549 return 0;
550
Pekka Paalanencbe7fb02017-09-14 11:43:29 +0300551 wl_event_source_remove(output->finish_frame_timer);
552 output->finish_frame_timer = NULL;
553
Pekka Paalanen61e5a272017-09-13 17:22:38 +0300554 pixman_renderer_output_destroy(&output->base);
Pekka Paalanena51e71f2017-09-13 17:14:19 +0300555 fbdev_frame_buffer_unmap(output);
Pekka Paalanenacd71fb2017-08-15 10:35:09 +0300556
Pekka Paalanenacd71fb2017-08-15 10:35:09 +0300557 return 0;
558}
559
Pekka Paalanen23058122017-09-14 16:50:44 +0300560static struct fbdev_head *
561fbdev_head_create(struct fbdev_backend *backend, const char *device)
562{
563 struct fbdev_head *head;
564 int fb_fd;
565
566 head = zalloc(sizeof *head);
567 if (!head)
568 return NULL;
569
570 head->device = strdup(device);
571
572 /* Create the frame buffer. */
573 fb_fd = fbdev_frame_buffer_open(head->device, &head->fb_info);
574 if (fb_fd < 0) {
575 weston_log("Creating frame buffer head failed.\n");
576 goto out_free;
577 }
578 close(fb_fd);
579
580 weston_head_init(&head->base, "fbdev");
581 weston_head_set_connection_status(&head->base, true);
582 weston_head_set_monitor_strings(&head->base, "unknown",
583 head->fb_info.id, NULL);
584 weston_head_set_subpixel(&head->base, WL_OUTPUT_SUBPIXEL_UNKNOWN);
585 weston_head_set_physical_size(&head->base, head->fb_info.width_mm,
586 head->fb_info.height_mm);
587
588 weston_compositor_add_head(backend->compositor, &head->base);
589
590 weston_log("Created head '%s' for device %s (%s)\n",
591 head->base.name, head->device, head->base.model);
592
593 return head;
594
595out_free:
596 free(head->device);
597 free(head);
598
599 return NULL;
600}
601
602static void
603fbdev_head_destroy(struct fbdev_head *head)
604{
605 weston_head_release(&head->base);
606 free(head->device);
607 free(head);
608}
609
610static struct weston_output *
611fbdev_output_create(struct weston_compositor *compositor,
612 const char *name)
Philip Withnall4f499172013-02-02 12:02:32 +0000613{
614 struct fbdev_output *output;
Philip Withnall4f499172013-02-02 12:02:32 +0000615
616 weston_log("Creating fbdev output.\n");
617
Bryce Harringtonde16d892014-11-20 22:21:57 -0800618 output = zalloc(sizeof *output);
619 if (output == NULL)
Pekka Paalanen23058122017-09-14 16:50:44 +0300620 return NULL;
Philip Withnall4f499172013-02-02 12:02:32 +0000621
Pekka Paalanen23058122017-09-14 16:50:44 +0300622 output->backend = to_fbdev_backend(compositor);
Philip Withnall4f499172013-02-02 12:02:32 +0000623
Pekka Paalanen23058122017-09-14 16:50:44 +0300624 weston_output_init(&output->base, compositor, name);
Pekka Paalanen26ac2e12017-04-03 13:18:13 +0300625
Philip Withnall4f499172013-02-02 12:02:32 +0000626 output->base.destroy = fbdev_output_destroy;
Pekka Paalanen82ffe792017-09-13 17:25:41 +0300627 output->base.disable = fbdev_output_disable;
Armin Krezović6ba369d2016-09-30 14:11:06 +0200628 output->base.enable = fbdev_output_enable;
Pekka Paalanen23058122017-09-14 16:50:44 +0300629 output->base.attach_head = fbdev_output_attach_head;
Armin Krezović6ba369d2016-09-30 14:11:06 +0200630
Pekka Paalanen23058122017-09-14 16:50:44 +0300631 weston_compositor_add_pending_output(&output->base, compositor);
Philip Withnall4f499172013-02-02 12:02:32 +0000632
Pekka Paalanen23058122017-09-14 16:50:44 +0300633 return &output->base;
Philip Withnall4f499172013-02-02 12:02:32 +0000634}
635
636static void
637fbdev_output_destroy(struct weston_output *base)
638{
639 struct fbdev_output *output = to_fbdev_output(base);
640
641 weston_log("Destroying fbdev output.\n");
642
Pekka Paalanen82ffe792017-09-13 17:25:41 +0300643 fbdev_output_disable(base);
Philip Withnall4f499172013-02-02 12:02:32 +0000644
645 /* Remove the output. */
Pekka Paalanenae6d35d2017-08-16 12:07:14 +0300646 weston_output_release(&output->base);
Philip Withnall4f499172013-02-02 12:02:32 +0000647
648 free(output);
649}
650
651/* strcmp()-style return values. */
652static int
653compare_screen_info (const struct fbdev_screeninfo *a,
654 const struct fbdev_screeninfo *b)
655{
656 if (a->x_resolution == b->x_resolution &&
657 a->y_resolution == b->y_resolution &&
658 a->width_mm == b->width_mm &&
659 a->height_mm == b->height_mm &&
660 a->bits_per_pixel == b->bits_per_pixel &&
661 a->pixel_format == b->pixel_format &&
662 a->refresh_rate == b->refresh_rate)
663 return 0;
664
665 return 1;
666}
667
668static int
Giulio Camuffo954f1832014-10-11 18:27:30 +0300669fbdev_output_reenable(struct fbdev_backend *backend,
Philip Withnall4f499172013-02-02 12:02:32 +0000670 struct weston_output *base)
671{
672 struct fbdev_output *output = to_fbdev_output(base);
Pekka Paalanen23058122017-09-14 16:50:44 +0300673 struct fbdev_head *head;
Philip Withnall4f499172013-02-02 12:02:32 +0000674 struct fbdev_screeninfo new_screen_info;
675 int fb_fd;
676
Pekka Paalanen23058122017-09-14 16:50:44 +0300677 head = fbdev_output_get_head(output);
678
Philip Withnall4f499172013-02-02 12:02:32 +0000679 weston_log("Re-enabling fbdev output.\n");
Pekka Paalanen55916d52017-09-13 16:19:02 +0300680 assert(output->base.enabled);
Philip Withnall4f499172013-02-02 12:02:32 +0000681
682 /* Create the frame buffer. */
Pekka Paalanen23058122017-09-14 16:50:44 +0300683 fb_fd = fbdev_frame_buffer_open(head->device, &new_screen_info);
Philip Withnall4f499172013-02-02 12:02:32 +0000684 if (fb_fd < 0) {
685 weston_log("Creating frame buffer failed.\n");
Pekka Paalanen55916d52017-09-13 16:19:02 +0300686 return -1;
Philip Withnall4f499172013-02-02 12:02:32 +0000687 }
688
689 /* Check whether the frame buffer details have changed since we were
690 * disabled. */
Pekka Paalanen23058122017-09-14 16:50:44 +0300691 if (compare_screen_info(&head->fb_info, &new_screen_info) != 0) {
Philip Withnall4f499172013-02-02 12:02:32 +0000692 /* Perform a mode-set to restore the old mode. */
Pekka Paalanen23058122017-09-14 16:50:44 +0300693 if (fbdev_set_screen_info(fb_fd, &head->fb_info) < 0) {
Philip Withnall4f499172013-02-02 12:02:32 +0000694 weston_log("Failed to restore mode settings. "
695 "Attempting to re-open output anyway.\n");
696 }
697
Rob Bradford581b3fd2013-07-26 16:29:39 +0100698 close(fb_fd);
699
Pekka Paalanen55916d52017-09-13 16:19:02 +0300700 /* Disable and enable the output so that resources depending on
Philip Withnall4f499172013-02-02 12:02:32 +0000701 * the frame buffer X/Y resolution (such as the shadow buffer)
702 * are re-initialised. */
Pekka Paalanen55916d52017-09-13 16:19:02 +0300703 fbdev_output_disable(&output->base);
704 return fbdev_output_enable(&output->base);
Philip Withnall4f499172013-02-02 12:02:32 +0000705 }
706
707 /* Map the device if it has the same details as before. */
Pekka Paalanene77f8ad2016-06-08 17:39:37 +0300708 if (fbdev_frame_buffer_map(output, fb_fd) < 0) {
709 weston_log("Mapping frame buffer failed.\n");
Pekka Paalanen55916d52017-09-13 16:19:02 +0300710 return -1;
Philip Withnall4f499172013-02-02 12:02:32 +0000711 }
712
713 return 0;
Philip Withnall4f499172013-02-02 12:02:32 +0000714}
715
Philip Withnall4f499172013-02-02 12:02:32 +0000716static void
Giulio Camuffo954f1832014-10-11 18:27:30 +0300717fbdev_backend_destroy(struct weston_compositor *base)
Philip Withnall4f499172013-02-02 12:02:32 +0000718{
Giulio Camuffo954f1832014-10-11 18:27:30 +0300719 struct fbdev_backend *backend = to_fbdev_backend(base);
Pekka Paalanen23058122017-09-14 16:50:44 +0300720 struct weston_head *head, *next;
Philip Withnall4f499172013-02-02 12:02:32 +0000721
Giulio Camuffo954f1832014-10-11 18:27:30 +0300722 udev_input_destroy(&backend->input);
Philip Withnall4f499172013-02-02 12:02:32 +0000723
724 /* Destroy the output. */
Giulio Camuffo954f1832014-10-11 18:27:30 +0300725 weston_compositor_shutdown(base);
Philip Withnall4f499172013-02-02 12:02:32 +0000726
Pekka Paalanen23058122017-09-14 16:50:44 +0300727 wl_list_for_each_safe(head, next, &base->head_list, compositor_link)
728 fbdev_head_destroy(to_fbdev_head(head));
729
Philip Withnall4f499172013-02-02 12:02:32 +0000730 /* Chain up. */
Giulio Camuffo954f1832014-10-11 18:27:30 +0300731 weston_launcher_destroy(base->launcher);
Philip Withnall4f499172013-02-02 12:02:32 +0000732
Pekka Paalanen513f9a42017-09-13 16:49:02 +0300733 udev_unref(backend->udev);
734
Giulio Camuffo954f1832014-10-11 18:27:30 +0300735 free(backend);
Philip Withnall4f499172013-02-02 12:02:32 +0000736}
737
738static void
Kristian Høgsberg61741a22013-09-17 16:02:57 -0700739session_notify(struct wl_listener *listener, void *data)
Philip Withnall4f499172013-02-02 12:02:32 +0000740{
Pekka Paalanen3f897942015-08-19 13:52:47 +0300741 struct weston_compositor *compositor = data;
742 struct fbdev_backend *backend = to_fbdev_backend(compositor);
Philip Withnall4f499172013-02-02 12:02:32 +0000743 struct weston_output *output;
744
Giulio Camuffo954f1832014-10-11 18:27:30 +0300745 if (compositor->session_active) {
Philip Withnall4f499172013-02-02 12:02:32 +0000746 weston_log("entering VT\n");
Giulio Camuffo954f1832014-10-11 18:27:30 +0300747 compositor->state = backend->prev_state;
Philip Withnall4f499172013-02-02 12:02:32 +0000748
Giulio Camuffo954f1832014-10-11 18:27:30 +0300749 wl_list_for_each(output, &compositor->output_list, link) {
750 fbdev_output_reenable(backend, output);
Philip Withnall4f499172013-02-02 12:02:32 +0000751 }
752
Giulio Camuffo954f1832014-10-11 18:27:30 +0300753 weston_compositor_damage_all(compositor);
Philip Withnall4f499172013-02-02 12:02:32 +0000754
Giulio Camuffo954f1832014-10-11 18:27:30 +0300755 udev_input_enable(&backend->input);
Kristian Høgsberg61741a22013-09-17 16:02:57 -0700756 } else {
Philip Withnall4f499172013-02-02 12:02:32 +0000757 weston_log("leaving VT\n");
Giulio Camuffo954f1832014-10-11 18:27:30 +0300758 udev_input_disable(&backend->input);
Philip Withnall4f499172013-02-02 12:02:32 +0000759
Giulio Camuffo954f1832014-10-11 18:27:30 +0300760 wl_list_for_each(output, &compositor->output_list, link) {
Pekka Paalanena51e71f2017-09-13 17:14:19 +0300761 fbdev_frame_buffer_unmap(to_fbdev_output(output));
Philip Withnall4f499172013-02-02 12:02:32 +0000762 }
763
Giulio Camuffo954f1832014-10-11 18:27:30 +0300764 backend->prev_state = compositor->state;
765 weston_compositor_offscreen(compositor);
Philip Withnall4f499172013-02-02 12:02:32 +0000766
767 /* If we have a repaint scheduled (from the idle handler), make
768 * sure we cancel that so we don't try to pageflip when we're
Philipp Brüschweiler57edf7f2013-03-29 13:01:56 +0100769 * vt switched away. The OFFSCREEN state will prevent
Bryce Harringtonc2be8572015-12-11 13:11:37 -0800770 * further attempts at repainting. When we switch
Philip Withnall4f499172013-02-02 12:02:32 +0000771 * back, we schedule a repaint, which will process
772 * pending frame callbacks. */
773
774 wl_list_for_each(output,
Giulio Camuffo954f1832014-10-11 18:27:30 +0300775 &compositor->output_list, link) {
Daniel Stone09a97e22017-03-01 11:34:06 +0000776 output->repaint_needed = false;
Philip Withnall4f499172013-02-02 12:02:32 +0000777 }
nerdopolis38946702014-12-03 15:53:03 +0000778 }
Philip Withnall4f499172013-02-02 12:02:32 +0000779}
780
Giulio Camuffo954f1832014-10-11 18:27:30 +0300781static struct fbdev_backend *
Pekka Paalanena256c5e2016-06-03 14:56:18 +0300782fbdev_backend_create(struct weston_compositor *compositor,
Benoit Gschwind934e89a2016-04-27 23:56:42 +0200783 struct weston_fbdev_backend_config *param)
Philip Withnall4f499172013-02-02 12:02:32 +0000784{
Giulio Camuffo954f1832014-10-11 18:27:30 +0300785 struct fbdev_backend *backend;
Rob Bradford2387fde2013-05-31 18:09:57 +0100786 const char *seat_id = default_seat;
nerdopolisd68109b2018-06-29 08:17:47 -0400787 const char *session_seat;
788
789 session_seat = getenv("XDG_SEAT");
790 if (session_seat)
791 seat_id = session_seat;
792 if (param->seat_id)
793 seat_id = param->seat_id;
Philip Withnall4f499172013-02-02 12:02:32 +0000794
795 weston_log("initializing fbdev backend\n");
796
Giulio Camuffo954f1832014-10-11 18:27:30 +0300797 backend = zalloc(sizeof *backend);
798 if (backend == NULL)
Philip Withnall4f499172013-02-02 12:02:32 +0000799 return NULL;
800
Giulio Camuffo954f1832014-10-11 18:27:30 +0300801 backend->compositor = compositor;
Pekka Paalanen7da9a382017-08-30 11:29:49 +0300802 compositor->backend = &backend->base;
Pekka Paalanenb5eedad2014-09-23 22:08:45 -0400803 if (weston_compositor_set_presentation_clock_software(
Giulio Camuffo954f1832014-10-11 18:27:30 +0300804 compositor) < 0)
Pekka Paalanenb5eedad2014-09-23 22:08:45 -0400805 goto out_compositor;
806
Giulio Camuffo954f1832014-10-11 18:27:30 +0300807 backend->udev = udev_new();
808 if (backend->udev == NULL) {
Philip Withnall4f499172013-02-02 12:02:32 +0000809 weston_log("Failed to initialize udev context.\n");
810 goto out_compositor;
811 }
812
813 /* Set up the TTY. */
Giulio Camuffo954f1832014-10-11 18:27:30 +0300814 backend->session_listener.notify = session_notify;
815 wl_signal_add(&compositor->session_signal,
816 &backend->session_listener);
817 compositor->launcher =
nerdopolisd68109b2018-06-29 08:17:47 -0400818 weston_launcher_connect(compositor, param->tty, seat_id, false);
Giulio Camuffo954f1832014-10-11 18:27:30 +0300819 if (!compositor->launcher) {
Pekka Paalanena453f4d2017-10-31 10:19:48 +0200820 weston_log("fatal: fbdev backend should be run using "
821 "weston-launch binary, or your system should "
822 "provide the logind D-Bus API.\n");
Philip Withnall4f499172013-02-02 12:02:32 +0000823 goto out_udev;
824 }
825
Giulio Camuffo954f1832014-10-11 18:27:30 +0300826 backend->base.destroy = fbdev_backend_destroy;
Pekka Paalanen23058122017-09-14 16:50:44 +0300827 backend->base.create_output = fbdev_output_create;
Philip Withnall4f499172013-02-02 12:02:32 +0000828
Giulio Camuffo954f1832014-10-11 18:27:30 +0300829 backend->prev_state = WESTON_COMPOSITOR_ACTIVE;
Philip Withnall4f499172013-02-02 12:02:32 +0000830
Bob Ham91880f12016-01-12 10:21:47 +0000831 weston_setup_vt_switch_bindings(compositor);
832
Pekka Paalanene77f8ad2016-06-08 17:39:37 +0300833 if (pixman_renderer_init(compositor) < 0)
834 goto out_launcher;
Philip Withnall4f499172013-02-02 12:02:32 +0000835
Pekka Paalanen23058122017-09-14 16:50:44 +0300836 if (!fbdev_head_create(backend, param->device))
Dawid Gajownik82d49252015-07-31 00:02:28 -0300837 goto out_launcher;
Philip Withnall4f499172013-02-02 12:02:32 +0000838
Giulio Camuffo8aedf7b2016-06-02 21:48:12 +0300839 udev_input_init(&backend->input, compositor, backend->udev,
840 seat_id, param->configure_device);
Philip Withnall4f499172013-02-02 12:02:32 +0000841
Giulio Camuffo954f1832014-10-11 18:27:30 +0300842 return backend;
Philip Withnall4f499172013-02-02 12:02:32 +0000843
Kristian Høgsberg3f495872013-09-18 23:00:17 -0700844out_launcher:
Giulio Camuffo954f1832014-10-11 18:27:30 +0300845 weston_launcher_destroy(compositor->launcher);
Philip Withnall4f499172013-02-02 12:02:32 +0000846
847out_udev:
Giulio Camuffo954f1832014-10-11 18:27:30 +0300848 udev_unref(backend->udev);
Philip Withnall4f499172013-02-02 12:02:32 +0000849
850out_compositor:
Giulio Camuffo954f1832014-10-11 18:27:30 +0300851 weston_compositor_shutdown(compositor);
Giulio Camuffo954f1832014-10-11 18:27:30 +0300852 free(backend);
Philip Withnall4f499172013-02-02 12:02:32 +0000853
854 return NULL;
855}
856
Benoit Gschwind934e89a2016-04-27 23:56:42 +0200857static void
858config_init_to_defaults(struct weston_fbdev_backend_config *config)
859{
860 /* TODO: Ideally, available frame buffers should be enumerated using
861 * udev, rather than passing a device node in as a parameter. */
862 config->tty = 0; /* default to current tty */
863 config->device = "/dev/fb0"; /* default frame buffer */
nerdopolisd68109b2018-06-29 08:17:47 -0400864 config->seat_id = NULL;
Benoit Gschwind934e89a2016-04-27 23:56:42 +0200865}
866
Giulio Camuffo954f1832014-10-11 18:27:30 +0300867WL_EXPORT int
Quentin Glidic23e1d6f2016-12-02 14:08:44 +0100868weston_backend_init(struct weston_compositor *compositor,
869 struct weston_backend_config *config_base)
Philip Withnall4f499172013-02-02 12:02:32 +0000870{
Giulio Camuffo954f1832014-10-11 18:27:30 +0300871 struct fbdev_backend *b;
Benoit Gschwind934e89a2016-04-27 23:56:42 +0200872 struct weston_fbdev_backend_config config = {{ 0, }};
Philip Withnall4f499172013-02-02 12:02:32 +0000873
Benoit Gschwind934e89a2016-04-27 23:56:42 +0200874 if (config_base == NULL ||
875 config_base->struct_version != WESTON_FBDEV_BACKEND_CONFIG_VERSION ||
876 config_base->struct_size > sizeof(struct weston_fbdev_backend_config)) {
877 weston_log("fbdev backend config structure is invalid\n");
878 return -1;
879 }
Philip Withnall4f499172013-02-02 12:02:32 +0000880
Benoit Gschwind934e89a2016-04-27 23:56:42 +0200881 config_init_to_defaults(&config);
882 memcpy(&config, config_base, config_base->struct_size);
Philip Withnall4f499172013-02-02 12:02:32 +0000883
Pekka Paalanena256c5e2016-06-03 14:56:18 +0300884 b = fbdev_backend_create(compositor, &config);
Giulio Camuffo954f1832014-10-11 18:27:30 +0300885 if (b == NULL)
886 return -1;
887 return 0;
Philip Withnall4f499172013-02-02 12:02:32 +0000888}