compositor-fbdev: migrate to head-based output API
authorPekka Paalanen <pekka.paalanen@collabora.co.uk>
Thu, 14 Sep 2017 13:50:44 +0000 (16:50 +0300)
committerPekka Paalanen <pekka.paalanen@collabora.co.uk>
Tue, 17 Apr 2018 12:19:57 +0000 (15:19 +0300)
Implement the head-based output API in this backend, and stop relying on
the implicit weston_output::head.

The split between fbdev_head and fbdev_output is somewhat arbitrary.
There is no hotplug or unplug, and there is always 1:1 relationship.
Struct fbdev_screeninfo could have been split as well, but it would not
have made much difference.

I chose fbdev_output to carry the mmap details (buffer_length is now
duplicated here), and fbdev_head to carry the display parameters and
device node path. The device node identifies the head, similar to a
connector.

The backend init creates a head. The compositor uses it to create an
output. Libweston core attaches the head automatically after creating
the output. The attach hook is a suitable place to set up the video
modes on the output as they are dictated by the head, it would be too
late at enable() time.

v7:
- use name argument instead of hardcoded "fbdev" in
  fbdev_output_create()

Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.co.uk>
Reviewed-by: Ian Ray <ian.ray@ge.com>
Acked-by: Daniel Stone <daniels@collabora.com>
Acked-by: Derek Foreman <derekf@osg.samsung.com>
libweston/compositor-fbdev.c

index af33862..de40c21 100644 (file)
@@ -78,6 +78,14 @@ struct fbdev_screeninfo {
        unsigned int refresh_rate; /* Hertz */
 };
 
