1// Copyright 2018 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <stdio.h>
6#include <stdlib.h>
7#include <fcntl.h>
8#include <unistd.h>
9#include <errno.h>
10#include <string.h>
11
12#include <lib/fidl/coding.h>
13#include <zircon/assert.h>
14#include <zircon/device/display-controller.h>
15#include <zircon/pixelformat.h>
16#include <zircon/process.h>
17#include <zircon/syscalls.h>
18#include <zircon/types.h>
19
20#include "fuchsia/display/c/fidl.h"
21#include "lib/framebuffer/framebuffer.h"
22
23static int32_t dc_fd = -1;
24static zx_handle_t dc_handle = ZX_HANDLE_INVALID;
25
26static int32_t txid;
27static int32_t display_id;
28static int32_t layer_id;
29
30static int32_t width;
31static int32_t height;
32static int32_t stride;
33static zx_pixel_format_t format;
34static bool type_set;
35static uint32_t image_type;
36
37static zx_handle_t vmo = ZX_HANDLE_INVALID;
38
39static bool inited = false;
40static bool in_single_buffer_mode;
41
42static zx_status_t set_layer_config(int32_t layer_id, uint32_t width, uint32_t height,
43                                    zx_pixel_format_t format, int32_t type) {
44    fuchsia_display_ControllerSetLayerPrimaryConfigRequest layer_cfg_msg = {};
45    layer_cfg_msg.hdr.ordinal = fuchsia_display_ControllerSetLayerPrimaryConfigOrdinal;
46    layer_cfg_msg.layer_id = layer_id;
47    layer_cfg_msg.image_config.width = width;
48    layer_cfg_msg.image_config.height = height;
49    layer_cfg_msg.image_config.pixel_format = format;
50    layer_cfg_msg.image_config.type = type;
51
52    return zx_channel_write(dc_handle, 0, &layer_cfg_msg, sizeof(layer_cfg_msg), NULL, 0);
53}
54
55zx_status_t fb_bind(bool single_buffer, const char** err_msg_out) {
56    const char* err_msg;
57    if (!err_msg_out) {
58        err_msg_out = &err_msg;
59    }
60    *err_msg_out = "";
61
62    if (inited) {
63        *err_msg_out = "framebufer already initialzied";
64        return ZX_ERR_ALREADY_BOUND;
65    }
66
67    // TODO(stevensd): Don't hardcode display controller 0
68    zx_status_t status;
69    dc_fd = open("/dev/class/display-controller/000", O_RDWR);
70    if (dc_fd < 0) {
71        *err_msg_out = "Failed to open display controller";
72        status = ZX_ERR_NO_RESOURCES;
73        goto err;
74    }
75
76    if (ioctl_display_controller_get_handle(dc_fd, &dc_handle) != sizeof(zx_handle_t)) {
77        *err_msg_out = "Failed to get display controller handle";
78        status = ZX_ERR_INTERNAL;
79        goto err;
80    }
81
82    uint8_t bytes[ZX_CHANNEL_MAX_MSG_BYTES];
83    uint32_t actual_bytes, actual_handles;
84    bool has_display = false;
85    do {
86        zx_handle_t observed;
87        uint32_t signals = ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED;
88        if ((status = zx_object_wait_one(dc_handle,
89                                         signals, ZX_TIME_INFINITE, &observed)) != ZX_OK) {
90            *err_msg_out = "Failed waiting for display";
91            goto err;
92        }
93        if (observed & ZX_CHANNEL_PEER_CLOSED) {
94            *err_msg_out = "Display controller connection closed";
95            status = ZX_ERR_PEER_CLOSED;
96            goto err;
97        }
98
99        if ((status = zx_channel_read(dc_handle, 0, bytes, NULL, ZX_CHANNEL_MAX_MSG_BYTES, 0,
100                                      &actual_bytes, &actual_handles)) != ZX_OK
101                || actual_bytes < sizeof(fidl_message_header_t)) {
102            *err_msg_out = "Reading display addded callback failed";
103            goto err;
104        }
105
106        fidl_message_header_t* hdr = (fidl_message_header_t*) bytes;
107        if (hdr->ordinal == fuchsia_display_ControllerDisplaysChangedOrdinal) {
108            if ((status = fidl_decode(&fuchsia_display_ControllerDisplaysChangedEventTable,
109                                      bytes, actual_bytes, NULL, 0, err_msg_out)) != ZX_OK) {
110                goto err;
111            }
112            has_display = true;
113        }
114    } while (!has_display);
115
116    // We're guaranteed that added contains at least one display, since we haven't
117    // been notified of any displays to remove.
118    fuchsia_display_ControllerDisplaysChangedEvent* changes =
119            (fuchsia_display_ControllerDisplaysChangedEvent*) bytes;
120    fuchsia_display_Info* display = (fuchsia_display_Info*) changes->added.data;
121    fuchsia_display_Mode* mode = (fuchsia_display_Mode*) display->modes.data;
122    zx_pixel_format_t pixel_format = ((int32_t*)(display->pixel_format.data))[0];
123
124    fuchsia_display_ControllerComputeLinearImageStrideRequest stride_msg;
125    stride_msg.hdr.ordinal = fuchsia_display_ControllerComputeLinearImageStrideOrdinal;
126    stride_msg.hdr.txid = txid++;
127    stride_msg.width = mode->horizontal_resolution;
128    stride_msg.pixel_format = pixel_format;
129
130    fuchsia_display_ControllerComputeLinearImageStrideResponse stride_rsp;
131    zx_channel_call_args_t stride_call = {};
132    stride_call.wr_bytes = &stride_msg;
133    stride_call.rd_bytes = &stride_rsp;
134    stride_call.wr_num_bytes = sizeof(stride_msg);
135    stride_call.rd_num_bytes = sizeof(stride_rsp);
136    if ((status = zx_channel_call(dc_handle, 0, ZX_TIME_INFINITE, &stride_call,
137                                  &actual_bytes, &actual_handles)) != ZX_OK) {
138        *err_msg_out = "Failed to get linear stride";
139        goto err;
140    }
141
142    fuchsia_display_ControllerCreateLayerRequest create_layer_msg;
143    create_layer_msg.hdr.ordinal = fuchsia_display_ControllerCreateLayerOrdinal;
144
145    fuchsia_display_ControllerCreateLayerResponse create_layer_rsp;
146    zx_channel_call_args_t call_args = {};
147    call_args.wr_bytes = &create_layer_msg;
148    call_args.rd_bytes = &create_layer_rsp;
149    call_args.wr_num_bytes = sizeof(create_layer_msg);
150    call_args.rd_num_bytes = sizeof(create_layer_rsp);
151    if ((status = zx_channel_call(dc_handle, 0, ZX_TIME_INFINITE, &call_args,
152                                  &actual_bytes, &actual_handles)) != ZX_OK) {
153        *err_msg_out = "Create layer call failed";
154        goto err;
155    }
156    if (create_layer_rsp.res != ZX_OK) {
157        *err_msg_out = "Failed to create layer";
158        status = create_layer_rsp.res;
159        goto err;
160    }
161
162    uint8_t fidl_bytes[sizeof(fuchsia_display_ControllerSetDisplayLayersRequest)
163                       + FIDL_ALIGN(sizeof(uint64_t))];
164    fuchsia_display_ControllerSetDisplayLayersRequest* set_display_layer_request =
165            (fuchsia_display_ControllerSetDisplayLayersRequest*) fidl_bytes;
166    *(uint64_t*)(fidl_bytes + sizeof(fuchsia_display_ControllerSetDisplayLayersRequest)) =
167            create_layer_rsp.layer_id;
168
169    set_display_layer_request->hdr.ordinal = fuchsia_display_ControllerSetDisplayLayersOrdinal;
170    set_display_layer_request->display_id = display->id;
171    set_display_layer_request->layer_ids.count = 1;
172    set_display_layer_request->layer_ids.data = (void*) FIDL_ALLOC_PRESENT;
173
174    if ((status = zx_channel_write(dc_handle, 0, fidl_bytes,
175                                   sizeof(fidl_bytes), NULL, 0)) != ZX_OK) {
176        *err_msg_out = "Failed to set display layers";
177        goto err;
178    }
179
180    if ((status = set_layer_config(create_layer_rsp.layer_id, mode->horizontal_resolution,
181                                   mode->vertical_resolution, pixel_format,
182                                   IMAGE_TYPE_SIMPLE)) != ZX_OK) {
183        *err_msg_out = "Failed to set layer config";
184        goto err;
185    }
186
187    display_id = display->id;
188    layer_id = create_layer_rsp.layer_id;
189
190    width = mode->horizontal_resolution;
191    height = mode->vertical_resolution;
192    format = pixel_format;
193    stride = stride_rsp.stride;
194
195    type_set = false;
196
197    inited = true;
198
199    if (single_buffer) {
200        uint32_t size = stride * height * ZX_PIXEL_FORMAT_BYTES(format);
201        fuchsia_display_ControllerAllocateVmoRequest alloc_msg;
202        alloc_msg.hdr.ordinal = fuchsia_display_ControllerAllocateVmoOrdinal;
203        alloc_msg.hdr.txid = txid++;
204        alloc_msg.size = size;
205
206        fuchsia_display_ControllerAllocateVmoResponse alloc_rsp;
207        zx_channel_call_args_t call_args = {};
208        call_args.wr_bytes = &alloc_msg;
209        call_args.rd_bytes = &alloc_rsp;
210        call_args.rd_handles = &vmo;
211        call_args.wr_num_bytes = sizeof(alloc_msg);
212        call_args.rd_num_bytes = sizeof(alloc_rsp);
213        call_args.rd_num_handles = 1;
214        uint32_t actual_bytes, actual_handles;
215        if ((status = zx_channel_call(dc_handle, 0, ZX_TIME_INFINITE, &call_args,
216                                      &actual_bytes, &actual_handles)) != ZX_OK) {
217            *err_msg_out = "Failed vmo alloc call";
218            goto err;
219        }
220        if (alloc_rsp.res != ZX_OK) {
221            status = alloc_rsp.res;
222            *err_msg_out = "Failed to alloc vmo";
223            goto err;
224        }
225
226        // Failure to set the cache policy isn't a fatal error
227        zx_vmo_set_cache_policy(vmo, ZX_CACHE_POLICY_WRITE_COMBINING);
228
229        zx_handle_t dup;
230        if ((status = zx_handle_duplicate(vmo, ZX_RIGHT_SAME_RIGHTS, &dup)) != ZX_OK) {
231            *err_msg_out = "Couldn't duplicate vmo\n";;
232            goto err;
233        }
234
235        // fb_(present|import)_image expect to not be in single buffer
236        // mode, so make sure this is false for now. It will get set properly later.
237        in_single_buffer_mode = false;
238
239        uint64_t image_id;
240        if ((status = fb_import_image(dup, 0, &image_id)) != ZX_OK) {
241            *err_msg_out = "Couldn't import framebuffer";
242            goto err;
243        }
244
245        if ((status = fb_present_image(image_id, INVALID_ID, INVALID_ID, INVALID_ID)) != ZX_OK) {
246            *err_msg_out = "Failed to present single_buffer mode framebuffer";
247            goto err;
248        }
249    }
250
251    in_single_buffer_mode = single_buffer;
252
253    return ZX_OK;
254err:
255    inited = false;
256
257    zx_handle_close(dc_handle);
258    dc_handle = ZX_HANDLE_INVALID;
259
260    if (dc_fd >= 0) {
261        close(dc_fd);
262        dc_fd = -1;
263    }
264
265    zx_handle_close(vmo);
266    vmo = ZX_HANDLE_INVALID;
267
268    return status;
269}
270
271void fb_release() {
272    if (!inited) {
273        return;
274    }
275
276    zx_handle_close(dc_handle);
277    dc_handle = ZX_HANDLE_INVALID;
278
279    close(dc_fd);
280    dc_fd = -1;
281
282    if (in_single_buffer_mode) {
283        zx_handle_close(vmo);
284        vmo = ZX_HANDLE_INVALID;
285    }
286
287    inited = false;
288}
289
290void fb_get_config(uint32_t* width_out, uint32_t* height_out,
291                   uint32_t* linear_stride_px_out, zx_pixel_format_t* format_out) {
292    ZX_ASSERT(inited);
293
294    *width_out = width;
295    *height_out = height;
296    *format_out = format;
297    *linear_stride_px_out = stride;
298}
299
300zx_handle_t fb_get_single_buffer() {
301    ZX_ASSERT(inited && in_single_buffer_mode);
302    return vmo;
303}
304
305zx_status_t fb_import_image(zx_handle_t handle, uint32_t type, uint64_t *id_out) {
306    ZX_ASSERT(inited && !in_single_buffer_mode);
307    zx_status_t status;
308
309    if (type_set && type != image_type) {
310        return ZX_ERR_BAD_STATE;
311    } else if (!type_set && type != IMAGE_TYPE_SIMPLE) {
312        if ((status = set_layer_config(layer_id, width, height, format, type)) != ZX_OK) {
313            return status;
314        }
315        image_type = type;
316        type_set = true;
317    }
318
319    fuchsia_display_ControllerImportVmoImageRequest import_msg = {};
320    import_msg.hdr.ordinal = fuchsia_display_ControllerImportVmoImageOrdinal;
321    import_msg.hdr.txid = txid++;
322    import_msg.image_config.height = height;
323    import_msg.image_config.width = width;
324    import_msg.image_config.pixel_format = format;
325    import_msg.image_config.type = type;
326    import_msg.vmo = FIDL_HANDLE_PRESENT;
327    import_msg.offset = 0;
328
329    fuchsia_display_ControllerImportVmoImageResponse import_rsp;
330    zx_channel_call_args_t import_call = {};
331    import_call.wr_bytes = &import_msg;
332    import_call.wr_handles = &handle;
333    import_call.rd_bytes = &import_rsp;
334    import_call.wr_num_bytes = sizeof(import_msg);
335    import_call.wr_num_handles = 1;
336    import_call.rd_num_bytes = sizeof(import_rsp);
337    uint32_t actual_bytes, actual_handles;
338    if ((status = zx_channel_call(dc_handle, 0, ZX_TIME_INFINITE, &import_call,
339                                  &actual_bytes, &actual_handles)) != ZX_OK) {
340        return status;
341    }
342
343    if (import_rsp.res != ZX_OK) {
344        return import_rsp.res;
345    }
346
347    *id_out = import_rsp.image_id;
348    return ZX_OK;
349}
350
351void fb_release_image(uint64_t image_id) {
352    ZX_ASSERT(inited && !in_single_buffer_mode);
353
354    fuchsia_display_ControllerReleaseEventRequest release_img_msg;
355    release_img_msg.hdr.ordinal = fuchsia_display_ControllerReleaseEventOrdinal;
356    release_img_msg.hdr.txid = txid++;
357    release_img_msg.id = image_id;
358
359    // There's nothing meaningful to do if this call fails
360    zx_channel_write(dc_handle, 0, &release_img_msg, sizeof(release_img_msg), NULL, 0);
361}
362
363zx_status_t fb_import_event(zx_handle_t handle, uint64_t id) {
364    ZX_ASSERT(inited && !in_single_buffer_mode);
365
366    fuchsia_display_ControllerImportEventRequest import_evt_msg;
367    import_evt_msg.hdr.ordinal = fuchsia_display_ControllerImportEventOrdinal;
368    import_evt_msg.hdr.txid = txid++;
369    import_evt_msg.id = id;
370    import_evt_msg.event = FIDL_HANDLE_PRESENT;
371
372    return zx_channel_write(dc_handle, 0, &import_evt_msg, sizeof(import_evt_msg), &handle, 1);
373}
374
375void fb_release_event(uint64_t id) {
376    ZX_ASSERT(inited && !in_single_buffer_mode);
377
378    fuchsia_display_ControllerReleaseEventRequest release_evt_msg;
379    release_evt_msg.hdr.ordinal = fuchsia_display_ControllerReleaseEventOrdinal;
380    release_evt_msg.hdr.txid = txid++;
381    release_evt_msg.id = id;
382
383    // There's nothing meaningful we can do if this call fails
384    zx_channel_write(dc_handle, 0, &release_evt_msg, sizeof(release_evt_msg), NULL, 0);
385}
386
387zx_status_t fb_present_image2(uint64_t image_id, uint64_t wait_event_id, uint64_t signal_event_id) {
388    return fb_present_image(image_id, wait_event_id, INVALID_ID, signal_event_id);
389}
390
391zx_status_t fb_present_image(uint64_t image_id, uint64_t wait_event_id,
392                             uint64_t present_event_id, uint64_t signal_event_id) {
393    ZX_ASSERT(present_event_id == INVALID_ID);
394    ZX_ASSERT(inited && !in_single_buffer_mode);
395    zx_status_t status;
396
397    fuchsia_display_ControllerSetLayerImageRequest set_msg;
398    set_msg.hdr.ordinal = fuchsia_display_ControllerSetLayerImageOrdinal;
399    set_msg.hdr.txid = txid++;
400    set_msg.layer_id = layer_id;
401    set_msg.image_id = image_id;
402    set_msg.wait_event_id = wait_event_id;
403    set_msg.signal_event_id = signal_event_id;
404    if ((status = zx_channel_write(dc_handle, 0, &set_msg, sizeof(set_msg), NULL, 0)) != ZX_OK) {
405        return status;
406    }
407
408    // It's not necessary to validate the configuration, since we're guaranteed that a single
409    // fullscreen framebuffer on a single monitor will work.
410    fuchsia_display_ControllerApplyConfigRequest apply_msg;
411    apply_msg.hdr.txid = txid++;
412    apply_msg.hdr.ordinal = fuchsia_display_ControllerApplyConfigOrdinal;
413    return zx_channel_write(dc_handle, 0, &apply_msg, sizeof(apply_msg), NULL, 0);
414}
415
416zx_status_t fb_alloc_image_buffer(zx_handle_t* vmo_out) {
417    uint32_t size = stride * height * ZX_PIXEL_FORMAT_BYTES(format);
418    fuchsia_display_ControllerAllocateVmoRequest alloc_msg;
419    alloc_msg.hdr.ordinal = fuchsia_display_ControllerAllocateVmoOrdinal;
420    alloc_msg.hdr.txid = txid++;
421    alloc_msg.size = size;
422
423    fuchsia_display_ControllerAllocateVmoResponse alloc_rsp;
424    zx_channel_call_args_t call_args = {};
425    call_args.wr_bytes = &alloc_msg;
426    call_args.rd_bytes = &alloc_rsp;
427    call_args.rd_handles = vmo_out;
428    call_args.wr_num_bytes = sizeof(alloc_msg);
429    call_args.rd_num_bytes = sizeof(alloc_rsp);
430    call_args.rd_num_handles = 1;
431    uint32_t actual_bytes, actual_handles;
432    zx_status_t status;
433    if ((status = zx_channel_call(dc_handle, 0, ZX_TIME_INFINITE, &call_args,
434                                  &actual_bytes, &actual_handles)) != ZX_OK) {
435        if (alloc_rsp.res != ZX_OK) {
436            status = alloc_rsp.res;
437        }
438        return status;
439    }
440    static const char kVmoName[] = "framebuffer";
441    zx_object_set_property(*vmo_out, ZX_PROP_NAME, kVmoName, sizeof(kVmoName));
442    return ZX_OK;
443}
444
445zx_status_t fb_enable_vsync(bool enable) {
446    fuchsia_display_ControllerEnableVsyncRequest enable_vsync;
447    enable_vsync.hdr.ordinal = fuchsia_display_ControllerEnableVsyncOrdinal;
448    enable_vsync.enable = enable;
449    zx_status_t status;
450    if ((status = zx_channel_write(dc_handle, 0, &enable_vsync, sizeof(enable_vsync),
451                                   NULL, 0)) != ZX_OK) {
452        return status;
453    }
454    return ZX_OK;
455}
456
457zx_status_t fb_wait_for_vsync(zx_time_t* timestamp, uint64_t* image_id) {
458    zx_status_t status;
459
460    zx_handle_t observed;
461    uint32_t signals = ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED;
462    if ((status = zx_object_wait_one(dc_handle, signals, ZX_TIME_INFINITE,
463                                     &observed)) != ZX_OK) {
464        return status;
465    }
466    if (observed & ZX_CHANNEL_PEER_CLOSED) {
467        return ZX_ERR_PEER_CLOSED;
468    }
469
470    uint8_t bytes[ZX_CHANNEL_MAX_MSG_BYTES];
471    uint32_t actual_bytes, actual_handles;
472    if ((status = zx_channel_read(dc_handle, 0, bytes, NULL, ZX_CHANNEL_MAX_MSG_BYTES, 0,
473                                  &actual_bytes, &actual_handles)) != ZX_OK) {
474        return ZX_ERR_STOP;
475    }
476
477    if (actual_bytes < sizeof(fidl_message_header_t)) {
478        return ZX_ERR_INTERNAL;
479    }
480
481    fidl_message_header_t* header = (fidl_message_header_t*) bytes;
482
483    switch (header->ordinal) {
484    case fuchsia_display_ControllerDisplaysChangedOrdinal:
485        return ZX_ERR_STOP;
486    case fuchsia_display_ControllerClientOwnershipChangeOrdinal:
487        return ZX_ERR_NEXT;
488    case fuchsia_display_ControllerVsyncOrdinal:
489        break;
490    default:
491        return ZX_ERR_STOP;
492    }
493
494    const char* err_msg;
495    if ((status = fidl_decode(&fuchsia_display_ControllerVsyncEventTable,
496                              bytes, actual_bytes, NULL, 0, &err_msg)) != ZX_OK) {
497        return ZX_ERR_STOP;
498    }
499
500    fuchsia_display_ControllerVsyncEvent* vsync =
501        (fuchsia_display_ControllerVsyncEvent*) bytes;
502    *timestamp = vsync->timestamp;
503    *image_id = vsync->images.count ? *((uint64_t*)vsync->images.data) : FB_INVALID_ID;
504    return ZX_OK;
505}
506