+struct fbdev_head {
+       struct weston_head base;
+
+       /* Frame buffer details. */
+       char *device;
+       struct fbdev_screeninfo fb_info;
+};
+
 struct fbdev_output {
        struct fbdev_backend *backend;
        struct weston_output base;
@@ -85,10 +93,9 @@ struct fbdev_output {
        struct weston_mode mode;
        struct wl_event_source *finish_frame_timer;
 
-       /* Frame buffer details. */
-       char *device;
-       struct fbdev_screeninfo fb_info;
-       void *fb; /* length is fb_info.buffer_length */
+       /* framebuffer mmap details */
+       size_t buffer_length;
+       void *fb;
 
        /* pixman details. */
        pixman_image_t *hw_surface;
@@ -96,6 +103,12 @@ struct fbdev_output {
 
 static const char default_seat[] = "seat0";
 
+static inline struct fbdev_head *
+to_fbdev_head(struct weston_head *base)
+{
+       return container_of(base, struct fbdev_head, base);
+}
+
 static inline struct fbdev_output *
 to_fbdev_output(struct weston_output *base)
 {
@@ -108,6 +121,16 @@ to_fbdev_backend(struct weston_compositor *base)
        return container_of(base->backend, struct fbdev_backend, base);
 }
 
+static struct fbdev_head *
+fbdev_output_get_head(struct fbdev_output *output)
+{
+       if (wl_list_length(&output->base.head_list) != 1)
+               return NULL;
+
+       return container_of(output->base.head_list.next,
+                           struct fbdev_head, base.output_link);
+}
+
 static void
 fbdev_output_start_repaint_loop(struct weston_output *output)
 {
@@ -368,13 +391,17 @@ fbdev_frame_buffer_open(const char *fb_dev,
 static int
 fbdev_frame_buffer_map(struct fbdev_output *output, int fd)
 {
+       struct fbdev_head *head;
        int retval = -1;
 
+       head = fbdev_output_get_head(output);
+
        weston_log("Mapping fbdev frame buffer.\n");
 
        /* Map the frame buffer. Write-only mode, since we don't want to read
         * anything back (because it's slow). */
-       output->fb = mmap(NULL, output->fb_info.buffer_length,
+       output->buffer_length = head->fb_info.buffer_length;
+       output->fb = mmap(NULL, output->buffer_length,
                          PROT_WRITE, MAP_SHARED, fd, 0);
        if (output->fb == MAP_FAILED) {
                weston_log("Failed to mmap frame buffer: %s\n",
@@ -385,11 +412,11 @@ fbdev_frame_buffer_map(struct fbdev_output *output, int fd)
 
        /* Create a pixman image to wrap the memory mapped frame buffer. */
        output->hw_surface =
-               pixman_image_create_bits(output->fb_info.pixel_format,
-                                        output->fb_info.x_resolution,
-                                        output->fb_info.y_resolution,
+               pixman_image_create_bits(head->fb_info.pixel_format,
+                                        head->fb_info.x_resolution,
+                                        head->fb_info.y_resolution,
                                         output->fb,
-                                        output->fb_info.line_length);
+                                        head->fb_info.line_length);
        if (output->hw_surface == NULL) {
                weston_log("Failed to create surface for frame buffer.\n");
                goto out_unmap;
@@ -400,7 +427,7 @@ fbdev_frame_buffer_map(struct fbdev_output *output, int fd)
 
 out_unmap:
        if (retval != 0 && output->fb != NULL) {
-               munmap(output->fb, output->fb_info.buffer_length);
+               munmap(output->fb, output->buffer_length);
                output->fb = NULL;
        }
 
@@ -425,13 +452,37 @@ fbdev_frame_buffer_unmap(struct fbdev_output *output)
                pixman_image_unref(output->hw_surface);
        output->hw_surface = NULL;
 
-       if (munmap(output->fb, output->fb_info.buffer_length) < 0)
+       if (munmap(output->fb, output->buffer_length) < 0)
                weston_log("Failed to munmap frame buffer: %s\n",
                           strerror(errno));
 
        output->fb = NULL;
 }
 
+
+static int
+fbdev_output_attach_head(struct weston_output *output_base,
+                        struct weston_head *head_base)
+{
+       struct fbdev_output *output = to_fbdev_output(output_base);
+       struct fbdev_head *head = to_fbdev_head(head_base);
+
+       /* Clones not supported. */
+       if (!wl_list_empty(&output->base.head_list))
+               return -1;
+
+       /* only one static mode in list */
+       output->mode.flags = WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
+       output->mode.width = head->fb_info.x_resolution;
+       output->mode.height = head->fb_info.y_resolution;
+       output->mode.refresh = head->fb_info.refresh_rate;
+       wl_list_init(&output->base.mode_list);
+       wl_list_insert(&output->base.mode_list, &output->mode.link);
+       output->base.current_mode = &output->mode;
+
+       return 0;
+}
+
 static void fbdev_output_destroy(struct weston_output *base);
 
 static int
@@ -439,11 +490,14 @@ fbdev_output_enable(struct weston_output *base)
 {
        struct fbdev_output *output = to_fbdev_output(base);
        struct fbdev_backend *backend = to_fbdev_backend(base->compositor);
+       struct fbdev_head *head;
        int fb_fd;
        struct wl_event_loop *loop;
 
+       head = fbdev_output_get_head(output);
+
        /* Create the frame buffer. */
-       fb_fd = fbdev_frame_buffer_open(output->device, &output->fb_info);
+       fb_fd = fbdev_frame_buffer_open(head->device, &head->fb_info);
        if (fb_fd < 0) {
                weston_log("Creating frame buffer failed.\n");
                return -1;
@@ -494,64 +548,80 @@ fbdev_output_disable(struct weston_output *base)
        return 0;
 }
 
-static int
-fbdev_output_create(struct fbdev_backend *backend,
-                    const char *device)
+static struct fbdev_head *
+fbdev_head_create(struct fbdev_backend *backend, const char *device)
 {
-       struct fbdev_output *output;
-       struct weston_head *head;
+       struct fbdev_head *head;
        int fb_fd;
 
-       weston_log("Creating fbdev output.\n");
-
-       output = zalloc(sizeof *output);
-       if (output == NULL)
-               return -1;
+       head = zalloc(sizeof *head);
+       if (!head)
+               return NULL;
 
-       output->backend = backend;
-       output->device = strdup(device);
+       head->device = strdup(device);
 
        /* Create the frame buffer. */
-       fb_fd = fbdev_frame_buffer_open(device, &output->fb_info);
+       fb_fd = fbdev_frame_buffer_open(head->device, &head->fb_info);
        if (fb_fd < 0) {
-               weston_log("Creating frame buffer failed.\n");
+               weston_log("Creating frame buffer head failed.\n");
                goto out_free;
        }
+       close(fb_fd);
 
-       weston_output_init(&output->base, backend->compositor, "fbdev");
+       weston_head_init(&head->base, "fbdev");
+       weston_head_set_connection_status(&head->base, true);
+       weston_head_set_monitor_strings(&head->base, "unknown",
+                                       head->fb_info.id, NULL);
+       weston_head_set_subpixel(&head->base, WL_OUTPUT_SUBPIXEL_UNKNOWN);
+       weston_head_set_physical_size(&head->base, head->fb_info.width_mm,
+                                     head->fb_info.height_mm);
 
-       output->base.destroy = fbdev_output_destroy;
-       output->base.disable = fbdev_output_disable;
-       output->base.enable = fbdev_output_enable;
+       weston_compositor_add_head(backend->compositor, &head->base);
 
-       /* only one static mode in list */
-       output->mode.flags =
-               WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
-       output->mode.width = output->fb_info.x_resolution;
-       output->mode.height = output->fb_info.y_resolution;
-       output->mode.refresh = output->fb_info.refresh_rate;
-       wl_list_insert(&output->base.mode_list, &output->mode.link);
+       weston_log("Created head '%s' for device %s (%s)\n",
+                  head->base.name, head->device, head->base.model);
 
-       output->base.current_mode = &output->mode;
+       return head;
 
-       head = &output->base.head;
-       weston_head_set_monitor_strings(head, "unknown", output->fb_info.id,
-                                       NULL);
-       weston_head_set_subpixel(head, WL_OUTPUT_SUBPIXEL_UNKNOWN);
-       weston_head_set_physical_size(head, output->fb_info.width_mm,
-                                     output->fb_info.height_mm);
+out_free:
+       free(head->device);
+       free(head);
 
-       close(fb_fd);
+       return NULL;
+}
 
-       weston_compositor_add_pending_output(&output->base, backend->compositor);
+static void
+fbdev_head_destroy(struct fbdev_head *head)
+{
+       weston_head_release(&head->base);
+       free(head->device);
+       free(head);
+}
 
-       return 0;
+static struct weston_output *
+fbdev_output_create(struct weston_compositor *compositor,
+                    const char *name)
+{
+       struct fbdev_output *output;
 
-out_free:
-       free(output->device);
-       free(output);
+       weston_log("Creating fbdev output.\n");
 
-       return -1;
+       output = zalloc(sizeof *output);
+       if (output == NULL)
+               return NULL;
+
+       output->backend = to_fbdev_backend(compositor);
+
+       weston_output_init(&output->base, compositor, name);
+
+       output->base.destroy = fbdev_output_destroy;
+       output->base.disable = fbdev_output_disable;
+       output->base.enable = fbdev_output_enable;
+       output->base.attach_head = fbdev_output_attach_head;
+
+       weston_compositor_add_pending_output(&output->base, compositor);
+
+       return &output->base;
 }
 
 static void
@@ -566,7 +636,6 @@ fbdev_output_destroy(struct weston_output *base)
        /* Remove the output. */
        weston_output_release(&output->base);
 
-       free(output->device);
        free(output);
 }
 
@@ -592,14 +661,17 @@ fbdev_output_reenable(struct fbdev_backend *backend,
                       struct weston_output *base)
 {
        struct fbdev_output *output = to_fbdev_output(base);
+       struct fbdev_head *head;
        struct fbdev_screeninfo new_screen_info;
        int fb_fd;
 
+       head = fbdev_output_get_head(output);
+
        weston_log("Re-enabling fbdev output.\n");
        assert(output->base.enabled);
 
        /* Create the frame buffer. */
-       fb_fd = fbdev_frame_buffer_open(output->device, &new_screen_info);
+       fb_fd = fbdev_frame_buffer_open(head->device, &new_screen_info);
        if (fb_fd < 0) {
                weston_log("Creating frame buffer failed.\n");
                return -1;
@@ -607,9 +679,9 @@ fbdev_output_reenable(struct fbdev_backend *backend,
 
        /* Check whether the frame buffer details have changed since we were
         * disabled. */
-       if (compare_screen_info (&output->fb_info, &new_screen_info) != 0) {
+       if (compare_screen_info(&head->fb_info, &new_screen_info) != 0) {
                /* Perform a mode-set to restore the old mode. */
-               if (fbdev_set_screen_info(fb_fd, &output->fb_info) < 0) {
+               if (fbdev_set_screen_info(fb_fd, &head->fb_info) < 0) {
                        weston_log("Failed to restore mode settings. "
                                   "Attempting to re-open output anyway.\n");
                }
@@ -636,12 +708,16 @@ static void
 fbdev_backend_destroy(struct weston_compositor *base)
 {
        struct fbdev_backend *backend = to_fbdev_backend(base);
+       struct weston_head *head, *next;
 
        udev_input_destroy(&backend->input);
 
        /* Destroy the output. */
        weston_compositor_shutdown(base);
 
+       wl_list_for_each_safe(head, next, &base->head_list, compositor_link)
+               fbdev_head_destroy(to_fbdev_head(head));
+
        /* Chain up. */
        weston_launcher_destroy(base->launcher);
 
@@ -732,6 +808,7 @@ fbdev_backend_create(struct weston_compositor *compositor,
        }
 
        backend->base.destroy = fbdev_backend_destroy;
+       backend->base.create_output = fbdev_output_create;
 
        backend->prev_state = WESTON_COMPOSITOR_ACTIVE;
 
@@ -740,7 +817,7 @@ fbdev_backend_create(struct weston_compositor *compositor,
        if (pixman_renderer_init(compositor) < 0)
                goto out_launcher;
 
-       if (fbdev_output_create(backend, param->device) < 0)
+       if (!fbdev_head_create(backend, param->device))
                goto out_launcher;
 
        udev_input_init(&backend->input, compositor, backend->udev,